<commit_before>/**
 * Menus
 *
 * Handles the behaviour of menu items
 *
 * References:
 *
 * - http://api.jquery.com/slidetoggle/
 * - http://stackoverflow.com/questions/6752677/use-jquery-to-automatically-add-arrows-to-all-parent-menus
 */

(function( $ ) {
    'use strict';

    function subMenuToggle( selector ) {
        var html = $( selector ).html();

        if ( html.indexOf( 'fa-caret-up' ) >= 0 ) {
            $( selector ).html( 'Sub-menu' );
        } else {
            $( selector ).html( 'Sub-menu' );
        }

        $( selector ).toggleClass( 'closed' );
        $( selector ).next( 'ul' ).slideToggle({
            'duration': 200
        }); // override `display:none;` in CSS for hover
    }

    // Make the mobile menu button work
    $( '.js-mobile-menu' ).hide();
    $( '.js-mobile-menu-button' ).attr( 'href', '#' );
    $( '.js-mobile-menu-button' ).on( 'click', function( event ) {
        $( '.js-mobile-menu' ).slideToggle({
            'duration': 200
        });

        event.preventDefault();
    });

    // Add icons to all parent menu items
    $( '.menu li > ul' ).before( '<button class="js-sub-menu-button sub-menu-toggle closed">Sub-menu</button>' );

    // Sub-menus toggle
    $( '.js-sub-menu-button' ).next( 'ul' ).hide();
    $( '.js-sub-menu-button' ).prev( 'a' ).on( 'click', function( event ) {
        if ( '#' == $( this ).attr( 'href' ) ) {
            subMenuToggle( $( this ).next( 'button' ) );

            event.preventDefault();
        }
    });
    $( '.js-sub-menu-button' ).on( 'click', function( event ) {
        subMenuToggle( this );

        event.preventDefault();
    });
})( jQuery );
<commit_msg>added @since annotation to menu js doc block<commit_after>/**
 * Menus
 *
 * Handles the behaviour of menu items
 *
 * @since 0.1.0
 *
 * References:
 *
 * - http://api.jquery.com/slidetoggle/
 * - http://stackoverflow.com/questions/6752677/use-jquery-to-automatically-add-arrows-to-all-parent-menus
 */

(function( $ ) {
    'use strict';

    function subMenuToggle( selector ) {
        var html = $( selector ).html();

        if ( html.indexOf( 'fa-caret-up' ) >= 0 ) {
            $( selector ).html( 'Sub-menu' );
        } else {
            $( selector ).html( 'Sub-menu' );
        }

        $( selector ).toggleClass( 'closed' );
        $( selector ).next( 'ul' ).slideToggle({
            'duration': 200
        }); // override `display:none;` in CSS for hover
    }

    // Make the mobile menu button work
    $( '.js-mobile-menu' ).hide();
    $( '.js-mobile-menu-button' ).attr( 'href', '#' );
    $( '.js-mobile-menu-button' ).on( 'click', function( event ) {
        $( '.js-mobile-menu' ).slideToggle({
            'duration': 200
        });

        event.preventDefault();
    });

    // Add icons to all parent menu items
    $( '.menu li > ul' ).before( '<button class="js-sub-menu-button sub-menu-toggle closed">Sub-menu</button>' );

    // Sub-menus toggle
    $( '.js-sub-menu-button' ).next( 'ul' ).hide();
    $( '.js-sub-menu-button' ).prev( 'a' ).on( 'click', function( event ) {
        if ( '#' == $( this ).attr( 'href' ) ) {
            subMenuToggle( $( this ).next( 'button' ) );

            event.preventDefault();
        }
    });
    $( '.js-sub-menu-button' ).on( 'click', function( event ) {
        subMenuToggle( this );

        event.preventDefault();
    });
})( jQuery );
<|endoftext|>


<commit_before>// From https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md

var jsdom = require('jsdom').jsdom;
var sinon = require('sinon');

var exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('

');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

// For some reason, this property does not get set above.
global.Image = global.window.Image;

// React assumes console.debug can/should be used when the userAgent matches Chrome
// For tests, we want these debug statements suppressed
global.console.debug = () => {};

global.navigator = {
  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) nteract/0.0.12 Chrome/50.0.2661.102 Electron/1.1.3 Safari/537.36',
  platform: 'MacIntel',
};

global.Range = function Range() {
}

const createContextualFragment = (html) => {
  const div = document.createElement('div');
  div.innerHTML = html;
  return div.children[0]; // so hokey it's not even funny
};

Range.prototype.createContextualFragment = function createContextualFragment(html) {
  createContextualFragment(html);
}

// HACK: Polyfil that allows codemirror to render in a JSDOM env.
global.window.document.createRange = function createRange() {
  return {
    setEnd: () => {},
    setStart: () => {},
    getBoundingClientRect: () => {
      return { right: 0 };
    },
    getClientRects: () => {
      return []
    },
    createContextualFragment,
  }
};

// Mocks for tests
var mock = require('mock-require');
mock('electron-json-storage', {
  'get': function(key, callback){
    callback(null, { theme: 'light' });
  },
  'set': function(key, json, callback) {
    if (!json && !key) {
      callback(new Error('Must provide JSON and key'));
    }

    callback(null);
  },
})

mock('plotly.js/dist/plotly', {
  'newPlot': function(data, layout, config) {},
})

mock('enchannel-zmq-backend', {
  'createControlSubject': function(config, identity) {
    return;
  },
  'createStdinSubject': function(config, identity) {
    return;
  },
  createIOPubSubject: function(config, identity) {
    return;
  },
  createShellSubject: function(config, identity) {
    return;
  },
})

mock('electron', {
  'shell': {
    'openExternal': function(url) { },
  },
  'remote': {
    'app': {
      getPath: function(key) {
        if(key === 'home') {
          return '/home/home/on/the/range';
        }
        throw Error('not mocked');
      }
    },
    'dialog': {
      'showSaveDialog': function(config) { },
    }
    'BrowserWindow': {
      'getFocusedWindow': function() {
        return {
          'setTitle': function() {},
        };
      }
    },
    'getCurrentWindow': function() {
      return {
        'setTitle': function(){},
        'setDocumentEdited': function(){},
        'setRepresentedFilename': function() {},
      };
    }
  },
  'webFrame': {
    'setZoomLevel': function(zoom) { },
    'getZoomLevel': function() { return 1; },
  },
  'ipcRenderer': {
    'on': function() {},
    'send': function(message, action) {}
  },
});

mock('github', function () {
  return {
    authenticate: function(config) { },
    gists: {
        edit: function(request, callback) { callback(null, { id: 123, html_url: 'foo' }) },
        create: function(request, callback) { callback(null, { id: 123, html_url: 'foo' }) },
    },
  };
});

mock('react-notification-system', function () {
  return {
    'addNotification': function(config) { },
    'render': function() {return null;},
  };
});

mock('spawnteract', {
  'launch': function(kernelSpec, config) {
    function writeConnectionFile(config) { return new Promise((resolve, reject) => {
        try {
         resolve({config, connectionFile: 'connectionFile'});
        }
        catch (err) {
          reject(err);
        }
      })
    }
    return writeConnectionFile('config').then((c) => {
      return {
        spawn: 'runningKernel',
        connectionFile: c.connectionFile,
        config: c.config,
        kernelSpec,
      };
    });
  },
});

mock('fs', {
  unlinkSync: sinon.spy(),
});
<commit_msg>refactor(saveDialog): Fix missing comma<commit_after>// From https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md

var jsdom = require('jsdom').jsdom;
var sinon = require('sinon');

var exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('

');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

// For some reason, this property does not get set above.
global.Image = global.window.Image;

// React assumes console.debug can/should be used when the userAgent matches Chrome
// For tests, we want these debug statements suppressed
global.console.debug = () => {};

global.navigator = {
  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) nteract/0.0.12 Chrome/50.0.2661.102 Electron/1.1.3 Safari/537.36',
  platform: 'MacIntel',
};

global.Range = function Range() {
}

const createContextualFragment = (html) => {
  const div = document.createElement('div');
  div.innerHTML = html;
  return div.children[0]; // so hokey it's not even funny
};

Range.prototype.createContextualFragment = function createContextualFragment(html) {
  createContextualFragment(html);
}

// HACK: Polyfil that allows codemirror to render in a JSDOM env.
global.window.document.createRange = function createRange() {
  return {
    setEnd: () => {},
    setStart: () => {},
    getBoundingClientRect: () => {
      return { right: 0 };
    },
    getClientRects: () => {
      return []
    },
    createContextualFragment,
  }
};

// Mocks for tests
var mock = require('mock-require');
mock('electron-json-storage', {
  'get': function(key, callback){
    callback(null, { theme: 'light' });
  },
  'set': function(key, json, callback) {
    if (!json && !key) {
      callback(new Error('Must provide JSON and key'));
    }

    callback(null);
  },
})

mock('plotly.js/dist/plotly', {
  'newPlot': function(data, layout, config) {},
})

mock('enchannel-zmq-backend', {
  'createControlSubject': function(config, identity) {
    return;
  },
  'createStdinSubject': function(config, identity) {
    return;
  },
  createIOPubSubject: function(config, identity) {
    return;
  },
  createShellSubject: function(config, identity) {
    return;
  },
})

mock('electron', {
  'shell': {
    'openExternal': function(url) { },
  },
  'remote': {
    'app': {
      getPath: function(key) {
        if(key === 'home') {
          return '/home/home/on/the/range';
        }
        throw Error('not mocked');
      }
    },
    'dialog': {
      'showSaveDialog': function(config) { },
    },
    'BrowserWindow': {
      'getFocusedWindow': function() {
        return {
          'setTitle': function() {},
        };
      }
    },
    'getCurrentWindow': function() {
      return {
        'setTitle': function(){},
        'setDocumentEdited': function(){},
        'setRepresentedFilename': function() {},
      };
    }
  },
  'webFrame': {
    'setZoomLevel': function(zoom) { },
    'getZoomLevel': function() { return 1; },
  },
  'ipcRenderer': {
    'on': function() {},
    'send': function(message, action) {}
  },
});

mock('github', function () {
  return {
    authenticate: function(config) { },
    gists: {
        edit: function(request, callback) { callback(null, { id: 123, html_url: 'foo' }) },
        create: function(request, callback) { callback(null, { id: 123, html_url: 'foo' }) },
    },
  };
});

mock('react-notification-system', function () {
  return {
    'addNotification': function(config) { },
    'render': function() {return null;},
  };
});

mock('spawnteract', {
  'launch': function(kernelSpec, config) {
    function writeConnectionFile(config) { return new Promise((resolve, reject) => {
        try {
         resolve({config, connectionFile: 'connectionFile'});
        }
        catch (err) {
          reject(err);
        }
      })
    }
    return writeConnectionFile('config').then((c) => {
      return {
        spawn: 'runningKernel',
        connectionFile: c.connectionFile,
        config: c.config,
        kernelSpec,
      };
    });
  },
});

mock('fs', {
  unlinkSync: sinon.spy(),
});
<|endoftext|>


<commit_before>
var Quad=Fiber.extend(function() {
  return {
    init: function(options) {

      this.size=1.0;

      if(options) {
        if(options.size) {
          this.size = options.size;
        }
      }
      this.power = {
        left: 0,
        right: 0,
        left_motor: 0,
        right_motor: 0,
        left_position: -0.4*this.size * 0.5,
        right_position: 0.4*this.size * 0.5,
        max: 4*(this.size*this.size)
      };

      this.audio={
        left: audio_load("motor"),
        right: audio_load("motor")
      };

      this.body = new p2.Body({
        mass: 0.2*(this.size*this.size),
        position: [0, 0.08*this.size]
      });

      this.body.inertia=100 * this.size;

      var blade_shape = new p2.Rectangle(0.8*this.size, 0.07*this.size*0.5);
      this.body.addShape(blade_shape, [0, -0.07*this.size]);

      var body_shape = new p2.Rectangle(0.6*this.size, 0.07*this.size);
      this.body.addShape(body_shape, [0, -0.07*this.size/2]);

      var base_shape = new p2.Rectangle(0.2*this.size, 0.05*this.size);
      this.body.addShape(base_shape, [0, 0.07*this.size/2]);

      this.target = [0, 0];
      this.target_offset = [0, 0];

      this.body.updateMassProperties();

      this.body.damping=0.5;
      this.body.angularDamping=0.5;

      prop.world.world.addBody(this.body);

      this.autopilot = {
        enabled: true,
        vspeed: new PID(0.5, 0.2, 0.1),
        hspeed: new PID(50, 40, 0.7),
        angular_velocity: new PID(0.3, 0.005, 0.001)
      };

      if(options) {
        if(options.position) this.body.position=options.position;
        if(options.target_offset) this.target_offset=options.target_offset;
      }
      this.body.position[1]=Math.max(this.body.position[1], 0.08*this.size);

    },

    reset: function() {

    },

    updateTarget: function(t) {
      t*=0.2;
      var target=[0, 0];
//      t=crange(-1, Math.sin(t), 1, 0.5, 1.0);
      target[0] = prop.quad.target[0] + this.target_offset[0];
      target[1] = prop.quad.target[1] + this.target_offset[1];
      target[1] = Math.max(0, target[1]);
//      return target;
      target[0] = prop.quad.target[0] + (-Math.sin(t)*this.target_offset[0]) + (Math.cos(t)*this.target_offset[1]);
      target[1] = prop.quad.target[1] + (Math.sin(t)*this.target_offset[1]) + (Math.cos(t)*this.target_offset[0]);
      target[1] = Math.max(0, target[1]);
      return target;
    },

    updateAutopilot: function() {
      var real_target=this.updateTarget(time());
      var target=this.updateTarget(time()+0.5);

      var target_position = target[0];
      var target_altitude = target[1];
      var target_hspeed = crange(-80, target_position - this.body.position[0], 80, -80, 80);

      var floor=1.0;

      if(target_altitude < 0.1) {
        target_altitude  = scrange(0.02, Math.abs(real_target[0] - this.body.position[0]), 0.5*this.size, 0.08*this.size, Math.max(target_altitude, floor));
        target_hspeed   *= crange(0, this.body.position[1], floor*2, 1.5, 1);
        target_hspeed   *= crange(0, this.body.position[1], floor, 3, 1);
      }

      var target_vspeed = crange(-70, target_altitude - this.body.position[1], 70, -70, 70);
      target_vspeed    *= crange(0, Math.abs(target_altitude - this.body.position[1]), 60, 1.2, 1);
      target_vspeed    *= crange(0.1*this.size, this.body.position[1], floor, 3, 1);

      this.autopilot.vspeed.target = target_vspeed;
      this.autopilot.vspeed.input  = this.body.velocity[1];
      this.autopilot.vspeed.tick();

      this.autopilot.hspeed.target = target_hspeed;
      this.autopilot.hspeed.input  = this.body.velocity[0];
      var e=750;
      this.autopilot.hspeed.error  = clamp(-e, this.autopilot.hspeed.error, e);
      this.autopilot.hspeed.tick();

      var target_angle=crange(-80, this.autopilot.hspeed.get(), 80, Math.PI/3, -Math.PI/3);
      target_angle *= crange(0.1*this.size, this.body.position[1], floor, 0, 1);

      if(prop.quad.flip && this.body.position[1] > floor) target_angle+=Math.PI;

      var s=crange(1,this.size, 3, 1, 0.1)*2;
      var target_angular_velocity = crange(-Math.PI/2, angle_difference(this.body.angle, target_angle), Math.PI/2, Math.PI*4*s, -Math.PI*4*s);

      this.autopilot.angular_velocity.target = target_angular_velocity;
      this.autopilot.angular_velocity.input  = this.body.angularVelocity;
      this.autopilot.angular_velocity.tick();

      var mix=0.7;
      mix+=crange(1, this.size,5, 0, -0.3);

      var angle=this.body.angle;
      this.power.left   = this.autopilot.vspeed.get() * Math.cos(angle);
      this.power.right  = this.autopilot.vspeed.get() * Math.cos(angle);

      this.power.left=clamp(-mix, this.power.left, mix);
      this.power.right=clamp(-mix, this.power.right, mix);

      mix*=1.2;

      this.power.left  -= this.autopilot.angular_velocity.get() * (1-mix);
      this.power.right += this.autopilot.angular_velocity.get() * (1-mix);

      if(target_altitude < 0.1*this.size) {
        this.power.left *= crange(0.08*this.size, this.body.position[1], 0.12*this.size, 0, 1);
        this.power.right *= crange(0.08*this.size, this.body.position[1], 0.12*this.size, 0, 1);
      }

    },

    updatePower: function() {

      var left=clamp(-1, this.power.left, 1);
      var right=clamp(-1, this.power.right, 1);

      var mix=0.99;

      this.power.left_motor  = (left *mix) + (this.power.left_motor*(1-mix));
      this.power.right_motor = (right*mix) + (this.power.right_motor*(1-mix));

      var v=this.body.angle;
      var thrust=this.power.max*this.power.left_motor;
      var force=[-Math.sin(v)*thrust, Math.cos(v)*thrust];
      var point=[0,0];
      this.body.toWorldFrame(point,[this.power.left_position,0]);
      this.body.applyForce(force,point);

      thrust=this.power.max*this.power.right_motor;
      force=[-Math.sin(v)*thrust, Math.cos(v)*thrust];
      point=[0,0];
      this.body.toWorldFrame(point,[this.power.right_position,0]);
      this.body.applyForce(force,point);
    },
    updateAudio: function() {
      var v=0.5;
      this.audio.left.setVolume(crange(0.05,Math.abs(this.power.left_motor), 0.1, 0, 0.7)*v);
      this.audio.right.setVolume(crange(0.05,Math.abs(this.power.right_motor), 0.1, 0, 0.7)*v);
      var s=crange(1, this.size, 5, 1, 0.5);
      this.audio.left.setRate(crange(0,Math.abs(this.power.left_motor), 1, 0.5, 3)*s);
      this.audio.right.setRate(crange(0,Math.abs(this.power.right_motor), 1, 0.5, 3)*s);
    },
    update: function() {
      this.target = this.updateTarget(time());
      if(this.autopilot.enabled) this.updateAutopilot();
      this.updatePower();
      this.updateAudio();
    }
  };
});

function quad_init_pre() {
  prop.quad={};
  prop.quad.quads=[];
  prop.quad.target_speed=[0,0];
  prop.quad.target=[5,3];
  prop.quad.flip=false;
}

function quad_init() {
  quad_add(new Quad({
    position: [0, 10],
    target_offset: [0, 0]
  }));
  return;
  var separation=4;
  var d = 3;
  var number=2;
  for(var i=0;i<number;i++) {
    var angle=i/number*Math.PI*2;
    quad_add(new Quad({
      position: [((i-number/2)*separation), 0],
      target_offset: [Math.sin(angle)*d, Math.cos(angle)*d]
    }));
  }
}

function quad_add(quad) {
  if(!quad) quad = new Quad({});
  prop.quad.quads.push(quad);
}

function quad_update_pre() {
  prop.quad.target_speed[0] *= 0.9;
  prop.quad.target_speed[1] *= 0.9;
  prop.quad.target[0] += prop.quad.target_speed[0];
  prop.quad.target[1] += prop.quad.target_speed[1];
  prop.quad.target[1]  = Math.max(0, prop.quad.target[1]);
  for(var i=0;i<prop.quad.quads.length;i++) {
    prop.quad.quads[i].update();
  }
}

function quad_reset() {
  for(var i=0;i<prop.quad.quads.length;i++) {
    prop.quad.quads[i].reset();
  }
}
<commit_msg>improved autopilot<commit_after>
var Quad=Fiber.extend(function() {
  return {
    init: function(options) {

      this.size=1.0;

      if(options) {
        if(options.size) {
          this.size = options.size;
        }
      }
      this.power = {
        left: 0,
        right: 0,
        left_motor: 0,
        right_motor: 0,
        left_position: -0.4*this.size * 0.5,
        right_position: 0.4*this.size * 0.5,
        max: 4*(this.size*this.size)
      };

      this.audio={
        left: audio_load("motor"),
        right: audio_load("motor")
      };

      this.body = new p2.Body({
        mass: 0.2*(this.size*this.size),
        position: [0, 0.08*this.size]
      });

      this.body.inertia=100 * this.size;

      var blade_shape = new p2.Rectangle(0.8*this.size, 0.07*this.size*0.5);
      this.body.addShape(blade_shape, [0, -0.07*this.size]);

      var body_shape = new p2.Rectangle(0.6*this.size, 0.07*this.size);
      this.body.addShape(body_shape, [0, -0.07*this.size/2]);

      var base_shape = new p2.Rectangle(0.2*this.size, 0.05*this.size);
      this.body.addShape(base_shape, [0, 0.07*this.size/2]);

      this.target = [0, 0];
      this.target_offset = [0, 0];

      this.body.updateMassProperties();

      this.body.damping=0.5;
      this.body.angularDamping=0.5;

      prop.world.world.addBody(this.body);

      this.autopilot = {
        enabled: true,
        vspeed: new PID(0.5, 0.2, 0.1),
        hspeed: new PID(50, 40, 0.7),
        angular_velocity: new PID(0.3, 0.005, 0.001)
      };

      if(options) {
        if(options.position) this.body.position=options.position;
        if(options.target_offset) this.target_offset=options.target_offset;
        this.data = options.data;
      }
      this.body.position[1]=Math.max(this.body.position[1], 0.08*this.size);

    },

    reset: function() {

    },

    updateTarget: function(t) {
      if(this.data == "child") {
        var target = [0, 0];
        var quad=prop.quad.quads[0];
        var offset=3;
        var angle=Math.atan2(quad.body.position[0]-this.body.position[0],-(quad.body.position[1]-this.body.position[1]));
        angle=t*0.05;
        target[0] = quad.body.position[0]-Math.sin(angle)*offset;
        target[1] = quad.body.position[1]+Math.cos(angle)*offset;
        return target;
      }
      t*=0.2;
      var target=[0, 0];
//      t=crange(-1, Math.sin(t), 1, 0.5, 1.0);
      target[0] = prop.quad.target[0] + this.target_offset[0];
      target[1] = prop.quad.target[1] + this.target_offset[1];
      target[1] = Math.max(0, target[1]);
      return target;
      target[0] = prop.quad.target[0] + (-Math.sin(t)*this.target_offset[0]) + (Math.cos(t)*this.target_offset[1]);
      target[1] = prop.quad.target[1] + (Math.sin(t)*this.target_offset[1]) + (Math.cos(t)*this.target_offset[0]);
      target[1] = Math.max(0, target[1]);
      return target;
    },

    updateAutopilot: function() {
      var real_target=this.updateTarget(time());
      var target=this.updateTarget(time()+0.5);

      var target_position = target[0];
      var target_altitude = target[1];
      var target_hspeed = crange(-80, target_position - this.body.position[0], 80, -60, 60);

      var floor=1.0;

      if(target_altitude < 0.1) {
        target_altitude  = scrange(0.02, Math.abs(real_target[0] - this.body.position[0]), 0.5*this.size, 0.08*this.size, Math.max(target_altitude, floor));
        target_hspeed   *= crange(0, this.body.position[1], floor*2, 2/this.size, 1);
        target_hspeed   *= crange(0, this.body.position[1], floor, 3/this.size, 1);
      }

      var target_vspeed = crange(-700, target_altitude - this.body.position[1], 700, -700, 700);
      target_vspeed    *= crange(0, Math.abs(target_altitude - this.body.position[1]), 60, 1.2, 1);
      target_vspeed    *= crange(0.1*this.size, this.body.position[1], floor, 3, 1);

      this.autopilot.vspeed.target = target_vspeed;
      this.autopilot.vspeed.input  = this.body.velocity[1];
      var e=10;
      this.autopilot.vspeed.error  = clamp(-e, this.autopilot.vspeed.error, e);
      this.autopilot.vspeed.tick();

      this.autopilot.hspeed.target = target_hspeed;
      this.autopilot.hspeed.input  = this.body.velocity[0];
      e=200;
      this.autopilot.hspeed.error  = clamp(-e, this.autopilot.hspeed.error, e);
      this.autopilot.hspeed.tick();

      var target_angle=crange(-80, this.autopilot.hspeed.get(), 80, Math.PI/4, -Math.PI/4);
      target_angle *= crange(0.1*this.size, this.body.position[1], floor, 0, 1);

      if(prop.quad.flip && this.body.position[1] > floor) target_angle+=Math.PI;

      var s=crange(1,this.size, 3, 1, 0.1)*2;
      var target_angular_velocity = crange(-Math.PI/2, angle_difference(this.body.angle, target_angle), Math.PI/2, Math.PI*4*s, -Math.PI*4*s);

      this.autopilot.angular_velocity.target = target_angular_velocity;
      this.autopilot.angular_velocity.input  = this.body.angularVelocity;
      this.autopilot.angular_velocity.tick();

      var mix=0.6;
      mix+=crange(1, this.size,5, 0, -0.3);

      var angle=this.body.angle;
      this.power.left   = this.autopilot.vspeed.get() * Math.cos(angle);
      this.power.right  = this.autopilot.vspeed.get() * Math.cos(angle);

      this.power.left=clamp(-mix, this.power.left, mix);
      this.power.right=clamp(-mix, this.power.right, mix);

      mix*=1.2;

      this.power.left  -= this.autopilot.angular_velocity.get() * (1-mix);
      this.power.right += this.autopilot.angular_velocity.get() * (1-mix);

      if(this.data == "parent") {
        this.power.left  = Math.max(0.1, this.power.left);
        this.power.right = Math.max(0.1, this.power.right);
      }

      if(target_altitude < 0.1*this.size) {
        this.power.left *= crange(0.08*this.size, this.body.position[1], 0.12*this.size, 0, 1);
        this.power.right *= crange(0.08*this.size, this.body.position[1], 0.12*this.size, 0, 1);
      }
      var distance=distance2d(target, this.body.position);
      if(this.data == "child" && false) {
        this.power.left  *= crange(0.3, distance, 0.32, 0, 1);
        this.power.right *= crange(0.3, distance, 0.32, 0, 1);
      }

    },

    updatePower: function() {

      var left=clamp(-1, this.power.left, 1);
      var right=clamp(-1, this.power.right, 1);

      var mix=0.999999999;

      this.power.left_motor  = (left *mix) + (this.power.left_motor*(1-mix));
      this.power.right_motor = (right*mix) + (this.power.right_motor*(1-mix));

      var v=this.body.angle;
      var thrust=this.power.max*this.power.left_motor;
      var force=[-Math.sin(v)*thrust, Math.cos(v)*thrust];
      var point=[0,0];
      this.body.toWorldFrame(point,[this.power.left_position,0]);
      this.body.applyForce(force,point);

      thrust=this.power.max*this.power.right_motor;
      force=[-Math.sin(v)*thrust, Math.cos(v)*thrust];
      point=[0,0];
      this.body.toWorldFrame(point,[this.power.right_position,0]);
      this.body.applyForce(force,point);
    },
    updateAudio: function() {
      var v=0.5;
      this.audio.left.setVolume(crange(0.05,Math.abs(this.power.left_motor), 0.1, 0, 0.7)*v);
      this.audio.right.setVolume(crange(0.05,Math.abs(this.power.right_motor), 0.1, 0, 0.7)*v);
      var s=crange(1, this.size, 5, 1, 0.5);
      this.audio.left.setRate(crange(0,Math.abs(this.power.left_motor), 1, 0.5, 3)*s);
      this.audio.right.setRate(crange(0,Math.abs(this.power.right_motor), 1, 0.5, 3)*s);
    },
    update: function() {
      this.target = this.updateTarget(time());
      if(this.autopilot.enabled) this.updateAutopilot();
      this.updatePower();
      this.updateAudio();
    }
  };
});

function quad_init_pre() {
  prop.quad={};
  prop.quad.quads=[];
  prop.quad.target_speed=[0,0];
  prop.quad.target=[0,3];
  prop.quad.flip=false;
}

function quad_init() {
  quad_add(new Quad({
    position: [0, 0],
    target_offset: [0, 0],
    size: 1
  }));
  if(false) {
    quad_add(new Quad({
      data: "child",
      position: [10, 5]
    }));
  }
  var separation=2;
  var d = 2;
  var number=3;
  for(var i=0;i<number;i++) {
    var angle=i/number*Math.PI*2;
    var p=[-((i-number/2)*separation), 0];
    if(i == 0) p=[0, 1];
    quad_add(new Quad({
      data: i,
      position: p,
      target_offset: [Math.sin(angle)*d, Math.cos(angle)*d]
    }));
  }
}

function quad_add(quad) {
  if(!quad) quad = new Quad({});
  prop.quad.quads.push(quad);
}

function quad_update_pre() {
  prop.quad.target_speed[0] *= 0.9;
  prop.quad.target_speed[1] *= 0.9;
  prop.quad.target[0] += prop.quad.target_speed[0];
  prop.quad.target[1] += prop.quad.target_speed[1];
  prop.quad.target[1]  = Math.max(0, prop.quad.target[1]);
  for(var i=0;i<prop.quad.quads.length;i++) {
    prop.quad.quads[i].update();
  }
}

function quad_reset() {
  for(var i=0;i<prop.quad.quads.length;i++) {
    prop.quad.quads[i].reset();
  }
}
<|endoftext|>


<commit_before>const chai = require('chai')
const sinon = require('sinon')

global.assert = chai.assert
global.expect = chai.expect

global.spy = sinon.spy

process.env.NODE_ENV = 'test'
global.settings = require('../lib/settings')
<commit_msg>kz - update test helper<commit_after>const chai = require('chai')
const sinon = require('sinon')

global.assert = chai.assert
global.expect = chai.expect

global.spy = sinon.spy

process.env.NODE_ENV = 'test'
require(../init)()
<|endoftext|>


<commit_before>describe("Share", function() {

	describe("Method _containSupportKey", function() {

		it("Method _containSupportKey: add a service", function() {
			expect(sharejs._containSupportKey('title')).toBeTruthy();
			expect(sharejs._containSupportKey('notsupport')).toBeFalsy();
		});

	});

	describe("Method pushService", function() {

	    beforeEach(function() {
	        sharejs._service = {};
	    });

		it("Method pushService: add a service", function() {
	        var service = {
	            'apiUrl': 'http://v.t.sina.com.cn/share/share.php',
	            'title': 'title',
	            'url': 'url',
	            'pic': 'pic'
	        };
	        sharejs.pushService('weibo', service);

	        expect(sharejs._service['weibo']).toEqual(service);

	    });

		it("Method pushService: without parameter apiUrl", function() {
	        var service = {
	            'url': 'url'
	        };

			expect(function(){
				sharejs.pushService('weibo', service);
			}).toThrow(new Error('Service must specify the key "apiUrl"'));
	    });

		it("Method pushService: without parameter url", function() {
	        var service = {
	            'apiUrl': 'http://v.t.sina.com.cn/share/share.php'
	        };

			expect(function(){
				sharejs.pushService('weibo', service);
			}).toThrow(new Error('Service must specify the key "url"'));
	    });

		it("Method pushService: key not support", function() {
	        var service = {
	            'apiUrl': 'http://v.t.sina.com.cn/share/share.php',
				'url': 'url',
				'notsupportkey': 'notsupportvalue'
	        };

			expect(function(){
				sharejs.pushService('weibo', service);
			}).toThrow(new Error('Key "notsupportkey" is not supported'));
	    });

	});

	describe("Method parse", function() {

		beforeEach(function() {
	        sharejs.pushService('weibo',{
				'apiUrl': 'http://v.t.sina.com.cn/share/share.php',
				'title': 'title',
				'url': 'url',
				'pic': 'pic'
			});
	    });

		it("Method parse: no service configuration", function() {

			expect(function(){
				sharejs.parse('weibo2',{});
			}).toThrow(new Error('No Service "weibo2" Configuration'));
	    });

		it("Method parse: no param", function() {
			var result = sharejs.parse('weibo');
			expect(result).toEqual('http://v.t.sina.com.cn/share/share.php?');
	    });

		it("Method parse: parse weibo", function() {
			var result = sharejs.parse('weibo',{
				title : '文案'
			});
			expect(result).toEqual('http://v.t.sina.com.cn/share/share.php?title='+encodeURIComponent('文案'));
	    });

		it("Method parse: not support key", function() {
			var result = sharejs.parse('weibo',{
				title : '文案',
				newkey : 'newkey'
			});
			expect(result).toEqual('http://v.t.sina.com.cn/share/share.php?title='+encodeURIComponent('文案'));
	    });

	});

});<commit_msg>去掉url必填的测试用例<commit_after>describe("Share", function() {

	describe("Method _containSupportKey", function() {

		it("Method _containSupportKey: add a service", function() {
			expect(sharejs._containSupportKey('title')).toBeTruthy();
			expect(sharejs._containSupportKey('notsupport')).toBeFalsy();
		});

	});

	describe("Method pushService", function() {

	    beforeEach(function() {
	        sharejs._service = {};
	    });

		it("Method pushService: add a service", function() {
	        var service = {
	            'apiUrl': 'http://v.t.sina.com.cn/share/share.php',
	            'title': 'title',
	            'url': 'url',
	            'pic': 'pic'
	        };
	        sharejs.pushService('weibo', service);

	        expect(sharejs._service['weibo']).toEqual(service);

	    });

		it("Method pushService: without parameter apiUrl", function() {
	        var service = {
	            'url': 'url'
	        };

			expect(function(){
				sharejs.pushService('weibo', service);
			}).toThrow(new Error('Service must specify the key "apiUrl"'));
	    });

		it("Method pushService: key not support", function() {
	        var service = {
	            'apiUrl': 'http://v.t.sina.com.cn/share/share.php',
				'url': 'url',
				'notsupportkey': 'notsupportvalue'
	        };

			expect(function(){
				sharejs.pushService('weibo', service);
			}).toThrow(new Error('Key "notsupportkey" is not supported'));
	    });

	});

	describe("Method parse", function() {

		beforeEach(function() {
	        sharejs.pushService('weibo',{
				'apiUrl': 'http://v.t.sina.com.cn/share/share.php',
				'title': 'title',
				'url': 'url',
				'pic': 'pic'
			});
	    });

		it("Method parse: no service configuration", function() {

			expect(function(){
				sharejs.parse('weibo2',{});
			}).toThrow(new Error('No Service "weibo2" Configuration'));
	    });

		it("Method parse: no param", function() {
			var result = sharejs.parse('weibo');
			expect(result).toEqual('http://v.t.sina.com.cn/share/share.php?');
	    });

		it("Method parse: parse weibo", function() {
			var result = sharejs.parse('weibo',{
				title : '文案'
			});
			expect(result).toEqual('http://v.t.sina.com.cn/share/share.php?title='+encodeURIComponent('文案'));
	    });

		it("Method parse: not support key", function() {
			var result = sharejs.parse('weibo',{
				title : '文案',
				newkey : 'newkey'
			});
			expect(result).toEqual('http://v.t.sina.com.cn/share/share.php?title='+encodeURIComponent('文案'));
	    });

	});

});
<|endoftext|>


<commit_before>describe('JediValidate', function () {
    describe('Get value', function () {
        var textInput = document.getElementById('textInput');
        var checkboxInput = document.getElementById('checkboxInput');
        var radioInput1 = document.getElementById('radioInput1');
        var radioInput2 = document.getElementById('radioInput2');
        var select = document.getElementById('select');
        var radioGroup = document.getElementsByName('radio');

        it('Get text value', function () {
            assert.equal(JediValidate.getInputValue(textInput), 'test');
        });

        it('Get checkbox checked value', function () {
            checkboxInput.checked = true;
            assert.equal(JediValidate.getInputValue(checkboxInput), 'test');
        });

        it('Get checkbox not checked value', function () {
            checkboxInput.checked = false;
            assert.equal(JediValidate.getInputValue(checkboxInput), '');
        });

        it('Get radio checked value', function () {
            radioInput1.checked = true;
            assert.equal(JediValidate.getInputValue(radioInput1), 'first');
        });

        it('Get radio not checked value', function () {
            radioInput1.checked = false;
            assert.equal(JediValidate.getInputValue(radioInput1), '');
        });

        it('Get radio group value', function () {
            radioInput2.checked = true;
            assert.equal(JediValidate.getRadioGroupValue(radioGroup), 'second');
        });

	it('Get radio not checked value', function () {
               radioInput1.checked = false;
              radioInput2.checked = false;
		 assert.equal(JediValidate.getInputValue(radioInput1), '');
		 assert.equal(JediValidate.getInputValue(radioInput2), '');
        });

	it('Get select value', function () {
            assert.equal(JediValidate.getInputValue(select), 'second');
        });
    });

    describe('Form options', function () {
        var form = document.querySelector('#myForm');

        it('Check FormData', function () {
            assert.deepEqual(JediValidate.getFormOptions(form), {
                ajax: {
                    url: './ajax.php',
                    method: 'POST',
                    enctype: 'multipart/form-data',
                    sendType: 'formData'
                }
            });
        });

        it('Check with out enctype', function () {
            form.removeAttribute('enctype');

            assert.deepEqual(JediValidate.getFormOptions(form), {
                ajax: {
                    url: './ajax.php',
                    method: 'POST'
                }
            });
        });
    });

    describe('Parse input name', function () {
        var simpleName = 'name';
        var arrayName = 'name[]';
        var objectName = 'name[a]';
        var objectArrayName = 'name[a][]';
        var arrayObjectName = 'name[][a]';
        var value = 'value';

        it('Simple Name', function () {
            assert.deepEqual(JediValidate.parseInputName(simpleName, value), {name: value});
        });

        it('Array Name', function () {
            assert.deepEqual(JediValidate.parseInputName(arrayName, value), {name: [value]});
        });

        it('Object Name', function () {
            assert.deepEqual(JediValidate.parseInputName(objectName, value), {name: {a: value}});
        });

        it('Object Array Name', function () {
            assert.deepEqual(JediValidate.parseInputName(objectArrayName, value), {name: {a: [value]}});
        });

        it('Array Object Name', function () {
            assert.deepEqual(JediValidate.parseInputName(arrayObjectName, value), {name: [{a: value}]});
        });
    });
});
<commit_msg>Revert "add more tests"<commit_after>describe('JediValidate', function () {
    describe('Get value', function () {
        var textInput = document.getElementById('textInput');
        var checkboxInput = document.getElementById('checkboxInput');
        var radioInput1 = document.getElementById('radioInput1');
        var radioInput2 = document.getElementById('radioInput2');
        var select = document.getElementById('select');
        var radioGroup = document.getElementsByName('radio');

        it('Get text value', function () {
            assert.equal(JediValidate.getInputValue(textInput), 'test');
        });

        it('Get checkbox checked value', function () {
            checkboxInput.checked = true;
            assert.equal(JediValidate.getInputValue(checkboxInput), 'test');
        });

        it('Get checkbox not checked value', function () {
            checkboxInput.checked = false;
            assert.equal(JediValidate.getInputValue(checkboxInput), '');
        });

        it('Get radio checked value', function () {
            radioInput1.checked = true;
            assert.equal(JediValidate.getInputValue(radioInput1), 'first');
        });

        it('Get radio not checked value', function () {
            radioInput1.checked = false;
            assert.equal(JediValidate.getInputValue(radioInput1), '');
        });

        it('Get radio group value', function () {
            radioInput2.checked = true;
            assert.equal(JediValidate.getRadioGroupValue(radioGroup), 'second');
        });

        it('Get select value', function () {
            assert.equal(JediValidate.getInputValue(select), 'second');
        });
    });

    describe('Form options', function () {
        var form = document.querySelector('#myForm');

        it('Check FormData', function () {
            assert.deepEqual(JediValidate.getFormOptions(form), {
                ajax: {
                    url: './ajax.php',
                    method: 'POST',
                    enctype: 'multipart/form-data',
                    sendType: 'formData'
                }
            });
        });

        it('Check with out enctype', function () {
            form.removeAttribute('enctype');

            assert.deepEqual(JediValidate.getFormOptions(form), {
                ajax: {
                    url: './ajax.php',
                    method: 'POST'
                }
            });
        });
    });

    describe('Parse input name', function () {
        var simpleName = 'name';
        var arrayName = 'name[]';
        var objectName = 'name[a]';
        var objectArrayName = 'name[a][]';
        var arrayObjectName = 'name[][a]';
        var value = 'value';

        it('Simple Name', function () {
            assert.deepEqual(JediValidate.parseInputName(simpleName, value), {name: value});
        });

        it('Array Name', function () {
            assert.deepEqual(JediValidate.parseInputName(arrayName, value), {name: [value]});
        });

        it('Object Name', function () {
            assert.deepEqual(JediValidate.parseInputName(objectName, value), {name: {a: value}});
        });

        it('Object Array Name', function () {
            assert.deepEqual(JediValidate.parseInputName(objectArrayName, value), {name: {a: [value]}});
        });

        it('Array Object Name', function () {
            assert.deepEqual(JediValidate.parseInputName(arrayObjectName, value), {name: [{a: value}]});
        });
    });
});
<|endoftext|>


<commit_before>  Tempreites = typeof Tempreites != 'undefined' ? Tempreites : require('../dist/tempreites')

  var template = '<!doctype html>
\n  <input/>\n
\n
Habitantes
\n
\n
\n
\n          	\n          	\n          	\n
\n
\n
\n
\n          	\n          \n            \n          	\n
\n
\n
\n
\n
\n
habitantes antigos
\n
\n
\n
\n          	 \n          	 \n
 \n
\n
\n
\n          	\n          \n            \n          	\n
\n
 \n
\n
\n
\n'
  var data = {
    ativos: [{
      quarto: '213',
      nome: 'fulano',
      link: 'fulano.com'
    },{
      quarto: '405',
      nome: 'beltrano',
      link: 'beltrano.com'
    },{
      quarto: '414',
      nome: 'ciclano',
      link: 'ciclano.com'
    }],
    //inativos: []
  }

  var res = Tempreites.render(template, data)

  if (typeof document === 'undefined') console.log(res)
  else document.getElementById('main').innerHTML = res

  var template = '
\n
\n

\n

\n
\n
dia do vencimento:
\n
cpf:
\n
telefone:
\n
\n
\n      estada anterior\n
\n
quarto:
\n
entrada:
\n
sa\xc3\xadda:
\n
\n      estada seguinte\n
\n
\n
\n
\n
\n
\n          m\xc3\xaas	\n          total	\n          pago	\n          em aberto	\n
\n
\n
\n
\n          	\n          	\n          	\n          	\n
\n
\n
\n
\n
\n    <form class="pure-form saida">\n      <fieldset>\n        <input type="text" class="datepicker" name="dia">       <button type="submit" class="pure-button">saiu do quarto</button>\n      </fieldset>\n    </form>\n
\n
\n

\n'
  var doc = {"_id":"e7e1a61e-61b3-11e2-877c-00e04d49d684","_rev":"1-5d60c795b1257e1ae42818a344bf9451","vencimento":5,"estadas":[{"entrada":"2012-06-18","quarto":"8"},{"entrada":"2012-06-18","quarto":"8"}],"type":"habitante","nome":"Marcos Luiz","_revisions":{"start":1,"ids":["5d60c795b1257e1ae42818a344bf9451"]},"quarto":"8","estada":{"entrada":"2012-06-18","quarto":"8"},"prevlink":"?estada=[object Object]","nextlink":null}

  var res = Tempreites.render(template, doc)

  if (typeof document === 'undefined') console.log(res)
  else document.getElementById('main').innerHTML = res

  var template = '<!DOCTYPE html>\n\n  \n      <title>hotel</title>\n      <link href="//cdnjs.cloudflare.com/ajax/libs/pure/0.3.0/pure-min.css" rel="stylesheet" type="text/css" media="not print">\n      <link href="../../default.css" rel="stylesheet" type="text/css" media="not print" />\n      <link href=\'../../modules//pikaday.css\' rel=\'stylesheet\' type=\'text/css\' media="not print">\n      <link href=\'../../lib//jsmodal-light.css\' rel=\'stylesheet\' type=\'text/css\' media="not print">\n      <link rel="stylesheet" href="../../boletos.css" type="text/css" media="print"> \n      \n  \n  \n  \n\n'
  var doc = {}

  var res = Tempreites.render(template, doc)

  if (typeof document === 'undefined') console.log(res)
  else document.getElementById('main').innerHTML = res
<commit_msg>change the order of these ugly tests<commit_after>  var template = '
\n
\n

\n

\n
\n
dia do vencimento:
\n
cpf:
\n
telefone:
\n
\n
\n      estada anterior\n
\n
quarto:
\n
entrada:
\n
sa\xc3\xadda:
\n
\n      estada seguinte\n
\n
\n
\n
\n
\n
\n          m\xc3\xaas	\n          total	\n          pago	\n          em aberto	\n
\n
\n
\n
\n          	\n          	\n          	\n          	\n
\n
\n
\n
\n
\n    <form class="pure-form saida">\n      <fieldset>\n        <input type="text" class="datepicker" name="dia">       <button type="submit" class="pure-button">saiu do quarto</button>\n      </fieldset>\n    </form>\n
\n
\n

\n'
  var doc = {"_id":"e7e1a61e-61b3-11e2-877c-00e04d49d684","_rev":"1-5d60c795b1257e1ae42818a344bf9451","vencimento":5,"estadas":[{"entrada":"2012-06-18","quarto":"8"},{"entrada":"2012-06-18","quarto":"8"}],"type":"habitante","nome":"Marcos Luiz","_revisions":{"start":1,"ids":["5d60c795b1257e1ae42818a344bf9451"]},"quarto":"8","estada":{"entrada":"2012-06-18","quarto":"8"},"prevlink":"?estada=[object Object]","nextlink":null}

  var res = Tempreites.render(template, doc)

  if (typeof document === 'undefined') console.log(res)
  else document.getElementById('main').innerHTML = res

  var template = '<!DOCTYPE html>\n\n  \n      <title>hotel</title>\n      <link href="//cdnjs.cloudflare.com/ajax/libs/pure/0.3.0/pure-min.css" rel="stylesheet" type="text/css" media="not print">\n      <link href="../../default.css" rel="stylesheet" type="text/css" media="not print" />\n      <link href=\'../../modules//pikaday.css\' rel=\'stylesheet\' type=\'text/css\' media="not print">\n      <link href=\'../../lib//jsmodal-light.css\' rel=\'stylesheet\' type=\'text/css\' media="not print">\n      <link rel="stylesheet" href="../../boletos.css" type="text/css" media="print"> \n      \n  \n  \n  \n\n'
  var doc = {}

  var res = Tempreites.render(template, doc)

  if (typeof document === 'undefined') console.log(res)
  else document.getElementById('main').innerHTML = res

  Tempreites = typeof Tempreites != 'undefined' ? Tempreites : require('../dist/tempreites')

  var template = '<!doctype html>
\n  <input/>\n
\n
Habitantes
\n
\n
\n
\n          	\n          	\n          	\n
\n
\n
\n
\n          	\n          \n            \n          	\n
\n
\n
\n
\n
\n
habitantes antigos
\n
\n
\n
\n          	 \n          	 \n
 \n
\n
\n
\n          	\n          \n            \n          	\n
\n
 \n
\n
\n
\n'
  var data = {
    ativos: [{
      quarto: '213',
      nome: 'fulano',
      link: 'fulano.com'
    },{
      quarto: '405',
      nome: 'beltrano',
      link: 'beltrano.com'
    },{
      quarto: '414',
      nome: 'ciclano',
      link: 'ciclano.com'
    }],
    //inativos: []
  }

  var res = Tempreites.render(template, data)

  if (typeof document === 'undefined') console.log(res)
  else document.getElementById('main').innerHTML = res

<|endoftext|>


<commit_before>var wd = require('wd');
var ar = require('appium-running');
var intentClick = require('../node/');
(function test() {
  var browser = wd.remote('localhost', 4723);
  browser.on('status', function(info) {
    console.log(info);
  });

  function error(browser) {
    console.log(error);
    browser.quit();
    process.exit(1);
  }

  function success() {
    console.log("SUCCESS");
    browser.quit();
    process.quit(0);
  }

  browser.on('command', function(meth, path, data) {
    console.log([meth, path, data].join(" "));
  });

  function test() {
    browser.init({
      browserName:'Chrome',
      deviceName:'Android',
      platformName: 'Android',
    }, function(){
      browser.get("http://labs.urucas.com/zoster", function(){
        browser.elementByLinkText("Open App", function(err, el) {
          if(err) error(err);
          intentClick(browser, el, success, error);
        });
      });
    });
  }

  ar(4723, function(success){
    if(!success) {
      console.log("Appium not running or available!");
      return;
    }
    test();
  });

})();

<commit_msg>update test<commit_after>var wd = require('wd');
var ar = require('appium-running');
var intentClick = require('../node/');
(function test() {
  var browser = wd.remote('localhost', 4723);
  browser.on('status', function(info) {
    console.log(info);
  });

  function error(browser) {
    console.log(error);
    browser.quit();
    process.exit(1);
  }

  function success() {
    console.log("SUCCESS");
    browser.quit();
    process.exit(0);
  }

  browser.on('command', function(meth, path, data) {
    console.log([meth, path, data].join(" "));
  });

  function test() {
    browser.init({
      browserName:'Chrome',
      deviceName:'Android',
      platformName: 'Android',
    }, function(){
      browser.get("http://labs.urucas.com/zoster", function(){
        browser.elementByLinkText("Open App", function(err, el) {
          if(err) error(err);
          intentClick(browser, el, success, error);
        });
      });
    });
  }

  ar(4723, function(success){
    if(!success) {
      console.log("Appium not running or available!");
      return;
    }
    test();
  });

})();

<|endoftext|>


<commit_before>var test = require('tap').test
var fs = require('fs')
var gfs = require('../graceful-fs.js')

test('graceful fs uses same stats constructor as fs', function (t) {
  t.equal(gfs.Stats, fs.Stats, 'should reference the same constructor')
  t.ok(fs.statSync(__filename) instanceof fs.Stats,
    'should be instance of fs.Stats')
  t.ok(gfs.statSync(__filename) instanceof fs.Stats,
    'should be instance of fs.Stats')
  t.end()
})
<commit_msg>Revert "Add test case"<commit_after><|endoftext|>


<commit_before>/**
 * @fileOverview Search Folder Manager for Thunderbird
 * @author       ClearCode Inc.
 * @version      2
 *
 * @description
 *   Usage:
 *     Components.utills.import('resource://.../SerchFolderManager.jsm');
 *     new SerchFolderManager([
 *       { label:         'Important',
 *         subjectPrefix: '[Important]' },
 *       { label:         'FYI',
 *         conditions:    'OR (subject,begins with,FYI) OR (subject,begins with,[FYI])' },
 *       { label:         'DenyHosts Reports',
 *         subject:       'DenyHosts Report from www.example.com' },
 *       ...
 *     ]);
 *
 * @license
 *   The MIT License, Copyright (c) 2014 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-search-folder-manager
 */

var EXPORTED_SYMBOLS = ['SearchFolderManager'];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');

XPCOMUtils.defineLazyModuleGetter(this,
                                  'Services',
                                  'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'MailServices',
                                  'resource:///modules/mailServices.js');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'VirtualFolderHelper',
                                  'resource:///modules/virtualFolderWrapper.js');

/**
 * Initializes a search folder manager for given folder
 * definitions (search conditions).
 *
 * @param Array of Definition
 *        An array of search folder definitions (search conditions).
 *
 * Definition:
 *   @param label         String: the name of the folder
 *   @param conditions    String: the search conditions (optional)
 *   @param subject       String: the subject to be matched (optional)
 *   @param subjectPrefix String: the prefix of the subject to be matched (optional)
 *   @param searchTargets String: "all" or comma-separated URIs of mail folders (optional, default=all)
 */
function SearchFolderManager(aDefinitions) {
  this.definitions = aDefinitions;

  Services.obs.addObserver(this, 'mail-tabs-session-restored', false);
}
SearchFolderManager.prototype = {
  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },
  getDescendants: function(aRoot) {
    var folders = [];
    if ('descendants' in aRoot) { // Thunderbird 24
      let descendants = aRoot.descendants;
      for (let i = 0, maxi = descendants.length; i < maxi; i++) {
        let folder = descendants.queryElementAt(i, Ci.nsIMsgFolder);
        folders.push(folder);
      }
    } else { // Thunderbird 17 or olders
      let descendants = Cc['@mozilla.org/supports-array;1']
                          .createInstance(Ci.nsISupportsArray);
      aRoot.ListDescendents(descendants);
      for (let i = 0, maxi = descendants.Count(); i < maxi; i++) {
        let folder = descendants.GetElementAt(i).QueryInterface(Ci.nsIMsgFolder);
        folders.push(folder);
      }
    }
    return folders;
  },

  observe: function(aSubject, aTopic, aData) {
    Services.obs.removeObserver(this, 'mail-tabs-session-restored');
    // This must be done after mail-tabs-session-restored.
    // Otherwise, non-ASCII search conditions are not saved correctly.
    this.buildSearchFolders();

    this.startListenFolderChanges();
  },
  startListenFolderChanges: function() {
    var notifyFlags = Ci.nsIFolderListener.added |
                        Ci.nsIFolderListener.removed;
    MailSession.AddFolderListener(this, notifyFlags);
  },
  // nsIFolderListener
  OnItemAdded: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      addedFolder: aItem.URI
    });
  },
  OnItemRemoved: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      removedFolder: aItem.URI
    });
  },
  OnItemPropertyChanged: function() {},
  OnItemIntPropertyChanged: function() {},
  OnItemBoolPropertyChanged: function() {},
  OnItemUnicharPropertyChanged: function() {},
  OnItemEvent: function() {},

  buildSearchFolders: function() {
    this.allAccounts.forEach(function(aAccount) {
      if (!aAccount.incomingServer)
        return;
      this.buildSearchFoldersIn(aAccount.incomingServer.rootMsgFolder);
    }, this);
  },
  buildSearchFoldersIn: function(aRoot, aModification) {
    if (!aRoot)
      return;
    this.definitions.forEach(function(aDefinition) {
      try {
        this.buildSearchFolderIn(aDefinition, aRoot, aModification);
      }
      catch(e) {
        Cu.reportError(e);
      }
    }, this);
  },
  buildSearchFolderIn: function(aDefinition, aRoot, aModification) {
    if (!aDefinition.subjectPrefix &&
        !aDefinition.subject ||
        !aRoot)
      return;

    var searchTargets = aDefinition.searchTargets || 'all';
    var searchFolders;
    if (!aModification) {
      searchFolders = this.getSearchFolders(aRoot, searchTargets.split(/[,\s]+/));
      if (!searchFolders)
        return;
    }

    var name = 'autogen-' + aDefinition.label;

    var isCreation = false;
    var isModified = false;
    var virtualFolder;
    try {
      virtualFolder = aRoot.getChildNamed(name);
    } catch(e) {
      try {
        virtualFolder = aRoot.getChildNamed(aDefinition.label);
        if (!(virtualFolder.flags & Ci.nsMsgFolderFlags.Virtual))
          virtualFolder = null;
      } catch(e) {
        // folder not found!
      }
    }
    if (!virtualFolder) {
      isCreation = true;
      isModified = true;
      virtualFolder = aRoot.addSubfolder(name);
      virtualFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
    }

    // We always have to set prettyName because it is not saved.
    virtualFolder.prettyName = aDefinition.label;

    var wrapper = VirtualFolderHelper.wrapVirtualFolder(virtualFolder);

    var conditions = this.buildSearchCondition(aDefinition);
    if (wrapper.searchString != conditions) {
      wrapper.searchString = conditions;
      isModified = true;
    }
    var currentSearchFolders = wrapper.searchFolders.map(function(aFolder) {
      return aFolder.URI
    }).join('|');
    if (aModification) {
      searchFolders = currentSearchFolders.split('|');
      if (aModification.addedFolder) {
        let index = searchFolders.indexOf(aModification.addedFolder);
        if (index < 0) {
          searchFolders.push(aModification.addedFolder);
        }
      }
      if (aModification.removedFolder) {
        let index = searchFolders.indexOf(aModification.removedFolder);
        if (index > -1) {
          searchFolders.splice(index, 1);
        }
      }
      searchFolders = searchFolders.join('|');
    }
    if (currentSearchFolders != searchFolders) {
      wrapper.searchFolders = searchFolders;
      isModified = true;
    }
    if (isCreation) {
      wrapper.onlineSearch = false;
    }

    if (!isModified)
      return;

    wrapper.cleanUpMessageDatabase();
    if (isCreation) {
      virtualFolder.msgDatabase.Close(true);
      aRoot.NotifyItemAdded(virtualFolder);
    }
    MailServices.accounts.saveVirtualFolders();
  },
  getSearchFolders: function(aRoot, aKeys) {
    var flags = {};
    Object.keys(Ci.nsMsgFolderFlags).forEach(function(aKey) {
      flags[aKey.toLowerCase()] = Ci.nsMsgFolderFlags[aKey];
    });
    var folders = [];
    aKeys.some(function(aKey) {
      aKey = aKey.toLowerCase();
      if (aKey == 'all') {
        folders = this.getDescendants(aRoot).map(function(aFolder) {
          return aFolder.URI;
        });
        return true;
      }
      if (typeof flags[aKey] == 'number') {
        let folder = aRoot.getFolderWithFlags(flags[aKey]);
        if (folder)
          folders.push(folder.URI);
      }
      return false;
    }, this);
    try {
      if (folders.length > 0)
        folders.unshift(aRoot.URI);
    } catch(e) {
    }
    return folders.join('|');
  },
  buildSearchCondition: function(aDefinition) {
    if (aDefinition.conditions)
      return this.UnicodeToUTF8(aDefinition.conditions);

    if (aDefinition.subjectPrefix)
      return 'AND (subject,begins with,' + this.UnicodeToUTF8(aDefinition.subjectPrefix) + ')';

    return 'AND (subject,is,' + this.UnicodeToUTF8(aDefinition.subject) + ')';
  },
  UnicodeToUTF8: function(aString) {
    return unescape(encodeURIComponent(aString));
  }
};
<commit_msg>Accept definition only with "conditions" parameter<commit_after>/**
 * @fileOverview Search Folder Manager for Thunderbird
 * @author       ClearCode Inc.
 * @version      3
 *
 * @description
 *   Usage:
 *     Components.utills.import('resource://.../SerchFolderManager.jsm');
 *     new SerchFolderManager([
 *       { label:         'Important',
 *         subjectPrefix: '[Important]' },
 *       { label:         'FYI',
 *         conditions:    'OR (subject,begins with,FYI) OR (subject,begins with,[FYI])' },
 *       { label:         'DenyHosts Reports',
 *         subject:       'DenyHosts Report from www.example.com' },
 *       ...
 *     ]);
 *
 * @license
 *   The MIT License, Copyright (c) 2014 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-search-folder-manager
 */

var EXPORTED_SYMBOLS = ['SearchFolderManager'];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');

XPCOMUtils.defineLazyModuleGetter(this,
                                  'Services',
                                  'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'MailServices',
                                  'resource:///modules/mailServices.js');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'VirtualFolderHelper',
                                  'resource:///modules/virtualFolderWrapper.js');

/**
 * Initializes a search folder manager for given folder
 * definitions (search conditions).
 *
 * @param Array of Definition
 *        An array of search folder definitions (search conditions).
 *
 * Definition:
 *   @param label         String: the name of the folder
 *   @param conditions    String: the search conditions (optional)
 *   @param subject       String: the subject to be matched (optional)
 *   @param subjectPrefix String: the prefix of the subject to be matched (optional)
 *   @param searchTargets String: "all" or comma-separated URIs of mail folders (optional, default=all)
 */
function SearchFolderManager(aDefinitions) {
  this.definitions = aDefinitions;

  Services.obs.addObserver(this, 'mail-tabs-session-restored', false);
}
SearchFolderManager.prototype = {
  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },
  getDescendants: function(aRoot) {
    var folders = [];
    if ('descendants' in aRoot) { // Thunderbird 24
      let descendants = aRoot.descendants;
      for (let i = 0, maxi = descendants.length; i < maxi; i++) {
        let folder = descendants.queryElementAt(i, Ci.nsIMsgFolder);
        folders.push(folder);
      }
    } else { // Thunderbird 17 or olders
      let descendants = Cc['@mozilla.org/supports-array;1']
                          .createInstance(Ci.nsISupportsArray);
      aRoot.ListDescendents(descendants);
      for (let i = 0, maxi = descendants.Count(); i < maxi; i++) {
        let folder = descendants.GetElementAt(i).QueryInterface(Ci.nsIMsgFolder);
        folders.push(folder);
      }
    }
    return folders;
  },

  observe: function(aSubject, aTopic, aData) {
    Services.obs.removeObserver(this, 'mail-tabs-session-restored');
    // This must be done after mail-tabs-session-restored.
    // Otherwise, non-ASCII search conditions are not saved correctly.
    this.buildSearchFolders();

    this.startListenFolderChanges();
  },
  startListenFolderChanges: function() {
    var notifyFlags = Ci.nsIFolderListener.added |
                        Ci.nsIFolderListener.removed;
    MailSession.AddFolderListener(this, notifyFlags);
  },
  // nsIFolderListener
  OnItemAdded: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      addedFolder: aItem.URI
    });
  },
  OnItemRemoved: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      removedFolder: aItem.URI
    });
  },
  OnItemPropertyChanged: function() {},
  OnItemIntPropertyChanged: function() {},
  OnItemBoolPropertyChanged: function() {},
  OnItemUnicharPropertyChanged: function() {},
  OnItemEvent: function() {},

  buildSearchFolders: function() {
    this.allAccounts.forEach(function(aAccount) {
      if (!aAccount.incomingServer)
        return;
      this.buildSearchFoldersIn(aAccount.incomingServer.rootMsgFolder);
    }, this);
  },
  buildSearchFoldersIn: function(aRoot, aModification) {
    if (!aRoot)
      return;
    this.definitions.forEach(function(aDefinition) {
      try {
        this.buildSearchFolderIn(aDefinition, aRoot, aModification);
      }
      catch(e) {
        Cu.reportError(e);
      }
    }, this);
  },
  buildSearchFolderIn: function(aDefinition, aRoot, aModification) {
    if (!aDefinition.condition &&
        !aDefinition.subjectPrefix &&
        !aDefinition.subject ||
        !aRoot)
      return;

    var searchTargets = aDefinition.searchTargets || 'all';
    var searchFolders;
    if (!aModification) {
      searchFolders = this.getSearchFolders(aRoot, searchTargets.split(/[,\s]+/));
      if (!searchFolders)
        return;
    }

    var name = 'autogen-' + aDefinition.label;

    var isCreation = false;
    var isModified = false;
    var virtualFolder;
    try {
      virtualFolder = aRoot.getChildNamed(name);
    } catch(e) {
      try {
        virtualFolder = aRoot.getChildNamed(aDefinition.label);
        if (!(virtualFolder.flags & Ci.nsMsgFolderFlags.Virtual))
          virtualFolder = null;
      } catch(e) {
        // folder not found!
      }
    }
    if (!virtualFolder) {
      isCreation = true;
      isModified = true;
      virtualFolder = aRoot.addSubfolder(name);
      virtualFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
    }

    // We always have to set prettyName because it is not saved.
    virtualFolder.prettyName = aDefinition.label;

    var wrapper = VirtualFolderHelper.wrapVirtualFolder(virtualFolder);

    var conditions = this.buildSearchCondition(aDefinition);
    if (wrapper.searchString != conditions) {
      wrapper.searchString = conditions;
      isModified = true;
    }
    var currentSearchFolders = wrapper.searchFolders.map(function(aFolder) {
      return aFolder.URI
    }).join('|');
    if (aModification) {
      searchFolders = currentSearchFolders.split('|');
      if (aModification.addedFolder) {
        let index = searchFolders.indexOf(aModification.addedFolder);
        if (index < 0) {
          searchFolders.push(aModification.addedFolder);
        }
      }
      if (aModification.removedFolder) {
        let index = searchFolders.indexOf(aModification.removedFolder);
        if (index > -1) {
          searchFolders.splice(index, 1);
        }
      }
      searchFolders = searchFolders.join('|');
    }
    if (currentSearchFolders != searchFolders) {
      wrapper.searchFolders = searchFolders;
      isModified = true;
    }
    if (isCreation) {
      wrapper.onlineSearch = false;
    }

    if (!isModified)
      return;

    wrapper.cleanUpMessageDatabase();
    if (isCreation) {
      virtualFolder.msgDatabase.Close(true);
      aRoot.NotifyItemAdded(virtualFolder);
    }
    MailServices.accounts.saveVirtualFolders();
  },
  getSearchFolders: function(aRoot, aKeys) {
    var flags = {};
    Object.keys(Ci.nsMsgFolderFlags).forEach(function(aKey) {
      flags[aKey.toLowerCase()] = Ci.nsMsgFolderFlags[aKey];
    });
    var folders = [];
    aKeys.some(function(aKey) {
      aKey = aKey.toLowerCase();
      if (aKey == 'all') {
        folders = this.getDescendants(aRoot).map(function(aFolder) {
          return aFolder.URI;
        });
        return true;
      }
      if (typeof flags[aKey] == 'number') {
        let folder = aRoot.getFolderWithFlags(flags[aKey]);
        if (folder)
          folders.push(folder.URI);
      }
      return false;
    }, this);
    try {
      if (folders.length > 0)
        folders.unshift(aRoot.URI);
    } catch(e) {
    }
    return folders.join('|');
  },
  buildSearchCondition: function(aDefinition) {
    if (aDefinition.conditions)
      return this.UnicodeToUTF8(aDefinition.conditions);

    if (aDefinition.subjectPrefix)
      return 'AND (subject,begins with,' + this.UnicodeToUTF8(aDefinition.subjectPrefix) + ')';

    return 'AND (subject,is,' + this.UnicodeToUTF8(aDefinition.subject) + ')';
  },
  UnicodeToUTF8: function(aString) {
    return unescape(encodeURIComponent(aString));
  }
};
<|endoftext|>


<commit_before>import moment from 'moment'
import sortBy from 'lodash/sortBy'

const initialState = {
  messages: [],
  savedMessages: [],
  filter: 'all'
}

const toggleSavedMessage = (message) => {
  return {
    type: 'toggle-saved-message',
    message
  }
}

const setMessageFilter = (value) => {
  return {
    type: 'set-message-filter',
    value
  }
}

const getVisibleMessages = (state, filter) => {
  switch(filter) {
    case 'successful':
      return sortBy(state.messages.filter(m => m.hasError === false), 'timestamp')
    case 'failed':
      return sortBy(state.messages.filter(m => m.hasError === true), 'timestamp')
    case 'saved':
      return sortBy(state.savedMessages, 'timestamp')
    default:
      return sortBy(state.messages, 'timestamp')
  }
}

export {
  getVisibleMessages,
  setMessageFilter,
  toggleSavedMessage
}

const insert = (array, index, ...items) => {
  return [
    ...array.slice(0, index),
    ...items,
    ...array.slice(index)
  ]
}

export default (state = initialState, action = {}) => {
  switch(action.type) {
    case 'bus-message-succeeded':
      action.envelope.saved = false
      action.envelope.timestamp = moment().format()
      return Object.assign({}, state, {
        messages: insert(state.messages, 0, action.envelope)
      })
    case 'bus-message-failed':
      action.envelope.saved = false
      action.envelope.timestamp = moment().format()
      return Object.assign({}, state, {
        messages: insert(state.messages, 0, action.envelope)
      })
    case 'set-message-filter':
      return Object.assign({}, state, {
        filter: action.value
      })
    case 'toggle-saved-message': {
      const wasSaved = action.message.saved
      action.message.saved = !action.message.saved

      if (!wasSaved) {
        return Object.assign({}, state, {
          messages: state.messages.filter(m => m.correlationId !== action.message.correlationId).concat([action.message]),
          savedMessages: insert(state.savedMessages, 0, action.message)
        })
      } else {
        return Object.assign({}, state, {
          messages: state.messages.filter(m => m.correlationId !== action.message.correlationId).concat([action.message]),
          savedMessages: state.savedMessages.filter(m => m.headers.id !== action.message.headers.id)
        })
      }
    }
    default: return state
  }
}
<commit_msg>Fix redux store for starred messages<commit_after>import moment from 'moment'
import sortBy from 'lodash/sortBy'

const initialState = {
  messages: [],
  savedMessages: [],
  filter: 'all'
}

const toggleSavedMessage = (message) => {
  return {
    type: 'toggle-saved-message',
    message
  }
}

const setMessageFilter = (value) => {
  return {
    type: 'set-message-filter',
    value
  }
}

const getVisibleMessages = (state, filter) => {
  switch(filter) {
    case 'successful':
      return sortBy(state.messages.filter(m => m.hasError === false), 'timestamp')
    case 'failed':
      return sortBy(state.messages.filter(m => m.hasError === true), 'timestamp')
    case 'saved':
      return sortBy(state.savedMessages, 'timestamp')
    default:
      return sortBy(state.messages, 'timestamp')
  }
}

export {
  getVisibleMessages,
  setMessageFilter,
  toggleSavedMessage
}

const insert = (array, index, ...items) => {
  return [
    ...array.slice(0, index),
    ...items,
    ...array.slice(index)
  ]
}

export default (state = initialState, action = {}) => {
  switch(action.type) {
    case 'bus-message-succeeded':
      action.envelope.saved = false
      action.envelope.timestamp = moment().format()
      return {
        ...state,
        messages: insert(state.messages, 0, action.envelope)
      }
    case 'bus-message-failed':
      action.envelope.saved = false
      action.envelope.timestamp = moment().format()
      return {
        ...state,
        messages: insert(state.messages, 0, action.envelope)
      }
    case 'set-message-filter':
      return {
        ...state,
        filter: action.value
      }
    case 'toggle-saved-message': {
      const save = !action.message.saved

      if (save === true) {
        return {
          ...state,
          messages: state.messages.map(m => m.correlationId == action.message.correlationId ?
            { ...m, saved: save} :
            m
          ),
          savedMessages: [
            ...state.savedMessages.filter(m => m.correlationId !== action.message.correlationId),
            { ...action.message, saved: save }
          ]
        }
      } else {
        return {
          ...state,
          messages: state.messages.map(m => m.correlationId == action.message.correlationId ?
            { ...m, saved: save} :
            m
          ),
          savedMessages: [
            ...state.savedMessages.filter(m => m.correlationId !== action.message.correlationId)
          ]
        }
      }
    }
    default: return state
  }
}
<|endoftext|>


<commit_before>function createTable() {
    return new ATable({
        fetchData : "fetchData500Rows4Cols",
        columns : [
            {name : "Column 1"},
            {name : "Column 2"},
            {name : "Column 3"},
            {name : "Column 4"}
        ],
        el : "#qunit-fixture",
        height : 300,
        rowsToRender : 100
    });
}

module("Row rendering");
asyncTest("Initial render 500 rows", 7, function () {
    var table = createTable();
    table.render(function () {
        start();
        ok(table.rowHeight > 0, "table.rowHeight should be positive");
        ok(table.visibleRows > 0, "table.visibleRows should be positive");
        ok(table.bufferRows > 0, "table.bufferRows should be positive");
        equal(table.rows.length, 500, "table.rows should have 10 rows");
        equal(table.rowRange.first, 0, "first row to render should be 0");
        equal(table.rowRange.last, 100, "last row to render should be 100");
        var rows = table.tbodyElt.find("tr");
        equal(rows.length, 100, "DOM table should have 100 rows");

    });
});

asyncTest("Remove rows", 5, function () {
    var table = createTable();
    table.render(function () {
        start();
        var rows = table.tbodyElt.find("tr");
        equal(rows.length, 100, "DOM table should have 100 rows");
        table.removeRows(2, true);
        rows = table.tbodyElt.find("tr");
        equal(rows.length, 98, "DOM table should have 98 rows");
        equal(parseInt(rows[0].cells[0].innerText), 8, "first cell rendered should contain 8");
        table.removeRows(2, false);
        rows = table.tbodyElt.find("tr");
        equal(rows.length, 96, "DOM table should have 96 rows");
        equal(parseInt(rows[95].cells[0].innerText), 388, "last cell rendered should contain 388");
    });
});

asyncTest("Scroll table", 20, function () {
    var table = createTable();
    table.render(function () {
        start();
        //Scroll down, but not enough to render any new rows in the table
        table.scrollTable({target : {scrollTop : 40}});
        equal(table.rowRange.first, 0, "previous first row to render should be 0");
        equal(table.rowRange.last, 100, "previous last row to render should be 100");
        equal(table.prevRowRange.first, 0, "previous first row to render should be 0");
        equal(table.prevRowRange.last, 100, "previous last row to render should be 100");

        // Scroll down enough to render new rows
        var threshold = (table.tbodyElt[0].scrollHeight - table.tbodyElt[0].clientHeight) * .75;
        table.scrollTable({target : {scrollTop : threshold + 1000}});
        var rows = table.tbodyElt.find("tr");
        var expectedFirstRow = parseInt(1000 / table.rowHeight);
        equal(table.rowRange.first, expectedFirstRow, "first row to render should be " + expectedFirstRow);
        equal(table.rowRange.last, expectedFirstRow + table.rowsToRender, "last row to render should be (firstRow + rowsToRender)");
        equal(parseInt(rows[0].cells[0].innerText), table.rowRange.first * 4, "first cell rendered should contain " + table.rowRange.first * 4);
        equal(parseInt(rows[99].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);

        // Scroll back up some
        table.scrollTable({target : {scrollTop : threshold - 100}});
        rows = table.tbodyElt.find("tr");
        equal(table.rowRange.first, expectedFirstRow, "first row to render should be " + expectedFirstRow);
        equal(table.rowRange.last, expectedFirstRow + table.rowsToRender, "last row to render should be (firstRow + rowsToRender)");
        equal(parseInt(rows[0].cells[0].innerText), table.rowRange.first * 4, "first cell rendered should contain " + table.rowRange.first * 4);
        equal(parseInt(rows[99].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);

        // Scroll up past the upper threshold
        threshold = table.tbodyElt[0].scrollHeight * .25;
        table.scrollTable({target : {scrollTop : threshold - 400}});
        rows = table.tbodyElt.find("tr");
        expectedFirstRow -= parseInt(400 / table.rowHeight);
        equal(table.rowRange.first, expectedFirstRow, "first row to render should be " + expectedFirstRow);
        equal(table.rowRange.last, expectedFirstRow + table.rowsToRender, "last row to render should be " + (expectedFirstRow + table.rowsToRender));
        equal(parseInt(rows[0].cells[0].innerText), expectedFirstRow * 4, "first cell rendered should contain 0");
        equal(parseInt(rows[99].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);

        // Scroll up to the top
        table.scrollTable({target : {scrollTop : 0}});
        rows = table.tbodyElt.find("tr");
        equal(table.rowRange.first, 0, "first row to render should 0");
        equal(table.rowRange.last, 100, "last row to render should be 100");
        equal(parseInt(rows[0].cells[0].innerText), 0, "first cell rendered should contain 0");
        equal(parseInt(rows[99].cells[0].innerText), 396, "last row's first cell should contain 396");
    });
});

asyncTest("Random scroll stress test", 4000, function () {
    var table = createTable();
    table.render(function () {
        start();
        var scrollHeight = table.tbodyElt[0].scrollHeight;
        var bottomThreshold = (table.tbodyElt[0].scrollHeight - table.tbodyElt[0].clientHeight) * .8;
        var topThreshold = table.tbodyElt[0].scrollHeight * .1;
        for (var i = 1; i <= 1000; i++) {
            var scrollTop = Math.floor(Math.random() * (scrollHeight + 1));
            table.scrollTable({target : {scrollTop : scrollTop}});
            var rows = table.tbodyElt.find("tr");
            var expectedFirstRow;
            if(scrollTop > bottomThreshold && table.rowRange.first > table.prevRowRange.first){
                var rowsPast = parseInt((scrollTop - bottomThreshold) / table.rowHeight);
                expectedFirstRow = table.prevRowRange.first + rowsPast;
            }
            else if(scrollTop < topThreshold && table.rowRange.first < table.prevRowRange.first){
                var rowsPast = parseInt((topThreshold - scrollTop) / table.rowHeight);
                expectedFirstRow = table.prevRowRange.first - rowsPast;
            }
            else{
                expectedFirstRow = table.prevRowRange.first;
            }
            expectedFirstRow = expectedFirstRow >= 0 ? expectedFirstRow : 0;
            var expectedLastRow = expectedFirstRow + table.rowsToRender;
            expectedLastRow = expectedLastRow < table.rows.length ? expectedLastRow : table.rows.length - 1;
            if(expectedLastRow === table.rows.length) expectedFirstRow = expectedLastRow - table.rowsToRender;
            equal(table.rowRange.first, expectedFirstRow, "first row to render should be  " + expectedFirstRow);
            equal(table.rowRange.last, expectedLastRow, "last row to render should be " + expectedLastRow);
            equal(parseInt(rows[0].cells[0].innerText), table.rowRange.first * 4, "first cell rendered should contain " + table.rowRange.first * 4);
            equal(parseInt(rows[99].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);
        }
    });
});<commit_msg>#2 .. modify scroll tests to reflect change in row rendering method<commit_after>function createTable() {
    return new ATable({
        fetchData : "fetchData500Rows4Cols",
        columns : [
            {name : "Column 1"},
            {name : "Column 2"},
            {name : "Column 3"},
            {name : "Column 4"}
        ],
        el : "#qunit-fixture",
        height : 300,
        rowsToRender : 100
    });
}

module("Row rendering");
asyncTest("Initial render 500 rows", 6, function () {
    var table = createTable();
    table.render(function () {
        start();
        ok(table.rowHeight > 0, "table.rowHeight should be positive");
        ok(table.visibleRows > 0, "table.visibleRows should be positive");
        equal(table.rows.length, 500, "table.rows should have 10 rows");
        equal(table.rowRange.first, 0, "first row to render should be 0");
        equal(table.rowRange.last, 100, "last row to render should be 100");
        var rows = table.tbodyElt.find("tr");
        equal(rows.length, 102, "DOM table should have 102 rows");
    });
});

asyncTest("Remove rows", 5, function () {
    var table = createTable();
    table.render(function () {
        start();
        var rows = table.tbodyElt.find("tr");
        equal(rows.length, 102, "DOM table should have 102 rows");
        table.removeRows(2, true);
        rows = table.tbodyElt.find("tr");
        equal(rows.length, 100, "DOM table should have 100 rows");
        equal(parseInt(rows[1].cells[0].innerText), 8, "first cell rendered should contain 8");
        table.removeRows(2, false);
        rows = table.tbodyElt.find("tr");
        equal(rows.length, 98, "DOM table should have 98 rows");
        equal(parseInt(rows[96].cells[0].innerText), 388, "last cell rendered should contain 388");
    });
});

asyncTest("Scroll table", 20, function () {
    var table = createTable();
    table.render(function () {
        start();
        // Scroll down, but not enough to render any new rows in the table
        table.tbodyElt[0].scrollTop = 4;
        table.scrollTable({target : {scrollTop : 2}});
        equal(table.rowRange.first, 0, "first row to render should be 0");
        equal(table.rowRange.last, 100, "last row to render should be 100");
        equal(table.prevRowRange.first, 0, "previous first row to render should be 0");
        equal(table.prevRowRange.last, 100, "previous last row to render should be 100");

        // Scroll down enough to render new rows
        table.tbodyElt[0].scrollTop = 1000;
        table.scrollTable({target : {scrollTop : 1000}});
        var rows = table.tbodyElt.find("tr");
        var expectedFirstRow = parseInt(998 / table.rowHeight);
        equal(table.rowRange.first, expectedFirstRow, "first row to render should be " + expectedFirstRow);
        equal(table.rowRange.last, expectedFirstRow + table.rowsToRender, "last row to render should be " + (expectedFirstRow + table.rowsToRender));
        equal(parseInt(rows[1].cells[0].innerText), table.rowRange.first * 4, "first cell rendered should contain " + table.rowRange.first * 4);
        equal(parseInt(rows[100].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);

        // Scroll back up some
        table.tbodyElt[0].scrollTop = 900;
        table.scrollTable({target : {scrollTop : 900}});
        rows = table.tbodyElt.find("tr");
        expectedFirstRow -= parseInt(100 / table.rowHeight);
        equal(table.rowRange.first, expectedFirstRow, "first row to render should be " + expectedFirstRow);
        equal(table.rowRange.last, expectedFirstRow + table.rowsToRender, "last row to render should be " + (expectedFirstRow + table.rowsToRender));
        equal(parseInt(rows[1].cells[0].innerText), table.rowRange.first * 4, "first cell rendered should contain " + table.rowRange.first * 4);
        equal(parseInt(rows[100].cells[0].innerText), (table.rowRange.last) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);

        // Scroll up more
        table.tbodyElt[0].scrollTop = 100;
        table.scrollTable({target : {scrollTop : 100}});
        rows = table.tbodyElt.find("tr");
        expectedFirstRow = parseInt(100 / table.rowHeight) + 1;
        equal(table.rowRange.first, expectedFirstRow, "first row to render should be " + expectedFirstRow);
        equal(table.rowRange.last, expectedFirstRow + table.rowsToRender, "last row to render should be " + (expectedFirstRow + table.rowsToRender));
        equal(parseInt(rows[1].cells[0].innerText), expectedFirstRow * 4, "first cell rendered should contain 0");
        equal(parseInt(rows[100].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);

        // Scroll up to the top
        table.tbodyElt[0].scrollTop = 0;
        table.scrollTable({target : {scrollTop : 0}});
        rows = table.tbodyElt.find("tr");
        equal(table.rowRange.first, 0, "first row to render should 0");
        equal(table.rowRange.last, 100, "last row to render should be 100");
        equal(parseInt(rows[1].cells[0].innerText), 0, "first cell rendered should contain 0");
        equal(parseInt(rows[100].cells[0].innerText), 396, "last row's first cell should contain 396");
    });
});

asyncTest("Random scroll stress test", 4000, function () {
    var table = createTable();
    table.render(function () {
        start();
        var scrollHeight = table.tbodyElt[0].scrollHeight;
        var bottomThreshold = (table.tbodyElt[0].scrollHeight - table.tbodyElt[0].clientHeight) * .8;
        var topThreshold = table.tbodyElt[0].scrollHeight * .1;
        for (var i = 1; i <= 1000; i++) {
            var scrollTop = Math.floor(Math.random() * (scrollHeight + 1));
            table.scrollTable({target : {scrollTop : scrollTop}});
            var rows = table.tbodyElt.find("tr");
            var expectedFirstRow;
            if(scrollTop > bottomThreshold && table.rowRange.first > table.prevRowRange.first){
                var rowsPast = parseInt((scrollTop - bottomThreshold) / table.rowHeight);
                expectedFirstRow = table.prevRowRange.first + rowsPast;
            }
            else if(scrollTop < topThreshold && table.rowRange.first < table.prevRowRange.first){
                var rowsPast = parseInt((topThreshold - scrollTop) / table.rowHeight);
                expectedFirstRow = table.prevRowRange.first - rowsPast;
            }
            else{
                expectedFirstRow = table.prevRowRange.first;
            }
            expectedFirstRow = expectedFirstRow >= 0 ? expectedFirstRow : 0;
            var expectedLastRow = expectedFirstRow + table.rowsToRender;
            expectedLastRow = expectedLastRow < table.rows.length ? expectedLastRow : table.rows.length - 1;
            if(expectedLastRow === table.rows.length) expectedFirstRow = expectedLastRow - table.rowsToRender;
            equal(table.rowRange.first, expectedFirstRow, "first row to render should be  " + expectedFirstRow);
            equal(table.rowRange.last, expectedLastRow, "last row to render should be " + expectedLastRow);
            equal(parseInt(rows[0].cells[0].innerText), table.rowRange.first * 4, "first cell rendered should contain " + table.rowRange.first * 4);
            equal(parseInt(rows[99].cells[0].innerText), (table.rowRange.last - 1) * 4, "last row's first cell should contain " + (table.rowRange.last - 1) * 4);
        }
    });
});<|endoftext|>