<commit_before>'use strict';

var math = require( '../../../math' );
var util = require( '../../../util' );
var Heap = require( '../../../heap' );
var defs = require( './texture-cache-defs' );

// TODO round textures so that they always have integer sizes

// TODO optimise these values

var minTxrH = 25; // the size of the texture cache for small height eles (special case)
var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
var minLvl = -4; // when scaling smaller than that we don't need to re-render
var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
var defTxrWidth = 1024; // default/minimum texture width
var maxTxrW = 1024; // the maximum width of a texture
var maxTxrH = 1024;  // the maximum height of a texture
var minUtility = 0.5; // if usage of texture is less than this, it is retired
var maxFullness = 0.8; // fullness of texture after which queue removal is checked
var maxFullnessChecks = 10; // dequeued after this many checks
var allowEdgeTxrCaching = false; // whether edges can be cached as textures (TODO maybe better on if webgl supported?)
var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
var deqFastCost = 0.9; // % of frame time to be used when >60fps
var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch

var getTxrReasons = {
  dequeue: 'dequeue',
  downscale: 'downscale',
  highQuality: 'highQuality'
};

var ElementTextureCache = function( renderer ){
  var self = this;

  self.renderer = renderer;
  self.onDequeues = [];

  self.setupDequeueing();
};

var ETCp = ElementTextureCache.prototype;

ETCp.reasons = getTxrReasons;

// the list of textures in which new subtextures for elements can be placed
ETCp.getTextureQueue = function( txrH ){
  var self = this;
  self.eleImgCaches = self.eleImgCaches || {};

  return ( self.eleImgCaches[ txrH ] = self.eleImgCaches[ txrH ] || [] );
};

// the list of usused textures which can be recycled (in use in texture queue)
ETCp.getRetiredTextureQueue = function( txrH ){
  var self = this;

  var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
  var rtxtrQ = rtxtrQs[ txrH ] = rtxtrQs[ txrH ] || [];

  return rtxtrQ;
};

// queue of element draw requests at different scale levels
ETCp.getElementQueue = function(){
  var self = this;

  var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function( a, b ){
    return b.reqs - a.reqs;
  });

  return q;
};

// queue of element draw requests at different scale levels (element id lookup)
ETCp.getElementIdToQueue = function(){
  var self = this;

  var id2q = self.eleIdToCacheQueue = self.eleIdToCacheQueue || {};

  return id2q;
};

ETCp.getElement = function( ele, bb, pxRatio, lvl, reason ){
  var self = this;
  var r = this.renderer;
  var rs = ele._private.rscratch;
  var zoom = r.cy.zoom();

  if( lvl == null ){
    lvl = Math.ceil( math.log2( zoom * pxRatio ) );
  }

  if( lvl < minLvl ){
    lvl = minLvl;
  } else if( zoom >= maxZoom || lvl > maxLvl ){
    return null;
  }

  // if the visibility of labels changes with zoom, then we can't cache
  if( ele.pstyle('min-zoomed-font-size').pfValue !== 0 ){
    return null;
  }

  var scale = Math.pow( 2, lvl );
  var eleScaledH = bb.h * scale;
  var eleScaledW = bb.w * scale;
  var caches = rs.imgCaches = rs.imgCaches || {};
  var eleCache = caches[lvl];

  if( eleCache ){
    return eleCache;
  }

  var txrH; // which texture height this ele belongs to

  if( eleScaledH <= minTxrH ){
    txrH = minTxrH;
  } else if( eleScaledH <= txrStepH ){
    txrH = txrStepH;
  } else {
    txrH = Math.ceil( eleScaledH / txrStepH ) * txrStepH;
  }

  if( eleScaledH > maxTxrH || eleScaledW > maxTxrW || ( !allowEdgeTxrCaching && ele.isEdge() ) ){
    return null; // caching large elements is not efficient
  }

  var txrQ = self.getTextureQueue( txrH );

  // first try the second last one in case it has space at the end
  var txr = txrQ[ txrQ.length - 2 ];

  var addNewTxr = function(){
    return self.recycleTexture( txrH, eleScaledW ) || self.addTexture( txrH, eleScaledW );
  };

  // try the last one if there is no second last one
  if( !txr ){
    txr = txrQ[ txrQ.length - 1 ];
  }

  // if the last one doesn't exist, we need a first one
  if( !txr ){
    txr = addNewTxr();
  }

  // if there's no room in the current texture, we need a new one
  if( txr.width - txr.usedWidth < eleScaledW ){
    txr = addNewTxr();
  }

  var deqing = reason && reason === getTxrReasons.dequeue;
  var highQualityReq = reason && reason === getTxrReasons.highQuality;

  var higherCache; // the nearest cache with a higher level
  for( var l = lvl + 1; l <= maxLvl; l++ ){
    var c = caches[l];

    if( c ){ higherCache = c; break; }
  }

  var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;

  var downscale = function(){
    txr.context.drawImage(
      oneUpCache.texture.canvas,
      oneUpCache.x, 0,
      oneUpCache.width, oneUpCache.height,
      txr.usedWidth, 0,
      eleScaledW, eleScaledH
    );
  };

  if( oneUpCache ){
    // then we can relatively cheaply rescale the existing image w/o rerendering
    downscale();

  } else if( higherCache ){
    // then use the higher cache for now and queue the next level down
    // to cheaply scale towards the smaller level

    if( highQualityReq ){
      for( var l = higherCache.level; l > lvl; l-- ){
        oneUpCache = self.getElement( ele, bb, l, reason );
      }

      downscale();

    } else {
      self.queueElement( ele, bb, higherCache.level - 1 );

      return higherCache;
    }
  } else {

    var lowerCache; // the nearest cache with a lower level
    if( !deqing && !highQualityReq ){
      for( var l = lvl - 1; l >= minLvl; l-- ){
        var c = caches[l];

        if( c ){ lowerCache = c; break; }
      }
    }

    if( lowerCache ){
      // then use the lower quality cache for now and queue the better one for later

      self.queueElement( ele, bb, lvl );

      return lowerCache;
    }

    txr.context.translate( txr.usedWidth, 0 );
    txr.context.scale( scale, scale );

    r.drawElement( txr.context, ele, bb );

    txr.context.scale( 1/scale, 1/scale );
    txr.context.translate( -txr.usedWidth, 0 );
  }

  eleCache = caches[lvl] = {
    ele: ele,
    x: txr.usedWidth,
    texture: txr,
    level: lvl,
    scale: scale,
    width: eleScaledW,
    height: eleScaledH
  };

  txr.usedWidth += Math.ceil( eleScaledW + eleTxrSpacing );

  txr.eleCaches.push( eleCache );

  self.checkTextureFullness( txr );

  return eleCache;
};

ETCp.invalidateElement = function( ele ){
  var self = this;
  var caches = ele._private.rscratch.imgCaches;

  if( caches ){
    for( var lvl = minLvl; lvl <= maxLvl; lvl++ ){
      var cache = caches[ lvl ];

      if( cache ){
        var txr = cache.texture;

        // remove space from the texture it belongs to
        txr.invalidatedWidth += cache.width;

        // remove refs with the element
        caches[ lvl ] = null;
        util.removeFromArray( txr.eleCaches, cache );

        // might have to remove the entire texture if it's not efficiently using its space
        self.checkTextureUtility( txr );
      }
    }
  }
};

ETCp.checkTextureUtility = function( txr ){
  // invalidate all entries in the cache if the cache size is small
  if( txr.invalidatedWidth >= minUtility * txr.width ){
    this.retireTexture( txr );
  }
};

ETCp.checkTextureFullness = function( txr ){
  // if texture has been mostly filled and passed over several times, remove
  // it from the queue so we don't need to waste time looking at it to put new things

  var self = this;
  var txrQ = self.getTextureQueue( txr.height );

  if( txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks ){
    util.removeFromArray( txrQ, txr );
  } else {
    txr.fullnessChecks++;
  }
};

ETCp.retireTexture = function( txr ){
  var self = this;
  var txrH = txr.height;
  var txrQ = self.getTextureQueue( txrH );

  // retire the texture from the active / searchable queue:

  util.removeFromArray( txrQ, txr );

  txr.retired = true;

  // remove the refs from the eles to the caches:

  var eleCaches = txr.eleCaches;

  for( var i = 0; i < eleCaches.length; i++ ){
    var eleCache = eleCaches[i];
    var ele = eleCache.ele;
    var lvl = eleCache.level;
    var imgCaches = ele._private.rscratch.imgCaches;

    if( imgCaches ){
      imgCaches[ lvl ] = null;
    }
  }

  util.clearArray( eleCaches );

  // add the texture to a retired queue so it can be recycled in future:

  var rtxtrQ = self.getRetiredTextureQueue( txrH );

  rtxtrQ.push( txr );
};

ETCp.addTexture = function( txrH, minW ){
  var self = this;
  var txrQ = self.getTextureQueue( txrH );
  var txr = {};

  txrQ.push( txr );

  txr.eleCaches = [];

  txr.height = txrH;
  txr.width = Math.max( defTxrWidth, minW );
  txr.usedWidth = 0;
  txr.invalidatedWidth = 0;
  txr.fullnessChecks = 0;

  txr.canvas = document.createElement('canvas');
  txr.canvas.width = txr.width;
  txr.canvas.height = txr.height;

  txr.context = txr.canvas.getContext('2d');

  return txr;
};

ETCp.recycleTexture = function( txrH, minW ){
  var self = this;
  var txrQ = self.getTextureQueue( txrH );
  var rtxtrQ = self.getRetiredTextureQueue( txrH );

  for( var i = 0; i < rtxtrQ.length; i++ ){
    var txr = rtxtrQ[i];

    if( txr.width >= minW ){
      txr.retired = false;

      txr.usedWidth = 0;
      txr.invalidatedWidth = 0;
      txr.fullnessChecks = 0;

      util.clearArray( txr.eleCaches );

      txr.context.clearRect( 0, 0, txr.width, txr.height );

      util.removeFromArray( rtxtrQ, txr );
      txrQ.push( txr );

      return txr;
    }
  }
};

ETCp.queueElement = function( ele, bb, lvl ){
  var self = this;
  var q = self.getElementQueue();
  var id2q = self.getElementIdToQueue();
  var id = ele.id();
  var existingReq = id2q[ id ];

  if( existingReq ){ // use the max lvl b/c in between lvls are cheap to make
    existingReq.level = Math.max( existingReq.level, lvl );
    existingReq.reqs++;

    q.updateItem( existingReq );
  } else {
    var req = {
      ele: ele,
      bb: bb,
      level: lvl,
      reqs: 1
    };

    q.push( req );

    id2q[ id ] = req;
  }
};

ETCp.dequeue = function( pxRatio, extent ){
  var self = this;
  var q = self.getElementQueue();
  var id2q = self.getElementIdToQueue();
  var dequeued = [];

  for( var i = 0; i < maxDeqSize; i++ ){
    if( q.size() > 0 ){
      var req = q.pop();

      id2q[ req.ele.id() ] = null;

      dequeued.push( req );
      self.getElement( req.ele, req.bb, pxRatio, req.level, getTxrReasons.dequeue );
    } else {
      break;
    }
  }

  return dequeued;
};

ETCp.onDequeue = function( fn ){ this.onDequeues.push( fn ); };
ETCp.offDequeue = function( fn ){ util.removeFromArray( this.onDequeues, fn ); };

ETCp.setupDequeueing = defs.setupDequeueing({
  deqRedrawThreshold: deqRedrawThreshold,
  deqCost: deqCost,
  deqAvgCost: deqAvgCost,
  deqNoDrawCost: deqNoDrawCost,
  deqFastCost: deqFastCost,
  deq: function( self, pxRatio, extent ){
    return self.dequeue( pxRatio, extent );
  },
  onDeqd: function( self, deqd ){
    for( var i = 0; i < self.onDequeues.length; i++ ){
      var fn = self.onDequeues[i];

      fn( deqd );
    }
  },
  shouldRedraw: function( self, deqd, pxRatio, extent ){
    for( var i = 0; i < deqd.length; i++ ){
      var bb = deqd[i].bb;

      if( math.boundingBoxesIntersect( bb, extent ) ){
        return true;
      }
    }

    return false;
  }
});

module.exports = ElementTextureCache;
<commit_msg>downscaling tweaks<commit_after>'use strict';

var math = require( '../../../math' );
var util = require( '../../../util' );
var Heap = require( '../../../heap' );
var defs = require( './texture-cache-defs' );

// TODO round textures so that they always have integer sizes

// TODO optimise these values

var minTxrH = 25; // the size of the texture cache for small height eles (special case)
var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
var minLvl = -4; // when scaling smaller than that we don't need to re-render
var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
var defTxrWidth = 1024; // default/minimum texture width
var maxTxrW = 1024; // the maximum width of a texture
var maxTxrH = 1024;  // the maximum height of a texture
var minUtility = 0.5; // if usage of texture is less than this, it is retired
var maxFullness = 0.8; // fullness of texture after which queue removal is checked
var maxFullnessChecks = 10; // dequeued after this many checks
var allowEdgeTxrCaching = false; // whether edges can be cached as textures (TODO maybe better on if webgl supported?)
var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
var deqFastCost = 0.9; // % of frame time to be used when >60fps
var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch

var getTxrReasons = {
  dequeue: 'dequeue',
  downscale: 'downscale',
  highQuality: 'highQuality'
};

var ElementTextureCache = function( renderer ){
  var self = this;

  self.renderer = renderer;
  self.onDequeues = [];

  self.setupDequeueing();
};

var ETCp = ElementTextureCache.prototype;

ETCp.reasons = getTxrReasons;

// the list of textures in which new subtextures for elements can be placed
ETCp.getTextureQueue = function( txrH ){
  var self = this;
  self.eleImgCaches = self.eleImgCaches || {};

  return ( self.eleImgCaches[ txrH ] = self.eleImgCaches[ txrH ] || [] );
};

// the list of usused textures which can be recycled (in use in texture queue)
ETCp.getRetiredTextureQueue = function( txrH ){
  var self = this;

  var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
  var rtxtrQ = rtxtrQs[ txrH ] = rtxtrQs[ txrH ] || [];

  return rtxtrQ;
};

// queue of element draw requests at different scale levels
ETCp.getElementQueue = function(){
  var self = this;

  var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function( a, b ){
    return b.reqs - a.reqs;
  });

  return q;
};

// queue of element draw requests at different scale levels (element id lookup)
ETCp.getElementIdToQueue = function(){
  var self = this;

  var id2q = self.eleIdToCacheQueue = self.eleIdToCacheQueue || {};

  return id2q;
};

ETCp.getElement = function( ele, bb, pxRatio, lvl, reason ){
  var self = this;
  var r = this.renderer;
  var rs = ele._private.rscratch;
  var zoom = r.cy.zoom();

  if( lvl == null ){
    lvl = Math.ceil( math.log2( zoom * pxRatio ) );
  }

  if( lvl < minLvl ){
    lvl = minLvl;
  } else if( zoom >= maxZoom || lvl > maxLvl ){
    return null;
  }

  // if the visibility of labels changes with zoom, then we can't cache
  if( ele.pstyle('min-zoomed-font-size').pfValue !== 0 ){
    return null;
  }

  var scale = Math.pow( 2, lvl );
  var eleScaledH = bb.h * scale;
  var eleScaledW = bb.w * scale;
  var caches = rs.imgCaches = rs.imgCaches || {};
  var eleCache = caches[lvl];

  if( eleCache ){
    return eleCache;
  }

  var txrH; // which texture height this ele belongs to

  if( eleScaledH <= minTxrH ){
    txrH = minTxrH;
  } else if( eleScaledH <= txrStepH ){
    txrH = txrStepH;
  } else {
    txrH = Math.ceil( eleScaledH / txrStepH ) * txrStepH;
  }

  if( eleScaledH > maxTxrH || eleScaledW > maxTxrW || ( !allowEdgeTxrCaching && ele.isEdge() ) ){
    return null; // caching large elements is not efficient
  }

  var txrQ = self.getTextureQueue( txrH );

  // first try the second last one in case it has space at the end
  var txr = txrQ[ txrQ.length - 2 ];

  var addNewTxr = function(){
    return self.recycleTexture( txrH, eleScaledW ) || self.addTexture( txrH, eleScaledW );
  };

  // try the last one if there is no second last one
  if( !txr ){
    txr = txrQ[ txrQ.length - 1 ];
  }

  // if the last one doesn't exist, we need a first one
  if( !txr ){
    txr = addNewTxr();
  }

  // if there's no room in the current texture, we need a new one
  if( txr.width - txr.usedWidth < eleScaledW ){
    txr = addNewTxr();
  }

  var deqing = reason && reason === getTxrReasons.dequeue;
  var highQualityReq = reason && reason === getTxrReasons.highQuality;
  var downscaleReq = reason && reason === getTxrReasons.downscale;

  var higherCache; // the nearest cache with a higher level
  for( var l = lvl + 1; l <= maxLvl; l++ ){
    var c = caches[l];

    if( c ){ higherCache = c; break; }
  }

  var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;

  var downscale = function(){
    txr.context.drawImage(
      oneUpCache.texture.canvas,
      oneUpCache.x, 0,
      oneUpCache.width, oneUpCache.height,
      txr.usedWidth, 0,
      eleScaledW, eleScaledH
    );
  };

  if( oneUpCache ){
    // then we can relatively cheaply rescale the existing image w/o rerendering
    downscale();

  } else if( higherCache ){
    // then use the higher cache for now and queue the next level down
    // to cheaply scale towards the smaller level

    if( highQualityReq ){
      for( var l = higherCache.level; l > lvl; l-- ){
        oneUpCache = self.getElement( ele, bb, pxRatio, l, getTxrReasons.downscale );
      }

      downscale();

    } else {
      self.queueElement( ele, bb, higherCache.level - 1 );

      return higherCache;
    }
  } else {

    var lowerCache; // the nearest cache with a lower level
    if( !deqing && !highQualityReq && !downscaleReq ){
      for( var l = lvl - 1; l >= minLvl; l-- ){
        var c = caches[l];

        if( c ){ lowerCache = c; break; }
      }
    }

    if( lowerCache ){
      // then use the lower quality cache for now and queue the better one for later

      self.queueElement( ele, bb, lvl );

      return lowerCache;
    }

    txr.context.translate( txr.usedWidth, 0 );
    txr.context.scale( scale, scale );

    r.drawElement( txr.context, ele, bb );

    txr.context.scale( 1/scale, 1/scale );
    txr.context.translate( -txr.usedWidth, 0 );
  }

  eleCache = caches[lvl] = {
    ele: ele,
    x: txr.usedWidth,
    texture: txr,
    level: lvl,
    scale: scale,
    width: eleScaledW,
    height: eleScaledH
  };

  txr.usedWidth += Math.ceil( eleScaledW + eleTxrSpacing );

  txr.eleCaches.push( eleCache );

  self.checkTextureFullness( txr );

  return eleCache;
};

ETCp.invalidateElement = function( ele ){
  var self = this;
  var caches = ele._private.rscratch.imgCaches;

  if( caches ){
    for( var lvl = minLvl; lvl <= maxLvl; lvl++ ){
      var cache = caches[ lvl ];

      if( cache ){
        var txr = cache.texture;

        // remove space from the texture it belongs to
        txr.invalidatedWidth += cache.width;

        // remove refs with the element
        caches[ lvl ] = null;
        util.removeFromArray( txr.eleCaches, cache );

        // might have to remove the entire texture if it's not efficiently using its space
        self.checkTextureUtility( txr );
      }
    }
  }
};

ETCp.checkTextureUtility = function( txr ){
  // invalidate all entries in the cache if the cache size is small
  if( txr.invalidatedWidth >= minUtility * txr.width ){
    this.retireTexture( txr );
  }
};

ETCp.checkTextureFullness = function( txr ){
  // if texture has been mostly filled and passed over several times, remove
  // it from the queue so we don't need to waste time looking at it to put new things

  var self = this;
  var txrQ = self.getTextureQueue( txr.height );

  if( txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks ){
    util.removeFromArray( txrQ, txr );
  } else {
    txr.fullnessChecks++;
  }
};

ETCp.retireTexture = function( txr ){
  var self = this;
  var txrH = txr.height;
  var txrQ = self.getTextureQueue( txrH );

  // retire the texture from the active / searchable queue:

  util.removeFromArray( txrQ, txr );

  txr.retired = true;

  // remove the refs from the eles to the caches:

  var eleCaches = txr.eleCaches;

  for( var i = 0; i < eleCaches.length; i++ ){
    var eleCache = eleCaches[i];
    var ele = eleCache.ele;
    var lvl = eleCache.level;
    var imgCaches = ele._private.rscratch.imgCaches;

    if( imgCaches ){
      imgCaches[ lvl ] = null;
    }
  }

  util.clearArray( eleCaches );

  // add the texture to a retired queue so it can be recycled in future:

  var rtxtrQ = self.getRetiredTextureQueue( txrH );

  rtxtrQ.push( txr );
};

ETCp.addTexture = function( txrH, minW ){
  var self = this;
  var txrQ = self.getTextureQueue( txrH );
  var txr = {};

  txrQ.push( txr );

  txr.eleCaches = [];

  txr.height = txrH;
  txr.width = Math.max( defTxrWidth, minW );
  txr.usedWidth = 0;
  txr.invalidatedWidth = 0;
  txr.fullnessChecks = 0;

  txr.canvas = document.createElement('canvas');
  txr.canvas.width = txr.width;
  txr.canvas.height = txr.height;

  txr.context = txr.canvas.getContext('2d');

  return txr;
};

ETCp.recycleTexture = function( txrH, minW ){
  var self = this;
  var txrQ = self.getTextureQueue( txrH );
  var rtxtrQ = self.getRetiredTextureQueue( txrH );

  for( var i = 0; i < rtxtrQ.length; i++ ){
    var txr = rtxtrQ[i];

    if( txr.width >= minW ){
      txr.retired = false;

      txr.usedWidth = 0;
      txr.invalidatedWidth = 0;
      txr.fullnessChecks = 0;

      util.clearArray( txr.eleCaches );

      txr.context.clearRect( 0, 0, txr.width, txr.height );

      util.removeFromArray( rtxtrQ, txr );
      txrQ.push( txr );

      return txr;
    }
  }
};

ETCp.queueElement = function( ele, bb, lvl ){
  var self = this;
  var q = self.getElementQueue();
  var id2q = self.getElementIdToQueue();
  var id = ele.id();
  var existingReq = id2q[ id ];

  if( existingReq ){ // use the max lvl b/c in between lvls are cheap to make
    existingReq.level = Math.max( existingReq.level, lvl );
    existingReq.reqs++;

    q.updateItem( existingReq );
  } else {
    var req = {
      ele: ele,
      bb: bb,
      level: lvl,
      reqs: 1
    };

    q.push( req );

    id2q[ id ] = req;
  }
};

ETCp.dequeue = function( pxRatio, extent ){
  var self = this;
  var q = self.getElementQueue();
  var id2q = self.getElementIdToQueue();
  var dequeued = [];

  for( var i = 0; i < maxDeqSize; i++ ){
    if( q.size() > 0 ){
      var req = q.pop();

      id2q[ req.ele.id() ] = null;

      dequeued.push( req );
      self.getElement( req.ele, req.bb, pxRatio, req.level, getTxrReasons.dequeue );
    } else {
      break;
    }
  }

  return dequeued;
};

ETCp.onDequeue = function( fn ){ this.onDequeues.push( fn ); };
ETCp.offDequeue = function( fn ){ util.removeFromArray( this.onDequeues, fn ); };

ETCp.setupDequeueing = defs.setupDequeueing({
  deqRedrawThreshold: deqRedrawThreshold,
  deqCost: deqCost,
  deqAvgCost: deqAvgCost,
  deqNoDrawCost: deqNoDrawCost,
  deqFastCost: deqFastCost,
  deq: function( self, pxRatio, extent ){
    return self.dequeue( pxRatio, extent );
  },
  onDeqd: function( self, deqd ){
    for( var i = 0; i < self.onDequeues.length; i++ ){
      var fn = self.onDequeues[i];

      fn( deqd );
    }
  },
  shouldRedraw: function( self, deqd, pxRatio, extent ){
    for( var i = 0; i < deqd.length; i++ ){
      var bb = deqd[i].bb;

      if( math.boundingBoxesIntersect( bb, extent ) ){
        return true;
      }
    }

    return false;
  }
});

module.exports = ElementTextureCache;
<|endoftext|>


<commit_before>/*
 * index.js: Top-level include for the smart-private-npm for serving all CouchDB traffic.
 *
 * (C) 2013, Nodejitsu Inc.
 *
 */

var director = require('director');

var patterns = {
  packageAnd:  /\/([_\.\(\)!\\ %@&a-zA-Z0-9-]+)\/.*/,
  packageOnly: /\/([_\.\(\)!\\ %@&a-zA-Z0-9-]+)/,
  wildcard:    /\*$/
};

//
// Export the NpmProxy.
//
exports.Proxy = require('./npm-proxy');

//
// ### function routes (options, callback)
// #### @options {Object} Options for starting the proxy
// ####   @https     {Object}   HTTPS options.
// ####   @rewrites  {Array}    All known rewrites to the npm registry.
// ####   @proxy     {NpmProxy} Proxy for npm requests.
//
exports.createRouter = function createRouter(options, callback) {
  var router = new director.http.Router(),
      log    = options.log || console.log,
      proxy  = options.proxy,
      rewrites,
      server;

  //
  // Get a default set of rewrites if we weren't provided
  // with a custom set.
  //
  options.rewrites = options.rewrites || require('../config/rewrites');

  //
  // Configure director to always use async HTTP
  // stream based routing with a custom 404 handler.
  //
  router.configure({
    async: true,
    stream: true,
    notfound: function () {
      //
      // Pass along the not found notification to the proxy.
      //
      proxy.notFound(this.req, this.res);
    }
  });

  //
  // Remark: we will probably asynchronously load these things in some manner
  // but for now we just expect them
  //
  rewrites = {
    list: options.rewrites.filter(function (rr) {
      return /^_list/.test(rr.to)
        || /^\/-\/_view/.test(rr.from)
        || /^\/-\/_show/.test(rr.from);
    }),
    user: options.rewrites.filter(function (rr) {
      return /_users/.test(rr.to);
    })
  };

  //
  // `/`, `favicon.ico` and `_session` are special cases.
  // TODO: Add route for _utils to make futon hiding more seamless.
  //
  router.get('/',           function () { proxy.merge(this.req, this.res);  });
  router.get('favicon.ico', function () { proxy.public(this.req, this.res); });
  ['GET', 'PUT', 'POST', 'DELETE']
    .forEach(function (method) {
      router[method.toLowerCase()]('_session', function () {
        proxy.private(this.req, this.res);
      });
    });

  //
  // All _list/* functions require a merger between
  // public and private npm database views.
  //
  rewrites.list.forEach(function (rr) {
    var wc     = patterns.wildcard,
        method = rr.method.toLowerCase(),
        from   = wc.test(rr.from)
          ? new RegExp(rr.from.replace(wc, '.*'))
          : rr.from;

    log('[route] %s - %s %s', 'merge', method, rr.from);
    router[method](from, function () {
      proxy.merge(this.req, this.res);
    });
  });

  //
  // Your private npm must manually sync whatever passwords you want
  // since the public npm _user database is the authority for all that
  // is Open Source.
  //
  // Optimizing for ONE PASSWORD EVERYWHERE, ONE .npmrc EVERYWHERE.
  //
  rewrites.user.forEach(function (rr) {
    var method = rr.method.toLowerCase(),
        from   = rr.from;

    log('[route] %s - %s %s', 'private', method, from);
    router[method](from, function () {
      proxy.private(this.req, this.res);
    });
  });

  //
  // For core `/:pkg` and `/:pkg/*` routes use the more complex, higher-level
  // proxy logic.
  //
  [patterns.packageOnly, patterns.packageAnd].forEach(function (re) {
    ['GET', 'PUT', 'DELETE'].forEach(function (method) {
      method = method.toLowerCase();

      log('[route] %s - %s %s', 'decide', method, re.source);
      router[method](re, function () {
        proxy.decide(this.req, this.res);
      });
    });
  });

  router.proxy = proxy;
  return router;
};
<commit_msg>[fix] Proxy all user requests to the public registry since this is the central authority.<commit_after>/*
 * index.js: Top-level include for the smart-private-npm for serving all CouchDB traffic.
 *
 * (C) 2013, Nodejitsu Inc.
 *
 */

var director = require('director');

var patterns = {
  packageAnd:  /\/([_\.\(\)!\\ %@&a-zA-Z0-9-]+)\/.*/,
  packageOnly: /\/([_\.\(\)!\\ %@&a-zA-Z0-9-]+)/,
  wildcard:    /\*$/
};

//
// Export the NpmProxy.
//
exports.Proxy = require('./npm-proxy');

//
// ### function routes (options, callback)
// #### @options {Object} Options for starting the proxy
// ####   @https     {Object}   HTTPS options.
// ####   @rewrites  {Array}    All known rewrites to the npm registry.
// ####   @proxy     {NpmProxy} Proxy for npm requests.
//
exports.createRouter = function createRouter(options, callback) {
  var router = new director.http.Router(),
      log    = options.log || console.log,
      proxy  = options.proxy,
      rewrites,
      server;

  //
  // Get a default set of rewrites if we weren't provided
  // with a custom set.
  //
  options.rewrites = options.rewrites || require('../config/rewrites');

  //
  // Configure director to always use async HTTP
  // stream based routing with a custom 404 handler.
  //
  router.configure({
    async: true,
    stream: true,
    notfound: function () {
      //
      // Pass along the not found notification to the proxy.
      //
      proxy.notFound(this.req, this.res);
    }
  });

  //
  // Remark: we will probably asynchronously load these things in some manner
  // but for now we just expect them
  //
  rewrites = {
    list: options.rewrites.filter(function (rr) {
      return /^_list/.test(rr.to)
        || /^\/-\/_view/.test(rr.from)
        || /^\/-\/_show/.test(rr.from);
    }),
    user: options.rewrites.filter(function (rr) {
      return /_users/.test(rr.to);
    })
  };

  //
  // `/`, `favicon.ico` and `_session` are special cases.
  // TODO: Add route for _utils to make futon hiding more seamless.
  //
  router.get('/',           function () { proxy.merge(this.req, this.res);  });
  router.get('favicon.ico', function () { proxy.public(this.req, this.res); });
  ['GET', 'PUT', 'POST', 'DELETE']
    .forEach(function (method) {
      router[method.toLowerCase()]('_session', function () {
        proxy.private(this.req, this.res);
      });
    });

  //
  // All _list/* functions require a merger between
  // public and private npm database views.
  //
  rewrites.list.forEach(function (rr) {
    var wc     = patterns.wildcard,
        method = rr.method.toLowerCase(),
        from   = wc.test(rr.from)
          ? new RegExp(rr.from.replace(wc, '.*'))
          : rr.from;

    log('[route] %s - %s %s', 'merge', method, rr.from);
    router[method](from, function () {
      proxy.merge(this.req, this.res);
    });
  });

  //
  // Your private npm must manually sync whatever passwords you want
  // since the public npm _user database is the authority for all that
  // is Open Source.
  //
  // Optimizing for ONE PASSWORD EVERYWHERE, ONE .npmrc EVERYWHERE.
  //
  rewrites.user.forEach(function (rr) {
    var method = rr.method.toLowerCase(),
        from   = rr.from;

    log('[route] %s - %s %s', 'public', method, from);
    router[method](from, function () {
      proxy.public(this.req, this.res);
    });
  });

  //
  // For core `/:pkg` and `/:pkg/*` routes use the more complex, higher-level
  // proxy logic.
  //
  [patterns.packageOnly, patterns.packageAnd].forEach(function (re) {
    ['GET', 'PUT', 'DELETE'].forEach(function (method) {
      method = method.toLowerCase();

      log('[route] %s - %s %s', 'decide', method, re.source);
      router[method](re, function () {
        proxy.decide(this.req, this.res);
      });
    });
  });

  router.proxy = proxy;
  return router;
};
<|endoftext|>


<commit_before>// Load in dependencies
var gui = require('nw.gui');

// Generate a window for Google Music
// https://github.com/rogerwang/node-webkit/wiki/Window
// TODO: Can we change window.location for the same effect?
// TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
var googleWindow = window.open('https://play.google.com/music');
var $googleWindow = gui.Window.get(googleWindow);

// console.log(googleWindow.document.querySelector('button'));

// TODO: Once full-formed, break out class as a node module so it can be reused in browsers with no penalty
// TODO: Create class for managing application state that emits events
// TODO: Set up `tray.tooltip` to update on song change
// TODO: Add methods for play/pause/forward/next/shuffle/repeat that we can bind to menus and media keys
// TODO: Fill out package.json in full
// TODO: Add tray menu for all methods we want

// TODO: Inherit from EventEmitter
function GoogleMusic(win) {
  this.win = win;
}
GoogleMusic.prototype = {
  togglePlay: function () {

  }
};

// TODO: Look into icon for taskbar item https://github.com/rogerwang/node-webkit/wiki/Icons

// Set up minification
// Modified from https://github.com/rogerwang/node-webkit/wiki/Minimize-to-tray

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray
var trayMenu = new gui.Menu();
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    console.log('hai');
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, show the window
// TODO: Should be a toggle
tray.on('click', function maximizeFromTray () {
  $googleWindow.restore();
});

// When we minimize our window, hide it
$googleWindow.on('minimize', function minimizeToTray () {
  $googleWindow.minimize();
});
<commit_msg>Attempting to get playPause working<commit_after>// Load in dependencies
var gui = require('nw.gui');

// Generate a window for Google Music
// https://github.com/rogerwang/node-webkit/wiki/Window
// TODO: Can we change window.location for the same effect?
// TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
var googleWindow = window.open('https://play.google.com/music');
var $googleWindow = gui.Window.get(googleWindow);

// console.log(googleWindow.document.querySelector('button'));

// TODO: Once full-formed, break out class as a node module so it can be reused in browsers with no penalty
// TODO: Create class for managing application state that emits events
// TODO: Set up `tray.tooltip` to update on song change
// TODO: Add methods for play/pause/forward/next/shuffle/repeat that we can bind to menus and media keys
// TODO: Fill out package.json in full
// TODO: Add tray menu for all methods we want

// TODO: Inherit from EventEmitter
// Define a constructor to interact with a GoogleMusic window
function GoogleMusic(win) {
  this.win = win;
}
GoogleMusic.prototype = {
  playPause: function () {
    // Find and click the play pause button
    // DEV: We cannot cache this in the constructor because we can be behind login wall
    // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.click
    var $playPause = win.document.querySelector('button[data-id="play-pause"]');
    $playPause.click();
  }
};

// Create a GoogleMusic instance for our window
var googleMusic = new GoogleMusic(googleWindow);

// TODO: Look into icon for taskbar item https://github.com/rogerwang/node-webkit/wiki/Icons

// Set up minification
// Modified from https://github.com/rogerwang/node-webkit/wiki/Minimize-to-tray

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    googleMusic.playPause();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, show the window
// TODO: Should be a toggle
tray.on('click', function maximizeFromTray () {
  $googleWindow.restore();
});

// When we minimize our window, hide it
$googleWindow.on('minimize', function minimizeToTray () {
  $googleWindow.minimize();
});
<|endoftext|>


<commit_before>"use strict";
var util = require('util'),
  Product = require('./model/product'),
  ApiCore = require('facet-core').ApiCore;

/**
 * Product API constructor
 *
 * @param   {Object}  options   Options object - must contain 'db' (mongoose instance)
 *                              and 'intercom' (EventEmitter instance) keys.
 *
 * @return  {void}
 */
var ProductAPI = function ( options ){

  // set the options
  this.setCommonAttributes( options );

  // set the model
  this.Model = new Product( this.options );

  // set the router for automation
  this.setupRouterManifest();

  // register the api events
  this.registerEvents();
};

/**
 * Product API inherits from Core API
 */
util.inherits(ProductAPI, ApiCore);

/**
 * Sets up the router manifest for route automation
 *
 * @return   {void}
 */
ProductAPI.prototype.setupRouterManifest = function () {

  // update the router manifest
  this.routerManifest
    .setApiEventType('product')
    .setApiModelId('productId')
    .setRouteBase('/products')
    .setRoutes([
      { verb: 'GET',    route: '/:productId', emit: 'facet:product:findone' },  // GET a single Product by id
      { verb: 'GET',    route: '',            emit: 'facet:product:find'    },  // GET an array of Product objects
      { verb: 'POST',   route: '',            emit: 'facet:product:create'  },  // POST new Product
      { verb: 'PUT',    route: '/:productId', emit: 'facet:product:update'  },  // PUT single/multiple Products
      { verb: 'DELETE', route: '/:productId', emit: 'facet:product:delete'  },  // DELETE a single Product resource
    ])
    .extendRouteErrorMessages({
      container: 'The API user does not have a valid container id.',
      conditions: 'No query conditions were specified',
      query: 'Error querying for product(s): ',
      notFound: 'No product was found.',
      find: 'No product(s) matched your criteria.',
      findOne: 'No product matched your criteria.',
      update: 'No updates were specified.',
      updateMatch: 'No products were updated based on your criteria.',
      create: 'No data supplied for creating new product.',
      createMatch: 'No product was created based on your criteria.',
      remove: 'No data supplied for removing product.',
      removeMatch: 'No product was removed based on your criteria.'
    });
};

/**
 * Registers the Product API event listeners
 *
 * @return   {void}
 */
ProductAPI.prototype.registerEvents = function () {
  var _this = this;

  this.intercom.on('facet:product:data', function handleProductData( data, nodeStack ) {
    data.then( function( productData ) {
      if( null === productData ){
        _this.intercom.emit('facet:response:error', 404, 'Product was not found.');
      }
      else {
        _this.intercom.emit('facet:response:product:data', productData);
      }
    },
    function( err ) {
      _this.intercom.emit('facet:response:error', 404, 'Error querying for product(s): ' + err.message);
    }).end();
  });
  this.intercom.on( 'facet:product:find',     this.find.bind(this)    );
  this.intercom.on( 'facet:product:findone',  this.findOne.bind(this) );
  this.intercom.on( 'facet:product:create',   this.create.bind(this)  );
  this.intercom.on( 'facet:product:update',   this.update.bind(this)  );
  this.intercom.on( 'facet:product:remove',   this.remove.bind(this)  );
};

/**
 * Exports the Product API
 *
 * @type   {Object}
 */
exports = module.exports = ProductAPI;
<commit_msg>calling parent ctor<commit_after>"use strict";
var util = require('util'),
  Product = require('./model/product'),
  ApiCore = require('facet-core').ApiCore;

/**
 * Product API constructor
 *
 * @param   {Object}  options   Options object - must contain 'db' (mongoose instance)
 *                              and 'intercom' (EventEmitter instance) keys.
 *
 * @return  {void}
 */
var ProductAPI = function ( options ){

  // set the options
  // this.setCommonAttributes( options );

  ProductAPI.super_.call(this, options);

  // set the model
  this.Model = new Product( this.options );

  // set the router for automation
  this.setupRouterManifest();

  // register the api events
  this.registerEvents();
};

/**
 * Product API inherits from Core API
 */
util.inherits(ProductAPI, ApiCore);

/**
 * Sets up the router manifest for route automation
 *
 * @return   {void}
 */
ProductAPI.prototype.setupRouterManifest = function () {

  // update the router manifest
  this.routerManifest
    .setApiEventType('product')
    .setApiModelId('productId')
    .setRouteBase('/products')
    .setRoutes([
      { verb: 'GET',    route: '/:productId', emit: 'facet:product:findone' },  // GET a single Product by id
      { verb: 'GET',    route: '',            emit: 'facet:product:find'    },  // GET an array of Product objects
      { verb: 'POST',   route: '',            emit: 'facet:product:create'  },  // POST new Product
      { verb: 'PUT',    route: '/:productId', emit: 'facet:product:update'  },  // PUT single/multiple Products
      { verb: 'DELETE', route: '/:productId', emit: 'facet:product:delete'  },  // DELETE a single Product resource
    ])
    .extendRouteErrorMessages({
      container: 'The API user does not have a valid container id.',
      conditions: 'No query conditions were specified',
      query: 'Error querying for product(s): ',
      notFound: 'No product was found.',
      find: 'No product(s) matched your criteria.',
      findOne: 'No product matched your criteria.',
      update: 'No updates were specified.',
      updateMatch: 'No products were updated based on your criteria.',
      create: 'No data supplied for creating new product.',
      createMatch: 'No product was created based on your criteria.',
      remove: 'No data supplied for removing product.',
      removeMatch: 'No product was removed based on your criteria.'
    });
};

/**
 * Registers the Product API event listeners
 *
 * @return   {void}
 */
ProductAPI.prototype.registerEvents = function () {
  var _this = this;

  this.intercom.on('facet:product:data', function handleProductData( data, nodeStack ) {
    data.then( function( productData ) {
      if( null === productData ){
        _this.intercom.emit('facet:response:error', 404, 'Product was not found.');
      }
      else {
        _this.intercom.emit('facet:response:product:data', productData);
      }
    },
    function( err ) {
      _this.intercom.emit('facet:response:error', 404, 'Error querying for product(s): ' + err.message);
    }).end();
  });
  this.intercom.on( 'facet:product:find',     this.find.bind(this)    );
  this.intercom.on( 'facet:product:findone',  this.findOne.bind(this) );
  this.intercom.on( 'facet:product:create',   this.create.bind(this)  );
  this.intercom.on( 'facet:product:update',   this.update.bind(this)  );
  this.intercom.on( 'facet:product:remove',   this.remove.bind(this)  );
};

/**
 * Exports the Product API
 *
 * @type   {Object}
 */
exports = module.exports = ProductAPI;
<|endoftext|>


<commit_before>(function() {
  (function(factory) {
    if (typeof exports === "object") {
      return module.exports = factory(require("madLib-console"), require("madlib-settings"), require("madLib-object-utils"));
    } else if (typeof define === "function" && define.amd) {
      return define(["madLib-console", "madlib-settings", "madLib-object-utils"], factory);
    }
  })(function(console, settings, objectUtils) {
    var HostMapping;
    settings.init("hostConfig", {});
    settings.init("hostMapping", {});
    settings.init("xdmConfig", {});
    HostMapping = (function() {
      function HostMapping() {}

      HostMapping.prototype.overrideMapping = function(newMapping) {
        return settings.set("currentHostMapping", newMapping);
      };

      HostMapping.prototype.determineTarget = function(hostname) {
        var allHostMappings, overrideMapping;
        overrideMapping = settings.get("overrideMapping");
        if (overrideMapping != null) {
          settings.set("currentHostMapping", overrideMapping);
          return console.log("[hostMapping] Override active: " + settings.overrideMapping);
        } else {
          allHostMappings = settings.get("hostMapping");
          if ((hostname == null) && (typeof document !== "undefined" && document !== null)) {
            if (typeof document !== "undefined" && document !== null) {
              hostname = document.location.hostname;
            }
          }
          if (allHostMappings[hostname] != null) {
            settings.set("currentHostMapping", allHostMappings[hostname]);
            return console.log("[hostMapping] Target found: " + settings.currentMapping);
          } else {
            settings.set("currentHostMapping", "production");
            return console.log("[hostMapping] No target found, defaulting to production");
          }
        }
      };

      HostMapping.prototype.getHostName = function(hostType) {
        var allHostConfigs, currentHostMapping;
        if (hostType == null) {
          hostType = "api";
        }
        if (settings.get("currentHostMapping") == null) {
          this.determineTarget();
        }
        allHostConfigs = settings.get("hostConfig");
        currentHostMapping = settings.get("currentHostMapping");
        return objectUtils.getValue("" + currentHostMapping + "." + hostType, allHostConfigs);
      };

      HostMapping.prototype.getXdmSettings = function(hostName) {
        var allXdmConfigs, cleanHostName;
        cleanHostName = this.extractHostName(hostName);
        allXdmConfigs = settings.get("xdmConfig");
        return allXdmConfigs[cleanHostName];
      };

      HostMapping.prototype.extractHostName = function(url) {
        return url.replace(/(^https?:)?\/\//, "").split("/").slice(0, 1).pop().split(":").slice(0, 1).pop();
      };

      return HostMapping;

    })();
    return new HostMapping();
  });

}).call(this);
<commit_msg>Updated dist file<commit_after>(function() {
  (function(factory) {
    if (typeof exports === "object") {
      return module.exports = factory(require("madLib-console"), require("madLib-object-utils"));
    } else if (typeof define === "function" && define.amd) {
      return define(["madLib-console", "madLib-object-utils"], factory);
    }
  })(function(console, objectUtils) {
    var HostMapping;
    return HostMapping = (function() {
      function HostMapping(settings) {
        settings.init("hostConfig", {});
        settings.init("hostMapping", {});
        settings.init("xdmConfig", {});
        this.settings = settings;
      }

      HostMapping.prototype.overrideMapping = function(newMapping) {
        return this.settings.set("currentHostMapping", newMapping);
      };

      HostMapping.prototype.determineTarget = function(hostname) {
        var allHostMappings, overrideMapping;
        overrideMapping = this.settings.get("overrideMapping");
        if (overrideMapping != null) {
          this.settings.set("currentHostMapping", overrideMapping);
          return console.log("[hostMapping] Override active: " + this.settings.overrideMapping);
        } else {
          allHostMappings = this.settings.get("hostMapping");
          if ((hostname == null) && (typeof document !== "undefined" && document !== null)) {
            if (typeof document !== "undefined" && document !== null) {
              hostname = document.location.hostname;
            }
          }
          if (allHostMappings[hostname] != null) {
            this.settings.set("currentHostMapping", allHostMappings[hostname]);
            return console.log("[hostMapping] Target found: " + this.settings.currentMapping);
          } else {
            this.settings.set("currentHostMapping", "production");
            return console.log("[hostMapping] No target found, defaulting to production");
          }
        }
      };

      HostMapping.prototype.getHostName = function(hostType) {
        var allHostConfigs, currentHostMapping;
        if (hostType == null) {
          hostType = "api";
        }
        if (this.settings.get("currentHostMapping") == null) {
          this.determineTarget();
        }
        allHostConfigs = this.settings.get("hostConfig");
        currentHostMapping = this.settings.get("currentHostMapping");
        return objectUtils.getValue("" + currentHostMapping + "." + hostType, allHostConfigs);
      };

      HostMapping.prototype.getXdmSettings = function(hostName) {
        var allXdmConfigs, cleanHostName;
        cleanHostName = this.extractHostName(hostName);
        allXdmConfigs = this.settings.get("xdmConfig");
        return allXdmConfigs[cleanHostName];
      };

      HostMapping.prototype.extractHostName = function(url) {
        return url.replace(/(^https?:)?\/\//, "").split("/").slice(0, 1).pop().split(":").slice(0, 1).pop();
      };

      return HostMapping;

    })();
  });

}).call(this);
<|endoftext|>


<commit_before>/*
 Welcome to the MongoDB connector!
 */
var _ = require('lodash');

/**
 * Creates the MongoDB connector for Arrow.
 */
exports.create = function (Arrow) {
	var Connector = Arrow.Connector,
		Capabilities = Connector.Capabilities;

	return Connector.extend({
		filename: module.filename,
		capabilities: [
			Capabilities.ConnectsToADataSource,
			Capabilities.ValidatesConfiguration,
			//Capabilities.ContainsModels,
			Capabilities.GeneratesModels,
			Capabilities.CanCreate,
			Capabilities.CanRetrieve,
			Capabilities.CanUpdate,
			Capabilities.CanDelete,
			Capabilities.wireModelAuthenticatesThroughConnector
		],

		// Looks through a query "where" for $like and $notLike values that can be translated to $regex strings.
		translateWhereRegex: true
	});
};
<commit_msg>Fix Capability<commit_after>/*
 Welcome to the MongoDB connector!
 */
var _ = require('lodash');

/**
 * Creates the MongoDB connector for Arrow.
 */
exports.create = function (Arrow) {
	var Connector = Arrow.Connector,
		Capabilities = Connector.Capabilities;

	return Connector.extend({
		filename: module.filename,
		capabilities: [
			Capabilities.ConnectsToADataSource,
			Capabilities.ValidatesConfiguration,
			//Capabilities.ContainsModels,
			Capabilities.GeneratesModels,
			Capabilities.CanCreate,
			Capabilities.CanRetrieve,
			Capabilities.CanUpdate,
			Capabilities.CanDelete,
			Capabilities.AuthenticatesThroughConnector
		],

		// Looks through a query "where" for $like and $notLike values that can be translated to $regex strings.
		translateWhereRegex: true
	});
};
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var CSSBlendModes = require('./CSSBlendModes');
var GameObject = require('../GameObject');
var TransformMatrix = require('../components/TransformMatrix');

var tempMatrix1 = new TransformMatrix();
var tempMatrix2 = new TransformMatrix();
var tempMatrix3 = new TransformMatrix();

/**
 * Renders this Game Object with the WebGL Renderer to the given Camera.
 * The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera.
 * This method should not be called directly. It is a utility function of the Render module.
 *
 * @method Phaser.GameObjects.DOMElement#renderWebGL
 * @since 3.17.0
 * @private
 *
 * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active renderer.
 * @param {Phaser.GameObjects.DOMElement} src - The Game Object being rendered in this call.
 * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
 * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested
 */
var DOMElementCSSRenderer = function (renderer, src, camera, parentMatrix)
{
    var node = src.node;
    var style = node.style;
    var settings = src.scene.sys.settings;

    if (!node || !style || !settings.visible || GameObject.RENDER_MASK !== src.renderFlags || (src.cameraFilter !== 0 && (src.cameraFilter & camera.id)) || (src.parentContainer && !src.parentContainer.willRender()))
    {
        if (node)
        {
            style.display = 'none';
        }

        return;
    }

    var parent = src.parentContainer;
    var alpha = camera.alpha * src.alpha;

    if (parent)
    {
        alpha *= parent.alpha;
    }

    var camMatrix = tempMatrix1;
    var srcMatrix = tempMatrix2;
    var calcMatrix = tempMatrix3;

    var dx = 0;
    var dy = 0;

    var tx = '0%';
    var ty = '0%';

    if (parentMatrix)
    {
        dx = (src.width * src.scaleX) * src.originX;
        dy = (src.height * src.scaleY) * src.originY;

        srcMatrix.applyITRS(src.x - dx, src.y - dy, src.rotation, src.scaleX, src.scaleY);

        camMatrix.copyFrom(camera.matrix);

        //  Multiply the camera by the parent matrix
        camMatrix.multiplyWithOffset(parentMatrix, -camera.scrollX * src.scrollFactorX, -camera.scrollY * src.scrollFactorY);

        //  Undo the camera scroll
        srcMatrix.e = src.x - dx;
        srcMatrix.f = src.y - dy;

        //  Multiply by the src matrix, store result in calcMatrix
        camMatrix.multiply(srcMatrix, calcMatrix);
    }
    else
    {
        dx = (src.width) * src.originX;
        dy = (src.height) * src.originY;

        srcMatrix.applyITRS(src.x - dx, src.y - dy, src.rotation, src.scaleX, src.scaleY);

        camMatrix.copyFrom(camera.matrix);

        tx = (100 * src.originX) + '%';
        ty = (100 * src.originY) + '%';

        srcMatrix.e -= camera.scrollX * src.scrollFactorX;
        srcMatrix.f -= camera.scrollY * src.scrollFactorY;

        //  Multiply by the src matrix, store result in calcMatrix
        camMatrix.multiply(srcMatrix, calcMatrix);
    }

    if (!src.transformOnly)
    {
        style.display = 'block';
        style.opacity = alpha;
        style.zIndex = src._depth;
        style.pointerEvents = 'auto';
        style.mixBlendMode = CSSBlendModes[src._blendMode];
    }

    // https://developer.mozilla.org/en-US/docs/Web/CSS/transform

    style.transform =
        calcMatrix.getCSSMatrix() +
        ' skew(' + src.skewX + 'rad, ' + src.skewY + 'rad)' +
        ' rotate3d(' + src.rotate3d.x + ',' + src.rotate3d.y + ',' + src.rotate3d.z + ',' + src.rotate3d.w + src.rotate3dAngle + ')';

    style.transformOrigin = tx + ' ' + ty;
};

module.exports = DOMElementCSSRenderer;
<commit_msg>`DOMElementCSSRenderer` will now return early if `src.node` doesn't exist or is null, rather than trying to extract the `style` property from it. Fix #5566<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var CSSBlendModes = require('./CSSBlendModes');
var GameObject = require('../GameObject');
var TransformMatrix = require('../components/TransformMatrix');

var tempMatrix1 = new TransformMatrix();
var tempMatrix2 = new TransformMatrix();
var tempMatrix3 = new TransformMatrix();

/**
 * Renders this Game Object with the WebGL Renderer to the given Camera.
 * The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera.
 * This method should not be called directly. It is a utility function of the Render module.
 *
 * @method Phaser.GameObjects.DOMElement#renderWebGL
 * @since 3.17.0
 * @private
 *
 * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active renderer.
 * @param {Phaser.GameObjects.DOMElement} src - The Game Object being rendered in this call.
 * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
 * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested
 */
var DOMElementCSSRenderer = function (renderer, src, camera, parentMatrix)
{
    if (!src.node)
    {
        return;
    }

    var style = src.node.style;
    var settings = src.scene.sys.settings;

    if (!style || !settings.visible || GameObject.RENDER_MASK !== src.renderFlags || (src.cameraFilter !== 0 && (src.cameraFilter & camera.id)) || (src.parentContainer && !src.parentContainer.willRender()))
    {
        style.display = 'none';

        return;
    }

    var parent = src.parentContainer;
    var alpha = camera.alpha * src.alpha;

    if (parent)
    {
        alpha *= parent.alpha;
    }

    var camMatrix = tempMatrix1;
    var srcMatrix = tempMatrix2;
    var calcMatrix = tempMatrix3;

    var dx = 0;
    var dy = 0;

    var tx = '0%';
    var ty = '0%';

    if (parentMatrix)
    {
        dx = (src.width * src.scaleX) * src.originX;
        dy = (src.height * src.scaleY) * src.originY;

        srcMatrix.applyITRS(src.x - dx, src.y - dy, src.rotation, src.scaleX, src.scaleY);

        camMatrix.copyFrom(camera.matrix);

        //  Multiply the camera by the parent matrix
        camMatrix.multiplyWithOffset(parentMatrix, -camera.scrollX * src.scrollFactorX, -camera.scrollY * src.scrollFactorY);

        //  Undo the camera scroll
        srcMatrix.e = src.x - dx;
        srcMatrix.f = src.y - dy;

        //  Multiply by the src matrix, store result in calcMatrix
        camMatrix.multiply(srcMatrix, calcMatrix);
    }
    else
    {
        dx = (src.width) * src.originX;
        dy = (src.height) * src.originY;

        srcMatrix.applyITRS(src.x - dx, src.y - dy, src.rotation, src.scaleX, src.scaleY);

        camMatrix.copyFrom(camera.matrix);

        tx = (100 * src.originX) + '%';
        ty = (100 * src.originY) + '%';

        srcMatrix.e -= camera.scrollX * src.scrollFactorX;
        srcMatrix.f -= camera.scrollY * src.scrollFactorY;

        //  Multiply by the src matrix, store result in calcMatrix
        camMatrix.multiply(srcMatrix, calcMatrix);
    }

    if (!src.transformOnly)
    {
        style.display = 'block';
        style.opacity = alpha;
        style.zIndex = src._depth;
        style.pointerEvents = 'auto';
        style.mixBlendMode = CSSBlendModes[src._blendMode];
    }

    // https://developer.mozilla.org/en-US/docs/Web/CSS/transform

    style.transform =
        calcMatrix.getCSSMatrix() +
        ' skew(' + src.skewX + 'rad, ' + src.skewY + 'rad)' +
        ' rotate3d(' + src.rotate3d.x + ',' + src.rotate3d.y + ',' + src.rotate3d.z + ',' + src.rotate3d.w + src.rotate3dAngle + ')';

    style.transformOrigin = tx + ' ' + ty;
};

module.exports = DOMElementCSSRenderer;
<|endoftext|>


<commit_before>/*"""
=========================================================
Universe View
=========================================================
*/
'use strict';

require.config({
    shim: {
        'nbextensions/exa/container': {
            exports: 'container'
        },

        'nbextensions/exa/atomic/app': {
            exports: 'app'
        }
    },
});

define([
    'widgets/js/widget',
    'nbextensions/exa/container',
    'nbextensions/exa/atomic/app',
], function(widget, container, app){
    var UniverseView = container.ContainerView.extend({
        /*"""
        UniverseView
        ====================
        Frontend representation of an atomic universe instance.
        */
        render: function() {
            /*"""
            render
            --------------
            Main entry point for the universe container frontend.
            */
            console.log('Initializing universe...');
            var self = this;
        },
    });

    return {'UniverseView': UniverseView};
});
<commit_msg>atom_ visualization JS<commit_after>/*"""
=========================================================
Universe View
=========================================================
*/
'use strict';

require.config({
    shim: {
        'nbextensions/exa/container': {
            exports: 'container'
        },

        'nbextensions/exa/atomic/app': {
            exports: 'app'
        }
    },
});

define([
    'widgets/js/widget',
    'nbextensions/exa/container',
    'nbextensions/exa/atomic/app',
], function(widget, container, app){
    var UniverseView = container.ContainerView.extend({
        /*"""
        UniverseView
        ====================
        Frontend representation of an atomic universe instance.
        */
        render: function() {
            /*"""
            render
            --------------
            Main entry point for the universe container frontend.
            */
            console.log('Initializing universe...');
            var self = this;
            this.model.on('change:atom_x', this.update_x, this);
            this.model.on('change:atom_y', this.update_y, this);
            this.model.on('change:atom_z', this.update_z, this);
            this.update_atom_x();
            this.update_atom_y();
            this.update_atom_z();
            console.log(this.atom_x);
            console.log(this.atom_y);
            console.log(this.atom_z);
            if (this.atom_z.length == undefined) {
                console.log('true');
            } else {
                console.log('false');
            };
        },

        update_atom_x: function() {
            /*"""
            update_atom_x
            -----------
            Updates x component of nuclear coordinates.
            */
            this.atom_x = this.get_trait('atom_x');
        },

        update_atom_y: function() {
            /*"""
            update_atom_y
            -----------
            Updates y component of nuclear coordinates.
            */
            this.atom_y = this.get_trait('atom_y');
        },

        update_atom_z: function() {
            /*"""
            update_atom_z
            -----------
            Updates z component of nuclear coordinates.
            */
            this.atom_z = this.get_trait('atom_z');
        },
    });

    return {'UniverseView': UniverseView};
});
<|endoftext|>


<commit_before>// Load in dependencies
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var gui = require('nw.gui');
var util = require('util');
var extend = require('obj-extend');
var forwardEvents = require('forward-events');
var GoogleMusic = require('./google-music');

// TODO: Fill out package.json in full
// TODO: Add play/pause icons for tray menu items
// TODO: Add/remove tray menu items when we leave (should be reused though for memory leaksO

// Define constructor for internalizing state
function GoogleMusicWebkit() {
  // Load up EventEmitter
  var that = this;
  EventEmitter.call(this);

  // Save app for later
  this.app = gui.App;

  // Generate a window for Google Music
  // https://github.com/rogerwang/node-webkit/wiki/Window
  // TODO: Can we change window.location for the same effect?
  // TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
  this.googleWindow = window.open('https://play.google.com/music');
  this.googleWebkit = gui.Window.get(this.googleWindow);

  // When we close the googleWebkit window, close the app
  // https://github.com/rogerwang/node-webkit/wiki/Window#closed
  this.googleWebkit.on('closed', function closeApp () {
    that.quit();
  });

  // By default, the window is visible (corresponds to `minimize` events and `toggleVisibility`)
  this.isVisible = true;

  // When the window is minimized, record the update
  this.googleWebkit.on('minimize', function handleMinimizeEvent () {
    that.isVisible = false;
  });

  // When our url changes
  this.googleWebkit.on('loaded', function () {
    // If we have arrived to a logged in state in Google Music
    var loc = that.googleWindow.location;
    if (loc.href.indexOf('//play.google.com/music') !== -1 && loc.hash !== '') {
      that.initMusic();
    // If we have left Google Music (e.g. logged out), destroy our music instance
    } else {
      that.destroyMusic();
    }
  });
}
util.inherits(GoogleMusicWebkit, EventEmitter);
extend(GoogleMusicWebkit.prototype, {
  // init/destroy first, then alphabetical
  initMusic: function () {
    // If we need to add scripts
    if (!this.googleMusic) {
      // Bind to the window
      this.googleMusic = new GoogleMusic(this.googleWindow);

      // Forward all events
      forwardEvents(this.googleMusic, this);

      // Emit an event
      this.emit('init-music');
    }
  },
  destroyMusic: function () {
    // If there is music, remove clean it up
    if (this.googleMusic) {
      this.googleMusic.removeAllListeners();
    }
    this.googleMusic = null;

    // Emit an event
    this.emit('destroy-music');
  },

  // ALPHABETICAL ORDER STARTS HERE

  playPause: function () {
    // TODO: If we are not logged in, prompt for login
    if (this.googleMusic) {
      this.googleMusic.playPause();
    }
  },
  next: function () {
    if (this.googleMusic) {
      this.googleMusic.forward();
    }
  },
  previous: function () {
    if (this.googleMusic) {
      this.googleMusic.rewind();
    }
  },

  toggleVisibility: function () {
    // If it is visible, hide it
    if (this.isVisible) {
      this.googleWebkit.minimize();
      this.isVisible = false;
    // Otherwise, show it
    } else {
      this.googleWebkit.restore();
      this.isVisible = true;
    }

    // TODO: Emit a visibility change event (requires handling `minimize` event)
  },
  quit: function () {
    this.app.quit();
  }
});

// Create our webkit
var gmw = new GoogleMusicWebkit();

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
// TODO: Add/remove play/pause depending on if music is playing or not
trayMenu.append(new gui.MenuItem({
  label: 'Show/Hide window',
  click: function () {
    gmw.toggleVisibility();
  }
}));
trayMenu.append(new gui.MenuItem({
  type: 'separator'
}));
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    gmw.playPause();
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Next',
  click: function () {
    gmw.next();
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Previous',
  click: function () {
    gmw.previous();
  }
}));
trayMenu.append(new gui.MenuItem({
  type: 'separator'
}));
trayMenu.append(new gui.MenuItem({
  label: 'Quit',
  click: function () {
    gmw.quit();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, toggle visibility
tray.on('click', function maximizeFromTray () {
  gmw.toggleVisibility();
});

// When the song changes, update the tooltip
// Data looks like: {title, artist, album, art, duration}
gmw.on('song:changed', function (data) {
  tray.tooltip = [
    'Title: ' + data.title,
    'Artist: ' + data.artist,
    'Album: ' + data.album,
  ].join('\n');
});
gmw.on('destroy-music', function () {
  delete tray.tooltip;
});
<commit_msg>Moved google music instance reading from window to prevent double binding but also persistance on reload<commit_after>// Load in dependencies
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var gui = require('nw.gui');
var util = require('util');
var extend = require('obj-extend');
var forwardEvents = require('forward-events');
var GoogleMusic = require('./google-music');

// TODO: Fill out package.json in full
// TODO: Add play/pause icons for tray menu items
// TODO: Add/remove tray menu items when we leave (should be reused though for memory leaksO

// Define constructor for internalizing state
function GoogleMusicWebkit() {
  // Load up EventEmitter
  var that = this;
  EventEmitter.call(this);

  // Save app for later
  this.app = gui.App;

  // Generate a window for Google Music
  // https://github.com/rogerwang/node-webkit/wiki/Window
  // TODO: Can we change window.location for the same effect?
  // TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
  this.googleWindow = window.open('https://play.google.com/music');
  this.googleWebkit = gui.Window.get(this.googleWindow);

  // When we close the googleWebkit window, close the app
  // https://github.com/rogerwang/node-webkit/wiki/Window#closed
  this.googleWebkit.on('closed', function closeApp () {
    that.quit();
  });

  // By default, the window is visible (corresponds to `minimize` events and `toggleVisibility`)
  this.isVisible = true;

  // When the window is minimized, record the update
  this.googleWebkit.on('minimize', function handleMinimizeEvent () {
    that.isVisible = false;
  });

  // When our url changes
  this.googleWebkit.on('loaded', function () {
    // If we have arrived to a logged in state in Google Music
    var loc = that.googleWindow.location;
    if (loc.href.indexOf('//play.google.com/music') !== -1 && loc.hash !== '') {
      that.initMusic();
    // If we have left Google Music (e.g. logged out), destroy our music instance
    } else {
      that.destroyMusic();
    }
  });
}
util.inherits(GoogleMusicWebkit, EventEmitter);
extend(GoogleMusicWebkit.prototype, {
  // init/destroy first, then alphabetical
  initMusic: function () {
    // If we need to add scripts
    this.googleMusic = this.googleWindow.googleMusic;
    if (!this.googleMusic) {
      // Bind to the window
      this.googleMusic = this.googleWindow.googleMusic = new GoogleMusic(this.googleWindow);

      // Forward all events
      forwardEvents(this.googleMusic, this);

      // Emit an event
      this.emit('init-music');
    }
  },
  destroyMusic: function () {
    // If there is music, remove clean it up
    if (this.googleMusic) {
      this.googleMusic.removeAllListeners();
    }
    delete this.googleMusic;
    delete this.googleWindow.googleMusic;

    // Emit an event
    this.emit('destroy-music');
  },

  // ALPHABETICAL ORDER STARTS HERE

  playPause: function () {
    // TODO: If we are not logged in, prompt for login
    if (this.googleMusic) {
      this.googleMusic.playPause();
    }
  },
  next: function () {
    if (this.googleMusic) {
      this.googleMusic.forward();
    }
  },
  previous: function () {
    if (this.googleMusic) {
      this.googleMusic.rewind();
    }
  },

  toggleVisibility: function () {
    // If it is visible, hide it
    if (this.isVisible) {
      this.googleWebkit.minimize();
      this.isVisible = false;
    // Otherwise, show it
    } else {
      this.googleWebkit.restore();
      this.isVisible = true;
    }

    // TODO: Emit a visibility change event (requires handling `minimize` event)
  },
  quit: function () {
    this.app.quit();
  }
});

// Create our webkit
var gmw = new GoogleMusicWebkit();

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
// TODO: Add/remove play/pause depending on if music is playing or not
trayMenu.append(new gui.MenuItem({
  label: 'Show/Hide window',
  click: function () {
    gmw.toggleVisibility();
  }
}));
trayMenu.append(new gui.MenuItem({
  type: 'separator'
}));
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    gmw.playPause();
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Next',
  click: function () {
    gmw.next();
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Previous',
  click: function () {
    gmw.previous();
  }
}));
trayMenu.append(new gui.MenuItem({
  type: 'separator'
}));
trayMenu.append(new gui.MenuItem({
  label: 'Quit',
  click: function () {
    gmw.quit();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, toggle visibility
tray.on('click', function maximizeFromTray () {
  gmw.toggleVisibility();
});

// When the song changes, update the tooltip
// Data looks like: {title, artist, album, art, duration}
gmw.on('song:changed', function (data) {
  tray.tooltip = [
    'Title: ' + data.title,
    'Artist: ' + data.artist,
    'Album: ' + data.album,
  ].join('\n');
});
gmw.on('destroy-music', function () {
  delete tray.tooltip;
});
<|endoftext|>


<commit_before>import request from 'request-promise';
import { fs } from 'appium-support';
import logger from './logger';

async function registerNode (configFile, addr, port) {
  let data;
  try {
    data = await fs.readFile(configFile, 'utf-8');
  } catch (err) {
    logger.error(`Unable to load node configuration file to register with grid: ${err.message}`);
    return;
  }

  // Check presence of data before posting  it to the selenium grid
  if (!data) {
    logger.error('No data found in the node configuration file to send to the grid');
    return;
  }
  await postRequest(data, addr, port);
}

async function registerToGrid (options_post, jsonObject) {
  try {
    let response = await request(options_post);
    if (response === undefined || response.statusCode !== 200) {
      throw new Error('Request failed');
    }
    let logMessage = `Appium successfully registered with the grid on ${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}`;
    logger.debug(logMessage);
  } catch (err) {
    logger.error(`Request to register with grid was unsuccessful: ${err.message}`);
  }
}

async function postRequest (data, addr, port) {
  // parse json to get hub host and port
  let jsonObject;
  try {
    jsonObject = JSON.parse(data);
  } catch (err) {
    logger.errorAndThrow(`Syntax error in node configuration file: ${err.message}`);
  }

  // if the node config does not have the appium/webdriver url, host, and port,
  // automatically add it based on how appium was initialized
  // otherwise, we will take whatever the user setup
  // because we will always set localhost/127.0.0.1. this won't work if your
  // node and grid aren't in the same place
  if (!jsonObject.configuration.url || !jsonObject.configuration.host || !jsonObject.configuration.port) {
    jsonObject.configuration.url = `http://${addr}:${port}/wd/hub`;
    jsonObject.configuration.host = addr;
    jsonObject.configuration.port = port;

    // re-serialize the configuration with the auto populated data
    data = JSON.stringify(jsonObject);
  }

  // prepare the header
  let post_headers = {
    'Content-Type': 'application/json',
    'Content-Length': data.length
  };
  // the post options
  let post_options = {
    url: `http://${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}/grid/register`,
    method: 'POST',
    body: data,
    headers: post_headers,
    resolveWithFullResponse: true // return the full response, not just the body
  };

  if (jsonObject.configuration.register !== true) {
    logger.debug(`No registration sent (${jsonObject.configuration.register} = false)`);
    return;
  }

  let registerCycleTime = jsonObject.configuration.registerCycle;
  if (registerCycleTime !== null && registerCycleTime > 0) {
    // initiate a new Thread
    let first = true;
    logger.debug(`Starting auto register thread for grid. Will try to register every ${registerCycleTime} ms.`);
    setInterval(async function () {
      if (first !== true) {
        let isRegistered = await isAlreadyRegistered(jsonObject);
        if (isRegistered !== null && isRegistered !== true) {
          // make the http POST to the grid for registration
          await registerToGrid(post_options, jsonObject);
        }
      } else {
        first = false;
        await registerToGrid(post_options, jsonObject);
      }
    }, registerCycleTime);
  }
}

async function isAlreadyRegistered (jsonObject) {
  //check if node is already registered
  let id = `http://${jsonObject.configuration.host}:${jsonObject.configuration.port}`;
  try {
    let response = await request({
      uri: `http://${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}/grid/api/proxy?id=${id}`,
      method  : 'GET',
      timeout : 10000,
      resolveWithFullResponse: true // return the full response, not just the body
    });
    if (response === undefined || response.statusCode !== 200) {
      throw new Error(`Request failed`);
    }
    let responseData = JSON.parse(response.body);
    if (responseData.success !== true) {
      logger.debug("[ERROR] " + responseData.msg);  // if register fail,print the debug msg
    }
    return responseData.success;
  } catch (err) {
    logger.debug("Hub down or not responding: ${err.message}");
  }
}

export default registerNode;
<commit_msg>Fix string handling in grid register<commit_after>import request from 'request-promise';
import { fs } from 'appium-support';
import logger from './logger';

async function registerNode (configFile, addr, port) {
  let data;
  try {
    data = await fs.readFile(configFile, 'utf-8');
  } catch (err) {
    logger.error(`Unable to load node configuration file to register with grid: ${err.message}`);
    return;
  }

  // Check presence of data before posting  it to the selenium grid
  if (!data) {
    logger.error('No data found in the node configuration file to send to the grid');
    return;
  }
  await postRequest(data, addr, port);
}

async function registerToGrid (options_post, jsonObject) {
  try {
    let response = await request(options_post);
    if (response === undefined || response.statusCode !== 200) {
      throw new Error('Request failed');
    }
    let logMessage = `Appium successfully registered with the grid on ${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}`;
    logger.debug(logMessage);
  } catch (err) {
    logger.error(`Request to register with grid was unsuccessful: ${err.message}`);
  }
}

async function postRequest (data, addr, port) {
  // parse json to get hub host and port
  let jsonObject;
  try {
    jsonObject = JSON.parse(data);
  } catch (err) {
    logger.errorAndThrow(`Syntax error in node configuration file: ${err.message}`);
  }

  // if the node config does not have the appium/webdriver url, host, and port,
  // automatically add it based on how appium was initialized
  // otherwise, we will take whatever the user setup
  // because we will always set localhost/127.0.0.1. this won't work if your
  // node and grid aren't in the same place
  if (!jsonObject.configuration.url || !jsonObject.configuration.host || !jsonObject.configuration.port) {
    jsonObject.configuration.url = `http://${addr}:${port}/wd/hub`;
    jsonObject.configuration.host = addr;
    jsonObject.configuration.port = port;

    // re-serialize the configuration with the auto populated data
    data = JSON.stringify(jsonObject);
  }

  // prepare the header
  let post_headers = {
    'Content-Type': 'application/json',
    'Content-Length': data.length
  };
  // the post options
  let post_options = {
    url: `http://${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}/grid/register`,
    method: 'POST',
    body: data,
    headers: post_headers,
    resolveWithFullResponse: true // return the full response, not just the body
  };

  if (jsonObject.configuration.register !== true) {
    logger.debug(`No registration sent (${jsonObject.configuration.register} = false)`);
    return;
  }

  let registerCycleTime = jsonObject.configuration.registerCycle;
  if (registerCycleTime !== null && registerCycleTime > 0) {
    // initiate a new Thread
    let first = true;
    logger.debug(`Starting auto register thread for grid. Will try to register every ${registerCycleTime} ms.`);
    setInterval(async function () {
      if (first !== true) {
        let isRegistered = await isAlreadyRegistered(jsonObject);
        if (isRegistered !== null && isRegistered !== true) {
          // make the http POST to the grid for registration
          await registerToGrid(post_options, jsonObject);
        }
      } else {
        first = false;
        await registerToGrid(post_options, jsonObject);
      }
    }, registerCycleTime);
  }
}

async function isAlreadyRegistered (jsonObject) {
  //check if node is already registered
  let id = `http://${jsonObject.configuration.host}:${jsonObject.configuration.port}`;
  try {
    let response = await request({
      uri: `http://${jsonObject.configuration.hubHost}:${jsonObject.configuration.hubPort}/grid/api/proxy?id=${id}`,
      method  : 'GET',
      timeout : 10000,
      resolveWithFullResponse: true // return the full response, not just the body
    });
    if (response === undefined || response.statusCode !== 200) {
      throw new Error(`Request failed`);
    }
    let responseData = JSON.parse(response.body);
    if (responseData.success !== true) {
      // if register fail, print the debug msg
      logger.debug(`Grid registration error: ${responseData.msg}`);
    }
    return responseData.success;
  } catch (err) {
    logger.debug(`Hub down or not responding: ${err.message}`);
  }
}

export default registerNode;
<|endoftext|>


<commit_before>define([
	'goo/fsmpack/statemachine/actions/Action',
	'goo/math/Vector3',
	'goo/util/TWEEN'
], function (
	Action,
	Vector3,
	TWEEN
) {
	'use strict';

	function ShakeAction(/*id, settings*/) {
		Action.apply(this, arguments);
	}

	ShakeAction.prototype = Object.create(Action.prototype);
	ShakeAction.prototype.constructor = ShakeAction;

	ShakeAction.external = {
		name: 'Shake',
		type: 'animation',
		description: 'Shakes the entity. Optionally performs a transition.',
		canTransition: true,
		parameters: [{
			name: 'Start level',
			key: 'startLevel',
			type: 'float',
			description: 'Shake amount at start',
			'default': 0
		}, {
			name: 'End level',
			key: 'endLevel',
			type: 'float',
			description: 'Shake amount at the end',
			'default': 10
		}, {
			name: 'Time (ms)',
			key: 'time',
			type: 'float',
			description: 'Shake time amount',
			'default': 1000
		}, {
			name: 'Speed',
			key: 'speed',
			type: 'string',
			control: 'dropdown',
			description: 'Speed of shaking',
			'default': 'Fast',
			options: ['Fast', 'Medium', 'Slow']
		}],
		transitions: [{
			key: 'complete',
			name: 'On Completion',
			description: 'State to transition to when the shake completes'
		}]
	};

	ShakeAction.prototype.configure = function (settings) {
		this.startLevel = settings.startLevel;
		this.endLevel = settings.endLevel;
		this.time = settings.time;
		this.speed = { 'Fast': 1, 'Medium': 2, 'Slow': 4 }[settings.speed];
		this.easing = TWEEN.Easing.Quadratic.InOut;
		this.eventToEmit = { channel: settings.transitions.complete };
	};

	ShakeAction.prototype.enter = function (fsm) {
		this.tween = new TWEEN.Tween();

		var entity = fsm.getOwnerEntity();
		var transformComponent = entity.transformComponent;
		var translation = transformComponent.transform.translation;
		var time = entity._world.time * 1000;

		var oldVal = new Vector3();
		var target = new Vector3();
		var vel = new Vector3();

		var that = this;
		var iter = 0;
		this.tween.from({ level: +this.startLevel }).to({ level: +this.endLevel }, +this.time).easing(this.easing).onUpdate(function () {
			iter++;
			if (iter > that.speed) {
				iter = 0;

				target.setDirect(
					-oldVal.x + (Math.random() - 0.5) * this.level * 2,
					-oldVal.y + (Math.random() - 0.5) * this.level * 2,
					-oldVal.z + (Math.random() - 0.5) * this.level * 2
				);
			}

			vel.setDirect(
				vel.x * 0.98 + (target.x) * 0.1,
				vel.y * 0.98 + (target.y) * 0.1,
				vel.z * 0.98 + (target.z) * 0.1
			);

			translation.add(vel).sub(oldVal);
			oldVal.copy(vel);
			transformComponent.setUpdated();
		}).onComplete(function () {
			translation.sub(oldVal);
			transformComponent.setUpdated();
			fsm.send(this.eventToEmit.channel);
		}.bind(this)).start(time);
	};

	ShakeAction.prototype.cleanup = function (/*fsm*/) {
		if (this.tween) {
			this.tween.stop();
		}
	};

	return ShakeAction;
});<commit_msg>Fixed shake action<commit_after>define([
	'goo/fsmpack/statemachine/actions/Action',
	'goo/math/MathUtils',
	'goo/math/Vector3',
	'goo/util/TWEEN'
], function (
	Action,
	MathUtils,
	Vector3,
	TWEEN
) {
	'use strict';

	function ShakeAction(/*id, settings*/) {
		Action.apply(this, arguments);

		this.oldVal = new Vector3();
		this.target = new Vector3();
		this.vel = new Vector3();
	}

	ShakeAction.prototype = Object.create(Action.prototype);
	ShakeAction.prototype.constructor = ShakeAction;

	ShakeAction.external = {
		name: 'Shake',
		type: 'animation',
		description: 'Shakes the entity. Optionally performs a transition.',
		canTransition: true,
		parameters: [{
			name: 'Start level',
			key: 'startLevel',
			type: 'float',
			description: 'Shake amount at start',
			'default': 0
		}, {
			name: 'End level',
			key: 'endLevel',
			type: 'float',
			description: 'Shake amount at the end',
			'default': 10
		}, {
			name: 'Time (ms)',
			key: 'time',
			type: 'float',
			description: 'Shake time amount',
			'default': 1000
		}, {
			name: 'Speed',
			key: 'speed',
			type: 'string',
			control: 'dropdown',
			description: 'Speed of shaking',
			'default': 'Fast',
			options: ['Fast', 'Medium', 'Slow']
		}],
		transitions: [{
			key: 'complete',
			name: 'On Completion',
			description: 'State to transition to when the shake completes'
		}]
	};

	ShakeAction.prototype.configure = function (settings) {
		this.startLevel = settings.startLevel;
		this.endLevel = settings.endLevel;
		this.time = settings.time;
		this.speed = { 'Fast': 1, 'Medium': 2, 'Slow': 4 }[settings.speed];
		this.easing = TWEEN.Easing.Quadratic.InOut;
		this.eventToEmit = settings.transitions.complete;
	};

	ShakeAction.prototype.enter = function (fsm) {
		this.oldVal.set(Vector3.ZERO);
		this.target.set(Vector3.ZERO);
		this.vel.set(Vector3.ZERO);
		this.iter = 0;
		this.startTime = fsm.getTime();
	};

	ShakeAction.prototype.update = function (fsm) {
		var entity = fsm.getOwnerEntity();
		var transformComponent = entity.transformComponent;
		var translation = transformComponent.transform.translation;

		var t = Math.min((fsm.getTime() - this.startTime) * 1000 / this.time, 1);
		var fT = this.easing(t);

		var level = MathUtils.lerp(fT, this.startLevel, this.endLevel);

		this.iter++;
		if (this.iter > this.speed) {
			this.iter = 0;

			this.target.setDirect(
				-this.oldVal.x + (Math.random() - 0.5) * level * 2,
				-this.oldVal.y + (Math.random() - 0.5) * level * 2,
				-this.oldVal.z + (Math.random() - 0.5) * level * 2
			);
		}

		this.vel.setDirect(
			this.vel.x * 0.98 + (this.target.x) * 0.1,
			this.vel.y * 0.98 + (this.target.y) * 0.1,
			this.vel.z * 0.98 + (this.target.z) * 0.1
		);

		translation.add(this.vel).sub(this.oldVal);
		this.oldVal.copy(this.vel);
		transformComponent.setUpdated();

		if (t >= 1) {
			translation.sub(this.oldVal);
			transformComponent.setUpdated();
			fsm.send(this.eventToEmit);
		}
	};

	return ShakeAction;
});<|endoftext|>


<commit_before>var jwt = require('jsonwebtoken');
var UnauthorizedError = require('./errors/UnauthorizedError');
var unless = require('express-unless');
var async = require('async');

var DEFAULT_REVOKED_FUNCTION = function(_, __, cb) { return cb(null, false); }

var getClass = {}.toString;
function isFunction(object) {
  return object && getClass.call(object) == '[object Function]';
}

function wrapStaticSecretInCallback(secret){
  return function(_, __, cb){
    return cb(null, secret);
  };
}

module.exports = function(options) {
  if (!options || !options.secret) throw new Error('secret should be set');

  var secretCallback = options.secret;

  if (!isFunction(secretCallback)){
    secretCallback = wrapStaticSecretInCallback(secretCallback);
  }

  var isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION;

  var _requestProperty = options.userProperty || options.requestProperty || 'user';
  var credentialsRequired = typeof options.credentialsRequired === 'undefined' ? true : options.credentialsRequired;

  var middleware = function(req, res, next) {
    var token;

    if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
      var hasAuthInAccessControl = !!~req.headers['access-control-request-headers']
                                    .split(',').map(function (header) {
                                      return header.trim();
                                    }).indexOf('authorization');

      if (hasAuthInAccessControl) {
        return next();
      }
    }

    if (typeof options.skip !== 'undefined') {
      console.warn('WARN: express-jwt: options.skip is deprecated');
      console.warn('WARN: use app.use(jwt(options).unless({path: \'/x\'}))');
      if (options.skip.indexOf(req.url) > -1) {
        return next();
      }
    }

    if (options.getToken && typeof options.getToken === 'function') {
      try {
        token = options.getToken(req);
      } catch (e) {
        return next(e);
      }
    } else if (req.headers && req.headers.authorization) {
      var parts = req.headers.authorization.split(' ');
      if (parts.length == 2) {
        var scheme = parts[0];
        var credentials = parts[1];

        if (/^Bearer$/i.test(scheme)) {
          token = credentials;
        } else {
          return next(new UnauthorizedError('credentials_bad_scheme', { message: 'Format is Authorization: Bearer [token]' }));
        }
      } else {
        return next(new UnauthorizedError('credentials_bad_format', { message: 'Format is Authorization: Bearer [token]' }));
      }
    }

    if (!token) {
      if (credentialsRequired) {
        return next(new UnauthorizedError('credentials_required', { message: 'No authorization token was found' }));
      } else {
        return next();
      }
    }

    var dtoken = jwt.decode(token, { complete: true }) || {};

    async.parallel([
      function(callback){
        var arity = secretCallback.length;
        if (arity == 4) {
          secretCallback(req, dtoken.header, dtoken.payload, callback);
        } else { // arity == 3
          secretCallback(req, dtoken.payload, callback);
        }
      },
      function(callback){
        isRevokedCallback(req, dtoken.payload, callback);
      }
    ], function(err, results){
      if (err) { return next(new UnauthorizedError(err.message, {message: err.message})); }
      var revoked = results[1];
      if (revoked){
        return next(new UnauthorizedError('revoked_token', { message: 'The token has been revoked.'}));
      }

      var secret = results[0];

      jwt.verify(token, secret, options, function(err, decoded) {
        if (err && credentialsRequired) return next(new UnauthorizedError('invalid_token', err));

        req[_requestProperty] = decoded;
        next();
      });
    });
  };

  middleware.unless = unless;

  return middleware;
};
<commit_msg>Update index.js<commit_after>var jwt = require('jsonwebtoken');
var UnauthorizedError = require('./errors/UnauthorizedError');
var unless = require('express-unless');
var async = require('async');

var DEFAULT_REVOKED_FUNCTION = function(_, __, cb) { return cb(null, false); }

var getClass = {}.toString;
function isFunction(object) {
  return object && getClass.call(object) == '[object Function]';
}

function wrapStaticSecretInCallback(secret){
  return function(_, __, cb){
    return cb(null, secret);
  };
}

module.exports = function(options) {
  if (!options || !options.secret) throw new Error('secret should be set');

  var secretCallback = options.secret;

  if (!isFunction(secretCallback)){
    secretCallback = wrapStaticSecretInCallback(secretCallback);
  }

  var isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION;

  var _requestProperty = options.userProperty || options.requestProperty || 'user';
  var credentialsRequired = typeof options.credentialsRequired === 'undefined' ? true : options.credentialsRequired;

  var middleware = function(req, res, next) {
    var token;

    if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
      var hasAuthInAccessControl = !!~req.headers['access-control-request-headers']
                                    .split(',').map(function (header) {
                                      return header.trim();
                                    }).indexOf('authorization');

      if (hasAuthInAccessControl) {
        return next();
      }
    }

    if (typeof options.skip !== 'undefined') {
      console.warn('WARN: express-jwt: options.skip is deprecated');
      console.warn('WARN: use app.use(jwt(options).unless({path: \'/x\'}))');
      if (options.skip.indexOf(req.url) > -1) {
        return next();
      }
    }

    if (options.getToken && typeof options.getToken === 'function') {
      try {
        token = options.getToken(req);
      } catch (e) {
        return next(e);
      }
    } else if (req.headers && req.headers.authorization) {
      var parts = req.headers.authorization.split(' ');
      if (parts.length == 2) {
        var scheme = parts[0];
        var credentials = parts[1];

        if (/^Bearer$/i.test(scheme)) {
          token = credentials;
        } else {
          return next(new UnauthorizedError('credentials_bad_scheme', { message: 'Format is Authorization: Bearer [token]' }));
        }
      } else {
        return next(new UnauthorizedError('credentials_bad_format', { message: 'Format is Authorization: Bearer [token]' }));
      }
    }

    if (!token) {
      if (credentialsRequired) {
        return next(new UnauthorizedError('credentials_required', { message: 'No authorization token was found' }));
      } else {
        return next();
      }
    }

    var dtoken = jwt.decode(token, { complete: true }) || {};

    async.parallel([
      function(callback){
        var arity = secretCallback.length;
        if (arity == 4) {
          secretCallback(req, dtoken.header, dtoken.payload, callback);
        } else { // arity == 3
          secretCallback(req, dtoken.payload, callback);
        }
      },
      function(callback){
        isRevokedCallback(req, dtoken.payload, callback);
      }
    ], function(err, results){
      if (err) { return next(new UnauthorizedError(err.code, {message: err.message})); }
      var revoked = results[1];
      if (revoked){
        return next(new UnauthorizedError('revoked_token', { message: 'The token has been revoked.'}));
      }

      var secret = results[0];

      jwt.verify(token, secret, options, function(err, decoded) {
        if (err && credentialsRequired) return next(new UnauthorizedError('invalid_token', err));

        req[_requestProperty] = decoded;
        next();
      });
    });
  };

  middleware.unless = unless;

  return middleware;
};
<|endoftext|>


<commit_before>var docxReader = require("./docx-reader");
var DocumentConverter = require("./document-to-html").DocumentConverter;
var htmlPaths = require("./html-paths");

exports.Converter = Converter;
exports.standardOptions = {
    paragraphStyleMap: {
        "Heading1": htmlPaths.topLevelElement("h1"),
        "Heading2": htmlPaths.topLevelElement("h2"),
        "Heading3": htmlPaths.topLevelElement("h3"),
        "Heading4": htmlPaths.topLevelElement("h4")
    }
};

function Converter(options) {
    this._documentConverter = new DocumentConverter(options);
}

Converter.prototype.convertToHtml = function(inputOptions) {
    var documentConverter = this._documentConverter;
    return docxReader.read(inputOptions)
        .then(function(documentResult) {
            return documentResult.flatMapThen(function(document) {
                return documentConverter.convertToHtml(document);
            });
        });
}
<commit_msg>Expose read and convertToHtml functions from index.js<commit_after>var docxReader = require("./docx-reader");
var DocumentConverter = require("./document-to-html").DocumentConverter;
var htmlPaths = require("./html-paths");

exports.Converter = Converter;
exports.read = read;
exports.convertToHtml = convertToHtml;
exports.standardOptions = {
    paragraphStyleMap: {
        "Heading1": htmlPaths.topLevelElement("h1"),
        "Heading2": htmlPaths.topLevelElement("h2"),
        "Heading3": htmlPaths.topLevelElement("h3"),
        "Heading4": htmlPaths.topLevelElement("h4")
    }
};

function Converter(options) {
    this._options = options;
}

Converter.prototype.convertToHtml = function(inputOptions) {
    var options = this._options;
    return read(inputOptions)
        .then(function(documentResult) {
            return convertToHtml(documentResult, options);
        });
}

function read(inputOptions) {
    return docxReader.read(inputOptions);
}

function convertToHtml(documentResult, options) {
    var documentConverter = new DocumentConverter(options);
    return documentResult.flatMapThen(function(document) {
        return documentConverter.convertToHtml(document);
    });
}
<|endoftext|>