<commit_before>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings.

var version = '2.0 Beta';
var SCBversion = '3.03'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var incoming_ONG = 0;
var NinjaAutoClickStatus = 1;
var Time_to_ONG = 1800000;
var lootBoxes = ['boosts', 'ninj', 'cyb', 'hpt', 'bean', 'chron', 'badges', 'badgesav'];

//BB Options Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var refreshRate = 1000;
var RKAlertFrequency = 8;
var RKAutoClickStatus = 1;
var RKPlayAudio = 1;

//RK Variables
var start = -1;
var content = "empty";
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var len = 0;
var Logicat = 0;
var oldRKLocation = -1;
var oldRC = Molpy.redactedClicks - 1;
var oldLC = Molpy.Boosts['Logicat'].power - 1;
var RKLevel = '-1';
var RKLocation = '123';
var RKNew = 1;
var RKNewAudio = 1;
var RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

//Ninja AutoClicker and Border Warnings
function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","1px solid white");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","1px solid white");
	}
}

function ToggleMenus(wantOpen) {
	for (i=0, len = lootBoxes.length; i < len; i++) {
		if (lootBoxes[i] == wantOpen) {
			if (Molpy.options.showhide[lootBoxes[i]] != 1) {
				showhideToggle(lootBoxes[i]);
			}
		}
		else {
			if (Molpy.options.showhide[lootBoxes[i]]) {
				showhideToggle(lootBoxes[i]);
			}
		}
	}
}

function FindRK() {
	/*
	RV of 1 is Sand Tools
	RV of 2 is Castle Tools
	RV of 3 is Boosts Main Page
	RV of 4 is Boosts Menus, Hill People Tech, etc.
	RV of 5 is Badges Earned
	RV of 6 is Badges Available
	*/

	//Determines RK location
	RKLocation = '123';
	Molpy.Notify('RK Group: ' + Molpy.redactedGr, 1);
	if (Molpy.redactedVisible > 3) {
		RKLocation = Molpy.redactedGr;
	}

	//Opens RK location
	ToggleMenus(RKLocation);

	//Resets old RK variables
	oldRKLocation = Molpy.redactedVisible;
	oldRC = Molpy.redactedClicks;
	oldLC = Molpy.Boosts['Logicat'].power;
}

function ManualRKClick() {

}

function RedundaKitty() {
	//Refresh Timer Variable
	RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

	//If a RedundaKitty is active
	if (Molpy.redactedVisible > 0) {

		//Checks if RK is new
		if (RKNew == 1 || Molpy.redactedVisible != oldRKLocation || Molpy.redactedClicks > oldRC || Molpy.Boosts['Logicat'].power != oldLC) {
			RKNewAudio = 1;
			RKNew = 0;
			//Finds RK if appropriate BeachBall option enabled
			if (RKAutoClickStatus > 0) {
				FindRK();
			}
		}

		//Determines if it is an RK or LC
		//If RK is visible
		if ($('#redacteditem').length) {
			$('#redacteditem').css("border","2px solid red"); //Highlights RK
			content = $('#redacteditem').html();
			//If RK contains word statement, it is a LC.
			if (content.indexOf("statement") !== -1) {
				//Molpy.Notify('Logicat Found', 1);
				Logicat = 1;
			}
			//Otherwise it is an RK
			else {
				Logicat = 0;
				start = content.indexOf("ClickRedacted");
				content = content.substring(start,start+17);
				content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
				len = content.length;
				RKLevel = content.substring(13,len);
				if (RKLevel != 0) {
					Molpy.Notify('RedundaKitty Level is: ' + RKLevel, 1);
				}
			}
		}
		//If RK not visible, LC to 99.
		else {
			Logicat = 99;
		}

		//Clicks RK if AutoClick Enabled
		if (RKAutoClickStatus == 2 && Logicat == 0 ) {
			Molpy.ClickRedacted(RKLevel);
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}
		//Solves LC if AutoClick enabled
		else if (Logicat == 1 && LCAutoClickStatus == 1) {
			SolveLogicat();
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}

		//Redundakitty Notifications for Manual Clicking (Title Bar, Audio)
		else {
			document.title = "! kitten !";
			//If RK Audio Alerts Enabled
			if (AudioAlertsStatus == 1 || AudioAlertsStatus == 3) {
				//If proper mNP and hasn't yet played this mNP
				if (Math.floor(RKTimer % RKAlertFrequency) == 0 && RKPlayAudio == 1) {
					audio_Bell.play();
					RKPlayAudio = 0;
				}
				else {
					RKPlayAudio = 1;
				}
			}
		}
	}
	//If no RK active, update title Timer. Reset some variables.
	else {
		document.title = RKTimer;
		oldRKLocation = -1;
		RKNew = 1;
		RKPlayAudio = 0;
	}
}

function SolveLogicat() {
	var i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 2) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
		case 'RefreshRate':
			refreshRate = prompt('Please enter your desired BeachBall refresh rate in milliseconds (500 - 3600):', 1000);
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error', 1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only'; RKPlayAudio = 1;}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG'; RKPlayAudio = 1;}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status, 1);}
	}
	else if (option == 'RKAutoClick') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'Find RK Only'; RKNew = 1;}
		else if (status == 2) {description = 'On'; RKNew = 1;}
		else {Molpy.Notify('Display Description Error - RKAutoClick: ' + status, 1);}
	}
	else if (option == 'RefreshRate') {
		description = refreshRate;
	}
	else {
		Molpy.Notify(option + ' is not a valid option', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
$('#optionsItems').append('

Refresh Rate

');
$('#optionsItems').append('

Spawn RK

');
//$('#optionsItems').append('

Blank

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);
DisplayDescription('RefreshRate', refreshRate);

function MainLoop() {
	Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1);
	setInterval(function() {
		Molpy.Notify('1 Refresh', 0);
		Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
		RedundaKitty();
		Ninja();
	}, refreshRate);
}

//Run Main Loop after 1 second startup delay
setTimeout(MainLoop, 1000);

function SpawnRK() {
	Molpy.redactedCountup = Molpy.redactedToggle - 1;
}

<commit_msg>Bug fixing<commit_after>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings.

var version = '2.0 Beta';
var SCBversion = '3.03'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var incoming_ONG = 0;
var NinjaAutoClickStatus = 1;
var Time_to_ONG = 1800000;
var lootBoxes = ['boosts', 'ninj', 'cyb', 'hpt', 'bean', 'chron', 'badges', 'badgesav'];

//BB Options Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var refreshRate = 1000;
var RKAlertFrequency = 8;
var RKAutoClickStatus = 1;
var RKPlayAudio = 1;

//RK Variables
var start = -1;
var content = "empty";
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var len = 0;
var Logicat = 0;
var oldRKLocation = -1;
var oldRC = Molpy.redactedClicks - 1;
var oldLC = Molpy.Boosts['Logicat'].power - 1;
var RKLevel = '-1';
var RKLocation = '123';
var RKNew = 1;
var RKNewAudio = 1;
var RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

//Ninja AutoClicker and Border Warnings
function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
                Molpy.ClickBeach();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","1px solid white");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid green");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","1px solid white");
	}
}

function ToggleMenus(wantOpen) {
	for (i=0, len = lootBoxes.length; i < len; i++) {
		if (lootBoxes[i] == wantOpen) {
			if (Molpy.options.showhide[lootBoxes[i]] != 1) {
				showhideToggle(lootBoxes[i]);
			}
		}
		else {
			if (Molpy.options.showhide[lootBoxes[i]]) {
				showhideToggle(lootBoxes[i]);
			}
		}
	}
}

function FindRK() {
	/*
	RV of 1 is Sand Tools
	RV of 2 is Castle Tools
	RV of 3 is Boosts Main Page
	RV of 4 is Boosts Menus, Hill People Tech, etc.
	RV of 5 is Badges Earned
	RV of 6 is Badges Available
	*/

	//Determines RK location
	RKLocation = '123';
	Molpy.Notify('RK Group: ' + Molpy.redactedGr, 1);
	if (Molpy.redactedVisible > 3) {
		RKLocation = Molpy.redactedGr;
	}

	//Opens RK location
	ToggleMenus(RKLocation);

	//Resets old RK variables
	oldRKLocation = Molpy.redactedVisible;
	oldRC = Molpy.redactedClicks;
	oldLC = Molpy.Boosts['Logicat'].power;
}

function ManualRKClick() {

}

function RedundaKitty() {
	//Refresh Timer Variable
	RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

	//If a RedundaKitty is active
	if (Molpy.redactedVisible > 0) {

		//Checks if RK is new
		if (RKNew == 1 || Molpy.redactedVisible != oldRKLocation || Molpy.redactedClicks > oldRC || Molpy.Boosts['Logicat'].power != oldLC) {
			RKNewAudio = 1;
			RKNew = 0;
			//Finds RK if appropriate BeachBall option enabled
			if (RKAutoClickStatus > 0) {
				FindRK();
			}
		}

		//Determines if it is an RK or LC
		//If RK is visible
		if ($('#redacteditem').length) {
			$('#redacteditem').css("border","2px solid red"); //Highlights RK
			content = $('#redacteditem').html();
			//If RK contains word statement, it is a LC.
			if (content.indexOf("statement") !== -1) {
				//Molpy.Notify('Logicat Found', 1);
				Logicat = 1;
			}
			//Otherwise it is an RK
			else {
				Logicat = 0;
				start = content.indexOf("ClickRedacted");
				content = content.substring(start,start+17);
				content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
				len = content.length;
				RKLevel = content.substring(13,len);
				if (RKLevel != 0) {
					Molpy.Notify('RedundaKitty Level is: ' + RKLevel, 1);
				}
			}
		}
		//If RK not visible, LC to 99.
		else {
			Logicat = 99;
		}

		//Clicks RK if AutoClick Enabled
		if (RKAutoClickStatus == 2 && Logicat == 0 ) {
			Molpy.ClickRedacted(RKLevel);
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}
		//Solves LC if AutoClick enabled
		else if (Logicat == 1 && LCAutoClickStatus == 1) {
			SolveLogicat();
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}

		//Redundakitty Notifications for Manual Clicking (Title Bar, Audio)
		else {
			document.title = "! kitten !";
			//If RK Audio Alerts Enabled
			if (AudioAlertsStatus == 1 || AudioAlertsStatus == 3) {
				//If proper mNP and hasn't yet played this mNP
				if (Math.floor(RKTimer % RKAlertFrequency) == 0 && RKPlayAudio == 1) {
					audio_Bell.play();
					RKPlayAudio = 0;
				}
				else {
					RKPlayAudio = 1;
				}
			}
		}
	}
	//If no RK active, update title Timer. Reset some variables.
	else {
		document.title = RKTimer;
		oldRKLocation = -1;
		RKNew = 1;
		RKPlayAudio = 0;
	}
}

function SolveLogicat() {
	var i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 2) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
		case 'RefreshRate':
			refreshRate = parsseInt(prompt('Please enter your desired BeachBall refresh rate in milliseconds (500 - 3600):', 1000));
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error', 1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only'; RKPlayAudio = 1;}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG'; RKPlayAudio = 1;}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status, 1);}
	}
	else if (option == 'RKAutoClick') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'Find RK Only'; RKNew = 1;}
		else if (status == 2) {description = 'On'; RKNew = 1;}
		else {Molpy.Notify('Display Description Error - RKAutoClick: ' + status, 1);}
	}
	else if (option == 'RefreshRate') {
		description = refreshRate;
	}
	else {
		Molpy.Notify(option + ' is not a valid option', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
$('#optionsItems').append('

Refresh Rate

');
$('#optionsItems').append('

Spawn RK

');
//$('#optionsItems').append('

Blank

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);
DisplayDescription('RefreshRate', refreshRate);

function MainLoop() {
	Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1);
	setInterval(function() {
		Molpy.Notify('1 Refresh', 0);
		Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
		RedundaKitty();
		Ninja();
	}, refreshRate);
}

//Run Main Loop after 1 second startup delay
setTimeout(MainLoop, 1000);

function SpawnRK() {
	Molpy.redactedCountup = Molpy.redactedToggle - 1;
}

<|endoftext|>


<commit_before>const { filter: _filter, reduce: _reduce } = require('lodash');

Memory.sources || (Memory.sources = {});

function sourceMem(source, key, value) {
  Memory.sources[source.id] || (Memory.sources[source.id] = {});
  if(key) {
    if(value) {
      Memory.sources[source.id][key] = value;
    }

    return Memory.sources[source.id][key];
  }

  return Memory.sources[source.id];
}

function hasWorkableSpots(source) {
  if(!sourceMem(source, 'maxWorkableSpots')) {
    // determine number of workable spots
    var terrain = source.room.lookForAtArea(LOOK_TERRAIN, source.pos.y - 1, source.pos.x - 1, source.pos.y + 1, source.pos.x + 1, true);
    terrain = _filter(terrain, function(t) { return t.terrain != 'wall' });

    sourceMem(source, 'maxWorkableSpots', terrain.length);
    sourceMem(source, 'workableSpots', terrain.length);
  }
  else if(sourceMem(source, 'recalc')) {
    // count creeps that have reserved this source
    let reservedSpots = _reduce(Memory.creeps, (count, _, creep) => ( count + (creep.reservedSource === source.id )), 0);
    sourceMem(source, 'workableSpots', reservedSpots);
    sourceMem(source, 'recalc', false);
  }

  return sourceMem(source, 'workableSpots') > 0;
}

const { harvestResource, checkinSpot } = require('utils'),
      role = {

        /** @param {Creep} creep **/
        run: function(creep) {
          if(creep.carryCapacity !== 0 && creep.memory.harvesting && creep.carry.energy === creep.carryCapacity) {
            creep.memory.harvesting = false;
          }

          if(!creep.memory.harvesting && creep.carry.energy === 0) {
            creep.memory.harvesting = true;
          }

          if(creep.memory.harvesting) {
            role.harvestResource(creep);
          }
          else {
            // Find the nearest non-full container
            let target = creep.pos.findClosestByPath(FIND_STRUCTURES,
                                                     { filter: bldg => (
                                                       bldg.structureType === STRUCTURE_CONTAINER &&
                                                       bldg.store[RESOURCE_ENERGY] < bldg.storeCapacity
                                                     )
                                                     });

            if(target) {
              if(creep.transfer(target, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                creep.doMove(target, {visualizePathStyle: {stroke: '#ffffff'}});
              }
            }
          }
        },

        // Path the creep to the nearest workable resource
        harvestResource: function(creep) {
          let source;

          if (creep.memory.reservedSource) {
            source = Game.getObjectById(creep.memory.reservedSource);
          }
          else {
            source = creep.pos.findClosestByPath(FIND_SOURCES,
                                                 { filter: (source) => source.energy > 0 && hasWorkableSpots(source) });
          }

          // reserve source if it's not already reserved
          if(creep.memory.reservedSource != source.id && sourceMem(source, 'workableSpots') > 0) {
            sourceMem(source, 'workableSpots', sourceMem(source, 'workableSpots') - 1);
            creep.memory.reservedSource = source.id;
          }

          if(source) {
            if(creep.harvest(source) == ERR_NOT_IN_RANGE) {
              creep.doMove(source, {visualizePathStyle: {stroke: '#00aaff'}});
            }
          }
        }
      };

module.exports = role;
<commit_msg>Slightly better source reservation<commit_after>const { filter: _filter, reduce: _reduce } = require('lodash');

Memory.sources || (Memory.sources = {});

function sourceMem(source, key, value) {
  Memory.sources[source.id] || (Memory.sources[source.id] = {});
  if(key) {
    if(value) {
      Memory.sources[source.id][key] = value;
    }

    return Memory.sources[source.id][key];
  }

  return Memory.sources[source.id];
}

function hasWorkableSpots(source) {
  if(!sourceMem(source, 'maxWorkableSpots')) {
    // determine number of workable spots
    var terrain = source.room.lookForAtArea(LOOK_TERRAIN, source.pos.y - 1, source.pos.x - 1, source.pos.y + 1, source.pos.x + 1, true);
    terrain = _filter(terrain, function(t) { return t.terrain != 'wall' });

    sourceMem(source, 'maxWorkableSpots', terrain.length);
    sourceMem(source, 'workableSpots', terrain.length);
  }
  else if(sourceMem(source, 'recalc')) {
    // count creeps that have reserved this source
    let reservedSpots = _reduce(Memory.creeps, (count, _, creep) => ( count + (creep.reservedSource === source.id )), 0);
    sourceMem(source, 'workableSpots', reservedSpots);
    sourceMem(source, 'recalc', false);
  }

  return sourceMem(source, 'workableSpots') > 0;
}

const { harvestResource, checkinSpot } = require('utils'),
      role = {

        /** @param {Creep} creep **/
        run: function(creep) {
          if(creep.carryCapacity !== 0 && creep.memory.harvesting && creep.carry.energy === creep.carryCapacity) {
            creep.memory.harvesting = false;
          }

          if(!creep.memory.harvesting && creep.carry.energy === 0) {
            creep.memory.harvesting = true;
          }

          if(creep.memory.harvesting) {
            role.harvestResource(creep);
          }
          else {
            // Find the nearest non-full container
            let target = creep.pos.findClosestByPath(FIND_STRUCTURES,
                                                     { filter: bldg => (
                                                       bldg.structureType === STRUCTURE_CONTAINER &&
                                                       bldg.store[RESOURCE_ENERGY] < bldg.storeCapacity
                                                     )
                                                     });

            if(target) {
              if(creep.transfer(target, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                creep.doMove(target, {visualizePathStyle: {stroke: '#ffffff'}});
              }
            }
          }
        },

        // Path the creep to the nearest workable resource
        harvestResource: function(creep) {
          let source;

          if (creep.memory.reservedSource) {
            source = Game.getObjectById(creep.memory.reservedSource);
          }
          else {
            source = creep.pos.findClosestByPath(FIND_SOURCES,
                                                 { filter: (source) => source.energy > 0 && hasWorkableSpots(source) });
          }

          // reserve source if it's not already reserved
          if(creep.memory.reservedSource != source.id) {
            let workableSpots = sourceMem(source, 'workableSpots');
            if(workableSpots > 0) {
              sourceMem(source, 'workableSpots', workableSpots - 1);
              creep.memory.reservedSource = source.id;
            }
            else {
              source = null; // clear source if it's not workable
            }
          }

          if(source) {
            if(creep.harvest(source) == ERR_NOT_IN_RANGE) {
              creep.doMove(source, {visualizePathStyle: {stroke: '#00aaff'}});
            }
          }
        }
      };

module.exports = role;
<|endoftext|>


<commit_before>/*global kontext, describe, it, expect*/
describe('Kontext Extension Each', function() {
	'use strict';

	var element;

	beforeEach(function(done) {
		element = document.createElement('div');
		element.appendChild(document.createElement('strong')).appendChild(document.createTextNode('{a} and {b}'));

		done();
	});

	afterEach(function(done) {
		if (element) {
			if (element.parentNode) {
				element.parentNode.removeChild(element);
			}

			element = null;
		}

		done();
	});

	it('reflects the items in bound array', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: list');
		model = kontext.bind({list: []}, element);

		model.on('update', function(mod, key) {
			expect(element.childNodes.length).toBe(mod[key].length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('works with nested arrays', function(done) {
		var nest, model, list;

		//  clean up the element, as we want a different structure
		while (element.lastChild) {
			element.removeChild(element.lastChild);
		}

		element.setAttribute('data-kontext', 'each: list');
		nest = element.appendChild(document.createElement('div'));
		nest.appendChild(document.createElement('h3')).setAttribute('data-kontext', 'text: name');

		nest = nest.appendChild(document.createElement('div'));
		nest.setAttribute('data-kontext', 'each: child');
		nest.appendChild(document.createElement('span')).setAttribute('data-kontext', 'text: name');

		model = kontext.bind({list: [
			{name: 'a', child: [{name: 'a.1'}, {name: 'a.2'}]},
			{name: 'b', child: [{name: 'b.1'}, {name: 'b.2'}]}
		]}, element);

		list = element.querySelectorAll('h3');
		expect(list.length).toBe(2);
		expect(list[0].firstChild.nodeValue).toBe('a');
		expect(list[1].firstChild.nodeValue).toBe('b');

		list = element.querySelectorAll('span[data-kontext]');
		expect(list.length).toBe(4);
		expect(list[0].firstChild.nodeValue).toBe('a.1');
		expect(list[1].firstChild.nodeValue).toBe('a.2');
		expect(list[2].firstChild.nodeValue).toBe('b.1');
		expect(list[3].firstChild.nodeValue).toBe('b.2');

		model.on('update', function(mod) {
			var child;

			child = element.querySelectorAll('h3');
			expect(child.length).toBe(3);
			expect(child[2].firstChild.nodeValue).toBe('c');

			child = element.querySelectorAll('span[data-kontext]');
			expect(child.length).toBe(5);
			expect(child[4].firstChild.nodeValue).toBe('check');

			done();
		});

		model.list.push({name: 'c', child: [{name: 'check'}]});
	});

	it('works with settings in objects', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: {target: list}');
		model = kontext.bind({list: []}, element);

		model.on('update', function(mod, key) {
			expect(element.childNodes.length).toBe(mod[key].length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('filters using a single model method', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: {target: list, filter: even}');
		model = kontext.bind({
			list: [],
			even: function(m, i) {
				return i % 2 === 0;
			}
		}, element);

		model.on('update', function(mod, key) {
			var expectation = model.list.filter(model.even);

			expect(element.childNodes.length).toBe(expectation.length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('filters using an array of model methods and global/window functions', function(done) {
		var model;

		window.skipFirst = function(m, i) {
			return i > 0;
		};

		element.setAttribute('data-kontext', 'each: {target: list, filter: [even, skipFirst]}');
		model = kontext.bind({
			list: [],
			even: function(m, i) {
				return i % 2 === 0;
			}
		}, element);

		model.on('update', function(mod, key) {
			var expectation = model.list.filter(model.even).filter(window.skipFirst);

			expect(element.childNodes.length).toBe(expectation.length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('removes elements which are no longer needed', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: list');
		element.removeChild(element.firstChild);
		element.appendChild(document.createElement('div')).appendChild(document.createTextNode('{$item}'));

		model = kontext.bind({
			list: ['a', 'b', 'c']
		}, element);

		model.list.splice(1, 1);

		setTimeout(function() {
			expect(element.childNodes.length).toBe(model.list.length);
			expect(element.childNodes[0].firstChild.data).toBe('a');
			expect(element.childNodes[1].firstChild.data).toBe('c');

			done();
		}, 100);
	});

	it('throws errors if there is no target', function(done) {
		element.setAttribute('data-kontext', 'each: {}');

		expect(function() {
			kontext.bind({list: []}, element);
		}).toThrow(new Error('Missing target for "each"'));

		done();
	});

	it('throws errors if there if a filter method/function does not exist', function(done) {
		element.setAttribute('data-kontext', 'each: {target: list, filter: nope}');

		expect(function() {
			kontext.bind({list: []}, element);
		}).toThrow(new Error('nope is not a filter function'));

		done();
	});

	it('throws errors if there if a map method/function does not exist', function(done) {
		element.setAttribute('data-kontext', 'each: {target: list, map: nope}');

		expect(function() {
			kontext.bind({list: []}, element);
		}).toThrow(new Error('nope is not a map function'));

		done();
	});
});
<commit_msg>removed unused variables, added `before-` and `afterEach` to known variables<commit_after>/*global kontext, describe, it, beforeEach, afterEach, expect*/
describe('Kontext Extension Each', function() {
	'use strict';

	var element;

	beforeEach(function(done) {
		element = document.createElement('div');
		element.appendChild(document.createElement('strong')).appendChild(document.createTextNode('{a} and {b}'));

		done();
	});

	afterEach(function(done) {
		if (element) {
			if (element.parentNode) {
				element.parentNode.removeChild(element);
			}

			element = null;
		}

		done();
	});

	it('reflects the items in bound array', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: list');
		model = kontext.bind({list: []}, element);

		model.on('update', function(mod, key) {
			expect(element.childNodes.length).toBe(mod[key].length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('works with nested arrays', function(done) {
		var nest, model, list;

		//  clean up the element, as we want a different structure
		while (element.lastChild) {
			element.removeChild(element.lastChild);
		}

		element.setAttribute('data-kontext', 'each: list');
		nest = element.appendChild(document.createElement('div'));
		nest.appendChild(document.createElement('h3')).setAttribute('data-kontext', 'text: name');

		nest = nest.appendChild(document.createElement('div'));
		nest.setAttribute('data-kontext', 'each: child');
		nest.appendChild(document.createElement('span')).setAttribute('data-kontext', 'text: name');

		model = kontext.bind({list: [
			{name: 'a', child: [{name: 'a.1'}, {name: 'a.2'}]},
			{name: 'b', child: [{name: 'b.1'}, {name: 'b.2'}]}
		]}, element);

		list = element.querySelectorAll('h3');
		expect(list.length).toBe(2);
		expect(list[0].firstChild.nodeValue).toBe('a');
		expect(list[1].firstChild.nodeValue).toBe('b');

		list = element.querySelectorAll('span[data-kontext]');
		expect(list.length).toBe(4);
		expect(list[0].firstChild.nodeValue).toBe('a.1');
		expect(list[1].firstChild.nodeValue).toBe('a.2');
		expect(list[2].firstChild.nodeValue).toBe('b.1');
		expect(list[3].firstChild.nodeValue).toBe('b.2');

		model.on('update', function() {
			var child;

			child = element.querySelectorAll('h3');
			expect(child.length).toBe(3);
			expect(child[2].firstChild.nodeValue).toBe('c');

			child = element.querySelectorAll('span[data-kontext]');
			expect(child.length).toBe(5);
			expect(child[4].firstChild.nodeValue).toBe('check');

			done();
		});

		model.list.push({name: 'c', child: [{name: 'check'}]});
	});

	it('works with settings in objects', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: {target: list}');
		model = kontext.bind({list: []}, element);

		model.on('update', function(mod, key) {
			expect(element.childNodes.length).toBe(mod[key].length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('filters using a single model method', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: {target: list, filter: even}');
		model = kontext.bind({
			list: [],
			even: function(m, i) {
				return i % 2 === 0;
			}
		}, element);

		model.on('update', function(mod, key) {
			var expectation = model.list.filter(model.even);

			expect(element.childNodes.length).toBe(expectation.length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('filters using an array of model methods and global/window functions', function(done) {
		var model;

		window.skipFirst = function(m, i) {
			return i > 0;
		};

		element.setAttribute('data-kontext', 'each: {target: list, filter: [even, skipFirst]}');
		model = kontext.bind({
			list: [],
			even: function(m, i) {
				return i % 2 === 0;
			}
		}, element);

		model.on('update', function(mod, key) {
			var expectation = model.list.filter(model.even).filter(window.skipFirst);

			expect(element.childNodes.length).toBe(expectation.length);

			if (mod[key].length < 5) {
				mod[key].push({
					a: 'a' + mod[key].length,
					b: 'b' + mod[key].length
				});
			}
			else {
				done();
			}
		});

		expect(element.childNodes.length).toBe(0);

		model.list.push({
			a: 'initial',
			b: 'initial'
		});
	});

	it('removes elements which are no longer needed', function(done) {
		var model;

		element.setAttribute('data-kontext', 'each: list');
		element.removeChild(element.firstChild);
		element.appendChild(document.createElement('div')).appendChild(document.createTextNode('{$item}'));

		model = kontext.bind({
			list: ['a', 'b', 'c']
		}, element);

		model.list.splice(1, 1);

		setTimeout(function() {
			expect(element.childNodes.length).toBe(model.list.length);
			expect(element.childNodes[0].firstChild.data).toBe('a');
			expect(element.childNodes[1].firstChild.data).toBe('c');

			done();
		}, 100);
	});

	it('throws errors if there is no target', function(done) {
		element.setAttribute('data-kontext', 'each: {}');

		expect(function() {
			kontext.bind({list: []}, element);
		}).toThrow(new Error('Missing target for "each"'));

		done();
	});

	it('throws errors if there if a filter method/function does not exist', function(done) {
		element.setAttribute('data-kontext', 'each: {target: list, filter: nope}');

		expect(function() {
			kontext.bind({list: []}, element);
		}).toThrow(new Error('nope is not a filter function'));

		done();
	});

	it('throws errors if there if a map method/function does not exist', function(done) {
		element.setAttribute('data-kontext', 'each: {target: list, map: nope}');

		expect(function() {
			kontext.bind({list: []}, element);
		}).toThrow(new Error('nope is not a map function'));

		done();
	});
});
<|endoftext|>


<commit_before>var roleHarvester = {

  /** @param {Creep} creep **/
  run: function(creep) {
    if (creep.memory.harvesting && creep.carry.energy === creep.carryCapacity) {
      creep.memory.harvesting = false;
      creep.say('depositing');
    }
    if (!creep.memory.harvesting && creep.carry.energy === 0) {
      creep.memory.harvesting = true;
      creep.say('🔄 harvest');
    }

    if (creep.memory.harvesting) {
      var source = creep.pos.findClosestByPath(FIND_SOURCES_ACTIVE);
      var success = creep.harvest(source);
      if (success === ERR_NOT_IN_RANGE || success === ERR_NOT_ENOUGH_RESOURCES) {
        creep.moveTo(source, {visualizePathStyle: {stroke: '#ffaa00'}});
      }
    } else {
      // prioritize an empty tower over harvesting to make new creeps
      var target = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
        filter: (structure) => structure.type == STRUCTURE_TOWER && structure.energyAvailable < 200
      });
      if (!target) {
        target = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
          filter: (structure) => {
            var type = structure.structureType;
            return (type == STRUCTURE_EXTENSION || type == STRUCTURE_SPAWN || type == STRUCTURE_TOWER)
               && structure.energy < structure.energyCapacity;
          }
        });
      }

      if (target) {
        if (creep.transfer(target, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
          creep.moveTo(target, {visualizePathStyle: {stroke: '#ffffff'}});
        }
      }
    }
  }
};

module.exports = roleHarvester;
<commit_msg>for real this time<commit_after>var roleHarvester = {

  /** @param {Creep} creep **/
  run: function(creep) {
    if (creep.memory.harvesting && creep.carry.energy === creep.carryCapacity) {
      creep.memory.harvesting = false;
      creep.say('depositing');
    }
    if (!creep.memory.harvesting && creep.carry.energy === 0) {
      creep.memory.harvesting = true;
      creep.say('🔄 harvest');
    }

    if (creep.memory.harvesting) {
      var source = creep.pos.findClosestByPath(FIND_SOURCES_ACTIVE);
      var success = creep.harvest(source);
      if (success === ERR_NOT_IN_RANGE || success === ERR_NOT_ENOUGH_RESOURCES) {
        creep.moveTo(source, {visualizePathStyle: {stroke: '#ffaa00'}});
      }
    } else {
      // prioritize an empty tower over harvesting to make new creeps
      var target = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
        filter: (structure) => structure.structureType === STRUCTURE_TOWER && structure.energy < 200
      });
      if (!target) {
        target = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
          filter: (structure) => {
            var type = structure.structureType;
            return (type == STRUCTURE_EXTENSION || type == STRUCTURE_SPAWN || type == STRUCTURE_TOWER)
               && structure.energy < structure.energyCapacity;
          }
        });
      }

      if (target) {
        if (creep.transfer(target, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
          creep.moveTo(target, {visualizePathStyle: {stroke: '#ffffff'}});
        }
      }
    }
  }
};

module.exports = roleHarvester;
<|endoftext|>


<commit_before>const createElement = require('virtual-dom/create-element')
const h = require('virtual-dom/virtual-hyperscript')

module.exports = render

function render(){
  return createElement(root())
}

function root(){
  return slideshowWrapper([
    markdownSlide(`
      # MetaMask
      ### bridging browsers to blockchains
    `),

    markdownSlide(`ease of adoption is our core goal`),

    //
    // METAMASK TODAY
    //

    markdownSlide(`
      # MetaMask Today
    `),

    slide(img('./images/flow00a.png')),
    slide(img('./images/flow00b.png')),
    slide(img('./images/flow00c.png')),
    slide(img('./images/flow00d.png')),
    slide(img('./images/flow01.png')),
    slide(img('./images/flow02.png')),
    slide(img('./images/flow03.png')),
    slide(img('./images/flow04.png')),
    slide(img('./images/flow05.png')),
    slide(img('./images/flow05a.png')),
    slide(img('./images/flow06.png')),
    slide(img('./images/flow07.png')),
    slide(img('./images/flow08.png')),
    slide(img('./images/flow09.png')),
    slide(img('./images/flow11.png')),

    markdownSlide(`
      ### status: Public Beta
    `),

    slide([
      img('./images/chrome-store.png'), h('br'),
      '44 releases since march (weekly!)', h('br'),
      '~7k users', h('br'),
      '~14 million RPC requests / day', h('br'),
      '( thanks infura! )', h('br'),
      '1 lil buterin name drop', h('br'),
    ]),

    // browser dapp compat table
    slide([`metamask extension compat`, h('br'),h('br'),h('br'),
      table([
        // 'browser', 'status',
      ],[
        [`chrome:`, `live!`],
        [`opera:`, `ready!`],
        [`edge:`, `ready!`],
        [`firefox:`, `ready!`],
        [`safari:`, `nope :(`],
        [`mobile:`, `nope :(`],
      ])
    ]),

    //
    // HOW IT WORKS
    //

    markdownSlide(`
      # How it works
    `),

    slide(`just throw an ethereum client in an extension?`),
    markdownSlide(`
      blocked on networking:

      no tcp/udp`),
    slide(`mandatory sync time: non-starter`),
    slide(`rpc against trusted node`),
    slide(`id mgmt? rpc interception`),

    slide(img('./images/nomnoml.png')),

    markdownSlide(`
      ### web3-provider-engine
      make your own web3 provider

      a stack of rpc-handling middleware
    `),

    markdownSlide(`
      ### middleware stack
      * cache layer
      * id mgmt
      * trusted rpc node
    `),

    markdownSlide(`
      different trust model

      100% RPC - bring your own node!

      lock-in devalues product
    `),

// Dan Presents
// CHALLENGES
//

markdownSlide(`# LESSONS
## From Developers
               `),

markdownSlide(`### What's it like building a 3rd party Ethereum browser?
- We see cool Dapps being made!
- We handle developer problems!
`),
 //
    // FUTURE
    //
    markdownSlide(`# Future`),

    slide(`Multiple Account types (uPort, remote key stores)`),

    slide(`
      Browser Light Client
      via p2p network on webRTC
    `),

    slide(`Mascara MetaMask Polyfill`),
    slide(img('./images/add-script-tag.gif')),
    // slide(img('./images/mascara.gif')),

    // browser dapp compat table
    slide([`browser dapp compat`, h('br'),h('br'),h('br'),
      table([
        '', 'user has metamask', 'no metamask',
      ],[
        ['dapp has mascara', ':D', ':D'],
        ['no mascara', ':D', ':('],
      ])
    ]),

    slide(`private keys are safe, useable across dapps`),

    slide(`thanks`),

  ])
}

function markdown(text) {
  return h('script', {
    'data-markdown': '',
    'attributes': {
      'type': 'text/template'
    }
  }, text)
}

function markdownSlide(text){
  return (

    h('section', {
      'attributes': {
        'data-markdown': '',
      }
    }, [
      h('script', {
        'attributes': {
          'type': 'text/template'
        }
      }, text)
    ])

  )
}

function slide(content){
  return (

    h('section', content)

  )
}

function slideshowWrapper(content){
  return (

    h('.reveal', [ h('.slides', content) ])

  )
}

function img(src) {
  return h('img', {
    src: src,
    style: {
      // maxWidth: '660px',
      maxHeight: '80vh',
      background: 'whitesmoke',
    },
  })
}

function table(headers, rows){
  return h('table', [
    h('thead', headers.map(label => h('td', label))),
    h('tbody', rows.map(row => h('tr', row.map( datum => h('td', datum) )))),
  ])
}
<commit_msg>fix max-height<commit_after>const createElement = require('virtual-dom/create-element')
const h = require('virtual-dom/virtual-hyperscript')

module.exports = render

function render(){
  return createElement(root())
}

function root(){
  return slideshowWrapper([
    markdownSlide(`
      # MetaMask
      ### bridging browsers to blockchains
    `),

    markdownSlide(`ease of adoption is our core goal`),

    //
    // METAMASK TODAY
    //

    markdownSlide(`
      # MetaMask Today
    `),

    slide(img('./images/flow00a.png')),
    slide(img('./images/flow00b.png')),
    slide(img('./images/flow00c.png')),
    slide(img('./images/flow00d.png')),
    slide(img('./images/flow01.png')),
    slide(img('./images/flow02.png')),
    slide(img('./images/flow03.png')),
    slide(img('./images/flow04.png')),
    slide(img('./images/flow05.png')),
    slide(img('./images/flow05a.png')),
    slide(img('./images/flow06.png')),
    slide(img('./images/flow07.png')),
    slide(img('./images/flow08.png')),
    slide(img('./images/flow09.png')),
    slide(img('./images/flow11.png')),

    markdownSlide(`
      ### status: Public Beta
    `),

    slide([
      img('./images/chrome-store.png'), h('br'),
      '44 releases since march (weekly!)', h('br'),
      '~7k users', h('br'),
      '~14 million RPC requests / day', h('br'),
      '( thanks infura! )', h('br'),
      '1 lil buterin name drop', h('br'),
    ]),

    // browser dapp compat table
    slide([`metamask extension compat`, h('br'),h('br'),h('br'),
      table([
        // 'browser', 'status',
      ],[
        [`chrome:`, `live!`],
        [`opera:`, `ready!`],
        [`edge:`, `ready!`],
        [`firefox:`, `ready!`],
        [`safari:`, `nope :(`],
        [`mobile:`, `nope :(`],
      ])
    ]),

    //
    // HOW IT WORKS
    //

    markdownSlide(`
      # How it works
    `),

    slide(`just throw an ethereum client in an extension?`),
    markdownSlide(`
      blocked on networking:

      no tcp/udp`),
    slide(`mandatory sync time: non-starter`),
    slide(`rpc against trusted node`),
    slide(`id mgmt? rpc interception`),

    slide(img('./images/nomnoml.png')),

    markdownSlide(`
      ### web3-provider-engine
      make your own web3 provider

      a stack of rpc-handling middleware
    `),

    markdownSlide(`
      ### middleware stack
      * cache layer
      * id mgmt
      * trusted rpc node
    `),

    markdownSlide(`
      different trust model

      100% RPC - bring your own node!

      lock-in devalues product
    `),

// Dan Presents
// CHALLENGES
//

markdownSlide(`# LESSONS
## From Developers
               `),

markdownSlide(`### What's it like building a 3rd party Ethereum browser?
- We see cool Dapps being made!
- We handle developer problems!
`),
 //
    // FUTURE
    //
    markdownSlide(`# Future`),

    slide(`Multiple Account types (uPort, remote key stores)`),

    slide(`
      Browser Light Client
      via p2p network on webRTC
    `),

    slide(`Mascara MetaMask Polyfill`),
    slide(img('./images/add-script-tag.gif')),
    // slide(img('./images/mascara.gif')),

    // browser dapp compat table
    slide([`browser dapp compat`, h('br'),h('br'),h('br'),
      table([
        '', 'user has metamask', 'no metamask',
      ],[
        ['dapp has mascara', ':D', ':D'],
        ['no mascara', ':D', ':('],
      ])
    ]),

    slide(`private keys are safe, useable across dapps`),

    slide(`thanks`),

  ])
}

function markdown(text) {
  return h('script', {
    'data-markdown': '',
    'attributes': {
      'type': 'text/template'
    }
  }, text)
}

function markdownSlide(text){
  return (

    h('section', {
      'attributes': {
        'data-markdown': '',
      }
    }, [
      h('script', {
        'attributes': {
          'type': 'text/template'
        }
      }, text)
    ])

  )
}

function slide(content){
  return (

    h('section', content)

  )
}

function slideshowWrapper(content){
  return (

    h('.reveal', [ h('.slides', content) ])

  )
}

function img(src) {
  return h('img', {
    src: src,
    style: {
      // maxWidth: '660px',
      maxHeight: '75vh',
      background: 'whitesmoke',
    },
  })
}

function table(headers, rows){
  return h('table', [
    h('thead', headers.map(label => h('td', label))),
    h('tbody', rows.map(row => h('tr', row.map( datum => h('td', datum) )))),
  ])
}
<|endoftext|>


<commit_before>'use strict'

const MetaRole = require('roles_meta')

class Factotum extends MetaRole {
  constructor () {
    super()
    this.defaultEnergy = 800
  }

  getPriority (creep) {
    return PRIORITIES_CREEP_FACTOTUM
  }

  getBuild (room, options) {
    this.setBuildDefaults(room, options)
    let build = [MOVE, CARRY, CARRY, CARRY, CARRY]
    return Creep.buildFromTemplate(build, options.energy)
  }

  manageCreep (creep) {
    if (creep.ticksToLive < 50) {
      return creep.recycle()
    }

    if (creep.memory.labs) {
      if (creep.memory.labs === 'fill') {
        this.fillFeeders(creep)
      } else {
        this.emptyLabs(creep)
      }
      return
    }

    if (_.sum(creep.carry) > 0) {
      if (creep.carry[RESOURCE_ENERGY]) {
        this.emptyEnergy(creep)
      } else {
        this.emptyResources(creep)
      }
      return
    }

    const storage = creep.room.storage
    const terminal = creep.room.terminal

    // empty link
    const storageLink = storage.getLink()
    if (storageLink && storageLink.energy) {
      if (creep.pos.isNearTo(storageLink)) {
        creep.withdraw(storageLink, RESOURCE_ENERGY)
      } else {
        creep.travelTo(storageLink)
      }
      return
    }

    if (terminal && terminal.store[RESOURCE_ENERGY] > TERMINAL_ENERGY) {
      const overflow = terminal.store[RESOURCE_ENERGY] - TERMINAL_ENERGY
      const transferAmount = overflow < creep.carryCapacity ? overflow : creep.carryCapacity
      creep.withdraw(terminal, RESOURCE_ENERGY, transferAmount)
      return
    }

    // pick up dropped resources and creep renewal energy
    const suicideBooth = creep.room.getSuicideBooth()
    if (suicideBooth) {
      const resources = suicideBooth.lookFor(LOOK_RESOURCES)
      if (resources.length > 0) {
        creep.pickup(resources[0])
      }
    }

    if (terminal && storage) {
      // Does terminal have something storage needs?
      const terminalResources = _.shuffle(Object.keys(terminal.store))
      for (const resource of terminalResources) {
        if (resource === RESOURCE_ENERGY) {
          continue
        }
        const need = storage.getResourceNeed(resource)
        if (need > 0) {
          const amount = Math.min(need, creep.carryCapacity - creep.carry, terminal.store[resource])
          creep.withdraw(terminal, resource, amount)
          return
        }
      }

      // Does storage have terminal overflow?
      const storageResources = _.shuffle(Object.keys(storage.store))
      for (const resource of storageResources) {
        if (resource === RESOURCE_ENERGY) {
          continue
        }
        const need = storage.getResourceNeed(resource)
        if (need < 0) {
          const amount = Math.min((-need), creep.carryCapacity - creep.carry, terminal.store[resource])
          creep.withdraw(storage, resource, amount)
          return
        }
      }
    }

    creep.memory.labs = 'fill'
  }

  fillFeeders (creep) {
    const reaction = creep.room.getActiveReaction()
    const feeders = creep.room.getFeederLabs()
    if (feeders.length < 2) {
      delete creep.memory.labs
      delete creep.memory.filling
      return
    }

    if (feeders[0].mineralAmount > 0 && feeders[0].mineralType !== reaction[0]) {
      creep.memory.labs = 'empty'
      return
    }
    if (feeders[1].mineralAmount > 0 && feeders[1].mineralType !== reaction[1]) {
      creep.memory.labs = 'empty'
      return
    }

    if (_.sum(creep.carry) <= 0) {
      if (feeders[0].mineralAmount / feeders[0].mineralCapacity >= 0.5) {
        if (feeders[1].mineralAmount / feeders[1].mineralCapacity >= 0.5) {
          creep.memory.labs = 'empty'
        }
      }
    }

    const carrying = Object.keys(creep.carry)
    if (carrying.length > 3) {
      creep.memory.labs = 'empty'
      return
    }

    const individualMax = creep.carryCapacity / 2
    let reactionPrimaryAvailable = creep.room.storage.store[reaction[0]]
    if (creep.carry[reaction[0]]) {
      reactionPrimaryAvailable += creep.carry[reaction[0]]
    }
    let reactionSecondaryAvailable = creep.room.storage.store[reaction[1]]
    if (creep.carry[reaction[1]]) {
      reactionSecondaryAvailable += creep.carry[reaction[1]]
    }

    const targetAmount = Math.min(individualMax, reactionPrimaryAvailable, reactionSecondaryAvailable)
    if (targetAmount <= 0 || creep.carry[carrying[0]] > targetAmount || creep.carry[carrying[1]] > targetAmount) {
      creep.memory.labs = 'empty'
      return
    }

    if (creep.memory.filling) {
      if (creep.carry[reaction[0]] && feeders[0].canFill()) {
        if (creep.pos.isNearTo(feeders[0])) {
          creep.transfer(feeders[0], reaction[0])
        } else {
          creep.travelTo(feeders[0])
        }
      } else if (creep.carry[reaction[1]] && feeders[1].canFill()) {
        if (creep.pos.isNearTo(feeders[1])) {
          creep.transfer(feeders[1], reaction[1])
        } else {
          creep.travelTo(feeders[1])
        }
      } else {
        delete creep.memory.filling
        creep.memory.labs = 'empty'
      }
    } else {
      if (!creep.pos.isNearTo(creep.room.storage)) {
        creep.travelTo(creep.room.storage)
        return
      }

      if (!creep.carry[reaction[0]] || creep.carry[reaction[0]] < targetAmount) {
        const amount = creep.carry[reaction[0]] ? targetAmount - creep.carry[reaction[0]] : targetAmount
        creep.withdraw(creep.room.storage, reaction[0], amount)
      } else if (!creep.carry[reaction[1]] || creep.carry[reaction[1]] < targetAmount) {
        const amount = creep.carry[reaction[1]] ? targetAmount - creep.carry[reaction[1]] : targetAmount
        creep.withdraw(creep.room.storage, reaction[1], amount)
      } else {
        creep.memory.filling = true
      }
    }
  }

  emptyLabs (creep) {
    if (creep.carryCapacity - _.sum(creep.carry) <= 0) {
      delete creep.memory.labs
      return
    }
    const lab = this.getLabToEmpty(creep)
    if (!lab) {
      delete creep.memory.labs
      return
    }
    if (creep.pos.isNearTo(lab)) {
      creep.withdraw(lab, lab.mineralType)
    } else {
      creep.travelTo(lab)
    }
  }

  getLabToEmpty (creep) {
    const feeders = creep.room.getFeederLabs()
    const reaction = creep.room.getActiveReaction()

    // If one feeder doesn't have the right mineral and the other is empty then empty the first.
    if (feeders[0].mineralAmount) {
      if (feeders[1].mineralAmount === 0 && feeders[0].mineralType !== reaction[0]) {
        return feeders[0]
      }
    }
    if (feeders[1].mineralAmount) {
      if (feeders[0].mineralAmount === 0 && feeders[1].mineralType !== reaction[1]) {
        return feeders[1]
      }
    }

    // If feeders can not produce a reaction start emptying them.
    // Once one is empty the above code will finish the other.
    if (feeders[0].mineralAmount > 0 && feeders[1].mineralAmount > 0) {
      if (!REACTIONS[feeders[0].mineralType][feeders[1].mineralType]) {
        return feeders[0]
      }
    }

    // Empty vats that don't have a boost that will work with the feeders, or that are filling up.
    const vats = creep.room.getVatLabs()
    let currentProduct = false
    if (feeders[0].mineralAmount > 0 && feeders[1].mineralAmount > 0) {
      currentProduct = REACTIONS[feeders[0].mineralType][feeders[1].mineralType]
    }
    const desiredProduct = REACTIONS[reaction[0]][reaction[1]]
    let target = false
    let amount = 150
    for (const vat of vats) {
      if (!vat.mineralType) {
        continue
      }
      if ((!currentProduct || vat.mineralType !== currentProduct) && vat.mineralType !== desiredProduct) {
        return vat
      }
      if (vat.mineralAmount > amount) {
        amount = vat.mineralAmount
        target = vat
      }
    }
    return target
  }

  emptyEnergy (creep) {
    // fill terminal with energy
    if (creep.room.terminal && creep.room.terminal.store[RESOURCE_ENERGY] < TERMINAL_ENERGY) {
      if (creep.pos.isNearTo(creep.room.terminal)) {
        const need = TERMINAL_ENERGY - creep.room.terminal.store[RESOURCE_ENERGY]
        const amount = need > creep.carry[RESOURCE_ENERGY] ? creep.carry[RESOURCE_ENERGY] : need
        creep.transfer(creep.room.terminal, RESOURCE_ENERGY, amount)
      } else {
        creep.travelTo(creep.room.terminal)
      }
      return
    }

    if (creep.room.structures[STRUCTURE_NUKER] && creep.room.isEconomyCapable('FILL_NUKER')) {
      const nuker = creep.room.structures[STRUCTURE_NUKER][0]
      if (nuker.energy < nuker.energyCapacity) {
        this.dumpEnergyToStructure(creep, nuker)
        return
      }
    }

    if (creep.room.structures[STRUCTURE_POWER_SPAWN] && creep.room.isEconomyCapable('FILL_POWER_SPAWN')) {
      const powerspawn = creep.room.structures[STRUCTURE_POWER_SPAWN][0]
      if (powerspawn.energy < powerspawn.energyCapacity) {
        this.dumpEnergyToStructure(creep, powerspawn)
        return
      }
    }

    // fill storage with energy
    this.dumpEnergyToStructure(creep, creep.room.storage)
  }

  dumpEnergyToStructure (creep, structure) {
    if (creep.pos.isNearTo(structure)) {
      creep.transfer(structure, RESOURCE_ENERGY)
    } else {
      creep.travelTo(structure)
    }
  }

  emptyResources (creep) {
    // Must be near storage to empty creep.
    if (!creep.pos.isNearTo(creep.room.storage)) {
      creep.travelTo(creep.room.storage)
      return
    }

    const resources = _.filter(Object.keys(creep.carry), a => a !== RESOURCE_ENERGY)
    if (resources.length < 1) {
      return false
    }
    const resource = resources[0]

    // If there's no other option just put it all in storage.
    if (!creep.room.terminal) {
      creep.transfer(creep.room.storage, resource)
      return true
    }

    // Intermediate boosts should stay in room.
    const resourceType = Mineral.getResourceType(resource)
    if (resourceType === 'tier1' || resourceType === 'tier2') {
      creep.transfer(creep.room.storage, resource)
      return true
    }

    // Put needed resources into storage and excess into terminal.
    const currentNeed = creep.room.storage.getResourceNeed(resource)
    if (currentNeed > 0) {
      const amount = creep.carry[resource] < currentNeed ? creep.carry[resource] : currentNeed
      creep.transfer(creep.room.storage, resource, amount)
    } else {
      if (creep.pos.isNearTo(creep.room.terminal)) {
        creep.transfer(creep.room.terminal, resource)
      } else {
        creep.travelTo(creep.room.terminal)
      }
    }
    return true
  }
}

module.exports = Factotum
<commit_msg>More factotum/lab fixes<commit_after>'use strict'

const MetaRole = require('roles_meta')

class Factotum extends MetaRole {
  constructor () {
    super()
    this.defaultEnergy = 800
  }

  getPriority (creep) {
    return PRIORITIES_CREEP_FACTOTUM
  }

  getBuild (room, options) {
    this.setBuildDefaults(room, options)
    let build = [MOVE, CARRY, CARRY, CARRY, CARRY]
    return Creep.buildFromTemplate(build, options.energy)
  }

  manageCreep (creep) {
    if (creep.ticksToLive < 50) {
      return creep.recycle()
    }

    if (creep.memory.labs) {
      if (creep.memory.labs === 'fill') {
        this.fillFeeders(creep)
      } else {
        this.emptyLabs(creep)
      }
      return
    }

    if (_.sum(creep.carry) > 0) {
      if (creep.carry[RESOURCE_ENERGY]) {
        this.emptyEnergy(creep)
      } else {
        this.emptyResources(creep)
      }
      return
    }

    const storage = creep.room.storage
    const terminal = creep.room.terminal

    // empty link
    const storageLink = storage.getLink()
    if (storageLink && storageLink.energy) {
      if (creep.pos.isNearTo(storageLink)) {
        creep.withdraw(storageLink, RESOURCE_ENERGY)
      } else {
        creep.travelTo(storageLink)
      }
      return
    }

    if (terminal && terminal.store[RESOURCE_ENERGY] > TERMINAL_ENERGY) {
      const overflow = terminal.store[RESOURCE_ENERGY] - TERMINAL_ENERGY
      const transferAmount = overflow < creep.carryCapacity ? overflow : creep.carryCapacity
      creep.withdraw(terminal, RESOURCE_ENERGY, transferAmount)
      return
    }

    // pick up dropped resources and creep renewal energy
    const suicideBooth = creep.room.getSuicideBooth()
    if (suicideBooth) {
      const resources = suicideBooth.lookFor(LOOK_RESOURCES)
      if (resources.length > 0) {
        creep.pickup(resources[0])
      }
    }

    if (terminal && storage) {
      // Does terminal have something storage needs?
      const terminalResources = _.shuffle(Object.keys(terminal.store))
      for (const resource of terminalResources) {
        if (resource === RESOURCE_ENERGY) {
          continue
        }
        const need = storage.getResourceNeed(resource)
        if (need > 0) {
          const amount = Math.min(need, creep.carryCapacity - creep.carry, terminal.store[resource])
          creep.withdraw(terminal, resource, amount)
          return
        }
      }

      // Does storage have terminal overflow?
      const storageResources = _.shuffle(Object.keys(storage.store))
      for (const resource of storageResources) {
        if (resource === RESOURCE_ENERGY) {
          continue
        }
        const need = storage.getResourceNeed(resource)
        if (need < 0) {
          const amount = Math.min((-need), creep.carryCapacity - creep.carry, terminal.store[resource])
          creep.withdraw(storage, resource, amount)
          return
        }
      }
    }

    creep.memory.labs = 'fill'
  }

  fillFeeders (creep) {
    const reaction = creep.room.getActiveReaction()
    const feeders = creep.room.getFeederLabs()
    if (feeders.length < 2) {
      delete creep.memory.labs
      delete creep.memory.filling
      return
    }

    if (feeders[0].mineralAmount > 0 && feeders[0].mineralType !== reaction[0]) {
      creep.memory.labs = 'empty'
      return
    }
    if (feeders[1].mineralAmount > 0 && feeders[1].mineralType !== reaction[1]) {
      creep.memory.labs = 'empty'
      return
    }

    if (_.sum(creep.carry) <= 0) {
      if (feeders[0].mineralAmount / feeders[0].mineralCapacity >= 0.5) {
        if (feeders[1].mineralAmount / feeders[1].mineralCapacity >= 0.5) {
          creep.memory.labs = 'empty'
        }
      }
    }

    const carrying = Object.keys(creep.carry)
    if (carrying.length > 3) {
      creep.memory.labs = 'empty'
      return
    }

    const individualMax = creep.carryCapacity / 2
    let reactionPrimaryAvailable = creep.room.storage.store[reaction[0]]
    if (creep.carry[reaction[0]]) {
      reactionPrimaryAvailable += creep.carry[reaction[0]]
    }
    let reactionSecondaryAvailable = creep.room.storage.store[reaction[1]]
    if (creep.carry[reaction[1]]) {
      reactionSecondaryAvailable += creep.carry[reaction[1]]
    }

    let primaryTargetAmount = Math.min(individualMax, reactionPrimaryAvailable, reactionSecondaryAvailable)
    if (feeders[0].mineralAmount / feeders[0].mineralCapacity >= 0.5) {
      primaryTargetAmount = 0
    }

    let secondaryTargetAmount = Math.min(individualMax, reactionPrimaryAvailable, reactionSecondaryAvailable)
    if (feeders[1].mineralAmount / feeders[1].mineralCapacity >= 0.5) {
      secondaryTargetAmount = 0
    }

    if (primaryTargetAmount <= 0 && secondaryTargetAmount <= 0) {
      creep.memory.labs = 'empty'
      return
    }

    if (creep.carry[carrying[0]] > primaryTargetAmount || creep.carry[carrying[1]] > secondaryTargetAmount) {
      creep.memory.labs = 'empty'
      return
    }

    if (creep.memory.filling) {
      if (creep.carry[reaction[0]] && feeders[0].canFill()) {
        if (creep.pos.isNearTo(feeders[0])) {
          creep.transfer(feeders[0], reaction[0])
        } else {
          creep.travelTo(feeders[0])
        }
      } else if (creep.carry[reaction[1]] && feeders[1].canFill()) {
        if (creep.pos.isNearTo(feeders[1])) {
          creep.transfer(feeders[1], reaction[1])
        } else {
          creep.travelTo(feeders[1])
        }
      } else {
        delete creep.memory.filling
        creep.memory.labs = 'empty'
      }
    } else {
      if (!creep.pos.isNearTo(creep.room.storage)) {
        creep.travelTo(creep.room.storage)
        return
      }

      if (!creep.carry[reaction[0]] || creep.carry[reaction[0]] < primaryTargetAmount) {
        const amount = creep.carry[reaction[0]] ? primaryTargetAmount - creep.carry[reaction[0]] : primaryTargetAmount
        creep.withdraw(creep.room.storage, reaction[0], Math.min(amount, creep.carryCapacity - _.sum(creep.carry)))
      } else if (!creep.carry[reaction[1]] || creep.carry[reaction[1]] < secondaryTargetAmount) {
        const amount = creep.carry[reaction[1]] ? secondaryTargetAmount - creep.carry[reaction[1]] : secondaryTargetAmount
        creep.withdraw(creep.room.storage, reaction[1], Math.min(amount, creep.carryCapacity - _.sum(creep.carry)))
      } else {
        creep.memory.filling = true
      }
    }
  }

  emptyLabs (creep) {
    if (creep.carryCapacity - _.sum(creep.carry) <= 0) {
      delete creep.memory.labs
      return
    }
    const lab = this.getLabToEmpty(creep)
    if (!lab) {
      delete creep.memory.labs
      return
    }
    if (creep.pos.isNearTo(lab)) {
      creep.withdraw(lab, lab.mineralType)
    } else {
      creep.travelTo(lab)
    }
  }

  getLabToEmpty (creep) {
    const feeders = creep.room.getFeederLabs()
    const reaction = creep.room.getActiveReaction()

    // If one feeder doesn't have the right mineral and the other is empty then empty the first.
    if (feeders[0].mineralAmount) {
      if (feeders[1].mineralAmount === 0 && feeders[0].mineralType !== reaction[0]) {
        return feeders[0]
      }
    }
    if (feeders[1].mineralAmount) {
      if (feeders[0].mineralAmount === 0 && feeders[1].mineralType !== reaction[1]) {
        return feeders[1]
      }
    }

    // If feeders can not produce a reaction start emptying them.
    // Once one is empty the above code will finish the other.
    if (feeders[0].mineralAmount > 0 && feeders[1].mineralAmount > 0) {
      if (!REACTIONS[feeders[0].mineralType][feeders[1].mineralType]) {
        return feeders[0]
      }
    }

    // Empty vats that don't have a boost that will work with the feeders, or that are filling up.
    const vats = creep.room.getVatLabs()
    let currentProduct = false
    if (feeders[0].mineralAmount > 0 && feeders[1].mineralAmount > 0) {
      currentProduct = REACTIONS[feeders[0].mineralType][feeders[1].mineralType]
    }
    const desiredProduct = REACTIONS[reaction[0]][reaction[1]]
    let target = false
    let amount = 150
    for (const vat of vats) {
      if (!vat.mineralType) {
        continue
      }
      if ((!currentProduct || vat.mineralType !== currentProduct) && vat.mineralType !== desiredProduct) {
        return vat
      }
      if (vat.mineralAmount > amount) {
        amount = vat.mineralAmount
        target = vat
      }
    }
    return target
  }

  emptyEnergy (creep) {
    // fill terminal with energy
    if (creep.room.terminal && creep.room.terminal.store[RESOURCE_ENERGY] < TERMINAL_ENERGY) {
      if (creep.pos.isNearTo(creep.room.terminal)) {
        const need = TERMINAL_ENERGY - creep.room.terminal.store[RESOURCE_ENERGY]
        const amount = need > creep.carry[RESOURCE_ENERGY] ? creep.carry[RESOURCE_ENERGY] : need
        creep.transfer(creep.room.terminal, RESOURCE_ENERGY, amount)
      } else {
        creep.travelTo(creep.room.terminal)
      }
      return
    }

    if (creep.room.structures[STRUCTURE_NUKER] && creep.room.isEconomyCapable('FILL_NUKER')) {
      const nuker = creep.room.structures[STRUCTURE_NUKER][0]
      if (nuker.energy < nuker.energyCapacity) {
        this.dumpEnergyToStructure(creep, nuker)
        return
      }
    }

    if (creep.room.structures[STRUCTURE_POWER_SPAWN] && creep.room.isEconomyCapable('FILL_POWER_SPAWN')) {
      const powerspawn = creep.room.structures[STRUCTURE_POWER_SPAWN][0]
      if (powerspawn.energy < powerspawn.energyCapacity) {
        this.dumpEnergyToStructure(creep, powerspawn)
        return
      }
    }

    // fill storage with energy
    this.dumpEnergyToStructure(creep, creep.room.storage)
  }

  dumpEnergyToStructure (creep, structure) {
    if (creep.pos.isNearTo(structure)) {
      creep.transfer(structure, RESOURCE_ENERGY)
    } else {
      creep.travelTo(structure)
    }
  }

  emptyResources (creep) {
    // Must be near storage to empty creep.
    if (!creep.pos.isNearTo(creep.room.storage)) {
      creep.travelTo(creep.room.storage)
      return
    }

    const resources = _.filter(Object.keys(creep.carry), a => a !== RESOURCE_ENERGY)
    if (resources.length < 1) {
      return false
    }
    const resource = resources[0]

    // If there's no other option just put it all in storage.
    if (!creep.room.terminal) {
      creep.transfer(creep.room.storage, resource)
      return true
    }

    // Intermediate boosts should stay in room.
    const resourceType = Mineral.getResourceType(resource)
    if (resourceType === 'tier1' || resourceType === 'tier2') {
      creep.transfer(creep.room.storage, resource)
      return true
    }

    // Put needed resources into storage and excess into terminal.
    const currentNeed = creep.room.storage.getResourceNeed(resource)
    if (currentNeed > 0) {
      const amount = creep.carry[resource] < currentNeed ? creep.carry[resource] : currentNeed
      creep.transfer(creep.room.storage, resource, amount)
    } else {
      if (creep.pos.isNearTo(creep.room.terminal)) {
        creep.transfer(creep.room.terminal, resource)
      } else {
        creep.travelTo(creep.room.terminal)
      }
    }
    return true
  }
}

module.exports = Factotum
<|endoftext|>


<commit_before>const request = require('request-promise').defaults({ jar: true });
const Extractor = require('../extractor');
const libxmljs = require("libxmljs");
const $ = require('cheerio');
const _ = require('lodash');
const moment = require("moment");

const pubmedEfetch = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed';
const returnMode = '&retmode=xml';
const abstractSource = 'PUBMED';

const pubmedExtractor = function (googleScholarEntry) {
  const uriOptions = {
    uri: googleScholarEntry.url,
    resolveWithFullResponse :true,
    simple: true
  };
  return request(googleScholarEntry.url)
    .then(html => {
      // get pmid from page so we can get actual article from pubmedEfetch
      // so we dont have to scrape
      try {
        const pmid = $('meta[name="citation_pmid" i]','head', html).prop('content');
        return pmid;
      } catch(err) {
        console.log(`could not get pmid for: ${googleScholarEntry.url}`);
        return null
      }
    })
  // get article from efetch
    .then(pmid => request(`${pubmedEfetch}&id=${pmid}${returnMode}`))
  // parse abstract
    .then(abstractXml => libxmljs.parseXml(abstractXml,{ noblanks: true }))
    .then(abstractXmlDoc => {
      const abstract = { };
      const abstractTitleTag = '//ArticleTitle';
      const abstractTextTag = '//AbstractText';
      const journalIssue = '//JournalIssue';
      const abstractPubDateTag = '//PubDate';

      abstract.title = abstractXmlDoc.get(abstractTitleTag).text();

      const abstractTextSections = abstractXmlDoc.find(abstractTextTag);

      // we must have at least one item in order to enter this block
      if (abstractTextSections[0]) {
        const abstractHasOneElement = (abstractTextSections.length === 1);
        const abstractHasNoCategory = !(abstractTextSections[0].attr('NlmCategory'));

        if (abstractHasOneElement && abstractHasNoCategory) {
          // if abstractText has one child and node and that node does
          // not have a category, then that node must have the abstract text
          // and abstract is not divided into sections. so we save that text as background
          abstract.background = abstractTextSections[0].text();
        } else {
          let sectionTitle = '';
          _.each(abstractTextSections, section => {
            sectionTitle = _.lowerCase(section.attr('NlmCategory').value());
            abstract[sectionTitle] = section.text();
          });
        }
      }

      const pubDateElement = abstractXmlDoc.get(abstractPubDateTag);
      const pubYear = pubDateElement.get('Year').text();

      try {
        const pubMonth = pubDateElement.get('Month').text();
        // save date as iso string courtesy of moment
        abstract.date = moment(`${pubMonth}/1/${pubYear}`, "MMM-DD-YYYY").toISOString();
      } catch(err) {
        // we most likely dont have a month so default to the jan 1st of the year
        abstract.date = moment(`1/1/${pubYear}`, "MMM-DD-YYYY").toISOString();
      }

      try {
        const journalIssueElement = abstractXmlDoc.get(journalIssue);
        abstract.journalName = journalIssueElement.get('Title').text();
        abstract.journalISOAbbreviation = journalIssueElement.get('ISOAbbreviation').text();
      } catch(err) {
        console.log('could not get journal name');
      }

      abstract.authors = googleScholarEntry.authors;
      abstract.citedCount = googleScholarEntry.citedCount;
      abstract.citedUrl = googleScholarEntry.citedUrl;
      abstract.link = googleScholarEntry.url;
      abstract.source = abstractSource;

      return abstract;
    })
}
const acceptedUrls = [
'ncbi.nlm.nih.gov'
];

module.exports = new Extractor(pubmedExtractor, { acceptedUrls });
<commit_msg>add check for article year<commit_after>const request = require('request-promise').defaults({ jar: true });
const Extractor = require('../extractor');
const libxmljs = require("libxmljs");
const $ = require('cheerio');
const _ = require('lodash');
const moment = require("moment");

const pubmedEfetch = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed';
const returnMode = '&retmode=xml';
const abstractSource = 'PUBMED';

const pubmedExtractor = function (googleScholarEntry) {
  const uriOptions = {
    uri: googleScholarEntry.url,
    resolveWithFullResponse :true,
    simple: true
  };
  return request(googleScholarEntry.url)
    .then(html => {
      // get pmid from page so we can get actual article from pubmedEfetch
      // so we dont have to scrape
      try {
        const pmid = $('meta[name="citation_pmid" i]','head', html).prop('content');
        return pmid;
      } catch(err) {
        console.log(`could not get pmid for: ${googleScholarEntry.url}`);
        return null
      }
    })
  // get article from efetch
    .then(pmid => request(`${pubmedEfetch}&id=${pmid}${returnMode}`))
  // parse abstract
    .then(abstractXml => libxmljs.parseXml(abstractXml,{ noblanks: true }))
    .then(abstractXmlDoc => {
      const abstract = { };
      const abstractTitleTag = '//ArticleTitle';
      const abstractTextTag = '//AbstractText';
      const journalIssue = '//JournalIssue';
      const abstractPubDateTag = '//PubDate';

      abstract.title = abstractXmlDoc.get(abstractTitleTag).text();

      const abstractTextSections = abstractXmlDoc.find(abstractTextTag);

      // we must have at least one item in order to enter this block
      if (abstractTextSections[0]) {
        const abstractHasOneElement = (abstractTextSections.length === 1);
        const abstractHasNoCategory = !(abstractTextSections[0].attr('NlmCategory'));

        if (abstractHasOneElement && abstractHasNoCategory) {
          // if abstractText has one child and node and that node does
          // not have a category, then that node must have the abstract text
          // and abstract is not divided into sections. so we save that text as background
          abstract.background = abstractTextSections[0].text();
        } else {
          let sectionTitle = '';
          _.each(abstractTextSections, section => {
            sectionTitle = _.lowerCase(section.attr('NlmCategory').value());
            abstract[sectionTitle] = section.text();
          });
        }
      }

      const pubDateElement = abstractXmlDoc.get(abstractPubDateTag);

      try {
        const pubYear = pubDateElement.get('Year').text();
        const pubMonth = pubDateElement.get('Month').text();
        // save date as iso string courtesy of moment
        abstract.date = moment(`${pubMonth}/1/${pubYear}`, "MMM-DD-YYYY").toISOString();
      } catch(err) {
        // we most likely dont have a month so default to the jan 1st of the year
        // abstract.date = moment(`1/1/${pubYear}`, "MMM-DD-YYYY").toISOString();
        console.log('could not accurately get artical date');
      }

      try {
        const journalIssueElement = abstractXmlDoc.get(journalIssue);
        abstract.journalName = journalIssueElement.get('Title').text();
        abstract.journalISOAbbreviation = journalIssueElement.get('ISOAbbreviation').text();
      } catch(err) {
        console.log('could not get journal name');
      }

      abstract.authors = googleScholarEntry.authors;
      abstract.citedCount = googleScholarEntry.citedCount;
      abstract.citedUrl = googleScholarEntry.citedUrl;
      abstract.link = googleScholarEntry.url;
      abstract.source = abstractSource;

      return abstract;
    })
}
const acceptedUrls = [
'ncbi.nlm.nih.gov'
];

module.exports = new Extractor(pubmedExtractor, { acceptedUrls });
<|endoftext|>


<commit_before>(function(root, undefined) {

		/**
		 * Quick reference to Array.prototype.splice
		 * for duplicating arrays (while removing the
		 * first parameter ala trigger)
		 * @type {Function}
		 */
	var _splicer = [].splice,

		/**
		 * Object merger
		 * @param {Objects}
		 * @return {Object}
		 */
		_extend = function() {
			var args = arguments,
				base = args[0],
				idx = 1, length = args.length,
				key, merger;
			for (; idx < length; idx += 1) {
				merger = args[idx];

				for (key in merger) {
					base[key] = merger[key];
				}
			}
		},

		/**
		 * Holds cached, parsed event keys by string
		 * @type {Object}
		 */
		_cache = {},

		/**
		 * Unique Id
		 * @type {Number}
		 */
		_id = 0,
		_uniqueId = function() {
			return _id++;
		},

		/**
		 * Holds cached, parsed event keys by string
		 * @type {Object}
		 */
		_NAME_REGEX = /(?:([\w-]+):)?([\w-]*)(?:.([\w-]+))?/,
		_parseConfig = function(eventname) {
			var match = _NAME_REGEX.exec(eventname);
			return {
				// [0] : the entire match, don't care!
				// [1] : handle
				handle:    (match[1] === undefined) ? '' : match[1],
				// [2] : event
				evt:       (match[2] === undefined) ? '' : match[2],
				// [3] : namespace
				namespace: (match[3] === undefined) ? '' : match[3]
			};
		};

	var Signal = function() {
		/**
		 * Holds active events by handle + event + namespace
		 * @type {Object}
		 */
		this._active = {};

		/**
		 * Holds inactive events by handle - lazy creation
		 * @type {Object}
		 */
		// this._inactive;

		/**
		 * Holds subscriptions - lazy creation
		 * @type {Object}
		 */
		// this._subscriptions;
	};

	/**
	 * Returns a new Signal instance
	 * @return {Signal}
	 */
	Signal.construct = function() {
		return new Signal();
	};

	/**
	 * Klass extend method
	 * @param  {Function} constructor
	 * @param  {Object} extension   prototype extension
	 * @return {Function} constructor
	 */
	Signal.extend = function(constructor, extension) {
		var hasConstructor = (typeof constructor === 'function');
		if (!hasConstructor) { extension = constructor; }

		var self = this,
			fn = function() {
				var ret = self.apply(this, arguments);
				if (hasConstructor) {
					ret = constructor.apply(this, arguments);
				}
				return ret;
			};

		// Add properties to the object
		_extend(fn, this);

		// Duplicate the prototype
		var NoOp = function() {};
		NoOp.prototype = this.prototype;
		fn.prototype = new NoOp();

		// Merge the prototypes
		_extend(fn.prototype, this.prototype, extension);
		fn.prototype.constructor = constructor || fn;

		return fn;
	};

	Signal.prototype = {

		constructor: Signal,

		subscribe: function(name, func) {
			this._subscriptions = this._subscriptions || {};

			var id = _uniqueId(),
				location = this._subscriptions[name] || (this._subscriptions[name] = []);

			func.__subid__ = id;
			location.push(func);

			return id;
		},

		unsubscribe: function(name, id) {
			this._subscriptions = this._subscriptions || {};

			var location = this._subscriptions[name];
			if (!location) { return; }

			var idx = 0, length = location.length;
			for (; idx < length; idx += 1) {
				if (location[idx].__subid__ === id) {
					location.splice(idx, 1);
					return true;
				}
			}

			return false;
		},

		dispatch: function() {
			this._subscriptions = this._subscriptions || {};

			var args = arguments,
				name = _splicer.call(args, 0, 1)[0],
				location = this._subscriptions[name] || (this._subscriptions[name] = []),
				idx = 0, length = location.length,
				func;
			for (; idx < length; idx++) {
				func = location[idx];
				if (func) { func.apply(null, args); }
			}
		},

		// Disable | Enable *************************************
		disable: function(handle) {
			this._inactive = this._inactive || {};
			this._inactive[handle] = this._inactive[handle] || {};
			this._inactive[handle] = _extend({}, this._active[handle]);
			delete this._active[handle];

			return this;
		},

		enable: function(handle) {
			this._inactive = this._inactive || {};
			this._active[handle] = this._active[handle] || {};
			this._active[handle] = _extend({}, this._inactive[handle]);
			delete this._inactive[handle];

			return this;
		},

		// On | Off ************************************************
		on: function(eventname, callback) {
			var eventConfig = _cache[eventname] || (_cache[eventname] = _parseConfig(eventname)),
				location = this._evtLookup(eventConfig);

			location.push(callback);

			return this;
		},
		bind: function() { this.on.apply(this, arguments); },

		off: function(eventname) {
			var eventConfig = _cache[eventname] || (_cache[eventname] = _parseConfig(eventname));

			if (eventConfig.evt === '') { // Removing a namespace

				var events = this._active[eventConfig.handle],
					eventName,
					namespaceName;
				for (eventName in events) {
					for (namespaceName in events[eventName]) {
						if (namespaceName === eventConfig.namespace) {
							this._active[eventConfig.handle][eventName][namespaceName].length = 0;
						}
					}
				}

			} else if (eventConfig.namespace !== '') { // Has a namespace
				this._active[eventConfig.handle][eventConfig.evt][eventConfig.namespace].length = 0;
			} else { // Does not have a namespace
				this._active[eventConfig.handle][eventConfig.evt] = { '': [] };
			}

			return this;
		},
		unbind: function() { this.off.apply(this, arguments); },

		once: function(eventname, callback) {
			var hasRan = false, memo;

			return this.on(eventname, function() {
				return function() {
					if (hasRan) { return memo; }
					hasRan = true;

					memo = callback.apply(this, arguments);
					callback = null;

					return memo;
				};
			});
		},

		// Trigger ************************************************
		trigger: function() {
			var args = arguments,
				eventname = _splicer.call(args, 0, 1)[0],
				cacheConfig = _cache[eventname];

			if (cacheConfig) {
				eventConfig = cacheConfig;
			} else {
				eventConfig = _cache[eventname] = _parseConfig(eventname);
			}

			var location = this._getEventLocation(eventConfig);

			if (eventConfig.namespace !== '') { // If there's a namespace, trigger only that array
				this._callEventArray(location, args);
			} else { // Else, trigger everything registered to the event
				var subSignal = this._active[eventConfig.handle][eventConfig.evt], key;
				for (key in subSignal) {
					this._callEventArray(subSignal[key], args);
				}
			}

			return this;
		},

		// ListenTo | StopListening ********************************
		listenTo: function(obj, eventname, callback) {
			obj.on(eventname, callback);
			return this;
		},
		stopListening: function(obj, eventname) {
			obj.off(eventname);
			return this;
		},

		_callEventArray: function(events, args) {
			args = args || [];

			var idx = 0, length = events.length,
				evt;
			for (; idx < length; idx += 1) {
				evt = events[idx];
				if (!evt) { continue; }
				if (evt.apply(null, args) === false) { return; }
			}
		},

		_getEventLocation: function(eventConfig, location) {
			location = location || this._active;

			var handle = location[eventConfig.handle] || (location[eventConfig.handle] = {}),
				evt = handle[eventConfig.evt] || (handle[eventConfig.evt] = {}),
				namespace = evt[eventConfig.namespace] || (evt[eventConfig.namespace] = []);

			return namespace;
		},

		toString: function() {
			return '[Signal]';
		}
	};

	// Create a pub/sub to expose Signal as
	// e.g. Signal.on(), Signal.trigger()
	var pubSub = new Signal();

	// Attach the Signal object as a property
	// of the exposed object so that new instances
	// can be constructed/extended
	// e.g. Signal.core.construct(), Signal.core.extend({})
	pubSub.core = Signal;

	// Expose
	root.Signal = pubSub;

}(this));<commit_msg>Rename private methods<commit_after>(function(root, undefined) {

		/**
		 * Quick reference to Array.prototype.splice
		 * for duplicating arrays (while removing the
		 * first parameter ala trigger)
		 * @type {Function}
		 */
	var _splicer = [].splice,

		/**
		 * Object merger
		 * @param {Objects}
		 * @return {Object}
		 */
		_extend = function() {
			var args = arguments,
				base = args[0],
				idx = 1, length = args.length,
				key, merger;
			for (; idx < length; idx += 1) {
				merger = args[idx];

				for (key in merger) {
					base[key] = merger[key];
				}
			}
		},

		/**
		 * Holds cached, parsed event keys by string
		 * @type {Object}
		 */
		_cache = {},

		/**
		 * Unique Id
		 * @type {Number}
		 */
		_id = 0,
		_uniqueId = function() {
			return _id++;
		},

		/**
		 * Holds cached, parsed event keys by string
		 * @type {Object}
		 */
		_NAME_REGEX = /(?:([\w-]+):)?([\w-]*)(?:.([\w-]+))?/,
		_parseConfig = function(eventname) {
			var match = _NAME_REGEX.exec(eventname);
			return {
				// [0] : the entire match, don't care!
				// [1] : handle
				handle:    (match[1] === undefined) ? '' : match[1],
				// [2] : event
				evt:       (match[2] === undefined) ? '' : match[2],
				// [3] : namespace
				namespace: (match[3] === undefined) ? '' : match[3]
			};
		};

	var Signal = function() {
		/**
		 * Holds active events by handle + event + namespace
		 * @type {Object}
		 */
		this._active = {};

		/**
		 * Holds inactive events by handle - lazy creation
		 * @type {Object}
		 */
		// this._inactive;

		/**
		 * Holds subscriptions - lazy creation
		 * @type {Object}
		 */
		// this._subscriptions;
	};

	/**
	 * Returns a new Signal instance
	 * @return {Signal}
	 */
	Signal.construct = function() {
		return new Signal();
	};

	/**
	 * Klass extend method
	 * @param  {Function} constructor
	 * @param  {Object} extension   prototype extension
	 * @return {Function} constructor
	 */
	Signal.extend = function(constructor, extension) {
		var hasConstructor = (typeof constructor === 'function');
		if (!hasConstructor) { extension = constructor; }

		var self = this,
			fn = function() {
				var ret = self.apply(this, arguments);
				if (hasConstructor) {
					ret = constructor.apply(this, arguments);
				}
				return ret;
			};

		// Add properties to the object
		_extend(fn, this);

		// Duplicate the prototype
		var NoOp = function() {};
		NoOp.prototype = this.prototype;
		fn.prototype = new NoOp();

		// Merge the prototypes
		_extend(fn.prototype, this.prototype, extension);
		fn.prototype.constructor = constructor || fn;

		return fn;
	};

	Signal.prototype = {

		constructor: Signal,

		subscribe: function(name, func) {
			this._subscriptions = this._subscriptions || {};

			var id = _uniqueId(),
				location = this._subscriptions[name] || (this._subscriptions[name] = []);

			func.__subid__ = id;
			location.push(func);

			return id;
		},

		unsubscribe: function(name, id) {
			this._subscriptions = this._subscriptions || {};

			var location = this._subscriptions[name];
			if (!location) { return; }

			var idx = 0, length = location.length;
			for (; idx < length; idx += 1) {
				if (location[idx].__subid__ === id) {
					location.splice(idx, 1);
					return true;
				}
			}

			return false;
		},

		dispatch: function() {
			this._subscriptions = this._subscriptions || {};

			var args = arguments,
				name = _splicer.call(args, 0, 1)[0],
				location = this._subscriptions[name] || (this._subscriptions[name] = []),
				idx = 0, length = location.length,
				func;
			for (; idx < length; idx++) {
				func = location[idx];
				if (func) { func.apply(null, args); }
			}
		},

		// Disable | Enable *************************************
		disable: function(handle) {
			this._inactive = this._inactive || {};
			this._inactive[handle] = this._inactive[handle] || {};
			this._inactive[handle] = _extend({}, this._active[handle]);
			delete this._active[handle];

			return this;
		},

		enable: function(handle) {
			this._inactive = this._inactive || {};
			this._active[handle] = this._active[handle] || {};
			this._active[handle] = _extend({}, this._inactive[handle]);
			delete this._inactive[handle];

			return this;
		},

		// On | Off ************************************************
		on: function(eventname, callback) {
			var eventConfig = _cache[eventname] || (_cache[eventname] = _parseConfig(eventname)),
				location = this._evtLookup(eventConfig);

			location.push(callback);

			return this;
		},
		bind: function() { this.on.apply(this, arguments); },

		off: function(eventname) {
			var eventConfig = _cache[eventname] || (_cache[eventname] = _parseConfig(eventname));

			if (eventConfig.evt === '') { // Removing a namespace

				var events = this._active[eventConfig.handle],
					eventName,
					namespaceName;
				for (eventName in events) {
					for (namespaceName in events[eventName]) {
						if (namespaceName === eventConfig.namespace) {
							this._active[eventConfig.handle][eventName][namespaceName].length = 0;
						}
					}
				}

			} else if (eventConfig.namespace !== '') { // Has a namespace
				this._active[eventConfig.handle][eventConfig.evt][eventConfig.namespace].length = 0;
			} else { // Does not have a namespace
				this._active[eventConfig.handle][eventConfig.evt] = { '': [] };
			}

			return this;
		},
		unbind: function() { this.off.apply(this, arguments); },

		once: function(eventname, callback) {
			var hasRan = false, memo;

			return this.on(eventname, function() {
				return function() {
					if (hasRan) { return memo; }
					hasRan = true;

					memo = callback.apply(this, arguments);
					callback = null;

					return memo;
				};
			});
		},

		// Trigger ************************************************
		trigger: function() {
			var args = arguments,
				eventname = _splicer.call(args, 0, 1)[0],
				cacheConfig = _cache[eventname];

			if (cacheConfig) {
				eventConfig = cacheConfig;
			} else {
				eventConfig = _cache[eventname] = _parseConfig(eventname);
			}

			var location = this._evtLookup(eventConfig);

			if (eventConfig.namespace !== '') { // If there's a namespace, trigger only that array
				this._callEvts(location, args);
			} else { // Else, trigger everything registered to the event
				var subSignal = this._active[eventConfig.handle][eventConfig.evt],
					key;
				for (key in subSignal) {
					this._callEvts(subSignal[key], args);
				}
			}

			return this;
		},

		// ListenTo | StopListening ********************************
		listenTo: function(obj, eventname, callback) {
			obj.on(eventname, callback);
			return this;
		},
		stopListening: function(obj, eventname) {
			obj.off(eventname);
			return this;
		},

		// Private *************************************************
		_callEvts: function(events, args) {
			args = args || [];

			var idx = 0, length = events.length,
				evt;
			for (; idx < length; idx += 1) {
				evt = events[idx];
				if (!evt) { continue; }
				if (evt.apply(null, args) === false) { return; }
			}
		},

		_evtLookup: function(eventConfig, location) {
			location = location || this._active;

			var handle    = location[eventConfig.handle] || (location[eventConfig.handle] = {}),
				evt       = handle[eventConfig.evt]      || (handle[eventConfig.evt]      = {}),
				namespace = evt[eventConfig.namespace]   || (evt[eventConfig.namespace]   = []);

			return namespace;
		},

		toString: function() {
			return '[Signal]';
		}
	};

	// Create a pub/sub to expose Signal as
	// e.g. Signal.on(), Signal.trigger()
	var pubSub = new Signal();

	// Attach the Signal object as a property
	// of the exposed object so that new instances
	// can be constructed/extended
	// e.g. Signal.core.construct(), Signal.core.extend({})
	pubSub.core = Signal;

	// Expose
	root.Signal = pubSub;

}(this));<|endoftext|>