<commit_before>function smf_fileUpload(oOptions)
{
	var previewNode = document.querySelector('#au-template');
	previewNode.id = '';
	var previewTemplate = previewNode.parentNode.innerHTML;
	previewNode.parentNode.removeChild(previewNode);

	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		parallelUploads : 1,
		filesizeBase:1000,
		paramName: 'attachment',
		uploadMultiple:true,
		previewsContainer: '#au-previews',
		previewTemplate: previewTemplate,
		acceptedFiles: '.doc,.gif,.jpg,.pdf,.png,.txt,.zip',
		thumbnailWidth: 100,
		thumbnailHeight: null,
		autoQueue: false,
		clickable: '.fileinput-button',
		accept: function(file, done) {

			// Need to check if the added file doesn't surpass the total max size setting.
			myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize + file.size;

			if (myDropzone.options.totalMaxSize > myDropzone.options.limitMultiFileUploadSize){
				done(myDropzone.options.text_totalMaxSize.replace('{currentTotal}', myDropzone.options.limitMultiFileUploadSize * 0.001).replace('{currentRemain}', myDropzone.options.totalMaxSize * 0.001));
			}
			else{
				done();
			}
		},
		totalMaxSize: 0
	};

	$.extend(true, dOptions, oOptions);

	var myDropzone = new Dropzone('div#attachUpload', dOptions);

	myDropzone.on('addedfile', function(file) {

		_thisElement = $(file.previewElement);

		// Create a generic thumb for non image files.
		if (!file.type.match(/image.*/)) {
			myDropzone.emit('thumbnail', file, smf_images_url +'/generic_attach.png');
		}

		// Show the file info.
		_thisElement.find('.attach-ui').fadeIn();

		// Create a function to insert the BBC attach tag.
		file.insertAttachment = function (_innerElement, attachmentId){

			_innerElement.find('.insertBBC').on('click', function (e) {
				e.preventDefault();

				// Get the editor stuff.
				var oEditor = $('#' + oEditorID).data('sceditor');

				oEditor.insert('[attach]' + attachmentId + '[/attach]');
			});
		};

		// Replace the field with a message when the attachment is deleted.
		file.deleteAttachment = function (_innerElement, attachmentId){

			deleteButton = $('')
			.addClass('button_submit')
			.prop('disabled', false)
			.text(myDropzone.options.text_deleteAttach)
			.one('click', function (e) {

				$this = $(this);

				// Perform the action only after receiving the confirmation.
				if (!confirm(smf_you_sure)){
					return;
				}

				// Let the server know you want to delete the file you just recently uploaded...
				$.ajax({
					url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ attachmentId +';' + smf_session_var + '=' + smf_session_id,
					type: 'GET',
					dataType: 'json',
					beforeSend: function(){
						ajax_indicator(true);
					},
					complete: function(jqXHR, textStatus){
						ajax_indicator(false);

						// Delete the button.
						$this.fadeOutAndRemove('slow');
					},
					success: function (data, textStatus, xhr) {

						// Remove the text field and show a nice confirmation message.
						_innerElement.find('.attached_BBC').text(myDropzone.options.text_attachDeleted);
					},
					error: function (xhr, textStatus, errorThrown) {

						// Tell the user something horrible happen!
						_thisElement.find('p.error').append(textStatus.error.join('
'));

						// For dramatic purposes only!
						_thisElement.removeClass('infobox').addClass('errorbox');
					}
				});
			})
			.appendTo(_innerElement.find('.attach-ui'));
		};

		// Hookup the start button.
		_thisElement.find('.start').on( 'click', function() {
			myDropzone.enqueueFile(file);
		});

		// Show the main stuff!
		_thisElement.addClass('descbox');

		// Show the upload and cancel all buttons only if there is something to cancel/upload.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 1){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeIn();
		}
	});

	// Stuff to do when a file gets cancel.
	myDropzone.on('removedfile', function(file) {

		// Need to remove the file size to make sure theres plenty of room for another one.
		myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize - file.size;

		// Hide the cancel and upload all buttons if there is nothing to cancel/upload anymore.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 0){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeOut();
		}
	});

	// Update the total progress bar.
	myDropzone.on('totaluploadprogress', function(progress) {
		$('#total-progress span').width(progress + '%');
	});

	myDropzone.on('error', function(file, errorMessage, xhr) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Set a nice css class to make it more obvious theres an error.
		_thisElement.addClass('errorbox').removeClass('descbox');
	});

	myDropzone.on('success', function(file, responseText, e) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Don't do anything if there is no response from server.
		if (!responseText){
			return;
		}

		// There is a general error.
		if (responseText.generalErrors){
			_thisElement.find('p.error').append(responseText.generalErrors.join('
'));
			return;
		}

		// Server returns an array.
		response = responseText.files[0];

		// Show the input field.
		_thisElement.find('.attach-info p.attached_BBC').fadeIn();

		// The request was complete but the server returned an error.
		if (typeof response.errors !== 'undefined' && response.errors.length > 0){

			_thisElement.addClass('errorbox').removeClass('descbox');

			// Show the server error.
			_thisElement.find('p.error').append(response.errors.join('
'));
			return;
		}

		// If there wasn't any error, change the current cover.
		_thisElement.addClass('infobox').removeClass('descbox');

		// Append the BBC.
		_thisElement.find('input[name="attachBBC"]').val('[attach]' + response.attachID + '[/attach]');

		file.insertAttachment(_thisElement, response.attachID);

		// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
		_thisElement.find('a.delete').fadeOutAndRemove('slow');

		// Fire up the delete button.
		file.deleteAttachment(_thisElement, response.attachID);
	});

	myDropzone.on('uploadprogress', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Get the current file box progress bar, set its inner span's width accordingly.
		_thisElement.find('p.progressBar span').width(progress + '%');
	});

	myDropzone.on('complete', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Hide the progress bar.
		_thisElement.find('p.progressBar').fadeOut();

		// Finishing up mocking!
		if (typeof file.isMock !== "undefined" && typeof file.attachID !== "undefined"){
			// Show the input field.
			_thisElement.find('.attach-info p.attached_BBC').fadeIn();

			// If there wasn't any error, change the current cover.
			_thisElement.addClass('infobox').removeClass('descbox');

			// Remove the 'start' button.
			_thisElement.find('.start').fadeOutAndRemove('slow');

			// Append the BBC.
			_thisElement.find('input[name="attachBBC"]').val('[attach]' + file.attachID + '[/attach]');

			file.insertAttachment(_thisElement, file.attachID);

			// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
			_thisElement.find('a.delete').fadeOutAndRemove('slow');

			// Fire up the delete button.
			file.deleteAttachment(_thisElement, file.attachID);
		}
	});

	// Show each individual's progress bar.
	myDropzone.on('sending', function(file) {

		_thisElement = $(file.previewElement);

		// Show the progress bar when upload starts.
		_thisElement.find('p.progressBar').fadeIn();

		// Show the total progress bar when upload starts.
		$("#total-progress").fadeIn();
	});

	// Update the total progress bar.
	myDropzone.on("totaluploadprogress", function(progress) {
		$("#total-progress span").width(progress + '%');
	});

	// Hide the total progress bar when nothing's uploading anymore.
	myDropzone.on("queuecomplete", function(progress) {
		$("#total-progress").fadeOut();
	});

	// Add an event for uploading and cancelling all files.
	$('a#attach-cancelAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.removeAllFiles(true);
	});

	$('a#attach-uploadAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
	});

	// Need to tell the user they cannot post until all files are either uploaded or canceled.
	$("input[name ='post']").on('click', function(e) {

		attachAdded = myDropzone.getFilesWithStatus(Dropzone.ADDED).length;
		attachQueued = myDropzone.getFilesWithStatus(Dropzone.QUEUED).length;

		if (attachAdded > 0 || attachQueued > 0 ){
			alert(myDropzone.options.text_attachLeft);
			e.preventDefault();
			e.stopPropagation();
			return false;
		}
	});

	// Hide the default way to show already atached files.
	$('#postAttachment').fadeOutAndRemove('slow');

	// Show any attachments already uploaded.
	if (typeof current_attachments !== "undefined"){
		$.each(current_attachments, function(key, mock) {

			// Tell the world this is a mock file!
			mock.isMock = true;

			myDropzone.emit("addedfile", mock);

			// If the attachment is an image and has a thumbnail, show it. Otherwise fallback to the generic thumbfile.
			if (!mock.type.match(/image.*/)) {
				myDropzone.emit('thumbnail', mock, smf_images_url +'/generic_attach.png');
			}

			// If the file is too small, it won't have a thumbnail, show the regular file.
			myDropzone.emit('thumbnail', mock, smf_prepareScriptUrl(smf_scripturl) +'action=dlattach;attach='+ (mock.thumbID > 0 ? mock.thumbID : mock.attachID) + ';type=preview');

			// This file is "completed".
			myDropzone.emit("complete", mock);
		});
	}

}
<commit_msg>don't just show a generic message Show the actual message returned by the server.<commit_after>function smf_fileUpload(oOptions)
{
	var previewNode = document.querySelector('#au-template');
	previewNode.id = '';
	var previewTemplate = previewNode.parentNode.innerHTML;
	previewNode.parentNode.removeChild(previewNode);

	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		parallelUploads : 1,
		filesizeBase:1000,
		paramName: 'attachment',
		uploadMultiple:true,
		previewsContainer: '#au-previews',
		previewTemplate: previewTemplate,
		acceptedFiles: '.doc,.gif,.jpg,.pdf,.png,.txt,.zip',
		thumbnailWidth: 100,
		thumbnailHeight: null,
		autoQueue: false,
		clickable: '.fileinput-button',
		accept: function(file, done) {

			// Need to check if the added file doesn't surpass the total max size setting.
			myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize + file.size;

			if (myDropzone.options.totalMaxSize > myDropzone.options.limitMultiFileUploadSize){
				done(myDropzone.options.text_totalMaxSize.replace('{currentTotal}', myDropzone.options.limitMultiFileUploadSize * 0.001).replace('{currentRemain}', myDropzone.options.totalMaxSize * 0.001));
			}
			else{
				done();
			}
		},
		totalMaxSize: 0
	};

	$.extend(true, dOptions, oOptions);

	var myDropzone = new Dropzone('div#attachUpload', dOptions);

	myDropzone.on('addedfile', function(file) {

		_thisElement = $(file.previewElement);

		// Create a generic thumb for non image files.
		if (!file.type.match(/image.*/)) {
			myDropzone.emit('thumbnail', file, smf_images_url +'/generic_attach.png');
		}

		// Show the file info.
		_thisElement.find('.attach-ui').fadeIn();

		// Create a function to insert the BBC attach tag.
		file.insertAttachment = function (_innerElement, attachmentId){

			_innerElement.find('.insertBBC').on('click', function (e) {
				e.preventDefault();

				// Get the editor stuff.
				var oEditor = $('#' + oEditorID).data('sceditor');

				oEditor.insert('[attach]' + attachmentId + '[/attach]');
			});
		};

		// Replace the field with a message when the attachment is deleted.
		file.deleteAttachment = function (_innerElement, attachmentId){

			deleteButton = $('')
			.addClass('button_submit')
			.prop('disabled', false)
			.text(myDropzone.options.text_deleteAttach)
			.one('click', function (e) {

				$this = $(this);

				// Perform the action only after receiving the confirmation.
				if (!confirm(smf_you_sure)){
					return;
				}

				// Let the server know you want to delete the file you just recently uploaded...
				$.ajax({
					url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ attachmentId +';' + smf_session_var + '=' + smf_session_id,
					type: 'GET',
					dataType: 'json',
					beforeSend: function(){
						ajax_indicator(true);
					},
					complete: function(jqXHR, textStatus){
						ajax_indicator(false);

						// Delete the button.
						$this.fadeOutAndRemove('slow');
					},
					success: function (data, textStatus, xhr) {

						// For dramatic purposes only!
						_thisElement.removeClass('infobox').addClass(data.type +'box');

						// Remove the text field and show a nice confirmation message.
						_innerElement.find('.attached_BBC').text(data.text);
					},
					error: function (xhr, textStatus, errorThrown) {

						// Tell the user something horrible happen!
						_thisElement.find('p.error').append(textStatus.error.join('
'));

						// For dramatic purposes only!
						_thisElement.removeClass('infobox').addClass('errorbox');
					}
				});
			})
			.appendTo(_innerElement.find('.attach-ui'));
		};

		// Hookup the start button.
		_thisElement.find('.start').on( 'click', function() {
			myDropzone.enqueueFile(file);
		});

		// Show the main stuff!
		_thisElement.addClass('descbox');

		// Show the upload and cancel all buttons only if there is something to cancel/upload.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 1){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeIn();
		}
	});

	// Stuff to do when a file gets cancel.
	myDropzone.on('removedfile', function(file) {

		// Need to remove the file size to make sure theres plenty of room for another one.
		myDropzone.options.totalMaxSize = myDropzone.options.totalMaxSize - file.size;

		// Hide the cancel and upload all buttons if there is nothing to cancel/upload anymore.
		if (myDropzone.getFilesWithStatus(Dropzone.ADDED).length == 0){
			$('div#attachUpload').find('#attach-cancelAll, #attach-uploadAll').fadeOut();
		}
	});

	// Update the total progress bar.
	myDropzone.on('totaluploadprogress', function(progress) {
		$('#total-progress span').width(progress + '%');
	});

	myDropzone.on('error', function(file, errorMessage, xhr) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Set a nice css class to make it more obvious theres an error.
		_thisElement.addClass('errorbox').removeClass('descbox');
	});

	myDropzone.on('success', function(file, responseText, e) {

		_thisElement = $(file.previewElement);

		// Remove the 'start' button.
		_thisElement.find('.start').fadeOutAndRemove('slow');

		// Don't do anything if there is no response from server.
		if (!responseText){
			return;
		}

		// There is a general error.
		if (responseText.generalErrors){
			_thisElement.find('p.error').append(responseText.generalErrors.join('
'));
			return;
		}

		// Server returns an array.
		response = responseText.files[0];

		// Show the input field.
		_thisElement.find('.attach-info p.attached_BBC').fadeIn();

		// The request was complete but the server returned an error.
		if (typeof response.errors !== 'undefined' && response.errors.length > 0){

			_thisElement.addClass('errorbox').removeClass('descbox');

			// Show the server error.
			_thisElement.find('p.error').append(response.errors.join('
'));
			return;
		}

		// If there wasn't any error, change the current cover.
		_thisElement.addClass('infobox').removeClass('descbox');

		// Append the BBC.
		_thisElement.find('input[name="attachBBC"]').val('[attach]' + response.attachID + '[/attach]');

		file.insertAttachment(_thisElement, response.attachID);

		// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
		_thisElement.find('a.delete').fadeOutAndRemove('slow');

		// Fire up the delete button.
		file.deleteAttachment(_thisElement, response.attachID);
	});

	myDropzone.on('uploadprogress', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Get the current file box progress bar, set its inner span's width accordingly.
		_thisElement.find('p.progressBar span').width(progress + '%');
	});

	myDropzone.on('complete', function(file, progress, bytesSent) {

		_thisElement = $(file.previewElement);

		// Hide the progress bar.
		_thisElement.find('p.progressBar').fadeOut();

		// Finishing up mocking!
		if (typeof file.isMock !== "undefined" && typeof file.attachID !== "undefined"){
			// Show the input field.
			_thisElement.find('.attach-info p.attached_BBC').fadeIn();

			// If there wasn't any error, change the current cover.
			_thisElement.addClass('infobox').removeClass('descbox');

			// Remove the 'start' button.
			_thisElement.find('.start').fadeOutAndRemove('slow');

			// Append the BBC.
			_thisElement.find('input[name="attachBBC"]').val('[attach]' + file.attachID + '[/attach]');

			file.insertAttachment(_thisElement, file.attachID);

			// You have already loaded this attachment, to prevent abuse, you cannot cancel it and upload a new one.
			_thisElement.find('a.delete').fadeOutAndRemove('slow');

			// Fire up the delete button.
			file.deleteAttachment(_thisElement, file.attachID);
		}
	});

	// Show each individual's progress bar.
	myDropzone.on('sending', function(file) {

		_thisElement = $(file.previewElement);

		// Show the progress bar when upload starts.
		_thisElement.find('p.progressBar').fadeIn();

		// Show the total progress bar when upload starts.
		$("#total-progress").fadeIn();
	});

	// Update the total progress bar.
	myDropzone.on("totaluploadprogress", function(progress) {
		$("#total-progress span").width(progress + '%');
	});

	// Hide the total progress bar when nothing's uploading anymore.
	myDropzone.on("queuecomplete", function(progress) {
		$("#total-progress").fadeOut();
	});

	// Add an event for uploading and cancelling all files.
	$('a#attach-cancelAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.removeAllFiles(true);
	});

	$('a#attach-uploadAll' ).on('click', function() {

		if (!confirm(smf_you_sure)){
			return;
		}

		myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
	});

	// Need to tell the user they cannot post until all files are either uploaded or canceled.
	$("input[name ='post']").on('click', function(e) {

		attachAdded = myDropzone.getFilesWithStatus(Dropzone.ADDED).length;
		attachQueued = myDropzone.getFilesWithStatus(Dropzone.QUEUED).length;

		if (attachAdded > 0 || attachQueued > 0 ){
			alert(myDropzone.options.text_attachLeft);
			e.preventDefault();
			e.stopPropagation();
			return false;
		}
	});

	// Hide the default way to show already atached files.
	$('#postAttachment').fadeOutAndRemove('slow');

	// Show any attachments already uploaded.
	if (typeof current_attachments !== "undefined"){
		$.each(current_attachments, function(key, mock) {

			// Tell the world this is a mock file!
			mock.isMock = true;

			myDropzone.emit("addedfile", mock);

			// If the attachment is an image and has a thumbnail, show it. Otherwise fallback to the generic thumbfile.
			if (!mock.type.match(/image.*/)) {
				myDropzone.emit('thumbnail', mock, smf_images_url +'/generic_attach.png');
			}

			// If the file is too small, it won't have a thumbnail, show the regular file.
			myDropzone.emit('thumbnail', mock, smf_prepareScriptUrl(smf_scripturl) +'action=dlattach;attach='+ (mock.thumbID > 0 ? mock.thumbID : mock.attachID) + ';type=preview');

			// This file is "completed".
			myDropzone.emit("complete", mock);
		});
	}

}
<|endoftext|>


<commit_before>//Copyright {2016} {NIIT Limited, Wipro Limited}
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
//	Name of Developers Anil Sawant

var uuid = require('node-uuid'), // used to generate unique game ids
    questionBank = require('../questionBank'),
    LeaderBoard = require('./Leaderboard.js');

/**
** @param no constructor params
** @desc GameManager is a class that will handle all the games running in QuizRT.
**       It uses questionBank for getting questions for a game,
**       and LeaderBoard for maintaining the user scores.
*/
var GameManager = function() {
  this.games = new Map(); // holds all the games. Waiting, Live, and Finished
  this.players = new Map();// to map userId to [gameIds]
  this.topicsWaiting = {}; // holds only the games which are waiting for players. Maps topicId to gameId

  /**
  ** @param topicId as String, playersNeeded as Number
  ** @return true if the new game was created, otherwise false
  */
  this.createNewGame = function( topicId, playersNeeded ) {
    var newGame = { // create a newGame, generate a new gameId using uuid and set the game into this.games
      topicId: topicId,
      playersNeeded: playersNeeded,
      leaderBoard: [],
      players: []
    }
    var gameId = uuid.v1(); // generate a unique gameId
    this.games.set( gameId, newGame );// set the game into this.games against the new gameId
    this.topicsWaiting[topicId] = gameId; // save topicId as key and gameId as value to track which topics are waiting for more players to join
    return gameId; // return gameId so that it can be used later
  };

  /**
  ** @param topicId as String, playersNeeded as Number, incomingPlayer as Object
  ** @return true if player was added to a game, otherwise false
  */
  this.managePlayer = function( topicId, playersNeeded, incomingPlayer ) {
    var gameId4TopicInWaitStack = this.topicsWaiting[topicId];
    if ( gameId4TopicInWaitStack ) { // if the game is waiting in the wait stack
      var isPlayerAdded = this.addPlayerToGame( gameId4TopicInWaitStack, topicId, incomingPlayer );
      if ( isPlayerAdded ) {
        if ( this.isGameReady( gameId4TopicInWaitStack ) ) {
          this.startGame( gameId4TopicInWaitStack ); //start the game
          delete this.topicsWaiting[topicId]; //remove the topic from wait stack
          return true;
        }
        this.emitPendingPlayers( gameId4TopicInWaitStack );
        return true; // GameManager started managing the player
      }
      return false;
    } else {
      var gameId = this.createNewGame( topicId, playersNeeded ); // create a new game
      if ( gameId ) { // if the game was created successfully
        var isPlayerAdded = this.addPlayerToGame( gameId, topicId, incomingPlayer );
        if ( isPlayerAdded ) {
          if ( this.isGameReady( gameId ) ) {
            this.startGame( gameId ); //start the game
            delete this.topicsWaiting[topicId]; //remove the topic from wait stack
            return true;
          }
          this.emitPendingPlayers( gameId );
          return true; // GameManager started managing the player
        }
        return false;
      }
      return false;
    }
  };

  /**
  ** @param gameId as String, topicId as String, gamePlayer as Object
  ** @return true if gamePlayer was added to a game; otherwise false
  */
  this.addPlayerToGame = function( gameId, topicId, gamePlayer ) {
    var playerGames = this.players.get( gamePlayer.userId ),
        gamePlayers = this.games.get( gameId ) ? this.games.get( gameId ).players : null,
        self = this;

  	if ( gamePlayers ) { // players Array exists in the game. This check is not necessary
      if ( playerGames && playerGames.length ) { // gamePlayer is already playing some game(s)
        var isPlayingSameTopic = playerGames.some( function( savedGameId ) {
          if ( savedGameId == gameId && self.games.get( savedGameId ) && (self.games.get( savedGameId ).topicId == topicId) ) {
            return true;
          }
          return false;
        });
        if ( !isPlayingSameTopic ) {
          playerGames.push( gameId ); // add the gameId to gamePlayer's array of playing-games
          gamePlayers.push( gamePlayer ); // since the game already exists, add gamePlayer to players array the game
          console.log( gamePlayer.userId , ' was added to ' , gameId);
          return true;
        }
        return false;// player is already playing topicId.
                     // Can't play the same topic until the game is finished/popped
      } else { // gamePlayer is not playing any game(s) so far
        this.players.set( gamePlayer.userId, [gameId] ); // set the gameId as the first game gamePlayer is playing i.e. set it in the map
        gamePlayers.push( gamePlayer ); // since the game already exists, add gamePlayer to players array the game
        console.log( gamePlayer.userId , ' was added to ' , gameId);
        return true;
      }
  	}
    return false;
  };

  /**
  ** @param gameId as String
  ** @return true if the game is ready to start, otherwise false
  */
  this.isGameReady = function( gameId ) {
    var game = this.games.get( gameId );
    if ( game.playersNeeded === game.players.length ) {
      return true;
    }
  	return false;
  };

  /**
  ** @param gameId as String
  ** @desc emits 'pendingPlayers' event for every player in a game with gameId = gameId
  */
  this.emitPendingPlayers = function( gameId ) {
    var game = this.games.get( gameId );
    if ( game.players ) {
      game.players.forEach( function(player) {
        console.log('Emitting pending players = ' + (game.playersNeeded - game.players.length) + ' for ' + player.userId );
        player.client.emit('pendingPlayers', { gameId: gameId, pendingPlayers: (game.playersNeeded - game.players.length) } );
      });
    }
  };

  /**
  ** @param gameId as String
  ** @return true if everything is setup before starting a Game and 'startGame' events are emitted
  */
  this.startGame = function( gameId ) {
    var game = this.games.get( gameId ),
        self = this;

    questionBank.getQuizQuestions( game.topicId, 5 , function( err, questions ) { // get questions from the questionBank
      if ( err ) {
        console.log('ERROR: Failed to get quiz questions for ' + gameId + '. Cannot start the game. Terminating the game launch.');
        console.error(err);
        // return false;
      }
      //prepare the LeaderBoard for the game
      var players = []
      game.players.forEach( function(player) { // extra loop to exclude player.client from leaderBoard and prevent CallStack Overflow error
        var gamePlayer = {
          userId: player.userId,
          playerName: player.playerName,
          playerPic: player.playerPic,
          score: 0 // add score to gamePlayer and initialize it to zero
        }
        players.push( gamePlayer );
      });
      LeaderBoard.createNewLeaderBoard( gameId, players, function(err, leaderBoardCreated) {
        if ( err ) {
          console.log('ERROR: Failed to create LeaderBoard for ' + gameId + '. Cannot start the game. Terminating the game launch.');
          return false;
        }
        game.leaderBoard = players; // set the leaderBoard on game also. gives 2nd way to access it. other being getLeaderBoard()
        console.log('\n');
        console.log('Starting game: ' + gameId);
        game.players.forEach( function(player) {
          console.log('Starting game for ' + player.userId );
          player.client.emit('startGame', { topicId: game.topicId, gameId: gameId, playersNeeded: game.playersNeeded, questions: questions });
        });
        if ( !questions || !questions.length ) {
          self.popGame( gameId );
        }
        console.log('\n');
        return true;
      }); // create the leaderBoard for the game before starting
    });// end getQuizQuestions
  };

  /**
  ** @param topicId as String
  ** @return Array of players playing topicId
  */
  this.getGamePlayers = function( gameId ) {
  	return this.games.get( gameId );
  };

  /**
  ** @param gamePlayer as Obj
  ** @return Array of topics gamePlayer is playing
  */
  this.getPlayerTopics = function( userId ) {
  	return this.players.get( userId );
  };

  /**
  ** @param gameId as String, gamePlayer as Object
  ** @return true if gamePlayer left the game successfully; otherwise false
  */
  this.leaveGame = function( gameId, userId ) {
    var playerGames = this.players.get( userId ),
        game = this.games.get( gameId ),
        gamePlayers = game ? game.players : null, // array of players playing gameId
        playerLeft = false;

    if ( gamePlayers && gamePlayers.length ) {
      playerLeft = gamePlayers.some( function( savedPlayer, index ) {
        if ( savedPlayer.userId == userId ) {
          console.log( gamePlayers.splice( index, 1 ) , ' left ' + gameId );
          return true;
        }
        return false;
      });
    }
    if ( playerLeft ) { // do some cleanup
      if ( gamePlayers && !gamePlayers.length ) { // remove the game mapping if the game doesn't have any players
        delete this.topicsWaiting[game.topicId]; //remove the topic from wait stack
        this.games.delete( gameId );
      }
      var index = playerGames.indexOf( gameId );
      if ( playerGames && playerGames.length &&  (index != -1)) {
        console.log( playerGames.splice( index, 1 ), ' was removed from ' + userId  + "'s array of games.");
      }
      if ( playerGames && !playerGames.length ) { // remove the player mapping if the player is not playing any topic
        this.players.delete( userId );
      }
      return true; // player successfully left the game and other cleanup was done
    }
    return false; // the player or the game does not exist
  };

  /**
  ** @param gameId as String
  ** @return true if the game was successfully popped; otherwise false
  */
  this.popGame = function( gameId ) {
    var game = this.games.get( gameId ),
        gamePlayers = game ? game.players : null,
        self = this;
    if ( gamePlayers && gamePlayers.length ) {
      gamePlayers.forEach( function( gamePlayer ) { // before deleting the game delete the gameId entry in all the players
        var playerGames = self.players.get( gamePlayer.userId );
        if ( playerGames && playerGames.length ) { // player is playing some games
          var index = playerGames.indexOf( gameId );
          if ( index != -1 ) { // if player is playing the gameId to be popped
            console.log( gamePlayer.userId + ' was removed from ' + playerGames.splice( index, 1 ) );
          }
          if ( playerGames && !playerGames.length ) { // remove the player mapping if the player is not playing any topic
            self.players.delete( gamePlayer.userId );
          }
        }
      });
    }
    if ( this.games.has( gameId ) ) {
      delete this.topicsWaiting[game.topicId]; //remove the topic from wait stack
      this.games.delete( gameId ); // pop the game if it exists
      return true;
    }
    return false; // game doesn't exist
  }

  /**
  ** @param gamePlayer as Object
  ** @return true if the gamePlayer was successfully popped; otherwise false
  */
  this.popPlayer = function( userId ) {
    var playerGames = this.players.get( userId ),
        self = this,
        removedFromGamesCount = 0;
    if ( playerGames && playerGames.length ) {
      playerGames.forEach( function( gameId ) { // before popping the player, delete the gamePlayer entry in all the games
        var game = self.games.get( gameId ),
            gamePlayers = game ? game.players : null; // to check where if gamePlayer entry is there in games
        if ( gamePlayers && gamePlayers.length ) { // game has some players
          gamePlayers.some( function( savedGamePlayer, index ) {
            if ( savedGamePlayer.userId == userId ) {
              savedGamePlayer.client.emit('serverMsg', {type:'LOGOUT', msg:'Multiple logins!! All sessions in GameManager will be popped.'});
              console.log( gamePlayers.splice( index, 1 )[0].userId , ' was removed from ' + gameId );
              self.emitPendingPlayers( gameId );
              removedFromGamesCount++ ;
              return true;
            }
            return false;
          });
          if ( gamePlayers && !gamePlayers.length ) { // cleanup
            delete self.topicsWaiting[game.topicId]; //remove the topic from wait stack
            self.games.delete( gameId );
          }
        }
      });
      if ( removedFromGamesCount == playerGames.length ) { // gamePlayer was removed from all the games he was part of
        if ( this.players.has( userId ) ) {
          this.players.delete( userId ); // delete the player mapping
          return true;
        }
      }
      return false; // gamePlayer was removed from a few games but not from all he was part of
    }
  }

  /**
  ** @param gameId as String
  ** @return Array of players playing gameId
  */
  this.getGamePlayers = function( gameId ) {
  	return this.games.has( gameId ) ? this.games.get( gameId ).players : null ;
  };

  /**
  ** @param gamePlayer as Obj
  ** @return Array of topics gamePlayer is playing
  */
  this.getPlayerGames = function( gamePlayer ) {
  	return this.players.get( gamePlayer.userId );
  };

  /**
  ** @desc exposes the LeaderBoard.get method from GameManager
  */
  this.getLeaderBoard = function( gameId ) { // expose the LeaderBoard from GameManager
    return LeaderBoard.get( gameId ); // call the LeaderBoard to get game specific leaderBoard
  };

  /**
  ** @desc exposes the LeaderBoard.updateScore method from GameManager
  */
  this.updateScore = function( gameId, userId, score) {
    LeaderBoard.updateScore( gameId, userId, score); // call the LeaderBoard to update the player score
  };
}

module.exports = new GameManager();
<commit_msg>Changed Leaderboard.js to leaderboard.js<commit_after>//Copyright {2016} {NIIT Limited, Wipro Limited}
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
//	Name of Developers Anil Sawant

var uuid = require('node-uuid'), // used to generate unique game ids
    questionBank = require('../questionBank'),
    LeaderBoard = require('./leaderboard.js');

/**
** @param no constructor params
** @desc GameManager is a class that will handle all the games running in QuizRT.
**       It uses questionBank for getting questions for a game,
**       and LeaderBoard for maintaining the user scores.
*/
var GameManager = function() {
  this.games = new Map(); // holds all the games. Waiting, Live, and Finished
  this.players = new Map();// to map userId to [gameIds]
  this.topicsWaiting = {}; // holds only the games which are waiting for players. Maps topicId to gameId

  /**
  ** @param topicId as String, playersNeeded as Number
  ** @return true if the new game was created, otherwise false
  */
  this.createNewGame = function( topicId, playersNeeded ) {
    var newGame = { // create a newGame, generate a new gameId using uuid and set the game into this.games
      topicId: topicId,
      playersNeeded: playersNeeded,
      leaderBoard: [],
      players: []
    }
    var gameId = uuid.v1(); // generate a unique gameId
    this.games.set( gameId, newGame );// set the game into this.games against the new gameId
    this.topicsWaiting[topicId] = gameId; // save topicId as key and gameId as value to track which topics are waiting for more players to join
    return gameId; // return gameId so that it can be used later
  };

  /**
  ** @param topicId as String, playersNeeded as Number, incomingPlayer as Object
  ** @return true if player was added to a game, otherwise false
  */
  this.managePlayer = function( topicId, playersNeeded, incomingPlayer ) {
    var gameId4TopicInWaitStack = this.topicsWaiting[topicId];
    if ( gameId4TopicInWaitStack ) { // if the game is waiting in the wait stack
      var isPlayerAdded = this.addPlayerToGame( gameId4TopicInWaitStack, topicId, incomingPlayer );
      if ( isPlayerAdded ) {
        if ( this.isGameReady( gameId4TopicInWaitStack ) ) {
          this.startGame( gameId4TopicInWaitStack ); //start the game
          delete this.topicsWaiting[topicId]; //remove the topic from wait stack
          return true;
        }
        this.emitPendingPlayers( gameId4TopicInWaitStack );
        return true; // GameManager started managing the player
      }
      return false;
    } else {
      var gameId = this.createNewGame( topicId, playersNeeded ); // create a new game
      if ( gameId ) { // if the game was created successfully
        var isPlayerAdded = this.addPlayerToGame( gameId, topicId, incomingPlayer );
        if ( isPlayerAdded ) {
          if ( this.isGameReady( gameId ) ) {
            this.startGame( gameId ); //start the game
            delete this.topicsWaiting[topicId]; //remove the topic from wait stack
            return true;
          }
          this.emitPendingPlayers( gameId );
          return true; // GameManager started managing the player
        }
        return false;
      }
      return false;
    }
  };

  /**
  ** @param gameId as String, topicId as String, gamePlayer as Object
  ** @return true if gamePlayer was added to a game; otherwise false
  */
  this.addPlayerToGame = function( gameId, topicId, gamePlayer ) {
    var playerGames = this.players.get( gamePlayer.userId ),
        gamePlayers = this.games.get( gameId ) ? this.games.get( gameId ).players : null,
        self = this;

  	if ( gamePlayers ) { // players Array exists in the game. This check is not necessary
      if ( playerGames && playerGames.length ) { // gamePlayer is already playing some game(s)
        var isPlayingSameTopic = playerGames.some( function( savedGameId ) {
          if ( savedGameId == gameId && self.games.get( savedGameId ) && (self.games.get( savedGameId ).topicId == topicId) ) {
            return true;
          }
          return false;
        });
        if ( !isPlayingSameTopic ) {
          playerGames.push( gameId ); // add the gameId to gamePlayer's array of playing-games
          gamePlayers.push( gamePlayer ); // since the game already exists, add gamePlayer to players array the game
          console.log( gamePlayer.userId , ' was added to ' , gameId);
          return true;
        }
        return false;// player is already playing topicId.
                     // Can't play the same topic until the game is finished/popped
      } else { // gamePlayer is not playing any game(s) so far
        this.players.set( gamePlayer.userId, [gameId] ); // set the gameId as the first game gamePlayer is playing i.e. set it in the map
        gamePlayers.push( gamePlayer ); // since the game already exists, add gamePlayer to players array the game
        console.log( gamePlayer.userId , ' was added to ' , gameId);
        return true;
      }
  	}
    return false;
  };

  /**
  ** @param gameId as String
  ** @return true if the game is ready to start, otherwise false
  */
  this.isGameReady = function( gameId ) {
    var game = this.games.get( gameId );
    if ( game.playersNeeded === game.players.length ) {
      return true;
    }
  	return false;
  };

  /**
  ** @param gameId as String
  ** @desc emits 'pendingPlayers' event for every player in a game with gameId = gameId
  */
  this.emitPendingPlayers = function( gameId ) {
    var game = this.games.get( gameId );
    if ( game.players ) {
      game.players.forEach( function(player) {
        console.log('Emitting pending players = ' + (game.playersNeeded - game.players.length) + ' for ' + player.userId );
        player.client.emit('pendingPlayers', { gameId: gameId, pendingPlayers: (game.playersNeeded - game.players.length) } );
      });
    }
  };

  /**
  ** @param gameId as String
  ** @return true if everything is setup before starting a Game and 'startGame' events are emitted
  */
  this.startGame = function( gameId ) {
    var game = this.games.get( gameId ),
        self = this;

    questionBank.getQuizQuestions( game.topicId, 5 , function( err, questions ) { // get questions from the questionBank
      if ( err ) {
        console.log('ERROR: Failed to get quiz questions for ' + gameId + '. Cannot start the game. Terminating the game launch.');
        console.error(err);
        // return false;
      }
      //prepare the LeaderBoard for the game
      var players = []
      game.players.forEach( function(player) { // extra loop to exclude player.client from leaderBoard and prevent CallStack Overflow error
        var gamePlayer = {
          userId: player.userId,
          playerName: player.playerName,
          playerPic: player.playerPic,
          score: 0 // add score to gamePlayer and initialize it to zero
        }
        players.push( gamePlayer );
      });
      LeaderBoard.createNewLeaderBoard( gameId, players, function(err, leaderBoardCreated) {
        if ( err ) {
          console.log('ERROR: Failed to create LeaderBoard for ' + gameId + '. Cannot start the game. Terminating the game launch.');
          return false;
        }
        game.leaderBoard = players; // set the leaderBoard on game also. gives 2nd way to access it. other being getLeaderBoard()
        console.log('\n');
        console.log('Starting game: ' + gameId);
        game.players.forEach( function(player) {
          console.log('Starting game for ' + player.userId );
          player.client.emit('startGame', { topicId: game.topicId, gameId: gameId, playersNeeded: game.playersNeeded, questions: questions });
        });
        if ( !questions || !questions.length ) {
          self.popGame( gameId );
        }
        console.log('\n');
        return true;
      }); // create the leaderBoard for the game before starting
    });// end getQuizQuestions
  };

  /**
  ** @param topicId as String
  ** @return Array of players playing topicId
  */
  this.getGamePlayers = function( gameId ) {
  	return this.games.get( gameId );
  };

  /**
  ** @param gamePlayer as Obj
  ** @return Array of topics gamePlayer is playing
  */
  this.getPlayerTopics = function( userId ) {
  	return this.players.get( userId );
  };

  /**
  ** @param gameId as String, gamePlayer as Object
  ** @return true if gamePlayer left the game successfully; otherwise false
  */
  this.leaveGame = function( gameId, userId ) {
    var playerGames = this.players.get( userId ),
        game = this.games.get( gameId ),
        gamePlayers = game ? game.players : null, // array of players playing gameId
        playerLeft = false;

    if ( gamePlayers && gamePlayers.length ) {
      playerLeft = gamePlayers.some( function( savedPlayer, index ) {
        if ( savedPlayer.userId == userId ) {
          console.log( gamePlayers.splice( index, 1 ) , ' left ' + gameId );
          return true;
        }
        return false;
      });
    }
    if ( playerLeft ) { // do some cleanup
      if ( gamePlayers && !gamePlayers.length ) { // remove the game mapping if the game doesn't have any players
        delete this.topicsWaiting[game.topicId]; //remove the topic from wait stack
        this.games.delete( gameId );
      }
      var index = playerGames.indexOf( gameId );
      if ( playerGames && playerGames.length &&  (index != -1)) {
        console.log( playerGames.splice( index, 1 ), ' was removed from ' + userId  + "'s array of games.");
      }
      if ( playerGames && !playerGames.length ) { // remove the player mapping if the player is not playing any topic
        this.players.delete( userId );
      }
      return true; // player successfully left the game and other cleanup was done
    }
    return false; // the player or the game does not exist
  };

  /**
  ** @param gameId as String
  ** @return true if the game was successfully popped; otherwise false
  */
  this.popGame = function( gameId ) {
    var game = this.games.get( gameId ),
        gamePlayers = game ? game.players : null,
        self = this;
    if ( gamePlayers && gamePlayers.length ) {
      gamePlayers.forEach( function( gamePlayer ) { // before deleting the game delete the gameId entry in all the players
        var playerGames = self.players.get( gamePlayer.userId );
        if ( playerGames && playerGames.length ) { // player is playing some games
          var index = playerGames.indexOf( gameId );
          if ( index != -1 ) { // if player is playing the gameId to be popped
            console.log( gamePlayer.userId + ' was removed from ' + playerGames.splice( index, 1 ) );
          }
          if ( playerGames && !playerGames.length ) { // remove the player mapping if the player is not playing any topic
            self.players.delete( gamePlayer.userId );
          }
        }
      });
    }
    if ( this.games.has( gameId ) ) {
      delete this.topicsWaiting[game.topicId]; //remove the topic from wait stack
      this.games.delete( gameId ); // pop the game if it exists
      return true;
    }
    return false; // game doesn't exist
  }

  /**
  ** @param gamePlayer as Object
  ** @return true if the gamePlayer was successfully popped; otherwise false
  */
  this.popPlayer = function( userId ) {
    var playerGames = this.players.get( userId ),
        self = this,
        removedFromGamesCount = 0;
    if ( playerGames && playerGames.length ) {
      playerGames.forEach( function( gameId ) { // before popping the player, delete the gamePlayer entry in all the games
        var game = self.games.get( gameId ),
            gamePlayers = game ? game.players : null; // to check where if gamePlayer entry is there in games
        if ( gamePlayers && gamePlayers.length ) { // game has some players
          gamePlayers.some( function( savedGamePlayer, index ) {
            if ( savedGamePlayer.userId == userId ) {
              savedGamePlayer.client.emit('serverMsg', {type:'LOGOUT', msg:'Multiple logins!! All sessions in GameManager will be popped.'});
              console.log( gamePlayers.splice( index, 1 )[0].userId , ' was removed from ' + gameId );
              self.emitPendingPlayers( gameId );
              removedFromGamesCount++ ;
              return true;
            }
            return false;
          });
          if ( gamePlayers && !gamePlayers.length ) { // cleanup
            delete self.topicsWaiting[game.topicId]; //remove the topic from wait stack
            self.games.delete( gameId );
          }
        }
      });
      if ( removedFromGamesCount == playerGames.length ) { // gamePlayer was removed from all the games he was part of
        if ( this.players.has( userId ) ) {
          this.players.delete( userId ); // delete the player mapping
          return true;
        }
      }
      return false; // gamePlayer was removed from a few games but not from all he was part of
    }
  }

  /**
  ** @param gameId as String
  ** @return Array of players playing gameId
  */
  this.getGamePlayers = function( gameId ) {
  	return this.games.has( gameId ) ? this.games.get( gameId ).players : null ;
  };

  /**
  ** @param gamePlayer as Obj
  ** @return Array of topics gamePlayer is playing
  */
  this.getPlayerGames = function( gamePlayer ) {
  	return this.players.get( gamePlayer.userId );
  };

  /**
  ** @desc exposes the LeaderBoard.get method from GameManager
  */
  this.getLeaderBoard = function( gameId ) { // expose the LeaderBoard from GameManager
    return LeaderBoard.get( gameId ); // call the LeaderBoard to get game specific leaderBoard
  };

  /**
  ** @desc exposes the LeaderBoard.updateScore method from GameManager
  */
  this.updateScore = function( gameId, userId, score) {
    LeaderBoard.updateScore( gameId, userId, score); // call the LeaderBoard to update the player score
  };
}

module.exports = new GameManager();
<|endoftext|>


<commit_before>import index from 'lib/index';

export default index();
<commit_msg>fix(instantiation): fix bad require path<commit_after>import index from './lib/index';

export default index();
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var sys = require('sys');
var log = require('./log');
var ps = require('./pubsub');

var config = {
  'user': 'nobody',
  'port': 49700,
  'address': '0.0.0.0',
  'secret': null
};

function drop_uid(uid)
{
  /* TOOD: username string -> UID */
  cuid = process.getuid();
  /* TODO: setgid? */
  if (cuid === 0) {
    process.setuid(uid);
  }
  else {
    log.warn('Currently running as', ''+cuid, 'not changing to', ''+uid);
  }
}

function parse_config(path) {
  /**
  * TODO: re-convert this to non-sync version, but it appears
  * that readFile async returns the data in a object that is
  * not parsable by JSON.parse()
  */
  setTimeout(function() {
    var data = fs.readFileSync('test.json',encoding='utf8')
    parsed = JSON.parse(data);
    for (var attrname in parsed) {
      if (parsed.hasOwnProperty(attrname)) {
        config[attrname] = parsed[attrname];
      }
    }
    ps.pub(ps.CONFIG_DONE);
  });
}

exports.init = function() {
  if (process.argv.length != 3) {
    log.err('3rd argument must be configuration file.');
    return false;
  }
  cfile = process.argv[2];
  log.debug('Reading configuration from', cfile);
  parse_config(cfile);
  return true;
};

exports.get = function() {
  return config;
};
<commit_msg>Bug or feature, who knows<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var sys = require('sys');
var log = require('./log');
var ps = require('./pubsub');

var config = {
  'user': 'nobody',
  'port': 49700,
  'address': '0.0.0.0',
  'secret': null
};

function drop_uid(uid)
{
  /* TOOD: username string -> UID */
  cuid = process.getuid();
  /* TODO: setgid? */
  if (cuid === 0) {
    process.setuid(uid);
  }
  else {
    log.warn('Currently running as', ''+cuid, 'not changing to', ''+uid);
  }
}

function parse_config(path) {
  fs.readFile('test.json', function(err, data) {
      parsed = JSON.parse(data.toString());
      log.err(parsed);
      for (var attrname in parsed) {
        if (parsed.hasOwnProperty(attrname)) {
          config[attrname] = parsed[attrname];
        }
      }
      ps.pub(ps.CONFIG_DONE);
    });
}

exports.init = function() {
  if (process.argv.length != 3) {
    log.err('3rd argument must be configuration file.');
    return false;
  }
  cfile = process.argv[2];
  log.debug('Reading configuration from', cfile);
  parse_config(cfile);
  return true;
};

exports.get = function() {
  return config;
};
<|endoftext|>


<commit_before>/**
 * External dependencies
 */
import babel from 'gulp-babel';
import gulp from 'gulp';
import refresh from 'gulp-refresh';
import sass from 'gulp-sass';
import sourcemaps from 'gulp-sourcemaps';

gulp.task('babel', () => gulp.src('./src/babel/**/*.js')
  .pipe(sourcemaps.init())
  .pipe(babel({
    presets: ['es2015'],
  }))
  .pipe(sourcemaps.write())
  .pipe(gulp.dest('./assets/js'))
  .pipe(refresh()));

gulp.task('babel:watch', () => {
  refresh.listen();

  gulp.watch('./babel/**/*.js', ['babel']);
});

gulp.task('sass', () => gulp.src('./src/sass/**/*.scss')
  .pipe(sourcemaps.init())
  .pipe(sass().on('error', sass.logError))
  .pipe(sourcemaps.write())
  .pipe(gulp.dest('./assets/css'))
  .pipe(refresh()));

gulp.task('sass:watch', () => {
  refresh.listen();

  gulp.watch('./sass/**/*.scss', ['sass']);
});

gulp.task('watch', () => {
  refresh.listen();

  gulp.watch('./src/sass/**/*.scss', ['sass']);
  gulp.watch('./src/babel/**/*.js', ['babel']);
});
<commit_msg>Added in error handling for gulp babel.<commit_after>/**
 * External dependencies
 */
import babel from 'gulp-babel';
import gulp from 'gulp';
import refresh from 'gulp-refresh';
import sass from 'gulp-sass';
import sourcemaps from 'gulp-sourcemaps';

gulp.task('babel', () => gulp.src('./src/babel/**/*.js')
  .pipe(sourcemaps.init())
  .pipe(babel({
    presets: ['es2015'],
  }))
  .on('error', function (err) {
    console.log('[Compilation Error]');
    console.log(err.fileName + (err.loc ? `( ${err.loc.line}, ${err.loc.column} ): ` : ': '));
    console.log(`error Babel:${err.message}\n`);
    console.log(err.codeFrame);

    this.emit('end');
  })
  .pipe(sourcemaps.write())
  .pipe(gulp.dest('./assets/js'))
  .pipe(refresh()));

gulp.task('babel:watch', () => {
  refresh.listen();

  gulp.watch('./babel/**/*.js', ['babel']);
});

gulp.task('sass', () => gulp.src('./src/sass/**/*.scss')
  .pipe(sourcemaps.init())
  .pipe(sass().on('error', sass.logError))
  .pipe(sourcemaps.write())
  .pipe(gulp.dest('./assets/css'))
  .pipe(refresh()));

gulp.task('sass:watch', () => {
  refresh.listen();

  gulp.watch('./sass/**/*.scss', ['sass']);
});

gulp.task('watch', () => {
  refresh.listen();

  gulp.watch('./src/sass/**/*.scss', ['sass']);
  gulp.watch('./src/babel/**/*.js', ['babel']);
});
<|endoftext|>


<commit_before>let webpack = require('webpack');
let mix = require('laravel-mix');
let fs = require('fs');
let path = require('path');
let exec = require('child_process').exec;
let package = JSON.parse(fs.readFileSync('./package.json'));
let CopyWebpackPlugin = require('copy-webpack-plugin');
let purge = require('laravel-mix-purgecss');
let tailwindcss = require('tailwindcss');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for your application, as well as bundling up your JS files.
 |
 */

// Error Files
mix.copy([
        'vendor/waynestate/error-404/dist/404.css',
        'vendor/waynestate/error-404/dist/404.css.map',
        'vendor/waynestate/error-403/dist/403.css',
        'vendor/waynestate/error-403/dist/403.css.map',
        'vendor/waynestate/error-500/dist/500.css',
        'vendor/waynestate/error-500/dist/500.css.map'
    ], 'public/_resources/css')

    // Header files
    .copy([
        'vendor/waynestate/error-404/dist/404.png',
        'vendor/waynestate/error-403/dist/403.png',
        'vendor/waynestate/error-500/dist/500.png'
    ], 'public/_resources/images')

    // Fonts
    .copy([
        'resources/fonts/**/*'
    ], 'public/_resources/fonts')

    // Images
    .copy([
        'resources/images/**/*.jpg',
        'resources/images/**/*.gif',
        'resources/images/**/*.png',
        'resources/images/**/*.svg'
    ], 'public/_resources/images');

// Compile assets and setup browersync
mix.js('resources/js/main.js', 'public/_resources/js')
   .sass('resources/scss/main.scss', 'public/_resources/css')
   .purgeCss({
        globs: [
            path.join(__dirname, "resources/views/**/*.blade.php"),
            path.join(__dirname, "styleguide/Views/**/*.blade.php"),
            path.join(__dirname, "factories/**/*.php"),
            path.join(__dirname, "resources/js/**/*.js"),
            path.join(__dirname, "node_modules/slideout/dist/slideout.js"),
            path.join(__dirname, "node_modules/flickity/dist/flickity.pkgd.js")
        ],
        extensions: ['html', 'js', 'php', 'vue'],
        whitelistPatterns: [/icon-/, /flickity-/, /mediabox-/, /at-/]
    })
   .sourceMaps()
   .options({
        processCssUrls: false,
        postCss: [
            tailwindcss('./tailwind.js'),
            require('autoprefixer')
        ]
    })
   .browserSync({
        proxy: 'https://' + package.name + '.wayne.local',
        open: false,
        files: [
            'app/**/*.php',
            'resources/views/**/*.php',
            'public/_resources/js/**/*.js',
            'public/_resources/css/**/*.css'
        ],
        watchOptions: {
            usePolling: true,
            interval: 500
        }
    });

// Create the _static symlink
fs.symlink(
    path.resolve('./storage/app/public'),
    path.resolve('./public/_static'),
    function (err) { err != null && err.errno != -17 ? console.log(err) : console.log("Done."); }
);

config = {
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            enforce: 'pre',
            use: [{
                loader: 'eslint-loader'
            }],
        }],
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
                eslint: {
                    configFile: path.join(__dirname, '.eslintrc'),
                },
            },
        }),
        new CopyWebpackPlugin([
            {
                from: 'node_modules/@waynestate/wsuheader/dist/header.html',
                to: path.resolve('resources/views/components/header.blade.php'),
            },
            {
                from: 'node_modules/@waynestate/wsufooter/dist/footer.html',
                to: path.resolve('resources/views/components/footer.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-404/dist/404.php',
                to: path.resolve('resources/views/errors/404.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-403/dist/403.php',
                to: path.resolve('resources/views/errors/403.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-500/dist/500.php',
                to: path.resolve('resources/views/errors/500.blade.php'),
            },
            {
                from: 'hooks',
                to: path.resolve('.git/hooks'),
            }
        ])
    ]
}

if (mix.inProduction()) {
    // Version the CSS for cache busting
    mix.version();
}

// Override webpack configuration
mix.webpackConfig(config);
<commit_msg>Search javascript files instead of whitelisting them incase it finds more CSS to keep<commit_after>let webpack = require('webpack');
let mix = require('laravel-mix');
let fs = require('fs');
let path = require('path');
let exec = require('child_process').exec;
let package = JSON.parse(fs.readFileSync('./package.json'));
let CopyWebpackPlugin = require('copy-webpack-plugin');
let purge = require('laravel-mix-purgecss');
let tailwindcss = require('tailwindcss');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for your application, as well as bundling up your JS files.
 |
 */

// Error Files
mix.copy([
        'vendor/waynestate/error-404/dist/404.css',
        'vendor/waynestate/error-404/dist/404.css.map',
        'vendor/waynestate/error-403/dist/403.css',
        'vendor/waynestate/error-403/dist/403.css.map',
        'vendor/waynestate/error-500/dist/500.css',
        'vendor/waynestate/error-500/dist/500.css.map'
    ], 'public/_resources/css')

    // Header files
    .copy([
        'vendor/waynestate/error-404/dist/404.png',
        'vendor/waynestate/error-403/dist/403.png',
        'vendor/waynestate/error-500/dist/500.png'
    ], 'public/_resources/images')

    // Fonts
    .copy([
        'resources/fonts/**/*'
    ], 'public/_resources/fonts')

    // Images
    .copy([
        'resources/images/**/*.jpg',
        'resources/images/**/*.gif',
        'resources/images/**/*.png',
        'resources/images/**/*.svg'
    ], 'public/_resources/images');

// Compile assets and setup browersync
mix.js('resources/js/main.js', 'public/_resources/js')
   .sass('resources/scss/main.scss', 'public/_resources/css')
   .purgeCss({
        globs: [
            path.join(__dirname, "resources/views/**/*.blade.php"),
            path.join(__dirname, "styleguide/Views/**/*.blade.php"),
            path.join(__dirname, "factories/**/*.php"),
            path.join(__dirname, "resources/js/**/*.js"),
            path.join(__dirname, "node_modules/slideout/dist/slideout.js"),
            path.join(__dirname, "node_modules/flickity/dist/flickity.pkgd.js"),
            path.join(__dirname, "node_modules/mediabox/dist/mediabox.js")
        ],
        extensions: ['html', 'js', 'php', 'vue'],
        whitelistPatterns: [/at-/]
    })
   .sourceMaps()
   .options({
        processCssUrls: false,
        postCss: [
            tailwindcss('./tailwind.js'),
            require('autoprefixer')
        ]
    })
   .browserSync({
        proxy: 'https://' + package.name + '.wayne.local',
        open: false,
        files: [
            'app/**/*.php',
            'resources/views/**/*.php',
            'public/_resources/js/**/*.js',
            'public/_resources/css/**/*.css'
        ],
        watchOptions: {
            usePolling: true,
            interval: 500
        }
    });

// Create the _static symlink
fs.symlink(
    path.resolve('./storage/app/public'),
    path.resolve('./public/_static'),
    function (err) { err != null && err.errno != -17 ? console.log(err) : console.log("Done."); }
);

config = {
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            enforce: 'pre',
            use: [{
                loader: 'eslint-loader'
            }],
        }],
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
                eslint: {
                    configFile: path.join(__dirname, '.eslintrc'),
                },
            },
        }),
        new CopyWebpackPlugin([
            {
                from: 'node_modules/@waynestate/wsuheader/dist/header.html',
                to: path.resolve('resources/views/components/header.blade.php'),
            },
            {
                from: 'node_modules/@waynestate/wsufooter/dist/footer.html',
                to: path.resolve('resources/views/components/footer.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-404/dist/404.php',
                to: path.resolve('resources/views/errors/404.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-403/dist/403.php',
                to: path.resolve('resources/views/errors/403.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-500/dist/500.php',
                to: path.resolve('resources/views/errors/500.blade.php'),
            },
            {
                from: 'hooks',
                to: path.resolve('.git/hooks'),
            }
        ])
    ]
}

if (mix.inProduction()) {
    // Version the CSS for cache busting
    mix.version();
}

// Override webpack configuration
mix.webpackConfig(config);
<|endoftext|>


<commit_before>var http = require('http'),
    fs = require("fs"),
    zip = require("node-native-zip"),
    less = require("less"),
    util = require("util"),
    http = require('http'),
    qs = require('querystring'),
    resolvePath = require('path').resolve;

http.createServer(function (req, res) {
    res.writeHead(200, {
            'Content-Type': 'application/zip',
            'Content-Disposition': 'attachment; filename=bootstrap.zip'
    });
   var data = '';
   req.on("data", function(chunk) {
        data += chunk;
    });
    req.on("end", function() {
        var post = qs.parse(data);
        archive = new BootstrapZipBuilder();
        for (var variable in post) {
            if (variable[0] == "@"){
                archive.setVariable(variable, post[variable])
            }
        }

        post.css = (post.css.constructor == Array ? post.css : [post.css])

        post.js = (post.js.constructor == Array ? post.js : [post.js])

        for(var i=0; i<post.css.length; i++){
            archive.addLessCss(post.css[i])
        }
        for(var i=0; i<post.js.length; i++) {
              archive.addJavascript(post.js[i])
          }

        var buffer = archive.toBuffer();
        res.write(buffer);
        res.end();
    });

}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

BootstrapZipBuilder = function(){
    this.pathBootstrap = resolvePath(__dirname + '/../../');
    this.variables = fs.readFileSync( this.pathBootstrap + '/lib/variables.less', 'utf-8' );
    this.mixins = fs.readFileSync( this.pathBootstrap + '/lib/mixins.less', 'utf-8' );
}
BootstrapZipBuilder.prototype = new zip()
BootstrapZipBuilder.prototype.addLessCss = function(name){
    var that = this;
    var content = this.mixins + this.variables + this.readCSSLess(name);
    parser.parse(content, function (e, tree) {
        var css = tree.toCSS({ compress: false })
        that.add(name + '.css', new Buffer(css, "utf8"));
    });
}
BootstrapZipBuilder.prototype.addJavascript = function(name){
    var content = this.readJavascript(name);
    this.add( name, new Buffer(content, "utf8") );
}
BootstrapZipBuilder.prototype.setVariable = function(name, value){
        this.variaveis += ( name + ": " + value + ";\n" );
}
BootstrapZipBuilder.prototype.readJavascript = function(name){
    return fs.readFileSync( this.pathBootstrap + '/js/'+ name, 'utf-8' );
}
BootstrapZipBuilder.prototype.readCSSLess = function(name){
    return fs.readFileSync( this.pathBootstrap + '/lib/'+ name + '.less', 'utf-8' );
}

var parser = new(less.Parser)({
        paths: [resolvePath(__dirname + '/../../lib')]
})
<commit_msg>fixing bug that only generate zip if some of css or js is checked<commit_after>var http = require('http'),
    fs = require("fs"),
    zip = require("node-native-zip"),
    less = require("less"),
    util = require("util"),
    http = require('http'),
    qs = require('querystring'),
    resolvePath = require('path').resolve;

http.createServer(function (req, res) {
    res.writeHead(200, {
            'Content-Type': 'application/zip',
            'Content-Disposition': 'attachment; filename=bootstrap.zip'
    });
   var data = '';
   req.on("data", function(chunk) {
        data += chunk;
    });
    req.on("end", function() {
        var post = qs.parse(data);
        archive = new BootstrapZipBuilder();
        for (var variable in post) {
            if (variable[0] == "@"){
                archive.setVariable(variable, post[variable])
            }
        }

        if(post.css) {
          post.css = (post.css.constructor == Array ? post.css : [post.css])
        }

        if(post.js) {
          post.js = (post.js.constructor == Array ? post.js : [post.js])
        }

        for(var i=0; i<post.css.length; i++){
            archive.addLessCss(post.css[i])
        }

        for(var i=0; i<post.js.length; i++) {
              archive.addJavascript(post.js[i])
          }

        var buffer = archive.toBuffer();
        res.write(buffer);
        res.end();
    });

}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

BootstrapZipBuilder = function(){
    this.pathBootstrap = resolvePath(__dirname + '/../../');
    this.variables = fs.readFileSync( this.pathBootstrap + '/lib/variables.less', 'utf-8' );
    this.mixins = fs.readFileSync( this.pathBootstrap + '/lib/mixins.less', 'utf-8' );
}
BootstrapZipBuilder.prototype = new zip()
BootstrapZipBuilder.prototype.addLessCss = function(name){
    var that = this;
    var content = this.mixins + this.variables + this.readCSSLess(name);
    parser.parse(content, function (e, tree) {
        var css = tree.toCSS({ compress: false })
        that.add(name + '.css', new Buffer(css, "utf8"));
    });
}
BootstrapZipBuilder.prototype.addJavascript = function(name){
    var content = this.readJavascript(name);
    this.add( name, new Buffer(content, "utf8") );
}
BootstrapZipBuilder.prototype.setVariable = function(name, value){
        this.variaveis += ( name + ": " + value + ";\n" );
}
BootstrapZipBuilder.prototype.readJavascript = function(name){
    return fs.readFileSync( this.pathBootstrap + '/js/'+ name, 'utf-8' );
}
BootstrapZipBuilder.prototype.readCSSLess = function(name){
    return fs.readFileSync( this.pathBootstrap + '/lib/'+ name + '.less', 'utf-8' );
}

var parser = new(less.Parser)({
        paths: [resolvePath(__dirname + '/../../lib')]
})
<|endoftext|>


<commit_before>function smf_fileUpload(oOptions)
{
	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		dataType: 'json',
		dropZone: $('.drop_zone'),
		singleFileUploads:false,
		forceIframeTransport: false,
		autoUpload: false,
		paramName: 'attachment[]',
		acceptFileTypes: /(\.|\/)(doc|gif|jpg|mpg|pdf|png|txt|zip)$/i,
		maxFileSize: 128000, // 128 KB
		disableImageResize: /Android(?!.*Chrome)|Opera/
			.test(window.navigator.userAgent),
		previewMaxWidth: 100,
		previewMaxHeight: 100,
		previewCrop: false,
		maxNumberOfFiles: 10,
		limitMultiFileUploads: 10,
		limitMultiFileUploadSize: 1000000,
		smf_mainDiv: '#fileupload',
		smf_containerDiv: '#postAttachment',
		smf_text: {}
	};

	$.extend(true, dOptions, oOptions);

	if ($.support.fileInput) {
		$(dOptions.smf_mainDiv).prop('multiple', true);
		dOptions.dropZone.find('h3').show();
	}

	var uploadButton = $('')
		.addClass('button_submit uploadButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.upload)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $('#attach_holder_' + data.uniqueID);

			// Show the progress bar.
			node.find('.progressBar').fadeIn();

			data.instance.submit();
			$this.remove();
		}),
		cancelButton = $('')
		.addClass('button_submit cancelButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancel)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.instance.context);

			// Gotta remove this from the number of files.
			--numberOfFiles;

			// And remove this file's size from the total.
			totalSize = totalSize - data.currentFile.size;

			// Need to remove this entry from the track array
			fileUpload.track = $.grep(fileUpload.track,
				function(o,i) { return o === data.index; },
			true);

			// And actually remove the button and the node.
			$this.remove();
			data.currentNode.fadeOut('slow', function() {
				data.currentNode.remove();
				data.instance.abort();
			});
		}),
	deleteButton = $('')
		.addClass('button_submit deleteButton you_sure')
		.prop('disabled', false)
		.text(dOptions.smf_text.deleteAttach)
		.on('click', function (e) {

			var $this = $(this),
				mainData = $this.data(),
				node = $(mainData.context);

			// Let the server know you want to delete the file you just recently uploaded...
			$.ajax({
				url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ mainData.currentFile['attachID'] +';' + smf_session_var + '=' + smf_session_id,
				type: 'GET',
				dataType: 'json',
				success: function (data, textStatus, xhr) {
					// Gotta remove this from the number of files.
					--numberOfFiles;

					// And remove this file's size from the total.
					totalSize = totalSize - mainData.currentFile['size'];

					// Replace the delete button with the "cancel" one.
					$this.replaceWith(cancelButton.clone(true).data(mainData));

					// Don't remove the entire node, just leave a message.
					node.find('.file_details').fadeOut('slow', function() {
						node.find('.file_details').text(dOptions.smf_text.attachDeleted);
						node.find('.file_details').fadeIn('slow', function() {
						});
					});

					// Remove the BBC tag.
					node.find('.file_info').empty();

					// Lastly, abort the whole thing.
					mainData.abort();
				},
				error: function (xhr, textStatus, errorThrown) {
					// Lastly, abort the whole thing.
					mainData.abort();

					node
						.find('.file_info')
						.append($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

					node.removeClass('descbox').addClass('errorbox');
					node.find('.uploadButton').remove();
				}
			});
		}),
	insertBBC = $('')
		.addClass('button_submit insertBBC')
		.prop('disabled', false)
		.text(dOptions.smf_text.insertBBC)
		.on('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Get the text field value.
			oTag = node.find('input[name=attachBBC]').val();

			$('#' + oEditorID).data('sceditor').sourceEditorInsertText(oTag);
		}),
	uploadAll = $('')
		.addClass('button_submit uploadAllButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.uploadAll)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data();

			// Show the progress bar.
			data.context.find('.progressBar').fadeIn();

			data.submit();
		}),
	cancelAll = $('')
		.addClass('button_submit cancelAllButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancelAll)
		.on('click', function (e) {
			e.preventDefault();

			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Gotta remove everything.
			numberOfFiles = 0;

			// And this stuff too!.
			totalSize = 0;

			// Reset!
			fileIndicator = false;

			$(fileUpload.track).each(function(index, i) {

				$('#attach_holder_' + i).fadeOut('slow', function() {
					$('#attach_holder_' + i).remove();
				});
			});

			// Remove both upload and cancel all buttons.
			$('.cancelAllButton').fadeOut('slow', function() {
				$(this).remove();
			});

			$('.uploadAllButton').fadeOut('slow', function() {
				$(this).remove();
			});

			// Reset the track array too!
			fileUpload.track = [];

			// Finally, abort all the things!
			data.abort();
		}),
	numberOfTimes = 0,
	numberOfFiles = 0,
	totalSize = 0,
	fileIndicator = false,
	uniqueID = 0

	fileUpload =  $(dOptions.smf_mainDiv).fileupload(dOptions)
		.on('fileuploadadd', function (e, data) {

			// Track the number of times this event has been fired.
			++numberOfTimes;

			// Create a master and empty div.
			data.context = $(dOptions.smf_containerDiv);

			// Show some general controls.
			if (!fileIndicator){
				// Keep track of each unique file added
				fileUpload.track = [];
			}

			// Append the file.
			$.each(data.files, function (index, file) {

				// Set a unique identifier for this file.
				++uniqueID;

				var node = $('
').addClass('attach_holder smalltext')
				.attr('id', 'attach_holder_' + uniqueID)
				.html('

');

				// Hide the progress bar, we don't want to show it just yet!
				node.find('.progressBar').hide();

				node.find('.file_details')
						.prepend($('
').text(file.name + ' (' + Math.round(file.size / 1024) + ' KB)'));

				// Need to pass some stuff to the upload and cancel buttons.
				toButtons = {'uniqueID': uniqueID, 'currentFile': file, 'currentNode': node, 'instance': data};

				// Append the current node info so it would be easier for the buttons to target it.
				node.find('.file_buttons')
						.append(cancelButton.clone(true).data(toButtons))
						.append(uploadButton.clone(true).data(toButtons));

				node.appendTo(data.context);
				fileUpload.track.push(uniqueID);
				data.files[index]['uniqueID'] = uniqueID;
			});

			// Show some general controls.
			if (!fileIndicator){
				fileIndicator = true;

				$('.attachControl')
					.append(cancelAll.clone(true).data(data))
					.append(uploadAll.clone(true).data(data));
			}
		})
		.on('fileuploadprocessalways', function (e, data) {
			$.each(data.files, function (index, file) {
				var node = $('#attach_holder_' + file.uniqueID);

				// Track the file size.
				totalSize = totalSize + file.size;

				// Show a nice preview.
				if (file.preview) {
					node
						.prepend($('
').prepend(file.preview));
				}

				if (file.error || numberOfFiles >= dOptions.maxNumberOfFiles || totalSize >= dOptions.limitMultiFileUploadSize) {
					// There isn't an error with the actual file, must be something else then!
					if (!file.error && numberOfFiles >= dOptions.maxNumberOfFiles)
						file.error = dOptions.messages.maxNumberOfFiles;

					// You reached the uploads total size.
					else if (totalSize >= dOptions.limitMultiFileUploadSize){
						file.error = dOptions.messages.maxTotalSize
							.replace('{currentTotal}', smf_fileUpload_bytesToSize(dOptions.limitMultiFileUploadSize))
							.replace('{currentRemain}', smf_fileUpload_bytesToSize(totalSize));
					}

					// Cancel the current upload.
					node
						.find('.file_info')
						.html($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

					node.removeClass('descbox').addClass('errorbox');
					data.abort();
				}

				// The file was added.
				if (index + 1 === data.files.length) {
					// "un-disable" the upload button :P
					data.context.find('.uploadButton')
						.prop('disabled', !!data.files.error);

					// The file has been appended, lets keep track of it!
					++numberOfFiles;

					// append some text here to tell the user what to do, hit Upload or hit Cancel...
					// or add some other indication that the file passed the client tests.
				}
			});
		})
		.on('fileuploaddone', function (e, data) {

			// Check out the general errors first...
			if (data.result.generalErrors){

				// Show the big fat generic error list!
				genericErrors = $('#attachGenericErrors ul');
				$.each(data.result.generalErrors, function (index, error) {
					genericErrors.append('
'+ error + '
');
				});

				genericErrors.fadeIn();
			}

			else{
				$.each(data.result.files, function (index, file) {

					// Gotta find the right node.
					$.each(data.files, function (dataIndex, dataFile) {
						if (dataFile.name == file.name && dataFile.size == file.size){
							var node = $('#attach_holder_' + dataFile.uniqueID);
						}
					});

					var node = $(data.context.children('.attach_holder')[index]);

					// Hide the progress bar.
					node.find('.progressBar').fadeOut(function() {
						// Show the brand new attach ID bbc tag.
						if (file.attachID) {
							var bbcTag = $('
').append(dOptions.smf_text.insertAttach + '<input type="text" name="attachBBC" value="[attach]' + file.attachID + '[/attach]" readonly>');

							// Replace the cancel button with a lovely "Delete" one.
							node.find('.cancelButton').replaceWith(deleteButton.clone(true).data(data));

							node
								.find('.file_info')
								.append(bbcTag);
							node
								.find('.file_buttons')
								.prepend(insertBBC.clone(true).data(data));

							// Append the current node/file to make it easier to handle it.
							data.currentNode = node;
							data.currentFile = file;
						}

						// Nope!
						else if (file.errors) {
							var errors = $('
').html('
');

							$.each(data.result.error, function (index, singleError) {
								errors.append('
' + singleError + '
');
							});

							// Close the ul
							errors.append('
');

							node
								.find('.file_info')
								.append(errors);

							node.removeClass('descbox').addClass('errorbox');
						}
					});
				});
			}
		})
		.on('fileuploadprogress', function (e, data) {
			data.context.find('.uploadButton')
				.text(dOptions.smf_text.processing).fadeOut('slow', function() {
					data.context.find('.uploadButton').remove();
				});

			var progress = parseInt(data.loaded / data.total * 100, 10);
			data.context.find('.progressBar').children().css(
				'width',
				progress + '%'
			);
		});
}

function smf_fileUpload_bytesToSize(bytes) {

	if(typeof bytes == 'undefined' || bytes == 0){
		return 0;
	}

	var k = 1000; // change to 1024 for binary stuff
	var i = Math.floor(Math.log(bytes) / Math.log(k));
	return (bytes / Math.pow(k, i)).toPrecision(3);
}

$(function() {
	if (typeof oEditorID !== 'undefined'){
		$('.editeIinsertBBC').on('click', function() {
				// Get the text field value.
				var oValue = $(this).data('attach'),
					oTag = $('input[name=editedAttachBBC_'+ oValue +']').val();

				$('#' + oEditorID).data('sceditor').sourceEditorInsertText(oTag);
		});
	}

	// Since people will inevitably drop their files outside the drop area...
	window.addEventListener('dragover', function(e){
		e = e || event;
		e.preventDefault();
	},false);
	window.addEventListener('drop', function(e){
		e = e || event;
		e.preventDefault();
	},false);
});
<commit_msg>eed to remove this entry from the track array, need to get the correct index to do so<commit_after>function smf_fileUpload(oOptions)
{
	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		dataType: 'json',
		dropZone: $('.drop_zone'),
		singleFileUploads:false,
		forceIframeTransport: false,
		autoUpload: false,
		paramName: 'attachment[]',
		acceptFileTypes: /(\.|\/)(doc|gif|jpg|mpg|pdf|png|txt|zip)$/i,
		maxFileSize: 128000, // 128 KB
		disableImageResize: /Android(?!.*Chrome)|Opera/
			.test(window.navigator.userAgent),
		previewMaxWidth: 100,
		previewMaxHeight: 100,
		previewCrop: false,
		maxNumberOfFiles: 10,
		limitMultiFileUploads: 10,
		limitMultiFileUploadSize: 1000000,
		smf_mainDiv: '#fileupload',
		smf_containerDiv: '#postAttachment',
		smf_text: {}
	};

	$.extend(true, dOptions, oOptions);

	if ($.support.fileInput) {
		$(dOptions.smf_mainDiv).prop('multiple', true);
		dOptions.dropZone.find('h3').show();
	}

	var uploadButton = $('')
		.addClass('button_submit uploadButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.upload)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $('#attach_holder_' + data.uniqueID);

			// Show the progress bar.
			node.find('.progressBar').fadeIn();

			data.instance.submit();
			$this.remove();
		}),
		cancelButton = $('')
		.addClass('button_submit cancelButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancel)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.instance.context);

			// Gotta remove this from the number of files.
			--numberOfFiles;

			// And remove this file's size from the total.
			totalSize = totalSize - data.currentFile.size;

			// Need to remove this entry from the track array, need to get the correct index to do so.
			$.each(fileUpload.track, function (index, file) {
				if(fileUpload.track[index] === data.uniqueID) {
					fileUpload.track.splice(index, 1);
				}
			});

			// And actually remove the button and the node.
			$this.remove();
			data.currentNode.fadeOut('slow', function() {});
			data.currentNode.remove();
			data.instance.abort();
		}),
	deleteButton = $('')
		.addClass('button_submit deleteButton you_sure')
		.prop('disabled', false)
		.text(dOptions.smf_text.deleteAttach)
		.on('click', function (e) {

			var $this = $(this),
				mainData = $this.data(),
				node = $(mainData.context);

			// Let the server know you want to delete the file you just recently uploaded...
			$.ajax({
				url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ mainData.currentFile['attachID'] +';' + smf_session_var + '=' + smf_session_id,
				type: 'GET',
				dataType: 'json',
				success: function (data, textStatus, xhr) {
					// Gotta remove this from the number of files.
					--numberOfFiles;

					// And remove this file's size from the total.
					totalSize = totalSize - mainData.currentFile['size'];

					// Replace the delete button with the "cancel" one.
					$this.replaceWith(cancelButton.clone(true).data(mainData));

					// Don't remove the entire node, just leave a message.
					node.find('.file_details').fadeOut('slow', function() {
						node.find('.file_details').text(dOptions.smf_text.attachDeleted);
						node.find('.file_details').fadeIn('slow', function() {
						});
					});

					// Remove the BBC tag.
					node.find('.file_info').empty();

					// Lastly, abort the whole thing.
					mainData.abort();
				},
				error: function (xhr, textStatus, errorThrown) {
					// Lastly, abort the whole thing.
					mainData.abort();

					node
						.find('.file_info')
						.append($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

					node.removeClass('descbox').addClass('errorbox');
					node.find('.uploadButton').remove();
				}
			});
		}),
	insertBBC = $('')
		.addClass('button_submit insertBBC')
		.prop('disabled', false)
		.text(dOptions.smf_text.insertBBC)
		.on('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Get the text field value.
			oTag = node.find('input[name=attachBBC]').val();

			$('#' + oEditorID).data('sceditor').sourceEditorInsertText(oTag);
		}),
	uploadAll = $('')
		.addClass('button_submit uploadAllButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.uploadAll)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data();

			// Show the progress bar.
			data.context.find('.progressBar').fadeIn();

			data.submit();
		}),
	cancelAll = $('')
		.addClass('button_submit cancelAllButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancelAll)
		.on('click', function (e) {
			e.preventDefault();

			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Gotta remove everything.
			numberOfFiles = 0;

			// And this stuff too!.
			totalSize = 0;

			// Reset!
			fileIndicator = false;

			$(fileUpload.track).each(function(index, i) {

				$('#attach_holder_' + i).fadeOut('slow', function() {
					$('#attach_holder_' + i).remove();
				});
			});

			// Remove both upload and cancel all buttons.
			$('.cancelAllButton').fadeOut('slow', function() {
				$(this).remove();
			});

			$('.uploadAllButton').fadeOut('slow', function() {
				$(this).remove();
			});

			// Reset the track array too!
			fileUpload.track = [];

			// Finally, abort all the things!
			data.abort();
		}),
	numberOfTimes = 0,
	numberOfFiles = 0,
	totalSize = 0,
	fileIndicator = false,
	uniqueID = 0

	fileUpload =  $(dOptions.smf_mainDiv).fileupload(dOptions)
		.on('fileuploadadd', function (e, data) {

			// Track the number of times this event has been fired.
			++numberOfTimes;

			// Create a master and empty div.
			data.context = $(dOptions.smf_containerDiv);

			// Show some general controls.
			if (!fileIndicator){
				// Keep track of each unique file added
				fileUpload.track = [];
			}

			// Append the file.
			$.each(data.files, function (index, file) {

				// Set a unique identifier for this file.
				++uniqueID;

				var node = $('
').addClass('attach_holder smalltext')
				.attr('id', 'attach_holder_' + uniqueID)
				.html('

');

				// Hide the progress bar, we don't want to show it just yet!
				node.find('.progressBar').hide();

				node.find('.file_details')
						.prepend($('
').text(file.name + ' (' + Math.round(file.size / 1024) + ' KB)'));

				// Need to pass some stuff to the upload and cancel buttons.
				toButtons = {'uniqueID': uniqueID, 'currentFile': file, 'currentNode': node, 'instance': data};

				// Append the current node info so it would be easier for the buttons to target it.
				node.find('.file_buttons')
						.append(cancelButton.clone(true).data(toButtons))
						.append(uploadButton.clone(true).data(toButtons));

				node.appendTo(data.context);
				fileUpload.track.push(uniqueID);
				data.files[index]['uniqueID'] = uniqueID;
			});

			// Show some general controls.
			if (!fileIndicator){
				fileIndicator = true;

				$('.attachControl')
					.append(cancelAll.clone(true).data(data))
					.append(uploadAll.clone(true).data(data));
			}
		})
		.on('fileuploadprocessalways', function (e, data) {
			$.each(data.files, function (index, file) {
				var node = $('#attach_holder_' + file.uniqueID);

				// Track the file size.
				totalSize = totalSize + file.size;

				// Show a nice preview.
				if (file.preview) {
					node
						.prepend($('
').prepend(file.preview));
				}

				if (file.error || numberOfFiles >= dOptions.maxNumberOfFiles || totalSize >= dOptions.limitMultiFileUploadSize) {
					// There isn't an error with the actual file, must be something else then!
					if (!file.error && numberOfFiles >= dOptions.maxNumberOfFiles)
						file.error = dOptions.messages.maxNumberOfFiles;

					// You reached the uploads total size.
					else if (totalSize >= dOptions.limitMultiFileUploadSize){
						file.error = dOptions.messages.maxTotalSize
							.replace('{currentTotal}', smf_fileUpload_bytesToSize(dOptions.limitMultiFileUploadSize))
							.replace('{currentRemain}', smf_fileUpload_bytesToSize(totalSize));
					}

					// Cancel the current upload.
					node
						.find('.file_info')
						.html($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

					node.removeClass('descbox').addClass('errorbox');
					data.abort();
				}

				// The file was added.
				if (index + 1 === data.files.length) {
					// "un-disable" the upload button :P
					data.context.find('.uploadButton')
						.prop('disabled', !!data.files.error);

					// The file has been appended, lets keep track of it!
					++numberOfFiles;

					// append some text here to tell the user what to do, hit Upload or hit Cancel...
					// or add some other indication that the file passed the client tests.
				}
			});
		})
		.on('fileuploaddone', function (e, data) {

			// Check out the general errors first...
			if (data.result.generalErrors){

				// Show the big fat generic error list!
				genericErrors = $('#attachGenericErrors ul');
				$.each(data.result.generalErrors, function (index, error) {
					genericErrors.append('
'+ error + '
');
				});

				genericErrors.fadeIn();
			}

			else{
				$.each(data.result.files, function (index, file) {

					// Gotta find the right node. Do note this is extremely fragile.
					$.each(data.files, function (dataIndex, dataFile) {
						if (dataFile.name == file.name && dataFile.size == file.size){
							var node = $('#attach_holder_' + dataFile.uniqueID);
						}
					});

					// Hide the progress bar.
					node.find('.progressBar').fadeOut(function() {
						// Show the brand new attach ID bbc tag.
						if (file.attachID) {
							var bbcTag = $('
').append(dOptions.smf_text.insertAttach + '<input type="text" name="attachBBC" value="[attach]' + file.attachID + '[/attach]" readonly>');

							// Replace the cancel button with a lovely "Delete" one.
							node.find('.cancelButton').replaceWith(deleteButton.clone(true).data(data));

							node
								.find('.file_info')
								.append(bbcTag);
							node
								.find('.file_buttons')
								.prepend(insertBBC.clone(true).data(data));

							// Append the current node/file to make it easier to handle it.
							data.currentNode = node;
							data.currentFile = file;
						}

						// Nope!
						else if (file.errors) {
							var errors = $('
').html('
');

							$.each(data.result.error, function (index, singleError) {
								errors.append('
' + singleError + '
');
							});

							// Close the ul
							errors.append('
');

							node
								.find('.file_info')
								.append(errors);

							node.removeClass('descbox').addClass('errorbox');
						}
					});
				});
			}
		})
		.on('fileuploadprogress', function (e, data) {
			data.context.find('.uploadButton')
				.text(dOptions.smf_text.processing).fadeOut('slow', function() {
					data.context.find('.uploadButton').remove();
				});

			var progress = parseInt(data.loaded / data.total * 100, 10);
			data.context.find('.progressBar').children().css(
				'width',
				progress + '%'
			);
		});
}

function smf_fileUpload_bytesToSize(bytes) {

	if(typeof bytes == 'undefined' || bytes == 0){
		return 0;
	}

	var k = 1000; // change to 1024 for binary stuff
	var i = Math.floor(Math.log(bytes) / Math.log(k));
	return (bytes / Math.pow(k, i)).toPrecision(3);
}

$(function() {
	if (typeof oEditorID !== 'undefined'){
		$('.editeIinsertBBC').on('click', function() {
				// Get the text field value.
				var oValue = $(this).data('attach'),
					oTag = $('input[name=editedAttachBBC_'+ oValue +']').val();

				$('#' + oEditorID).data('sceditor').sourceEditorInsertText(oTag);
		});
	}

	// Since people will inevitably drop their files outside the drop area...
	window.addEventListener('dragover', function(e){
		e = e || event;
		e.preventDefault();
	},false);
	window.addEventListener('drop', function(e){
		e = e || event;
		e.preventDefault();
	},false);
});
<|endoftext|>