<commit_before>var chromeDgram = require('chrome-dgram')
var dgram = require('dgram')
var SrflxSocket = require('../src/srflx_socket')
var winston = require('winston')

var chai = require('chai')
var chaiAsPromised = require('chai-as-promised')
var expect = chai.expect
chai.use(chaiAsPromised)
chai.should()

var argv = require('yargs')
  .usage('Usage: $0 [params]')
  .demand('a')
  .default('a', 'stun.l.google.com')
  .alias('a', 'addr')
  .nargs('a', 1)
  .describe('a', 'STUN server address')
  .demand('p')
  .default('p', '19302')
  .alias('p', 'port')
  .nargs('p', 1)
  .describe('p', 'STUN server port')
  .default('l', 'debug')
  .choices('l', ['info', 'debug', 'warn', 'error', 'verbose', 'silly'])
  .alias('l', 'log')
  .nargs('l', 1)
  .describe('l', 'Log level')
  .help('h')
  .alias('h', 'help')
  .argv

var testAddr = argv.addr
var testPort = argv.port
winston.level = argv.log

describe('#STUN operations', function () {
  this.timeout(5000)

  it('should execute STUN bind operation (using promises)', function () {
    var socket = new SrflxSocket(testAddr, testPort)
    return socket.listenP()
      .then(function (localAddress) {
        return socket.bindP()
      })
      .then(function (mappedAddress) {
        expect(mappedAddress).not.to.be.undefined
        expect(mappedAddress).to.have.property('address')
        expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      })
  })

  it('should execute STUN bind operation (using callbacks)', function (done) {
    var socket = new SrflxSocket(testAddr, testPort)
    // if something fails
    var onFailure = function (error) {
      done(error)
    }
    // once some test results are available
    var onSuccess = function (mappedAddress) {
      expect(mappedAddress).not.to.be.undefined
      expect(mappedAddress).to.have.property('address')
      expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      socket.close()
      done()
    }
    // if socket is listening
    var onListening = function (localAddress) {
      socket.bind(onSuccess, onFailure)
    }
    socket.listen({},
      onListening, // on ready
      onFailure // on failure
    )
  })

  it('should execute STUN bind operation using a specific dgram socket (using promises)', function () {
    var udpSocket = dgram.createSocket('udp4')
    var socket = new SrflxSocket(testAddr, testPort, udpSocket)
    return socket.listenP()
      .then(function (localAddress) {
        return socket.bindP()
      })
      .then(function (mappedAddress) {
        expect(mappedAddress).not.to.be.undefined
        expect(mappedAddress).to.have.property('address')
        expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      })
  })

  it('should execute STUN bind operation using a specific chrome-dgram socket (using promises)', function () {
    var udpSocket = chromeDgram.createSocket('udp4')
    var socket = new SrflxSocket(testAddr, testPort, udpSocket)
    return socket.listenP()
      .then(function (localAddress) {
        return socket.bindP()
      })
      .then(function (mappedAddress) {
        expect(mappedAddress).not.to.be.undefined
        expect(mappedAddress).to.have.property('address')
        expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      })
  })

  // it('should receive messages that are sent to a srflx address', function (done) {
  //   var testData = 'hello there'
  //   var testRuns = 1
  //   var messagesReceived = 0
  //
  //   var socketAlice = new SrflxSocket(testAddr, testPort)
  //   var socketBob = new SrflxSocket(testAddr, testPort)
  //   var addressAlice, addressBob
  //
  //   // subscribe to incoming messages
  //   socketBob.on('message', function (msg, rinfo) {
  //     expect(msg.toString()).to.equal(testData)
  //     winston.debug('[libstun] receiving test message ' + msg)
  //     messagesReceived++
  //     if (messagesReceived === testRuns) {
  //       socketBob.close()
  //       done()
  //     }
  //   })
  //
  //   // open alice's socket ...
  //   socketAlice.listenP()
  //     .then(function (localAddress) {
  //       // and determine her public address (not really needed for this test, but it also doesn't hurt ...)
  //       return socketAlice.bindP()
  //     })
  //     .then(function (mappedAddress) {
  //       addressAlice = mappedAddress
  //       winston.debug("[libstun] alice's srflx address = " + addressAlice.address + ':' + addressAlice.port)
  //       // open bob's socket ...
  //       return socketBob.listenP()
  //     })
  //     .then(function (localAddress) {
  //       // and determine his public address
  //       return socketBob.bindP()
  //     })
  //     .then(function (mappedAddress) {
  //       addressBob = mappedAddress
  //       winston.debug("[libstun] bob's srflx address = " + addressBob.address + ':' + addressBob.port)
  //       // send test message n times
  //       for (var i = 0; i < testRuns; i++) {
  //         socketAlice.sendData(
  //           testData,
  //           addressBob.address,
  //           addressBob.port,
  //           function () { // on success
  //             winston.debug('[libstun] test message sent to ' + addressBob.address + ':' + addressBob.port)
  //           },
  //           function (error) { // on failure
  //             done(error)
  //           }
  //         )
  //       }
  //     })
  // })
})
<commit_msg>removing chrome-dgram test<commit_after>var dgram = require('dgram')
var SrflxSocket = require('../src/srflx_socket')
var winston = require('winston')

var chai = require('chai')
var chaiAsPromised = require('chai-as-promised')
var expect = chai.expect
chai.use(chaiAsPromised)
chai.should()

var argv = require('yargs')
  .usage('Usage: $0 [params]')
  .demand('a')
  .default('a', 'stun.l.google.com')
  .alias('a', 'addr')
  .nargs('a', 1)
  .describe('a', 'STUN server address')
  .demand('p')
  .default('p', '19302')
  .alias('p', 'port')
  .nargs('p', 1)
  .describe('p', 'STUN server port')
  .default('l', 'debug')
  .choices('l', ['info', 'debug', 'warn', 'error', 'verbose', 'silly'])
  .alias('l', 'log')
  .nargs('l', 1)
  .describe('l', 'Log level')
  .help('h')
  .alias('h', 'help')
  .argv

var testAddr = argv.addr
var testPort = argv.port
winston.level = argv.log

describe('#STUN operations', function () {
  this.timeout(5000)

  it('should execute STUN bind operation (using promises)', function () {
    var socket = new SrflxSocket(testAddr, testPort)
    return socket.listenP()
      .then(function (localAddress) {
        return socket.bindP()
      })
      .then(function (mappedAddress) {
        expect(mappedAddress).not.to.be.undefined
        expect(mappedAddress).to.have.property('address')
        expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      })
  })

  it('should execute STUN bind operation (using callbacks)', function (done) {
    var socket = new SrflxSocket(testAddr, testPort)
    // if something fails
    var onFailure = function (error) {
      done(error)
    }
    // once some test results are available
    var onSuccess = function (mappedAddress) {
      expect(mappedAddress).not.to.be.undefined
      expect(mappedAddress).to.have.property('address')
      expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      socket.close()
      done()
    }
    // if socket is listening
    var onListening = function (localAddress) {
      socket.bind(onSuccess, onFailure)
    }
    socket.listen({},
      onListening, // on ready
      onFailure // on failure
    )
  })

  it('should execute STUN bind operation using a specific dgram socket (using promises)', function () {
    var udpSocket = dgram.createSocket('udp4')
    var socket = new SrflxSocket(testAddr, testPort, udpSocket)
    return socket.listenP()
      .then(function (localAddress) {
        return socket.bindP()
      })
      .then(function (mappedAddress) {
        expect(mappedAddress).not.to.be.undefined
        expect(mappedAddress).to.have.property('address')
        expect(mappedAddress).to.have.property('port')
      // expect(mappedAddress.address).to.equal(testGW)
      })
  })

  // it('should receive messages that are sent to a srflx address', function (done) {
  //   var testData = 'hello there'
  //   var testRuns = 1
  //   var messagesReceived = 0
  //
  //   var socketAlice = new SrflxSocket(testAddr, testPort)
  //   var socketBob = new SrflxSocket(testAddr, testPort)
  //   var addressAlice, addressBob
  //
  //   // subscribe to incoming messages
  //   socketBob.on('message', function (msg, rinfo) {
  //     expect(msg.toString()).to.equal(testData)
  //     winston.debug('[libstun] receiving test message ' + msg)
  //     messagesReceived++
  //     if (messagesReceived === testRuns) {
  //       socketBob.close()
  //       done()
  //     }
  //   })
  //
  //   // open alice's socket ...
  //   socketAlice.listenP()
  //     .then(function (localAddress) {
  //       // and determine her public address (not really needed for this test, but it also doesn't hurt ...)
  //       return socketAlice.bindP()
  //     })
  //     .then(function (mappedAddress) {
  //       addressAlice = mappedAddress
  //       winston.debug("[libstun] alice's srflx address = " + addressAlice.address + ':' + addressAlice.port)
  //       // open bob's socket ...
  //       return socketBob.listenP()
  //     })
  //     .then(function (localAddress) {
  //       // and determine his public address
  //       return socketBob.bindP()
  //     })
  //     .then(function (mappedAddress) {
  //       addressBob = mappedAddress
  //       winston.debug("[libstun] bob's srflx address = " + addressBob.address + ':' + addressBob.port)
  //       // send test message n times
  //       for (var i = 0; i < testRuns; i++) {
  //         socketAlice.sendData(
  //           testData,
  //           addressBob.address,
  //           addressBob.port,
  //           function () { // on success
  //             winston.debug('[libstun] test message sent to ' + addressBob.address + ':' + addressBob.port)
  //           },
  //           function (error) { // on failure
  //             done(error)
  //           }
  //         )
  //       }
  //     })
  // })
})
<|endoftext|>


<commit_before>/*
  backgrid-text-cell
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong
  Licensed under the MIT @license.
*/

(function (window, $, _, Backbone, Backgrid)  {

  /**
     Renders a form with a text area and a save button in a modal dialog.

     @class Backgrid.Extension.TextareaEditor
     @extends Backgrid.InputCellEditor
   */
  var TextareaEditor = Backgrid.Extension.TextareaEditor = Backgrid.CellEditor.extend({

    /** @property */
    tagName: "div",

    /** @property */
    className: "modal hide fade",

    /** @property {function(Object, ?Object=): string} template */
    template: _.template('<form>
<button type="button" class="close" data-dismiss="modal">×</button>
<%- column.get("label") %>

<textarea cols="<%= cols %>" rows="<%= rows %>"><%- content %></textarea>

<input class="btn" type="submit" value="Save"/>
</form>'),

    /** @property */
    cols: 80,

    /** @property */
    rows: 10,

    /** @property */
    events: {
      "submit": "save",
      "hide": "cancel",
      "hidden": "close"
    },

    /**
       @property {Object} modalOptions The options passed to Bootstrap's modal
       plugin.
    */
    modalOptions: {
      backdrop: false
    },

    /**
       Renders a modal form dialog with a textarea, submit button and a close button.
    */
    render: function () {
      this.$el.html($(this.template({
        column: this.column,
        cols: this.cols,
        rows: this.rows,
        content: this.formatter.fromRaw(this.model.get(this.column.get("name")))
      })));

      this.$el.modal(this.modalOptions);

      return this;
    },

    /**
       Event handler. Saves the text in the text area to the model.

       Triggers a Backbone `error` event if the value cannot be
       converted. Classes listening to the `error` event, usually the Cell
       classes, should respond appropriately, usually by rendering some kind of
       error feedback.

       @param {Event} e
    */
    save: function (e) {
      if (e) e.preventDefault();

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      if (_.isUndefined(content) ||
          !this.model.set(this.column.get("name"), content, {validate: true})) {
        this.trigger("error");
      }
      else {
        this.$el.modal("hide");
      }
    },

    /**
       Event handler. Revert the text in the model after asking for confirmation
       if dirty, otherwise just close the editor.

       @param {Event} e
    */
    cancel: function (e) {

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      // Dirty
      if (content !== this.model.get(this.column.get("name")).replace(/\r/g, '') &&
          window.confirm("Would you like to save your changes?")) {
        e.preventDefault();
        e.stopPropagation();
        return this.save();
      }
    },

    /**
       Triggers a `done` event after the modal is hidden.
     */
    close: function () {
      this.trigger("done");
    }

  });

  /**
     TextCell is a string cell type that renders a form with a text area in a
     modal dialog instead of an input box editor. It is best suited for entering
     a large body of text.

     @class Backgrid.Extension.TextCell
     @extends Backgrid.StringCell
  */
  var TextCell = Backgrid.Extension.TextCell = Backgrid.StringCell.extend({

    /** @property */
    className: "text-cell",

    /** @property  */
    editor: TextareaEditor

  });

}(window, jQuery, _, Backbone, Backgrid));
<commit_msg>Fix TextareaEditor extends jsdoc<commit_after>/*
  backgrid-text-cell
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong
  Licensed under the MIT @license.
*/

(function (window, $, _, Backbone, Backgrid)  {

  /**
     Renders a form with a text area and a save button in a modal dialog.

     @class Backgrid.Extension.TextareaEditor
     @extends Backgrid.CellEditor
   */
  var TextareaEditor = Backgrid.Extension.TextareaEditor = Backgrid.CellEditor.extend({

    /** @property */
    tagName: "div",

    /** @property */
    className: "modal hide fade",

    /** @property {function(Object, ?Object=): string} template */
    template: _.template('<form>
<button type="button" class="close" data-dismiss="modal">×</button>
<%- column.get("label") %>

<textarea cols="<%= cols %>" rows="<%= rows %>"><%- content %></textarea>

<input class="btn" type="submit" value="Save"/>
</form>'),

    /** @property */
    cols: 80,

    /** @property */
    rows: 10,

    /** @property */
    events: {
      "submit": "save",
      "hide": "cancel",
      "hidden": "close"
    },

    /**
       @property {Object} modalOptions The options passed to Bootstrap's modal
       plugin.
    */
    modalOptions: {
      backdrop: false
    },

    /**
       Renders a modal form dialog with a textarea, submit button and a close button.
    */
    render: function () {
      this.$el.html($(this.template({
        column: this.column,
        cols: this.cols,
        rows: this.rows,
        content: this.formatter.fromRaw(this.model.get(this.column.get("name")))
      })));

      this.$el.modal(this.modalOptions);

      return this;
    },

    /**
       Event handler. Saves the text in the text area to the model.

       Triggers a Backbone `error` event if the value cannot be
       converted. Classes listening to the `error` event, usually the Cell
       classes, should respond appropriately, usually by rendering some kind of
       error feedback.

       @param {Event} e
    */
    save: function (e) {
      if (e) e.preventDefault();

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      if (_.isUndefined(content) ||
          !this.model.set(this.column.get("name"), content, {validate: true})) {
        this.trigger("error");
      }
      else {
        this.$el.modal("hide");
      }
    },

    /**
       Event handler. Revert the text in the model after asking for confirmation
       if dirty, otherwise just close the editor.

       @param {Event} e
    */
    cancel: function (e) {

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      // Dirty
      if (content !== this.model.get(this.column.get("name")).replace(/\r/g, '') &&
          window.confirm("Would you like to save your changes?")) {
        e.preventDefault();
        e.stopPropagation();
        return this.save();
      }
    },

    /**
       Triggers a `done` event after the modal is hidden.
     */
    close: function () {
      this.trigger("done");
    }

  });

  /**
     TextCell is a string cell type that renders a form with a text area in a
     modal dialog instead of an input box editor. It is best suited for entering
     a large body of text.

     @class Backgrid.Extension.TextCell
     @extends Backgrid.StringCell
  */
  var TextCell = Backgrid.Extension.TextCell = Backgrid.StringCell.extend({

    /** @property */
    className: "text-cell",

    /** @property  */
    editor: TextareaEditor

  });

}(window, jQuery, _, Backbone, Backgrid));
<|endoftext|>


<commit_before>const os = require(`os`)
const path = require(`path`)
const Store = require(`./store`)
const fetch = require(`node-fetch`)
const Configstore = require(`configstore`)
const { ensureDirSync } = require(`fs-extra`)

const isTruthy = require(`./is-truthy`)

/* The events data collection is a spooled process that
 * buffers events to a local fs based buffer
 * which then is asynchronously flushed to the server.
 * This both increases the fault tolerancy and allows collection
 * to continue even when working offline.
 */
module.exports = class EventStorage {
  constructor() {
    try {
      this.config = new Configstore(`gatsby`, {}, { globalConfigPath: true })
    } catch (e) {
      // This should never happen
      this.config = {
        get: key => this.config[key],
        set: (key, value) => (this.config[key] = value),
        all: this.config,
        path: path.join(os.tmpdir(), `gatsby`),
        "telemetry.enabled": true,
        "telemetry.machineId": `not-a-machine-id`,
      }
    }

    const baseDir = path.dirname(this.config.path)

    try {
      ensureDirSync(baseDir)
    } catch (e) {
      // TODO: Log this event
    }

    this.store = new Store(baseDir)
    this.verbose = isTruthy(process.env.GATSBY_TELEMETRY_VERBOSE)
    this.debugEvents = isTruthy(process.env.GATSBY_TELEMETRY_DEBUG)
    this.disabled = isTruthy(process.env.GATSBY_TELEMETRY_DISABLED)
  }

  isTrackingDisabled() {
    return this.disabled
  }

  addEvent(event) {
    if (this.disabled) {
      return
    }

    const eventString = JSON.stringify(event)

    if (this.debugEvents || this.verbose) {
      console.error(`Captured event:`, JSON.parse(eventString))

      if (this.debugEvents) {
        // Bail because we don't want to send debug events
        return
      }
    }

    this.store.appendToBuffer(eventString + `\n`)
  }

  async sendEvents() {
    return this.store.startFlushEvents(async eventsData => {
      const events = eventsData
        .split(`\n`)
        .filter(e => e && e.length > 2) // drop empty lines
        .map(e => JSON.parse(e))

      return this.submitEvents(events)
    })
  }

  async submitEvents(events) {
    try {
      const res = await fetch(`https://analytics.gatsbyjs.com/events`, {
        method: `POST`,
        headers: { "content-type": `application/json` },
        body: JSON.stringify(events),
      })
      return res.ok
    } catch (e) {
      return false
    }
  }

  getConfig(key) {
    if (key) {
      return this.config.get(key)
    }
    return this.config.all
  }

  updateConfig(key, value) {
    return this.config.set(key, value)
  }
}
<commit_msg>chore(gatsby-telemetry): Allow overriding API url from ENV (#16689)<commit_after>const os = require(`os`)
const path = require(`path`)
const Store = require(`./store`)
const fetch = require(`node-fetch`)
const Configstore = require(`configstore`)
const { ensureDirSync } = require(`fs-extra`)

const isTruthy = require(`./is-truthy`)

/* The events data collection is a spooled process that
 * buffers events to a local fs based buffer
 * which then is asynchronously flushed to the server.
 * This both increases the fault tolerancy and allows collection
 * to continue even when working offline.
 */
module.exports = class EventStorage {
  analyticsApi =
    process.env.GATSBY_TELEMETRY_API || `https://analytics.gatsbyjs.com/events`
  constructor() {
    try {
      this.config = new Configstore(`gatsby`, {}, { globalConfigPath: true })
    } catch (e) {
      // This should never happen
      this.config = {
        get: key => this.config[key],
        set: (key, value) => (this.config[key] = value),
        all: this.config,
        path: path.join(os.tmpdir(), `gatsby`),
        "telemetry.enabled": true,
        "telemetry.machineId": `not-a-machine-id`,
      }
    }

    const baseDir = path.dirname(this.config.path)

    try {
      ensureDirSync(baseDir)
    } catch (e) {
      // TODO: Log this event
    }

    this.store = new Store(baseDir)
    this.verbose = isTruthy(process.env.GATSBY_TELEMETRY_VERBOSE)
    this.debugEvents = isTruthy(process.env.GATSBY_TELEMETRY_DEBUG)
    this.disabled = isTruthy(process.env.GATSBY_TELEMETRY_DISABLED)
  }

  isTrackingDisabled() {
    return this.disabled
  }

  addEvent(event) {
    if (this.disabled) {
      return
    }

    const eventString = JSON.stringify(event)

    if (this.debugEvents || this.verbose) {
      console.error(`Captured event:`, JSON.parse(eventString))

      if (this.debugEvents) {
        // Bail because we don't want to send debug events
        return
      }
    }

    this.store.appendToBuffer(eventString + `\n`)
  }

  async sendEvents() {
    return this.store.startFlushEvents(async eventsData => {
      const events = eventsData
        .split(`\n`)
        .filter(e => e && e.length > 2) // drop empty lines
        .map(e => JSON.parse(e))

      return this.submitEvents(events)
    })
  }

  async submitEvents(events) {
    try {
      const res = await fetch(this.analyticsApi, {
        method: `POST`,
        headers: { "content-type": `application/json` },
        body: JSON.stringify(events),
      })
      return res.ok
    } catch (e) {
      return false
    }
  }

  getConfig(key) {
    if (key) {
      return this.config.get(key)
    }
    return this.config.all
  }

  updateConfig(key, value) {
    return this.config.set(key, value)
  }
}
<|endoftext|>


<commit_before>"use strict";

const split        = require('../express-split'),
      express      = require('express'),
      request      = require('supertest');

describe('#storage-in-memory', function() {
  let app;

  beforeEach(function() {
    app = express();
    app.use(split({
      experiments: {
       'test1': {options: ['1', '2', '3']}
      }
    }));
  });

  it('chooses a random option', function(done) {
    // Run 1000 tests, check that the distribution is pretty much equal between the variants
    app.get('/test1', function (req, res) {
      const seed = Math.floor(Math.random() * 10000) + 1000;
      for (let i = seed; i < seed + 1000; i++) {
        req.split.set_id(i);
        req.split.start('test1', function() {
          res.send();
        });
      }
      req.split.results((results) => {
        for (let r in results.results.test1) {
          // At least 29% impressions for each variant is considered normal
          if (results.results.test1[r].impressions < 290) {
            console.log(JSON.stringify(results));
            throw new Error('Got uneven distribution');
          }
        }
        done();
      });
    });
    request(app)
      .get('/test1')
      .end();
  });

  xit('remembers the chosen option between requests', function(done) {
  });

  xit('adds only a single impression for each user', function(done) {
  });

  xit('adds only a single conversion for each user', function(done) {
  });

});
<commit_msg>in-memory storage - test remembering the assigned variant between calls<commit_after>"use strict";

const split        = require('../express-split'),
      express      = require('express'),
      request      = require('supertest');

describe('#storage-in-memory', function() {
  let app;
  const seed = Math.floor(Math.random() * 10000) + 1000;

  beforeEach(function() {
    app = express();
    app.use(split({
      experiments: {
       'test1': {options: ['1', '2', '3']}
      }
    }));
  });

  it('chooses a random option', function(done) {
    // Run 1000 tests, check that the distribution is pretty much equal between the variants
    app.get('/test1', function (req, res) {
      for (let i = seed; i < seed + 1000; i++) {
        req.split.set_id(i);
        req.split.start('test1', function() {
          res.send();
        });
      }
      req.split.results((results) => {
        for (let r in results.results.test1) {
          // At least 29% impressions for each variant is considered normal
          if (results.results.test1[r].impressions < 290) {
            console.log(JSON.stringify(results));
            throw new Error('Got uneven distribution');
          }
        }
        done();
      });
    });
    request(app)
      .get('/test1')
      .end(function(err, res){
        if (err) throw err;
      });
  });

  // Run 10 times to better avoid false positives
  for (let i = 1; i <= 10; i++) {
    it('remembers the chosen option between requests #' + i, function(done) {
      app.get('/test1', function (req, res) {
        req.split.set_id(seed);
        req.split.start('test1', function() {
          req.split.get('test1', function(variant) {
            res.send({variant: variant});
          });
        });
      });

      let variant = false;
      const second_call = function() {
        request(app)
          .get('/test1')
          .expect({
            variant: variant
          }).end(function(err, res){
            if (err) throw err;
            done();
          });
      };

      request(app)
        .get('/test1')
        .expect(function(res) {
          variant = res.body.variant;
          second_call();
        })
        .end(function(err, res){
          if (err) throw err;
        });
    });
  }

  xit('adds only a single impression for each user', function(done) {
  });

  xit('adds only a single conversion for each user', function(done) {
  });

});
<|endoftext|>


<commit_before>( function () {

	THREE.WebRTCClient = function ( server, params ) {

		THREE.NetworkClient.call( this, params );

		this.server = server;

		this.init();

	};

	THREE.WebRTCClient.prototype = Object.create( THREE.NetworkClient.prototype );
	THREE.WebRTCClient.prototype.constructor = THREE.WebRTCClient;

	Object.assign( THREE.WebRTCClient.prototype, {

		init: function () {

			var self = this;

			this.server.addEventListener( 'open', function ( id ) { self.onOpen( id ); } );
			this.server.addEventListener( 'close', function ( id ) { self.onClose( id ); } );
			this.server.addEventListener( 'error', function ( error ) { self.onError( error ); } );

			this.server.addEventListener( 'receive', function ( signal ) {

				for ( var i = 0, il = self.connections.length; i < il; i ++ ) {

					self.connections[ i ].onReceiveSignal( signal );

				}

			} );

			this.server.addEventListener( 'remotejoin', function ( params ) {

				var id = params.peer;

				if ( id === self.id || self.hasConnection( id ) ) return;

				var timestamp = params.joinTimestamp;
				var timestamp2 = params.peerJoinTimestamp;

				// TODO: need a workaround for timestamp === timestamp2
				var connectFromMe = timestamp > timestamp2;

				var peer = new WebRTCPeer( self.id, id, self.server, self.stream );

				peer.addEventListener( 'open', function ( id ) {

					self.onConnect( id, ! connectFromMe );

				} );

				peer.addEventListener( 'close', function ( id ) {

					if ( self.removeConnection( id ) ) self.onDisconnect( id );

				} );

				peer.addEventListener( 'error', function ( error ) {

					self.onError( error );

				} );

				peer.addEventListener( 'receive', function ( message ) {

					self.onReceive( message );

				} );

				peer.addEventListener( 'receivestream', function ( stream ) {

					self.onRemoteStream( stream );

				} );

				self.addConnection( id, peer );

				if ( connectFromMe ) peer.offer();

			} );

			if ( this.server.id !== '' ) {

				requestAnimationFrame( function () { self.onOpen( self.server.id ); } );

			}

		},

		connect: function ( roomId ) {

			var self = this;

			this.roomId = roomId;

			this.server.connect( roomId );

		},

		send: function ( id, data ) {

			var connection = this.connectionTable[ id ];

			if ( connection === undefined ) return;

			connection.send( data );

		},

		broadcast: function ( data ) {

			for ( var i = 0, il = this.connections.length; i < il; i ++ ) {

				this.send( this.connections[ i ].peer, data );

			}

		}

	} );

	var ICE_SERVERS = [
		{ url: 'stun:stun.l.google.com:19302' },
		{ url: 'stun:stun1.l.google.com:19302' },
		{ url: 'stun:stun2.l.google.com:19302' },
		{ url: 'stun:stun3.l.google.com:19302' },
		{ url: 'stun:stun4.l.google.com:19302' }
	];

	var WebRTCPeer = function ( id, peer, server, stream ) {

		this.id = id;
		this.peer = peer;
		this.server = server;
		this.pc = this.createPeerConnection( stream );

		this.onOpens = [];
		this.onCloses = [];
		this.onReceives = [];
		this.onReceiveStreams = [];
		this.onErrors = [];

	};

	Object.assign( WebRTCPeer.prototype, {

		addEventListener: function ( type, func ) {

			switch ( type ) {

				case 'open':
					this.onOpens.push( func );
					break;

				case 'close':
					this.onCloses.push( func );
					break;

				case 'receive':
					this.onReceives.push( func );
					break;

				case 'receivestream':
					this.onReceiveStreams.push( func );
					break;

				case 'error':
					this.onErrors.push( func );
					break;

				default:
					console.log( 'WebRTCPeer.addEventListener: Unkown type ' + type );
					break;

			}

		},

		onOpen: function ( id ) {

			for ( var i = 0, il = this.onOpens.length; i < il; i ++ ) {

				this.onOpens[ i ]( id );

			}

		},

		onClose: function ( id ) {

			for ( var i = 0, il = this.onCloses.length; i < il; i ++ ) {

				this.onCloses[ i ]( id );

			}

		},

		onReceive: function ( message ) {

			for ( var i = 0, il = this.onReceives.length; i < il; i ++ ) {

				this.onReceives[ i ]( message );

			}

		},

		onReceiveStream: function ( stream ) {

			for ( var i = 0, il = this.onReceiveStreams.length; i < il; i ++ ) {

				this.onReceiveStreams[ i ]( stream );

			}

		},

		onError: function ( error ) {

			for ( var i = 0, il = this.onErrors.length; i < il; i ++ ) {

				this.onErrors[ i ]( error );

			}

		},

		createPeerConnection: function ( stream ) {

			var self = this;

			var RTCPeerConnection = window.RTCPeerConnection ||
						window.webkitRTCPeerConnection ||
						window.mozRTCPeerConnection ||
						window.msRTCPeerConnection;

			if ( RTCPeerConnection === undefined ) {

				throw new Error( 'Peer: This browser does not seem to support WebRTC.' );

			}

			var pc = new RTCPeerConnection( { 'iceServers': ICE_SERVERS } );

			if ( stream !== null ) pc.addStream( stream );

			pc.onicecandidate = function ( event ) {

				if ( event.candidate ) {

					var params = {
						id: self.id,
						peer: self.peer,
						type: 'candidate',
						sdpMLineIndex: event.candidate.sdpMLineIndex,
						candidate: event.candidate.candidate
					};

					self.server.send( params );

				}

			};

			pc.onaddstream = function ( event ) {

				self.onReceiveStream( event.stream );

			};

			return pc;

		},

		onReceiveSignal: function ( signal ) {

			if ( this.id !== signal.peer || this.peer !== signal.id ) return;

			switch ( signal.type ) {

				case 'offer':
					this.onReceiveOffer( signal );
					break;

				case 'answer':
					this.onReceiveAnswer( signal );
					break;

				case 'candidate':
					this.onReceiveCandidate( signal );
					break;

				default:
					console.log( 'WebRTCPeer: Unknown signal type ' + signal.type );
					break;

			}

		},

		onReceiveOffer: function ( message ) {

			var self = this;

			this.pc.ondatachannel = function ( event ) {

				self.channel = event.channel;
				self.setupChannelListener();

			};

			this.setRemoteDescription( message );

			this.pc.createAnswer(

				function ( sdp ) {

					self.onReceiveSDP( sdp );

				},

				function ( error ) {

					console.log( error );
					self.onError( error );

				}

			);

		},

		onReceiveAnswer: function ( message ) {

			this.setRemoteDescription( message );

		},

		onReceiveCandidate: function ( message ) {

			var self = this;

			var RTCIceCandidate = window.RTCIceCandidate ||
						window.webkitRTCIceCandidate ||
						window.mozRTCIceCandidate;

			this.pc.addIceCandidate(

				new RTCIceCandidate( message ),

				function () {},

				function ( error ) {

					console.log( error );
					self.onError( error );

				}

			);

		},

		onReceiveSDP: function ( sdp ) {

			var self = this;

			this.pc.setLocalDescription( sdp,

				function () {},

				function ( error ) {

					console.log( error );
					self.onError( error );

				}

			);

			this.server.send( {
				id: this.id,
				peer: this.peer,
				type: sdp.type,
				sdp: sdp.sdp
			} );

		},

		setRemoteDescription: function ( message ) {

			var self = this;

			var RTCSessionDescription = window.RTCSessionDescription ||
							window.webkitRTCSessionDescription ||
							window.mozRTCSessionDescription ||
							window.msRTCSessionDescription;

			this.pc.setRemoteDescription(

				new RTCSessionDescription( message ),

				function () {},

				function ( error ) {

					console.log( error );
					self.onError( error );

				}

			);

		},

		offer: function () {

			var self = this;

			this.channel = this.pc.createDataChannel( 'mychannel', { reliable: false } );

			this.setupChannelListener();

			this.pc.createOffer(

				function ( sdp ) {

					self.onReceiveSDP( sdp );

				},

				function ( error ) {

					console.log( error );
					self.onError( error );

				}

			);

		},

		send: function ( data ) {

			this.channel.send( JSON.stringify( data ) );

		},

		setupChannelListener: function ( channel ) {

			var self = this;

			this.channel.onmessage = function ( event ) {

				self.onReceive( JSON.parse( event.data ) );

			};

			this.channel.onopen = function ( event ) {

				self.onOpen( self.peer );

			};

			this.channel.onclose = function ( event ) {

				self.onClose( self.peer );

			};

			this.channel.onerror = function( error ) {

				self.onError( error );

			};

		}

	} );

} )();
<commit_msg>Write comment and clean up WebRTCClient<commit_after>/**
 * @author Takahiro https://github.com/takahirox
 */

( function () {

	/**
	 * WebRTCClient constructor.
	 * General WebRTC Client, establishes a connection via Signaling server.
	 * @param {THREE.SignalingServer} server
	 * @param {object} params - parameters for instantiate (optional)
	 */
	THREE.WebRTCClient = function ( server, params ) {

		if ( params === undefined ) params = {};

		THREE.NetworkClient.call( this, params );

		this.server = server;

		this.init();

	};

	THREE.WebRTCClient.prototype = Object.create( THREE.NetworkClient.prototype );
	THREE.WebRTCClient.prototype.constructor = THREE.WebRTCClient;

	Object.assign( THREE.WebRTCClient.prototype, {

		/**
		 * Initializes signaling server event listener.
		 */
		init: function () {

			var self = this;

			// connected with server
			this.server.addEventListener( 'open',

				function ( id ) {

					self.invokeOpenListeners( id );

				}

			);

			// disconnected from server
			this.server.addEventListener( 'close',

				function ( id ) {

					self.invokeCloseListeners( id );

				}

			);

			// error occurred with server
			this.server.addEventListener( 'error',

				function ( error ) {

					self.invokeErrorListeners( error );

				}

			);

			// aware of a remote peer join the room
			this.server.addEventListener( 'remote_join', function ( id, localTimestamp, remoteTimestamp ) {

				if ( id === self.id || self.hasConnection( id ) ) return;

				// TODO: need a workaround for localTimestamp === remoteTimestamp
				var connectFromMe = localTimestamp > remoteTimestamp;

				var peer = new WebRTCPeer( self.id, id, self.server, self.stream );

				// received signal from a remote peer via server
				self.server.addEventListener( 'receive',

					function ( signal ) {

						peer.handleSignal( signal );

					}

				);

				// connected with a remote peer
				peer.addEventListener( 'open', function ( id ) {

					if ( self.addConnection( id, peer ) ) {

						self.invokeConnectListeners( id, ! connectFromMe );

					}

					// TODO: remove server 'receive' listener here.
					//       if .addConnection() fails here?

				} );

				// disconnected from a remote peer
				peer.addEventListener( 'close', function ( id ) {

					if ( self.removeConnection( id ) ) {

						// TODO: remove server 'receive' listener here.

						self.invokeDisconnectListeners( id );

					}

				} );

				// error occurred with a remote peer
				peer.addEventListener( 'error', function ( error ) {

					self.invokeErrorListeners( error );

				} );

				// received data from a remote peer
				peer.addEventListener( 'receive', function ( data ) {

					self.invokeReceiveListeners( data );

				} );

				// received remote media streaming
				peer.addEventListener( 'receive_stream', function ( stream ) {

					self.invokeRemoteStreamListeners( stream );

				} );

				if ( connectFromMe ) peer.offer();

			} );

			// for the compatibility with other NetworkClient classes.
			// if already connected with signaling server, asynchronously invokes open listeners.
			if ( this.server.id !== '' ) {

				requestAnimationFrame(

					function () {

						self.invokeOpenListeners( self.server.id );

					}

				);

			}

		},

		// public concrete method

		connect: function ( roomId ) {

			var self = this;

			this.roomId = roomId;

			this.server.connect( roomId );

		},

		send: function ( id, data ) {

			var connection = this.connectionTable[ id ];

			if ( connection === undefined ) return;

			connection.send( data );

		},

		broadcast: function ( data ) {

			for ( var i = 0, il = this.connections.length; i < il; i ++ ) {

				this.send( this.connections[ i ].peer, data );

			}

		}

	} );

	// ice servers for RTCPeerConnection.

	var ICE_SERVERS = [
		{ url: 'stun:stun.l.google.com:19302' },
		{ url: 'stun:stun1.l.google.com:19302' },
		{ url: 'stun:stun2.l.google.com:19302' },
		{ url: 'stun:stun3.l.google.com:19302' },
		{ url: 'stun:stun4.l.google.com:19302' }
	];

	/**
	 * WebRTCPeer constructor.
	 * WebRTCPeer handles WebRTC connection and data transfer with RTCPeerConnection.
	 * Refer to RTCPeerConnection document for the message handling detail.
	 * @param {string} id - local peer id
	 * @param {string} peer - remote peer id
	 * @param {SignalingServer} server
	 * @param {MediaStream} stream - sends media stream to remote peer if it's provided (optional)
	 */
	var WebRTCPeer = function ( id, peer, server, stream ) {

		this.id = id;
		this.peer = peer;
		this.server = server;
		this.pc = this.createPeerConnection( stream );

		// event listeners

		this.onOpens = [];
		this.onCloses = [];
		this.onErrors = [];
		this.onReceives = [];
		this.onReceiveStreams = [];

	};

	Object.assign( WebRTCPeer.prototype, {

		/**
		 * Adds EventListener. Callback function will be invoked when
		 * 'open': a connection is established with a remote peer
		 * 'close': a connection is disconnected from a remote peer
		 * 'error': error occurs
		 * 'receive': receives data from a remote peer
		 * 'remote_stream': receives a remote media stream
		 *
		 * Arguments for callback functions are
		 * 'open': {string} local peer id
		 * 'close': {string} local peer id
		 * 'error': {string} error message
		 * 'receive': {anything} signal sent from a remote peer
		 * 'remote_stream': {MediaStream} remote media stream
		 *
		 * @param {string} type - event type
		 * @param {function} func - callback function
		 */
		addEventListener: function ( type, func ) {

			switch ( type ) {

				case 'open':
					this.onOpens.push( func );
					break;

				case 'close':
					this.onCloses.push( func );
					break;

				case 'error':
					this.onErrors.push( func );
					break;

				case 'receive':
					this.onReceives.push( func );
					break;

				case 'receive_stream':
					this.onReceiveStreams.push( func );
					break;

				default:
					console.log( 'WebRTCPeer.addEventListener: Unknown type ' + type );
					break;

			}

		},

		/**
		 * Creates peer connection.
		 * @param {MediaStream} stream - sends media stream to remote if it's provided (optional)
		 * @returns {RTCPeerConnection}
		 */
		createPeerConnection: function ( stream ) {

			var self = this;

			var RTCPeerConnection = window.RTCPeerConnection ||
						window.webkitRTCPeerConnection ||
						window.mozRTCPeerConnection ||
						window.msRTCPeerConnection;

			if ( RTCPeerConnection === undefined ) {

				throw new Error( 'WebRTCPeer.createPeerConnection: This browser does not seem to support WebRTC.' );

			}

			var pc = new RTCPeerConnection( { 'iceServers': ICE_SERVERS } );

			if ( stream !== null && stream !== undefined ) pc.addStream( stream );

			pc.onicecandidate = function ( event ) {

				if ( event.candidate ) {

					var params = {
						id: self.id,
						peer: self.peer,
						type: 'candidate',
						sdpMLineIndex: event.candidate.sdpMLineIndex,
						candidate: event.candidate.candidate
					};

					self.server.send( params );

				}

			};

			pc.onaddstream = function ( event ) {

				self.invokeReceiveStreamListeners( event.stream );

			};

			return pc;

		},

		/**
		 * Handles offer request.
		 * @param {object} message - message sent from a remote peer
		 */
		handleOffer: function ( message ) {

			var self = this;

			this.pc.ondatachannel = function ( event ) {

				self.channel = event.channel;
				self.setupChannelListener();

			};

			this.setRemoteDescription( message );

			this.pc.createAnswer(

				function ( sdp ) {

					self.handleSessionDescription( sdp );

				},

				function ( error ) {

					console.log( 'WebRTCPeer.handleOffer: ' + error );
					self.invokeErrorListeners( error );

				}

			);

		},

		/**
		 * Handles answer response.
		 * @param {object} message - message sent from a remote peer
		 */
		handleAnswer: function ( message ) {

			this.setRemoteDescription( message );

		},

		/**
		 * Handles candidate sent from a remote peer.
		 * @param {object} message - message sent from a remote peer
		 */
		handleCandidate: function ( message ) {

			var self = this;

			var RTCIceCandidate = window.RTCIceCandidate ||
						window.webkitRTCIceCandidate ||
						window.mozRTCIceCandidate;

			this.pc.addIceCandidate(

				new RTCIceCandidate( message ),

				function () {},

				function ( error ) {

					console.log( 'WebRTCPeer.handleCandidate: ' + error );
					self.invokeErrorListeners( error );

				}

			);

		},

		/**
		 * Handles SessionDescription.
		 * @param {RTCSessionDescription} sdp
		 */
		handleSessionDescription: function ( sdp ) {

			var self = this;

			this.pc.setLocalDescription( sdp,

				function () {},

				function ( error ) {

					console.log( 'WebRTCPeer.handleSessionDescription: ' + error );
					self.invokeErrorListeners( error );

				}

			);

			this.server.send( {
				id: this.id,
				peer: this.peer,
				type: sdp.type,
				sdp: sdp.sdp
			} );

		},

		/**
		 * Sets remote description.
		 * @param {object} message - message sent from a remote peer
		 */
		setRemoteDescription: function ( message ) {

			var self = this;

			var RTCSessionDescription = window.RTCSessionDescription ||
							window.webkitRTCSessionDescription ||
							window.mozRTCSessionDescription ||
							window.msRTCSessionDescription;

			this.pc.setRemoteDescription(

				new RTCSessionDescription( message ),

				function () {},

				function ( error ) {

					console.log( 'WebRTCPeer.setRemoteDescription: ' + error );
					self.invokeErrorListeners( error );

				}

			);

		},

		/**
		 * Sets up channel listeners.
		 */
		setupChannelListener: function () {

			var self = this;

			// received data from a remote peer
			this.channel.onmessage = function ( event ) {

				self.invokeReceiveListeners( JSON.parse( event.data ) );

			};

			// connected with a remote peer
			this.channel.onopen = function ( event ) {

				self.invokeOpenListeners( self.peer );

			};

			// disconnected from a remote peer
			this.channel.onclose = function ( event ) {

				self.invokeCloseListeners( self.peer );

			};

			// error occurred with a remote peer
			this.channel.onerror = function( error ) {

				self.invokeErrorListeners( error );

			};

		},

		// event listeners, refer to .addEventListeners() comment for the arguments.

		invokeOpenListeners: function ( id ) {

			for ( var i = 0, il = this.onOpens.length; i < il; i ++ ) {

				this.onOpens[ i ]( id );

			}

		},

		invokeCloseListeners: function ( id ) {

			for ( var i = 0, il = this.onCloses.length; i < il; i ++ ) {

				this.onCloses[ i ]( id );

			}

		},

		invokeErrorListeners: function ( error ) {

			for ( var i = 0, il = this.onErrors.length; i < il; i ++ ) {

				this.onErrors[ i ]( error );

			}

		},

		invokeReceiveListeners: function ( message ) {

			for ( var i = 0, il = this.onReceives.length; i < il; i ++ ) {

				this.onReceives[ i ]( message );

			}

		},

		invokeReceiveStreamListeners: function ( stream ) {

			for ( var i = 0, il = this.onReceiveStreams.length; i < il; i ++ ) {

				this.onReceiveStreams[ i ]( stream );

			}

		},

		// public

		/**
		 * Sends connection request (offer) to a remote peer.
		 */
		offer: function () {

			var self = this;

			this.channel = this.pc.createDataChannel( 'mychannel', { reliable: false } );

			this.setupChannelListener();

			this.pc.createOffer(

				function ( sdp ) {

					self.handleSessionDescription( sdp );

				},

				function ( error ) {

					console.log( error );
					self.onError( error );

				}

			);

		},

		/**
		 * Sends data to a remote peer.
		 * @param {anything} data
		 */
		send: function ( data ) {

			this.channel.send( JSON.stringify( data ) );

		},

		/**
		 * Handles signal sent from a remote peer via server.
		 * @param {object} signal - must have .peer as destination peer id and .id as source peer id
		 */
		handleSignal: function ( signal ) {

			// ignores signal if it isn't for me
			if ( this.id !== signal.peer || this.peer !== signal.id ) return;

			switch ( signal.type ) {

				case 'offer':
					this.handleOffer( signal );
					break;

				case 'answer':
					this.handleAnswer( signal );
					break;

				case 'candidate':
					this.handleCandidate( signal );
					break;

				default:
					console.log( 'WebRTCPeer: Unknown signal type ' + signal.type );
					break;

			}

		}

	} );

} )();
<|endoftext|>


<commit_before>/* Copyright (c) 2012-2013 The TagSpaces Authors. All rights reserved.
 * Use of this source code is governed by a AGPL3 license that
 * can be found in the LICENSE file. */
define(function(require, exports, module) {
"use strict";

console.log("Loading perspective.manager.js ...");

var perspectives = undefined;

var TSCORE = require("tscore");

var initPerspectives = function () {

	perspectives = [];

	$("#viewSwitcher").empty();
	$("#viewToolbars").empty();
	$("#viewContainers").empty();
	$("#viewFooters").empty();

	initWelcomeScreen();

	var defaultViewLoaded = false;

    var extensions = TSCORE.Config.getPerspectives();
    for (var i=0; i < extensions.length; i++) {

        // TODO Some libraries such as ace editor are not working using paths like this "file:///C:/blabal/extension.js"
        var extPath = TSCORE.Config.getExtensionPath()+"/"+extensions[i].id+"/extension.js";

        require([extPath], function(perspective) {
            perspectives.push(perspective);
            try {
                // Creating perspective's toolbar
                $("#viewToolbars").append($("
", {
                    id: perspective.ID+"Toolbar",
                    class: "btn-toolbar",
                    text: perspective.Title,
                }).hide());

                // Creating perspective's container
                $("#viewContainers").append($("
", {
                    id: perspective.ID+"Container",
                    class: "btn-toolbar",
                    text: perspective.Title,
                    style: "width: 100%; height: 100%",
                }).hide());

                // Creating perspective's footer
                $("#viewFooters").append($("
", {
                    id: perspective.ID+"Footer",
                    class: "btn-toolbar",
                    text: perspective.Title,
                    style: "width: 100%; height: 100%",
                }).hide());
                perspective.init();
            } catch(e) {
                console.log("Error while executing 'init' on "+perspectives[i].ID+" - "+e);
            } finally {
                if( perspectives.length == extensions.length) {
                    initPerspectiveSwitcher();
                }
            }
        });
    }
};

var initWelcomeScreen = function() {
        require([
              "text!templates/WelcomeScreen.html",
            ], function(uiTPL) {
                // Check if dialog already created
                if($("#dialogLocationEdit").length < 1) {
                    var uiTemplate = Handlebars.compile( uiTPL );
                    $("#viewContainers").append(uiTemplate());

                }
        });
};

var initPerspectiveSwitcher = function() {
    var extensions = TSCORE.Config.getPerspectives();
	$("#viewSwitcher").empty();
	$("#viewSwitcher").append("
Perspective Switch<button type='button' class='close'>×</button>
");
	$("#viewSwitcher").append("

");
    for (var i=0; i < extensions.length; i++) {
        var curPers = undefined;
        // Finding the right perspective
        perspectives.forEach(function(value) {
            if(value.ID == extensions[i].id) {
                curPers = value;
            }
        });

        $("#viewSwitcher").append($("
", {})
        .append($("", {
            "viewid":   curPers.ID,
            "title":    curPers.ID,
            "id":       curPers.ID+"Button",
            "text":     " "+curPers.Title
        })
        .prepend($("", {
            "class":  curPers.Icon
        }))));

        // Adding event listener & icon to the radio button
        $( "#"+curPers.ID+"Button" ).click(function() {
            changePerspective($(this).attr("viewid"));
        });
    };
};

var redrawCurrentPerspective = function () {
    for (var i=0; i < perspectives.length; i++) {
        if(perspectives[i].ID == TSCORE.currentView) {
            try {
                perspectives[i].load();
                break;
            } catch(e) {
                console.log("Error while executing 'redrawCurrentPerspective' on "+perspectives[i].ID+" "+e);
            }
        }
    }
};

var removeFileUI = function (filePath) {
    console.log("Removing file from perspectives");
    for (var i=0; i < perspectives.length; i++) {
        try {
            perspectives[i].removeFileUI(filePath);
        } catch(e) {
            console.log("Error while executing 'removeFileUI' on "+perspectives[i].ID+" "+e);
        }
    }
};

var updateFileUI = function (oldFilePath,newFilePath) {
    console.log("Updating file in perspectives");
    for (var i=0; i < perspectives.length; i++) {
        try {
            perspectives[i].updateFileUI(oldFilePath, newFilePath);
        } catch(e) {
            console.log("Error while executing 'updateFileUI' on "+perspectives[i].ID+" "+e);
        }
    }
};

var getNextFile = function (filePath) {
	for (var i=0; i < perspectives.length; i++) {
		if(perspectives[i].ID == TSCORE.currentView) {
	 		try {
	 			return perspectives[i].getNextFile(filePath);
	 		} catch(e) {
	 			console.log("Error while executing 'getNextFile' on "+perspectives[i].ID+" "+e);
	 		}
 		}
	}
};

var getPrevFile = function (filePath) {
	for (var i=0; i < perspectives.length; i++) {
		if(perspectives[i].ID == TSCORE.currentView) {
	 		try {
	 			return perspectives[i].getPrevFile(filePath);
	 		} catch(e) {
	 			console.log("Error while executing 'getPrevFile' on "+perspectives[i].ID+" "+e);
	 		}
 		}
	}
};

var updateTreeData = function (treeData) {
	for (var i=0; i < perspectives.length; i++) {
 		try {
 			perspectives[i].updateTreeData(treeData);
 		} catch(e) {
 			console.log("Error while executing 'updateTreeData' on "+perspectives[i].ID+" "+e);
 		}
	}
};

var updateFileBrowserData = function(dirList) {
    console.log("Updating the file browser data...");

    TSCORE.fileList = [];
    var tags = undefined;
    var ext = undefined;
    var title = undefined;
    var fileSize = undefined;
    var fileLMDT = undefined;
    var path = undefined;
    var filename = undefined;
    for (var i=0; i < dirList.length; i++) {
        if (dirList[i].isFile){
            // Considering Unix HiddenEntries (. in the beginning of the filename)
            if (TSCORE.Config.getShowUnixHiddenEntries() ||
               (!TSCORE.Config.getShowUnixHiddenEntries() && (dirList[i].name.indexOf(".") != 0))) {
                 filename = dirList[i].name;
                 path = dirList[i].path;
                 tags = TSCORE.TagUtils.extractTags(path);
                 title = TSCORE.TagUtils.extractTitle(path);
				 ext = TSCORE.TagUtils.extractFileExtension(path);
                 fileSize = dirList[i].size;
                 fileLMDT = dirList[i].lmdt;

                 if(fileSize == undefined) fileSize = "";
                 if(fileLMDT == undefined) fileLMDT = "";
                 var entry = [ext,title,tags,fileSize,fileLMDT,path,filename];
                 TSCORE.fileList.push(entry);
            }
        }
    }
    changePerspective(TSCORE.currentView);
};

var refreshFileListContainer = function() {
	// TODO consider search view
    TSCORE.IO.listDirectory(TSCORE.currentPath);
};

var hideAllPerspectives = function() {
    $( "#welcomeScreen" ).hide();
    for (var i=0; i < perspectives.length; i++) {
        $( "#"+perspectives[i].ID+"Container" ).hide();
        $( "#"+perspectives[i].ID+"Toolbar" ).hide();
        $( "#"+perspectives[i].ID+"Footer" ).hide();
    }
};

var changePerspective = function (viewType) {
    console.log("Change to "+viewType+" view.");
    TSCORE.showLoadingAnimation();

    // Loading first perspective by default
    if(viewType == undefined) {
        TSCORE.currentView = perspectives[0].ID;
    } else {
        //Setting the current view
        TSCORE.currentView = viewType;
    }

    if(TSCORE.currentView == undefined) {
        TSCORE.showAlertDialog("No Perspectives found","");
        return false;
    }

	hideAllPerspectives();

	for (var i=0; i < perspectives.length; i++) {
 		if(perspectives[i].ID == viewType) {
            $('#currentPerspectitveIcon').removeClass();
            $('#currentPerspectitveIcon').addClass(perspectives[i].Icon);
            $('#currentPerspectitveName').text(" "+perspectives[i].Title);
            $('#currentPerspectitveName').attr("title",perspectives[i].ID);

 			perspectives[i].load();

			$( "#"+perspectives[i].ID+"Container" ).show();
			$( "#"+perspectives[i].ID+"Toolbar" ).show();
            $( "#"+perspectives[i].ID+"Footer" ).show();
 		}
	}

    // Clear the list with the selected files
    clearSelectedFiles();

    // Enabled the search functionality
    TSCORE.enableTopToolbar();

    TSCORE.hideLoadingAnimation();
};

var clearSelectedFiles = function () {
    // Clear selected files
    TSCORE.selectedFiles = [];
	for (var i=0; i < perspectives.length; i++) {
 		try {
 			perspectives[i].clearSelectedFiles();
 		} catch(e) {
 			console.log("Error while executing 'clearSelectedFiles' on "+perspectives[i].ID+" - "+e);
 		}
	}
};

exports.initPerspectives 			 = initPerspectives;
exports.hideAllPerspectives          = hideAllPerspectives;
exports.redrawCurrentPerspective     = redrawCurrentPerspective;
exports.getNextFile					 = getNextFile;
exports.getPrevFile 				 = getPrevFile;
exports.updateTreeData				 = updateTreeData;
exports.updateFileBrowserData		 = updateFileBrowserData;
exports.refreshFileListContainer	 = refreshFileListContainer;
exports.clearSelectedFiles			 = clearSelectedFiles;
exports.removeFileUI                 = removeFileUI;
exports.updateFileUI                 = updateFileUI;
exports.changePerspective            = changePerspective;

});<commit_msg>moving the tagspaces logo from the template to the dom<commit_after>/* Copyright (c) 2012-2013 The TagSpaces Authors. All rights reserved.
 * Use of this source code is governed by a AGPL3 license that
 * can be found in the LICENSE file. */
define(function(require, exports, module) {
"use strict";

console.log("Loading perspective.manager.js ...");

var perspectives = undefined;

var TSCORE = require("tscore");

var initPerspectives = function () {

	perspectives = [];

	$("#viewSwitcher").empty();
	$("#viewToolbars").empty();
	$("#viewContainers").empty();
	$("#viewFooters").empty();

	initWelcomeScreen();

	var defaultViewLoaded = false;

    var extensions = TSCORE.Config.getPerspectives();
    for (var i=0; i < extensions.length; i++) {

        // TODO Some libraries such as ace editor are not working using paths like this "file:///C:/blabal/extension.js"
        var extPath = TSCORE.Config.getExtensionPath()+"/"+extensions[i].id+"/extension.js";

        require([extPath], function(perspective) {
            perspectives.push(perspective);
            try {
                // Creating perspective's toolbar
                $("#viewToolbars").append($("
", {
                    id: perspective.ID+"Toolbar",
                    class: "btn-toolbar",
                    text: perspective.Title,
                }).hide());

                // Creating perspective's container
                $("#viewContainers").append($("
", {
                    id: perspective.ID+"Container",
                    class: "btn-toolbar",
                    text: perspective.Title,
                    style: "width: 100%; height: 100%",
                }).hide());

                // Creating perspective's footer
                $("#viewFooters").append($("
", {
                    id: perspective.ID+"Footer",
                    class: "btn-toolbar",
                    text: perspective.Title,
                    style: "width: 100%; height: 100%",
                }).hide());
                perspective.init();
            } catch(e) {
                console.log("Error while executing 'init' on "+perspectives[i].ID+" - "+e);
            } finally {
                if( perspectives.length == extensions.length) {
                    initPerspectiveSwitcher();
                }
            }
        });
    }
};

var initWelcomeScreen = function() {
        /* require([
              "text!templates/WelcomeScreen.html",
            ], function(uiTPL) {
                // Check if dialog already created
                if($("#dialogLocationEdit").length < 1) {
                    var uiTemplate = Handlebars.compile( uiTPL );
                    $("#viewContainers").append(uiTemplate());
                }
        }); */
        $("#viewContainers").append('

');
};

var initPerspectiveSwitcher = function() {
    var extensions = TSCORE.Config.getPerspectives();
	$("#viewSwitcher").empty();
	$("#viewSwitcher").append("
Perspective Switch<button type='button' class='close'>×</button>
");
	$("#viewSwitcher").append("

");
    for (var i=0; i < extensions.length; i++) {
        var curPers = undefined;
        // Finding the right perspective
        perspectives.forEach(function(value) {
            if(value.ID == extensions[i].id) {
                curPers = value;
            }
        });

        $("#viewSwitcher").append($("
", {})
        .append($("", {
            "viewid":   curPers.ID,
            "title":    curPers.ID,
            "id":       curPers.ID+"Button",
            "text":     " "+curPers.Title
        })
        .prepend($("", {
            "class":  curPers.Icon
        }))));

        // Adding event listener & icon to the radio button
        $( "#"+curPers.ID+"Button" ).click(function() {
            changePerspective($(this).attr("viewid"));
        });
    };
};

var redrawCurrentPerspective = function () {
    for (var i=0; i < perspectives.length; i++) {
        if(perspectives[i].ID == TSCORE.currentView) {
            try {
                perspectives[i].load();
                break;
            } catch(e) {
                console.log("Error while executing 'redrawCurrentPerspective' on "+perspectives[i].ID+" "+e);
            }
        }
    }
};

var removeFileUI = function (filePath) {
    console.log("Removing file from perspectives");
    for (var i=0; i < perspectives.length; i++) {
        try {
            perspectives[i].removeFileUI(filePath);
        } catch(e) {
            console.log("Error while executing 'removeFileUI' on "+perspectives[i].ID+" "+e);
        }
    }
};

var updateFileUI = function (oldFilePath,newFilePath) {
    console.log("Updating file in perspectives");
    for (var i=0; i < perspectives.length; i++) {
        try {
            perspectives[i].updateFileUI(oldFilePath, newFilePath);
        } catch(e) {
            console.log("Error while executing 'updateFileUI' on "+perspectives[i].ID+" "+e);
        }
    }
};

var getNextFile = function (filePath) {
	for (var i=0; i < perspectives.length; i++) {
		if(perspectives[i].ID == TSCORE.currentView) {
	 		try {
	 			return perspectives[i].getNextFile(filePath);
	 		} catch(e) {
	 			console.log("Error while executing 'getNextFile' on "+perspectives[i].ID+" "+e);
	 		}
 		}
	}
};

var getPrevFile = function (filePath) {
	for (var i=0; i < perspectives.length; i++) {
		if(perspectives[i].ID == TSCORE.currentView) {
	 		try {
	 			return perspectives[i].getPrevFile(filePath);
	 		} catch(e) {
	 			console.log("Error while executing 'getPrevFile' on "+perspectives[i].ID+" "+e);
	 		}
 		}
	}
};

var updateTreeData = function (treeData) {
	for (var i=0; i < perspectives.length; i++) {
 		try {
 			perspectives[i].updateTreeData(treeData);
 		} catch(e) {
 			console.log("Error while executing 'updateTreeData' on "+perspectives[i].ID+" "+e);
 		}
	}
};

var updateFileBrowserData = function(dirList) {
    console.log("Updating the file browser data...");

    TSCORE.fileList = [];
    var tags = undefined;
    var ext = undefined;
    var title = undefined;
    var fileSize = undefined;
    var fileLMDT = undefined;
    var path = undefined;
    var filename = undefined;
    for (var i=0; i < dirList.length; i++) {
        if (dirList[i].isFile){
            // Considering Unix HiddenEntries (. in the beginning of the filename)
            if (TSCORE.Config.getShowUnixHiddenEntries() ||
               (!TSCORE.Config.getShowUnixHiddenEntries() && (dirList[i].name.indexOf(".") != 0))) {
                 filename = dirList[i].name;
                 path = dirList[i].path;
                 tags = TSCORE.TagUtils.extractTags(path);
                 title = TSCORE.TagUtils.extractTitle(path);
				 ext = TSCORE.TagUtils.extractFileExtension(path);
                 fileSize = dirList[i].size;
                 fileLMDT = dirList[i].lmdt;

                 if(fileSize == undefined) fileSize = "";
                 if(fileLMDT == undefined) fileLMDT = "";
                 var entry = [ext,title,tags,fileSize,fileLMDT,path,filename];
                 TSCORE.fileList.push(entry);
            }
        }
    }
    changePerspective(TSCORE.currentView);
};

var refreshFileListContainer = function() {
	// TODO consider search view
    TSCORE.IO.listDirectory(TSCORE.currentPath);
};

var hideAllPerspectives = function() {
    $( "#welcomeScreen" ).hide();
    for (var i=0; i < perspectives.length; i++) {
        $( "#"+perspectives[i].ID+"Container" ).hide();
        $( "#"+perspectives[i].ID+"Toolbar" ).hide();
        $( "#"+perspectives[i].ID+"Footer" ).hide();
    }
};

var changePerspective = function (viewType) {
    console.log("Change to "+viewType+" view.");
    TSCORE.showLoadingAnimation();

    // Loading first perspective by default
    if(viewType == undefined) {
        TSCORE.currentView = perspectives[0].ID;
    } else {
        //Setting the current view
        TSCORE.currentView = viewType;
    }

    if(TSCORE.currentView == undefined) {
        TSCORE.showAlertDialog("No Perspectives found","");
        return false;
    }

	hideAllPerspectives();

	for (var i=0; i < perspectives.length; i++) {
 		if(perspectives[i].ID == viewType) {
            $('#currentPerspectitveIcon').removeClass();
            $('#currentPerspectitveIcon').addClass(perspectives[i].Icon);
            $('#currentPerspectitveName').text(" "+perspectives[i].Title);
            $('#currentPerspectitveName').attr("title",perspectives[i].ID);

 			perspectives[i].load();

			$( "#"+perspectives[i].ID+"Container" ).show();
			$( "#"+perspectives[i].ID+"Toolbar" ).show();
            $( "#"+perspectives[i].ID+"Footer" ).show();
 		}
	}

    // Clear the list with the selected files
    clearSelectedFiles();

    // Enabled the search functionality
    TSCORE.enableTopToolbar();

    TSCORE.hideLoadingAnimation();
};

var clearSelectedFiles = function () {
    // Clear selected files
    TSCORE.selectedFiles = [];
	for (var i=0; i < perspectives.length; i++) {
 		try {
 			perspectives[i].clearSelectedFiles();
 		} catch(e) {
 			console.log("Error while executing 'clearSelectedFiles' on "+perspectives[i].ID+" - "+e);
 		}
	}
};

exports.initPerspectives 			 = initPerspectives;
exports.hideAllPerspectives          = hideAllPerspectives;
exports.redrawCurrentPerspective     = redrawCurrentPerspective;
exports.getNextFile					 = getNextFile;
exports.getPrevFile 				 = getPrevFile;
exports.updateTreeData				 = updateTreeData;
exports.updateFileBrowserData		 = updateFileBrowserData;
exports.refreshFileListContainer	 = refreshFileListContainer;
exports.clearSelectedFiles			 = clearSelectedFiles;
exports.removeFileUI                 = removeFileUI;
exports.updateFileUI                 = updateFileUI;
exports.changePerspective            = changePerspective;

});<|endoftext|>


<commit_before>const manual = new ManualState();
Meteor.methods({
    setLightOn(id, on) {
        if(!Meteor.isServer) return;
        check(id, String);
        check(on, Boolean);
        manual.setLight(id, {on: on});
    },
    setLightColor(id, r, g, b) {
        if(!Meteor.isServer) return;
        check(id, String);
        check(r, Number);
        check(g, Number);
        check(b, Number);
        manual.setLight(id, {rgb: {r,g,b}});
    },
    setLightBrightness(id, brightness){
        if(!Meteor.isServer) return;
        check(id, String);
        check(brightness, Number);
        manual.setLight(id, {bri: brightness});

    },
    setLightRandom(id){
        if(!Meteor.isServer) return;
        check(id, String);
        manual.setLightRandom(id);
    },
    allOff(){
        if(!Meteor.isServer) return;
        manual.setAllOff();
    }
});<commit_msg>re-enable latency comp on light server methods<commit_after>const manual = new ManualState();
Meteor.methods({
    setLightOn(id, on) {
        check(id, String);
        check(on, Boolean);
        manual.setLight(id, {on: on});
    },
    setLightColor(id, r, g, b) {
        check(id, String);
        check(r, Number);
        check(g, Number);
        check(b, Number);
        manual.setLight(id, {rgb: {r,g,b}});
    },
    setLightBrightness(id, brightness){
        check(id, String);
        check(brightness, Number);
        manual.setLight(id, {bri: brightness});
    },
    setLightRandom(id){
        if(!Meteor.isServer) return;
        check(id, String);
        manual.setLightRandom(id);
    },
    allOff(){
        if(!Meteor.isServer) return;
        manual.setAllOff();
    }
});<|endoftext|>


<commit_before>"use strict"

import React from "react"
import StatusMessage from "./status-message"
import PollTable from "./poll-table"
import PollReport from "./poll-report"
import Nav from "./nav"
import throttle from "lodash/function/throttle"

// Declare here for global throttling
const updateWho = throttle((flux, who) => flux.getActions("poll").updateWho(who), 5000)

export default class App extends React.Component {
  constructor(props) {
    super(props)
    // "who" is a state here for better reactivity (*)
    this.state = {"who": props.who}
  }

  onChangeWho(e) {
    // (*) Note this function is throttled because it's an onChange
    // (*) so prop "who" will not always be updated, which could cause undecent latency
    updateWho(this.props.flux, e.target.value)
    // (*) So we rely on state
    this.setState({"who": e.target.value})
  }

  isValidWho(who) {
    return who.length > 3 && !who.match(/^\s+/) && !who.match(/\s+$/)
  }

  render() {
    const isValidWho = this.state.who && this.isValidWho(String(this.state.who))
    const props = {...this.props, ...this.state, validWho: isValidWho}
    const input = this.props.showTable ? <input placeholder="Entrez votre nom" defaultValue={ this.state.who } onChange={ e => this.onChangeWho(e) } className={ isValidWho ? "" : "error" } /> : null
    const inputError = !isValidWho ? Nom invalide : null
    const table = this.props.showTable ? <PollTable { ...props } /> : null
    const report = this.props.showReport ? <PollReport { ...props } /> : null

    return (

        { input } { inputError }
        { table }

        { report }
        <StatusMessage />

    );
  }
}
<commit_msg>Do not trigger action if invalid who<commit_after>"use strict"

import React from "react"
import StatusMessage from "./status-message"
import PollTable from "./poll-table"
import PollReport from "./poll-report"
import Nav from "./nav"
import throttle from "lodash/function/throttle"

// Declare here for global throttling
const updateWho = throttle((flux, who) => flux.getActions("poll").updateWho(who), 5000)

export default class App extends React.Component {
  constructor(props) {
    super(props)
    // "who" is a state here for better reactivity (*)
    this.state = {"who": props.who}
  }

  onChangeWho(e) {
    if (this.isValidWho(e.target.value)) {
      // (*) Note this function is throttled because it's an onChange
      // (*) so prop "who" will not always be updated, which could cause undecent latency
      updateWho(this.props.flux, e.target.value)
      // (*) So we rely on state
    }
    this.setState({"who": e.target.value})
  }

  isValidWho(who) {
    return who.length > 3 && !who.match(/^\s+/) && !who.match(/\s+$/)
  }

  render() {
    const isValidWho = this.state.who && this.isValidWho(String(this.state.who))
    const props = {...this.props, ...this.state, validWho: isValidWho}
    const input = this.props.showTable ? <input placeholder="Entrez votre nom" defaultValue={ this.state.who } onChange={ e => this.onChangeWho(e) } className={ isValidWho ? "" : "error" } /> : null
    const inputError = !isValidWho ? Entrez un nom valide pour participer : null
    const table = this.props.showTable ? <PollTable { ...props } /> : null
    const report = this.props.showReport ? <PollReport { ...props } /> : null

    return (

        { input } { inputError }
        { table }

        { report }
        <StatusMessage />

    );
  }
}
<|endoftext|>


<commit_before>// plone integration for textext.
//
// Author: Rok Garbas
// Contact: rok@garbas.si
// Version: 1.0
// Depends:
//    ++resource++plone.app.jquery.js
//    ++resource++plone.app.widgets/textext.js
//
// Description:
//
// License:
//
// Copyright (C) 2010 Plone Foundation
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

/*jshint bitwise:true, curly:true, eqeqeq:true, immed:true, latedef:true,
  newcap:true, noarg:true, noempty:true, nonew:true, plusplus:true,
  undef:true, strict:true, trailing:true, browser:true, evil:true */
/*global define:false */

define([
  'jquery',
  'underscore',
  'js/patterns/base',
  'js/patterns/select2',
  'js/patterns/queryhelper'
], function($, _, Base, Select2, QueryHelper) {
  "use strict";

  var RelatedItems = Base.extend({
    name: "relateditems",
    browsing: false,
    currentPath: null,
    defaults: {
      ajaxvocabulary: null, // must be set to work
      multiple: true,
      tokenSeparators: [",", " "],
      separator: ",",
      orderable: true,
      cache: true,
      closeOnSelect: false,
      basePath: '/',
      browseText: 'Browse',
      searchText: 'Search',
      homeText: 'home',
      folderTypes: ['Folder'],
      attributes: ['UID','Title', 'Type', 'path'],
      dropdownCssClass: 'pat-relateditems-dropdown',
      resultTemplate: '' +
        '<div class="pat-relateditems-result pat-relateditems-type-<%= Type %>">' +
        ' ' +
        '  <% if (folderish) { %>' +
        '     <a class="pat-relateditems-result-browse" href="#" data-path="<%= path %>">' +
        '       ' +
        '    ' +
        '   <% } %>' +
        '   ' +
        '       ' +
        '   ' +
        ' ' +
        ' <%= Title %>' +
        ' <%= path %>' +
        '
',
      resultTemplateSelector: null,
      selectionTemplate: '' +
        '<span class="pat-relateditems-item pat-relateditems-type-<%= Type %>">' +
        ' <%= Title %>' +
        ' <%= path %>' +
        '',
      selectionTemplateSelector: null,
      tabsTemplate: '' +
        '
' +
        ' <%= searchText %>' +
        ' <%= browseText %>' +
        '
',
      tabsTemplateSelector: null,
      breadCrumbsTemplate: '' +
        '<%= items %>',
      breadCrumbsTemplateSelector: null,
      breadCrumbTemplate: '' +
        '/<a href="<%= path %>"><%= text %>',
      breadCrumbTemplateSelector: null,
      escapeMarkup: function(text) {
        return text;
      }
    },
    applyTemplate: function(tpl, item) {
      var self = this;
      var template;
      if (self.options[tpl+'TemplateSelector']) {
        template = $(self.options[tpl+'TemplateSelector']).html();
        if (!template) {
          template = self.options[tpl+'Template'];
        }
      } else {
        template = self.options[tpl+'Template'];
      }
      return _.template(template, item);
    },
    activateBrowsing: function(){
      var self = this;
      self.browsing = true;
      self.setBreadCrumbs();
      self.setTabs();
    },
    deactivateBrowsing: function(){
      var self = this;
      self.browsing = false;
      self.setBreadCrumbs();
      self.setTabs();
    },
    browseTo: function(path) {
      var self = this;
      self.trigger('before-browse');
      self.currentPath = path;
      self.activateBrowsing();
      self.$el.select2('close');
      self.$el.select2('open');
      self.trigger('after-browse');
    },
    setTabs: function() {
      var self = this;
      if (self.browsing) {
        self.$browseBtn.addClass('active');
        self.$searchBtn.removeClass('active');
      } else {
        self.$browseBtn.removeClass('active');
        self.$searchBtn.addClass('active');
      }
    },
    setBreadCrumbs: function() {
      var self = this;
      var path = self.currentPath ? self.currentPath : self.options.basePath;
      if (self.browsing) {
        var html;
        if (path === '/') {
          html = self.applyTemplate('breadCrumbs', {items:''});
        } else {
          var paths = path.split('/');
          var itemPath = '';
          var itemsHtml = '';
          _.each(paths, function(node) {
            if (node !== '') {
              var item = {};
              itemPath = itemPath + '/' + node;
              item.text = node;
              item.path = itemPath;
              itemsHtml = itemsHtml + self.applyTemplate('breadCrumb', item);
            }
          });
          html = self.applyTemplate('breadCrumbs', {items:itemsHtml});
        }
        var $crumbs = $(html);
        $('a', $crumbs).on('click', function(event) {
          self.browseTo($(this).attr('href'));
          return false;
        });
        self.$browsePath.html($crumbs);
      } else {
        self.$browsePath.html('');
      }
    },
    selectItem: function(item) {
      var self = this;
      self.trigger('selecting');
      var data = self.$el.select2("data");
      data.push(item);
      self.$el.select2("data", data);
      self.trigger('selected');
    },
    init: function() {
      var self = this;

      self.query = new QueryHelper(self.$el,
          $.extend(true, {}, self.options, {basePattern: self}));

      self.$el.wrap('
');
      self.$container = self.$el.parents('.pat-relateditems-container');
      self.$container.width(self.options.width);

      Select2.prototype.initializeValueMap.call(self);
      Select2.prototype.initializeTags.call(self);
      Select2.prototype.initializeOrdering.call(self);

      if(self.query.valid){
        self.options.ajax = self.query.selectAjax();
      } else {
        self.options.tags = [];
      }

      self.options.formatSelection = function(item) {
        return self.applyTemplate('selection', item);
      };

      self.options.formatResult = function(item) {
        if(!item.Type || _.indexOf(self.options.folderTypes, item.Type) === -1){
          item.folderish = false;
        }else{
          item.folderish = true;
        }
        var result = $(self.applyTemplate('result', item));

        $('.pat-relateditems-result-select', result).on('click', function(event) {
          self.selectItem(item);
          return false;
        });

        $('.pat-relateditems-result-browse', result).on('click', function(event) {
          var path = $(this).data('path');
          self.browseTo(path);
          return false;
        });

        return $(result);
      };

      self.options.initSelection = function(element, callback) {
        var data = [];
        var value = $(element).val();
        if (value !== '') {
          var ids = value.split(',');
          self.query.search(
            'UID', 'plone.app.querystring.operation.list.contains', ids,
            function(data){
              callback(data.results);
          });
        }
      };

      self.options.id = function(item) {
        var folderish = item.folderish;
        /* Trick select2 into not removing a folder from the list if it has been added.
           Allows user to browse folders that have been selected. */
        if (item.Title === 'About Us') {
          var foo = 0;
        }
        if (folderish) {
          var data = self.$el.select2("data");
          var selected = false;
          if (item.Title === 'About Us') {
            selected = false;
          }
          _.each(data, function(selItem) {
            if (item.UID === selItem.UID) {
              selected = true;
            }
          }, self);
          if (selected) {
            return item.UID + Math.random() + '' + Math.random();
          }
        }
        return item.UID;
      };

      Select2.prototype.initializeSelect2.call(self);

      // Browsing functionality
      var browseOpts = {
        browseText: self.options.browseText,
        searchText: self.options.searchText
      };
      self.$browse = $(self.applyTemplate('tabs', browseOpts));
      self.$container.prepend(self.$browse);
      self.$browseBtn = $('.pat-relateditems-tabs-browse', self.$browse);
      self.$searchBtn = $('.pat-relateditems-tabs-search', self.$browse);
      self.$browsePath = $('');
      self.$browse.after(self.$browsePath);
      self.$browsePath.width(self.$container.width()-2);
      self.deactivateBrowsing();

      self.$browseBtn.click(function(e){
        self.activateBrowsing();
        self.$el.select2('close');
        self.$el.select2('open');
        return false;
      });

      self.$searchBtn.click(function(e){
        self.deactivateBrowsing();
        self.$el.select2('close');
        self.$el.select2('open');
        return false;
      });

      self.$el.on("select2-selecting", function(event) {
        event.preventDefault();
      });

    }
  });

  return RelatedItems;

});

<commit_msg>hide add button on folder after it has been added<commit_after>// plone integration for textext.
//
// Author: Rok Garbas
// Contact: rok@garbas.si
// Version: 1.0
// Depends:
//    ++resource++plone.app.jquery.js
//    ++resource++plone.app.widgets/textext.js
//
// Description:
//
// License:
//
// Copyright (C) 2010 Plone Foundation
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

/*jshint bitwise:true, curly:true, eqeqeq:true, immed:true, latedef:true,
  newcap:true, noarg:true, noempty:true, nonew:true, plusplus:true,
  undef:true, strict:true, trailing:true, browser:true, evil:true */
/*global define:false */

define([
  'jquery',
  'underscore',
  'js/patterns/base',
  'js/patterns/select2',
  'js/patterns/queryhelper'
], function($, _, Base, Select2, QueryHelper) {
  "use strict";

  var RelatedItems = Base.extend({
    name: "relateditems",
    browsing: false,
    currentPath: null,
    defaults: {
      ajaxvocabulary: null, // must be set to work
      multiple: true,
      tokenSeparators: [",", " "],
      separator: ",",
      orderable: true,
      cache: true,
      closeOnSelect: false,
      basePath: '/',
      browseText: 'Browse',
      searchText: 'Search',
      homeText: 'home',
      folderTypes: ['Folder'],
      attributes: ['UID','Title', 'Type', 'path'],
      dropdownCssClass: 'pat-relateditems-dropdown',
      resultTemplate: '' +
        '<div class="pat-relateditems-result pat-relateditems-type-<%= Type %>">' +
        ' ' +
        '  <% if (folderish) { %>' +
        '     <a class="pat-relateditems-result-browse" href="#" data-path="<%= path %>">' +
        '       ' +
        '    ' +
        '   <% } %>' +
        '  <% if (!selected) { %>' +
        '     ' +
        '         ' +
        '     ' +
        '  <% } %>' +
        ' ' +
        ' <%= Title %>' +
        ' <%= path %>' +
        '
',
      resultTemplateSelector: null,
      selectionTemplate: '' +
        '<span class="pat-relateditems-item pat-relateditems-type-<%= Type %>">' +
        ' <%= Title %>' +
        ' <%= path %>' +
        '',
      selectionTemplateSelector: null,
      tabsTemplate: '' +
        '
' +
        ' <%= searchText %>' +
        ' <%= browseText %>' +
        '
',
      tabsTemplateSelector: null,
      breadCrumbsTemplate: '' +
        '<%= items %>',
      breadCrumbsTemplateSelector: null,
      breadCrumbTemplate: '' +
        '/<a href="<%= path %>"><%= text %>',
      breadCrumbTemplateSelector: null,
      escapeMarkup: function(text) {
        return text;
      }
    },
    applyTemplate: function(tpl, item) {
      var self = this;
      var template;
      if (self.options[tpl+'TemplateSelector']) {
        template = $(self.options[tpl+'TemplateSelector']).html();
        if (!template) {
          template = self.options[tpl+'Template'];
        }
      } else {
        template = self.options[tpl+'Template'];
      }
      return _.template(template, item);
    },
    activateBrowsing: function(){
      var self = this;
      self.browsing = true;
      self.setBreadCrumbs();
      self.setTabs();
    },
    deactivateBrowsing: function(){
      var self = this;
      self.browsing = false;
      self.setBreadCrumbs();
      self.setTabs();
    },
    browseTo: function(path) {
      var self = this;
      self.trigger('before-browse');
      self.currentPath = path;
      self.activateBrowsing();
      self.$el.select2('close');
      self.$el.select2('open');
      self.trigger('after-browse');
    },
    setTabs: function() {
      var self = this;
      if (self.browsing) {
        self.$browseBtn.addClass('active');
        self.$searchBtn.removeClass('active');
      } else {
        self.$browseBtn.removeClass('active');
        self.$searchBtn.addClass('active');
      }
    },
    setBreadCrumbs: function() {
      var self = this;
      var path = self.currentPath ? self.currentPath : self.options.basePath;
      if (self.browsing) {
        var html;
        if (path === '/') {
          html = self.applyTemplate('breadCrumbs', {items:''});
        } else {
          var paths = path.split('/');
          var itemPath = '';
          var itemsHtml = '';
          _.each(paths, function(node) {
            if (node !== '') {
              var item = {};
              itemPath = itemPath + '/' + node;
              item.text = node;
              item.path = itemPath;
              itemsHtml = itemsHtml + self.applyTemplate('breadCrumb', item);
            }
          });
          html = self.applyTemplate('breadCrumbs', {items:itemsHtml});
        }
        var $crumbs = $(html);
        $('a', $crumbs).on('click', function(event) {
          self.browseTo($(this).attr('href'));
          return false;
        });
        self.$browsePath.html($crumbs);
      } else {
        self.$browsePath.html('');
      }
    },
    selectItem: function(item) {
      var self = this;
      self.trigger('selecting');
      var data = self.$el.select2("data");
      data.push(item);
      self.$el.select2("data", data);
      item.selected = true;
      self.trigger('selected');
    },
    init: function() {
      var self = this;

      self.query = new QueryHelper(self.$el,
          $.extend(true, {}, self.options, {basePattern: self}));

      self.$el.wrap('
');
      self.$container = self.$el.parents('.pat-relateditems-container');
      self.$container.width(self.options.width);

      Select2.prototype.initializeValueMap.call(self);
      Select2.prototype.initializeTags.call(self);
      Select2.prototype.initializeOrdering.call(self);

      if(self.query.valid){
        self.options.ajax = self.query.selectAjax();
      } else {
        self.options.tags = [];
      }

      self.options.formatSelection = function(item) {
        return self.applyTemplate('selection', item);
      };

      self.options.formatResult = function(item) {
        if(!item.Type || _.indexOf(self.options.folderTypes, item.Type) === -1){
          item.folderish = false;
        }else{
          item.folderish = true;
        }

        if (item.selected === undefined) {
          var data = self.$el.select2("data");
          item.selected = false;
          _.each(data, function(obj) {
            if (obj.UID === item.UID) {
              item.selected = true;
            }
          });
        }

        var result = $(self.applyTemplate('result', item));

        $('.pat-relateditems-result-select', result).on('click', function(event) {
          self.selectItem(item);
          $(this).remove();
          return false;
        });

        $('.pat-relateditems-result-browse', result).on('click', function(event) {
          var path = $(this).data('path');
          self.browseTo(path);
          return false;
        });

        return $(result);
      };

      self.options.initSelection = function(element, callback) {
        var data = [];
        var value = $(element).val();
        if (value !== '') {
          var ids = value.split(',');
          self.query.search(
            'UID', 'plone.app.querystring.operation.list.contains', ids,
            function(data){
              callback(data.results);
          });
        }
      };

      self.options.id = function(item) {
        var folderish = item.folderish;
        /* Trick select2 into not removing a folder from the list if it has been added.
           Allows user to browse folders that have been selected. */
        if (item.Title === 'About Us') {
          var foo = 0;
        }
        if (folderish) {
          var data = self.$el.select2("data");
          var selected = false;
          if (item.Title === 'About Us') {
            selected = false;
          }
          _.each(data, function(selItem) {
            if (item.UID === selItem.UID) {
              selected = true;
            }
          }, self);
          if (selected) {
            return item.UID + Math.random() + '' + Math.random();
          }
        }
        return item.UID;
      };

      Select2.prototype.initializeSelect2.call(self);

      // Browsing functionality
      var browseOpts = {
        browseText: self.options.browseText,
        searchText: self.options.searchText
      };
      self.$browse = $(self.applyTemplate('tabs', browseOpts));
      self.$container.prepend(self.$browse);
      self.$browseBtn = $('.pat-relateditems-tabs-browse', self.$browse);
      self.$searchBtn = $('.pat-relateditems-tabs-search', self.$browse);
      self.$browsePath = $('');
      self.$browse.after(self.$browsePath);
      self.$browsePath.width(self.$container.width()-2);
      self.deactivateBrowsing();

      self.$browseBtn.click(function(e){
        self.activateBrowsing();
        self.$el.select2('close');
        self.$el.select2('open');
        return false;
      });

      self.$searchBtn.click(function(e){
        self.deactivateBrowsing();
        self.$el.select2('close');
        self.$el.select2('open');
        return false;
      });

      self.$el.on("select2-selecting", function(event) {
        event.preventDefault();
      });

    }
  });

  return RelatedItems;

});

<|endoftext|>