<commit_before>

// SETUP /////////////////////////////////////////////////////////////
// these functions set up specific areas after the boot function
// created a basic framework. All of these functions should only ever
// be run once.

window.setupLargeImagePreview = function() {
  $('#portaldetails').on('click', '.imgpreview img', function() {
    var ex = $('#largepreview');
    if(ex.length > 0) {
      ex.remove();
      return;
    }
    var img = $(this).parent().html();
    var w = $(this)[0].naturalWidth/2;
    var h = $(this)[0].naturalHeight/2;
    var c = $('#portaldetails').attr('class');
    $('body').append(
      '
' + img + '
'
    );
    $('#largepreview').click(function() { $(this).remove() });
    $('#largepreview img').attr('title', '');
  });
}

window.setupStyles = function() {
  $('head').append('');
}

window.setupMap = function() {
  $('#map').text('');

  var osmOpt = {attribution: 'Map data © OpenStreetMap contributors', maxZoom: 18};
  var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', osmOpt);

  var cmOpt = {attribution: 'Map data © OpenStreetMap contributors, Imagery © CloudMade', maxZoom: 18};
  var cmMin = new L.TileLayer('http://{s}.tile.cloudmade.com/654cef5fd49a432ab81267e200ecc502/22677/256/{z}/{x}/{y}.png', cmOpt);
  var cmMid = new L.TileLayer('http://{s}.tile.cloudmade.com/654cef5fd49a432ab81267e200ecc502/999/256/{z}/{x}/{y}.png', cmOpt);

  var views = [cmMid, cmMin, osm, new L.Google('INGRESS'), new L.Google('ROADMAP'),
               new L.Google('SATELLITE'), new L.Google('HYBRID')];

  portalsLayer = L.layerGroup([]);
  linksLayer = L.layerGroup([]);
  fieldsLayer = L.layerGroup([]);
  window.map = new L.Map('map', $.extend(getPosition(),
    {zoomControl: !(localStorage['iitc.zoom.buttons'] === 'false')}
  ));
  try {
    map.addLayer(views[readCookie('ingress.intelmap.type')]);
  } catch(e) { map.addLayer(views[0]); }
  map.addLayer(portalsLayer);
  map.addLayer(fieldsLayer, true);
  map.addLayer(linksLayer, true);
  map.addControl(new L.Control.Layers({
    'OSM Cloudmade Midnight': views[0],
    'OSM Cloudmade Minimal': views[1],
    'OSM Mapnik': views[2],
    'Google Roads Ingress Style': views[3],
    'Google Roads':  views[4],
    'Google Satellite':  views[5],
    'Google Hybrid':  views[6]
    }, {
    'Portals': portalsLayer,
    'Links': linksLayer,
    'Fields': fieldsLayer
    }));
  map.attributionControl.setPrefix('');
  // listen for changes and store them in cookies
  map.on('moveend', window.storeMapPosition);
  map.on('zoomend', window.storeMapPosition);
  $("[name='leaflet-base-layers']").change(function () {
    writeCookie('ingress.intelmap.type', $(this).parent().index());
  });

  // map update status handling
  map.on('movestart zoomstart', function() { window.mapRunsUserAction = true });
  map.on('moveend zoomend', function() { window.mapRunsUserAction = false });

  // update map hooks
  map.on('movestart zoomstart', window.requests.abort);
  map.on('moveend zoomend', function() { window.startRefreshTimeout(500) });

  // run once on init
  window.requestData();
  window.startRefreshTimeout();

  window.addResumeFunction(window.requestData);
  window.requests.addRefreshFunction(window.requestData);
};

// renders player details into the website. Since the player info is
// included as inline script in the original site, the data is static
// and cannot be updated.
window.setupPlayerStat = function() {
  var level;
  var ap = parseInt(PLAYER.ap);
  for(level = 0; level < MIN_AP_FOR_LEVEL.length; level++) {
    if(ap < MIN_AP_FOR_LEVEL[level]) break;
  }

  var thisLvlAp = MIN_AP_FOR_LEVEL[level-1];
  var nextLvlAp = MIN_AP_FOR_LEVEL[level] || ap;
  var lvlUpAp = digits(nextLvlAp-ap);
  var lvlApProg = Math.round((ap-thisLvlAp)/(nextLvlAp-thisLvlAp)*100);

  var xmMax = MAX_XM_PER_LEVEL[level];
  var xmRatio = Math.round(PLAYER.energy/xmMax*100);

  var cls = PLAYER.team === 'ALIENS' ? 'enl' : 'res';

  var t = 'Level:\t\t' + level + '\n'
        + 'XM:\t\t\t' + PLAYER.energy + ' / ' + xmMax + '\n'
        + 'AP:\t\t\t' + digits(ap) + '\n'
        + (level < 8 ? 'level up in:\t' + lvlUpAp + ' AP' : 'Congrats! (neeeeerd)')
        + '\n\Invites:\t\t'+PLAYER.available_invites;
        + '\n\nNote: your player stats can only be updated by a full reload (F5)';

  $('#playerstat').html(''
    + '
'+level+' '
    + ''+PLAYER.nickname+''
    + '
'
    + 'XM: '+xmRatio+'%'
    + '' + (level < 8 ? 'level: '+lvlApProg+'%' : 'max level') + ''
    + '
'
    + '
'
  );
}

// BOOTING ///////////////////////////////////////////////////////////

function boot() {
  console.log('loading done, booting');
  window.setupStyles();
  window.setupMap();
  window.setupGeosearch();
  window.setupRedeem();
  window.setupLargeImagePreview();
  window.updateGameScore();
  window.setupPlayerStat();
  window.chat.setup();
  // read here ONCE, so the URL is only evaluated one time after the
  // necessary data has been loaded.
  urlPortal = getURLParam('pguid');

  // load only once
  var n = window.PLAYER['nickname'];
  window.PLAYER['nickMatcher'] = new RegExp('\\b('+n+')\\b');

  $('#sidebar').show();
}

// this is the minified load.js script that allows us to easily load
// further javascript files async as well as in order.
// https://github.com/chriso/load.js
// Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com>. MIT Licensed
function asyncLoadScript(a){return function(b,c){var d=document.createElement("script");d.type="text/javascript",d.src=a,d.onload=b,d.onerror=c,d.onreadystatechange=function(){var a=this.readyState;if(a==="loaded"||a==="complete")d.onreadystatechange=null,b()},head.insertBefore(d,head.firstChild)}}(function(a){a=a||{};var b={},c,d;c=function(a,d,e){var f=a.halt=!1;a.error=function(a){throw a},a.next=function(c){c&&(f=!1);if(!a.halt&&d&&d.length){var e=d.shift(),g=e.shift();f=!0;try{b[g].apply(a,[e,e.length,g])}catch(h){a.error(h)}}return a};for(var g in b){if(typeof a[g]=="function")continue;(function(e){a[e]=function(){var g=Array.prototype.slice.call(arguments);if(e==="onError"){if(d)return b.onError.apply(a,[g,g.length]),a;var h={};return b.onError.apply(h,[g,g.length]),c(h,null,"onError")}return g.unshift(e),d?(a.then=a[e],d.push(g),f?a:a.next()):c({},[g],e)}})(g)}return e&&(a.then=a[e]),a.call=function(b,c){c.unshift(b),d.unshift(c),a.next(!0)},a.next()},d=a.addMethod=function(d){var e=Array.prototype.slice.call(arguments),f=e.pop();for(var g=0,h=e.length;g<h;g++)typeof e[g]=="string"&&(b[e[g]]=f);--h||(b["then"+d.substr(0,1).toUpperCase()+d.substr(1)]=f),c(a)},d("chain",function(a){var b=this,c=function(){if(!b.halt){if(!a.length)return b.next(!0);try{null!=a.shift().call(b,c,b.error)&&c()}catch(d){b.error(d)}}};c()}),d("run",function(a,b){var c=this,d=function(){c.halt||--b||c.next(!0)},e=function(a){c.error(a)};for(var f=0,g=b;!c.halt&&f<g;f++)null!=a[f].call(c,d,e)&&d()}),d("defer",function(a){var b=this;setTimeout(function(){b.next(!0)},a.shift())}),d("onError",function(a,b){var c=this;this.error=function(d){c.halt=!0;for(var e=0;e<b;e++)a[e].call(c,d)}})})(this);var head=document.getElementsByTagName("head")[0]||document.documentElement;addMethod("load",function(a,b){for(var c=[],d=0;d<b;d++)(function(b){c.push(asyncLoadScript(a[b]))})(d);this.call("run",c)})

// modified version of https://github.com/shramov/leaflet-plugins. Also
// contains the default Ingress map style.
var LLGMAPS = 'http://breunigs.github.com/ingress-intel-total-conversion/leaflet_google.js';
var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js';
var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js';
var AUTOLINK = 'https://raw.github.com/bryanwoods/autolink-js/master/autolink.js';

// after all scripts have loaded, boot the actual app
load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).thenRun(boot);
<commit_msg>fix chat input being too narrow<commit_after>

// SETUP /////////////////////////////////////////////////////////////
// these functions set up specific areas after the boot function
// created a basic framework. All of these functions should only ever
// be run once.

window.setupLargeImagePreview = function() {
  $('#portaldetails').on('click', '.imgpreview img', function() {
    var ex = $('#largepreview');
    if(ex.length > 0) {
      ex.remove();
      return;
    }
    var img = $(this).parent().html();
    var w = $(this)[0].naturalWidth/2;
    var h = $(this)[0].naturalHeight/2;
    var c = $('#portaldetails').attr('class');
    $('body').append(
      '
' + img + '
'
    );
    $('#largepreview').click(function() { $(this).remove() });
    $('#largepreview img').attr('title', '');
  });
}

window.setupStyles = function() {
  $('head').append('');
}

window.setupMap = function() {
  $('#map').text('');

  var osmOpt = {attribution: 'Map data © OpenStreetMap contributors', maxZoom: 18};
  var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', osmOpt);

  var cmOpt = {attribution: 'Map data © OpenStreetMap contributors, Imagery © CloudMade', maxZoom: 18};
  var cmMin = new L.TileLayer('http://{s}.tile.cloudmade.com/654cef5fd49a432ab81267e200ecc502/22677/256/{z}/{x}/{y}.png', cmOpt);
  var cmMid = new L.TileLayer('http://{s}.tile.cloudmade.com/654cef5fd49a432ab81267e200ecc502/999/256/{z}/{x}/{y}.png', cmOpt);

  var views = [cmMid, cmMin, osm, new L.Google('INGRESS'), new L.Google('ROADMAP'),
               new L.Google('SATELLITE'), new L.Google('HYBRID')];

  portalsLayer = L.layerGroup([]);
  linksLayer = L.layerGroup([]);
  fieldsLayer = L.layerGroup([]);
  window.map = new L.Map('map', $.extend(getPosition(),
    {zoomControl: !(localStorage['iitc.zoom.buttons'] === 'false')}
  ));
  try {
    map.addLayer(views[readCookie('ingress.intelmap.type')]);
  } catch(e) { map.addLayer(views[0]); }
  map.addLayer(portalsLayer);
  map.addLayer(fieldsLayer, true);
  map.addLayer(linksLayer, true);
  map.addControl(new L.Control.Layers({
    'OSM Cloudmade Midnight': views[0],
    'OSM Cloudmade Minimal': views[1],
    'OSM Mapnik': views[2],
    'Google Roads Ingress Style': views[3],
    'Google Roads':  views[4],
    'Google Satellite':  views[5],
    'Google Hybrid':  views[6]
    }, {
    'Portals': portalsLayer,
    'Links': linksLayer,
    'Fields': fieldsLayer
    }));
  map.attributionControl.setPrefix('');
  // listen for changes and store them in cookies
  map.on('moveend', window.storeMapPosition);
  map.on('zoomend', window.storeMapPosition);
  $("[name='leaflet-base-layers']").change(function () {
    writeCookie('ingress.intelmap.type', $(this).parent().index());
  });

  // map update status handling
  map.on('movestart zoomstart', function() { window.mapRunsUserAction = true });
  map.on('moveend zoomend', function() { window.mapRunsUserAction = false });

  // update map hooks
  map.on('movestart zoomstart', window.requests.abort);
  map.on('moveend zoomend', function() { window.startRefreshTimeout(500) });

  // run once on init
  window.requestData();
  window.startRefreshTimeout();

  window.addResumeFunction(window.requestData);
  window.requests.addRefreshFunction(window.requestData);
};

// renders player details into the website. Since the player info is
// included as inline script in the original site, the data is static
// and cannot be updated.
window.setupPlayerStat = function() {
  var level;
  var ap = parseInt(PLAYER.ap);
  for(level = 0; level < MIN_AP_FOR_LEVEL.length; level++) {
    if(ap < MIN_AP_FOR_LEVEL[level]) break;
  }

  var thisLvlAp = MIN_AP_FOR_LEVEL[level-1];
  var nextLvlAp = MIN_AP_FOR_LEVEL[level] || ap;
  var lvlUpAp = digits(nextLvlAp-ap);
  var lvlApProg = Math.round((ap-thisLvlAp)/(nextLvlAp-thisLvlAp)*100);

  var xmMax = MAX_XM_PER_LEVEL[level];
  var xmRatio = Math.round(PLAYER.energy/xmMax*100);

  var cls = PLAYER.team === 'ALIENS' ? 'enl' : 'res';

  var t = 'Level:\t\t' + level + '\n'
        + 'XM:\t\t\t' + PLAYER.energy + ' / ' + xmMax + '\n'
        + 'AP:\t\t\t' + digits(ap) + '\n'
        + (level < 8 ? 'level up in:\t' + lvlUpAp + ' AP' : 'Congrats! (neeeeerd)')
        + '\n\Invites:\t\t'+PLAYER.available_invites;
        + '\n\nNote: your player stats can only be updated by a full reload (F5)';

  $('#playerstat').html(''
    + '
'+level+' '
    + ''+PLAYER.nickname+''
    + '
'
    + 'XM: '+xmRatio+'%'
    + '' + (level < 8 ? 'level: '+lvlApProg+'%' : 'max level') + ''
    + '
'
    + '
'
  );
}

// BOOTING ///////////////////////////////////////////////////////////

function boot() {
  console.log('loading done, booting');
  window.setupStyles();
  window.setupMap();
  window.setupGeosearch();
  window.setupRedeem();
  window.setupLargeImagePreview();
  window.updateGameScore();
  window.setupPlayerStat();
  window.chat.setup();
  // read here ONCE, so the URL is only evaluated one time after the
  // necessary data has been loaded.
  urlPortal = getURLParam('pguid');

  // load only once
  var n = window.PLAYER['nickname'];
  window.PLAYER['nickMatcher'] = new RegExp('\\b('+n+')\\b');

  $('#sidebar').show();
}

// this is the minified load.js script that allows us to easily load
// further javascript files async as well as in order.
// https://github.com/chriso/load.js
// Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com>. MIT Licensed
function asyncLoadScript(a){return function(b,c){var d=document.createElement("script");d.type="text/javascript",d.src=a,d.onload=b,d.onerror=c,d.onreadystatechange=function(){var a=this.readyState;if(a==="loaded"||a==="complete")d.onreadystatechange=null,b()},head.insertBefore(d,head.firstChild)}}(function(a){a=a||{};var b={},c,d;c=function(a,d,e){var f=a.halt=!1;a.error=function(a){throw a},a.next=function(c){c&&(f=!1);if(!a.halt&&d&&d.length){var e=d.shift(),g=e.shift();f=!0;try{b[g].apply(a,[e,e.length,g])}catch(h){a.error(h)}}return a};for(var g in b){if(typeof a[g]=="function")continue;(function(e){a[e]=function(){var g=Array.prototype.slice.call(arguments);if(e==="onError"){if(d)return b.onError.apply(a,[g,g.length]),a;var h={};return b.onError.apply(h,[g,g.length]),c(h,null,"onError")}return g.unshift(e),d?(a.then=a[e],d.push(g),f?a:a.next()):c({},[g],e)}})(g)}return e&&(a.then=a[e]),a.call=function(b,c){c.unshift(b),d.unshift(c),a.next(!0)},a.next()},d=a.addMethod=function(d){var e=Array.prototype.slice.call(arguments),f=e.pop();for(var g=0,h=e.length;g<h;g++)typeof e[g]=="string"&&(b[e[g]]=f);--h||(b["then"+d.substr(0,1).toUpperCase()+d.substr(1)]=f),c(a)},d("chain",function(a){var b=this,c=function(){if(!b.halt){if(!a.length)return b.next(!0);try{null!=a.shift().call(b,c,b.error)&&c()}catch(d){b.error(d)}}};c()}),d("run",function(a,b){var c=this,d=function(){c.halt||--b||c.next(!0)},e=function(a){c.error(a)};for(var f=0,g=b;!c.halt&&f<g;f++)null!=a[f].call(c,d,e)&&d()}),d("defer",function(a){var b=this;setTimeout(function(){b.next(!0)},a.shift())}),d("onError",function(a,b){var c=this;this.error=function(d){c.halt=!0;for(var e=0;e<b;e++)a[e].call(c,d)}})})(this);var head=document.getElementsByTagName("head")[0]||document.documentElement;addMethod("load",function(a,b){for(var c=[],d=0;d<b;d++)(function(b){c.push(asyncLoadScript(a[b]))})(d);this.call("run",c)})

// modified version of https://github.com/shramov/leaflet-plugins. Also
// contains the default Ingress map style.
var LLGMAPS = 'http://breunigs.github.com/ingress-intel-total-conversion/leaflet_google.js';
var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js';
var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js';
var AUTOLINK = 'https://raw.github.com/bryanwoods/autolink-js/master/autolink.js';

// after all scripts have loaded, boot the actual app
load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).thenRun(boot);
<|endoftext|>


<commit_before>// 在 Cloud code 里初始化 Express 框架
var express = require('express')
var app = express()
var http = require('http')

// App 全局配置
app.set('views', 'cloud/views') // 设置模板目录
app.set('view engine', 'ejs') // 设置 template 引擎
app.use(express.bodyParser())

app.get('/hello', function(req, res) {
	res.render('hello', {
		message: '004'
	})
})

app.get('/chatroom', function(req, res) {
	res.render('chatroom', {
		title: 'Chat Room'
	})
})

var server = http.createServer(app)
var io = require('socket.io')(server)

io.on('connection', function(socket) {
	socket.on('chat msg', function(msg) {
		io.emit('chat msg', msg)
	})
})

server.listen(8000)

<commit_msg>server<commit_after>// 在 Cloud code 里初始化 Express 框架
var express = require('express')
var app = express()
var http = require('http')

// App 全局配置
app.set('views', 'cloud/views') // 设置模板目录
app.set('view engine', 'ejs') // 设置 template 引擎
app.use(express.bodyParser())

app.get('/hello', function(req, res) {
	res.render('hello', {
		message: '004'
	})
})

app.get('/chatroom', function(req, res) {
	res.render('chatroom', {
		title: 'Chat Room'
	})
})

var server = http.Server(app)
var io = require('socket.io')(server)

io.on('connection', function(socket) {
	socket.on('chat msg', function(msg) {
		io.emit('chat msg', msg)
	})
})

server.listen(8000)

<|endoftext|>


<commit_before>/* Poidbsubmission.js

  KC3Kai poi-statistics data submission library

 */
(function(){
    "use strict";

    window.PoiDBSubmission = {
	    // `state` should take one of the following value:
	    // * `null` if the module awaits nothing
	    // * `create_ship`: having "createship" consumed
	    //   waiting for "kdock" message
	    // * `drop_ship_1`: having "start" or "next" consumed
	    //   waiting for formation info ("battle")
	    // * `drop_ship_2`: waiting for the final piece of data (rank etc, shipId if any)
	    state: null,
	    createShipData: null,
	    dropShipData: null,

	    // api handler
	    handlers: {},
	    // <map id> => <event rank>
	    mapInfo: {},

	    // *INTERNAL USE ONLY*
	    // because when building this dictionary
	    // fields are not yet available
	    // so we initialize handler table
	    // after object's creation
	    _initialize: function () {
	        this.handlers = {
		        'api_get_member/mapinfo': this.processMapInfo,
		        'api_req_kousyou/createship': this.processCreateShip,
		        'api_get_member/kdock': this.processKDock,
		        'api_req_kousyou/createitem': this.processCreateItem,
                'api_req_map/select_eventmap_rank': this.processSelectEventMapRank,
		        // start or next
		        'api_req_map/start': this.processStartNext,
		        'api_req_map/next': this.processStartNext,
		        // detect formation
		        'api_req_sortie/battle': this.processBattle,
		        'api_req_sortie/airbattle': this.processBattle,
		        'api_req_sortie/night_to_day': this.processBattle,
		        "api_req_sortie/ld_airbattle": this.processBattle,
		        // 'api_req_battle_midnight/battle': this.processBattle,
		        'api_req_battle_midnight/sp_midnight': this.processBattle,
		        'api_req_combined_battle/airbattle': this.processBattle,
		        'api_req_combined_battle/battle': this.processBattle,
		        'api_req_combined_battle/sp_midnight': this.processBattle,
		        'api_req_combined_battle/battle_water': this.processBattle,
		        "api_req_combined_battle/ld_airbattle": this.processBattle,
		        // detect ship id
		        'api_req_sortie/battleresult': this.processBattleResult,
		        'api_req_combined_battle/battleresult': this.processBattleResult,
	        };

	    },
        processSelectEventMapRank: function (requestObj) {
            var params = requestObj.params;
            // I know they are strings, just being 100% sure.
            var mapId = String(params.api_maparea_id) + String(params.api_map_no);
            var rank = parseInt(params.api_rank, 10);
            this.mapInfo[mapId] = rank;
            var selectRankData = {
                teitokuLv: PlayerManager.hq.level,
                teitokuId: PlayerManager.hq.nameId,
                mapareaId: mapId,
                rank: rank
            };
            this.sendData("select_rank", selectRankData);
        },
	    processCreateShip: function ( requestObj ) {
	        this.cleanup();
	        var params = requestObj.params;
	        var createShipData = {
		        items: [
		            parseInt(params.api_item1),
		            parseInt(params.api_item2),
		            parseInt(params.api_item3),
		            parseInt(params.api_item4),
		            parseInt(params.api_item5)],
		        kdockId: parseInt(params.api_kdock_id) - 1,
		        secretary: PlayerManager.fleets[0].ship(0).masterId,
		        teitokuLv: PlayerManager.hq.level,
		        largeFlag: (parseInt(params.api_large_flag) === 0) ? false : true,
		        highspeed: parseInt(params.api_highspeed)
	        };
	        this.createShipData = createShipData;
	        this.state = "create_ship";
	    },
	    processKDock: function( requestObj ) {
	        if (this.state !== 'create_ship') {
		        this.cleanup();
		        return;
	        }
	        var createShipData = this.createShipData;
	        createShipData.shipId =
		        requestObj.response.api_data[createShipData.kdockId].api_created_ship_id;

	        // console.log( "[createship] prepared: " + JSON.stringify( createShipData ) );
	        this.sendData("create_ship", createShipData);
	        this.state = null;
	    },
	    processCreateItem: function( requestObj ) {
	        this.cleanup();
	        var params = requestObj.params;
	        var response = requestObj.response.api_data;
	        var createItemData = {
		        items: [
		            parseInt(params.api_item1),
		            parseInt(params.api_item2),
		            parseInt(params.api_item3),
		            parseInt(params.api_item4)],
		        secretary: PlayerManager.fleets[0].ship(0).masterId,
		        teitokuLv: PlayerManager.hq.level
	        };

	        createItemData.successful = (response.api_create_flag === 1);
	        if (createItemData.successful) {
		        createItemData.itemId = response.api_slot_item.api_slotitem_id;
	        } else {
		        createItemData.itemId = parseInt( response.api_fdata.split(',')[1] );
	        }
	        // console.log( "[createitem] prepared: " + JSON.stringify( createItemData ));
	        this.sendData("create_item", createItemData);
	    },
	    processStartNext: function( requestObj ) {
	        this.cleanup();
	        var response = requestObj.response.api_data;

	        var dropShipData = {
		        mapId: response.api_maparea_id*10 + response.api_mapinfo_no,
		        cellId: response.api_no,
		        isBoss: ( response.api_event_id === 5)
	        };

	        this.dropShipData = dropShipData;
	        this.state = 'drop_ship_1';
	    },
	    processBattle: function( requestObj ) {
	        if (this.state !== 'drop_ship_1' &&
		        // could have night battles, in which case "processBattle"
		        // is entered more than once
		        this.state !== 'drop_ship_2') {
		        this.cleanup();
		        return;
	        }
	        var response = requestObj.response.api_data;
	        var dropShipData = this.dropShipData;

	        try {
		        dropShipData.enemyFormation = response.api_formation[1];
	        } catch (err) {
		        console.warn("error while extracting enemy formation");
		        console.warn(err);
		        // when there's something wrong extracting enemy formation
		        // 0 is returned respecting poi's behavior
		        // see: https://github.com/poooi/poi/blob/53e5ac3a992f72b3d2f4a7db9feb094879a12851/views/battle-env.coffee#L24
		        dropShipData.enemyFormation = 0;
	        }

	        this.state = 'drop_ship_2';
	    },
	    processMapInfo: function( requestObj ) {
            var self = this;
	        $.each( requestObj.response.api_data, function(i, entry) {
		        if (entry.api_eventmap) {
		            self.mapInfo[entry.api_id] = entry.api_eventmap.api_selected_rank;
		        }
	        });
	    },
	    processBattleResult: function( requestObj ) {
	        if (this.state !== 'drop_ship_2') {
		        this.cleanup();
		        return;
	        }

	        var response = requestObj.response.api_data;
	        var dropShipData = this.dropShipData;

	        dropShipData.shipId = response.api_get_ship ? response.api_get_ship.api_ship_id : -1;
	        dropShipData.quest = response.api_quest_name;
	        dropShipData.enemy = response.api_enemy_info.api_deck_name;
	        dropShipData.mapLv = this.mapInfo[dropShipData.mapId] || 0;
            // TODO: verify
            console.log( "PoiDBSubmission: mapInfo debug:", dropShipData.mapLv );
	        dropShipData.rank = response.api_win_rank;
	        dropShipData.teitokuLv = PlayerManager.hq.level;
	        dropShipData.enemyShips = response.api_ship_id.slice(1);
	        console.log( "[dropship] prepared: " + JSON.stringify( dropShipData ) );
	        this.sendData( "drop_ship", dropShipData );
            this.state = null;

            if (response.api_get_eventitem) {
                // TODO: verify
                console.log( response.api_get_eventitem );
                // having this field means the player has completed this event map.
                var passEventData = {
                    teitokuId: PlayerManager.hq.nameId,
                    teitokuLv: PlayerManager.hq.level,
                    teitoku: PlayerManager.hq.name,
                    mapId: dropShipData.mapId,
                    mapLv: dropShipData.mapLv
                };
                // TOOD: verify
                console.log( "Passing an event map!" );
                console.log( JSON.stringify( passEventData ) );
                this.sendData("pass_event", passEventData);
            }
        },
	    getApiName: function(url) {
	        var KcsApiIndex = url.indexOf("/kcsapi/");
	        return url.substring( KcsApiIndex+8 );
	    },
	    // get data handler based on URL given
	    // `null` is returned if no handler is found
	    processData: function( requestObj ) {
	        var apiName = this.getApiName( requestObj.url );
	        var handler = this.handlers[apiName];
	        if ( handler ) {
		        // bind module to "this"
		        handler.call(this, requestObj);
		        return true;
	        }
	        return false;
	    },
	    cleanup: function() {
	        if (this.state !== null) {
		        console.log( "aborting previous data report" );
		        console.log( "interal state was: " + this.state );
	        }

	        this.state = null;
	        this.createShipData = null;
	        this.dropShipData = null;
	    },
	    sendData: function(target, payload) {
	        var server = "http://poi.0u0.moe" ;
	        var url = server + "/api/report/v2/" + target;
	        var myVersion = chrome.runtime.getManifest().version;
	        var client = "KC3Kai " + myVersion;
	        payload.origin = client;
	        // console.log( JSON.stringify( payload ) );
	        $.ajax({
		        url: url,
		        method: "POST",
		        data: {
		            'data': JSON.stringify( payload )
		        },
	        }).done( function() {
		        console.log( "Poi DB Submission done." );
	        }).fail( function(jqXHR,textStatus,errorThrown) {
		        console.warn( "Poi DB Submission failed." );
		        console.warn( textStatus );
		        console.warn( errorThrown );
		        console.warn( jqXHR.status );
	        });
	        return;
	    }
    };
    window.PoiDBSubmission._initialize();
})();
<commit_msg>all verified, cleaning up<commit_after>/* Poidbsubmission.js

  KC3Kai poi-statistics data submission library

 */
(function(){
    "use strict";

    window.PoiDBSubmission = {
	    // `state` should take one of the following value:
	    // * `null` if the module awaits nothing
	    // * `create_ship`: having "createship" consumed
	    //   waiting for "kdock" message
	    // * `drop_ship_1`: having "start" or "next" consumed
	    //   waiting for formation info ("battle")
	    // * `drop_ship_2`: waiting for the final piece of data (rank etc, shipId if any)
	    state: null,
	    createShipData: null,
	    dropShipData: null,

	    // api handler
	    handlers: {},
	    // <map id> => <event rank>
	    mapInfo: {},

	    // *INTERNAL USE ONLY*
	    // because when building this dictionary
	    // fields are not yet available
	    // so we initialize handler table
	    // after object's creation
	    _initialize: function () {
	        this.handlers = {
		        'api_get_member/mapinfo': this.processMapInfo,
		        'api_req_kousyou/createship': this.processCreateShip,
		        'api_get_member/kdock': this.processKDock,
		        'api_req_kousyou/createitem': this.processCreateItem,
                'api_req_map/select_eventmap_rank': this.processSelectEventMapRank,
		        // start or next
		        'api_req_map/start': this.processStartNext,
		        'api_req_map/next': this.processStartNext,
		        // detect formation
		        'api_req_sortie/battle': this.processBattle,
		        'api_req_sortie/airbattle': this.processBattle,
		        'api_req_sortie/night_to_day': this.processBattle,
		        "api_req_sortie/ld_airbattle": this.processBattle,
		        // 'api_req_battle_midnight/battle': this.processBattle,
		        'api_req_battle_midnight/sp_midnight': this.processBattle,
		        'api_req_combined_battle/airbattle': this.processBattle,
		        'api_req_combined_battle/battle': this.processBattle,
		        'api_req_combined_battle/sp_midnight': this.processBattle,
		        'api_req_combined_battle/battle_water': this.processBattle,
		        "api_req_combined_battle/ld_airbattle": this.processBattle,
		        // detect ship id
		        'api_req_sortie/battleresult': this.processBattleResult,
		        'api_req_combined_battle/battleresult': this.processBattleResult,
	        };

	    },
        processSelectEventMapRank: function (requestObj) {
            var params = requestObj.params;
            // I know they are strings, just being 100% sure.
            var mapId = String(params.api_maparea_id) + String(params.api_map_no);
            var rank = parseInt(params.api_rank, 10);
            this.mapInfo[mapId] = rank;
            var selectRankData = {
                teitokuLv: PlayerManager.hq.level,
                teitokuId: PlayerManager.hq.nameId,
                mapareaId: mapId,
                rank: rank
            };
            this.sendData("select_rank", selectRankData);
        },
	    processCreateShip: function ( requestObj ) {
	        this.cleanup();
	        var params = requestObj.params;
	        var createShipData = {
		        items: [
		            parseInt(params.api_item1),
		            parseInt(params.api_item2),
		            parseInt(params.api_item3),
		            parseInt(params.api_item4),
		            parseInt(params.api_item5)],
		        kdockId: parseInt(params.api_kdock_id) - 1,
		        secretary: PlayerManager.fleets[0].ship(0).masterId,
		        teitokuLv: PlayerManager.hq.level,
		        largeFlag: (parseInt(params.api_large_flag) === 0) ? false : true,
		        highspeed: parseInt(params.api_highspeed)
	        };
	        this.createShipData = createShipData;
	        this.state = "create_ship";
	    },
	    processKDock: function( requestObj ) {
	        if (this.state !== 'create_ship') {
		        this.cleanup();
		        return;
	        }
	        var createShipData = this.createShipData;
	        createShipData.shipId =
		        requestObj.response.api_data[createShipData.kdockId].api_created_ship_id;

	        // console.log( "[createship] prepared: " + JSON.stringify( createShipData ) );
	        this.sendData("create_ship", createShipData);
	        this.state = null;
	    },
	    processCreateItem: function( requestObj ) {
	        this.cleanup();
	        var params = requestObj.params;
	        var response = requestObj.response.api_data;
	        var createItemData = {
		        items: [
		            parseInt(params.api_item1),
		            parseInt(params.api_item2),
		            parseInt(params.api_item3),
		            parseInt(params.api_item4)],
		        secretary: PlayerManager.fleets[0].ship(0).masterId,
		        teitokuLv: PlayerManager.hq.level
	        };

	        createItemData.successful = (response.api_create_flag === 1);
	        if (createItemData.successful) {
		        createItemData.itemId = response.api_slot_item.api_slotitem_id;
	        } else {
		        createItemData.itemId = parseInt( response.api_fdata.split(',')[1] );
	        }
	        // console.log( "[createitem] prepared: " + JSON.stringify( createItemData ));
	        this.sendData("create_item", createItemData);
	    },
	    processStartNext: function( requestObj ) {
	        this.cleanup();
	        var response = requestObj.response.api_data;

	        var dropShipData = {
		        mapId: response.api_maparea_id*10 + response.api_mapinfo_no,
		        cellId: response.api_no,
		        isBoss: ( response.api_event_id === 5)
	        };

	        this.dropShipData = dropShipData;
	        this.state = 'drop_ship_1';
	    },
	    processBattle: function( requestObj ) {
	        if (this.state !== 'drop_ship_1' &&
		        // could have night battles, in which case "processBattle"
		        // is entered more than once
		        this.state !== 'drop_ship_2') {
		        this.cleanup();
		        return;
	        }
	        var response = requestObj.response.api_data;
	        var dropShipData = this.dropShipData;

	        try {
		        dropShipData.enemyFormation = response.api_formation[1];
	        } catch (err) {
		        console.warn("error while extracting enemy formation");
		        console.warn(err);
		        // when there's something wrong extracting enemy formation
		        // 0 is returned respecting poi's behavior
		        // see: https://github.com/poooi/poi/blob/53e5ac3a992f72b3d2f4a7db9feb094879a12851/views/battle-env.coffee#L24
		        dropShipData.enemyFormation = 0;
	        }

	        this.state = 'drop_ship_2';
	    },
	    processMapInfo: function( requestObj ) {
            var self = this;
	        $.each( requestObj.response.api_data, function(i, entry) {
		        if (entry.api_eventmap) {
		            self.mapInfo[entry.api_id] = entry.api_eventmap.api_selected_rank;
		        }
	        });
	    },
	    processBattleResult: function( requestObj ) {
	        if (this.state !== 'drop_ship_2') {
		        this.cleanup();
		        return;
	        }

	        var response = requestObj.response.api_data;
	        var dropShipData = this.dropShipData;

	        dropShipData.shipId = response.api_get_ship ? response.api_get_ship.api_ship_id : -1;
	        dropShipData.quest = response.api_quest_name;
	        dropShipData.enemy = response.api_enemy_info.api_deck_name;
	        dropShipData.mapLv = this.mapInfo[dropShipData.mapId] || 0;
	        dropShipData.rank = response.api_win_rank;
	        dropShipData.teitokuLv = PlayerManager.hq.level;
	        dropShipData.enemyShips = response.api_ship_id.slice(1);
	        console.log( "[dropship] prepared: " + JSON.stringify( dropShipData ) );
	        this.sendData( "drop_ship", dropShipData );
            this.state = null;

            if (response.api_get_eventitem) {
                // having this field means the player has completed this event map.
                var passEventData = {
                    teitokuId: PlayerManager.hq.nameId,
                    teitokuLv: PlayerManager.hq.level,
                    teitoku: PlayerManager.hq.name,
                    mapId: dropShipData.mapId,
                    mapLv: dropShipData.mapLv
                };
                console.log( "Passing an event map!" );
                console.log( JSON.stringify( passEventData ) );
                this.sendData("pass_event", passEventData);
            }
        },
	    getApiName: function(url) {
	        var KcsApiIndex = url.indexOf("/kcsapi/");
	        return url.substring( KcsApiIndex+8 );
	    },
	    // get data handler based on URL given
	    // `null` is returned if no handler is found
	    processData: function( requestObj ) {
	        var apiName = this.getApiName( requestObj.url );
	        var handler = this.handlers[apiName];
	        if ( handler ) {
		        // bind module to "this"
		        handler.call(this, requestObj);
		        return true;
	        }
	        return false;
	    },
	    cleanup: function() {
	        if (this.state !== null) {
		        console.log( "aborting previous data report" );
		        console.log( "interal state was: " + this.state );
	        }

	        this.state = null;
	        this.createShipData = null;
	        this.dropShipData = null;
	    },
	    sendData: function(target, payload) {
	        var server = "http://poi.0u0.moe" ;
	        var url = server + "/api/report/v2/" + target;
	        var myVersion = chrome.runtime.getManifest().version;
	        var client = "KC3Kai " + myVersion;
	        payload.origin = client;
	        // console.log( JSON.stringify( payload ) );
	        $.ajax({
		        url: url,
		        method: "POST",
		        data: {
		            'data': JSON.stringify( payload )
		        },
	        }).done( function() {
		        console.log( "Poi DB Submission done." );
	        }).fail( function(jqXHR,textStatus,errorThrown) {
		        console.warn( "Poi DB Submission failed." );
		        console.warn( textStatus );
		        console.warn( errorThrown );
		        console.warn( jqXHR.status );
	        });
	        return;
	    }
    };
    window.PoiDBSubmission._initialize();
})();
<|endoftext|>


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

var util = require('util');
var async = require('async');
var __ = require('underscore');

var constants = require('../constants.js');
var Subscription = require('./subscription.js');
var utils = require('../utils');

function Account(env, adalAuth, resourceClient, log) {
  this._env = env;
  this._adalAuth = adalAuth;
  this._resourceClient = resourceClient;
  this._log = log;
  this.WarnToUserInteractiveFieldName = 'warnToUserInteractive';
}

Account.prototype.load = function (username, password, tenant, loginType, callback) {
  var self = this;
  if (!tenant) {
    tenant = constants.AAD_COMMON_TENANT;
  }
  if (username) {
    username = self._adalAuth.normalizeUserName(username);
  }
  self._username = username;
  var userType = 'user';

  if (loginType.servicePrincipal) {
    userType = 'servicePrincipal';
  }

  function processAndReturnSubscriptions(err, result) {
    if (err) return callback(err);
    var subscriptions = result.subscriptions;
    var subs = __.map(subscriptions, function (s) {
      return new Subscription({
        id: s.subscriptionId,
        name: s.displayName || s.subscriptionName,
        user: {
          name: self._username,
          type: userType
        },
        tenantId: s.tenantId ? s.tenantId : tenant
      }, self._env);
    });

    callback(null, { subscriptions: subs, tenantIds: result.tenantIds });
  }

  if (loginType.servicePrincipal) {
    this._adalAuth.createServicePrincipalTokenCredentials(self._env.getAuthConfig(tenant), username, password, function (err, credential) {
      if (err) { return callback(err); }
      var tenantInfo = {
        tenantId: tenant,
        credential: credential
      };
      self._getSubscriptionsFromTenants(username, [tenantInfo], function (err, subscriptions) {
        if (err) return callback(err);
        return processAndReturnSubscriptions(null, { subscriptions: subscriptions });
      });
    });
  } else if (loginType.interactive) {
    self._getMFASubscriptions(tenant, processAndReturnSubscriptions);
  } else {
    self._getNonMFASubscriptions(username, password, tenant, processAndReturnSubscriptions);
  }
};

Account.prototype._getMFASubscriptions = function (tenant, callback) {
  var self = this;
  var authConfig = self._env.getAuthConfig(tenant);
  async.waterfall([
    function (callback) {
      self._adalAuth.acquireUserCode(authConfig, function (err, userCodeResponse) {
        if (err) return callback(err);
        return callback(null, userCodeResponse);
      });
    },
    function (userCodeResponse, callback) {
      self._log.info(userCodeResponse.message);
      self._log.verbose('code response from AAD is :' + JSON.stringify(userCodeResponse));
      self._adalAuth.authenticateWithDeviceCode(authConfig, userCodeResponse, function (err, credential) {
        if (err) return callback(err);
        self._username = credential.userId;

        async.waterfall([
          function (callback) {
            self._buildTenantList(self._username, null, tenant, credential, callback);
          },
          function (tenantList, callback) {
            self._getSubscriptionsFromTenants(self._username, tenantList, callback);
          },
        ], function (err, subscriptions) {
          callback(err, { subscriptions : subscriptions });
        });
      });
    }
  ], function (err, subscriptions) {
    callback(err, subscriptions);
  });
};

Account.prototype._getNonMFASubscriptions = function (username, password, tenant, callback) {
  var self = this;
  self._adalAuth.authenticateWithUsernamePassword(self._env.getAuthConfig(tenant), username, password, function (err, credential) {
    if (err && err.message && self._errorSuggestsInteractiveFlow(err.message)) {
      err[self.WarnToUserInteractiveFieldName] = true;
    }
    if (err) return callback(err);
    username = _crossCheckUserNameWithToken(username, credential.userId);

    async.waterfall([
      function (callback) {
        self._buildTenantList(username, password, tenant, credential, callback);
      },
      function (tenantList, callback) {
        self._getSubscriptionsFromTenants(username, tenantList, callback);
      },
    ], function (err, subscriptions) {
      callback(err, { subscriptions : subscriptions });
    });
  });
};

Account.prototype._errorSuggestsInteractiveFlow = function (errMessage) {
  var codes = ['50072', '50074', '50076', '50077', '50078', '50079'];
  var contains2FAErrCode =  __.any(codes, function (code) {
    return (errMessage.indexOf('AADSTS' + code) !== -1);
  });
  var containsErrCausedByUsingMsa = utils.ignoreCaseEquals(errMessage, 'Server returned an unknown AccountType: undefined') ||
      utils.ignoreCaseEquals(errMessage, 'Server returned error in RSTR - ErrorCode: NONE : FaultMessage: NONE');
  return contains2FAErrCode || containsErrCausedByUsingMsa;
};

Account.prototype._polishError = function (errMessage) {
  if (utils.ignoreCaseEquals(errMessage, 'Server returned an unknown AccountType: undefined') ||
      utils.ignoreCaseEquals(errMessage, 'Server returned error in RSTR - ErrorCode: NONE : FaultMessage: NONE')) {
    errMessage = 'Your account type requires to login using interactive flow. Please rerun "login" command without -u/--username parameter';
  }
  return errMessage;
};

Account.prototype._getSubscriptionsFromTenants = function (username, tenantList, callback) {
  var self = this;
  var subscriptions = [];
  async.eachSeries(tenantList, function (tenant, cb) {
    var tenantId = tenant.tenantId;
    var credential = new self._adalAuth.UserTokenCredentials(self._env.getAuthConfig(tenantId), username);
    var armClient = self._getArmClient(credential);
    armClient.subscriptions.list(function (err, result) {
      if (!err) {
        subscriptions = subscriptions.concat(result.subscriptions.map(function (s) {
          s.tenantId = tenantId;
          s.username = username;
          return s;
        }));
      }
      cb(err);
    });
  }, function (err) {
    callback(err, subscriptions);
  });
};

Account.prototype._buildTenantList = function (username, password, tenant, credential, callback) {
  var self = this;
  var tenants = [];
  if (tenant && tenant !== constants.AAD_COMMON_TENANT) {
    tenants = [{
        tenantId: tenant,//'tenant' could be a logical name, interchangable with a tenant guid though
        credential: credential
      }];
    return callback(null, tenants);
  }
  self._getAllTenants(credential, function (err, result) {
    if (err) return callback(err);
    async.eachSeries(result.tenantIds/*'tenantInfos' could be a better name*/, function (tenantInfo, cb) {
      tenants.push({
        tenantId: tenantInfo.tenantId,
        credential: credential
      });
      cb(err);
    }, function (err) {
      callback(err, tenants);
    });
  });
};

Account.prototype._getAllTenants = function (credential, callback) {
  var armClient = this._getArmClient(credential);
  armClient.tenants.list(callback);
};

Account.prototype._getArmClient = function (credentials) {
  return this._resourceClient.createResourceSubscriptionClient(credentials, this._env.resourceManagerEndpointUrl);
};

module.exports = Account;

function _crossCheckUserNameWithToken(usernameFromCommandline, userIdFromToken) {
  //to maintain the casing consistency between 'azureprofile.json' and token cache. (RD 1996587)
  //use the 'userId' here, which should be the same with "username" except the casing.
  if (utils.ignoreCaseEquals(usernameFromCommandline, userIdFromToken)) {
    return userIdFromToken;
  } else {
    throw new Error(util.format('The userId of \'%s\' in access token doesn\'t match the  command line username of \'%s\'',
      userIdFromToken, usernameFromCommandline));
  }
}

<commit_msg>fix bug soe service principal can login<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var util = require('util');
var async = require('async');
var __ = require('underscore');

var constants = require('../constants.js');
var Subscription = require('./subscription.js');
var utils = require('../utils');

function Account(env, adalAuth, resourceClient, log) {
  this._env = env;
  this._adalAuth = adalAuth;
  this._resourceClient = resourceClient;
  this._log = log;
  this.WarnToUserInteractiveFieldName = 'warnToUserInteractive';
}

Account.prototype.load = function (username, password, tenant, loginType, callback) {
  var self = this;
  if (!tenant) {
    tenant = constants.AAD_COMMON_TENANT;
  }
  if (username) {
    username = self._adalAuth.normalizeUserName(username);
  }
  self._username = username;
  var userType = 'user';

  if (loginType.servicePrincipal) {
    userType = 'servicePrincipal';
  }

  function processAndReturnSubscriptions(err, result) {
    if (err) return callback(err);
    var subscriptions = result.subscriptions;
    var subs = __.map(subscriptions, function (s) {
      return new Subscription({
        id: s.subscriptionId,
        name: s.displayName || s.subscriptionName,
        user: {
          name: self._username,
          type: userType
        },
        tenantId: s.tenantId ? s.tenantId : tenant
      }, self._env);
    });

    callback(null, { subscriptions: subs, tenantIds: result.tenantIds });
  }

  if (loginType.servicePrincipal) {
    this._adalAuth.createServicePrincipalTokenCredentials(self._env.getAuthConfig(tenant), username, password, function (err, credential) {
      if (err) { return callback(err); }
      self._getSubscriptionsFromTenants(username, [tenant], credential, function (err, subscriptions) {
        if (err) return callback(err);
        return processAndReturnSubscriptions(null, { subscriptions: subscriptions });
      });
    });
  } else if (loginType.interactive) {
    self._getMFASubscriptions(tenant, processAndReturnSubscriptions);
  } else {
    self._getNonMFASubscriptions(username, password, tenant, processAndReturnSubscriptions);
  }
};

Account.prototype._getMFASubscriptions = function (tenant, callback) {
  var self = this;
  var authConfig = self._env.getAuthConfig(tenant);
  async.waterfall([
    function (callback) {
      self._adalAuth.acquireUserCode(authConfig, function (err, userCodeResponse) {
        if (err) return callback(err);
        return callback(null, userCodeResponse);
      });
    },
    function (userCodeResponse, callback) {
      self._log.info(userCodeResponse.message);
      self._log.verbose('code response from AAD is :' + JSON.stringify(userCodeResponse));
      self._adalAuth.authenticateWithDeviceCode(authConfig, userCodeResponse, function (err, credential) {
        if (err) return callback(err);
        self._username = credential.userId;

        async.waterfall([
          function (callback) {
            self._buildTenantList(self._username, tenant, credential, callback);
          },
          function (tenantList, callback) {
            self._getSubscriptionsFromTenants(self._username, tenantList, null, callback);
          },
        ], function (err, subscriptions) {
          callback(err, { subscriptions : subscriptions });
        });
      });
    }
  ], function (err, subscriptions) {
    callback(err, subscriptions);
  });
};

Account.prototype._getNonMFASubscriptions = function (username, password, tenant, callback) {
  var self = this;
  self._adalAuth.authenticateWithUsernamePassword(self._env.getAuthConfig(tenant), username, password, function (err, credential) {
    if (err && err.message && self._errorSuggestsInteractiveFlow(err.message)) {
      err[self.WarnToUserInteractiveFieldName] = true;
    }
    if (err) return callback(err);
    username = _crossCheckUserNameWithToken(username, credential.userId);

    async.waterfall([
      function (callback) {
        self._buildTenantList(username, tenant, credential, callback);
      },
      function (tenantList, callback) {
        self._getSubscriptionsFromTenants(username, tenantList, null, callback);
      },
    ], function (err, subscriptions) {
      callback(err, { subscriptions : subscriptions });
    });
  });
};

Account.prototype._errorSuggestsInteractiveFlow = function (errMessage) {
  var codes = ['50072', '50074', '50076', '50077', '50078', '50079'];
  var contains2FAErrCode =  __.any(codes, function (code) {
    return (errMessage.indexOf('AADSTS' + code) !== -1);
  });
  var containsErrCausedByUsingMsa = utils.ignoreCaseEquals(errMessage, 'Server returned an unknown AccountType: undefined') ||
      utils.ignoreCaseEquals(errMessage, 'Server returned error in RSTR - ErrorCode: NONE : FaultMessage: NONE');
  return contains2FAErrCode || containsErrCausedByUsingMsa;
};

Account.prototype._polishError = function (errMessage) {
  if (utils.ignoreCaseEquals(errMessage, 'Server returned an unknown AccountType: undefined') ||
      utils.ignoreCaseEquals(errMessage, 'Server returned error in RSTR - ErrorCode: NONE : FaultMessage: NONE')) {
    errMessage = 'Your account type requires to login using interactive flow. Please rerun "login" command without -u/--username parameter';
  }
  return errMessage;
};

Account.prototype._getSubscriptionsFromTenants = function (username, tenantList, preBuiltCredential, callback) {
  var self = this;
  var subscriptions = [];
  async.eachSeries(tenantList, function (tenant, cb) {
    var credential = preBuiltCredential;
    if (!credential) {
      credential = new self._adalAuth.UserTokenCredentials(self._env.getAuthConfig(tenant), username);
    }
    var armClient = self._getArmClient(credential);
    armClient.subscriptions.list(function (err, result) {
      if (!err) {
        subscriptions = subscriptions.concat(result.subscriptions.map(function (s) {
          s.tenantId = tenant;
          s.username = username;
          return s;
        }));
      }
      cb(err);
    });
  }, function (err) {
    callback(err, subscriptions);
  });
};

Account.prototype._buildTenantList = function (username, tenant, credential, callback) {
  var self = this;
  var tenants = [];
  if (tenant && tenant !== constants.AAD_COMMON_TENANT) {
    return callback(null, [tenant]);
  }
  self._getAllTenants(credential, function (err, result) {
    if (err) return callback(err);
    async.eachSeries(result.tenantIds/*'tenantInfos' could be a better name*/, function (tenantInfo, cb) {
      tenants.push(tenantInfo.tenantId);
      cb(err);
    }, function (err) {
      callback(err, tenants);
    });
  });
};

Account.prototype._getAllTenants = function (credential, callback) {
  var armClient = this._getArmClient(credential);
  armClient.tenants.list(callback);
};

Account.prototype._getArmClient = function (credentials) {
  return this._resourceClient.createResourceSubscriptionClient(credentials, this._env.resourceManagerEndpointUrl);
};

module.exports = Account;

function _crossCheckUserNameWithToken(usernameFromCommandline, userIdFromToken) {
  //to maintain the casing consistency between 'azureprofile.json' and token cache. (RD 1996587)
  //use the 'userId' here, which should be the same with "username" except the casing.
  if (utils.ignoreCaseEquals(usernameFromCommandline, userIdFromToken)) {
    return userIdFromToken;
  } else {
    throw new Error(util.format('The userId of \'%s\' in access token doesn\'t match the  command line username of \'%s\'',
      userIdFromToken, usernameFromCommandline));
  }
}

<|endoftext|>


<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Behavior for "fixed" headers and footers
//>>label: Fixedtoolbar

define( [ "jquery", "./jquery.mobile.widget", "./jquery.mobile.core", "./jquery.mobile.navigation", "./jquery.mobile.page", "./jquery.mobile.page.sections", "./jquery.mobile.zoom" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

	$.widget( "mobile.fixedtoolbar", $.mobile.widget, {
		options: {
			visibleOnPageShow: true,
			togglePageZoom: true,
			transition: "fade", //can be none, fade, slide (slide maps to slideup or slidedown)
			fullscreen: false,
			tapToggle: true,
			updatePagePadding: true,

			// Browser detection! Weeee, here we go...
			// Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
			// Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
			// Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
			// The following function serves to rule out some popular browsers with known fixed-positioning issues
			// This is a plugin option like any other, so feel free to improve or overwrite it
			supportBlacklist: function(){
				var ua = navigator.userAgent,
					platform = navigator.platform,
					// Rendering engine is Webkit, and capture major version
					wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
					wkversion = !!wkmatch && wkmatch[ 1 ],
					ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
					ffversion = !!ffmatch && ffmatch[ 1 ],
					operammobilematch = ua.match( /Opera Mobile\/([0-9]+)/ ),
					omversion = !!operammobilematch && operammobilematch[ 1 ],

					w = window;

				if(
					// iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
					( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1  || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 )
					||
					// Opera Mini
					( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" )
					||
					( operammobilematch && omverson < 7458 )
					||
					//Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
					( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 )
					||
					// Firefox Mobile before 6.0 -
					( ffversion && ffversion < 6 )
					||
					// WebOS less than 3
					( "palmGetResource" in window && wkversion && wkversion < 534 )
				){
					return true;
				}

				return false;
			},
			initSelector: ":jqmData(position='fixed')"
		},

		_create: function() {

			var self = this,
				o = self.options,
				$el = self.element,
				tbtype = $el.is( ".ui-header" ) ? "header" : "footer",
				$page = $el.closest(".ui-page");

			// Feature detecting support for
			if( o.supportBlacklist() ){
				self.destroy();
				return;
			}

			$el.addClass( "ui-"+ tbtype +"-fixed" );

			// "fullscreen" overlay positioning
			// NOTE - this used to be only "data-fullscreen" on page element. Support both or deprecate page?
			if( $el.jqmData( "fullscreen" ) || $page.jqmData( "fullscreen" ) ){
				$el.addClass( "ui-"+ tbtype +"-fullscreen" );
				$page.addClass( "ui-page-" + tbtype + "-fullscreen" );
			}
			// If not fullscreen, add class to page to set top or bottom padding
			else{
				$page.addClass( "ui-page-" + tbtype + "-fixed" );
			}

			self._addTransitionClass();
			self._bindPageEvents();
			self._bindToggleHandlers();
		},

		_addTransitionClass: function(){
			var tclass = this.options.transition;

			if( tclass && tclass !== "none" ){
				// use appropriate slide for header or footer
				if( tclass === "slide" ){
					tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
				}

				this.element.addClass( tclass );
			}
		},

		_bindPageEvents: function(){
			var self = this,
				o = self.options,
				$el = self.element;

			//page event bindings
			// Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
			// This method is meant to disable zoom while a fixed-positioned toolbar page is visible
			$el.closest( ".ui-page" )
				.bind( "pagebeforeshow", function(){
					if( o.togglePageZoom ){
						$.mobile.zoom.disable( true );
					}
					if( o.visibleOnPageShow ){
						self.show();
					}
				} )
				.bind( "webkitAnimationStart animationstart", function(){
					if( o.updatePagePadding ){
						self.updatePagePadding();
					}
				})
				.bind( "pageshow", function(){
					self.updatePagePadding();
					if( o.updatePagePadding ){
						$( window ).bind( "throttledresize." + self.widgetName, function(){
						 	self.updatePagePadding();
						});
					}
				})
				.bind( "pagebeforehide", function(){
					if( o.togglePageZoom ){
						$.mobile.zoom.enable( true );
					}
					if( o.updatePagePadding ){
						$( window ).unbind( "throttledresize." + self.widgetName );
					}
				});
		},

		_visible: false,

		// This will set the content element's top or bottom padding equal to the toolbar's height
		updatePagePadding: function() {
			var $el = this.element,
				header = $el.is( ".ui-header" );

			// This behavior only applies to "fixed", not "fullscreen"
			if( this.options.fullscreen ){ return; }

			$el.closest( ".ui-page" ).css( "padding-" + ( header ? "top" : "bottom" ), $el.height() );
		},

		show: function(){
			var hideClass = "ui-fixed-hidden",
				$el = this.element,
				$win = $( window ),
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest( ".ui-page" ).height(),
				viewportHeight = Math.min( screen.height, $win.height() ),
				tbtype = $el.is( ".ui-header" ) ? "header" : "footer";

				if( this.options.transition && this.options.transition !== "none" &&
					(
					( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
					( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
					) || this.options.fullscreen ){
				$el
					.removeClass( "out " + hideClass )
					.addClass( "in" );
			}
			else {
				$el.removeClass( hideClass );
			}
			this._visible = true;
		},

		hide: function(){
			var hideClass = "ui-fixed-hidden",
				$el = this.element,
				$win = $( window ),
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest( ".ui-page" ).height(),
				viewportHeight = Math.min( screen.height, $win.height() ),
				tbtype = $el.is( ".ui-header" ) ? "header" : "footer";

				if( this.options.transition && this.options.transition !== "none" &&
					(
					( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
					( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
					) || this.options.fullscreen ){
				$el
					.removeClass( "in" )
					.addClass( "out" )
					.animationComplete( function(){
						$el.addClass( hideClass );
					});
			}
			else {
				this.element.addClass( hideClass );
			}
			this._visible = false;
		},

		toggle: function(){
			this[ this._visible ? "hide" : "show" ]();
		},

		_bindToggleHandlers: function(){
			var self = this,
				o = self.options,
				$el = self.element;

			// tap toggle
			$el.closest( ".ui-page" )
				.bind( "vclick", function( e ){
					if( o.tapToggle && $el.find( e.target ).length === 0 ){
						self.toggle();
					}
				});
		},

		destroy: function(){
			this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" )
			this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
		}

	});

	//auto self-init widgets
	$( document ).bind( "pagecreate create", function( e ){
		$( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).fixedtoolbar();
	});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>removed ability to set "fullscreen" on the page itself. Instead, this should be set on the fixed toolbar elements.<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Behavior for "fixed" headers and footers
//>>label: Fixedtoolbar

define( [ "jquery", "./jquery.mobile.widget", "./jquery.mobile.core", "./jquery.mobile.navigation", "./jquery.mobile.page", "./jquery.mobile.page.sections", "./jquery.mobile.zoom" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

	$.widget( "mobile.fixedtoolbar", $.mobile.widget, {
		options: {
			visibleOnPageShow: true,
			togglePageZoom: true,
			transition: "fade", //can be none, fade, slide (slide maps to slideup or slidedown)
			fullscreen: false,
			tapToggle: true,
			updatePagePadding: true,

			// Browser detection! Weeee, here we go...
			// Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
			// Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
			// Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
			// The following function serves to rule out some popular browsers with known fixed-positioning issues
			// This is a plugin option like any other, so feel free to improve or overwrite it
			supportBlacklist: function(){
				var ua = navigator.userAgent,
					platform = navigator.platform,
					// Rendering engine is Webkit, and capture major version
					wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
					wkversion = !!wkmatch && wkmatch[ 1 ],
					ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
					ffversion = !!ffmatch && ffmatch[ 1 ],
					operammobilematch = ua.match( /Opera Mobile\/([0-9]+)/ ),
					omversion = !!operammobilematch && operammobilematch[ 1 ],

					w = window;

				if(
					// iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
					( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1  || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 )
					||
					// Opera Mini
					( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" )
					||
					( operammobilematch && omverson < 7458 )
					||
					//Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
					( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 )
					||
					// Firefox Mobile before 6.0 -
					( ffversion && ffversion < 6 )
					||
					// WebOS less than 3
					( "palmGetResource" in window && wkversion && wkversion < 534 )
				){
					return true;
				}

				return false;
			},
			initSelector: ":jqmData(position='fixed')"
		},

		_create: function() {

			var self = this,
				o = self.options,
				$el = self.element,
				tbtype = $el.is( ".ui-header" ) ? "header" : "footer",
				$page = $el.closest(".ui-page");

			// Feature detecting support for
			if( o.supportBlacklist() ){
				self.destroy();
				return;
			}

			$el.addClass( "ui-"+ tbtype +"-fixed" );

			// "fullscreen" overlay positioning
			if( $el.jqmData( "fullscreen" ) ){
				$el.addClass( "ui-"+ tbtype +"-fullscreen" );
				$page.addClass( "ui-page-" + tbtype + "-fullscreen" );
			}
			// If not fullscreen, add class to page to set top or bottom padding
			else{
				$page.addClass( "ui-page-" + tbtype + "-fixed" );
			}

			self._addTransitionClass();
			self._bindPageEvents();
			self._bindToggleHandlers();
		},

		_addTransitionClass: function(){
			var tclass = this.options.transition;

			if( tclass && tclass !== "none" ){
				// use appropriate slide for header or footer
				if( tclass === "slide" ){
					tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
				}

				this.element.addClass( tclass );
			}
		},

		_bindPageEvents: function(){
			var self = this,
				o = self.options,
				$el = self.element;

			//page event bindings
			// Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
			// This method is meant to disable zoom while a fixed-positioned toolbar page is visible
			$el.closest( ".ui-page" )
				.bind( "pagebeforeshow", function(){
					if( o.togglePageZoom ){
						$.mobile.zoom.disable( true );
					}
					if( o.visibleOnPageShow ){
						self.show();
					}
				} )
				.bind( "webkitAnimationStart animationstart", function(){
					if( o.updatePagePadding ){
						self.updatePagePadding();
					}
				})
				.bind( "pageshow", function(){
					self.updatePagePadding();
					if( o.updatePagePadding ){
						$( window ).bind( "throttledresize." + self.widgetName, function(){
						 	self.updatePagePadding();
						});
					}
				})
				.bind( "pagebeforehide", function(){
					if( o.togglePageZoom ){
						$.mobile.zoom.enable( true );
					}
					if( o.updatePagePadding ){
						$( window ).unbind( "throttledresize." + self.widgetName );
					}
				});
		},

		_visible: false,

		// This will set the content element's top or bottom padding equal to the toolbar's height
		updatePagePadding: function() {
			var $el = this.element,
				header = $el.is( ".ui-header" );

			// This behavior only applies to "fixed", not "fullscreen"
			if( this.options.fullscreen ){ return; }

			$el.closest( ".ui-page" ).css( "padding-" + ( header ? "top" : "bottom" ), $el.height() );
		},

		show: function(){
			var hideClass = "ui-fixed-hidden",
				$el = this.element,
				$win = $( window ),
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest( ".ui-page" ).height(),
				viewportHeight = Math.min( screen.height, $win.height() ),
				tbtype = $el.is( ".ui-header" ) ? "header" : "footer";

				if( this.options.transition && this.options.transition !== "none" &&
					(
					( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
					( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
					) || this.options.fullscreen ){
				$el
					.removeClass( "out " + hideClass )
					.addClass( "in" );
			}
			else {
				$el.removeClass( hideClass );
			}
			this._visible = true;
		},

		hide: function(){
			var hideClass = "ui-fixed-hidden",
				$el = this.element,
				$win = $( window ),
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest( ".ui-page" ).height(),
				viewportHeight = Math.min( screen.height, $win.height() ),
				tbtype = $el.is( ".ui-header" ) ? "header" : "footer";

				if( this.options.transition && this.options.transition !== "none" &&
					(
					( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
					( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
					) || this.options.fullscreen ){
				$el
					.removeClass( "in" )
					.addClass( "out" )
					.animationComplete( function(){
						$el.addClass( hideClass );
					});
			}
			else {
				this.element.addClass( hideClass );
			}
			this._visible = false;
		},

		toggle: function(){
			this[ this._visible ? "hide" : "show" ]();
		},

		_bindToggleHandlers: function(){
			var self = this,
				o = self.options,
				$el = self.element;

			// tap toggle
			$el.closest( ".ui-page" )
				.bind( "vclick", function( e ){
					if( o.tapToggle && $el.find( e.target ).length === 0 ){
						self.toggle();
					}
				});
		},

		destroy: function(){
			this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" )
			this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
		}

	});

	//auto self-init widgets
	$( document ).bind( "pagecreate create", function( e ){
		$( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).fixedtoolbar();
	});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>define(['reqanim', 'grid', 'zepto', 'entityFactory', 'moment', 'canvas', 'projectFactory'],
  function(AnimationFrame, Grid, $, EntityFactory, moment, Canvas, ProjectFactory) {

	/**
	 *
	 *
	 */
	var Game = function() {

    // self
    var self = this;

    // If game is running
    this.isRunning = false;

  	// Sounds
  	this.clicksound = new Audio('audio/click.ogg');
  	this.register = new Audio('audio/register.ogg');
  	this.notifysound = new Audio('audio/notify.ogg');
  	this.slapsound = new Audio('audio/Slap.ogg');
	this.keyboardsound = new Audio('audio/keyboard.ogg');
	this.playKeyboardsound = 0;

    // Projects..
    this.projectFactory = new ProjectFactory();

    // Gameplay related settings
    this.gameplay = {
      money: 30000,
	  losecondition: -50000,
      time: new Date(1970,01,01).getMilliseconds(),
      project: this.projectFactory.getProject(),
      costs: {
        coder: 5000,
        cleaner: 2500,
        manager: 25000,
        cook: 3500,
        buyFloor: 150000
      },
      // our entities holder per office floors
      floors: {
        1: {
          coder: {
          },
          cleaner: {
          },
          manager: {
          },
          cook: {
          }
        }
      },
      // floor player is viewing
      currentFloor: 1,
      selectedEntity: null
    };

    // Entity factory
    this.entityFactory = new EntityFactory(this.gameplay);

    // Canvas
    this.canvas = new Canvas(this.gameplay);

    // Ui elements
    this.ui = {
      // Overall
      money: $('#money'),
      time: $('#time p'),
      project: $('#project'),
      notification: $('#notification'),
      // Persons
      coder: $('#coder'),
      cleaner: $('#cleaner'),
      manager: $('#manager'),
      cook: $('#cook'),
      // Floor
      buyFloor: $('#buy-floor'),
      // Canvas
      canvas: $('#drawable-canvas'),
      // Prices
      coderPrice: $('#coder').find('.price'),
      cleanerPrice: $('#cleaner').find('.price'),
      managerPrice: $('#manager').find('.price'),
      cookPrice: $('#cook').find('.price'),
      buyFloorPrice: $('#buy-floor').find('.price'),
      // Person area of selections
      personArea: $('.person-area'),
      // Buttons
      slap: $('#slap'),
      toilet: $('#toilet'),
      feed: $('#feed')
    };

    /**
     * Binding click events
     *
     */

    // Professions..
    $(".profession").click(function(e) {

      var type = $(this).attr('id');
	    self.clicksound.currentTime = 0;
      self.clicksound.play();
      self.addEntity(type);

    });

    // Buttons..
    $(".action").click(function(e) {

      var type = $(this).attr('id');

      self.actionClick(type);

    });

    // New floor..
    this.ui.buyFloor.click(function(e) {

      alert("Buy new floor..");

    });

    // Click canvas
    this.ui.canvas.click(function(e) {

      var x = Math.floor((e.pageX-$(this).offset().left) / 64),
          y = Math.floor((e.pageY-$(this).offset().top) / 48);

      self.onClick(x, y);

    });

  };

  Game.prototype.actionClick = function(type) {

    if(this.gameplay.selectedEntity != null) {

      if(type === "slap") {

        this.gameplay.selectedEntity.slap();

        if(this.gameplay.selectedEntity.stateCurrent === "normal") {
          this.slapsound.currentTime = 0;
          this.slapsound.play();
        } else {
          this.clicksound.currentTime = 0;
          this.clicksound.play();
        }

      } else if(type === "feed") {

        this.gameplay.selectedEntity.feed();

        this.clicksound.currentTime = 0;
        this.clicksound.play();

      }

      return;

    }

    this.clearSelections();

  };

  /**
   * On canvas click...
   *
   */
  Game.prototype.onClick = function(x, y) {

    for(var type in this.gameplay.floors[this.gameplay.currentFloor]) {

      if(type === "coder") {

        // Converting to possible coder coordinates...
        x -= 4;

        if(typeof this.gameplay.floors[this.gameplay.currentFloor][type][x + "," + y] !== "undefined")
          this.selectEntity(this.gameplay.floors[this.gameplay.currentFloor][type][x + "," + y]);
        else
          this.clearSelections();

      }

    }

  };

  /**
   * Called when entity clicked on canvas
   *
   */
  Game.prototype.selectEntity = function(entity) {

    // If selected same again... just go away..
    if(this.gameplay.selectedEntity != null &&
      this.gameplay.selectedEntity.x === entity.x &&
      this.gameplay.selectedEntity.y === entity.y) {
      // Clear selections..
      this.clearSelections();
      return;
    }

    // Clear previous
    this.clearSelections();

    // Show correct view...
    $('#person-' + entity.type).find('.person img').attr("src", "/images/" + entity.stateCurrent + ".png");
    $('#person-' + entity.type).removeClass('hidden');

    this.gameplay.selectedEntity = entity;

  };

  /**
   * Called when UI selections needs to be toggled away.. (e.g. floor change)
   *
   */
  Game.prototype.clearSelections = function(entity) {

    // Setting UI to default hidden state
    this.ui.personArea.addClass("hidden");
    $('.person img').attr("src", "/images/normal.png");
    // todo add manager image..

    this.gameplay.selectedEntity = null;

  };

  /**
   *
   *
   */
  Game.prototype.addEntity = function(type) {

    var result = this.entityFactory.getFactory(type).addEntity();

    if(result.error) {

      this.notify(result.error);

      return;

    }
	// Entity added, play sound!
	this.register.currentTime = 0;
	this.register.play();

  };

  /**
   *
   *
   */
  Game.prototype.notify = function(text) {
    $('#notification span').animate({
          opacity: 0
        }, 150, 'linear', function() {
          $(this).text(text).css({ opacity: 1 });
    });
  	this.notifysound.currentTime = 0;
  	this.notifysound.play();
  };

  /**
   *
   *
   */
  Game.prototype.removeEntity = function(type, entity) {
    /* */
  };

	/**
	 *
	 *
	 */
	Game.prototype.init = function() {

		console.log("Game init..");

    var self = this;

    // Updating DOM before waiting rest to be loaded...
    this.ui.coderPrice.text(this.gameplay.costs.coder);
    this.ui.cleanerPrice.text(this.gameplay.costs.cleaner);
    this.ui.managerPrice.text(this.gameplay.costs.manager);
    this.ui.cookPrice.text(this.gameplay.costs.cook);
    this.ui.buyFloorPrice.text(this.gameplay.costs.buyFloor);
    this.refreshUI();

    // Init tile factory... block until finished
    this.entityFactory.init(function(err) {

      if(err) return alert(err);

      // FPS 30
  		var animFrame = new AnimationFrame(30),

  			// Game loop specifics
  			now = 0,
  			then = 0,
  			sub = 0,
  			dt = 0.0;

      // console.log("Testing Grid functionality");

      // self.testGrid();

  		/**
  		 * "The" loop
  		 *
  		 */
  		var loop = function() {

        if(self.isRunning) {

          // delta time (now)
          now = Date.now();
          sub = now - then;
          dt = sub / 1000;

          // draw
          self.draw();

          // update
          self.update(dt);

          // delta time (then)
          then = now;

          // request next frame
          animFrame.request(loop);

        }

  		};

  		// Start the loop
      self.isRunning = true;

  		loop();

    });

	};

	/**
	 *
	 *
	 */
	Game.prototype.draw = function() {

    this.refreshUI();
    this.canvas.draw();

	};

	/**
	 *
	 *
	 */
	Game.prototype.update = function(dt) {

    this.gameplay.time += 10000;

	//RELATED TO KEYBOARDSOUNDS
	if (this.playKeyboardsound>0) {
		if (typeof this.keyboardsound.loop == 'boolean')
		{
			this.keyboardsound.loop = true;
		}
		else
		{
			this.keyboardsound.addEventListener('ended', function() {
				this.currentTime = 0;
				this.play();
			}, false);
		}
		this.keyboardsound.play();
	} else {
		this.keyboardsound.pause();
	}
	// NO LONGER AT THIS POINT RELATED TO KEYBOARD SOUNDS

    // Update all entities
	this.canvas.update();

	// PLAYING KEYBOARD SOUNDS
	for(var type in this.gameplay.floors[this.gameplay.currentFloor]) {

	      if(type === "coder") {

	        for(var key in this.gameplay.floors[this.gameplay.currentFloor][type]) {

	          if (this.gameplay.floors[this.gameplay.currentFloor][type][key].stateCurrent === "normal"){
				this.playKeyboardsound = 1;
				break;
			  } else {
			  this.playKeyboardsound = 0;
			  }

	      }
		   for(var key in this.gameplay.floors[this.gameplay.currentFloor][type]) {
				if (this.gameplay.floors[this.gameplay.currentFloor][type][key].stateCurrent === "normal"){
					this.gameplay.money += 5;
				}
			}
		  if (Object.keys(this.gameplay.floors[this.gameplay.currentFloor]["coder"]).length>0){
			this.gameplay.money -= (Object.keys(this.gameplay.floors[this.gameplay.currentFloor]["coder"]).length)*3;
			if (this.gameplay.money<this.gameplay.losecondition) {
				alert("YOU LOSE! YOU'RE A FAILURE EVEN IN THIS STUPID BUSINESS!");
			}
		  }
	      break;

	    }

		}
	};
	// DONE PLAYING KEYBOARD SOUNDS

  /**
   * Method for updating UI element states
   *
   */
  Game.prototype.refreshUI = function() {

    this.ui.money.text(this.gameplay.money);
    this.ui.time.text(moment(new Date(this.gameplay.time)).format('MMMM Do YYYY, h:mm:ss a'));
    this.ui.project.text(this.gameplay.project);

  };

	Game.prototype.testGrid = function() {

		var kentta = new Grid( { size: 5 });
		kentta.testGrid();
	};

	return Game;

});
<commit_msg>projects!<commit_after>define(['reqanim', 'grid', 'zepto', 'entityFactory', 'moment', 'canvas', 'projectFactory'],
  function(AnimationFrame, Grid, $, EntityFactory, moment, Canvas, ProjectFactory) {

	/**
	 *
	 *
	 */
	var Game = function() {

    // self
    var self = this;

    // If game is running
    this.isRunning = false;

  	// Sounds
  	this.clicksound = new Audio('audio/click.ogg');
  	this.register = new Audio('audio/register.ogg');
  	this.notifysound = new Audio('audio/notify.ogg');
  	this.slapsound = new Audio('audio/Slap.ogg');
	this.keyboardsound = new Audio('audio/keyboard.ogg');
	this.playKeyboardsound = 0;

    // Projects..
    this.projectFactory = new ProjectFactory();

    // Gameplay related settings
    this.gameplay = {
      money: 30000,
	  losecondition: -50000,
	  progress: 0,
      time: new Date(1970,01,01).getMilliseconds(),
      project: this.projectFactory.getProject(),
      costs: {
        coder: 5000,
        cleaner: 2500,
        manager: 25000,
        cook: 3500,
        buyFloor: 150000
      },
      // our entities holder per office floors
      floors: {
        1: {
          coder: {
          },
          cleaner: {
          },
          manager: {
          },
          cook: {
          }
        }
      },
      // floor player is viewing
      currentFloor: 1,
      selectedEntity: null
    };

    // Entity factory
    this.entityFactory = new EntityFactory(this.gameplay);

    // Canvas
    this.canvas = new Canvas(this.gameplay);

    // Ui elements
    this.ui = {
      // Overall
      money: $('#money'),
      time: $('#time p'),
      project: $('#project'),
      notification: $('#notification'),
      // Persons
      coder: $('#coder'),
      cleaner: $('#cleaner'),
      manager: $('#manager'),
      cook: $('#cook'),
      // Floor
      buyFloor: $('#buy-floor'),
      // Canvas
      canvas: $('#drawable-canvas'),
      // Prices
      coderPrice: $('#coder').find('.price'),
      cleanerPrice: $('#cleaner').find('.price'),
      managerPrice: $('#manager').find('.price'),
      cookPrice: $('#cook').find('.price'),
      buyFloorPrice: $('#buy-floor').find('.price'),
      // Person area of selections
      personArea: $('.person-area'),
      // Buttons
      slap: $('#slap'),
      toilet: $('#toilet'),
      feed: $('#feed')
    };

    /**
     * Binding click events
     *
     */

    // Professions..
    $(".profession").click(function(e) {

      var type = $(this).attr('id');
	    self.clicksound.currentTime = 0;
      self.clicksound.play();
      self.addEntity(type);

    });

    // Buttons..
    $(".action").click(function(e) {

      var type = $(this).attr('id');

      self.actionClick(type);

    });

    // New floor..
    this.ui.buyFloor.click(function(e) {

      alert("Buy new floor..");

    });

    // Click canvas
    this.ui.canvas.click(function(e) {

      var x = Math.floor((e.pageX-$(this).offset().left) / 64),
          y = Math.floor((e.pageY-$(this).offset().top) / 48);

      self.onClick(x, y);

    });

  };

  Game.prototype.actionClick = function(type) {

    if(this.gameplay.selectedEntity != null) {

      if(type === "slap") {

        this.gameplay.selectedEntity.slap();

        if(this.gameplay.selectedEntity.stateCurrent === "normal") {
          this.slapsound.currentTime = 0;
          this.slapsound.play();
        } else {
          this.clicksound.currentTime = 0;
          this.clicksound.play();
        }

      } else if(type === "feed") {

        this.gameplay.selectedEntity.feed();

        this.clicksound.currentTime = 0;
        this.clicksound.play();

      }

      return;

    }

    this.clearSelections();

  };

  /**
   * On canvas click...
   *
   */
  Game.prototype.onClick = function(x, y) {

    for(var type in this.gameplay.floors[this.gameplay.currentFloor]) {

      if(type === "coder") {

        // Converting to possible coder coordinates...
        x -= 4;

        if(typeof this.gameplay.floors[this.gameplay.currentFloor][type][x + "," + y] !== "undefined")
          this.selectEntity(this.gameplay.floors[this.gameplay.currentFloor][type][x + "," + y]);
        else
          this.clearSelections();

      }

    }

  };

  /**
   * Called when entity clicked on canvas
   *
   */
  Game.prototype.selectEntity = function(entity) {

    // If selected same again... just go away..
    if(this.gameplay.selectedEntity != null &&
      this.gameplay.selectedEntity.x === entity.x &&
      this.gameplay.selectedEntity.y === entity.y) {
      // Clear selections..
      this.clearSelections();
      return;
    }

    // Clear previous
    this.clearSelections();

    // Show correct view...
    $('#person-' + entity.type).find('.person img').attr("src", "/images/" + entity.stateCurrent + ".png");
    $('#person-' + entity.type).removeClass('hidden');

    this.gameplay.selectedEntity = entity;

  };

  /**
   * Called when UI selections needs to be toggled away.. (e.g. floor change)
   *
   */
  Game.prototype.clearSelections = function(entity) {

    // Setting UI to default hidden state
    this.ui.personArea.addClass("hidden");
    $('.person img').attr("src", "/images/normal.png");
    // todo add manager image..

    this.gameplay.selectedEntity = null;

  };

  /**
   *
   *
   */
  Game.prototype.addEntity = function(type) {

    var result = this.entityFactory.getFactory(type).addEntity();

    if(result.error) {

      this.notify(result.error);

      return;

    }
	// Entity added, play sound!
	this.register.currentTime = 0;
	this.register.play();

  };

  /**
   *
   *
   */
  Game.prototype.notify = function(text) {
    $('#notification span').animate({
          opacity: 0
        }, 150, 'linear', function() {
          $(this).text(text).css({ opacity: 1 });
    });
  	this.notifysound.currentTime = 0;
  	this.notifysound.play();
  };

  /**
   *
   *
   */
  Game.prototype.removeEntity = function(type, entity) {
    /* */
  };

	/**
	 *
	 *
	 */
	Game.prototype.init = function() {

		console.log("Game init..");

    var self = this;

    // Updating DOM before waiting rest to be loaded...
    this.ui.coderPrice.text(this.gameplay.costs.coder);
    this.ui.cleanerPrice.text(this.gameplay.costs.cleaner);
    this.ui.managerPrice.text(this.gameplay.costs.manager);
    this.ui.cookPrice.text(this.gameplay.costs.cook);
    this.ui.buyFloorPrice.text(this.gameplay.costs.buyFloor);
    this.refreshUI();

    // Init tile factory... block until finished
    this.entityFactory.init(function(err) {

      if(err) return alert(err);

      // FPS 30
  		var animFrame = new AnimationFrame(30),

  			// Game loop specifics
  			now = 0,
  			then = 0,
  			sub = 0,
  			dt = 0.0;

      // console.log("Testing Grid functionality");

      // self.testGrid();

  		/**
  		 * "The" loop
  		 *
  		 */
  		var loop = function() {

        if(self.isRunning) {

          // delta time (now)
          now = Date.now();
          sub = now - then;
          dt = sub / 1000;

          // draw
          self.draw();

          // update
          self.update(dt);

          // delta time (then)
          then = now;

          // request next frame
          animFrame.request(loop);

        }

  		};

  		// Start the loop
      self.isRunning = true;

  		loop();

    });

	};

	/**
	 *
	 *
	 */
	Game.prototype.draw = function() {

    this.refreshUI();
    this.canvas.draw();

	};

	/**
	 *
	 *
	 */
	Game.prototype.update = function(dt) {

    this.gameplay.time += 10000;

	//RELATED TO KEYBOARDSOUNDS
	if (this.playKeyboardsound>0) {
		if (typeof this.keyboardsound.loop == 'boolean')
		{
			this.keyboardsound.loop = true;
		}
		else
		{
			this.keyboardsound.addEventListener('ended', function() {
				this.currentTime = 0;
				this.play();
			}, false);
		}
		this.keyboardsound.play();
	} else {
		this.keyboardsound.pause();
	}
	// NO LONGER AT THIS POINT RELATED TO KEYBOARD SOUNDS

    // Update all entities
	this.canvas.update();

	// PLAYING KEYBOARD SOUNDS
	for(var type in this.gameplay.floors[this.gameplay.currentFloor]) {

	      if(type === "coder") {

	        for(var key in this.gameplay.floors[this.gameplay.currentFloor][type]) {

	          if (this.gameplay.floors[this.gameplay.currentFloor][type][key].stateCurrent === "normal"){
				this.playKeyboardsound = 1;
				break;
			  } else {
			  this.playKeyboardsound = 0;
			  }

	      }
		   for(var key in this.gameplay.floors[this.gameplay.currentFloor][type]) {
				if (this.gameplay.floors[this.gameplay.currentFloor][type][key].stateCurrent === "normal"){
					this.gameplay.money += 5;
					this.gameplay.progress += 1;
					if (this.gameplay.progress === 5000) {
						this.notify("Project Completed: gained 10000 money!");
						this.gameplay.project= this.projectFactory.getProject();
						this.gameplay.progress = 0;
						this.gameplay.money += 10000;
					}
				}
			}
		  if (Object.keys(this.gameplay.floors[this.gameplay.currentFloor]["coder"]).length>0){
			this.gameplay.money -= (Object.keys(this.gameplay.floors[this.gameplay.currentFloor]["coder"]).length)*3;
			if (this.gameplay.money<this.gameplay.losecondition) {
				alert("YOU LOSE! YOU'RE A FAILURE EVEN IN THIS STUPID BUSINESS!");
			}
		  }
	      break;

	    }

		}
	};
	// DONE PLAYING KEYBOARD SOUNDS

  /**
   * Method for updating UI element states
   *
   */
  Game.prototype.refreshUI = function() {

    this.ui.money.text(this.gameplay.money);
    this.ui.time.text(moment(new Date(this.gameplay.time)).format('MMMM Do YYYY, h:mm:ss a'));
    this.ui.project.text(this.gameplay.project);

  };

	Game.prototype.testGrid = function() {

		var kentta = new Grid( { size: 5 });
		kentta.testGrid();
	};

	return Game;

});
<|endoftext|>