<commit_before>'use babel';

import path from 'path';
import fs from 'fs-plus';
import React from 'react';
import _ from 'lodash';
import Immutable from 'immutable';
import {CompositeDisposable} from 'atom';
import {File} from 'pathwatcher';
import {Emitter} from 'event-kit';
import {Transformime} from 'transformime';
import {
  StreamTransformer,
  TracebackTransformer,
  MarkdownTransformer,
  LaTeXTransformer,
  PDFTransformer
} from 'transformime-jupyter-transformers';
import {
  listRunningKernels,
  connectToKernel,
  startNewKernel,
  getKernelSpecs
} from 'jupyter-js-services';
import {XMLHttpRequest} from 'xmlhttprequest';
import ws from 'ws';
import Dispatcher from './dispatcher';
import NotebookEditorView from './notebook-editor-view';
import NotebookCell from './notebook-cell';
global.XMLHttpRequest = XMLHttpRequest;
global.WebSocket = ws;

export default class NotebookEditor {

    constructor(uri) {
      console.log('NotebookEditor created for', uri);
      this.loadNotebookFile(uri);
      this.emitter = new Emitter();
      this.subscriptions = new CompositeDisposable();
      this.transformer = new Transformime();
      this.transformer.transformers.push(new StreamTransformer());
      this.transformer.transformers.push(new TracebackTransformer());
      this.transformer.transformers.push(new MarkdownTransformer());
      this.transformer.transformers.push(new LaTeXTransformer());
      this.transformer.transformers.push(new PDFTransformer());
      let language = this.state.getIn(['metadata', 'kernelspec', 'language']);
      getKernelSpecs('http://localhost:8888').then((kernelSpecs) => {
        if (_.include(Object.keys(kernelSpecs.kernelspecs), language)) {
          startNewKernel({
            baseUrl: 'http://localhost:8888',
            wsUrl: 'ws://localhost:8888',
            name: kernelSpecs.kernelspecs[language].name
          }).then((kernel) => {
            this.session = kernel;
          });
        }
      });
      Dispatcher.register(this.onAction);
      //TODO: remove these development handles
      global.editor = this;
      global.Dispatcher = Dispatcher;
    }

    findCellByID(id) {
      return this.state.get('cells').findEntry(cell => {
        return cell.get('id') == id;
      });
    }

    findActiveCell() {
      let activeCellInfo = this.state.get('cells').findEntry(cell => cell.get('focus'));
      console.log('Active cell:', activeCellInfo);
      return activeCellInfo;
    }

    onAction = (payload) => {
      console.log(`Action '${payload.actionType.toString()}'received in NotebookEditor`);
      //TODO: add a notebook ID field to events and filter on it
      this.setModified(true);
      let cellInfo,
          cellIndex,
          cell;
      switch (payload.actionType) {
        case Dispatcher.actions.add_cell:
          let newCell = Immutable.fromJS({
            cell_type: 'code',
            execution_count: null,
            metadata: {
              collapsed: false
            },
            outputs: [],
            source: []
          });
          this.state = this.state.set('cells', this.state.get('cells').push(newCell));
          this._onChange();
          break;
        case Dispatcher.actions.cell_source_changed:
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
          }
          this.state = this.state.setIn(['cells', cellIndex, 'source'], payload.source);
          break;
        case Dispatcher.actions.cell_focus:
          let activeCellInfo = this.findActiveCell();
          if (!activeCellInfo || activeCellInfo === undefined || activeCellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [activeCellIndex, activeCell] = activeCellInfo;
            console.log(`Cell is at index ${cellIndex}`);
          }
          this.state = this.state.setIn(['cells', activeCellIndex, 'focus'], false);
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
          }
          this.state = this.state.setIn(['cells', cellIndex, 'focus'], payload.isFocused);
          this._onChange();
          break;
        case Dispatcher.actions.run_cell:
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
          }
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t run.');
          this.state = this.state.setIn(['cells', cellIndex, 'outputs'], Immutable.List());
          var future = this.session.execute({code: cell.get('source')}, false);
          future.onDone = () => {
            console.log('Future is fulfilled');
            setTimeout(this.dispose, 1500);
          }
          future.onIOPub = (msg) => {
            Dispatcher.dispatch({
              actionType: Dispatcher.actions.output_received,
              cellID: payload.cellID,
              message: msg
            });
          }
          this._onChange();
          break;
        case Dispatcher.actions.run_active_cell:
          cellInfo = this.findActiveCell();
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
            console.log(`Cell is at index ${cellIndex}`);
          }
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t run.');
          this.state = this.state.setIn(['cells', cellIndex, 'outputs'], Immutable.List());
          var future = this.session.execute({code: cell.get('source')}, false);
          future.onDone = () => {
            console.log('Future is fulfilled');
            setTimeout(this.dispose, 1500);
          }
          future.onIOPub = (msg) => {
            Dispatcher.dispatch({
              actionType: Dispatcher.actions.output_received,
              cellID: cell.get('id'),
              message: msg
            });
          }
          this._onChange();
          break;
        case Dispatcher.actions.output_received:
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            console.log('Message is for another notebook');
            return;
          } else {
            [cellIndex, cell] = cellInfo;
          }
          console.log('output_received', payload.message);
          let mimeBundle = this.makeMimeBundle(payload.message);
          if (mimeBundle) {
            this.transformer.transformRichest(mimeBundle, document).then(res => {
              let {el} = res;
              let outputs = this.state.getIn(['cells', cellIndex, 'outputs']);
              outputs = outputs.push(el.outerHTML);
              this.state = this.state.setIn(['cells', cellIndex, 'outputs'], outputs);
              this._onChange();
            }).catch(e => {
              debugger;
            });
          }
          break;
        case Dispatcher.actions.interrupt_kernel:
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t interrupt.');
          this.session.interrupt();
          break;
        case Dispatcher.actions.destroy:
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t interrupt.');
          destroy();
          break;
      }
    }

    addStateChangeListener(callback) {
      return this.emitter.on('state-changed', callback);
    }

    _onChange = () => {
      this.emitter.emit('state-changed');
    }

    getState() {
      return this.state;
    }

    loadNotebookFile(uri) {
      console.log('LOAD NOTEBOOK FILE');
      this.file = new File(uri);
      let parsedFile = this.parseNotebookFile(this.file);
      parsedFile.cells = _.map(parsedFile.cells, cell => {
        cell.id = _.uniqueId().toString();
        cell.outputs = [];
        cell.focus = false;
        return cell;
      });
      parsedFile.cells[0].focus = true;
      this.state = Immutable.fromJS(parsedFile);
    }

    parseNotebookFile(file) {
      let fileString = this.file.readSync();
      return JSON.parse(fileString);
    }

    //-------------------------------------------------------------------------
    // Dirty dirty code for rendering messages
    // Stolen from jupyter-display-area; all the good stuff is them,
    // all the bugs are me.
    //-------------------------------------------------------------------------

    //TODO: make this less dirty
    makeMimeBundle(msg) {
      let json = {};
      let msgType = json.output_type = msg.header.msg_type;
      let content = msg.content;
      switch (msgType) {
        case 'clear_output':
          // msg spec v4 had stdout, stderr, display keys
          // v4.1 replaced these with just wait
          // The default behavior is the same (stdout=stderr=display=True, wait=False),
          // so v4 messages will still be properly handled,
          // except for the rarely used clearing less than all output.
          console.log('Not handling clear message!');
          this.clear_output(msg.content.wait || false);
          return;
        case 'stream':
          json.text = content.text;
          json.name = content.name;
          break;
        case 'display_data':
          json.data = content.data;
          json.metadata = content.metadata;
          break;
        case 'execute_result':
          json.data = content.data;
          json.metadata = content.metadata;
          json.execution_count = content.execution_count;
          break;
        case 'error':
          json.ename = content.ename;
          json.evalue = content.evalue;
          json.traceback = content.traceback;
          break;
        case 'status':
        case 'execute_input':
          // Explicit ignore of status changes and input
          return false;
        default:
          console.log('unhandled output message', msg);
          return false;
      }
      // this.append_output(json);
      let bundle,
          el;
      bundle = {};
      switch (json.output_type) {
        case 'execute_result':
        case 'display_data':
          bundle = json.data;
          break;
        case 'stream':
          // bundle = {'text/plain': json.text};
          bundle = {
            'jupyter/stream': json
          };
          break;
        case 'error':
          bundle = {
            'jupyter/traceback': json
          };
          break;
        default:
          console.warn('Unrecognized output type: ' + json.output_type);
          bundle = {
            'text/plain': 'Unrecognized output type' + JSON.stringify(json)
          };
      }
      return bundle;
    }

    save() {
      this.saveAs(this.getPath());
    }

    saveAs(uri) {
      let nbData = this.asJSON()
      try {
        fs.writeFileSync(uri, nbData);
        this.modified = false;
      } catch(e) {
        console.error(e.stack);
        debugger;
      }
      this.emitter.emit('did-change-modified');
    }

    asJSON() {
      return JSON.stringify(this.state.toJSON(), null, 4);
    }

    shouldPromptToSave() {
      return this.isModified();
    }

    getSaveDialogOptions() {
      return {};
    }

    modified = false;
    // modifiedCallbacks = [];

    isModified() {
      return this.modified;
    }

    setModified(modified) {
      console.log('setting modified');
      this.modified = modified;
      this.emitter.emit('did-change-modified');
    }

    onDidChangeModified(callback) {
      return this.emitter.on('did-change-modified', callback);
    }

    //----------------------------------------
    // Listeners, currently never called
    //----------------------------------------

    onDidChange(callback) {
      return this.emitter.on('did-change', callback);
    }

    onDidChangeTitle(callback) {
      return this.emitter.on('did-change-title', callback);
    }

    //----------------------------------------
    // Various info-fetching methods
    //----------------------------------------

    getTitle() {
      let filePath = this.getPath();
      if (filePath !== undefined && filePath !== null) {
        return path.basename(filePath);
      } else {
        return 'untitled';
      }
    }

    getURI() {
      console.log('getURI called');
      return this.getPath();
    }

    getPath() {
      console.log('getPath called');
      return this.file.getPath();
    }

    isEqual(other) {
      return (other instanceof ImageEditor && this.getURI() == other.getURI());
    }

    destroy() {
      console.log('destroy called');
      this.subscriptions.dispose();
      try {
        this.session.shutdown();
      } catch(e) {}
    }

    //----------------------------------------
    // Serialization (one of these days...)
    //----------------------------------------

    // static deserialize({filePath}) {
    //     if (fs.isFileSync(filePath)) {
    //         new NotebookEditor(filePath);
    //     } else {
    //         console.warn(`Could not deserialize notebook editor for path \
    //                      '${filePath}' because that file no longer exists.`);
    //     }
    // }

    // serialize() {
    //     return {
    //         filePath: this.getPath(),
    //         deserializer: this.constructor.name
    //     }
    // }

}

// atom.deserializers.add(NotebookEditor);
<commit_msg>Fix issue with stream messages not rendering<commit_after>'use babel';

import path from 'path';
import fs from 'fs-plus';
import React from 'react';
import _ from 'lodash';
import Immutable from 'immutable';
import {CompositeDisposable} from 'atom';
import {File} from 'pathwatcher';
import {Emitter} from 'event-kit';
import {Transformime} from 'transformime';
import {
  StreamTransformer,
  TracebackTransformer,
  MarkdownTransformer,
  LaTeXTransformer,
  PDFTransformer
} from 'transformime-jupyter-transformers';
import {
  listRunningKernels,
  connectToKernel,
  startNewKernel,
  getKernelSpecs
} from 'jupyter-js-services';
import {XMLHttpRequest} from 'xmlhttprequest';
import ws from 'ws';
import Dispatcher from './dispatcher';
import NotebookEditorView from './notebook-editor-view';
import NotebookCell from './notebook-cell';
global.XMLHttpRequest = XMLHttpRequest;
global.WebSocket = ws;

export default class NotebookEditor {

    constructor(uri) {
      console.log('NotebookEditor created for', uri);
      this.loadNotebookFile(uri);
      this.emitter = new Emitter();
      this.subscriptions = new CompositeDisposable();
      this.transformer = new Transformime();
      this.transformer.transformers.push(new StreamTransformer());
      this.transformer.transformers.push(new TracebackTransformer());
      this.transformer.transformers.push(new MarkdownTransformer());
      this.transformer.transformers.push(new LaTeXTransformer());
      this.transformer.transformers.push(new PDFTransformer());
      let language = this.state.getIn(['metadata', 'kernelspec', 'language']);
      getKernelSpecs('http://localhost:8888').then((kernelSpecs) => {
        if (_.include(Object.keys(kernelSpecs.kernelspecs), language)) {
          startNewKernel({
            baseUrl: 'http://localhost:8888',
            wsUrl: 'ws://localhost:8888',
            name: kernelSpecs.kernelspecs[language].name
          }).then((kernel) => {
            this.session = kernel;
          });
        }
      });
      Dispatcher.register(this.onAction);
      //TODO: remove these development handles
      global.editor = this;
      global.Dispatcher = Dispatcher;
    }

    findCellByID(id) {
      return this.state.get('cells').findEntry(cell => {
        return cell.get('id') == id;
      });
    }

    findActiveCell() {
      let activeCellInfo = this.state.get('cells').findEntry(cell => cell.get('focus'));
      console.log('Active cell:', activeCellInfo);
      return activeCellInfo;
    }

    onAction = (payload) => {
      console.log(`Action '${payload.actionType.toString()}'received in NotebookEditor`);
      //TODO: add a notebook ID field to events and filter on it
      this.setModified(true);
      let cellInfo,
          cellIndex,
          cell;
      switch (payload.actionType) {
        case Dispatcher.actions.add_cell:
          let newCell = Immutable.fromJS({
            cell_type: 'code',
            execution_count: null,
            metadata: {
              collapsed: false
            },
            outputs: [],
            source: []
          });
          this.state = this.state.set('cells', this.state.get('cells').push(newCell));
          this._onChange();
          break;
        case Dispatcher.actions.cell_source_changed:
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
          }
          this.state = this.state.setIn(['cells', cellIndex, 'source'], payload.source);
          break;
        case Dispatcher.actions.cell_focus:
          let activeCellInfo = this.findActiveCell();
          if (!activeCellInfo || activeCellInfo === undefined || activeCellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [activeCellIndex, activeCell] = activeCellInfo;
            console.log(`Cell is at index ${cellIndex}`);
          }
          this.state = this.state.setIn(['cells', activeCellIndex, 'focus'], false);
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
          }
          this.state = this.state.setIn(['cells', cellIndex, 'focus'], payload.isFocused);
          this._onChange();
          break;
        case Dispatcher.actions.run_cell:
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
          }
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t run.');
          this.state = this.state.setIn(['cells', cellIndex, 'outputs'], Immutable.List());
          var future = this.session.execute({code: cell.get('source')}, false);
          future.onDone = () => {
            console.log('Future is fulfilled');
            setTimeout(this.dispose, 1500);
          }
          future.onIOPub = (msg) => {
            Dispatcher.dispatch({
              actionType: Dispatcher.actions.output_received,
              cellID: payload.cellID,
              message: msg
            });
          }
          this._onChange();
          break;
        case Dispatcher.actions.run_active_cell:
          cellInfo = this.findActiveCell();
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            return console.log('Message is for another notebook');
          } else {
            [cellIndex, cell] = cellInfo;
            console.log(`Cell is at index ${cellIndex}`);
          }
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t run.');
          this.state = this.state.setIn(['cells', cellIndex, 'outputs'], Immutable.List());
          var future = this.session.execute({code: cell.get('source')}, false);
          future.onDone = () => {
            console.log('Future is fulfilled');
            setTimeout(this.dispose, 1500);
          }
          future.onIOPub = (msg) => {
            Dispatcher.dispatch({
              actionType: Dispatcher.actions.output_received,
              cellID: cell.get('id'),
              message: msg
            });
          }
          this._onChange();
          break;
        case Dispatcher.actions.output_received:
          cellInfo = this.findCellByID(payload.cellID);
          if (!cellInfo || cellInfo === undefined || cellInfo === null) {
            console.log('Message is for another notebook');
            return;
          } else {
            [cellIndex, cell] = cellInfo;
          }
          console.log('output_received', payload.message);
          let mimeBundle = this.makeMimeBundle(payload.message);
          if (mimeBundle) {
            this.transformer.transformRichest(mimeBundle, document).then(res => {
              let {el} = res;
              let outputs = this.state.getIn(['cells', cellIndex, 'outputs']);
              outputs = outputs.push(el.outerHTML);
              this.state = this.state.setIn(['cells', cellIndex, 'outputs'], outputs);
              this._onChange();
            }).catch(e => {
              debugger;
            });
          }
          break;
        case Dispatcher.actions.interrupt_kernel:
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t interrupt.');
          this.session.interrupt();
          break;
        case Dispatcher.actions.destroy:
          if (this.session === undefined || this.session === null) return console.log('No session, can\'t interrupt.');
          destroy();
          break;
      }
    }

    addStateChangeListener(callback) {
      return this.emitter.on('state-changed', callback);
    }

    _onChange = () => {
      this.emitter.emit('state-changed');
    }

    getState() {
      return this.state;
    }

    loadNotebookFile(uri) {
      console.log('LOAD NOTEBOOK FILE');
      this.file = new File(uri);
      let parsedFile = this.parseNotebookFile(this.file);
      parsedFile.cells = _.map(parsedFile.cells, cell => {
        cell.id = _.uniqueId().toString();
        cell.outputs = [];
        cell.focus = false;
        return cell;
      });
      parsedFile.cells[0].focus = true;
      this.state = Immutable.fromJS(parsedFile);
    }

    parseNotebookFile(file) {
      let fileString = this.file.readSync();
      return JSON.parse(fileString);
    }

    //-------------------------------------------------------------------------
    // Dirty dirty code for rendering messages
    // Stolen from jupyter-display-area; all the good stuff is them,
    // all the bugs are me.
    //-------------------------------------------------------------------------

    //TODO: make this less dirty
    makeMimeBundle(msg) {
      let json = {};
      let msgType = json.output_type = msg.header.msg_type;
      let content = msg.content;
      switch (msgType) {
        case 'clear_output':
          // msg spec v4 had stdout, stderr, display keys
          // v4.1 replaced these with just wait
          // The default behavior is the same (stdout=stderr=display=True, wait=False),
          // so v4 messages will still be properly handled,
          // except for the rarely used clearing less than all output.
          console.log('Not handling clear message!');
          this.clear_output(msg.content.wait || false);
          return;
        case 'stream':
          json.text = content.text;
          json.name = content.name;
          break;
        case 'display_data':
          json.data = content.data;
          json.metadata = content.metadata;
          break;
        case 'execute_result':
          json.data = content.data;
          json.metadata = content.metadata;
          json.execution_count = content.execution_count;
          break;
        case 'error':
          json.ename = content.ename;
          json.evalue = content.evalue;
          json.traceback = content.traceback;
          break;
        case 'status':
        case 'execute_input':
          // Explicit ignore of status changes and input
          return false;
        default:
          console.log('unhandled output message', msg);
          return false;
      }
      // this.append_output(json);
      let bundle,
          el;
      bundle = {};
      switch (json.output_type) {
        case 'execute_result':
        case 'display_data':
          bundle = json.data;
          break;
        case 'stream':
          bundle = {'text/plain': json.text};
          // bundle = {
          //   'jupyter/stream': json
          // };
          break;
        case 'error':
          bundle = {
            'jupyter/traceback': json
          };
          break;
        default:
          console.warn('Unrecognized output type: ' + json.output_type);
          bundle = {
            'text/plain': 'Unrecognized output type' + JSON.stringify(json)
          };
      }
      return bundle;
    }

    save() {
      this.saveAs(this.getPath());
    }

    saveAs(uri) {
      let nbData = this.asJSON()
      try {
        fs.writeFileSync(uri, nbData);
        this.modified = false;
      } catch(e) {
        console.error(e.stack);
        debugger;
      }
      this.emitter.emit('did-change-modified');
    }

    asJSON() {
      return JSON.stringify(this.state.toJSON(), null, 4);
    }

    shouldPromptToSave() {
      return this.isModified();
    }

    getSaveDialogOptions() {
      return {};
    }

    modified = false;
    // modifiedCallbacks = [];

    isModified() {
      return this.modified;
    }

    setModified(modified) {
      console.log('setting modified');
      this.modified = modified;
      this.emitter.emit('did-change-modified');
    }

    onDidChangeModified(callback) {
      return this.emitter.on('did-change-modified', callback);
    }

    //----------------------------------------
    // Listeners, currently never called
    //----------------------------------------

    onDidChange(callback) {
      return this.emitter.on('did-change', callback);
    }

    onDidChangeTitle(callback) {
      return this.emitter.on('did-change-title', callback);
    }

    //----------------------------------------
    // Various info-fetching methods
    //----------------------------------------

    getTitle() {
      let filePath = this.getPath();
      if (filePath !== undefined && filePath !== null) {
        return path.basename(filePath);
      } else {
        return 'untitled';
      }
    }

    getURI() {
      console.log('getURI called');
      return this.getPath();
    }

    getPath() {
      console.log('getPath called');
      return this.file.getPath();
    }

    isEqual(other) {
      return (other instanceof ImageEditor && this.getURI() == other.getURI());
    }

    destroy() {
      console.log('destroy called');
      this.subscriptions.dispose();
      try {
        this.session.shutdown();
      } catch(e) {}
    }

    //----------------------------------------
    // Serialization (one of these days...)
    //----------------------------------------

    // static deserialize({filePath}) {
    //     if (fs.isFileSync(filePath)) {
    //         new NotebookEditor(filePath);
    //     } else {
    //         console.warn(`Could not deserialize notebook editor for path \
    //                      '${filePath}' because that file no longer exists.`);
    //     }
    // }

    // serialize() {
    //     return {
    //         filePath: this.getPath(),
    //         deserializer: this.constructor.name
    //     }
    // }

}

// atom.deserializers.add(NotebookEditor);
<|endoftext|>


<commit_before>var expect = require("chai").expect;
var fs = require('fs');
var StepDefinitionGenerator = require("../lib/StepDefinitionGenerator.js");
var generator;
describe('StepDefinitionGenerator', function() {
    before(function() {
        generator = new StepDefinitionGenerator();
        if (!fs.existsSync('StepDefinitionGenerator.feature')) {
            fs.writeSync('StepDefinitionGenerator.feature', '');
        }
    });

    after(function() {
        //fs.unlinkSync('StepDefinitionGenerator.feature');
    });

    describe('matchStep()', function() {
        it('should match a valid GWT step', function() {
            var data = generator.matchStep('Given a test');
            expect(data).to.eql({ type: 'given', keyword: 'Given', step: 'a test', raw: 'Given a test'});
        });
        it('should return null an invalid GWT step', function() {
            var data = generator.matchStep('This format is not supported');
            expect(data).to.be.null;
        });
    });

    describe('matchSteps()', function() {

    });

    describe('getFeatureSteps()', function() {

    });

    describe('generate()', function() {

    });

    describe('generateFromFile()', function() {

    });

    describe('getMatchingStepFileName()', function() {

    });

    describe('prompt()', function() {

    });
});<commit_msg>Fixed test bug<commit_after>var expect = require("chai").expect;
var fs = require('fs');
var StepDefinitionGenerator = require("../lib/StepDefinitionGenerator.js");
var generator;
describe('StepDefinitionGenerator', function() {
    before(function() {
        generator = new StepDefinitionGenerator();
        if (!fs.existsSync('StepDefinitionGenerator.feature')) {
            fs.writeFileSync('StepDefinitionGenerator.feature', '');
        }
    });

    after(function() {
        fs.unlinkSync('StepDefinitionGenerator.feature');
    });

    describe('matchStep()', function() {
        it('should match a valid GWT step', function() {
            var data = generator.matchStep('Given a test');
            expect(data).to.eql({ type: 'given', keyword: 'Given', step: 'a test', raw: 'Given a test'});
        });
        it('should return null an invalid GWT step', function() {
            var data = generator.matchStep('This format is not supported');
            expect(data).to.be.null;
        });
    });

    describe('matchSteps()', function() {

    });

    describe('getFeatureSteps()', function() {

    });

    describe('generate()', function() {

    });

    describe('generateFromFile()', function() {

    });

    describe('getMatchingStepFileName()', function() {

    });

    describe('prompt()', function() {

    });
});<|endoftext|>


<commit_before>'use strict';

var debug = require('debug');
var View = require('vinyl-view');
var utils = require('../utils');

/**
 * Plugin for adding data and context related methods to
 * an instance of `app` or `collection`.
 */

module.exports = function(app) {
  var opts = utils.extend({namespace: true}, app.options);

  /**
   * Custom debug methods
   */

  debug.context = debug('base:templates:context');
  debug.helper = function(key) {
    return debug('base:templates:helper:', key);
  };

  /**
   * Set, get and load data to be passed to templates as
   * context at render-time.
   *
   * ```js
   * app.data('a', 'b');
   * app.data({c: 'd'});
   * console.log(app.cache.data);
   * //=> {a: 'b', c: 'd'}
   * ```
   *
   * @name .data
   * @param {String|Object} `key` Pass a key-value pair or an object to set.
   * @param {any} `val` Any value when a key-value pair is passed. This can also be options if a glob pattern is passed as the first value.
   * @return {Object} Returns an instance of `Templates` for chaining.
   * @api public
   */

  app.use(utils.baseData(opts));

  /**
   * Register a default data loader
   */

  app.dataLoader('json', function(str) {
    return JSON.parse(str);
  });

  /**
   * Build the context for the given `view` and `locals`.
   *
   * @name .context
   * @param  {Object} `view` The view being rendered
   * @param  {Object} `locals`
   * @return {Object} The object to be passed to engines/views as context.
   * @api public
   */

  app.define('context', function(view, locals) {
    // backwards support for `mergeContext`
    var fn = this.options.context || this.options.mergeContext;
    if (typeof fn === 'function') {
      return fn.apply(this, arguments);
    }
    if (typeof view.context !== 'function') {
      view.context = View.context;
    }
    return utils.merge({}, this.cache.data, view.context(locals));
  });

  /**
   * Bind context to helpers.
   */

  app.define('bindHelpers', function(view, context, isAsync) {
    debug.context('binding helpers for %s <%s>', view.options.inflection, view.basename);
    var optsHelpers = utils.isObject(this.options.helpers) ? this.options.helpers : {};

    // merge cached _synchronous_ helpers with helpers defined on `app.options`
    var helpers = utils.extend({}, optsHelpers, this._.helpers.sync);

    // add helpers defined on the context
    if (utils.isObject(context.helpers)) {
      helpers = utils.extend({}, helpers, context.helpers);
    }

    // if any _async_ helpers are defined, merge those onto the context LAST
    if (isAsync) {
      helpers = utils.extend({}, helpers, this._.helpers.async);
    }

    // create the "helper context" to be exposed as `this` inside helper functions
    var thisArg = new Context(this, view, context, helpers);

    // bind the context to helpers.
    context.helpers = utils.bindAll(helpers, thisArg, {
      bindFn: function(thisArg, key, parent, options) {
        thisArg.debug = debug.helper(key);
        setHelperOptions(thisArg, key);
        thisArg.helper.name = key;
        thisArg._parent = parent;
        thisArg = utils.extend({}, thisArg, parent);
        return thisArg;
      }
    });
  });

  /**
   * Update context in a helper so that `this.helper.options` is
   * the options for that specific helper.
   *
   * @param {Object} `context`
   * @param {String} `key`
   * @api public
   */

  function setHelperOptions(context, key) {
    var optsHelper = context.options.helper || {};
    if (optsHelper.hasOwnProperty(key)) {
      context.helper.options = optsHelper[key];
    }
  }

  /**
   * Create a new context object to expose inside helpers.
   *
   * ```js
   * app.helper('lowercase', function(str) {
   *   // the 'this' object is the _helper_ context
   *   console.log(this);
   *   // 'this.app' => the application instance, e.g. templates, assemble, verb etc.
   *   // 'this.view' => the current view being rendered
   *   // 'this.helper' => helper name and options
   *   // 'this.context' => view context (as opposed to _helper_ context)
   *   // 'this.options' => options created for the specified helper being called
   * });
   * ```
   * @param {Object} `app` The application instance
   * @param {Object} `view` The view being rendered
   * @param {Object} `context` The view's context
   * @param {Object} `options`
   */

  function Context(app, view, context, helpers) {
    this.helper = {};
    this.helper.options = createHelperOptions(app, view, helpers);

    this.options = utils.merge({}, app.options, view.options, this.helper.options);
    this.context = context;

    utils.define(this.options, 'handled', this.options.handled);
    utils.define(this.context, 'view', view);

    decorate(this.context);
    decorate(this.options);
    decorate(this);

    this.view = view;
    this.app = app;
    this.ctx = function() {
      return helperContext.apply(this, arguments);
    };
  }

  /**
   * Expose `this.ctx()` in helpers for creating a custom context object
   * from a `view` being rendered, `locals` and helper `options` (which
   * can optionally have a handlebars `options.hash` property)
   *
   * The context object is created from:
   *
   * - `this.context`: the context for the **current view being rendered**
   * - `this.view.locals`: the `locals` object for current view being rendered
   * - `this.view.data`: front-matter from current view being rendered
   * - `view.locals`: locals defined on the view being injected
   * - `view.data`: front-matter on the view being injected
   * - helper `locals`: locals passed to the helper
   * - helper `options`: options passed to the helper
   * - helper `options.hash` if helper is registered as a handlebars helper
   *
   * Also note that `view` is the view being injected, whereas `this.view`
   * is the `view` being rendered.
   *
   * ```js
   * // handlebars helper
   * app.helper('foo', function(name, locals, options) {
   *   var view = this.app.find(name);
   *   var ctx = this.ctx(view, locals, options);
   *   return options.fn(ctx);
   * });
   *
   * // async helper
   * app.helper('foo', function(name, locals, options, cb) {
   *   var view = this.app.find(name);
   *   var ctx = this.ctx(view, locals, options);
   *   view.render(ctx, function(err, res) {
   *     if (err) return cb(err);
   *     cb(null, res.content);
   *   });
   * });
   * ```
   * @name .this.ctx
   * @param {Object} `view` the view being injected
   * @param {Object} `locals` Helper locals
   * @param {Object} `options` Helper options
   * @return {Object}
   * @public
   */

  function helperContext(view, locals, options) {
    var fn = this.options.helperContext;
    var merge = utils.merge;
    var context = {};

    if (typeof fn === 'function') {
      context = fn.call(this, view, locals, options);
    } else {
      // merge "view" front-matter with context
      context = merge({}, this.context, this.view.data);
      // merge in partial locals and front-matter
      context = merge({}, context, view.locals, view.data);
      // merge in helper locals and options.hash
      context = merge({}, context, locals, options.hash);
    }
    return context;
  }

  /**
   * Decorate the given object with `merge`, `set` and `get` methods
   */

  function decorate(obj) {
    utils.define(obj, 'merge', function() {
      var args = [].concat.apply([], [].slice.call(arguments));
      var len = args.length;
      var idx = -1;

      while (++idx < len) {
        var val = args[idx];
        if (!utils.isObject(val)) continue;
        if (val.hasOwnProperty('hash')) {
          // shallow clone and delete the `data` object
          val = utils.merge({}, val, val.hash);
          delete val.data;
        }
        utils.merge(obj, val);
      }
      // ensure methods aren't overwritten
      decorate(obj);
      if (obj.hasOwnProperty('app') && obj.hasOwnProperty('options')) {
        decorate(obj.options);
      }
      return obj;
    });

    utils.define(obj, 'get', function(prop) {
      return utils.get(obj, prop);
    });

    utils.define(obj, 'set', function(key, val) {
      return utils.set(obj, key, val);
    });
  }

  /**
   * Support helper options defined on `app.options.helper`. For example,
   * to define options for helper `foo`:
   *
   * ```js
   * app.option('helper.foo', {doStuff: true});
   * ```
   * @param {Object} `app`
   * @param {Object} `helpers` Currently defined helpers, to match up options
   * @return {Object} Returns helper options
   */

  function createHelperOptions(app, view, helpers) {
    var options = utils.merge({}, app.options, view.options);
    var helperOptions = {};

    if (options.hasOwnProperty('helper')) {
      var opts = options.helper;
      if (!utils.isObject(opts)) {
        return helperOptions;
      }

      for (var key in opts) {
        if (opts.hasOwnProperty(key) && helpers.hasOwnProperty(key)) {
          helperOptions[key] = opts[key];
        }
      }
    }
    return helperOptions;
  }

  /**
   * Merge "partials" view types. This is necessary for template
   * engines have no support for partials or only support one
   * type of partials.
   *
   * @name .mergePartials
   * @param {Object} `options` Optionally pass an array of `viewTypes` to include on `options.viewTypes`
   * @return {Object} Merged partials
   * @api public
   */

  function mergePartials(options) {
    var opts = utils.merge({}, this.options, options);
    var names = opts.mergeTypes || this.viewTypes.partial;
    var partials = {};
    var self = this;

    names.forEach(function(name) {
      var collection = self.views[name];
      for (var key in collection) {
        var view = collection[key];

        // handle `onMerge` middleware
        self.handleOnce('onMerge', view, function(err, res) {
          if (err) throw err;
          view = res;
        });

        if (view.options.nomerge) continue;
        if (opts.mergePartials !== false) {
          name = 'partials';
        }

        // convert the partial to:
        //=> {'foo.hbs': 'some content...'};
        partials[name] = partials[name] || {};
        partials[name][key] = view.content;
      }
    });
    return partials;
  };

  /**
   * Merge "partials" view types. This is necessary for template engines
   * have no support for partials or only support one type of partials.
   *
   * @name .mergePartialsAsync
   * @param {Object} `options` Optionally pass an array of `viewTypes` to include on `options.viewTypes`
   * @param {Function} `callback` Function that exposes `err` and `partials` parameters
   * @api public
   */

  mergePartials.async = function(options, done) {
    if (typeof options === 'function') {
      done = options;
      options = {};
    }

    var opts = utils.merge({}, this.options, options);
    var names = opts.mergeTypes || this.viewTypes.partial;
    var partials = {};
    var self = this;

    utils.each(names, function(name, cb) {
      var collection = self.views[name];
      var keys = Object.keys(collection);

      utils.each(keys, function(key, next) {
        var view = collection[key];

        // handle `onMerge` middleware
        self.handleOnce('onMerge', view, function(err, file) {
          if (err) return next(err);

          if (file.options.nomerge) {
            return next();
          }

          if (opts.mergePartials !== false) {
            name = 'partials';
          }

          // convert the partial to:
          //=> {'foo.hbs': 'some content...'};
          partials[name] = partials[name] || {};
          partials[name][key] = file.content;
          next();
        });
      }, cb);
    }, function(err) {
      if (err) return done(err);
      done(null, partials);
    });
  };

  /**
   * Expose `mergePartials` functions as methods
   */

  app.define('mergePartials', mergePartials);
  app.define('mergePartialsAsync', mergePartials.async);
};
<commit_msg>improve helper context merging<commit_after>'use strict';

var debug = require('debug');
var View = require('vinyl-view');
var utils = require('../utils');

/**
 * Plugin for adding data and context related methods to
 * an instance of `app` or `collection`.
 */

module.exports = function(app) {
  var opts = utils.extend({namespace: true}, app.options);

  /**
   * Custom debug methods
   */

  debug.context = debug('base:templates:context');
  debug.helper = function(key) {
    return debug('base:templates:helper:', key);
  };

  /**
   * Set, get and load data to be passed to templates as
   * context at render-time.
   *
   * ```js
   * app.data('a', 'b');
   * app.data({c: 'd'});
   * console.log(app.cache.data);
   * //=> {a: 'b', c: 'd'}
   * ```
   *
   * @name .data
   * @param {String|Object} `key` Pass a key-value pair or an object to set.
   * @param {any} `val` Any value when a key-value pair is passed. This can also be options if a glob pattern is passed as the first value.
   * @return {Object} Returns an instance of `Templates` for chaining.
   * @api public
   */

  app.use(utils.baseData(opts));

  /**
   * Register a default data loader
   */

  app.dataLoader('json', function(str) {
    return JSON.parse(str);
  });

  /**
   * Build the context for the given `view` and `locals`.
   *
   * @name .context
   * @param  {Object} `view` The view being rendered
   * @param  {Object} `locals`
   * @return {Object} The object to be passed to engines/views as context.
   * @api public
   */

  app.define('context', function(view, locals) {
    // backwards support for `mergeContext`
    var fn = this.options.context || this.options.mergeContext;
    if (typeof fn === 'function') {
      return fn.apply(this, arguments);
    }
    if (typeof view.context !== 'function') {
      view.context = View.context;
    }
    return utils.merge({}, this.cache.data, view.context(locals));
  });

  /**
   * Bind context to helpers.
   */

  app.define('bindHelpers', function(view, context, isAsync) {
    debug.context('binding helpers for %s <%s>', view.options.inflection, view.basename);
    var optsHelpers = utils.isObject(this.options.helpers) ? this.options.helpers : {};

    // merge cached _synchronous_ helpers with helpers defined on `app.options`
    var helpers = utils.extend({}, optsHelpers, this._.helpers.sync);

    // add helpers defined on the context
    if (utils.isObject(context.helpers)) {
      helpers = utils.extend({}, helpers, context.helpers);
    }

    // if any _async_ helpers are defined, merge those onto the context LAST
    if (isAsync) {
      helpers = utils.extend({}, helpers, this._.helpers.async);
    }

    // create the "helper context" to be exposed as `this` inside helper functions
    var thisArg = new Context(this, view, context, helpers);

    // bind the context to helpers.
    context.helpers = utils.bindAll(helpers, thisArg, {
      bindFn: function(thisArg, key, parent, options) {
        thisArg.debug = debug.helper(key);
        thisArg.helper.name = key;
        setHelperOptions(thisArg, key);
        thisArg._parent = parent;
        thisArg = utils.merge({}, thisArg, parent);
        return thisArg;
      }
    });
  });

  /**
   * Update context in a helper so that `this.helper.options` is
   * the options for that specific helper.
   *
   * @param {Object} `context`
   * @param {String} `key`
   * @api public
   */

  function setHelperOptions(context, key) {
    var optsHelper = context.options.helper || {};
    if (optsHelper.hasOwnProperty(key)) {
      context.helper.options = optsHelper[key];
    }
  }

  /**
   * Create a new context object to expose inside helpers.
   *
   * ```js
   * app.helper('lowercase', function(str) {
   *   // the 'this' object is the _helper_ context
   *   console.log(this);
   *   // 'this.app' => the application instance, e.g. templates, assemble, verb etc.
   *   // 'this.view' => the current view being rendered
   *   // 'this.helper' => helper name and options
   *   // 'this.context' => view context (as opposed to _helper_ context)
   *   // 'this.options' => options created for the specified helper being called
   * });
   * ```
   * @param {Object} `app` The application instance
   * @param {Object} `view` The view being rendered
   * @param {Object} `context` The view's context
   * @param {Object} `options`
   */

  function Context(app, view, context, helpers) {
    this.helper = {};
    this.helper.options = createHelperOptions(app, view, helpers);

    this.context = context;
    this.options = utils.merge({}, app.options, view.options, this.helper.options);

    // make `this.options.handled` non-enumberable
    utils.define(this.options, 'handled', this.options.handled);

    decorate(this.context);
    decorate(this.options);
    decorate(this);

    this.view = view;
    this.app = app;
    this.ctx = function() {
      return helperContext.apply(this, arguments);
    };
  }

  /**
   * Expose `this.ctx()` in helpers for creating a custom context object
   * from a `view` being rendered, `locals` and helper `options` (which
   * can optionally have a handlebars `options.hash` property)
   *
   * The context object is created from:
   *
   * - `this.context`: the context for the **current view being rendered**
   * - `this.view.locals`: the `locals` object for current view being rendered
   * - `this.view.data`: front-matter from current view being rendered
   * - `view.locals`: locals defined on the view being injected
   * - `view.data`: front-matter on the view being injected
   * - helper `locals`: locals passed to the helper
   * - helper `options`: options passed to the helper
   * - helper `options.hash` if helper is registered as a handlebars helper
   *
   * Also note that `view` is the view being injected, whereas `this.view`
   * is the `view` being rendered.
   *
   * ```js
   * // handlebars helper
   * app.helper('foo', function(name, locals, options) {
   *   var view = this.app.find(name);
   *   var ctx = this.ctx(view, locals, options);
   *   return options.fn(ctx);
   * });
   *
   * // async helper
   * app.helper('foo', function(name, locals, options, cb) {
   *   var view = this.app.find(name);
   *   var ctx = this.ctx(view, locals, options);
   *   view.render(ctx, function(err, res) {
   *     if (err) return cb(err);
   *     cb(null, res.content);
   *   });
   * });
   * ```
   * @name .this.ctx
   * @param {Object} `view` the view being injected
   * @param {Object} `locals` Helper locals
   * @param {Object} `options` Helper options
   * @return {Object}
   * @public
   */

  function helperContext(view, locals, options) {
    var fn = this.options.helperContext;
    var context = {};

    if (!utils.isObject(view) || !view.isView) {
      locals = view;
      options = locals;
      view = this.view;
    }

    options = options || {};
    locals = locals || {};
    if (typeof fn === 'function') {
      return fn.call(this, view, locals, options);
    }

    // merge "view" front-matter with context
    context = utils.merge({}, context, this.view.locals, this.view.data);

    // merge in partial locals and front-matter
    if (view !== this.view) {
      context = utils.merge({}, context, view.locals, view.data);
    }

    // merge in helper locals and options.hash
    context = utils.merge({}, context, locals, options.hash);
    return context;
  }

  /**
   * Decorate the given object with `merge`, `set` and `get` methods
   */

  function decorate(obj) {
    utils.define(obj, 'merge', function() {
      var args = [].concat.apply([], [].slice.call(arguments));
      var len = args.length;
      var idx = -1;

      while (++idx < len) {
        var val = args[idx];
        if (!utils.isObject(val)) continue;
        if (val.hasOwnProperty('hash')) {
          // shallow clone and delete the `data` object
          val = utils.merge({}, val, val.hash);
          delete val.data;
        }
        utils.merge(obj, val);
      }
      // ensure methods aren't overwritten
      decorate(obj);
      if (obj.hasOwnProperty('app') && obj.hasOwnProperty('options')) {
        decorate(obj.options);
      }
      return obj;
    });

    utils.define(obj, 'get', function(prop) {
      return utils.get(obj, prop);
    });

    utils.define(obj, 'set', function(key, val) {
      return utils.set(obj, key, val);
    });
  }

  /**
   * Support helper options defined on `app.options.helper`. For example,
   * to define options for helper `foo`:
   *
   * ```js
   * app.option('helper.foo', {doStuff: true});
   * ```
   * @param {Object} `app`
   * @param {Object} `helpers` Currently defined helpers, to match up options
   * @return {Object} Returns helper options
   */

  function createHelperOptions(app, view, helpers) {
    var options = utils.merge({}, app.options, view.options);
    var helperOptions = {};

    if (options.hasOwnProperty('helper')) {
      var opts = options.helper;
      if (!utils.isObject(opts)) {
        return helperOptions;
      }

      for (var key in opts) {
        if (opts.hasOwnProperty(key) && helpers.hasOwnProperty(key)) {
          helperOptions[key] = opts[key];
        }
      }
    }
    return helperOptions;
  }

  /**
   * Merge "partials" view types. This is necessary for template
   * engines have no support for partials or only support one
   * type of partials.
   *
   * @name .mergePartials
   * @param {Object} `options` Optionally pass an array of `viewTypes` to include on `options.viewTypes`
   * @return {Object} Merged partials
   * @api public
   */

  function mergePartials(options) {
    var opts = utils.merge({}, this.options, options);
    var names = opts.mergeTypes || this.viewTypes.partial;
    var partials = {};
    var self = this;

    names.forEach(function(name) {
      var collection = self.views[name];
      for (var key in collection) {
        var view = collection[key];

        // handle `onMerge` middleware
        self.handleOnce('onMerge', view, function(err, res) {
          if (err) throw err;
          view = res;
        });

        if (view.options.nomerge) continue;
        if (opts.mergePartials !== false) {
          name = 'partials';
        }

        // convert the partial to:
        //=> {'foo.hbs': 'some content...'};
        partials[name] = partials[name] || {};
        partials[name][key] = view.content;
      }
    });
    return partials;
  };

  /**
   * Merge "partials" view types. This is necessary for template engines
   * have no support for partials or only support one type of partials.
   *
   * @name .mergePartialsAsync
   * @param {Object} `options` Optionally pass an array of `viewTypes` to include on `options.viewTypes`
   * @param {Function} `callback` Function that exposes `err` and `partials` parameters
   * @api public
   */

  mergePartials.async = function(options, done) {
    if (typeof options === 'function') {
      done = options;
      options = {};
    }

    var opts = utils.merge({}, this.options, options);
    var names = opts.mergeTypes || this.viewTypes.partial;
    var partials = {};
    var self = this;

    utils.each(names, function(name, cb) {
      var collection = self.views[name];
      var keys = Object.keys(collection);

      utils.each(keys, function(key, next) {
        var view = collection[key];

        // handle `onMerge` middleware
        self.handleOnce('onMerge', view, function(err, file) {
          if (err) return next(err);

          if (file.options.nomerge) {
            return next();
          }

          if (opts.mergePartials !== false) {
            name = 'partials';
          }

          // convert the partial to:
          //=> {'foo.hbs': 'some content...'};
          partials[name] = partials[name] || {};
          partials[name][key] = file.content;
          next();
        });
      }, cb);
    }, function(err) {
      if (err) return done(err);
      done(null, partials);
    });
  };

  /**
   * Expose `mergePartials` functions as methods
   */

  app.define('mergePartials', mergePartials);
  app.define('mergePartialsAsync', mergePartials.async);
};
<|endoftext|>


<commit_before>/*
 * windows.js: Pluging to add appropriate hooks to build on Windows.
 *
 * (C) 2012 Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    spawn = require('child_process').spawn,
    async = require('async');

//
// Name this plugin
//
exports.name = 'windows';

//
// ### function attach ()
// Attaches this plugin by adding a before hook for
// 'npm.configure' for every build performed.
//
exports.attach = function () {
  if (process.platform !== 'win32') {
    //
    // WINDOWS ONLY! Prepare thyself...
    //
    return;
  }

  var app = this;

  //
  // ### function spawnBatch(script, args, next)
  // Spawns the specified batch `script` with the
  // `args` provided.
  //
  function spawnBatch(script, args, next) {
    if (!next && typeof args === 'function') {
      next = args;
      args = [];
    }

    var child = spawn(script, args, { env: process.env });
    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);
    child.on('exit', function () {
      //
      // Remark: These shouldn't error on a properly
      // configured Windows machine.
      //
      next();
    });
  }

  app.before('build.create', function (bot, done) {
    bot.before('npm.configure', function (description, next) {
      //
      // Execute the batch setup scripts for:
      // 1. Set any expanded PATH.
      // 2. Mixing extra ENV vars from Microsoft Windows SDK.
      // 3. Target Node.js version.
      //
      var cpu = description.cpu || 'x86';

      async.series([
        function setxPath(next) {
          return description.expand && description.expand.Path
            ? spawnBatch('setx', ['PATH', description.expand.Path.slice(1)], next)
            : next();
        },
        function extraEnv(next) {
          var expandPath = app.config.get(['arch', cpu, 'expand', 'Path'].join(':')),
              arch       = app.config.get(['arch', cpu, 'env'].join(':')),
              platform   = app.config.get('platform:windows:env');

          [arch, platform].forEach(function (obj) {
            if (obj && typeof obj === 'object') {
              Object.keys(obj).forEach(function (key) {
                description.env[key] = obj[key];
              });
            }
          });

          if (expandPath) {
            description.env.Path += expandPath;
          }

          next();
        },
        async.apply(spawnBatch, path.join(description.directories.node, 'nodevars.bat'))
      ], function (err) {
        return !err
          ? next(null, description)
          : next(err);
      });
    });

    done(null, bot);
  });
};

//
// ### function init (callback)
// #### @callback {function} Continuation to respond to.
// Initializes this plugin by read the specified directory
// containing all known node.js installations (from MSI).
//
exports.init = function (callback) {
  if (process.platform !== 'win32') {
    //
    // WINDOWS ONLY! Prepare thyself...
    //
    return callback();
  }

  var versionsDir  = this.config.get('directories:node'),
      expandPath   = this.config.get('defaults:expand:Path'),
      nodeVersions = {};

  //
  // ### function normalizeScripts(version, dir, next)
  // Node.js has a quirk on Windows that:
  //   - C:\node\v0.8.*\x86\nodejsvars.bat
  //   - C:\node\v0.10.*\x86\nodevars.bat
  //
  function normalizeScripts(version, dir, next) {
    if (/0\.8/.test(version)) {
      return fs.rename(
        path.join(dir, 'nodejsvars.bat'),
        path.join(dir, 'nodevars.bat'),
        next
      );
    }

    next();
  }

  //
  // Expand process.env.PATH if supplied.
  //
  if (expandPath) {
    process.env.Path += expandPath;
  }

  async.waterfall([
    //
    // 1. Read all of the node versions (e.g. `C:\node`)
    //
    async.apply(fs.readdir, versionsDir),
    //
    // 2. Read all of the architectures for all versions.
    //    (e.g. `C:\node\v0.8.25\x86`).
    //
    function readVersionsArch(dirs, done) {
      async.forEachLimit(dirs, 5, function readArch(version, next) {
        var dir = path.join(versionsDir, version);

        fs.readdir(dir, function (err, archs) {
          if (err) { return next(err) }
          nodeVersions[version] = archs;

          async.forEach(archs.map(function (arch) {
            return path.join(dir, arch);
          }), normalizeScripts.bind(null, version), next)
        });
      }, done);
    }
  ], callback);
};<commit_msg>[fix] Handle all `PATH`, `Path`, or `path` edgecases in Windows env vars.<commit_after>/*
 * windows.js: Pluging to add appropriate hooks to build on Windows.
 *
 * (C) 2012 Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    spawn = require('child_process').spawn,
    async = require('async');

//
// Name this plugin
//
exports.name = 'windows';

//
// ### function attach ()
// Attaches this plugin by adding a before hook for
// 'npm.configure' for every build performed.
//
exports.attach = function () {
  if (process.platform !== 'win32') {
    //
    // WINDOWS ONLY! Prepare thyself...
    //
    return;
  }

  var app = this;

  //
  // ### function spawnBatch(script, args, next)
  // Spawns the specified batch `script` with the
  // `args` provided.
  //
  function spawnBatch(script, args, next) {
    if (!next && typeof args === 'function') {
      next = args;
      args = [];
    }

    var child = spawn(script, args, { env: process.env });
    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);
    child.on('exit', function () {
      //
      // Remark: These shouldn't error on a properly
      // configured Windows machine.
      //
      next();
    });
  }

  app.before('build.create', function (bot, done) {
    bot.before('npm.configure', function (description, next) {
      //
      // Execute the batch setup scripts for:
      // 1. Set any expanded PATH.
      // 2. Mixing extra ENV vars from Microsoft Windows SDK.
      // 3. Target Node.js version.
      //
      var cpu = description.cpu || 'x86';

      async.series([
        function setxPath(next) {
          return description.expand && description.expand.Path
            ? spawnBatch('setx', ['PATH', description.expand.Path.slice(1)], next)
            : next();
        },
        function extraEnv(next) {
          var expandPath = app.config.get(['arch', cpu, 'expand', 'Path'].join(':')),
              arch       = app.config.get(['arch', cpu, 'env'].join(':')),
              platform   = app.config.get('platform:windows:env');

          [arch, platform].forEach(function (obj) {
            if (obj && typeof obj === 'object') {
              Object.keys(obj).forEach(function (key) {
                description.env[key] = obj[key];
              });
            }
          });

          if (expandPath) {
            description.env.Path = description.env.Path
              || description.env.PATH
              || description.env.path
              || '';

            delete description.env.PATH;
            delete description.env.path;
            description.env.Path += expandPath;
          }

          next();
        },
        async.apply(spawnBatch, path.join(description.directories.node, 'nodevars.bat'))
      ], function (err) {
        return !err
          ? next(null, description)
          : next(err);
      });
    });

    done(null, bot);
  });
};

//
// ### function init (callback)
// #### @callback {function} Continuation to respond to.
// Initializes this plugin by read the specified directory
// containing all known node.js installations (from MSI).
//
exports.init = function (callback) {
  if (process.platform !== 'win32') {
    //
    // WINDOWS ONLY! Prepare thyself...
    //
    return callback();
  }

  var versionsDir  = this.config.get('directories:node'),
      expandPath   = this.config.get('defaults:expand:Path'),
      nodeVersions = {};

  //
  // ### function normalizeScripts(version, dir, next)
  // Node.js has a quirk on Windows that:
  //   - C:\node\v0.8.*\x86\nodejsvars.bat
  //   - C:\node\v0.10.*\x86\nodevars.bat
  //
  function normalizeScripts(version, dir, next) {
    if (/0\.8/.test(version)) {
      return fs.rename(
        path.join(dir, 'nodejsvars.bat'),
        path.join(dir, 'nodevars.bat'),
        next
      );
    }

    next();
  }

  //
  // Expand process.env.PATH if supplied.
  //
  if (expandPath) {
    process.env.Path += expandPath;
  }

  async.waterfall([
    //
    // 1. Read all of the node versions (e.g. `C:\node`)
    //
    async.apply(fs.readdir, versionsDir),
    //
    // 2. Read all of the architectures for all versions.
    //    (e.g. `C:\node\v0.8.25\x86`).
    //
    function readVersionsArch(dirs, done) {
      async.forEachLimit(dirs, 5, function readArch(version, next) {
        var dir = path.join(versionsDir, version);

        fs.readdir(dir, function (err, archs) {
          if (err) { return next(err) }
          nodeVersions[version] = archs;

          async.forEach(archs.map(function (arch) {
            return path.join(dir, arch);
          }), normalizeScripts.bind(null, version), next)
        });
      }, done);
    }
  ], callback);
};<|endoftext|>


<commit_before>'use strict';
var ko = require('knockout');
var $ = require('jquery');
var $osf = require('osfHelpers');

var mathrender = require('mathrender');
var md = require('markdown').full;
var mdQuick = require('markdown').quick;
var diffTool = require('diffTool');

var THROTTLE = 500;

//

ko.bindingHandlers.mathjaxify = {
    update: function(element, valueAccessor, allBindingsAccessor, data, context) {
        var vm = context.$data;
        //Need to unwrap the data in order for KO to know it's changed.
        ko.unwrap(valueAccessor());

        if(vm.allowMathjaxification() && vm.allowFullRender()) {
            mathrender.mathjaxify('#' + element.id);
        }
    }
};

function ViewWidget(visible, version, viewText, rendered, contentURL, allowMathjaxification, allowFullRender, editor) {
    var self = this;
    self.version = version;
    self.viewText = viewText; // comes from EditWidget.viewText
    self.rendered = rendered;
    self.visible = visible;
    self.allowMathjaxification = allowMathjaxification;
    self.editor = editor;
    self.allowFullRender = allowFullRender;
    self.renderTimeout = null;
    self.displaySource = ko.observable('');
    self.debouncedAllowFullRender = $osf.debounce(function() {
        self.allowFullRender(true);
    }, THROTTLE);

    self.renderMarkdown = function(rawContent){
        if(self.visible()) {
            if (self.allowFullRender()) {
                return md.render(rawContent);
            } else {
                return mdQuick.render(rawContent);
            }
        } else {
            return '';
        }
    };

    if (typeof self.editor !== 'undefined') {
        self.editor.on('change', function () {
            // Quick render
            self.allowFullRender(false);
            // Full render
            self.debouncedAllowFullRender();
        });
    } else {
        self.allowFullRender(true);
    }

    self.displayText =  ko.computed(function() {
        self.allowFullRender();
        var requestURL;
        if (typeof self.version() !== 'undefined') {
            if (self.version() === 'preview') {
                self.rendered(self.renderMarkdown(self.viewText()));
                self.displaySource(self.viewText());
            } else {
                if (self.version() === 'current') {
                    requestURL = contentURL;
                } else {
                    requestURL= contentURL + self.version();
                }
                var request = $.ajax({
                    url: requestURL
                });

                request.done(function (resp) {
                    var rawContent = resp.wiki_content || '*No wiki content*';
                    if (resp.wiki_rendered) {
                        // Use pre-rendered python, if provided. Don't mathjaxify
                        self.allowMathjaxification(false);
                        if(self.visible()) {
                            self.rendered(resp.wiki_rendered);
                        }
                    } else {
                        // Render raw markdown
                        if(self.visible()) {
                            self.allowMathjaxification(true);
                            self.rendered(self.renderMarkdown(rawContent));
                        }
                    }
                    self.displaySource(rawContent);
                });
            }
        } else {
            self.displaySource('');
        }
    });
}

    // currentText comes from ViewWidget.displayText
function CompareWidget(visible, compareVersion, currentText, rendered, contentURL) {
    var self = this;

    self.compareVersion = compareVersion;
    self.currentText = currentText;
    self.rendered = rendered;
    self.visible = visible;
    self.contentURL = contentURL;
    self.compareSource = ko.observable('');

    self.compareText = ko.computed(function() {
        var requestURL;
        if (self.compareVersion() === 'current') {
            requestURL = self.contentURL;
        } else {
            requestURL= self.contentURL + self.compareVersion();
        }
        var request = $.ajax({
            url: requestURL
        });
        request.done(function (resp) {
            var rawText = resp.wiki_content;
            self.compareSource(rawText);
        });

    });

    self.compareOutput = ko.computed(function() {
        var output = diffTool.diff(self.compareSource(), self.currentText());
        self.rendered(output);
        return output;
    }).extend({ notify: 'always' });

}

var defaultOptions = {
    editVisible: false,
    viewVisible: true,
    compareVisible: false,
    menuVisible: true,
    canEdit: true,
    viewVersion: 'current',
    compareVersion: 'previous',
    urls: {
        content: '',
        draft: '',
        page: ''
    },
    metadata: {}
};

function ViewModel(options){
    var self = this;

    // enabled?
    self.editVis = ko.observable(options.editVisible);
    self.viewVis = ko.observable(options.viewVisible);
    self.compareVis = ko.observable(options.compareVisible);
    self.menuVis = ko.observable(options.menuVisible);

    self.compareVersion = ko.observable(options.compareVersion);
    self.viewVersion = ko.observable(options.viewVersion);
    self.draftURL = options.urls.draft;
    self.contentURL = options.urls.content;
    self.pageURL = options.urls.page;
    self.editorMetadata = options.metadata;
    self.canEdit = options.canEdit;

    self.viewText = ko.observable('');
    self.renderedView = ko.observable('');
    self.renderedCompare = ko.observable('');
    self.allowMathjaxification = ko.observable(true);
    self.allowFullRender = ko.observable(true);
    self.viewVersionDisplay = ko.computed(function() {
        var versionString = '';
        if (self.viewVersion() === 'preview') {
            versionString = 'Live preview';
        } else if (self.viewVersion() === 'current'){
            versionString = 'Current version';
        } else if (self.viewVersion() === 'previous'){
            versionString = 'Previous version';
        } else {
            versionString = 'Version ' + self.viewVersion();
        }
        return versionString;
    });

    self.currentURL = ko.computed(function() {
        var url = self.pageURL;

        // Default view is special cased
        if (!self.editVis() && self.viewVis() && self.viewVersion() === 'current' && !self.compareVis() && self.menuVis()) {
            window.history.replaceState({}, '', url);
            return;
        }

        var paramPrefix = '?';

        if (self.editVis()) {
            url += paramPrefix + 'edit';
            paramPrefix = '&';
        }
        if (self.viewVis()) {
            url += paramPrefix + 'view';
            paramPrefix = '&';
            if  ((!self.editVis() && self.viewVersion() !== 'current' ) ||
                 (self.editVis() && self.viewVersion() !== 'preview')) {
                url += '=' + self.viewVersion();
            }
        }
        if (self.compareVis()) {
            url += paramPrefix + 'compare';
            paramPrefix = '&';
            if (self.compareVersion() !== 'previous'){
                url += '=' + self.compareVersion();
            }
        }
        if (self.menuVis()) {
            url += paramPrefix + 'menu';
        }

        window.history.replaceState({}, '', url);
    });

    if(self.canEdit) {
        self.editor = ace.edit('editor'); // jshint ignore: line

        var ShareJSDoc = require('addons/wiki/static/ShareJSDoc.js');
        self.editVM = new ShareJSDoc(self.draftURL, self.editorMetadata, self.viewText, self.editor);
    }
    self.viewVM = new ViewWidget(self.viewVis, self.viewVersion, self.viewText, self.renderedView, self.contentURL, self.allowMathjaxification, self.allowFullRender, self.editor);
    self.compareVM = new CompareWidget(self.compareVis, self.compareVersion, self.viewVM.displaySource, self.renderedCompare, self.contentURL);

    var bodyElement = $('body');
    bodyElement.on('togglePanel', function (event, panel, display) {
        // Update self.editVis, self.viewVis, or self.compareVis in viewmodel
        self[panel + 'Vis'](display);

        //URL needs to be a computed observable, and this should just update the panel states, which will feed URL

        // Switch view to correct version
        if (panel === 'edit') {
            if (display) {
                self.viewVersion('preview');
            } else if (self.viewVersion() === 'preview') {
                self.viewVersion('current');
            }
        }
    });

    bodyElement.on('toggleMenu', function(event, menuVisible) {
        self.menuVis(menuVisible);
    });
}

var WikiPage = function(selector, options) {
    var self = this;
    self.options = $.extend({}, defaultOptions, options);

    this.viewModel = new ViewModel(self.options);
    $osf.applyBindings(self.viewModel, selector);
};

module.exports = WikiPage;

<commit_msg>Add better switching of view in various edit mode situations<commit_after>'use strict';
var ko = require('knockout');
var $ = require('jquery');
var $osf = require('osfHelpers');

var mathrender = require('mathrender');
var md = require('markdown').full;
var mdQuick = require('markdown').quick;
var diffTool = require('diffTool');

var THROTTLE = 500;

//

ko.bindingHandlers.mathjaxify = {
    update: function(element, valueAccessor, allBindingsAccessor, data, context) {
        var vm = context.$data;
        //Need to unwrap the data in order for KO to know it's changed.
        ko.unwrap(valueAccessor());

        if(vm.allowMathjaxification() && vm.allowFullRender()) {
            mathrender.mathjaxify('#' + element.id);
        }
    }
};

function ViewWidget(visible, version, viewText, rendered, contentURL, allowMathjaxification, allowFullRender, editor) {
    var self = this;
    self.version = version;
    self.viewText = viewText; // comes from EditWidget.viewText
    self.rendered = rendered;
    self.visible = visible;
    self.allowMathjaxification = allowMathjaxification;
    self.editor = editor;
    self.allowFullRender = allowFullRender;
    self.renderTimeout = null;
    self.displaySource = ko.observable('');
    self.debouncedAllowFullRender = $osf.debounce(function() {
        self.allowFullRender(true);
    }, THROTTLE);

    self.renderMarkdown = function(rawContent){
        if(self.visible()) {
            if (self.allowFullRender()) {
                return md.render(rawContent);
            } else {
                return mdQuick.render(rawContent);
            }
        } else {
            return '';
        }
    };

    if (typeof self.editor !== 'undefined') {
        self.editor.on('change', function () {
            // Quick render
            self.allowFullRender(false);
            // Full render
            self.debouncedAllowFullRender();
        });
    } else {
        self.allowFullRender(true);
    }

    self.displayText =  ko.computed(function() {
        self.allowFullRender();
        self.visible();
        var requestURL;
        if (typeof self.version() !== 'undefined') {
            if (self.version() === 'preview') {
                self.rendered(self.renderMarkdown(self.viewText()));
                self.displaySource(self.viewText());
            } else {
                if (self.version() === 'current') {
                    requestURL = contentURL;
                } else {
                    requestURL= contentURL + self.version();
                }
                var request = $.ajax({
                    url: requestURL
                });

                request.done(function (resp) {
                    var rawContent = resp.wiki_content || '*No wiki content*';
                    if (resp.wiki_rendered) {
                        // Use pre-rendered python, if provided. Don't mathjaxify
                        self.allowMathjaxification(false);
                        if(self.visible()) {
                            self.rendered(resp.wiki_rendered);
                        }
                    } else {
                        // Render raw markdown
                        if(self.visible()) {
                            self.allowMathjaxification(true);
                            self.rendered(self.renderMarkdown(rawContent));
                        }
                    }
                    self.displaySource(rawContent);
                });
            }
        } else {
            self.displaySource('');
        }
    });
}

    // currentText comes from ViewWidget.displayText
function CompareWidget(visible, compareVersion, currentText, rendered, contentURL) {
    var self = this;

    self.compareVersion = compareVersion;
    self.currentText = currentText;
    self.rendered = rendered;
    self.visible = visible;
    self.contentURL = contentURL;
    self.compareSource = ko.observable('');

    self.compareText = ko.computed(function() {
        var requestURL;
        if (self.compareVersion() === 'current') {
            requestURL = self.contentURL;
        } else {
            requestURL= self.contentURL + self.compareVersion();
        }
        var request = $.ajax({
            url: requestURL
        });
        request.done(function (resp) {
            var rawText = resp.wiki_content;
            self.compareSource(rawText);
        });

    });

    self.compareOutput = ko.computed(function() {
        var output = diffTool.diff(self.compareSource(), self.currentText());
        self.rendered(output);
        return output;
    }).extend({ notify: 'always' });

}

var defaultOptions = {
    editVisible: false,
    viewVisible: true,
    compareVisible: false,
    menuVisible: true,
    canEdit: true,
    viewVersion: 'current',
    compareVersion: 'previous',
    urls: {
        content: '',
        draft: '',
        page: ''
    },
    metadata: {}
};

function ViewModel(options){
    var self = this;

    // enabled?
    self.editVis = ko.observable(options.editVisible);
    self.viewVis = ko.observable(options.viewVisible);
    self.compareVis = ko.observable(options.compareVisible);
    self.menuVis = ko.observable(options.menuVisible);

    self.compareVersion = ko.observable(options.compareVersion);
    self.viewVersion = ko.observable(options.viewVersion);
    self.draftURL = options.urls.draft;
    self.contentURL = options.urls.content;
    self.pageURL = options.urls.page;
    self.editorMetadata = options.metadata;
    self.canEdit = options.canEdit;

    self.viewText = ko.observable('');
    self.renderedView = ko.observable('');
    self.renderedCompare = ko.observable('');
    self.allowMathjaxification = ko.observable(true);
    self.allowFullRender = ko.observable(true);
    self.viewVersionDisplay = ko.computed(function() {
        var versionString = '';
        if (self.viewVersion() === 'preview') {
            versionString = 'Live preview';
        } else if (self.viewVersion() === 'current'){
            versionString = 'Current version';
        } else if (self.viewVersion() === 'previous'){
            versionString = 'Previous version';
        } else {
            versionString = 'Version ' + self.viewVersion();
        }
        return versionString;
    });

    self.currentURL = ko.computed(function() {
        var url = self.pageURL;

        // Default view is special cased
        if (!self.editVis() && self.viewVis() && self.viewVersion() === 'current' && !self.compareVis() && self.menuVis()) {
            window.history.replaceState({}, '', url);
            return;
        }

        var paramPrefix = '?';

        if (self.editVis()) {
            url += paramPrefix + 'edit';
            paramPrefix = '&';
        }
        if (self.viewVis()) {
            url += paramPrefix + 'view';
            paramPrefix = '&';
            if  ((!self.editVis() && self.viewVersion() !== 'current' ) ||
                 (self.editVis() && self.viewVersion() !== 'preview')) {
                url += '=' + self.viewVersion();
            }
        }
        if (self.compareVis()) {
            url += paramPrefix + 'compare';
            paramPrefix = '&';
            if (self.compareVersion() !== 'previous'){
                url += '=' + self.compareVersion();
            }
        }
        if (self.menuVis()) {
            url += paramPrefix + 'menu';
        }

        window.history.replaceState({}, '', url);
    });

    if(self.canEdit) {
        self.editor = ace.edit('editor'); // jshint ignore: line

        var ShareJSDoc = require('addons/wiki/static/ShareJSDoc.js');
        self.editVM = new ShareJSDoc(self.draftURL, self.editorMetadata, self.viewText, self.editor);
    }
    self.viewVM = new ViewWidget(self.viewVis, self.viewVersion, self.viewText, self.renderedView, self.contentURL, self.allowMathjaxification, self.allowFullRender, self.editor);
    self.compareVM = new CompareWidget(self.compareVis, self.compareVersion, self.viewVM.displaySource, self.renderedCompare, self.contentURL);

    var bodyElement = $('body');
    bodyElement.on('togglePanel', function (event, panel, display) {
        // Update self.editVis, self.viewVis, or self.compareVis in viewmodel
        self[panel + 'Vis'](display);

        //URL needs to be a computed observable, and this should just update the panel states, which will feed URL

        // Switch view to correct version
        if (panel === 'edit') {
            if (display) {
                self.viewVersion('preview');
            } else if (self.viewVersion() === 'preview') {
                self.viewVersion('current');
            }
        } else if (panel === 'view') {
            if(!display && self.compareVis() && self.editVis()){
                self.viewVersion('preview');
            }
        }
    });

    bodyElement.on('toggleMenu', function(event, menuVisible) {
        self.menuVis(menuVisible);
    });
}

var WikiPage = function(selector, options) {
    var self = this;
    self.options = $.extend({}, defaultOptions, options);

    this.viewModel = new ViewModel(self.options);
    $osf.applyBindings(self.viewModel, selector);
};

module.exports = WikiPage;

<|endoftext|>


<commit_before>module.exports = exports = Prediction;

var _ = require('underscore');
var ds = require('../data-store');
var Observation = require('../atmosphere').AtmosphericObservation;
var Profile = require('../atmosphere').AtmosphericProfile;
var elevation = require('../mapquest').ElevationService;
var Balloon = require('./balloon');
var Parachute = require('./parachute');
var ode45 = require('numeric').dopri;
var geo = require('../geodesic');

function Prediction(parameters) {
	this.time = parameters.time;
	this.loc = parameters.loc;
	this.balloon = new Balloon(parameters.balloon);
	this.mass = parameters.mass;
	this.chute = new Parachute(parameters.parachute);
}

Prediction.defaults = {};

Prediction.prototype.run = function(options, next) {
	if (typeof next === 'undefined') {
		next = options;
		options = null;
	}

	var tStep = options.tStep || 60;

	var pred = _.extend(this, options);
	pred.type = 'Feature';
	pred.geometry = {type: 'LineString', coordinates: []};
	pred.properties = {};
	// Get the nearest sounding
	ds.getSounding(pred.time, pred.loc[1], pred.loc[0], function(err, sounding) {
		if (err) {
			next(err);
		} else {
			// Create an atmosphere profile
			var prof = new Profile();
			prof.time = sounding.time;
			prof.loc = sounding.loc;
			var obs = [];
			for (var i = 0; i < sounding.data.length; i++) {
				obs.unshift(new Observation(_.extend({
					timestamp: sounding.time,
					latitude: sounding.loc[1],
					longitude: sounding.loc[0]
				}, sounding.data[i])));
			}
			prof.addObservation(obs);

			pred.startAlt = obs[0].altitude;
			elevation([pred.loc[1], pred.loc[0]], function(err, res) {
				var resHeight;
				if(!err) resHeight = res[0].height;
				if(!isNaN(resHeight) && resHeight !== null) pred.startAlt = resHeight;

				var alt = pred.startAlt;
				var dx = 0;
				var dy = 0;
				var obs = prof.getObservationAtAltitude(pred.startAlt);
				var totalLift = pred.balloon.getLift(obs);
				var rhoA = obs.getAirDensity();
				var drag = pred.balloon.drag;
				var area = pred.balloon.getArea(obs);
				var radius = pred.balloon.getRadius(obs);
				pred.ascentRate = Math.pow((totalLift - (pred.mass * 9.81)) / (0.5 * rhoA * drag * area), 0.5);

				var elapsedTime = 0;
				pred.geometry.coordinates.push([pred.loc[0], pred.loc[1], pred.startAlt, 0]);

				// Iterate until burst
				var ascent = function(t, y) {
					var out = [];
					obs = prof.getObservationAtAltitude(y[2]);
					out[0] = obs.windU;
					out[1] = obs.windV;
					out[2] = pred.ascentRate;
					return out;
				};
				while(radius <= pred.balloon.burstRadius) {
					var sol = ode45(0, pred.tStep, [dx, dy, alt], ascent);
					var r = sol.y.pop();
					dx = r[0];
					dy = r[1];
					alt = r[2];
					elapsedTime += pred.tStep;
					var point = geo.directGeodesic(pred.loc, dx, dy);
					pred.geometry.coordinates.push([point[0], point[1], alt, elapsedTime]);
					obs = prof.getObservationAtAltitude(alt);
					radius = pred.balloon.getRadius(obs);
				}

				// Iterate until landing
				var descent = function(t, y) {
					var out = [];
					obs = prof.getObservationAtAltitude(y[2]);
					out[0] = obs.windU;
					out[1] = obs.windV;
					out[2] = -Math.sqrt((pred.mass * 9.81) / (0.5 * obs.getAirDensity() * pred.chute.area * pred.chute.drag));
					return out;
				};
				while(alt >= pred.startAlt) {
					var sol = ode45(0, pred.tStep, [dx, dy, alt], descent);
					var r = sol.y.pop();
					dx = r[0];
					dy = r[1];
					alt = r[2];
					elapsedTime += pred.tStep;
					var point = geo.directGeodesic(pred.loc, dx, dy);
					pred.geometry.coordinates.push([point[0], point[1], alt, elapsedTime]);
				}

				next(null, pred);
			});
		}
	});
};<commit_msg>test geolocation fix<commit_after>module.exports = exports = Prediction;

var _ = require('underscore');
var ds = require('../data-store');
var Observation = require('../atmosphere').AtmosphericObservation;
var Profile = require('../atmosphere').AtmosphericProfile;
var elevation = require('../mapquest').ElevationService;
var Balloon = require('./balloon');
var Parachute = require('./parachute');
var ode45 = require('numeric').dopri;
var geo = require('../geodesic');

function Prediction(parameters) {
	this.time = parameters.time;
	this.loc = parameters.loc;
	this.balloon = new Balloon(parameters.balloon);
	this.mass = parameters.mass;
	this.chute = new Parachute(parameters.parachute);
}

Prediction.defaults = {};

Prediction.prototype.run = function(options, next) {
	if (typeof next === 'undefined') {
		next = options;
		options = null;
	}

	var tStep = options.tStep || 60;

	var pred = _.extend(this, options);
	pred.type = 'Feature';
	pred.geometry = {type: 'LineString', coordinates: []};
	pred.properties = {};
	// Get the nearest sounding
	ds.getSounding(pred.time, pred.loc[1], pred.loc[0], function(err, sounding) {
		if (err) {
			next(err);
		} else {
			// Create an atmosphere profile
			var prof = new Profile();
			prof.time = sounding.time;
			prof.loc = sounding.loc;
			var obs = [];
			for (var i = 0; i < sounding.data.length; i++) {
				obs.unshift(new Observation(_.extend({
					timestamp: sounding.time,
					latitude: sounding.loc[1],
					longitude: sounding.loc[0]
				}, sounding.data[i])));
			}
			prof.addObservation(obs);

			pred.startAlt = obs[0].altitude;
			elevation([pred.loc[1], pred.loc[0]], function(err, res) {
				var resHeight;
				if(!err) {
					if(res) resHeight = res[0].height;
				}
				if(!isNaN(resHeight) && resHeight !== null) pred.startAlt = resHeight;

				var alt = pred.startAlt;
				var dx = 0;
				var dy = 0;
				var obs = prof.getObservationAtAltitude(pred.startAlt);
				var totalLift = pred.balloon.getLift(obs);
				var rhoA = obs.getAirDensity();
				var drag = pred.balloon.drag;
				var area = pred.balloon.getArea(obs);
				var radius = pred.balloon.getRadius(obs);
				pred.ascentRate = Math.pow((totalLift - (pred.mass * 9.81)) / (0.5 * rhoA * drag * area), 0.5);

				var elapsedTime = 0;
				pred.geometry.coordinates.push([pred.loc[0], pred.loc[1], pred.startAlt, 0]);

				// Iterate until burst
				var ascent = function(t, y) {
					var out = [];
					obs = prof.getObservationAtAltitude(y[2]);
					out[0] = obs.windU;
					out[1] = obs.windV;
					out[2] = pred.ascentRate;
					return out;
				};
				while(radius <= pred.balloon.burstRadius) {
					var sol = ode45(0, pred.tStep, [dx, dy, alt], ascent);
					var r = sol.y.pop();
					dx = r[0];
					dy = r[1];
					alt = r[2];
					elapsedTime += pred.tStep;
					var point = geo.directGeodesic(pred.loc, dx, dy);
					pred.geometry.coordinates.push([point[0], point[1], alt, elapsedTime]);
					obs = prof.getObservationAtAltitude(alt);
					radius = pred.balloon.getRadius(obs);
				}

				// Iterate until landing
				var descent = function(t, y) {
					var out = [];
					obs = prof.getObservationAtAltitude(y[2]);
					out[0] = obs.windU;
					out[1] = obs.windV;
					out[2] = -Math.sqrt((pred.mass * 9.81) / (0.5 * obs.getAirDensity() * pred.chute.area * pred.chute.drag));
					return out;
				};
				while(alt >= pred.startAlt) {
					var sol = ode45(0, pred.tStep, [dx, dy, alt], descent);
					var r = sol.y.pop();
					dx = r[0];
					dy = r[1];
					alt = r[2];
					elapsedTime += pred.tStep;
					var point = geo.directGeodesic(pred.loc, dx, dy);
					pred.geometry.coordinates.push([point[0], point[1], alt, elapsedTime]);
				}

				next(null, pred);
			});
		}
	});
};<|endoftext|>


<commit_before>module.exports = {
    'maxerr': false,
    'raw-ignore-regex': false,
    'attr-bans': [
        'align',
        'background',
        'bgcolor',
        'border',
        'frameborder',
        'longdesc',
        'marginwidth',
        'marginheight',
        'scrolling',
        'style',
        'width'
    ],
    'indent-delta': false,
    'indent-style': 'nonmixed',
    'indent-width': 4,
    'indent-width-cont': false,
    'spec-char-escape': true,
    'text-ignore-regex': false,
    'tag-bans': ['style', 'b', 'i'],
    'tag-close': true,
    'tag-name-lowercase': true,
    'tag-name-match': true,
    'tag-self-close': false,
    'doctype-first': false,
    'doctype-html5': false,
    'attr-name-style': 'dash',
    'attr-name-ignore-regex': false,
    'attr-no-dup': true,
    'attr-no-unsafe-char': true,
    'attr-order': false,
    'attr-quote-style': 'double',
    'attr-req-value': true,
    'attr-new-line': false,
    'attr-validate': true,
    'id-no-dup': true,
    'id-class-no-ad': true,
    'id-class-style': 'underscore',
    'class-no-dup': true,
    'class-style': false,
    'id-class-ignore-regex': false,
    'img-req-alt': true,
    'img-req-src': true,
    'html-valid-content-model': true,
    'head-valid-content-model': true,
    'href-style': false,
    'label-req-for': true,
    'line-end-style': 'lf',
    'line-no-trailing-whitespace': true,
    'line-max-len': false,
    'line-max-len-ignore-regex': false,
    'head-req-title': true,
    'title-no-dup': true,
    'title-max-len': 60,
    'html-req-lang': false,
    'lang-style': 'case',
    'fig-req-figcaption' : false,
    'focusable-tabindex-style' : false,
    'input-radio-req-name': true,
    'input-req-label': false,
    'table-req-caption': false,
    'table-req-header': false
};
<commit_msg>Disable line-no-trailing-whitespace by default for v0.7.1 patch release<commit_after>module.exports = {
    'maxerr': false,
    'raw-ignore-regex': false,
    'attr-bans': [
        'align',
        'background',
        'bgcolor',
        'border',
        'frameborder',
        'longdesc',
        'marginwidth',
        'marginheight',
        'scrolling',
        'style',
        'width'
    ],
    'indent-delta': false,
    'indent-style': 'nonmixed',
    'indent-width': 4,
    'indent-width-cont': false,
    'spec-char-escape': true,
    'text-ignore-regex': false,
    'tag-bans': ['style', 'b', 'i'],
    'tag-close': true,
    'tag-name-lowercase': true,
    'tag-name-match': true,
    'tag-self-close': false,
    'doctype-first': false,
    'doctype-html5': false,
    'attr-name-style': 'dash',
    'attr-name-ignore-regex': false,
    'attr-no-dup': true,
    'attr-no-unsafe-char': true,
    'attr-order': false,
    'attr-quote-style': 'double',
    'attr-req-value': true,
    'attr-new-line': false,
    'attr-validate': true,
    'id-no-dup': true,
    'id-class-no-ad': true,
    'id-class-style': 'underscore',
    'class-no-dup': true,
    'class-style': false,
    'id-class-ignore-regex': false,
    'img-req-alt': true,
    'img-req-src': true,
    'html-valid-content-model': true,
    'head-valid-content-model': true,
    'href-style': false,
    'label-req-for': true,
    'line-end-style': 'lf',
    'line-no-trailing-whitespace': false,
    'line-max-len': false,
    'line-max-len-ignore-regex': false,
    'head-req-title': true,
    'title-no-dup': true,
    'title-max-len': 60,
    'html-req-lang': false,
    'lang-style': 'case',
    'fig-req-figcaption' : false,
    'focusable-tabindex-style' : false,
    'input-radio-req-name': true,
    'input-req-label': false,
    'table-req-caption': false,
    'table-req-header': false
};
<|endoftext|>


<commit_before>'use strict';
var ko = require('knockout');

var $osf = require('js/osfHelpers');
var Raven = require('raven-js');

var ViewModel = function() {
    var self = this;
    self.instNames = ko.observableArray([]);
    self.selectedInst = ko.observable();
    self.insts = {};
    self.fetchInstitutions = function() {
        var url = window.contextVars.apiV2Prefix + 'institutions/';
        return $osf.ajaxJSON(
            'GET',
            url,
            {
                isCors: true
            }
        ).done(function (response) {
            self.instNames(response.data.map(function(item){
                var name = item.attributes.name;
                self.insts[name] = item.attributes.auth_url + '&target=' + encodeURIComponent(window.contextVars.institution_redirect);
                self.insts[item.id] = name;
                return name;
            }));
            var inst_redirect = decodeURIComponent(decodeURIComponent(window.contextVars.institution_redirect));
            if (inst_redirect){
                var inst_id = inst_redirect.split('institutions')[1];
                inst_id = inst_id ? inst_id.split('/')[1] : false;
                if (inst_id){
                    self.selectedInst(self.insts[inst_id]);
                }
            }
        }).fail(function (xhr, status, error) {
            Raven.captureMessage('Unable to fetch institutions', {
                url: url,
                status: status,
                error: error
            });
        });
    };

    self.instLogin = function(){
        window.location = self.insts[self.selectedInst()];
    };
};

var InstitutionSignIn = function(selector) {
    this.viewModel = new ViewModel();
    $osf.applyBindings(this.viewModel, selector);
    this.viewModel.fetchInstitutions();
};

module.exports = InstitutionSignIn;

<commit_msg>filter out the items that don't have an auth_url<commit_after>'use strict';
var ko = require('knockout');

var $osf = require('js/osfHelpers');
var Raven = require('raven-js');

var ViewModel = function() {
    var self = this;
    self.instNames = ko.observableArray([]);
    self.selectedInst = ko.observable();
    self.insts = {};
    self.fetchInstitutions = function() {
        var url = window.contextVars.apiV2Prefix + 'institutions/';
        return $osf.ajaxJSON(
            'GET',
            url,
            {
                isCors: true
            }
        ).done(function (response) {
            self.instNames(
                response.data.map(function(item){
                    var name = item.attributes.name;
                    self.insts[name] = item.attributes.auth_url + '&target=' + encodeURIComponent(window.contextVars.institution_redirect);
                    self.insts[item.id] = name;
                    if (item.attributes.auth_url){
                        return name;
                    }
                }).filter(function (item) {
                   return typeof item !== 'undefined';
                })
            );
            var inst_redirect = decodeURIComponent(decodeURIComponent(window.contextVars.institution_redirect));
            if (inst_redirect){
                var inst_id = inst_redirect.split('institutions')[1];
                inst_id = inst_id ? inst_id.split('/')[1] : false;
                if (inst_id){
                    self.selectedInst(self.insts[inst_id]);
                }
            }
        }).fail(function (xhr, status, error) {
            Raven.captureMessage('Unable to fetch institutions', {
                url: url,
                status: status,
                error: error
            });
        });
    };

    self.instLogin = function(){
        window.location = self.insts[self.selectedInst()];
    };
};

var InstitutionSignIn = function(selector) {
    this.viewModel = new ViewModel();
    $osf.applyBindings(this.viewModel, selector);
    this.viewModel.fetchInstitutions();
};

module.exports = InstitutionSignIn;

<|endoftext|>