<commit_before>'use strict'

// I use "const" throughout just like I use 'final' in Java:
// to avoid shooting myself in the foot.
// By convention, I only uppercase the names of true global constants,
// generally strings
const express = require('express');
const exphbs  = require('express-handlebars');
const bodyParser = require('body-parser');
const mongodb = require("mongodb");
const states = require('./util/statelist');
const dateFormat = require('dateformat');

// invert our states mapping; we only want abbreviations in our form,
// as they're our hash key for vaslidation back
const stateAbbrevs = Object.keys(states);
stateAbbrevs.sort();

const validator = require("./util/validator.js");
const app = express();

// configuration constants; should move to a properties file perhaps?
// page and route data
const REGISTER_TITLE = "Registration";
const REGISTER_PATH = "/register";
const REGISTER_VIEW = "register";
const REGISTER_STYLE = "styles/register.css";

const REPORT_PATH = "/report";
const REPORT_VIEW = "report";
const REPORT_TITLE = "Attendee Report";
const REPORT_STYLE = "styles/report.css";
const CONFIRMATION_PATH = REGISTER_PATH;
const CONFIRMATION_VIEW = "confirmation";
const CONFIRMATION_TITLE = "Registration Confirmation";
const CONFIRMATION_STYLE = "styles/confirmation.css";
const NOT_FOUND_VIEW  = "404";
const NOT_FOUND_STYLE  = "404";
const NOT_FOUND_TITLES = [
    "Je ne peux pas le trouver",
    "Non lo trovo",
    "no puedo encontrarlo",
    "nad oes modd i ddod o hyd iddo",
    "я не могу найти его",
    "Watashi wa sore o mitsukeru koto ga dekimasen"
];

// Messages
const VALIDATION_ERROR = "Failed to create new attendee.";
const SAVE_ERROR =  "Error saving new attendee";
const FIND_ERROR = "Failed to get attendee list";

const ATTENDEES_COLLECTION = "attendees";

app.set("port", (process.env.PORT || 5000));

// Create a database variable outside of the database connection callback to reuse the connection pool in your app.
const mongoUrl = process.env.MONGODB_URI || "mongodb://localhost:27017/myproject";

var db;
mongodb.MongoClient.connect(mongoUrl,
    function (err, database) {
        if (err) {
            console.log(err);
            process.exit(1);
        }
        // database object is deliberate global so it is usuable
        // throughout the application and we don"t continuously reconnect.
        // Don"t like it at all; should wrap this in a service module.
        console.log("Database connection ready");
        db = database;
});

// parse application/x-www-form-urlencoded
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.engine("handlebars", exphbs({defaultLayout: "main"}));
app.set("view engine", "handlebars");

app.get("/", function (req, res) {
    res.redirect(REGISTER_PATH);
});

app.get(REGISTER_PATH,  function(req,res) {
    res.render(REGISTER_VIEW, {
        title: REGISTER_TITLE,
        states: stateAbbrevs,
        stylesheet: REGISTER_STYLE
    });
});

// Handle posts to the register URL -- cannot GET against the data,
// Only post from client-side validation form, and we do
// server-side validation so, theoretically,
// we should never try to save bad data
app.post(CONFIRMATION_PATH, urlencodedParser, function(req,res) {
    var posted = req.body;
    console.log(posted);

    var newAttendee = new Attendee(posted);
    var validationErrors = validator.validateAttendee(newAttendee);
    if(Object.keys(validationErrors).length == 0) {
    db.collection(ATTENDEES_COLLECTION).insertOne(newAttendee, function(err, doc) {
        if (err) {
            handleError(res,err.message,SAVE_ERROR);
        } else {
            // success; display confirmation page
            res.render(CONFIRMATION_VIEW, {
                title: CONFIRMATION_TITLE,
                stylesheet: CONFIRMATION_STYLE,
                attendee: doc.ops[0]
            });
        }
    });
    } else {
        // handleError(res,SAVE_ERROR,VALIDATION_ERROR,validationErrors);
        res.render(REGISTER_VIEW, {
            title: REGISTER_TITLE,
            states: stateAbbrevs,
            stylesheet: REGISTER_STYLE,
            errors: validationErrors
        });
    }
}
);

app.get(REPORT_PATH, function(req,res) {
    db.collection(ATTENDEES_COLLECTION).find({}).toArray(function(err, docs) {
        if (err) {
            handleError(res, err.message,FIND_ERROR);
        } else {
            //
            // sort by reverse date
            //
            docs.sort(compareDates);
            res.render(REPORT_VIEW, {
                title: REPORT_TITLE,
                stylesheet: REPORT_STYLE,
                attendees: docs
            });
        }
    });
});

const staticDir = __dirname + "/public";
console.log(staticDir);
app.use(express.static(staticDir, {
    fallthrough: true
}));

// default route
app.use(function(req, res,next) {
    var idx = Math.ceil((Math.random() * 3))-1;
    var not_found = NOT_FOUND_TITLES[idx];
       res.render(NOT_FOUND_VIEW, {
           title: not_found,
           stylesheet: NOT_FOUND_STYLE,
       });
});

// Not the best handling; informative for testing with curl, though
function handleError(res, reason, message, code) {
    console.log("ERROR: " + reason);
    var errData = {"error": message };
    if (code) {
        errData.details = code;
    }
    res.status(500).json(errData);
}

app.listen(app.get('port'), function() {
    console.log('Node app is running on port', app.get('port'));
});

//
// wasn't working with 'require', darn it
//
function Attendee (data) {
    this.firstName = data["firstName"];
    this.lastName = data["lastName"];
    this.address = data["address"];
    this.address2 = data["address2"];
    this.city = data["city"];
    this.state = data["state"];
    this.country = data["country"];
    this.zipCode = data["zipCode"];
    var now = new Date();
    this.registerDate = now;
    this.formattedRegisterDate = dateFormat(now,"yyyy-mm-dd HH:MM:ss");
}

function compareDates(a,b) {
    if (a && a["registerDate"]) {
        if (b && b["registerDate"]) {
            if (a == b) {
              return 0;
            } else  {
              return a["registerDate"] < b["registerDate"] ? 1:-1;
            }
        } else {
            // no b -- sort lower
            return 1;
        }
    } else {
        // no a -- sort a lower
        return -1;
    }
};
<commit_msg>Fixed error in silly random title generation<commit_after>'use strict'

// I use "const" throughout just like I use 'final' in Java:
// to avoid shooting myself in the foot.
// By convention, I only uppercase the names of true global constants,
// generally strings
const express = require('express');
const exphbs  = require('express-handlebars');
const bodyParser = require('body-parser');
const mongodb = require("mongodb");
const states = require('./util/statelist');
const dateFormat = require('dateformat');

// invert our states mapping; we only want abbreviations in our form,
// as they're our hash key for vaslidation back
const stateAbbrevs = Object.keys(states);
stateAbbrevs.sort();

const validator = require("./util/validator.js");
const app = express();

// configuration constants; should move to a properties file perhaps?
// page and route data
const REGISTER_TITLE = "Registration";
const REGISTER_PATH = "/register";
const REGISTER_VIEW = "register";
const REGISTER_STYLE = "styles/register.css";

const REPORT_PATH = "/report";
const REPORT_VIEW = "report";
const REPORT_TITLE = "Attendee Report";
const REPORT_STYLE = "styles/report.css";
const CONFIRMATION_PATH = REGISTER_PATH;
const CONFIRMATION_VIEW = "confirmation";
const CONFIRMATION_TITLE = "Registration Confirmation";
const CONFIRMATION_STYLE = "styles/confirmation.css";
const NOT_FOUND_VIEW  = "404";
const NOT_FOUND_STYLE  = "404";
const NOT_FOUND_TITLES = [
    "Je ne peux pas le trouver",
    "Non lo trovo",
    "no puedo encontrarlo",
    "nad oes modd i ddod o hyd iddo",
    "я не могу найти его",
    "Watashi wa sore o mitsukeru koto ga dekimasen"
];

// Messages
const VALIDATION_ERROR = "Failed to create new attendee.";
const SAVE_ERROR =  "Error saving new attendee";
const FIND_ERROR = "Failed to get attendee list";

const ATTENDEES_COLLECTION = "attendees";

app.set("port", (process.env.PORT || 5000));

// Create a database variable outside of the database connection callback to reuse the connection pool in your app.
const mongoUrl = process.env.MONGODB_URI || "mongodb://localhost:27017/myproject";

var db;
mongodb.MongoClient.connect(mongoUrl,
    function (err, database) {
        if (err) {
            console.log(err);
            process.exit(1);
        }
        // database object is deliberate global so it is usuable
        // throughout the application and we don"t continuously reconnect.
        // Don"t like it at all; should wrap this in a service module.
        console.log("Database connection ready");
        db = database;
});

// parse application/x-www-form-urlencoded
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.engine("handlebars", exphbs({defaultLayout: "main"}));
app.set("view engine", "handlebars");

app.get("/", function (req, res) {
    res.redirect(REGISTER_PATH);
});

app.get(REGISTER_PATH,  function(req,res) {
    res.render(REGISTER_VIEW, {
        title: REGISTER_TITLE,
        states: stateAbbrevs,
        stylesheet: REGISTER_STYLE
    });
});

// Handle posts to the register URL -- cannot GET against the data,
// Only post from client-side validation form, and we do
// server-side validation so, theoretically,
// we should never try to save bad data
app.post(CONFIRMATION_PATH, urlencodedParser, function(req,res) {
    var posted = req.body;
    console.log(posted);

    var newAttendee = new Attendee(posted);
    var validationErrors = validator.validateAttendee(newAttendee);
    if(Object.keys(validationErrors).length == 0) {
    db.collection(ATTENDEES_COLLECTION).insertOne(newAttendee, function(err, doc) {
        if (err) {
            handleError(res,err.message,SAVE_ERROR);
        } else {
            // success; display confirmation page
            res.render(CONFIRMATION_VIEW, {
                title: CONFIRMATION_TITLE,
                stylesheet: CONFIRMATION_STYLE,
                attendee: doc.ops[0]
            });
        }
    });
    } else {
        // handleError(res,SAVE_ERROR,VALIDATION_ERROR,validationErrors);
        res.render(REGISTER_VIEW, {
            title: REGISTER_TITLE,
            states: stateAbbrevs,
            stylesheet: REGISTER_STYLE,
            errors: validationErrors
        });
    }
}
);

app.get(REPORT_PATH, function(req,res) {
    db.collection(ATTENDEES_COLLECTION).find({}).toArray(function(err, docs) {
        if (err) {
            handleError(res, err.message,FIND_ERROR);
        } else {
            //
            // sort by reverse date
            //
            docs.sort(compareDates);
            res.render(REPORT_VIEW, {
                title: REPORT_TITLE,
                stylesheet: REPORT_STYLE,
                attendees: docs
            });
        }
    });
});

const staticDir = __dirname + "/public";
console.log(staticDir);
app.use(express.static(staticDir, {
    fallthrough: true
}));

// default route
app.use(function(req, res,next) {
    var idx = Math.ceil((Math.random() * NOT_FOUND_TITLES.length))-1;
    var not_found = NOT_FOUND_TITLES[idx];
       res.render(NOT_FOUND_VIEW, {
           title: not_found,
           stylesheet: NOT_FOUND_STYLE,
       });
});

// Not the best handling; informative for testing with curl, though
function handleError(res, reason, message, code) {
    console.log("ERROR: " + reason);
    var errData = {"error": message };
    if (code) {
        errData.details = code;
    }
    res.status(500).json(errData);
}

app.listen(app.get('port'), function() {
    console.log('Node app is running on port', app.get('port'));
});

//
// wasn't working with 'require', darn it
//
function Attendee (data) {
    this.firstName = data["firstName"];
    this.lastName = data["lastName"];
    this.address = data["address"];
    this.address2 = data["address2"];
    this.city = data["city"];
    this.state = data["state"];
    this.country = data["country"];
    this.zipCode = data["zipCode"];
    var now = new Date();
    this.registerDate = now;
    this.formattedRegisterDate = dateFormat(now,"yyyy-mm-dd HH:MM:ss");
}

function compareDates(a,b) {
    if (a && a["registerDate"]) {
        if (b && b["registerDate"]) {
            if (a == b) {
              return 0;
            } else  {
              return a["registerDate"] < b["registerDate"] ? 1:-1;
            }
        } else {
            // no b -- sort lower
            return 1;
        }
    } else {
        // no a -- sort a lower
        return -1;
    }
};
<|endoftext|>


<commit_before>var postcss = require('postcss');

module.exports = postcss.plugin('postcss-pointer', function () {

  return function (css) {
    css.eachDecl(function transformDecl (decl) {
      if (decl.prop === 'pointer') {
        if (decl.value.indexOf('cursor') !== -1) {
          decl.prop  = 'cursor';
          decl.value = 'pointer';
        }
      }
    });
  };

});
<commit_msg>Refactoring for more clear code<commit_after>var postcss = require('postcss');

module.exports = postcss.plugin('postcss-pointer', function () {

  return function (css) {
    css.eachDecl('pointer', function (decl) {
      if (decl.value.indexOf('cursor') !== -1) {
        decl.prop  = 'cursor';
        decl.value = 'pointer';
      }
    });
  };

});
<|endoftext|>


<commit_before>const PORT = 8080;
const SESSION_SECRET = process.env.TANDEM_SESSION_SECRET;
const SOUNDCLOUD_APP_ID = process.env.TANDEM_SOUNDCLOUD_APP_ID;
const SOUNDCLOUD_APP_SECRET = process.env.TANDEM_SOUNDCLOUD_APP_SECRET;
const YOUTUBE_APP_ID = process.env.TANDEM_YOUTUBE_APP_ID;
const YOUTUBE_APP_SECRET = process.env.TANDEM_YOUTUBE_APP_SECRET;
const YOUTUBE_API_KEY = process.env.TANDEM_YOUTUBE_API_KEY;
const MONGO_URL = process.env.TANDEM_MONGO_URL || 'mongodb://localhost/tandem';
const URL = process.env.TANDEM_URL || 'http://dev.tandem.io:8080';
const ENV = process.env.NODE_ENV;
const SOUNDCLOUD_API_BASE_URL = 'https://api.soundcloud.com';
const YOUTUBE_API_BASE_URL = 'https://www.googleapis.com/youtube/v3';
const NO_OP = function(){};

var http = require('http');
var socket_io = require('socket.io');
var _ = require('underscore');
var request = require('request');
var express = require('express');
var expose = require('express-expose');
var server = express();
var http_server = http.createServer( server );
var io = socket_io.listen( http_server );

var generateAuthToken = require('./utils/generateAuthToken.js');

var Room = require('./models/room.js')({ io: io });

// Database
var mongoose = require('mongoose');
mongoose.connect( MONGO_URL );

// Set up templates for Express
var express_handlebars = require('express3-handlebars');
var handlebars = express_handlebars({
	extname: '.hbs',
	defaultLayout: false,
});
server.engine( 'hbs', handlebars );
server.set( 'view engine', 'hbs' );

// Static file serving
server.use( express.compress() );
server.use( express.static( __dirname + '/assets' ) );

// Sessions
var MongoStore = require('connect-mongo')( express );
server.use( express.cookieParser() );
server.use( express.bodyParser() );
server.use( express.session({
	secret: SESSION_SECRET,
	cookie: {
		maxAge: new Date( Date.now() + 1000 * 60 * 60 * 24 * 14 ) // 2 weeks from now
	},
	store: new MongoStore({
		url: MONGO_URL
	})
}) );

// Pass environment to views
server.locals.dev = ( ENV === 'development' );

// Passport stuff
var passport = require('passport');
var guestSetup = require('./middleware/guest.js');

server.use( passport.initialize() );
server.use( passport.session() );
server.use( guestSetup );

passport.serializeUser( function( user, done ){
	done( null, user );
});

passport.deserializeUser( function( obj, done ){
	done( null, obj );
});

// Soundcloud login
var passport_soundcloud = require('passport-soundcloud');
var SoundcloudStrategy = passport_soundcloud.Strategy;

passport.use( new SoundcloudStrategy({
	clientID: SOUNDCLOUD_APP_ID,
	clientSecret: SOUNDCLOUD_APP_SECRET,
	callbackURL: URL +'/auth/soundcloud/callback',
	passReqToCallback: true
}, function( req, access_token, refresh_token, params, profile, done ){
	var user = {
		soundcloud_id: profile.id,
		soundcloud_access_token: access_token,
		soundcloud_access_token_expiry: Date.now() + ( params.expires_in * 1000 ),
		soundcloud_refresh_token: refresh_token
	};
	// if we already have a user session, merge them
	if( req.user ){
		user = _.extend( req.user, user );
	}
	done( null, user );
}));

// Youtube login
var passport_google = require('passport-google-oauth');
var GoogleStrategy = passport_google.OAuth2Strategy;

var getLikesID = function( access_token, callback ){
	callback = callback || NO_OP;
	request({
		url: YOUTUBE_API_BASE_URL +'/channels',
		qs: {
			part: 'contentDetails',
			mine: true
		},
		headers: {
			'Authorization': 'Bearer '+ access_token
		},
		method: 'GET',
		json: true
	}, function( err, res, body ){
		callback( err, body.items[0].contentDetails.relatedPlaylists.likes );
	});
};

passport.use( new GoogleStrategy({
	clientID: YOUTUBE_APP_ID,
	clientSecret: YOUTUBE_APP_SECRET,
	callbackURL: URL +'/auth/youtube/callback',
	profileURL: 'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true',
	passReqToCallback: true
}, function( req, access_token, refresh_token, params, profile, done ){
	var user = {
		youtube_id: profile.id,
		youtube_access_token: access_token,
		youtube_access_token_expiry: Date.now() + ( params.expires_in * 1000 ),
		youtube_refresh_token: refresh_token
	};
	// get ID of user's likes playlist
	getLikesID( access_token, function( err, likes_id ){
		user.youtube_likes_id = likes_id;
		// if we already have a user session, merge them
		if( req.user ){
			user = _.extend( req.user, user );
		}
		done( null, user );
	});
}));

// Routes
server.get( '/auth/soundcloud',	passport.authenticate('soundcloud') );

server.get( '/auth/soundcloud/callback', passport.authenticate( 'soundcloud', {
	successRedirect: '/',
	failureRedirect: '/?err=soundcloud-login-failed'
}));

server.get( '/auth/youtube', passport.authenticate( 'google', {
	scope: 'https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
	accessType: 'offline'
}));

server.get( '/auth/youtube/callback', passport.authenticate( 'google', {
	successRedirect: '/',
	failureRedirect: '/?err=youtube-login-failed'
}));

server.post( '/api/v1/rooms', function( req, res ){
	var room = new Room;
	room.save( function( err, room ){
		res.json( room );
	});
});

server.get( '/api/v1/rooms/:id?', function( req, res ){
	if( req.params.id ){
		Room.findById( req.params.id, function( err, room ){
			res.json( room );
		});
	}
	else {
		Room.find( function( err, rooms ){
			res.json( rooms );
		});
	}
});

server.put( '/api/v1/rooms/:id', function( req, res ){
	var new_data = _.omit( req.body, '_id' );
	Room.findByIdAndUpdate( req.params.id, new_data, function( err, room ){
		res.json( room );
	});
});

server.delete( '/api/v1/rooms/:id', function( req, res ){
	Room.findByIdAndRemove( req.params.id, function( err, room ){
		res.json( room );
	});
});

server.all( /^\/api\/v1\/proxy\/soundcloud\/(.+)$/, function( req, res ){
	var query = _.extend( req.query, {
		oauth_token: req.user.soundcloud_access_token
	});
	var endpoint = req.params[0];
	request({
		url: SOUNDCLOUD_API_BASE_URL +'/'+ endpoint,
		qs: query,
		method: req.method
	}).pipe( res );
});

server.all( /^\/api\/v1\/proxy\/youtube\/(.+)$/, function( req, res ){
	var query = _.extend( req.query, {
		key: YOUTUBE_API_KEY
	});
	var endpoint = req.params[0];
	request({
		url: YOUTUBE_API_BASE_URL +'/'+ endpoint,
		qs: query,
		json: req.body,
		headers: {
			'Authorization': 'Bearer '+ req.user.youtube_access_token
		},
		method: req.method
	}).pipe( res );
});

server.get( '/logout', function( req, res ){
	req.logout();
	res.redirect('/');
});

server.post( '/rooms', function( req, res ){
	var room = new Room;
	res.redirect( '/rooms/'+ room.id );
});

server.get( '/rooms/:id', function( req, res ){
	var user = req.session.passport.user || {};
	user = _.pick( user, 'id', 'name', 'youtube_id', 'youtube_likes_id', 'soundcloud_id' );
	user.token = generateAuthToken( user.id, user.name );
	var room = Room.findById( req.params.id, true );
	res.expose( room, 'tandem.bridge.room' );
	res.expose( user, 'tandem.bridge.user' );
	res.expose( {
		soundcloud: {
			client_id: SOUNDCLOUD_APP_ID
		}
	}, 'tandem.bridge.apis' );
	res.render( 'room.hbs', {
		room: room,
		user: user
	});
});

var renderIndex = function( req, res ){
	var rooms = Room.list( true );
	res.render( 'index.hbs', {
		rooms: rooms
	});
};

server.get( '/', renderIndex );
server.get( '/rooms/:id', renderIndex );

http_server.listen( PORT );

console.log('[tandem] listening on port', PORT );<commit_msg>properly assign session cookie maxAge fixes #30<commit_after>const PORT = 8080;
const SESSION_SECRET = process.env.TANDEM_SESSION_SECRET;
const SOUNDCLOUD_APP_ID = process.env.TANDEM_SOUNDCLOUD_APP_ID;
const SOUNDCLOUD_APP_SECRET = process.env.TANDEM_SOUNDCLOUD_APP_SECRET;
const YOUTUBE_APP_ID = process.env.TANDEM_YOUTUBE_APP_ID;
const YOUTUBE_APP_SECRET = process.env.TANDEM_YOUTUBE_APP_SECRET;
const YOUTUBE_API_KEY = process.env.TANDEM_YOUTUBE_API_KEY;
const MONGO_URL = process.env.TANDEM_MONGO_URL || 'mongodb://localhost/tandem';
const URL = process.env.TANDEM_URL || 'http://dev.tandem.io:8080';
const ENV = process.env.NODE_ENV;
const SOUNDCLOUD_API_BASE_URL = 'https://api.soundcloud.com';
const YOUTUBE_API_BASE_URL = 'https://www.googleapis.com/youtube/v3';
const NO_OP = function(){};

var http = require('http');
var socket_io = require('socket.io');
var _ = require('underscore');
var request = require('request');
var express = require('express');
var expose = require('express-expose');
var server = express();
var http_server = http.createServer( server );
var io = socket_io.listen( http_server );

var generateAuthToken = require('./utils/generateAuthToken.js');

var Room = require('./models/room.js')({ io: io });

// Database
var mongoose = require('mongoose');
mongoose.connect( MONGO_URL );

// Set up templates for Express
var express_handlebars = require('express3-handlebars');
var handlebars = express_handlebars({
	extname: '.hbs',
	defaultLayout: false,
});
server.engine( 'hbs', handlebars );
server.set( 'view engine', 'hbs' );

// Static file serving
server.use( express.compress() );
server.use( express.static( __dirname + '/assets' ) );

// Sessions
var MongoStore = require('connect-mongo')( express );
server.use( express.cookieParser() );
server.use( express.bodyParser() );
server.use( express.session({
	secret: SESSION_SECRET,
	cookie: {
		maxAge: 1000 * 60 * 60 * 24 * 14 // 2 weeks from now
	},
	store: new MongoStore({
		url: MONGO_URL
	})
}) );

// Pass environment to views
server.locals.dev = ( ENV === 'development' );

// Passport stuff
var passport = require('passport');
var guestSetup = require('./middleware/guest.js');

server.use( passport.initialize() );
server.use( passport.session() );
server.use( guestSetup );

passport.serializeUser( function( user, done ){
	done( null, user );
});

passport.deserializeUser( function( obj, done ){
	done( null, obj );
});

// Soundcloud login
var passport_soundcloud = require('passport-soundcloud');
var SoundcloudStrategy = passport_soundcloud.Strategy;

passport.use( new SoundcloudStrategy({
	clientID: SOUNDCLOUD_APP_ID,
	clientSecret: SOUNDCLOUD_APP_SECRET,
	callbackURL: URL +'/auth/soundcloud/callback',
	passReqToCallback: true
}, function( req, access_token, refresh_token, params, profile, done ){
	var user = {
		soundcloud_id: profile.id,
		soundcloud_access_token: access_token,
		soundcloud_access_token_expiry: Date.now() + ( params.expires_in * 1000 ),
		soundcloud_refresh_token: refresh_token
	};
	// if we already have a user session, merge them
	if( req.user ){
		user = _.extend( req.user, user );
	}
	done( null, user );
}));

// Youtube login
var passport_google = require('passport-google-oauth');
var GoogleStrategy = passport_google.OAuth2Strategy;

var getLikesID = function( access_token, callback ){
	callback = callback || NO_OP;
	request({
		url: YOUTUBE_API_BASE_URL +'/channels',
		qs: {
			part: 'contentDetails',
			mine: true
		},
		headers: {
			'Authorization': 'Bearer '+ access_token
		},
		method: 'GET',
		json: true
	}, function( err, res, body ){
		callback( err, body.items[0].contentDetails.relatedPlaylists.likes );
	});
};

passport.use( new GoogleStrategy({
	clientID: YOUTUBE_APP_ID,
	clientSecret: YOUTUBE_APP_SECRET,
	callbackURL: URL +'/auth/youtube/callback',
	profileURL: 'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true',
	passReqToCallback: true
}, function( req, access_token, refresh_token, params, profile, done ){
	var user = {
		youtube_id: profile.id,
		youtube_access_token: access_token,
		youtube_access_token_expiry: Date.now() + ( params.expires_in * 1000 ),
		youtube_refresh_token: refresh_token
	};
	// get ID of user's likes playlist
	getLikesID( access_token, function( err, likes_id ){
		user.youtube_likes_id = likes_id;
		// if we already have a user session, merge them
		if( req.user ){
			user = _.extend( req.user, user );
		}
		done( null, user );
	});
}));

// Routes
server.get( '/auth/soundcloud',	passport.authenticate('soundcloud') );

server.get( '/auth/soundcloud/callback', passport.authenticate( 'soundcloud', {
	successRedirect: '/',
	failureRedirect: '/?err=soundcloud-login-failed'
}));

server.get( '/auth/youtube', passport.authenticate( 'google', {
	scope: 'https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
	accessType: 'offline'
}));

server.get( '/auth/youtube/callback', passport.authenticate( 'google', {
	successRedirect: '/',
	failureRedirect: '/?err=youtube-login-failed'
}));

server.post( '/api/v1/rooms', function( req, res ){
	var room = new Room;
	room.save( function( err, room ){
		res.json( room );
	});
});

server.get( '/api/v1/rooms/:id?', function( req, res ){
	if( req.params.id ){
		Room.findById( req.params.id, function( err, room ){
			res.json( room );
		});
	}
	else {
		Room.find( function( err, rooms ){
			res.json( rooms );
		});
	}
});

server.put( '/api/v1/rooms/:id', function( req, res ){
	var new_data = _.omit( req.body, '_id' );
	Room.findByIdAndUpdate( req.params.id, new_data, function( err, room ){
		res.json( room );
	});
});

server.delete( '/api/v1/rooms/:id', function( req, res ){
	Room.findByIdAndRemove( req.params.id, function( err, room ){
		res.json( room );
	});
});

server.all( /^\/api\/v1\/proxy\/soundcloud\/(.+)$/, function( req, res ){
	var query = _.extend( req.query, {
		oauth_token: req.user.soundcloud_access_token
	});
	var endpoint = req.params[0];
	request({
		url: SOUNDCLOUD_API_BASE_URL +'/'+ endpoint,
		qs: query,
		method: req.method
	}).pipe( res );
});

server.all( /^\/api\/v1\/proxy\/youtube\/(.+)$/, function( req, res ){
	var query = _.extend( req.query, {
		key: YOUTUBE_API_KEY
	});
	var endpoint = req.params[0];
	request({
		url: YOUTUBE_API_BASE_URL +'/'+ endpoint,
		qs: query,
		json: req.body,
		headers: {
			'Authorization': 'Bearer '+ req.user.youtube_access_token
		},
		method: req.method
	}).pipe( res );
});

server.get( '/logout', function( req, res ){
	req.logout();
	res.redirect('/');
});

server.post( '/rooms', function( req, res ){
	var room = new Room;
	res.redirect( '/rooms/'+ room.id );
});

server.get( '/rooms/:id', function( req, res ){
	var user = req.session.passport.user || {};
	user = _.pick( user, 'id', 'name', 'youtube_id', 'youtube_likes_id', 'soundcloud_id' );
	user.token = generateAuthToken( user.id, user.name );
	var room = Room.findById( req.params.id, true );
	res.expose( room, 'tandem.bridge.room' );
	res.expose( user, 'tandem.bridge.user' );
	res.expose( {
		soundcloud: {
			client_id: SOUNDCLOUD_APP_ID
		}
	}, 'tandem.bridge.apis' );
	res.render( 'room.hbs', {
		room: room,
		user: user
	});
});

var renderIndex = function( req, res ){
	var rooms = Room.list( true );
	res.render( 'index.hbs', {
		rooms: rooms
	});
};

server.get( '/', renderIndex );
server.get( '/rooms/:id', renderIndex );

http_server.listen( PORT );

console.log('[tandem] listening on port', PORT );<|endoftext|>


<commit_before>// View model for the courses page
function pageViewModel(gvm) {
    gvm.userId = -1;
    gvm.studentlistName = ko.observable('Name');
    gvm.listId = -1
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("AccountTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("EditListTitle");}, gvm);

    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    gvm.myLists = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("myLists");}, gvm);
    gvm.addStudListBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("addStudListBtn");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.codeTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CodeTableTitle");}, gvm);
    gvm.nameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("NameTableTitle");}, gvm);
    gvm.descTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("DescTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.firstname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Firstname");}, gvm);
    gvm.lastname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Lastname");}, gvm);
    gvm.email = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Email");}, gvm);
    gvm.memberSince = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MemberSince");}, gvm);
    gvm.myProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MyProjects");}, gvm);

    gvm.tabledata = ko.observableArray([]);

    gvm.addTableData = function(id, firstname, lastname, username) {
        // Push data
        var tblOject = {tid: id, tfirstname: firstname, tlastname: lastname, tusername: username};
        gvm.tabledata.push(tblOject);

        $('#editbtn-' + id).bind('click', function(event, data) {
            showEditStudentModal(tblOject);
            event.stopPropagation();
        });

        $('#removebtn-' + id).bind('click', function(event, data){
            // Delete the table item
            deleteTableItem(id, tblOject);
            event.stopPropagation();
        });
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }

}

function showEditStudentModal(tblObject) {
    resetGeneralModal();
    setGeneralModalTitle("Edit Student");
    setGeneralModalBody('<form id="updateStudent"> \

 \
                <input type="text" class="form-control input-lg" placeholder="' + tblObject.tfirstname + '" " name="firstname" value="' + tblObject.tfirstname + '"> \

 \

 \
                <input type="text" class="form-control input-lg" placeholder="' + tblObject.tlastname + '" name="lastname" value="' + tblObject.tlastname + '"> \

 \

 \
                <input type="text" class="form-control input-lg" placeholder="' + tblObject.tusername + '" name="username" value="' + tblObject.tusername + '"> \

 \
        </form>');
    $.getJSON()

    addGeneralModalButton(i18n.__("SaveBtn"), function(){
        updateStudent(tblObject.tid, $('#updateStudent').serialize(), function(result){
            hideModal();
        });
    });

    addGeneralModalButton(i18n.__("CancelBtn"), function(){
        hideModal();
    })

    showGeneralModal();
}

function showNewStudentModal() {
    resetGeneralModal();
    setGeneralModalTitle("Add Student");
    setGeneralModalBody('<form id="newStudentFrom"> \

 \
                Student:  \
                <input id="fruitlist" /> \

 \
        </form>');
    $.getJSON()

    addGeneralModalButton(i18n.__("AddBtn"), function(){
        addNewStudent($('#newStudentFrom').serialize(), function(result){
            hideModal();
        });
    });

    addGeneralModalButton(i18n.__("CancelBtn"), function(){
        hideModal();
    })

    showGeneralModal();
}

function addNewStudent(serialData, callback) {
    console.log(serialData);
    $.ajax({
        url: "/api/newstudent/" + viewModel.listId,
        type: "POST",
        data: serialData,
        success: function(data) {
            viewModel.addTableData(data['id'], data['firstname'], data['lastname'], data['mail']);
            callback(true);
        },
        error: function(data) {
            callback(false);
        }
    });
}

function updateStudent(id, object, callback) {
    $.ajax({
        url: "/api/studentrapport/" + id,
        type: "PUT",
        data: object,
        success: function(data) {
            loadStudentTable();
            callback(true);
        },
        error: function(data) {
            callback(false);
        }
    });
}

function deleteTableItem(id, tblObject){
    showYesNoModal("Bent u zeker dat u dit item wil verwijderen?", function(val){
        if(val){
            $.ajax({
                url: '/api/studentlistrapport/' + $("#page-header").data('value') + '/delete/student/' + id,
                type: "DELETE",
                success: function() {
                    viewModel.tabledata.remove(tblObject);
                }
            });
        }
    });
}

function loadStudentTable() {
    $.getJSON('/api/studentlistrapporten/students/' + $("#page-header").data('value'), function(data) {
        viewModel.clearTable();
        // Load table data
        $.each(data, function(i, item) {
            viewModel.addTableData(item.id, item.firstname, item.lastname, item.username);
        });
    });
}

function initPage() {
    $.getJSON('/api/studentlistrapport/info/' + $("#page-header").data('value'), function(data) {
        console.log(data[0].id);
        viewModel.studentlistName(data[0].name);
        viewModel.listId = (data[0].id);
    });

    var fruits = [
      "Apple",
      "Pear",
      "Mango",
      "Strawberry",
      "Pineapple"
    ];

    $('#fruitlist').autocomplete({ source: fruits });

    $('#addStudent').click(function(){
        showNewStudentModal();
    });

    loadStudentTable();

}<commit_msg>add student to list<commit_after>// View model for the courses page
function pageViewModel(gvm) {
    gvm.userId = -1;
    gvm.studentlistName = ko.observable('Name');
    gvm.listId = -1
    // Page specific i18n bindings
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("AccountTitle");}, gvm);
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("EditListTitle");}, gvm);

    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    gvm.myLists = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("myLists");}, gvm);
    gvm.addStudListBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("addStudListBtn");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.codeTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("CodeTableTitle");}, gvm);
    gvm.nameTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("NameTableTitle");}, gvm);
    gvm.descTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("DescTableTitle");}, gvm);
    gvm.actionTableTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ActionTableTitle");}, gvm);

    gvm.firstname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Firstname");}, gvm);
    gvm.lastname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Lastname");}, gvm);
    gvm.email = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("Email");}, gvm);
    gvm.memberSince = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MemberSince");}, gvm);
    gvm.myProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("MyProjects");}, gvm);

    gvm.tabledata = ko.observableArray([]);

    gvm.addTableData = function(id, firstname, lastname, username) {
        // Push data
        var tblOject = {tid: id, tfirstname: firstname, tlastname: lastname, tusername: username};
        gvm.tabledata.push(tblOject);

        $('#editbtn-' + id).bind('click', function(event, data) {
            showEditStudentModal(tblOject);
            event.stopPropagation();
        });

        $('#removebtn-' + id).bind('click', function(event, data){
            // Delete the table item
            deleteTableItem(id, tblOject);
            event.stopPropagation();
        });
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }

}

function showEditStudentModal(tblObject) {
    resetGeneralModal();
    setGeneralModalTitle("Edit Student");
    setGeneralModalBody('<form id="updateStudent"> \

 \
                <input type="text" class="form-control input-lg" placeholder="' + tblObject.tfirstname + '" " name="firstname" value="' + tblObject.tfirstname + '"> \

 \

 \
                <input type="text" class="form-control input-lg" placeholder="' + tblObject.tlastname + '" name="lastname" value="' + tblObject.tlastname + '"> \

 \

 \
                <input type="text" class="form-control input-lg" placeholder="' + tblObject.tusername + '" name="username" value="' + tblObject.tusername + '"> \

 \
        </form>');
    $.getJSON()

    addGeneralModalButton(i18n.__("SaveBtn"), function(){
        updateStudent(tblObject.tid, $('#updateStudent').serialize(), function(result){
            hideModal();
        });
    });

    addGeneralModalButton(i18n.__("CancelBtn"), function(){
        hideModal();
    })

    showGeneralModal();
}

function showNewStudentModal() {
    resetGeneralModal();
    setGeneralModalTitle("Add Student");
    setGeneralModalBody('<form id="newStudentFrom"> \

 \
                Student:  \
                <input id="fruitlist" /> \

 \
        </form>');
    $.getJSON()

    addGeneralModalButton(i18n.__("AddBtn"), function(){
        addNewStudent($('#newStudentFrom').serialize(), function(result){
            hideModal();
        });
    });

    addGeneralModalButton(i18n.__("CancelBtn"), function(){
        hideModal();
    })

    showGeneralModal();
}

function addNewStudent(serialData, callback) {
    console.log(serialData);
    $.ajax({
        url: "/api/newstudent/" + viewModel.listId,
        type: "POST",
        data: serialData,
        success: function(data) {
            viewModel.addTableData(data['id'], data['firstname'], data['lastname'], data['mail']);
            callback(true);
        },
        error: function(data) {
            callback(false);
        }
    });
}

function updateStudent(id, object, callback) {
    $.ajax({
        url: "/api/studentrapport/" + id,
        type: "PUT",
        data: object,
        success: function(data) {
            loadStudentTable();
            callback(true);
        },
        error: function(data) {
            callback(false);
        }
    });
}

function deleteTableItem(id, tblObject){
    showYesNoModal("Bent u zeker dat u dit item wil verwijderen?", function(val){
        if(val){
            $.ajax({
                url: '/api/studentlistrapport/' + $("#page-header").data('value') + '/delete/student/' + id,
                type: "DELETE",
                success: function() {
                    viewModel.tabledata.remove(tblObject);
                }
            });
        }
    });
}

function loadStudentTable() {
    $.getJSON('/api/studentlistrapporten/students/' + $("#page-header").data('value'), function(data) {
        viewModel.clearTable();
        // Load table data
        $.each(data, function(i, item) {
            viewModel.addTableData(item.id, item.firstname, item.lastname, item.username);
        });
    });
}

function initPage() {
    $.getJSON('/api/studentlistrapport/info/' + $("#page-header").data('value'), function(data) {
        console.log(data[0].id);
        viewModel.studentlistName(data[0].name);
        viewModel.listId = (data[0].id);
    });

    $('#fruitlist').autocomplete({ source: ["Apple", "Pear", "Mango", "Strawberry", "Pineapple"] });

    $('#addStudent').click(function(){
        showNewStudentModal();
    });

    loadStudentTable();

}<|endoftext|>


<commit_before>const React = require('react');
const { Button, Col, Container, Row, Spinner, Table } = require('elemental');
const DemoBox = require('../components/DemoBox');
const ExampleSource = require('../components/ExampleSource');

var Buttons = React.createClass({
	displayName: 'VIEW_Spinner',
	render () {
		return (
			<Container maxWidth={800} className="demo-container">

Spinner

Common use cases

Page Element

						<Row>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" />
								</DemoBox>
							</Col>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" type="primary" />
								</DemoBox>
							</Col>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }} inverted >
									<Spinner size="md" type="inverted" />
								</DemoBox>
							</Col>
						</Row>

					<ExampleSource>
						{`
						<Spinner size="md" />
						<Spinner size="md" type="primary" />
						<Spinner size="md" type="inverted" />
						`}
					</ExampleSource>

Inside Buttons

							<Button disabled>
								<Spinner />
							</Button>

							<Button disabled>
								<Spinner type="primary" />
								Saving
							</Button>

							<Button type="primary" disabled>
								<Spinner type="inverted" />
								Submitting
							</Button>

					<ExampleSource>
						{`
						<Button disabled><Spinner /></Button>
						<Button disabled><Spinner type="primary" />Saving</Button>
						<Button type="primary" disabled><Spinner type="inverted" />Submitting</Button>
						`}
					</ExampleSource>

Full Page Load

							<Spinner size="lg" />

					<ExampleSource>
						{`
						<Spinner size="lg" />
						`}
					</ExampleSource>

Usage

								Prop
								Type
								Default
								Description

								size
								enum
								sm

Declare the size of the dots in the spinner. Possible options:

									sm md lg

									Spinners automatically become small when inside of buttons

								type
								enum
								default

									Declare the colour of the dots in the spinner. Possible options:

									default primary inverted

			</Container>
		);
	}
});

module.exports = Buttons;
<commit_msg>Updating spinner demo to include enabled buttons<commit_after>const React = require('react');
const { Button, Col, Container, Row, Spinner, Table } = require('elemental');
const DemoBox = require('../components/DemoBox');
const ExampleSource = require('../components/ExampleSource');

var Buttons = React.createClass({
	displayName: 'VIEW_Spinner',
	render () {
		return (
			<Container maxWidth={800} className="demo-container">

Spinner

Common use cases

Page Element

						<Row>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" />
								</DemoBox>
							</Col>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" type="primary" />
								</DemoBox>
							</Col>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }} inverted >
									<Spinner size="md" type="inverted" />
								</DemoBox>
							</Col>
						</Row>

					<ExampleSource>
						{`
						<Spinner size="md" />
						<Spinner size="md" type="primary" />
						<Spinner size="md" type="inverted" />
						`}
					</ExampleSource>

Inside Buttons

							<Button>
								<Spinner />
							</Button>

							<Button disabled>
								<Spinner type="primary" />
								Saving
							</Button>

							<Button type="primary">
								<Spinner type="inverted" />
								Submitting
							</Button>

					<ExampleSource>
						{`
						<Button><Spinner /></Button>
						<Button disabled><Spinner type="primary" />Saving</Button>
						<Button type="primary"><Spinner type="inverted" />Submitting</Button>
						`}
					</ExampleSource>

Full Page Load

							<Spinner size="lg" />

					<ExampleSource>
						{`
						<Spinner size="lg" />
						`}
					</ExampleSource>

Usage

								Prop
								Type
								Default
								Description

								size
								enum
								sm

Declare the size of the dots in the spinner. Possible options:

									sm md lg

									Spinners automatically become small when inside of buttons

								type
								enum
								default

									Declare the colour of the dots in the spinner. Possible options:

									default primary inverted

			</Container>
		);
	}
});

module.exports = Buttons;
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose');
var GamesController = require('./app/controllers/games.js');
var io = require('socket.io').listen(3000, '0.0.0.0');
var State = require('./app/models/state');

mongoose.connect('mongodb://localhost/hackathon-war-dev');

var waiting = {};
var games = [];
var playing = {};
var index = 0;

io.set('log level', 1);
io.on('connection', function(socket){
  socket.emit('games', Object.keys(waiting));

  function update() {
    socket.broadcast.emit('games', Object.keys(waiting));
  }

  socket.on('new-game', function (player) {
    if (player) {
      waiting[player] = socket;
      socket.player = player;
      update();
    }
  });

  socket.on('disconnect', function () {
    if (socket.player) {
      if (waiting[socket.player]) {
        delete waiting[socket.player];
      } else if (playing[socket.player]) {
        var gameId = playing[socket.player].gameId;
        games[gameId].players.filter(function (p) {
          return p !== socket.player;
        }).forEach(function (player) {
          playing[player.username].emit('win-wo');
        });

        games[gameId].players.forEach(function (p) {
          delete playing[p.username];
        });
        delete games[gameId];
      }
      update();
    }
  });

  socket.on('join-game', function (players) {
    State.allRandom(function (states) {
      players = players.map(function (username) {
        var player = {
          username: username,
          states: states.splice(0, 13),
        };

        playing[username] = waiting[username] || socket;
        playing[username].player = username;
        delete waiting[username];

        return player;
      });

      var game = {
        id: games.length,
        players: players
      };

      games.push(game);

      game.players.forEach(function (player) {
        playing[player.username].gameId = game.id;
        playing[player.username].emit('created-game', game);
      });

      next(game.id);
    });
  });

  socket.on('add-marker', function (marker) {
    var game = games[marker.gameId];
    console.log(game, marker.gameId, games);
    game.players.forEach(function (player) {
      playing[player.username].emit('add-marker', marker);
    });
  });

  function next(gameId) {
    var player = games[gameId].players[index++ % 2];
    console.log(player.username);
    playing[player.username].emit('play');
  }
  socket.on('next', next);
});
<commit_msg>Remove console logs from server<commit_after>'use strict';

var mongoose = require('mongoose');
var GamesController = require('./app/controllers/games.js');
var io = require('socket.io').listen(3000, '0.0.0.0');
var State = require('./app/models/state');

mongoose.connect('mongodb://localhost/hackathon-war-dev');

var waiting = {};
var games = [];
var playing = {};
var index = 0;

io.set('log level', 1);
io.on('connection', function(socket){
  socket.emit('games', Object.keys(waiting));

  function update() {
    socket.broadcast.emit('games', Object.keys(waiting));
  }

  socket.on('new-game', function (player) {
    if (player) {
      waiting[player] = socket;
      socket.player = player;
      update();
    }
  });

  socket.on('disconnect', function () {
    if (socket.player) {
      if (waiting[socket.player]) {
        delete waiting[socket.player];
      } else if (playing[socket.player]) {
        var gameId = playing[socket.player].gameId;
        games[gameId].players.filter(function (p) {
          return p !== socket.player;
        }).forEach(function (player) {
          playing[player.username].emit('win-wo');
        });

        games[gameId].players.forEach(function (p) {
          delete playing[p.username];
        });
        delete games[gameId];
      }
      update();
    }
  });

  socket.on('join-game', function (players) {
    State.allRandom(function (states) {
      players = players.map(function (username) {
        var player = {
          username: username,
          states: states.splice(0, 13),
        };

        playing[username] = waiting[username] || socket;
        playing[username].player = username;
        delete waiting[username];

        return player;
      });

      var game = {
        id: games.length,
        players: players
      };

      games.push(game);

      game.players.forEach(function (player) {
        playing[player.username].gameId = game.id;
        playing[player.username].emit('created-game', game);
      });

      next(game.id);
    });
  });

  socket.on('add-marker', function (marker) {
    var game = games[marker.gameId];
    game.players.forEach(function (player) {
      playing[player.username].emit('add-marker', marker);
    });
  });

  function next(gameId) {
    var player = games[gameId].players[index++ % 2];
    playing[player.username].emit('play');
  }
  socket.on('next', next);
});
<|endoftext|>


<commit_before>module.exports = stringify
stringify.default = stringify
stringify.stable = deterministicStringify
stringify.stableStringify = deterministicStringify

const arr = []

// Regular stringify
function stringify (obj, replacer, spacer) {
  decirc(obj, '', [], null)
  const res = JSON.stringify(obj, replacer, spacer)
  while (arr.length !== 0) {
    const part = arr.pop()
    part[0][part[1]] = part[2]
  }
  return res
}
function decirc (val, k, stack, parent) {
  var i
  if (typeof val === 'object' && val !== null) {
    for (i = 0; i < stack.length; i++) {
      if (stack[i] === val) {
        parent[k] = '[Circular]'
        arr.push([parent, k, val])
        return
      }
    }
    stack.push(val)
    // Optimize for Arrays. Big arrays could kill the performance otherwise!
    if (Array.isArray(val)) {
      for (i = 0; i < val.length; i++) {
        decirc(val[i], i, stack, val)
      }
    } else {
      const keys = Object.keys(val)
      for (i = 0; i < keys.length; i++) {
        const key = keys[i]
        decirc(val[key], key, stack, val)
      }
    }
    stack.pop()
  }
}

// Stable-stringify
function compareFunction (a, b) {
  if (a < b) {
    return -1
  }
  if (a > b) {
    return 1
  }
  return 0
}

function deterministicStringify (obj, replacer, spacer) {
  const tmp = deterministicDecirc(obj, '', [], null) || obj
  const res = JSON.stringify(tmp, replacer, spacer)
  while (arr.length !== 0) {
    const part = arr.pop()
    part[0][part[1]] = part[2]
  }
  return res
}

function deterministicDecirc (val, k, stack, parent) {
  var i
  if (typeof val === 'object' && val !== null) {
    for (i = 0; i < stack.length; i++) {
      if (stack[i] === val) {
        parent[k] = '[Circular]'
        arr.push([parent, k, val])
        return
      }
    }
    if (typeof val.toJSON === 'function') {
      return
    }
    stack.push(val)
    // Optimize for Arrays. Big arrays could kill the performance otherwise!
    if (Array.isArray(val)) {
      for (i = 0; i < val.length; i++) {
        deterministicDecirc(val[i], i, stack, val)
      }
    } else {
      // Create a temporary object in the required way
      const tmp = {}
      const keys = Object.keys(val).sort(compareFunction)
      for (i = 0; i < keys.length; i++) {
        const key = keys[i]
        deterministicDecirc(val[key], key, stack, val)
        tmp[key] = val[key]
      }
      if (parent) {
        arr.push([parent, k, val])
        parent[k] = tmp
      } else {
        return tmp
      }
    }
    stack.pop()
  }
}
<commit_msg>performance: be specific about the expected parent<commit_after>module.exports = stringify
stringify.default = stringify
stringify.stable = deterministicStringify
stringify.stableStringify = deterministicStringify

const arr = []

// Regular stringify
function stringify (obj, replacer, spacer) {
  decirc(obj, '', [])
  const res = JSON.stringify(obj, replacer, spacer)
  while (arr.length !== 0) {
    const part = arr.pop()
    part[0][part[1]] = part[2]
  }
  return res
}
function decirc (val, k, stack, parent) {
  var i
  if (typeof val === 'object' && val !== null) {
    for (i = 0; i < stack.length; i++) {
      if (stack[i] === val) {
        parent[k] = '[Circular]'
        arr.push([parent, k, val])
        return
      }
    }
    stack.push(val)
    // Optimize for Arrays. Big arrays could kill the performance otherwise!
    if (Array.isArray(val)) {
      for (i = 0; i < val.length; i++) {
        decirc(val[i], i, stack, val)
      }
    } else {
      const keys = Object.keys(val)
      for (i = 0; i < keys.length; i++) {
        const key = keys[i]
        decirc(val[key], key, stack, val)
      }
    }
    stack.pop()
  }
}

// Stable-stringify
function compareFunction (a, b) {
  if (a < b) {
    return -1
  }
  if (a > b) {
    return 1
  }
  return 0
}

function deterministicStringify (obj, replacer, spacer) {
  const tmp = deterministicDecirc(obj, '', []) || obj
  const res = JSON.stringify(tmp, replacer, spacer)
  while (arr.length !== 0) {
    const part = arr.pop()
    part[0][part[1]] = part[2]
  }
  return res
}

function deterministicDecirc (val, k, stack, parent) {
  var i
  if (typeof val === 'object' && val !== null) {
    for (i = 0; i < stack.length; i++) {
      if (stack[i] === val) {
        parent[k] = '[Circular]'
        arr.push([parent, k, val])
        return
      }
    }
    if (typeof val.toJSON === 'function') {
      return
    }
    stack.push(val)
    // Optimize for Arrays. Big arrays could kill the performance otherwise!
    if (Array.isArray(val)) {
      for (i = 0; i < val.length; i++) {
        deterministicDecirc(val[i], i, stack, val)
      }
    } else {
      // Create a temporary object in the required way
      const tmp = {}
      const keys = Object.keys(val).sort(compareFunction)
      for (i = 0; i < keys.length; i++) {
        const key = keys[i]
        deterministicDecirc(val[key], key, stack, val)
        tmp[key] = val[key]
      }
      if (parent !== undefined) {
        arr.push([parent, k, val])
        parent[k] = tmp
      } else {
        return tmp
      }
    }
    stack.pop()
  }
}
<|endoftext|>


<commit_before>'use strict';
let visibility;
let hideRegExp;
let toggleOn = true;

const injector = window.gitHubInjection;

function createHtml(str) {
	const frag = document.createDocumentFragment();
	const temp = document.createElement('div');

	temp.innerHTML = str;

	while (temp.firstChild) {
		frag.appendChild(temp.firstChild);
	}

	return frag;
}

function toggleFiles() {
	const rows = Array.from(document.querySelectorAll('.files tr'));
	let i = 0;

	for (const el of rows) {
		if (el.querySelector('.content a')) {
			const fileName = el.querySelector('td.content a').innerText;

			if (hideRegExp && hideRegExp.test(fileName)) {
				if (visibility === 'hidden') {
					el.style.display = toggleOn ? 'none' : 'table-row';
				} else if (visibility === 'dimmed') {
					if (toggleOn) {
						el.classList.add('dimmed');
					} else {
						el.classList.remove('dimmed');
					}
				}
			}
		} else if (++i === 1) {
			// remove top border
			el.classList.add('first');
		}
	}
}

function addToggleBtn() {
	const toggleBtn = createHtml(`<a class="hide-files-btn btn btn-sm">${label()}</a>`);
	const btnContainer = document.querySelector('.file-navigation .right .btn-group');

	fixupOtherButtons();

	if (document.querySelector('.hide-files-btn')) {
		addToggleBtnEvents();
		return;
	}

	if (btnContainer) {
		// insert after
		btnContainer.insertBefore(toggleBtn, btnContainer.children[0]);
		addToggleBtnEvents();
	}
}

function fixupOtherButtons() {
	const sidebar = document.querySelector('.file-navigation .right');
	if (!sidebar) {
		return;
	}

	const buttons = Array.from(sidebar.getElementsByClassName('btn btn-sm'));
	for (const button of buttons) {
		const text = button.textContent.trim();
		if (text === 'Download ZIP') {
			button.innerHTML = '⇣ ZIP';
		}
	}
}

function addToggleBtnEvents() {
	const btn = document.querySelector('.hide-files-btn');

	if (btn) {
		btn.addEventListener('click', () => {
			toggleOn = !toggleOn;
			btn.textContent = label();
			toggleFiles();
		});
	}
}

function label() {
	return toggleOn ? 'Show dotfiles' : 'Hide dotfiles';
}

function trigger() {
	addToggleBtn();
	toggleFiles();
}

document.addEventListener('DOMContentLoaded', () => {
	trigger();

	const container = document.querySelector('#js-repo-pjax-container');

	if (!container) {
		return;
	}

	new MutationObserver(trigger).observe(container, {childList: true});

	window.HideFilesOnGitHub.storage.get((err, items) => {
		if (err) {
			throw err;
		}

		visibility = items.visibility;
		hideRegExp = items.hideRegExp === '' ? undefined : new RegExp(items.hideRegExp, 'i');

		injector(window, err => {
			if (err) {
				console.error(err);
				return;
			}

			addToggleBtnEvents();
			trigger();
		});
	});
});
<commit_msg>fix selector (#30)<commit_after>'use strict';
let visibility;
let hideRegExp;
let toggleOn = true;

const injector = window.gitHubInjection;

function createHtml(str) {
	const frag = document.createDocumentFragment();
	const temp = document.createElement('div');

	temp.innerHTML = str;

	while (temp.firstChild) {
		frag.appendChild(temp.firstChild);
	}

	return frag;
}

function toggleFiles() {
	const rows = Array.from(document.querySelectorAll('.files tr'));
	let i = 0;

	for (const el of rows) {
		if (el.querySelector('.content a')) {
			const fileName = el.querySelector('td.content a').innerText;

			if (hideRegExp && hideRegExp.test(fileName)) {
				if (visibility === 'hidden') {
					el.style.display = toggleOn ? 'none' : 'table-row';
				} else if (visibility === 'dimmed') {
					if (toggleOn) {
						el.classList.add('dimmed');
					} else {
						el.classList.remove('dimmed');
					}
				}
			}
		} else if (++i === 1) {
			// remove top border
			el.classList.add('first');
		}
	}
}

function addToggleBtn() {
	const toggleBtn = createHtml(`<a class="hide-files-btn btn btn-sm">${label()}</a>`);
	const btnContainer = document.querySelector('.file-navigation .btn-group.right');

	fixupOtherButtons();

	if (document.querySelector('.hide-files-btn')) {
		addToggleBtnEvents();
		return;
	}

	if (btnContainer) {
		// insert after
		btnContainer.insertBefore(toggleBtn, btnContainer.children[0]);
		addToggleBtnEvents();
	}
}

function fixupOtherButtons() {
	const sidebar = document.querySelector('.file-navigation .right');
	if (!sidebar) {
		return;
	}

	const buttons = Array.from(sidebar.getElementsByClassName('btn btn-sm'));
	for (const button of buttons) {
		const text = button.textContent.trim();
		if (text === 'Download ZIP') {
			button.innerHTML = '⇣ ZIP';
		}
	}
}

function addToggleBtnEvents() {
	const btn = document.querySelector('.hide-files-btn');

	if (btn) {
		btn.addEventListener('click', () => {
			toggleOn = !toggleOn;
			btn.textContent = label();
			toggleFiles();
		});
	}
}

function label() {
	return toggleOn ? 'Show dotfiles' : 'Hide dotfiles';
}

function trigger() {
	addToggleBtn();
	toggleFiles();
}

document.addEventListener('DOMContentLoaded', () => {
	trigger();

	const container = document.querySelector('#js-repo-pjax-container');

	if (!container) {
		return;
	}

	new MutationObserver(trigger).observe(container, {childList: true});

	window.HideFilesOnGitHub.storage.get((err, items) => {
		if (err) {
			throw err;
		}

		visibility = items.visibility;
		hideRegExp = items.hideRegExp === '' ? undefined : new RegExp(items.hideRegExp, 'i');

		injector(window, err => {
			if (err) {
				console.error(err);
				return;
			}

			addToggleBtnEvents();
			trigger();
		});
	});
});
<|endoftext|>


<commit_before>var request = require('request');
var url = require('url');
var qs = require('querystring');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var WebSocket = require('ws');

function EurekaDongle(base) {
  this.url = base;
  this.urlParts = url.parse(base);
  EventEmitter.call(this);
}

util.inherits(EurekaDongle, EventEmitter);

EurekaDongle.prototype.timer = null;
EurekaDongle.prototype.currentApp = null;

var appList = null;
EurekaDongle.prototype.collectAppList = function(fn) {
  request('https://clients3.google.com/cast/chromecast/device/config', function(e, r, b) {
    var obj = JSON.parse(b.replace(')]}', ''));
    appList = {};
    obj.applications.forEach(function(app) {
      appList[app.app_name.toLowerCase()] = app;
    });

    fn();
  });
};

EurekaDongle.prototype.statusInterval = 500;

EurekaDongle.prototype.start = function(name, resource, callback) {
  var that = this;

  if (!name) {
    return request(this.url + '/apps', {followRedirect: false}, function(e, r, b) {/* jshint unused: false */
      if (r && r.headers.location) {
        var name = r.headers.location.split('/').pop();
        that.start(name, resource, callback);
      }
    });
  }

  var req = {
    headers: {
      'Connection' : 'close',
    },
    method : 'POST',
  };

  this.currentApp = name;

  if (name.toLowerCase() === 'youtube') {
    if (resource && resource.indexOf('v=')) {
      var urlParts = url.parse(resource);

      if (urlParts.query) {
        var queryParts = qs.parse(urlParts.query);
        if (queryParts.v) {
          resource = queryParts.v;
        } else {
          return;
        }
      } else {
        return;
      }

      resource = 'v=' + resource + '&pairingCode=eac4ae42-8b54-4441-9be3-1111111111';
    } else {
      resource = '';
    }

    req.headers['Content-Type'] = 'application/x-www-form-urlencoded';

    req.headers['Content-Length'] = resource.length;
    req.body = resource;

    var payload = JSON.stringify({
      channel: 0,
      senderId : {
        appName : 'ChromeCast',
        senderId: 'myid' + Math.random()
      }
    });

    that = this;

    this.emit('launching');

    (function waitForDevice() {
      request.post(that.url + '/connection/YouTube', {
        headers: {
          'Content-Type' : 'application/json',
          'Content-Length' : payload.length,
          'Host' : that.urlParts.host,
          'Origin' : 'chrome-extension://boadgeojelhgndaghljhdicfkmllpafd'
        },
        body : payload,
      }, function(e, r, b) {

        clearTimeout(that.timer);

        if (r.statusCode === 200) {
          that.timer = setTimeout(function() {
            that.prepareRampSocket(b);
          }, 2000);
        } else {
          that.timer = setTimeout(waitForDevice, 500);
        }
      });
    })();
  }

  resource && request(this.url + '/apps/' + name, req, callback);
};

EurekaDongle.prototype.stop = function(name, callback) {
  var appsUrl = this.url + '/apps/' + name;
  var that = this;

  if (!arguments.length) {
    name = this.currentApp;
  }

  request.del(appsUrl, function() {
    // We really need to ensure it's gone or a restart will cause
    // 408s
    that.timer = setTimeout(function() {
      that.emit('stopped');
      callback && callback();
    }, 2000);
  });

  this.timer && clearTimeout(this.timer);
  this.ws && this.ws.close();
  this.currentApp = null;
};

EurekaDongle.prototype.pause = function() {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { cmd_id: 3, type: 'STOP' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.resume = function(position) {
  try {
    this.ws.send(JSON.stringify([
      'ramp', ((!!position) ? { position: position, cmd_id: 5, type: 'PLAY' } : { cmd_id: 4, type: 'PLAY' })
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.volume = function(volume) {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { volume: volume, cmd_id: 6, type: 'VOLUME' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.muted = function(muted) {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { muted: muted, cmd_id: 7, type: 'VOLUME' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.prepareRampSocket = function(b) {
  var wsUrl = JSON.parse(b.toString()).URL;
  var that = this;

  var ws = this.ws = new WebSocket(wsUrl, {
    origin: 'chrome-extension://boadgeojelhgndaghljhdicfkmllpafd'
  });

  var first = true;
  ws.on('message', function(data, flags) {/* jshint unused: false */
    data = JSON.parse(data);

    if (first) {
      var status = data[1].status;

      try {
        ws.send(JSON.stringify([
          "cv", {
            "type":"activity",
            "message": {
              "type":"update_description",
              "activityId":status.content_id,
              "description":status.title,
              "iconUrl":status.image_url
            }
          }
        ]), { mask: true });
      } catch (e) {
        that.emit('error', e);
      }

      that.getStatus();

      first = false;
    } else {
      clearTimeout(that.timer);

      if (data[1] && data[1].cmd_id && data[1].cmd_id === 1000) {
        that.emit('status', data);
      }

      that.timer = setTimeout(function() {
        that.getStatus();
      }, that.statusInterval);
    }
  }).on('error', function(err) {
    that.emit('error', e);
  });
};

EurekaDongle.prototype.getStatus = function() {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { cmd_id: 1000, type: 'INFO' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

module.exports = EurekaDongle;
<commit_msg>catch the right error<commit_after>var request = require('request');
var url = require('url');
var qs = require('querystring');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var WebSocket = require('ws');

function EurekaDongle(base) {
  this.url = base;
  this.urlParts = url.parse(base);
  EventEmitter.call(this);
}

util.inherits(EurekaDongle, EventEmitter);

EurekaDongle.prototype.timer = null;
EurekaDongle.prototype.currentApp = null;

var appList = null;
EurekaDongle.prototype.collectAppList = function(fn) {
  request('https://clients3.google.com/cast/chromecast/device/config', function(e, r, b) {
    var obj = JSON.parse(b.replace(')]}', ''));
    appList = {};
    obj.applications.forEach(function(app) {
      appList[app.app_name.toLowerCase()] = app;
    });

    fn();
  });
};

EurekaDongle.prototype.statusInterval = 500;

EurekaDongle.prototype.start = function(name, resource, callback) {
  var that = this;

  if (!name) {
    return request(this.url + '/apps', {followRedirect: false}, function(e, r, b) {/* jshint unused: false */
      if (r && r.headers.location) {
        var name = r.headers.location.split('/').pop();
        that.start(name, resource, callback);
      }
    });
  }

  var req = {
    headers: {
      'Connection' : 'close',
    },
    method : 'POST',
  };

  this.currentApp = name;

  if (name.toLowerCase() === 'youtube') {
    if (resource && resource.indexOf('v=')) {
      var urlParts = url.parse(resource);

      if (urlParts.query) {
        var queryParts = qs.parse(urlParts.query);
        if (queryParts.v) {
          resource = queryParts.v;
        } else {
          return;
        }
      } else {
        return;
      }

      resource = 'v=' + resource + '&pairingCode=eac4ae42-8b54-4441-9be3-1111111111';
    } else {
      resource = '';
    }

    req.headers['Content-Type'] = 'application/x-www-form-urlencoded';

    req.headers['Content-Length'] = resource.length;
    req.body = resource;

    var payload = JSON.stringify({
      channel: 0,
      senderId : {
        appName : 'ChromeCast',
        senderId: 'myid' + Math.random()
      }
    });

    that = this;

    this.emit('launching');

    (function waitForDevice() {
      request.post(that.url + '/connection/YouTube', {
        headers: {
          'Content-Type' : 'application/json',
          'Content-Length' : payload.length,
          'Host' : that.urlParts.host,
          'Origin' : 'chrome-extension://boadgeojelhgndaghljhdicfkmllpafd'
        },
        body : payload,
      }, function(e, r, b) {

        clearTimeout(that.timer);

        if (r.statusCode === 200) {
          that.timer = setTimeout(function() {
            that.prepareRampSocket(b);
          }, 2000);
        } else {
          that.timer = setTimeout(waitForDevice, 500);
        }
      });
    })();
  }

  resource && request(this.url + '/apps/' + name, req, callback);
};

EurekaDongle.prototype.stop = function(name, callback) {
  var appsUrl = this.url + '/apps/' + name;
  var that = this;

  if (!arguments.length) {
    name = this.currentApp;
  }

  request.del(appsUrl, function() {
    // We really need to ensure it's gone or a restart will cause
    // 408s
    that.timer = setTimeout(function() {
      that.emit('stopped');
      callback && callback();
    }, 2000);
  });

  this.timer && clearTimeout(this.timer);
  this.ws && this.ws.close();
  this.currentApp = null;
};

EurekaDongle.prototype.pause = function() {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { cmd_id: 3, type: 'STOP' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.resume = function(position) {
  try {
    this.ws.send(JSON.stringify([
      'ramp', ((!!position) ? { position: position, cmd_id: 5, type: 'PLAY' } : { cmd_id: 4, type: 'PLAY' })
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.volume = function(volume) {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { volume: volume, cmd_id: 6, type: 'VOLUME' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.muted = function(muted) {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { muted: muted, cmd_id: 7, type: 'VOLUME' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

EurekaDongle.prototype.prepareRampSocket = function(b) {
  var wsUrl = JSON.parse(b.toString()).URL;
  var that = this;

  var ws = this.ws = new WebSocket(wsUrl, {
    origin: 'chrome-extension://boadgeojelhgndaghljhdicfkmllpafd'
  });

  var first = true;
  ws.on('message', function(data, flags) {/* jshint unused: false */
    data = JSON.parse(data);

    if (first) {
      var status = data[1].status;

      try {
        ws.send(JSON.stringify([
          "cv", {
            "type":"activity",
            "message": {
              "type":"update_description",
              "activityId":status.content_id,
              "description":status.title,
              "iconUrl":status.image_url
            }
          }
        ]), { mask: true });
      } catch (e) {
        that.emit('error', e);
      }

      that.getStatus();

      first = false;
    } else {
      clearTimeout(that.timer);

      if (data[1] && data[1].cmd_id && data[1].cmd_id === 1000) {
        that.emit('status', data);
      }

      that.timer = setTimeout(function() {
        that.getStatus();
      }, that.statusInterval);
    }
  }).on('error', function(err) {
    that.emit('error', err);
  });
};

EurekaDongle.prototype.getStatus = function() {
  try {
    this.ws.send(JSON.stringify([
      'ramp', { cmd_id: 1000, type: 'INFO' }
    ]), { mask: true });
  } catch (e) {
    this.emit('error', e);
  }
};

module.exports = EurekaDongle;
<|endoftext|>


<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('changes-utils', function(Y) {

  var ChangesUtils = {};

  var removeBrackets = /^\(?(.{0,}?)\)?$/;

  /**
    Return the counts for each type of ecs change.

    @method getChangeCounts
    @param {Object} services The list of services from the db.
    @returns {Object} The ecs change counts.
  */
  ChangesUtils.getChangeCounts = function(changeSet) {
    var counts = {};
    var total = 0;
    Object.keys(changeSet).forEach(function(key) {
      var change = changeSet[key];
      var method = change.command && change.command.method;
      if (method) {
        if (counts[method]) {
          counts[method] = counts[method] += 1;
        } else {
          counts[method] = 1;
        }
        total += 1;
      }
    }, this);
    counts.total = total;
    return counts;
  };

  /**
    Return the counts for each type of ecs change.

    @method getChangeCounts
    @param {Object} services The list of services from the db.
    @returns {Object} The ecs change counts.
  */
  ChangesUtils.getGroupedChanges = function(changeSet) {
    var changes = {};
    Object.keys(changeSet).forEach((key) => {
      var change = changeSet[key];
      var method = change.command && change.command.method;
      if (method) {
        if (!changes[method]) {
          changes[method] = {};
        }
        changes[method][key] = change;
      }
    }, this);
    return changes;
  };

  /**
    Return a list of all change descriptions.

    @method generateAllChangeDescriptions
    @param {Object} services The list of services from the db.
    @param {Object} units The list of units from the db.
    @param {Object} changeSet The current environment change set.
  */
  ChangesUtils.generateAllChangeDescriptions = function(changeSet, services,
                                                        units) {
    var changes = [],
        change;
    Object.keys(changeSet).forEach(function(key) {
      change = this.generateChangeDescription(
          services, units, changeSet[key]);
      if (change) {
        changes.push(change);
      }
    }, this);
    return changes;
  };

  /**
    Return a description of an ecs change for the summary.

    @method generateChangeDescription
    @param {Object} services The list of services from the db.
    @param {Object} units The list of units from the db.
    @param {Object} change The environment change.
    @param {Bool} skipTime optional, used for testing, don't generate time.
  */
  ChangesUtils.generateChangeDescription = function(services, units, change,
                                                    skipTime) {
    var changeItem = {};

    if (change && change.command) {
      changeItem.id = change.id;
      // XXX: The add_unit is just the same as the service because adding
      // the service also adds the unit. We need to look at the UX for
      // units as follow up.
      switch (change.command.method) {
        case '_addCharm':
          var command = change.command;
          changeItem.description = ' ' + command.args[0] +
            ' charm has been added.';
          // TODO frankban: retrieve the icon from the charm itself. We cannot
          // always pass applicationId as an option, and maybe we should never
          // do that, and just get what we need from the charm.
          changeItem.icon = 'charm-added';
          var appId = command.options && command.options.applicationId;
          if (appId) {
            changeItem.icon = services.getById(appId).get('icon');
          }
          break;
        case '_deploy':
          if (!change.command.options || !change.command.options.modelId) {
            // When using the deploy-target query parameter we want to auto
            // deploy so we can skip generating the line item in the deployer
            // bar.
            return;
          }
          var ghostService = services.getById(
              change.command.options.modelId);
          changeItem.icon = ghostService.get('icon');
          changeItem.description = ' ' + ghostService.get('name') +
              ' has been added.';
          break;
        case '_destroyApplication':
          changeItem.icon = 'changes-service-destroyed';
          changeItem.description = ' ' + change.command.args[0] +
              ' has been destroyed.';
          break;
        case '_add_unit':
          var service = this.getServiceByUnitId(
              change.command.options.modelId, services, units);
          changeItem.icon = 'changes-units-added';
          var units = change.command.args[1],
              msg;
          if (units !== 1) {
            msg = 'units have been added.';
          } else {
            msg = 'unit has been added.';
          }
          changeItem.description = ' ' + units + ' ' +
              service.get('name') + ' ' + msg;
          break;
        case '_remove_units':
          changeItem.icon = 'changes-units-removed';
          /*jslint -W004*/
          var units = change.command.args[0];
          changeItem.description = units.length + ' unit' +
              (units.length === 1 ? ' has' : 's have') +
              ' been removed from ' + units[0].split('/')[0];
          break;
        case '_expose':
          var name = change.command.args[0];
          if (name.indexOf('$') > -1) {
            var ghostService = services.getById(name);
            name = ghostService.get('name');
          }
          changeItem.icon = 'exposed_16';
          changeItem.description = name + ' exposed';
          break;
        case '_unexpose':
          changeItem.icon = 'exposed_16';
          changeItem.description = change.command.args[0] + ' unexposed';
          break;
        case '_add_relation':
          var serviceList = this.getRealRelationEndpointNames(
              change.command.args, services);
          changeItem.icon = 'changes-relation-added';
          changeItem.description = change.command.args[0][1].name +
              ' relation added between ' +
              serviceList[0] + ' and ' + serviceList[1] + '.';
          break;
        case '_remove_relation':
          changeItem.icon = 'changes-relation-removed';
          changeItem.description = change.command.args[0][1].name +
              ' relation removed between ' +
              change.command.args[0][0] +
              ' and ' +
              change.command.args[1][0] + '.';
          break;
        case '_addMachines':
          var machineType = change.command.args[0][0].parentId ?
              'container' : 'machine';
          changeItem.icon = 'changes-' + machineType + '-created';
          changeItem.description = change.command.args[0].length +
              ' ' + machineType +
              (change.command.args[0].length !== 1 ? 's have' : ' has') +
              ' been added.';
          break;
        case '_destroyMachines':
          var machineType = change.command.args[0][0].indexOf('/') !== -1 ?
              'container' : 'machine';
          changeItem.icon = 'changes-' + machineType + '-destroyed';
          changeItem.description = change.command.args[0].length +
              ' ' + machineType +
              (change.command.args[0].length !== 1 ? 's have' : ' has') +
              ' been destroyed.';
          break;
        case '_set_config':
          var cfgServ = services.getById(change.command.args[0]);
          changeItem.icon = 'changes-config-changed';
          changeItem.description = 'Configuration values changed for ' +
              cfgServ.get('displayName').match(removeBrackets)[1] + '.';
          break;
        default:
          changeItem.icon = 'changes-unknown';
          changeItem.description = 'An unknown change has been made ' +
              'to this enviroment via the CLI.';
          break;
      }
    }
    if (skipTime || !change) {
      changeItem.time = '-';
    } else {
      changeItem.time = this._formatAMPM(new Date(change.timestamp));
    }
    return changeItem;
  };

  /**
    Return formatted time for display.

    @method _formatAMPM
    @param {Date} date The current date.
  */
  ChangesUtils._formatAMPM = function(date) {
    var hours = date.getHours();
    var minutes = date.getMinutes();
    var ampm = hours >= 12 ? 'pm' : 'am';
    hours = hours % 12;
    hours = hours ? hours : 12;
    minutes = minutes < 10 ? '0' + minutes : minutes;
    var strTime = hours + ':' + minutes + ' ' + ampm;
    return strTime;
  };

  /**
    Loops through the services in the db to find ones which have id's which
    match the temporary id's assigned to the add relation call.

    @method getRealRelationEndpointNames
    @param {Array} args The arguments array from the ecs add relations call.
    @return {Array} An array of the service names involved in the relation.
  */
  ChangesUtils.getRealRelationEndpointNames = function(args, services) {
    var serviceList = [],
        serviceId;
    services.each(function(service) {
      serviceId = service.get('id');
      args.forEach(function(arg) {
        if (serviceId === arg[0]) {
          var matches = service.get('displayName').match(removeBrackets);
          serviceList.push(matches[matches.length - 1]);
        }
      });
    });
    return serviceList;
  };

  /**
    Return the service for a unit with the supplied id.
    Raise an error if the unit is not found.

    @method getServiceByUnitId
    @param {String} unitId The unit identifier in the database.
    @param {Object} services The list of services from the db.
    @param {Object} units The list of units from the db.
    @return {Model} The service model instance.
  */
  ChangesUtils.getServiceByUnitId = function(unitId, services, units) {
    var unit = units.getById(unitId);
    if (!unit) {
      // This should never happen in the deployer panel context.
      throw 'unit ' + unitId + ' not found';
    }
    return services.getById(unit.service);
  };

  window.juju.utils.ChangesUtils = ChangesUtils;

}, '0.0.1', {requires: []});
<commit_msg>Update doc strings<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('changes-utils', function(Y) {

  var ChangesUtils = {};

  var removeBrackets = /^\(?(.{0,}?)\)?$/;

  /**
    Return the counts for each type of ecs change.

    @method getChangeCounts
    @param {Object} services The list of services from the db.
    @returns {Object} The ecs change counts.
  */
  ChangesUtils.getChangeCounts = function(changeSet) {
    var counts = {};
    var total = 0;
    Object.keys(changeSet).forEach(function(key) {
      var change = changeSet[key];
      var method = change.command && change.command.method;
      if (method) {
        if (counts[method]) {
          counts[method] = counts[method] += 1;
        } else {
          counts[method] = 1;
        }
        total += 1;
      }
    }, this);
    counts.total = total;
    return counts;
  };

  /**
    Group the ecs changes by method type.

    @method getGroupedChanges
    @param {Object} changeSet The current environment change set.
    @returns {Object} The grouped ecs changes.
  */
  ChangesUtils.getGroupedChanges = function(changeSet) {
    var changes = {};
    Object.keys(changeSet).forEach((key) => {
      var change = changeSet[key];
      var method = change.command && change.command.method;
      if (method) {
        if (!changes[method]) {
          changes[method] = {};
        }
        changes[method][key] = change;
      }
    }, this);
    return changes;
  };

  /**
    Return a list of all change descriptions.

    @method generateAllChangeDescriptions
    @param {Object} changeSet The current environment change set.
    @param {Object} services The list of services from the db.
    @param {Object} units The list of units from the db.
  */
  ChangesUtils.generateAllChangeDescriptions = function(changeSet, services,
                                                        units) {
    var changes = [],
        change;
    Object.keys(changeSet).forEach(function(key) {
      change = this.generateChangeDescription(
          services, units, changeSet[key]);
      if (change) {
        changes.push(change);
      }
    }, this);
    return changes;
  };

  /**
    Return a description of an ecs change for the summary.

    @method generateChangeDescription
    @param {Object} services The list of services from the db.
    @param {Object} units The list of units from the db.
    @param {Object} change The environment change.
    @param {Bool} skipTime optional, used for testing, don't generate time.
  */
  ChangesUtils.generateChangeDescription = function(services, units, change,
                                                    skipTime) {
    var changeItem = {};

    if (change && change.command) {
      changeItem.id = change.id;
      // XXX: The add_unit is just the same as the service because adding
      // the service also adds the unit. We need to look at the UX for
      // units as follow up.
      switch (change.command.method) {
        case '_addCharm':
          var command = change.command;
          changeItem.description = ' ' + command.args[0] +
            ' charm has been added.';
          // TODO frankban: retrieve the icon from the charm itself. We cannot
          // always pass applicationId as an option, and maybe we should never
          // do that, and just get what we need from the charm.
          changeItem.icon = 'charm-added';
          var appId = command.options && command.options.applicationId;
          if (appId) {
            changeItem.icon = services.getById(appId).get('icon');
          }
          break;
        case '_deploy':
          if (!change.command.options || !change.command.options.modelId) {
            // When using the deploy-target query parameter we want to auto
            // deploy so we can skip generating the line item in the deployer
            // bar.
            return;
          }
          var ghostService = services.getById(
              change.command.options.modelId);
          changeItem.icon = ghostService.get('icon');
          changeItem.description = ' ' + ghostService.get('name') +
              ' has been added.';
          break;
        case '_destroyApplication':
          changeItem.icon = 'changes-service-destroyed';
          changeItem.description = ' ' + change.command.args[0] +
              ' has been destroyed.';
          break;
        case '_add_unit':
          var service = this.getServiceByUnitId(
              change.command.options.modelId, services, units);
          changeItem.icon = 'changes-units-added';
          var units = change.command.args[1],
              msg;
          if (units !== 1) {
            msg = 'units have been added.';
          } else {
            msg = 'unit has been added.';
          }
          changeItem.description = ' ' + units + ' ' +
              service.get('name') + ' ' + msg;
          break;
        case '_remove_units':
          changeItem.icon = 'changes-units-removed';
          /*jslint -W004*/
          var units = change.command.args[0];
          changeItem.description = units.length + ' unit' +
              (units.length === 1 ? ' has' : 's have') +
              ' been removed from ' + units[0].split('/')[0];
          break;
        case '_expose':
          var name = change.command.args[0];
          if (name.indexOf('$') > -1) {
            var ghostService = services.getById(name);
            name = ghostService.get('name');
          }
          changeItem.icon = 'exposed_16';
          changeItem.description = name + ' exposed';
          break;
        case '_unexpose':
          changeItem.icon = 'exposed_16';
          changeItem.description = change.command.args[0] + ' unexposed';
          break;
        case '_add_relation':
          var serviceList = this.getRealRelationEndpointNames(
              change.command.args, services);
          changeItem.icon = 'changes-relation-added';
          changeItem.description = change.command.args[0][1].name +
              ' relation added between ' +
              serviceList[0] + ' and ' + serviceList[1] + '.';
          break;
        case '_remove_relation':
          changeItem.icon = 'changes-relation-removed';
          changeItem.description = change.command.args[0][1].name +
              ' relation removed between ' +
              change.command.args[0][0] +
              ' and ' +
              change.command.args[1][0] + '.';
          break;
        case '_addMachines':
          var machineType = change.command.args[0][0].parentId ?
              'container' : 'machine';
          changeItem.icon = 'changes-' + machineType + '-created';
          changeItem.description = change.command.args[0].length +
              ' ' + machineType +
              (change.command.args[0].length !== 1 ? 's have' : ' has') +
              ' been added.';
          break;
        case '_destroyMachines':
          var machineType = change.command.args[0][0].indexOf('/') !== -1 ?
              'container' : 'machine';
          changeItem.icon = 'changes-' + machineType + '-destroyed';
          changeItem.description = change.command.args[0].length +
              ' ' + machineType +
              (change.command.args[0].length !== 1 ? 's have' : ' has') +
              ' been destroyed.';
          break;
        case '_set_config':
          var cfgServ = services.getById(change.command.args[0]);
          changeItem.icon = 'changes-config-changed';
          changeItem.description = 'Configuration values changed for ' +
              cfgServ.get('displayName').match(removeBrackets)[1] + '.';
          break;
        default:
          changeItem.icon = 'changes-unknown';
          changeItem.description = 'An unknown change has been made ' +
              'to this enviroment via the CLI.';
          break;
      }
    }
    if (skipTime || !change) {
      changeItem.time = '-';
    } else {
      changeItem.time = this._formatAMPM(new Date(change.timestamp));
    }
    return changeItem;
  };

  /**
    Return formatted time for display.

    @method _formatAMPM
    @param {Date} date The current date.
  */
  ChangesUtils._formatAMPM = function(date) {
    var hours = date.getHours();
    var minutes = date.getMinutes();
    var ampm = hours >= 12 ? 'pm' : 'am';
    hours = hours % 12;
    hours = hours ? hours : 12;
    minutes = minutes < 10 ? '0' + minutes : minutes;
    var strTime = hours + ':' + minutes + ' ' + ampm;
    return strTime;
  };

  /**
    Loops through the services in the db to find ones which have id's which
    match the temporary id's assigned to the add relation call.

    @method getRealRelationEndpointNames
    @param {Array} args The arguments array from the ecs add relations call.
    @return {Array} An array of the service names involved in the relation.
  */
  ChangesUtils.getRealRelationEndpointNames = function(args, services) {
    var serviceList = [],
        serviceId;
    services.each(function(service) {
      serviceId = service.get('id');
      args.forEach(function(arg) {
        if (serviceId === arg[0]) {
          var matches = service.get('displayName').match(removeBrackets);
          serviceList.push(matches[matches.length - 1]);
        }
      });
    });
    return serviceList;
  };

  /**
    Return the service for a unit with the supplied id.
    Raise an error if the unit is not found.

    @method getServiceByUnitId
    @param {String} unitId The unit identifier in the database.
    @param {Object} services The list of services from the db.
    @param {Object} units The list of units from the db.
    @return {Model} The service model instance.
  */
  ChangesUtils.getServiceByUnitId = function(unitId, services, units) {
    var unit = units.getById(unitId);
    if (!unit) {
      // This should never happen in the deployer panel context.
      throw 'unit ' + unitId + ' not found';
    }
    return services.getById(unit.service);
  };

  window.juju.utils.ChangesUtils = ChangesUtils;

}, '0.0.1', {requires: []});
<|endoftext|>