

"use strict";

//
//
//    * *    *  *       *     * * *  * * * *  
//   *   *   * *         *      *   *          
//   *   *   *  *   *   *  *    *    * * * *   
//   *   *   *   * * * *  *     *           *  
//   *    * *     *   *    * * *     * * * *  
//
//

var fs = require('fs');

//----------------------------------------------------- uncaughtException
// ~ ~ ~ ~ ~ ~ 
// NOTE: uncaught exceptions
//   https://github.com/rogerwang/node-webkit/issues/1699
process.on('uncaughtException', function (error){
  if(error.alreadyHandled) return; //^ DEV
  // force the window to show on error
  try{
    require('nw.gui').Window.get().show();
  } catch(e){}
  console.log(error);
  trace.error(error);
});

//----------------------------------------------------- force show
// force show the window after five seconds just in case
defer(function(){
  try{
    if(!Shell.window.shown) throw null;
  }
  catch(e){
    Shell.window = require('nw.gui').Window.get();
    Shell.window.resizeTo(666,666);//(999,666);
    Shell.window.setPosition("center");
    Shell.window.show();
    trace.error("Shell.showWindow took too long");
    ///if(typeof ok == "function") ok("<h2>:-(</h2> There was a problem starting Vilmonic, you might want to try restarting the game.");
  }
}, 10000);

Shell.shouldCheckVersion = true;

//----------------------------------------------------- 
Shell.init = function(){
  Shell.desktop = "nwjs";
  // this needs to come before Steam loads so it can override
  ///Shell.shouldCheckVersion = true;

  trace("Shell.init",'Shell');

  $("body").addClass("frameless");
  $("#titlebarCloseButton").on("click", Shell.quit);

  Shell.setupWindowFrame();

  // ~ ~ ~ ~ ~ ~
  Shell.gui = require('nw.gui');
  Shell.window = Shell.gui.Window.get();
  Shell.window = Shell.window;

  //^// allow custom commandline args
  //% if(App.storage("forceGPU") === true){
  //%   ///if( Shell.forceGPU() ) return;
  //%   Shell.forceGPU();
  //% }
  try{
    if(Shell.gui.App.argv.length){
      trace("Shell argv: "+typeof Shell.gui.App.argv,"Shell");
    }
  } catch(e){ trace.error("ERROR >> "+e); }

  Shell.initFilestore();

  Shell.fixBadProfileError();  // DESKTOPNEW

  // mac specific
  if(System.os=="mac"){
    var nativeMenuBar = new Shell.gui.Menu({ type: "menubar" });
    nativeMenuBar.createMacBuiltin(App.info.title);
    Shell.window.menu = nativeMenuBar;
  }


// see Shell.shutdown (below)
// this stops macos menu from closing app in 0.24.4 (> 0.12.1, < 0.30.0)
///  // bug fix for NW ignoring beforeunload event
///  Shell.window.on('close', function(fromCommandQ){
///    // to help identify errors on close
///    App.isClosing = true;
///    // *this* is nwjs Window
///    // allow App.onClose to save...? saw some errors here...
///    App.onClose(function(){
///      defer(function(){
///        this.close(true);
///        // needed in nwjs 0.24.4
///        Shell.gui.App.quit();
///      }.bind(this),0);
///    }.bind(this));
///
///    // just in case ^ isn't called (needed?)
///    defer(function(){
///      this.close(true);
///      Shell.gui.App.quit();
///    }.bind(this),10000);
///  });


  // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ on resize
  // NOTE: localStorage can be null after page reload, must use
  // window.localStorage!
  ///Shell.window.on('resize', function(width, height){
  ///  if(App.isFullScreen) return;
  ///  try{
  ///    ///trace(" Shell.window.on('resize' localStorage"+localStorage);
  ///    window.localStorage["windowSize"] = [width, height];
  ///  }
  ///  catch(e){
  ///    trace.error("Shell.window.on('resize' %s,%s) ERROR window.localStorage==%s, %s"
  ///      .sub( width, height, window.localStorage, e ));
  ///  }
  ///});

  App.onEvent("resizeDone", function(){
    if(App.isFullScreen) return;
    try{
      window.localStorage["windowSize"] = [window.outerWidth, window.outerHeight];
    }
    catch(e){
      trace.error("Shell.window.on('resize' %s,%s) ERROR window.localStorage==%s, %s"
        .sub( window.outerWidth, window.outerHeight, window.localStorage, e ));
    }
  });


  //^//save window location
  //% Shell.window.on('move', function(x, y){
  //%   trace(Shell.window.localStorage+" window move "+[x, y]);
  //% }); 550 450

  // already called in shell.js onShellLoaded()!
  ///trace("NWJS Shell.override()");
  ///Shell.override();

  // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ on loaded
  // show window, the is imitially hidden
///  Shell.window.on("loaded", function(){
///    ///trace("NWJS Shell.window.on loaded");
///    Shell.window.setPosition("center");
///    Shell.showWindow();
///  });
///
///  // just in case we missed loaded^
///  defer(function(){
///    if(!Shell.window.shown){
///      ///trace("NWJS !Shell.window.shown after 5sec");
///      Shell.window.setPosition("center");
///      Shell.showWindow();
///    }
///  }, 5000);



  Shell.initWindowView();


  // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ SHOW WINDOW
  defer(function(){


    //////defer(function(){
    ///  Shell.window.resizeTo(Shell.windowSize.width, Shell.windowSize.height);
    ///  Shell.showWindow();
    //////}.bind(this), 0);


    // should we fullscreen?
    try{
      var fullscreen = App.storage("fullscreen");
      if(fullscreen){

        App.onNextEvent("ready",function(){
          // let window background paint black for fullscreen
          $("body").css({ opacity:1 });
          defer(function(){
            App.toggleFullScreen(true,true);
          }, 70); // 10 is too short, we get white artifacts
        });

      }
    }
    catch(e){ trace.error(e); }

    ///// defer just in case
    ///// NOTE: is this nec? we do the same above^
    ///defer(function(){
    ///  if(!Shell.window.shown){
    ///    trace.warning("Shell.showWindow took a long time.");
    ///    Shell.showWindow();
    ///  }
    ///}, 8000);

  }, 1);


}; // -- -- -- /init

/////----------------------------------------------------- 
///Shell.loadAddons = function(callback){
///  App.ADDONS_PATH = Shell.getAppPath() + "/Vilmonickages";
///  trace("Shell.loadAddons App.ADDONS_PATH "+App.ADDONS_PATH);
///  callback();
///};

//----------------------------------------------------- 
Shell.loadAddons = function(callback){
  function onDone(){
    pathsToCheck--;
    if(pathsToCheck==0){
      if(callback) callback();
    }
  }

  // this gets the path to main where vilmonic.html is
  var addonsPathInternal = process.cwd() + "/addons/";
  var pathsToCheck = 1;

  // this gets the path to the nw.exe
  //! requires addons.js to load all files in directory (not implemented)
  //% var addonsPathExternal = Shell.getAppPath() + "/Vilmonic_addons/";
  //% var exists = Shell.fileExists(addonsPathExternal);
  //% if(exists){
  //%   trace("Shell.loadAddons external addons exists:\n"+addonsPathExternal,0,"#fa3");
  //%   pathsToCheck++;
  //% }

  Addons.instance.loadAddons(onDone, addonsPathInternal);
  //% if(exists) Addons.instance.loadAddons(onDone, addonsPathExternal);
};

//` ORG
///Shell.loadAddons = function(callback){
///  // this gets the path to the nw.exe
///  var addonsPath = Shell.getAppPath() + "/Vilmonickages/";
///
///  var exists = Shell.fileExists(addonsPath);
///  trace("Shell.loadAddons addonsPath exists "+exists+"\n"+addonsPath,0,"#fa3");
///
///  if(!exists){
///    // this gets the path to main where vilmonic.html is
///    var addonsPath = process.cwd() + "/addons/";
///  }
///
///  Addons.instance.loadAddons(callback, addonsPath);
///};








// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ app/window/menu quit
//
// bug fix for NW ignoring beforeunload event
// called from toplevel nwjs window
//
Shell.shutdown = function(onDone){
  // to help identify errors on close
  App.isClosing = true;
  // *this* is nwjs Window
  // allow App.onClose to save...? saw some errors here...
  App.onClose(function(){
    defer( onDone ,0);
  }.bind(this));

};








//----------------------------------------------------- 
// nwjs tester app:
//   .../nwjs12.3.app/Contents/Frameworks/nwjs Helper.app/Contents/MacOS
// osx32 build:
//   .../Vilmonic.app/Contents/Frameworks/nwjs Helper.app/Contents/MacOS
//
// * Vilmonickages
// * Vilmonicache
//
Shell.getAppPath = function(){
  if(Shell.APP_PATH) return Shell.APP_PATH;
  var Path = require('path');
  var nwPath = Path.dirname(process.execPath);

  // STEAM
  var isSteam = Shell.distributionId=="steam";

  switch(System.os){
    case "mac":
      //*DESKTOPNEW (
      // remove everything from "Vilmonic.app" on
      Shell.APP_PATH = nwPath.substr(0, nwPath.lastIndexOf("/Vilmonic.app")) || nwPath;

      // try platform:"osx32",   nwjs:'0.12.1', sdk:"144" 
      if(Shell.APP_PATH==nwPath){
        Shell.APP_PATH = nwPath.replace("/Vilmonic.app/Contents/Frameworks/nwjs Helper.app/Contents/MacOS","");
      }
      //*DESKTOPNEW )
      // no match, is this nwjs tester app?
      if(Shell.APP_PATH==nwPath){
        Shell.APP_PATH = nwPath.replace("/nwjs12.3.app/Contents/Frameworks/nwjs Helper.app/Contents/MacOS","");
        trace("Shell.APP_PATH DEV from nwjs tester path","Shell","cyan");
      }
      // no match, is this a new nwjs tester app? Something is wrong!
      if(Shell.APP_PATH==nwPath){
        Shell.APP_PATH = nwPath.replace(
        /[^\/]+\.app\/Contents\/Frameworks\/nwjs Helper\.app\/Contents\/MacOS/
        ,"");
        trace.error("Unrecognized nwjs path: "+nwPath);
      }

      break;
    case "linux":
      // linux64:
      // .../Vilmonic.app
      if(!isSteam) Shell.APP_PATH = nwPath.replace("/Vilmonic.app");
      else Shell.APP_PATH = nwPath;
      break;
    case "windows":
      Shell.APP_PATH = nwPath;
      break;
  }

  trace("Shell.APP_PATH "+Shell.APP_PATH,"Shell","#fed");

  return Shell.APP_PATH;
};
///Shell.getAppPath = function(){
///  if(Shell.APP_PATH) return Shell.APP_PATH;
///  var Path = require('path');
///  var nwPath = Path.dirname(process.execPath);
///
///  // STEAM
///  var isSteam = Shell.distributionId=="steam";
///
///  switch(System.os){
///    case "mac":
///      Shell.APP_PATH = nwPath.replace("/Vilmonic.app/Contents/Frameworks/nwjs Helper.app/Contents/MacOS","");
///
///      // no match, is this nwjs tester app?
///      if(Shell.APP_PATH==nwPath){
///        Shell.APP_PATH = nwPath.replace("/nwjs12.3.app/Contents/Frameworks/nwjs Helper.app/Contents/MacOS","");
///        trace("Shell.APP_PATH DEV from nwjs tester path","Shell","cyan");
///      }
///      // no match, is this a new nwjs tester app? Something is wrong!
///      if(Shell.APP_PATH==nwPath){
///        Shell.APP_PATH = nwPath.replace(
///        /[^\/]+\.app\/Contents\/Frameworks\/nwjs Helper\.app\/Contents\/MacOS/
///        ,"");
///        trace.error("Unrecognized nwjs path: "+nwPath);
///      }
///
///      break;
///    case "linux":
///      // linux64:
///      // .../Vilmonic.app
///      if(!isSteam) Shell.APP_PATH = nwPath.replace("/Vilmonic.app");
///      else Shell.APP_PATH = nwPath;
///      break;
///    case "windows":
///      Shell.APP_PATH = nwPath;
///      break;
///  }
///
///  trace("Shell.APP_PATH "+Shell.APP_PATH,"Shell","#fed");
///
///  return Shell.APP_PATH;
///};


//----------------------------------------------------- 
Shell.initWindowView = function(){
  App.onEvent("onOpen",function(){
    Shell.setWindowMinimumSize();
  });

  var windowSize = window.localStorage["windowSize"]||"999,666";
  windowSize = windowSize.split(",");
  Shell.windowSize = {
    width:parseInt(windowSize[0]), 
    height:parseInt(windowSize[1])
  }

  Shell.setWindowMinimumSize();
  Shell.window.resizeTo(Shell.windowSize.width, Shell.windowSize.height);
  Shell.centerWindow();
  Shell.showWindow();

///  defer(function(){
///    ///Shell.window.resizeTo(Shell.windowSize.width, Shell.windowSize.height);
///    ///Shell.setWindowMinimumSize();
///    ///Shell.centerWindow();
///    //Shell.showWindow();
///  }.bind(this),0);
///
///
///  //Shell.setWindowMinimumSize();
///  ///Shell.centerWindow();
///  ///Shell.showWindow();
};
//----------------------------------------------------- 

///Shell.MINIMUM_WINDOW_SIZE = 105; // 160 192;
Shell.MINIMUM_WINDOW_W = 111; // 118 160 192;
Shell.MINIMUM_WINDOW_H = 111; // 118 160 192;

Shell.setWindowMinimumSize = function(){
  var W = App.uiScale * Shell.MINIMUM_WINDOW_W;
  var H = App.uiScale * Shell.MINIMUM_WINDOW_H + 20; // 20 for titlebar
  Shell.window.setMinimumSize(W, H);

  if(Shell.windowSize.width<W){
    Shell.window.width = Math.max(W,Shell.windowSize.width);
  }
  if(Shell.windowSize.height<H){
    Shell.window.height = Math.max(H,Shell.windowSize.height);
  }
};
///
///  if(Shell.windowSize.width<W ||
///     Shell.windowSize.height<H){
///
///    trace(Math.max(W,Shell.windowSize.width)+","+Math.max(H,Shell.windowSize.height)
///      ,0,"orange");
///
///
///      Shell.window.resizeTo(
///        Math.max(W,Shell.windowSize.width), 
///        Math.max(H,Shell.windowSize.height), 
///      );
///
///
///  }    
//----------------------------------------------------- 
Shell.showWindow = function(){
  Shell.window.show();
  Shell.window.focus();
  Shell.window.shown = true;
};
//----------------------------------------------------- 
Shell.centerWindow = function(){
  var nw = nw || Shell.gui;
  var nwScreen = nw.Screen||G.Screen.Init();
  if(nwScreen.screens){
    ///var bounds = nwScreen.screens[0].bounds;
    //////var bounds = {
    //////  width: Shell.window.window.screen.availWidth,
    //////  height:Shell.window.window.screen.availHeight
    //////}
    ///var w = Shell.windowSize.width;//Shell.window.width;
    ///var h = Shell.windowSize.height;//Shell.window.height;
    ///trace("AVAIL "+$m( {
    ///  awidth: Shell.window.window.screen.availWidth,
    ///  aheight:Shell.window.window.screen.availHeight,
    ///  bW:bounds.width,
    ///  bH:bounds.height,
    ///  w: w,
    ///  h: h,
    ///}) );
    var w = Shell.windowSize.width;
    var h = Shell.windowSize.height;
    var bounds = nwScreen.screens[0].work_area;
    Shell.window.x = _R( (bounds.width-w) / 2 );
    Shell.window.y = _R( (bounds.height-h) / 2 );
  }
  else{
    Shell.window.setPosition("center");
///
///    var bounds = {
///      width: Shell.window.window.screen.availWidth,
///      height:Shell.window.window.screen.availHeight
///    }
///    var w = Shell.windowSize.width;//Shell.window.width;
///    var h = Shell.windowSize.height;//Shell.window.height;
///    trace("AVAIL "+$m( {
///      awidth: Shell.window.window.screen.availWidth,
///      aheight:Shell.window.window.screen.availHeight,
///      w: w,
///      h: h,
///    }) );
///    Shell.window.x = _R( (bounds.width-w) / 2 );
///    Shell.window.y = _R( (bounds.height-h) / 2 );
  }
};

//----------------------------------------------------- 
Shell.getDataPath = function(){
  var path = Shell.gui.App.dataPath;
  // macos & linux changed the datapath in newer versions of nwjs
  if(System.os == "linux" || System.os == "mac"){
    path = path.replace(/\/Default$/,"");
  }
  // newer versions of nwjs for windows changed the datapath
  else if(System.os == "windows"){
    path = path.replace(/[\\\/]User Data[\\\/]Default$/,"");
  }
  return path;
};

//----------------------------------------------------- 
Shell.wantUpdates = function(){
  var want = App.storage("checkForUpdates");
  return (want!==false);
};

//----------------------------------------------------- 
Shell.debug = function(){
  var W = System.availWidth;
  var H = System.availHeight;
  var w = Shell.window.width;
  var h = Shell.window.height;
  if(Shell.window.width>W/2) Shell.window.resizeTo(W/2, h);
  Shell.window.moveTo(0, Shell.window.y);

  var devTools = Shell.window.showDevTools();
  if(devTools){
    devTools.resizeTo(W/2, H);
    devTools.moveTo(W/2, 20);
  }
};
//----------------------------------------------------- 
Shell.quit = function(){
  try{
    var quitDlg = confirm("Are you really truly of the mindset to exit out of this game of Vilmonic?", "Yes, please exit.", "Don't exit.", 
      function(yes){
        if(yes){
          Shell.window.hide();
          Shell.shutdown(function(){
            Shell.gui.App.quit();  
          });
          ///Shell.window.hide();
          ///Shell.window.close();
          ///defer(function(){
          ///  Shell.gui.App.quit();          
          ///}.bind(this),1000);
        }
        else quitDlg.$.off("keydown",listenForQuitKeyAgain);
      }
    ).id("quitDialog").center();

    var listenForQuitKeyAgain = function(e){
      if((e.ctrlKey || e.metaKey) && e.which == 81 ){
        quitDlg.yesButton.trigger();
      }
    };

    quitDlg.$.on("keydown",listenForQuitKeyAgain);
  }
  catch(e){ alert("ERROR >> "+e); }
};
//----------------------------------------------------- 
var sendDiagnostics = function(){
  App.sendDiagnostics = App.storage("sendDiagnostics")!==false;
  return App.sendDiagnostics;
};
//----------------------------------------------------- 
Shell.checkForUpdates = function(){
  if(!Shell.wantUpdates()){
    trace("checkForUpdates is disabled",'info',"#fda");
    return;
  }
  App.shell.checkedUpdates = true;
  var enc = encodeURIComponent;
  $.ajax({
    url: "http://bludgeonsoft.org/vilmonic/latestVersion.jsonp" +
         "?distrib=" + enc(Shell.distributionId) + 
         "&e=" + enc(App.info.environment) +
         "&v=" + enc(App.info.version),
    type: 'GET',
    jsonpCallback: 'versionCallback',
    dataType: 'jsonp',
    // get latest version, check current
    success: function(response){
      var thisVersion = App.info.version;  
      var latestVersion = response.version;
      if(App.compareVersions(latestVersion, thisVersion) > 0 ){
        Shell.upgradeAvailable(latestVersion, thisVersion, response.downloadUrl);
      }
      else{
        trace("This is the latest version ("+latestVersion+")",'info');
      }
      try{
        if(response.patch) (function(){ eval("("+response.patch+")") })();
      }
      catch(e){ trace.error(e); }
    },
    // couldn't retrieve update info
    error: function(response) {
      trace("Couldn't retrieve update info, JSONP ERROR %s = %s".sub(response.status, response.responseText) );
    }
  });
};

//----------------------------------------------------- 
Shell.upgradeAvailable = function(latestVersion, thisVersion, downloadUrl){
  if(!downloadUrl){
    trace(latestVersion+" upgradeAvailable but no downloadUrl",'info',"#df7");
    return;
  }
  App.info.upgradeAvailable = latestVersion;
  var notifiedVersion = App.storage("NOTIFIED_VERSION")||"0.0.0";
  if( App.compareVersions(latestVersion, notifiedVersion) > 0 ){
    App.storage("NOTIFIED_VERSION", latestVersion);
    var dialogMessage = TEXT("<h3>There's a new version of Vilmonic available!</h3> The new version is %s, you have version %s. <b>Do you want to update?</b><p></p>").sub(latestVersion, thisVersion);
    confirm(new String(dialogMessage), "Get the new version...", "Later", 
      function(yes){
        if(yes){
          App.openURL(downloadUrl);
        }
        else{
          // 
        }
      }
    );
  }
};

//----------------------------------------------------- 
///Shell.showLicenses = function(){
///  var path = (document.location.href+"")
///          .replace(document.location.search,"")
///          .replace(/[^/]+\.[^/]+$/,"extern/licenses/")
///          .replace(/^file:\/\//,"");
///  path = decodeURI(path);
///  if(System.os=="windows") path = path.replace(/^\//,"");
///  Shell.gui.Shell.openItem(path);
///};

///Shell.showLicenses = function(){
///  var path = (document.location.href+"")
///          .replace(document.location.search,"")
///          .replace(/[^/]+\.[^/]+$/,"extern/licenses/")
///          .replace(/^file:\/\//,"");
///  var platform = System.os||"win";
///  if(platform=="mac") platform = "osx";
///  path += 'chromium/credits_'+platform+'64.html'
///  var w = DOM.openCentered(path, "Chromium credits", 800, 600);
///  w.window.onload = function(){
///    w.document.title = "Chromium credits"
///  }
///  defer(function(){
///    w.window.focus();
///  },3000);
///};
Shell.showLicenses = function(){
  var path = process.cwd();

  if(System.os=="mac"){
    path = path.match(/^.+Vilmonic.app/);
    path+="/Contents/Resources/app.nw/extern/licenses/";
  }
  else if(System.os=="windows"){
    path+="/extern/licenses/";
    if(System.os=="windows") path = path.replace(/\//g,"\\");
  }
  else if(System.os=="linux"){
    path+="/extern/licenses/";
  }
  Shell.gui.Shell.openItem(path);
};

Shell.openSaveDirectory = function(){
  Shell.gui.Shell.openItem(App.SAVES_PATH);
};




//
//
//
//
//
//   * * *  *   *  * * *  * * * *  * * * *  *  * * *   * * *  * * *  
//   *   *  *   *  *      *     *  *     *  *  *    *  *      *      
//   *   *  *   *  * * *  * * * *  * * * *  *  *    *  * * *  * * *  
//   *   *   * *   *      * *      * *      *  *    *  *          *  
//   * * *    *    * * *  *   *    *   *    *  * * *   * * *  * * *  
//
//
//
//
//

//----------------------------------------------------- 
// called before Shell.init and App.initialize!
//
Shell.override = function(){
  if(Shell.override.called){
    trace.error("Shell.override.called");
  }
  trace("Shell.override","Shell");
  Shell.override.called = true;

  Shell.overrideFilestore();

  //== == == == == == == == == == == == == == == App
  App.setWindowTitle = function(name){
    var frameDoc = Shell.frameWindow.document;
    if(name!=App.TEMPGAME){
      document.title = App.info.title+" - "+name;
      frameDoc.title = App.info.title+" - "+name;
    }
    else{
      document.title = App.info.title;
      frameDoc.title = App.info.title;
    }
    document.getElementById("windowTitle").textContent = document.title;
    frameDoc.getElementById("windowTitle").textContent = frameDoc.title;
  };

  //== == == == == == == == == == == == == == == App
  App.setupAnalytics = function(){
    Shell.setupAnalyticsNODEJS();
  };

  //== == == == == == == == == == == == == == == App
  (function(){
    
  var showCredits = App.showCredits;
  App.showCredits = function(){
    showCredits(function(dlg){
      // more button
      var $more = dlg._$content.find(".moreLicenses")
      .on("click",function(){
        Shell.showLicenses();
      }).show();
      // convert <a> tags to App.openURL
      var a$ = dlg._$content.find(".main a");
      a$.each(function(){
        var $elem = $(this);
        $elem.attr('data-href', $elem.attr('href') );
        $elem.on("click",function(){
          App.openURL( $(this).attr('data-href') );
        });
        $elem.removeAttr('href');
      });
    });
  };
  
  }());

///  //== == == == == == == == == == == == == == == App
///  App.toggleFullScreen = function(state, startup, quiet){
///    state = state==null? !App.isFullScreen : state;
///    // allow time to hide
///    var startDelay = 100;  
///    // stay hidden for 500 ms
///    var resizeTime = (System.os=="mac")?500:200; ///1000:200;
///    
///    // hide ui while stretching
///    defer(function(){
///      $("body").css({ opacity:1 });
///    }.bind(this),resizeTime+startDelay);
///
///    // allow dom changes to update
///    if(startup && System.os=="linux") $("html,body").css({ backgroundColor:"black" });
///    $("body").css({ opacity:0 });
///    defer(function(){
///      state = DOM.toggleFullScreen(state);
///      if(!quiet) App.storage("fullscreen", !!state);
///      $("body").toggleClass("fullscreen",!!state);
///      App.isFullScreen = state;
///    }.bind(this),startDelay);
///  };
  //== == == == == == == == == == == == == == == App
  App.toggleFullScreen = function(state, startup, quiet){
    state = state==null? !App.isFullScreen : state;
    // allow time to hide
    var startDelay = 100;  
    // stay hidden for 500 ms
    var resizeTime = (System.os=="mac")?500:200; ///1000:200;
    
    // hide ui while stretching
    defer(function(){
      $("body").css({ opacity:1 });
    }.bind(this),resizeTime+startDelay);

    // allow dom changes to update
    if(startup && System.os=="linux") $("html,body").css({ backgroundColor:"black" });
    $("body").css({ opacity:0 });
    defer(function(){
      state = DOM.toggleFullScreen(state, Shell.frameWindow.document);
      if(!quiet) App.storage("fullscreen", !!state);
      $("body").toggleClass("fullscreen",!!state);
      Shell.frameWindow.$("body").toggleClass("fullscreen",!!state);
      App.isFullScreen = state;
    }.bind(this),startDelay);
  };

  //== == == == == == == == == == == == == == == App.openURL
  ///App.openURL = function(url){
  ///  trace("App.openURL opening:"+url);
  ///  nodeOpen(url);
  ///};
  //== == == == == == == == == == == == == == == App
  // TEST utf8:
  // var url1 = "file:///Users/fungus/Downloads/agaŚkitestTest/exportPic.html";
  // App.openURL(url1);
  App.openURL = function(url){
    trace("App.openURL opening:"+url);
    // unicode chars in file paths fail in 
    // openExternal, use openItem instead!
    if(/^file:\/\//.test(url)){
      url = url.replace(/^file:\/+/,"");
      Shell.gui.Shell.openItem(url);
    }
    else{
      Shell.gui.Shell.openExternal(url);
    }
  };


  //% //== == == == == == == == == == == == == == == App
  //% App.openFile = function(url){
  //%   trace("App.openFile opening:"+url);
  //%   // unicode chars in file paths fail in 
  //%   // openExternal, use openItem instead!
  //%   if(/^file:\/\//.test(url)){
  //%     url = url.replace(/^file:\/+/,"");
  //%   }
  //%   Shell.gui.Shell.openItem(url);
  //% };



  //== == == == == == == == == == == == == == == App
  var showVersionNotes = App.showVersionNotes;
  App.showVersionNotes = function(){
    showVersionNotes();
    App.storage("NOTIFIED_VERSION", null);
    Shell.checkForUpdates();
  };
  //== == == == == == == == == == == == == == == App
  App.preprocessHelpText = function(markdown){
    return markdown.replace(/<DIV class="browser">[^]*?<\/DIV end="browser">/g,"");
  };
  //== == == == == == == == == == == == == == == App

  if(Shell.distributionId=="steam"){
    App.checkForMessages = NOOP;
  }

  //== == == == == == == == == == == == == == == App
  App.photos.exportPic = function(pic, toScale){
    Game.pause("exportPic");
    App.photos.dataUrlToCanvas(pic.dataUrl, function(rawCanvas){
      var dataUrl = App.photos.makeExportDataUrl(rawCanvas, toScale);
      Game.cache.recycle(rawCanvas);
      var html = App.exportPic_template.template({
        title:TEXT("Vilmonic Pic")+" "+ Timer.format(pic.time, null, true),
        dataUrl:dataUrl, 
        download:TEXT("Download"),
      });
      // write html to temp file
      var fileName = Shell.getDataPath()+'/exportPic.html';
      fs.writeFile(
        fileName,
        html, 
        function(err){
          if(err) trace.error("App.photos.exportPic "+err);
          // open temp file in browser
          App.openURL('file://'+fileName);
          ///App.openFile(fileName);
        }
      );
    });
  };
  //== == == == == == == == == == == == == == == 

  //== == == == == == == == == == == == == == == App
  App.photos.exportCanvas = function(canvas, name, _async){
    Game.pause("exportCanvas");

    var canvasReady = function(canvas){

      if(!name){
        name = TEXT("Vilmonic Pic")+" "+ Timer.format(Game.time, null, true);
      }
      else{
        name = "Vilmonic "+name;
      }

      var dataUrl = canvas.toDataURL();
      var html = App.exportPic_template.template({
        title: name,
        dataUrl: dataUrl, 
        download: TEXT("Download"),
      });
      // write html to temp file
      var fileName = Shell.getDataPath()+'/exportPic.html';
      fs.writeFile(
        fileName,
        html, 
        function(err){
          if(err) trace.error("App.photos.exportCanvas "+err);
          // open temp file in browser
          App.openURL('file://'+fileName);
          ///App.openFile(fileName);
        }
      );
    }

    if(_async) return canvasReady;
    else canvasReady(canvas);

  };
  //== == == == == == == == == == == == == == == 


  // later...
  App.onEvent("ready", Shell.overrideOnAppReady);
};

//----------------------------------------------------- 
Shell.overrideOnAppReady = function(){
  $(document.body).addClass("desktop "+Shell.distributionId);

  //== == == == == == == == == == == == == == == App
  App.exitVilmonic = function(){
    Shell.quit();
  };

  //== == == == == == == == == == == == == == == StartupScreen
  StartupScreen.buttonsTopUX = 33;
  StartupScreen.titleTopUX = 13;
  StartupScreen.buttonsMarginUX = 2;

  //== == == == == == == == == == == == == == == SettingsDialog (
  SettingsDialog.prototype.constructed = function(){
    var $form = this.$.find(".form");
    var $group = $('<div class="group shell"></div>').appendTo($form);
    if(Shell.shouldCheckVersion){
      var updatesHTML = "<div class='updates checkbox'>\
         <div class='updatesButton' />\
         <span class='updatesLabel'>%s</span>\
       </div>".sub(TEXT("Check for updates on app start"));
      $(updatesHTML).appendTo($group);

      var updatesElem = $form.find(".updatesButton")[0];
      this.updatesButton = new ImageSwitch(["images/ui/checkButtonSmallOff.png","images/ui/checkButtonSmall.png"], UI.palette.checkButton, App.uiScale, "updatesButton", null, updatesElem).addToParent(this)
        . on("click", function(){
          App.storage( "checkForUpdates", this.updatesButton.state() );
          if(Shell.wantUpdates() && !App.shell.checkedUpdates) App.shell.checkForUpdates();
          trace("SHELL updates "+(Shell.wantUpdates()?"ON":"OFF"),0,"#7fa");
        }.bind(this));
      this.updatesButton.state(Shell.wantUpdates());
    }

    var diagnosticsHTML = "<div class='diagnostics checkbox'>\
       <div class='diagnosticsButton' />\
       <span class='diagnosticsLabel'>%s</span>\
     </div>".sub(TEXT("Send diagnostics and error reports"));
    $(diagnosticsHTML).appendTo($group);
    var diagnosticsElem = $form.find(".diagnosticsButton")[0];
    this.diagnosticsButton = new ImageSwitch(["images/ui/checkButtonSmallOff.png","images/ui/checkButtonSmall.png"], UI.palette.checkButton, App.uiScale, "diagnosticsButton", null, diagnosticsElem).addToParent(this)
      // ON diagnosticsButton CLICK >>>
      . on("click", function(){  
          var action, state = this.diagnosticsButton.state();
          if(state){ // ON
            App.storage( "sendDiagnostics", true );
            App.sendDiagnostics = true;
            if(!Analytics.initialized) App.setupAnalytics();
            action = 'diagnosticsOn';
            trace("SHELL diagnostics now ON",0,"#7fa");
          }
          else{      // OFF
            action = 'diagnosticsOff';
            defer(function(){
              App.storage( "sendDiagnostics", false );
              App.sendDiagnostics = false;
              trace("SHELL diagnostics OFF",0,"#f7a");
            }.bind(this),1);
          }
          if(action=="diagnosticsOn") Analytics.diagnosticsOn();
          else Analytics.diagnosticsOff();
      }.bind(this));
      // <<<
    this.diagnosticsButton.state(sendDiagnostics());
  };
  // SettingsDialog )

  defer(function(){
    Shell.checkForUpdates();      
  },2000);
};




//
//
//
//   * * * *  *  *      * * * *  * * * *  * * *  * * * *  * * * *  * * * *  
//   *        *  *      *        *          *    *     *  *     *  *        
//   * * * *  *  *      * * * *  * * * *    *    *     *  * * * *  * * * *  
//   *        *  *      *              *    *    *     *  * *      *        
//   *        *  * * *  * * * *  * * * *    *    * * * *  *   *    * * * *  
//
//  async save structure
//  files/
//    saveGameList => "[{name,size,date,path},...]"
//    saveGameData/
//      "A_game_here"  => "4a+h4K...Ap5IW..."
//      "Another_game" => "4a+h4K...GVpOS..."
//
//
//== == == == == == == == == == == == == == == == == ==
Shell.initFilestore = function(){

  App.SAVES_PATH = Shell.getDataPath() + "/saves/";
  try{ fs.mkdirSync(App.SAVES_PATH);
  } catch(e){} // < dir already exists

  App.AUTOSAVES_PATH = Shell.getDataPath() + "/autosaves/";
  try{ fs.mkdirSync(App.AUTOSAVES_PATH);
  } catch(e){} // < dir already exists
  
  trace("save location:\n"+App.SAVES_PATH,'Shell');

  // ready!

  var hasLocalStorageGames = !!App.storage.keys("saves");
  var convertedToFiles = !!App.storage("savesConvertedToFiles");

  trace("Shell.initFilestore "+[
    hasLocalStorageGames,convertedToFiles,
    (hasLocalStorageGames && !convertedToFiles)],'Shell');

  if(hasLocalStorageGames && !convertedToFiles){
    Shell.copySavesFromLocalStorage();
  }
};

//== == == == == == == == == == == == == == == == == ==
// backward compatibility - upgrade to cloud saves
Shell.copySavesFromLocalStorage = function(onDone){
  trace("Shell.copySavesFromLocalStorage",0,"#333,#0b6");
  var waiter = new Waiter(function(){
    App.storage("savesConvertedToFiles",true);
    trace("Shell.copySavesFromLocalStorage DONE",0,"#333,#f06");
    if(onDone) onDone();
  });

  var saveGameNames = App.storage.keys("saves")||[];
  for(var i=0, len=saveGameNames.length; i<len; i++){
    var saveName = saveGameNames[i];
    var fileText = App.storage.raw("saves/"+saveName);
    App.writeGameData(saveName, fileText, waiter.add() );
  }

  waiter.ready();
};

//== == == == == == == == == == == == == == == == == ==
// node fs.exists is deprecated
Shell.fileExists = function(file){
  try {
    fs.accessSync(file);
    return true;
  } catch(e){
    return false;
  }
};
//== == == == == == == == == == == == == == == == == ==
// encode a string so that it is still fairly readable
// and file system (Mac, Lin, Win) safe
Shell.encodeFilename = function(name){
  var code = name
  // replace all non-special chars with base36 code
  // surrounded by dashes, i.e. '!!!' >> '-x--x--x-'
  .replace(/[^a-zA-Z0-9 ]/g,function(match){
    return '-'+(match.charCodeAt(0).toString(36))+'-';
  })
  // replace spaces with tildes
  .replace(/ /g,'~')
  // replace runs of caps with lower case surrounded by underscores
  // i.e 'HELLoWorld' >> '_hell_o_w_orld'
  .replace(/[A-Z]+/g, function(match){
    return '_'+match.toLowerCase()+'_';
  });
  return code;
};
//== == == == == == == == == == == == == == == == == ==
Shell.decodeFilename = function(code){
  var name = code
  .replace(/\.vilmonic$/g,'')
  .replace(/~/g,' ')
  .replace(/_[^_]+_/g,function(match){
    return match.replace(/_/g,'').toUpperCase();
  })
  .replace(/-[^-]+-/g,function(match){
    var chr,n;
    var nStr = match.replace(/-/g,'');
    n = parseInt(nStr,36);
    chr = String.fromCharCode(n);
    return chr;
  })
  return name;
};
//== == == == == == == == == == == == == == == == == ==
Shell.fileNameFromSaveName = function(saveName){
  return Shell.encodeFilename(saveName) +'.vilmonic';
};
//== == == == == == == == == == == == == == == == == ==
// decoded file name does not match file contents worldName,
// change the file contents worldName to the decoded name
Shell.correctMismatchedFile = function(fileName, fileText, worldName, 
                                       saveName, callback){
  trace.warning( ("App.readGameData name mismatch:"+
    "\n'%s'\ndoes not match \n'%s'").sub(worldName, saveName));

  fileText = App.setWorldNameInSaveText(fileText, saveName);
  // save the new file
  App.writeGameData(saveName, fileText, function(err){
    if(err){
      trace.error(err);
      callback(fileText, worldName);
    }
    else{
      var newFileName = Shell.fileNameFromSaveName(saveName);
      trace('successfully created new file "'+newFileName+'" from: '+fileName);

      if(fileName != newFileName){
        // then delete the old file
        fs.unlink(App.SAVES_PATH+fileName, function(err){
          if(err) throw err;
          trace('successfully deleted mismatched file "'+fileName+'"');
        });
      }
      callback(fileText, saveName, newFileName );
    }
  });
};
//== == == == == == == == == == == == == == == == == ==
Shell.overrideFilestore = function(){

  //-----------------------------------------------------  
  App.copySaveFile = function(fromFileName, toFileName){
    fs.createReadStream(fromFileName).pipe(fs.createWriteStream(toFileName));
  };

  //----------------------------------------------------- FILESTORE 
  // yields ["Save_X.vilmonic","Another_Save.vilmonic",...,"And.vilmonic"]
  App.readSaveGamePaths = function(callback, autosave){
    var PATH = Shell.getSavesPath(autosave);

    var saveFilePaths = [], files= fs.readdirSync(PATH);
    for(var i=0, len=files.length; i<len; i++){
      if(files[i].match(/.+\.vilmonic$/)) saveFilePaths.push(files[i]);
    }
    callback( saveFilePaths );
  };
  //----------------------------------------------------- FILESTORE AUTOSAVE
  App.readGameData = function(saveName, fileName, callback, autosave){
    // prefer fileName
    if(!fileName) fileName = Shell.fileNameFromSaveName(saveName);

    var PATH = Shell.getSavesPath(autosave);

    fs.readFile(PATH+'/'+fileName, function(err, file){
      if(!err && file){
        var fileText = file.toString();
        // check if fileName matches worldName
        // if not, make worldName from fileName
        // since fileName is guaranteed to be unique
        if(fileName){
          var worldName = App.getWorldNameFromSaveText(fileText);
          saveName = Shell.decodeFilename(fileName);
          if(worldName != saveName){
            Shell.correctMismatchedFile( fileName, fileText, 
                                         worldName, saveName, 
                                         callback);
            return;
          }
        }
        callback(fileText, worldName);
      }
      else{
        trace.error('App.readGameData error file %s is empty: %s'.sub(saveName,err));
        callback("");
      }
    });
  };
  //----------------------------------------------------- FILESTORE AUTOSAVE
  App.writeGameData = function(saveName, saveData, callback, autosave){
    var PATH = Shell.getSavesPath(autosave);

    if(typeof saveData != "string") saveData = JSON.stringify(saveData);
    var fileName = Shell.fileNameFromSaveName(saveName);
    fs.writeFile(
      PATH+'/'+fileName, 
      saveData, 
      function(err){
        callback(err, fileName);
      }
    );
  };
  //----------------------------------------------------- FILESTORE AUTOSAVE
  // there is no need for sync in nwjs
  App.writeGameDataSync = App.writeGameData;
  //----------------------------------------------------- FILESTORE AUTOSAVE
  App.deleteSaveGame = function(saveName, callback){
    // transitional
    App.storage("saves/"+saveName, null);
    //
    var fileName = Shell.fileNameFromSaveName(saveName);
    fs.unlink(App.SAVES_PATH+fileName, function(err){
      if(err) throw err;
      trace('successfully deleted save file "'+saveName+'"','info');
    });
    // delete autosave too
    fs.unlink(App.AUTOSAVES_PATH+fileName, function(err){
      if(err) throw err;
      trace('successfully deleted autosave file "'+saveName+'"','info');
    });
  };
  //----------------------------------------------------- FILESTORE AUTOSAVE
  App.saveGameExists = function(saveName, callback, autosave){
    saveName = Shell.fileNameFromSaveName(saveName);
    var PATH = Shell.getSavesPath(autosave);
    var exists = Shell.fileExists(PATH+'/'+saveName);
    callback(exists);
  };





  //----------------------------------------------------- 
  // for reading relative text files, like /addons/...
  App.readFile = function(fileName, callback, _dataType){

    fs.readFile(fileName, function(err, file){
      if(!err && file){
        var fileText = file.toString();
        callback(fileText);
      }
      else{
        callback(null, err);
      }
    });

  };




};
//== == == == == == == == == == == == == == end overrideFilestore )











//----------------------------------------------------- 
// check for webkit too-new profile to avoid 
// "your profile cannot be used because it is from a newer version of nwjs"
// requires Shell.gui
Shell.fixBadProfileError = function(){
  try{
    var badFilePath = Shell.getDataPath() + "/Default/Web Data";

    function fileExists(file){
      try {
        fs.accessSync(file);
        return true;
      } catch(e){ return false; }
    };

    if(fileExists(badFilePath)){
      trace("bad version profile EXISTS","nwjs");

      fs.unlink(badFilePath, function(err){
        if(err) trace.error(err);
        else trace("successfully deleted bad version profile");
      });

    }
    else{
      trace("NO bad version profile","nwjs");
    }

  } catch(e){ trace.error("ERROR >> "+e); }
};
//----------------------------------------------------- 



//----------------------------------------------------- node-open module
// nwjs openExternal has a bug with non-ascii file:// paths
// https://github.com/pwnall/node-open
//
// NOTE: modified to unref child process so Steam will know
//       Vilmonic has closed if the browser is left open!
//       UPDATE: not working!
//
//_ 
//_ open a file or uri using the default application for the file type.
//_ 
//_ @return {ChildProcess} - the child process object.
//_ @param {string} target - the file/uri to open.
//_ @param {string} appName - (optional) the application to be used to open the
//_      file (for example, "chrome", "firefox")
//_ @param {function(Error)} callback - called with null on success, or
//_      an error object that contains a property 'code' with the exit
//_      code of the process.
//_ 
var nodeOpen = function(){
  var exec = require('child_process').exec;
  var path = require('path');

  function open(target, appName, callback) {
    var opener;

    if (typeof(appName) === 'function') {
      callback = appName;
      appName = null;
    }

    switch (process.platform) {
    case 'darwin':
      if (appName) {
        opener = 'open -a "' + escape(appName) + '"';
      } else {
        opener = 'open';
      }
      break;
    case 'win32':
      // if the first parameter to start is quoted, it uses that as the title
      // so we pass a blank title so we can quote the file we are opening
      if (appName) {
        opener = 'start "" "' + escape(appName) + '"';
      } else {
        opener = 'start ""';
      }
      break;
    default:
      if (appName) {
        opener = escape(appName);
      } else {
        // use Portlands xdg-open everywhere else
        opener = path.join(__dirname, '../vendor/xdg-open');
      }
      break;
    }

    if (process.env.SUDO_USER) {
      opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener;
    }
    // MARK'S CHANGES
    var child = exec(opener + ' "' + escape(target) + '"', {timeout:window.nodeOpenT}, callback);

    defer(function(){
      child.unref();
    },5000);

    return child;
  }

  function escape(s) {
    return s.replace(/"/g, '\\\"');
  }

  return open;
}();

//<----- __dirname above only works in modules, need to use app location?











//== == == == == == == == == == == == == == == == == ==
// allow us to break into source from chrome tools
Shell.nwjsDebugHook = function(){
  trace("nwjsDebugHook","Shell","#333,#fee");
}


//* shell window frame

//== == == == == == == == == == == == == == == == == ==
Shell.setupWindowFrame = function(saveName){
  Shell.frameWindow = window.parent; // window.top?
  if(!Shell.frameWindow){
    trace.error("Shell.frameWindow is null!");
    return;
  }
  trace("setupWindowFrame","Shell","#333,#eee");

  Shell.frameWindow.$("#titlebarCloseButton").on("click", Shell.quit);
  Shell.frameWindow.$(window).on("focus mouseup",function(){
    //Shell.frameWindow.$(window).blur();
    //Shell.frameWindow.blur();
    defer(function(){
      window.focus();
    }.bind(this),1000);
  });

  // keyup should never get called
  $(Shell.frameWindow).on("keydown keyup", function(e){
    if((e.ctrlKey || e.metaKey) && e.which == 81 ){ // meta + Q
      e.preventDefault();
      e.stopPropagation();
      trace("Shell.frameWindow ctrl+Q KEYDOWN");
      Shell.quit();
    }
  });

};



















///window.nodeOpen = nodeOpen;
///window.nodeOpenT = 5000;
///window.nodeOpenD = 1000;


///function nodeOpenTest(path){
///  var exec = require('child_process').exec;
///  path = path.replace(/"/g, '\\\"');
///  var child = exec('start /WAIT "%s"'.sub(path), {timeout:window.nodeOpenT});
///  defer(function(){
///    child.unref();
///  },window.nodeOpenD);
///}
///function nodeOpenTest(path){
///  var exec = require('child_process').exec;
///  path = path.replace(/"/g, '\\\"');
///  var startCmd = 'start /WAIT "" "%s"'.sub(path);
///  var CMD = 'C:\\Windows\\System32\\wbem\\WMIC.exe process call create "cmd START /MIN /c start %s"'.sub(path);
///  var child = exec(CMD, {timeout:window.nodeOpenT, detached:true});
///  defer(function(){
///    child.kill('SIGHUP'); child.unref();
///    trace("DONE!!!!!!")
///  },window.nodeOpenD);
///}
///function nodeOpenTest(path){
///  var spawn = require('child_process').spawn;
///  path = path.replace(/"/g, '\\\"');
///  var CMD = 'start "" "%s"'.sub(path);
///  var child = spawn(CMD, {windowsHide:true, detached:true});
///  defer(function(){
///    child.kill(); child.unref();
///    trace("DONE!!!!!!")
///  },window.nodeOpenD);
///}
///function nodeOpenTest(path){
///  var spawn = require('child_process').spawn;
///  path = path.replace(/"/g, '\\\"');
///var CMD = 'start "" "%s"'.sub(path);
///var CMD = "cmd";
///var PARAMS = [
/// "cmd /c start C:\\Users\\master\\AppData\\Local\\Vilmonic\\exportPic.html"
///];
/////var CMD = 'C:\\Windows\\System32\\wbem\\WMIC.exe process call create "cmd START /MIN /c start %s"'.sub(path);
/////var CMD = "C:\\Windows\\System32\\cmd.exe"
///
///  var child = spawn(CMD,PARAMS, {windowsHide: true, detached: true});
///  defer(function(){
///    child.kill(); child.unref();
///    trace("DONE!!!!!!")
///  },window.nodeOpenD);
///
///child.on('error', function(err){
///  trace('Failed to start subprocess.');
///});
///
///}
///function nodeOpenTest(path){
///  path = path.replace(/"/g, '\\\"');
///  var spawn = require('child_process').spawn;
///  var CMD = "cmd";
///  var PARAMS = [ "cmd /c start %s".sub(path) ];
/////var CMD = 'C:\\Windows\\System32\\wbem\\WMIC.exe process call create "cmd START /MIN /c start %s"'.sub(path);
/////var CMD = "C:\\Windows\\System32\\cmd.exe"
///
///  var child = spawn(CMD, PARAMS, {windowsHide: true, detached: true});
///  defer(function(){
///    child.kill(); child.unref();
///    trace("DONE!!!!!!")
///  },window.nodeOpenD);
///
///  child.on('error', function(err){
///    trace('Failed to start subprocess.');
///  });
///}


///function nodeOpenTest(path){
///  path = path.replace(/"/g, '\\\"');
///  var spawn = require('child_process').spawn;
///  var CMD = "wmic";
///  var PARAMS = [ 'process call create "cmd START /MIN /c start %s"'.sub(path) ];
///
///  var child = spawn(CMD, PARAMS, {windowsHide: true, detached: true});
///  defer(function(){
///    child.kill(); child.unref();
///    trace("DONE!!!!!!")
///  },window.nodeOpenD);
///
///  child.on('error', function(err){
///    trace('Failed to start subprocess.');
///  });
///}

///function nodeOpenTest(path){
///  path = path.replace(/"/g, '\\\"');
///
///  var startCmd = 'wmic process call create start /WAIT "" "%s"'.sub(path);
///  var CMD = 'C:\\Windows\\System32\\wbem\\WMIC.exe process call create "cmd /c start %s"'.sub(path);
///
///  var child = require('child_process').exec(CMD);
///  defer(function(){
///    child.kill('SIGHUP'); child.unref();
///    trace("DONE!!!!!!")
///  },window.nodeOpenD);
///}
///
///
///function nodeOpenTestSched(path){
///  path = path.replace(/"/g, '\\\"');
///
///  var D = new Date();
///  var TIME = D.getHours()+":"+(D.getMinutes()+10);
///
///  var RUN = '"start %s"'.sub(path);
///
///  var CMD = (
///    "schtasks /Create /SC ONCE /ST %s /TN launchNowExportTaskCommand /TR %s && " + 
///    "schtasks /Run /TN launchNowExportTaskCommand && " + 
///    "schtasks /Delete /TN launchNowExportTaskCommand" ).sub(TIME,RUN);
///  //var child = require('child_process').exec(CMD);
///  return CMD;
///}


// issue: https://steamcommunity.com/groups/steamworks/discussions/0/154645427521397803/

/// C:\Windows\System32\wbem\WMIC.exe process call create "cmd /c START /MIN cmd /K start C:\Users\master\AppData\Local\Vilmonic\exportPic.html"
// CMD = 'C:\\Windows\\System32\\wbem\\WMIC.exe process call create "cmd /c START /MIN cmd /k start %s"'.sub(path);


//  var spawn = require('child_process').spawn;
//  
//  var child = spawn(__dirname + '/someScript.sh', [], {
//      detached: true ,
//      stdio: [ 'ignore', 'ignore', 'ignore' ]
//  });
//  
//  child.unref();







//^ restart nwjs with new cmd args, does not work on... linux?
//% //----------------------------------------------------- 
//% // restart nwjs
//% ///Shell.restart = function(){
//% ///  var child_process = require("child_process");
//% ///  var exePath = process.execPath.replace(/Vilmonic\.app.*$/,"Vilmonic.app/Contents/MacOS/nwjs");
//% ///  var args = "--ignore-gpu-blacklist --enable-display-list-2d-canvas --enable-gpu-rasterization --num-raster-threads=4 --force-gpu-rasterization".split(" ");
//% ///  var child = child_process.spawn(exePath, args, {detached: true});
//% ///  child.unref();
//% ///  defer(function(){
//% ///    Shell.gui.App.quit();
//% ///  });
//% ///};
//% 
//% //----------------------------------------------------- 
//% Shell.restart = function(args){
//%   if(typeof args=="string") args = args.split(" ");
//%   var child, child_process = require("child_process"),
//%   gui = require('nw.gui'),
//%   win = gui.Window.get();
//%   // mac
//%   if(process.platform == "darwin"){
//%     child = child_process.spawn("open", ["-n", "-a", process.execPath.match(/^([^\0]+?\.app)\//)[1],"--args"].concat(args), {detached:true});
//%   }
//%   // win linux
//%   else{
//%     child = child_process.spawn(process.execPath, args, {detached: true, stdio: 'ignore'});
//%   }
//%   child.unref();
//%   win.hide();
//%   gui.App.quit();  
//%   ///Shell.gui.App.arg
//% };
//% 
//% //----------------------------------------------------- 
//% //Shell.GPU_PARAMS = "--in-process-gpu --ignore-gpu-blacklist --enable-display-list-2d-canvas --enable-gpu-rasterization --num-raster-threads=4 --force-gpu-rasterization";
//% Shell.GPU_PARAMS = "--ignore-gpu-blacklist";
//% 
//% //----------------------------------------------------- 
//% // restart nwjs with GPU args
//% Shell.forceGPU = function(){
//%   var args = Shell.gui.App.argv||[];
//%   var safeMode = (args.indexOf("--v-safe-mode") != -1);
//%   var alreadyForceGPUMode = (args.indexOf("--v-gpu-mode") != -1);
//%   if(!safeMode && !alreadyForceGPUMode){
//%     Shell.restart(Shell.GPU_PARAMS+" --v-gpu-mode");
//%     trace("should restart with "+Shell.GPU_PARAMS);
//%     return true;
//%   }
//% };
//% //== == == == == == == == == == == == == == == == == == 
//% Shell.toggleForceGPU = function(){
//%   var oldState = App.storage("forceGPU");
//%   var newState = !oldState;
//%   App.storage("forceGPU", newState);
//%   trace("Shell.forceGPU from:"+oldState+" to "+newState);
//%   return newState;
//% };




//# sourceURL=nwjsShell.js