<commit_before>'use strict';

var Game = require( '../../../src/js/game' );
var Color = require( '../../../src/js/math/color' );
var Geometry = require( '../../../src/js/geometry/geometry' );
var Material = require( '../../../src/js/materials/material' );
var LambertMaterial = require( '../../../src/js/materials/lambert-material' );
var LambertGlowMaterial = require( '../../../src/js/materials/lambert-glow-material' );
var Mesh = require( '../../../src/js/objects/mesh' );
var DirectionalLight = require( '../../../src/js/lights/directional-light' );

var Controls = require( '../../main/controls' );

function addFuselageGeometry( geometry, forward, aft, width, height ) {
  forward = forward || 3;
  aft = aft || 1;
  width = width || 1;
  height = height || 0.5;

  var halfWidth = width / 2,
      halfHeight = height / 2;

  return geometry.push(
    // Vertices.
    [
      // Left vertex. :: 0
      halfWidth, 0, 0,
      // Right vertex. :: 1
      -halfWidth, 0, 0,

      // Top. :: 2
      0, halfHeight, 0,
      // Bottom. :: 3
      0, -halfHeight, 0,

      // Front. :: 4
      0, 0, forward,
      // Back. :: 5
      0, 0, -aft
    ],
    // Faces.
    [
      // Left faces.
      // Left top front.
      [ 0, 2, 4 ],
      // Left top back.
      [ 0, 5, 2 ],
      // Left bottom front.
      [ 0, 4, 3 ],
      // Left bottom back.
      [ 0, 3, 5 ],

      // Right faces.
      // Right top front.
      [ 1, 4, 2 ],
      // Right top back.
      [ 1, 2, 5 ],
      // Right bottom front.
      [ 1, 3, 4 ],
      // Right bottom back.
      [ 1, 5, 3 ]
    ]
  );
}

function addWingGeometry( geometry, offsetX, width, height, length, shear, forwardOffsetX, scaleX ) {
  var halfHeight = height / 2;

  var vertices = [
    // Wing tip. :: 0
    width, 0, -Math.tan( shear ) * width,

    // Fuselage connection top. :: 1
    0, halfHeight, 0,
    // Fuselage connection bottom. :: 2
    0, -halfHeight, 0,

    // Forward vertex. :: 3
    forwardOffsetX, 0, length
  ];

  var indices = [
    // Top.
    [ 0, 1, 3 ],
    // Bottom.
    [ 0, 3, 2 ],
    // Connection.
    [ 1, 2, 3 ],
    // Back.
    [ 1, 0, 2 ]
  ];

  for ( var i = 0, il = vertices.length; i < il; i += 3 ) {
    vertices[ i ] = scaleX * ( vertices[ i ] + offsetX );
    vertices[ i + 1 ] *= scaleX;
  }

  return geometry.push( vertices, indices );
}

function createShipGeometry() {
  var geometry = new Geometry();

  var fuselageForward = 4;
  var fuselageAft = 1;
  var fuselageWidth = 0.8;
  var fuselageHeight = 0.8;

  // Body.
  addFuselageGeometry(
    geometry,
    fuselageForward, fuselageAft,
    fuselageWidth, fuselageHeight
  );

  var wingOffsetX = 0.7;
  var wingWidth = 0.8;
  var wingHeight = 0.2;
  var wingLength = 1.2;
  var wingShear = Math.PI / 4;
  var wingForwardOffsetX = -0.2;

  // Wing.
  addWingGeometry(
    geometry,
    wingOffsetX,
    wingWidth,
    wingHeight,
    wingLength,
    wingShear,
    wingForwardOffsetX,
    1
  );

  addWingGeometry(
    geometry,
    wingOffsetX,
    wingWidth,
    wingHeight,
    wingLength,
    wingShear,
    wingForwardOffsetX,
    -1
  );

  geometry.computeFaceNormals();
  return geometry;
}

var movement = false;
document.addEventListener( 'keydown', function( event ) {
  if ( event.which === 32 ) {
    movement = true;
  }
});

document.addEventListener( 'keyup', function( event ) {
  if ( event.which === 32 ) {
    movement = false;
  }
});

document.addEventListener( 'touchstart', function() { movement = true;  });
document.addEventListener( 'touchend',   function() { movement = false; });

window.ShipTest = function() {
  var game = new Game( 568, 320 );
  var scene = game.scene;
  document.body.appendChild( game.canvas );

  var shipGeometry = createShipGeometry();
  var material = new LambertMaterial({
    color: new Color( 0.6, 0.6, 0.65 ),
    strokeColor: new Color( 0.3, 0.3, 0.3 ),
    diffuse: new Color( 1, 1, 1 ),
    ambient: new Color( 0.3, 0.3, 0.3 ),
    wireframe: true,
    lineWidth: 1
  });

  var mesh = new Mesh( shipGeometry, material );
  scene.add( mesh );

  game.ambient.setRGB( 0.4, 0.4, 0.4 );

  game.camera.position.set( 0, 1.8, -5 );
  game.camera.lookAt( 0, 0, 0 );
  game.camera.fov = 60;
  game.camera.updateProjectionMatrix();
  var controls = new Controls( game.camera );

  var light = new DirectionalLight( new Color( 1, 1, 1 ) );
  light.position.set( -5, 10, 0 );
  scene.add( light );

  var time = 0;
  var prev = 0;
  var rotateZ = 0;
  mesh.update = function( dt ) {
    if ( !movement ) {
      return;
    }

    // Undo previous rotation.
    mesh.rotateZ( -prev );

    time += dt;
    rotateZ = Math.sin( time );
    mesh.rotateZ( rotateZ );
    prev = rotateZ;

    mesh.position.x = 3 * Math.sin( time );
  };

  game.play();
};

exports.addFuselageGeometry = addFuselageGeometry;
exports.addWingGeometry = addWingGeometry;
exports.createShipGeometry = createShipGeometry;
<commit_msg>Add key controls to Ship test.<commit_after>'use strict';

var Game = require( '../../../src/js/game' );
var Color = require( '../../../src/js/math/color' );
var Geometry = require( '../../../src/js/geometry/geometry' );
var Material = require( '../../../src/js/materials/material' );
var LambertMaterial = require( '../../../src/js/materials/lambert-material' );
var LambertGlowMaterial = require( '../../../src/js/materials/lambert-glow-material' );
var Mesh = require( '../../../src/js/objects/mesh' );
var DirectionalLight = require( '../../../src/js/lights/directional-light' );

var Controls = require( '../../main/controls' );

var DEG_TO_RAD = Math.PI / 180;
var HALF_PI = Math.PI / 2;

function addFuselageGeometry( geometry, forward, aft, width, height ) {
  forward = forward || 3;
  aft = aft || 1;
  width = width || 1;
  height = height || 0.5;

  var halfWidth = width / 2,
      halfHeight = height / 2;

  return geometry.push(
    // Vertices.
    [
      // Left vertex. :: 0
      halfWidth, 0, 0,
      // Right vertex. :: 1
      -halfWidth, 0, 0,

      // Top. :: 2
      0, halfHeight, 0,
      // Bottom. :: 3
      0, -halfHeight, 0,

      // Front. :: 4
      0, 0, forward,
      // Back. :: 5
      0, 0, -aft
    ],
    // Faces.
    [
      // Left faces.
      // Left top front.
      [ 0, 2, 4 ],
      // Left top back.
      [ 0, 5, 2 ],
      // Left bottom front.
      [ 0, 4, 3 ],
      // Left bottom back.
      [ 0, 3, 5 ],

      // Right faces.
      // Right top front.
      [ 1, 4, 2 ],
      // Right top back.
      [ 1, 2, 5 ],
      // Right bottom front.
      [ 1, 3, 4 ],
      // Right bottom back.
      [ 1, 5, 3 ]
    ]
  );
}

function addWingGeometry( geometry, offsetX, width, height, length, shear, forwardOffsetX, scaleX ) {
  var halfHeight = height / 2;

  var vertices = [
    // Wing tip. :: 0
    width, 0, -Math.tan( shear ) * width,

    // Fuselage connection top. :: 1
    0, halfHeight, 0,
    // Fuselage connection bottom. :: 2
    0, -halfHeight, 0,

    // Forward vertex. :: 3
    forwardOffsetX, 0, length
  ];

  var indices = [
    // Top.
    [ 0, 1, 3 ],
    // Bottom.
    [ 0, 3, 2 ],
    // Connection.
    [ 1, 2, 3 ],
    // Back.
    [ 1, 0, 2 ]
  ];

  for ( var i = 0, il = vertices.length; i < il; i += 3 ) {
    vertices[ i ] = scaleX * ( vertices[ i ] + offsetX );
    vertices[ i + 1 ] *= scaleX;
  }

  return geometry.push( vertices, indices );
}

function createShipGeometry() {
  var geometry = new Geometry();

  var fuselageForward = 3;
  var fuselageAft = 0.8;
  var fuselageWidth = 0.8;
  var fuselageHeight = 0.6;

  // Body.
  addFuselageGeometry(
    geometry,
    fuselageForward, fuselageAft,
    fuselageWidth, fuselageHeight
  );

  var wingOffsetX = 0.8;
  var wingWidth = 0.8;
  var wingHeight = 0.2;
  var wingLength = 1.2;
  var wingShear = Math.PI / 4;
  var wingForwardOffsetX = -0.2;

  // Wing.
  addWingGeometry(
    geometry,
    wingOffsetX,
    wingWidth,
    wingHeight,
    wingLength,
    wingShear,
    wingForwardOffsetX,
    1
  );

  addWingGeometry(
    geometry,
    wingOffsetX,
    wingWidth,
    wingHeight,
    wingLength,
    wingShear,
    wingForwardOffsetX,
    -1
  );

  geometry.computeFaceNormals();
  return geometry;
}

var keys = [];
document.addEventListener( 'keydown', function( event ) {
  keys[ event.which ] = true;
});

document.addEventListener( 'keyup', function( event ) {
  keys[ event.which ] = false;
});

document.addEventListener( 'touchstart', function() { keys[ 32 ] = true;  });
document.addEventListener( 'touchend',   function() { keys[ 32 ] = false; });

window.ShipTest = function() {
  var game = new Game( 568, 320 );
  var scene = game.scene;
  document.body.appendChild( game.canvas );

  var shipGeometry = createShipGeometry();
  var material = new LambertMaterial({
    color: new Color( 0.6, 0.6, 0.65 ),
    strokeColor: new Color( 0.3, 0.3, 0.3 ),
    diffuse: new Color( 1, 1, 1 ),
    ambient: new Color( 0.3, 0.3, 0.3 ),
    wireframe: true,
    lineWidth: 1
  });

  var mesh = new Mesh( shipGeometry, material );
  scene.add( mesh );

  game.ambient.setRGB( 0.4, 0.4, 0.4 );

  game.camera.position.set( 0, 4, -6 );
  var controls = new Controls( game.camera );
  controls.target.set( 0, 3, 8 );
  controls.update();

  var light = new DirectionalLight( new Color( 1, 1, 1 ) );
  light.position.set( -5, 10, 0 );
  scene.add( light );

  var speed = 24;
  var limit = 6;
  var turnRate = 180 * DEG_TO_RAD;

  var time = 0;
  var prev = 0;
  var rotateZ = 0;
  mesh.update = function( dt ) {
    // Space.
    if ( keys[ 32 ] ) {
      // Undo previous rotation.
      mesh.rotateZ( -prev );

      time += dt;
      rotateZ = Math.sin( time );
      mesh.rotateZ( rotateZ );
      prev = rotateZ;

      mesh.position.x = 4 * Math.sin( time );
      return;
    }

    var position = mesh.position;
    var rotation = mesh.rotation;
    // Right arrow. D.
    if ( keys[ 39 ] || keys[ 68 ] ) {
      if ( position.x > -limit ) {
        position.x -= speed * dt;
        rotation.z = Math.max( rotation.z - turnRate * dt, -HALF_PI );
      }
    }

    // Left arrow. A.
    if ( keys[ 37 ] || keys[ 65 ] ) {
      if ( position.x < limit ) {
        position.x += speed * dt;
        rotation.z = Math.min( rotation.z + turnRate * dt, HALF_PI );
      }
    }

    rotation.z -= 4 * rotation.z * dt;

    if ( Math.abs( rotation.z ) < 1e-2 ) {
      rotation.z = 0;
    }

    mesh.updateQuaternion();
  };

  game.play();
};

exports.addFuselageGeometry = addFuselageGeometry;
exports.addWingGeometry = addWingGeometry;
exports.createShipGeometry = createShipGeometry;
<|endoftext|>


<commit_before>(function($) {

$.widget('ui.sympalSlot', {

  options : {
    edit_mode : 'popup'
  },

  _init : function()
  {
    if (edit_mode = this.element.metadata().edit_mode)
    {
      this._setData('edit_mode', edit_mode);
    }

    if (slot_type = this.element.metadata().slot_type)
    {
      this._setData('slot_type', slot_type);
    }

    if (view_url = this.element.metadata().view_url)
    {
      this._setData('view_url', view_url);
    }

    this.initialize();
  },

  openEditor : function()
  {
    self = this;

    // determine the editor object
    if (this._getData('edit_mode') == 'in-place')
    {
      var editor = $('.sympal_slot_content', self.element);
    }
    else
    {
      var editor = $('#fancybox-inner');
    }

    // register the ajaxSuccess function on the editor
    editor.bind('ajaxResponseSuccess', function() {
      editor.trigger('block');

      // setup the ajax form submit
      $('form', editor).submit(function() {
        editor.trigger('block');

        $(this).ajaxSubmit( {
          error: function(xhr, textStatus, errorThrown)
          {
            editor.trigger('unblock');
            // display some sort of error
          },
          success: function(responseText, statusText, xhr)
          {
            $('form_body', $(this)).html(responseText);
            editor.trigger('ajaxResponseSuccess');
            editor.trigger('unblock');
          }
        });

        return false;
      }); // end ajax for submit

      // initialize any slot-specific functionality if applicable
      formClass = 'sfSympalSlot'+self.element.metadata().type;
      if ($.isFunction(self.element[formClass]))
      {
        self.element[formClass](self.element);
      }

      /*
      $('input', this).sympalDroppableInput(function(){
        // callback for when something is dropped
      });
      */

      // hook up the cancel button
      $('form input.cancel', this).click(function() {
        editor.trigger('close');

        return false;
      });

      editor.trigger('unblock');
    }); // end ajaxResponseSuccess

    // bind the block and unblocks
    editor.bind('block', function() {
      // inline, we've gotta block the whole page
      if ($(this).css('display') == 'block')
      {
        // you actually want to block the parent, #facebox-wrapper
        $(this).parent().block();
      }
      else
      {
        $.blockUI();
      }
    });
    editor.bind('unblock', function() {
      if ($(this).css('display') == 'block')
      {
        $(this).parent().unblock();
      }
      else
      {
        $.unblockUI();
      }
    });

    // actually ajax in the data and trigger editor.ajaxResponseSuccess
    href = $('.sympal_slot_button', this.element).attr('href');
    if (this._getData('edit_mode') == 'in-place')
    {
      editor.load(href, function() {
        editor.trigger('ajaxResponseSuccess');
      });
    }
    else
    {
      $.fancybox(href, {
        'zoomSpeedIn': 300,
        'zoomSpeedOut': 300,
        'overlayShow': true,
        'autoDimensions': false,
        'hideOnContentClick': false,
        'type': 'ajax',
        'onComplete': function() {
          editor.trigger('ajaxResponseSuccess');
        },
        'onCleanup': function() {
          editor.trigger('close')
        },
      });
    }

    editor.bind('close', function() {
      if (editor.attr('id') == 'fancybox-inner')
      {
        $.fancybox.close();
      }
      else
      {
        editor.html('');
      }

      $.blockUI();

      $('.sympal_slot_content', self.element).load(
        self._getData('view_url'),
        function() {
          $.unblockUI();
        }
      );
    });
  },

  closeEditor : function()
  {
    if ($('form', this.element).eq(0).hasClass('popup'))
    {
      // popup closing
      $.fancybox.close();
    }
    else
    {
      // inline closing
      //sympal_toggle_inline_edit_slot(wrapperEle, false);
    }
  },

  initialize: function()
  {
    var self = this;

    // open up the editor
    $('a.sympal_slot_button', this.element).click(function() {
      self.openEditor()

      return false;
    });

    // show the edit button when hovering over editable area
    this.element.hover(function() {
      $('.sympal_slot_button:not(.no-hover)', self.element).show();
    }, function() {
      $('.sympal_slot_button:not(.no-hover)', self.element).hide();
    });

    $('.sympal_slot_content').bind('dblclick', function() {
      $('.sympal_slot_button', self.element).click();
    });

    // highlight editable area on edit button hover
    $('a.sympal_slot_button', this.element).hover(function() {
      $('.sympal_slot_content', self.element)
        .css('opacity', .2)
        .children().css('opacity', .2);
    }, function() {
      $('.sympal_slot_content', self.element)
        .css('opacity', 1)
        .children().css('opacity', 1);
    });
  }
});

})(jQuery);

jQuery(document).ready(function(){

  $('.sympal_slot_wrapper').sympalSlot();

  $('.sympal_inline_edit_bar_edit_buttons').show();

  // globally save slots on the "save" edit button
  $('#inline-edit-bar-buttons-menu .sympal_save_content_slots').click(function(){
    $('form.sympal_slot_form').submit();
  });

  // globally hide slot formss on the "cancel" edit button
  $('#inline-edit-bar-buttons-menu .sympal_disable_edit_mode').click(function(){
    $('form.sympal_slot_form input.cancel').click();
  });
});<commit_msg>[1.4][sfSympalPlugin][1.0] Fixing bug where the double-click function was being bound to all matching elements (wasn't bound to the current element's scope).<commit_after>(function($) {

$.widget('ui.sympalSlot', {

  options : {
    edit_mode : 'popup'
  },

  _init : function()
  {
    if (edit_mode = this.element.metadata().edit_mode)
    {
      this._setData('edit_mode', edit_mode);
    }

    if (slot_type = this.element.metadata().slot_type)
    {
      this._setData('slot_type', slot_type);
    }

    if (view_url = this.element.metadata().view_url)
    {
      this._setData('view_url', view_url);
    }

    this.initialize();
  },

  openEditor : function()
  {
    self = this;

    // determine the editor object
    if (this._getData('edit_mode') == 'in-place')
    {
      var editor = $('.sympal_slot_content', self.element);
    }
    else
    {
      var editor = $('#fancybox-inner');
    }

    // register the ajaxSuccess function on the editor
    editor.bind('ajaxResponseSuccess', function() {
      editor.trigger('block');

      // setup the ajax form submit
      $('form', editor).submit(function() {
        editor.trigger('block');

        $(this).ajaxSubmit( {
          error: function(xhr, textStatus, errorThrown)
          {
            editor.trigger('unblock');
            // display some sort of error
          },
          success: function(responseText, statusText, xhr)
          {
            $('form_body', $(this)).html(responseText);
            editor.trigger('ajaxResponseSuccess');
            editor.trigger('unblock');
          }
        });

        return false;
      }); // end ajax for submit

      // initialize any slot-specific functionality if applicable
      formClass = 'sfSympalSlot'+self.element.metadata().type;
      if ($.isFunction(self.element[formClass]))
      {
        self.element[formClass](self.element);
      }

      /*
      $('input', this).sympalDroppableInput(function(){
        // callback for when something is dropped
      });
      */

      // hook up the cancel button
      $('form input.cancel', this).click(function() {
        editor.trigger('close');

        return false;
      });

      editor.trigger('unblock');
    }); // end ajaxResponseSuccess

    // bind the block and unblocks
    editor.bind('block', function() {
      // inline, we've gotta block the whole page
      if ($(this).css('display') == 'block')
      {
        // you actually want to block the parent, #facebox-wrapper
        $(this).parent().block();
      }
      else
      {
        $.blockUI();
      }
    });
    editor.bind('unblock', function() {
      if ($(this).css('display') == 'block')
      {
        $(this).parent().unblock();
      }
      else
      {
        $.unblockUI();
      }
    });

    // actually ajax in the data and trigger editor.ajaxResponseSuccess
    href = $('.sympal_slot_button', this.element).attr('href');
    if (this._getData('edit_mode') == 'in-place')
    {
      editor.load(href, function() {
        editor.trigger('ajaxResponseSuccess');
      });
    }
    else
    {
      $.fancybox(href, {
        'zoomSpeedIn': 300,
        'zoomSpeedOut': 300,
        'overlayShow': true,
        'autoDimensions': false,
        'hideOnContentClick': false,
        'type': 'ajax',
        'onComplete': function() {
          editor.trigger('ajaxResponseSuccess');
        },
        'onCleanup': function() {
          editor.trigger('close')
        },
      });
    }

    editor.bind('close', function() {
      if (editor.attr('id') == 'fancybox-inner')
      {
        $.fancybox.close();
      }
      else
      {
        editor.html('');
      }

      $.blockUI();

      $('.sympal_slot_content', self.element).load(
        self._getData('view_url'),
        function() {
          $.unblockUI();
        }
      );
    });
  },

  closeEditor : function()
  {
    if ($('form', this.element).eq(0).hasClass('popup'))
    {
      // popup closing
      $.fancybox.close();
    }
    else
    {
      // inline closing
      //sympal_toggle_inline_edit_slot(wrapperEle, false);
    }
  },

  initialize: function()
  {
    var self = this;

    // open up the editor
    $('a.sympal_slot_button', this.element).click(function() {
      self.openEditor()

      return false;
    });

    $('.sympal_slot_content', this.element).bind('dblclick', function() {
      self.openEditor()
    });

    // show the edit button when hovering over editable area
    this.element.hover(function() {
      $('.sympal_slot_button:not(.no-hover)', self.element).show();
    }, function() {
      $('.sympal_slot_button:not(.no-hover)', self.element).hide();
    });

    // highlight editable area on edit button hover
    $('a.sympal_slot_button', this.element).hover(function() {
      $('.sympal_slot_content', self.element)
        .css('opacity', .2)
        .children().css('opacity', .2);
    }, function() {
      $('.sympal_slot_content', self.element)
        .css('opacity', 1)
        .children().css('opacity', 1);
    });
  }
});

})(jQuery);

jQuery(document).ready(function(){

  $('.sympal_slot_wrapper').sympalSlot();

  $('.sympal_inline_edit_bar_edit_buttons').show();

  // globally save slots on the "save" edit button
  $('#inline-edit-bar-buttons-menu .sympal_save_content_slots').click(function(){
    $('form.sympal_slot_form').submit();
  });

  // globally hide slot formss on the "cancel" edit button
  $('#inline-edit-bar-buttons-menu .sympal_disable_edit_mode').click(function(){
    $('form.sympal_slot_form input.cancel').click();
  });
});<|endoftext|>


<commit_before>define([
    "./core"
], function( Cinematic ) {

//TODO re-indent
Cinematic.extend( "query", function( name, reset) {
    // We do two requests each first search
    // since omdbAPI limit each request to 10
    // movies per page.
    var
        reset = reset || false;

    if ( reset ) {
        // Reset
        Cinematic.lastPage = 1;
        Cinematic.clearResults();
        Cinematic.newRequest( name );
        Cinematic.newRequest( name );
    } else {
        Cinematic.newRequest( name );
    }
}, true);

Cinematic.extend( "newRequest", function( name ) {
        var msg = new Message( "loading...", "bottom");
        const URI = "http://www.omdbapi.com/?s=" + name + "&page=" + Cinematic.lastPage++;
        $.ajax({
            url: URI,
            "success": function( data ){
                if ( data.Response == 'True' ) {
                    Cinematic.lastResults = data;
                    Cinematic.lastTitleSearch = name;
                    Cinematic.appendResults();
                    msg.miss();
                } else {
                    msg.miss();
                    new Message( "No results for search", "top" );
                }
            }
        });
}, true);

Cinematic.extend( "update", function() {
    Cinematic.query( Cinematic.lastTitleSearch );
}, true)

Cinematic.debug.extend( "newRequest", function( onSuccessFn ) {
        const
            URI = "../test/assets/movie.fixture.json";

        $.ajax({
            url: URI,
            "success": onSuccessFn
        });
}, true);

});
<commit_msg>Ajax: improved loading msg to be shown on timeout<commit_after>define([
    "./core"
], function( Cinematic ) {

var msg;

function connTimeout() {
    msg = new Message( "loading...", "bottom");
}

//TODO re-indent
Cinematic.extend( "query", function( name, reset) {
    // We do two requests each first search
    // since omdbAPI limit each request to 10
    // movies per page.
    var
        reset = reset || false;

    if ( reset ) {
        // Reset
        Cinematic.lastPage = 1;
        Cinematic.clearResults();
        Cinematic.newRequest( name );
        Cinematic.newRequest( name );
    } else {
        Cinematic.newRequest( name );
    }
}, true);

Cinematic.extend( "newRequest", function( name ) {
        var timeout = setTimeout( connTimeout, 1000),
            URI = "http://www.omdbapi.com/?s=" + name + "&page=" + Cinematic.lastPage++;
        $.ajax({
            url: URI,
            "success": function( data ){
                clearTimeout( timeout );
                if ( data.Response == 'True' ) {
                    Cinematic.lastResults = data;
                    Cinematic.lastTitleSearch = name;
                    Cinematic.appendResults();
                    msg.miss();
                } else {
                    msg.miss();
                    new Message( "No results for search", "top" );
                }
            }
        });
}, true);

Cinematic.extend( "update", function() {
    Cinematic.query( Cinematic.lastTitleSearch );
}, true)

Cinematic.debug.extend( "newRequest", function( onSuccessFn ) {
        const
            URI = "../test/assets/movie.fixture.json";

        $.ajax({
            url: URI,
            "success": onSuccessFn
        });
}, true);

});
<|endoftext|>


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

  var Dyframe = window.Dyframe;
  var element = document.createElement('div');
  var dyframe;

  // Helpers
  var hasClass = function (element, className) {
    if (element.classList) {
      return element.classList.contains(className);
    }
    else {
      return new RegExp('(^|\\s)' + className + '(?!\\S)', 'g').test(element.className);
    }
  };
  var addClass = function (element, className) {
    if (element.classList) {
      element.classList.add(className);
    }
    else {
      element.className += ' ' + className;
    }
  };

  // Set up fixtures
  element.style.width = '100px';
  element.style.height = '200px';
  document.body.appendChild(element);

  // Specs
  describe('Dyframe', function () {

    describe('.addProfile()', function () {

      afterEach(function () {
        dyframe.destroy();
      });

      it('adds active profile for dyframe objects', function () {
        Dyframe.addProfile('custom', {});
        dyframe = new Dyframe(element, {
          profile: 'custom'
        });
        expect(dyframe.hasActiveProfile()).to.be.true;
      });

    });

    describe('Constructor', function () {

      afterEach(function () {
        dyframe.destroy();
      });

      it('creates
 and  in the target element', function () {
        dyframe = new Dyframe(element);
        expect(element.querySelector('div')).not.to.be.null;
        expect(element.querySelector('iframe')).not.to.be.null;
      });

      it('creates HTML content in ', function () {
        dyframe = new Dyframe(element, {
          html: 'Hello, world!'
        });
        var iframe = element.querySelector('iframe');
        var body = iframe.contentWindow.document.body;
        expect(body.innerHTML).to.equal('Hello, world!');
      });

      it('sets default options when no options given', function () {
        var defaults = {
          html: '',
          width: 980,
          deviceWidth: null,
          profile: null
        };
        dyframe = new Dyframe(element);
        expect(dyframe.options).to.deep.equal(defaults);
      });

      it('overrides default options when options given', function () {
        var options = {
          html: 'Hello, Dyframe!',
          deviceWidth: 360
        };
        var expected = {
          html: 'Hello, Dyframe!',
          width: 980,
          deviceWidth: 360,
          profile: null
        };
        dyframe = new Dyframe(element, options);
        expect(dyframe.options).to.deep.equal(expected);
      });

      it('adds "df-element" class to the target element', function () {
        dyframe = new Dyframe(element);
        expect(hasClass(element, 'df-element')).to.be.true;
      });

      it('adds "df-profile-<name>" class when profile option given', function () {
        dyframe = new Dyframe(element, {
          profile: 'smartphone'
        });
        expect(hasClass(element, 'df-profile-smartphone')).to.be.true;
      });

    });

  });

  describe('dyframe', function () {

    describe('.render()', function () {

      var iframe;
      var body;

      beforeEach(function () {
        dyframe = new Dyframe(element, {
          html: 'Hello, world!'
        });
        iframe = element.querySelector('iframe');
      });

      afterEach(function () {
        dyframe.destroy();
      });

      it('overrides options when argument given', function () {
        dyframe.render({
          width: 1200
        });
        expect(dyframe.options.width).to.equal(1200);
      });

      // Somehow this is not working on PhantomJS...
      // it('makes  same size with target element', function () {
      //   dyframe.render();
      //   var rect = iframe.getBoundingClientRect();
      //   expect(rect.width).to.equal(100);
      //   expect(rect.height).to.equal(200);
      // });

      it('renders HTML according to default options', function (done) {
        dyframe.render();
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML according to width option', function (done) {
        dyframe.render({
          width: 1200
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(1200);
          done();
        }, 0);
      });

      it('renders HTML accroding to deviceWidth option if HTML has meta-viewport', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(360);
          done();
        }, 0);
      });

      it('renders HTML accroding to width option if HTML does not have meta-viewport', function (done) {
        dyframe.render({
          html: '',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML accroding to width value in meta-viewport', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=720">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(720);
          done();
        }, 0);
      });

      it('renders HTML accroding to initial-scale value in meta-viewport', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="initial-scale=0.5">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(720);
          done();
        }, 0);
      });

      it('renders HTML ignoring meta-viewport that does not have content', function (done) {
        dyframe.render({
          html: '<meta name="viewport">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML ignoring meta-viewport that has invalid content', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width:device-width">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML accroding to "smartphone" profile', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          profile: 'smartphone'
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(375);
          done();
        }, 0);
      });

      it('renders HTML accroding to "tablet" profile', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          profile: 'tablet'
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(768);
          done();
        }, 0);
      });

      it('renders HTML accroding to custom profile', function (done) {
        Dyframe.addProfile('nexus-6', {
          width: 980,
          deviceWidth: 412
        });
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          profile: 'nexus-6'
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(412);
          done();
        }, 0);
      });

    });

    describe('.destroy()', function () {

      beforeEach(function () {
        dyframe = new Dyframe(element, {
          html: 'Hello, world!'
        });
      });

      it('cleans up the target element', function () {
        dyframe.destroy();
        expect(element.innerHTML).to.be.empty;
      });

      it('removes "df" related classes', function () {
        dyframe.render({
          profile: 'smartphone'
        });
        dyframe.destroy();
        expect(hasClass(element, 'df-element')).to.be.false;
        expect(hasClass(element, 'df-profile-smartphone')).to.be.false;
      });

      it('preserves non-"df" classes', function () {
        addClass(element, 'non-df-class');
        dyframe.destroy();
        expect(hasClass(element, 'non-df-class')).to.be.true;
      });

      it('can be called twice, but does nothing', function () {
        dyframe.destroy();
        expect(element.innerHTML).to.be.empty;
        expect(function () {dyframe.destroy();}).to.not.throw(Error);
        expect(element.innerHTML).to.be.empty;
      });

    });

  });

}());
<commit_msg>Tweak<commit_after>(function () {
  'use strict';

  var Dyframe = window.Dyframe;
  var element = document.createElement('div');
  var dyframe;

  // Helpers
  var hasClass = function (element, className) {
    if (element.classList) {
      return element.classList.contains(className);
    }
    else {
      return new RegExp('(^|\\s)' + className + '(?!\\S)', 'g').test(element.className);
    }
  };
  var addClass = function (element, className) {
    if (element.classList) {
      element.classList.add(className);
    }
    else {
      element.className += ' ' + className;
    }
  };

  // Set up fixtures
  element.style.width = '100px';
  element.style.height = '200px';
  document.body.appendChild(element);

  // Specs
  describe('Dyframe', function () {

    describe('.addProfile()', function () {

      afterEach(function () {
        dyframe.destroy();
      });

      it('adds active profile for dyframe objects', function () {
        Dyframe.addProfile('custom', {});
        dyframe = new Dyframe(element, {
          profile: 'custom'
        });
        expect(dyframe.hasActiveProfile()).to.be.true;
      });

    });

    describe('Constructor', function () {

      afterEach(function () {
        dyframe.destroy();
      });

      it('creates
 and  in the target element', function () {
        dyframe = new Dyframe(element);
        expect(element.querySelector('div')).not.to.be.null;
        expect(element.querySelector('iframe')).not.to.be.null;
      });

      it('creates HTML content in ', function () {
        dyframe = new Dyframe(element, {
          html: 'Hello, world!'
        });
        var iframe = element.querySelector('iframe');
        var body = iframe.contentWindow.document.body;
        expect(body.innerHTML).to.equal('Hello, world!');
      });

      it('sets default options when no options given', function () {
        var defaults = {
          html: '',
          width: 980,
          deviceWidth: null,
          profile: null
        };
        dyframe = new Dyframe(element);
        expect(dyframe.options).to.deep.equal(defaults);
      });

      it('overrides default options when options given', function () {
        var options = {
          html: 'Hello, Dyframe!',
          deviceWidth: 360
        };
        var expected = {
          html: 'Hello, Dyframe!',
          width: 980,
          deviceWidth: 360,
          profile: null
        };
        dyframe = new Dyframe(element, options);
        expect(dyframe.options).to.deep.equal(expected);
      });

      it('adds "df-element" class to the target element', function () {
        dyframe = new Dyframe(element);
        expect(hasClass(element, 'df-element')).to.be.true;
      });

      it('adds "df-profile-<name>" class when profile option given', function () {
        dyframe = new Dyframe(element, {
          profile: 'smartphone'
        });
        expect(hasClass(element, 'df-profile-smartphone')).to.be.true;
      });

    });

  });

  describe('dyframe', function () {

    describe('.render()', function () {

      var iframe;
      var body;

      beforeEach(function () {
        dyframe = new Dyframe(element, {
          html: 'Hello, world!'
        });
        iframe = element.querySelector('iframe');
      });

      afterEach(function () {
        dyframe.destroy();
      });

      it('overrides options when argument given', function () {
        dyframe.render({
          width: 1200
        });
        expect(dyframe.options.width).to.equal(1200);
      });

      // Somehow this is not working on PhantomJS...
      // it('makes  same size with target element', function () {
      //   dyframe.render();
      //   var rect = iframe.getBoundingClientRect();
      //   expect(rect.width).to.equal(100);
      //   expect(rect.height).to.equal(200);
      // });

      it('renders HTML according to default options', function (done) {
        dyframe.render();
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML according to width option', function (done) {
        dyframe.render({
          width: 1200
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(1200);
          done();
        }, 0);
      });

      it('renders HTML accroding to deviceWidth option if HTML has meta-viewport', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(360);
          done();
        }, 0);
      });

      it('renders HTML accroding to width option if HTML does not have meta-viewport', function (done) {
        dyframe.render({
          html: '',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML accroding to width value in meta-viewport', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=720">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(720);
          done();
        }, 0);
      });

      it('renders HTML accroding to initial-scale value in meta-viewport', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="initial-scale=0.5">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(720);
          done();
        }, 0);
      });

      it('renders HTML ignoring meta-viewport that does not have content', function (done) {
        dyframe.render({
          html: '<meta name="viewport">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML ignoring meta-viewport that has invalid content', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width:device-width">',
          width: 980,
          deviceWidth: 360
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(980);
          done();
        }, 0);
      });

      it('renders HTML accroding to "smartphone" profile', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          profile: 'smartphone'
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(375);
          done();
        }, 0);
      });

      it('renders HTML accroding to "tablet" profile', function (done) {
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          profile: 'tablet'
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(768);
          done();
        }, 0);
      });

      it('renders HTML accroding to custom profile', function (done) {
        Dyframe.addProfile('nexus-6', {
          width: 980,
          deviceWidth: 412
        });
        dyframe.render({
          html: '<meta name="viewport" content="width=device-width">',
          profile: 'nexus-6'
        });
        setTimeout(function () {
          expect(iframe.contentWindow.innerWidth).to.equal(412);
          done();
        }, 0);
      });

    });

    describe('.destroy()', function () {

      beforeEach(function () {
        dyframe = new Dyframe(element, {
          html: 'Hello, world!'
        });
      });

      it('cleans up the target element', function () {
        dyframe.destroy();
        expect(element.innerHTML).to.be.empty;
      });

      it('removes "df" related classes', function () {
        dyframe.render({
          profile: 'smartphone'
        });
        dyframe.destroy();
        expect(hasClass(element, 'df-element')).to.be.false;
        expect(hasClass(element, 'df-profile-smartphone')).to.be.false;
      });

      it('preserves non-"df" classes', function () {
        addClass(element, 'non-df-class');
        dyframe.destroy();
        expect(hasClass(element, 'df-element')).to.be.false;
        expect(hasClass(element, 'non-df-class')).to.be.true;
      });

      it('can be called twice, but does nothing', function () {
        dyframe.destroy();
        expect(element.innerHTML).to.be.empty;
        expect(function () {dyframe.destroy();}).to.not.throw(Error);
        expect(element.innerHTML).to.be.empty;
      });

    });

  });

}());
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////  CRUD //////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

// POSTS //////////////////////////////////////////////////////////////////////////////////

lenv.posts = {
    create: function(data, cb) {
        var fd = new FormData, params;

        // Add the attachments and content to the FormData instance
        if (data.attachments) {
            for (var i = data.attachments.length - 1; i >= 0; i--) {
                if (data.attachments[i]) // Can be null
                    fd.append('attachment', data.attachments[i].file);
            }
        }

        if (data.content)
            fd.append('content', data.content);

        // Id and type of the base item
        if (data.image)
            fd.append('image', data.image);
        else if (data.post)
            fd.append('post', data.post);
        else;

        // POST request
        console.log('lenv.posts.create', data)
        $.ajax({
            type: 'POST',
            url: VARS.xhr + '/posts',
            data: fd,
            processData: false,
            contentType: false,
            success: function(data) { cb(data) }
        });
    },
    load: function(data, cb) {
        $.ajax({
            url: VARS.xhr + '/posts?' + $.param(data),
            success: function (data) { cb(data) }
        });
    },
    delete: function(ids, cb) {
        $.ajax({
            url: VARS.xhr + '/posts?ids=' + JSON.stringify(ids),
            type: 'DELETE',
            success: function (data) { cb(data) }
        })
    }
};

// IMAGES /////////////////////////////////////////////////////////////////////////////////

lenv.images = {
    load: function(data, cb) {
        $.ajax({
            url: VARS.xhr + '/images?' + $.param(data),
            success: function (data) {
                cb(data);
            }
        });
    },
    upload: function (data, cb) {
        var fd = new FormData;

        // Add images to the FormData instance

        for (var i = 0; i < (data.files || []).length; i++)
            fd.append('image', data.files[i]);

        var params = $.param({
            url: data.url || null,
            id: data.id || null,
            set: data.set || null
        });
        $.ajax({
            url: VARS.xhr + '/images?' + params,
            type: 'POST',
            data: fd,
            processData: false,
            contentType: false,
            success: function(data) { cb(data) }
        });
    },
    delete: function (ids, cb) {
        $.ajax({
            url: VARS.xhr + '/images?ids=' + JSON.stringify(ids),
            type: 'DELETE',
            success: function (data) { cb(data) }
        });
    }
};

// ACCS ///////////////////////////////////////////////////////////////////////////////////

lenv.account_data = {
    get: function (data, cb) {
        $.ajax({
            url: VARS.xhr + '/records?' + $.param(data),
            success: function (data) { cb(data) }
        })
    },
    update: function (data, cb) {
        $.ajax({
            type: 'PUT',
            url: VARS.xhr + '/records/set', // Empty values will be eliminated anyway
            data: JSON.stringify(data),
            success: function (data) { cb(data) }
        })
    },
    unset: function (data, cb) {
        $.ajax({
            url: VARS.xhr + '/records/unset',
            type: 'PUT',
            data: data,
            success: function(data) {
                console.log(data);
            }
        });
    }
};

// NOTIFICATIONS //////////////////////////////////////////////////////////////////////////

lenv.notifications  = {
    get: function(data, cb) {
        $.ajax({
            url: VARS.xhr + '/notifications',
            success: function(data) {
                console.log(data)
            }
        })
    },
    delete: function(ids) {
        if (typeof ids != 'undefined') {
            $.ajax({
                url: VARS.xhr + '/notifications',
                type: 'DELETE',
                data: JSON.stringify({'ids': ids}),
                success: function(data) {
                    console.log(data)
                }
            });
        } else {
            $.ajax({
                url: VARS.xhr + '/notifications',
                type: 'DELETE',
                success: function(data) {
                    console.log(data)
                }
            });
        }
    }
}<commit_msg>Did api.js<commit_after>let APIS = {

    // POSTS
    posts: {
        create(content, attachments=[], post=undefined, image=undefined) {
            let fd = new FormData(), params;

            // Add the attachments and content to the FormData instance
            for (let attachment of attachments)
                if (attachment) // Can be null
                    fd.append('attachment', attachment.file);

            if (data.content)
                fd.append('content', data.content);

            // Id and type of the base item
            if (image) fd.append('image', image);
            else if (post) fd.append('post', post);

            return $.ajax({
                type: 'POST',
                url:  `${ VARS.xhr }/posts`,
                data: fd,
                processData: false,
                contentType: false,
            });

        }, load(data) {
            return $.ajax({
                url: `${ VARS.xhr }/posts?${ $.param(data)} `,
            });
        }, delete(ids) {
            return $.ajax({
                url: `${ VARS.xhr }/posts?ids=${ JSON.stringify(ids) }`,
                type: 'DELETE',
            });
        }
    },

    // IMAGES

    images: {
        load(data) {
            return $.ajax({
                url: `${ VARS.xhr }/images?${ $.param(data) }`,
            });
        }, upload(files=[], url=null, id=null, set=null) {
            let fd = new FormData();

            // Add images to the FormData instance

            for (let file of files)
                fd.append('image', file);

            let params = $.param({ url: url, id: id, set: set });

            return $.ajax({
                url: `${ VARS.xhr }/images?${ params }`,
                type: 'POST',
                data: fd,
                processData: false,
                contentType: false,
            });
        }, delete(ids) {
            return $.ajax({
                url: `${ VARS.xhr }/images?ids=${ JSON.stringify(ids) }`,
                type: 'DELETE',
            });
        }
    },

    // ACCOUNTS

    account_data: {
        get(data) {
            return $.ajax({
                url: `${ VARS.xhr }/records?${ $.param(data) }`,
            });
        }, update(data) {
            return $.ajax({
                type: 'PUT',
                url: `${ VARS.xhr }/records/set`, // Empty values will be eliminated anyway
                data: JSON.stringify(data),
            });
        }, unset(data) {
            return $.ajax({
                url: `${ VARS.xhr }/records/unset`,
                type: 'PUT',
                data: JSON.stringify(data), // Do we need this?
            });
        }
    },

    // NOTIFICATIONS

    notifications : {
        get(data) {
            return $.ajax({
                url: `${ VARS.xhr }/notifications`,
            });
        }, delete(ids=null) {
            if (ids !== null) return $.ajax({
                url: `${ VARS.xhr }/notifications`,
                type: 'DELETE',
                data: JSON.stringify({'ids': ids}),
            });
            else return $.ajax({
                url: `${ VARS.xhr }/notifications`,
                type: 'DELETE',
            });
        }
    }
};<|endoftext|>


<commit_before>/**
 * @fileOverview Job item status and props
 */

var sinon  = require('sinon');
var assert = require('chai').assert;

var kickq  = require('../../');
var tester = require('../lib/tester');
var jobItemTest = require('../asserts/jobitem.assert');

// var noop = function(){};

suite('3. Job Item Status and Props', function() {

  var jobId;

  setup(tester.reset);
  setup(function(done) {
    tester.clear(function(){
      // create a dummy job and get the id
      kickq.create('jobItem test plain job', function(err, job){
        if (err) {
          done(err);
          return;
        }
        jobId = job.id;
        done();
      });
    });
  });

  teardown(function() {
  });

  // The numbering (e.g. 1.1.1) has nothing to do with order
  // The purpose is to provide a unique string so specific tests are
  // run by using the mocha --grep "1.1.1" option.
  suite('3.0 A new job fetched manualy', function() {
    var jobItem;
    setup(function(done) {
      kickq.get(jobId, function(err, job){
        jobItem = job;
        done(err);
      });
    });
    test('3.0.1 Passes the jobItem prop tests', function(){
      jobItemTest.testNewItemPropsType(jobItem);
    });
    test('3.0.2 Check default values', function(){
      assert.equal(10000, jobItem.processTimeout, 'processTimeout should have the proper default value');
      assert.equal(null, jobItem.delay, 'delay should have the proper default value');
      assert.equal(true, jobItem.ghostRetry, 'ghostRetry should have the proper default value');
      assert.equal(1, jobItem.ghostTimes, 'ghostTimes should have the proper default value');
      assert.equal(1800000, jobItem.ghostInterval, 'ghostInterval should have the proper default value');
      assert.equal(false, jobItem.hotjob, 'hotjob should have the proper default value');
      assert.equal(10000, jobItem.hotjobTimeout, 'hotjobTimeout should have the proper default value');
      assert.equal(false, jobItem.retry, 'retry should have the proper default value');
      assert.equal(3, jobItem.retryTimes, 'retryTimes should have the proper default value');
      assert.equal(1800000, jobItem.retryInterval, 'retryInterval should have the proper default value');
    });

    test('3.0.3 Check .get callback is called when no results', function(done) {
      var spy = sinon.spy();
      kickq.get('does not exist', spy);

      // wait a bit and check if callback called
      setTimeout(function() {
        assert.ok(spy.calledOnce, 'Callback should be called once');
        done();
      }, 100);
    });
    test('3.0.4 Check .get error callback is of right type', function(done) {
      var spy = sinon.spy();
      kickq.get('does not exist', spy);

      // wait a bit and check if callback called
      setTimeout(function() {
        var args = spy.getCall(0).args;
        assert.equal(1, args.length, 'One argument should be present only');
        assert.instanceOf(args[0], kickq.Error.NoRecord, 'should be instance of' +
          'NoRecord Error');
        done();
      }, 100);
    });

  });

  suite('3.1 A new plain job item when processed', function() {
    test('3.1.0 Has the right properties', function(done) {
      kickq.process('jobItem test plain job', function(jobItem){
        try {
          jobItemTest.testJobItemProps(jobItem);
        } catch(ex) {
          done(ex);
          return;
        }
        done();
      });
    });

    test('3.1.1 has state: "processing"', function(done) {
      kickq.process('jobItem test plain job', function(jobItem){
        assert.equal('processing', jobItem.state, 'state should be "processing"');
        done();
      });
    });
    test('3.1.2 passes all jobItem prop tests"', function(done) {
      kickq.process('jobItem test plain job', function(jobItem){
        try {
          jobItemTest.testNewItemPropsType(jobItem);
          done();
        } catch(ex){
          done(ex);
        }
      });
    });
  });

  suite('3.2 A new plain job item after being processed successfully', function() {

    setup(function(done) {
      // create a dummy job and get the id
      kickq.process('jobItem test plain job', function(jobItem, data, cb){
        if (jobId !== jobItem.id) {
          done('Different job id! Created: ' + jobId + ' processed: ' + jobItem.id);
          return;
        }
        setInterval(function() {
          cb(null, done);
        }, 20);
      });
    });

    teardown(function() {});

    test('3.2.0 Check proper props', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          jobItemTest.testJobItemProps(jobItem);
        });
    });
    test('3.2.1 Check proper prop values', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          assert.ok(jobItem.complete, '"complete" prop should be true');
          assert.ok(jobItem.success, '"success" prop should be true');
          assert.isNumber(jobItem.finishTime, '"finishTime" prop should be a number');
          assert.isNumber(jobItem.totalProcessTime, '"totalProcessTime" prop should be a number');
        });
    });

    test('3.2.2 Process Item count', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          assert.lengthOf(jobItem.runs, 1, 'Should have only 1 process item');
        });
    });

    test('3.2.3 Passes all Process Item props tests', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          var processItem = jobItem.runs[0];
          jobItemTest.testProcessItemProps(processItem);
        });
    });

    test('3.2.4 Process Item has proper values', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          var processItem = jobItem.runs[0];

          assert.equal(1, processItem.count, 'Process count No should be 1');
          assert.isNumber(processItem.startTime, 'startTime must be a number');
          assert.isNumber(processItem.processTime, 'processTime should be a number');
          assert.operator(0, '<', processItem.processTime, 'processTime should be' +
            ' larger than 0');
          assert.equal(kickq.states.Job.SUCCESS, processItem.state, 'State should be "success"');
          assert.isNull(processItem.errorMessage, 'errorMessage should be null');
        });
    });
  });
});

suite('3.3 Failure Conditions', function() {
  var jobId;

  setup(tester.reset);
  setup(function(done) {
    kickq.config({
      // loggerConsole: true,
      // loggerLevel: kickq.LogLevel.FINE,
      redisNamespace: tester.NS,
      // rapid polling
      schedulerInterval: 100,
      schedulerFuzz: 50,
      schedulerLookAhead: 150,
      // short timeouts and intervals
      processTimeout: 20,
      ghostInterval: 200
    });
    tester.clear(function(){
      // create a dummy job and get the id
      kickq.create('jobItem test fail job', function(err, job){
        if (err) {
          done(err);
          return;
        }
        jobId = job.id;
        done();
      });
    });
  });

  teardown(function() {
  });

  suite('3.3.1 Ghost Jobs', function() {
    setup(function(done) {
      done();
    });

    test('3.3.1.1 Will ghost and wait to reprocess', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job', function() {
        console.log('PROCESS INVOKE');
        processTimes++;
      });

      setTimeout(function() {
        assert.equal(2, processTimes, 'The job should be processed only two times');
        done();
      }, 1000);
    });

    test('3.3.1.2 Will ghost and wait to reprocess 11 jobs', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job', function() {
        processTimes++;
      });

      // create 10 jobs
      for (var i = 0; i < 10; i++) {
        kickq.create('jobItem test fail job');
      }

      setTimeout(function() {
        assert.equal(22, processTimes, 'The job should be processed only 22 times');
        done();
      }, 1000);
    });

    test('3.3.1.3 Will ghost and add a new worker in the mix', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job', function() {
        processTimes++;
      });

      // create 10 jobs
      for (var i = 0; i < 10; i++) {
        kickq.create('jobItem test fail job');
      }

      var noTimes = 0;
      setTimeout(function() {
        kickq.process('jobItem test fail job', function(){
          noTimes++;
        });

        setTimeout(function() {
          assert.equal(0, noTimes, 'No more job items should be processed');
          done();
        }, 500);
      }, 1000);
    });

    test('3.3.1.4 Will ghost and examine the Job Item properties', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job', function() {
        processTimes++;
      });

      setTimeout(function() {
        kickq.get(jobId)
          .then(function(jobItemFetched) {
            assert.equal(2, jobItemFetched.runs.length, 'There should be two' +
              ' process items');

            assert.equal(kickq.states.Job.FAIL, jobItemFetched.state,
              'Job Item state should be "fail"');

            assert.ok(jobItemFetched.complete, 'Job Item should be complete');
            assert.ok(!jobItemFetched.success, 'Job Item should have success prop false');

            assert.equal(kickq.states.Job.GHOST, jobItemFetched.runs[0].state,
              'First process item state should be "ghost"');
            assert.equal(kickq.states.Job.GHOST, jobItemFetched.runs[1].state,
              'Second process item state should be "ghost"');
            done();
          })
          .catch(done);
      }, 1000);
    });
  });

  suite('3.3.2 Failed Jobs', function() {
    var jobItem;

    // process, reject and fetch the job.
    setup(function(done) {
      kickq.process('jobItem test fail job', function(job, data, cb) {
        cb(false, function() {
          kickq.get(job.id).then(function(fetchedJob) {
            jobItem = fetchedJob;
            done();
          }, done);
        });
      });
    });

    test('3.3.2.1 Job item should have the proper values in its props', function(){
      assert.equal('fail', jobItem.state, 'Job item should have a state of "fail"');
    });
  });
});

suite('3.4 Configuring Job Item', function() {
  var jobId;

  setup(tester.reset);
  setup(function(done) {
    kickq.config({
      redisNamespace: tester.NS,
      // loggerConsole: true,
      // loggerLevel: kickq.LogLevel.FINE,
      delay: 10,
      ghostRetry: false,
      processTimeout: 20,
      ghostInterval: 200,
      retry: true,
      retryTimes: 5,
      retryInterval: 100
    });
    tester.clear(function(){
      // create a dummy job and get the id
      kickq.create('jobItem test fail job', function(err, job){
        if (err) {
          done(err);
          return;
        }
        jobId = job.id;
        done();
      });
    });
  });

  teardown(function() {
  });

  suite('3.4.1 Global Cofiguration Options', function() {
    var jobItem;
    setup(function(done) {
      kickq.get(jobId).then(function(jobItemFetched) {
        jobItem = jobItemFetched;
        done();
      });
    });

    test('3.4.1.1 Proper values on configurable properties', function(done){
      assert.equal(10, jobItem.delay, 'Prop "delay" should have proper value');
      assert.equal(false, jobItem.ghostRetry, 'Prop "ghostRetry" should have proper value');
      assert.equal(20, jobItem.processTimeout, 'Prop "processTimeout" should have proper value');
      assert.equal(200, jobItem.ghostInterval, 'Prop "ghostInterval" should have proper value');
      assert.equal(true, jobItem.retry, 'Prop "retry" should have proper value');
      assert.equal(5, jobItem.retryTimes, 'Prop "retryTimes" should have proper value');
      assert.equal(100, jobItem.retryInterval, 'Prop "retryInterval" should have proper value');
      done();
    });

    test('3.4.1.2 Testing setState method', function(done){
      var jobIt = new kickq.JobItem(jobItem);

      jobIt.setState(kickq.states.Job.QUEUED).then(function() {
        kickq.get(jobId).then(function(jobItemFetched) {
          assert.equal(kickq.states.Job.QUEUED, jobItemFetched.state, 'State of' +
            ' fetched item should be "queued"');
          done();
        }, done).catch(done);

      }, done);
    });

  });

});<commit_msg>refactor jobitem fail tests<commit_after>/**
 * @fileOverview Job item status and props
 */
var Promise = require('bluebird');

var sinon  = require('sinon');
var assert = require('chai').assert;

var kickq  = require('../../');
var tester = require('../lib/tester');
var jobItemTest = require('../asserts/jobitem.assert');

// var noop = function(){};

suite('3. Job Item Status and Props', function() {

  var jobId;

  setup(tester.reset);
  setup(function(done) {
    tester.clear(function(){
      // create a dummy job and get the id
      kickq.create('jobItem test plain job', function(err, job){
        if (err) {
          done(err);
          return;
        }
        jobId = job.id;
        done();
      });
    });
  });

  teardown(function() {
  });

  // The numbering (e.g. 1.1.1) has nothing to do with order
  // The purpose is to provide a unique string so specific tests are
  // run by using the mocha --grep "1.1.1" option.
  suite('3.0 A new job fetched manualy', function() {
    var jobItem;
    setup(function(done) {
      kickq.get(jobId, function(err, job){
        jobItem = job;
        done(err);
      });
    });
    test('3.0.1 Passes the jobItem prop tests', function(){
      jobItemTest.testNewItemPropsType(jobItem);
    });
    test('3.0.2 Check default values', function(){
      assert.equal(10000, jobItem.processTimeout, 'processTimeout should have the proper default value');
      assert.equal(null, jobItem.delay, 'delay should have the proper default value');
      assert.equal(true, jobItem.ghostRetry, 'ghostRetry should have the proper default value');
      assert.equal(1, jobItem.ghostTimes, 'ghostTimes should have the proper default value');
      assert.equal(1800000, jobItem.ghostInterval, 'ghostInterval should have the proper default value');
      assert.equal(false, jobItem.hotjob, 'hotjob should have the proper default value');
      assert.equal(10000, jobItem.hotjobTimeout, 'hotjobTimeout should have the proper default value');
      assert.equal(false, jobItem.retry, 'retry should have the proper default value');
      assert.equal(3, jobItem.retryTimes, 'retryTimes should have the proper default value');
      assert.equal(1800000, jobItem.retryInterval, 'retryInterval should have the proper default value');
    });

    test('3.0.3 Check .get callback is called when no results', function(done) {
      var spy = sinon.spy();
      kickq.get('does not exist', spy);

      // wait a bit and check if callback called
      setTimeout(function() {
        assert.ok(spy.calledOnce, 'Callback should be called once');
        done();
      }, 100);
    });
    test('3.0.4 Check .get error callback is of right type', function(done) {
      var spy = sinon.spy();
      kickq.get('does not exist', spy);

      // wait a bit and check if callback called
      setTimeout(function() {
        var args = spy.getCall(0).args;
        assert.equal(1, args.length, 'One argument should be present only');
        assert.instanceOf(args[0], kickq.Error.NoRecord, 'should be instance of' +
          'NoRecord Error');
        done();
      }, 100);
    });

  });

  suite('3.1 A new plain job item when processed', function() {
    test('3.1.0 Has the right properties', function(done) {
      kickq.process('jobItem test plain job', function(jobItem){
        try {
          jobItemTest.testJobItemProps(jobItem);
        } catch(ex) {
          done(ex);
          return;
        }
        done();
      });
    });

    test('3.1.1 has state: "processing"', function(done) {
      kickq.process('jobItem test plain job', function(jobItem){
        assert.equal('processing', jobItem.state, 'state should be "processing"');
        done();
      });
    });
    test('3.1.2 passes all jobItem prop tests"', function(done) {
      kickq.process('jobItem test plain job', function(jobItem){
        try {
          jobItemTest.testNewItemPropsType(jobItem);
          done();
        } catch(ex){
          done(ex);
        }
      });
    });
  });

  suite('3.2 A new plain job item after being processed successfully', function() {

    setup(function(done) {
      // create a dummy job and get the id
      kickq.process('jobItem test plain job', function(jobItem, data, cb){
        if (jobId !== jobItem.id) {
          done('Different job id! Created: ' + jobId + ' processed: ' + jobItem.id);
          return;
        }
        setInterval(function() {
          cb(null, done);
        }, 20);
      });
    });

    teardown(function() {});

    test('3.2.0 Check proper props', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          jobItemTest.testJobItemProps(jobItem);
        });
    });
    test('3.2.1 Check proper prop values', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          assert.ok(jobItem.complete, '"complete" prop should be true');
          assert.ok(jobItem.success, '"success" prop should be true');
          assert.isNumber(jobItem.finishTime, '"finishTime" prop should be a number');
          assert.isNumber(jobItem.totalProcessTime, '"totalProcessTime" prop should be a number');
        });
    });

    test('3.2.2 Process Item count', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          assert.lengthOf(jobItem.runs, 1, 'Should have only 1 process item');
        });
    });

    test('3.2.3 Passes all Process Item props tests', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          var processItem = jobItem.runs[0];
          jobItemTest.testProcessItemProps(processItem);
        });
    });

    test('3.2.4 Process Item has proper values', function(){
      return kickq.get(jobId)
        .then(function(jobItem) {
          var processItem = jobItem.runs[0];

          assert.equal(1, processItem.count, 'Process count No should be 1');
          assert.isNumber(processItem.startTime, 'startTime must be a number');
          assert.isNumber(processItem.processTime, 'processTime should be a number');
          assert.operator(0, '<', processItem.processTime, 'processTime should be' +
            ' larger than 0');
          assert.equal(kickq.states.Job.SUCCESS, processItem.state, 'State should be "success"');
          assert.isNull(processItem.error, 'errorMessage should be null');
        });
    });
  });
});

suite.only('3.3 Failure Conditions', function() {
  setup(tester.reset);
  setup(function(done) {
    kickq.config({
      // loggerConsole: true,
      // loggerLevel: kickq.LogLevel.FINE,
      redisNamespace: tester.NS,
      // rapid polling
      schedulerInterval: 100,
      schedulerFuzz: 50,
      schedulerLookAhead: 150,
      // short timeouts and intervals
      processTimeout: 20,
      ghostInterval: 200
    });

    var self = this;
    tester.clear(function(){
      // create a dummy jobs and get the id
      Promise.all([
        kickq.create('jobItem test fail job1'),
        kickq.create('jobItem test fail job2'),
        kickq.create('jobItem test fail job3'),
        kickq.create('jobItem test fail job4'),
        kickq.create('jobItem test fail job5'),
      ])
        .bind(self)
        .spread(function(jobItem1, jobItem2, jobItem3, jobItem4, jobItem5) {
          this.jobItem1 = jobItem1;
          this.jobItem2 = jobItem2;
          this.jobItem3 = jobItem3;
          this.jobItem4 = jobItem4;
          this.jobItem5 = jobItem5;
        })
        .then(done)
        .catch(done);
    });
  });

  teardown(function() {
  });

  suite('3.3.1 Ghost Jobs', function() {
    setup(function(done) {
      done();
    });

    test('3.3.1.1 Will ghost and wait to reprocess', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job1', function() {
        console.log('PROCESS INVOKE');
        processTimes++;
      });

      setTimeout(function() {
        assert.equal(2, processTimes, 'The job should be processed only two times');
        done();
      }, 1000);
    });

    test.only('3.3.1.2 Will ghost and wait to reprocess 11 jobs', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job2', function() {
        processTimes++;
      });

      // create 10 jobs
      for (var i = 0; i < 10; i++) {
        kickq.create('jobItem test fail job2');
      }

      setTimeout(function() {
        assert.equal(22, processTimes, 'The job should be processed only 22 times');
        done();
      }, 1000);
    });

    test('3.3.1.3 Will ghost and add a new worker in the mix', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job3', function() {
        processTimes++;
      });

      // create 10 jobs
      for (var i = 0; i < 10; i++) {
        kickq.create('jobItem test fail job3');
      }

      var noTimes = 0;
      setTimeout(function() {
        kickq.process('jobItem test fail job3', function(){
          noTimes++;
        });

        setTimeout(function() {
          assert.equal(0, noTimes, 'No more job items should be processed');
          done();
        }, 500);
      }, 1000);
    });

    test('3.3.1.4 Will ghost and examine the Job Item properties', function(done){
      var processTimes = 0;
      kickq.process('jobItem test fail job4', function() {
        processTimes++;
      });

      var self = this;
      setTimeout(function() {
        kickq.get(self.jobItem4.id)
          .then(function(jobItemFetched) {
            assert.equal(2, jobItemFetched.runs.length, 'There should be two' +
              ' process items');

            assert.equal(kickq.states.Job.FAIL, jobItemFetched.state,
              'Job Item state should be "fail"');

            assert.ok(jobItemFetched.complete, 'Job Item should be complete');
            assert.ok(!jobItemFetched.success, 'Job Item should have success prop false');

            assert.equal(kickq.states.Job.GHOST, jobItemFetched.runs[0].state,
              'First process item state should be "ghost"');
            assert.equal(kickq.states.Job.GHOST, jobItemFetched.runs[1].state,
              'Second process item state should be "ghost"');
            done();
          })
          .catch(done);
      }, 1000);
    });
  });

  suite('3.3.2 Failed Jobs', function() {
    var jobItem;

    // process, reject and fetch the job.
    setup(function(done) {
      kickq.process('jobItem test fail job5', function(job, data, cb) {
        cb(false, function() {
          kickq.get(job.id).then(function(fetchedJob) {
            jobItem = fetchedJob;
            done();
          }, done);
        });
      });
    });

    test('3.3.2.1 Job item should have the proper values in its props', function(){
      assert.equal('fail', jobItem.state, 'Job item should have a state of "fail"');
    });
  });
});

suite('3.4 Configuring Job Item', function() {
  var jobId;

  setup(tester.reset);
  setup(function(done) {
    kickq.config({
      redisNamespace: tester.NS,
      // loggerConsole: true,
      // loggerLevel: kickq.LogLevel.FINE,
      delay: 10,
      ghostRetry: false,
      processTimeout: 20,
      ghostInterval: 200,
      retry: true,
      retryTimes: 5,
      retryInterval: 100
    });
    tester.clear(function(){
      // create a dummy job and get the id
      kickq.create('jobItem test fail job', function(err, job){
        if (err) {
          done(err);
          return;
        }
        jobId = job.id;
        done();
      });
    });
  });

  teardown(function() {
  });

  suite('3.4.1 Global Cofiguration Options', function() {
    var jobItem;
    setup(function(done) {
      kickq.get(jobId).then(function(jobItemFetched) {
        jobItem = jobItemFetched;
        done();
      });
    });

    test('3.4.1.1 Proper values on configurable properties', function(done){
      assert.equal(10, jobItem.delay, 'Prop "delay" should have proper value');
      assert.equal(false, jobItem.ghostRetry, 'Prop "ghostRetry" should have proper value');
      assert.equal(20, jobItem.processTimeout, 'Prop "processTimeout" should have proper value');
      assert.equal(200, jobItem.ghostInterval, 'Prop "ghostInterval" should have proper value');
      assert.equal(true, jobItem.retry, 'Prop "retry" should have proper value');
      assert.equal(5, jobItem.retryTimes, 'Prop "retryTimes" should have proper value');
      assert.equal(100, jobItem.retryInterval, 'Prop "retryInterval" should have proper value');
      done();
    });

    test('3.4.1.2 Testing setState method', function(done){
      var jobIt = new kickq.JobItem(jobItem);

      jobIt.setState(kickq.states.Job.QUEUED).then(function() {
        kickq.get(jobId).then(function(jobItemFetched) {
          assert.equal(kickq.states.Job.QUEUED, jobItemFetched.state, 'State of' +
            ' fetched item should be "queued"');
          done();
        }, done).catch(done);

      }, done);
    });

  });

});<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
'use strict';
/* global enumerateStats */

function Call(config, test) {
  this.test = test;
  this.traceEvent = report.traceEventAsync('call');
  this.traceEvent({config: config});
  this.statsGatheringRunning = false;

  this.pc1 = new RTCPeerConnection(config);
  this.pc2 = new RTCPeerConnection(config);

  this.pc1.addEventListener('icecandidate', this.onIceCandidate_.bind(this,
      this.pc2));
  this.pc2.addEventListener('icecandidate', this.onIceCandidate_.bind(this,
      this.pc1));

  this.iceCandidateFilter_ = Call.noFilter;
}

Call.prototype = {
  establishConnection: function() {
    this.traceEvent({state: 'start'});
    this.pc1.createOffer().then(
        this.gotOffer_.bind(this),
        this.test.reportFatal.bind(this.test)
    );
  },

  close: function() {
    this.traceEvent({state: 'end'});
    this.pc1.close();
    this.pc2.close();
  },

  setIceCandidateFilter: function(filter) {
    this.iceCandidateFilter_ = filter;
  },

  // Constraint max video bitrate by modifying the SDP when creating an answer.
  constrainVideoBitrate: function(maxVideoBitrateKbps) {
    this.constrainVideoBitrateKbps_ = maxVideoBitrateKbps;
  },

  // Remove video FEC if available on the offer.
  disableVideoFec: function() {
    this.constrainOfferToRemoveVideoFec_ = true;
  },

  // When the peerConnection is closed the statsCb is called once with an array
  // of gathered stats.
  gatherStats: function(peerConnection,peerConnection2, localStream, statsCb) {
    var stats = [];
    var stats2 = [];
    var statsCollectTime = [];
    var statsCollectTime2 = [];
    var self = this;
    var statStepMs = 100;
    self.localTrackIds = {
      audio: '',
      video: ''
    };
    self.remoteTrackIds = {
      audio: '',
      video: ''
    };

    peerConnection.getSenders().forEach(function(sender) {
      if (sender.track.kind === 'audio') {
        self.localTrackIds.audio = sender.track.id;
      } else if (sender.track.kind === 'video') {
        self.localTrackIds.video = sender.track.id;
      }
    }.bind(self));

    if (peerConnection2) {
      peerConnection2.getReceivers().forEach(function(receiver) {
        if (receiver.track.kind === 'audio') {
          self.remoteTrackIds.audio = receiver.track.id;
        } else if (receiver.track.kind === 'video') {
          self.remoteTrackIds.video = receiver.track.id;
        }
      }.bind(self));
    }

    var selector = localStream;
    this.statsGatheringRunning = true;
    getStats_();

    function getStats_() {
      if (peerConnection.signalingState === 'closed') {
        self.statsGatheringRunning = false;
        statsCb(stats, statsCollectTime, stats2, statsCollectTime2);
        return;
      }
      peerConnection.getStats(selector)
          .then(gotStats_)
          .catch(function(error) {
            self.test.reportError('Could not gather stats: ' + error);
            self.statsGatheringRunning = false;
            statsCb(stats, statsCollectTime);
          }.bind(self));
      if (peerConnection2) {
        peerConnection2.getStats(selector)
            .then(gotStats2_);
      }
    }
    // Stats for pc2, some stats are only available on the receiving end of a
    // peerconnection.
    function gotStats2_(response) {
      if (adapter.browserDetails.browser === 'chrome') {
        var enumeratedStats = enumerateStats(response, self.localTrackIds,
            self.remoteTrackIds);
        stats2.push(enumeratedStats);
        statsCollectTime2.push(Date.now());
      } else if (adapter.browserDetails.browser === 'firefox') {
        for (var h in response) {
          var stat = response[h];
          stats2.push(stat);
          statsCollectTime2.push(Date.now());
        }
      } else {
        self.test.reportError('Only Firefox and Chrome getStats ' +
            'implementations are supported.');
      }
    }

    function gotStats_(response) {
      // TODO: Remove browser specific stats gathering hack once adapter.js or
      // browsers converge on a standard.
      if (adapter.browserDetails.browser === 'chrome') {
        var enumeratedStats = enumerateStats(response, self.localTrackIds,
            self.remoteTrackIds);
        stats.push(enumeratedStats);
        statsCollectTime.push(Date.now());
      } else if (adapter.browserDetails.browser === 'firefox') {
        for (var j in response) {
          var stat = response[j];
          stats.push(stat);
          statsCollectTime.push(Date.now());
        }
      } else {
        self.test.reportError('Only Firefox and Chrome getStats ' +
            'implementations are supported.');
      }
      setTimeout(getStats_, statStepMs);
    }
  },

  gotOffer_: function(offer) {
    if (this.constrainOfferToRemoveVideoFec_) {
      offer.sdp = offer.sdp.replace(/(m=video 1 [^\r]+)(116 117)(\r\n)/g,
          '$1\r\n');
      offer.sdp = offer.sdp.replace(/a=rtpmap:116 red\/90000\r\n/g, '');
      offer.sdp = offer.sdp.replace(/a=rtpmap:117 ulpfec\/90000\r\n/g, '');
      offer.sdp = offer.sdp.replace(/a=rtpmap:98 rtx\/90000\r\n/g, '');
      offer.sdp = offer.sdp.replace(/a=fmtp:98 apt=116\r\n/g, '');
    }
    this.pc1.setLocalDescription(offer);
    this.pc2.setRemoteDescription(offer);
    this.pc2.createAnswer().then(
        this.gotAnswer_.bind(this),
        this.test.reportFatal.bind(this.test)
    );
  },

  gotAnswer_: function(answer) {
    if (this.constrainVideoBitrateKbps_) {
      answer.sdp = answer.sdp.replace(
          /a=mid:video\r\n/g,
          'a=mid:video\r\nb=AS:' + this.constrainVideoBitrateKbps_ + '\r\n');
    }
    this.pc2.setLocalDescription(answer);
    this.pc1.setRemoteDescription(answer);
  },

  onIceCandidate_: function(otherPeer, event) {
    if (event.candidate) {
      var parsed = Call.parseCandidate(event.candidate.candidate);
      if (this.iceCandidateFilter_(parsed)) {
        otherPeer.addIceCandidate(event.candidate);
      }
    }
  }
};

Call.noFilter = function() {
  return true;
};

Call.isRelay = function(candidate) {
  return candidate.type === 'relay';
};

Call.isNotHostCandidate = function(candidate) {
  return candidate.type !== 'host';
};

Call.isReflexive = function(candidate) {
  return candidate.type === 'srflx';
};

Call.isHost = function(candidate) {
  return candidate.type === 'host';
};

Call.isIpv6 = function(candidate) {
  return candidate.address.indexOf(':') !== -1;
};

// Parse a 'candidate:' line into a JSON object.
Call.parseCandidate = function(text) {
  var candidateStr = 'candidate:';
  var pos = text.indexOf(candidateStr) + candidateStr.length;
  var fields = text.substr(pos).split(' ');
  return {
    'type': fields[7],
    'protocol': fields[2],
    'address': fields[4]
  };
};

// Store the ICE server response from the network traversal server.
Call.cachedIceServers_ = null;
// Keep track of when the request was made.
Call.cachedIceConfigFetchTime_ = null;

// Get a TURN config, either from settings or from network traversal server.
Call.asyncCreateTurnConfig = function(onSuccess, onError) {
  var settings = currentTest.settings;
  if (typeof(settings.turnURI) === 'string' && settings.turnURI !== '') {
    var iceServer = {
      'username': settings.turnUsername || '',
      'credential': settings.turnCredential || '',
      'urls': settings.turnURI.split(',')
    };
    var config = {'iceServers': [iceServer]};
    report.traceEventInstant('turn-config', config);
    setTimeout(onSuccess.bind(null, config), 0);
  } else {
    Call.fetchTurnConfig_(function(response) {
      var config = {'iceServers': response.iceServers};
      report.traceEventInstant('turn-config', config);
      onSuccess(config);
    }, onError);
  }
};

// Get a STUN config, either from settings or from network traversal server.
Call.asyncCreateStunConfig = function(onSuccess, onError) {
  var settings = currentTest.settings;
  if (typeof(settings.stunURI) === 'string' && settings.stunURI !== '') {
    var iceServer = {
      'urls': settings.stunURI.split(',')
    };
    var config = {'iceServers': [iceServer]};
    report.traceEventInstant('stun-config', config);
    setTimeout(onSuccess.bind(null, config), 0);
  } else {
    Call.fetchTurnConfig_(function(response) {
      var config = {'iceServers': response.iceServers.urls};
      report.traceEventInstant('stun-config', config);
      onSuccess(config);
    }, onError);
  }
};

// Ask network traversal API to give us TURN server credentials and URLs.
Call.fetchTurnConfig_ = function(onSuccess, onError) {
  // Check if credentials exist or have expired (and subtract testRuntTIme so
  // that the test can finish if near the end of the lifetime duration).
  // lifetimeDuration is in seconds.
  var testRunTime = 240; // Time in seconds to allow a test run to complete.
  if (Call.cachedIceServers_) {
    var isCachedIceConfigExpired =
      ((Date.now() - Call.cachedIceConfigFetchTime_) / 1000 >
      parseInt(Call.cachedIceServers_.lifetimeDuration) - testRunTime);
    if (!isCachedIceConfigExpired) {
      report.traceEventInstant('fetch-ice-config', 'Using cached credentials.');
      onSuccess(Call.getCachedIceCredentials_());
      return;
    }
  }

  var xhr = new XMLHttpRequest();
  function onResult() {
    if (xhr.readyState !== 4) {
      return;
    }

    if (xhr.status !== 200) {
      onError('TURN request failed');
      return;
    }

    var response = JSON.parse(xhr.responseText);
    Call.cachedIceServers_ = response;
    Call.getCachedIceCredentials_ = function() {
      // Make a new object due to tests modifying the original response object.
      return JSON.parse(JSON.stringify(Call.cachedIceServers_));
    };
    Call.cachedIceConfigFetchTime_ = Date.now();
    report.traceEventInstant('fetch-ice-config', 'Fetching new credentials.');
    onSuccess(Call.getCachedIceCredentials_());
  }

  xhr.onreadystatechange = onResult;
  // API_KEY and TURN_URL is replaced with API_KEY environment variable via
  // Gruntfile.js during build time by uglifyJS.
  xhr.open('POST', TURN_URL + API_KEY, true);
  xhr.send();
};
<commit_msg>remove pc.getStats(mediaStream) (#253)<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
'use strict';
/* global enumerateStats */

function Call(config, test) {
  this.test = test;
  this.traceEvent = report.traceEventAsync('call');
  this.traceEvent({config: config});
  this.statsGatheringRunning = false;

  this.pc1 = new RTCPeerConnection(config);
  this.pc2 = new RTCPeerConnection(config);

  this.pc1.addEventListener('icecandidate', this.onIceCandidate_.bind(this,
      this.pc2));
  this.pc2.addEventListener('icecandidate', this.onIceCandidate_.bind(this,
      this.pc1));

  this.iceCandidateFilter_ = Call.noFilter;
}

Call.prototype = {
  establishConnection: function() {
    this.traceEvent({state: 'start'});
    this.pc1.createOffer().then(
        this.gotOffer_.bind(this),
        this.test.reportFatal.bind(this.test)
    );
  },

  close: function() {
    this.traceEvent({state: 'end'});
    this.pc1.close();
    this.pc2.close();
  },

  setIceCandidateFilter: function(filter) {
    this.iceCandidateFilter_ = filter;
  },

  // Constraint max video bitrate by modifying the SDP when creating an answer.
  constrainVideoBitrate: function(maxVideoBitrateKbps) {
    this.constrainVideoBitrateKbps_ = maxVideoBitrateKbps;
  },

  // Remove video FEC if available on the offer.
  disableVideoFec: function() {
    this.constrainOfferToRemoveVideoFec_ = true;
  },

  // When the peerConnection is closed the statsCb is called once with an array
  // of gathered stats.
  gatherStats: function(peerConnection,peerConnection2, localStream, statsCb) {
    var stats = [];
    var stats2 = [];
    var statsCollectTime = [];
    var statsCollectTime2 = [];
    var self = this;
    var statStepMs = 100;
    self.localTrackIds = {
      audio: '',
      video: ''
    };
    self.remoteTrackIds = {
      audio: '',
      video: ''
    };

    peerConnection.getSenders().forEach(function(sender) {
      if (sender.track.kind === 'audio') {
        self.localTrackIds.audio = sender.track.id;
      } else if (sender.track.kind === 'video') {
        self.localTrackIds.video = sender.track.id;
      }
    }.bind(self));

    if (peerConnection2) {
      peerConnection2.getReceivers().forEach(function(receiver) {
        if (receiver.track.kind === 'audio') {
          self.remoteTrackIds.audio = receiver.track.id;
        } else if (receiver.track.kind === 'video') {
          self.remoteTrackIds.video = receiver.track.id;
        }
      }.bind(self));
    }

    this.statsGatheringRunning = true;
    getStats_();

    function getStats_() {
      if (peerConnection.signalingState === 'closed') {
        self.statsGatheringRunning = false;
        statsCb(stats, statsCollectTime, stats2, statsCollectTime2);
        return;
      }
      peerConnection.getStats()
          .then(gotStats_)
          .catch(function(error) {
            self.test.reportError('Could not gather stats: ' + error);
            self.statsGatheringRunning = false;
            statsCb(stats, statsCollectTime);
          }.bind(self));
      if (peerConnection2) {
        peerConnection2.getStats()
            .then(gotStats2_);
      }
    }
    // Stats for pc2, some stats are only available on the receiving end of a
    // peerconnection.
    function gotStats2_(response) {
      if (adapter.browserDetails.browser === 'chrome') {
        var enumeratedStats = enumerateStats(response, self.localTrackIds,
            self.remoteTrackIds);
        stats2.push(enumeratedStats);
        statsCollectTime2.push(Date.now());
      } else if (adapter.browserDetails.browser === 'firefox') {
        for (var h in response) {
          var stat = response[h];
          stats2.push(stat);
          statsCollectTime2.push(Date.now());
        }
      } else {
        self.test.reportError('Only Firefox and Chrome getStats ' +
            'implementations are supported.');
      }
    }

    function gotStats_(response) {
      // TODO: Remove browser specific stats gathering hack once adapter.js or
      // browsers converge on a standard.
      if (adapter.browserDetails.browser === 'chrome') {
        var enumeratedStats = enumerateStats(response, self.localTrackIds,
            self.remoteTrackIds);
        stats.push(enumeratedStats);
        statsCollectTime.push(Date.now());
      } else if (adapter.browserDetails.browser === 'firefox') {
        for (var j in response) {
          var stat = response[j];
          stats.push(stat);
          statsCollectTime.push(Date.now());
        }
      } else {
        self.test.reportError('Only Firefox and Chrome getStats ' +
            'implementations are supported.');
      }
      setTimeout(getStats_, statStepMs);
    }
  },

  gotOffer_: function(offer) {
    if (this.constrainOfferToRemoveVideoFec_) {
      offer.sdp = offer.sdp.replace(/(m=video 1 [^\r]+)(116 117)(\r\n)/g,
          '$1\r\n');
      offer.sdp = offer.sdp.replace(/a=rtpmap:116 red\/90000\r\n/g, '');
      offer.sdp = offer.sdp.replace(/a=rtpmap:117 ulpfec\/90000\r\n/g, '');
      offer.sdp = offer.sdp.replace(/a=rtpmap:98 rtx\/90000\r\n/g, '');
      offer.sdp = offer.sdp.replace(/a=fmtp:98 apt=116\r\n/g, '');
    }
    this.pc1.setLocalDescription(offer);
    this.pc2.setRemoteDescription(offer);
    this.pc2.createAnswer().then(
        this.gotAnswer_.bind(this),
        this.test.reportFatal.bind(this.test)
    );
  },

  gotAnswer_: function(answer) {
    if (this.constrainVideoBitrateKbps_) {
      answer.sdp = answer.sdp.replace(
          /a=mid:video\r\n/g,
          'a=mid:video\r\nb=AS:' + this.constrainVideoBitrateKbps_ + '\r\n');
    }
    this.pc2.setLocalDescription(answer);
    this.pc1.setRemoteDescription(answer);
  },

  onIceCandidate_: function(otherPeer, event) {
    if (event.candidate) {
      var parsed = Call.parseCandidate(event.candidate.candidate);
      if (this.iceCandidateFilter_(parsed)) {
        otherPeer.addIceCandidate(event.candidate);
      }
    }
  }
};

Call.noFilter = function() {
  return true;
};

Call.isRelay = function(candidate) {
  return candidate.type === 'relay';
};

Call.isNotHostCandidate = function(candidate) {
  return candidate.type !== 'host';
};

Call.isReflexive = function(candidate) {
  return candidate.type === 'srflx';
};

Call.isHost = function(candidate) {
  return candidate.type === 'host';
};

Call.isIpv6 = function(candidate) {
  return candidate.address.indexOf(':') !== -1;
};

// Parse a 'candidate:' line into a JSON object.
Call.parseCandidate = function(text) {
  var candidateStr = 'candidate:';
  var pos = text.indexOf(candidateStr) + candidateStr.length;
  var fields = text.substr(pos).split(' ');
  return {
    'type': fields[7],
    'protocol': fields[2],
    'address': fields[4]
  };
};

// Store the ICE server response from the network traversal server.
Call.cachedIceServers_ = null;
// Keep track of when the request was made.
Call.cachedIceConfigFetchTime_ = null;

// Get a TURN config, either from settings or from network traversal server.
Call.asyncCreateTurnConfig = function(onSuccess, onError) {
  var settings = currentTest.settings;
  if (typeof(settings.turnURI) === 'string' && settings.turnURI !== '') {
    var iceServer = {
      'username': settings.turnUsername || '',
      'credential': settings.turnCredential || '',
      'urls': settings.turnURI.split(',')
    };
    var config = {'iceServers': [iceServer]};
    report.traceEventInstant('turn-config', config);
    setTimeout(onSuccess.bind(null, config), 0);
  } else {
    Call.fetchTurnConfig_(function(response) {
      var config = {'iceServers': response.iceServers};
      report.traceEventInstant('turn-config', config);
      onSuccess(config);
    }, onError);
  }
};

// Get a STUN config, either from settings or from network traversal server.
Call.asyncCreateStunConfig = function(onSuccess, onError) {
  var settings = currentTest.settings;
  if (typeof(settings.stunURI) === 'string' && settings.stunURI !== '') {
    var iceServer = {
      'urls': settings.stunURI.split(',')
    };
    var config = {'iceServers': [iceServer]};
    report.traceEventInstant('stun-config', config);
    setTimeout(onSuccess.bind(null, config), 0);
  } else {
    Call.fetchTurnConfig_(function(response) {
      var config = {'iceServers': response.iceServers.urls};
      report.traceEventInstant('stun-config', config);
      onSuccess(config);
    }, onError);
  }
};

// Ask network traversal API to give us TURN server credentials and URLs.
Call.fetchTurnConfig_ = function(onSuccess, onError) {
  // Check if credentials exist or have expired (and subtract testRuntTIme so
  // that the test can finish if near the end of the lifetime duration).
  // lifetimeDuration is in seconds.
  var testRunTime = 240; // Time in seconds to allow a test run to complete.
  if (Call.cachedIceServers_) {
    var isCachedIceConfigExpired =
      ((Date.now() - Call.cachedIceConfigFetchTime_) / 1000 >
      parseInt(Call.cachedIceServers_.lifetimeDuration) - testRunTime);
    if (!isCachedIceConfigExpired) {
      report.traceEventInstant('fetch-ice-config', 'Using cached credentials.');
      onSuccess(Call.getCachedIceCredentials_());
      return;
    }
  }

  var xhr = new XMLHttpRequest();
  function onResult() {
    if (xhr.readyState !== 4) {
      return;
    }

    if (xhr.status !== 200) {
      onError('TURN request failed');
      return;
    }

    var response = JSON.parse(xhr.responseText);
    Call.cachedIceServers_ = response;
    Call.getCachedIceCredentials_ = function() {
      // Make a new object due to tests modifying the original response object.
      return JSON.parse(JSON.stringify(Call.cachedIceServers_));
    };
    Call.cachedIceConfigFetchTime_ = Date.now();
    report.traceEventInstant('fetch-ice-config', 'Fetching new credentials.');
    onSuccess(Call.getCachedIceCredentials_());
  }

  xhr.onreadystatechange = onResult;
  // API_KEY and TURN_URL is replaced with API_KEY environment variable via
  // Gruntfile.js during build time by uglifyJS.
  xhr.open('POST', TURN_URL + API_KEY, true);
  xhr.send();
};
<|endoftext|>