<commit_before>
Polymer('ulti-viewer', {
  selectedObject : null,
  selectedObjects:null,

  showGrid: true,
  showAxes: true,
  showControls: true,
  showDimensions: true,
  autoRotate: false,

  resources : null,
  meshes    : null,

  dismissalTimeOnError:3000, //how much time do we wait for before removing loading bar in case of an error
  created: function()
  {
    this.resources = [];
    this.meshes    = [];
    this.minObjectSize = 20;//minimum size (in arbitrarty/opengl units) before requiring re-scaling (upwards)
    this.maxObjectSize = 200;//maximum size (in arbitrarty/opengl units) before requiring re-scaling (downwards)
  },
  attached:function()
  {
    this.threeJs      = this.$.threeJs;
    this.assetManager = this.$.assetManager;
    /*//workaround/hack for some css issues:FIXME: is this still necessary??
    try{
    $('<style></style>').appendTo($(document.body)).remove();
    }catch(error){}*/
    //needed since we do not inherit from three-js but do composition
    if (this.requestFullscreen) document.addEventListener("fullscreenchange", this.onFullScreenChange.bind(this), false);
    if (this.mozRequestFullScreen) document.addEventListener("mozfullscreenchange", this.onFullScreenChange.bind(this), false);
    if (this.msRequestFullScreen) document.addEventListener("msfullscreenchange", this.onFullScreenChange.bind(this), false);
    if (this.webkitRequestFullScreen) document.addEventListener("webkitfullscreenchange", this.onFullScreenChange.bind(this), false);
  },
  detached:function()
  {
    this.clearResources();
  },
  //public api
  loadMesh:function( uriOrData, options )
  {
    var options = options || {};
    var display = options.display === undefined ? true: options.display;
    var keepRawData = options.keepRawData === undefined ? true: options.keepRawData;

    var resourcePromise = this._loadResource( uriOrData );

    function loadFailed(res)
    {
      //TODO: do this cleanly via type checking or somethg
      var error = res.error;
      if( error.indexOf("No parser found") != -1)
      {
        error = "sorry, the "+resource.ext+" format is not supported";
      }
    }
    function onDisplayError(error){console.log("FAILED to display",error);};
    if( display ) resourcePromise.then( this.addToScene.bind(this), onDisplayError )

    //      resourcePromise.then(resource.onLoaded.bind(resource), loadFailed, resource.onDownloadProgress.bind(resource) );
    //possible alternative to resizing : zooming in on objects
          /*var viewOffset = this.camera.position.clone().sub( this.camera.target ) ;
          var stuff =viewOffset.clone().normalize();
          stuff.multiplyScalar( geometry.boundingSphere.radius*4 );
          //this.camera.position.copy(stuff);
          var camPos = this.camera.position.clone();
          var tgt = stuff;
          var cam = this.camera;
          var tween = new TWEEN.Tween( camPos )
            .to( tgt , 700 )
            .easing( TWEEN.Easing.Quadratic.In )
            .onUpdate( function () {
              cam.position.copy(camPos);
            } )
            .start();*/
  },
  clearScene:function(sceneName){
    var sceneName = sceneName || "main";
    this.threeJs.clearScene(sceneName);
    this.selectedObjects = [];//TODO: move this to thre-js element ?
  },
  addToScene:function( object, sceneName, options )
  {
    var autoCenter = autoCenter === undefined ? true: autoCenter;
    var autoResize = autoResize === undefined ? true: autoResize;

    var options = {autoCenter:true,autoResize:true,minSize:this.minObjectSize,maxSize:this.maxObjectSize};

    //this.meshes.push( object );
    this.threeJs.addToScene( object, sceneName, options );
  },
  removeFromScene:function( object, sceneName )
  {
    var sceneName = sceneName || "main" ;
    this.threeJs.removeFromScene( object, sceneName );
  },
  _loadResource:function(uriOrData)
  {
    var self = this;
    var resource = this.assetManager.loadResource( uriOrData, {parsing:{useWorker:true,useBuffers:true} } );
    this.resources.push( resource );

    var resourceDeferred = resource.deferred;

    //TODO: UNIFY api for parsers, this is redundant
    function formatResource(resource)
    {
      //geometry
      var shape = resource.data;
      if( !(shape instanceof THREE.Object3D) )
      {
        console.log("formating resource");
        //nice color: 0x00a9ff
        var material = new THREE.MeshPhongMaterial( { color: 0x17a9f5, specular: 0xffffff, shininess: 10, shading: THREE.FlatShading} );
        //new THREE.MeshLambertMaterial( {opacity:1,transparent:false,color: 0x0088ff} );
        var shape = new THREE.Mesh(shape, material);
      }

      /*var geometry = shape.geometry;
      if(geometry)
      {
        geometry.computeBoundingBox();
        geometry.computeBoundingSphere();
        geometry.computeVertexNormals();//needed at least for .ply files
        geometry.computeFaceNormals();
      }*/
      return shape;
    }

    var self = this;
    return resourceDeferred.promise
      .then( formatResource)
      .fail( this.resourceLoadFailed.bind(this) );
  },
  resourceLoadFailed:function(resource)
  {
    console.log("failed to load resource", resource.error);
    //do not keep error message on screen for too long, remove it after a while
    this.async(function() {
      this.dismissResource(resource);
    }, null, this.dismissalTimeOnError);
  },
  clearResources:function()
  {
    this.assetManager.clearResources();
    this.resources = [];
  },
  //remove a resource
  dismissResource:function(resource){
    var index = this.resources.indexOf(resource);
    resource.deferred.reject("cancelling");
    this.assetManager.unloadResource( resource.uri );
    if (index > -1) this.resources.splice(index, 1);
  },
  //event handlers
  //prevents scrolling whole page if using scroll & mouse is within viewer
  onMouseWheel:function (event)
  {
    event.preventDefault();
    return false;
  },
  handleDragOver:function(e) {
    if (e.preventDefault) {
      e.preventDefault();
    }
  },
  handleDrop:function(e)
  {
    if (e.preventDefault) {
      e.preventDefault(); // Necessary. Allows us to drop.
    }
    var data=e.dataTransfer.getData("Text");
    if( data!= "" ){
        this.asyncFire('text-dropped', {data:data} );
        this.loadMesh( e.detail.data );
    }

    var files = e.dataTransfer.files;
    if(files)
    {
      this.asyncFire('files-dropped', {data:files});
      for (var i = 0, f; f = files[i]; i++) {
        this.loadMesh( f );
      }
    }
  },
  onReqDismissResource:function(event, detail, sender) {
    var resource = sender.templateInstance.model.resource;
    console.log("resource",resource);
    this.dismissResource( resource );
  },
  onDownloadTap:function(event)
  {
     var link = document.createElement("a");
     //TODO: rethink this whole aspect: even when keeping the initial "raw" data
     //things are a bit convoluted : perhaps an intermediary step would be for the viewer
     //to be able to act as a "format converter": once we have working "writers/serialisers"
     //the initial data format would not matter and exporting data would be more clean
     var href = null;
     var download = null;

     //walk up the object tree
     function findSelectionsResource(object)
     {
        var currentObject = object;
        while(currentObject)
        {
           if (!(!currentObject.meta))
           {
              return currentObject;
           }
           currentObject= object.parent;
        }
     }
     resourceRoot = findSelectionsResource(this.selectedObject);

     if( resourceRoot ) href = resourceRoot.meta.resource.uri;
     if( ! href) return;

		 link.href = href;
		 link.click();
     event.preventDefault();
     event.stopPropagation();
  },
  //attribute change handlers
  onFullScreenChange:function()
  {
    //workaround to reset this.fullScreen to correct value when pressing exit etc in full screen mode
    this.fullScreen = !(!document.fullscreenElement &&    // alternative standard method
    !document.mozFullScreenElement && !document.webkitFullscreenElement);
  },
  fullScreenChanged:function()
  {
    if(this.fullScreen)
    {
      if(this.requestFullScreen)this.requestFullScreen();
      if(this.webkitRequestFullScreen)this.webkitRequestFullScreen();
      if(this.mozRequestFullScreen)this.mozRequestFullScreen();
      if(this.msRequestFullscreen)this.msRequestFullscreen();
    }
    else
    {
      if(document.cancelFullScreen) document.cancelFullScreen();
      if(document.webkitCancelFullScreen) document.webkitCancelFullScreen();
      if(document.mozCancelFullScreen) document.mozCancelFullScreen();
      if(document.msCancelFullScreen) document.msCancelFullScreen();
    }

  },
  //FIXME
  /*autoRotateChanged:function()
  {
    this.controls.autoRotate = this.autoRotate;

    if(this.autoRotate ==false ) return;

    var rotSpeed = {rotSpeed:0.0};
    var rotSpeedTarget = {rotSpeed:2.0}
    var controls = this.controls;
    var tween = new TWEEN.Tween( rotSpeed )
            .to( rotSpeedTarget , 1000 )
            .easing( TWEEN.Easing.Linear.None )
            .onUpdate( function () {
              controls.autoRotateSpeed = rotSpeed.rotSpeed;
            } )
            .start();
  },*/
  highlightedObjectChanged:function(oldHovered,newHovered)
  {
    return;
    //console.log("highlightedObjectChanged",oldHovered,newHovered);
    this.selectionColor = 0xf7c634;//0xff5400;//0xfffccc;
	  this.outlineColor = 0xffc200;
    function validForOutline(selection)
    {
      return (!(selection.hoverOutline != null) && !(selection.outline != null) && !(selection.name === "hoverOutline") && !(selection.name === "boundingCage") && !(selection.name === "selectOutline"))
    }

    var curHovered = this.highlightedObject;

    if (curHovered != null )
    {
      var hoverEffect = new THREE.Object3D();
      var outline, outlineMaterial;
      curHovered.currentHoverHex = curHovered.material.color.getHex();
      curHovered.material.color.setHex(this.selectionColor);
      //curHovered.material.vertexColors = THREE.FaceColors;

      //curHovered.currentHoverHexSpec = curHovered.material.specular.getHex();
      //curHovered.material.specular.setHex(this.selectionColor);

      //curHovered.material.shininess=8; //.setHex(this.selectionColor);
      outlineMaterial = new THREE.MeshBasicMaterial({
          color: 0xffc200,
          side: THREE.BackSide
        });

      outlineMaterialTest = new THREE.LineBasicMaterial({
          color: 0xffc200,
          linewidth: 10
          //side: THREE.BackSide
        });
      outlineMaterialTest = new THREE.MeshBasicMaterial({
          color: 0xffc200,
          wireframe: true, wireframeLinewidth: 4 ,side: THREE.BackSide} );

      outline = new THREE.Object3D();//new THREE.Mesh(curHovered.geometry, outlineMaterial);
      outline.scale.multiplyScalar(1.03);
      outline.name = "hoverOutline";
      curHovered.hoverOutline = outline;
      curHovered.add(outline);
    }
    if(oldHovered != null)
    {
      if (oldHovered.hoverOutline != null)
      {
        oldHovered.material.color.setHex(oldHovered.currentHoverHex);
        //oldHovered.material.specular.setHex(oldHovered.currentHoverHexSpec);
        //oldHovered.material.shininess = 10;

        oldHovered.remove(oldHovered.hoverOutline);
        oldHovered.hoverOutline = null;
      }
    }
  },
  selectedObjectsChanged:function()
  {
    //f96a5e
    console.log("selectedObjectsChanged", this.selectedObjects);
    if(this.selectedObjects)
    {
      if(this.selectedObjects.length>0)
      {
        this.selectedObject = this.selectedObjects[0];
      }
      else{
        this.selectedObject = null;
      }
    }
  },
  selectedObjectChanged:function(oldSelection)
  {
    console.log("selectedObjectChanged", this.selectedObject);
    var newSelection = this.selectedObject;
    this.selectObject(newSelection, oldSelection);

    //this.clearVisualFocusOnSelection();

    if(oldSelection && oldSelection.helpers)
      {

        if(this.showDimensions)
        {
          var dims = oldSelection.helpers.dims;
          if(dims)
          {
            oldSelection.remove(dims);
            oldSelection.helpers.dims = null;
          }
        }
      }

    if(newSelection)
    {
      //TODO: refactor
      if(this.showDimensions)
      {
        var objDims = new ObjectDimensionsHelper({mesh:newSelection});
        newSelection.add( objDims );
        if(!(newSelection.helpers)) newSelection.helpers = {}
        newSelection.helpers.dims = objDims;
      }
      //this.visualFocusOnSelection(newSelection);
      //if(this.autoRotate) this.autoRotate = false;//TODO: this should be a selection effect aswell
    }
  },
  //TODO: move this somewhere else, preferably a helper
  visualFocusOnSelection : function(selection)
  {
    //visual helper: make object other than main selection slightly transparent
      //console.log("this.rootAssembly.children",this.renderer)
      for(var i = 0; i < this.rootAssembly.children.length;i++)
      {
        var child = this.rootAssembly.children[i];
        if(selection == child)
        {
          child.material.opacity = child.material._oldOpacity;
          child.material.transparent = child.material._oldTransparent;
          continue;
        }
        child.material._oldOpacity = child.material.opacity;
        child.material._oldTransparent = child.material.transparent;
        child.oldRenderDepth = child.renderDepth;

        //child.renderDepth = 0;
        //child.material.renderDepth = 0;
        child.material.opacity = 0.3;
        child.material.transparent = true;
      }
  },
  //TODO: move this somewhere else, preferably a helper
  clearVisualFocusOnSelection : function()
  {
      for(var i = 0; i < this.rootAssembly.children.length;i++)
      {
        var child = this.rootAssembly.children[i];
        child.material.opacity = child.material._oldOpacity;
        child.material.transparent = child.material._oldTransparent;
        //child.renderDepth = child.material._oldRenderDepth;
      }
  },
  selectObject:function(newSelection, oldSelection)
  {
    this.selectionColor = 0xfffccc;
    if(oldSelection != null && newSelection != null)
    {
      //console.log("SELECTED object changed",newSelection.name,"OLD",oldSelection.name);
    }
    //remove from old selection
    if(oldSelection != null)
    {
      oldSelection.remove(oldSelection.outline);
      oldSelection.cage = null;
      oldSelection.outline = null;
      //oldSelection.material.color.setHex( oldSelection.currentSelectHex );
    }
    //add to new selection
    if(newSelection != null)
    {
        var outlineMaterial = new THREE.MeshBasicMaterial({
          color: 0xff0000,//0xffc200,
          side: THREE.BackSide
        });
        outline = new THREE.Mesh(newSelection.geometry, outlineMaterial);
        outline.name = "selectOutline";
        outline.scale.multiplyScalar(1.02);
        newSelection.outline = outline;
        newSelection.add(outline);
    }
  },
});
<commit_msg>- updated internal code for improved dimensions display:  * now using new api : attach/detach so elements are not recreated every time on selection  * modified the creation & insertion of the dimensionsHelper accordingly  * closes #2  * also cleaned up a good chunk of the object selection code<commit_after>
Polymer('ulti-viewer', {
  selectedObject : null,
  selectedObjects:null,

  showGrid: true,
  showAxes: true,
  showControls: true,
  showDimensions: true,
  autoRotate: false,

  resources : null,
  meshes    : null,

  dismissalTimeOnError:3000, //how much time do we wait for before removing loading bar in case of an error
  created: function()
  {
    this.resources = [];
    this.meshes    = [];
    this.minObjectSize = 20;//minimum size (in arbitrarty/opengl units) before requiring re-scaling (upwards)
    this.maxObjectSize = 200;//maximum size (in arbitrarty/opengl units) before requiring re-scaling (downwards)

  },
  attached:function()
  {
    this.threeJs      = this.$.threeJs;
    this.assetManager = this.$.assetManager;

    //add the selection helper
    //dimensions display helper
    this.objDimensionsHelper = new ObjectDimensionsHelper();
    this.addToScene( this.objDimensionsHelper, "helpers", {autoResize:false, autoCenter:false, persistent:true} );

    //needed since we do not inherit from three-js but do composition
    if (this.requestFullscreen) document.addEventListener("fullscreenchange", this.onFullScreenChange.bind(this), false);
    if (this.mozRequestFullScreen) document.addEventListener("mozfullscreenchange", this.onFullScreenChange.bind(this), false);
    if (this.msRequestFullScreen) document.addEventListener("msfullscreenchange", this.onFullScreenChange.bind(this), false);
    if (this.webkitRequestFullScreen) document.addEventListener("webkitfullscreenchange", this.onFullScreenChange.bind(this), false);

    /*//workaround/hack for some css issues:FIXME: is this still necessary??
    try{
    $('<style></style>').appendTo($(document.body)).remove();
    }catch(error){}*/
  },
  detached:function()
  {
    this.clearResources();
  },
  //public api
  loadMesh:function( uriOrData, options )
  {
    var options = options || {};
    var display = options.display === undefined ? true: options.display;
    var keepRawData = options.keepRawData === undefined ? true: options.keepRawData;

    var resourcePromise = this._loadResource( uriOrData );

    function loadFailed(res)
    {
      //TODO: do this cleanly via type checking or somethg
      var error = res.error;
      if( error.indexOf("No parser found") != -1)
      {
        error = "sorry, the "+resource.ext+" format is not supported";
      }
    }
    function onDisplayError(error){console.log("FAILED to display",error);};
    if( display ) resourcePromise.then( this.addToScene.bind(this), onDisplayError )

    //      resourcePromise.then(resource.onLoaded.bind(resource), loadFailed, resource.onDownloadProgress.bind(resource) );
    //possible alternative to resizing : zooming in on objects
          /*var viewOffset = this.camera.position.clone().sub( this.camera.target ) ;
          var stuff =viewOffset.clone().normalize();
          stuff.multiplyScalar( geometry.boundingSphere.radius*4 );
          //this.camera.position.copy(stuff);
          var camPos = this.camera.position.clone();
          var tgt = stuff;
          var cam = this.camera;
          var tween = new TWEEN.Tween( camPos )
            .to( tgt , 700 )
            .easing( TWEEN.Easing.Quadratic.In )
            .onUpdate( function () {
              cam.position.copy(camPos);
            } )
            .start();*/
  },
  clearScene:function(sceneName){
    var sceneName = sceneName || "main";
    this.threeJs.clearScene(sceneName);
    this.selectedObjects = [];//TODO: move this to thre-js element ?
  },
  addToScene:function( object, sceneName, options )
  {
    var options = options || {};
    options.autoCenter = options.autoCenter === undefined ? true: options.autoCenter;
    options.autoResize = options.autoResize === undefined ? true: options.autoResize;
    options.minSize    = options.minSize === undefined ? this.minObjectSize: options.minSize;
    options.maxSize    = options.maxSize === undefined ? this.maxObjectSize: options.maxSize;
    options.persistent = options.persistent === undefined ? false: options.persistent;

    this.threeJs.addToScene( object, sceneName, options );
  },
  removeFromScene:function( object, sceneName )
  {
    var sceneName = sceneName || "main" ;
    this.threeJs.removeFromScene( object, sceneName );
  },
  _loadResource:function(uriOrData)
  {
    var self = this;
    var resource = this.assetManager.loadResource( uriOrData, {parsing:{useWorker:true,useBuffers:true} } );
    this.resources.push( resource );

    var resourceDeferred = resource.deferred;

    //TODO: UNIFY api for parsers, this is redundant
    function formatResource(resource)
    {
      //geometry
      var shape = resource.data;
      if( !(shape instanceof THREE.Object3D) )
      {
        //console.log("formating resource");
        //nice color: 0x00a9ff
        var material = new THREE.MeshPhongMaterial( { color: 0x17a9f5, specular: 0xffffff, shininess: 10, shading: THREE.FlatShading} );
        //new THREE.MeshLambertMaterial( {opacity:1,transparent:false,color: 0x0088ff} );
        var shape = new THREE.Mesh(shape, material);
      }

      /*var geometry = shape.geometry;
      if(geometry)
      {
        geometry.computeBoundingBox();
        geometry.computeBoundingSphere();
        geometry.computeVertexNormals();//needed at least for .ply files
        geometry.computeFaceNormals();
      }*/
      return shape;
    }

    var self = this;
    return resourceDeferred.promise
      .then( formatResource)
      .fail( this.resourceLoadFailed.bind(this) );
  },
  resourceLoadFailed:function(resource)
  {
    console.log("failed to load resource", resource.error);
    //do not keep error message on screen for too long, remove it after a while
    this.async(function() {
      this.dismissResource(resource);
    }, null, this.dismissalTimeOnError);
  },
  clearResources:function()
  {
    this.assetManager.clearResources();
    this.resources = [];
  },
  //remove a resource
  dismissResource:function(resource){
    var index = this.resources.indexOf(resource);
    resource.deferred.reject("cancelling");
    this.assetManager.unloadResource( resource.uri );
    if (index > -1) this.resources.splice(index, 1);
  },
  //event handlers
  //prevents scrolling whole page if using scroll & mouse is within viewer
  onMouseWheel:function (event)
  {
    event.preventDefault();
    return false;
  },
  handleDragOver:function(e) {
    if (e.preventDefault) {
      e.preventDefault();
    }
  },
  handleDrop:function(e)
  {
    if (e.preventDefault) {
      e.preventDefault(); // Necessary. Allows us to drop.
    }
    var data=e.dataTransfer.getData("Text");
    if( data!= "" ){
        this.asyncFire('text-dropped', {data:data} );
        this.loadMesh( e.detail.data );
    }

    var files = e.dataTransfer.files;
    if(files)
    {
      this.asyncFire('files-dropped', {data:files});
      for (var i = 0, f; f = files[i]; i++) {
        this.loadMesh( f );
      }
    }
  },
  onReqDismissResource:function(event, detail, sender) {
    var resource = sender.templateInstance.model.resource;
    //console.log("resource",resource);
    this.dismissResource( resource );
  },
  onDownloadTap:function(event)
  {
     var link = document.createElement("a");
     //TODO: rethink this whole aspect: even when keeping the initial "raw" data
     //things are a bit convoluted : perhaps an intermediary step would be for the viewer
     //to be able to act as a "format converter": once we have working "writers/serialisers"
     //the initial data format would not matter and exporting data would be more clean
     var href = null;
     var download = null;

     //walk up the object tree
     function findSelectionsResource(object)
     {
        var currentObject = object;
        while(currentObject)
        {
           if (!(!currentObject.meta))
           {
              return currentObject;
           }
           currentObject= object.parent;
        }
     }
     resourceRoot = findSelectionsResource(this.selectedObject);

     if( resourceRoot ) href = resourceRoot.meta.resource.uri;
     if( ! href) return;

		 link.href = href;
		 link.click();
     event.preventDefault();
     event.stopPropagation();
  },
  //attribute change handlers
  onFullScreenChange:function()
  {
    //workaround to reset this.fullScreen to correct value when pressing exit etc in full screen mode
    this.fullScreen = !(!document.fullscreenElement &&    // alternative standard method
    !document.mozFullScreenElement && !document.webkitFullscreenElement);
  },
  fullScreenChanged:function()
  {
    if(this.fullScreen)
    {
      if(this.requestFullScreen)this.requestFullScreen();
      if(this.webkitRequestFullScreen)this.webkitRequestFullScreen();
      if(this.mozRequestFullScreen)this.mozRequestFullScreen();
      if(this.msRequestFullscreen)this.msRequestFullscreen();
    }
    else
    {
      if(document.cancelFullScreen) document.cancelFullScreen();
      if(document.webkitCancelFullScreen) document.webkitCancelFullScreen();
      if(document.mozCancelFullScreen) document.mozCancelFullScreen();
      if(document.msCancelFullScreen) document.msCancelFullScreen();
    }

  },
  //FIXME
  /*autoRotateChanged:function()
  {
    this.controls.autoRotate = this.autoRotate;

    if(this.autoRotate ==false ) return;

    var rotSpeed = {rotSpeed:0.0};
    var rotSpeedTarget = {rotSpeed:2.0}
    var controls = this.controls;
    var tween = new TWEEN.Tween( rotSpeed )
            .to( rotSpeedTarget , 1000 )
            .easing( TWEEN.Easing.Linear.None )
            .onUpdate( function () {
              controls.autoRotateSpeed = rotSpeed.rotSpeed;
            } )
            .start();
  },*/
  highlightedObjectChanged:function(oldHovered,newHovered)
  {
    return;
    //console.log("highlightedObjectChanged",oldHovered,newHovered);
    this.selectionColor = 0xf7c634;//0xff5400;//0xfffccc;
	  this.outlineColor = 0xffc200;
    function validForOutline(selection)
    {
      return (!(selection.hoverOutline != null) && !(selection.outline != null) && !(selection.name === "hoverOutline") && !(selection.name === "boundingCage") && !(selection.name === "selectOutline"))
    }

    var curHovered = this.highlightedObject;

    if (curHovered != null )
    {
      var hoverEffect = new THREE.Object3D();
      var outline, outlineMaterial;
      curHovered.currentHoverHex = curHovered.material.color.getHex();
      curHovered.material.color.setHex(this.selectionColor);
      //curHovered.material.vertexColors = THREE.FaceColors;

      //curHovered.currentHoverHexSpec = curHovered.material.specular.getHex();
      //curHovered.material.specular.setHex(this.selectionColor);

      //curHovered.material.shininess=8; //.setHex(this.selectionColor);
      outlineMaterial = new THREE.MeshBasicMaterial({
          color: 0xffc200,
          side: THREE.BackSide
        });

      outlineMaterialTest = new THREE.LineBasicMaterial({
          color: 0xffc200,
          linewidth: 10
          //side: THREE.BackSide
        });
      outlineMaterialTest = new THREE.MeshBasicMaterial({
          color: 0xffc200,
          wireframe: true, wireframeLinewidth: 4 ,side: THREE.BackSide} );

      outline = new THREE.Object3D();//new THREE.Mesh(curHovered.geometry, outlineMaterial);
      outline.scale.multiplyScalar(1.03);
      outline.name = "hoverOutline";
      curHovered.hoverOutline = outline;
      curHovered.add(outline);
    }
    if(oldHovered != null)
    {
      if (oldHovered.hoverOutline != null)
      {
        oldHovered.material.color.setHex(oldHovered.currentHoverHex);
        //oldHovered.material.specular.setHex(oldHovered.currentHoverHexSpec);
        //oldHovered.material.shininess = 10;

        oldHovered.remove(oldHovered.hoverOutline);
        oldHovered.hoverOutline = null;
      }
    }
  },
  selectedObjectsChanged:function()
  {
    //f96a5e
    //console.log("selectedObjectsChanged", this.selectedObjects);
    if(this.selectedObjects)
    {
      if(this.selectedObjects.length>0)
      {
        this.selectedObject = this.selectedObjects[0];
      }
      else{
        this.selectedObject = null;
      }
    }
  },
  selectedObjectChanged:function(oldSelection)
  {
    //console.log("selectedObjectChanged", this.selectedObject);
    var newSelection = this.selectedObject;
    //FIXME: keep the red outline ?
    this.selectObject(newSelection, oldSelection);

    //this.clearVisualFocusOnSelection();
    if(oldSelection && oldSelection.helpers)
    {
      this.objDimensionsHelper.detach( oldSelection );
    }
    if(newSelection)
    {
      if(this.showDimensions)
      {
        this.objDimensionsHelper.attach( newSelection );
        if(!(newSelection.helpers)) newSelection.helpers = {}
      }
      //this.visualFocusOnSelection(newSelection);
    }
  },
  //TODO: move this somewhere else, preferably a helper
  visualFocusOnSelection : function(selection)
  {
    //visual helper: make object other than main selection slightly transparent
      //console.log("this.rootAssembly.children",this.renderer)
      for(var i = 0; i < this.rootAssembly.children.length;i++)
      {
        var child = this.rootAssembly.children[i];
        if(selection == child)
        {
          child.material.opacity = child.material._oldOpacity;
          child.material.transparent = child.material._oldTransparent;
          continue;
        }
        child.material._oldOpacity = child.material.opacity;
        child.material._oldTransparent = child.material.transparent;
        child.oldRenderDepth = child.renderDepth;

        //child.renderDepth = 0;
        //child.material.renderDepth = 0;
        child.material.opacity = 0.3;
        child.material.transparent = true;
      }
  },
  //TODO: move this somewhere else, preferably a helper
  clearVisualFocusOnSelection : function()
  {
      for(var i = 0; i < this.rootAssembly.children.length;i++)
      {
        var child = this.rootAssembly.children[i];
        child.material.opacity = child.material._oldOpacity;
        child.material.transparent = child.material._oldTransparent;
        //child.renderDepth = child.material._oldRenderDepth;
      }
  },
  selectObject:function(newSelection, oldSelection)
  {
    this.selectionColor = 0xfffccc;
    if(oldSelection != null && newSelection != null)
    {
      //console.log("SELECTED object changed",newSelection.name,"OLD",oldSelection.name);
    }
    //remove from old selection
    if(oldSelection != null)
    {
      oldSelection.remove(oldSelection.outline);
      oldSelection.cage = null;
      oldSelection.outline = null;
      //oldSelection.material.color.setHex( oldSelection.currentSelectHex );
    }
    //add to new selection
    if(newSelection != null)
    {
        var outlineMaterial = new THREE.MeshBasicMaterial({
          color: 0xff0000,//0xffc200,
          side: THREE.BackSide
        });
        outline = new THREE.Mesh(newSelection.geometry, outlineMaterial);
        outline.name = "selectOutline";
        outline.scale.multiplyScalar(1.02);
        newSelection.outline = outline;
        newSelection.add(outline);
    }
  },
});
<|endoftext|>


<commit_before>/**
 * Easily reference fixtures provided to the UAT tests
 * @type {}
 */
module.exports = {
  company: {
    companiesHouse: {
      name: 'Exobite Skeletons Ltd',
      address1: '999 Juliet Street',
      town: 'Llangefni',
      postcode: 'LL77 5RN',
      country: 'United Kingdom',
    },
    foreign: {
      pk: '0fb3379c-341c-4da4-b825-bf8d47b26baa',
      name: 'Lambda plc',
      address1: '12 St George\'s Road',
      town: 'Paris',
      postcode: '75001',
      country: 'France',
      primaryAddress: '12 St George\'s Road, Paris, 75001, France',
      businessType: 'Company',
      headquarterType: 'Not a headquarters',
      sector: 'Retail',
      description: 'This is a dummy company for testing',
      employeeRange: '500+',
      turnoverRange: '£33.5M+',
      currentlyExportingTo: 'France, Germany',
      futureCountriesOfInterest: 'Yemen',
    },
    foreignOther: {
      pk: 'b2c34b41-1d5a-4b4b-9249-7c53ff2868dd',
      name: 'Mars Exports Ltd',
      address1: '12 First Street',
      town: 'New York',
      postcode: '765413',
      country: 'United States',
    },
    ukLtd: {
      pk: '0f5216e0-849f-11e6-ae22-56b6b6499611',
      name: 'Venus Ltd',
      address1: '66 Marcham Road',
      town: 'Bordley',
      postcode: 'BD23 8RZ',
      country: 'United Kingdom',
      primaryAddress: '66 Marcham Road, Bordley, BD23 8RZ, United Kingdom',
      ukRegion: 'North West',
      headquarterType: 'Not a headquarters',
      sector: 'Retail',
      description: 'This is a dummy company for testing',
      referenceCode: 'ORG-10096257',
    },
  },
  contact: {
    georginaClark: {
      pk: '048f2edc-e7ed-4881-b1cc-29142a80238a',
      name: 'Georgina Clark',
    },
    johnnyCakeman: {
      pk: '9b1138ab-ec7b-497f-b8c3-27fed21694ef',
      name: 'Johnny Cakeman',
    },
  },
  event: {
    oneDayExhibition: {
      pk: 'b93d4273-36fe-4008-ac40-fbc197910791',
      name: 'One-day exhibition',
    },
    grandExhibition: {
      pk: 'bda12a57-433c-4a0c-a7ce-5ebd080e09e8',
      name: 'Grand exhibition',
    },
  },
  interaction: {
    attendedGammaEvent: {
      pk: 'ec4a46ef-6e50-4a5c-bba0-e311f0471312',
      company: 'Venus Ltd',
      contact: 'Johnny Cakeman',
      serviceProvider: 'CBBC North EAST',
      service: 'Events - UK Based',
      subject: 'Attended gamma event',
      name: 'Attended gamma event',
      notes: 'This is a dummy service delivery for testing',
      date: '5 September 2017',
      ditAdviser: 'Puck Head',
      event: 'Grand exhibition',
      documents: 'View files and documents (will open another website)',
    },
    grandExhibition: {
      pk: '0dcb3748-c097-4f20-b84f-0114bbb1a8e0',
      subject: 'Provided funding information',
    },
    tapStatus: {
      pk: 'aa350238-5d84-4bed-be68-b08dea7ea6d5',
      company: 'Venus Ltd',
      contact: 'Dean Cox',
      serviceProvider: 'Marketing - Marketing Team',
      service: 'Tradeshow Access Programme (TAP)',
      tapStatus: 'Offered',
      grantOffered: '£2,500.00',
      subject: 'TAP grant',
      name: 'TAP grant',
      notes: 'This is a dummy service delivery for testing',
      date: '15 September 2017',
      ditAdviser: 'John Rogers',
      event: 'No',
      documents: 'There are no files or documents',
    },
  },
  investmentProject: {
    newHotelCommitmentToInvest: {
      pk: 'fb5b5006-56af-40e0-8615-7aba53e0e4bf',
      name: 'New hotel (commitment to invest)',
    },
    newRollercoaster: {
      pk: '0e686ea4-b8a2-4337-aec4-114d92ad4588',
      name: 'New rollercoaster',
    },
    newHotelFdi: {
      pk: '721e2a04-21c3-4172-a321-4368463a4b2d',
      name: 'New hotel (FDI)',
    },
    newGolfCourse: {
      pk: 'e32b3c33-80ac-4589-a8c4-dda305d726ba',
      name: 'New golf course (DA)',
    },
    newZoo: {
      pk: 'ba1f0b14-5fe4-4c36-bf6a-ddf115272977',
      name: 'New zoo (LEP)',
    },
  },
}
<commit_msg>Fix type in fixture object name<commit_after>/**
 * Easily reference fixtures provided to the UAT tests
 * @type {}
 */
module.exports = {
  company: {
    companiesHouse: {
      name: 'Exobite Skeletons Ltd',
      address1: '999 Juliet Street',
      town: 'Llangefni',
      postcode: 'LL77 5RN',
      country: 'United Kingdom',
    },
    foreign: {
      pk: '0fb3379c-341c-4da4-b825-bf8d47b26baa',
      name: 'Lambda plc',
      address1: '12 St George\'s Road',
      town: 'Paris',
      postcode: '75001',
      country: 'France',
      primaryAddress: '12 St George\'s Road, Paris, 75001, France',
      businessType: 'Company',
      headquarterType: 'Not a headquarters',
      sector: 'Retail',
      description: 'This is a dummy company for testing',
      employeeRange: '500+',
      turnoverRange: '£33.5M+',
      currentlyExportingTo: 'France, Germany',
      futureCountriesOfInterest: 'Yemen',
    },
    foreignOther: {
      pk: 'b2c34b41-1d5a-4b4b-9249-7c53ff2868dd',
      name: 'Mars Exports Ltd',
      address1: '12 First Street',
      town: 'New York',
      postcode: '765413',
      country: 'United States',
    },
    ukLtd: {
      pk: '0f5216e0-849f-11e6-ae22-56b6b6499611',
      name: 'Venus Ltd',
      address1: '66 Marcham Road',
      town: 'Bordley',
      postcode: 'BD23 8RZ',
      country: 'United Kingdom',
      primaryAddress: '66 Marcham Road, Bordley, BD23 8RZ, United Kingdom',
      ukRegion: 'North West',
      headquarterType: 'Not a headquarters',
      sector: 'Retail',
      description: 'This is a dummy company for testing',
      referenceCode: 'ORG-10096257',
    },
  },
  contact: {
    georginaClark: {
      pk: '048f2edc-e7ed-4881-b1cc-29142a80238a',
      name: 'Georgina Clark',
    },
    johnnyCakeman: {
      pk: '9b1138ab-ec7b-497f-b8c3-27fed21694ef',
      name: 'Johnny Cakeman',
    },
  },
  event: {
    oneDayExhibition: {
      pk: 'b93d4273-36fe-4008-ac40-fbc197910791',
      name: 'One-day exhibition',
    },
    grandExhibition: {
      pk: 'bda12a57-433c-4a0c-a7ce-5ebd080e09e8',
      name: 'Grand exhibition',
    },
  },
  interaction: {
    attendedGammaEvent: {
      pk: 'ec4a46ef-6e50-4a5c-bba0-e311f0471312',
      company: 'Venus Ltd',
      contact: 'Johnny Cakeman',
      serviceProvider: 'CBBC North EAST',
      service: 'Events - UK Based',
      subject: 'Attended gamma event',
      name: 'Attended gamma event',
      notes: 'This is a dummy service delivery for testing',
      date: '5 September 2017',
      ditAdviser: 'Puck Head',
      event: 'Grand exhibition',
      documents: 'View files and documents (will open another website)',
    },
    grandExhibition: {
      pk: '0dcb3748-c097-4f20-b84f-0114bbb1a8e0',
      subject: 'Provided funding information',
    },
    tapGrant: {
      pk: 'aa350238-5d84-4bed-be68-b08dea7ea6d5',
      company: 'Venus Ltd',
      contact: 'Dean Cox',
      serviceProvider: 'Marketing - Marketing Team',
      service: 'Tradeshow Access Programme (TAP)',
      tapStatus: 'Offered',
      grantOffered: '£2,500.00',
      subject: 'TAP grant',
      name: 'TAP grant',
      notes: 'This is a dummy service delivery for testing',
      date: '15 September 2017',
      ditAdviser: 'John Rogers',
      event: 'No',
      documents: 'There are no files or documents',
    },
  },
  investmentProject: {
    newHotelCommitmentToInvest: {
      pk: 'fb5b5006-56af-40e0-8615-7aba53e0e4bf',
      name: 'New hotel (commitment to invest)',
    },
    newRollercoaster: {
      pk: '0e686ea4-b8a2-4337-aec4-114d92ad4588',
      name: 'New rollercoaster',
    },
    newHotelFdi: {
      pk: '721e2a04-21c3-4172-a321-4368463a4b2d',
      name: 'New hotel (FDI)',
    },
    newGolfCourse: {
      pk: 'e32b3c33-80ac-4589-a8c4-dda305d726ba',
      name: 'New golf course (DA)',
    },
    newZoo: {
      pk: 'ba1f0b14-5fe4-4c36-bf6a-ddf115272977',
      name: 'New zoo (LEP)',
    },
  },
}
<|endoftext|>


<commit_before>'use strict';

// import * as _ from 'underscore';
import clone from 'lodash/clone';
import extend from 'lodash/extend';
import domready from "domready";
import * as setup from './setup.js';
import * as openClose from './open-close.js';
import * as matrix from './matrix.js';
import * as animations from './animations.js';
import * as caption from './caption.js';
import * as utils from './utils.js';

// TODO rebuild animation speed options
// TODO fix weird momentum scrolling
// TODO fix resizing when scrolling
// TODO customizable speeds, colors, cursor,..
// TODO slideshow

class UnInVisible extends EventEmitter {
	constructor(options) {
		this.options = extend(clone(UnInVisible.defaults), (options || {}));
		domready(this._init.bind(this));
	}
}

UnInVisible.defaults = {
	document: document,
	zoom: 'default',
	animationSpeed: 400,
	trackSpeed: 0.085
};

_.extend(UnInVisible.prototype, setup, openClose, matrix, animations, caption, utils);

module.exports = new UnInVisible();
// export default new UnInVisible(); // exports es6 module, but then in the browser you get UnInVisible = Object {default: UnInVisible, __esModule: true}

// var _ = require('underscore');
// var EventEmitter = require('events');
// var util = require('util');
// var domready = require("domready");

// function UnInVisible(options) {
// 	this.options = _.extend(_.clone(UnInVisible.defaults), (options || {}));
// 	domready(this._init.bind(this));
// }
//
// util.inherits(UnInVisible, EventEmitter);
//
// UnInVisible.defaults = {
// 	document: document,
// 	zoom: 'default',
// 	animationSpeed: 400,
// 	trackSpeed: 0.085
// };
//
// _.extend(UnInVisible.prototype,
// 	require('./src/setup.js'),
// 	require('./src/open-close.js'),
// 	require('./src/matrix.js'),
// 	require('./src/animations.js'),
// 	require('./src/caption.js'),
// 	require('./src/utils.js')
// );
//
// module.exports = new UnInVisible();
<commit_msg>backbone events<commit_after>'use strict';

// import * as _ from 'underscore';
import clone from 'lodash/clone';
import extend from 'lodash/extend';
import EventEmitter from 'backbone-events-standalone';
import domready from "domready";
import * as setup from './setup.js';
import * as openClose from './open-close.js';
import * as matrix from './matrix.js';
import * as animations from './animations.js';
import * as caption from './caption.js';
import * as utils from './utils.js';

// TODO rebuild animation speed options
// TODO fix weird momentum scrolling
// TODO fix resizing when scrolling
// TODO customizable speeds, colors, cursor,..
// TODO slideshow

class UnInVisible{
	constructor(options) {
		this.options = extend(clone(UnInVisible.defaults), (options || {}));
		domready(this._init.bind(this));
	}
}

UnInVisible.defaults = {
	document: document,
	zoom: 'default',
	animationSpeed: 400,
	trackSpeed: 0.085
};

extend(UnInVisible.prototype, setup, openClose, matrix, animations, caption, utils, EventEmitter);

module.exports = new UnInVisible();
// export default new UnInVisible(); // exports es6 module, but then in the browser you get UnInVisible = Object {default: UnInVisible, __esModule: true}
<|endoftext|>


<commit_before>$(function(){

	Object.prototype.findloop = function (a){
		var arr = a || [this];
		if(this.chainage){
			for(var i = 0; i < this.chainage.length; i++){
				if(arr.indexOf(this.chainage[i])>-1){
					console.log(this.chainage[i]);
					throw new Error('loop in chain tree');
				}else{
					arr.push(this.chainage[i]);
					this.chainage[i].findloop(arr);
				}
			}
		}else{
			return false;
		}
	}

	Object.prototype.chain = function (){
		if(!this.chainage){
			this.chainage = [];
		}
		for(var i = 0; i < arguments.length; i++){
			this.chainage.push(arguments[i]);
			if(Object.prototype.chain.allowloops == false){
				this.findloop();
			}
		}
		return this;
	}
	Object.prototype.chain.allowloops = false;

	Object.prototype.get = function (p,own){
		var prop = this[p];
		var that = this;
		if(this.chainage){
			var i = 0;
			while(typeof prop === 'undefined' && i < this.chainage.length){
				if(!own){that = this.chainage[i]}
				prop = this.chainage[i][p];
				i ++;
			}
		}
		if(prop instanceof Function){
			return function (){
				var args = [];
				for(var i = 0; i < arguments.length; i ++){
					args.push(arguments[i]);
				}
				return prop.apply(that,args);
			}
		}
		return prop;
	}

	Object.prototype.getget = function (p,own,recursion){
		var prop = this[p];
		var that = (recursion && own)?own:this;
		if(this.chainage){
			var i = 0;
			while(typeof prop === 'undefined' && i < this.chainage.length){
				if(!own){that = this.chainage[i]}
				prop = this.chainage[i].getget(p,(own?that:false),true);
				i ++;
			}
		}
		if(prop instanceof Function){
			return function (){
				var args = [];
				for(var i = 0; i < arguments.length; i ++){
					args.push(arguments[i]);
				}
				return prop.apply(that,args);
			}
		}
		return prop;
	}

	Object.prototype.raw = function (p,own){
		var raw = [this[p]];
		var that = [this];
		if(this.chainage){
			for(var i = 0; i < this.chainage.length; i ++){
				own ? that.push(this) : that.push(this.chainage[i]);
				raw.push(this.chainage[i][p]);
			}
		}
		var raw2 = [];
		for(var ii = 0; ii < raw.length; ii ++){
			if(raw[ii] instanceof Function){
				var meth = raw[ii];
				var vic = that[ii];
				raw2.push(function (){
					var args = [];
					for(var i = 0; i < arguments.length; i ++){
						args.push(arguments[i]);
					}
					return meth.apply(vic,args);
				});
			}else{
				raw2.push(raw[ii]);
			}
		}
		return raw2;
	}

	Object.prototype.rawraw = function (p,own){
		var rawraw = [];
		rawraw.push(this.getget(p,own?this:false,true));
		if(this.chainage){
			for(var i = 0; i < this.chainage.length; i ++){
				rawraw.push(this.chainage[i].getget(p,own?this:false,true));
			}
		}
		return rawraw;
	}

	Object.prototype.dechain = function (){
		if(this.chainage){
			if(!arguments.length){
				this.chainage = [];
			}else{
				for(var i = 0; i < arguments.length; i ++){
					while(this.chainage.indexOf(arguments[i]) > -1){
						this.chainage.splice(this.chainage.indexOf(arguments[i]),1);
					}
				}
			}
		}
		return this;
	}

	Object.prototype.rechain = function (x,o){
		if(!this.chainage){
			this.chainage = [];
		}
		if(!(this.chainage.indexOf(x) < 0)){
			this.chainage.splice(this.chainage.indexOf(x),1,o);
		}
		return this;
	}

	Object.prototype.inchain = function (){
		if(!this.chainage){
			this.chainage = [];
		}
		for(var i = arguments.length; i > 0; i --){
			this.chainage.unshift(arguments[i-1]);
		}
	}

});<commit_msg>warn<commit_after>$(function(){

	var dummyconsole = {
		log: function(){},
		warn: function(){}
	}
	if(!console){
		console = dummyconsole;
	}

	Object.prototype.findloop = function (a){
		var arr = a || [this];
		if(this.chainage){
			for(var i = 0; i < this.chainage.length; i++){
				if(arr.indexOf(this.chainage[i])>-1){
					console.warn('Looping',this.chainage[i]);	throw new Error('loop in chain tree');
				}else{
					arr.push(this.chainage[i]);
					this.chainage[i].findloop(arr);
				}
			}
		}else{
			return false;
		}
	}

	Object.prototype.chain = function (){
		if(!this.chainage){
			this.chainage = [];
		}
		for(var i = 0; i < arguments.length; i++){
			this.chainage.push(arguments[i]);
			if(Object.prototype.chain.allowloops == false){
				this.findloop();
			}
		}
		return this;
	}
	Object.prototype.chain.allowloops = false;

	Object.prototype.get = function (p,own){
		var prop = this[p];
		var that = this;
		if(this.chainage){
			var i = 0;
			while(typeof prop === 'undefined' && i < this.chainage.length){
				if(!own){that = this.chainage[i]}
				prop = this.chainage[i][p];
				i ++;
			}
		}
		if(prop instanceof Function){
			return function (){
				var args = [];
				for(var i = 0; i < arguments.length; i ++){
					args.push(arguments[i]);
				}
				return prop.apply(that,args);
			}
		}
		return prop;
	}

	Object.prototype.getget = function (p,own,recursion){
		if(typeof p != 'string'){throw new Error('Getget: property name cannot be '+p+'.');}
		if(Object.prototype.chain.allowloops == true){console.warn('Getget called while chain.allowloops is set to '+chain.allowloops+'.')}
		var prop = this[p];
		var that = (recursion && own)?own:this;
		if(this.chainage){
			var i = 0;
			while(typeof prop === 'undefined' && i < this.chainage.length){
				if(!own){that = this.chainage[i]}
				prop = this.chainage[i].getget(p,(own?that:false),true);
				i ++;
			}
		}
		if(prop instanceof Function){
			return function (){
				var args = [];
				for(var i = 0; i < arguments.length; i ++){
					args.push(arguments[i]);
				}
				return prop.apply(that,args);
			}
		}
		return prop;
	}

	Object.prototype.raw = function (p,own){
		var raw = [this[p]];
		var that = [this];
		if(this.chainage){
			for(var i = 0; i < this.chainage.length; i ++){
				own ? that.push(this) : that.push(this.chainage[i]);
				raw.push(this.chainage[i][p]);
			}
		}
		var raw2 = [];
		for(var ii = 0; ii < raw.length; ii ++){
			if(raw[ii] instanceof Function){
				var meth = raw[ii];
				var vic = that[ii];
				raw2.push(function (){
					var args = [];
					for(var i = 0; i < arguments.length; i ++){
						args.push(arguments[i]);
					}
					return meth.apply(vic,args);
				});
			}else{
				raw2.push(raw[ii]);
			}
		}
		return raw2;
	}

	Object.prototype.rawraw = function (p,own){
		if(typeof p != 'string'){throw new Error('Rawraw: property name cannot be '+p+'.');}
		var rawraw = [];
		rawraw.push(this.getget(p,own?this:false,true));
		if(this.chainage){
			for(var i = 0; i < this.chainage.length; i ++){
				rawraw.push(this.chainage[i].getget(p,own?this:false,true));
			}
		}
		return rawraw;
	}

	Object.prototype.dechain = function (){
		if(this.chainage){
			if(!arguments.length){
				this.chainage = [];
			}else{
				for(var i = 0; i < arguments.length; i ++){
					while(this.chainage.indexOf(arguments[i]) > -1){
						this.chainage.splice(this.chainage.indexOf(arguments[i]),1);
					}
				}
			}
		}
		return this;
	}

	Object.prototype.rechain = function (x,o){
		if(!this.chainage){
			this.chainage = [];
		}
		if(!(this.chainage.indexOf(x) < 0)){
			this.chainage.splice(this.chainage.indexOf(x),1,o);
		}
		return this;
	}

	Object.prototype.inchain = function (){
		if(!this.chainage){
			this.chainage = [];
		}
		for(var i = arguments.length; i > 0; i --){
			this.chainage.unshift(arguments[i-1]);
		}
	}

});<|endoftext|>


<commit_before>import path from 'path';
import fs from 'fs-extra';
import React from 'react';
import {shallow, mount} from 'enzyme';

import Commit from '../../lib/models/commit';
import {nullBranch} from '../../lib/models/branch';
import UserStore from '../../lib/models/user-store';

import CommitController, {COMMIT_GRAMMAR_SCOPE} from '../../lib/controllers/commit-controller';
import {cloneRepository, buildRepository, buildRepositoryWithPipeline} from '../helpers';

describe('CommitController', function() {
  let atomEnvironment, workspace, commandRegistry, notificationManager, lastCommit, config, confirm, tooltips;
  let app;

  beforeEach(function() {
    atomEnvironment = global.buildAtomEnvironment();
    workspace = atomEnvironment.workspace;
    commandRegistry = atomEnvironment.commands;
    notificationManager = atomEnvironment.notifications;
    config = atomEnvironment.config;
    tooltips = atomEnvironment.tooltips;
    confirm = sinon.stub(atomEnvironment, 'confirm');

    lastCommit = new Commit({sha: 'a1e23fd45', message: 'last commit message'});
    const noop = () => {};
    const store = new UserStore({config});

    app = (
      <CommitController
        workspace={workspace}
        grammars={atomEnvironment.grammars}
        commandRegistry={commandRegistry}
        tooltips={tooltips}
        config={config}
        notificationManager={notificationManager}
        repository={{}}
        isMerging={false}
        mergeConflictsExist={false}
        stagedChangesExist={false}
        mergeMessage={''}
        lastCommit={lastCommit}
        currentBranch={nullBranch}
        userStore={store}
        prepareToCommit={noop}
        commit={noop}
        abortMerge={noop}
      />
    );
  });

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

  it('correctly updates state when switching repos', async function() {
    const workdirPath1 = await cloneRepository('three-files');
    const repository1 = await buildRepository(workdirPath1);
    const workdirPath2 = await cloneRepository('three-files');
    const repository2 = await buildRepository(workdirPath2);

    app = React.cloneElement(app, {repository: repository1});
    const wrapper = shallow(app, {disableLifecycleMethods: true});

    assert.strictEqual(wrapper.instance().getCommitMessage(), '');

    wrapper.instance().setCommitMessage('message 1');

    wrapper.setProps({repository: repository2});

    assert.strictEqual(wrapper.instance().getCommitMessage(), '');

    wrapper.setProps({repository: repository1});
    assert.equal(wrapper.instance().getCommitMessage(), 'message 1');
  });

  describe('the passed commit message', function() {
    let repository;

    beforeEach(async function() {
      const workdirPath = await cloneRepository('three-files');
      repository = await buildRepository(workdirPath);
      app = React.cloneElement(app, {repository});
    });

    it('is set to the getCommitMessage() in the default case', function() {
      repository.setCommitMessage('some message');
      const wrapper = shallow(app);
      assert.strictEqual(wrapper.find('CommitView').prop('messageBuffer').getText(), 'some message');
    });

    it('does not cause the repository to update when commit message changes', function() {
      repository.setCommitMessage('some message');
      const wrapper = shallow(app, {disableLifecycleMethods: true}).instance();
      sinon.spy(wrapper.props.repository.state, 'didUpdate');
      assert.strictEqual(wrapper.getCommitMessage(), 'some message');
      wrapper.handleMessageChange('new message');
      assert.strictEqual(wrapper.getCommitMessage(), 'new message');
      assert.isFalse(wrapper.props.repository.state.didUpdate.called);
    });

    describe('when a merge message is defined', function() {
      it('is set to the merge message when merging', function() {
        app = React.cloneElement(app, {isMerging: true, mergeMessage: 'merge conflict!'});
        const wrapper = shallow(app);
        assert.strictEqual(wrapper.find('CommitView').prop('messageBuffer').getText(), 'merge conflict!');
      });

      it('is set to getCommitMessage() if it is set', function() {
        repository.setCommitMessage('some commit message');
        app = React.cloneElement(app, {isMerging: true, mergeMessage: 'merge conflict!'});
        const wrapper = shallow(app, {disableLifecycleMethods: true});
        assert.strictEqual(wrapper.find('CommitView').prop('messageBuffer').getText(), 'some commit message');
      });
    });
  });

  describe('committing', function() {
    let workdirPath, repository, commit;

    beforeEach(async function() {
      workdirPath = await cloneRepository('three-files');
      repository = await buildRepositoryWithPipeline(workdirPath, {confirm, notificationManager, workspace});
      commit = sinon.stub().callsFake((...args) => repository.commit(...args));

      app = React.cloneElement(app, {repository, commit});
    });

    it('clears the commit messages', async function() {
      repository.setCommitMessage('a message');

      await fs.writeFile(path.join(workdirPath, 'a.txt'), 'some changes', {encoding: 'utf8'});
      await repository.git.exec(['add', '.']);

      const wrapper = shallow(app, {disableLifecycleMethods: true});
      await wrapper.instance().commit('another message');

      assert.strictEqual(repository.getCommitMessage(), '');
    });

    it('sets the verbatim flag when committing from the mini editor', async function() {
      await fs.writeFile(path.join(workdirPath, 'a.txt'), 'some changes', {encoding: 'utf8'});
      await repository.git.exec(['add', '.']);

      const wrapper = shallow(app, {disableLifecycleMethods: true});
      await wrapper.instance().commit('message\n\n#123 do some things');

      assert.isTrue(commit.calledWith('message\n\n#123 do some things', {
        amend: false,
        coAuthors: [],
        verbatim: true,
      }));
    });

    it('issues a notification on failure', async function() {
      repository.setCommitMessage('some message');

      sinon.spy(notificationManager, 'addError');

      const wrapper = shallow(app, {disableLifecycleMethods: true});

      // Committing with no staged changes should cause commit error
      try {
        await wrapper.instance().commit('message');
      } catch (e) {
        assert(e, 'is error');
      }

      assert.isTrue(notificationManager.addError.called);

      assert.strictEqual(repository.getCommitMessage(), 'some message');
    });

    describe('message formatting', function() {
      let commitSpy, wrapper;

      beforeEach(function() {
        commitSpy = sinon.stub().returns(Promise.resolve());
        app = React.cloneElement(app, {commit: commitSpy});
        wrapper = shallow(app, {disableLifecycleMethods: true});
      });

      describe('with automatic wrapping disabled', function() {
        beforeEach(function() {
          config.set('github.automaticCommitMessageWrapping', false);
        });

        it('passes commit messages through unchanged', async function() {
          await wrapper.instance().commit([
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
          ].join('\n'));

          assert.strictEqual(commitSpy.args[0][0], [
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
          ].join('\n'));
        });
      });

      describe('with automatic wrapping enabled', function() {
        beforeEach(function() {
          config.set('github.automaticCommitMessageWrapping', true);
        });

        it('wraps lines within the commit body at 72 characters', async function() {
          await wrapper.instance().commit([
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
          ].join('\n'));

          assert.strictEqual(commitSpy.args[0][0], [
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ',
            'ut aliquip ex ea commodo consequat.',
          ].join('\n'));
        });

        it('preserves existing line wraps within the commit body', async function() {
          await wrapper.instance().commit('a\n\nb\n\nc');
          assert.strictEqual(commitSpy.args[0][0], 'a\n\nb\n\nc');
        });
      });
    });

    describe('toggling between commit box and commit editor', function() {
      it('transfers the commit message contents of the last editor', async function() {
        const wrapper = shallow(app);

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('message in box');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());
        wrapper.update();
        assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));

        const editor = workspace.getActiveTextEditor();
        assert.strictEqual(editor.getText(), 'message in box');
        editor.setText('message in editor');
        await editor.save();

        workspace.paneForItem(editor).splitRight({copyActiveItem: true});
        await assert.async.notEqual(workspace.getActiveTextEditor(), editor);

        editor.destroy();
        assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));

        workspace.getActiveTextEditor().destroy();
        assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));
        await assert.async.strictEqual(wrapper.update().find('CommitView').prop('messageBuffer').getText(), 'message in editor');
      });

      it('activates editor if already opened but in background', async function() {
        const wrapper = shallow(app, {disableLifecycleMethods: true});

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('sup');
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());
        const editor = workspace.getActiveTextEditor();

        await workspace.open(path.join(workdirPath, 'a.txt'));
        workspace.getActivePane().splitRight();
        await workspace.open(path.join(workdirPath, 'b.txt'));
        assert.notStrictEqual(workspace.getActiveTextEditor(), editor);

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.strictEqual(workspace.getActiveTextEditor(), editor);
      });

      it('closes all open commit message editors if one is in the foreground of a pane, prompting for unsaved changes', async function() {
        const wrapper = shallow(app);

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('sup');
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        workspace.paneForItem(editor).splitRight({copyActiveItem: true});
        assert.lengthOf(wrapper.instance().getCommitMessageEditors(), 2);

        // Activate another editor but keep commit message editor in foreground of inactive pane
        await workspace.open(path.join(workdirPath, 'a.txt'));
        assert.notStrictEqual(workspace.getActiveTextEditor(), editor);

        editor.setText('make some new changes');

        // atom internals calls `confirm` on the ApplicationDelegate instead of the atom environment
        sinon.stub(atomEnvironment.applicationDelegate, 'confirm').callsFake((options, callback) => {
          if (typeof callback === 'function') {
            callback(0); // Save
          }
          return 0; // TODO: Remove this return and typeof check once https://github.com/atom/atom/pull/16229 is on stable
        });
        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.lengthOf(wrapper.instance().getCommitMessageEditors(), 0);
        assert.isTrue(atomEnvironment.applicationDelegate.confirm.called);
        await assert.async.strictEqual(wrapper.update().find('CommitView').prop('messageBuffer').getText(), 'make some new changes');
      });
    });

    describe('committing from commit editor', function() {
      it('uses git commit grammar in the editor', async function() {
        const wrapper = shallow(app, {disableLifecycleMethods: true});
        await atomEnvironment.packages.activatePackage('language-git');
        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('sup');
        await assert.async.strictEqual(workspace.getActiveTextEditor().getGrammar().scopeName, COMMIT_GRAMMAR_SCOPE);
      });

      it('takes the commit message from the editor and deletes the `ATOM_COMMIT_EDITMSG` file', async function() {
        fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'some changes');
        await repository.stageFiles(['a.txt']);

        app = React.cloneElement(app, {prepareToCommit: () => true, stagedChangesExist: true});
        const wrapper = shallow(app, {disableLifecycleMethods: true});

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        editor.setText('message in editor');
        await editor.save();

        await wrapper.find('CommitView').prop('commit')('message in box');

        assert.strictEqual((await repository.getLastCommit()).getMessageSubject(), 'message in editor');
        assert.isFalse(fs.existsSync(wrapper.instance().getCommitMessagePath()));
        assert.isTrue(commit.calledWith('message in editor', {
          amend: false, coAuthors: [], verbatim: false,
        }));
      });

      it('asks user to confirm if commit editor has unsaved changes', async function() {
        app = React.cloneElement(app, {confirm, prepareToCommit: () => true, stagedChangesExist: true});
        const wrapper = shallow(app, {disableLifecycleMethods: true});

        sinon.stub(repository.git, 'commit');
        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        editor.setText('unsaved changes');
        workspace.paneForItem(editor).splitRight({copyActiveItem: true});
        await assert.async.notStrictEqual(workspace.getActiveTextEditor(), editor);
        assert.lengthOf(workspace.getTextEditors(), 2);

        confirm.returns(1); // Cancel
        wrapper.find('CommitView').prop('commit')('message in box');
        assert.strictEqual(repository.git.commit.callCount, 0);

        confirm.returns(0); // Commit
        wrapper.find('CommitView').prop('commit')('message in box');
        await assert.async.equal(repository.git.commit.callCount, 1);
        await assert.async.lengthOf(workspace.getTextEditors(), 0);
      });
    });

    it('delegates focus management to its view', function() {
      const wrapper = mount(app);
      const viewHolder = wrapper.instance().refCommitView;
      assert.isFalse(viewHolder.isEmpty());
      const view = viewHolder.get();

      sinon.spy(view, 'rememberFocus');
      sinon.spy(view, 'setFocus');
      sinon.spy(view, 'hasFocus');
      sinon.spy(view, 'hasFocusEditor');

      const element = wrapper.find('AtomTextEditor').getDOMNode().querySelector('atom-text-editor');
      wrapper.instance().rememberFocus({target: element});
      assert.isTrue(view.rememberFocus.called);

      wrapper.instance().setFocus(CommitController.focus.EDITOR);
      assert.isTrue(view.setFocus.called);

      wrapper.instance().hasFocus();
      assert.isTrue(view.hasFocus.called);

      wrapper.instance().hasFocusEditor();
      assert.isTrue(view.hasFocusEditor.called);
    });

    it('no-ops focus management methods when the view ref is unassigned', function() {
      const wrapper = shallow(app);
      assert.isTrue(wrapper.instance().refCommitView.isEmpty());

      assert.isNull(wrapper.instance().rememberFocus({}));
      assert.isFalse(wrapper.instance().setFocus(CommitController.focus.EDITOR));
      assert.isFalse(wrapper.instance().hasFocus());
      assert.isFalse(wrapper.instance().hasFocusEditor());
    });
  });
});
<commit_msg>Update another CommitController test to use the messageBuffer<commit_after>import path from 'path';
import fs from 'fs-extra';
import React from 'react';
import {shallow, mount} from 'enzyme';

import Commit from '../../lib/models/commit';
import {nullBranch} from '../../lib/models/branch';
import UserStore from '../../lib/models/user-store';

import CommitController, {COMMIT_GRAMMAR_SCOPE} from '../../lib/controllers/commit-controller';
import {cloneRepository, buildRepository, buildRepositoryWithPipeline} from '../helpers';

describe('CommitController', function() {
  let atomEnvironment, workspace, commandRegistry, notificationManager, lastCommit, config, confirm, tooltips;
  let app;

  beforeEach(function() {
    atomEnvironment = global.buildAtomEnvironment();
    workspace = atomEnvironment.workspace;
    commandRegistry = atomEnvironment.commands;
    notificationManager = atomEnvironment.notifications;
    config = atomEnvironment.config;
    tooltips = atomEnvironment.tooltips;
    confirm = sinon.stub(atomEnvironment, 'confirm');

    lastCommit = new Commit({sha: 'a1e23fd45', message: 'last commit message'});
    const noop = () => {};
    const store = new UserStore({config});

    app = (
      <CommitController
        workspace={workspace}
        grammars={atomEnvironment.grammars}
        commandRegistry={commandRegistry}
        tooltips={tooltips}
        config={config}
        notificationManager={notificationManager}
        repository={{}}
        isMerging={false}
        mergeConflictsExist={false}
        stagedChangesExist={false}
        mergeMessage={''}
        lastCommit={lastCommit}
        currentBranch={nullBranch}
        userStore={store}
        prepareToCommit={noop}
        commit={noop}
        abortMerge={noop}
      />
    );
  });

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

  it('correctly updates state when switching repos', async function() {
    const workdirPath1 = await cloneRepository('three-files');
    const repository1 = await buildRepository(workdirPath1);
    const workdirPath2 = await cloneRepository('three-files');
    const repository2 = await buildRepository(workdirPath2);

    app = React.cloneElement(app, {repository: repository1});
    const wrapper = shallow(app, {disableLifecycleMethods: true});

    assert.strictEqual(wrapper.instance().getCommitMessage(), '');

    wrapper.instance().setCommitMessage('message 1');

    wrapper.setProps({repository: repository2});

    assert.strictEqual(wrapper.instance().getCommitMessage(), '');

    wrapper.setProps({repository: repository1});
    assert.equal(wrapper.instance().getCommitMessage(), 'message 1');
  });

  describe('the passed commit message', function() {
    let repository;

    beforeEach(async function() {
      const workdirPath = await cloneRepository('three-files');
      repository = await buildRepository(workdirPath);
      app = React.cloneElement(app, {repository});
    });

    it('is set to the getCommitMessage() in the default case', function() {
      repository.setCommitMessage('some message');
      const wrapper = shallow(app);
      assert.strictEqual(wrapper.find('CommitView').prop('messageBuffer').getText(), 'some message');
    });

    it('does not cause the repository to update when commit message changes', function() {
      repository.setCommitMessage('some message');
      const wrapper = shallow(app, {disableLifecycleMethods: true});
      const instance = wrapper.instance();
      sinon.spy(repository.state, 'didUpdate');
      assert.strictEqual(instance.getCommitMessage(), 'some message');
      wrapper.find('CommitView').prop('messageBuffer').setText('new message');
      assert.strictEqual(instance.getCommitMessage(), 'new message');
      assert.isFalse(repository.state.didUpdate.called);
    });

    describe('when a merge message is defined', function() {
      it('is set to the merge message when merging', function() {
        app = React.cloneElement(app, {isMerging: true, mergeMessage: 'merge conflict!'});
        const wrapper = shallow(app);
        assert.strictEqual(wrapper.find('CommitView').prop('messageBuffer').getText(), 'merge conflict!');
      });

      it('is set to getCommitMessage() if it is set', function() {
        repository.setCommitMessage('some commit message');
        app = React.cloneElement(app, {isMerging: true, mergeMessage: 'merge conflict!'});
        const wrapper = shallow(app, {disableLifecycleMethods: true});
        assert.strictEqual(wrapper.find('CommitView').prop('messageBuffer').getText(), 'some commit message');
      });
    });
  });

  describe('committing', function() {
    let workdirPath, repository, commit;

    beforeEach(async function() {
      workdirPath = await cloneRepository('three-files');
      repository = await buildRepositoryWithPipeline(workdirPath, {confirm, notificationManager, workspace});
      commit = sinon.stub().callsFake((...args) => repository.commit(...args));

      app = React.cloneElement(app, {repository, commit});
    });

    it('clears the commit messages', async function() {
      repository.setCommitMessage('a message');

      await fs.writeFile(path.join(workdirPath, 'a.txt'), 'some changes', {encoding: 'utf8'});
      await repository.git.exec(['add', '.']);

      const wrapper = shallow(app, {disableLifecycleMethods: true});
      await wrapper.instance().commit('another message');

      assert.strictEqual(repository.getCommitMessage(), '');
    });

    it('sets the verbatim flag when committing from the mini editor', async function() {
      await fs.writeFile(path.join(workdirPath, 'a.txt'), 'some changes', {encoding: 'utf8'});
      await repository.git.exec(['add', '.']);

      const wrapper = shallow(app, {disableLifecycleMethods: true});
      await wrapper.instance().commit('message\n\n#123 do some things');

      assert.isTrue(commit.calledWith('message\n\n#123 do some things', {
        amend: false,
        coAuthors: [],
        verbatim: true,
      }));
    });

    it('issues a notification on failure', async function() {
      repository.setCommitMessage('some message');

      sinon.spy(notificationManager, 'addError');

      const wrapper = shallow(app, {disableLifecycleMethods: true});

      // Committing with no staged changes should cause commit error
      try {
        await wrapper.instance().commit('message');
      } catch (e) {
        assert(e, 'is error');
      }

      assert.isTrue(notificationManager.addError.called);

      assert.strictEqual(repository.getCommitMessage(), 'some message');
    });

    describe('message formatting', function() {
      let commitSpy, wrapper;

      beforeEach(function() {
        commitSpy = sinon.stub().returns(Promise.resolve());
        app = React.cloneElement(app, {commit: commitSpy});
        wrapper = shallow(app, {disableLifecycleMethods: true});
      });

      describe('with automatic wrapping disabled', function() {
        beforeEach(function() {
          config.set('github.automaticCommitMessageWrapping', false);
        });

        it('passes commit messages through unchanged', async function() {
          await wrapper.instance().commit([
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
          ].join('\n'));

          assert.strictEqual(commitSpy.args[0][0], [
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
          ].join('\n'));
        });
      });

      describe('with automatic wrapping enabled', function() {
        beforeEach(function() {
          config.set('github.automaticCommitMessageWrapping', true);
        });

        it('wraps lines within the commit body at 72 characters', async function() {
          await wrapper.instance().commit([
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
          ].join('\n'));

          assert.strictEqual(commitSpy.args[0][0], [
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor',
            '',
            'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ',
            'ut aliquip ex ea commodo consequat.',
          ].join('\n'));
        });

        it('preserves existing line wraps within the commit body', async function() {
          await wrapper.instance().commit('a\n\nb\n\nc');
          assert.strictEqual(commitSpy.args[0][0], 'a\n\nb\n\nc');
        });
      });
    });

    describe('toggling between commit box and commit editor', function() {
      it('transfers the commit message contents of the last editor', async function() {
        const wrapper = shallow(app);

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('message in box');
        await assert.async.equal(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());
        wrapper.update();
        assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));

        const editor = workspace.getActiveTextEditor();
        assert.strictEqual(editor.getText(), 'message in box');
        editor.setText('message in editor');
        await editor.save();

        workspace.paneForItem(editor).splitRight({copyActiveItem: true});
        await assert.async.notEqual(workspace.getActiveTextEditor(), editor);

        editor.destroy();
        assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));

        workspace.getActiveTextEditor().destroy();
        assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));
        await assert.async.strictEqual(wrapper.update().find('CommitView').prop('messageBuffer').getText(), 'message in editor');
      });

      it('activates editor if already opened but in background', async function() {
        const wrapper = shallow(app, {disableLifecycleMethods: true});

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('sup');
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());
        const editor = workspace.getActiveTextEditor();

        await workspace.open(path.join(workdirPath, 'a.txt'));
        workspace.getActivePane().splitRight();
        await workspace.open(path.join(workdirPath, 'b.txt'));
        assert.notStrictEqual(workspace.getActiveTextEditor(), editor);

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.strictEqual(workspace.getActiveTextEditor(), editor);
      });

      it('closes all open commit message editors if one is in the foreground of a pane, prompting for unsaved changes', async function() {
        const wrapper = shallow(app);

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('sup');
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        workspace.paneForItem(editor).splitRight({copyActiveItem: true});
        assert.lengthOf(wrapper.instance().getCommitMessageEditors(), 2);

        // Activate another editor but keep commit message editor in foreground of inactive pane
        await workspace.open(path.join(workdirPath, 'a.txt'));
        assert.notStrictEqual(workspace.getActiveTextEditor(), editor);

        editor.setText('make some new changes');

        // atom internals calls `confirm` on the ApplicationDelegate instead of the atom environment
        sinon.stub(atomEnvironment.applicationDelegate, 'confirm').callsFake((options, callback) => {
          if (typeof callback === 'function') {
            callback(0); // Save
          }
          return 0; // TODO: Remove this return and typeof check once https://github.com/atom/atom/pull/16229 is on stable
        });
        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.lengthOf(wrapper.instance().getCommitMessageEditors(), 0);
        assert.isTrue(atomEnvironment.applicationDelegate.confirm.called);
        await assert.async.strictEqual(wrapper.update().find('CommitView').prop('messageBuffer').getText(), 'make some new changes');
      });
    });

    describe('committing from commit editor', function() {
      it('uses git commit grammar in the editor', async function() {
        const wrapper = shallow(app, {disableLifecycleMethods: true});
        await atomEnvironment.packages.activatePackage('language-git');
        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('sup');
        await assert.async.strictEqual(workspace.getActiveTextEditor().getGrammar().scopeName, COMMIT_GRAMMAR_SCOPE);
      });

      it('takes the commit message from the editor and deletes the `ATOM_COMMIT_EDITMSG` file', async function() {
        fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'some changes');
        await repository.stageFiles(['a.txt']);

        app = React.cloneElement(app, {prepareToCommit: () => true, stagedChangesExist: true});
        const wrapper = shallow(app, {disableLifecycleMethods: true});

        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        editor.setText('message in editor');
        await editor.save();

        await wrapper.find('CommitView').prop('commit')('message in box');

        assert.strictEqual((await repository.getLastCommit()).getMessageSubject(), 'message in editor');
        assert.isFalse(fs.existsSync(wrapper.instance().getCommitMessagePath()));
        assert.isTrue(commit.calledWith('message in editor', {
          amend: false, coAuthors: [], verbatim: false,
        }));
      });

      it('asks user to confirm if commit editor has unsaved changes', async function() {
        app = React.cloneElement(app, {confirm, prepareToCommit: () => true, stagedChangesExist: true});
        const wrapper = shallow(app, {disableLifecycleMethods: true});

        sinon.stub(repository.git, 'commit');
        wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
        await assert.async.strictEqual(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());

        const editor = workspace.getActiveTextEditor();
        editor.setText('unsaved changes');
        workspace.paneForItem(editor).splitRight({copyActiveItem: true});
        await assert.async.notStrictEqual(workspace.getActiveTextEditor(), editor);
        assert.lengthOf(workspace.getTextEditors(), 2);

        confirm.returns(1); // Cancel
        wrapper.find('CommitView').prop('commit')('message in box');
        assert.strictEqual(repository.git.commit.callCount, 0);

        confirm.returns(0); // Commit
        wrapper.find('CommitView').prop('commit')('message in box');
        await assert.async.equal(repository.git.commit.callCount, 1);
        await assert.async.lengthOf(workspace.getTextEditors(), 0);
      });
    });

    it('delegates focus management to its view', function() {
      const wrapper = mount(app);
      const viewHolder = wrapper.instance().refCommitView;
      assert.isFalse(viewHolder.isEmpty());
      const view = viewHolder.get();

      sinon.spy(view, 'rememberFocus');
      sinon.spy(view, 'setFocus');
      sinon.spy(view, 'hasFocus');
      sinon.spy(view, 'hasFocusEditor');

      const element = wrapper.find('AtomTextEditor').getDOMNode().querySelector('atom-text-editor');
      wrapper.instance().rememberFocus({target: element});
      assert.isTrue(view.rememberFocus.called);

      wrapper.instance().setFocus(CommitController.focus.EDITOR);
      assert.isTrue(view.setFocus.called);

      wrapper.instance().hasFocus();
      assert.isTrue(view.hasFocus.called);

      wrapper.instance().hasFocusEditor();
      assert.isTrue(view.hasFocusEditor.called);
    });

    it('no-ops focus management methods when the view ref is unassigned', function() {
      const wrapper = shallow(app);
      assert.isTrue(wrapper.instance().refCommitView.isEmpty());

      assert.isNull(wrapper.instance().rememberFocus({}));
      assert.isFalse(wrapper.instance().setFocus(CommitController.focus.EDITOR));
      assert.isFalse(wrapper.instance().hasFocus());
      assert.isFalse(wrapper.instance().hasFocusEditor());
    });
  });
});
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import AppActions from '../../actions/AppActions';

/*
|--------------------------------------------------------------------------
| WindowControls
|--------------------------------------------------------------------------
*/

export default class WindowControls extends PureComponent {

    static propTypes = {
        active: React.PropTypes.bool,
    }

    constructor(props) {
        super(props);
    }

    render() {
        if(!this.props.active) return null;

        return (

                <button className='window-control window-minimize' onClick={ this.winMinimize }>－</button> { /* U+FF0D FULLWIDTH HYPHEN-MINUS */ }
                <button className='window-control window-maximize' onClick={ this.winMaximize } /> { /* custom square with ::after */ }
                <button className='window-control window-close' onClick={ this.winClose }></button> { /* custom cross with ::before and ::after */ }

        );
    }

    winClose() {
        AppActions.close();
    }

    winMinimize() {
        AppActions.minimize();
    }

    winMaximize() {
        AppActions.maximize();
    }
}
<commit_msg>Fix eslint<commit_after>import React, { PureComponent } from 'react';
import AppActions from '../../actions/AppActions';

/*
|--------------------------------------------------------------------------
| WindowControls
|--------------------------------------------------------------------------
*/

export default class WindowControls extends PureComponent {

    static propTypes = {
        active: React.PropTypes.bool,
    }

    constructor(props) {
        super(props);
    }

    render() {
        if(!this.props.active) return null;

        return (

                <button className='window-control window-minimize' onClick={ this.winMinimize }>－</button> { /* U+FF0D FULLWIDTH HYPHEN-MINUS */ }
                <button className='window-control window-maximize' onClick={ this.winMaximize } /> { /* custom square with ::after */ }
                <button className='window-control window-close' onClick={ this.winClose } /> { /* custom cross with ::before and ::after */ }

        );
    }

    winClose() {
        AppActions.close();
    }

    winMinimize() {
        AppActions.minimize();
    }

    winMaximize() {
        AppActions.maximize();
    }
}
<|endoftext|>


<commit_before>import { Link } from "react-router";
import prettycron from "prettycron";
import React from "react";
import { Tooltip } from "reactjs-components";

import Job from "#SRC/js/structs/Job";
import JobTree from "#SRC/js/structs/JobTree";
import TaskStates from "#PLUGINS/services/src/js/constants/TaskStates";

import Breadcrumb from "../../components/Breadcrumb";
import BreadcrumbSupplementalContent
  from "../../components/BreadcrumbSupplementalContent";
import BreadcrumbTextContent from "../../components/BreadcrumbTextContent";
import Icon from "../Icon";
import PageHeaderBreadcrumbs from "../../components/PageHeaderBreadcrumbs";

function getLongestRunningTask(item) {
  if (!(item instanceof Job)) {
    return null;
  }

  const longestRunningActiveRun = item
    .getActiveRuns()
    .getLongestRunningActiveRun();

  if (!longestRunningActiveRun) {
    return null;
  }

  return longestRunningActiveRun.getTasks().getLongestRunningTask();
}

function getItemStatus(item) {
  const longestRunningTask = getLongestRunningTask(item);

  if (!longestRunningTask) {
    return null;
  }

  return (
    <BreadcrumbSupplementalContent>

        ({TaskStates[longestRunningTask.getStatus()].displayName})

    </BreadcrumbSupplementalContent>
  );
}

function getItemSchedule(item) {
  if (!(item instanceof Job)) {
    return null;
  }

  const schedule = item.getSchedules()[0];

  if (!schedule || !schedule.enabled) {
    return null;
  }

  return (
    <BreadcrumbSupplementalContent>
      <Tooltip
        content={prettycron.toString(schedule.cron)}
        maxWidth={250}
        wrapText={true}
      >
        <Icon color="grey" id="repeat" size="mini" />
      </Tooltip>
    </BreadcrumbSupplementalContent>
  );
}

function getBreadcrumb(item, details = true) {
  const id = item.getId();
  const name = item.getName();
  const link = item instanceof Job
    ? `/jobs/detail/${id}`
    : `/jobs/overview/${id}`;

  return (
    <Breadcrumb key={id} title="Jobs">
      <BreadcrumbTextContent>
        <Link to={link}>
          {name === "" ? "Jobs" : name}
        </Link>
      </BreadcrumbTextContent>
      {details ? getItemSchedule(item) : null}
      {details ? getItemStatus(item) : null}
    </Breadcrumb>
  );
}

const JobsBreadcrumbs = ({ tree, item, children, details = true }) => {
  const elements = tree
    .filterItems(function(currentItem) {
      return (
        item != null &&
        item.getId() === currentItem.getId() &&
        item.constructor === currentItem.constructor
      );
    })
    .reduceItems(function(acc, currentItem) {
      return acc.concat(getBreadcrumb(currentItem, details));
    }, []);

  const breadcrumbs = [].concat(
    getBreadcrumb(tree, details),
    elements,
    React.Children.toArray(children)
  );

  return <PageHeaderBreadcrumbs iconID="jobs" breadcrumbs={breadcrumbs} />;
};

JobsBreadcrumbs.propTypes = {
  tree: React.PropTypes.instanceOf(JobTree).isRequired,
  item: React.PropTypes.oneOfType([
    React.PropTypes.instanceOf(JobTree),
    React.PropTypes.instanceOf(Job)
  ])
};

module.exports = JobsBreadcrumbs;
<commit_msg>refactor(JobsBreadcrumbs): optimize the filter tree items condition<commit_after>import { Link } from "react-router";
import prettycron from "prettycron";
import React from "react";
import { Tooltip } from "reactjs-components";

import Job from "#SRC/js/structs/Job";
import JobTree from "#SRC/js/structs/JobTree";
import TaskStates from "#PLUGINS/services/src/js/constants/TaskStates";

import Breadcrumb from "../../components/Breadcrumb";
import BreadcrumbSupplementalContent
  from "../../components/BreadcrumbSupplementalContent";
import BreadcrumbTextContent from "../../components/BreadcrumbTextContent";
import Icon from "../Icon";
import PageHeaderBreadcrumbs from "../../components/PageHeaderBreadcrumbs";

function getLongestRunningTask(item) {
  if (!(item instanceof Job)) {
    return null;
  }

  const longestRunningActiveRun = item
    .getActiveRuns()
    .getLongestRunningActiveRun();

  if (!longestRunningActiveRun) {
    return null;
  }

  return longestRunningActiveRun.getTasks().getLongestRunningTask();
}

function getItemStatus(item) {
  const longestRunningTask = getLongestRunningTask(item);

  if (!longestRunningTask) {
    return null;
  }

  return (
    <BreadcrumbSupplementalContent>

        ({TaskStates[longestRunningTask.getStatus()].displayName})

    </BreadcrumbSupplementalContent>
  );
}

function getItemSchedule(item) {
  if (!(item instanceof Job)) {
    return null;
  }

  const schedule = item.getSchedules()[0];

  if (!schedule || !schedule.enabled) {
    return null;
  }

  return (
    <BreadcrumbSupplementalContent>
      <Tooltip
        content={prettycron.toString(schedule.cron)}
        maxWidth={250}
        wrapText={true}
      >
        <Icon color="grey" id="repeat" size="mini" />
      </Tooltip>
    </BreadcrumbSupplementalContent>
  );
}

function getBreadcrumb(item, details = true) {
  const id = item.getId();
  const name = item.getName();
  const link = item instanceof Job
    ? `/jobs/detail/${id}`
    : `/jobs/overview/${id}`;

  return (
    <Breadcrumb key={id} title="Jobs">
      <BreadcrumbTextContent>
        <Link to={link}>
          {name === "" ? "Jobs" : name}
        </Link>
      </BreadcrumbTextContent>
      {details ? getItemSchedule(item) : null}
      {details ? getItemStatus(item) : null}
    </Breadcrumb>
  );
}

function getBreadcrumbList(tree, item, details) {
  if (item == null) {
    return [];
  }

  return tree
    .filterItems(function(currentItem) {
      return (
        item.getId() === currentItem.getId() &&
        item.constructor === currentItem.constructor
      );
    })
    .reduceItems(function(acc, currentItem) {
      return acc.concat(getBreadcrumb(currentItem, details));
    }, []);
}

const JobsBreadcrumbs = ({ tree, item, children, details = true }) => {
  const breadcrumbs = [].concat(
    getBreadcrumb(tree, details),
    getBreadcrumbList(tree, item, details),
    React.Children.toArray(children)
  );

  return <PageHeaderBreadcrumbs iconID="jobs" breadcrumbs={breadcrumbs} />;
};

JobsBreadcrumbs.propTypes = {
  tree: React.PropTypes.instanceOf(JobTree).isRequired,
  item: React.PropTypes.oneOfType([
    React.PropTypes.instanceOf(JobTree),
    React.PropTypes.instanceOf(Job)
  ])
};

module.exports = JobsBreadcrumbs;
<|endoftext|>


<commit_before>/*
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

const {openRoomDirectory} = require('./create-room');

module.exports = async function join(session, roomName) {
    session.log.step(`joins room "${roomName}"`);
    await openRoomDirectory(session);
    const roomInput = await session.query('.mx_DirectorySearchBox input');
    await session.replaceInputText(roomInput, roomName);

    const joinFirstLink = await session.query('.mx_RoomDirectory_table .mx_RoomDirectory_join .mx_AccessibleButton');
    await joinFirstLink.click();
    await session.query('.mx_MessageComposer');
    session.log.done();
};
<commit_msg>test e2e tests workaround<commit_after>/*
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

const {openRoomDirectory} = require('./create-room');

module.exports = async function join(session, roomName) {
    session.log.step(`joins room "${roomName}"`);
    while (true) {
        try {
            const toastDismissButton = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger');
            await toastDismissButton.click();
        } catch (e) {
            break;
        }
    }

    await openRoomDirectory(session);
    const roomInput = await session.query('.mx_DirectorySearchBox input');
    await session.replaceInputText(roomInput, roomName);

    const joinFirstLink = await session.query('.mx_RoomDirectory_table .mx_RoomDirectory_join .mx_AccessibleButton');
    await joinFirstLink.click();
    await session.query('.mx_MessageComposer');
    session.log.done();
};
<|endoftext|>