<commit_before>function inviteToShareOnTwitter() {
  return;
}

function trackFacebookStatus(facebookStatus) {
  $.ajax({
    type: 'post',
    url: VK.social_tracking_url,
    data: {facebook_action: "status", facebook_status: facebookStatus.status}
  });
}

function initFacebookApp() {
  var appId = $('meta[property="fb:app_id"]').attr('content');
  FB.init({
    appId: appId,
    status: true, // check login status
    cookie: true, // enable cookies to allow the server to access the session
    xfbml: true,  // parse XFBML
    frictionless: true // for facebook request dialog
  });

  if (FB.getLoginStatus) { FB.getLoginStatus(trackFacebookStatus); }
}

function socialTrackingParams(facebook_action, action_id, request_id, request_to_ids) {
  var params = { petition_id: VK.petition_id, facebook_action: facebook_action };

  if (VK.signature_id !== "") { params.signature_id = VK.signature_id; }
  if (action_id !== "")       { params.action_id = action_id; }
  if (request_id !== "")      { params.request_id = request_id; }
  if (request_to_ids !== "")  { params.friend_ids = request_to_ids; }

  return params;
}

function setupSocialTrackingControllerRequest(facebook_action, action_id, request_id, request_to_ids) {
  $.ajax({
    type: 'post',
    url: VK.social_tracking_url,
    data: socialTrackingParams(facebook_action, action_id, request_id, request_to_ids)
  });
}

function setupSocialTracking() {
  try {
    if (FB && FB.Event && FB.Event.subscribe) {
      FB.Event.subscribe('edge.create', function (targetUrl) {
        _gaq.push(['_trackSocial', 'facebook', 'like', targetUrl]);
        //Google doesn't export social event data yet, so we have to track social actions as events too
        _gaq.push(['_trackEvent', 'facebook', 'like', targetUrl]);
        setupSocialTrackingControllerRequest('like');
        inviteToShareOnTwitter();
      });
      FB.Event.subscribe('edge.remove', function (targetUrl) {
        _gaq.push(['_trackSocial', 'facebook', 'unlike', targetUrl]);
        _gaq.push(['_trackEvent', 'facebook', 'unlike', targetUrl]);
      });
    }
  } catch (e) {
  }
}

function submitFacebookAction() {
  FB.login(function (response) {
    if (response.authResponse) {
      FB.api(
        '/me/watchdognet:sign',
        'post',
        {
          petition: $('meta[property="og:url"]').attr("content")
        },
        function (response) {
          if (!response || response.error) {
            $('.fb_share_message').text("Please try again.");
          } else {
            setupSocialTrackingControllerRequest('share', response.id, '', '');
          }
        }
      );
    } else {
      $('.fb_share_message').hide();
    }
  }, {scope: 'publish_actions'});
}

function setupShareFacebookButton() {
  var shareButton = $('.fb_share.btn');
  shareButton.click(function (event) {
    $('.fb_share_message').text("Connecting to Facebook...");
    $('.fb_share_message').show();
    submitFacebookAction();
  });
}

function preventWhitespaceOn(input) {
  $(input).change(function () {
    this.value = this.value.replace(/ /g, '');
  });
}

function EmailSuggestions() {
  var self = this;
  var $email = $('#signature_email');
  var $hint = $("#hint");

  this.init = function () {
    $('.suggested_email').live("click", function () {
      $email.val($(this).html());
      $hint.css('display', 'none');
      return false;
    });
  };

  this.mailCheckSuggestions = function (event) {
    $hint.css('display', 'none');
    $email.mailcheck({
      //annoyingly, mailcheck doesn't let you add to their default list of domains, so we have to duplicate them all here.
      domains:["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
        "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
        "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com", "q.com"],
      suggested:function (element, suggestion) {
        event.go = true;
        if (!$hint.html()) {
          var suggestionFragment = 'Did you mean ' + suggestion.full + "?" +
            "
Click the '" + $("#sign_petition").val() + "' button again if your address is correct";
          $hint.html(suggestionFragment).fadeIn(150);
          event.go = false;
        }
      }
    });
  };
}

function initTwitter() {
  var js, fjs = document.getElementsByTagName("script")[0];
  if (!document.getElementById("twitter-wjs")) {
    js = document.createElement("script");
    js.id = "twitter-wjs";
    js.src = "//platform.twitter.com/widgets.js";
    fjs.parentNode.insertBefore(js, fjs);
  }
}

function bindFacebookPopupButton() {

  function openPopup() {
    var sharer = "https://www.facebook.com/sharer/sharer.php?u=";
    var domain = location.href.replace(/\?.*/,"");
    var memberHash = $.cookie('member_id');
    var url = sharer + encodeURIComponent(domain + '?share_ref=' + memberHash);
    window.open(url , 'sharer', 'width=626,height=436');
  }

  $('.fb_popup_btn').click(function() {
    openPopup();
    setupSocialTrackingControllerRequest('popup');
    inviteToShareOnTwitter();
    $('.giantbox').hide();
  });
}

function bindFacebookDialogButton() {
  function getProperty(propertyName) {
    return encodeURIComponent($('meta[property="' + propertyName + '"]').attr('content'));
  }

  function openDialog() {
    var domain = location.href.replace(/\?.*/,"");
    var memberHash = $.cookie('member_id');
    var link = [domain, '?fd=', memberHash].join('');
    var dialog = "https://www.facebook.com/dialog/feed?" +
      "app_id=" + getProperty('fb:app_id') + "&" +
      "link=" + encodeURIComponent(link) + "&" +
      "picture=" + getProperty('og:image') + "&" +
      "name=" + getProperty('og:title') + "&" +
      "description=" + getProperty('og:description') + "&" +
      "redirect_uri=" + location.href + "&" +
      "display=popup";
    window.open(dialog , 'dialog', 'width=626,height=436');
  }
  $('.fb_dialog_btn').click(function() {
    openDialog();
    setupSocialTrackingControllerRequest('dialog');
    inviteToShareOnTwitter();
    $('.giantbox').hide();
  });
}

function bindFacebookRequestButton() {

  function requestCallbackForSendRequest(response) {
    if(response && response.request) {
      setupSocialTrackingControllerRequest('request', '', response.request, response.to);
    }
    inviteToShareOnTwitter();
  }

  function sendRequestViaMultiFriendSelector() {
    FB.ui({method: 'apprequests',
      message: VK.petition_title
    }, requestCallbackForSendRequest);
  }

  $('.fb_request_btn').click(sendRequestViaMultiFriendSelector);
}

function bindFacebookRequestAutofillFriendsButton() {
  function requestCallbackForAutofillFriendsRequest(response) {
    if(response && response.request) {
      setupSocialTrackingControllerRequest('autofill_request', '', response.request, '');
    }
    inviteToShareOnTwitter();
  }

  function sendAutofillFriendRequests() {
    FB.ui({method: 'apprequests',
      message: VK.petition_title,
      to: VK.facebook_friend_ids
    }, requestCallbackForAutofillFriendsRequest);
  }

  $('.fb_autofill_request_btn').click(sendAutofillFriendRequests);
}

function wasSigned() {
  return $("#petition_page").hasClass("was_signed");
}

function drawModalAfterSigning() {
  var modal = $("#thanksModal");
  if (screen.width > 480 && modal.length && wasSigned()) {
    modal.modal('toggle');
    if (($(".modal_centered_hide-sidebar").length > 0) || ($(".modal_left_hide-sidebar").length > 0) || ($(".modal_sidebar_hide-sidebar").length > 0)) {
      $(".secondary").css("display", "none");
      $('#thanksModal').on('hidden', function () {
        $(".secondary").css("display", "block");
      });
    }
  }
}

function mobileSignErrorHandling() {
  if (($('.sidebar_test').find('.help-inline').length > 0) && (screen.width < 768)) {
    $('.sidebar_test').show();
  }
}

function initMobileSign() {
  $('.mobile_signup_button').click(function() {
    $('.sidebar_test').show();
    $('body').animate({scrollTop:'40px'}, '0');
    return false;
  });
}

function initShowPetition() {
  mobileSignErrorHandling();
  initMobileSign();
  preventWhitespaceOn('#signature_email');
  setupSocialTracking();

  $('form').on("submit", function (event) {
    if (!VK.signing_from_email) {
      var emailSuggestor = new EmailSuggestions();
      emailSuggestor.init();
      emailSuggestor.mailCheckSuggestions(event);
    }
    return event.go;
  });
}

function initModalColor() {
  if (VK.modal_coloring === 'white-box-on-blackout') {
    $('body').addClass('blackout_modal');
  }
}

function updateCounter() {
  $('.tickcounter').each(function(idx, tc) {
    var counter = parseInt(tc.innerHTML.replace(/,/g, ''), 10) + 1;
    counter = counter.toString();
    var addcomma = /(\d+)(\d{3})/;
    while (addcomma.test(counter)) {
      counter = counter.replace(addcomma, '$1' + ',' + '$2');
    }
    tc.innerHTML = counter;
  });
  setTimeout(updateCounter, 1500);
}

function initSharePetition() {
  initModalColor();
  initFacebookApp();
  setupSocialTracking();
  setupShareFacebookButton();
  bindFacebookPopupButton();
  bindFacebookDialogButton();
  bindFacebookRequestButton();
  bindFacebookRequestAutofillFriendsButton();
  bindFacebookRecommendationButton();
  if ($("#mobile_thanks").length > 0 && wasSigned()) {
    $('body').animate({ scrollTop: '-40px' }, '0');
  }
  if ($('.tickcounter').length > 0) { updateCounter(); }
}

function toggleUserCanSignPetition(enabledFlag) {
  var CLASSES = "btn-danger btn-primary";
  if (enabledFlag) {
    $("#sign_petition").removeAttr("disabled").addClass(CLASSES);
  } else {
    $("#sign_petition").attr("disabled", "disabled").removeClass(CLASSES);
  }
}

function clearAllSignatureErrors() {
  $(".control-group").removeClass("error").find(".alert-error").remove();
}

function updatePageToReflectUserSignature(data) {
  clearAllSignatureErrors();
  VK.signature_id = data.signature_id;
  $("#petition_page").removeClass("not_signed").addClass("was_signed");
  $("#thanks-for-signing-message.thanks_first_name").text(data.member.first_name);
  if (window.history && window.history.pushState) {
    window.history.pushState({}, "", data.url);
  }
}

function indicateUserPetitionSignedAfterAjax(data) {
  $('.progress_box').hide();
  updatePageToReflectUserSignature(data);
  initSharePetition();
  drawModalAfterSigning();
}

function shareAfterUserPetitionSigned(data) {
  updatePageToReflectUserSignature(data);
  initSharePetition();
  $("#thanksModal .btn.share").click();
}

function indicateUserSignatureFailedAfterAjax(response) {
  clearAllSignatureErrors();
  var data = JSON.parse(response.responseText);
  for (var field in data) {
    var htmlField = $("[name='signature[" + field + "]']"),
        error = $("").addClass("help-inline alert alert-error").text(data[field][0]);
    htmlField.closest(".control-group").addClass("error").append(error);
  }
  toggleUserCanSignPetition(true);
}

$(document).ready(function() {
  $("#sign_petition, #sign_petition_and_share").click(function(evt) {
    var button = $(this),
        form = button.closest("form");

    evt.preventDefault();
    toggleUserCanSignPetition(false);

    $.ajax({
      type: "post",
      url: form.attr("action"),
      data: form.serialize()
    }).success(
      button.attr("id") === "sign_petition_and_share" ?
        shareAfterUserPetitionSigned : indicateUserPetitionSignedAfterAjax
    ).fail(
      indicateUserSignatureFailedAfterAjax
    );
  });
});
<commit_msg>fix line length breaking lint on petition.js<commit_after>function inviteToShareOnTwitter() {
  return;
}

function trackFacebookStatus(facebookStatus) {
  $.ajax({
    type: 'post',
    url: VK.social_tracking_url,
    data: {facebook_action: "status", facebook_status: facebookStatus.status}
  });
}

function initFacebookApp() {
  var appId = $('meta[property="fb:app_id"]').attr('content');
  FB.init({
    appId: appId,
    status: true, // check login status
    cookie: true, // enable cookies to allow the server to access the session
    xfbml: true,  // parse XFBML
    frictionless: true // for facebook request dialog
  });

  if (FB.getLoginStatus) { FB.getLoginStatus(trackFacebookStatus); }
}

function socialTrackingParams(facebook_action, action_id, request_id, request_to_ids) {
  var params = { petition_id: VK.petition_id, facebook_action: facebook_action };

  if (VK.signature_id !== "") { params.signature_id = VK.signature_id; }
  if (action_id !== "")       { params.action_id = action_id; }
  if (request_id !== "")      { params.request_id = request_id; }
  if (request_to_ids !== "")  { params.friend_ids = request_to_ids; }

  return params;
}

function setupSocialTrackingControllerRequest(facebook_action, action_id, request_id, request_to_ids) {
  $.ajax({
    type: 'post',
    url: VK.social_tracking_url,
    data: socialTrackingParams(facebook_action, action_id, request_id, request_to_ids)
  });
}

function setupSocialTracking() {
  try {
    if (FB && FB.Event && FB.Event.subscribe) {
      FB.Event.subscribe('edge.create', function (targetUrl) {
        _gaq.push(['_trackSocial', 'facebook', 'like', targetUrl]);
        //Google doesn't export social event data yet, so we have to track social actions as events too
        _gaq.push(['_trackEvent', 'facebook', 'like', targetUrl]);
        setupSocialTrackingControllerRequest('like');
        inviteToShareOnTwitter();
      });
      FB.Event.subscribe('edge.remove', function (targetUrl) {
        _gaq.push(['_trackSocial', 'facebook', 'unlike', targetUrl]);
        _gaq.push(['_trackEvent', 'facebook', 'unlike', targetUrl]);
      });
    }
  } catch (e) {
  }
}

function submitFacebookAction() {
  FB.login(function (response) {
    if (response.authResponse) {
      FB.api(
        '/me/watchdognet:sign',
        'post',
        {
          petition: $('meta[property="og:url"]').attr("content")
        },
        function (response) {
          if (!response || response.error) {
            $('.fb_share_message').text("Please try again.");
          } else {
            setupSocialTrackingControllerRequest('share', response.id, '', '');
          }
        }
      );
    } else {
      $('.fb_share_message').hide();
    }
  }, {scope: 'publish_actions'});
}

function setupShareFacebookButton() {
  var shareButton = $('.fb_share.btn');
  shareButton.click(function (event) {
    $('.fb_share_message').text("Connecting to Facebook...");
    $('.fb_share_message').show();
    submitFacebookAction();
  });
}

function preventWhitespaceOn(input) {
  $(input).change(function () {
    this.value = this.value.replace(/ /g, '');
  });
}

function EmailSuggestions() {
  var self = this;
  var $email = $('#signature_email');
  var $hint = $("#hint");

  this.init = function () {
    $('.suggested_email').live("click", function () {
      $email.val($(this).html());
      $hint.css('display', 'none');
      return false;
    });
  };

  this.mailCheckSuggestions = function (event) {
    $hint.css('display', 'none');
    $email.mailcheck({
      //annoyingly, mailcheck doesn't let you add to their default list of domains, so we have to duplicate them all here.
      domains:["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
        "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
        "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com", "q.com"],
      suggested:function (element, suggestion) {
        event.go = true;
        if (!$hint.html()) {
          var suggestionFragment = 'Did you mean ' + suggestion.full + "?" +
            "
Click the '" + $("#sign_petition").val() + "' button again if your address is correct";
          $hint.html(suggestionFragment).fadeIn(150);
          event.go = false;
        }
      }
    });
  };
}

function initTwitter() {
  var js, fjs = document.getElementsByTagName("script")[0];
  if (!document.getElementById("twitter-wjs")) {
    js = document.createElement("script");
    js.id = "twitter-wjs";
    js.src = "//platform.twitter.com/widgets.js";
    fjs.parentNode.insertBefore(js, fjs);
  }
}

function bindFacebookPopupButton() {

  function openPopup() {
    var sharer = "https://www.facebook.com/sharer/sharer.php?u=";
    var domain = location.href.replace(/\?.*/,"");
    var memberHash = $.cookie('member_id');
    var url = sharer + encodeURIComponent(domain + '?share_ref=' + memberHash);
    window.open(url , 'sharer', 'width=626,height=436');
  }

  $('.fb_popup_btn').click(function() {
    openPopup();
    setupSocialTrackingControllerRequest('popup');
    inviteToShareOnTwitter();
    $('.giantbox').hide();
  });
}

function bindFacebookDialogButton() {
  function getProperty(propertyName) {
    return encodeURIComponent($('meta[property="' + propertyName + '"]').attr('content'));
  }

  function openDialog() {
    var domain = location.href.replace(/\?.*/,"");
    var memberHash = $.cookie('member_id');
    var link = [domain, '?fd=', memberHash].join('');
    var dialog = "https://www.facebook.com/dialog/feed?" +
      "app_id=" + getProperty('fb:app_id') + "&" +
      "link=" + encodeURIComponent(link) + "&" +
      "picture=" + getProperty('og:image') + "&" +
      "name=" + getProperty('og:title') + "&" +
      "description=" + getProperty('og:description') + "&" +
      "redirect_uri=" + location.href + "&" +
      "display=popup";
    window.open(dialog , 'dialog', 'width=626,height=436');
  }
  $('.fb_dialog_btn').click(function() {
    openDialog();
    setupSocialTrackingControllerRequest('dialog');
    inviteToShareOnTwitter();
    $('.giantbox').hide();
  });
}

function bindFacebookRequestButton() {

  function requestCallbackForSendRequest(response) {
    if(response && response.request) {
      setupSocialTrackingControllerRequest('request', '', response.request, response.to);
    }
    inviteToShareOnTwitter();
  }

  function sendRequestViaMultiFriendSelector() {
    FB.ui({method: 'apprequests',
      message: VK.petition_title
    }, requestCallbackForSendRequest);
  }

  $('.fb_request_btn').click(sendRequestViaMultiFriendSelector);
}

function bindFacebookRequestAutofillFriendsButton() {
  function requestCallbackForAutofillFriendsRequest(response) {
    if(response && response.request) {
      setupSocialTrackingControllerRequest('autofill_request', '', response.request, '');
    }
    inviteToShareOnTwitter();
  }

  function sendAutofillFriendRequests() {
    FB.ui({method: 'apprequests',
      message: VK.petition_title,
      to: VK.facebook_friend_ids
    }, requestCallbackForAutofillFriendsRequest);
  }

  $('.fb_autofill_request_btn').click(sendAutofillFriendRequests);
}

function wasSigned() {
  return $("#petition_page").hasClass("was_signed");
}

function drawModalAfterSigning() {
  var modal = $("#thanksModal");
  if (screen.width > 480 && modal.length && wasSigned()) {
    modal.modal('toggle');
    if (($(".modal_centered_hide-sidebar").length > 0) || +
        ($(".modal_left_hide-sidebar").length > 0) || +
        ($(".modal_sidebar_hide-sidebar").length > 0)) {
      $(".secondary").css("display", "none");
      $('#thanksModal').on('hidden', function () {
        $(".secondary").css("display", "block");
      });
    }
  }
}

function mobileSignErrorHandling() {
  if (($('.sidebar_test').find('.help-inline').length > 0) && (screen.width < 768)) {
    $('.sidebar_test').show();
  }
}

function initMobileSign() {
  $('.mobile_signup_button').click(function() {
    $('.sidebar_test').show();
    $('body').animate({scrollTop:'40px'}, '0');
    return false;
  });
}

function initShowPetition() {
  mobileSignErrorHandling();
  initMobileSign();
  preventWhitespaceOn('#signature_email');
  setupSocialTracking();

  $('form').on("submit", function (event) {
    if (!VK.signing_from_email) {
      var emailSuggestor = new EmailSuggestions();
      emailSuggestor.init();
      emailSuggestor.mailCheckSuggestions(event);
    }
    return event.go;
  });
}

function initModalColor() {
  if (VK.modal_coloring === 'white-box-on-blackout') {
    $('body').addClass('blackout_modal');
  }
}

function updateCounter() {
  $('.tickcounter').each(function(idx, tc) {
    var counter = parseInt(tc.innerHTML.replace(/,/g, ''), 10) + 1;
    counter = counter.toString();
    var addcomma = /(\d+)(\d{3})/;
    while (addcomma.test(counter)) {
      counter = counter.replace(addcomma, '$1' + ',' + '$2');
    }
    tc.innerHTML = counter;
  });
  setTimeout(updateCounter, 1500);
}

function initSharePetition() {
  initModalColor();
  initFacebookApp();
  setupSocialTracking();
  setupShareFacebookButton();
  bindFacebookPopupButton();
  bindFacebookDialogButton();
  bindFacebookRequestButton();
  bindFacebookRequestAutofillFriendsButton();
  bindFacebookRecommendationButton();
  if ($("#mobile_thanks").length > 0 && wasSigned()) {
    $('body').animate({ scrollTop: '-40px' }, '0');
  }
  if ($('.tickcounter').length > 0) { updateCounter(); }
}

function toggleUserCanSignPetition(enabledFlag) {
  var CLASSES = "btn-danger btn-primary";
  if (enabledFlag) {
    $("#sign_petition").removeAttr("disabled").addClass(CLASSES);
  } else {
    $("#sign_petition").attr("disabled", "disabled").removeClass(CLASSES);
  }
}

function clearAllSignatureErrors() {
  $(".control-group").removeClass("error").find(".alert-error").remove();
}

function updatePageToReflectUserSignature(data) {
  clearAllSignatureErrors();
  VK.signature_id = data.signature_id;
  $("#petition_page").removeClass("not_signed").addClass("was_signed");
  $("#thanks-for-signing-message.thanks_first_name").text(data.member.first_name);
  if (window.history && window.history.pushState) {
    window.history.pushState({}, "", data.url);
  }
}

function indicateUserPetitionSignedAfterAjax(data) {
  $('.progress_box').hide();
  updatePageToReflectUserSignature(data);
  initSharePetition();
  drawModalAfterSigning();
}

function shareAfterUserPetitionSigned(data) {
  updatePageToReflectUserSignature(data);
  initSharePetition();
  $("#thanksModal .btn.share").click();
}

function indicateUserSignatureFailedAfterAjax(response) {
  clearAllSignatureErrors();
  var data = JSON.parse(response.responseText);
  for (var field in data) {
    var htmlField = $("[name='signature[" + field + "]']"),
        error = $("").addClass("help-inline alert alert-error").text(data[field][0]);
    htmlField.closest(".control-group").addClass("error").append(error);
  }
  toggleUserCanSignPetition(true);
}

$(document).ready(function() {
  $("#sign_petition, #sign_petition_and_share").click(function(evt) {
    var button = $(this),
        form = button.closest("form");

    evt.preventDefault();
    toggleUserCanSignPetition(false);

    $.ajax({
      type: "post",
      url: form.attr("action"),
      data: form.serialize()
    }).success(
      button.attr("id") === "sign_petition_and_share" ?
        shareAfterUserPetitionSigned : indicateUserPetitionSignedAfterAjax
    ).fail(
      indicateUserSignatureFailedAfterAjax
    );
  });
});
<|endoftext|>


<commit_before>"use strict";

/**
 * Mapper that automatically maps keyboard / gamepad input to different player numbers.
 * This can be used to implement keyboard / gamepad controls for a single player or a local
 * multiplayer game that allows players on the keyboard to play against players on gamepads.
 * Requires gamepad.js, mousetrap.js and mousetrap-global-bind.js to be included.
 * @constructor
 * @param {Object} callbackObj Object on which the callback functions will be called.
 * @param {number} maxPlayers Maximum number of players. If there are more active controllers
 * than this, then two controllers may be mapped to the same player.
 */
var InputMapper = function(callbackObj, maxPlayers) {
    this.gamepads = new Gamepad(this);
    this.callbackObj = callbackObj;
    this.maxPlayers = maxPlayers;
    this.resetPlayerMap();
    this.keysDown = []; // Keyboard keys that are currently down
    this.callbacks = []; // Callback information for mapping callbacks back to buttons
    this.upCallbacksForKey = {}; // Map from keys to lists of callbacks, so each key can have multiple callbacks
    this.downCallbacksForKey = {}; // Map from keys to lists of callbacks, so each key can have multiple callbacks
    this._defaultController = new InputMapper.Controller(InputMapper.GAMEPAD, 0);
};

// Controller types
InputMapper.GAMEPAD = 0;
InputMapper.KEYBOARD = 1;

/**
 * Helper class to store the controller config each player has.
 * @constructor
 * @param {number} controllerType Controller type: either InputMapper.GAMEPAD or InputMapper.KEYBOARD
 * @param {number} controllerIndex Controller index: in case of keyboard, index into the array of keyboard keys given
 * to addListener. In case of gamepad, index of the gamepad.
 */
InputMapper.Controller = function(controllerType, controllerIndex) {
    this.controllerType = controllerType;
    this.controllerIndex = controllerIndex;
    this.lastUsed = 0; // A timestamp for when this controller was last used
};

/**
 * Reset the map between controllers and player numbers.
 * @param {number?} maxPlayers Maximum player count. Default is to keep existing value.
 */
InputMapper.prototype.resetPlayerMap = function(maxPlayers) {
    if (maxPlayers !== undefined) {
        this.maxPlayers = maxPlayers;
    }
    this.players = []; // An array of arrays of controllers. Each player can have multiple controllers.
    for (var i = 0; i < this.maxPlayers; ++i) {
        this.players.push([]);
    }
};

/**
 * Update the controller state and call listeners based on that.
 */
InputMapper.prototype.update = function() {
    this.gamepads.update();
};

/**
 * Return a player index for a player using a given controller.
 * @param {number} controllerType Controller type: either InputMapper.GAMEPAD or InputMapper.KEYBOARD
 * @param {number} controllerIndex Controller index: in case of keyboard, index into the array of keyboard keys given
 * to addListener. In case of gamepad, index of the gamepad.
 */
InputMapper.prototype.getPlayerIndex = function(controllerType, controllerIndex) {
    for (var i = 0; i < this.players.length; ++i) {
        var player = this.players[i];
        for (var j = 0; j < player.length; ++j) {
            if (player[j].controllerType == controllerType && player[j].controllerIndex == controllerIndex) {
                player[j].lastUsed = Date.now();
                return i;
            }
        }
    }
    var controller = new InputMapper.Controller(controllerType, controllerIndex);
    controller.lastUsed = Date.now();
    // Map the controller for a player without a controller if there is one
    for (var i = 0; i < this.players.length; ++i) {
        var player = this.players[i];
        if (player.length === 0) {
            player.push(controller);
            return i;
        }
    }
    // Map the controller for the first player without this type of a controller
    for (var i = 0; i < this.players.length; ++i) {
        var player = this.players[i];
        var hasSameTypeController = false;
        for (var j = 0; j < player.length; ++j) {
            if (player[j].controllerType == controllerType) {
                hasSameTypeController = true;
            }
        }
        if (!hasSameTypeController) {
            player.push(controller);
            return i;
        }
    }
    // Just map the controller for the first player
    this.players[0].push(controller);
    return 0;
};

/**
 * @param {number} gamepadButton A button from Gamepad.BUTTONS
 * @param {Array} keyboardBindings List of bindings for different players, for example ['up', 'w']
 * @param {function=} downCallback Callback when the button is pressed down, that takes a player number as a parameter.
 * @param {function=} upCallback Callback when the button is released, that takes a player number as a parameter.
 */
InputMapper.prototype.addListener = function(gamepadButton, keyboardButtons, downCallback, upCallback) {
    var gamepadDownCallback = function(gamepadNumber) {
        var player = this.getPlayerIndex(InputMapper.GAMEPAD, gamepadNumber);
        if (downCallback !== undefined) {
            downCallback.call(this.callbackObj, player);
        }
    };
    var gamepadUpCallback = function(gamepadNumber) {
        var player = this.getPlayerIndex(InputMapper.GAMEPAD, gamepadNumber);
        if (upCallback !== undefined) {
            upCallback.call(this.callbackObj, player);
        }
    };
    this.gamepads.addButtonChangeListener(gamepadButton, gamepadDownCallback, gamepadUpCallback);

    var gamepadInstruction;

    if (gamepadButton < 100) {
        gamepadInstruction = Gamepad.BUTTON_INSTRUCTION[gamepadButton];
    } else {
        gamepadInstruction = Gamepad.BUTTON_INSTRUCTION[gamepadButton - 100];
    }

    if (downCallback !== undefined) {
        this.callbacks.push({key: gamepadInstruction, callback: downCallback, controllerType: InputMapper.GAMEPAD});
    }
    if (upCallback !== undefined) {
        this.callbacks.push({key: gamepadInstruction, callback: upCallback, controllerType: InputMapper.GAMEPAD});
    }

    var that = this;
    for (var i = 0; i < keyboardButtons.length; ++i) {
        (function(kbIndex) {
            if (!that.downCallbacksForKey.hasOwnProperty(keyboardButtons[kbIndex])) {
                that.keysDown[keyboardButtons[kbIndex]] = false;
                that.downCallbacksForKey[keyboardButtons[kbIndex]] = [];
                that.upCallbacksForKey[keyboardButtons[kbIndex]] = [];
                var keyDownCallback = function() {
                    var player = that.getPlayerIndex(InputMapper.KEYBOARD, kbIndex);
                    // Down events get generated multiple times while a key is down. Work around this.
                    if (!that.keysDown[keyboardButtons[kbIndex]]) {
                        that.keysDown[keyboardButtons[kbIndex]] = true;
                        var callbacksToCall = that.downCallbacksForKey[keyboardButtons[kbIndex]];
                        for (var i = 0; i < callbacksToCall.length; ++i) {
                            callbacksToCall[i].call(that.callbackObj, player);
                        }
                    }
                };
                var keyUpCallback = function() {
                    var player = that.getPlayerIndex(InputMapper.KEYBOARD, kbIndex);
                    that.keysDown[keyboardButtons[kbIndex]] = false;
                    var callbacksToCall = that.upCallbacksForKey[keyboardButtons[kbIndex]];
                    for (var i = 0; i < callbacksToCall.length; ++i) {
                        callbacksToCall[i].call(that.callbackObj, player);
                    }
                };
                window.Mousetrap.bindGlobal(keyboardButtons[kbIndex], keyDownCallback, 'keydown');
                window.Mousetrap.bindGlobal(keyboardButtons[kbIndex], keyUpCallback, 'keyup');
            }
            if (downCallback !== undefined) {
                that.downCallbacksForKey[keyboardButtons[kbIndex]].push(downCallback);
            }
            if (upCallback !== undefined) {
                that.upCallbacksForKey[keyboardButtons[kbIndex]].push(upCallback);
            }
        })(i);
        if (downCallback !== undefined) {
            this.callbacks.push({key: keyboardButtons[i], callback: downCallback, controllerType: InputMapper.KEYBOARD, kbIndex: i});
        }
        if (upCallback !== undefined) {
            this.callbacks.push({key: keyboardButtons[i], callback: upCallback, controllerType: InputMapper.KEYBOARD, kbIndex: i});
        }
    }
};

/**
 * Check if a given callback uses a given type of controller. Doesn't care about gamepad indices.
 * @protected
 * @param {InputMapper.Controller} controller
 * @param {Object} cbInfo Information on the callback, with keys controllerType and kbIndex in case of a keyboard.
 * @return {boolean} True if the given callback uses the given type of a controller.
 */
InputMapper._usesController = function(controller, cbInfo) {
    if (cbInfo.controllerType === controller.controllerType) {
        if (cbInfo.controllerType === InputMapper.KEYBOARD && controller.controllerIndex !== cbInfo.kbIndex) {
            // Each keyboard "controller" has different key bindings.
            return false;
        }
        return true;
    }
};

/**
 * From an array of controllers, determine the one that was most recently used.
 * @protected
 * @param {Array.<InputMapper.Controller>} player Array of controllers to check.
 * @return {InputMapper.Controller} The most recently used controller.
 */
InputMapper.prototype._getLastUsedController = function(player) {
    var controller;
    var lastUsed = 0;
    for (var j = 0; j < player.length; ++j) {
        if (player[j].lastUsed > lastUsed) {
            controller = player[j];
            lastUsed = player[j].lastUsed;
        }
    }
    return controller;
};

/**
 * Cycle the controller that is used for showing instructions by default.
 */
InputMapper.prototype.cycleDefaultControllerForInstruction = function() {
    if (this._defaultController.controllerType === InputMapper.KEYBOARD) {
        this._defaultController = new InputMapper.Controller(InputMapper.GAMEPAD, 0);
    } else {
        this._defaultController = new InputMapper.Controller(InputMapper.KEYBOARD, 0);
    }
};

/**
 * Get instruction for a key. Prioritizes gamepad over keyboard if keyboard hasn't been used. If you want to change the
 * controller which is prioritized, call cycleDefaultControllerForInstruction().
 * @param {function} callback A callback that has been previously attached to a button.
 * @param {playerIndex} index of the player to return information for. Set to undefined if the listener doesn't care
 * about the player number.
 * @return {string} String identifying the button for the player.
 */
InputMapper.prototype.getKeyInstruction = function(callback, playerIndex) {
    var controller;
    if (playerIndex !== undefined && this.players.length > playerIndex) {
        if (this.players[playerIndex].length > 0) {
            controller = this._getLastUsedController(this.players[playerIndex]);
        } else {
            // Gamepad instructions by default
            controller = this._defaultController;
        }
    }
    var returnStr = [];
    for (var i = 0; i < this.callbacks.length; ++i) {
        var cbInfo = this.callbacks[i];
        if (cbInfo.callback === callback) {
            if (controller === undefined) {
                // Listener doesn't care about the player number.
                // Determine all keys mapped to that callback from different controllers.
                for (var j = 0; j < this.players.length; ++j) {
                    for (var k = 0; k < this.players[j].length; ++k) {
                        if (InputMapper._usesController(this.players[j][k], cbInfo)) {
                            var hasInstruction = false;
                            var instruction = cbInfo.key.toUpperCase();
                            for (var l = 0; l < returnStr.length; ++l) {
                                if (returnStr[l] == instruction) {
                                    hasInstruction = true;
                                }
                            }
                            if (!hasInstruction) {
                                returnStr.push(instruction);
                            }
                        }
                    }
                }
            } else {
                if (InputMapper._usesController(controller, cbInfo)) {
                    return cbInfo.key.toUpperCase();
                }
            }
        }
    }
    if (controller === undefined) {
        return returnStr.join('/');
    }
    return '';
};
<commit_msg>Prevent default on key presses<commit_after>"use strict";

/**
 * Mapper that automatically maps keyboard / gamepad input to different player numbers.
 * This can be used to implement keyboard / gamepad controls for a single player or a local
 * multiplayer game that allows players on the keyboard to play against players on gamepads.
 * Requires gamepad.js, mousetrap.js and mousetrap-global-bind.js to be included.
 * @constructor
 * @param {Object} callbackObj Object on which the callback functions will be called.
 * @param {number} maxPlayers Maximum number of players. If there are more active controllers
 * than this, then two controllers may be mapped to the same player.
 */
var InputMapper = function(callbackObj, maxPlayers) {
    this.gamepads = new Gamepad(this);
    this.callbackObj = callbackObj;
    this.maxPlayers = maxPlayers;
    this.resetPlayerMap();
    this.keysDown = []; // Keyboard keys that are currently down
    this.callbacks = []; // Callback information for mapping callbacks back to buttons
    this.upCallbacksForKey = {}; // Map from keys to lists of callbacks, so each key can have multiple callbacks
    this.downCallbacksForKey = {}; // Map from keys to lists of callbacks, so each key can have multiple callbacks
    this._defaultController = new InputMapper.Controller(InputMapper.GAMEPAD, 0);
};

// Controller types
InputMapper.GAMEPAD = 0;
InputMapper.KEYBOARD = 1;

/**
 * Helper class to store the controller config each player has.
 * @constructor
 * @param {number} controllerType Controller type: either InputMapper.GAMEPAD or InputMapper.KEYBOARD
 * @param {number} controllerIndex Controller index: in case of keyboard, index into the array of keyboard keys given
 * to addListener. In case of gamepad, index of the gamepad.
 */
InputMapper.Controller = function(controllerType, controllerIndex) {
    this.controllerType = controllerType;
    this.controllerIndex = controllerIndex;
    this.lastUsed = 0; // A timestamp for when this controller was last used
};

/**
 * Reset the map between controllers and player numbers.
 * @param {number?} maxPlayers Maximum player count. Default is to keep existing value.
 */
InputMapper.prototype.resetPlayerMap = function(maxPlayers) {
    if (maxPlayers !== undefined) {
        this.maxPlayers = maxPlayers;
    }
    this.players = []; // An array of arrays of controllers. Each player can have multiple controllers.
    for (var i = 0; i < this.maxPlayers; ++i) {
        this.players.push([]);
    }
};

/**
 * Update the controller state and call listeners based on that.
 */
InputMapper.prototype.update = function() {
    this.gamepads.update();
};

/**
 * Return a player index for a player using a given controller.
 * @param {number} controllerType Controller type: either InputMapper.GAMEPAD or InputMapper.KEYBOARD
 * @param {number} controllerIndex Controller index: in case of keyboard, index into the array of keyboard keys given
 * to addListener. In case of gamepad, index of the gamepad.
 */
InputMapper.prototype.getPlayerIndex = function(controllerType, controllerIndex) {
    for (var i = 0; i < this.players.length; ++i) {
        var player = this.players[i];
        for (var j = 0; j < player.length; ++j) {
            if (player[j].controllerType == controllerType && player[j].controllerIndex == controllerIndex) {
                player[j].lastUsed = Date.now();
                return i;
            }
        }
    }
    var controller = new InputMapper.Controller(controllerType, controllerIndex);
    controller.lastUsed = Date.now();
    // Map the controller for a player without a controller if there is one
    for (var i = 0; i < this.players.length; ++i) {
        var player = this.players[i];
        if (player.length === 0) {
            player.push(controller);
            return i;
        }
    }
    // Map the controller for the first player without this type of a controller
    for (var i = 0; i < this.players.length; ++i) {
        var player = this.players[i];
        var hasSameTypeController = false;
        for (var j = 0; j < player.length; ++j) {
            if (player[j].controllerType == controllerType) {
                hasSameTypeController = true;
            }
        }
        if (!hasSameTypeController) {
            player.push(controller);
            return i;
        }
    }
    // Just map the controller for the first player
    this.players[0].push(controller);
    return 0;
};

/**
 * @param {number} gamepadButton A button from Gamepad.BUTTONS
 * @param {Array} keyboardBindings List of bindings for different players, for example ['up', 'w']
 * @param {function=} downCallback Callback when the button is pressed down, that takes a player number as a parameter.
 * @param {function=} upCallback Callback when the button is released, that takes a player number as a parameter.
 */
InputMapper.prototype.addListener = function(gamepadButton, keyboardButtons, downCallback, upCallback) {
    var gamepadDownCallback = function(gamepadNumber) {
        var player = this.getPlayerIndex(InputMapper.GAMEPAD, gamepadNumber);
        if (downCallback !== undefined) {
            downCallback.call(this.callbackObj, player);
        }
    };
    var gamepadUpCallback = function(gamepadNumber) {
        var player = this.getPlayerIndex(InputMapper.GAMEPAD, gamepadNumber);
        if (upCallback !== undefined) {
            upCallback.call(this.callbackObj, player);
        }
    };
    this.gamepads.addButtonChangeListener(gamepadButton, gamepadDownCallback, gamepadUpCallback);

    var gamepadInstruction;

    if (gamepadButton < 100) {
        gamepadInstruction = Gamepad.BUTTON_INSTRUCTION[gamepadButton];
    } else {
        gamepadInstruction = Gamepad.BUTTON_INSTRUCTION[gamepadButton - 100];
    }

    if (downCallback !== undefined) {
        this.callbacks.push({key: gamepadInstruction, callback: downCallback, controllerType: InputMapper.GAMEPAD});
    }
    if (upCallback !== undefined) {
        this.callbacks.push({key: gamepadInstruction, callback: upCallback, controllerType: InputMapper.GAMEPAD});
    }

    var that = this;
    for (var i = 0; i < keyboardButtons.length; ++i) {
        (function(kbIndex) {
            if (!that.downCallbacksForKey.hasOwnProperty(keyboardButtons[kbIndex])) {
                that.keysDown[keyboardButtons[kbIndex]] = false;
                that.downCallbacksForKey[keyboardButtons[kbIndex]] = [];
                that.upCallbacksForKey[keyboardButtons[kbIndex]] = [];
                var keyDownCallback = function(e) {
                    var player = that.getPlayerIndex(InputMapper.KEYBOARD, kbIndex);
                    // Down events get generated multiple times while a key is down. Work around this.
                    if (!that.keysDown[keyboardButtons[kbIndex]]) {
                        that.keysDown[keyboardButtons[kbIndex]] = true;
                        var callbacksToCall = that.downCallbacksForKey[keyboardButtons[kbIndex]];
                        for (var i = 0; i < callbacksToCall.length; ++i) {
                            callbacksToCall[i].call(that.callbackObj, player);
                        }
                    }
                    e.preventDefault();
                };
                var keyUpCallback = function(e) {
                    var player = that.getPlayerIndex(InputMapper.KEYBOARD, kbIndex);
                    that.keysDown[keyboardButtons[kbIndex]] = false;
                    var callbacksToCall = that.upCallbacksForKey[keyboardButtons[kbIndex]];
                    for (var i = 0; i < callbacksToCall.length; ++i) {
                        callbacksToCall[i].call(that.callbackObj, player);
                    }
                    e.preventDefault();
                };
                window.Mousetrap.bindGlobal(keyboardButtons[kbIndex], keyDownCallback, 'keydown');
                window.Mousetrap.bindGlobal(keyboardButtons[kbIndex], keyUpCallback, 'keyup');
            }
            if (downCallback !== undefined) {
                that.downCallbacksForKey[keyboardButtons[kbIndex]].push(downCallback);
            }
            if (upCallback !== undefined) {
                that.upCallbacksForKey[keyboardButtons[kbIndex]].push(upCallback);
            }
        })(i);
        if (downCallback !== undefined) {
            this.callbacks.push({key: keyboardButtons[i], callback: downCallback, controllerType: InputMapper.KEYBOARD, kbIndex: i});
        }
        if (upCallback !== undefined) {
            this.callbacks.push({key: keyboardButtons[i], callback: upCallback, controllerType: InputMapper.KEYBOARD, kbIndex: i});
        }
    }
};

/**
 * Check if a given callback uses a given type of controller. Doesn't care about gamepad indices.
 * @protected
 * @param {InputMapper.Controller} controller
 * @param {Object} cbInfo Information on the callback, with keys controllerType and kbIndex in case of a keyboard.
 * @return {boolean} True if the given callback uses the given type of a controller.
 */
InputMapper._usesController = function(controller, cbInfo) {
    if (cbInfo.controllerType === controller.controllerType) {
        if (cbInfo.controllerType === InputMapper.KEYBOARD && controller.controllerIndex !== cbInfo.kbIndex) {
            // Each keyboard "controller" has different key bindings.
            return false;
        }
        return true;
    }
};

/**
 * From an array of controllers, determine the one that was most recently used.
 * @protected
 * @param {Array.<InputMapper.Controller>} player Array of controllers to check.
 * @return {InputMapper.Controller} The most recently used controller.
 */
InputMapper.prototype._getLastUsedController = function(player) {
    var controller;
    var lastUsed = 0;
    for (var j = 0; j < player.length; ++j) {
        if (player[j].lastUsed > lastUsed) {
            controller = player[j];
            lastUsed = player[j].lastUsed;
        }
    }
    return controller;
};

/**
 * Cycle the controller that is used for showing instructions by default.
 */
InputMapper.prototype.cycleDefaultControllerForInstruction = function() {
    if (this._defaultController.controllerType === InputMapper.KEYBOARD) {
        this._defaultController = new InputMapper.Controller(InputMapper.GAMEPAD, 0);
    } else {
        this._defaultController = new InputMapper.Controller(InputMapper.KEYBOARD, 0);
    }
};

/**
 * Get instruction for a key. Prioritizes gamepad over keyboard if keyboard hasn't been used. If you want to change the
 * controller which is prioritized, call cycleDefaultControllerForInstruction().
 * @param {function} callback A callback that has been previously attached to a button.
 * @param {playerIndex} index of the player to return information for. Set to undefined if the listener doesn't care
 * about the player number.
 * @return {string} String identifying the button for the player.
 */
InputMapper.prototype.getKeyInstruction = function(callback, playerIndex) {
    var controller;
    if (playerIndex !== undefined && this.players.length > playerIndex) {
        if (this.players[playerIndex].length > 0) {
            controller = this._getLastUsedController(this.players[playerIndex]);
        } else {
            // Gamepad instructions by default
            controller = this._defaultController;
        }
    }
    var returnStr = [];
    for (var i = 0; i < this.callbacks.length; ++i) {
        var cbInfo = this.callbacks[i];
        if (cbInfo.callback === callback) {
            if (controller === undefined) {
                // Listener doesn't care about the player number.
                // Determine all keys mapped to that callback from different controllers.
                for (var j = 0; j < this.players.length; ++j) {
                    for (var k = 0; k < this.players[j].length; ++k) {
                        if (InputMapper._usesController(this.players[j][k], cbInfo)) {
                            var hasInstruction = false;
                            var instruction = cbInfo.key.toUpperCase();
                            for (var l = 0; l < returnStr.length; ++l) {
                                if (returnStr[l] == instruction) {
                                    hasInstruction = true;
                                }
                            }
                            if (!hasInstruction) {
                                returnStr.push(instruction);
                            }
                        }
                    }
                }
            } else {
                if (InputMapper._usesController(controller, cbInfo)) {
                    return cbInfo.key.toUpperCase();
                }
            }
        }
    }
    if (controller === undefined) {
        return returnStr.join('/');
    }
    return '';
};
<|endoftext|>


<commit_before>// module generate object code from ast
var Interpreter = function(ast) {
  this.ast = ast;
  this.objects = [];
  this.define = [];
  this.rules = {};
  this.computed = [];
  this.maxdepth = 100;
  this.depth = 0;
  this.maxobjects = 1000;
  this.objectnum = 0;
  this.minsize = .2;
  this.maxsize = 1.0;
  this.seed = 'initial'; // integer or 'initial'
  this.stack = [];
  this.currMatrix = new Matrix4().identity();
  this.currHex = Color('#ff0000');
  this.currHsv = _.extend(Color({ hue: 0, saturation: 1, value: 1 }), { computed: false });
  this.currBlend = { color: null, strength: 0, computed: false };
  this.currAlpha = 1;
  this.mt = new MersenneTwister();
}

// termination criteria
Interpreter.prototype.terminated = function() {
  if (this.objectnum > this.maxobjects) return true;
  if (this.depth >= this.maxdepth) return true;
  return false;
}

// stack current transformation state
Interpreter.prototype.pushState = function() {
  this.stack.push({
    matrix: this.currMatrix.clone(),
    hex: this.currHex.clone(),
    hsv: this.currHsv.clone(),
    blend: _.extend({}, this.currBlend),
    alpha: this.currAlpha
  });
}

// pull the parent transformation state
Interpreter.prototype.popState = function() {
  if (this.stack.length === 0) return;
  var state = this.stack.pop();
  this.currMatrix = state.matrix
  this.currHex = state.hex;
  this.currHsv = state.hsv;
  this.currBlend = state.blend;
  this.currAlpha = state.alpha;
}

// execute eisenscript
Interpreter.prototype.generate = function() {
  // rewriting ast
  var that = this;
  this.ast.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Rule: that.rewriteRule(statement); break;
    }
  });

  // pull the defines
  this.ast.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Define: that.define.push(statement); break;
      case Symbol.Set: that.define.push(statement); break;
      case Symbol.Statement: if (statement.computed) that.computed.push(statement); break;
    }
  });

  // creating intermediate code...
  // promise
  this.define.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Set:
        switch (statement.key) {
          case Condition.Maxdepth: that.maxdepth = statement.value; break;
          case Condition.Maxobjects: that.maxobjects = statement.value; break;
          case Condition.Minsize: that.minsize = statement.value; break;
          case Condition.Maxsize: that.maxsize = statement.value; break;
          case Condition.Seed: that.seed = statement.value; break;
        }
        break;
      case Symbol.Define:
        // not implemented
        break;
    }
  });

  // initial value is randomised chosen integer
  this.mt.setSeed(this.seed === 'initial' ? randInt(0, 65535) : this.seed);

  // pull the statement of system environment
  this.define.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Set:
        if (statement.key === Condition.Background) that.generateBackground(statement);
        break;
    }
  });

  // execute main
  this.parseStatements(this.computed);

  // return the intermediate code
  return this.objects;
}

// rewrite subtree related to rule statement
Interpreter.prototype.rewriteRule = function(rule) {
  rule.params.forEach(function(param) {
    if (param.type === Symbol.Modifier) {
      switch (param.key) {
        case Condition.Weight: rule.weight = param.value; break;
        case Condition.Maxdepth: rule.maxdepth = param.value; rule.alternate = param.alternate; break;
      }
    }
  });
  if (!this.rules[rule.id]) this.rules[rule.id] = [];
  this.rules[rule.id].push(rule);
}

// execute statements
Interpreter.prototype.parseStatements = function(statements) {
  var i = 0, len = statements.length;
  while (i < len) {
    if (this.terminated()) break;
    this.parseStatement(statements[i], 0);
    i++;
  }
}

// execute a statement
Interpreter.prototype.parseStatement = function(statement, index) {
  // parse transformation expression
  var expr = statement.exprs[index];
  if (expr) {
    this.depth++;
    this.pushState();
    for (var i = 0; i < expr.left; i++) {
      if (this.terminated()) break;
      this.parseTransformStatement(expr.right);
      // if statement.exprs[index + 1] is undefined, it would break the transformation loops.
      this.parseStatement(statement, index + 1);
    }
    this.popState();
    this.depth--;
    return;
  }
  // if not primitive, call rule and parse next transformation loops
  if (_.values(Primitive).indexOf(statement.id) === -1) {
    var rule = this.sampling(statement.id);
    if (rule) this.parseStatements(rule.body);
    return;
  }
  // achieve the end of nested transformation loops
  this.generatePrimitive(statement);
}

// break down transformation set
Interpreter.prototype.parseTransformStatement = function(transform) {
  var i = 0, len = transform.properties.length;
  while (i < len) {
    this.parseTransform(transform.properties[i]);
    i++;
  }
}

// parse transformation property
Interpreter.prototype.parseTransform = function(property) {
  var k = property.key, v = property.value;
  switch (k) {
    case Property.XShift:
      this.currMatrix.translate({ x:v, y:0, z:0 });
      break;
    case Property.YShift:
      this.currMatrix.translate({ x:0, y:v, z:0 });
      break;
    case Property.ZShift:
      this.currMatrix.translate({ x:0, y:0, z:v });
      break;
    case Property.RotateX:
      this.currMatrix.rotateX(degToRad(v));
      break;
    case Property.RotateY:
      this.currMatrix.rotateY(degToRad(v));
      break;
    case Property.RotateZ:
      this.currMatrix.rotateZ(degToRad(v));
      break;
    case Property.Size:
      this.currMatrix.scale({ x:v[0], y:v[1], z:v[2] });
      break;
    case Property.Matrix:
      // make 3x3 rotation matrix to 4x4 matrix
      // test: { m 1 0 0 0 .53 -.85 0 .85 .53 } box
      this.currMatrix.set(
        v[0], v[1], v[2], 0,
        v[3], v[4], v[5], 0,
        v[6], v[7], v[8], 0,
           0,    0,    0, 1
      );
      break;
    case Property.Color:
      var hex = v;
      if (hex === 'random') hex = sprintf('#%06s', Math.floor(this.mt.next() * 0xFFFFFF).toString(16));
      this.currHex = Color(hex);
      break;
    case Property.Hue:
      this.currHsv.computed = true;
      this.currHsv.hue += v % 360;
      break;
    case Property.Saturation:
      this.currHsv.computed = true;
      this.currHsv.saturation = clamp(this.currHsv.saturation * v, 0, 1);
      break;
    case Property.Brightness:
      this.currHsv.computed = true;
      this.currHsv.value = clamp(this.currHsv.value * v, 0, 1);;
      break;
    case Property.Blend:
      this.currBlend.computed = true;
      this.currBlend.color = property.color;
      this.currBlend.strength = this.currBlend.strength + clamp(property.strength, 0, 1);
      break;
    case Property.Alpha:
      this.currAlpha *= v;
      break;
  }
}

// create primitive object and stack it as intermediate code for renderer
Interpreter.prototype.generatePrimitive = function(statement) {
  // if achieved maxobjects
  this.objectnum++;
  if (this.terminated()) return;

  // blend the current color with the specified color
  if (this.currBlend.computed) {
    this.currHex = this.currHex.toHSV();
    var blend = Color(this.currBlend.color).toHSV();
    this.currHex.hue += (blend.hue - this.currHex.hue) * this.currBlend.strength / 6;
    this.currHex.hue %= 360;
  }

  // primitive object
  this.objects.push({
    type: Type.Primitive,
    name: statement.id,
    matrix: this.currMatrix.clone(),
    color: this.currHsv.computed ? this.currHex.blend(this.currHsv, 1).toCSS() : this.currHex.toCSS(),
    opacity: this.currAlpha
  });
}

// create background object code and stack it as intermediate code for renderer
Interpreter.prototype.generateBackground = function(statement) {
  this.objects.push({
    type: Type.Background,
    color: statement.value
  });
}

// randomly choose one of the rules according to their weights
Interpreter.prototype.sampling = function(name, retry) {
  if (!this.rules[name]) {
    // it is better to be reported with profiler or logger
    console.warn('eisenscript: undefined rule');
    return;
  }

  // sum weights of each rules
  var sum = 0;
  this.rules[name].forEach(function(rule) {
    rule.weight = rule.weight || 1;
    sum += rule.weight;
  });

  // choosing...
  var rand = this.mt.next() * sum;
  var chosen;
  for (var i = 0; i < this.rules[name].length; i++) {
    var rule = this.rules[name][i];
    if (rule.weight - rand < 0) {
      rand -= rule.weight
      continue;
    }
    chosen = rule;
    break;
  }

  // if rule could not be selected, interpreter tries to choose until 3 times
  if (!chosen) {
    retry = retry || 0;
    if (retry < 3) return this.sampling(name, ++retry);
    // if achieve max retry count
    return;
  }

  // if achieved maxdepth
  chosen.depth = (chosen.depth || 0) + 1;
  if (chosen.maxdepth && chosen.maxdepth < chosen.depth) {
    if (chosen.alternate) return this.sampling(chosen.alternate);
    return;
  }

  // the rule randomly chosen
  return chosen;
}<commit_msg>no reason to identify matrix. matrix have been already identified.<commit_after>// module generate object code from ast
var Interpreter = function(ast) {
  this.ast = ast;
  this.objects = [];
  this.define = [];
  this.rules = {};
  this.computed = [];
  this.maxdepth = 100;
  this.depth = 0;
  this.maxobjects = 1000;
  this.objectnum = 0;
  this.minsize = .2;
  this.maxsize = 1.0;
  this.seed = 'initial'; // integer or 'initial'
  this.stack = [];
  this.currMatrix = new Matrix4();
  this.currHex = Color('#ff0000');
  this.currHsv = _.extend(Color({ hue: 0, saturation: 1, value: 1 }), { computed: false });
  this.currBlend = { color: null, strength: 0, computed: false };
  this.currAlpha = 1;
  this.mt = new MersenneTwister();
}

// termination criteria
Interpreter.prototype.terminated = function() {
  if (this.objectnum > this.maxobjects) return true;
  if (this.depth >= this.maxdepth) return true;
  return false;
}

// stack current transformation state
Interpreter.prototype.pushState = function() {
  this.stack.push({
    matrix: this.currMatrix.clone(),
    hex: this.currHex.clone(),
    hsv: this.currHsv.clone(),
    blend: _.extend({}, this.currBlend),
    alpha: this.currAlpha
  });
}

// pull the parent transformation state
Interpreter.prototype.popState = function() {
  if (this.stack.length === 0) return;
  var state = this.stack.pop();
  this.currMatrix = state.matrix
  this.currHex = state.hex;
  this.currHsv = state.hsv;
  this.currBlend = state.blend;
  this.currAlpha = state.alpha;
}

// execute eisenscript
Interpreter.prototype.generate = function() {
  // rewriting ast
  var that = this;
  this.ast.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Rule: that.rewriteRule(statement); break;
    }
  });

  // pull the defines
  this.ast.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Define: that.define.push(statement); break;
      case Symbol.Set: that.define.push(statement); break;
      case Symbol.Statement: if (statement.computed) that.computed.push(statement); break;
    }
  });

  // creating intermediate code...
  // promise
  this.define.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Set:
        switch (statement.key) {
          case Condition.Maxdepth: that.maxdepth = statement.value; break;
          case Condition.Maxobjects: that.maxobjects = statement.value; break;
          case Condition.Minsize: that.minsize = statement.value; break;
          case Condition.Maxsize: that.maxsize = statement.value; break;
          case Condition.Seed: that.seed = statement.value; break;
        }
        break;
      case Symbol.Define:
        // not implemented
        break;
    }
  });

  // initial value is randomised chosen integer
  this.mt.setSeed(this.seed === 'initial' ? randInt(0, 65535) : this.seed);

  // pull the statement of system environment
  this.define.forEach(function(statement) {
    switch (statement.type) {
      case Symbol.Set:
        if (statement.key === Condition.Background) that.generateBackground(statement);
        break;
    }
  });

  // execute main
  this.parseStatements(this.computed);

  // return the intermediate code
  return this.objects;
}

// rewrite subtree related to rule statement
Interpreter.prototype.rewriteRule = function(rule) {
  rule.params.forEach(function(param) {
    if (param.type === Symbol.Modifier) {
      switch (param.key) {
        case Condition.Weight: rule.weight = param.value; break;
        case Condition.Maxdepth: rule.maxdepth = param.value; rule.alternate = param.alternate; break;
      }
    }
  });
  if (!this.rules[rule.id]) this.rules[rule.id] = [];
  this.rules[rule.id].push(rule);
}

// execute statements
Interpreter.prototype.parseStatements = function(statements) {
  var i = 0, len = statements.length;
  while (i < len) {
    if (this.terminated()) break;
    this.parseStatement(statements[i], 0);
    i++;
  }
}

// execute a statement
Interpreter.prototype.parseStatement = function(statement, index) {
  // parse transformation expression
  var expr = statement.exprs[index];
  if (expr) {
    this.depth++;
    this.pushState();
    for (var i = 0; i < expr.left; i++) {
      if (this.terminated()) break;
      this.parseTransformStatement(expr.right);
      // if statement.exprs[index + 1] is undefined, it would break the transformation loops.
      this.parseStatement(statement, index + 1);
    }
    this.popState();
    this.depth--;
    return;
  }
  // if not primitive, call rule and parse next transformation loops
  if (_.values(Primitive).indexOf(statement.id) === -1) {
    var rule = this.sampling(statement.id);
    if (rule) this.parseStatements(rule.body);
    return;
  }
  // achieve the end of nested transformation loops
  this.generatePrimitive(statement);
}

// break down transformation set
Interpreter.prototype.parseTransformStatement = function(transform) {
  var i = 0, len = transform.properties.length;
  while (i < len) {
    this.parseTransform(transform.properties[i]);
    i++;
  }
}

// parse transformation property
Interpreter.prototype.parseTransform = function(property) {
  var k = property.key, v = property.value;
  switch (k) {
    case Property.XShift:
      this.currMatrix.translate({ x:v, y:0, z:0 });
      break;
    case Property.YShift:
      this.currMatrix.translate({ x:0, y:v, z:0 });
      break;
    case Property.ZShift:
      this.currMatrix.translate({ x:0, y:0, z:v });
      break;
    case Property.RotateX:
      this.currMatrix.rotateX(degToRad(v));
      break;
    case Property.RotateY:
      this.currMatrix.rotateY(degToRad(v));
      break;
    case Property.RotateZ:
      this.currMatrix.rotateZ(degToRad(v));
      break;
    case Property.Size:
      this.currMatrix.scale({ x:v[0], y:v[1], z:v[2] });
      break;
    case Property.Matrix:
      // make 3x3 rotation matrix to 4x4 matrix
      // test: { m 1 0 0 0 .53 -.85 0 .85 .53 } box
      this.currMatrix.set(
        v[0], v[1], v[2], 0,
        v[3], v[4], v[5], 0,
        v[6], v[7], v[8], 0,
           0,    0,    0, 1
      );
      break;
    case Property.Color:
      var hex = v;
      if (hex === 'random') hex = sprintf('#%06s', Math.floor(this.mt.next() * 0xFFFFFF).toString(16));
      this.currHex = Color(hex);
      break;
    case Property.Hue:
      this.currHsv.computed = true;
      this.currHsv.hue += v % 360;
      break;
    case Property.Saturation:
      this.currHsv.computed = true;
      this.currHsv.saturation = clamp(this.currHsv.saturation * v, 0, 1);
      break;
    case Property.Brightness:
      this.currHsv.computed = true;
      this.currHsv.value = clamp(this.currHsv.value * v, 0, 1);;
      break;
    case Property.Blend:
      this.currBlend.computed = true;
      this.currBlend.color = property.color;
      this.currBlend.strength = this.currBlend.strength + clamp(property.strength, 0, 1);
      break;
    case Property.Alpha:
      this.currAlpha *= v;
      break;
  }
}

// create primitive object and stack it as intermediate code for renderer
Interpreter.prototype.generatePrimitive = function(statement) {
  // if achieved maxobjects
  this.objectnum++;
  if (this.terminated()) return;

  // blend the current color with the specified color
  if (this.currBlend.computed) {
    this.currHex = this.currHex.toHSV();
    var blend = Color(this.currBlend.color).toHSV();
    this.currHex.hue += (blend.hue - this.currHex.hue) * this.currBlend.strength / 6;
    this.currHex.hue %= 360;
  }

  // primitive object
  this.objects.push({
    type: Type.Primitive,
    name: statement.id,
    matrix: this.currMatrix.clone(),
    color: this.currHsv.computed ? this.currHex.blend(this.currHsv, 1).toCSS() : this.currHex.toCSS(),
    opacity: this.currAlpha
  });
}

// create background object code and stack it as intermediate code for renderer
Interpreter.prototype.generateBackground = function(statement) {
  this.objects.push({
    type: Type.Background,
    color: statement.value
  });
}

// randomly choose one of the rules according to their weights
Interpreter.prototype.sampling = function(name, retry) {
  if (!this.rules[name]) {
    // it is better to be reported with profiler or logger
    console.warn('eisenscript: undefined rule');
    return;
  }

  // sum weights of each rules
  var sum = 0;
  this.rules[name].forEach(function(rule) {
    rule.weight = rule.weight || 1;
    sum += rule.weight;
  });

  // choosing...
  var rand = this.mt.next() * sum;
  var chosen;
  for (var i = 0; i < this.rules[name].length; i++) {
    var rule = this.rules[name][i];
    if (rule.weight - rand < 0) {
      rand -= rule.weight
      continue;
    }
    chosen = rule;
    break;
  }

  // if rule could not be selected, interpreter tries to choose until 3 times
  if (!chosen) {
    retry = retry || 0;
    if (retry < 3) return this.sampling(name, ++retry);
    // if achieve max retry count
    return;
  }

  // if achieved maxdepth
  chosen.depth = (chosen.depth || 0) + 1;
  if (chosen.maxdepth && chosen.maxdepth < chosen.depth) {
    if (chosen.alternate) return this.sampling(chosen.alternate);
    return;
  }

  // the rule randomly chosen
  return chosen;
}<|endoftext|>


<commit_before>import React, { Component } from 'react';

class FontPreviewTyper extends Component {
  render() {
    let style = {
      fontFamily: this.props.fontName,
      fontSize: '20.0em',
      fontWeight: this.props.fontWeightSelected,
      border: 'none',
      marginTop: -40,
      marginLeft: -27,
      height: 400
    };

    return (

        <input defaultValue={this.props.message} type="text" style={style} />

    );
  }
}

export default FontPreviewTyper;<commit_msg>FontPreviewTyper message slice<commit_after>import React, { Component } from 'react';

class FontPreviewTyper extends Component {
  constructor(props){
    super(props);

    this.state = {
      msg: "",
      userUpdated: false
    };
  }

  componentDidMount(){
    this.updateMsg(this.props);
  }

  componentWillReceiveProps(newProps){
    this.updateMsg(newProps);
  }

  updateMsg(props){
    let { message } = this.props;

    if (!this.state.userUpdated){

      this.setState({
        msg: message
      });

    }
  }

  handleChange(e){
    this.setState({
      userUpdated: true,
      msg: e.value
    });
  }

  render() {
    let style = {
      fontFamily: this.props.fontName,
      fontSize: '20.0em',
      fontWeight: this.props.fontWeightSelected,
      border: 'none',
      marginTop: -40,
      marginLeft: -27,
      height: 400,
    };

    return (

        <input value={this.state.msg} type="text" onChange={this.handleChange.bind(this)} style={style} />

    );
  }
}

export default FontPreviewTyper;<|endoftext|>