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

var ProtoBuf = require('protobufjs');
var OrUtils = require('open-registry-utils');
var Promise = require('bluebird');
var builder = ProtoBuf.loadJson(require('../schemas/schema.proto.json'));
var Schema = builder.build('Schema').Schema;

/**
 * Instantiates RegistrantSdk.
 * @constructor
 * @param {Provider} provider to communicate with blockchain.
 * @returns {Consumer} sdk that uses specified provider to communicate with blockchain.
 */
function Registrant (provider) {
  if (provider) {
    this.registrar = provider.getRegistrar();
    this.registry = provider.getRegistry();
    this.registrarAddress = provider.registrarAddress;
    this.registryAddress = provider.registryAddress;
    this.address = provider.getAddress();
    this.web3 = provider.getWeb3();
    this.provider = provider;
  }
}

/**
 * Publish Thing on the blockchain.
 * @function
 * @param {Object} Thing information object that is going to be added. Should complement the specified schema.
 * @param {number} schemaIndex of one of the Protocol Buffers schemas stored in Registry contract.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Thing object is of template: { identities: '', data: { name: '', description: '' }};
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.createThing = function(thing, schemaIndex) {
  if (!schemaIndex) {
    schemaIndex = 1;
  }
  var self = this;
  return self.registry.schemasAsync(schemaIndex, {from: self.address}).then(function(protoBufferSchema) {
    if (protoBufferSchema.slice(0, 2) == '0x') {
      protoBufferSchema = protoBufferSchema.slice(2);
    }
    var builder = ProtoBuf.loadProto(Schema.decodeHex(protoBufferSchema).get('definition'));
    var Thing = builder.build("Thing");
    var dataProto = new Thing(thing.data);
    var dataChunks = OrUtils.bytes32Array.slice(dataProto.encodeHex());

    if (thing.identities.length == 0){
      throw('Error, no identities supplied.');
    }
    // if (!OrUtils.urn.check(thing.identities)) {
    //   throw('Error, identities format is invalid. Details: https://github.com/chronicled/open-registry-ethereum/wiki');
    // }
    var encodedIdentities = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(thing.identities));

    return self.registry.createThingAsync(encodedIdentities, dataChunks, schemaIndex, {from: self.address});
  });
};

/**
 * Publish many Things on the blockchain.
 * @function
 * @param {Object[]} Things that are going to be added. Should be an array of Things and complement the specified schema.
 * @param {number} schemaIndex of one of the Protocol Buffers schemas stored in Registry contract.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Things object is of template: [{ identities: '', data: { name: '', description: '' }}, ...];
 * @note Same schemaIndex will be used for all the items.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.createThings = function(things, schemaIndex, opts = {}) {
  const defaults = { gas: 4000000};
  const settings = Object.assign({}, defaults, opts);
  if (!schemaIndex){
    schemaIndex = 1;
  }
  var self = this;
  return self.registry.schemasAsync(schemaIndex, {from: self.address}).then(function(protoBufferSchema) {
    if (protoBufferSchema.slice(0, 2) == '0x') {
      protoBufferSchema = protoBufferSchema.slice(2);
    }
    // Pack data into chunked hex bytes32 packets using ProtoBuffer
    var builder = ProtoBuf.loadProto(Schema.decodeHex(protoBufferSchema).get('definition'));
    var Thing = builder.build("Thing");

    var idsChunks = [];
    var idsPerThing = [];
    var dataLength = [];
    var dataChunks = [];

    for (var index in things) {
      var thing = things[index];
      // if (!OrUtils.urn.check(thing.identities)) {
      //   throw ('Error, identities format is invalid. Details: https://github.com/chronicled/open-registry-ethereum/#urn-specification . Identities: ' + thing.identities.join(', '));
      // }
      idsPerThing.push(thing.identities.length);
      var encodedIdentities = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(thing.identities));
      idsChunks = idsChunks.concat(encodedIdentities);

      var chunks = OrUtils.bytes32Array.slice((new Thing(thing.data)).encodeHex());
      dataChunks = dataChunks.concat(chunks);
      dataLength.push(chunks.length);
    }

    return self.registry.createThingsAsync(idsChunks, idsPerThing, dataChunks, dataLength, schemaIndex, {from: self.address, gas: settings.gas});
  });
};

/**
 * Update existing Thing data on the blockchain.
 * @function
 * @param {string} identity in URN format of the Thing to be updated.
 * @param {Object} data of the Thing to be replaced by.
 * @param {number} schemaIndex of one of the Protocol Buffers schemas stored in Registry contract.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Data object is of template: { name: '', description: '' };
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.updateThingData = function(identity, data, schemaIndex) {
  if (!schemaIndex){
    schemaIndex = 1;
  }
  var self = this;
  return self.registry.schemasAsync(schemaIndex, {from: self.address}).then(function(protoBufferSchema) {
    if (protoBufferSchema.slice(0, 2) == '0x') {
      protoBufferSchema = protoBufferSchema.slice(2);
    }
    var builder = ProtoBuf.loadProto(Schema.decodeHex(protoBufferSchema).get('definition'));
    var Thing = builder.build("Thing");
    var dataProto = new Thing(data);
    var dataChunks = OrUtils.bytes32Array.slice(dataProto.encodeHex());

    var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));

    return self.registry.updateThingDataAsync(encodedIdentity, dataChunks, schemaIndex, {from: self.address});
  });
};

/**
 * Add identities to the existing Thing on the blockchain.
 * @function
 * @param {string} identity in the URN format of the Thing to be updated.
 * @param {string[]} newIdentities to be added to the Thing.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.addIdentities = function(identity, newIdentities) {
  var self = this;
  return Promise.try(function() {
    var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));
    // if (!OrUtils.urn.check(newIdentities)) {
    //   throw ('Error, identities format is invalid. Details: https://github.com/chronicled/open-registry-ethereum/wiki . Identities: ' + newIdentities.join(', '));
    // }
    var encodedNewIdentities = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(newIdentities));

    return self.registry.addIdentitiesAsync(encodedIdentity, encodedNewIdentities, {from: self.address});
  });
};

/**
 * Set validity of the existing Thing on the blockchain.
 * @function
 * @param {string} identity, in URN format, of the Thing to be updated.
 * @param {boolean} isValid thing validity value to be replaced by.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.setThingValid = function(identity, isValid) {
  var self = this;
  return Promise.try(function() {
    var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));
    return self.registry.setThingValidAsync(encodedIdentity, isValid, {from: self.address});
  });
};

/**
 * Get result of the blockchain operation / transaction.
 * @function
 * @param {string} transactionHash that was returned by one of the "write" functions.
 * @returns {Promise} that resolves with true, or the error message strings array.
 */
Registrant.prototype.getTransactionResult = function(transactionHash) {
  var self = this;
  return waitForTx(transactionHash, this.web3).then(function(transactionData) {
    var events = transactionData.logs.filter(function(log) {
      return log.address === self.registryAddress;
    });
    if (events.length == 0) {
      throw 'Transaction ran out of gas.';
    }
    var errors = events.filter(function(log) {
      return log.topics[0] === '0xe887de22eef9e399f405f7821ce61fcbe181b8acba1709d9b1360af087485401';
    });
    if (errors.length == 0) {
      return true;
    }
    var errorMessages = errors.map(function(log) {
      switch (self.web3.toDecimal(log.data.slice(0, 66))) {
        case 1: return 'Identity collision, already assigned to another Thing.';
        case 2: return 'Not found, identity does not exist.';
        case 3: return 'Unauthorized, modification only by owner.';
        case 4: return 'Unknown schema specified.';
        case 5: return 'Incorrect input, at least one identity is required.';
        case 6: return 'Incorrect input, data is required.';
        case 7: return 'Incorrect format of the identity, schema length and identity length cannot be empty.';
        case 8: return 'Incorrect format of the identity, identity must be padded with trailing 0s.';
      }
    });
    throw errorMessages;
  });
};

function waitForTx(txHash, web3) {
  var checkTx = function() {
    return web3.eth.getTransactionReceiptAsync(txHash);
  };
  return checkTx().catch(function() {
    return Promise.delay(100).then(checkTx);
  });
}

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

var ProtoBuf = require('protobufjs');
var OrUtils = require('open-registry-utils');
var Promise = require('bluebird');
var builder = ProtoBuf.loadJson(require('../schemas/schema.proto.json'));
var Schema = builder.build('Schema').Schema;

/**
 * Instantiates RegistrantSdk.
 * @constructor
 * @param {Provider} provider to communicate with blockchain.
 * @returns {Consumer} sdk that uses specified provider to communicate with blockchain.
 */
function Registrant (provider) {
  if (provider) {
    this.registrar = provider.getRegistrar();
    this.registry = provider.getRegistry();
    this.registrarAddress = provider.registrarAddress;
    this.registryAddress = provider.registryAddress;
    this.address = provider.getAddress();
    this.web3 = provider.getWeb3();
    this.provider = provider;
  }
}

/**
 * Publish Thing on the blockchain.
 * @function
 * @param {Object} Thing information object that is going to be added. Should complement the specified schema.
 * @param {number} schemaIndex of one of the Protocol Buffers schemas stored in Registry contract.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Thing object is of template: { identities: '', data: { name: '', description: '' }};
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.createThing = function(thing, schemaIndex) {
  if (!schemaIndex) {
    schemaIndex = 1;
  }
  var self = this;
  return self.registry.schemasAsync(schemaIndex, {from: self.address}).then(function(protoBufferSchema) {
    if (protoBufferSchema.slice(0, 2) == '0x') {
      protoBufferSchema = protoBufferSchema.slice(2);
    }
    var builder = ProtoBuf.loadProto(Schema.decodeHex(protoBufferSchema).get('definition'));
    var Thing = builder.build("Thing");
    var dataProto = new Thing(thing.data);
    var dataChunks = OrUtils.bytes32Array.slice(dataProto.encodeHex());

    if (thing.identities.length == 0){
      throw('Error, no identities supplied.');
    }
    // if (!OrUtils.urn.check(thing.identities)) {
    //   throw('Error, identities format is invalid. Details: https://github.com/chronicled/open-registry-ethereum/wiki');
    // }
    var encodedIdentities = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(thing.identities));

    return self.registry.createThingAsync(encodedIdentities, dataChunks, schemaIndex, {from: self.address});
  });
};

/**
 * Publish many Things on the blockchain.
 * @function
 * @param {Object[]} Things that are going to be added. Should be an array of Things and complement the specified schema.
 * @param {number} schemaIndex of one of the Protocol Buffers schemas stored in Registry contract.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Things object is of template: [{ identities: '', data: { name: '', description: '' }}, ...];
 * @note Same schemaIndex will be used for all the items.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.createThings = function(things, schemaIndex, opts = {}) {
  const defaults = { gas: 4000000};
  const settings = Object.assign({}, defaults, opts);
  if (!schemaIndex){
    schemaIndex = 1;
  }
  var self = this;
  return self.registry.schemasAsync(schemaIndex, {from: self.address}).then(function(protoBufferSchema) {
    if (protoBufferSchema.slice(0, 2) == '0x') {
      protoBufferSchema = protoBufferSchema.slice(2);
    }
    // Pack data into chunked hex bytes32 packets using ProtoBuffer
    var builder = ProtoBuf.loadProto(Schema.decodeHex(protoBufferSchema).get('definition'));
    var Thing = builder.build("Thing");

    var idsChunks = [];
    var idsPerThing = [];
    var dataLength = [];
    var dataChunks = [];

    for (var index in things) {
      var thing = things[index];
      // if (!OrUtils.urn.check(thing.identities)) {
      //   throw ('Error, identities format is invalid. Details: https://github.com/chronicled/open-registry-ethereum/#urn-specification . Identities: ' + thing.identities.join(', '));
      // }
      idsPerThing.push(thing.identities.length);
      var encodedIdentities = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(thing.identities));
      idsChunks = idsChunks.concat(encodedIdentities);

      var chunks = OrUtils.bytes32Array.slice((new Thing(thing.data)).encodeHex());
      dataChunks = dataChunks.concat(chunks);
      dataLength.push(chunks.length);
    }

    return self.registry.createThingsAsync(idsChunks, idsPerThing, dataChunks, dataLength, schemaIndex, {from: self.address, gas: settings.gas});
  });
};

/**
 * Update existing Thing data on the blockchain.
 * @function
 * @param {string} identity in URN format of the Thing to be updated.
 * @param {Object} data of the Thing to be replaced by.
 * @param {number} schemaIndex of one of the Protocol Buffers schemas stored in Registry contract.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Data object is of template: { name: '', description: '' };
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.updateThingData = function(identity, data, schemaIndex) {
  if (!schemaIndex){
    schemaIndex = 1;
  }
  var self = this;
  return self.registry.schemasAsync(schemaIndex, {from: self.address}).then(function(protoBufferSchema) {
    if (protoBufferSchema.slice(0, 2) == '0x') {
      protoBufferSchema = protoBufferSchema.slice(2);
    }
    var builder = ProtoBuf.loadProto(Schema.decodeHex(protoBufferSchema).get('definition'));
    var Thing = builder.build("Thing");
    var dataProto = new Thing(data);
    var dataChunks = OrUtils.bytes32Array.slice(dataProto.encodeHex());

    var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));

    return self.registry.updateThingDataAsync(encodedIdentity, dataChunks, schemaIndex, {from: self.address});
  });
};

/**
 * Delete existing Thing data on the blockchain.
 * @function
 * @param {string} identity in URN format of the Thing to be updated.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.deleteThing = function(identity) {
  var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));
  return this.registry.deleteThingAsync(encodedIdentity, {from: this.address});
};
/**
 * Add identities to the existing Thing on the blockchain.
 * @function
 * @param {string} identity in the URN format of the Thing to be updated.
 * @param {string[]} newIdentities to be added to the Thing.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.addIdentities = function(identity, newIdentities) {
  var self = this;
  return Promise.try(function() {
    var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));
    // if (!OrUtils.urn.check(newIdentities)) {
    //   throw ('Error, identities format is invalid. Details: https://github.com/chronicled/open-registry-ethereum/wiki . Identities: ' + newIdentities.join(', '));
    // }
    var encodedNewIdentities = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(newIdentities));

    return self.registry.addIdentitiesAsync(encodedIdentity, encodedNewIdentities, {from: self.address});
  });
};

/**
 * Set validity of the existing Thing on the blockchain.
 * @function
 * @param {string} identity, in URN format, of the Thing to be updated.
 * @param {boolean} isValid thing validity value to be replaced by.
 * @returns {Promise} that resolves with transaction hash of the operation.
 * @note Sends transaction, costs Ether.
 */
Registrant.prototype.setThingValid = function(identity, isValid) {
  var self = this;
  return Promise.try(function() {
    var encodedIdentity = OrUtils.urn.packer.encodeAndChunk(OrUtils.common.compressAll(identity));
    return self.registry.setThingValidAsync(encodedIdentity, isValid, {from: self.address});
  });
};

/**
 * Get result of the blockchain operation / transaction.
 * @function
 * @param {string} transactionHash that was returned by one of the "write" functions.
 * @returns {Promise} that resolves with true, or the error message strings array.
 */
Registrant.prototype.getTransactionResult = function(transactionHash) {
  var self = this;
  return waitForTx(transactionHash, this.web3).then(function(transactionData) {
    var events = transactionData.logs.filter(function(log) {
      return log.address === self.registryAddress;
    });
    if (events.length == 0) {
      throw 'Transaction ran out of gas.';
    }
    var errors = events.filter(function(log) {
      return log.topics[0] === '0xe887de22eef9e399f405f7821ce61fcbe181b8acba1709d9b1360af087485401';
    });
    if (errors.length == 0) {
      return true;
    }
    var errorMessages = errors.map(function(log) {
      switch (self.web3.toDecimal(log.data.slice(0, 66))) {
        case 1: return 'Identity collision, already assigned to another Thing.';
        case 2: return 'Not found, identity does not exist.';
        case 3: return 'Unauthorized, modification only by owner.';
        case 4: return 'Unknown schema specified.';
        case 5: return 'Incorrect input, at least one identity is required.';
        case 6: return 'Incorrect input, data is required.';
        case 7: return 'Incorrect format of the identity, schema length and identity length cannot be empty.';
        case 8: return 'Incorrect format of the identity, identity must be padded with trailing 0s.';
      }
    });
    throw errorMessages;
  });
};

function waitForTx(txHash, web3) {
  var checkTx = function() {
    return web3.eth.getTransactionReceiptAsync(txHash);
  };
  return checkTx().catch(function() {
    return Promise.delay(100).then(checkTx);
  });
}

module.exports = Registrant;
<|endoftext|>


<commit_before>var fs = require('fs')
var build = require('buildModules')

var buildDirectory = __dirname+'/generatedBuilds/'
if(!fs.existsSync(buildDirectory)) {
    fs.mkdirSync(buildDirectory)
}

var commonJsModule = fs.readFileSync(__dirname + '/asyncFuture.js').toString()
var copywrite = '/* Copyright (c) 2013 Billy Tetrud - Free to use for any purpose: MIT License*/'

console.log('building and minifying...')
build(buildDirectory, 'asyncFuture', copywrite, commonJsModule, undefined)
console.log('done')<commit_msg>fixing browser build<commit_after>var fs = require('fs')
var build = require('build-modules')

var buildDirectory = __dirname+'/generatedBuilds/'
if(!fs.existsSync(buildDirectory)) {
    fs.mkdirSync(buildDirectory)
}

var commonJsModule = fs.readFileSync(__dirname + '/asyncFuture.js').toString()
var copywrite = '/* Copyright (c) 2013 Billy Tetrud - Free to use for any purpose: MIT License*/'

console.log('building and minifying...')

build(buildDirectory, 'asyncFuture', copywrite, __dirname + '/asyncFuture.js', undefined, function(e) {
    if(e !== undefined) {
        console.error(e.stack)
        process.exit(1)
    } else {
        console.log('done')
    }
})

<|endoftext|>


<commit_before>import Vue from 'vue';
import store from '~/ide/stores';
import listItem from '~/ide/components/commit_sidebar/list_item.vue';
import router from '~/ide/ide_router';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file, resetStore } from '../../helpers';

describe('Multi-file editor commit sidebar list item', () => {
  let vm;
  let f;

  beforeEach(() => {
    const Component = Vue.extend(listItem);

    f = file('test-file');

    store.state.entries[f.path] = f;

    vm = createComponentWithStore(Component, store, {
      file: f,
      actionComponent: 'stage-button',
      activeFileKey: `staged-${f.key}`,
    }).$mount();
  });

  afterEach(() => {
    vm.$destroy();

    resetStore(store);
  });

  it('renders file path', () => {
    expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
  });

  it('renders actionn button', () => {
    expect(vm.$el.querySelector('.multi-file-discard-btn')).not.toBeNull();
  });

  it('opens a closed file in the editor when clicking the file path', done => {
    spyOn(vm, 'openPendingTab').and.callThrough();
    spyOn(router, 'push');

    vm.$el.querySelector('.multi-file-commit-list-path').click();

    setTimeout(() => {
      expect(vm.openPendingTab).toHaveBeenCalled();
      expect(router.push).toHaveBeenCalled();

      done();
    });
  });

  it('calls updateViewer with diff when clicking file', done => {
    spyOn(vm, 'openFileInEditor').and.callThrough();
    spyOn(vm, 'updateViewer').and.callThrough();
    spyOn(router, 'push');

    vm.$el.querySelector('.multi-file-commit-list-path').click();

    setTimeout(() => {
      expect(vm.updateViewer).toHaveBeenCalledWith('diff');

      done();
    });
  });

  describe('computed', () => {
    describe('iconName', () => {
      it('returns modified when not a tempFile', () => {
        expect(vm.iconName).toBe('file-modified');
      });

      it('returns addition when not a tempFile', () => {
        f.tempFile = true;

        expect(vm.iconName).toBe('file-addition');
      });

      it('returns deletion', () => {
        f.deleted = true;

        expect(vm.iconName).toBe('file-deletion');
      });
    });

    describe('iconClass', () => {
      it('returns modified when not a tempFile', () => {
        expect(vm.iconClass).toContain('ide-file-modified');
      });

      it('returns addition when not a tempFile', () => {
        f.tempFile = true;

        expect(vm.iconClass).toContain('ide-file-addition');
      });

      it('returns deletion', () => {
        f.deleted = true;

        expect(vm.iconClass).toContain('ide-file-deletion');
      });
    });
  });

  describe('is active', () => {
    it('does not add active class when dont keys match', () => {
      expect(vm.$el.querySelector('.is-active')).toBe(null);
    });

    it('adds active class when keys match', done => {
      vm.keyPrefix = 'staged';

      vm.$nextTick(() => {
        expect(vm.$el.querySelector('.is-active')).not.toBe(null);

        done();
      });
    });
  });
});
<commit_msg>fixed `list_item_spec.js`<commit_after>import Vue from 'vue';
import store from '~/ide/stores';
import listItem from '~/ide/components/commit_sidebar/list_item.vue';
import router from '~/ide/ide_router';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file, resetStore } from '../../helpers';

describe('Multi-file editor commit sidebar list item', () => {
  let vm;
  let f;

  beforeEach(() => {
    const Component = Vue.extend(listItem);

    f = file('test-file');

    store.state.entries[f.path] = f;

    vm = createComponentWithStore(Component, store, {
      file: f,
      actionComponent: 'stage-button',
      activeFileKey: `staged-${f.key}`,
    }).$mount();
  });

  afterEach(() => {
    vm.$destroy();

    resetStore(store);
  });

  it('renders file path', () => {
    expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent).toContain(f.path);
  });

  it('renders actionn button', () => {
    expect(vm.$el.querySelector('.multi-file-discard-btn')).not.toBeNull();
  });

  it('opens a closed file in the editor when clicking the file path', done => {
    spyOn(vm, 'openPendingTab').and.callThrough();
    spyOn(router, 'push');

    vm.$el.querySelector('.multi-file-commit-list-path').click();

    setTimeout(() => {
      expect(vm.openPendingTab).toHaveBeenCalled();
      expect(router.push).toHaveBeenCalled();

      done();
    });
  });

  it('calls updateViewer with diff when clicking file', done => {
    spyOn(vm, 'openFileInEditor').and.callThrough();
    spyOn(vm, 'updateViewer').and.callThrough();
    spyOn(router, 'push');

    vm.$el.querySelector('.multi-file-commit-list-path').click();

    setTimeout(() => {
      expect(vm.updateViewer).toHaveBeenCalledWith('diff');

      done();
    });
  });

  describe('computed', () => {
    describe('iconName', () => {
      it('returns modified when not a tempFile', () => {
        expect(vm.iconName).toBe('file-modified');
      });

      it('returns addition when not a tempFile', () => {
        f.tempFile = true;

        expect(vm.iconName).toBe('file-addition');
      });

      it('returns deletion', () => {
        f.deleted = true;

        expect(vm.iconName).toBe('file-deletion');
      });
    });

    describe('iconClass', () => {
      it('returns modified when not a tempFile', () => {
        expect(vm.iconClass).toContain('ide-file-modified');
      });

      it('returns addition when not a tempFile', () => {
        f.tempFile = true;

        expect(vm.iconClass).toContain('ide-file-addition');
      });

      it('returns deletion', () => {
        f.deleted = true;

        expect(vm.iconClass).toContain('ide-file-deletion');
      });
    });
  });

  describe('is active', () => {
    it('does not add active class when dont keys match', () => {
      expect(vm.$el.querySelector('.is-active')).toBe(null);
    });

    it('adds active class when keys match', done => {
      vm.keyPrefix = 'staged';

      vm.$nextTick(() => {
        expect(vm.$el.querySelector('.is-active')).not.toBe(null);

        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>var compose = (function () {

var exports = {};
var is_composing_message = false;

function show(tabname, focus_area) {
    if (tabname === "stream") {
        $('#private-message').hide();
        $('#stream-message').show();
        $("#stream_toggle").addClass("active");
        $("#private_message_toggle").removeClass("active");
    } else {
        $('#private-message').show();
        $('#stream-message').hide();
        $("#stream_toggle").removeClass("active");
        $("#private_message_toggle").addClass("active");
    }
    $("#send-status").removeClass(status_classes).hide();
    $('#compose').css({visibility: "visible"});
    $("#new_message_content").trigger("autosize");
    $('.message_comp').slideDown(100, function () {
        // If the compose box is obscuring the currently selected message,
        // scroll up until the message is no longer occluded.
        if (selected_message_id === -1) {
            // If there's no selected message, there's no need to
            // scroll the compose box to avoid it.
            return;
        }
        var cover = selected_message.offset().top + selected_message.height()
            - $("#compose").offset().top;
        if (cover > 0) {
            disable_pointer_movement = true;
            // We use $('html, body') because you can't animate window.scrollTop
            // on Chrome (http://bugs.jquery.com/ticket/10419).
            $('html, body').animate({
                scrollTop: viewport.scrollTop() + cover + 5
            }, {
                complete: function () {
                    // The complete callback is actually called before the
                    // scrolling has completed, so we try to let scrolling
                    // finish before allowing pointer movements again or the
                    // pointer may still move.
                    setTimeout(function () {
                        disable_pointer_movement = false;
                    }, 50);
                }
            });
        }
    });
    focus_area.focus();
    focus_area.select();
    // Disable the notifications bar if it overlaps with the composebox
    notifications_bar.maybe_disable();
}

// In an attempt to decrease mixing, make the composebox's
// stream bar look like what you're replying to.
// (In particular, if there's a color associated with it,
//  have that color be reflected here too.)
exports.decorate_stream_bar = function (stream_name) {
    var color = subs.get_color(stream_name);
    $("#stream-message .message_header_stream").css("background-color", color);
};

exports.start = function (msg_type, opts) {
    if (reload.is_in_progress()) {
        return;
    }

    compose.clear();

    var default_stream = '';
    if (msg_type === 'stream' && lurk_stream !== undefined)
        default_stream = lurk_stream;
    opts = $.extend({ message_type:     msg_type,
                      stream:           default_stream,
                      subject:          '',
                      private_message_recipient: ''
                    }, opts);

    compose.stream_name(opts.stream);
    compose.subject(opts.subject);
    compose.recipient(opts.private_message_recipient);
    // If the user opens the compose box, types some text, and then clicks on a
    // different stream/subject, we want to keep the text in the compose box
    if (opts.message !== undefined) {
        compose.message_content(opts.message);
    }

    ui.change_tab_to("#home");

    var focus_area;
    if (opts.stream && ! opts.subject) {
        focus_area = 'subject';
    } else if (opts.stream || opts.private_message_recipient) {
        focus_area = 'new_message_content';
    }

    if (msg_type === 'stream') {
        show('stream', $("#" + (focus_area || 'stream')));
    } else {
        show('private', $("#" + (focus_area || 'private_message_recipient')));
    }

    is_composing_message = msg_type;
    exports.decorate_stream_bar(opts.stream);
    $(document).trigger($.Event('compose_started.zephyr', opts));
};

exports.cancel = function () {
    compose.hide();
    is_composing_message = false;
    $(document).trigger($.Event('compose_canceled.zephyr'));
};

var send_options;

function send_message() {
    var send_status = $('#send-status');

    // TODO: this should be collapsed with the code in composebox_typeahead.js
    var recipients = compose.recipient().split(/\s*[,;]\s*/);

    var request = {client: 'website',
                   type:        compose.composing(),
                   subject:     compose.subject(),
                   content:     compose.message_content()};
    if (request.type === "private") {
        request.to = JSON.stringify(recipients);
    } else {
        request.to = compose.stream_name();
    }

    $.ajax({
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        url: '/json/send_message',
        type: 'POST',
        data: request,
        success: function (resp, statusText, xhr) {
            compose.clear();
            send_status.hide();
            is_composing_message = false;
            compose.hide();
            $("#compose-send-button").removeAttr('disabled');
        },
        error: function (xhr, error_type) {
            if (error_type !== 'timeout' && reload.is_pending()) {
                // The error might be due to the server changing
                reload.initiate({immediate: true, send_after_reload: true});
                return;
            }
            var response = "Error sending message";
            if (xhr.status.toString().charAt(0) === "4") {
                // Only display the error response for 4XX, where we've crafted
                // a nice response.
                response += ": " + $.parseJSON(xhr.responseText).msg;
            }
            show(is_composing_message, $("#new_message_content"));
            send_status.removeClass(status_classes)
                       .addClass('alert-error')
                       .text(response)
                       .append($('<span />')
                           .addClass('send-status-close').html('×')
                           .click(function () { send_status.stop(true).fadeOut(500); }))
                       .stop(true).fadeTo(0,1);

            $("#compose-send-button").removeAttr('disabled');
        }
    });

    send_status.hide();
}

exports.finish = function () {
    if (! compose.validate()) {
        return false;
    }
    send_message();
    // TODO: Do we want to fire the event even if the send failed due
    // to a server-side error?
    $(document).trigger($.Event('compose_finished.zephyr'));
    return true;
};

$(function () {
    $("#compose form").on("submit", function (e) {
       e.preventDefault();
       compose.finish();
    });
});

exports.hide = function () {
    $('input, textarea, button').blur();
    $('.message_comp').slideUp(100,
                              function() { $('#compose').css({visibility: "hidden"});});
    notifications_bar.enable();
};

exports.clear = function () {
    $("#compose").find('input[type=text], textarea').val('');
};

// Set the mode of a compose already in progress.
// Does not clear the input fields.
exports.set_mode = function (mode) {
    if (!is_composing_message) {
        exports.start(mode);
    }
    if (mode === 'private') {
        show('private', $("#private_message_recipient"));
        is_composing_message = "private";
    } else {
        show('stream', $(lurk_stream !== undefined ? "#subject" : "#stream"));
        is_composing_message = "stream";
    }
};

exports.composing = function () {
    return is_composing_message;
};

function get_or_set(fieldname, keep_outside_whitespace) {
    // We can't hoist the assignment of 'elem' out of this lambda,
    // because the DOM element might not exist yet when get_or_set
    // is called.
    return function (newval) {
        var elem = $('#'+fieldname);
        var oldval = elem.val();
        if (newval !== undefined) {
            elem.val(newval);
        }
        return keep_outside_whitespace ? oldval : $.trim(oldval);
    };
}

exports.stream_name     = get_or_set('stream');
exports.subject         = get_or_set('subject');
exports.message_content = get_or_set('new_message_content', true);
exports.recipient       = get_or_set('private_message_recipient');

function compose_error(error_text, bad_input) {
    $('#send-status').removeClass(status_classes)
               .addClass('alert-error')
               .text(error_text)
               .stop(true).fadeTo(0, 1);
    $("#compose-send-button").removeAttr('disabled');
    bad_input.focus().select();
}

// *Synchronously* check if a stream exists.
// If not, displays an error and returns false.
function check_stream_for_send(stream_name) {
    var result = "error";
    $.ajax({
        type: "POST",
        url: "/json/subscriptions/exists",
        data: {'stream': stream_name},
        async: false,
        success: function (data) {
            if (!data.exists) {
                // The stream doesn't exist
                result = "does-not-exist";
                $('#send-status').removeClass(status_classes);
                $('#stream-dne-name').text(stream_name);
                $('#stream-dne').show();
                $("#compose-send-button").removeAttr('disabled');
                exports.hide();
                $('#create-it').focus();
            } else if (data.subscribed) {
                result = "subscribed";
            } else {
                result = "not-subscribed";
            }
            $("#home-error").hide();
        },
        error: function (xhr) {
            result = "error";
            ui.report_error("Error checking subscription", xhr, $("#home-error"));
            $("#stream").focus();
            $("#compose-send-button").removeAttr('disabled');
        }
    });
    return result;
}

function validate_stream_message() {
    var stream_name = exports.stream_name();
    if (stream_name === "") {
        compose_error("Please specify a stream", $("#stream"));
        return false;
    }

    if (exports.subject() === "") {
        compose_error("Please specify a subject", $("#subject"));
        return false;
    }

    if (!subs.have(stream_name)) {
        switch(check_stream_for_send(stream_name)) {
        case "does-not-exist":
        case "error":
            return false;
        case "subscribed":
            // You're actually subscribed to the stream, but this
            // browser window doesn't know it.
            return true;
        case "not-subscribed":
            if (lurk_stream !== undefined &&
                lurk_stream.toLowerCase() === stream_name.toLowerCase()) {
                // Just subscribe them.
                subs.subscribe_for_send(stream_name);
                // When the subscription request completes,
                // subscribe_for_send will send our message (like when
                // the user clicks on subscribe-and-send).
                return false;
            }

            $('#send-status').removeClass(status_classes);
            $('#stream-nosub-name').text(stream_name);
            $('#stream-nosub').show();
            $("#compose-send-button").removeAttr('disabled');
            exports.hide();
            $('#sub-it').focus();
            return false;
        }
    }

    return true;
}

function validate_private_message() {
    if (exports.recipient() === "") {
        compose_error("Please specify at least one recipient", $("#private_message_recipient"));
        return false;
    }

    return true;
}

exports.validate = function () {
    $("#compose-send-button").attr('disabled', 'disabled').blur();

    if (exports.message_content() === "") {
        compose_error("You have nothing to send!", $("#new_message_content"));
        return false;
    }

    if (exports.composing() === 'private') {
        return validate_private_message();
    } else {
        return validate_stream_message();
    }
};

$(function () {
    $("#new_message_content").autosize();
});

return exports;

}());
<commit_msg>compose: split check_stream_for_send into into request and UI response functions.<commit_after>var compose = (function () {

var exports = {};
var is_composing_message = false;

function show(tabname, focus_area) {
    if (tabname === "stream") {
        $('#private-message').hide();
        $('#stream-message').show();
        $("#stream_toggle").addClass("active");
        $("#private_message_toggle").removeClass("active");
    } else {
        $('#private-message').show();
        $('#stream-message').hide();
        $("#stream_toggle").removeClass("active");
        $("#private_message_toggle").addClass("active");
    }
    $("#send-status").removeClass(status_classes).hide();
    $('#compose').css({visibility: "visible"});
    $("#new_message_content").trigger("autosize");
    $('.message_comp').slideDown(100, function () {
        // If the compose box is obscuring the currently selected message,
        // scroll up until the message is no longer occluded.
        if (selected_message_id === -1) {
            // If there's no selected message, there's no need to
            // scroll the compose box to avoid it.
            return;
        }
        var cover = selected_message.offset().top + selected_message.height()
            - $("#compose").offset().top;
        if (cover > 0) {
            disable_pointer_movement = true;
            // We use $('html, body') because you can't animate window.scrollTop
            // on Chrome (http://bugs.jquery.com/ticket/10419).
            $('html, body').animate({
                scrollTop: viewport.scrollTop() + cover + 5
            }, {
                complete: function () {
                    // The complete callback is actually called before the
                    // scrolling has completed, so we try to let scrolling
                    // finish before allowing pointer movements again or the
                    // pointer may still move.
                    setTimeout(function () {
                        disable_pointer_movement = false;
                    }, 50);
                }
            });
        }
    });
    focus_area.focus();
    focus_area.select();
    // Disable the notifications bar if it overlaps with the composebox
    notifications_bar.maybe_disable();
}

// In an attempt to decrease mixing, make the composebox's
// stream bar look like what you're replying to.
// (In particular, if there's a color associated with it,
//  have that color be reflected here too.)
exports.decorate_stream_bar = function (stream_name) {
    var color = subs.get_color(stream_name);
    $("#stream-message .message_header_stream").css("background-color", color);
};

exports.start = function (msg_type, opts) {
    if (reload.is_in_progress()) {
        return;
    }

    compose.clear();

    var default_stream = '';
    if (msg_type === 'stream' && lurk_stream !== undefined)
        default_stream = lurk_stream;
    opts = $.extend({ message_type:     msg_type,
                      stream:           default_stream,
                      subject:          '',
                      private_message_recipient: ''
                    }, opts);

    compose.stream_name(opts.stream);
    compose.subject(opts.subject);
    compose.recipient(opts.private_message_recipient);
    // If the user opens the compose box, types some text, and then clicks on a
    // different stream/subject, we want to keep the text in the compose box
    if (opts.message !== undefined) {
        compose.message_content(opts.message);
    }

    ui.change_tab_to("#home");

    var focus_area;
    if (opts.stream && ! opts.subject) {
        focus_area = 'subject';
    } else if (opts.stream || opts.private_message_recipient) {
        focus_area = 'new_message_content';
    }

    if (msg_type === 'stream') {
        show('stream', $("#" + (focus_area || 'stream')));
    } else {
        show('private', $("#" + (focus_area || 'private_message_recipient')));
    }

    is_composing_message = msg_type;
    exports.decorate_stream_bar(opts.stream);
    $(document).trigger($.Event('compose_started.zephyr', opts));
};

exports.cancel = function () {
    compose.hide();
    is_composing_message = false;
    $(document).trigger($.Event('compose_canceled.zephyr'));
};

var send_options;

function send_message() {
    var send_status = $('#send-status');

    // TODO: this should be collapsed with the code in composebox_typeahead.js
    var recipients = compose.recipient().split(/\s*[,;]\s*/);

    var request = {client: 'website',
                   type:        compose.composing(),
                   subject:     compose.subject(),
                   content:     compose.message_content()};
    if (request.type === "private") {
        request.to = JSON.stringify(recipients);
    } else {
        request.to = compose.stream_name();
    }

    $.ajax({
        dataType: 'json', // This seems to be ignored. We still get back an xhr.
        url: '/json/send_message',
        type: 'POST',
        data: request,
        success: function (resp, statusText, xhr) {
            compose.clear();
            send_status.hide();
            is_composing_message = false;
            compose.hide();
            $("#compose-send-button").removeAttr('disabled');
        },
        error: function (xhr, error_type) {
            if (error_type !== 'timeout' && reload.is_pending()) {
                // The error might be due to the server changing
                reload.initiate({immediate: true, send_after_reload: true});
                return;
            }
            var response = "Error sending message";
            if (xhr.status.toString().charAt(0) === "4") {
                // Only display the error response for 4XX, where we've crafted
                // a nice response.
                response += ": " + $.parseJSON(xhr.responseText).msg;
            }
            show(is_composing_message, $("#new_message_content"));
            send_status.removeClass(status_classes)
                       .addClass('alert-error')
                       .text(response)
                       .append($('<span />')
                           .addClass('send-status-close').html('×')
                           .click(function () { send_status.stop(true).fadeOut(500); }))
                       .stop(true).fadeTo(0,1);

            $("#compose-send-button").removeAttr('disabled');
        }
    });

    send_status.hide();
}

exports.finish = function () {
    if (! compose.validate()) {
        return false;
    }
    send_message();
    // TODO: Do we want to fire the event even if the send failed due
    // to a server-side error?
    $(document).trigger($.Event('compose_finished.zephyr'));
    return true;
};

$(function () {
    $("#compose form").on("submit", function (e) {
       e.preventDefault();
       compose.finish();
    });
});

exports.hide = function () {
    $('input, textarea, button').blur();
    $('.message_comp').slideUp(100,
                              function() { $('#compose').css({visibility: "hidden"});});
    notifications_bar.enable();
};

exports.clear = function () {
    $("#compose").find('input[type=text], textarea').val('');
};

// Set the mode of a compose already in progress.
// Does not clear the input fields.
exports.set_mode = function (mode) {
    if (!is_composing_message) {
        exports.start(mode);
    }
    if (mode === 'private') {
        show('private', $("#private_message_recipient"));
        is_composing_message = "private";
    } else {
        show('stream', $(lurk_stream !== undefined ? "#subject" : "#stream"));
        is_composing_message = "stream";
    }
};

exports.composing = function () {
    return is_composing_message;
};

function get_or_set(fieldname, keep_outside_whitespace) {
    // We can't hoist the assignment of 'elem' out of this lambda,
    // because the DOM element might not exist yet when get_or_set
    // is called.
    return function (newval) {
        var elem = $('#'+fieldname);
        var oldval = elem.val();
        if (newval !== undefined) {
            elem.val(newval);
        }
        return keep_outside_whitespace ? oldval : $.trim(oldval);
    };
}

exports.stream_name     = get_or_set('stream');
exports.subject         = get_or_set('subject');
exports.message_content = get_or_set('new_message_content', true);
exports.recipient       = get_or_set('private_message_recipient');

function compose_error(error_text, bad_input) {
    $('#send-status').removeClass(status_classes)
               .addClass('alert-error')
               .text(error_text)
               .stop(true).fadeTo(0, 1);
    $("#compose-send-button").removeAttr('disabled');
    bad_input.focus().select();
}

// *Synchronously* check if a stream exists.
exports.check_stream_existence = function (stream_name) {
    var result = "error";
    var error_xhr = "";
    $.ajax({
        type: "POST",
        url: "/json/subscriptions/exists",
        data: {'stream': stream_name},
        async: false,
        success: function (data) {
            if (!data.exists) {
                // The stream doesn't exist
                result = "does-not-exist";
            } else if (data.subscribed) {
                result = "subscribed";
            } else {
                result = "not-subscribed";
            }
        },
        error: function (xhr) {
            result = "error";
            error_xhr = xhr;
        }
    });
    return [result, error_xhr];
};

// Checks if a stream exists. If not, displays an error and returns
// false.
function check_stream_for_send(stream_name) {
    var stream_check = exports.check_stream_existence(stream_name);
    var result = stream_check[0];
    var xhr = stream_check[1];

    if (result === "error") {
        ui.report_error("Error checking subscription", xhr, $("#home-error"));
        $("#stream").focus();
        $("#compose-send-button").removeAttr('disabled');
    } else {
        if (result === "does-not-exist") {
           $('#send-status').removeClass(status_classes);
                $('#stream-dne-name').text(stream_name);
                $('#stream-dne').show();
                $("#compose-send-button").removeAttr('disabled');
                exports.hide();
                $('#create-it').focus();
        }
        $("#home-error").hide();
    }

    return result;
}

function validate_stream_message() {
    var stream_name = exports.stream_name();
    if (stream_name === "") {
        compose_error("Please specify a stream", $("#stream"));
        return false;
    }

    if (exports.subject() === "") {
        compose_error("Please specify a subject", $("#subject"));
        return false;
    }

    if (!subs.have(stream_name)) {
        switch(check_stream_for_send(stream_name)) {
        case "does-not-exist":
        case "error":
            return false;
        case "subscribed":
            // You're actually subscribed to the stream, but this
            // browser window doesn't know it.
            return true;
        case "not-subscribed":
            if (lurk_stream !== undefined &&
                lurk_stream.toLowerCase() === stream_name.toLowerCase()) {
                // Just subscribe them.
                subs.subscribe_for_send(stream_name);
                // When the subscription request completes,
                // subscribe_for_send will send our message (like when
                // the user clicks on subscribe-and-send).
                return false;
            }

            $('#send-status').removeClass(status_classes);
            $('#stream-nosub-name').text(stream_name);
            $('#stream-nosub').show();
            $("#compose-send-button").removeAttr('disabled');
            exports.hide();
            $('#sub-it').focus();
            return false;
        }
    }

    return true;
}

function validate_private_message() {
    if (exports.recipient() === "") {
        compose_error("Please specify at least one recipient", $("#private_message_recipient"));
        return false;
    }

    return true;
}

exports.validate = function () {
    $("#compose-send-button").attr('disabled', 'disabled').blur();

    if (exports.message_content() === "") {
        compose_error("You have nothing to send!", $("#new_message_content"));
        return false;
    }

    if (exports.composing() === 'private') {
        return validate_private_message();
    } else {
        return validate_stream_message();
    }
};

$(function () {
    $("#new_message_content").autosize();
});

return exports;

}());
<|endoftext|>


<commit_before>var TriangleRoutingApp = Backbone.Model.extend({

    appname: "r",

    defaults: {
        contextpath: "/"
    },
    initialize: function () {

        console.log("Exporting Application to r");
        window[this.appname] = this;

        // initializing
        this.server = new Server(this.get("contextpath"), this.error);
        this.countries = new SelectableAwareCollection([
            {name: "Not loaded yet", value: "Not loaded yet"}
        ]);
        this.router = new TriangleRoutingRouter({
            app: this
        });
    },

    start: function () {

        // the order of this methods must be in this specific order, PLEASE DO NOT CHANGE
        this.queryCountries();
        this.loadTerminals();
        this.loadSeaports();

        Backbone.history.start({
            pushState: false,
            root: "/"
        });
    },

    loadTerminals: function () {

        var terminals = new TerminalList();
        this.terminals = terminals;
        this.server.terminals(function (data) {
            terminals.add(data);
        });
        terminals.setDefaultValue();
    },

    loadSeaports: function () {

        var seaports = new SeaportList();
        this.seaports = seaports;

        this.server.getActiveSeaports(function (data) {
            seaports.add(data);
        });
        seaports.setDefaultValue();
    },

    showRequest: function (forcenew) {

        if ((!this.triangleModel) || forcenew) {

            this.triangleModel = new TriangleModel({
                terminals: this.terminals,
                seaports: this.seaports,
                server: this.server,
                countries: this.countries
            });

            this.triangleView = new TriangleView({
                el: "#appcontent",
                model: this.triangleModel
            });
        }

        console.log("Currently " + this.triangleModel.get("points").length + " points in triangle.");
    },

    queryCountries: function () {
        var countries = this.countries;

        this.server.countries(function (countryList) {
            countries.reset(countryList);
        });

        countries.setDefaultByValue("DE");
    },

    /**
     * boolean permanent, if the notify window will stay and not fadeOut by itself
     */
    error: function (msg, permanent) {
        var fadeOut = true;
        var closable = true;
        if (permanent !== undefined) {
            fadeOut = false;
            closable = true;
        }

        // for the ones that aren't closable and don't fade out there is a .close() function.
        $('.notifications').notify({
            type: "error",
            message: {
                text: msg
            },
            fadeOut: {
                enabled: fadeOut
            },
            closable: closable
        }).show();
    }

});
<commit_msg>remove not used appname<commit_after>var TriangleRoutingApp = Backbone.Model.extend({

    defaults: {
        contextpath: "/"
    },
    initialize: function () {
        // initializing
        this.server = new Server(this.get("contextpath"), this.error);
        this.countries = new SelectableAwareCollection([
            {name: "Not loaded yet", value: "Not loaded yet"}
        ]);
        this.router = new TriangleRoutingRouter({
            app: this
        });
    },

    start: function () {

        // the order of this methods must be in this specific order, PLEASE DO NOT CHANGE
        this.queryCountries();
        this.loadTerminals();
        this.loadSeaports();

        Backbone.history.start({
            pushState: false,
            root: "/"
        });
    },

    loadTerminals: function () {

        var terminals = new TerminalList();
        this.terminals = terminals;
        this.server.terminals(function (data) {
            terminals.add(data);
        });
        terminals.setDefaultValue();
    },

    loadSeaports: function () {

        var seaports = new SeaportList();
        this.seaports = seaports;

        this.server.getActiveSeaports(function (data) {
            seaports.add(data);
        });
        seaports.setDefaultValue();
    },

    showRequest: function (forcenew) {

        if ((!this.triangleModel) || forcenew) {

            this.triangleModel = new TriangleModel({
                terminals: this.terminals,
                seaports: this.seaports,
                server: this.server,
                countries: this.countries
            });

            this.triangleView = new TriangleView({
                el: "#appcontent",
                model: this.triangleModel
            });
        }

        console.log("Currently " + this.triangleModel.get("points").length + " points in triangle.");
    },

    queryCountries: function () {
        var countries = this.countries;

        this.server.countries(function (countryList) {
            countries.reset(countryList);
        });

        countries.setDefaultByValue("DE");
    },

    /**
     * boolean permanent, if the notify window will stay and not fadeOut by itself
     */
    error: function (msg, permanent) {
        var fadeOut = true;
        var closable = true;
        if (permanent !== undefined) {
            fadeOut = false;
            closable = true;
        }

        // for the ones that aren't closable and don't fade out there is a .close() function.
        $('.notifications').notify({
            type: "error",
            message: {
                text: msg
            },
            fadeOut: {
                enabled: fadeOut
            },
            closable: closable
        }).show();
    }

});
<|endoftext|>


<commit_before>//configure requirejs with application and dependencies.
requirejs.config({
  paths: {
    'Application': './Application',
    'jquery': './vendor/jquery-2.0.3',
    'knockout': './vendor/knockout-3.0.0'
  }
});

//initialize application
require(['Application', 'knockout'], function(ApplicationViewModel, ko) {
  var applicationViewModel = new ApplicationViewModel(json);
  ko.applyBindings(applicationViewModel);
});

//
require(['jquery'], function(jQuery){
  jQuery(function(){

  });
});

<commit_msg>cleanup<commit_after>//configure requirejs with application and dependencies.
requirejs.config({
  paths: {
    'Application': './Application',
    'jquery': './vendor/jquery-2.0.3',
    'knockout': './vendor/knockout-3.0.0'
  }
});

//initialize application
require(['Application', 'knockout'], function(ApplicationViewModel, ko) {
  var applicationViewModel = new ApplicationViewModel(json);
  ko.applyBindings(applicationViewModel);
});
<|endoftext|>


<commit_before>"use strict";

var
    Extensor,
    Location;

(function ($) {

    Extensor.new($, {

        prototype: {

            queryParam: function (param, value) {
                if (param) param = param.trim();

                var
                    self = window.location,
                    uri = location.href,
                    regexp = new RegExp("([?&])" + param + "=[^&#]*", "ig"),

                    results = regexp.exec(uri),
                    match = uri.match(regexp),

                    set = function (param, value) {
                        value = encodeURIComponent(value.toString().trim());
                        var
                            separator;

                        // now just remove duplicate vars, but between a set and a not defined, remove the not defined
                        if (match) {
                            // $n(1..9) is a placeholder flag to method "replace", it indicates which matching groups should be selected to replace
                            uri = uri.replace(regexp, "$1" + param + "=" + value);
                        } else {
                            separator = /\?/.test(uri) ? "&" : "?";
                            uri = location.origin + location.pathname + location.search + separator + param + "=" + value + location.hash;
                        }

                        window.history.pushState("", "", uri);
                    };

                if (value) {
                    set(param, value);
                    return self;
                }
                if (!results) return null;
                if (!results[2]) return "";
                return decodeURIComponent(results.last().replace(/\+/g, " "));
            }

        }

    });

})(Location);
<commit_msg>improves behavior of queryParam<commit_after>"use strict";

var
    Extensor,
    Location;

(function ($) {

    Extensor.new($, {

        prototype: {

            queryParam: function (param, value) {
                if (param) param = param.trim();

                var
                    self = window.location,
                    uri = location.href,
                    // (?=...) select all before ...
                    // [^...] select all except ...
                    regexp = new RegExp("("+ param + "(?=[&#]))|(" + param + "\=[^&])", "ig"),
                    results = regexp.exec(uri),
                    match = uri.match(regexp),

                    set = function (param, value) {
                        value = encodeURIComponent(value.toString().trim());
                        var
                            separator;

                        if (match) {
                            // $n(1..9) is a placeholder flag to method "replace", it indicates which matching groups should be selected to replace
                            uri = uri.replace(regexp, param + "=" + value);
                        } else {
                            separator = /\?/.test(uri) ? "&" : "?";
                            uri = location.origin + location.pathname + location.search + separator + param + "=" + value + location.hash;
                        }

                        window.history.pushState("", "", uri);
                    };

                if (value) {
                    set(param, value);
                    return self;
                }
                if (!results) return null;
                if (!results[2]) return "";
                return decodeURIComponent(results.last().replace(/\+/g, " "));
            }

        }

    });

})(Location);
<|endoftext|>


<commit_before>module.exports = {
  /**
   * @component Tooltip
   * @description When paired with `<OverlayTrigger>`, displays a tooltip on a specified user action
   *
   * @example ```js
   * var OverlayTrigger = require('pui-react-overlay-trigger').OverlayTrigger;
   * var Tooltip = require('pui-react-overlay-tooltip').Tooltip;
   * var MyComponent = React.createClass({
   *   render() {
   *     return (
   *       <OverlayTrigger placement="left" overlay={<Tooltip>This is the tooltip content</Tooltip>}>
   *         <a href="#">tooltip will show on the left on hover</a>
   *       </OverlayTrigger>
   *     );
   *   }
   * });
   * ```
   *
   * @see [Pivotal UI React](http://styleguide.pivotal.io/react.html#tooltips_react)
   */
  Tooltip: require('react-bootstrap').Tooltip
};

/*doc
---
title: Tooltips
name: tooltips_react
categories:
- React
---

npm install pui-react-tooltip --save
npm install pui-react-overlay-trigger --save

Require the subcomponent:

```
var Tooltip = require('pui-react-tooltip').Tooltip;
var OverlayTrigger = require('pui-react-overlay-trigger').OverlayTrigger;
```

Tooltips are used to display extra information on hover.
To make an element show a tooltip, wrap it in an `OverlayTrigger`.
If the `overlay` property passed into the `OverlayTrigger` will be displayed on hover,
this is where the `Tooltip` can be used. The is currently a thin wrapper around
React Bootstrap.

Tooltips are placed using the `placement` property on `OverlayTrigger`, "left", "right", "bottom", "top".

```react_example
<div>
  <p>
    Check out this
    <OverlayTrigger placement="left" overlay={<Tooltip>I should be on the left</Tooltip>}>
      <a href="#" role='tooltip'> tooltip on the left</a>
    </OverlayTrigger>
  </p>
  <p>
    Check out this
    <OverlayTrigger placement="right" overlay={<Tooltip>I should be on the right</Tooltip>}>
      <a href="#" role='tooltip'> tooltip on the right</a>
    </OverlayTrigger>
  </p>
  <p>
    Check out this
    <OverlayTrigger placement="top" overlay={<Tooltip>I should be on the top</Tooltip>}>
      <a href="#" role='tooltip'> tooltip on the top</a>
    </OverlayTrigger>
  </p>
  <p>
    Check out this
    <OverlayTrigger placement="bottom" overlay={<Tooltip>I should be on the bottom</Tooltip>}>
      <a href="#" role='tooltip'> tooltip on the bottom</a>
    </OverlayTrigger>
  </p>
</div>
```
*/
<commit_msg>chore(tooltips): fix accessibility of styleguide example<commit_after>module.exports = {
  /**
   * @component Tooltip
   * @description When paired with `<OverlayTrigger>`, displays a tooltip on a specified user action
   *
   * @example ```js
   * var OverlayTrigger = require('pui-react-overlay-trigger').OverlayTrigger;
   * var Tooltip = require('pui-react-overlay-tooltip').Tooltip;
   * var MyComponent = React.createClass({
   *   render() {
   *     return (
   *       <OverlayTrigger placement="left" overlay={<Tooltip>This is the tooltip content</Tooltip>}>
   *         <a href="#">tooltip will show on the left on hover</a>
   *       </OverlayTrigger>
   *     );
   *   }
   * });
   * ```
   *
   * @see [Pivotal UI React](http://styleguide.pivotal.io/react.html#tooltips_react)
   */
  Tooltip: require('react-bootstrap').Tooltip
};

/*doc
---
title: Tooltips
name: tooltips_react
categories:
- React
---

npm install pui-react-tooltip --save
npm install pui-react-overlay-trigger --save

Require the subcomponent:

```
var Tooltip = require('pui-react-tooltip').Tooltip;
var OverlayTrigger = require('pui-react-overlay-trigger').OverlayTrigger;
```

Tooltips are used to display extra information on hover.
To make an element show a tooltip, wrap it in an `OverlayTrigger`.
If the `overlay` property passed into the `OverlayTrigger` will be displayed on hover,
this is where the `Tooltip` can be used. The is currently a thin wrapper around
React Bootstrap.

Tooltips are placed using the `placement` property on `OverlayTrigger`, "left", "right", "bottom", "top".

```react_example
<div>
  <p>
    Check out this
    <OverlayTrigger placement="left" overlay={<Tooltip>I should be on the left</Tooltip>}>
      <a href="#"> tooltip on the left</a>
    </OverlayTrigger>
  </p>
  <p>
    Check out this
    <OverlayTrigger placement="right" overlay={<Tooltip>I should be on the right</Tooltip>}>
      <a href="#"> tooltip on the right</a>
    </OverlayTrigger>
  </p>
  <p>
    Check out this
    <OverlayTrigger placement="top" overlay={<Tooltip>I should be on the top</Tooltip>}>
      <a href="#"> tooltip on the top</a>
    </OverlayTrigger>
  </p>
  <p>
    Check out this
    <OverlayTrigger placement="bottom" overlay={<Tooltip>I should be on the bottom</Tooltip>}>
      <a href="#"> tooltip on the bottom</a>
    </OverlayTrigger>
  </p>
</div>
```
*/
<|endoftext|>


<commit_before>module.exports = function(config) {
  config.set({
    frameworks: ["jasmine", "karma-typescript"],
    files: [
      { pattern: "./node_modules/es6-object-assign/dist/object-assign-auto.js"},
      { pattern: "src/**/*.ts" }, // *.tsx for React Jsx
    ],
    preprocessors: {
      "src/**/*.ts": ["karma-typescript"], // *.tsx for React Jsx
    },
    reporters: ["progress", "karma-typescript"],
    browsers: ["PhantomJS"]
  });
};
<commit_msg>[project] better karma reporter<commit_after>module.exports = function(config) {
  config.set({
    frameworks: ["jasmine", "karma-typescript"],
    files: [
      { pattern: "./node_modules/es6-object-assign/dist/object-assign-auto.js"},
      { pattern: "src/**/*.ts" }, // *.tsx for React Jsx
    ],
    preprocessors: {
      "src/**/*.ts": ["karma-typescript"], // *.tsx for React Jsx
    },
    reporters: ["spec", "karma-typescript"],
    browsers: ["PhantomJS"],
    karmaTypescriptConfig:{
      reports:{
        html:"./coverage",
        "text-summary":""
      }
    }
  });
};
<|endoftext|>


<commit_before>/*
**  Microkernel -- Microkernel for Server Applications
**  Copyright (c) 2015 Ralf S. Engelschall <rse@engelschall.com>
**
**  Permission is hereby granted, free of charge, to any person obtaining
**  a copy of this software and associated documentation files (the
**  "Software"), to deal in the Software without restriction, including
**  without limitation the rights to use, copy, modify, merge, publish,
**  distribute, sublicense, and/or sell copies of the Software, and to
**  permit persons to whom the Software is furnished to do so, subject to
**  the following conditions:
**
**  The above copyright notice and this permission notice shall be included
**  in all copies or substantial portions of the Software.
**
**  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
**  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
**  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
**  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
**  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
**  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
**  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import toposort from "toposort"

/*  definition of states  */
var states = {
    num2state: [
        { state: "dead",       enter: null,        leave: null       },
        { state: "booted",     enter: "boot",      leave: "shutdown" },
        { state: "configured", enter: "configure", leave: "reset"    },
        { state: "prepared",   enter: "prepare",   leave: "release"  },
        { state: "running",    enter: "start",     leave: "stop"     }
    ],
    state2num: {}
}
var i = 0
states.num2state.forEach((state) => {
    states.state2num[state.state] = i++
})

/*  internal: topolocical sorting of modules  */
var topoSortModules = (mods) => {
    let DAG = {}
    let TAG = {}

    /*  determine all nodes  */
    let nodes = {}
    mods.forEach((mod) => {
        let name  = mod.module.name
        nodes[name] = true
    })

    /*  helper function for taking zero or more strings out of a field  */
    let takeField = (field) => {
        if (typeof field === "object" && field instanceof Array)
            return field
        else if (typeof field === "string")
            return [ field ]
        else
            return []
    }

    /*  helper function: insert edge into DAG  */
    let insertDAG = (list, order) => {
        list.forEach((mod) => {
            let mods = TAG[mod] !== undefined ? TAG[mod] : [ mod ]
            mods.forEach((mod) => {
                let [ before, after ] = order(mod)
                if (nodes[before] === undefined)
                    throw new Error(`unknown module: ${before}`)
                if (nodes[after] === undefined)
                    throw new Error(`unknown module: ${after}`)
                if (DAG[before] === undefined)
                    DAG[before] = {}
                DAG[before][after] = true
            })
        })
    }

    /*  iterate over all modules  */
    mods.forEach((mod) => {
        /*  take information of module  */
        let name   = mod.module.name
        let tag    = takeField(mod.module.tag)
        let before = takeField(mod.module.before)
        let after  = takeField(mod.module.after)

        /*  remember mapping of tag to module  */
        tag.forEach((tag) => {
            if (TAG[tag] === undefined)
                TAG[tag] = []
            TAG[tag].push(name)
        })

        /*  insert all "after" dependencies into DAG
            (as standard "after" dependencies)  */
        insertDAG(after,  (mod) => [ name, mod ])

        /*  insert all "before" dependencies into DAG
            (as inverse "after" dependencies)  */
        insertDAG(before, (mod) => [ mod, name ])
    })

    /*  determine resulting graph edges  */
    let edges = []
    Object.keys(DAG).forEach((before) => {
        Object.keys(DAG[before]).forEach((after) => {
            edges.push([ before, after ])
        })
    })

    /*  perform a topological sorting  */
    return toposort.array(Object.keys(nodes), edges).reverse()
}

/*  the mixin class  */
export default class MicrokernelState {
    /*  initialize the microkernel instance  */
    initializer () {
        this._state = "dead"
    }

    /*  retrieve current state or request transition to new state  */
    state (stateNew) {
        /*  special case: retrieve state only  */
        let stateOld = this._state
        if (typeof stateNew !== "string")
            return stateOld

        /*  special case: transit to same state  */
        if (stateNew === stateOld)
            return Promise.resolve(stateNew)

        /*  sanity check new state  */
        if (states.state2num[stateNew] === undefined)
            throw new Error(`state: invalid new state: ${stateNew}`)

        /*  perform deferred topological sorting of modules  */
        if (this.modOrder === null) {
            this.modOrder = topoSortModules(this.mod)
            this.publish("microkernel:state:toposort", this.modOrder, this.mod)
        }

        /*  create outer promise chain  */
        let promise = new Promise ((resolve, reject) => {
            /*  determine indexes of states  */
            let stateOldIdx = states.state2num[stateOld]
            let stateNewIdx = states.state2num[stateNew]

            /*  create inner promise chain  */
            let seq = Promise.resolve()

            /*  publish internal event (for use by an application)  */
            let publishEvent = (when, from, to, method) => {
                seq = seq.then(() => {
                    this.publish(
                        `microkernel:state:transit:${when}`,
                        states.num2state[from].state,
                        states.num2state[to].state,
                        method
                    )
                })
            }

            /*  helper function for transitioning  */
            let transit = (stateFrom, stateTo, methodType, reverse, step) => {
                while (stateFrom !== stateTo) {
                    /*  determine method to call  */
                    let methodName = states.num2state[
                        methodType === "enter" ? stateFrom + 1 : stateFrom
                    ][methodType]
                    publishEvent("before", stateFrom, stateFrom + step, methodName)

                    /*  determine modules to call (in expected order)  */
                    let names = this.modOrder
                    if (reverse)
                       names = names.reverse()

                    /*  call method on all modules  */
                    names.forEach((name) => {
                        let mod = this.name2mod.get(name)
                        if (typeof mod[methodName] === "function")
                            seq = seq.then(() => mod[methodName].call(mod, this))
                    })

                    /*  go to new state  */
                    stateFrom += step;
                    this._state = states.num2state[stateFrom].state
                    publishEvent("after", stateFrom - step, stateFrom, methodName)
                }
            }

            /*  determine whether we perform upgrade or downgrade transition  */
            if (stateOldIdx < stateNewIdx)
                transit(stateOldIdx, stateNewIdx, "enter", false,  1) /*  upgrade    */
            else if (stateOldIdx > stateNewIdx)
                transit(stateOldIdx, stateNewIdx, "leave", true,  -1) /*  downgrade  */

            /*  finally let inner promise chain either resolve or reject outer promise chain  */
            seq.then(() => {
                resolve(stateNew)
            }, (err) => {
                reject(err)
            })
        })

        /*  reset module order in case we reached the dead state (again)  */
        promise = promise.then((stateNew) => {
            if (stateNew === "dead")
                this.modOrder = null
            return stateNew
        })

        return promise
    }

    /*  provide convenience methods for state transitions  */
    boot ()      { return this.state("booted")     }
    configure () { return this.state("configured") }
    prepare ()   { return this.state("prepared")   }
    start ()     { return this.state("running")    }
    stop ()      { return this.state("prepared")   }
    release ()   { return this.state("configured") }
    reset ()     { return this.state("booted")     }
    shutdown ()  { return this.state("dead")       }
}

<commit_msg>move invariant out of loop<commit_after>/*
**  Microkernel -- Microkernel for Server Applications
**  Copyright (c) 2015 Ralf S. Engelschall <rse@engelschall.com>
**
**  Permission is hereby granted, free of charge, to any person obtaining
**  a copy of this software and associated documentation files (the
**  "Software"), to deal in the Software without restriction, including
**  without limitation the rights to use, copy, modify, merge, publish,
**  distribute, sublicense, and/or sell copies of the Software, and to
**  permit persons to whom the Software is furnished to do so, subject to
**  the following conditions:
**
**  The above copyright notice and this permission notice shall be included
**  in all copies or substantial portions of the Software.
**
**  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
**  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
**  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
**  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
**  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
**  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
**  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import toposort from "toposort"

/*  definition of states  */
var states = {
    num2state: [
        { state: "dead",       enter: null,        leave: null       },
        { state: "booted",     enter: "boot",      leave: "shutdown" },
        { state: "configured", enter: "configure", leave: "reset"    },
        { state: "prepared",   enter: "prepare",   leave: "release"  },
        { state: "running",    enter: "start",     leave: "stop"     }
    ],
    state2num: {}
}
var i = 0
states.num2state.forEach((state) => {
    states.state2num[state.state] = i++
})

/*  internal: topolocical sorting of modules  */
var topoSortModules = (mods) => {
    let DAG = {}
    let TAG = {}

    /*  determine all nodes  */
    let nodes = {}
    mods.forEach((mod) => {
        let name  = mod.module.name
        nodes[name] = true
    })

    /*  helper function for taking zero or more strings out of a field  */
    let takeField = (field) => {
        if (typeof field === "object" && field instanceof Array)
            return field
        else if (typeof field === "string")
            return [ field ]
        else
            return []
    }

    /*  helper function: insert edge into DAG  */
    let insertDAG = (list, order) => {
        list.forEach((mod) => {
            let mods = TAG[mod] !== undefined ? TAG[mod] : [ mod ]
            mods.forEach((mod) => {
                let [ before, after ] = order(mod)
                if (nodes[before] === undefined)
                    throw new Error(`unknown module: ${before}`)
                if (nodes[after] === undefined)
                    throw new Error(`unknown module: ${after}`)
                if (DAG[before] === undefined)
                    DAG[before] = {}
                DAG[before][after] = true
            })
        })
    }

    /*  iterate over all modules  */
    mods.forEach((mod) => {
        /*  take information of module  */
        let name   = mod.module.name
        let tag    = takeField(mod.module.tag)
        let before = takeField(mod.module.before)
        let after  = takeField(mod.module.after)

        /*  remember mapping of tag to module  */
        tag.forEach((tag) => {
            if (TAG[tag] === undefined)
                TAG[tag] = []
            TAG[tag].push(name)
        })

        /*  insert all "after" dependencies into DAG
            (as standard "after" dependencies)  */
        insertDAG(after,  (mod) => [ name, mod ])

        /*  insert all "before" dependencies into DAG
            (as inverse "after" dependencies)  */
        insertDAG(before, (mod) => [ mod, name ])
    })

    /*  determine resulting graph edges  */
    let edges = []
    Object.keys(DAG).forEach((before) => {
        Object.keys(DAG[before]).forEach((after) => {
            edges.push([ before, after ])
        })
    })

    /*  perform a topological sorting  */
    return toposort.array(Object.keys(nodes), edges).reverse()
}

/*  the mixin class  */
export default class MicrokernelState {
    /*  initialize the microkernel instance  */
    initializer () {
        this._state = "dead"
    }

    /*  retrieve current state or request transition to new state  */
    state (stateNew) {
        /*  special case: retrieve state only  */
        let stateOld = this._state
        if (typeof stateNew !== "string")
            return stateOld

        /*  special case: transit to same state  */
        if (stateNew === stateOld)
            return Promise.resolve(stateNew)

        /*  sanity check new state  */
        if (states.state2num[stateNew] === undefined)
            throw new Error(`state: invalid new state: ${stateNew}`)

        /*  perform deferred topological sorting of modules  */
        if (this.modOrder === null) {
            this.modOrder = topoSortModules(this.mod)
            this.publish("microkernel:state:toposort", this.modOrder, this.mod)
        }

        /*  create outer promise chain  */
        let promise = new Promise ((resolve, reject) => {
            /*  determine indexes of states  */
            let stateOldIdx = states.state2num[stateOld]
            let stateNewIdx = states.state2num[stateNew]

            /*  create inner promise chain  */
            let seq = Promise.resolve()

            /*  publish internal event (for use by an application)  */
            let publishEvent = (when, from, to, method) => {
                seq = seq.then(() => {
                    this.publish(
                        `microkernel:state:transit:${when}`,
                        states.num2state[from].state,
                        states.num2state[to].state,
                        method
                    )
                })
            }

            /*  helper function for transitioning  */
            let transit = (stateFrom, stateTo, methodType, reverse, step) => {
                /*  determine modules to call (in expected order)  */
                let names = this.modOrder
                if (reverse)
                   names = names.reverse()

                /*  loop until state is reached  */
                while (stateFrom !== stateTo) {
                    /*  determine method to call  */
                    let methodName = states.num2state[
                        methodType === "enter" ? stateFrom + 1 : stateFrom
                    ][methodType]
                    publishEvent("before", stateFrom, stateFrom + step, methodName)

                    /*  call method on all modules  */
                    names.forEach((name) => {
                        let mod = this.name2mod.get(name)
                        if (typeof mod[methodName] === "function")
                            seq = seq.then(() => mod[methodName].call(mod, this))
                    })

                    /*  go to new state  */
                    stateFrom += step;
                    this._state = states.num2state[stateFrom].state
                    publishEvent("after", stateFrom - step, stateFrom, methodName)
                }
            }

            /*  determine whether we perform upgrade or downgrade transition  */
            if (stateOldIdx < stateNewIdx)
                transit(stateOldIdx, stateNewIdx, "enter", false,  1) /*  upgrade    */
            else if (stateOldIdx > stateNewIdx)
                transit(stateOldIdx, stateNewIdx, "leave", true,  -1) /*  downgrade  */

            /*  finally let inner promise chain either resolve or reject outer promise chain  */
            seq.then(() => {
                resolve(stateNew)
            }, (err) => {
                reject(err)
            })
        })

        /*  reset module order in case we reached the dead state (again)  */
        promise = promise.then((stateNew) => {
            if (stateNew === "dead")
                this.modOrder = null
            return stateNew
        })

        return promise
    }

    /*  provide convenience methods for state transitions  */
    boot ()      { return this.state("booted")     }
    configure () { return this.state("configured") }
    prepare ()   { return this.state("prepared")   }
    start ()     { return this.state("running")    }
    stop ()      { return this.state("prepared")   }
    release ()   { return this.state("configured") }
    reset ()     { return this.state("booted")     }
    shutdown ()  { return this.state("dead")       }
}

<|endoftext|>


<commit_before>/**
  RemoteCall
  @description function that makes the remote call using the fetch polyfill,
    running a series of provided lifecycle hooks
  @exports @default {function} callRemoteResource
**/

import isomorphicFetch from 'isomorphic-fetch';

/**
  @private
  @name callRemoteResource
  @desc makes a call using the fetch API w/ the given request data & hooks
  @param {object} request
  @param {string} request.uri
  @param {string} request.method
  @param {object} request.headers
  @param {string} request.body
  @param {object} request.requestOps arbitrary options passed to fetch
  @param {obejct} statusActions obj keyed by codes w/ fn values
  @param {object} hooks the lifecycle hooks evaluated at various times
  @param {function} hooks.onBeforeCall
  @param {function} hooks.onCallSuccess(data, response)
  @param {function} hooks.onCallFailure(statusCode, data, response)
  @param {function|null} userFetch a custom fetch implementation to use
  @returns {Promise<Response|Error>}
**/
export default function callRemoteResource(
  request, statusActions, hooks, userFetch
) {
  // order of fetch priority:
  // user supplied fetch -> global fetch -> isomorphic-fetch
  const callFetch = userFetch || fetch || isomorphicFetch;
  const { uri, method, headers, body, requestOpts } = request;
  const fetchReq = Object.assign({ method, headers, body }, requestOpts);

  hooks.onBeforeCall();

  // NOTE: this expects to be caught in the surrounding block
  return callFetch(uri, fetchReq)
    .then(response => {
      // check to see if we have a status action to run
      const statusAction = statusActions[response.status];
      if (statusAction) return statusAction();

      // extract the data, being careful to catch JSON parsing errors if it's
      // empty
      return Promise.all([
        response, response.json().catch(() => Promise.resolve({}))
      ]);
    }).then(([ response, data ]) => {
      if (!response.ok)
        hooks.onCallFailure(response.status, data || {}, response);
      else
        hooks.onCallSuccess(data || {}, response);
    });
}
<commit_msg>Fix bug where status actions short circuiting breaks the following block<commit_after>/**
  RemoteCall
  @description function that makes the remote call using the fetch polyfill,
    running a series of provided lifecycle hooks
  @exports @default {function} callRemoteResource
**/

import isomorphicFetch from 'isomorphic-fetch';

/**
  @private
  @name callRemoteResource
  @desc makes a call using the fetch API w/ the given request data & hooks
  @param {object} request
  @param {string} request.uri
  @param {string} request.method
  @param {object} request.headers
  @param {string} request.body
  @param {object} request.requestOps arbitrary options passed to fetch
  @param {obejct} statusActions obj keyed by codes w/ fn values
  @param {object} hooks the lifecycle hooks evaluated at various times
  @param {function} hooks.onBeforeCall
  @param {function} hooks.onCallSuccess(data, response)
  @param {function} hooks.onCallFailure(statusCode, data, response)
  @param {function|null} userFetch a custom fetch implementation to use
  @returns {Promise<Response|Error>}
**/
export default function callRemoteResource(
  request, statusActions, hooks, userFetch
) {
  // order of fetch priority:
  // user supplied fetch -> global fetch -> isomorphic-fetch
  const callFetch = userFetch || fetch || isomorphicFetch;
  const { uri, method, headers, body, requestOpts } = request;
  const fetchReq = Object.assign({ method, headers, body }, requestOpts);

  hooks.onBeforeCall();

  // NOTE: this expects to be caught in the surrounding block
  return callFetch(uri, fetchReq)
    .then(response => {
      if (statusActions[response.status]) {
        statusActions[response.status]();
        return [];
      }

      // extract the data, catching JSON parsing errors if it's empty
      return Promise.all([
        response, response.json().catch(() => Promise.resolve({}))
      ]);
    }).then(([ response, data ]) => {
      if (!response) // we've been handled by a status action
        return;

      if (!response.ok)
        hooks.onCallFailure(response.status, data || {}, response);
      else
        hooks.onCallSuccess(data || {}, response);
    });
}
<|endoftext|>


<commit_before>describe("chorus.views.DatasetEditChorusView", function() {
    beforeEach(function() {

        this.dataset = fixtures.datasetChorusView();
        this.view = new chorus.views.DatasetEditChorusView({ model: this.dataset });
        $("#jasmine_content").append(this.view.el);
        this.clock = sinon.useFakeTimers();

        // in IE8, we can't 'select' a textrange whose textarea is not on the DOM
        if ($.browser.msie) {
            spyOn(window.TextRange.prototype, 'select');
        }
        spyOn(CodeMirror, "fromTextArea").andCallThrough();
    })

    context("without defer stubbed out", function() {
        it("defers call to CodeMirror", function() {
            this.view.render();
            expect(CodeMirror.fromTextArea).not.toHaveBeenCalled();
            this.clock.tick(1000);
            expect(CodeMirror.fromTextArea).toHaveBeenCalled();
        });
    });

    context("with defer stubbed out", function() {
        beforeEach(function() {
            stubDefer()
        });

        describe("#render", function() {
            beforeEach(function() {
                this.view.render();
            });

            context("when deferred CodeMirror creation happens twice in one dom render", function() {
                beforeEach(function() {
                    var deferredCodeMirror = _.defer.calls[0].args[0];
                    deferredCodeMirror();
                });

                it("only calls CodeMirror once", function() {
                    expect(CodeMirror.fromTextArea.callCount).toBe(1);
                });
            });

            describe("#editable", function() {
                it("set the file to be editable", function() {
                    this.view.model.canEdit = function() {
                        return true;
                    }
                    this.view.render();
                    expect(this.view.editor.getOption("readOnly")).toBe(false);
                });
                it("set the file to be not editable", function() {
                    this.view.model.canEdit = function() {
                        return false;
                    }
                    this.view.render();
                    expect(this.view.editor.getOption("readOnly")).toBeFalsy();
                });
            });

            it("displays line numbers", function() {
                expect(this.view.editor.getOption("lineNumbers")).toBe(true);
            });

            it("displays the text file content", function() {
                expect(this.view.editor.getValue()).toBe(this.dataset.get("query"));
            });

            it("uses the 'text/x-sql' mode for plain text files", function() {
                expect(this.view.editor.getOption("mode")).toBe("text/x-sql");
            });
        });

        describe("#saveChanges", function() {
            beforeEach(function() {
                this.view.$(".text_editor").text("select * from table_abc");
                this.view.render();

                spyOn(this.view.model, "save");
//                this.view.editor.setCursor(0, 23);
                this.view.trigger("dataset:saveEdit");
                this.clock.tick(1000);
            });

            it("sets cursor at the correct position", function() {
//                expect(this.view.editor.getCursor().ch).toBe(23);
                expect(this.view.editor.getCursor().line).toBe(0);
            });

            it("saves the model", function() {
                expect(this.view.model.save).toHaveBeenCalled();
            });

            it("sets the query in the model", function() {
//                expect(this.view.model.get("query")).toBe("select * from table_abc");
            })
        });
    });
});
<commit_msg>fix test for edit chorus view<commit_after>describe("chorus.views.DatasetEditChorusView", function() {
    beforeEach(function() {

        this.dataset = fixtures.datasetChorusView();
        this.view = new chorus.views.DatasetEditChorusView({ model: this.dataset });
        $("#jasmine_content").append(this.view.el);
        this.clock = sinon.useFakeTimers();

        // in IE8, we can't 'select' a textrange whose textarea is not on the DOM
        if ($.browser.msie) {
            spyOn(window.TextRange.prototype, 'select');
        }
        spyOn(CodeMirror, "fromTextArea").andCallThrough();
    })

    context("without defer stubbed out", function() {
        it("defers call to CodeMirror", function() {
            this.view.render();
            expect(CodeMirror.fromTextArea).not.toHaveBeenCalled();
            this.clock.tick(1000);
            expect(CodeMirror.fromTextArea).toHaveBeenCalled();
        });
    });

    context("with defer stubbed out", function() {
        beforeEach(function() {
            stubDefer()
        });

        describe("#render", function() {
            beforeEach(function() {
                this.view.render();
            });

            context("when deferred CodeMirror creation happens twice in one dom render", function() {
                beforeEach(function() {
                    var deferredCodeMirror = _.defer.calls[0].args[0];
                    deferredCodeMirror();
                });

                it("only calls CodeMirror once", function() {
                    expect(CodeMirror.fromTextArea.callCount).toBe(1);
                });
            });

            it("displays line numbers", function() {
                expect(this.view.editor.getOption("lineNumbers")).toBe(true);
            });

            it("displays the text file content", function() {
                expect(this.view.editor.getValue()).toBe(this.dataset.get("query"));
            });

            it("uses the 'text/x-sql' mode for plain text files", function() {
                expect(this.view.editor.getOption("mode")).toBe("text/x-sql");
            });
        });

        describe("#saveChanges", function() {
            beforeEach(function() {
                this.view.render();

                this.view.editor.setValue("select * from table_abc");
                spyOn(this.view.model, "save");
                this.view.trigger("dataset:saveEdit");
                this.clock.tick(1000);
            });

            it("saves the model", function() {
                expect(this.view.model.save).toHaveBeenCalled();
            });

            it("sets the query in the model", function() {
                expect(this.view.model.get("query")).toBe("select * from table_abc");
            })
        });
    });
});
<|endoftext|>