<commit_before>var
	dgram = require('dgram'),
	EventEmitter = require('events').EventEmitter,
	inherits = require('util').inherits,
	net = require('net'),
	uuid = require('uuid'),

	Base = require('./Base'),
	addrs = require('../Misc/addresses'),
	msgs = require('../Misc/messages'),
	neigh = require('../Misc/neigh');

// ----------------
// Client class
// ----------------
// Main client class
// Inherits from: EventEmitter

// ----------------
// Events emitted by this class

// server-connect
// server-close
// sever-error

// client-change
// client-cache-updated

// single-message

// listening
// close

// ----------------
// Class declaration

function Client(chan) {
	Base.call(this, chan);

	var self = this;

	// The groups the client has joined
	this._groups = {};

	// Client and group cache
	this._client_cache = new neigh.NeighborManager();
	this._group_cache = new neigh.NeighborManager();

	// ----------------
	// Server socket

	this._server_sock = new net.Socket();

	// When socket is ready try to find server
	this.on('listening', function(){

		self.findServer;

		var message = new msgs.Message(
			msgs.types.CLIENT_ADVERTISEMENT,
			self.getProfile(),
			"I'm Here..."
		);

		self.sendUDP(self._channel, message.toString());

	});
};
inherits(Client, Base);

// ----------------
// Public methods

// function findServer
// tries to find a server by sending a SERVER_SOLICITATION
// message. Fires server-found event.
Client.prototype.findServer = function() {
	var message = new msgs.Message(
		msgs.types.SERVER_SOLICITATION,
		this.getProfile(),
		"Server solicitation..."
	);
	this.sendUDP(addrs.channels.server, message.toString());
}

// function connect
// stablish a TCP connection to given address and port.
// Used for connecting to a server
Client.prototype.connect = function(address, port) {
	var self = this;
	var addr = address + ':' + port;

	this._server_sock.connect(port, address);

	// Data arrives
	this._server_sock.on('data', this._tcpMessageHandler);

	// Connection status
	this._server_sock.on('connect', function() {
		self.emit('server-connect');
	});

	this._server_sock.on('close', function() {
		self.emit('server-close');
	});

	this._server_sock.on('error', function(err) {
		self.emit('server-error', err);
	});
}

// @override
Client.prototype.close = function() {
	Base.prototype.close.call(this);
	if (this._server_sock)
		this._server_sock.end();
}

Client.prototype.createGroup = function(group_name) {
	// Sends a GROUP_CREATE message

	if (this._server_sock.writable) {

		var message = new msgs.Message(
			msgs.types.GROUP_CREATE,
			this.getProfile(),
			{
				name: group_name,
				comments: 'let m crat it plz'
			}
		);

		this._server_sock.write(message.toString());
	}
}

Client.prototype.joinGroup = function(group_uuid) {
	// Sends a GROUP_JOIN message

	if (this._server_sock.writable) {

		var message = new msgs.Message(
			msgs.types.GROUP_JOIN,
			this.getProfile(),
			{
				uuid: group_uuid,
				comments: 'let me jion plz'
			}
		);

		this._server_sock.write(message.toString());
	}
}

Client.prototype.leaveGroup = function(group_uuid) {
	// Sends GROUP_LEAVE message

	if (this._server_sock.writable) {

		var message = new msgs.Message(
			msgs.types.GROUP_LEAVE,
			this.getProfile(),
			{
				uuid: group_uuid,
				comments: 'let m go plz'
			}
		);

		this._server_sock.write(message.toString());
	}
}

// function updateClientCache
// Sends a CLIENT_SOLICITATION message to the
// channel to which this client is connected
Client.prototype.updateClientCache = function() {
	var message = new msgs.Message(
		msgs.types.CLIENT_SOLICITATION,
		this.getProfile()
	);

	this.sendUDP(this._channel, message.toString());
}

Client.prototype.getClientCache = function() {
	return this._client_cache.getNeighbors();
}

Client.prototype.setAlias = function(alias) {
	var message = new msgs.Message(

		msgs.types.CLIENT_CHANGE,
		this.getProfile(),
		{
			old_name: this.alias,
			new_name: alias
		}

	);

	this.sendUDP(this._channel, message.toString());

	this.alias = alias;
}

Client.prototype.updateAlias = function(uuid, message){

	var neighbor = this.getClientCache()[uuid].data;
	console.log('old alias ${neighbor.alias}');
	console.log();
	neighbor.alias = message.new_name;
	console.log('new alias ${neighbor.alias}');

	this.emit('client-change', uuid, message);

}

// ----------------
// Private methods

// function _tcpMessageHandler
// handles the messages comming from the server
// @param data the data received
// @returns undefined
Client.prototype._tcpMessageHandler = function(data) {

	// JSON parse
	data = JSON.parse(data.toString());

	switch(data.type) {

		// GROUP_*_OK
		case msgs.types.GROUP_CREATE_OK:
			// Tell everyone that we just created a group
			var message = new msgs.Message(
				msgs.types.GROUP_CREATE_OK,
				this.getProfile(),
				data.extras
			);
			this.sendUDP(this._channel, message.toString());
		case msgs.types.GROUP_JOIN_OK:
			this._sock.addMembership(data.extras.channel.address);
			// Tell group that we joined
			var message = new msgs.Message(
				msgs.types.GROUP_JOIN_OK,
				this.getProfile(),
				'Joined!'
			);
			this.sendUDP(data.extras.channel, message.toString());
			break;

		case msgs.types.GROUP_LEAVE_OK:
			// Tell group that we left
			var message = new msgs.Message(
				msgs.types.GROUP_LEAVE_OK,
				this.getProfile(),
				'Left!'
			);
			this.sendUDP(data.extras.channel, message.toString());
			this._sock.dropMembership(data.extras.channel.address);
			break;

		// GROUP_*_ERR
		case msgs.types.GROUP_CREATE_ERR:
			break;
		case msgs.types.GROUP_JOIN_ERR:
			break;
		case msgs.types.GROUP_LEAVE_OK:
			break;
	}
}

// function _messageHandler
// @param message buffer of the message
// @remote object that contains the address and port of the sender
// @returns undefined
Client.prototype._messageHandler = function(message, remote) {

	var data = JSON.parse(message.toString());

	// Checks that this message isn't comming from us
	if (data.sender.uuid == this._uuid) return;

	// Fixes the channel of the sender
	data.sender.channel = new addrs.Channel(remote.address, remote.port);

	switch(data.type) {

		// ----------------
		// Server messages
		case msgs.types.SERVER_ADVERTISEMENT:
			this.connect(remote.address, remote.port);
			break;

		// ----------------
		// General messages handling

		case msgs.types.CLIENT_ONLINE:
		case msgs.types.CLIENT_ADVERTISEMENT:
			// Save client in cache
			this._client_cache.addNeighbor(new neigh.Neighbor(data.sender.uuid, data.sender));
			this.emit('client-cache-updated');

			break;
		case msgs.types.CLIENT_CHANGE:
			// Update client data

			this.updateAlias(data.sender.uuid, data.extras);

			break;
		case msgs.types.CLIENT_SOLICITATION:
			// Tell client we are online
			var message = new msgs.Message(
				msgs.types.CLIENT_ADVERTISEMENT,
				this.getProfile()
			);

			this.sendUDP(data.sender.channel, message.toString());
			break;
		case msgs.types.CLIENT_OFFLINE:
			// Delete client from list of neighbors
			break;

		// ----------------
		// All / Group messages handling

		case msgs.types.GROUP_SOLICITATION:
			// Tell client of all groups we have
			break;
		case msgs.types.GROUP_ADVERTISEMENT:
			// Save group in neighbor-groups list
			break;

		// ----------------
		// Single / Group messages handling

		case msgs.types.GROUP_CREATE_OK:
			// Group was created, save it in neighbor-group list
			this._group_cache.addNeighbor(new neigh.Neighbor(data.extras.uuid, data.extras));
			break;
		case msgs.types.GROUP_JOIN_OK:
			// Client is trying to join a group
			// Probably a group we joined too!
			break;
		case msgs.types.GROUP_LEAVE_OK:
			// Somebody is leaving a group
			// Probably a group we joined too!
			break;

		case msgs.types.GROUP_MESSAGE:
			// Messages to a group
			break;
		case msgs.types.SINGLE_MESSAGE:
			// We received an unicast message
			this.emit('single-message', data.sender, data.extras);
			break;
	} // switch

} // messageHandler

// ----------------
// Exports
// ----------------

module.exports = Client;<commit_msg>Fixed conflict<commit_after>var
	dgram = require('dgram'),
	EventEmitter = require('events').EventEmitter,
	inherits = require('util').inherits,
	net = require('net'),
	uuid = require('uuid'),

	Base = require('./Base'),
	addrs = require('../Misc/addresses'),
	msgs = require('../Misc/messages'),
	neigh = require('../Misc/neigh');

// ----------------
// Client class
// ----------------
// Main client class
// Inherits from: EventEmitter

// ----------------
// Events emitted by this class

// server-connect
// server-close
// sever-error

// client-change
// client-cache-updated

// single-message

// listening
// close

// ----------------
// Class declaration

function Client(chan) {
	Base.call(this, chan);

	var self = this;

	// The groups the client has joined
	this._groups = {};

	// Client and group cache
	this._client_cache = new neigh.NeighborManager();
	this._group_cache = new neigh.NeighborManager();

	// ----------------
	// Server socket

	this._server_sock = new net.Socket();

	// When socket is ready try to find server
	this.on('listening', function(){

		self.findServer;

		var message = new msgs.Message(
			msgs.types.CLIENT_ADVERTISEMENT,
			self.getProfile(),
			"I'm Here..."
		);

		self.sendUDP(self._channel, message.toString());

	});
};
inherits(Client, Base);

// ----------------
// Public methods

// function findServer
// tries to find a server by sending a SERVER_SOLICITATION
// message. Fires server-found event.
Client.prototype.findServer = function() {
	var message = new msgs.Message(
		msgs.types.SERVER_SOLICITATION,
		this.getProfile(),
		"Server solicitation..."
	);
	this.sendUDP(addrs.channels.server, message.toString());
}

// function connect
// stablish a TCP connection to given address and port.
// Used for connecting to a server
Client.prototype.connect = function(address, port) {
	var self = this;
	var addr = address + ':' + port;

	this._server_sock.connect(port, address);

	// Data arrives
	this._server_sock.on('data', this._tcpMessageHandler);

	// Connection status
	this._server_sock.on('connect', function() {
		self.emit('server-connect');
	});

	this._server_sock.on('close', function() {
		self.emit('server-close');
	});

	this._server_sock.on('error', function(err) {
		self.emit('server-error', err);
	});
}

// @override
Client.prototype.close = function() {
	Base.prototype.close.call(this);
	if (this._server_sock)
		this._server_sock.end();
}

Client.prototype.createGroup = function(group_name) {
	// Sends a GROUP_CREATE message

	if (this._server_sock.writable) {

		var message = new msgs.Message(
			msgs.types.GROUP_CREATE,
			this.getProfile(),
			{
				name: group_name,
				comments: 'let m crat it plz'
			}
		);

		this._server_sock.write(message.toString());
	}
}

Client.prototype.joinGroup = function(group_uuid) {
	// Sends a GROUP_JOIN message

	if (this._server_sock.writable) {

		var message = new msgs.Message(
			msgs.types.GROUP_JOIN,
			this.getProfile(),
			{
				uuid: group_uuid,
				comments: 'let me jion plz'
			}
		);

		this._server_sock.write(message.toString());
	}
}

Client.prototype.leaveGroup = function(group_uuid) {
	// Sends GROUP_LEAVE message

	if (this._server_sock.writable) {

		var message = new msgs.Message(
			msgs.types.GROUP_LEAVE,
			this.getProfile(),
			{
				uuid: group_uuid,
				comments: 'let m go plz'
			}
		);

		this._server_sock.write(message.toString());
	}
}

// function updateClientCache
// Sends a CLIENT_SOLICITATION message to the
// channel to which this client is connected
Client.prototype.updateClientCache = function() {
	var message = new msgs.Message(
		msgs.types.CLIENT_SOLICITATION,
		this.getProfile()
	);

	this.sendUDP(this._channel, message.toString());
}

Client.prototype.getClientCache = function() {
	return this._client_cache.getNeighbors();
}

Client.prototype.setAlias = function(alias) {
	var message = new msgs.Message(

		msgs.types.CLIENT_CHANGE,
		this.getProfile(),
		{
			old_name: this.alias,
			new_name: alias
		}

	);

	this.sendUDP(this._channel, message.toString());

	this.alias = alias;
}

// function updateAlias
// @param uuid neighbor id
// @param message sended by neighbor
// @return undefined

Client.prototype.updateAlias = function(uuid, message){

	var neighbor = this.getClientCache()[uuid].data;
	console.log('old alias ${neighbor.alias}');
	console.log();
	neighbor.alias = message.new_name;
	console.log('new alias ${neighbor.alias}');

	this.emit('client-change', uuid, message);

}

// ----------------
// Private methods

// function _tcpMessageHandler
// handles the messages comming from the server
// @param data the data received
// @returns undefined
Client.prototype._tcpMessageHandler = function(data) {

	// JSON parse
	data = JSON.parse(data.toString());

	switch(data.type) {

		// GROUP_*_OK
		case msgs.types.GROUP_CREATE_OK:
			// Tell everyone that we just created a group
			var message = new msgs.Message(
				msgs.types.GROUP_CREATE_OK,
				this.getProfile(),
				data.extras
			);
			this.sendUDP(this._channel, message.toString());
		case msgs.types.GROUP_JOIN_OK:
			this._sock.addMembership(data.extras.channel.address);
			// Tell group that we joined
			var message = new msgs.Message(
				msgs.types.GROUP_JOIN_OK,
				this.getProfile(),
				'Joined!'
			);
			this.sendUDP(data.extras.channel, message.toString());
			break;

		case msgs.types.GROUP_LEAVE_OK:
			// Tell group that we left
			var message = new msgs.Message(
				msgs.types.GROUP_LEAVE_OK,
				this.getProfile(),
				'Left!'
			);
			this.sendUDP(data.extras.channel, message.toString());
			this._sock.dropMembership(data.extras.channel.address);
			break;

		// GROUP_*_ERR
		case msgs.types.GROUP_CREATE_ERR:
			break;
		case msgs.types.GROUP_JOIN_ERR:
			break;
		case msgs.types.GROUP_LEAVE_OK:
			break;
	}
}

// function _messageHandler
// @param message buffer of the message
// @remote object that contains the address and port of the sender
// @returns undefined
Client.prototype._messageHandler = function(message, remote) {

	var data = JSON.parse(message.toString());

	// Checks that this message isn't comming from us
	if (data.sender.uuid == this._uuid) return;

	// Fixes the channel of the sender
	data.sender.channel = new addrs.Channel(remote.address, remote.port);

	switch(data.type) {

		// ----------------
		// Server messages
		case msgs.types.SERVER_ADVERTISEMENT:
			this.connect(remote.address, remote.port);
			break;

		// ----------------
		// General messages handling

		case msgs.types.CLIENT_ONLINE:
		case msgs.types.CLIENT_ADVERTISEMENT:
			// Save client in cache
			this._client_cache.addNeighbor(new neigh.Neighbor(data.sender.uuid, data.sender));
			this.emit('client-cache-updated');

			break;
		case msgs.types.CLIENT_CHANGE:
			// Update client data

			this.updateAlias(data.sender.uuid, data.extras);

			break;
		case msgs.types.CLIENT_SOLICITATION:
			// Tell client we are online
			var message = new msgs.Message(
				msgs.types.CLIENT_ADVERTISEMENT,
				this.getProfile()
			);

			this.sendUDP(data.sender.channel, message.toString());
			break;
		case msgs.types.CLIENT_OFFLINE:
			// Delete client from list of neighbors
			break;

		// ----------------
		// All / Group messages handling

		case msgs.types.GROUP_SOLICITATION:
			// Tell client of all groups we have
			break;
		case msgs.types.GROUP_ADVERTISEMENT:
			// Save group in neighbor-groups list
			break;

		// ----------------
		// Single / Group messages handling

		case msgs.types.GROUP_CREATE_OK:
			// Group was created, save it in neighbor-group list
			this._group_cache.addNeighbor(new neigh.Neighbor(data.extras.uuid, data.extras));
			break;
		case msgs.types.GROUP_JOIN_OK:
			// Client is trying to join a group
			// Probably a group we joined too!
			break;
		case msgs.types.GROUP_LEAVE_OK:
			// Somebody is leaving a group
			// Probably a group we joined too!
			break;

		case msgs.types.GROUP_MESSAGE:
			// Messages to a group
			break;
		case msgs.types.SINGLE_MESSAGE:
			// We received an unicast message
			this.emit('single-message', data.sender, data.extras);
			break;
	} // switch

} // messageHandler

// ----------------
// Exports
// ----------------

module.exports = Client;<|endoftext|>


<commit_before>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require jquery.sticky
//= require_self
//= require cocoon
//= require autocomplete-rails
//= require dataTables/jquery.dataTables
//= require dataTables_numhtml_sort.js
//= require dataTables_numhtml_detect.js
//= require dataTables/jquery.dataTables.bootstrap
//= require bootstrap

$(document).ready(function() {
// For DataTables and Bootstrap
	$('.datatable').dataTable({
	  "sDom": "<'row'<'span4'l><'span5'f>r>t<'row'<'span3'i><'span6'p>>",
	  "sPaginationType": "bootstrap",
		"sScrollX": "100%",
		"aoColumnDefs": [
		      { "bSortable": false, "aTargets": [ "no_sort" ] }
		    ]
	});

// For fading out flash notices
	$(".alert .close").click( function() {
	     $(this).parent().addClass("fade");
	});

	$("#sidebarbottom").sticky({topSpacing: 50, bottomSpacing: 200});
});

$.datepicker.setDefaults({
   minDate: new Date(),
});

// auto-submit cart dates #only-cart-dates
  $(document).on('change', '.submitchange', function() {
      $('#cart_dates').load( update_cart_path.value, // defined in _cart_dates in hidden field
// params need to be passed
        { 'reserver_id': reserver_id.value,
          'start_date_cart': cart_start_date_cart.value,
          'due_date_cart': cart_due_date_cart.value }
      );
  });

// auto-submit cart dates #only-cart-reserver
  $(document).on('blur', '.submittable', function() {
      $('#cart_dates').load( update_cart_path.value, // defined in _cart_dates in hidden field
// params need to be passed
        { 'reserver_id': reserver_id.value,
          'start_date_cart': cart_start_date_cart.value,
          'due_date_cart': cart_due_date_cart.value }
      );
  });

// general submit on change class
  $(document).on('change', '.autosubmitme', function() {
    $(this).parents('form:first').submit();
  });
});
<commit_msg>fixed js load issue<commit_after>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require jquery.sticky
//= require_self
//= require cocoon
//= require autocomplete-rails
//= require dataTables/jquery.dataTables
//= require dataTables_numhtml_sort.js
//= require dataTables_numhtml_detect.js
//= require dataTables/jquery.dataTables.bootstrap
//= require bootstrap

$(document).ready(function() {
// For DataTables and Bootstrap
	$('.datatable').dataTable({
	  "sDom": "<'row'<'span4'l><'span5'f>r>t<'row'<'span3'i><'span6'p>>",
	  "sPaginationType": "bootstrap",
		"sScrollX": "100%",
		"aoColumnDefs": [
		      { "bSortable": false, "aTargets": [ "no_sort" ] }
		    ]
	});

// For fading out flash notices
	$(".alert .close").click( function() {
	     $(this).parent().addClass("fade");
	});

	$("#sidebarbottom").sticky({topSpacing: 50, bottomSpacing: 200});
});

$.datepicker.setDefaults({
   minDate: new Date(),
});

// auto-submit cart dates #only-cart-dates
  $(document).on('change', '.submitchange', function() {
      $('#cart_dates').load( update_cart_path.value, // defined in _cart_dates in hidden field
// params need to be passed
        { 'reserver_id': reserver_id.value,
          'start_date_cart': cart_start_date_cart.value,
          'due_date_cart': cart_due_date_cart.value }
      );
  });

// auto-submit cart dates #only-cart-reserver
  $(document).on('blur', '.submittable', function() {
      $('#cart_dates').load( update_cart_path.value, // defined in _cart_dates in hidden field
// params need to be passed
        { 'reserver_id': reserver_id.value,
          'start_date_cart': cart_start_date_cart.value,
          'due_date_cart': cart_due_date_cart.value }
      );
  });

// general submit on change class
  $(document).on('change', '.autosubmitme', function() {
    $(this).parents('form:first').submit();
  });

<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
// loaclhost:3000/?debug_assets=1 > View Page Source -- to view all assets client side loads
//
//= require jquery
//= require jquery_ujs
//= require jquery.ui.autocomplete
//= require mustache
//= require_tree .
//
<commit_msg>removed autocomplete.js file<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
// loaclhost:3000/?debug_assets=1 > View Page Source -- to view all assets client side loads
//
//= require jquery
//= require jquery_ujs
//# require jquery.ui.autocomplete
//= require mustache
//= require_tree .
//
<|endoftext|>


<commit_before>(function() {
    function init_datetime() {
        $('.datetime-picker').datetimepicker({
            sideBySide: true,
            format: "YYYY/MM/DD h:mm A",
            defaultDate: undefined
        });
    }

    function on_add_grader(evt, el) {
        form_tabs_init(el);
    }

    function form_init() {
        init_datetime();

        $('.graders-list').on('cocoon:after-insert', on_add_grader);
        $('.spinner').each(function (_ii, div) {
            activateSpinner(div);
        });
    }

    run_on_page("assignments/new", form_init);
    run_on_page("assignments/create", form_init);
    run_on_page("assignments/edit", form_init);
    run_on_page("assignments/update", form_init);
})();

/*
run_on_page "assignments/show", () ->
  inputs  = []
  by_user = {}

  find_inputs = () ->
    inputs = $(".grade-entry-box")

    by_user = {}
    inputs.each (ii, box) ->
      user_id = $(box).data("user-id")
      by_user[user_id] = ii

  focus_row = (row_number) ->
    next = inputs[row_number]
    if next
      $(next).focus()

  handle_arrow_keys = (event) ->
    user_id    = $(event.target).data("user-id")
    row_number = by_user[user_id]

    switch $.keyDecoder.parse(event)
      when "Up"
        focus_row(row_number - 1)

      when "Down"
        focus_row(row_number + 1)

  handle_form_send = (event) ->
    box = $(event.target).find(".grade-entry-box")[0]
    user_id = $(box).data("user-id")

    row_number = by_user[user_id]
    focus_row(row_number + 1)

  setup_handlers = () ->
    find_inputs()

    $(".grade-entry-box").off "keyup", handle_arrow_keys
    $(".grade-entry-box").on "keyup", handle_arrow_keys

    $(".sub-form").off "ajax:send", handle_form_send
    $(".sub-form").on "ajax:send", handle_form_send

  $(document).ajaxComplete () ->
    $('#ajax-status').text("ajax-status: done")
    setup_handlers()

  setup_handlers()
*/
<commit_msg>Try to make spinners.<commit_after>(function() {
    function init_datetime() {
        $('.datetime-picker').datetimepicker({
            sideBySide: true,
            format: "YYYY/MM/DD h:mm A",
            defaultDate: undefined
        });
    }

    function on_add_grader(evt, el) {
        el.find(".spinner").each(function(_ii, div) {
            // FIXME: Doesn't actually find spinners.
            activateSpinner(div);
        });

        form_tabs_init(el);

        console.log(el);
   }

    function form_init() {
        init_datetime();

        $('.graders-list').on('cocoon:after-insert', on_add_grader);
        $('.spinner').each(function (_ii, div) {
            activateSpinner(div);
        });
    }

    run_on_page("assignments/new", form_init);
    run_on_page("assignments/create", form_init);
    run_on_page("assignments/edit", form_init);
    run_on_page("assignments/update", form_init);
})();

/*
run_on_page "assignments/show", () ->
  inputs  = []
  by_user = {}

  find_inputs = () ->
    inputs = $(".grade-entry-box")

    by_user = {}
    inputs.each (ii, box) ->
      user_id = $(box).data("user-id")
      by_user[user_id] = ii

  focus_row = (row_number) ->
    next = inputs[row_number]
    if next
      $(next).focus()

  handle_arrow_keys = (event) ->
    user_id    = $(event.target).data("user-id")
    row_number = by_user[user_id]

    switch $.keyDecoder.parse(event)
      when "Up"
        focus_row(row_number - 1)

      when "Down"
        focus_row(row_number + 1)

  handle_form_send = (event) ->
    box = $(event.target).find(".grade-entry-box")[0]
    user_id = $(box).data("user-id")

    row_number = by_user[user_id]
    focus_row(row_number + 1)

  setup_handlers = () ->
    find_inputs()

    $(".grade-entry-box").off "keyup", handle_arrow_keys
    $(".grade-entry-box").on "keyup", handle_arrow_keys

    $(".sub-form").off "ajax:send", handle_form_send
    $(".sub-form").on "ajax:send", handle_form_send

  $(document).ajaxComplete () ->
    $('#ajax-status').text("ajax-status: done")
    setup_handlers()

  setup_handlers()
*/
<|endoftext|>


<commit_before>/* global Fae, FCH */

'use strict';

/**
 * Fae tables
 * @namespace tables
 * @memberof Fae
 */
Fae.tables = {

  init: function() {
    this.columnSorting();
    this.rowSorting();
    if(FCH.exists('.sticky-table-header')) {
      this.stickyTableHeader();
    }
    if(FCH.exists('.collapsible')) {
      this.collapsibleTable();
    }
    if(FCH.exists('form .main_content-section-area')) {
      this.endingSelectShim();
    }
    this.addToTable();
  },

  /**
   * Sort columns in tables if applicable
   */
  columnSorting: function() {
    $('.main_table-sort_columns').tablesorter();
    $('.main_table-sort_columns-cities').tablesorter({
      sortList: [[1,0]]
    });
  },

  /**
   * Make table rows draggable by user
   */
  rowSorting: function() {
    $('.main_content-sortable').sortable({
      items: 'tbody tr',
      opacity: 0.8,
      handle: ('.main_content-sortable-handle'),

      //helper funciton to preserve the width of the table row
      helper: function(e, tr) {
        var $originals = tr.children();
        var $helper = tr.clone();
        var $ths = $(tr).closest('table').find('th');

        $helper.children().each(function(index) {
          // Set helper cell sizes to match the original sizes
          $(this).width($originals.eq(index).width());
          //set the THs width so they don't go collapsey
          $ths.eq(index).width($ths.eq(index).width());
        });

        return $helper;
      },

      // on stop, set the THs back to no inline width for repsonsivity
      stop: function(e, ui) {
        $(ui.item).closest('table').find('th').css('width', '');
      },

      update: function() {
        var $this = $(this);
        var serial = $this.sortable('serialize');
        var object = serial.substr(0, serial.indexOf('['));

        $.ajax({
          url: Fae.path+'/sort/'+object,
          type: 'post',
          data: serial,
          dataType: 'script',
          complete: function(request){
            // sort complete messaging can go here
          }
        });
      }
    });
  },

  /**
   * Enable collapsible tables by clicking the h3 preceding a table. Also enables open/close all
   */
  collapsibleTable: function() {
    $('.collapsible-toggle').click(function() {
      var $this = $(this);

      if($this.hasClass('close-all')) {
        $this.text('Open All');
        $('.collapsible').removeClass('active');
      } else {
        $this.text('Close All');
        $('.collapsible').addClass('active');
      }

      $this.toggleClass('close-all');
    });

    $('.collapsible h3').click(function() {
      var $this = $(this);
      var $toggle = $('.collapsible-toggle');
      /** @type {Boolean} */
      var toggleHasCloseAll = $toggle.hasClass('close-all');

      $this.parent().toggleClass('active');

      // Change toggle messaging as appropriate
      // First check if there are open drawers
      if($('.collapsible.active').length > 1) {

        // Change toggle text if it isn't already close all
        if(!toggleHasCloseAll) {
          $toggle.text('Close All');
          $toggle.addClass('close-all');
        }
      } else {

        // Change toggle text if it isn't already open all
        if(toggleHasCloseAll) {
          $toggle.text('Open All');
          $toggle.removeClass('close-all');
        }
      }
    });
  },

  /**
   * Add extra space if the last item in a form is a select menu so the dropdown doesn't run off the screen or section
   */
  endingSelectShim: function() {
    $('form .main_content-section-area:last-of-type').each(function() {
      var $last_item = $(this).find('.input:last-of-type');

      if( $last_item.hasClass('select') ) {
        $(this).addClass('-bottom-shim');
      }
    });
  },

  /**
   * Fix a table header to the top of the view on scroll
   */
  stickyTableHeader: function() {
    var $sticky_tables = $('.sticky-table-header');
    var sticky_table_header_selector = '.sticky-table-header--hidden';
    var $window = $(window);

    // Add sticky psuedo tables after our main table to hold the fixed header
    $sticky_tables.each(function() {
      var $this = $(this);
      var $header = $this.find('thead').clone();
      var new_classes = $this.attr('class').replace('sticky-table-header', sticky_table_header_selector.substr(1));

      var $fixedHeader = $('<table />', {
        class: new_classes
      });

      $fixedHeader.append( $header );
      $this.after($fixedHeader);
    });

    /**
     * FCH callback for scroll - If the table header is in range, show it, otherwise hide it
     * @access private
     */
    var stickyTableHeaderScroll = function() {
      var offset = FCH.$window.scrollTop();

      $('.sticky-table-header--hidden').each(function() {
        var $this = $(this);
        var tableOffset = $this.data('table-offset');
        var tableBottom = $this.data('table-bottom');

        if (offset >= tableOffset && offset <= tableBottom) {
          $this.show();
        } else {
          $this.hide();
        }
      });
    };

    /**
     * FCH callback for resize and load - Get all values squared away again if the viewport gets smaller
     * @access private
     */
    var sizeTableHeader = function() {
      $sticky_tables.each(function() {
        Fae.tables.sizeFixedHeader($(this));
      });
    };

    FCH.load.push(sizeTableHeader);
    FCH.resize.push(sizeTableHeader);
    FCH.scroll.push(stickyTableHeaderScroll);
  },

  /**
   * Cache offset and height values to spare expensive calculations on scroll
   * @param {jQuery} $this
   */
  sizeFixedHeader: function($this) {
    var headerHeight = $('.main_content-header').outerHeight();
    if(FCH.large_down) {
      headerHeight = $('#main_header').outerHeight();
    }

    var tableOffset = $this.offset().top - headerHeight;
    var theadHeight = $this.find('thead').outerHeight();
    var bottomOffset = $this.height() + tableOffset - theadHeight;
    var $fixedHeader = $this.next('.sticky-table-header--hidden');

    // For whatever reason IE9 does not pickup the .sticky plugin
    if(!$('.js-will-be-sticky').length) {
      tableOffset += headerHeight;
      headerHeight = 0;
    }

    $fixedHeader.data({
      'table-offset' : tableOffset,
      'table-bottom' : bottomOffset
    });

    $fixedHeader.css({
      width: $this.outerWidth(),
      height: theadHeight,
      top: headerHeight,
    });

    $this.find('thead tr th').each(function(index){
      var original_width = $(this).outerWidth()
      // Using .width() as a setter is bunk
      $($fixedHeader.find('tr > th')[index]).css('width', original_width);
    });
  },

  /**
   * Scroll_to event for non-ajax'd table forms
   */
  addToTable: function() {
    $('.table-add-link-visible').click('on', function(){
      var $parent = $(this).closest('section');

      FCH.smoothScroll($parent.find('tbody tr:last-child'), 500, 100, 90);
    });
  }
};
<commit_msg>refs #45145 - save column sorting per session per table per page<commit_after>/* global Fae, FCH */

'use strict';

/**
 * Fae tables
 * @namespace tables
 * @memberof Fae
 */
Fae.tables = {

  /**
   * Base cookie string for sorting per site per page
   * @type {String}
   * @see {@link tables.columnSorting}
   * @see {@link tables.sortColumnsFromCookies}
   */
  sort_cookie_name: 'Fae_' + window.location.hostname + '_table_sort_preferences_' + window.location.pathname,

  init: function() {
    this.columnSorting();
    this.sortColumnsFromCookies();
    this.rowSorting();
    if(FCH.exists('.sticky-table-header')) {
      this.stickyTableHeader();
    }
    if(FCH.exists('.collapsible')) {
      this.collapsibleTable();
    }
    if(FCH.exists('form .main_content-section-area')) {
      this.endingSelectShim();
    }
    this.addToTable();
  },

  /**
   * Sort columns in tables if applicable
   */
  columnSorting: function() {
    var _this = this;

    $('.main_table-sort_columns')
      .tablesorter()
      .on('sortEnd', function(e) {
        var $this = $(this);
        var table_data = $this.data('tablesorter');
        var name = _this.sort_cookie_name;
        name += ($this.index() - 1);

        // Save it to the cookie as a string
        $.cookie(name, table_data.sortList[0].join(','));
      });
  },

  /**
   * Sort column by stored cookie
   */
  sortColumnsFromCookies: function() {
    var _this = this;

    $('.main_table-sort_columns').each(function(idx) {
      var name = _this.sort_cookie_name + idx;
      var cookie_value = $.cookie(name);

      if (cookie_value) {
        // Convert back to an array within an array within another array because of how tablesorter accepts this argument
        $(this).trigger('sorton', [[cookie_value.split(',')]]);
      }
    });
  },

  /**
   * Make table rows draggable by user
   */
  rowSorting: function() {
    $('.main_content-sortable').sortable({
      items: 'tbody tr',
      opacity: 0.8,
      handle: ('.main_content-sortable-handle'),

      //helper funciton to preserve the width of the table row
      helper: function(e, $tr) {
        var $originals = $tr.children();
        var $helper = $tr.clone();
        var $ths = $tr.closest('table').find('th');

        $helper.children().each(function(index) {
          // Set helper cell sizes to match the original sizes
          $(this).width($originals.eq(index).width());
          //set the THs width so they don't go collapsey
          $ths.eq(index).width($ths.eq(index).width());
        });

        return $helper;
      },

      // on stop, set the THs back to no inline width for repsonsivity
      stop: function(e, ui) {
        $(ui.item).closest('table').find('th').css('width', '');
      },

      update: function() {
        var $this = $(this);
        var serial = $this.sortable('serialize');
        var object = serial.substr(0, serial.indexOf('['));

        $.ajax({
          url: Fae.path+'/sort/'+object,
          type: 'post',
          data: serial,
          dataType: 'script',
          complete: function(request){
            // sort complete messaging can go here
          }
        });
      }
    });
  },

  /**
   * Enable collapsible tables by clicking the h3 preceding a table. Also enables open/close all
   */
  collapsibleTable: function() {
    $('.collapsible-toggle').click(function() {
      var $this = $(this);

      if($this.hasClass('close-all')) {
        $this.text('Open All');
        $('.collapsible').removeClass('active');
      } else {
        $this.text('Close All');
        $('.collapsible').addClass('active');
      }

      $this.toggleClass('close-all');
    });

    $('.collapsible h3').click(function() {
      var $this = $(this);
      var $toggle = $('.collapsible-toggle');
      /** @type {Boolean} */
      var toggleHasCloseAll = $toggle.hasClass('close-all');

      $this.parent().toggleClass('active');

      // Change toggle messaging as appropriate
      // First check if there are open drawers
      if($('.collapsible.active').length > 1) {

        // Change toggle text if it isn't already close all
        if(!toggleHasCloseAll) {
          $toggle.text('Close All');
          $toggle.addClass('close-all');
        }
      } else {

        // Change toggle text if it isn't already open all
        if(toggleHasCloseAll) {
          $toggle.text('Open All');
          $toggle.removeClass('close-all');
        }
      }
    });
  },

  /**
   * Add extra space if the last item in a form is a select menu so the dropdown doesn't run off the screen or section
   */
  endingSelectShim: function() {
    $('form .main_content-section-area:last-of-type').each(function() {
      var $last_item = $(this).find('.input:last-of-type');

      if( $last_item.hasClass('select') ) {
        $(this).addClass('-bottom-shim');
      }
    });
  },

  /**
   * Fix a table header to the top of the view on scroll
   */
  stickyTableHeader: function() {
    var $sticky_tables = $('.sticky-table-header');
    var sticky_table_header_selector = '.sticky-table-header--hidden';
    var $window = $(window);

    // Add sticky psuedo tables after our main table to hold the fixed header
    $sticky_tables.each(function() {
      var $this = $(this);
      var $header = $this.find('thead').clone();
      var new_classes = $this.attr('class').replace('sticky-table-header', sticky_table_header_selector.substr(1));

      var $fixedHeader = $('<table />', {
        class: new_classes
      });

      $fixedHeader.append( $header );
      $this.after($fixedHeader);
    });

    /**
     * FCH callback for scroll - If the table header is in range, show it, otherwise hide it
     * @access private
     */
    var stickyTableHeaderScroll = function() {
      var offset = FCH.$window.scrollTop();

      $('.sticky-table-header--hidden').each(function() {
        var $this = $(this);
        var tableOffset = $this.data('table-offset');
        var tableBottom = $this.data('table-bottom');

        if (offset >= tableOffset && offset <= tableBottom) {
          $this.show();
        } else {
          $this.hide();
        }
      });
    };

    /**
     * FCH callback for resize and load - Get all values squared away again if the viewport gets smaller
     * @access private
     */
    var sizeTableHeader = function() {
      $sticky_tables.each(function() {
        Fae.tables.sizeFixedHeader($(this));
      });
    };

    FCH.load.push(sizeTableHeader);
    FCH.resize.push(sizeTableHeader);
    FCH.scroll.push(stickyTableHeaderScroll);
  },

  /**
   * Cache offset and height values to spare expensive calculations on scroll
   * @param {jQuery} $this
   */
  sizeFixedHeader: function($this) {
    var headerHeight = $('.main_content-header').outerHeight();
    if(FCH.large_down) {
      headerHeight = $('#main_header').outerHeight();
    }

    var tableOffset = $this.offset().top - headerHeight;
    var theadHeight = $this.find('thead').outerHeight();
    var bottomOffset = $this.height() + tableOffset - theadHeight;
    var $fixedHeader = $this.next('.sticky-table-header--hidden');

    // For whatever reason IE9 does not pickup the .sticky plugin
    if(!$('.js-will-be-sticky').length) {
      tableOffset += headerHeight;
      headerHeight = 0;
    }

    $fixedHeader.data({
      'table-offset' : tableOffset,
      'table-bottom' : bottomOffset
    });

    $fixedHeader.css({
      width: $this.outerWidth(),
      height: theadHeight,
      top: headerHeight,
    });

    $this.find('thead tr th').each(function(index){
      var original_width = $(this).outerWidth()
      // Using .width() as a setter is bunk
      $($fixedHeader.find('tr > th')[index]).css('width', original_width);
    });
  },

  /**
   * Scroll_to event for non-ajax'd table forms
   */
  addToTable: function() {
    $('.table-add-link-visible').click('on', function(){
      var $parent = $(this).closest('section');

      FCH.smoothScroll($parent.find('tbody tr:last-child'), 500, 100, 90);
    });
  }
};
<|endoftext|>


<commit_before>var d2l = {

	jasmine: {

		_private: {

			createCompareStyle: function( property ) {
				return {
					compare: function( node, expected ) {

						if ( !node ) {
							return { pass: false, message: 'The node value is not defined.' };
						}
						if ( !expected ) {
							return { pass: false, message: 'The expected value is not defined.' };
						}

<<<<<<< HEAD
			},
=======
						var val = window.getComputedStyle( node, null ).getPropertyValue( property );
						if ( val === expected ) {
							return { pass: true };
						} else {
							return { pass: false, message: 'Expected to find ' + expected + ' but found ' + val + '.' };
						}

					}
				};
			}

		},
>>>>>>> master

		matchers: {

			toHaveCssSelector: function() {
				return {
					compare: function( doc, expected ) {

						if ( !doc ) {
							return { pass: false, message: 'The document value is not defined.' };
						}
						if ( !expected ) {
							return { pass: false, message: 'The expected value is not defined.' };
						}

						for ( var i=0; i<doc.styleSheets.length; ++i ) {
							for ( var j=0; j<doc.styleSheets[i].cssRules.length; ++j ) {
								var selectors = doc.styleSheets[i].cssRules[j].selectorText.split( ',' );
								for ( var k=0; k<selectors.length; k++ ) {
									if ( selectors[k].replace( /(^\s+|\s+$)/g,'' ) === expected ) {
										return { pass: true };
									}
								}
							}
						}

						return { pass: false, message: 'Expected to find ' + expected + ' css selector but did not.' };

					}
				};
			},

			toHaveColor: function() {
				return d2l.jasmine._private.createCompareStyle( 'color' );
			},

			toHaveFontFamily: function() {
				return d2l.jasmine._private.createCompareStyle( 'font-family' );
			},

			toHaveFontSize: function() {
				return d2l.jasmine._private.createCompareStyle( 'font-size' );
			},

			toHaveFontWeight: function() {
				return d2l.jasmine._private.createCompareStyle( 'font-weight' );
			},

			toHaveLineHeight: function() {
				return d2l.jasmine._private.createCompareStyle( 'line-height' );
			},

			toHaveTextDecoration: function() {
				return d2l.jasmine._private.createCompareStyle( 'text-decoration' );
			},

			toBeOnBrowser: function( ) {
				return {
					compare: function( actual, browserExpected ) {
						var userAgent = navigator.userAgent;
						var expected;

						if(userAgent.indexOf("Chrome") > -1) {
						    expected = browserExpected.Chrome;
						} else if (userAgent.indexOf("Safari") > -1) {
						    expected = browserExpected.Safari;
						} else if (userAgent.indexOf("Opera") > -1) {
						    expected = browserExpected.Opera;
						} else if (userAgent.indexOf("Firefox") > -1) {
						    expected = browserExpected.Firefox;
						} else if (userAgent.indexOf("MSIE") > -1) {
						    expected = browserExpected.MSIE;
						}

						return { pass: actual == (expected || browserExpected.default), message: 'Expected ' + actual + ' to be ' + (expected || browserExpected.default) }
					}
				};
			}

		}

	}

};<commit_msg>Cleanup conflict lines.<commit_after>var d2l = {

	jasmine: {

		_private: {

			createCompareStyle: function( property ) {
				return {
					compare: function( node, expected ) {

						if ( !node ) {
							return { pass: false, message: 'The node value is not defined.' };
						}
						if ( !expected ) {
							return { pass: false, message: 'The expected value is not defined.' };
						}

						var val = window.getComputedStyle( node, null ).getPropertyValue( property );
						if ( val === expected ) {
							return { pass: true };
						} else {
							return { pass: false, message: 'Expected to find ' + expected + ' but found ' + val + '.' };
						}

					}
				};
			}
		},

		matchers: {

			toHaveCssSelector: function() {
				return {
					compare: function( doc, expected ) {

						if ( !doc ) {
							return { pass: false, message: 'The document value is not defined.' };
						}
						if ( !expected ) {
							return { pass: false, message: 'The expected value is not defined.' };
						}

						for ( var i=0; i<doc.styleSheets.length; ++i ) {
							for ( var j=0; j<doc.styleSheets[i].cssRules.length; ++j ) {
								var selectors = doc.styleSheets[i].cssRules[j].selectorText.split( ',' );
								for ( var k=0; k<selectors.length; k++ ) {
									if ( selectors[k].replace( /(^\s+|\s+$)/g,'' ) === expected ) {
										return { pass: true };
									}
								}
							}
						}

						return { pass: false, message: 'Expected to find ' + expected + ' css selector but did not.' };

					}
				};
			},

			toHaveColor: function() {
				return d2l.jasmine._private.createCompareStyle( 'color' );
			},

			toHaveFontFamily: function() {
				return d2l.jasmine._private.createCompareStyle( 'font-family' );
			},

			toHaveFontSize: function() {
				return d2l.jasmine._private.createCompareStyle( 'font-size' );
			},

			toHaveFontWeight: function() {
				return d2l.jasmine._private.createCompareStyle( 'font-weight' );
			},

			toHaveLineHeight: function() {
				return d2l.jasmine._private.createCompareStyle( 'line-height' );
			},

			toHaveTextDecoration: function() {
				return d2l.jasmine._private.createCompareStyle( 'text-decoration' );
			},

			toBeOnBrowser: function( ) {
				return {
					compare: function( actual, browserExpected ) {
						var userAgent = navigator.userAgent;
						var expected;

						if(userAgent.indexOf("Chrome") > -1) {
						    expected = browserExpected.Chrome;
						} else if (userAgent.indexOf("Safari") > -1) {
						    expected = browserExpected.Safari;
						} else if (userAgent.indexOf("Opera") > -1) {
						    expected = browserExpected.Opera;
						} else if (userAgent.indexOf("Firefox") > -1) {
						    expected = browserExpected.Firefox;
						} else if (userAgent.indexOf("MSIE") > -1) {
						    expected = browserExpected.MSIE;
						}

						return { pass: actual == (expected || browserExpected.default), message: 'Expected ' + actual + ' to be ' + (expected || browserExpected.default) }
					}
				};
			}

		}

	}

};<|endoftext|>


<commit_before><commit_msg>Update paint.js<commit_after><|endoftext|>


<commit_before><commit_msg>code style fix<commit_after><|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */

import * as tf from '@tensorflow/tfjs';

// This is a helper class for loading and managing MNIST data specifically.
// It is a useful example of how you could create your own data manager class
// for arbitrary data though. It's worth a look :)
import {IMAGE_H, IMAGE_W, MnistData} from './data';

// This is a helper class for drawing loss graphs and MNIST images to the
// window. For the purposes of understanding the machine learning bits, you can
// largely ignore it
import * as ui from './ui';

/**
 * Creates a convolutional neural network (Convnet) for the MNIST data.
 *
 * @returns {tf.Model} An instance of tf.Model.
 */
function createConvModel() {
  // Create a sequential neural network model. tf.sequential provides an API
  // for creating "stacked" models where the output from one layer is used as
  // the input to the next layer.
  const model = tf.sequential();

  // The first layer of the convolutional neural network plays a dual role:
  // it is both the input layer of the neural network and a layer that performs
  // the first convolution operation on the input. It receives the 28x28 pixels
  // black and white images. This input layer uses 16 filters with a kernel size
  // of 5 pixels each. It uses a simple RELU activation function which pretty
  // much just looks like this: __/
  model.add(tf.layers.conv2d({
    inputShape: [IMAGE_H, IMAGE_W, 1],
    kernelSize: 3,
    filters: 16,
    activation: 'relu'
  }));

  // After the first layer we include a MaxPooling layer. This acts as a sort of
  // downsampling using max values in a region instead of averaging.
  // https://www.quora.com/What-is-max-pooling-in-convolutional-neural-networks
  model.add(tf.layers.maxPooling2d({poolSize: 2, strides: 2}));

  // Our third layer is another convolution, this time with 32 filters.
  model.add(tf.layers.conv2d({kernelSize: 3, filters: 32, activation: 'relu'}));

  // Max pooling again.
  model.add(tf.layers.maxPooling2d({poolSize: 2, strides: 2}));

  // Add another conv2d layer.
  model.add(tf.layers.conv2d({kernelSize: 3, filters: 32, activation: 'relu'}));

  // Now we flatten the output from the 2D filters into a 1D vector to prepare
  // it for input into our last layer. This is common practice when feeding
  // higher dimensional data to a final classification output layer.
  model.add(tf.layers.flatten({}));

  model.add(tf.layers.dense({units: 64, activation: 'relu'}));

  // Our last layer is a dense layer which has 10 output units, one for each
  // output class (i.e. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9). Here the classes actually
  // represent numbers, but it's the same idea if you had classes that
  // represented other entities like dogs and cats (two output classes: 0, 1).
  // We use the softmax function as the activation for the output layer as it
  // creates a probability distribution over our 10 classes so their output
  // values sum to 1.
  model.add(tf.layers.dense({units: 10, activation: 'softmax'}));

  return model;
}

/**
 * Creates a model consisting of only flatten, dense and dropout layers.
 *
 * The model create here has approximately the same number of parameters
 * (~31k) as the convnet created by `createConvModel()`, but is
 * expected to show a significantly worse accuracy after training, due to the
 * fact that it doesn't utilize the spatial information as the convnet does.
 *
 * This is for comparison with the convolutional network above.
 *
 * @returns {tf.Model} An instance of tf.Model.
 */
function createDenseModel() {
  const model = tf.sequential();
  model.add(tf.layers.flatten({inputShape: [IMAGE_H, IMAGE_W, 1]}));
  model.add(tf.layers.dense({units: 42, activation: 'relu'}));
  model.add(tf.layers.dense({units: 10, activation: 'softmax'}));
  return model;
}

/**
 * Compile and train the given model.
 *
 * @param {*} model The model to
 */
async function train(model, onIteration) {
  ui.logStatus('Training model...');

  // Now that we've defined our model, we will define our optimizer. The
  // optimizer will be used to optimize our model's weight values during
  // training so that we can decrease our training loss and increase our
  // classification accuracy.

  // The learning rate defines the magnitude by which we update our weights each
  // training step. The higher the value, the faster our loss values converge,
  // but also the more likely we are to overshoot optimal parameters
  // when making an update. A learning rate that is too low will take too long
  // to find optimal (or good enough) weight parameters while a learning rate
  // that is too high may overshoot optimal parameters. Learning rate is one of
  // the most important hyperparameters to set correctly. Finding the right
  // value takes practice and is often best found empirically by trying many
  // values.
  const LEARNING_RATE = 0.01;

  // We are using rmsprop as our optimizer.
  // An optimizer is an iterative method for minimizing an loss function.
  // It tries to find the minimum of our loss function with respect to the
  // model's weight parameters.
  const optimizer = 'rmsprop';

  // We compile our model by specifying an optimizer, a loss function, and a
  // list of metrics that we will use for model evaluation. Here we're using a
  // categorical crossentropy loss, the standard choice for a multi-class
  // classification problem like MNIST digits.
  // The categorical crossentropy loss is differentiable and hence makes
  // model training possible. But it is not amenable to easy interpretation
  // by a human. This is why we include a "metric", namely accuracy, which is
  // simply a measure of how many of the examples are classified correctly.
  // This metric is not differentiable and hence cannot be used as the loss
  // function of the model.
  model.compile({
    optimizer,
    loss: 'categoricalCrossentropy',
    metrics: ['accuracy'],
  });

  // Batch size is another important hyperparameter. It defines the number of
  // examples we group together, or batch, between updates to the model's
  // weights during training. A value that is too low will update weights using
  // too few examples and will not generalize well. Larger batch sizes require
  // more memory resources and aren't guaranteed to perform better.
  const batchSize = 320;

  // Leave out the last 15% of the training data for validation, to monitor
  // overfitting during training.
  const validationSplit = 0.15;

  // Get number of training epochs from the UI.
  const trainEpochs = ui.getTrainEpochs();

  // We'll keep a buffer of loss and accuracy values over time.
  let trainBatchCount = 0;

  const trainData = data.getTrainData();
  const testData = data.getTestData();

  const totalNumBatches =
      Math.ceil(trainData.xs.shape[0] * (1 - validationSplit) / batchSize) *
      trainEpochs;

  // During the long-running fit() call for model training, we include
  // callbacks, so that we can plot the loss and accuracy values in the page
  // as the training progresses.
  let valAcc;
  await model.fit(trainData.xs, trainData.labels, {
    batchSize,
    validationSplit,
    epochs: trainEpochs,
    callbacks: {
      onBatchEnd: async (batch, logs) => {
        trainBatchCount++;
        ui.logStatus(
            `Training... (` +
            `${(trainBatchCount / totalNumBatches * 100).toFixed(1)}%` +
            ` complete). To stop training, refresh or close page.`);
        ui.plotLoss(trainBatchCount, logs.loss, 'train');
        ui.plotAccuracy(trainBatchCount, logs.acc, 'train');
        if (onIteration && batch % 10 === 0) {
          onIteration('onBatchEnd', batch, logs);
        }
        await tf.nextFrame();
      },
      onEpochEnd: async (epoch, logs) => {
        valAcc = logs.val_acc;
        ui.plotLoss(trainBatchCount, logs.val_loss, 'validation');
        ui.plotAccuracy(trainBatchCount, logs.val_acc, 'validation');
        if (onIteration) {
          onIteration('onEpochEnd', epoch, logs);
        }
        await tf.nextFrame();
      }
    }
  });

  const testResult = model.evaluate(testData.xs, testData.labels);
  const testAccPercent = testResult[1].dataSync()[0] * 100;
  const finalValAccPercent = valAcc * 100;
  ui.logStatus(
      `Final validation accuracy: ${finalValAccPercent.toFixed(1)}%; ` +
      `Final test accuracy: ${testAccPercent.toFixed(1)}%`);
}

/**
 * Show predictions on a number of test examples.
 *
 * @param {tf.Model} model The model to be used for making the predictions.
 */
async function showPredictions(model) {
  const testExamples = 100;
  const examples = data.getTestData(testExamples);

  // Code wrapped in a tf.tidy() function callback will have their tensors freed
  // from GPU memory after execution without having to call dispose().
  // The tf.tidy callback runs synchronously.
  tf.tidy(() => {
    const output = model.predict(examples.xs);

    // tf.argMax() returns the indices of the maximum values in the tensor along
    // a specific axis. Categorical classification tasks like this one often
    // represent classes as one-hot vectors. One-hot vectors are 1D vectors with
    // one element for each output class. All values in the vector are 0
    // except for one, which has a value of 1 (e.g. [0, 0, 0, 1, 0]). The
    // output from model.predict() will be a probability distribution, so we use
    // argMax to get the index of the vector element that has the highest
    // probability. This is our prediction.
    // (e.g. argmax([0.07, 0.1, 0.03, 0.75, 0.05]) == 3)
    // dataSync() synchronously downloads the tf.tensor values from the GPU so
    // that we can use them in our normal CPU JavaScript code
    // (for a non-blocking version of this function, use data()).
    const axis = 1;
    const labels = Array.from(examples.labels.argMax(axis).dataSync());
    const predictions = Array.from(output.argMax(axis).dataSync());

    ui.showTestResults(examples, predictions, labels);
  });
}

function createModel() {
  let model;
  const modelType = ui.getModelTypeId();
  if (modelType === 'ConvNet') {
    model = createConvModel();
  } else if (modelType === 'DenseNet') {
    model = createDenseModel();
  } else {
    throw new Error(`Invalid model type: ${modelType}`);
  }
  return model;
}

let data;
async function load() {
  data = new MnistData();
  await data.load();
}

// This is our main function. It loads the MNIST data, trains the model, and
// then shows what the model predicted on unseen test data.
ui.setTrainButtonCallback(async () => {
  ui.logStatus('Loading MNIST data...');
  await load();

  ui.logStatus('Creating model...');
  const model = createModel();
  model.summary();

  ui.logStatus('Starting model training...');
  await train(model, () => showPredictions(model));
});
<commit_msg>Remove unused code and fix JSDoc (#219)<commit_after>/**
 * @license
 * Copyright 2018 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */

import * as tf from '@tensorflow/tfjs';

// This is a helper class for loading and managing MNIST data specifically.
// It is a useful example of how you could create your own data manager class
// for arbitrary data though. It's worth a look :)
import {IMAGE_H, IMAGE_W, MnistData} from './data';

// This is a helper class for drawing loss graphs and MNIST images to the
// window. For the purposes of understanding the machine learning bits, you can
// largely ignore it
import * as ui from './ui';

/**
 * Creates a convolutional neural network (Convnet) for the MNIST data.
 *
 * @returns {tf.Model} An instance of tf.Model.
 */
function createConvModel() {
  // Create a sequential neural network model. tf.sequential provides an API
  // for creating "stacked" models where the output from one layer is used as
  // the input to the next layer.
  const model = tf.sequential();

  // The first layer of the convolutional neural network plays a dual role:
  // it is both the input layer of the neural network and a layer that performs
  // the first convolution operation on the input. It receives the 28x28 pixels
  // black and white images. This input layer uses 16 filters with a kernel size
  // of 5 pixels each. It uses a simple RELU activation function which pretty
  // much just looks like this: __/
  model.add(tf.layers.conv2d({
    inputShape: [IMAGE_H, IMAGE_W, 1],
    kernelSize: 3,
    filters: 16,
    activation: 'relu'
  }));

  // After the first layer we include a MaxPooling layer. This acts as a sort of
  // downsampling using max values in a region instead of averaging.
  // https://www.quora.com/What-is-max-pooling-in-convolutional-neural-networks
  model.add(tf.layers.maxPooling2d({poolSize: 2, strides: 2}));

  // Our third layer is another convolution, this time with 32 filters.
  model.add(tf.layers.conv2d({kernelSize: 3, filters: 32, activation: 'relu'}));

  // Max pooling again.
  model.add(tf.layers.maxPooling2d({poolSize: 2, strides: 2}));

  // Add another conv2d layer.
  model.add(tf.layers.conv2d({kernelSize: 3, filters: 32, activation: 'relu'}));

  // Now we flatten the output from the 2D filters into a 1D vector to prepare
  // it for input into our last layer. This is common practice when feeding
  // higher dimensional data to a final classification output layer.
  model.add(tf.layers.flatten({}));

  model.add(tf.layers.dense({units: 64, activation: 'relu'}));

  // Our last layer is a dense layer which has 10 output units, one for each
  // output class (i.e. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9). Here the classes actually
  // represent numbers, but it's the same idea if you had classes that
  // represented other entities like dogs and cats (two output classes: 0, 1).
  // We use the softmax function as the activation for the output layer as it
  // creates a probability distribution over our 10 classes so their output
  // values sum to 1.
  model.add(tf.layers.dense({units: 10, activation: 'softmax'}));

  return model;
}

/**
 * Creates a model consisting of only flatten, dense and dropout layers.
 *
 * The model create here has approximately the same number of parameters
 * (~31k) as the convnet created by `createConvModel()`, but is
 * expected to show a significantly worse accuracy after training, due to the
 * fact that it doesn't utilize the spatial information as the convnet does.
 *
 * This is for comparison with the convolutional network above.
 *
 * @returns {tf.Model} An instance of tf.Model.
 */
function createDenseModel() {
  const model = tf.sequential();
  model.add(tf.layers.flatten({inputShape: [IMAGE_H, IMAGE_W, 1]}));
  model.add(tf.layers.dense({units: 42, activation: 'relu'}));
  model.add(tf.layers.dense({units: 10, activation: 'softmax'}));
  return model;
}

/**
 * This callback type is used by the `train` function for insertion into
 * the model.fit callback loop.
 *
 * @callback onIterationCallback
 * @param {string} eventType Selector for which type of event to fire on.
 * @param {number} batchOrEpochNumber The current epoch / batch number
 * @param {tf.Logs} logs Logs to append to
 */

/**
 * Compile and train the given model.
 *
 * @param {tf.Model} model The model to train.
 * @param {onIterationCallback} onIteration A callback to execute every 10
 *     batches & epoch end.
 */
async function train(model, onIteration) {
  ui.logStatus('Training model...');

  // Now that we've defined our model, we will define our optimizer. The
  // optimizer will be used to optimize our model's weight values during
  // training so that we can decrease our training loss and increase our
  // classification accuracy.

  // We are using rmsprop as our optimizer.
  // An optimizer is an iterative method for minimizing an loss function.
  // It tries to find the minimum of our loss function with respect to the
  // model's weight parameters.
  const optimizer = 'rmsprop';

  // We compile our model by specifying an optimizer, a loss function, and a
  // list of metrics that we will use for model evaluation. Here we're using a
  // categorical crossentropy loss, the standard choice for a multi-class
  // classification problem like MNIST digits.
  // The categorical crossentropy loss is differentiable and hence makes
  // model training possible. But it is not amenable to easy interpretation
  // by a human. This is why we include a "metric", namely accuracy, which is
  // simply a measure of how many of the examples are classified correctly.
  // This metric is not differentiable and hence cannot be used as the loss
  // function of the model.
  model.compile({
    optimizer,
    loss: 'categoricalCrossentropy',
    metrics: ['accuracy'],
  });

  // Batch size is another important hyperparameter. It defines the number of
  // examples we group together, or batch, between updates to the model's
  // weights during training. A value that is too low will update weights using
  // too few examples and will not generalize well. Larger batch sizes require
  // more memory resources and aren't guaranteed to perform better.
  const batchSize = 320;

  // Leave out the last 15% of the training data for validation, to monitor
  // overfitting during training.
  const validationSplit = 0.15;

  // Get number of training epochs from the UI.
  const trainEpochs = ui.getTrainEpochs();

  // We'll keep a buffer of loss and accuracy values over time.
  let trainBatchCount = 0;

  const trainData = data.getTrainData();
  const testData = data.getTestData();

  const totalNumBatches =
      Math.ceil(trainData.xs.shape[0] * (1 - validationSplit) / batchSize) *
      trainEpochs;

  // During the long-running fit() call for model training, we include
  // callbacks, so that we can plot the loss and accuracy values in the page
  // as the training progresses.
  let valAcc;
  await model.fit(trainData.xs, trainData.labels, {
    batchSize,
    validationSplit,
    epochs: trainEpochs,
    callbacks: {
      onBatchEnd: async (batch, logs) => {
        trainBatchCount++;
        ui.logStatus(
            `Training... (` +
            `${(trainBatchCount / totalNumBatches * 100).toFixed(1)}%` +
            ` complete). To stop training, refresh or close page.`);
        ui.plotLoss(trainBatchCount, logs.loss, 'train');
        ui.plotAccuracy(trainBatchCount, logs.acc, 'train');
        if (onIteration && batch % 10 === 0) {
          onIteration('onBatchEnd', batch, logs);
        }
        await tf.nextFrame();
      },
      onEpochEnd: async (epoch, logs) => {
        valAcc = logs.val_acc;
        ui.plotLoss(trainBatchCount, logs.val_loss, 'validation');
        ui.plotAccuracy(trainBatchCount, logs.val_acc, 'validation');
        if (onIteration) {
          onIteration('onEpochEnd', epoch, logs);
        }
        await tf.nextFrame();
      }
    }
  });

  const testResult = model.evaluate(testData.xs, testData.labels);
  const testAccPercent = testResult[1].dataSync()[0] * 100;
  const finalValAccPercent = valAcc * 100;
  ui.logStatus(
      `Final validation accuracy: ${finalValAccPercent.toFixed(1)}%; ` +
      `Final test accuracy: ${testAccPercent.toFixed(1)}%`);
}

/**
 * Show predictions on a number of test examples.
 *
 * @param {tf.Model} model The model to be used for making the predictions.
 */
async function showPredictions(model) {
  const testExamples = 100;
  const examples = data.getTestData(testExamples);

  // Code wrapped in a tf.tidy() function callback will have their tensors freed
  // from GPU memory after execution without having to call dispose().
  // The tf.tidy callback runs synchronously.
  tf.tidy(() => {
    const output = model.predict(examples.xs);

    // tf.argMax() returns the indices of the maximum values in the tensor along
    // a specific axis. Categorical classification tasks like this one often
    // represent classes as one-hot vectors. One-hot vectors are 1D vectors with
    // one element for each output class. All values in the vector are 0
    // except for one, which has a value of 1 (e.g. [0, 0, 0, 1, 0]). The
    // output from model.predict() will be a probability distribution, so we use
    // argMax to get the index of the vector element that has the highest
    // probability. This is our prediction.
    // (e.g. argmax([0.07, 0.1, 0.03, 0.75, 0.05]) == 3)
    // dataSync() synchronously downloads the tf.tensor values from the GPU so
    // that we can use them in our normal CPU JavaScript code
    // (for a non-blocking version of this function, use data()).
    const axis = 1;
    const labels = Array.from(examples.labels.argMax(axis).dataSync());
    const predictions = Array.from(output.argMax(axis).dataSync());

    ui.showTestResults(examples, predictions, labels);
  });
}

function createModel() {
  let model;
  const modelType = ui.getModelTypeId();
  if (modelType === 'ConvNet') {
    model = createConvModel();
  } else if (modelType === 'DenseNet') {
    model = createDenseModel();
  } else {
    throw new Error(`Invalid model type: ${modelType}`);
  }
  return model;
}

let data;
async function load() {
  data = new MnistData();
  await data.load();
}

// This is our main function. It loads the MNIST data, trains the model, and
// then shows what the model predicted on unseen test data.
ui.setTrainButtonCallback(async () => {
  ui.logStatus('Loading MNIST data...');
  await load();

  ui.logStatus('Creating model...');
  const model = createModel();
  model.summary();

  ui.logStatus('Starting model training...');
  await train(model, () => showPredictions(model));
});
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var passport = require('passport-strategy'),
  url = require('url'),
  util = require('util'),
  utils = require('./utils'),
  OAuth = require('wechat-oauth');

function WechatPublicStrategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = undefined;
  }
  options = options || {};

  if (!verify) {
    throw new TypeError('WechatPublicStrategy requires a verify callback');
  }

  if (!options.appId) {
    throw new TypeError('WechatPublicStrategy requires a appId option');
  }
  if (!options.appSecret) {
    throw new TypeError('WechatPublicStrategy requires a appSecret option');
  }

  passport.Strategy.call(this);
  this.name = 'wechat-public-strategy';
  this._oauth = new OAuth(options.appId, options.appSecret);

  this._callbackURL = options.callbackURL;
  this._scope = options.scope;
  this._scopeSeparator = options.scopeSeparator || ' ';
  this._state = options.state;
  this._verify = function(data, verified) {
    verified(null, data);
  }

};

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(WechatPublicStrategy, passport.Strategy);

WechatPublicStrategy.prototype.authenticate = function(req, options, verify) {
  options = options || {};
  if (verify && typeof verify === 'function') {
    this._verify = verify;
  }

  var self = this;
  if (req.query && req.query.error) {
    if (req.query.error == 'access_denied') {
      return this.fail({
        message: req.query.error_description
      });
    } else {
      return this.error(new Error(req.query.error_description));
    }
  }

  var callbackURL = options.callbackURL || this._callbackURL;
  if (callbackURL) {
    var parsed = url.parse(callbackURL);
    if (!parsed.protocol) {
      // The callback URL is relative, resolve a fully qualified URL from the
      // URL of the originating request.
      callbackURL = url.resolve(utils.originalURL(req, {
        proxy: this._trustProxy
      }), callbackURL);
    }
  }

  if (req.query && req.query.code) {
    var code = req.query.code;
    this._code = code;

    var params = this.tokenParams(options);

    function verified(err, user, info) {
      if (err) {
        return self.error(err);
      }
      if (!user) {
        return self.fail(info);
      }
      self.success(user, info);
    }

    this._oauth.getAccessToken(code,
      function(err, result) {
        if (err) return self.error(err);

        var data = result.data;
        if (data.scope === 'snsapi_base') {
          self._verify(data, verified);
        } else {
          self.getUserInfo(data.openid);
        }
      }
    );
  } else {
    var params = this.authorizationParams(options);
    params.redirect_uri = callbackURL;
    var scope = options.scope || this._scope;
    if (scope) {
      if (Array.isArray(scope)) {
        scope = scope.join(this._scopeSeparator);
      }
      params.scope = scope;
    }

    var location = this._oauth.getAuthorizeURL(params.redirect_uri, params.state, params.state);
    this.redirect(location, 302);
  }
};

WechatPublicStrategy.prototype.getUserInfo = function(openId) {
  var self = this;
  var lang = this._lang || 'zh_CN';
  var params = {
    openId: openId,
    lang: lang
  };
  this._oauth.getUser(params, function(err, result) {
    if (err) return self.error(err);
    this._verify(result, verified);
  });
};

WechatPublicStrategy.prototype.authorizationParams = function(options) {
  return {};
};

WechatPublicStrategy.prototype.tokenParams = function(options) {
  return {};
};

module.exports = WechatPublicStrategy;
<commit_msg>update to confirm the passport conventions<commit_after>
var passport = require('passport-strategy')
var url = require('url');
var util = require('util');
var utils = require('./utils');
var OAuth = require('wechat-oauth');

function WechatPublicStrategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = undefined;
  }
  options = options || {};

  if (!verify) {
    throw new TypeError('WechatPublicStrategy requires a verify callback');
  }

  if (!options.clientID) {
    throw new TypeError('WechatPublicStrategy requires a clientID option');
  }
  if (!options.clientSecret) {
    throw new TypeError('WechatPublicStrategy requires a clientSecret option');
  }

  passport.Strategy.call(this);
  this.name = 'wechat-public-strategy';
  this._oauth = new OAuth(options.clientID, options.clientSecret);

  this._callbackURL = options.callbackURL;
  this._scope = options.scope;
  this._scopeSeparator = options.scopeSeparator || ' ';
  this._state = options.state;
  this._verify = verify;
   this._passReqToCallback = options.passReqToCallback;
}

};

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(WechatPublicStrategy, passport.Strategy);

WechatPublicStrategy.prototype.authenticate = function(req, options) {
  options = options || {};

  var self = this;
  if (req.query && req.query.error) {
    if (req.query.error == 'access_denied') {
      return this.fail({
        message: req.query.error_description
      });
    } else {
      return this.error(new Error(req.query.error_description));
    }
  }

  var callbackURL = options.callbackURL || this._callbackURL;
  if (callbackURL) {
    var parsed = url.parse(callbackURL);
    if (!parsed.protocol) {
      // The callback URL is relative, resolve a fully qualified URL from the
      // URL of the originating request.
      callbackURL = url.resolve(utils.originalURL(req, {
        proxy: this._trustProxy
      }), callbackURL);
    }
  }

  function verifyResult(accessToken, refreshToken, params,profile,verified) {
    try {
      if (self._passReqToCallback) {
        var arity = self._verify.length;
        if (arity == 6) {
          self._verify(req, accessToken, refreshToken, params, profile, verified);
        } else { // arity == 5
          self._verify(req, accessToken, refreshToken, profile, verified);
        }
      } else {
        var arity = self._verify.length;
        if (arity == 5) {
          self._verify(accessToken, refreshToken, params, profile, verified);
        } else { // arity == 4
          self._verify();
        }
      }
    } catch (ex) {
      return self.error(ex);
    }
  }

  function verified(err, user, info) {
    if (err) {
      return self.error(err);
    }
    if (!user) {
      return self.fail(info);
    }
    self.success(user, info);
  }

  if (req.query && req.query.code) {
    var code = req.query.code;
    this._code = code;
    var params = this.tokenParams(options);
    this._oauth.getAccessToken(code,
      function(err, result) {
        if (err) return self.error(err);
        var data = result.data;
        var accessToken = data.access_token;
        var refreshToken = data.refresh_token;
        var openId = data.openid;
        if (data.scope === 'snsapi_base') {
          verifyResult(accessToken, refreshToken,{}, {},verified);
        } else {
          self.getUserInfo(openId, function(err,profile){
            if(err) return self.error(err);
            profile = profile;
            verifyResult(accessToken, refreshToken,{}, profile,verified)
          });
        }
      }
    );
  } else {
    var params = this.authorizationParams(options);
    params.redirect_uri = callbackURL;
    var scope = options.scope || this._scope;
    if (scope) {
      if (Array.isArray(scope)) {
        scope = scope.join(this._scopeSeparator);
      }
      params.scope = scope;
    }

    var location = this._oauth.getAuthorizeURL(params.redirect_uri, params.state, params.state);
    this.redirect(location, 302);
  }
};

WechatPublicStrategy.prototype.getUserInfo = function(openId, cb) {
  var self = this;
  var lang = this._lang || 'zh_CN';
  var params = {
    openId: openId,
    lang: lang
  };
  this._oauth.getUser(params, function(err, profile) {
    if (err) return cb(err);
    cb(null, profile);
  });
};

WechatPublicStrategy.prototype.authorizationParams = function(options) {
  return {};
};

WechatPublicStrategy.prototype.tokenParams = function(options) {
  return {};
};

module.exports = WechatPublicStrategy;<|endoftext|>


<commit_before>"use strict";

let assert = require( 'assert' )
let weave = require(  '../' )

context( 'weave', function () {
  describe( 'constructing a new app', function () {
    it( 'should return an instance of weave.App', function () {
      assert( weave() instanceof weave.App )
    })

    it( 'should allow naming of apps', function () {
      assert( weave( 'tester' ).appName === 'tester' )
      assert( new weave.App( 'tester2' ).appName === 'tester2' )
    })

    it( 'should allow configuring of apps', function () {
      assert( weave({ 'location': 'behavior' }).configuration.location === 'behavior' )
    })
  })
})
<commit_msg>This should also work<commit_after>let assert = require( 'assert' )
let weave = require(  '../' )

context( 'weave', function () {
  describe( 'constructing a new app', function () {
    it( 'should return an instance of weave.App', function () {
      assert( weave() instanceof weave.App )
    })

    it( 'should allow naming of apps', function () {
      assert( weave( 'tester' ).appName === 'tester' )
      assert( new weave.App( 'tester2' ).appName === 'tester2' )
    })

    it( 'should allow configuring of apps', function () {
      assert( weave({ 'location': 'behavior' }).configuration.location === 'behavior' )
    })
  })
})
<|endoftext|>