<commit_before>var env = process.env.NODE_ENV || 'test';

var expect = require('chai').expect;
var deleteStream = require('level-delete-stream');

describe("#Graph", function () {
  var leveldb, db;
  var Graph;

  before(function () {
    var level = require('level-test')();
    leveldb = level(env+'.db');
    db = require('levelgraph-jsonld')(
      require('levelgraph')(leveldb)
    );
  });

  beforeEach(function (done) {
    return db.createKeyStream()
    .pipe(deleteStream(db, done))
  });

  it("should load module", function () {
    Graph = require('../');
    expect(Graph).to.exist;
  });

  describe("Graph.isGraph()", function () {

    it("of simple graph should be true", function () {
      var graph = new Graph({
        id: "Things",
        db: db,
        type: {
          id: "Thing",
          type: 'object',
          properties: {
            description: {
              type: 'string',
            },
          },
        },
      });
      expect(Graph.isGraph(graph)).to.be.true;
    });
  });
});
<commit_msg>add base to test db<commit_after>var env = process.env.NODE_ENV || 'test';

var expect = require('chai').expect;
var deleteStream = require('level-delete-stream');

describe("#Graph", function () {
  var leveldb, db;
  var Graph;

  before(function () {
    var level = require('level-test')();
    leveldb = level(env+'.db');
    db = require('levelgraph-jsonld')(
      require('levelgraph')(leveldb)
    , {
      base: "http://open.app/",
    });
  });

  beforeEach(function (done) {
    return db.createKeyStream()
    .pipe(deleteStream(db, done))
  });

  it("should load module", function () {
    Graph = require('../');
    expect(Graph).to.exist;
  });

  describe("Graph.isGraph()", function () {

    it("of simple graph should be true", function () {
      var graph = new Graph({
        id: "Things",
        db: db,
        type: {
          id: "Thing",
          type: 'object',
          properties: {
            description: {
              type: 'string',
            },
          },
        },
      });
      expect(Graph.isGraph(graph)).to.be.true;
    });
  });
});
<|endoftext|>


<commit_before>var assert = require('assert');
var fs = require('fs');
var cgbiToPng = require('../index');
var tmp = require('tmp');

var filePath = './test/cgbi-image.png';
var tempFilePath = tmp.tmpNameSync();

describe('convert',function(){
    it('should convert cgbi to png',function(done){
        cgbiToPng(fs.createReadStream(filePath),function(err,imageStream){
            assert.ifError(err);
            assert.ok(imageStream);
            imageStream
                .pipe(fs.createWriteStream(tempFilePath))
                .on('error',done)
                .on('close',function(){
                    fs.unlink(tempFilePath);
                    done();
                })
        })
    })

    it('should convert cgbi to png (buffers)',function(){
        var cgbiBuffer = fs.readFileSync(filePath);
        var pngBuffer = cgbiToPng.revert(cgbiBuffer);
        assert.ok(pngBuffer);
        assert.notEqual(pngBuffer.toString(),cgbiBuffer.toString())
    })
})<commit_msg>Add test for non cgbi files<commit_after>var assert = require('assert');
var fs = require('fs');
var cgbiToPng = require('../index');
var tmp = require('tmp');

var filePath = './test/cgbi-image.png';
var PNGFilePath = './test/png-image.png';
var nonPNGFilePath = './test/index.js';
var tempFilePath = tmp.tmpNameSync();

describe('convert',function(){
    it('should convert cgbi to png',function(done){
        cgbiToPng(fs.createReadStream(filePath),function(err,imageStream){
            assert.ifError(err);
            assert.ok(imageStream);
            imageStream
                .pipe(fs.createWriteStream(tempFilePath))
                .on('error',done)
                .on('close',function(){
                    fs.unlink(tempFilePath);
                    done();
                })
        })
    })

    it('should convert cgbi to png (buffers)',function(){
        var cgbiBuffer = fs.readFileSync(filePath);
        var pngBuffer = cgbiToPng.revert(cgbiBuffer);
        assert.ok(pngBuffer);
        assert.notEqual(pngBuffer.toString(),cgbiBuffer.toString())
    })

    it('should leave png untouched',function(){
        var pngBuffer = fs.readFileSync(PNGFilePath);
        var pngBuffer2 = cgbiToPng.revert(pngBuffer);
        assert.ok(pngBuffer2);
        assert.equal(pngBuffer.toString(),pngBuffer2.toString())
    })

    it('should fail on non png files',function(){
        var nonPNGBuffer = fs.readFileSync(nonPNGFilePath);
        assert.throws(function(){
            cgbiToPng.revert(nonPNGBuffer);
        },'not a png file')
    })
})<|endoftext|>


<commit_before><commit_msg>Add feature handle released multiple times<commit_after><|endoftext|>


<commit_before>
var path = require('path');
var should = require('should');
var gutil = require('gulp-util');
var gulpBanner = require('../lib/index');

var comment = '/* i am a comment */';

function createFakeFile(filename, contents) {
  return new gutil.File({
    cwd: process.cwd(),
    base: path.join(__dirname),
    path: path.join(__dirname, filename),
    contents: contents
  });
}

describe('gulp-banner', function() {
  it('works', function(done) {
    var str = 'function test(){}';
    var fakeFile = createFakeFile('normal.js', new Buffer(str));
    gulpBanner(comment).once('data', function(bundled) {
      should.exist(bundled.contents);
      bundled.contents.toString().should.equal(comment + str);
      done();
    }).end(fakeFile);
  });

  it('multi-line', function(done) {
    comment = '/* i am a comment */\n\n';
    var str = 'function test(){}\n\nvar abc=1';
    var fakeFile = createFakeFile('normal.js', new Buffer(str));
    gulpBanner(comment).once('data', function(bundled) {
      should.exist(bundled.contents);
      bundled.contents.toString().should.equal(comment + str);
      done();
    }).end(fakeFile);
  });
});<commit_msg>add test case<commit_after>
var path = require('path');
var should = require('should');
var gutil = require('gulp-util');
var gulpBanner = require('../lib/index');
var template = require('underscore.template');

var comment = '/* i am a comment */';

function createFakeFile(filename, contents) {
  return new gutil.File({
    cwd: process.cwd(),
    base: path.join(__dirname),
    path: path.join(__dirname, filename),
    contents: contents
  });
}

describe('gulp-banner', function() {
  it('works', function(done) {
    var str = 'function test(){}';
    var fakeFile = createFakeFile('normal.js', new Buffer(str));
    gulpBanner(comment).once('data', function(bundled) {
      should.exist(bundled.contents);
      bundled.contents.toString().should.equal(comment + str);
      done();
    }).end(fakeFile);
  });

  it('multi-line', function(done) {
    comment = '/* i am a comment */\n\n';
    var str = 'function test(){}\n\nvar abc=1';
    var fakeFile = createFakeFile('normal.js', new Buffer(str));
    gulpBanner(comment).once('data', function(bundled) {
      should.exist(bundled.contents);
      bundled.contents.toString().should.equal(comment + str);
      done();
    }).end(fakeFile);
  });
});

describe('template', function() {
  it('template single line', function() {
    comment = '/* i am a <%= pkg.name %> <%= content %> comment */';
    template(comment, {
      pkg: {
        name: 'awesome'
      },
      content: 'project'
    }).should.equal('/* i am a awesome project comment */');
  });

  it('template multiple line', function() {
    comment = '/*\n' +
        ' * <%= pkg.name %> <%= pkg.version %>\n' +
        ' * <%= pkg.name %> <%= pkg.version %>\n' +
        '*/\n\n';
    var res = '/*\n' +
        ' * awesome 0.1.2\n' +
        ' * awesome 0.1.2\n' +
        '*/\n\n';
    template(comment, {
      pkg: {
        name: 'awesome',
        version: '0.1.2'
      }
    }).should.equal(res);
  });
});<|endoftext|>


<commit_before>/* ========================================================================
 * DOM-based Routing
 * Based on http://goo.gl/EUTi53 by Paul Irish
 *
 * Only fires on body classes that match. If a body class contains a dash,
 * replace the dash with an underscore when adding it to the object below.
 *
 * .noConflict()
 * The routing is enclosed within an anonymous function so that you can
 * always reference jQuery with $, even when in .noConflict() mode.
 * ======================================================================== */

(function($) {

  // Use this variable to set up the common and page specific functions. If you
  // rename this variable, you will also need to rename the namespace below.
  var Sage = {
    // All pages
    'common': {
      init: function() {
        // JavaScript to be fired on all pages
        $('.hp-section-hero, .hp-section-wrap').css( 'min-height', $(window).height() );
      },
      finalize: function() {
        // JavaScript to be fired on all pages, after page specific JS is fired
      }
    },
    // Home page
    'home': {
      init: function() {
        // JavaScript to be fired on the home page
      },
      finalize: function() {
        // JavaScript to be fired on the home page, after the init JS
      }
    },
    // About us page, note the change from about-us to about_us.
    'about_us': {
      init: function() {
        // JavaScript to be fired on the about us page
      }
    }
  };

  // The routing fires all common scripts, followed by the page specific scripts.
  // Add additional events for more control over timing e.g. a finalize event
  var UTIL = {
    fire: function(func, funcname, args) {
      var fire;
      var namespace = Sage;
      funcname = (funcname === undefined) ? 'init' : funcname;
      fire = func !== '';
      fire = fire && namespace[func];
      fire = fire && typeof namespace[func][funcname] === 'function';

      if (fire) {
        namespace[func][funcname](args);
      }
    },
    loadEvents: function() {
      // Fire common init JS
      UTIL.fire('common');

      // Fire page-specific init JS, and then finalize JS
      $.each(document.body.className.replace(/-/g, '_').split(/\s+/), function(i, classnm) {
        UTIL.fire(classnm);
        UTIL.fire(classnm, 'finalize');
      });

      // Fire common finalize JS
      UTIL.fire('common', 'finalize');
    }
  };

  // Load Events
  $(document).ready(UTIL.loadEvents);

})(jQuery); // Fully reference jQuery after this point.
<commit_msg>Added smooth scrolling<commit_after>/* ========================================================================
 * DOM-based Routing
 * Based on http://goo.gl/EUTi53 by Paul Irish
 *
 * Only fires on body classes that match. If a body class contains a dash,
 * replace the dash with an underscore when adding it to the object below.
 *
 * .noConflict()
 * The routing is enclosed within an anonymous function so that you can
 * always reference jQuery with $, even when in .noConflict() mode.
 * ======================================================================== */

(function($) {

  // Use this variable to set up the common and page specific functions. If you
  // rename this variable, you will also need to rename the namespace below.
  var Sage = {
    // All pages
    'common': {
      init: function() {
        // JavaScript to be fired on all pages
        $('.hp-section-hero, .hp-section-wrap').css( 'min-height', $(window).height() );
      },
      finalize: function() {
        // JavaScript to be fired on all pages, after page specific JS is fired
      }
    },
    // Home page
    'home': {
      init: function() {
        // JavaScript to be fired on the home page
        $('a[href*=#]:not([href=#])').click(function() {
          if (location.pathname.replace(/^\//,'') === this.pathname.replace(/^\//,'') && location.hostname === this.hostname) {
            var target = $(this.hash);
            target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
            if (target.length) {
              $('html,body').animate({
                scrollTop: target.offset().top
              }, 250);
              return false;
            }
          }
        });
      },
      finalize: function() {
        // JavaScript to be fired on the home page, after the init JS
      }
    },
    // About us page, note the change from about-us to about_us.
    'about_us': {
      init: function() {
        // JavaScript to be fired on the about us page
      }
    }
  };

  // The routing fires all common scripts, followed by the page specific scripts.
  // Add additional events for more control over timing e.g. a finalize event
  var UTIL = {
    fire: function(func, funcname, args) {
      var fire;
      var namespace = Sage;
      funcname = (funcname === undefined) ? 'init' : funcname;
      fire = func !== '';
      fire = fire && namespace[func];
      fire = fire && typeof namespace[func][funcname] === 'function';

      if (fire) {
        namespace[func][funcname](args);
      }
    },
    loadEvents: function() {
      // Fire common init JS
      UTIL.fire('common');

      // Fire page-specific init JS, and then finalize JS
      $.each(document.body.className.replace(/-/g, '_').split(/\s+/), function(i, classnm) {
        UTIL.fire(classnm);
        UTIL.fire(classnm, 'finalize');
      });

      // Fire common finalize JS
      UTIL.fire('common', 'finalize');
    }
  };

  // Load Events
  $(document).ready(UTIL.loadEvents);

})(jQuery); // Fully reference jQuery after this point.
<|endoftext|>


<commit_before>/* ========================================================================
 * DOM-based Routing
 * Based on http://goo.gl/EUTi53 by Paul Irish
 *
 * Only fires on body classes that match. If a body class contains a dash,
 * replace the dash with an underscore when adding it to the object below.
 *
 * .noConflict()
 * The routing is enclosed within an anonymous function so that you can
 * always reference jQuery with $, even when in .noConflict() mode.
 * ======================================================================== */

(function($) {

  // Use this variable to set up the common and page specific functions. If you
  // rename this variable, you will also need to rename the namespace below.
  var Sage = {
    // All pages
    'common': {
      init: function() {
        // JavaScript to be fired on all pages
      },
      finalize: function() {
        // JavaScript to be fired on all pages, after page specific JS is fired
        $('.acf-map').each(function(){
      		render_map( $(this) );
      	});
      }
    },
    // Home page
    'home': {
      init: function() {
        // JavaScript to be fired on the home page
        $(window).scroll(function(){
          var scroll = $(window).scrollTop();

          if(scroll >= 162) {
            $('body').removeClass('home').addClass('home-scrolled');
          } else {
            $('body').removeClass('home-scrolled').addClass('home');
          }
        });
      },
      finalize: function() {
        // JavaScript to be fired on the home page, after the init JS
      }
    },
    // About us page, note the change from about-us to about_us.
    'about_us': {
      init: function() {
        // JavaScript to be fired on the about us page
      }
    }
  };

  // The routing fires all common scripts, followed by the page specific scripts.
  // Add additional events for more control over timing e.g. a finalize event
  var UTIL = {
    fire: function(func, funcname, args) {
      var fire;
      var namespace = Sage;
      funcname = (funcname === undefined) ? 'init' : funcname;
      fire = func !== '';
      fire = fire && namespace[func];
      fire = fire && typeof namespace[func][funcname] === 'function';

      if (fire) {
        namespace[func][funcname](args);
      }
    },
    loadEvents: function() {
      // Fire common init JS
      UTIL.fire('common');

      // Fire page-specific init JS, and then finalize JS
      $.each(document.body.className.replace(/-/g, '_').split(/\s+/), function(i, classnm) {
        UTIL.fire(classnm);
        UTIL.fire(classnm, 'finalize');
      });

      // Fire common finalize JS
      UTIL.fire('common', 'finalize');
    }
  };

  function lightenDarkenColor(col, amt) {

    var usePound = false;

    if (col[0] === "#") {
      col = col.slice(1);
      usePound = true;
    }

    var num = parseInt(col,16);

    var r = (num >> 16) + amt;

    if (r > 255) {
      r = 255;
    } else if (r < 0) {
      r = 0;
    }

    var b = ((num >> 8) & 0x00FF) + amt;

    if (b > 255) {
      b = 255;
    } else if (b < 0) {
      b = 0;
    }

    var g = (num & 0x0000FF) + amt;

    if (g > 255) {
      g = 255;
    } else if (g < 0) {
      g = 0;
    }

    return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);

  }

  /*
  *  render_map
  *
  *  This function will render a Google Map onto the selected jQuery element
  *
  *  @type	function
  *  @date	8/11/2013
  *  @since	4.3.0
  *
  *  @param	$el (jQuery element)
  *  @return	n/a
  */

  function render_map( $el ) {

  	// var
  	var $markers = $el.find('.marker');

  	// vars
  	var args = {
  		zoom		: 15,
  		center		: new google.maps.LatLng(0, 0),
  		mapTypeId	: google.maps.MapTypeId.ROADMAP
  	};

  	// create map
  	var map = new google.maps.Map( $el[0], args);

  	// add a markers reference
  	map.markers = [];
    // add an infowindows reference
    map.infowindows = [];

  	// add markers
  	$markers.each(function(){
    	add_marker( $(this), map );
  	});

  	// center map
  	center_map( map );

    //map.markers
    google.maps.event.addListener(map, "click", function(event) {
      close_infowindows( map );
    });

    var mcOptions = {gridSize: 20, maxZoom: 9};
    var markerCluster = new MarkerClusterer(map, map.markers, mcOptions);

  }

  /*
  *  add_marker
  *
  *  This function will add a marker to the selected Google Map
  *
  *  @type	function
  *  @date	8/11/2013
  *  @since	4.3.0
  *
  *  @param	$marker (jQuery element)
  *  @param	map (Google Map object)
  *  @return	n/a
  */

  function add_marker( $marker, map ) {

  	// var
  	var latlng = new google.maps.LatLng( $marker.attr('data-lat'), $marker.attr('data-lng') );

    var pinColor = $marker.attr('data-pincolor');
    var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor);

    // var MAP_PIN = 'M0-165c-27.618 0-50 21.966-50 49.054C-50-88.849 0 0 0 0s50-88.849 50-115.946C50-143.034 27.605-165 0-165z';
    var pinIcon = {
      anchor: new google.maps.Point(6.27,22),
      path: 'M6.3,22c0.2-3.1,1.9-8.1,4.3-11.2c1.3-1.7,2-3.4,2-4.4C12.6,2.9,9.8,0,6.3,0h0C2.8,0,0,2.9,0,6.4 c0,1,0.6,2.7,2,4.4C4.4,13.9,6.1,18.9,6.3,22L6.3,22z',
      fillColor: pinColor,
      fillOpacity: 1,
      scale: 1.25,
      strokeColor: lightenDarkenColor(pinColor,-30),
      strokeWeight: 1.5
    };

  	// create marker
  	var marker = new google.maps.Marker({
  		position	: latlng,
  		map			: map,
      icon: pinIcon,
  	});

  	// add to array
  	map.markers.push( marker );

  	// if marker contains HTML, add it to an infoWindow
  	if( $marker.html() )
  	{
  		// create info window
  		var infowindow = new google.maps.InfoWindow({
  			content		: $marker.html()
  		});

      map.infowindows.push( infowindow );

  		// show info window when marker is clicked
  		google.maps.event.addListener(marker, 'click', function() {
        close_infowindows( map );
  			infowindow.open( map, marker );

  		});
  	}

  }

  /*
  *  center_map
  *
  *  This function will center the map, showing all markers attached to this map
  *
  *  @type	function
  *  @date	8/11/2013
  *  @since	4.3.0
  *
  *  @param	map (Google Map object)
  *  @return	n/a
  */

  function center_map( map ) {

  	// vars
  	var bounds = new google.maps.LatLngBounds();

  	// loop through all markers and create bounds
  	$.each( map.markers, function( i, marker ){

  		var latlng = new google.maps.LatLng( marker.position.lat(), marker.position.lng() );

  		bounds.extend( latlng );

  	});

  	// only 1 marker?
  	if( map.markers.length === 1 )
  	{
  		// set center of map
  	    map.setCenter( bounds.getCenter() );
  	    map.setZoom( 15 );
  	}
  	else
  	{
  		// fit to bounds
  		map.fitBounds( bounds );
  	}

  }

  /*
  *  close_infowindows
  *
  *  This function will close all the open infowindows
  *
  *  @type  function
  *  @date  7/23/2015
  *
  *  @param	map (Google Map object)
  *  @return  n/a
  */
  function close_infowindows( map ) {
    for( var i = 0; i < map.infowindows.length; i++ ) {
      map.infowindows[i].close();
    }
  }

  // Load Events
  $(document).ready(UTIL.loadEvents);

})(jQuery); // Fully reference jQuery after this point.
<commit_msg>Adjusted grid size of map clustering<commit_after>/* ========================================================================
 * DOM-based Routing
 * Based on http://goo.gl/EUTi53 by Paul Irish
 *
 * Only fires on body classes that match. If a body class contains a dash,
 * replace the dash with an underscore when adding it to the object below.
 *
 * .noConflict()
 * The routing is enclosed within an anonymous function so that you can
 * always reference jQuery with $, even when in .noConflict() mode.
 * ======================================================================== */

(function($) {

  // Use this variable to set up the common and page specific functions. If you
  // rename this variable, you will also need to rename the namespace below.
  var Sage = {
    // All pages
    'common': {
      init: function() {
        // JavaScript to be fired on all pages
      },
      finalize: function() {
        // JavaScript to be fired on all pages, after page specific JS is fired
        $('.acf-map').each(function(){
      		render_map( $(this) );
      	});
      }
    },
    // Home page
    'home': {
      init: function() {
        // JavaScript to be fired on the home page
        $(window).scroll(function(){
          var scroll = $(window).scrollTop();

          if(scroll >= 162) {
            $('body').removeClass('home').addClass('home-scrolled');
          } else {
            $('body').removeClass('home-scrolled').addClass('home');
          }
        });
      },
      finalize: function() {
        // JavaScript to be fired on the home page, after the init JS
      }
    },
    // About us page, note the change from about-us to about_us.
    'about_us': {
      init: function() {
        // JavaScript to be fired on the about us page
      }
    }
  };

  // The routing fires all common scripts, followed by the page specific scripts.
  // Add additional events for more control over timing e.g. a finalize event
  var UTIL = {
    fire: function(func, funcname, args) {
      var fire;
      var namespace = Sage;
      funcname = (funcname === undefined) ? 'init' : funcname;
      fire = func !== '';
      fire = fire && namespace[func];
      fire = fire && typeof namespace[func][funcname] === 'function';

      if (fire) {
        namespace[func][funcname](args);
      }
    },
    loadEvents: function() {
      // Fire common init JS
      UTIL.fire('common');

      // Fire page-specific init JS, and then finalize JS
      $.each(document.body.className.replace(/-/g, '_').split(/\s+/), function(i, classnm) {
        UTIL.fire(classnm);
        UTIL.fire(classnm, 'finalize');
      });

      // Fire common finalize JS
      UTIL.fire('common', 'finalize');
    }
  };

  function lightenDarkenColor(col, amt) {

    var usePound = false;

    if (col[0] === "#") {
      col = col.slice(1);
      usePound = true;
    }

    var num = parseInt(col,16);

    var r = (num >> 16) + amt;

    if (r > 255) {
      r = 255;
    } else if (r < 0) {
      r = 0;
    }

    var b = ((num >> 8) & 0x00FF) + amt;

    if (b > 255) {
      b = 255;
    } else if (b < 0) {
      b = 0;
    }

    var g = (num & 0x0000FF) + amt;

    if (g > 255) {
      g = 255;
    } else if (g < 0) {
      g = 0;
    }

    return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);

  }

  /*
  *  render_map
  *
  *  This function will render a Google Map onto the selected jQuery element
  *
  *  @type	function
  *  @date	8/11/2013
  *  @since	4.3.0
  *
  *  @param	$el (jQuery element)
  *  @return	n/a
  */

  function render_map( $el ) {

  	// var
  	var $markers = $el.find('.marker');

  	// vars
  	var args = {
  		zoom		: 15,
  		center		: new google.maps.LatLng(0, 0),
  		mapTypeId	: google.maps.MapTypeId.ROADMAP
  	};

  	// create map
  	var map = new google.maps.Map( $el[0], args);

  	// add a markers reference
  	map.markers = [];
    // add an infowindows reference
    map.infowindows = [];

  	// add markers
  	$markers.each(function(){
    	add_marker( $(this), map );
  	});

  	// center map
  	center_map( map );

    //map.markers
    google.maps.event.addListener(map, "click", function(event) {
      close_infowindows( map );
    });

    var mcOptions = {gridSize: 30, maxZoom: 9};
    var markerCluster = new MarkerClusterer(map, map.markers, mcOptions);

  }

  /*
  *  add_marker
  *
  *  This function will add a marker to the selected Google Map
  *
  *  @type	function
  *  @date	8/11/2013
  *  @since	4.3.0
  *
  *  @param	$marker (jQuery element)
  *  @param	map (Google Map object)
  *  @return	n/a
  */

  function add_marker( $marker, map ) {

  	// var
  	var latlng = new google.maps.LatLng( $marker.attr('data-lat'), $marker.attr('data-lng') );

    var pinColor = $marker.attr('data-pincolor');
    var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor);

    // var MAP_PIN = 'M0-165c-27.618 0-50 21.966-50 49.054C-50-88.849 0 0 0 0s50-88.849 50-115.946C50-143.034 27.605-165 0-165z';
    var pinIcon = {
      anchor: new google.maps.Point(6.27,22),
      path: 'M6.3,22c0.2-3.1,1.9-8.1,4.3-11.2c1.3-1.7,2-3.4,2-4.4C12.6,2.9,9.8,0,6.3,0h0C2.8,0,0,2.9,0,6.4 c0,1,0.6,2.7,2,4.4C4.4,13.9,6.1,18.9,6.3,22L6.3,22z',
      fillColor: pinColor,
      fillOpacity: 1,
      scale: 1.25,
      strokeColor: lightenDarkenColor(pinColor,-30),
      strokeWeight: 1.5
    };

  	// create marker
  	var marker = new google.maps.Marker({
  		position	: latlng,
  		map			: map,
      icon: pinIcon,
  	});

  	// add to array
  	map.markers.push( marker );

  	// if marker contains HTML, add it to an infoWindow
  	if( $marker.html() )
  	{
  		// create info window
  		var infowindow = new google.maps.InfoWindow({
  			content		: $marker.html()
  		});

      map.infowindows.push( infowindow );

  		// show info window when marker is clicked
  		google.maps.event.addListener(marker, 'click', function() {
        close_infowindows( map );
  			infowindow.open( map, marker );

  		});
  	}

  }

  /*
  *  center_map
  *
  *  This function will center the map, showing all markers attached to this map
  *
  *  @type	function
  *  @date	8/11/2013
  *  @since	4.3.0
  *
  *  @param	map (Google Map object)
  *  @return	n/a
  */

  function center_map( map ) {

  	// vars
  	var bounds = new google.maps.LatLngBounds();

  	// loop through all markers and create bounds
  	$.each( map.markers, function( i, marker ){

  		var latlng = new google.maps.LatLng( marker.position.lat(), marker.position.lng() );

  		bounds.extend( latlng );

  	});

  	// only 1 marker?
  	if( map.markers.length === 1 )
  	{
  		// set center of map
  	    map.setCenter( bounds.getCenter() );
  	    map.setZoom( 15 );
  	}
  	else
  	{
  		// fit to bounds
  		map.fitBounds( bounds );
  	}

  }

  /*
  *  close_infowindows
  *
  *  This function will close all the open infowindows
  *
  *  @type  function
  *  @date  7/23/2015
  *
  *  @param	map (Google Map object)
  *  @return  n/a
  */
  function close_infowindows( map ) {
    for( var i = 0; i < map.infowindows.length; i++ ) {
      map.infowindows[i].close();
    }
  }

  // Load Events
  $(document).ready(UTIL.loadEvents);

})(jQuery); // Fully reference jQuery after this point.
<|endoftext|>


<commit_before>'use strict';

var eslint = require('eslint'),
    should = require('should');

var cli = new eslint.CLIEngine();
var formatter = cli.getFormatter();

var report;

describe('code style guide', function () {
  // it('library files should follow our JavaScript style guide', function(done) {
  //   var files = glob.sync('lib/**/*.js');
  //   files.push('index.js');

  //   report = cli.executeOnFiles(files);
  //   if (report.errorCount > 0 || report.warningCount > 0) {
  //     console.log(formatter(report.results));
  //   }

  //   should(report.errorCount).equal(0);
  //   should(report.warningCount).equal(0);
  //   done();
  // });

  it('tests/main.js should follow our JavaScript style guide', function (done) {
    report = cli.executeOnFiles(['tests/main.js']);
    if (report.errorCount > 0 || report.warningCount > 0) {
      console.log(formatter(report.results));
    }

    should(report.errorCount).equal(0);
    should(report.warningCount).equal(0);
    done();
  });

  it('tesst/lint.js should follow our JavaScript style guide', function (done) {
    cli = new eslint.CLIEngine({
      'rules': {
        'no-console': 0
      }
    });
    report = cli.executeOnFiles(['tests/lint.js']);
    if (report.errorCount > 0 || report.warningCount > 0) {
      console.log(formatter(report.results));
    }

    should(report.errorCount).equal(0);
    should(report.warningCount).equal(0);
    done();
  });
});
<commit_msg>Lint the lib files!<commit_after>'use strict';

var eslint = require('eslint'),
    glob = require('glob'),
    should = require('should');

var cli = new eslint.CLIEngine();
var formatter = cli.getFormatter();

var report;

describe('code style guide', function () {
  it('library files should follow our JavaScript style guide', function (done) {
    var files = glob.sync('lib/**/*.js');
    files.push('index.js');

    report = cli.executeOnFiles(files);
    if (report.errorCount > 0 || report.warningCount > 0) {
      console.log(formatter(report.results));
    }

    should(report.errorCount).equal(0);
    should(report.warningCount).equal(0);
    done();
  });

  it('tests/main.js should follow our JavaScript style guide', function (done) {
    report = cli.executeOnFiles(['tests/main.js']);
    if (report.errorCount > 0 || report.warningCount > 0) {
      console.log(formatter(report.results));
    }

    should(report.errorCount).equal(0);
    should(report.warningCount).equal(0);
    done();
  });

  it('tesst/lint.js should follow our JavaScript style guide', function (done) {
    cli = new eslint.CLIEngine({
      'rules': {
        'no-console': 0
      }
    });
    report = cli.executeOnFiles(['tests/lint.js']);
    if (report.errorCount > 0 || report.warningCount > 0) {
      console.log(formatter(report.results));
    }

    should(report.errorCount).equal(0);
    should(report.warningCount).equal(0);
    done();
  });
});
<|endoftext|>


<commit_before>
/**
 * @file
 * Communicate with astro empires web site.
 *
 * Astro Empires object will take care of requesting data, parsing the
 * response and storing the data.
 *
 * @author Bryan Hazelbaker <bryan.hazelbaker@gmail.com>
 * @version 0.1
 */

// Name space.
var AstroEmpires = {
    // All languages should namespace themselves here.
    Language: {},
    // All skins should namespace themselves here.
    Skin: {},
    /**
     * Simplify pulling values out of a regular expression into a single line.
     *
     * @param string $pattern
     *   The regular expression pattern.
     * @param string subject
     *   The subject source to search for pattern in.
     * @param int index
     *   The matching index value to return.
     * @param mixed default
     *   A default value to return if match is not found.
     *
     * @return string|bool
     *   The matched value or false on failure.
     */
    regex: function(pattern, subject, index, value) {
        var match;
        if (match = pattern.exec(subject)) {
            value = match[index];
        }
        return value;
    },
    AE: function(server, email, pass) {
        Observable.call(this);
        // User credentials.
        this.user = {
            server: server,
            email: email,
            pass: pass,
            skin: false,
            language: false
        };
        // Statistics to be retrieved from AE website.
        this.stats = {
            // credits: options['credits'],
            // income: options['income'],
            // fleetSize: options['fleetSize'],
            // technology: options['technology'],
            // level: options['level'],
            // rank: options['rank'],
        };
        this.msg = {};
        // Add our message types.
        this.typeAdd('get_credits');
    }
};
AstroEmpires.AE.prototype = Observable.prototype;
jQuery.extend(AstroEmpires.AE.prototype, {
    /**
     * Send and receive ajax requests.
     *
     * @param string url
     *   The fully formed url to access.
     * @param string type
     *   Type of access (POST, GET).
     * @params string
     *   Name value pairs just as during a GET request.
     */
    ajax: function(url, type, params) {
        var alter = this.publish({url: url, type: type, params: params}, 'ajax', this);
        var xhr = new XMLHttpRequest();
        thisAEO = this;
        xhr.onreadystatechange = function () {
            if (this.readyState == this.DONE) {
                // Everything went 'ok'.
                if (this.status == 200) {
                    // Check if we have been asked to login.
                    if (this.responseText.match(/<title>Login.*?<\/title>/i)) {
                        // Login.
                        thisAEO.login();
                        // Now try getting data again.
                        thisAEO.getData(url);
                    }
                    // We have not been asked to login, this should be the
                    // requested page.
                    else {
                        thisAEO.processData(url, this.responseText);
                    }
                }
                // A response header other than 200 (ok) was returned. Remember
                // redirects are transparantly handled by XMLHttpRequest().
                else {
                    console.log('Failed to open ' + url);
                }
            }
        },
        xhr.open(type, url, true);
        // No post parameters exist, this may be a simple GET.
        if (null === params) {
            xhr.send();
        }
        else {
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(params);
        }
    },
    /**
     *
     * Examine the results of an ajax request.
     *
     * Uses regular expressions to scrape values out of the html response.
     *
     * @param string url
     *   The url that generated this response.
     * @param string response
     *   The response itself containing html.
     */
    processData: function (url, data) {
        // Pull out page name and view mode, if any, from the url.
        var page = '';
        var view = '';
        if (match = /.*\/([^\.]+)\.aspx(.+view=([a-z]+))?/i.exec(url)) {
            page = '_' + match[1];
            view = match[3] ? '_' + match[3] : '';
            // Call any observers that do not care about skin.
            var results = this.publish({data: data, url: url}, 'url' + page + view, this);
            if (this.user.skin) {
                // Call observers that do care about skin.
                this.publish({data: data, url: url}, 'skin_' + this.user.skin + page + view, this);
            }
        }
        else {
            throw exception()
        }
    },
    /**
     * Relogin to the website.
     */
    login: function() {
        var params = "email=" + this.user.email +
            "&pass=" + this.user.pass +
            "&navigator=Netscape" +
            "&hostname=" + this.user.server +
            "&javascript=false" +
            "&post_back=false";
        this.ajax('http://' + this.user.server + '/login.aspx', 'POST', params);
    },
    /**
     * Retrieve all pages to be parsed and examined.
     *
     * @param string url
     *   When specified only request this url.
     */
    getData: function(url) {
        if (url) {
            // Only poll a single page for information. Normally if a previous
            // request was rejected do to invalid login, after sending
            // credentials a re-poll of the original request is required.
            this.ajax(url, 'GET');
        }
        else {
            // @todo transfer is asynchronus. Ensure we get language and skin first.
            this.ajax('http://' + this.user.server + '/account.aspx?view=display', 'GET');
            this.ajax('http://' + this.user.server + '/account.aspx', 'GET');
            this.ajax('http://' + this.user.server + '/board.aspx', 'GET');
        }
    },
    /**
     * Determine if a message identifier already exists or not.
     *
     * @param int msgId
     *   The unique message identifier.
     *
     * @return bool
     *   true if the message already exists, false otherwise.
     */
    msgExists: function(msgId) {
        var exists = false;
        if (typeof(this.msg[msgId]) != undefined) {
            exists = true;
        }
        return exists;
    },
    /**
     * Add a message to the message log.
     *
     * @param int msgId
     *   The unique message identifier. If this message already exists then it
     *   will be overwritten.
     * @param object message
     *   The message object consisting of:
     *     'id': The message id.
     *     'message': Contents of the actual message.
     *
     * @return bool
     *   true if the message was added, false otherwise.
     */
    msgAdd: function(msgId, message) {
        var success = false;
        var alter = this.publish({msgId: msgId, message: message}, 'msg_add_pre', this);
        if (msgId && (typeof(message) != undefined)) {
            this.msg[msgId] = {
                id: message.id,
                message: message.message,
            }
            success = true;
        }
        return success;
    },
    /**
     * Sets the user.language
     *
     * @param int language
     *   An AstroEmpires.Languages constant.
     *
     * @return bool
     *   true on success, false on failure to set.
     */
    setUserLanguage: function(language) {
        this.user.language = language;
        return true;
    },
    /**
     * Getes the user.language property value.
     *
     * @return mixed
     *   The value of the user.language property.
     */
    getUserLanguage: function(language) {
        return this.user.language;
    },
    /**
     * Sets the user.skin
     *
     * @param string skin
     *   Name of the skin.
     *
     * @return bool
     *   true on success, false on failure to set.
     */
    setUserSkin: function(skin) {
        this.user.skin = skin;
        return true;
    },
    /**
     * Getes the user.skin property value.
     *
     * @return mixed
     *   The value of the user.language property.
     */
    getUserSkin: function(skin) {
        return this.user.skin;
    }
});
<commit_msg>Fixing bug in msgExists method.<commit_after>
/**
 * @file
 * Communicate with astro empires web site.
 *
 * Astro Empires object will take care of requesting data, parsing the
 * response and storing the data.
 *
 * @author Bryan Hazelbaker <bryan.hazelbaker@gmail.com>
 * @version 0.1
 */

// Name space.
var AstroEmpires = {
    // All languages should namespace themselves here.
    Language: {},
    // All skins should namespace themselves here.
    Skin: {},
    /**
     * Simplify pulling values out of a regular expression into a single line.
     *
     * @param string $pattern
     *   The regular expression pattern.
     * @param string subject
     *   The subject source to search for pattern in.
     * @param int index
     *   The matching index value to return.
     * @param mixed default
     *   A default value to return if match is not found.
     *
     * @return string|bool
     *   The matched value or false on failure.
     */
    regex: function(pattern, subject, index, value) {
        var match;
        if (match = pattern.exec(subject)) {
            value = match[index];
        }
        return value;
    },
    AE: function(server, email, pass) {
        Observable.call(this);
        // User credentials.
        this.user = {
            server: server,
            email: email,
            pass: pass,
            skin: false,
            language: false
        };
        // Statistics to be retrieved from AE website.
        this.stats = {
            // credits: options['credits'],
            // income: options['income'],
            // fleetSize: options['fleetSize'],
            // technology: options['technology'],
            // level: options['level'],
            // rank: options['rank'],
        };
        this.msg = {};
        // Add our message types.
        this.typeAdd('get_credits');
    }
};
AstroEmpires.AE.prototype = Observable.prototype;
jQuery.extend(AstroEmpires.AE.prototype, {
    /**
     * Send and receive ajax requests.
     *
     * @param string url
     *   The fully formed url to access.
     * @param string type
     *   Type of access (POST, GET).
     * @params string
     *   Name value pairs just as during a GET request.
     */
    ajax: function(url, type, params) {
        var alter = this.publish({url: url, type: type, params: params}, 'ajax', this);
        var xhr = new XMLHttpRequest();
        thisAEO = this;
        xhr.onreadystatechange = function () {
            if (this.readyState == this.DONE) {
                // Everything went 'ok'.
                if (this.status == 200) {
                    // Check if we have been asked to login.
                    if (this.responseText.match(/<title>Login.*?<\/title>/i)) {
                        // Login.
                        thisAEO.login();
                        // Now try getting data again.
                        thisAEO.getData(url);
                    }
                    // We have not been asked to login, this should be the
                    // requested page.
                    else {
                        thisAEO.processData(url, this.responseText);
                    }
                }
                // A response header other than 200 (ok) was returned. Remember
                // redirects are transparantly handled by XMLHttpRequest().
                else {
                    console.log('Failed to open ' + url);
                }
            }
        },
        xhr.open(type, url, true);
        // No post parameters exist, this may be a simple GET.
        if (null === params) {
            xhr.send();
        }
        else {
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(params);
        }
    },
    /**
     *
     * Examine the results of an ajax request.
     *
     * Uses regular expressions to scrape values out of the html response.
     *
     * @param string url
     *   The url that generated this response.
     * @param string response
     *   The response itself containing html.
     */
    processData: function (url, data) {
        // Pull out page name and view mode, if any, from the url.
        var page = '';
        var view = '';
        if (match = /.*\/([^\.]+)\.aspx(.+view=([a-z]+))?/i.exec(url)) {
            page = '_' + match[1];
            view = match[3] ? '_' + match[3] : '';
            // Call any observers that do not care about skin.
            var results = this.publish({data: data, url: url}, 'url' + page + view, this);
            if (this.user.skin) {
                // Call observers that do care about skin.
                this.publish({data: data, url: url}, 'skin_' + this.user.skin + page + view, this);
            }
        }
        else {
            throw exception()
        }
    },
    /**
     * Relogin to the website.
     */
    login: function() {
        var params = "email=" + this.user.email +
            "&pass=" + this.user.pass +
            "&navigator=Netscape" +
            "&hostname=" + this.user.server +
            "&javascript=false" +
            "&post_back=false";
        this.ajax('http://' + this.user.server + '/login.aspx', 'POST', params);
    },
    /**
     * Retrieve all pages to be parsed and examined.
     *
     * @param string url
     *   When specified only request this url.
     */
    getData: function(url) {
        if (url) {
            // Only poll a single page for information. Normally if a previous
            // request was rejected do to invalid login, after sending
            // credentials a re-poll of the original request is required.
            this.ajax(url, 'GET');
        }
        else {
            // @todo transfer is asynchronus. Ensure we get language and skin first.
            this.ajax('http://' + this.user.server + '/account.aspx?view=display', 'GET');
            this.ajax('http://' + this.user.server + '/account.aspx', 'GET');
            this.ajax('http://' + this.user.server + '/board.aspx', 'GET');
        }
    },
    /**
     * Determine if a message identifier already exists or not.
     *
     * @param int msgId
     *   The unique message identifier.
     *
     * @return bool
     *   true if the message already exists, false otherwise.
     */
    msgExists: function(msgId) {
        var exists = false;
        if (typeof(this.msg[msgId]) != 'undefined') {
            exists = true;
        }
        return exists;
    },
    /**
     * Add a message to the message log.
     *
     * @param int msgId
     *   The unique message identifier. If this message already exists then it
     *   will be overwritten.
     * @param object message
     *   The message object consisting of:
     *     'id': The message id.
     *     'message': Contents of the actual message.
     *
     * @return bool
     *   true if the message was added, false otherwise.
     */
    msgAdd: function(msgId, message) {
        var success = false;
        var alter = this.publish({msgId: msgId, message: message}, 'msg_add_pre', this);
        if (msgId && (typeof(message) != 'undefined')) {
            this.msg[msgId] = {
                id: message.id,
                message: message.message,
            }
            success = true;
        }
        return success;
    },
    /**
     * Sets the user.language
     *
     * @param int language
     *   An AstroEmpires.Languages constant.
     *
     * @return bool
     *   true on success, false on failure to set.
     */
    setUserLanguage: function(language) {
        this.user.language = language;
        return true;
    },
    /**
     * Getes the user.language property value.
     *
     * @return mixed
     *   The value of the user.language property.
     */
    getUserLanguage: function(language) {
        return this.user.language;
    },
    /**
     * Sets the user.skin
     *
     * @param string skin
     *   Name of the skin.
     *
     * @return bool
     *   true on success, false on failure to set.
     */
    setUserSkin: function(skin) {
        this.user.skin = skin;
        return true;
    },
    /**
     * Getes the user.skin property value.
     *
     * @return mixed
     *   The value of the user.language property.
     */
    getUserSkin: function(skin) {
        return this.user.skin;
    }
});
<|endoftext|>


<commit_before>define(function (require, exports, module) {

  var currentRoom = require('plug/models/currentRoom'),
    request = require('extplug/util/request'),
    fnUtils = require('extplug/util/function'),
    Backbone = require('backbone'),
    Events = require('plug/core/Events');

  var RoomSettings = Backbone.Model.extend({

    constructor(ext) {
      Backbone.Model.call(this, {});

      this._loaded = {};

      this.load   = this.load.bind(this);
      this.unload = this.unload.bind(this);
      this.reload = this.reload.bind(this);

      currentRoom.on('change:description', this.reload);
      Events.on('room:joined', this.load);

      if (currentRoom.get('joined')) {
        this.load();
      }
    },

    load() {
      let description = currentRoom.get('description'),
        m = description.match(/(?:^|\n)@p3=(.*?)(?:\n|$)/);

      if (m) {
        if (this._loaded[m[1]]) {
          this.onLoad(this._loaded[m[1]]);
        }
        else {
          request.json(m[1])
            .then(response => {
              this._loaded[m[1]] = response;
              this.onLoad(response);
            })
            .fail(e => {
              let message = ''
              if (e.status === 0) {
                message += ' Your browser or an extension may be blocking its URL.';
              }
              else if (e.status >= 400) {
                message += ' Its URL is not accessible.';
              }
              else if (e.status) {
                message += ' Status code: ' + e.status;
              }
              Events.trigger('notify', 'icon-chat-system',
                             'Room Settings could not be loaded for this room.' + message);
            });
        }
      }
    },

    onLoad(settings) {
      this.clear();
      this.trigger('load', settings);
      this.set(settings);
    },

    unload() {
      this.clear();
      this.trigger('unload');
    },

    reload: function () {
      if (currentRoom.get('joined')) {
        this.unload();
        this.load();
      }
    },

    dispose() {
      this.unload();
      currentRoom.off('change:description', this.reload);
    }

  });

  module.exports = RoomSettings;

});
<commit_msg>fix RoomSettings loading<commit_after>define(function (require, exports, module) {

  var currentRoom = require('plug/models/currentRoom'),
    request = require('extplug/util/request'),
    fnUtils = require('extplug/util/function'),
    Backbone = require('backbone'),
    Events = require('plug/core/Events');

  var RoomSettings = Backbone.Model.extend({

    constructor(ext) {
      Backbone.Model.call(this, {});

      this._loaded = {};

      this.load   = this.load.bind(this);
      this.unload = this.unload.bind(this);
      this.reload = this.reload.bind(this);

      currentRoom.on('change:description', this.reload);

      if (currentRoom.get('joined')) {
        this.load();
      }
    },

    load() {
      let description = currentRoom.get('description'),
        m = description.match(/(?:^|\n)@p3=(.*?)(?:\n|$)/);

      if (m) {
        if (this._loaded[m[1]]) {
          this.onLoad(this._loaded[m[1]]);
        }
        else {
          request.json(m[1])
            .then(response => {
              this._loaded[m[1]] = response;
              this.onLoad(response);
            })
            .fail(e => {
              let message = ''
              if (e.status === 0) {
                message += ' Your browser or an extension may be blocking its URL.';
              }
              else if (e.status >= 400) {
                message += ' Its URL is not accessible.';
              }
              else if (e.status) {
                message += ' Status code: ' + e.status;
              }
              Events.trigger('notify', 'icon-chat-system',
                             'Room Settings could not be loaded for this room.' + message);
            });
        }
      }
    },

    onLoad(settings) {
      this.clear();
      this.trigger('load', settings);
      this.set(settings);
    },

    unload() {
      this.clear();
      this.trigger('unload');
    },

    reload() {
      this.unload();
      // "joined" is set *after* "description"
      _.defer(() => {
        if (currentRoom.get('joined')) {
          this.load();
        }
      });
    },

    dispose() {
      this.unload();
      currentRoom.off('change:description', this.reload);
    }

  });

  module.exports = RoomSettings;

});
<|endoftext|>


<commit_before>import NylasStore from 'nylas-store';
import keytar from 'keytar';
import {ipcRenderer} from 'electron';
import request from 'request';
import url from 'url'

import Actions from '../actions';
import AccountStore from './account-store';

const configIdentityKey = "nylas.identity";
const keytarServiceName = 'Nylas';
const keytarIdentityKey = 'Nylas Account';

const State = {
  Trialing: 'Trialing',
  Valid: 'Valid',
  Lapsed: 'Lapsed',
};

class IdentityStore extends NylasStore {

  constructor() {
    super();

    this._subscriptionRequiredAfter = null;

    NylasEnv.config.onDidChange('env', this._onEnvChanged);
    this._onEnvChanged();

    this.listenTo(AccountStore, () => { this.trigger() });
    this.listenTo(Actions.setNylasIdentity, this._onSetNylasIdentity);
    this.listenTo(Actions.logoutNylasIdentity, this._onLogoutNylasIdentity);

    NylasEnv.config.onDidChange(configIdentityKey, () => {
      this._loadIdentity();
      this.trigger();
    });

    this._loadIdentity();

    if (NylasEnv.isWorkWindow() && ['staging', 'production'].includes(NylasEnv.config.get('env'))) {
      setInterval(this.refreshStatus, 1000 * 60 * 60);
      this.refreshStatus();
    }
  }

  _onEnvChanged = () => {
    const env = NylasEnv.config.get('env')
    if (['development', 'local'].includes(env)) {
      this.URLRoot = "http://localhost:5009";
    } else if (env === 'experimental') {
      this.URLRoot = "https://billing-experimental.nylas.com";
    } else if (env === 'staging') {
      this.URLRoot = "https://billing-staging.nylas.com";
    } else {
      this.URLRoot = "https://billing.nylas.com";
    }
  }

  _loadIdentity() {
    this._identity = NylasEnv.config.get(configIdentityKey);
    if (this._identity) {
      this._identity.token = keytar.getPassword(keytarServiceName, keytarIdentityKey);
    }
  }

  get State() {
    return State;
  }

  identity() {
    return this._identity;
  }

  identityId() {
    if (!this._identity) {
      return null;
    }
    return this._identity.id;
  }

  subscriptionState() {
    if (!this._identity || (this._identity.valid_until === null)) {
      return State.Trialing;
    }
    if (new Date(this._identity.valid_until) < new Date()) {
      return State.Lapsed;
    }
    return State.Valid;
  }

  daysUntilSubscriptionRequired() {
    if (!this._subscriptionRequiredAfter) {
      return null;
    }
    return Math.max(0, Math.round((this._subscriptionRequiredAfter.getTime() - Date.now()) / (1000 * 24 * 60 * 60)));
  }

  refreshStatus = () => {
    return Promise.all([
      this.fetchIdentity(),
      Promise.all(AccountStore.accounts().map((a) =>
        this.fetchSubscriptionRequiredDate(a))
      ).then((subscriptionRequriedDates) => {
        this._subscriptionRequiredAfter = subscriptionRequriedDates.sort().shift();
      }),
    ]).catch((err) => {
      console.error(`Unable to refresh IdentityStore status: ${err.message}`)
    });
  }

  /**
   * This passes utm_source, utm_campaign, and utm_content params to the
   * N1 billing site. Please reference:
   * https://paper.dropbox.com/doc/Analytics-ID-Unification-oVDTkakFsiBBbk9aeuiA3
   * for the full list of utm_ labels.
   */
  fetchSingleSignOnURL(path, {source, campaign, content}) {
    if (!this._identity) {
      return Promise.reject(new Error("fetchSingleSignOnURL: no identity set."));
    }

    const qs = {utm_medium: "N1"}
    if (source) { qs.utm_source = source }
    if (campaign) { qs.utm_campaign = campaign }
    if (content) { qs.utm_content = content }

    let pathWithUtm = url.parse(path, true);
    pathWithUtm.query = Object.assign({}, qs, (pathWithUtm.query || {}));
    pathWithUtm = url.format({
      pathname: pathWithUtm.pathname,
      query: pathWithUtm.query,
    })

    if (!pathWithUtm.startsWith('/')) {
      return Promise.reject(new Error("fetchSingleSignOnURL: path must start with a leading slash."));
    }

    return new Promise((resolve) => {
      request({
        method: 'POST',
        url: `${this.URLRoot}/n1/login-link`,
        qs: qs,
        json: true,
        body: {
          next_path: pathWithUtm,
          account_token: this._identity.token,
        },
      }, (error, response = {}, body) => {
        if (error || !body.startsWith('http')) {
          // Single-sign on attempt failed. Rather than churn the user right here,
          // at least try to open the page directly in the browser.
          resolve(`${this.URLRoot}${path}`);
        } else {
          resolve(body);
        }
      });
    });
  }

  fetchSubscriptionRequiredDate = (account) => {
    return this.fetchPath(`/n1/account/${account.id}`).then((json) => {
      const date = json.subscription_required_after ? new Date(json.subscription_required_after) : null;
      Promise.resolve(date);
    });
  }

  fetchIdentity = () => {
    if (!this._identity || !this._identity.token) {
      return Promise.resolve();
    }
    return this.fetchPath('/n1/user').then((json) => {
      const nextIdentity = Object.assign({}, this._identity, json);
      this._onSetNylasIdentity(nextIdentity)
    });
  }

  fetchPath = (path) => {
    return new Promise((resolve, reject) => {
      request({
        method: 'GET',
        url: `${this.URLRoot}${path}`,
        auth: {
          username: this._identity.token,
          password: '',
          sendImmediately: true,
        },
      }, (error, response = {}, body) => {
        if (response.statusCode === 200) {
          try {
            return resolve(JSON.parse(body));
          } catch (err) {
            NylasEnv.reportError(new Error(`IdentityStore.fetchPath: ${path} ${err.message}.`))
          }
        }
        return reject(error || new Error(`IdentityStore.fetchPath: ${path} ${response.statusCode}.`));
      });
    });
  }

  _onLogoutNylasIdentity = () => {
    keytar.deletePassword(keytarServiceName, keytarIdentityKey);
    NylasEnv.config.unset(configIdentityKey);
    ipcRenderer.send('command', 'application:relaunch-to-initial-windows');
  }

  _onSetNylasIdentity = (identity) => {
    keytar.replacePassword(keytarServiceName, keytarIdentityKey, identity.token);
    delete identity.token;
    NylasEnv.config.set(configIdentityKey, identity);
  }
}

export default new IdentityStore()
<commit_msg>fix(login-links): Cap req to 1.5s, ensure pages always open<commit_after>import NylasStore from 'nylas-store';
import keytar from 'keytar';
import {ipcRenderer} from 'electron';
import request from 'request';
import url from 'url'

import Actions from '../actions';
import AccountStore from './account-store';

const configIdentityKey = "nylas.identity";
const keytarServiceName = 'Nylas';
const keytarIdentityKey = 'Nylas Account';

const State = {
  Trialing: 'Trialing',
  Valid: 'Valid',
  Lapsed: 'Lapsed',
};

class IdentityStore extends NylasStore {

  constructor() {
    super();

    this._subscriptionRequiredAfter = null;

    NylasEnv.config.onDidChange('env', this._onEnvChanged);
    this._onEnvChanged();

    this.listenTo(AccountStore, () => { this.trigger() });
    this.listenTo(Actions.setNylasIdentity, this._onSetNylasIdentity);
    this.listenTo(Actions.logoutNylasIdentity, this._onLogoutNylasIdentity);

    NylasEnv.config.onDidChange(configIdentityKey, () => {
      this._loadIdentity();
      this.trigger();
    });

    this._loadIdentity();

    if (NylasEnv.isWorkWindow() && ['staging', 'production'].includes(NylasEnv.config.get('env'))) {
      setInterval(this.refreshStatus, 1000 * 60 * 60);
      this.refreshStatus();
    }
  }

  _onEnvChanged = () => {
    const env = NylasEnv.config.get('env')
    if (['development', 'local'].includes(env)) {
      this.URLRoot = "http://localhost:5009";
    } else if (env === 'experimental') {
      this.URLRoot = "https://billing-experimental.nylas.com";
    } else if (env === 'staging') {
      this.URLRoot = "https://billing-staging.nylas.com";
    } else {
      this.URLRoot = "https://billing.nylas.com";
    }
  }

  _loadIdentity() {
    this._identity = NylasEnv.config.get(configIdentityKey);
    if (this._identity) {
      this._identity.token = keytar.getPassword(keytarServiceName, keytarIdentityKey);
    }
  }

  get State() {
    return State;
  }

  identity() {
    return this._identity;
  }

  identityId() {
    if (!this._identity) {
      return null;
    }
    return this._identity.id;
  }

  subscriptionState() {
    if (!this._identity || (this._identity.valid_until === null)) {
      return State.Trialing;
    }
    if (new Date(this._identity.valid_until) < new Date()) {
      return State.Lapsed;
    }
    return State.Valid;
  }

  daysUntilSubscriptionRequired() {
    if (!this._subscriptionRequiredAfter) {
      return null;
    }
    return Math.max(0, Math.round((this._subscriptionRequiredAfter.getTime() - Date.now()) / (1000 * 24 * 60 * 60)));
  }

  refreshStatus = () => {
    return Promise.all([
      this.fetchIdentity(),
      Promise.all(AccountStore.accounts().map((a) =>
        this.fetchSubscriptionRequiredDate(a))
      ).then((subscriptionRequriedDates) => {
        this._subscriptionRequiredAfter = subscriptionRequriedDates.sort().shift();
      }),
    ]).catch((err) => {
      console.error(`Unable to refresh IdentityStore status: ${err.message}`)
    });
  }

  /**
   * This passes utm_source, utm_campaign, and utm_content params to the
   * N1 billing site. Please reference:
   * https://paper.dropbox.com/doc/Analytics-ID-Unification-oVDTkakFsiBBbk9aeuiA3
   * for the full list of utm_ labels.
   */
  fetchSingleSignOnURL(path, {source, campaign, content}) {
    if (!this._identity) {
      return Promise.reject(new Error("fetchSingleSignOnURL: no identity set."));
    }

    const qs = {utm_medium: "N1"}
    if (source) { qs.utm_source = source }
    if (campaign) { qs.utm_campaign = campaign }
    if (content) { qs.utm_content = content }

    let pathWithUtm = url.parse(path, true);
    pathWithUtm.query = Object.assign({}, qs, (pathWithUtm.query || {}));
    pathWithUtm = url.format({
      pathname: pathWithUtm.pathname,
      query: pathWithUtm.query,
    })

    if (!pathWithUtm.startsWith('/')) {
      return Promise.reject(new Error("fetchSingleSignOnURL: path must start with a leading slash."));
    }

    return new Promise((resolve) => {
      request({
        method: 'POST',
        url: `${this.URLRoot}/n1/login-link`,
        qs: qs,
        json: true,
        timeout: 1500,
        body: {
          next_path: pathWithUtm,
          account_token: this._identity.token,
        },
      }, (error, response = {}, body) => {
        if (error || !body.startsWith('http')) {
          // Single-sign on attempt failed. Rather than churn the user right here,
          // at least try to open the page directly in the browser.
          resolve(`${this.URLRoot}${path}`);
        } else {
          resolve(body);
        }
      });
    });
  }

  fetchSubscriptionRequiredDate = (account) => {
    return this.fetchPath(`/n1/account/${account.id}`).then((json) => {
      const date = json.subscription_required_after ? new Date(json.subscription_required_after) : null;
      Promise.resolve(date);
    });
  }

  fetchIdentity = () => {
    if (!this._identity || !this._identity.token) {
      return Promise.resolve();
    }
    return this.fetchPath('/n1/user').then((json) => {
      const nextIdentity = Object.assign({}, this._identity, json);
      this._onSetNylasIdentity(nextIdentity)
    });
  }

  fetchPath = (path) => {
    return new Promise((resolve, reject) => {
      request({
        method: 'GET',
        url: `${this.URLRoot}${path}`,
        auth: {
          username: this._identity.token,
          password: '',
          sendImmediately: true,
        },
      }, (error, response = {}, body) => {
        if (response.statusCode === 200) {
          try {
            return resolve(JSON.parse(body));
          } catch (err) {
            NylasEnv.reportError(new Error(`IdentityStore.fetchPath: ${path} ${err.message}.`))
          }
        }
        return reject(error || new Error(`IdentityStore.fetchPath: ${path} ${response.statusCode}.`));
      });
    });
  }

  _onLogoutNylasIdentity = () => {
    keytar.deletePassword(keytarServiceName, keytarIdentityKey);
    NylasEnv.config.unset(configIdentityKey);
    ipcRenderer.send('command', 'application:relaunch-to-initial-windows');
  }

  _onSetNylasIdentity = (identity) => {
    keytar.replacePassword(keytarServiceName, keytarIdentityKey, identity.token);
    delete identity.token;
    NylasEnv.config.set(configIdentityKey, identity);
  }
}

export default new IdentityStore()
<|endoftext|>


<commit_before>/**
 * Developer: Stepan Burguchev
 * Date: 10/13/2014
 * Copyright: 2009-2016 Comindware®
 *       All Rights Reserved
 * Published under the MIT license
 */

import { Handlebars, keypress } from 'lib';
import { helpers } from 'utils';
import LocalizationService from '../../services/LocalizationService';
import BaseItemEditorView from './base/BaseItemEditorView';
import template from './templates/textEditor.hbs';
import formRepository from '../formRepository';

const changeMode = {
    blur: 'blur',
    keydown: 'keydown'
};

const defaultOptions = function() {
    return {
        changeMode: 'blur',
        emptyPlaceholder: LocalizationService.get('CORE.FORM.EDITORS.TEXTEDITOR.PLACEHOLDER'),
        maxLength: null,
        mask: null,
        maskPlaceholder: '_',
        maskOptions: {},
        showTitle: true
    };
};

/**
 * @name TextEditorView
 * @memberof module:core.form.editors
 * @class Однострочный текстовый редактор. Поддерживаемый тип данных: String.
 * @extends module:core.form.editors.base.BaseEditorView
 * @param {Object} options Options object. All the properties of {@link module:core.form.editors.base.BaseEditorView BaseEditorView} class are also supported.
 * @param {Number|null} [options.maxLength=null] Максимальное количество символов. Если null, не ограничено.
 * @param {String} [options.changeMode='blur'] Определяет момент обновления значения редактора:

 *
'keydown' - при нажатии клавиши.

 *
'blur' - при потери фокуса.

 * @param {String} [options.emptyPlaceholder='Field is empty'] Текст placeholder.
 * @param {String} [options.mask=null] Если установлено, строка используется как опция mask плагина
 * [jquery.inputmask](https://github.com/RobinHerbots/jquery.inputmask).
 * @param {String} [options.maskPlaceholder='_'] При установленной опции mask, используется как опция placeholder плагина.
 * @param {Object} [options.maskOptions={}] При установленной опции mask, используется для передачи дополнительных опций плагина.
 * @param {Boolean} {options.showTitle=true} Whether to show title attribute.
 * */
formRepository.editors.Text = BaseItemEditorView.extend(/** @lends module:core.form.editors.TextEditorView.prototype */{
    initialize(options) {
        options = options || {};
        const defaults = defaultOptions();
        if (options.schema) {
            _.extend(this.options, defaults, _.pick(options.schema, _.keys(defaults)));
        } else {
            _.extend(this.options, defaults, _.pick(options || {}, _.keys(defaults)));
        }

        this.placeholder = this.options.emptyPlaceholder;
    },

    onShow() {
        if (this.options.mask) {
            this.ui.input.inputmask(_.extend({
                mask: this.options.mask,
                placeholder: this.options.maskPlaceholder,
                autoUnmask: true
            }, this.options.maskOptions || {}));
        }
    },

    focusElement: '.js-input',

    ui: {
        input: '.js-input'
    },

    className: 'editor',

    template: Handlebars.compile(template),

    templateHelpers() {
        return _.extend(this.options, {
            title: this.model.get('value') ? this.model.get('value') : ''
        });
    },

    events: {
        'keyup @ui.input': '__keyup',
        'change @ui.input': '__change',
        'click .js-clear-button': '__clear'
    },

    __keyup() {
        if (this.options.changeMode === changeMode.keydown) {
            this.__value(this.ui.input.val(), false, true);
        }

        this.trigger('keyup', this);
    },

    __change() {
        this.__value(this.ui.input.val(), false, true);
    },

    __clear() {
        this.__value(null, true, true);
        this.focus();
        return false;
    },

    setValue(value) {
        this.__value(value, true, false);
    },

    setPermissions(enabled, readonly) {
        BaseItemEditorView.prototype.setPermissions.call(this, enabled, readonly);
        this.setPlaceholder();
    },

    setPlaceholder() {
        if (!this.getEnabled() || this.getReadonly()) {
            this.placeholder = '';
        } else {
            this.placeholder = this.options.emptyPlaceholder;
        }

        this.ui.input.prop('placeholder', this.placeholder);
    },

    __setEnabled(enabled) {
        BaseItemEditorView.prototype.__setEnabled.call(this, enabled);
        this.ui.input.prop('disabled', !enabled);
    },

    __setReadonly(readonly) {
        BaseItemEditorView.prototype.__setReadonly.call(this, readonly);
        if (this.getEnabled()) {
            this.ui.input.prop('readonly', readonly);
            this.ui.input.prop('tabindex', readonly ? -1 : 0);
        }
    },

    onRender() {
        const value = this.getValue() || '';
        this.ui.input.val(value);
        if (this.options.showTitle) {
            this.$el.prop('title', value);
        }
        // Keyboard shortcuts listener
        if (this.keyListener) {
            this.keyListener.reset();
        }
        this.keyListener = new keypress.Listener(this.ui.input[0]);
    },

    /**
     * Позволяет добавить callback-функцию на ввод определенной клавиши или комбинации клавиш. Использует метод simple_combo плагина
     * [Keypress](https://dmauro.github.io/Keypress/).
     * @param {String} key Комбинация клавиш или несколько комбинаций, разделенных запятыми.
     * Полный список с названиями клавиш указан в исходном файле плагина:
     * [keypress.coffee](https://github.com/dmauro/Keypress/blob/master/keypress.coffee#L750-912).
     * @param {String} callback Callback-функция, вызываемая по срабатыванию комбо.
     * */
    addKeyboardListener(key, callback) {
        if (!this.keyListener) {
            helpers.throwInvalidOperationError('You must apply keyboard listener after \'render\' event has happened.');
        }
        const keys = key.split(',');
        _.each(keys, k => {
            this.keyListener.simple_combo(k, callback);
        });
    },

    __value(value, updateUi, triggerChange) {
        if (this.value === value) {
            return;
        }
        this.value = value;

        if (this.options.showTitle) {
            this.$el.prop('title', value);
        }
        if (updateUi) {
            this.ui.input.val(value);
        }
        if (triggerChange) {
            this.__triggerChange();
        }
    },

    /**
     * Focuses the editor's input and selects all the text in it.
     * */
    select() {
        this.ui.input.select();
    },

    deselect() {
        this.ui.input.deselect();
    }
});

export default formRepository.editors.Text;
<commit_msg>text editor fix<commit_after>/**
 * Developer: Stepan Burguchev
 * Date: 10/13/2014
 * Copyright: 2009-2016 Comindware®
 *       All Rights Reserved
 * Published under the MIT license
 */

import { Handlebars, keypress } from 'lib';
import { helpers } from 'utils';
import LocalizationService from '../../services/LocalizationService';
import BaseItemEditorView from './base/BaseItemEditorView';
import template from './templates/textEditor.hbs';
import formRepository from '../formRepository';

const changeMode = {
    blur: 'blur',
    keydown: 'keydown'
};

const defaultOptions = function() {
    return {
        changeMode: 'blur',
        emptyPlaceholder: LocalizationService.get('CORE.FORM.EDITORS.TEXTEDITOR.PLACEHOLDER'),
        maxLength: null,
        mask: null,
        maskPlaceholder: '_',
        maskOptions: {},
        showTitle: true
    };
};

/**
 * @name TextEditorView
 * @memberof module:core.form.editors
 * @class Однострочный текстовый редактор. Поддерживаемый тип данных: String.
 * @extends module:core.form.editors.base.BaseEditorView
 * @param {Object} options Options object. All the properties of {@link module:core.form.editors.base.BaseEditorView BaseEditorView} class are also supported.
 * @param {Number|null} [options.maxLength=null] Максимальное количество символов. Если null, не ограничено.
 * @param {String} [options.changeMode='blur'] Определяет момент обновления значения редактора:

 *
'keydown' - при нажатии клавиши.

 *
'blur' - при потери фокуса.

 * @param {String} [options.emptyPlaceholder='Field is empty'] Текст placeholder.
 * @param {String} [options.mask=null] Если установлено, строка используется как опция mask плагина
 * [jquery.inputmask](https://github.com/RobinHerbots/jquery.inputmask).
 * @param {String} [options.maskPlaceholder='_'] При установленной опции mask, используется как опция placeholder плагина.
 * @param {Object} [options.maskOptions={}] При установленной опции mask, используется для передачи дополнительных опций плагина.
 * @param {Boolean} {options.showTitle=true} Whether to show title attribute.
 * */
formRepository.editors.Text = BaseItemEditorView.extend(/** @lends module:core.form.editors.TextEditorView.prototype */{
    initialize(options) {
        options = options || {};
        const defaults = defaultOptions();
        if (options.schema) {
            _.extend(this.options, defaults, _.pick(options.schema, _.keys(defaults)));
        } else {
            _.extend(this.options, defaults, _.pick(options || {}, _.keys(defaults)));
        }

        this.placeholder = this.options.emptyPlaceholder;
    },

    onShow() {
        if (this.options.mask) {
            this.ui.input.inputmask(_.extend({
                mask: this.options.mask,
                placeholder: this.options.maskPlaceholder,
                autoUnmask: true
            }, this.options.maskOptions || {}));
        }
    },

    focusElement: '.js-input',

    ui: {
        input: '.js-input'
    },

    className: 'editor',

    template: Handlebars.compile(template),

    templateHelpers() {
        return _.extend(this.options, {
            title: this.value || ''
        });
    },

    events: {
        'keyup @ui.input': '__keyup',
        'change @ui.input': '__change',
        'click .js-clear-button': '__clear'
    },

    __keyup() {
        if (this.options.changeMode === changeMode.keydown) {
            this.__value(this.ui.input.val(), false, true);
        }

        this.trigger('keyup', this);
    },

    __change() {
        this.__value(this.ui.input.val(), false, true);
    },

    __clear() {
        this.__value(null, true, true);
        this.focus();
        return false;
    },

    setValue(value) {
        this.__value(value, true, false);
    },

    setPermissions(enabled, readonly) {
        BaseItemEditorView.prototype.setPermissions.call(this, enabled, readonly);
        this.setPlaceholder();
    },

    setPlaceholder() {
        if (!this.getEnabled() || this.getReadonly()) {
            this.placeholder = '';
        } else {
            this.placeholder = this.options.emptyPlaceholder;
        }

        this.ui.input.prop('placeholder', this.placeholder);
    },

    __setEnabled(enabled) {
        BaseItemEditorView.prototype.__setEnabled.call(this, enabled);
        this.ui.input.prop('disabled', !enabled);
    },

    __setReadonly(readonly) {
        BaseItemEditorView.prototype.__setReadonly.call(this, readonly);
        if (this.getEnabled()) {
            this.ui.input.prop('readonly', readonly);
            this.ui.input.prop('tabindex', readonly ? -1 : 0);
        }
    },

    onRender() {
        const value = this.getValue() || '';
        this.ui.input.val(value);
        if (this.options.showTitle) {
            this.$el.prop('title', value);
        }
        // Keyboard shortcuts listener
        if (this.keyListener) {
            this.keyListener.reset();
        }
        this.keyListener = new keypress.Listener(this.ui.input[0]);
    },

    /**
     * Позволяет добавить callback-функцию на ввод определенной клавиши или комбинации клавиш. Использует метод simple_combo плагина
     * [Keypress](https://dmauro.github.io/Keypress/).
     * @param {String} key Комбинация клавиш или несколько комбинаций, разделенных запятыми.
     * Полный список с названиями клавиш указан в исходном файле плагина:
     * [keypress.coffee](https://github.com/dmauro/Keypress/blob/master/keypress.coffee#L750-912).
     * @param {String} callback Callback-функция, вызываемая по срабатыванию комбо.
     * */
    addKeyboardListener(key, callback) {
        if (!this.keyListener) {
            helpers.throwInvalidOperationError('You must apply keyboard listener after \'render\' event has happened.');
        }
        const keys = key.split(',');
        _.each(keys, k => {
            this.keyListener.simple_combo(k, callback);
        });
    },

    __value(value, updateUi, triggerChange) {
        if (this.value === value) {
            return;
        }
        this.value = value;

        if (this.options.showTitle) {
            this.$el.prop('title', value);
        }
        if (updateUi) {
            this.ui.input.val(value);
        }
        if (triggerChange) {
            this.__triggerChange();
        }
    },

    /**
     * Focuses the editor's input and selects all the text in it.
     * */
    select() {
        this.ui.input.select();
    },

    deselect() {
        this.ui.input.deselect();
    }
});

export default formRepository.editors.Text;
<|endoftext|>


<commit_before>import { useAuthRequest } from 'expo-auth-session/providers/google';
import React from 'react';
import config from '../../config';
import { useAsyncError } from '../../utilities';

export const isAuthenticationExpired = (auth) => {
  const expireTime = (auth.issuedAt + parseInt(auth.expiresIn)) * 1000;

  return expireTime < Date.now();
};

export default function useGoogleProvider() {
  const authentication = React.useRef(null);
  const exchangePromise = React.useRef(null);
  const [_, result, promptAsync] = useAuthRequest(
    {
      expoClientId: process.env.GOOGLE_OAUTH_CLIENT_ID_EXPO_GO,
      androidClientId: process.env.GOOGLE_OAUTH_CLIENT_ID_ANDROID,
      iosClientId: process.env.GOOGLE_OAUTH_CLIENT_ID_IOS,
      selectAccount: true,
    },
    {
      path: config.OAUTH_REDIRECT_SCREEN,
    }
  );
  const throwAsyncError = useAsyncError();

  React.useEffect(() => {
    if (!exchangePromise.current) return;
    if (result?.type === 'success' && result.authentication) {
      authentication.current = result.authentication;
      exchangePromise.current.resolve(result.authentication);
    } else if (result?.type === 'error' || result?.type === 'cancel') {
      authentication.current = null;
      exchangePromise.current.reject(result?.error);
    }
  }, [result]);
  const getAuthentication = async () => {
    try {
      const promptResponse = await promptAsync();

      if (promptResponse?.type === 'success') {
        authentication.current = promptResponse.authentication;
        console.log('authentication successful');
        console.log(promptResponse);
        if (!promptResponse.authentication) {
          exchangePromise.current = new Promise();

          return exchangePromise.current;
        }

        return promptResponse.authentication;
      } else if (promptResponse?.type === 'cancel') {
        return null;
      } else {
        new Error(`${promptResponse.type} ${promptResponse.message ? promptResponse.message : null}`);
      }
    } catch (error) {
      throwAsyncError(error);
    }
  };

  const logIn = async () => {
    const { accessToken } = await getAuthentication();

    if (!accessToken) {
      return null;
    }

    let user;
    try {
      const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      });
      user = await response.json();
    } catch (error) {
      throwAsyncError(error);
    }

    return user;
  };

  const logOut = () => {
    authentication.current = null;
  };

  const getBearerToken = async () => {
    const prefix = `${config.PROVIDER_NAMES.google}:Bearer `;
    if (authentication.current && !isAuthenticationExpired(authentication.current)) {
      return prefix + authentication.current.accessToken;
    }

    const { accessToken } = await getAuthentication();

    if (accessToken) {
      return prefix + accessToken;
    } else {
      throwAsyncError(new Error('No access token'));
    }
  };

  return { logIn, logOut, getBearerToken };
}
<commit_msg>fix(front): fix promise<commit_after>import { useAuthRequest } from 'expo-auth-session/providers/google';
import React from 'react';
import config from '../../config';
import { useAsyncError } from '../../utilities';

export const isAuthenticationExpired = (auth) => {
  const expireTime = (auth.issuedAt + parseInt(auth.expiresIn)) * 1000;

  return expireTime < Date.now();
};

export default function useGoogleProvider() {
  const authentication = React.useRef(null);
  const exchangePromise = React.useRef(null);
  const [_, result, promptAsync] = useAuthRequest(
    {
      expoClientId: process.env.GOOGLE_OAUTH_CLIENT_ID_EXPO_GO,
      androidClientId: process.env.GOOGLE_OAUTH_CLIENT_ID_ANDROID,
      iosClientId: process.env.GOOGLE_OAUTH_CLIENT_ID_IOS,
      selectAccount: true,
    },
    {
      path: config.OAUTH_REDIRECT_SCREEN,
    }
  );
  const throwAsyncError = useAsyncError();

  React.useEffect(() => {
    if (!exchangePromise.current) return;
    if (result?.type === 'success' && result.authentication) {
      authentication.current = result.authentication;
      exchangePromise.current.resolve(result.authentication);
    } else if (result?.type === 'error' || result?.type === 'cancel') {
      authentication.current = null;
      exchangePromise.current.reject(result?.error);
    }
  }, [result]);
  const getAuthentication = async () => {
    try {
      const promptResponse = await promptAsync();

      if (promptResponse?.type === 'success') {
        authentication.current = promptResponse.authentication;
        console.log('authentication successful');
        console.log(promptResponse);
        if (!promptResponse.authentication) {
          exchangePromise.current = new Promise((resolve, reject) => {
            exchangePromise.current = { resolve, reject };
          });

          return exchangePromise.current;
        }

        return promptResponse.authentication;
      } else if (promptResponse?.type === 'cancel') {
        return null;
      } else {
        new Error(`${promptResponse.type} ${promptResponse.message ? promptResponse.message : null}`);
      }
    } catch (error) {
      throwAsyncError(error);
    }
  };

  const logIn = async () => {
    const { accessToken } = await getAuthentication();

    if (!accessToken) {
      return null;
    }

    let user;
    try {
      const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      });
      user = await response.json();
    } catch (error) {
      throwAsyncError(error);
    }

    return user;
  };

  const logOut = () => {
    authentication.current = null;
  };

  const getBearerToken = async () => {
    const prefix = `${config.PROVIDER_NAMES.google}:Bearer `;
    if (authentication.current && !isAuthenticationExpired(authentication.current)) {
      return prefix + authentication.current.accessToken;
    }

    const { accessToken } = await getAuthentication();

    if (accessToken) {
      return prefix + accessToken;
    } else {
      throwAsyncError(new Error('No access token'));
    }
  };

  return { logIn, logOut, getBearerToken };
}
<|endoftext|>


<commit_before>import {
  GraphQLObjectType,
  GraphQLFloat,
  GraphQLList,
  GraphQLUnionType,
} from 'graphql';

import Answer from 'graphql/models/Answer';
import Rumor from 'graphql/models/Rumor';
import CrawledDoc from 'graphql/models/CrawledDoc';

function scoredDocFactory(name, type) {
  return new GraphQLObjectType({
    name,
    fields: {
      score: { type: GraphQLFloat },
      doc: { type },
    },
  });
}

const ResultDocument = new GraphQLUnionType({
  name: 'ResultDocument',
  types: [Rumor, Answer, CrawledDoc],
  resolveType(result) {
    if (!result) return CrawledDoc;

    switch (result._type) {
      case 'RUMOR': return Rumor;
      case 'ANSWER': return Answer;
      default: return CrawledDoc;
    }
  },
});

function theBestDoc(scoredDocs, singleDocThreshold) {
  // If there is only one doc and has score over threshold,
  // or the first match is 2x larger than the second match,
  // return the first match.
  if (
    (scoredDocs.length === 1 && scoredDocs[0].score > singleDocThreshold) ||
    (scoredDocs.length > 1 && scoredDocs[0].score > 2 * scoredDocs[1].score)
  ) {
    return scoredDocs[0];
  }

  return null;
}

export default new GraphQLObjectType({
  name: 'SearchResult',
  fields: () => ({
    rumors: { type: new GraphQLList(scoredDocFactory('ScoredRumor', Rumor)) },
    answers: { type: new GraphQLList(scoredDocFactory('ScoredAnswer', Answer)) },
    crawledDoc: { type: new GraphQLList(scoredDocFactory('ScoredCrawledDoc', CrawledDoc)) },
    suggestedResult: {
      description: 'The document that is the best match in this search.',
      type: ResultDocument,
      resolve({ rumors: scoredRumors, answers: scoredAnswers, crawledDocs: scoredCrawledDocs }) {
        const bestScoredRumor = theBestDoc(scoredRumors, 15);
        if (bestScoredRumor) {
          return {
            ...bestScoredRumor.doc, _type: 'RUMOR',
          };
        }

        const bestScoredAnswer = theBestDoc(scoredAnswers, 10);
        if (bestScoredAnswer) {
          return {
            ...bestScoredAnswer.doc, _type: 'ANSWER',
          };
        }

        const bestScoredCrawledDoc = theBestDoc(scoredCrawledDocs, 0.8);
        if (bestScoredCrawledDoc) {
          return {
            ...bestScoredCrawledDoc.doc, _type: 'CRAWLED_DOC',
          };
        }

        return null;
      },
    },
  }),
});
<commit_msg>Enable bot to return the type when bestScoredRumor does not have any answer. Fixes #11.<commit_after>import {
  GraphQLObjectType,
  GraphQLFloat,
  GraphQLList,
  GraphQLUnionType,
} from 'graphql';

import Answer from 'graphql/models/Answer';
import Rumor from 'graphql/models/Rumor';
import CrawledDoc from 'graphql/models/CrawledDoc';

function scoredDocFactory(name, type) {
  return new GraphQLObjectType({
    name,
    fields: {
      score: { type: GraphQLFloat },
      doc: { type },
    },
  });
}

const ResultDocument = new GraphQLUnionType({
  name: 'ResultDocument',
  types: [Rumor, Answer, CrawledDoc],
  resolveType(result) {
    if (!result) return CrawledDoc;

    switch (result._type) {
      case 'RUMOR': return Rumor;
      case 'ANSWER': return Answer;
      default: return CrawledDoc;
    }
  },
});

function theBestDoc(scoredDocs, singleDocThreshold) {
  // If there is only one doc and has score over threshold,
  // or the first match is 2x larger than the second match,
  // return the first match.
  if (
    (scoredDocs.length === 1 && scoredDocs[0].score > singleDocThreshold) ||
    (scoredDocs.length > 1 && scoredDocs[0].score > 1.6 * scoredDocs[1].score)
  ) {
    return scoredDocs[0];
  }

  return null;
}

export default new GraphQLObjectType({
  name: 'SearchResult',
  fields: () => ({
    rumors: { type: new GraphQLList(scoredDocFactory('ScoredRumor', Rumor)) },
    answers: { type: new GraphQLList(scoredDocFactory('ScoredAnswer', Answer)) },
    crawledDoc: { type: new GraphQLList(scoredDocFactory('ScoredCrawledDoc', CrawledDoc)) },
    suggestedResult: {
      description: 'The document that is the best match in this search.',
      type: ResultDocument,
      resolve({ rumors: scoredRumors, answers: scoredAnswers, crawledDocs: scoredCrawledDocs }) {
        const bestScoredRumor = theBestDoc(scoredRumors, 15);
        if (bestScoredRumor && bestScoredRumor.doc.answerIds.length) {
          return {
            ...bestScoredRumor.doc, _type: 'RUMOR',
          };
        }

        const bestScoredAnswer = theBestDoc(scoredAnswers, 10);
        if (bestScoredAnswer) {
          return {
            ...bestScoredAnswer.doc, _type: 'ANSWER',
          };
        }

        const bestScoredCrawledDoc = theBestDoc(scoredCrawledDocs, 0.8);
        if (bestScoredCrawledDoc) {
          return {
            ...bestScoredCrawledDoc.doc, _type: 'CRAWLED_DOC',
          };
        }

        return null;
      },
    },
  }),
});
<|endoftext|>