<commit_before>(function($, eucalyptus) {
  $.widget('eucalyptus.volume', $.eucalyptus.eucawidget, {
    options : { },
    tableWrapper : null,
    delDialog : null,
    // TODO: is _init() the right method to instantiate everything?
    _init : function() {
      var thisObj = this;
      var $tmpl = $('html body').find('.templates #volumeTblTmpl').clone();
      var $wrapper = $($tmpl.render($.i18n.map));
      this.element.add($wrapper);
      var $base_table = $wrapper.find('table');
      tableWrapper = $wrapper.eucatable({
        id : 'volumes', // user of this widget should customize these options,
        base_table : $base_table,
        dt_arg : {
          "bProcessing": true,
          "sAjaxSource": "../ec2?type=volume&Action=DescribeVolumes",
          "sAjaxDataProp": "results",
          "bAutoWidth" : false,
          "sPaginationType": "full_numbers",
          "sDom": '<"table_volumes_header"><"table-volume-filter">f<"clear"><"table_volumes_top">rt<"table-volumes-legend">p<"clear">',
          "aoColumns": [
            {
              "bSortable": false,
              "fnRender": function(oObj) { return '<input type="checkbox"/>' },
              "sWidth": "20px",
            },
            { "mDataProp": "id" },
            {
              "fnRender": function(oObj) { s = (oObj.aData.status == 'in-use') ? oObj.aData.attach_data.status : oObj.aData.status; return '

'; },
              "sWidth": "20px",
              "bSearchable": false,
              "iDataSort": 8, // sort on hiden status column
            },
            { "mDataProp": "size" },
            { "mDataProp": "attach_data.instance_id" },
            { "mDataProp": "snapshot_id" },
            { "mDataProp": "zone" },
            { "mDataProp": "create_time" },
            {
              "bVisible": false,
              "fnRender": function(oObj) { s = (oObj.aData.status == 'in-use') ? oObj.aData.attach_data.status : oObj.aData.status; return s; }
            }
          ],
          "fnDrawCallback": function( oSettings ) {
             $('#table_volumes_count').html(oSettings.fnRecordsDisplay());
          }
        },
        header_title : volume_h_title,
        search_refresh : search_refresh,
        txt_create : volume_create,
        txt_found : volume_found,
        menu_text : table_menu_main_action,
        menu_actions : { delete: [table_menu_delete_action, function (args) { thisObj.deleteAction(args) } ] },
        row_click : function (args) { thisObj.handleRowClick(args); }
      });
      tableWrapper.appendTo(this.element);

      //add filter to the table
      $tableFilter = $('div.table-volume-filter');
      $tableFilter.addClass('euca-table-filter');
      $tableFilter.append(
        $('').addClass("filter-label").html(volume_filter_label),
        $('<select>').attr('id', 'volumes-selector'));

      //TODO: add more states
      attachedStates = { 'attached':1, 'attaching':1 };
      detachedStates = { 'available':1 };

      filterOptions = ['all', 'attached', 'detached'];
      $sel = $tableFilter.find("#volumes-selector");
      for (o in filterOptions)
        $sel.append($('<option>').val(filterOptions[o]).text($.i18n.map['volume_selecter_' + filterOptions[o]]));

      $.fn.dataTableExt.afnFiltering.push(
	function( oSettings, aData, iDataIndex ) {
          // first check if this is called on a volumes table
          if (oSettings.sInstance != 'volumes')
            return true;
          selectorValue = $("#volumes-selector").val();
          switch (selectorValue) {
            case 'attached':
              return attachedStates[aData[8]] == 1;
              break;
            case 'detached':
              return detachedStates[aData[8]] == 1;
              break;
          }
          return true;
        }
      );

      // attach action
      $("#volumes-selector").change( function() { thisObj.reDrawTable() } );

      //add leged to the volumes table
      $tableLegend = $("div.table-volumes-legend");
      $tableLegend.append($('').addClass('volume-legend').html(volume_legend));
      //TODO: this might not work in all browsers
      statuses = [].concat(Object.keys(attachedStates),Object.keys(detachedStates));
      for (s in statuses)
        $tableLegend.append($('').addClass('volume-status-legend').addClass('volume-status-' + statuses[s]).html($.i18n.map['volume_state_' + statuses[s]]));

      $tmpl = $('html body').find('.templates #volumeDelDlgTmpl').clone();
      $del_dialog = $($tmpl.render($.i18n.map));

      this.delDialog = $del_dialog.eucadialog({
         id: 'volumes-delete',
         title: volume_dialog_del_title,
         buttons: {
           'delete': {text: volume_dialog_del_btn, click: function() { thisObj._deleteSelectedVolumes(); $del_dialog.dialog("close");}},
           'cancel': {text: volume_dialog_cancel_btn, focus:true, click: function() { $del_dialog.dialog("close");}}
         }
       });

      var createButtonId = 'volume-add-btn';
      $tmpl = $('html body').find('.templates #volumeAddDlgTmpl').clone();
      $add_dialog = $($tmpl.render($.i18n.map));
/*
      // add custom event handler to dialog elements
      // when calling eucadialog, the buttons should have domid to attach the specific domid that's used by event handler written here
      $add_dialog.find('#key-name').keypress( function(e){
        var $createButton = $('#'+createButtonId);
        if( e.which === RETURN_KEY_CODE || e.which === RETURN_MAC_KEY_CODE )
           $createButton.trigger('click');
        else if ( e.which === 0 ) {
        } else if ( e.which === BACKSPACE_KEY_CODE && $(this).val().length == 1 )
           $createButton.prop("disabled", true).addClass("ui-state-disabled");
        else if ( $(this).val().length == 0 )
           $createButton.prop("disabled", true).addClass("ui-state-disabled");
        else
           $createButton.prop("disabled", false).removeClass("ui-state-disabled");
      });
*/
      $add_dialog.eucadialog({
        id: 'volumes-add',
        title: volume_dialog_add_title,
        buttons: {
        // e.g., add : { domid: keys-add-btn, text: "Add new key", disabled: true, focus: true, click : function() { }, keypress : function() { }, ...}
        'create': { domid: createButtonId, text: volume_dialog_create_btn, disabled: true,  click: function() { $add_dialog.dialog("close"); }},
        'cancel': {text: volume_dialog_cancel_btn, focus:true, click: function() { $add_dialog.dialog("close");}}
      }});
    },

    _create : function() {
    },

    _destroy : function() {
    },

    reDrawTable : function() {
      tableWrapper.eucatable('reDrawTable');
    },

    handleRowClick : function(args) {
      count = tableWrapper.eucatable('countSelectedRows');
      if ( count == 0 )
        // disable menu
        tableWrapper.eucatable('deactivateMenu');
      else
        // enable delete menu
        tableWrapper.eucatable('activateMenu');
    },
/*
    _addKeyPair : function(keyName) {
      $.ajax({
        type:"GET",
        url:"/ec2?type=key&Action=CreateKeyPair",
        data:"_xsrf="+$.cookie('_xsrf') + "&KeyName=" + keyName,
        dataType:"json",
        async:"false",
        success:
        function(data, textStatus, jqXHR){
          if (data.results && data.results.material) {
            $.generateFile({
              filename    : keyName,
              content     : data.results.material,
              script      : '/support?Action=DownloadFile&_xsrf=' + $.cookie('_xsrf')
            });
            successNotification(keypair_create_success + ' ' + keyName);
            tableWrapper.eucatable('refreshTable');
          } else {
            errorNotification(keypair_create_error + ' ' + keyName);
          }
        },
        error:
        function(jqXHR, textStatus, errorThrown){
          errorNotification(keypair_delete_error + ' ' + keyName);
        }
      });
    },
*/
    _deleteSelectedVolumes : function () {
      var rowsToDelete = tableWrapper.eucatable('getAllSelectedRows');
      for ( i = 0; i<rowsToDelete.length; i++ ) {
        var volumeId = rowsToDelete[i];
        $.ajax({
          type:"GET",
          url:"/ec2?type=key&Action=DeleteVolume&VolumeId=" + volumeId,
          data:"_xsrf="+$.cookie('_xsrf'),
          dataType:"json",
          async:"true",
          success:
          (function(volumeId) {
            return function(data, textStatus, jqXHR){
              if ( data.results && data.results == true ) {
                successNotification(volume_delete_success + ' ' + volumeId);
                tableWrapper.eucatable('refreshTable');
              } else {
                errorNotification(volume_delete_error + ' ' + volumeId);
              }
           }
          })(volumeId),
          error:
          (function(volumeId) {
            return function(jqXHR, textStatus, errorThrown){
              errorNotification(volume_delete_error + ' ' + volumeId);
            }
          })(volumeId)
        });
      }
    },

    close: function() {
      this._super('close');
    },

    deleteAction : function(rowsToDelete) {
      //TODO: add hide menu

      if ( rowsToDelete.length > 0 ) {
        // show delete dialog box
        $deleteNames = this.delDialog.find("span.delete-names")
        $deleteNames.html('');
        for ( i = 0; i<rowsToDelete.length; i++ ) {
          t = escapeHTML(rowsToDelete[i]);
          $deleteNames.append(t).append("
");
        }
        this.delDialog.dialog('open');
      }
    }

  });
})(jQuery,
   window.eucalyptus ? window.eucalyptus : window.eucalyptus = {});
<commit_msg>Outpus creation time in browser format and timezone<commit_after>(function($, eucalyptus) {
  $.widget('eucalyptus.volume', $.eucalyptus.eucawidget, {
    options : { },
    tableWrapper : null,
    delDialog : null,
    // TODO: is _init() the right method to instantiate everything?
    _init : function() {
      var thisObj = this;
      var $tmpl = $('html body').find('.templates #volumeTblTmpl').clone();
      var $wrapper = $($tmpl.render($.i18n.map));
      this.element.add($wrapper);
      var $base_table = $wrapper.find('table');
      tableWrapper = $wrapper.eucatable({
        id : 'volumes', // user of this widget should customize these options,
        base_table : $base_table,
        dt_arg : {
          "bProcessing": true,
          "sAjaxSource": "../ec2?type=volume&Action=DescribeVolumes",
          "sAjaxDataProp": "results",
          "bAutoWidth" : false,
          "sPaginationType": "full_numbers",
          "sDom": '<"table_volumes_header"><"table-volume-filter">f<"clear"><"table_volumes_top">rt<"table-volumes-legend">p<"clear">',
          "aoColumns": [
            {
              "bSortable": false,
              "fnRender": function(oObj) { return '<input type="checkbox"/>' },
              "sWidth": "20px",
            },
            { "mDataProp": "id" },
            {
              "fnRender": function(oObj) { s = (oObj.aData.status == 'in-use') ? oObj.aData.attach_data.status : oObj.aData.status; return '

'; },
              "sWidth": "20px",
              "bSearchable": false,
              "iDataSort": 8, // sort on hiden status column
            },
            { "mDataProp": "size" },
            { "mDataProp": "attach_data.instance_id" },
            { "mDataProp": "snapshot_id" },
            { "mDataProp": "zone" },
            // output creation time in browser format and timezone
            { "fnRender": function(oObj) { d = new Date(oObj.aData.create_time); return d.toLocaleString(); } },
            {
              "bVisible": false,
              "fnRender": function(oObj) { s = (oObj.aData.status == 'in-use') ? oObj.aData.attach_data.status : oObj.aData.status; return s; }
            }
          ],
          "fnDrawCallback": function( oSettings ) {
             $('#table_volumes_count').html(oSettings.fnRecordsDisplay());
          }
        },
        header_title : volume_h_title,
        search_refresh : search_refresh,
        txt_create : volume_create,
        txt_found : volume_found,
        menu_text : table_menu_main_action,
        menu_actions : { delete: [table_menu_delete_action, function (args) { thisObj.deleteAction(args) } ] },
        row_click : function (args) { thisObj.handleRowClick(args); }
      });
      tableWrapper.appendTo(this.element);

      //add filter to the table
      $tableFilter = $('div.table-volume-filter');
      $tableFilter.addClass('euca-table-filter');
      $tableFilter.append(
        $('').addClass("filter-label").html(volume_filter_label),
        $('<select>').attr('id', 'volumes-selector'));

      //TODO: add more states
      attachedStates = { 'attached':1, 'attaching':1 };
      detachedStates = { 'available':1 };

      filterOptions = ['all', 'attached', 'detached'];
      $sel = $tableFilter.find("#volumes-selector");
      for (o in filterOptions)
        $sel.append($('<option>').val(filterOptions[o]).text($.i18n.map['volume_selecter_' + filterOptions[o]]));

      $.fn.dataTableExt.afnFiltering.push(
	function( oSettings, aData, iDataIndex ) {
          // first check if this is called on a volumes table
          if (oSettings.sInstance != 'volumes')
            return true;
          selectorValue = $("#volumes-selector").val();
          switch (selectorValue) {
            case 'attached':
              return attachedStates[aData[8]] == 1;
              break;
            case 'detached':
              return detachedStates[aData[8]] == 1;
              break;
          }
          return true;
        }
      );

      // attach action
      $("#volumes-selector").change( function() { thisObj.reDrawTable() } );

      //add leged to the volumes table
      $tableLegend = $("div.table-volumes-legend");
      $tableLegend.append($('').addClass('volume-legend').html(volume_legend));
      //TODO: this might not work in all browsers
      statuses = [].concat(Object.keys(attachedStates),Object.keys(detachedStates));
      for (s in statuses)
        $tableLegend.append($('').addClass('volume-status-legend').addClass('volume-status-' + statuses[s]).html($.i18n.map['volume_state_' + statuses[s]]));

      $tmpl = $('html body').find('.templates #volumeDelDlgTmpl').clone();
      $del_dialog = $($tmpl.render($.i18n.map));

      this.delDialog = $del_dialog.eucadialog({
         id: 'volumes-delete',
         title: volume_dialog_del_title,
         buttons: {
           'delete': {text: volume_dialog_del_btn, click: function() { thisObj._deleteSelectedVolumes(); $del_dialog.dialog("close");}},
           'cancel': {text: volume_dialog_cancel_btn, focus:true, click: function() { $del_dialog.dialog("close");}}
         }
       });

      var createButtonId = 'volume-add-btn';
      $tmpl = $('html body').find('.templates #volumeAddDlgTmpl').clone();
      $add_dialog = $($tmpl.render($.i18n.map));
/*
      // add custom event handler to dialog elements
      // when calling eucadialog, the buttons should have domid to attach the specific domid that's used by event handler written here
      $add_dialog.find('#key-name').keypress( function(e){
        var $createButton = $('#'+createButtonId);
        if( e.which === RETURN_KEY_CODE || e.which === RETURN_MAC_KEY_CODE )
           $createButton.trigger('click');
        else if ( e.which === 0 ) {
        } else if ( e.which === BACKSPACE_KEY_CODE && $(this).val().length == 1 )
           $createButton.prop("disabled", true).addClass("ui-state-disabled");
        else if ( $(this).val().length == 0 )
           $createButton.prop("disabled", true).addClass("ui-state-disabled");
        else
           $createButton.prop("disabled", false).removeClass("ui-state-disabled");
      });
*/
      $add_dialog.eucadialog({
        id: 'volumes-add',
        title: volume_dialog_add_title,
        buttons: {
        // e.g., add : { domid: keys-add-btn, text: "Add new key", disabled: true, focus: true, click : function() { }, keypress : function() { }, ...}
        'create': { domid: createButtonId, text: volume_dialog_create_btn, disabled: true,  click: function() { $add_dialog.dialog("close"); }},
        'cancel': {text: volume_dialog_cancel_btn, focus:true, click: function() { $add_dialog.dialog("close");}}
      }});
    },

    _create : function() {
    },

    _destroy : function() {
    },

    reDrawTable : function() {
      tableWrapper.eucatable('reDrawTable');
    },

    handleRowClick : function(args) {
      count = tableWrapper.eucatable('countSelectedRows');
      if ( count == 0 )
        // disable menu
        tableWrapper.eucatable('deactivateMenu');
      else
        // enable delete menu
        tableWrapper.eucatable('activateMenu');
    },
/*
    _addKeyPair : function(keyName) {
      $.ajax({
        type:"GET",
        url:"/ec2?type=key&Action=CreateKeyPair",
        data:"_xsrf="+$.cookie('_xsrf') + "&KeyName=" + keyName,
        dataType:"json",
        async:"false",
        success:
        function(data, textStatus, jqXHR){
          if (data.results && data.results.material) {
            $.generateFile({
              filename    : keyName,
              content     : data.results.material,
              script      : '/support?Action=DownloadFile&_xsrf=' + $.cookie('_xsrf')
            });
            successNotification(keypair_create_success + ' ' + keyName);
            tableWrapper.eucatable('refreshTable');
          } else {
            errorNotification(keypair_create_error + ' ' + keyName);
          }
        },
        error:
        function(jqXHR, textStatus, errorThrown){
          errorNotification(keypair_delete_error + ' ' + keyName);
        }
      });
    },
*/
    _deleteSelectedVolumes : function () {
      var rowsToDelete = tableWrapper.eucatable('getAllSelectedRows');
      for ( i = 0; i<rowsToDelete.length; i++ ) {
        var volumeId = rowsToDelete[i];
        $.ajax({
          type:"GET",
          url:"/ec2?type=key&Action=DeleteVolume&VolumeId=" + volumeId,
          data:"_xsrf="+$.cookie('_xsrf'),
          dataType:"json",
          async:"true",
          success:
          (function(volumeId) {
            return function(data, textStatus, jqXHR){
              if ( data.results && data.results == true ) {
                successNotification(volume_delete_success + ' ' + volumeId);
                tableWrapper.eucatable('refreshTable');
              } else {
                errorNotification(volume_delete_error + ' ' + volumeId);
              }
           }
          })(volumeId),
          error:
          (function(volumeId) {
            return function(jqXHR, textStatus, errorThrown){
              errorNotification(volume_delete_error + ' ' + volumeId);
            }
          })(volumeId)
        });
      }
    },

    close: function() {
      this._super('close');
    },

    deleteAction : function(rowsToDelete) {
      //TODO: add hide menu

      if ( rowsToDelete.length > 0 ) {
        // show delete dialog box
        $deleteNames = this.delDialog.find("span.delete-names")
        $deleteNames.html('');
        for ( i = 0; i<rowsToDelete.length; i++ ) {
          t = escapeHTML(rowsToDelete[i]);
          $deleteNames.append(t).append("
");
        }
        this.delDialog.dialog('open');
      }
    }

  });
})(jQuery,
   window.eucalyptus ? window.eucalyptus : window.eucalyptus = {});
<|endoftext|>


<commit_before>var open = false;
var redo = 10;
var cdraw = 0;
var socket = io();
var gameStarted = false;
function unlock() {
    open = true;
    document.getElementById('pop').style.display = "none";
    document.getElementById('overlay').innerHTML = "<button onclick='move(1)'>↑</button>
<button onclick='move(3)'>←</button><button> </button><button onclick='move(4)'>→</button>
<button onclick='move(2)'>↓</button>Position: " + x + "," + y;
}
var width, height, blocksx, blocksy, canvas, c;
var localgamecache = [];

function setUp() {
    c = document.getElementById('game'),
    canvas = c.getContext('2d');
    c.width = window.innerWidth;
    c.height = window.innerHeight;
    canvas.beginPath();
    canvas.rect(0, 0, window.innerWidth, window.innerHeight);
    canvas.fillStyle = "#1e1e1e";
    canvas.fill();

    width = window.innerWidth;
    height = window.innerHeight;
    document.getElementById("game").innerHTML = "";
    blocksx = Math.round(width / 40);
    blocksy = Math.round(height / 40);

    if (blocksx < 10 || blocksy < 10) {
        if (blocksx > blocksy) {
            redo = blocksy;
        }
        if (blocksy > blocksx) {
            redo = blocksx;
        }
    }
    var tw = 0;
    var th = 0;

    canvas.beginPath();
    canvas.rect(0, 0, 40, 40);
    canvas.fillStyle = "purple";
    canvas.fill();

    for (var i = 0; i < blocksy; i++) {
        for (var b = 0; b <= blocksx; b++) {
            canvas.beginPath();
            canvas.rect(tw, th, tw + 40, th + 40);
            canvas.fillStyle = "#1e1e1e";
            canvas.fill();
            tw = tw + 40;
        }
    }
}
setUp();

function navch(bar) {
    if (bar == 1) {
        document.getElementById("game-pos").innerHTML = "
Credits

Lead Developer
 DatOneLefty

Game Idea
Lexumus

Developers
-stache-

Links
Project GitHub
Project Gitter";
    }
    if (bar == 2) {
        document.getElementById("game-pos").innerHTML = "
How To Play
Use WASD or the arrow pad to move around, you will not automatically move

As you move, you claim territory. But, people can overclaim your territory though.

You cannot die and if you leave the game and come back, you will return to your exact position and have the same color
(Hit begin to start!)";
    }
    if (bar == 3) {
        document.getElementById("game-pos").innerHTML = "
Your info

X: " + x + "
Y: " + y + "

Your Color
";
    }
    if (bar == 4) {
        document.getElementById("game-pos").innerHTML = "
Settings

Transfer Account
Account Code: " + getCookie("id") + "
<input id='reslog'></input><button onclick='transfer()'>Sign In</button>
Alliance Code: " + color + "
<input id='alliance'></input><button onclick='alliance()'>Alliance</button>";
				if (alliances != []) {
					document.getElementById("game-pos").innerHTML += "
Your Alliances
";
				alliances.forEach(function(entry) {
						document.getElementById("game-pos").innerHTML += '  ';
				});
			}
    };

}

function alliance() {
	socket.emit("add-alliance", id + "," +document.getElementById("alliance").value);
}

function transfer() {
    socket.emit("check-account", document.getElementById("reslog").value);
}

socket.on("verified", function(msg) {
    if (msg == "1") {
        setCookie("id", document.getElementById("reslog").value, 365 * 5);
        location.reload();
    } else {
        alert("ID not known");
        setCookie("id", id, 365 * 5);
    }
});

function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    var expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}

function getCookie(cname) {
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

var id = "";
if (getCookie("id") == "") {
    socket.emit("new-user", "help");
} else {
    socket.emit("new-join", getCookie("id"));
    id = getCookie("id");
}
var x, y, color;
alliances = [];
var positions = [];
socket.on('info', function(msg) {
    var inf = msg.split("|");
    x = inf[0];
    y = inf[1];
    y = parseInt(y);
    x = parseInt(x);
    color = inf[2];
		console.log(inf[3]);
		alliances = JSON.parse(inf[3]);
    navch(3)
    document.getElementById("color").style.backgroundColor = color;
    document.getElementById("game-button").style.backgroundColor = color;
    document.getElementById("game-button").disabled = false;
      	socket.emit("req-map", x + "x" + y);
});

socket.on('new-info', function(msg) {
    setCookie("id", msg, 365 * 5);
    socket.emit("new-join", getCookie("id"));
    id = getCookie("id");
});

socket.on('leader', function(msg) {
    var lead = msg;
    var cache = "Top Players
";
    lead.forEach(function(entry) {
        cache = cache + '  ' + entry[0];
        if (entry[1] == color) {
            cache = cache + " YOU";
        }
        cache = cache + "
";
    });
    document.getElementById("leaderboard").innerHTML = cache;
});

function draw(x2, y2, scol) {
    var corner_x = Math.round(x - (blocksx / 2));
    var corner_y = Math.round(parseInt(y) + (blocksy / 2));
    var corner_x_r = Math.round(parseInt(x) + (blocksx / 2));
    var corner_y_r = Math.round(y - (blocksy / 2));
    if (x2 >= corner_x && x2 <= corner_x_r) {
        if (y2 <= corner_y && y2 >= corner_y_r) {
            var gx = corner_x - x2;
            var gy = corner_y - y2;
            if (gx < 0) {
                gx = -gx;
            }
            if (gy < 0) {
                gy = -gy;
            }
            gx = gx * 40;
            gy = gy * 40;
            canvas.beginPath();
            canvas.rect(gx, gy, 40, 40);
            canvas.fillStyle = scol;
            canvas.fill();
        }
    }

}

socket.on('new-claim', function(msg) {
  if (gameStarted == true) {
    msg = JSON.parse("[" + msg + "]");
    var m2 = msg[0] + "x" + msg[1];
    map_data = map_data.filter(function(item) {
        return (item[0] !== m2)
    })
    map_data = map_data.concat([
        [m2, msg[2]]
    ]);
if (msg[2] != color) {
	positions.filter(function(item) {
        return (item[2] !== msg[2])
    })

    positions = positions.concat([[msg[0], msg[1], msg[2]]]);
    draw(msg[0], msg[1], msg[2]);
    redraw();
}
  }
});

var map_data, backup;
socket.on('gmap', function(msg) {
    map_data = msg;
    gameStarted = true;
    redraw();

});

socket.on('alliance-made', function(msg) {
    alert("alliance made with " + msg);

});
function redraw() {
if (gameStarted == true) {

    cdraw++;
    if (cdraw > redo) {
        socket.emit("req-map", x + "x" + y);
        cdraw = 0;
    }
    var map = map_data;
    canvas.beginPath();
    canvas.rect(0, 0, window.innerWidth, window.innerHeight);
    canvas.fillStyle = "#1e1e1e";
    canvas.fill();
	 localgamecache.forEach(function(element) {
        piece = element[0].split("x");
        draw(piece[0], piece[1], element[1]);
    });

    map.forEach(function(element) {
        piece = element[0].split("x");
        draw(piece[0], piece[1], element[1]);
    });

	    positions.forEach(function(element) {
        draw(element[0], element[1], "#757575");
    });

    draw(x, y, "#757575");
}
}
function isClaimed(element) {
return JSON.stringify(element) == JSON.stringify([m2ca, cach_col]);
}
var cach_col, m2ca;

function update() {
    var t1 = new Date();
    t1 = t1.getTime();
    var m2 = x + "x" + y;
		m2ca = m2;
		var allow_overwrite = true;
alliances.forEach(function(element) {
	cach_col = element;
			if (map_data.findIndex(isClaimed) > -1) {
				allow_overwrite = false;
			}

		});
		if (allow_overwrite == true) {
    map_data = map_data.filter(function(item) {
        return (item[0] !== m2);
    });
    localgamecache = localgamecache.concat([
        [m2, color]
    ]);

    socket.emit("move", x + "," + y + "," + id);
	}
    redraw();

    var t2 = new Date();
    t2 = t2.getTime();
    var t3 = t2 - t1;
    document.getElementById('overlay').innerHTML = "<button onclick='move(1)'>↑</button>
<button onclick='move(3)'>←</button><button> </button><button onclick='move(4)'>→</button>
<button onclick='move(2)'>↓</button>Position: " + x + "," + y;
}

function tooFarError() {
    open = false;
    document.getElementById('game-button').innerHTML = "Continue";
    document.getElementById('pop').style.display = "table";
    document.getElementById("game-pos").innerHTML = "Oops!
You are not allowed to go past 1000 blocks, positive or negative, in any direction";
    if (x > 999) {
        x = 999;
    }
    if (x < -999) {
        x = -999;
    }
    if (y > 999) {
        y = 999;
    }
    if (y < -999) {
        y = -999;
    }
    update();
}

function move(d) {
    if (open == true) {

        if (d == 1) {
            y++;
            update();
        }
        if (d == 2) {
            y--;
            update();
        }
        if (d == 3) {
            x--;
            update();
        }
        if (d == 4) {
            x++;
            update();
        }
        if (x > 999 || x < -999 || y > 999 || y < -999) {
            tooFarError();
        }
    }

}

function checkKey(e) {
    e = e || window.event;
    var code = e.keyCode;
    if (code == 38) {
        move(1); // up
    }
    if (code == 40) {
        move(2); // down
    }
    if (code == 37) {
        move(3); // left
    }
    if (code == 39) {
        move(4); // right
    }

    if (code == 87) {
        move(1); // up
    }
    if (code == 83) {
        move(2); // down
    }
    if (code == 65) {
        move(3); // left
    }
    if (code == 68) {
        move(4); // right
    }
}
document.onkeydown = checkKey;
window.onresize = function() {
    setUp();
    redraw();
}
<commit_msg>oops again. online editing sucks<commit_after>var open = false;
var redo = 10;
var cdraw = 0;
var socket = io();
var gameStarted = false;
function unlock() {
    open = true;
    document.getElementById('pop').style.display = "none";
    document.getElementById('overlay').innerHTML = "<button onclick='move(1)'>↑</button>
<button onclick='move(3)'>←</button><button> </button><button onclick='move(4)'>→</button>
<button onclick='move(2)'>↓</button>Position: " + x + "," + y;
}
var width, height, blocksx, blocksy, canvas, c;
var localgamecache = [];

function setUp() {
    c = document.getElementById('game'),
    canvas = c.getContext('2d');
    c.width = window.innerWidth;
    c.height = window.innerHeight;
    canvas.beginPath();
    canvas.rect(0, 0, window.innerWidth, window.innerHeight);
    canvas.fillStyle = "#1e1e1e";
    canvas.fill();

    width = window.innerWidth;
    height = window.innerHeight;
    document.getElementById("game").innerHTML = "";
    blocksx = Math.round(width / 40);
    blocksy = Math.round(height / 40);

    if (blocksx < 10 || blocksy < 10) {
        if (blocksx > blocksy) {
            redo = blocksy;
        }
        if (blocksy > blocksx) {
            redo = blocksx;
        }
    }
    var tw = 0;
    var th = 0;

    canvas.beginPath();
    canvas.rect(0, 0, 40, 40);
    canvas.fillStyle = "purple";
    canvas.fill();

    for (var i = 0; i < blocksy; i++) {
        for (var b = 0; b <= blocksx; b++) {
            canvas.beginPath();
            canvas.rect(tw, th, tw + 40, th + 40);
            canvas.fillStyle = "#1e1e1e";
            canvas.fill();
            tw = tw + 40;
        }
    }
}
setUp();

function navch(bar) {
    if (bar == 1) {
        document.getElementById("game-pos").innerHTML = "
Credits

Lead Developer
 DatOneLefty

Game Idea
Lexumus

Developers
-stache-

Links
Project GitHub
Project Gitter";
    }
    if (bar == 2) {
        document.getElementById("game-pos").innerHTML = "
How To Play
Use WASD or the arrow pad to move around, you will not automatically move

As you move, you claim territory. But, people can overclaim your territory though.

You cannot die and if you leave the game and come back, you will return to your exact position and have the same color
(Hit begin to start!)";
    }
    if (bar == 3) {
        document.getElementById("game-pos").innerHTML = "
Your info

X: " + x + "
Y: " + y + "

Your Color
";
    }
    if (bar == 4) {
        document.getElementById("game-pos").innerHTML = "
Settings

Transfer Account
Account Code: " + getCookie("id") + "
<input id='reslog'></input><button onclick='transfer()'>Sign In</button>
Alliance Code: " + color + "
<input id='alliance'></input><button onclick='alliance()'>Alliance</button>";
				if (alliances != []) {
					document.getElementById("game-pos").innerHTML += "
Your Alliances
";
				alliances.forEach(function(entry) {
						document.getElementById("game-pos").innerHTML += '  ';
				});
			}
    };

}

function alliance() {
	socket.emit("add-alliance", id + "," +document.getElementById("alliance").value);
}

function transfer() {
    socket.emit("check-account", document.getElementById("reslog").value);
}

socket.on("verified", function(msg) {
    if (msg == "1") {
        setCookie("id", document.getElementById("reslog").value, 365 * 5);
        location.reload();
    } else {
        alert("ID not known");
        setCookie("id", id, 365 * 5);
    }
});

function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    var expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}

function getCookie(cname) {
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

var id = "";
if (getCookie("id") == "") {
    socket.emit("new-user", "help");
} else {
    socket.emit("new-join", getCookie("id"));
    id = getCookie("id");
}
var x, y, color;
alliances = [];
var positions = [];
socket.on('info', function(msg) {
    var inf = msg.split("|");
    x = inf[0];
    y = inf[1];
    y = parseInt(y);
    x = parseInt(x);
    color = inf[2];
		console.log(inf[3]);
		alliances = JSON.parse(inf[3]);
    navch(3)
    document.getElementById("color").style.backgroundColor = color;
    document.getElementById("game-button").style.backgroundColor = color;
    document.getElementById("game-button").disabled = false;
      	socket.emit("req-map", x + "x" + y);
});

socket.on('new-info', function(msg) {
    setCookie("id", msg, 365 * 5);
    socket.emit("new-join", getCookie("id"));
    id = getCookie("id");
});

socket.on('leader', function(msg) {
    var lead = msg;
    var cache = "Top Players
";
    lead.forEach(function(entry) {
        cache = cache + '  ' + entry[0];
        if (entry[1] == color) {
            cache = cache + " YOU";
        }
        cache = cache + "
";
    });
    document.getElementById("leaderboard").innerHTML = cache;
});

function draw(x2, y2, scol) {
    var corner_x = Math.round(x - (blocksx / 2));
    var corner_y = Math.round(parseInt(y) + (blocksy / 2));
    var corner_x_r = Math.round(parseInt(x) + (blocksx / 2));
    var corner_y_r = Math.round(y - (blocksy / 2));
    if (x2 >= corner_x && x2 <= corner_x_r) {
        if (y2 <= corner_y && y2 >= corner_y_r) {
            var gx = corner_x - x2;
            var gy = corner_y - y2;
            if (gx < 0) {
                gx = -gx;
            }
            if (gy < 0) {
                gy = -gy;
            }
            gx = gx * 40;
            gy = gy * 40;
            canvas.beginPath();
            canvas.rect(gx, gy, 40, 40);
            canvas.fillStyle = scol;
            canvas.fill();
        }
    }

}

socket.on('new-claim', function(msg) {
  if (gameStarted == true) {
    msg = JSON.parse("[" + msg + "]");
    var m2 = msg[0] + "x" + msg[1];
    map_data = map_data.filter(function(item) {
        return (item[0] !== m2)
    })
    map_data = map_data.concat([
        [m2, msg[2]]
    ]);
if (msg[2] != color) {
	positions = positions.filter(function(item) {
        return (item[2] !== msg[2])
    })

    positions = positions.concat([[msg[0], msg[1], msg[2]]]);
    draw(msg[0], msg[1], msg[2]);
    redraw();
}
  }
});

var map_data, backup;
socket.on('gmap', function(msg) {
    map_data = msg;
    gameStarted = true;
    redraw();

});

socket.on('alliance-made', function(msg) {
    alert("alliance made with " + msg);

});
function redraw() {
if (gameStarted == true) {

    cdraw++;
    if (cdraw > redo) {
        socket.emit("req-map", x + "x" + y);
        cdraw = 0;
    }
    var map = map_data;
    canvas.beginPath();
    canvas.rect(0, 0, window.innerWidth, window.innerHeight);
    canvas.fillStyle = "#1e1e1e";
    canvas.fill();
	 localgamecache.forEach(function(element) {
        piece = element[0].split("x");
        draw(piece[0], piece[1], element[1]);
    });

    map.forEach(function(element) {
        piece = element[0].split("x");
        draw(piece[0], piece[1], element[1]);
    });

	    positions.forEach(function(element) {
        draw(element[0], element[1], "#757575");
    });

    draw(x, y, "#757575");
}
}
function isClaimed(element) {
return JSON.stringify(element) == JSON.stringify([m2ca, cach_col]);
}
var cach_col, m2ca;

function update() {
    var t1 = new Date();
    t1 = t1.getTime();
    var m2 = x + "x" + y;
		m2ca = m2;
		var allow_overwrite = true;
alliances.forEach(function(element) {
	cach_col = element;
			if (map_data.findIndex(isClaimed) > -1) {
				allow_overwrite = false;
			}

		});
		if (allow_overwrite == true) {
    map_data = map_data.filter(function(item) {
        return (item[0] !== m2);
    });
    localgamecache = localgamecache.concat([
        [m2, color]
    ]);

    socket.emit("move", x + "," + y + "," + id);
	}
    redraw();

    var t2 = new Date();
    t2 = t2.getTime();
    var t3 = t2 - t1;
    document.getElementById('overlay').innerHTML = "<button onclick='move(1)'>↑</button>
<button onclick='move(3)'>←</button><button> </button><button onclick='move(4)'>→</button>
<button onclick='move(2)'>↓</button>Position: " + x + "," + y;
}

function tooFarError() {
    open = false;
    document.getElementById('game-button').innerHTML = "Continue";
    document.getElementById('pop').style.display = "table";
    document.getElementById("game-pos").innerHTML = "Oops!
You are not allowed to go past 1000 blocks, positive or negative, in any direction";
    if (x > 999) {
        x = 999;
    }
    if (x < -999) {
        x = -999;
    }
    if (y > 999) {
        y = 999;
    }
    if (y < -999) {
        y = -999;
    }
    update();
}

function move(d) {
    if (open == true) {

        if (d == 1) {
            y++;
            update();
        }
        if (d == 2) {
            y--;
            update();
        }
        if (d == 3) {
            x--;
            update();
        }
        if (d == 4) {
            x++;
            update();
        }
        if (x > 999 || x < -999 || y > 999 || y < -999) {
            tooFarError();
        }
    }

}

function checkKey(e) {
    e = e || window.event;
    var code = e.keyCode;
    if (code == 38) {
        move(1); // up
    }
    if (code == 40) {
        move(2); // down
    }
    if (code == 37) {
        move(3); // left
    }
    if (code == 39) {
        move(4); // right
    }

    if (code == 87) {
        move(1); // up
    }
    if (code == 83) {
        move(2); // down
    }
    if (code == 65) {
        move(3); // left
    }
    if (code == 68) {
        move(4); // right
    }
}
document.onkeydown = checkKey;
window.onresize = function() {
    setUp();
    redraw();
}
<|endoftext|>


<commit_before>
fc.sourceNormalizers = [];
fc.sourceFetchers = [];

var ajaxDefaults = {
	dataType: 'json',
	cache: false
};

var eventGUID = 1;

function EventManager(options, _sources) {
	var t = this;

	// exports
	t.isFetchNeeded = isFetchNeeded;
	t.fetchEvents = fetchEvents;
	t.addEventSource = addEventSource;
	t.removeEventSource = removeEventSource;
	t.updateEvent = updateEvent;
	t.renderEvent = renderEvent;
	t.removeEvents = removeEvents;
	t.clientEvents = clientEvents;
	t.normalizeEvent = normalizeEvent;

	// imports
	var trigger = t.trigger;
	var getView = t.getView;
	var reportEvents = t.reportEvents;

	// locals
	var stickySource = { events: [] };
	var sources = [ stickySource ];
	var rangeStart, rangeEnd;
	var currentFetchID = 0;
	var pendingSourceCnt = 0;
	var loadingLevel = 0;
	var cache = [];

	for (var i=0; i<_sources.length; i++) {
		_addEventSource(_sources[i]);
	}

	/* Fetching
	-----------------------------------------------------------------------------*/

	function isFetchNeeded(start, end) {
		return !rangeStart || start < rangeStart || end > rangeEnd;
	}

	function fetchEvents(start, end) {
		rangeStart = start;
		rangeEnd = end;
		cache = [];
		var fetchID = ++currentFetchID;
		var len = sources.length;
		pendingSourceCnt = len;
		for (var i=0; i<len; i++) {
			fetchEventSource(sources[i], fetchID);
		}
	}

	function fetchEventSource(source, fetchID) {
		_fetchEventSource(source, function(events) {
			if (fetchID == currentFetchID) {
				if (events) {
					for (var i=0; i<events.length; i++) {
						events[i].source = source;
						normalizeEvent(events[i]);
					}
					cache = cache.concat(events);
				}
				pendingSourceCnt--;
				if (!pendingSourceCnt) {
					reportEvents(cache);
				}
			}
		});
	}

	function _fetchEventSource(source, callback) {
		var i;
		var fetchers = fc.sourceFetchers;
		var res;
		for (i=0; i<fetchers.length; i++) {
			res = fetchers[i](source, rangeStart, rangeEnd, callback);
			if (res === true) {
				// the fetcher is in charge. made its own async request
				return;
			}
			else if (typeof res == 'object') {
				// the fetcher returned a new source. process it
				_fetchEventSource(res, callback);
				return;
			}
		}
		var events = source.events;
		if (events) {
			if ($.isFunction(events)) {
				pushLoading();
				events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
					callback(events);
					popLoading();
				});
			}
			else if ($.isArray(events)) {
				callback(events);
			}
			else {
				callback();
			}
		}else{
			var url = source.url;
			if (url) {
				var success = source.success;
				var error = source.error;
				var complete = source.complete;
				var data = $.extend({}, source.data || {});
				var startParam = firstDefined(source.startParam, options.startParam);
				var endParam = firstDefined(source.endParam, options.endParam);
				if (startParam) {
					data[startParam] = Math.round(+rangeStart / 1000);
				}
				if (endParam) {
					data[endParam] = Math.round(+rangeEnd / 1000);
				}
				pushLoading();
				$.ajax($.extend({}, ajaxDefaults, source, {
					data: data,
					success: function(events) {
						events = events || [];
						var res = applyAll(success, this, arguments);
						if ($.isArray(res)) {
							events = res;
						}
						callback(events);
					},
					error: function() {
						applyAll(error, this, arguments);
						callback();
					},
					complete: function() {
						applyAll(complete, this, arguments);
						popLoading();
					}
				}));
			}else{
				callback();
			}
		}
	}

	/* Sources
	-----------------------------------------------------------------------------*/

	function addEventSource(source) {
		source = _addEventSource(source);
		if (source) {
			pendingSourceCnt++;
			fetchEventSource(source, currentFetchID); // will eventually call reportEvents
		}
	}

	function _addEventSource(source) {
		if ($.isFunction(source) || $.isArray(source)) {
			source = { events: source };
		}
		else if (typeof source == 'string') {
			source = { url: source };
		}
		if (typeof source == 'object') {
			normalizeSource(source);
			sources.push(source);
			return source;
		}
	}

	function removeEventSource(source) {
		sources = $.grep(sources, function(src) {
			return !isSourcesEqual(src, source);
		});
		// remove all client events from that source
		cache = $.grep(cache, function(e) {
			return !isSourcesEqual(e.source, source);
		});
		reportEvents(cache);
	}

	/* Manipulation
	-----------------------------------------------------------------------------*/

	function updateEvent(event) { // update an existing event
		var i, len = cache.length, e,
			defaultEventEnd = getView().defaultEventEnd, // getView???
			startDelta = event.start - event._start,
			endDelta = event.end ?
				(event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
				: 0;                                                      // was null and event was just resized
		for (i=0; i<len; i++) {
			e = cache[i];
			if (e._id == event._id && e != event) {
				e.start = new Date(+e.start + startDelta);
				if (event.end) {
					if (e.end) {
						e.end = new Date(+e.end + endDelta);
					}else{
						e.end = new Date(+defaultEventEnd(e) + endDelta);
					}
				}else{
					e.end = null;
				}
				e.title = event.title;
				e.url = event.url;
				e.allDay = event.allDay;
				e.className = event.className;
				e.editable = event.editable;
				e.color = event.color;
				e.backgroudColor = event.backgroudColor;
				e.borderColor = event.borderColor;
				e.textColor = event.textColor;
				normalizeEvent(e);
			}
		}
		normalizeEvent(event);
		reportEvents(cache);
	}

	function renderEvent(event, stick) {
		normalizeEvent(event);
		if (!event.source) {
			if (stick) {
				stickySource.events.push(event);
				event.source = stickySource;
			}
			cache.push(event);
		}
		reportEvents(cache);
	}

	function removeEvents(filter) {
		if (!filter) { // remove all
			cache = [];
			// clear all array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = [];
				}
			}
		}else{
			if (!$.isFunction(filter)) { // an event ID
				var id = filter + '';
				filter = function(e) {
					return e._id == id;
				};
			}
			cache = $.grep(cache, filter, true);
			// remove events from array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = $.grep(sources[i].events, filter, true);
				}
			}
		}
		reportEvents(cache);
	}

	function clientEvents(filter) {
		if ($.isFunction(filter)) {
			return $.grep(cache, filter);
		}
		else if (filter) { // an event ID
			filter += '';
			return $.grep(cache, function(e) {
				return e._id == filter;
			});
		}
		return cache; // else, return all
	}

	/* Loading State
	-----------------------------------------------------------------------------*/

	function pushLoading() {
		if (!loadingLevel++) {
			trigger('loading', null, true);
		}
	}

	function popLoading() {
		if (!--loadingLevel) {
			trigger('loading', null, false);
		}
	}

	/* Event Normalization
	-----------------------------------------------------------------------------*/

	function normalizeEvent(event) {
		var source = event.source || {};
		var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
		event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
		if (event.date) {
			if (!event.start) {
				event.start = event.date;
			}
			delete event.date;
		}
		event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
		event.end = parseDate(event.end, ignoreTimezone);
		if (event.end && event.end <= event.start) {
			event.end = null;
		}
		event._end = event.end ? cloneDate(event.end) : null;
		if (event.allDay === undefined) {
			event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
		}
		if (event.className) {
			if (typeof event.className == 'string') {
				event.className = event.className.split(/\s+/);
			}
		}else{
			event.className = [];
		}
		// TODO: if there is no start date, return false to indicate an invalid event
	}

	/* Utils
	------------------------------------------------------------------------------*/

	function normalizeSource(source) {
		if (source.className) {
			// TODO: repeat code, same code for event classNames
			if (typeof source.className == 'string') {
				source.className = source.className.split(/\s+/);
			}
		}else{
			source.className = [];
		}
		var normalizers = fc.sourceNormalizers;
		for (var i=0; i<normalizers.length; i++) {
			normalizers[i](source);
		}
	}

	function isSourcesEqual(source1, source2) {
		return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
	}

	function getSourcePrimitive(source) {
		return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
	}

}
<commit_msg>trigger loading callback with pending/total source count, not just true/false<commit_after>
fc.sourceNormalizers = [];
fc.sourceFetchers = [];

var ajaxDefaults = {
	dataType: 'json',
	cache: false
};

var eventGUID = 1;

function EventManager(options, _sources) {
	var t = this;

	// exports
	t.isFetchNeeded = isFetchNeeded;
	t.fetchEvents = fetchEvents;
	t.addEventSource = addEventSource;
	t.removeEventSource = removeEventSource;
	t.updateEvent = updateEvent;
	t.renderEvent = renderEvent;
	t.removeEvents = removeEvents;
	t.clientEvents = clientEvents;
	t.normalizeEvent = normalizeEvent;

	// imports
	var trigger = t.trigger;
	var getView = t.getView;
	var reportEvents = t.reportEvents;

	// locals
	var stickySource = { events: [] };
	var sources = [ stickySource ];
	var rangeStart, rangeEnd;
	var currentFetchID = 0;
	var pendingSourceCnt = 0;
	var cache = [];

	for (var i=0; i<_sources.length; i++) {
		_addEventSource(_sources[i]);
	}

	/* Fetching
	-----------------------------------------------------------------------------*/

	function isFetchNeeded(start, end) {
		return !rangeStart || start < rangeStart || end > rangeEnd;
	}

	function fetchEvents(start, end) {
		rangeStart = start;
		rangeEnd = end;
		cache = [];
		var fetchID = ++currentFetchID;
		var len = sources.length;
		pendingSourceCnt = len;
		for (var i=0; i<len; i++) {
			fetchEventSource(sources[i], fetchID);
		}
	}

	function fetchEventSource(source, fetchID) {
		_fetchEventSource(source, function(events) {
			if (fetchID == currentFetchID) {
				if (events) {
					for (var i=0; i<events.length; i++) {
						events[i].source = source;
						normalizeEvent(events[i]);
					}
					cache = cache.concat(events);
				}
				pendingSourceCnt--;
				if (!pendingSourceCnt) {
					reportEvents(cache);
				}
			}
		});
	}

	function _fetchEventSource(source, callback) {
		var i;
		var fetchers = fc.sourceFetchers;
		var res;
		for (i=0; i<fetchers.length; i++) {
			res = fetchers[i](source, rangeStart, rangeEnd, callback);
			if (res === true) {
				// the fetcher is in charge. made its own async request
				return;
			}
			else if (typeof res == 'object') {
				// the fetcher returned a new source. process it
				_fetchEventSource(res, callback);
				return;
			}
		}
		var events = source.events;
		if (events) {
			if ($.isFunction(events)) {
				loading();
				events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
					callback(events);
					loading();
				});
			}
			else if ($.isArray(events)) {
				callback(events);
			}
			else {
				callback();
			}
		}else{
			var url = source.url;
			if (url) {
				var success = source.success;
				var error = source.error;
				var complete = source.complete;
				var data = $.extend({}, source.data || {});
				var startParam = firstDefined(source.startParam, options.startParam);
				var endParam = firstDefined(source.endParam, options.endParam);
				if (startParam) {
					data[startParam] = Math.round(+rangeStart / 1000);
				}
				if (endParam) {
					data[endParam] = Math.round(+rangeEnd / 1000);
				}
				loading();
				$.ajax($.extend({}, ajaxDefaults, source, {
					data: data,
					success: function(events) {
						events = events || [];
						var res = applyAll(success, this, arguments);
						if ($.isArray(res)) {
							events = res;
						}
						callback(events);
					},
					error: function() {
						applyAll(error, this, arguments);
						callback();
					},
					complete: function() {
						applyAll(complete, this, arguments);
						loading();
					}
				}));
			}else{
				callback();
			}
		}
	}

	/* Sources
	-----------------------------------------------------------------------------*/

	function addEventSource(source) {
		source = _addEventSource(source);
		if (source) {
			pendingSourceCnt++;
			fetchEventSource(source, currentFetchID); // will eventually call reportEvents
		}
	}

	function _addEventSource(source) {
		if ($.isFunction(source) || $.isArray(source)) {
			source = { events: source };
		}
		else if (typeof source == 'string') {
			source = { url: source };
		}
		if (typeof source == 'object') {
			normalizeSource(source);
			sources.push(source);
			return source;
		}
	}

	function removeEventSource(source) {
		sources = $.grep(sources, function(src) {
			return !isSourcesEqual(src, source);
		});
		// remove all client events from that source
		cache = $.grep(cache, function(e) {
			return !isSourcesEqual(e.source, source);
		});
		reportEvents(cache);
	}

	/* Manipulation
	-----------------------------------------------------------------------------*/

	function updateEvent(event) { // update an existing event
		var i, len = cache.length, e,
			defaultEventEnd = getView().defaultEventEnd, // getView???
			startDelta = event.start - event._start,
			endDelta = event.end ?
				(event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
				: 0;                                                      // was null and event was just resized
		for (i=0; i<len; i++) {
			e = cache[i];
			if (e._id == event._id && e != event) {
				e.start = new Date(+e.start + startDelta);
				if (event.end) {
					if (e.end) {
						e.end = new Date(+e.end + endDelta);
					}else{
						e.end = new Date(+defaultEventEnd(e) + endDelta);
					}
				}else{
					e.end = null;
				}
				e.title = event.title;
				e.url = event.url;
				e.allDay = event.allDay;
				e.className = event.className;
				e.editable = event.editable;
				e.color = event.color;
				e.backgroudColor = event.backgroudColor;
				e.borderColor = event.borderColor;
				e.textColor = event.textColor;
				normalizeEvent(e);
			}
		}
		normalizeEvent(event);
		reportEvents(cache);
	}

	function renderEvent(event, stick) {
		normalizeEvent(event);
		if (!event.source) {
			if (stick) {
				stickySource.events.push(event);
				event.source = stickySource;
			}
			cache.push(event);
		}
		reportEvents(cache);
	}

	function removeEvents(filter) {
		if (!filter) { // remove all
			cache = [];
			// clear all array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = [];
				}
			}
		}else{
			if (!$.isFunction(filter)) { // an event ID
				var id = filter + '';
				filter = function(e) {
					return e._id == id;
				};
			}
			cache = $.grep(cache, filter, true);
			// remove events from array sources
			for (var i=0; i<sources.length; i++) {
				if ($.isArray(sources[i].events)) {
					sources[i].events = $.grep(sources[i].events, filter, true);
				}
			}
		}
		reportEvents(cache);
	}

	function clientEvents(filter) {
		if ($.isFunction(filter)) {
			return $.grep(cache, filter);
		}
		else if (filter) { // an event ID
			filter += '';
			return $.grep(cache, function(e) {
				return e._id == filter;
			});
		}
		return cache; // else, return all
	}

	/* Loading State
	-----------------------------------------------------------------------------*/

	function loading() {
		trigger('loading', null, pendingSourceCnt || false, sources.length);
	}

	/* Event Normalization
	-----------------------------------------------------------------------------*/

	function normalizeEvent(event) {
		var source = event.source || {};
		var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
		event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
		if (event.date) {
			if (!event.start) {
				event.start = event.date;
			}
			delete event.date;
		}
		event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
		event.end = parseDate(event.end, ignoreTimezone);
		if (event.end && event.end <= event.start) {
			event.end = null;
		}
		event._end = event.end ? cloneDate(event.end) : null;
		if (event.allDay === undefined) {
			event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
		}
		if (event.className) {
			if (typeof event.className == 'string') {
				event.className = event.className.split(/\s+/);
			}
		}else{
			event.className = [];
		}
		// TODO: if there is no start date, return false to indicate an invalid event
	}

	/* Utils
	------------------------------------------------------------------------------*/

	function normalizeSource(source) {
		if (source.className) {
			// TODO: repeat code, same code for event classNames
			if (typeof source.className == 'string') {
				source.className = source.className.split(/\s+/);
			}
		}else{
			source.className = [];
		}
		var normalizers = fc.sourceNormalizers;
		for (var i=0; i<normalizers.length; i++) {
			normalizers[i](source);
		}
	}

	function isSourcesEqual(source1, source2) {
		return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
	}

	function getSourcePrimitive(source) {
		return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
	}

}
<|endoftext|>


<commit_before><commit_msg>preserve the version and url of sortable.js when it is minimized<commit_after><|endoftext|>


<commit_before>/*Dice Game plugin by SilverTactic (Silveee)*/

'use strict';

const INACTIVE_END_TIME = 1 * 60 * 1000; // 1 minute
//const TAX = 0.10; // 10%
const TAX = 0;

function diceImg(num) {
	switch (num) {
	case 0:
		return "http://i.imgur.com/nUbpLTD.png";
	case 1:
		return "http://i.imgur.com/BSt9nfV.png";
	case 2:
		return "http://i.imgur.com/eTQMVhY.png";
	case 3:
		return "http://i.imgur.com/3Y2hCAJ.png";
	case 4:
		return "http://i.imgur.com/KP3Za7O.png";
	case 5:
		return "http://i.imgur.com/lvi2ZZe.png";
	}
}

class Dice {
	constructor(room, amount, starter) {
		this.room = room;
		if (!this.room.diceCount) this.room.diceCount = 0;
		this.bet = amount;
		this.players = [];
		this.timer = setTimeout(() => {
			this.room.add('|uhtmlchange|' + this.room.diceCount + '|
(This game of dice has been ended due to inactivity.)
').update();
			delete this.room.dice;
		}, INACTIVE_END_TIME);

		let buck = (this.bet === 1 ? 'buck' : 'bucks');
		this.startMessage = '
' + Tools.escapeHTML(starter) + ' has started a game of dice for ' + amount + ' ' + buck + '!
' +
			'' +
			'
' +
			'<button name="send" value="/joindice">Click to join!</button>';
		this.room.add('|uhtml|' + (++this.room.diceCount) + '|' + this.startMessage + '
').update();
	}

	join(user, self) {
		if (this.players.length >= 2) return self.errorReply("Two users have already joined this game of dice.");
		Economy.readMoney(user.userid, money => {
			if (money < this.bet) return self.sendReply('You don\'t have enough money for this game of dice.');
			if (this.players.includes(user)) return self.sendReply('You have already joined this game of dice.');
			if (this.players.length && this.players[0].latestIp === user.latestIp) return self.errorReply("You have already joined this game of dice under the alt '" + this.players[0].name + "'.");
			if (this.players.length >= 2) return self.errorReply("Two users have already joined this game of dice.");

			this.players.push(user);
			this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '' + Wisp.nameColor(user.name) + ' has joined the game!
').update();
			if (this.players.length === 2) this.play();
		});
	}

	leave(user, self) {
		if (!this.players.includes(user)) return self.sendReply('You haven\'t joined this game of dice yet.');
		if (this.players.length >= 2) return self.errorReply("You cannot leave a game of dice once it has been started.");
		this.players.splice(this.players.indexOf(user), 1);
		this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '
');
	}

	play() {
		let p1 = this.players[0], p2 = this.players[1];
		Economy.readMoney(p1.userid, money1 => {
			Economy.readMoney(p2.userid, money2 => {
				if (money1 < this.bet || money2 < this.bet) {
					let user = (money1 < this.bet ? p1 : p2);
					let other = (user === p1 ? p2 : p1);
					user.sendTo(this.room, 'You have been removed from this game of dice, as you do not have enough money.');
					other.sendTo(this.room, user.name + ' has been removed from this game of dice, as they do not have enough money. Wait for another user to join.');
					this.players.splice(this.players.indexOf(user), 1);
					this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '' + this.players.map(user => Wisp.nameColor(user.name)) + ' has joined the game!').update();
					return;
				}
				let players = this.players.map(user => Wisp.nameColor(user.name)).join(' and ');
				this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '' + players + ' have joined the game!
').update();
				let roll1, roll2;
				do {
					roll1 = Math.floor(Math.random() * 6);
					roll2 = Math.floor(Math.random() * 6);
				} while (roll1 === roll2);
				if (roll2 > roll1) this.players.reverse();
				let winner = this.players[0], loser = this.players[1];

				let taxedAmt = Math.round(this.bet * TAX);
				setTimeout(() => {
					let buck = (this.bet === 1 ? 'buck' : 'bucks');
					this.room.add('|uhtmlchange|' + this.room.diceCount + '|
' + players + ' have joined the game!

' +
						'The game has been started! Rolling the dice...
' +
						'
' +
						Wisp.nameColor(p1.name, true) + ' rolled ' + (roll1 + 1) + '!
' +
						Wisp.nameColor(p2.name, true) + ' rolled ' + (roll2 + 1) + '!
' +
						Wisp.nameColor(winner.name, true) + ' has won ' + (this.bet - taxedAmt) + ' ' + buck + '!
' +
						'Better luck next time, ' + Wisp.nameColor(loser.name) + '!'
					).update();
					Economy.writeMoney(winner.userid, (this.bet - taxedAmt), () => {
						Economy.writeMoney(loser.userid, -this.bet, () => {
							this.end();
						});
					});
				}, 800);
			});
		});
	}

	end(user) {
		if (user) this.room.add('|uhtmlchange|' + this.room.diceCount + '|
(This game of dice has been forcibly ended by ' + Tools.escapeHTML(user.name) + '.)
').update();
		clearTimeout(this.timer);
		delete this.room.dice;
	}
}

exports.commands = {
	startdice: 'dicegame',
	dicegame: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if (!user.can('broadcast', null, room) && room.id !== 'casino' && room.id !== 'coldfrontcasino') return this.errorReply("You must be ranked + or higher in this room to start a game of dice outside the Casino.");
		if ((user.locked || room.isMuted(user)) && !user.can('bypassall')) return this.errorReply("You cannot use this command while unable to talk.");
		if (room.dice) return this.errorReply("There is already a game of dice going on in this room.");

		let amount = Number(target) || 1;
		if (isNaN(target)) return this.errorReply('"' + target + '" isn\'t a valid number.');
		if (target.includes('.') || amount < 1 || amount > 5000) return this.sendReply('The number of bucks must be between 1 and 5,000 and cannot contain a decimal.');
		Economy.readMoney(user.userid, bucks => {
			if (bucks < amount) return this.sendReply("You don't have " + amount + " " + (amount === 1 ? "buck" : "bucks") + ".");
			room.dice = new Dice(room, amount, user.name);
			this.parse("/joindice");
		});
	},

	dicejoin: 'joindice',
	joindice: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if ((user.locked || room.isMuted(user)) && !user.can('bypassall')) return this.sendReply("You cannot use this command while unable to talk.");
		if (!room.dice) return this.errorReply('There is no game of dice going on in this room.');

		room.dice.join(user, this);
	},

	diceleave: 'leavedice',
	leavedice: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if (!room.dice) return this.errorReply('There is no game of dice going on in this room.');

		room.dice.leave(user, this);
	},

	diceend: 'enddice',
	enddice: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if (!this.can('broadcast', null, room)) return this.errorReply("You must be ranked + or higher in this room to end a game of dice.");
		if ((user.locked || room.isMuted(user)) && !user.can('bypassall')) return this.sendReply("You cannot use this command while unable to talk.");
		if (!room.dice) return this.errorReply('There is no game of dice going on in this room.');

		room.dice.end(user);
	},
	dicegamehelp: [
		'/startdice or /dicegame [amount] - Starts a game of dice in the room for a given number of bucks, 1 by default (NOTE: There is a 10% tax on bucks you win from dice games).',
		'/joindice - Joins the game of dice. You cannot use this command if you don\'t have the number of bucks the game is for.',
		'/leavedice - Leaves the game of dice.',
		'/enddice - Ends the game of dice. Requires + or higher to use.',
	],
};
<commit_msg>Remove trailing whitespace<commit_after>/*Dice Game plugin by SilverTactic (Silveee)*/

'use strict';

const INACTIVE_END_TIME = 1 * 60 * 1000; // 1 minute
//const TAX = 0.10; // 10%
const TAX = 0;

function diceImg(num) {
	switch (num) {
	case 0:
		return "http://i.imgur.com/nUbpLTD.png";
	case 1:
		return "http://i.imgur.com/BSt9nfV.png";
	case 2:
		return "http://i.imgur.com/eTQMVhY.png";
	case 3:
		return "http://i.imgur.com/3Y2hCAJ.png";
	case 4:
		return "http://i.imgur.com/KP3Za7O.png";
	case 5:
		return "http://i.imgur.com/lvi2ZZe.png";
	}
}

class Dice {
	constructor(room, amount, starter) {
		this.room = room;
		if (!this.room.diceCount) this.room.diceCount = 0;
		this.bet = amount;
		this.players = [];
		this.timer = setTimeout(() => {
			this.room.add('|uhtmlchange|' + this.room.diceCount + '|
(This game of dice has been ended due to inactivity.)
').update();
			delete this.room.dice;
		}, INACTIVE_END_TIME);

		let buck = (this.bet === 1 ? 'buck' : 'bucks');
		this.startMessage = '
' + Tools.escapeHTML(starter) + ' has started a game of dice for ' + amount + ' ' + buck + '!
' +
			'' +
			'
' +
			'<button name="send" value="/joindice">Click to join!</button>';
		this.room.add('|uhtml|' + (++this.room.diceCount) + '|' + this.startMessage + '
').update();
	}

	join(user, self) {
		if (this.players.length >= 2) return self.errorReply("Two users have already joined this game of dice.");
		Economy.readMoney(user.userid, money => {
			if (money < this.bet) return self.sendReply('You don\'t have enough money for this game of dice.');
			if (this.players.includes(user)) return self.sendReply('You have already joined this game of dice.');
			if (this.players.length && this.players[0].latestIp === user.latestIp) return self.errorReply("You have already joined this game of dice under the alt '" + this.players[0].name + "'.");
			if (this.players.length >= 2) return self.errorReply("Two users have already joined this game of dice.");

			this.players.push(user);
			this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '' + Wisp.nameColor(user.name) + ' has joined the game!
').update();
			if (this.players.length === 2) this.play();
		});
	}

	leave(user, self) {
		if (!this.players.includes(user)) return self.sendReply('You haven\'t joined this game of dice yet.');
		if (this.players.length >= 2) return self.errorReply("You cannot leave a game of dice once it has been started.");
		this.players.splice(this.players.indexOf(user), 1);
		this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '
');
	}

	play() {
		let p1 = this.players[0], p2 = this.players[1];
		Economy.readMoney(p1.userid, money1 => {
			Economy.readMoney(p2.userid, money2 => {
				if (money1 < this.bet || money2 < this.bet) {
					let user = (money1 < this.bet ? p1 : p2);
					let other = (user === p1 ? p2 : p1);
					user.sendTo(this.room, 'You have been removed from this game of dice, as you do not have enough money.');
					other.sendTo(this.room, user.name + ' has been removed from this game of dice, as they do not have enough money. Wait for another user to join.');
					this.players.splice(this.players.indexOf(user), 1);
					this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '' + this.players.map(user => Wisp.nameColor(user.name)) + ' has joined the game!').update();
					return;
				}
				let players = this.players.map(user => Wisp.nameColor(user.name)).join(' and ');
				this.room.add('|uhtmlchange|' + this.room.diceCount + '|' + this.startMessage + '' + players + ' have joined the game!
').update();
				let roll1, roll2;
				do {
					roll1 = Math.floor(Math.random() * 6);
					roll2 = Math.floor(Math.random() * 6);
				} while (roll1 === roll2);
				if (roll2 > roll1) this.players.reverse();
				let winner = this.players[0], loser = this.players[1];

				let taxedAmt = Math.round(this.bet * TAX);
				setTimeout(() => {
					let buck = (this.bet === 1 ? 'buck' : 'bucks');
					this.room.add('|uhtmlchange|' + this.room.diceCount + '|
' + players + ' have joined the game!

' +
						'The game has been started! Rolling the dice...
' +
						'
' +
						Wisp.nameColor(p1.name, true) + ' rolled ' + (roll1 + 1) + '!
' +
						Wisp.nameColor(p2.name, true) + ' rolled ' + (roll2 + 1) + '!
' +
						Wisp.nameColor(winner.name, true) + ' has won ' + (this.bet - taxedAmt) + ' ' + buck + '!
' +
						'Better luck next time, ' + Wisp.nameColor(loser.name) + '!'
					).update();
					Economy.writeMoney(winner.userid, (this.bet - taxedAmt), () => {
						Economy.writeMoney(loser.userid, -this.bet, () => {
							this.end();
						});
					});
				}, 800);
			});
		});
	}

	end(user) {
		if (user) this.room.add('|uhtmlchange|' + this.room.diceCount + '|
(This game of dice has been forcibly ended by ' + Tools.escapeHTML(user.name) + '.)
').update();
		clearTimeout(this.timer);
		delete this.room.dice;
	}
}

exports.commands = {
	startdice: 'dicegame',
	dicegame: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if (!user.can('broadcast', null, room) && room.id !== 'casino' && room.id !== 'coldfrontcasino') return this.errorReply("You must be ranked + or higher in this room to start a game of dice outside the Casino.");
		if ((user.locked || room.isMuted(user)) && !user.can('bypassall')) return this.errorReply("You cannot use this command while unable to talk.");
		if (room.dice) return this.errorReply("There is already a game of dice going on in this room.");

		let amount = Number(target) || 1;
		if (isNaN(target)) return this.errorReply('"' + target + '" isn\'t a valid number.');
		if (target.includes('.') || amount < 1 || amount > 5000) return this.sendReply('The number of bucks must be between 1 and 5,000 and cannot contain a decimal.');
		Economy.readMoney(user.userid, bucks => {
			if (bucks < amount) return this.sendReply("You don't have " + amount + " " + (amount === 1 ? "buck" : "bucks") + ".");
			room.dice = new Dice(room, amount, user.name);
			this.parse("/joindice");
		});
	},

	dicejoin: 'joindice',
	joindice: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if ((user.locked || room.isMuted(user)) && !user.can('bypassall')) return this.sendReply("You cannot use this command while unable to talk.");
		if (!room.dice) return this.errorReply('There is no game of dice going on in this room.');

		room.dice.join(user, this);
	},

	diceleave: 'leavedice',
	leavedice: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if (!room.dice) return this.errorReply('There is no game of dice going on in this room.');

		room.dice.leave(user, this);
	},

	diceend: 'enddice',
	enddice: function (target, room, user) {
		if (room.id === 'lobby') return this.errorReply("This command cannot be used in the Lobby.");
		if (!this.can('broadcast', null, room)) return this.errorReply("You must be ranked + or higher in this room to end a game of dice.");
		if ((user.locked || room.isMuted(user)) && !user.can('bypassall')) return this.sendReply("You cannot use this command while unable to talk.");
		if (!room.dice) return this.errorReply('There is no game of dice going on in this room.');

		room.dice.end(user);
	},
	dicegamehelp: [
		'/startdice or /dicegame [amount] - Starts a game of dice in the room for a given number of bucks, 1 by default (NOTE: There is a 10% tax on bucks you win from dice games).',
		'/joindice - Joins the game of dice. You cannot use this command if you don\'t have the number of bucks the game is for.',
		'/leavedice - Leaves the game of dice.',
		'/enddice - Ends the game of dice. Requires + or higher to use.',
	],
};
<|endoftext|>


<commit_before>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

'use strict';

class Poll {
	constructor(room, questionData, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = questionData.source;
		this.supportHTML = questionData.supportHTML;
		this.voters = {};
		this.voterIps = {};
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (let i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	vote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		}

		this.voters[userid] = option;
		this.voterIps[ip] = option;
		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	}

	blankvote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters[userid] = 0;
			this.voterIps[ip] = 0;
		}

		this.updateTo(user);
	}

	generateVotes() {
		let output = '

 Poll ' + this.getQuestionMarkup() + '
';
		this.options.forEach((option, number) => {
			output += '
<button value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + this.getOptionMarkup(option) + '</button>
';
		});
		output += '
<button value="/poll results" name="send" title="View results - you will not be able to vote after viewing results">(View results)</button>
';
		output += '
';

		return output;
	}

	generateResults(ended, option) {
		let icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		let output = '

' + icon + ' ' + this.getQuestionMarkup() + '
';
		let iter = this.options.entries();

		let i = iter.next();
		let c = 0;
		let colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			let percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + (i.value[0] === option ? '' : '') + this.getOptionMarkup(i.value[1]) + (i.value[0] === option ? '' : '') + ' (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')
 ' + percentage + '%
';
			i = iter.next();
			c++;
		}
		if (option === 0 && !ended) output += '
(You can\'t vote after viewing results)
';
		output += '
';

		return output;
	}

	getQuestionMarkup() {
		if (this.supportHTML) return this.question;
		return Tools.escapeHTML(this.question);
	}

	getOptionMarkup(option) {
		if (this.supportHTML) return option.name;
		return Tools.escapeHTML(option.name);
	}

	update() {
		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		// Update the poll results for everyone that has voted
		for (let i in this.room.users) {
			let user = this.room.users[i];
			if (user.userid in this.voters) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voters[user.userid]]);
			} else if (user.latestIp in this.voterIps) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voterIps[user.latestIp]]);
			}
		}
	}

	updateTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	updateFor(user) {
		if (user.userid in this.voters) {
			user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		}
	}

	display() {
		let votes = this.generateVotes();

		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		for (let i in this.room.users) {
			let thisUser = this.room.users[i];
			if (thisUser.userid in this.voters) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voters[thisUser.userid]]);
			} else if (thisUser.latestIp in this.voterIps) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voterIps[thisUser.latestIp]]);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes);
			}
		}
	}

	displayTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	onConnect(user, connection) {
		this.displayTo(user, connection);
	}

	end() {
		let results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|
(The poll has ended – scroll down to see the results)
');
		this.room.add('|html|' + results);
	}
}

exports.Poll = Poll;

exports.commands = {
	poll: {
		htmlcreate: 'new',
		create: 'new',
		new: function (target, room, user, connection, cmd, message) {
			if (!target) return this.parse('/help poll new');
			if (target.length > 1024) return this.errorReply("Poll too long.");

			const supportHTML = cmd === 'htmlcreate';
			const separator = target.match(/[\n\|,]/);
			if (!separator) return this.errorReply("Not enough arguments for /poll new.");
			if (separator[0] !== '\n') target = target.replace(/[\r\n]+/g, '');

			let params = target.split(separator[0]).map(param => param.trim());

			if (!this.can('minigame', null, room)) return false;
			if (supportHTML && !this.can('declare', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");
			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");

			if (supportHTML) params = params.map(parameter => this.canHTML(parameter));
			if (params.some(parameter => !parameter)) return;

			const options = params.splice(1);
			if (options.length > 8) {
				return this.errorReply("Too many options for poll (maximum is 8).");
			}

			room.poll = new Poll(room, {source: params[0], supportHTML: supportHTML}, options);
			room.poll.display();

			this.logEntry("" + user.name + " used " + message);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.parse('/help poll vote');

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			let parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can('minigame', null, room)) return false;
				if (target === 'clear') {
					if (!room.poll.timeout) return this.errorReply("There is no timer to clear.");
					clearTimeout(room.poll.timeout);
					room.poll.timeout = null;
					room.poll.timeoutMins = 0;
					return this.add("The poll timer was turned off.");
				}
				let timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0 || timeout > 0x7FFFFFFF) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout(() => {
					room.poll.end();
					delete room.poll;
				}, (timeout * 60000));
				room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
				return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
			} else {
				if (!this.runBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
				} else {
					return this.sendReply("The poll timer is off.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		show: 'display',
		display: function (target, room, user, connection) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				room.poll.display();
			} else {
				room.poll.displayTo(user, connection);
			}
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/help poll');
		},
	},
	pollhelp: ["/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.",
				"Accepts the following commands:",
				"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~",
				"/poll vote [number] - Votes for option [number].",
				"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: % @ # & ~",
				"/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this.",
				"/poll display - Displays the poll",
				"/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],
};

process.nextTick(() => {
	CommandParser.multiLinePattern.register('/poll (new|create|htmlcreate) ');
});
<commit_msg>Polls: warn when trying to queue commands on creation<commit_after>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

'use strict';

class Poll {
	constructor(room, questionData, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = questionData.source;
		this.supportHTML = questionData.supportHTML;
		this.voters = {};
		this.voterIps = {};
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (let i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	vote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		}

		this.voters[userid] = option;
		this.voterIps[ip] = option;
		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	}

	blankvote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters[userid] = 0;
			this.voterIps[ip] = 0;
		}

		this.updateTo(user);
	}

	generateVotes() {
		let output = '

 Poll ' + this.getQuestionMarkup() + '
';
		this.options.forEach((option, number) => {
			output += '
<button value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + this.getOptionMarkup(option) + '</button>
';
		});
		output += '
<button value="/poll results" name="send" title="View results - you will not be able to vote after viewing results">(View results)</button>
';
		output += '
';

		return output;
	}

	generateResults(ended, option) {
		let icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		let output = '

' + icon + ' ' + this.getQuestionMarkup() + '
';
		let iter = this.options.entries();

		let i = iter.next();
		let c = 0;
		let colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			let percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + (i.value[0] === option ? '' : '') + this.getOptionMarkup(i.value[1]) + (i.value[0] === option ? '' : '') + ' (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')
 ' + percentage + '%
';
			i = iter.next();
			c++;
		}
		if (option === 0 && !ended) output += '
(You can\'t vote after viewing results)
';
		output += '
';

		return output;
	}

	getQuestionMarkup() {
		if (this.supportHTML) return this.question;
		return Tools.escapeHTML(this.question);
	}

	getOptionMarkup(option) {
		if (this.supportHTML) return option.name;
		return Tools.escapeHTML(option.name);
	}

	update() {
		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		// Update the poll results for everyone that has voted
		for (let i in this.room.users) {
			let user = this.room.users[i];
			if (user.userid in this.voters) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voters[user.userid]]);
			} else if (user.latestIp in this.voterIps) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voterIps[user.latestIp]]);
			}
		}
	}

	updateTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	updateFor(user) {
		if (user.userid in this.voters) {
			user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		}
	}

	display() {
		let votes = this.generateVotes();

		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		for (let i in this.room.users) {
			let thisUser = this.room.users[i];
			if (thisUser.userid in this.voters) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voters[thisUser.userid]]);
			} else if (thisUser.latestIp in this.voterIps) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voterIps[thisUser.latestIp]]);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes);
			}
		}
	}

	displayTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	onConnect(user, connection) {
		this.displayTo(user, connection);
	}

	end() {
		let results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|
(The poll has ended – scroll down to see the results)
');
		this.room.add('|html|' + results);
	}
}

exports.Poll = Poll;

exports.commands = {
	poll: {
		htmlcreate: 'new',
		create: 'new',
		new: function (target, room, user, connection, cmd, message) {
			if (!target) return this.parse('/help poll new');
			if (target.length > 1024) return this.errorReply("Poll too long.");

			const supportHTML = cmd === 'htmlcreate';
			const separator = target.match(/[\n\|,]/);
			if (!separator) return this.errorReply("Not enough arguments for /poll new.");
			if (separator[0] !== '\n') {
				if (/\n\//.test(target)) return this.errorReply("/poll " + cmd + " is a multiline command now. Please send queued commands separately instead.");
				target = target.replace(/[\r\n]+/g, '');
			}

			let params = target.split(separator[0]).map(param => param.trim());

			if (!this.can('minigame', null, room)) return false;
			if (supportHTML && !this.can('declare', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");
			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");

			if (supportHTML) params = params.map(parameter => this.canHTML(parameter));
			if (params.some(parameter => !parameter)) return;

			const options = params.splice(1);
			if (options.length > 8) {
				return this.errorReply("Too many options for poll (maximum is 8).");
			}

			room.poll = new Poll(room, {source: params[0], supportHTML: supportHTML}, options);
			room.poll.display();

			this.logEntry("" + user.name + " used " + message);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.parse('/help poll vote');

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			let parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can('minigame', null, room)) return false;
				if (target === 'clear') {
					if (!room.poll.timeout) return this.errorReply("There is no timer to clear.");
					clearTimeout(room.poll.timeout);
					room.poll.timeout = null;
					room.poll.timeoutMins = 0;
					return this.add("The poll timer was turned off.");
				}
				let timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0 || timeout > 0x7FFFFFFF) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout(() => {
					room.poll.end();
					delete room.poll;
				}, (timeout * 60000));
				room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
				return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
			} else {
				if (!this.runBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
				} else {
					return this.sendReply("The poll timer is off.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		show: 'display',
		display: function (target, room, user, connection) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				room.poll.display();
			} else {
				room.poll.displayTo(user, connection);
			}
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/help poll');
		},
	},
	pollhelp: ["/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.",
				"Accepts the following commands:",
				"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~",
				"/poll vote [number] - Votes for option [number].",
				"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: % @ # & ~",
				"/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this.",
				"/poll display - Displays the poll",
				"/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],
};

process.nextTick(() => {
	CommandParser.multiLinePattern.register('/poll (new|create|htmlcreate) ');
});
<|endoftext|>