<commit_before>"use strict";

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");

function dumpln(s) { dump(s + "\n"); }

const CHILD_SCRIPT = "chrome://domfuzzhelper/content/domfuzzhelper.js";

/*****************
 * API INJECTION *
 *****************/

// Based on:
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript

function DOMFuzzHelperObserver() {
  this._isFrameScriptLoaded = false;
}

// Use runSoon to avoid false-positive leaks due to content JS on the stack (?)
function quitFromContent() { dumpln("Page called quitApplication."); runSoon(quitOnce); }
function quitApplicationSoon() { dumpln("Page called quitApplicationSoon."); runOnTimer(quitOnce); }

DOMFuzzHelperObserver.prototype = {
  classDescription: "DOM fuzz helper observer",
  classID:          Components.ID("{73DD0F4A-B201-44A1-8C56-D1D72432B02A}"),
  contractID:       "@squarefree.com/dom-fuzz-helper-observer;1",
  _xpcom_categories: [{category: "profile-after-change", service: true }],

  //QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIObserver]),

  observe: function(aSubject, aTopic, aData) {
    if (aTopic == "profile-after-change") {
      this.init();
    } else if (!this.isFrameScriptLoaded && aTopic == "chrome-document-global-created") {

      var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                               getService(Ci.nsIMessageListenerManager || Ci.nsIChromeFrameMessageManager);

      // Register for any messages our API needs us to handle
      messageManager.addMessageListener("DOMFuzzHelper.quitApplication", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitApplicationSoon", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitWithLeakCheck", this);
      messageManager.addMessageListener("DOMFuzzHelper.setGCZeal", this);
      messageManager.addMessageListener("DOMFuzzHelper.getProfileDirectory", this);
      messageManager.addMessageListener("DOMFuzzHelper.getBinDirectory", this);
      messageManager.loadFrameScript(CHILD_SCRIPT, true);

      this.isFrameScriptLoaded = true;

    } else if (aTopic == "xpcom-shutdown") {
        this.uninit();
    }
  },

  init: function() {
    var obs = Services.obs;

    obs.addObserver(this, "xpcom-shutdown", false);
    obs.addObserver(this, "chrome-document-global-created", false);
  },

  uninit: function() {
    var obs = Services.obs;

    obs.removeObserver(this, "chrome-document-global-created", false);
  },

  /**
    * messageManager callback function
    * This will get requests from our API in the window and process them in chrome for it
    **/

  receiveMessage: function(aMessage) {
    switch(aMessage.name) {
      case "DOMFuzzHelper.quitApplication":
        quitFromContent();
        break;

      case "DOMFuzzHelper.quitApplicationSoon":
        quitApplicationSoon();
        break;

      case "DOMFuzzHelper.quitWithLeakCheck":
        quitWithLeakCheck();
        break;

      case "DOMFuzzHelper.setGCZeal":
        setGCZeal(aMessage.json.zeal);
        break;

      case "DOMFuzzHelper.getProfileDirectory":
        return getProfileDirectory();

      case "DOMFuzzHelper.getBinDirectory":
        return getBinDirectory();

      default:
        dumpln("Unrecognized message sent to domfuzzhelperobserver.js");

    }
  }
};

const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFuzzHelperObserver]);

/********************************************
 * MISC PRIVILEGED FUNCTIONS - MAIN PROCESS *
 ********************************************/

function runSoon(f)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
             .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function() {
      f();
    }
  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}

function setGCZeal(zeal)
{
  if (typeof(zeal) == "number") {
    Services.prefs.setIntPref("javascript.options.gczeal", zeal)
  }
}

function getProfileDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
  return d.path;
}

function getBinDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("CurProcD", Components.interfaces.nsIFile);
  return d.path;
}

/************************
 * QUIT WITH LEAK CHECK *
 ************************/

var quitting = false;

function quitWithLeakCheck(leaveWindowsOpen)
{
  // if not pref nglayout.debug.disable_xul_cache
  //   return

  leaveWindowsOpen = !!leaveWindowsOpen;

  // Magic string that domInteresting.py looks for
  var messagePrefix = "Leaked until " + (leaveWindowsOpen ? "tab close" : "shutdown");

  if (quitting)
    return;
  quitting = true;

  runSoon(a);
  function a() { dumpln("QA"); if (!leaveWindowsOpen) closeAllWindows(); runOnTimer(b); dumpln("QAA"); }
  function b() { dumpln("QB"); mpUntilDone(c); }
  function c() { dumpln("QC"); bloatStats(d); }
  function d(objectCounts) {
    dumpln("QD");

    // Mac normally has extra documents (due to the hidden window?)
    var isMac = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS == "Darwin";

    var expected = {
      'nsGlobalWindow':          4 + 6*leaveWindowsOpen,
      'nsDocument':              4 + 4*isMac + 24*leaveWindowsOpen,
      'nsDocShell':              5,
      'BackstagePass':           1,
      'nsGenericElement':        1927,
      'nsHTMLDivElement':        4,
      'xpc::CompartmentPrivate': 3,
    }

    for (var p in expected) {
      if (objectCounts[p] > expected[p]) {
        dumpln(messagePrefix + ": " + p + "(" + objectCounts[p] + " > " + expected[p] + ")");
      } else if (objectCounts[p] < expected[p]) {
        dumpln("That's odd"  + ": " + p + "(" + objectCounts[p] + " < " + expected[p] + ")");
      }
    }

    runSoon(e);
  }
  function e() { dumpln("QE"); quitOnce(); }
}

var timerDeathGrip;
function runOnTimer(f)
{
    timerDeathGrip = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    timerDeathGrip.initWithCallback({notify: function(){ timerDeathGrip=null; f(); }}, 4000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

function closeAllWindows()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);
  var enumerator = ww.getWindowEnumerator();

  var windowsToClose = [];

  while (enumerator.hasMoreElements()) {
    windowsToClose.push(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
  }

  // if not mac...
  ww.openWindow(null, "about:blank", null, "width=200,height=200", null);

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

  dumpln("1");
}

function mpUntilDone(callback)
{
  function mpUntilDoneInner()
  {
    dumpln("MP " + j);
    sendMemoryPressureNotification();

    ++j;
    if (j > 9)
      runSoon(callback);
    else if (j % 2 == 1 && typeof Components.utils.schedulePreciseGC == "function")
      Components.utils.schedulePreciseGC(mpUntilDoneInner)
    else
      runSoon(mpUntilDoneInner);
  }

  var j = 0;
  mpUntilDoneInner();
}

/*
     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev

*/
// Grab the class name and the number of remaining objects.
var bloatRex = /\s*\d+\s+(\S+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+.*/;
const SET_QUOTA = false;
const USE_QUOTA = false;

function bloatStats(callback)
{
  var objectCounts = {};

  try {
    //d.d.d;
    NetUtil.asyncFetch("about:bloat", fetched);
  } catch(e) {
    dumpln("Can't open about:bloat -- maybe you forgot to use XPCOM_MEM_LEAK_LOG");
    callback(objectCounts);
  }

  function fetched(aInputStream, aResult)
  {
    var r = NetUtil.readInputStreamToString(aInputStream, aInputStream.available());
    var lines = r.split("\n");
    for (var i = 0; i < lines.length; ++i)
    {
      var a = bloatRex.exec(lines[i]);
      if (a) {
        if (SET_QUOTA) {
          dumpln("'" + a[1] + "': " + a[2] + ",");
        } else if (USE_QUOTA) {
          var quotaA = QUOTA[a[1]] || 0;
          if (a[2] > quotaA) { dumpln("Leak? Too many " + a[1] + " (" + a[2] + " > " + quotaA + ")"); }
        }
        objectCounts[a[1]] = a[2];
      }
    }
    runSoon(callCallback);
  }

  function callCallback()
  {
    callback(objectCounts)
  }
}

/********
 * QUIT *
 ********/

// From quit.js, which Bob Clary extracted from mozilla/toolkit/content

function canQuitApplication()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService(Components.interfaces.nsIObserverService);
  if (!os)
  {
    return true;
  }

  try
 {
    var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
      .createInstance(Components.interfaces.nsISupportsPRBool);
    os.notifyObservers(cancelQuit, "quit-application-requested", null);

    // Something aborted the quit process.
    if (cancelQuit.data)
    {
      return false;
    }
  }
  catch (ex)
  {
  }
  os.notifyObservers(null, "quit-application-granted", null);
  return true;
}

function goQuitApplication()
{
  dumpln("goQuitApplication (domfuzzhelperobserver.js component)");

  if (!canQuitApplication())
  {
    return false;
  }

  var kAppStartup = '@mozilla.org/toolkit/app-startup;1';
  var kAppShell   = '@mozilla.org/appshell/appShellService;1';
  var   appService;
  var   forceQuit;

  if (kAppStartup in Components.classes)
  {
    appService = Components.classes[kAppStartup].
      getService(Components.interfaces.nsIAppStartup);
    forceQuit  = Components.interfaces.nsIAppStartup.eForceQuit;

  }
  else if (kAppShell in Components.classes)
  {
    appService = Components.classes[kAppShell].
      getService(Components.interfaces.nsIAppShellService);
    forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
  }
  else
  {
    throw 'goQuitApplication: no AppStartup/appShell';
  }

  var windowManager = Components.
    classes['@mozilla.org/appshell/window-mediator;1'].getService();

  var windowManagerInterface = windowManager.
    QueryInterface(Components.interfaces.nsIWindowMediator);

  var enumerator = windowManagerInterface.getEnumerator(null);

  while (enumerator.hasMoreElements())
  {
    var domWindow = enumerator.getNext();
    if (("tryToClose" in domWindow) && !domWindow.tryToClose())
    {
      return false;
    }
    domWindow.close();
  }

  try
  {
    appService.quit(forceQuit);
  }
  catch(ex)
  {
    throw('goQuitApplication: ' + ex);
  }

  return true;
}

var alreadyQuitting = false;
function quitOnce()
{
  if (!alreadyQuitting) {
    alreadyQuitting = true;
    goQuitApplication();
  }
}
<commit_msg>Remove unused code for setting gczeal through prefs. Since rev fc0df8c2f2bb, the content script has used getJSTestingFunctions instead.<commit_after>"use strict";

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");

function dumpln(s) { dump(s + "\n"); }

const CHILD_SCRIPT = "chrome://domfuzzhelper/content/domfuzzhelper.js";

/*****************
 * API INJECTION *
 *****************/

// Based on:
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript

function DOMFuzzHelperObserver() {
  this._isFrameScriptLoaded = false;
}

// Use runSoon to avoid false-positive leaks due to content JS on the stack (?)
function quitFromContent() { dumpln("Page called quitApplication."); runSoon(quitOnce); }
function quitApplicationSoon() { dumpln("Page called quitApplicationSoon."); runOnTimer(quitOnce); }

DOMFuzzHelperObserver.prototype = {
  classDescription: "DOM fuzz helper observer",
  classID:          Components.ID("{73DD0F4A-B201-44A1-8C56-D1D72432B02A}"),
  contractID:       "@squarefree.com/dom-fuzz-helper-observer;1",
  _xpcom_categories: [{category: "profile-after-change", service: true }],

  //QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIObserver]),

  observe: function(aSubject, aTopic, aData) {
    if (aTopic == "profile-after-change") {
      this.init();
    } else if (!this.isFrameScriptLoaded && aTopic == "chrome-document-global-created") {

      var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                               getService(Ci.nsIMessageListenerManager || Ci.nsIChromeFrameMessageManager);

      // Register for any messages our API needs us to handle
      messageManager.addMessageListener("DOMFuzzHelper.quitApplication", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitApplicationSoon", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitWithLeakCheck", this);
      messageManager.addMessageListener("DOMFuzzHelper.getProfileDirectory", this);
      messageManager.addMessageListener("DOMFuzzHelper.getBinDirectory", this);
      messageManager.loadFrameScript(CHILD_SCRIPT, true);

      this.isFrameScriptLoaded = true;

    } else if (aTopic == "xpcom-shutdown") {
        this.uninit();
    }
  },

  init: function() {
    var obs = Services.obs;

    obs.addObserver(this, "xpcom-shutdown", false);
    obs.addObserver(this, "chrome-document-global-created", false);
  },

  uninit: function() {
    var obs = Services.obs;

    obs.removeObserver(this, "chrome-document-global-created", false);
  },

  /**
    * messageManager callback function
    * This will get requests from our API in the window and process them in chrome for it
    **/

  receiveMessage: function(aMessage) {
    switch(aMessage.name) {
      case "DOMFuzzHelper.quitApplication":
        quitFromContent();
        break;

      case "DOMFuzzHelper.quitApplicationSoon":
        quitApplicationSoon();
        break;

      case "DOMFuzzHelper.quitWithLeakCheck":
        quitWithLeakCheck();
        break;

      case "DOMFuzzHelper.getProfileDirectory":
        return getProfileDirectory();

      case "DOMFuzzHelper.getBinDirectory":
        return getBinDirectory();

      default:
        dumpln("Unrecognized message sent to domfuzzhelperobserver.js");

    }
  }
};

const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFuzzHelperObserver]);

/********************************************
 * MISC PRIVILEGED FUNCTIONS - MAIN PROCESS *
 ********************************************/

function runSoon(f)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
             .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function() {
      f();
    }
  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}

function getProfileDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
  return d.path;
}

function getBinDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("CurProcD", Components.interfaces.nsIFile);
  return d.path;
}

/************************
 * QUIT WITH LEAK CHECK *
 ************************/

var quitting = false;

function quitWithLeakCheck(leaveWindowsOpen)
{
  // if not pref nglayout.debug.disable_xul_cache
  //   return

  leaveWindowsOpen = !!leaveWindowsOpen;

  // Magic string that domInteresting.py looks for
  var messagePrefix = "Leaked until " + (leaveWindowsOpen ? "tab close" : "shutdown");

  if (quitting)
    return;
  quitting = true;

  runSoon(a);
  function a() { dumpln("QA"); if (!leaveWindowsOpen) closeAllWindows(); runOnTimer(b); dumpln("QAA"); }
  function b() { dumpln("QB"); mpUntilDone(c); }
  function c() { dumpln("QC"); bloatStats(d); }
  function d(objectCounts) {
    dumpln("QD");

    // Mac normally has extra documents (due to the hidden window?)
    var isMac = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS == "Darwin";

    var expected = {
      'nsGlobalWindow':          4 + 6*leaveWindowsOpen,
      'nsDocument':              4 + 4*isMac + 24*leaveWindowsOpen,
      'nsDocShell':              5,
      'BackstagePass':           1,
      'nsGenericElement':        1927,
      'nsHTMLDivElement':        4,
      'xpc::CompartmentPrivate': 3,
    }

    for (var p in expected) {
      if (objectCounts[p] > expected[p]) {
        dumpln(messagePrefix + ": " + p + "(" + objectCounts[p] + " > " + expected[p] + ")");
      } else if (objectCounts[p] < expected[p]) {
        dumpln("That's odd"  + ": " + p + "(" + objectCounts[p] + " < " + expected[p] + ")");
      }
    }

    runSoon(e);
  }
  function e() { dumpln("QE"); quitOnce(); }
}

var timerDeathGrip;
function runOnTimer(f)
{
    timerDeathGrip = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    timerDeathGrip.initWithCallback({notify: function(){ timerDeathGrip=null; f(); }}, 4000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

function closeAllWindows()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);
  var enumerator = ww.getWindowEnumerator();

  var windowsToClose = [];

  while (enumerator.hasMoreElements()) {
    windowsToClose.push(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
  }

  // if not mac...
  ww.openWindow(null, "about:blank", null, "width=200,height=200", null);

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

  dumpln("1");
}

function mpUntilDone(callback)
{
  function mpUntilDoneInner()
  {
    dumpln("MP " + j);
    sendMemoryPressureNotification();

    ++j;
    if (j > 9)
      runSoon(callback);
    else if (j % 2 == 1 && typeof Components.utils.schedulePreciseGC == "function")
      Components.utils.schedulePreciseGC(mpUntilDoneInner)
    else
      runSoon(mpUntilDoneInner);
  }

  var j = 0;
  mpUntilDoneInner();
}

/*
     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev

*/
// Grab the class name and the number of remaining objects.
var bloatRex = /\s*\d+\s+(\S+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+.*/;
const SET_QUOTA = false;
const USE_QUOTA = false;

function bloatStats(callback)
{
  var objectCounts = {};

  try {
    //d.d.d;
    NetUtil.asyncFetch("about:bloat", fetched);
  } catch(e) {
    dumpln("Can't open about:bloat -- maybe you forgot to use XPCOM_MEM_LEAK_LOG");
    callback(objectCounts);
  }

  function fetched(aInputStream, aResult)
  {
    var r = NetUtil.readInputStreamToString(aInputStream, aInputStream.available());
    var lines = r.split("\n");
    for (var i = 0; i < lines.length; ++i)
    {
      var a = bloatRex.exec(lines[i]);
      if (a) {
        if (SET_QUOTA) {
          dumpln("'" + a[1] + "': " + a[2] + ",");
        } else if (USE_QUOTA) {
          var quotaA = QUOTA[a[1]] || 0;
          if (a[2] > quotaA) { dumpln("Leak? Too many " + a[1] + " (" + a[2] + " > " + quotaA + ")"); }
        }
        objectCounts[a[1]] = a[2];
      }
    }
    runSoon(callCallback);
  }

  function callCallback()
  {
    callback(objectCounts)
  }
}

/********
 * QUIT *
 ********/

// From quit.js, which Bob Clary extracted from mozilla/toolkit/content

function canQuitApplication()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService(Components.interfaces.nsIObserverService);
  if (!os)
  {
    return true;
  }

  try
 {
    var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
      .createInstance(Components.interfaces.nsISupportsPRBool);
    os.notifyObservers(cancelQuit, "quit-application-requested", null);

    // Something aborted the quit process.
    if (cancelQuit.data)
    {
      return false;
    }
  }
  catch (ex)
  {
  }
  os.notifyObservers(null, "quit-application-granted", null);
  return true;
}

function goQuitApplication()
{
  dumpln("goQuitApplication (domfuzzhelperobserver.js component)");

  if (!canQuitApplication())
  {
    return false;
  }

  var kAppStartup = '@mozilla.org/toolkit/app-startup;1';
  var kAppShell   = '@mozilla.org/appshell/appShellService;1';
  var   appService;
  var   forceQuit;

  if (kAppStartup in Components.classes)
  {
    appService = Components.classes[kAppStartup].
      getService(Components.interfaces.nsIAppStartup);
    forceQuit  = Components.interfaces.nsIAppStartup.eForceQuit;

  }
  else if (kAppShell in Components.classes)
  {
    appService = Components.classes[kAppShell].
      getService(Components.interfaces.nsIAppShellService);
    forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
  }
  else
  {
    throw 'goQuitApplication: no AppStartup/appShell';
  }

  var windowManager = Components.
    classes['@mozilla.org/appshell/window-mediator;1'].getService();

  var windowManagerInterface = windowManager.
    QueryInterface(Components.interfaces.nsIWindowMediator);

  var enumerator = windowManagerInterface.getEnumerator(null);

  while (enumerator.hasMoreElements())
  {
    var domWindow = enumerator.getNext();
    if (("tryToClose" in domWindow) && !domWindow.tryToClose())
    {
      return false;
    }
    domWindow.close();
  }

  try
  {
    appService.quit(forceQuit);
  }
  catch(ex)
  {
    throw('goQuitApplication: ' + ex);
  }

  return true;
}

var alreadyQuitting = false;
function quitOnce()
{
  if (!alreadyQuitting) {
    alreadyQuitting = true;
    goQuitApplication();
  }
}
<|endoftext|>


<commit_before>"use strict";

$(function () {
    $(document).ready(function () {
        //Initialization
        var allButtons  = $('button');                              //Selector for all buttons
        var tableBoxes  = $('td');                                  //Selector for all table
        var ttt = new TicTacToe();                                  //Instantiate TicTacToe Object

        ttt.changeMessageDisplay('Pick your piece!');               //Prompt for p1 piece

        //---------- Click Listeners ----------
        //Button Listener
        allButtons.click(function () {
            if($(this).html() == 'X'){
                ttt.player1.piece = 'x';
                ttt.player2.piece = 'o';
            }
            else if($(this).html() == 'O'){
                ttt.player1.piece = 'o';
                ttt.player2.piece = 'x';
            }
            ttt.displayMessage.removeClass('error');
            ttt.changeMessageDisplay('Make your move!')
            allButtons.hide();
        });

        //Box Listener
        tableBoxes.click(function () {
            var clickedBox = $(this);
            //Variable that holds the box clicked
            if(clickedBox.hasClass('clicked') || clickedBox.hasClass('locked')){
                return;
            }
            else {
                //If pieces !set yet, show error
                if(ttt.player1.piece === ''){
                    ttt.displayMessage.addClass('error');
                }
                //If not valid move and there are less than 9 turns played
                else if((!ttt.verifyMove(clickedBox)) && (ttt.turnsPlayed <= 9)){
                    //Display error message
                    ttt.changeMessageDisplay('Pick a different box!');
                    ttt.displayMessage.addClass('error');
                    /*
                     Highlight all available moves
                     border: red solid;
                     border: solid red 5px;
                     height: 100%;
                     width: 100%;
                     padding: initial;
                     margin: inherit;
                     */
                }
                //If valid move and still valid turn
                else if(ttt.verifyMove(clickedBox) && ttt.turnsPlayed <= 9){
                    ttt.displayMessage.removeClass('error');
                    ttt.changeMessageDisplay('Make your move!')
                    ttt.setBox(clickedBox);
                    if(ttt.checkWinner()){
                        ttt.showWinner();
                        ttt.reset(ttt);
                    }
                    else{
                        ttt.setNextTurn();
                    }
                }
            }
        });
    })
}(jQuery));<commit_msg>Finish Tie Logic. Bug fix that allowed the display to change after a game is over.<commit_after>"use strict";

$(function () {
    $(document).ready(function () {
        //Initialization
        var allButtons  = $('button');                              //Selector for all buttons
        var tableBoxes  = $('td');                                  //Selector for all table
        var ttt = new TicTacToe();                                  //Instantiate TicTacToe Object

        ttt.changeMessageDisplay('Pick your piece!');               //Prompt for p1 piece

        //---------- Click Listeners ----------
        //Button Listener
        allButtons.click(function () {
            if($(this).html() == 'X'){
                ttt.player1.piece = 'x';
                ttt.player2.piece = 'o';
            }
            else if($(this).html() == 'O'){
                ttt.player1.piece = 'o';
                ttt.player2.piece = 'x';
            }
            ttt.displayMessage.removeClass('error');
            ttt.changeMessageDisplay('Make your move!')
            allButtons.hide();
        });

        //Box Listener
        tableBoxes.click(function () {
            //Variable that holds the box clicked
            var clickedBox = $(this);

            //If marked or locked, prevent click event
            if(clickedBox.hasClass('locked') || (ttt.checkWinner())){
                return;
            }
            //If not valid move and there are less than 9 turns played, display error message
            else if(clickedBox.hasClass('clicked') && ttt.turnsPlayed < 9){
                ttt.changeMessageDisplay('Pick a different box!');
                ttt.displayMessage.addClass('error');
                /*
                 Highlight all available moves
                 border: red solid;
                 border: solid red 5px;
                 height: 100%;
                 width: 100%;
                 padding: initial;
                 margin: inherit;
                 */
            }
            else {
                //If pieces !set yet, show error
                if(ttt.player1.piece === ''){
                    ttt.displayMessage.addClass('error');
                }
                //If valid move and still valid turn
                else if (ttt.verifyMove(clickedBox) && ttt.turnsPlayed <= 9){
                    ttt.displayMessage.removeClass('error');
                    ttt.changeMessageDisplay('Make your move!')
                    ttt.setBox(clickedBox);

                    //If tied
                    if (ttt.turnsPlayed === 9 && !ttt.checkWinner()) {
                        ttt.showTie();
                        ttt.reset(ttt);
                    }
                    //If Winner
                    else if (ttt.checkWinner()){
                        ttt.showWinner();
                        ttt.reset(ttt);
                    }
                    //Vaild move
                    else
                        ttt.setNextTurn();
                }
            }
        });
    })
}(jQuery));<|endoftext|>


<commit_before>module.exports = StorageReader

function StorageReader (stateManager) {
  this._stateManager = stateManager
  this._storageCache = new Map()
}

const proto = StorageReader.prototype

proto.getContractStorage = function getContractStorage (address, key, cb) {
  const self = this
  const addressHex = address.toString('hex')
  const keyHex = key.toString('hex')

  self._stateManager.getContractStorage(address, key, function (err, current) {
    if (err) return cb(err)

    let map = null
    if (!self._storageCache.has(addressHex)) {
      map = new Map()
      self._storageCache.set(addressHex, map)
    } else {
      map = self._storageCache.get(addressHex)
    }

    let original = null

    if (map.has(keyHex)) {
      original = map.get(keyHex)
    } else {
      map.set(keyHex, current)
      original = current
    }

    cb(null, {
      original,
      current
    })
  })
}
<commit_msg>Migrate storageReader to es6 syntax<commit_after>module.exports = class StorageReader {
  constructor (stateManager) {
    this._stateManager = stateManager
    this._storageCache = new Map()
  }

  getContractStorage (address, key, cb) {
    const addressHex = address.toString('hex')
    const keyHex = key.toString('hex')

    this._stateManager.getContractStorage(address, key, (err, current) => {
      if (err) return cb(err)

      let map = null
      if (!this._storageCache.has(addressHex)) {
        map = new Map()
        this._storageCache.set(addressHex, map)
      } else {
        map = this._storageCache.get(addressHex)
      }

      let original = null

      if (map.has(keyHex)) {
        original = map.get(keyHex)
      } else {
        map.set(keyHex, current)
        original = current
      }

      cb(null, {
        original,
        current
      })
    })
  }
}
<|endoftext|>


<commit_before>/*jshint -W079 */

/***********************************************************
 * AIR QUALITY CORDOVA APPLICATION
 *
 * Developed by:
 * Julius Osokinas (j.osokinas@mappingforchange.org.uk)
 *
 * Copyright © 2015-2016 Mapping for Change
 **********************************************************/

'use strict';

/**
 * MAIN MODULE WITH INCLUDES
 */

var AQ = angular.module('AQ', [
  'templates',
  'ngSanitize',
  'ngTouch',
  'ngOpbeat',
  'ui.router',
  'angularMoment',
]);

/**
 * APP CONFIGURATION
 */

AQ.config(function (config, $httpProvider, $urlRouterProvider, $stateProvider, $opbeatProvider) {
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/json';
  $urlRouterProvider.when('', '/').otherwise('/404');

  // Get rid of trailing slash from the URL
  $urlRouterProvider.rule(function ($injector, $location) {
    var path = $location.url();

    if ('/' === path[path.length - 1]) {
      return path.replace(/\/$/, '');
    }

    if (path.indexOf('/?') > 0) {
      return path.replace('/?', '?');
    }

    return false;
  });

  // Configure states
  $stateProvider
    .state('index', {
      url: '/',
      views: {
        content: {
          controller: 'IndexController'
        }
      }
    })
    .state('404', {
      url: '/404',
      views: {
        content: {
          controller: '404Controller'
        }
      }
    })
    .state('redirect', {
      url: '/redirect',
      views: {
        content: {
          controller: 'RedirectController'
        }
      }
    })
    .state('register', {
      url: '/register',
      views: {
        content: {
          controller: 'RegisterController',
          templateUrl: 'partials/register.html'
        }
      }
    })
    .state('login', {
      url: '/login',
      views: {
        content: {
          controller: 'LoginController',
          templateUrl: 'partials/login.html'
        }
      }
    })
    .state('locations', {
      url: '/locations',
      views: {
        content: {
          controller: 'LocationsController',
          templateUrl: 'partials/locations.html'
        }
      }
    })
    .state('locations.add', {
      url: '/add',
      views: {
        subcontent: {
          controller: 'LocationsAddController',
          templateUrl: 'partials/locations-add.html'
        }
      }
    })
    .state('location', {
      url: '/location/:locationId',
      views: {
        content: {
          controller: 'LocationController',
          templateUrl: 'partials/location.html'
        }
      }
    });

  // Configure and install Opbeat
  if (config.opbeatOrgId.length > 0 && config.opbeatAppId.length > 0) {
    $opbeatProvider.config({
      orgId: config.opbeatOrgId,
      appId: config.opbeatAppId
    });

    $opbeatProvider.install();
  }
});

/**
 * APP RUNNER
 */

AQ.run(function ($window, $rootScope, config, viewport, data, state, oauth) {
  oauth.authorize();

  // Always redirect to Login state when user is not authenticated (unless it is a state for logging in or registering)
  $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    if (_.isEmpty(data.authentication) && ['register', 'login'].indexOf(toState.name) === -1) {
      event.preventDefault();
      state.redirect('login');
    }
  });

  // Save state history each time it changes
  $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
    state.saveHistory(toState.name, toParams, fromState.name, fromParams);
  });

  // Make viewport ready only when device is ready
  document.addEventListener('deviceready', function () {
    function exitApp() {
      $window.navigator.app.exitApp();
    }

    if (_.isEmpty(config)) {
      $window.navigator.notification.alert(
        'App configuration not set.',
        exitApp,
        'Error',
        'OK'
      );

      return;
    }

    if (!config.url) {
      $window.navigator.notification.alert(
        'Path to platform not set.',
        exitApp,
        'Error',
        'OK'
      );

      return;
    }

    config.url = config.url.replace(/\/$/, '');
    config.version = '@@version';
    viewport.platform = config.url;

    // Check if network connection is available on the run...
    if ($window.navigator.connection.type !== 'none') {
      $rootScope.$apply(function () {
        viewport.online = true;
        viewport.ready = true;
      });
    } else {
      $rootScope.$apply(function () {
        viewport.online = false;
        viewport.ready = true;
      });
    }

    // ...and while running the app
    document.addEventListener('online', function () {
      $rootScope.$apply(function () {
        viewport.online = true;
      });
    }, false);

    document.addEventListener('offline', function () {
      $rootScope.$apply(function () {
        viewport.online = false;
      });
    }, false);
  }, false);
});
<commit_msg>Allowing to exclude Opbeat configuration<commit_after>/*jshint -W079 */

/***********************************************************
 * AIR QUALITY CORDOVA APPLICATION
 *
 * Developed by:
 * Julius Osokinas (j.osokinas@mappingforchange.org.uk)
 *
 * Copyright © 2015-2016 Mapping for Change
 **********************************************************/

'use strict';

/**
 * MAIN MODULE WITH INCLUDES
 */

var AQ = angular.module('AQ', [
  'templates',
  'ngSanitize',
  'ngTouch',
  'ngOpbeat',
  'ui.router',
  'angularMoment',
]);

/**
 * APP CONFIGURATION
 */

AQ.config(function (config, $httpProvider, $urlRouterProvider, $stateProvider, $opbeatProvider) {
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/json';
  $urlRouterProvider.when('', '/').otherwise('/404');

  // Get rid of trailing slash from the URL
  $urlRouterProvider.rule(function ($injector, $location) {
    var path = $location.url();

    if ('/' === path[path.length - 1]) {
      return path.replace(/\/$/, '');
    }

    if (path.indexOf('/?') > 0) {
      return path.replace('/?', '?');
    }

    return false;
  });

  // Configure states
  $stateProvider
    .state('index', {
      url: '/',
      views: {
        content: {
          controller: 'IndexController'
        }
      }
    })
    .state('404', {
      url: '/404',
      views: {
        content: {
          controller: '404Controller'
        }
      }
    })
    .state('redirect', {
      url: '/redirect',
      views: {
        content: {
          controller: 'RedirectController'
        }
      }
    })
    .state('register', {
      url: '/register',
      views: {
        content: {
          controller: 'RegisterController',
          templateUrl: 'partials/register.html'
        }
      }
    })
    .state('login', {
      url: '/login',
      views: {
        content: {
          controller: 'LoginController',
          templateUrl: 'partials/login.html'
        }
      }
    })
    .state('locations', {
      url: '/locations',
      views: {
        content: {
          controller: 'LocationsController',
          templateUrl: 'partials/locations.html'
        }
      }
    })
    .state('locations.add', {
      url: '/add',
      views: {
        subcontent: {
          controller: 'LocationsAddController',
          templateUrl: 'partials/locations-add.html'
        }
      }
    })
    .state('location', {
      url: '/location/:locationId',
      views: {
        content: {
          controller: 'LocationController',
          templateUrl: 'partials/location.html'
        }
      }
    });

  // Configure and install Opbeat
  if (config.opbeatOrgId && config.opbeatOrgId.length > 0 && config.opbeatAppId && config.opbeatAppId.length > 0) {
    $opbeatProvider.config({
      orgId: config.opbeatOrgId,
      appId: config.opbeatAppId
    });

    $opbeatProvider.install();
  }
});

/**
 * APP RUNNER
 */

AQ.run(function ($window, $rootScope, config, viewport, data, state, oauth) {
  oauth.authorize();

  // Always redirect to Login state when user is not authenticated (unless it is a state for logging in or registering)
  $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    if (_.isEmpty(data.authentication) && ['register', 'login'].indexOf(toState.name) === -1) {
      event.preventDefault();
      state.redirect('login');
    }
  });

  // Save state history each time it changes
  $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
    state.saveHistory(toState.name, toParams, fromState.name, fromParams);
  });

  // Make viewport ready only when device is ready
  document.addEventListener('deviceready', function () {
    function exitApp() {
      $window.navigator.app.exitApp();
    }

    if (_.isEmpty(config)) {
      $window.navigator.notification.alert(
        'App configuration not set.',
        exitApp,
        'Error',
        'OK'
      );

      return;
    }

    if (!config.url) {
      $window.navigator.notification.alert(
        'Path to platform not set.',
        exitApp,
        'Error',
        'OK'
      );

      return;
    }

    config.url = config.url.replace(/\/$/, '');
    config.version = '@@version';
    viewport.platform = config.url;

    // Check if network connection is available on the run...
    if ($window.navigator.connection.type !== 'none') {
      $rootScope.$apply(function () {
        viewport.online = true;
        viewport.ready = true;
      });
    } else {
      $rootScope.$apply(function () {
        viewport.online = false;
        viewport.ready = true;
      });
    }

    // ...and while running the app
    document.addEventListener('online', function () {
      $rootScope.$apply(function () {
        viewport.online = true;
      });
    }, false);

    document.addEventListener('offline', function () {
      $rootScope.$apply(function () {
        viewport.online = false;
      });
    }, false);
  }, false);
});
<|endoftext|>


<commit_before>/* global module:true */

module.exports = function(grunt) {
    'use strict';

    // Change any strings in the content that match ${some string here} to the value specified in replacements.json
    var BUILD_DIR = 'build',
        DIST_DIR = 'dist',
        STAGE_DIR = BUILD_DIR + '/' + DIST_DIR,
        resolveFileContent = function(content) {
            var resolvedContent = content,
                // The build version
                buildVersion = grunt.file.readJSON('package.json').version,
                // The build date
                buildDate = new Date(),
                buildUser = (function() {
                    if ('win32' === process.platform) {
                        return process.env.USERNAME;
                    } else if ('linux' === process.platform) {
                        return process.env.USER;
                    } else {
                        return 'unknown';
                    }
                })(),
                // The file which has replacements in JSON format
                replacementFilePath = 'replacements.json',
                // The replacements read in from the file
                replacements = (function() {
                    // If the file is not present, _replacements will be null
                    if (grunt.file.exists(replacementFilePath) && grunt.file.isFile(replacementFilePath)) {
                        return grunt.file.readJSON(replacementFilePath);
                    }
                }());

            // The default resolvers (build user, version and date)
            resolvedContent = resolvedContent.replace(new RegExp('\\${build.user}', 'gi'), buildUser);
            resolvedContent = resolvedContent.replace(new RegExp('\\${build.version}', 'gi'), buildVersion);
            resolvedContent = resolvedContent.replace(new RegExp('\\${build.date}', 'gi'), buildDate);

            // If the replacements file exists, use the key/value pairs from there
            if (replacements) {
                for (var key in replacements) {
                    resolvedContent = resolvedContent.replace(new RegExp('\\${' + key + '}', 'gi'), replacements[key]);
                }
            }

            // Return the resolved content
            return resolvedContent;
        };

    grunt.initConfig({

        watch: {
            build: {
                files: ['src/**/*.js', 'test/**/*.js'],
                tasks: ['build']
            }
        },
        uglify: {
            options: {
                sourceMap: true
            },
            build: {
                files: {
                    // TODO: Should this be STAGE_DIR
                    [STAGE_DIR + '/SettingsManager.min.js']: [STAGE_DIR + '/SettingsManager.js']
                }
            }
        },
        jshint: {
            options: {
                reporterOutput: ''
            },
            source: {
                options: {
                    reporter: require('jshint-stylish'),
                    jshintrc: true
                },
                all: ['Gruntfile.js', 'src/*.js', 'test/**/*.js']
            }
        },
        copy: {
            options: {
                process: resolveFileContent
            },
            build: {
                files: [
                    {
                        expand: true,
                        cwd: 'src/',
                        src: ['**/*.js'],
                        dest: STAGE_DIR,
                        filter: 'isFile'
                    }
                ]
            },
            dist: {
                files: [
                    {
                        expand: true,
                        cwd: STAGE_DIR,
                        src: ['**/*.js'],
                        dest: DIST_DIR,
                        filter: 'isFile'
                    }
                ]
            }
        }
    });

    // Load NPM tasks
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // Register tasks
    //
    // Build the artifacts
    grunt.registerTask('build', ['jshint', 'copy:build', 'uglify:build']);
    grunt.registerTask('build:watch', ['build', 'watch:build']);
    grunt.registerTask('dist', ['build', 'copy:dist']);

    // Default task
    grunt.registerTask('default', 'build');
};<commit_msg>Removed TODO from Gruntfile<commit_after>/* global module:true */

module.exports = function(grunt) {
    'use strict';

    // Change any strings in the content that match ${some string here} to the value specified in replacements.json
    var BUILD_DIR = 'build',
        DIST_DIR = 'dist',
        STAGE_DIR = BUILD_DIR + '/' + DIST_DIR,
        resolveFileContent = function(content) {
            var resolvedContent = content,
                // The build version
                buildVersion = grunt.file.readJSON('package.json').version,
                // The build date
                buildDate = new Date(),
                buildUser = (function() {
                    if ('win32' === process.platform) {
                        return process.env.USERNAME;
                    } else if ('linux' === process.platform) {
                        return process.env.USER;
                    } else {
                        return 'unknown';
                    }
                })(),
                // The file which has replacements in JSON format
                replacementFilePath = 'replacements.json',
                // The replacements read in from the file
                replacements = (function() {
                    // If the file is not present, _replacements will be null
                    if (grunt.file.exists(replacementFilePath) && grunt.file.isFile(replacementFilePath)) {
                        return grunt.file.readJSON(replacementFilePath);
                    }
                }());

            // The default resolvers (build user, version and date)
            resolvedContent = resolvedContent.replace(new RegExp('\\${build.user}', 'gi'), buildUser);
            resolvedContent = resolvedContent.replace(new RegExp('\\${build.version}', 'gi'), buildVersion);
            resolvedContent = resolvedContent.replace(new RegExp('\\${build.date}', 'gi'), buildDate);

            // If the replacements file exists, use the key/value pairs from there
            if (replacements) {
                for (var key in replacements) {
                    resolvedContent = resolvedContent.replace(new RegExp('\\${' + key + '}', 'gi'), replacements[key]);
                }
            }

            // Return the resolved content
            return resolvedContent;
        };

    grunt.initConfig({

        watch: {
            build: {
                files: ['src/**/*.js', 'test/**/*.js'],
                tasks: ['build']
            }
        },
        uglify: {
            options: {
                sourceMap: true
            },
            build: {
                files: {
                    [STAGE_DIR + '/SettingsManager.min.js']: [STAGE_DIR + '/SettingsManager.js']
                }
            }
        },
        jshint: {
            options: {
                reporterOutput: ''
            },
            source: {
                options: {
                    reporter: require('jshint-stylish'),
                    jshintrc: true
                },
                all: ['Gruntfile.js', 'src/*.js', 'test/**/*.js']
            }
        },
        copy: {
            options: {
                process: resolveFileContent
            },
            build: {
                files: [
                    {
                        expand: true,
                        cwd: 'src/',
                        src: ['**/*.js'],
                        dest: STAGE_DIR,
                        filter: 'isFile'
                    }
                ]
            },
            dist: {
                files: [
                    {
                        expand: true,
                        cwd: STAGE_DIR,
                        src: ['**/*.js'],
                        dest: DIST_DIR,
                        filter: 'isFile'
                    }
                ]
            }
        }
    });

    // Load NPM tasks
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // Register tasks
    //
    // Build the artifacts
    grunt.registerTask('build', ['jshint', 'copy:build', 'uglify:build']);
    grunt.registerTask('build:watch', ['build', 'watch:build']);
    grunt.registerTask('dist', ['build', 'copy:dist']);

    // Default task
    grunt.registerTask('default', 'build');
};<|endoftext|>


<commit_before>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        console.log();

        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        expect(fs.existsSync(__dirname + '/../LICENSE'), 'ERROR: LICENSE needs to exist! Please create one.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (ioPackage.common.name.indexOf('vis-') !== 0) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        expect(fs.existsSync(__dirname + '/../LICENSE'), 'A LICENSE must exist');
        var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        if (fileContentReadme.indexOf('## License') === -1) {
            console.log('Warning: The README.md should have a section ## License');
            console.log();
        }
        if (fileContentReadme.indexOf('## Changelog') === -1) {
            console.log('Warning: The README.md should have a section ## Changelog');
            console.log();
        }
        done();
    });
});
<commit_msg>update basic package-file testing<commit_after>/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint expr: true */
var expect = require('chai').expect;
var fs        = require('fs');

describe('Test package.json and io-package.json', function() {
    it('Test package files', function (done) {
        console.log();

        var fileContentIOPackage = fs.readFileSync(__dirname + '/../io-package.json', 'utf8');
        var ioPackage = JSON.parse(fileContentIOPackage);

        var fileContentNPMPackage = fs.readFileSync(__dirname + '/../package.json', 'utf8');
        var npmPackage = JSON.parse(fileContentNPMPackage);

        expect(ioPackage).to.be.an('object');
        expect(npmPackage).to.be.an('object');

        expect(ioPackage.common.version, 'ERROR: Version number in io-package.json needs to exist').to.exist;
        expect(npmPackage.version, 'ERROR: Version number in package.json needs to exist').to.exist;

        expect(ioPackage.common.version, 'ERROR: Version numbers in package.json and io-package.json needs to match').to.be.equal(npmPackage.version);

        if (!ioPackage.common.news || !ioPackage.common.news[ioPackage.common.version]) {
            console.log('WARNING: No news entry for current version exists in io-package.json, no rollback in Admin possible!');
            console.log();
        }

        expect(npmPackage.author, 'ERROR: Author in package.json needs to exist').to.exist;
        expect(ioPackage.common.authors, 'ERROR: Authors in io-package.json needs to exist').to.exist;

        if (ioPackage.common.name.indexOf('template') !== 0) {
            if (Array.isArray(ioPackage.common.authors)) {
                expect(ioPackage.common.authors.length, 'ERROR: Author in io-package.json needs to be set').to.not.be.equal(0);
                if (ioPackage.common.authors.length === 1) {
                    expect(ioPackage.common.authors[0], 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
                }
            }
            else {
                expect(ioPackage.common.authors, 'ERROR: Author in io-package.json needs to be a real name').to.not.be.equal('my Name <my@email.com>');
            }
        }
        else {
            console.log('WARNING: Testing for set authors field in io-package skipped because template adapter');
            console.log();
        }
        expect(fs.existsSync(__dirname + '/../README.md'), 'ERROR: README.md needs to exist! Please create one with description, detail information and changelog. English is mandatory.').to.be.true;
        expect(fs.existsSync(__dirname + '/../LICENSE'), 'ERROR: LICENSE needs to exist! Please create one.').to.be.true;
        if (!ioPackage.common.titleLang || typeof ioPackage.common.titleLang !== 'object') {
            console.log('WARNING: titleLang is not existing in io-package.json. Please add');
            console.log();
        }
        if (
            ioPackage.common.title.indexOf('iobroker') !== -1 ||
            ioPackage.common.title.indexOf('ioBroker') !== -1 ||
            ioPackage.common.title.indexOf('adapter') !== -1 ||
            ioPackage.common.title.indexOf('Adapter') !== -1
        ) {
            console.log('WARNING: title contains Adapter or ioBroker. It is clear anyway, that it is adapter for ioBroker.');
            console.log();
        }

        if (ioPackage.common.name.indexOf('vis-') !== 0) {
            if (!ioPackage.common.materialize || !fs.existsSync(__dirname + '/../admin/index_m.html') || !fs.existsSync(__dirname + '/../gulpfile.js')) {
                console.log('WARNING: Admin3 support is missing! Please add it');
                console.log();
            }
            if (ioPackage.common.materialize) {
                expect(fs.existsSync(__dirname + '/../admin/index_m.html'), 'Admin3 support is enabled in io-package.json, but index_m.html is missing!').to.be.true;
            }
        }

        var licenseFileExists = fs.existsSync(__dirname + '/../LICENSE');
        var fileContentReadme = fs.readFileSync(__dirname + '/../README.md', 'utf8');
        if (fileContentReadme.indexOf('## Changelog') === -1) {
            console.log('Warning: The README.md should have a section ## Changelog');
            console.log();
        }
        expect((licenseFileExists || fileContentReadme.indexOf('## License') !== -1), 'A LICENSE must exist as LICENSE file or as part of the README.md').to.be.true;
        if (!licenseFileExists) {
            console.log('Warning: The License should also exist as LICENSE file');
            console.log();
        }
        if (fileContentReadme.indexOf('## License') === -1) {
            console.log('Warning: The README.md should also have a section ## License to be shown in Admin3');
            console.log();
        }
        done();
    });
});
<|endoftext|>


<commit_before>/**
 * MooLib
 *
 * 会哞哞叫的Javascript工具类
 * A Javascript library with a moo moo here and there.
 *
 * - 此版本为MooJS的原型，可独立运行
 * - This is the original design of MooJS, and it can run standalone.
 *
 * @author Jakit
 * @copyright Jakit
 * @date 2016/3/31
 */

(function () {
	var Moo = function () {

		this.site_url = function () {
			return window.location.protocol + "//" + window.location.host;
		};

		/**
		 * 数据罗列显示
		 * Datalisting Methods
		 *
		 * var list = new Moo.
		 */
		this.listing = function () {

			/**
			 * 数据生成选项列表
			 * Data To <option>
			 *
			 * @param jQueryObject select  选项列表对象
			 * @param Array data           数据列表
			 * @param Object attr          属性
			 * list.select(
			 *     $("select"),
			 *     Array(...),
			 *     {text: "fieldA", value: "fieldB"}
			 * );
			 */
			this.select = function (select, data, attr) {
				select.html('');
				var row;
				for (i in data) {
					row = '<option value="' + data[i][attr.value] +'">' + data[i][attr.text] + '</option>';
					select.append(row);
				}
			};

			/**
			 * 数据生成表格
			 * Data To
 and
			 *
			 * @param jQueryObject tbody  表格对象
			 * @param Array data          数据列表
			 * @param Array fields        要显示的字段
			 * list.table(
			 *     $("tbody"),
			 *     Array(...),
			 *     Array("fieldA", "fieldB", ...)
			 * );
			 */
			this.table = function (tbody, data, fields) {
				tbody.html('');
				var row;
				for (i in data) {
					row = '
';
					for (j in fields) {
						row += '' + data[i][fields[j]] + '	';
					}
					row += '
';
					tbody.append(row);
				}
			};
		};

		/**
		 * 分页
		 * Data Paging Control
		 *
		 * @param jQueryObject pagination  分页控件
		 * @param number total             总页数
		 * @param number pages             每组页数
		 * @param function callback        回调函数
		 * Moo.paging(
		 *     $("pagination"),
		 *     100,
		 *     10,
		 *     function(data) {...}
		 * );
		 */
		this.paging = function (pagination, total, pages, callback) {
			// 处理参数，默认分页为每组10页
			if (typeof(pages) === "undefined") {
				pages = 10;
			} else if (typeof(pages) === "function") {
				callback = pages;
				pages = 10;
			};

			var page_groups = Math.ceil(total / pages);
			var current_group = 1;

			var change_status = function () {
				pagination.html('');
				var last = current_group * pages;
				var first = last - pages + 1;
				if (last > total) {last = total};

				// 添加分页项
				for (var i = first; i <= last; i++) {
					pagination.append('
'+i+'
');
				}
				if (typeof(callback) === "function") {
					pagination.find("a[aria-label='Page']").each(function (index) {
						$(this).click(function () {
							callback($(this).attr("page-data"));
						});
					});
				};

				// 添加前后跳转项
				pagination.prepend('
«
');
				pagination.append('
»
');
				if (current_group > 1) {
					pagination.find("a[aria-label='Previous']").click(function () {
						current_group--;
						change_status();
					});
				} else {
					pagination.find("a[aria-label='Previous']").parent().addClass("disabled");
				};
				if (current_group < page_groups) {
					pagination.find("a[aria-label='Next']").click(function () {
						current_group++;
						change_status();
					});
				} else {
					pagination.find("a[aria-label='Next']").parent().addClass("disabled");
				};
			}

			// 初始化分页
			change_status();
		};

		/**
		 * 模板生成
		 * Templater
		 *
		 * @param string data         数据字符串
		 * @param object replacement  替换字典对象
		 * Moo.template(
		 *     "String",
		 *     {key: "value"}
		 * );
		 */
		this.template = function (data, replacement) {
			var reg = RegExp(/\[\w+\]/g);
			var reg_word = RegExp(/\w+/);

			return data.replace(reg, function (word) {
				return replacement[word.match(reg_word)];
			});
		};

		/**
		 * Ajax数据处理
		 * Data Ajax cURLing
		 *
		 * var curl = new Moo.curl(...)
		 * @return Object
		 */
		this.curl = function () {

			/**
			 * 提交数据
			 * Data Submiting
			 *
			 * @param string url         接口地址
			 * @param string form_data   表单数据
			 * @param function callback  回调函数
			 */
			this.submit = function (url, form_data, callback) {
				$.post(url, form_data, callback);
			};

			/**
			 * 获取数据
			 * Data Fetching
			 *
			 * @param string url         接口地址
			 * @param function callback  回调函数
			 */
			this.fetch = function (url, callback) {
				$.get(url, callback);
			};

			/**
			 * 带参数获取数据
			 * Data Taking
			 *
			 * @param string url         接口地址
			 * @param object param       参数
			 * @param function callback  回调函数
			 */
			this.take = function (url, param, callback) {
				$.get(url, param, callback);
			};

			/**
			 * 上传文件
			 * File Upload
			 *
			 * @param DOMObject file     DOM元素
			 * @param string url         接口地址
			 * @param function callback  回调函数
			 */
			this.upload = function (file, url, type, name, callback) {
				var fd = new FormData();
				fd.append(name, file);

				var xhr = new XMLHttpRequest();
				xhr.open("POST", url, true);
				xhr.onreadystatechange = function() {
					if (xhr.readyState == 4 && xhr.status == 200) {
						callback(xhr.responseText);
						// console.log(xhr.responseText);
					} else if (xhr.readyState == 4 && xhr.status != 200) {
						// 记录错误信息
						console.log({state: xhr.readyState, status: xhr.status, text: xhr.responseText});
					}
				};
				xhr.send(fd);
			};
		};

		/**
		 * 懒惰时钟
		 * Lazy Clock
		 *
		 * @param function callback
		 * @param number time
		 * @param number times
		 * Moo.lazy(function() {
		 *     return true;
		 * }, 1000, 1);
		 */
		this.lazy = function (callback, time, times, t) {
			times = typeof(times) === "undefined" ? 1 : times;
			if (!t) {
				t = this;
			} else {
				var result = callback();
				if (result || !times) {
					return;
				};
				times--;
			};
			var lz = t.lazy;
			if (times > 0) {
				setTimeout(function () {
					lz(callback, time, times, t);
				}, time);
			};
		};
	};

	// The initialization of MooJS
	this.Moo = new Moo;

})(this);<commit_msg>	modified:   src/Moo.js<commit_after>/**
 * MooLib
 *
 * 会哞哞叫的Javascript工具类
 * A Javascript library with a moo moo here and there.
 *
 * - 此版本为MooJS的原型，可独立运行
 * - This is the original design of MooJS, and it can run standalone.
 *
 * @author Jakit
 * @copyright Jakit
 * @date 2016/3/31
 */

(function () {
	var Moo = function () {

		this.site_url = function () {
			return window.location.protocol + "//" + window.location.host;
		};

		/**
		 * 数据罗列显示
		 * Datalisting Methods
		 *
		 * var list = new Moo.
		 */
		this.listing = function () {

			/**
			 * 数据生成选项列表
			 * Data To <option>
			 *
			 * @param jQueryObject select  选项列表对象
			 * @param Array data           数据列表
			 * @param Object attr          属性
			 * list.select(
			 *     $("select"),
			 *     Array(...),
			 *     {text: "fieldA", value: "fieldB"}
			 * );
			 */
			this.select = function (select, data, attr) {
				select.html('');
				var row;
				for (i in data) {
					row = '<option value="' + data[i][attr.value] +'">' + data[i][attr.text] + '</option>';
					select.append(row);
				}
			};

			/**
			 * 数据生成表格
			 * Data To
 and
			 *
			 * @param jQueryObject tbody  表格对象
			 * @param Array data          数据列表
			 * @param Array fields        要显示的字段
			 * list.table(
			 *     $("tbody"),
			 *     Array(...),
			 *     Array("fieldA", "fieldB", ...)
			 * );
			 */
			this.table = function (tbody, data, fields) {
				tbody.html('');
				var row;
				for (i in data) {
					row = '
';
					for (j in fields) {
						row += '' + data[i][fields[j]] + '	';
					}
					row += '
';
					tbody.append(row);
				}
			};
		};

		/**
		 * 分页
		 * Data Paging Control
		 *
		 * @param jQueryObject pagination  分页控件
		 * @param number total             总页数
		 * @param number pages             每组页数
		 * @param function callback        回调函数
		 * Moo.paging(
		 *     $("pagination"),
		 *     100,
		 *     10,
		 *     function(data) {...}
		 * );
		 */
		this.paging = function (pagination, total, pages, callback) {
			// 处理参数，默认分页为每组10页
			if (typeof(pages) === "undefined") {
				pages = 10;
			} else if (typeof(pages) === "function") {
				callback = pages;
				pages = 10;
			};

			var page_groups = Math.ceil(total / pages);
			var current_group = 1;

			var change_status = function () {
				pagination.html('');
				var last = current_group * pages;
				var first = last - pages + 1;
				if (last > total) {last = total};

				// 添加分页项
				for (var i = first; i <= last; i++) {
					pagination.append('
'+i+'
');
				}
				if (typeof(callback) === "function") {
					pagination.find("a[aria-label='Page']").each(function (index) {
						$(this).click(function () {
							callback($(this).attr("page-data"));
						});
					});
				};

				// 添加前后跳转项
				pagination.prepend('
«
');
				pagination.append('
»
');
				if (current_group > 1) {
					pagination.find("a[aria-label='Previous']").click(function () {
						current_group--;
						change_status();
					});
				} else {
					pagination.find("a[aria-label='Previous']").parent().addClass("disabled");
				};
				if (current_group < page_groups) {
					pagination.find("a[aria-label='Next']").click(function () {
						current_group++;
						change_status();
					});
				} else {
					pagination.find("a[aria-label='Next']").parent().addClass("disabled");
				};
			}

			// 初始化分页
			change_status();
		};

		/**
		 * 模板生成
		 * Templater
		 *
		 * @param string data         数据字符串
		 * @param object replacement  替换字典对象
		 * Moo.template(
		 *     "String",
		 *     {key: "value"}
		 * );
		 */
		this.template = function (data, replacement) {
			var reg = RegExp(/\[\w+\]/g);
			var reg_word = RegExp(/\w+/);

			return data.replace(reg, function (word) {
				return replacement[word.match(reg_word)];
			});
		};

		/**
		 * Ajax数据处理
		 * Data Ajax cURLing
		 *
		 * var curl = new Moo.curl(...)
		 * @return Object
		 */
		this.curl = function () {

			/**
			 * 提交数据
			 * Data Submiting
			 *
			 * @param string url         接口地址
			 * @param string form_data   表单数据
			 * @param function callback  回调函数
			 */
			this.submit = function (url, form_data, callback) {
				$.post(url, form_data, callback);
			};

			/**
			 * 获取数据
			 * Data Fetching
			 *
			 * @param string url         接口地址
			 * @param function callback  回调函数
			 */
			this.fetch = function (url, callback) {
				$.get(url, callback);
			};

			/**
			 * 带参数获取数据
			 * Data Taking
			 *
			 * @param string url         接口地址
			 * @param object param       参数
			 * @param function callback  回调函数
			 */
			this.take = function (url, param, callback) {
				$.get(url, param, callback);
			};

			/**
			 * 上传文件
			 * File Upload
			 *
			 * @param DOMObject file     DOM元素
			 * @param string url         接口地址
			 * @param function callback  回调函数
			 */
			this.upload = function (file, url, name, callback) {
				var fd = new FormData();
				fd.append(name, file);

				var xhr = new XMLHttpRequest();
				xhr.open("POST", url, true);
				xhr.onreadystatechange = function() {
					if (xhr.readyState == 4 && xhr.status == 200) {
						callback(xhr.responseText);
						// console.log(xhr.responseText);
					} else if (xhr.readyState == 4 && xhr.status != 200) {
						// 记录错误信息
						console.log({state: xhr.readyState, status: xhr.status, text: xhr.responseText});
					}
				};
				xhr.send(fd);
			};
		};

		/**
		 * 懒惰时钟
		 * Lazy Clock
		 *
		 * @param function callback
		 * @param number time
		 * @param number times
		 * Moo.lazy(function() {
		 *     return true;
		 * }, 1000, 1);
		 */
		this.lazy = function (callback, time, times, t) {
			times = typeof(times) === "undefined" ? 1 : times;
			if (!t) {
				t = this;
			} else {
				var result = callback();
				if (result || !times) {
					return;
				};
				times--;
			};
			var lz = t.lazy;
			if (times > 0) {
				setTimeout(function () {
					lz(callback, time, times, t);
				}, time);
			};
		};
	};

	// The initialization of MooJS
	this.Moo = new Moo;

})(this);<|endoftext|>


<commit_before>__resources__["/gameMsgHandler.js"] = {meta: {mimetype: "application/javascript"}, data: function(exports, require, module, __filename, __dirname) {

	var pomelo = window.pomelo;
	var app = require('app');
	var AttackResult = require('consts').AttackResult;
	var mainPanel = require('mainPanelView');
	var dialogPanel = require('dialogPanelView');
	var EntityType = require('consts').EntityType;
	var SkillEffect = require('skillEffect');
	var utils = require('utils');
	var clientManager = require('clientManager');

	exports.init = init;

	function init() {
		/**
		 * Handle change area message
		 * @param  data {Object} The message
		 */
		pomelo.on('onChangeArea', function(data) {
			if(!data.success) {
				return;
			}
			clientManager.loadResource({jsonLoad: false}, function() {
				pomelo.areaId = data.target;
				pomelo.request("area.playerHandler.enterScene",{uid:pomelo.uid, playerId: pomelo.playerId, areaId: pomelo.areaId}, function(msg) {
					app.init(msg.data);
				});
			});
		});

		/**
		 * Handle add entities message
		 * @param data {Object} The message, contains entities to add
		 */
		pomelo.on('addEntities', function(data){
			var entities = data.entities;
			var area = app.getCurArea();

//			for(var id in entities){
//				console.log('add entity : ' + entities[id].entityId);
//			}
			if(!area) {
				return;
			}
			for(var i = 0; i < entities.length; i++){
				var entity = area.getEntity(entities[i].entityId);
				if(!entity){
					area.addEntity(entities[i]);
				}
			}
		});

		/**
		 * Handle remove entities message
		 * @param data {Object} The message, contains entitiy ids to remove
		 */
		pomelo.on('removeEntities', function(data){
			var entities = data.entities;
			var area = app.getCurArea();
			var player = area.getCurPlayer();
			for(var i = 0; i < entities.length; i++){
				if(entities[i] !== player.entityId) {
//					/console.log('remove entity : ' + entities[i]);
					area.removeEntity(entities[i]);
				}
			}
		});

		/**
		 * Handle move  message
		 * @param data {Object} The message, contains move information
		 */

		pomelo.on('onMove', function(data){
			var path = data.path;
			var character = app.getCurArea().getEntity(data.entityId);
			if(!character){
				console.log('no character exist for move!' + data.entityId);
				return;
			}

			var sprite = character.getSprite();
			var totalDistance = utils.totalDistance(path);
			var needTime = Math.floor(totalDistance / sprite.getSpeed() * 1000 - app.getDelayTime());
			var speed = totalDistance/needTime * 1000;
			sprite.movePath(path, speed);
		});

		pomelo.on('onPathCheckout', function(data) {
			var player = app.getCurArea().getEntity(data.entityId);
			var serverPosition = data.position;
			var clientposition = player.getSprite().getPosition();
			var realDistance = utils.distance(serverPosition.x, serverPosition.y, clientposition.x, clientposition.y);
			var distanceLimit = 100;

			if (realDistance > distanceLimit) {
				player.getSprite().translateTo(serverPosition.x, serverPosition.y);
			}
		});

		/**
		 * Handle player upgrade message
		 * @param data {Object} The message, contains the info for player upgrade
		 */
		pomelo.on('onUpgrade' , function(data) {
			var playerData = data.player;
			var area = app.getCurArea();
			var player = area.getPlayer(playerData.id);
			player.upgrade(playerData);
			player.getSprite().upgrade();
		});

		/**
		 * Handle update task data message
		 * @param data {Object} The message, contains the info for update task
		 */
		pomelo.on('onUpdateTaskData', function(data) {
			var reData = data.taskData;
			app.getCurPlayer().updateTaskData(reData);
		});

		/**
		 * Handle task complete message
		 * @param data {Object} The message, contains the info for complete task
		 */
		pomelo.on('onTaskCompleted', function(data) {
			var tasks = app.getCurPlayer().curTasks;
			var completeTask = tasks[data.taskId];
			completeTask.setState(1);
			mainPanel.notify('The task has been completed！');
		});

		/**
		 * Handle remove item message
		 * @param data {Object} The message, contains the info for remove item
		 */
		pomelo.on('onRemoveItem', function(data){
			app.getCurArea().removeEntity(data.entityId);
		});

		/**
		 * Handle pick item message
		 * @param data {Object} The message, contains the info for pick item
		 */
		pomelo.on('onPickItem', function(data) {
			var area = app.getCurArea();
			var player = area.getEntity(data.player);
			var item = area.getEntity(data.item);
			//Only add item for current player
			if (player.entityId === area.getCurPlayer().entityId && !!item) {
				player.bag.addItem({id: item.kindId, type: item.type}, data.index);
			}
			area.removeEntity(data.item);
		});

		/**
		 * Handle npc talk message
		 * @param data {Object} The message, contains the info for npc talk
		 */
		pomelo.on('onNPCTalk', function(data){
			var npc = app.getCurArea().getEntity(data.npc);
			var curPlayer = app.getCurPlayer();
			var dir = {
				x1: curPlayer.getPosition().x,
				y1: curPlayer.getPosition().y,
				x2: npc.getPosition().x,
				y2: npc.getPosition().y
			};
			curPlayer.getSprite().stand(dir);
			dialogPanel.open(data);
		});

		/**
		 * Handle checkout task message
		 * @param data {Object} The message, contains the info for check out task
		 */
		pomelo.on('onCheckoutTask', function(data) {
			dialogPanel.open(data);
		});

		/**
		 * the order of attack, whose result contains success, killed and not_in_range
		 * @param {Object} data  contains attacter, target, result ect.
		 */
		pomelo.on('onAttack', function(data){
			var area = app.getCurArea();
			var skillId = data.skillId;
			var attacker = area.getEntity(data.attacker);
			var target = area.getEntity(data.target);

			if(!attacker || !target){
				//console.log('attacker or target not exist ! attacker: ' + data.attacker + ', target : ' + data.target);
				return;
			}

			var attackerSprite = attacker.getSprite();
			var targetSprite = target.getSprite();
			var attackerPos = data.attackerPos || attackerSprite.getPosition();
			var targetPos = targetSprite.getPosition();
			var resultData = data.result;
			var result = resultData.result;
			var skillEffectParams = {
				id: skillId,
				player: attacker,
				position: {x: targetPos.x - attackerPos.x, y: targetPos.y - attackerPos.y}
			};
			if(skillId > 1) {
				mainPanel.skillBox[skillId].start();
			}
			var params ={
				attacker: attacker,
				attackerSprite: attackerSprite,
				target: target,
				targetSprite: targetSprite,
				attackerPos: attackerPos,
				targetPos: targetPos,
				resultData: resultData,
				skillEffectParams: skillEffectParams,
				experience: data.exp
			};
			if (result === AttackResult.SUCCESS) {
				successAction(params);
			} else if (result === AttackResult.KILLED) {
				killedAction(params);
			} else if (result === AttackResult.NOT_IN_RANGE) {
				targetSprite.stand({x1: attackerPos.x, x2:attackerPos.y, y1: targetPos.x, y2: targetPos.y});
			}
			uiUpdate();
		});

		/**
		 * when player revives, it works
		 * @param {Object} data
		 */
		pomelo.on('onRevive', function(data) {
			var area = app.getCurArea();
			var player = area.getEntity(data.entitrId);
			if (!player) {
				area.addEntity(data.entity);
				player = area.getEntity(data.entityId);
			}
			if (!player) {
				console.log('!player:', data);
			}
			player.died = false;
			player.set('hp', data.hp);
			var sprite = player.getSprite();
			sprite.revive(data);
			if (player.entityId === app.getCurPlayer().entityId) {
				area.map.centerTo(data.x, data.y);
				mainPanel.reviveMaskHide();
			}
		});
	}

	/**
	 * the action invokes when the result is success
	 * @param {Object} data
	 */
	var successAction = function(data) {
		new SkillEffect(data.skillEffectParams).createEffectAni();
		data.attackerSprite.translateTo(data.attackerPos.x, data.attackerPos.y);
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'noKilled', function() {
		});
		data.target.update({damage: data.resultData.damage});
		data.attacker.update({mpUse: data.resultData.mpUse});
		data.targetSprite.reduceBlood();
	};

	/**
	 * the action invokes when the result is killed
	 * @param {Object} data
	 */
	var killedAction = function(data) {
		if (!!data.target.died) {
			return;
		}
		if (app.getCurPlayer().entityId == data.target.entityId) {
			console.log('curPlayer died~~~~~~~~~~~~~~~!');
		}
		data.target.died = true;
		if (data.target.type === EntityType.MOB) {
			data.target = null;
		}
		new SkillEffect(data.skillEffectParams).createEffectAni();
		var attackerSprite = data.attackerSprite;
		var attackerPos = data.attackerPos;
		attackerSprite.translateTo(attackerPos.x, attackerPos.y);
		data.targetSprite.zeroBlood();
		if (data.attacker.type === EntityType.PLAYER) {
			data.attacker.update({mpUse: data.resultData.mpUse, experience: data.experience });
		}
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'killed', function() {
		});
		data.targetSprite.died({x1: data.targetPos.x, y1: data.targetPos.y, x2: data.attackerPos.x, y2: data.attackerPos.y}, function(){
		var items = data.resultData.items;
			if (!!items) {
				for (var i = 0; i < items.length; i ++) {
					app.getCurArea().addEntity(items[i]);
				}
			 }
		});
	};

	var uiUpdate = function() {
		var player = app.getCurPlayer();
		player.emit('change:hp');
		player.emit('change:maxHp');
		player.emit('change:mp');
		player.emit('change:maxMp');
		player.emit('change:experience');
	};
}};
<commit_msg>fix bug of revive<commit_after>__resources__["/gameMsgHandler.js"] = {meta: {mimetype: "application/javascript"}, data: function(exports, require, module, __filename, __dirname) {

	var pomelo = window.pomelo;
	var app = require('app');
	var AttackResult = require('consts').AttackResult;
	var mainPanel = require('mainPanelView');
	var dialogPanel = require('dialogPanelView');
	var EntityType = require('consts').EntityType;
	var SkillEffect = require('skillEffect');
	var utils = require('utils');
	var clientManager = require('clientManager');

	exports.init = init;

	function init() {
		/**
		 * Handle change area message
		 * @param  data {Object} The message
		 */
		pomelo.on('onChangeArea', function(data) {
			if(!data.success) {
				return;
			}
			clientManager.loadResource({jsonLoad: false}, function() {
				pomelo.areaId = data.target;
				pomelo.request("area.playerHandler.enterScene",{uid:pomelo.uid, playerId: pomelo.playerId, areaId: pomelo.areaId}, function(msg) {
					app.init(msg.data);
				});
			});
		});

		/**
		 * Handle add entities message
		 * @param data {Object} The message, contains entities to add
		 */
		pomelo.on('addEntities', function(data){
			var entities = data.entities;
			var area = app.getCurArea();

//			for(var id in entities){
//				console.log('add entity : ' + entities[id].entityId);
//			}
			if(!area) {
				return;
			}
			for(var i = 0; i < entities.length; i++){
				var entity = area.getEntity(entities[i].entityId);
				if(!entity){
					area.addEntity(entities[i]);
				}
			}
		});

		/**
		 * Handle remove entities message
		 * @param data {Object} The message, contains entitiy ids to remove
		 */
		pomelo.on('removeEntities', function(data){
			var entities = data.entities;
			var area = app.getCurArea();
			var player = area.getCurPlayer();
			for(var i = 0; i < entities.length; i++){
				if(entities[i] !== player.entityId) {
//					/console.log('remove entity : ' + entities[i]);
					area.removeEntity(entities[i]);
				}
			}
		});

		/**
		 * Handle move  message
		 * @param data {Object} The message, contains move information
		 */

		pomelo.on('onMove', function(data){
			var path = data.path;
			var character = app.getCurArea().getEntity(data.entityId);
			if(!character){
				console.log('no character exist for move!' + data.entityId);
				return;
			}

			var sprite = character.getSprite();
			var totalDistance = utils.totalDistance(path);
			var needTime = Math.floor(totalDistance / sprite.getSpeed() * 1000 - app.getDelayTime());
			var speed = totalDistance/needTime * 1000;
			sprite.movePath(path, speed);
		});

		pomelo.on('onPathCheckout', function(data) {
			var player = app.getCurArea().getEntity(data.entityId);
			var serverPosition = data.position;
			var clientposition = player.getSprite().getPosition();
			var realDistance = utils.distance(serverPosition.x, serverPosition.y, clientposition.x, clientposition.y);
			var distanceLimit = 100;

			if (realDistance > distanceLimit) {
				player.getSprite().translateTo(serverPosition.x, serverPosition.y);
			}
		});

		/**
		 * Handle player upgrade message
		 * @param data {Object} The message, contains the info for player upgrade
		 */
		pomelo.on('onUpgrade' , function(data) {
			var playerData = data.player;
			var area = app.getCurArea();
			var player = area.getPlayer(playerData.id);
			player.upgrade(playerData);
			player.getSprite().upgrade();
		});

		/**
		 * Handle update task data message
		 * @param data {Object} The message, contains the info for update task
		 */
		pomelo.on('onUpdateTaskData', function(data) {
			var reData = data.taskData;
			app.getCurPlayer().updateTaskData(reData);
		});

		/**
		 * Handle task complete message
		 * @param data {Object} The message, contains the info for complete task
		 */
		pomelo.on('onTaskCompleted', function(data) {
			var tasks = app.getCurPlayer().curTasks;
			var completeTask = tasks[data.taskId];
			completeTask.setState(1);
			mainPanel.notify('The task has been completed！');
		});

		/**
		 * Handle remove item message
		 * @param data {Object} The message, contains the info for remove item
		 */
		pomelo.on('onRemoveItem', function(data){
			app.getCurArea().removeEntity(data.entityId);
		});

		/**
		 * Handle pick item message
		 * @param data {Object} The message, contains the info for pick item
		 */
		pomelo.on('onPickItem', function(data) {
			var area = app.getCurArea();
			var player = area.getEntity(data.player);
			var item = area.getEntity(data.item);
			//Only add item for current player
			if (player.entityId === area.getCurPlayer().entityId && !!item) {
				player.bag.addItem({id: item.kindId, type: item.type}, data.index);
			}
			area.removeEntity(data.item);
		});

		/**
		 * Handle npc talk message
		 * @param data {Object} The message, contains the info for npc talk
		 */
		pomelo.on('onNPCTalk', function(data){
			var npc = app.getCurArea().getEntity(data.npc);
			var curPlayer = app.getCurPlayer();
			var dir = {
				x1: curPlayer.getPosition().x,
				y1: curPlayer.getPosition().y,
				x2: npc.getPosition().x,
				y2: npc.getPosition().y
			};
			curPlayer.getSprite().stand(dir);
			dialogPanel.open(data);
		});

		/**
		 * Handle checkout task message
		 * @param data {Object} The message, contains the info for check out task
		 */
		pomelo.on('onCheckoutTask', function(data) {
			dialogPanel.open(data);
		});

		/**
		 * the order of attack, whose result contains success, killed and not_in_range
		 * @param {Object} data  contains attacter, target, result ect.
		 */
		pomelo.on('onAttack', function(data){
			var area = app.getCurArea();
			var skillId = data.skillId;
			var attacker = area.getEntity(data.attacker);
			var target = area.getEntity(data.target);

			if(!attacker || !target){
				//console.log('attacker or target not exist ! attacker: ' + data.attacker + ', target : ' + data.target);
				return;
			}

			var attackerSprite = attacker.getSprite();
			var targetSprite = target.getSprite();
			var attackerPos = data.attackerPos || attackerSprite.getPosition();
			var targetPos = targetSprite.getPosition();
			var resultData = data.result;
			var result = resultData.result;
			var skillEffectParams = {
				id: skillId,
				player: attacker,
				position: {x: targetPos.x - attackerPos.x, y: targetPos.y - attackerPos.y}
			};
			/**
			if(skillId > 1) {
				mainPanel.skillBox[skillId].start();
			}
			*/
			var params ={
				attacker: attacker,
				attackerSprite: attackerSprite,
				target: target,
				targetSprite: targetSprite,
				attackerPos: attackerPos,
				targetPos: targetPos,
				resultData: resultData,
				skillEffectParams: skillEffectParams,
				experience: data.exp
			};
			if (result === AttackResult.SUCCESS) {
				successAction(params);
			} else if (result === AttackResult.KILLED) {
				killedAction(params);
			} else if (result === AttackResult.NOT_IN_RANGE) {
				targetSprite.stand({x1: attackerPos.x, x2:attackerPos.y, y1: targetPos.x, y2: targetPos.y});
			}
			uiUpdate();
		});

		/**
		 * when player revives, it works
		 * @param {Object} data
		 */
		pomelo.on('onRevive', function(data) {
			var area = app.getCurArea();
			var player = area.getEntity(data.entitrId);
			if (!player) {
				area.addEntity(data.entity);
				player = area.getEntity(data.entityId);
			}
			if (!player) {
				console.log('!player:', data);
			}
			player.died = false;
			player.set('hp', data.hp);
			var sprite = player.getSprite();
			sprite.revive(data);
			if (player.entityId === app.getCurPlayer().entityId) {
				area.map.centerTo(data.x, data.y);
				mainPanel.reviveMaskHide();
			}
		});
	}

	/**
	 * the action invokes when the result is success
	 * @param {Object} data
	 */
	var successAction = function(data) {
		new SkillEffect(data.skillEffectParams).createEffectAni();
		data.attackerSprite.translateTo(data.attackerPos.x, data.attackerPos.y);
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'noKilled', function() {
		});
		data.target.update({damage: data.resultData.damage});
		data.attacker.update({mpUse: data.resultData.mpUse});
		data.targetSprite.reduceBlood();
	};

	/**
	 * the action invokes when the result is killed
	 * @param {Object} data
	 */
	var killedAction = function(data) {
		if (!!data.target.died) {
			return;
		}
		if (app.getCurPlayer().entityId == data.target.entityId) {
			console.log('curPlayer died~~~~~~~~~~~~~~~!');
		}
		data.target.died = true;
		if (data.target.type === EntityType.MOB) {
			data.target = null;
		}
		new SkillEffect(data.skillEffectParams).createEffectAni();
		var attackerSprite = data.attackerSprite;
		var attackerPos = data.attackerPos;
		attackerSprite.translateTo(attackerPos.x, attackerPos.y);
		data.targetSprite.zeroBlood();
		if (data.attacker.type === EntityType.PLAYER) {
			data.attacker.update({mpUse: data.resultData.mpUse, experience: data.experience });
		}
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'killed', function() {
		});
		data.targetSprite.died({x1: data.targetPos.x, y1: data.targetPos.y, x2: data.attackerPos.x, y2: data.attackerPos.y}, function(){
		var items = data.resultData.items;
			if (!!items) {
				for (var i = 0; i < items.length; i ++) {
					app.getCurArea().addEntity(items[i]);
				}
			 }
		});
	};

	var uiUpdate = function() {
		var player = app.getCurPlayer();
		player.emit('change:hp');
		player.emit('change:maxHp');
		player.emit('change:mp');
		player.emit('change:maxMp');
		player.emit('change:experience');
	};
}};
<|endoftext|>