text:
local launch, main = ...

local pairs = pairs
local ipairs = ipairs
local t_insert = table.insert
local m_min = math.min
local m_max = math.max
local m_floor = math.floor
local m_abs = math.abs
local s_format = string.format

local normalBanditDropList = {
	{ label = "Passive point", banditId = "None" },
	{ label = "Oak (Life)", banditId = "Oak" },
	{ label = "Kraityn (Resists)", banditId = "Kraityn" },
	{ label = "Alira (Mana)", banditId = "Alira" },
}
local cruelBanditDropList = {
	{ label = "Passive point", banditId = "None" },
	{ label = "Oak (Endurance)", banditId = "Oak" },
	{ label = "Kraityn (Frenzy)", banditId = "Kraityn" },
	{ label = "Alira (Power)", banditId = "Alira" },
}
local mercilessBanditDropList = {
	{ label = "Passive point", banditId = "None" },
	{ label = "Oak (Phys Dmg)", banditId = "Oak" },
	{ label = "Kraityn (Att. Speed)", banditId = "Kraityn" },
	{ label = "Alira (Cast Speed)", banditId = "Alira" },
}
local fooBanditDropList = {
	{ label = "2 Passive Points", banditId = "None" },
	{ label = "Oak (Life Regen, Phys.Dmg. Reduction, Phys.Dmg)", banditId = "Oak" },
	{ label = "Kraityn (Attack/Cast Speed, Attack Dodge, Move Speed)", banditId = "Kraityn" },
	{ label = "Alira (Mana Regen, Crit Multiplier, Resists)", banditId = "Alira" },
}

local buildMode = common.New("ControlHost")

function buildMode:Init(dbFileName, buildName, buildXML, targetVersion)
	self.dbFileName = dbFileName
	self.buildName = buildName
	if dbFileName then
		self.dbFileSubPath = self.dbFileName:sub(#main.buildPath + 1, -#self.buildName - 5)
	else
		self.dbFileSubPath = main.modes.LIST.subPath or ""
	end
	if not buildName then
		main:SetMode("LIST")
	end

	if not dbFileName and not targetVersion and not buildXML then
		targetVersion = liveTargetVersion
		--self.targetVersion = nil
		--self:OpenTargetVersionPopup(true)
		--return
	end

	self.abortSave = true

	wipeTable(self.controls)

	local miscTooltip = common.New("Tooltip")

	-- Controls: top bar, left side
	self.anchorTopBarLeft = common.New("Control", nil, 4, 4, 0, 20)
	self.controls.back = common.New("ButtonControl", {"LEFT",self.anchorTopBarLeft,"RIGHT"}, 0, 0, 60, 20, "<< Back", function()
		if self.unsaved then
			self:OpenSavePopup("LIST")
		else
			self:CloseBuild()
		end
	end)
	self.controls.buildName = common.New("Control", {"LEFT",self.controls.back,"RIGHT"}, 8, 0, 0, 20)
	self.controls.buildName.width = function(control)
		local limit = self.anchorTopBarRight:GetPos() - 98 - 40 - self.controls.back:GetSize() - self.controls.save:GetSize() - self.controls.saveAs:GetSize()
		local bnw = DrawStringWidth(16, "VAR", self.buildName)
		self.strWidth = m_min(bnw, limit)
		self.strLimited = bnw > limit
		return self.strWidth + 98
	end
	self.controls.buildName.Draw = function(control)
		local x, y = control:GetPos()
		local width, height = control:GetSize()
		SetDrawColor(0.5, 0.5, 0.5)
		DrawImage(nil, x + 91, y, self.strWidth + 6, 20)
		SetDrawColor(0, 0, 0)
		DrawImage(nil, x + 92, y + 1, self.strWidth + 4, 18)
		SetDrawColor(1, 1, 1)
		SetViewport(x, y + 2, self.strWidth + 94, 16)
		DrawString(0, 0, "LEFT", 16, "VAR", "Current build:  "..self.buildName)
		SetViewport()
		if control:IsMouseInBounds() then
			SetDrawLayer(nil, 10)
			miscTooltip:Clear()
			if self.dbFileSubPath and self.dbFileSubPath ~= "" then
				miscTooltip:AddLine(16, self.dbFileSubPath..self.buildName)
			elseif self.strLimited then
				miscTooltip:AddLine(16, self.buildName)
			end
			miscTooltip:Draw(x, y, width, height, main.viewPort)
			SetDrawLayer(nil, 0)
		end
	end
	self.controls.save = common.New("ButtonControl", {"LEFT",self.controls.buildName,"RIGHT"}, 8, 0, 50, 20, "Save", function()
		self:SaveDBFile()
	end)
	self.controls.save.enabled = function()
		return not self.dbFileName or self.unsaved
	end
	self.controls.saveAs = common.New("ButtonControl", {"LEFT",self.controls.save,"RIGHT"}, 8, 0, 70, 20, "Save As", function()
		self:OpenSaveAsPopup()
	end)
	self.controls.saveAs.enabled = function()
		return self.dbFileName
	end

	-- Controls: top bar, right side
	self.anchorTopBarRight = common.New("Control", nil, function() return main.screenW / 2 + 6 end, 4, 0, 20)
	self.controls.pointDisplay = common.New("Control", {"LEFT",self.anchorTopBarRight,"RIGHT"}, -12, 0, 0, 20)
	self.controls.pointDisplay.x = function(control)
		local width, height = control:GetSize()
		if self.controls.saveAs:GetPos() + self.controls.saveAs:GetSize() < self.anchorTopBarRight:GetPos() - width - 16 then
			return -12 - width
		else
			return 0
		end
	end
	self.controls.pointDisplay.width = function(control)
		local used, ascUsed = self.spec:CountAllocNodes()
		local usedMax = 99 + (self.targetVersion == "2_6" and 21 or 22) + (self.calcsTab.mainOutput.ExtraPoints or 0)
		local ascMax = 8
		control.str = string.format("%s%3d / %3d   %s%d / %d", used > usedMax and "^1" or "^7", used, usedMax, ascUsed > ascMax and "^1" or "^7", ascUsed, ascMax)
		control.req = "Required level: "..m_max(1, (100 + used - usedMax))
		return DrawStringWidth(16, "FIXED", control.str) + 8
	end
	self.controls.pointDisplay.Draw = function(control)
		local x, y = control:GetPos()
		local width, height = control:GetSize()
		SetDrawColor(1, 1, 1)
		DrawImage(nil, x, y, width, height)
		SetDrawColor(0, 0, 0)
		DrawImage(nil, x + 1, y + 1, width - 2, height - 2)
		SetDrawColor(1, 1, 1)
		DrawString(x + 4, y + 2, "LEFT", 16, "FIXED", control.str)
		if control:IsMouseInBounds() then
			SetDrawLayer(nil, 10)
			miscTooltip:Clear()
			miscTooltip:AddLine(16, control.req)
			miscTooltip:Draw(x, y, width, height, main.viewPort)
			SetDrawLayer(nil, 0)
		end
	end
	self.controls.characterLevel = common.New("EditControl", {"LEFT",self.controls.pointDisplay,"RIGHT"}, 12, 0, 106, 20, "", "Level", "%D", 3, function(buf)
		self.characterLevel = m_min(tonumber(buf) or 1, 100)
		self.modFlag = true
		self.buildFlag = true
	end)
	self.controls.characterLevel.tooltipFunc = function(tooltip)
		if tooltip:CheckForUpdate(self.characterLevel) then
			tooltip:AddLine(16, "Experience multiplier:")
			local playerLevel = self.characterLevel
			local safeZone = 3 + m_floor(playerLevel / 16)
			for level, expLevel in ipairs(self.data.monsterExperienceLevelMap) do
				local diff = m_abs(playerLevel - expLevel) - safeZone
				local mult
				if diff <= 0 then
					mult = 1
				else
					mult = ((playerLevel + 5) / (playerLevel + 5 + diff ^ 2.5)) ^ 1.5
				end
				if playerLevel >= 95 then
					mult = mult * (1 / (1 + 0.1 * (playerLevel - 94)))
				end
				if mult > 0.01 then
					local line = level
					if level >= 68 then
						line = line .. string.format(" (Tier %d)", level - 67)
					end
					line = line .. string.format(": %.1f%%", mult * 100)
					tooltip:AddLine(14, line)
				end
			end
		end
	end
	self.controls.classDrop = common.New("DropDownControl", {"LEFT",self.controls.characterLevel,"RIGHT"}, 8, 0, 100, 20, nil, function(index, value)
		if value.classId ~= self.spec.curClassId then
			if self.spec:CountAllocNodes() == 0 or self.spec:IsClassConnected(value.classId) then
				self.spec:SelectClass(value.classId)
				self.spec:AddUndoState()
				self.buildFlag = true
			else
				main:OpenConfirmPopup("Class Change", "Changing class to "..value.label.." will reset your passive tree.\nThis can be avoided by connecting one of the "..value.label.." starting nodes to your tree.", "Continue", function()
					self.spec:SelectClass(value.classId)
					self.spec:AddUndoState()
					self.buildFlag = true
				end)
			end
		end
	end)
	self.controls.ascendDrop = common.New("DropDownControl", {"LEFT",self.controls.classDrop,"RIGHT"}, 8, 0, 120, 20, nil, function(index, value)
		self.spec:SelectAscendClass(value.ascendClassId)
		self.spec:AddUndoState()
		self.buildFlag = true
	end)

	-- List of display stats
	-- This defines the stats in the side bar, and also which stats show in node/item comparisons
	-- This may be user-customisable in the future
	self.displayStats = {
		{ stat = "ActiveMinionLimit", label = "Active Minion Limit", fmt = "d" },
		{ stat = "AverageHit", label = "Average Hit", fmt = ".1f", compPercent = true },
		{ stat = "AverageDamage", label = "Average Damage", fmt = ".1f", compPercent = true, flag = "attack" },
		{ stat = "Speed", label = "Attack Rate", fmt = ".2f", compPercent = true, flag = "attack" },
		{ stat = "Speed", label = "Cast Rate", fmt = ".2f", compPercent = true, flag = "spell" },
		{ stat = "HitSpeed", label = "Hit Rate", fmt = ".2f" },
		{ stat = "PreEffectiveCritChance", label = "Crit Chance", fmt = ".2f%%" },
		{ stat = "CritChance", label = "Effective Crit Chance", fmt = ".2f%%", condFunc = function(v,o) return v ~= o.PreEffectiveCritChance end },
		{ stat = "CritMultiplier", label = "Crit Multiplier", fmt = "d%%", pc = true, condFunc = function(v,o) return (o.CritChance or 0) > 0 end },
		{ stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", flag = "attack" },
		{ stat = "TotalDPS", label = "Total DPS", fmt = ".1f", compPercent = true, flag = "notAverage" },
		{ stat = "TotalDot", label = "DoT DPS", fmt = ".1f", compPercent = true },
		{ stat = "BleedDPS", label = "Bleed DPS", fmt = ".1f", compPercent = true },
		{ stat = "IgniteDPS", label = "Ignite DPS", fmt = ".1f", compPercent = true },
		{ stat = "IgniteDamage", label = "Total Damage per Ignite", fmt = ".1f", compPercent = true },
		{ stat = "WithIgniteDPS", label = "Total DPS inc. Ignite", fmt = ".1f", compPercent = true },
		{ stat = "WithIgniteAverageDamage", label = "Average Dmg. inc. Ignite", fmt = ".1f", compPercent = true },
		{ stat = "PoisonDPS", label = "Poison DPS", fmt = ".1f", compPercent = true },
		{ stat = "PoisonDamage", label = "Total Damage per Poison", fmt = ".1f", compPercent = true },
		{ stat = "WithPoisonDPS", label = "Total DPS inc. Poison", fmt = ".1f", compPercent = true, condFunc = function(v,o) return v ~= o.TotalDPS end },
		{ stat = "WithPoisonAverageDamage", label = "Average Dmg. inc. Poison", fmt = ".1f", compPercent = true, condFunc = function(v,o) return v ~= o.AverageDamage end },
		{ stat = "DecayDPS", label = "Decay DPS", fmt = ".1f", compPercent = true },
		{ stat = "Cooldown", label = "Skill Cooldown", fmt = ".2fs", lowerIsBetter = true },
		{ stat = "AreaOfEffectRadius", label = "AoE Radius", fmt = "d" },
		{ stat = "ManaCost", label = "Mana Cost", fmt = "d", compPercent = true, lowerIsBetter = true, condFunc = function() return true end },
		{ },
		{ stat = "Str", label = "Strength", fmt = "d" },
		{ stat = "ReqStr", label = "Strength Required", fmt = "d", lowerIsBetter = true, condFunc = function(v,o) return v > o.Str end },
		{ stat = "Dex", label = "Dexterity", fmt = "d" },
		{ stat = "ReqDex", label = "Dexterity Required", fmt = "d", lowerIsBetter = true, condFunc = function(v,o) return v > o.Dex end },
		{ stat = "Int", label = "Intelligence", fmt = "d" },
		{ stat = "ReqInt", label = "Intelligence Required", fmt = "d", lowerIsBetter = true, condFunc = function(v,o) return v > o.Int end },
		{ },
		{ stat = "Life", label = "Total Life", fmt = "d", compPercent = true },
		{ stat = "Spec:LifeInc", label = "%Inc Life from Tree", fmt = "d%%", condFunc = function(v,o) return v > 0 and o.Life > 1 end },
		{ stat = "LifeUnreserved", label = "Unreserved Life", fmt = "d", condFunc = function(v,o) return v < o.Life end, compPercent = true },
		{ stat = "LifeUnreservedPercent", label = "Unreserved Life", fmt = "d%%", condFunc = function(v,o) return v < 100 end },
		{ stat = "LifeRegen", label = "Life Regen", fmt = ".1f" },
		{ stat = "LifeLeechGainRate", label = "Life Leech/On Hit Rate", fmt = ".1f", compPercent = true },
		{ stat = "LifeLeechGainPerHit", label = "Life Leech/Gain per Hit", fmt = ".1f", compPercent = true },
		{ },
		{ stat = "Mana", label = "Total Mana", fmt = "d", compPercent = true },
		{ stat = "Spec:ManaInc", label = "%Inc Mana from Tree", fmt = "d%%" },
		{ stat = "ManaUnreserved", label = "Unreserved Mana", fmt = "d", condFunc = function(v,o) return v < o.Mana end, compPercent = true },
		{ stat = "ManaUnreservedPercent", label = "Unreserved Mana", fmt = "d%%", condFunc = function(v,o) return v < 100 end },
		{ stat = "ManaRegen", label = "Mana Regen", fmt = ".1f" },
		{ stat = "ManaLeechGainRate", label = "Mana Leech/On Hit Rate", fmt = ".1f", compPercent = true },
		{ stat = "ManaLeechGainPerHit", label = "Mana Leech/Gain per Hit", fmt = ".1f", compPercent = true },
		{ },
		{ stat = "TotalDegen", label = "Total Degen", fmt = ".1f", lowerIsBetter = true },
		{ stat = "NetRegen", label = "Net Regen", fmt = "+.1f" },
		{ stat = "NetLifeRegen", label = "Net Life Regen", fmt = "+.1f" },
		{ stat = "NetManaRegen", label = "Net Mana Regen", fmt = "+.1f" },
		{ },
		{ stat = "EnergyShield", label = "Energy Shield", fmt = "d", compPercent = true },
		{ stat = "Spec:EnergyShieldInc", label = "%Inc ES from Tree", fmt = "d%%" },
		{ stat = "EnergyShieldRegen", label = "Energy Shield Regen", fmt = ".1f" },
		{ stat = "EnergyShieldLeechGainRate", label = "ES Leech/On Hit Rate", fmt = ".1f", compPercent = true },
		{ stat = "EnergyShieldLeechGainPerHit", label = "ES Leech/Gain per Hit", fmt = ".1f", compPercent = true },
		{ stat = "Evasion", label = "Evasion rating", fmt = "d", compPercent = true },
		{ stat = "Spec:EvasionInc", label = "%Inc Evasion from Tree", fmt = "d%%" },
		{ stat = "MeleeEvadeChance", label = "Evade Chance", fmt = "d%%", condFunc = function(v,o) return v > 0 and o.MeleeEvadeChance == o.ProjectileEvadeChance end },
		{ stat = "MeleeEvadeChance", label = "Melee Evade Chance", fmt = "d%%", condFunc = function(v,o) return v > 0 and o.MeleeEvadeChance ~= o.ProjectileEvadeChance end },
		{ stat = "ProjectileEvadeChance", label = "Projectile Evade Chance", fmt = "d%%", condFunc = function(v,o) return v > 0 and o.MeleeEvadeChance ~= o.ProjectileEvadeChance end },
		{ stat = "Armour", label = "Armour", fmt = "d", compPercent = true },
		{ stat = "Spec:ArmourInc", label = "%Inc Armour from Tree", fmt = "d%%" },
		{ stat = "PhysicalDamageReduction", label = "Phys. Damage Reduction", fmt = "d%%" },
		{ stat = "MovementSpeedMod", label = "Movement Speed Modifier", fmt = "+d%%", mod = true },
		{ stat = "BlockChance", label = "Block Chance", fmt = "d%%" },
		{ stat = "SpellBlockChance", label = "Spell Block Chance", fmt = "d%%" },
		{ stat = "AttackDodgeChance", label = "Attack Dodge Chance", fmt = "d%%" },
		{ stat = "SpellDodgeChance", label = "Spell Dodge Chance", fmt = "d%%" },
		{ },
		{ stat = "FireResist", label = "Fire Resistance", fmt = "d%%", condFunc = function() return true end },
		{ stat = "ColdResist", label = "Cold Resistance", fmt = "d%%", condFunc = function() return true end },
		{ stat = "LightningResist", label = "Lightning Resistance", fmt = "d%%", condFunc = function() return true end },
		{ stat = "ChaosResist", label = "Chaos Resistance", fmt = "d%%", condFunc = function() return true end },
		{ stat = "FireResistOverCap", label = "Fire Res. Over Max", fmt = "d%%" },
		{ stat = "ColdResistOverCap", label = "Cold Res. Over Max", fmt = "d%%" },
		{ stat = "LightningResistOverCap", label = "Lightning Res. Over Max", fmt = "d%%" },
		{ stat = "ChaosResistOverCap", label = "Chaos Res. Over Max", fmt = "d%%" },
	}
	self.minionDisplayStats = {
		{ stat = "AverageDamage", label = "Average Damage", fmt = ".1f", compPercent = true },
		{ stat = "Speed", label = "Attack/Cast Rate", fmt = ".2f", compPercent = true },
		{ stat = "HitSpeed", label = "Hit Rate", fmt = ".2f" },
		{ stat = "TotalDPS", label = "Total DPS", fmt = ".1f", compPercent = true },
		{ stat = "TotalDot", label = "DoT DPS", fmt = ".1f", compPercent = true },
		{ stat = "BleedDPS", label = "Bleed DPS", fmt = ".1f", compPercent = true },
		{ stat = "IgniteDPS", label = "Ignite DPS", fmt = ".1f", compPercent = true },
		{ stat = "WithPoisonDPS", label = "DPS inc. Poison", fmt = ".1f", compPercent = true },
		{ stat = "DecayDPS", label = "Decay DPS", fmt = ".1f", compPercent = true },
		{ stat = "Cooldown", label = "Skill Cooldown", fmt = ".2fs", lowerIsBetter = true },
		{ stat = "Life", label = "Total Life", fmt = ".1f", compPercent = true },
		{ stat = "LifeRegen", label = "Life Regen", fmt = ".1f" },
		{ stat = "LifeLeechGainRate", label = "Life Leech/On Hit Rate", fmt = ".1f", compPercent = true },
	}

	self.viewMode = "TREE"

	self.targetVersion = defaultTargetVersion
	self.characterLevel = 1
	self.controls.characterLevel:SetText(tostring(self.characterLevel))
	self.banditNormal = "None"
	self.banditCruel = "None"
	self.banditMerciless = "None"
	self.spectreList = { }

	-- Load build file
	self.xmlSectionList = { }
	if buildXML then
		if self:LoadDB(buildXML, "Unnamed build") then
			self:CloseBuild()
			return
		end
		self.modFlag = true
	else
		if self:LoadDBFile() then
			self:CloseBuild()
			return
		end
		self.modFlag = false
	end

	if targetVersion then
		self.targetVersion = targetVersion
	end

	if buildName == "~~temp~~" then
		-- Remove temporary build file
		os.remove(self.dbFileName)
		self.buildName = "Unnamed build"
		self.dbFileName = false
		self.dbFileSubPath = nil
		self.modFlag = true
	end

	-- Controls: Side bar
	self.anchorSideBar = common.New("Control", nil, 4, 36, 0, 0)
	self.controls.modeImport = common.New("ButtonControl", {"TOPLEFT",self.anchorSideBar,"TOPLEFT"}, 0, 0, 134, 20, "Import/Export Build", function()
		self.viewMode = "IMPORT"
	end)
	self.controls.modeImport.locked = function() return self.viewMode == "IMPORT" end
	self.controls.modeNotes = common.New("ButtonControl", {"LEFT",self.controls.modeImport,"RIGHT"}, 4, 0, 58, 20, "Notes", function()
		self.viewMode = "NOTES"
	end)
	self.controls.modeNotes.locked = function() return self.viewMode == "NOTES" end
	self.controls.modeConfig = common.New("ButtonControl", {"TOPRIGHT",self.anchorSideBar,"TOPLEFT"}, 300, 0, 100, 20, "Configuration", function()
		self.viewMode = "CONFIG"
	end)
	self.controls.modeConfig.locked = function() return self.viewMode == "CONFIG" end
	self.controls.modeTree = common.New("ButtonControl", {"TOPLEFT",self.anchorSideBar,"TOPLEFT"}, 0, 26, 72, 20, "Tree", function()
		self.viewMode = "TREE"
	end)
	self.controls.modeTree.locked = function() return self.viewMode == "TREE" end
	self.controls.modeSkills = common.New("ButtonControl", {"LEFT",self.controls.modeTree,"RIGHT"}, 4, 0, 72, 20, "Skills", function()
		self.viewMode = "SKILLS"
	end)
	self.controls.modeSkills.locked = function() return self.viewMode == "SKILLS" end
	self.controls.modeItems = common.New("ButtonControl", {"LEFT",self.controls.modeSkills,"RIGHT"}, 4, 0, 72, 20, "Items", function()
		self.viewMode = "ITEMS"
	end)
	self.controls.modeItems.locked = function() return self.viewMode == "ITEMS" end
	self.controls.modeCalcs = common.New("ButtonControl", {"LEFT",self.controls.modeItems,"RIGHT"}, 4, 0, 72, 20, "Calcs", function()
		self.viewMode = "CALCS"
	end)
	self.controls.modeCalcs.locked = function() return self.viewMode == "CALCS" end
	if self.targetVersion == "2_6" then
		self.controls.banditNormal = common.New("DropDownControl", {"TOPLEFT",self.anchorSideBar,"TOPLEFT"}, 0, 70, 100, 16, normalBanditDropList, function(index, value)
			self.banditNormal = value.banditId
			self.modFlag = true
			self.buildFlag = true
		end)
		self.controls.banditNormalLabel = common.New("LabelControl", {"BOTTOMLEFT",self.controls.banditNormal,"TOPLEFT"}, 0, 0, 0, 14, "^7Normal Bandit:")
		self.controls.banditCruel = common.New("DropDownControl", {"LEFT",self.controls.banditNormal,"RIGHT"}, 0, 0, 100, 16, mercilessBanditDropList, function(index, value)
			self.banditCruel = value.banditId
			self.modFlag = true
			self.buildFlag = true
		end)
		self.controls.banditCruelLabel = common.New("LabelControl", {"BOTTOMLEFT",self.controls.banditCruel,"TOPLEFT"}, 0, 0, 0, 14, "^7Cruel Bandit:")
		self.controls.banditMerciless = common.New("DropDownControl", {"LEFT",self.controls.banditCruel,"RIGHT"}, 0, 0, 100, 16, cruelBanditDropList, function(index, value)
			self.banditMerciless = value.banditId
			self.modFlag = true
			self.buildFlag = true
		end)
		self.controls.banditMercilessLabel = common.New("LabelControl", {"BOTTOMLEFT",self.controls.banditMerciless,"TOPLEFT"}, 0, 0, 0, 14, "^7Merciless Bandit:")
	else
		self.controls.bandit = common.New("DropDownControl", {"TOPLEFT",self.anchorSideBar,"TOPLEFT"}, 0, 70, 300, 16, fooBanditDropList, function(index, value)
			self.bandit = value.banditId
			self.modFlag = true
			self.buildFlag = true
		end)
		self.controls.banditLabel = common.New("LabelControl", {"BOTTOMLEFT",self.controls.bandit,"TOPLEFT"}, 0, 0, 0, 14, "^7Bandit:")
	end
	self.controls.mainSkillLabel = common.New("LabelControl", {"TOPLEFT",self.anchorSideBar,"TOPLEFT"}, 0, 95, 300, 16, "^7Main Skill:")
	self.controls.mainSocketGroup = common.New("DropDownControl", {"TOPLEFT",self.controls.mainSkillLabel,"BOTTOMLEFT"}, 0, 2, 300, 16, nil, function(index, value)
		self.mainSocketGroup = index
		self.modFlag = true
		self.buildFlag = true
	end)
	self.controls.mainSocketGroup.tooltipFunc = function(tooltip, mode, index, value)
		local socketGroup = self.skillsTab.socketGroupList[index]
		if socketGroup and tooltip:CheckForUpdate(socketGroup, self.outputRevision) then
			self.skillsTab:AddSocketGroupTooltip(tooltip, socketGroup)
		end
	end
	self.controls.mainSkill = common.New("DropDownControl", {"TOPLEFT",self.controls.mainSocketGroup,"BOTTOMLEFT"}, 0, 2, 300, 16, nil, function(index, value)
		local mainSocketGroup = self.skillsTab.socketGroupList[self.mainSocketGroup]
		mainSocketGroup.mainActiveSkill = index
		self.modFlag = true
		self.buildFlag = true
	end)
	self.controls.mainSkillPart = common.New("DropDownControl", {"TOPLEFT",self.controls.mainSocketGroup,"BOTTOMLEFT"}, 0, 20, 150, 18, nil, function(index, value)
		local mainSocketGroup = self.skillsTab.socketGroupList[self.mainSocketGroup]
		local srcGem = mainSocketGroup.displaySkillList[mainSocketGroup.mainActiveSkill].activeGem.srcGem
		srcGem.skillPart = index
		self.modFlag = true
		self.buildFlag = true
	end)
	self.controls.mainSkillMinion = common.New("DropDownControl", {"TOPLEFT",self.controls.mainSocketGroup,"BOTTOMLEFT"}, 0, 20, 178, 18, nil, function(index, value)
		local mainSocketGroup = self.skillsTab.socketGroupList[self.mainSocketGroup]
		local srcGem = mainSocketGroup.displaySkillList[mainSocketGroup.mainActiveSkill].activeGem.srcGem
		if value.itemSetId then
			srcGem.skillMinionItemSet = value.itemSetId
		else
			srcGem.skillMinion = value.minionId
		end
		self.modFlag = true
		self.buildFlag = true
	end)
	function self.controls.mainSkillMinion.CanReceiveDrag(control, type, value)
		if type == "Item" and control.list[control.selIndex] and control.list[control.selIndex].itemSetId then
			local mainSocketGroup = self.skillsTab.socketGroupList[self.mainSocketGroup]
			local minionUses = mainSocketGroup.displaySkillList[mainSocketGroup.mainActiveSkill].activeGem.grantedEffect.minionUses
			return minionUses and minionUses[itemLib.getPrimarySlotForItem(value)] -- O_O
		end
	end
	function self.controls.mainSkillMinion.ReceiveDrag(control, type, value, source)
		self.itemsTab:EquipItemInSet(value, control.list[control.selIndex].itemSetId)
	end
	function self.controls.mainSkillMinion.tooltipFunc(tooltip, mode, index, value)
		tooltip:Clear()
		if value.itemSetId then
			self.itemsTab:AddItemSetTooltip(tooltip, self.itemsTab.itemSets[value.itemSetId])
			tooltip:AddSeparator(14)
			tooltip:AddLine(14, colorCodes.TIP.."Tip: You can drag items from the Items tab onto this dropdown to equip them onto the minion.")
		end
	end
	self.controls.mainSkillMinionLibrary = common.New("ButtonControl", {"LEFT",self.controls.mainSkillMinion,"RIGHT"}, 2, 0, 120, 18, "Manage Spectres...", function()
		self:OpenSpectreLibrary()
	end)
	self.controls.mainSkillMinionSkill = common.New("DropDownControl", {"TOPLEFT",self.controls.mainSkillMinion,"BOTTOMLEFT"}, 0, 2, 200, 16, nil, function(index, value)
		local mainSocketGroup = self.skillsTab.socketGroupList[self.mainSocketGroup]
		local srcGem = mainSocketGroup.displaySkillList[mainSocketGroup.mainActiveSkill].activeGem.srcGem
		srcGem.skillMinionSkill = index
		self.modFlag = true
		self.buildFlag = true
	end)
	self.controls.statBox = common.New("TextListControl", {"TOPLEFT",self.controls.mainSocketGroup,"BOTTOMLEFT"}, 0, 62, 300, 0, {{x=170,align="RIGHT_X"},{x=174,align="LEFT"}})
	self.controls.statBox.height = function(control)
		local x, y = control:GetPos()
		return main.screenH - main.mainBarHeight - 4 - y
	end

	-- Initialise build components
	self.data = data[self.targetVersion]
	self.tree = main.tree[self.targetVersion]
	self.importTab = common.New("ImportTab", self)
	self.notesTab = common.New("NotesTab", self)
	self.configTab = common.New("ConfigTab", self)
	self.itemsTab = common.New("ItemsTab", self)
	self.treeTab = common.New("TreeTab", self)
	self.skillsTab = common.New("SkillsTab", self)
	self.calcsTab = common.New("CalcsTab", self)

	-- Load sections from the build file
	self.savers = {
		["Config"] = self.configTab,
		["Notes"] = self.notesTab,
		["Tree"] = self.treeTab,
		["TreeView"] = self.treeTab.viewer,
		["Items"] = self.itemsTab,
		["Skills"] = self.skillsTab,
		["Calcs"] = self.calcsTab,
	}
	self.legacyLoaders = { -- Special loaders for legacy sections
		["Spec"] = self.treeTab,
	}
	for _, node in ipairs(self.xmlSectionList) do
		-- Check if there is a saver that can load this section
		local saver = self.savers[node.elem] or self.legacyLoaders[node.elem]
		if saver then
			if saver:Load(node, self.dbFileName) then
				self:CloseBuild()
				return
			end
		end
	end
	for _, saver in pairs(self.savers) do
		if saver.PostLoad then
			saver:PostLoad()
		end
	end

	if next(self.configTab.input) == nil then
		-- Check for old calcs tab settings
		self.configTab:ImportCalcSettings()
	end

	-- Initialise class dropdown
	for classId, class in pairs(self.tree.classes) do
		t_insert(self.controls.classDrop.list, {
			label = class.name,
			classId = classId,
		})
	end
	table.sort(self.controls.classDrop.list, function(a, b) return a.label < b.label end)

	-- Build calculation output tables
	self.outputRevision = 1
	self.calcsTab:BuildOutput()
	self:RefreshStatList()
	self.buildFlag = false

	--[[
	local testTooltip = common.New("Tooltip")
	for _, item in pairs(main.uniqueDB.list) do
		ConPrintf("%s", item.name)
		self.itemsTab:AddItemTooltip(testTooltip, item)
		testTooltip:Clear()
	end
	for _, item in pairs(main.rareDB.list) do
		ConPrintf("%s", item.name)
		self.itemsTab:AddItemTooltip(testTooltip, item)
		testTooltip:Clear()
	end
	--]]

	--[[
	local start = GetTime()
	SetProfiling(true)
	for i = 1, 10  do
		self.calcsTab:PowerBuilder()
	end
	SetProfiling(false)
	ConPrintf("Power build time: %d msec", GetTime() - start)
	--]]

	self.abortSave = false
end

function buildMode:CanExit(mode)
	if not self.targetVersion or not self.unsaved then
		return true
	end
	self:OpenSavePopup(mode)
	return false
end

function buildMode:Shutdown()
	if launch.devMode and self.targetVersion and not self.abortSave then
		if self.dbFileName then
			self:SaveDBFile()
		elseif self.unsaved then
			self.dbFileName = main.buildPath.."~~temp~~.xml"
			self.buildName = "~~temp~~"
			self.dbFileSubPath = ""
			self:SaveDBFile()
		end
	end
	self.abortSave = nil

	self.savers = nil
end

function buildMode:GetArgs()
	return self.dbFileName, self.buildName
end

function buildMode:CloseBuild()
	main:SetMode("LIST", self.dbFileName and self.buildName, self.dbFileSubPath)
end

function buildMode:Load(xml, fileName)
	self.targetVersion = data[xml.attrib.targetVersion] and xml.attrib.targetVersion or defaultTargetVersion
	if xml.attrib.viewMode then
		self.viewMode = xml.attrib.viewMode
	end
	self.characterLevel = tonumber(xml.attrib.level) or 1
	self.controls.characterLevel:SetText(tostring(self.characterLevel))
	for _, diff in pairs({"bandit","banditNormal","banditCruel","banditMerciless"}) do
		self[diff] = xml.attrib[diff] or "None"
	end
	self.mainSocketGroup = tonumber(xml.attrib.mainSkillIndex) or tonumber(xml.attrib.mainSocketGroup) or 1
	wipeTable(self.spectreList)
	for _, child in ipairs(xml) do
		if child.elem == "Spectre" then
			if child.attrib.id and data[self.targetVersion].minions[child.attrib.id] then
				t_insert(self.spectreList, child.attrib.id)
			end
		end
	end
end

function buildMode:Save(xml)
	xml.attrib = {
		targetVersion = self.targetVersion,
		viewMode = self.viewMode,
		level = tostring(self.characterLevel),
		className = self.spec.curClassName,
		ascendClassName = self.spec.curAscendClassName,
		bandit = self.bandit,
		banditNormal = self.banditNormal,
		banditCruel = self.banditCruel,
		banditMerciless = self.banditMerciless,
		mainSocketGroup = tostring(self.mainSocketGroup),
	}
	for _, id in ipairs(self.spectreList) do
		t_insert(xml, { elem = "Spectre", attrib = { id = id } })
	end
	for index, statData in ipairs(self.displayStats) do
		if statData.stat then
			local statVal = self.calcsTab.mainOutput[statData.stat]
			if statVal then
				t_insert(xml, { elem = "PlayerStat", attrib = { stat = statData.stat, value = tostring(statVal) } })
			end
		end
	end
	if self.calcsTab.mainEnv.minion then
		for index, statData in ipairs(self.minionDisplayStats) do
			if statData.stat then
				local statVal = self.calcsTab.mainOutput.Minion[statData.stat]
				if statVal then
					t_insert(xml, { elem = "MinionStat", attrib = { stat = statData.stat, value = tostring(statVal) } })
				end
			end
		end
	end
	self.modFlag = false
end

function buildMode:OnFrame(inputEvents)
	if not self.targetVersion then
		main:DrawBackground(main.viewPort)
		return
	end

	if self.abortSave and not launch.devMode then
		self:CloseBuild()
	end

	for id, event in ipairs(inputEvents) do
		if event.type == "KeyDown" then
			if event.key == "MOUSE4" then
				if self.unsaved then
					self:OpenSavePopup("LIST")
				else
					self:CloseBuild()
				end
		elseif IsKeyDown("CTRL") then
				if event.key == "s" then
					self:SaveDBFile()
					inputEvents[id] = nil
				elseif event.key == "w" then
					if self.unsaved then
						self:OpenSavePopup("LIST")
					else
						self:CloseBuild()
					end
				elseif event.key == "1" then
					self.viewMode = "TREE"
				elseif event.key == "2" then
					self.viewMode = "SKILLS"
				elseif event.key == "3" then
					self.viewMode = "ITEMS"
				elseif event.key == "4" then
					self.viewMode = "CALCS"
				elseif event.key == "5" then
					self.viewMode = "CONFIG"
				end
			end
		end
	end
	self:ProcessControlsInput(inputEvents, main.viewPort)

	-- Update contents of ascendancy class dropdown
	wipeTable(self.controls.ascendDrop.list)
	for i = 0, #self.spec.curClass.classes do
		local ascendClass = self.spec.curClass.classes[i]
		t_insert(self.controls.ascendDrop.list, {
			label = ascendClass.name,
			ascendClassId = i,
		})
	end

	self.controls.classDrop:SelByValue(self.spec.curClassId, "classId")
	self.controls.ascendDrop:SelByValue(self.spec.curAscendClassId, "ascendClassId")

	for _, diff in pairs({"bandit","banditNormal","banditCruel","banditMerciless"}) do
		if self.controls[diff] then
			self.controls[diff]:SelByValue(self[diff], "banditId")
		end
	end

	if self.buildFlag then
		-- Rebuild calculation output tables
		self.outputRevision = self.outputRevision + 1
		self.buildFlag = false
		self.calcsTab:BuildOutput()
		self:RefreshStatList()
	end
	if main.showThousandsSidebar ~= self.lastShowThousandsSidebar then
		self:RefreshStatList()
	end

	-- Update contents of main skill dropdowns
	self:RefreshSkillSelectControls(self.controls, self.mainSocketGroup, "")

	-- Draw contents of current tab
	local sideBarWidth = 312
	local tabViewPort = {
		x = sideBarWidth,
		y = 32,
		width = main.screenW - sideBarWidth,
		height = main.screenH - 32
	}
	if self.viewMode == "IMPORT" then
		self.importTab:Draw(tabViewPort, inputEvents)
	elseif self.viewMode == "NOTES" then
		self.notesTab:Draw(tabViewPort, inputEvents)
	elseif self.viewMode == "CONFIG" then
		self.configTab:Draw(tabViewPort, inputEvents)
	elseif self.viewMode == "TREE" then
		self.treeTab:Draw(tabViewPort, inputEvents)
	elseif self.viewMode == "SKILLS" then
		self.skillsTab:Draw(tabViewPort, inputEvents)
	elseif self.viewMode == "ITEMS" then
		self.itemsTab:Draw(tabViewPort, inputEvents)
	elseif self.viewMode == "CALCS" then
		self.calcsTab:Draw(tabViewPort, inputEvents)
	end

	self.unsaved = self.modFlag or self.notesTab.modFlag or self.configTab.modFlag or self.treeTab.modFlag or self.spec.modFlag or self.skillsTab.modFlag or self.itemsTab.modFlag or self.calcsTab.modFlag

	SetDrawLayer(5)

	-- Draw top bar background
	SetDrawColor(0.2, 0.2, 0.2)
	DrawImage(nil, 0, 0, main.screenW, 28)
	SetDrawColor(0.85, 0.85, 0.85)
	DrawImage(nil, 0, 28, main.screenW, 4)
	DrawImage(nil, main.screenW/2 - 2, 0, 4, 28)

	-- Draw side bar background
	SetDrawColor(0.1, 0.1, 0.1)
	DrawImage(nil, 0, 32, sideBarWidth - 4, main.screenH - 32)
	SetDrawColor(0.85, 0.85, 0.85)
	DrawImage(nil, sideBarWidth - 4, 32, 4, main.screenH - 32)

	self:DrawControls(main.viewPort)
end

-- Opens the game version selection popup
function buildMode:OpenTargetVersionPopup(initial)
	local controls = { }
	local function setVersion(version)
		if version == self.targetVersion then
			main:ClosePopup()
			return
		end
		if initial then
			main:ClosePopup()
			self:Shutdown()
			self:Init(false, self.buildName, nil, version)
		end
	end
	controls.label = common.New("LabelControl", nil, 0, 20, 0, 16, "^7Which game version will this build use?")
	controls.version2_6 = common.New("ButtonControl", nil, -90, 50, 170, 20, "2.6 (Atlas of Worlds)", function()
		setVersion("2_6")
	end)
	controls.version3_0 = common.New("ButtonControl", nil, 90, 50, 170, 20, "3.0 (Fall of Oriath)", function()
		setVersion("3_0")
	end)
	controls.note = common.New("LabelControl", nil, 0, 80, 0, 14, "^7Tip: Existing builds can be converted between versions\nusing the 'Game Version' option in the Configuration tab.")
	controls.cancel = common.New("ButtonControl", nil, 0, 120, 80, 20, "Cancel", function()
		main:ClosePopup()
		if initial then
			self:CloseBuild()
		end
	end)
	main:OpenPopup(370, 150, "Game Version", controls, nil, nil, "cancel")
end

function buildMode:OpenSavePopup(mode, newVersion)
	local modeDesc = {
		["LIST"] = "now?",
		["EXIT"] = "before exiting?",
		["UPDATE"] = "before updating?",
		["VERSION"] = "before converting?",
	}
	local controls = { }
	controls.label = common.New("LabelControl", nil, 0, 20, 0, 16, "^7This build has unsaved changes.\nDo you want to save them "..modeDesc[mode])
	controls.save = common.New("ButtonControl", nil, -90, 70, 80, 20, "Save", function()
		main:ClosePopup()
		self.actionOnSave = mode
		self.versionOnSave = newVersion
		self:SaveDBFile()
	end)
	controls.noSave = common.New("ButtonControl", nil, 0, 70, 80, 20, "Don't Save", function()
		main:ClosePopup()
		if mode == "LIST" then
			self:CloseBuild()
		elseif mode == "EXIT" then
			Exit()
		elseif mode == "UPDATE" then
			launch:ApplyUpdate(launch.updateAvailable)
		elseif mode == "VERSION" then
			self:Shutdown()
			self:Init(self.dbFileName, self.buildName, nil, newVersion)
		end
	end)
	controls.close = common.New("ButtonControl", nil, 90, 70, 80, 20, "Cancel", function()
		main:ClosePopup()
	end)
	main:OpenPopup(300, 100, "Save Changes", controls)
end

function buildMode:OpenSaveAsPopup()
	local newFileName, newBuildName
	local controls = { }
	local function updateBuildName()
		local buf = controls.edit.buf
		newFileName = main.buildPath..controls.folder.subPath..buf..".xml"
		newBuildName = buf
		controls.save.enabled = false
		if buf:match("%S") then
			local out = io.open(newFileName, "r")
			if out then
				out:close()
			else
				controls.save.enabled = true
			end
		end
	end
	controls.label = common.New("LabelControl", nil, 0, 20, 0, 16, "^7Enter new build name:")
	controls.edit = common.New("EditControl", nil, 0, 40, 450, 20, self.dbFileName and self.buildName, nil, "\\/:%*%?\"<>|%c", 100, function(buf)
		updateBuildName()
	end)
	controls.folderLabel = common.New("LabelControl", {"TOPLEFT",nil,"TOPLEFT"}, 10, 70, 0, 16, "^7Folder:")
	controls.newFolder = common.New("ButtonControl", {"TOPLEFT",nil,"TOPLEFT"}, 100, 67, 94, 20, "New Folder...", function()
		main:OpenNewFolderPopup(main.buildPath..controls.folder.subPath, function(newFolderName)
			if newFolderName then
				controls.folder:OpenFolder(newFolderName)
			end
		end)
	end)
	controls.folder = common.New("FolderList", nil, 0, 115, 450, 100, self.dbFileSubPath, function(subPath)
		updateBuildName()
	end)
	controls.save = common.New("ButtonControl", nil, -45, 225, 80, 20, "Save", function()
		main:ClosePopup()
		self.dbFileName = newFileName
		self.buildName = newBuildName
		self.dbFileSubPath = controls.folder.subPath
		self:SaveDBFile()
	end)
	controls.save.enabled = false
	controls.close = common.New("ButtonControl", nil, 45, 225, 80, 20, "Cancel", function()
		main:ClosePopup()
		self.actionOnSave = nil
		self.versionOnSave = nil
	end)
	main:OpenPopup(470, 255, self.dbFileName and "Save As" or "Save", controls, "save", "edit", "close")
end

-- Open the spectre library popup
function buildMode:OpenSpectreLibrary()
	local destList = copyTable(self.spectreList)
	local sourceList = { }
	for id in pairs(self.data.spectres) do
		t_insert(sourceList, id)
	end
	table.sort(sourceList, function(a,b)
		if self.data.minions[a].name == self.data.minions[b].name then
			return a < b
		else
			return self.data.minions[a].name < self.data.minions[b].name
		end
	end)
	local controls = { }
	controls.list = common.New("MinionList", nil, -100, 40, 190, 250, self.data, destList)
	controls.source = common.New("MinionList", nil, 100, 40, 190, 250, self.data, sourceList, controls.list)
	controls.save = common.New("ButtonControl", nil, -45, 300, 80, 20, "Save", function()
		self.spectreList = destList
		self.modFlag = true
		self.buildFlag = true
		main:ClosePopup()
	end)
	controls.cancel = common.New("ButtonControl", nil, 45, 300, 80, 20, "Cancel", function()
		main:ClosePopup()
	end)
	main:OpenPopup(410, 330, "Spectre Library", controls)
end

-- Refresh the set of controls used to select main group/skill/minion
function buildMode:RefreshSkillSelectControls(controls, mainGroup, suffix)
	controls.mainSocketGroup.selIndex = mainGroup
	wipeTable(controls.mainSocketGroup.list)
	for i, socketGroup in pairs(self.skillsTab.socketGroupList) do
		controls.mainSocketGroup.list[i] = { val = i, label = socketGroup.displayLabel }
	end
	if #controls.mainSocketGroup.list == 0 then
		controls.mainSocketGroup.list[1] = { val = 1, label = "<No skills added yet>" }
		controls.mainSkill.shown = false
		controls.mainSkillPart.shown = false
		controls.mainSkillMinion.shown = false
		controls.mainSkillMinionSkill.shown = false
	else
		local mainSocketGroup = self.skillsTab.socketGroupList[mainGroup]
		local displaySkillList = mainSocketGroup["displaySkillList"..suffix]
		local mainActiveSkill = mainSocketGroup["mainActiveSkill"..suffix] or 1
		wipeTable(controls.mainSkill.list)
		for i, activeSkill in ipairs(displaySkillList) do
			t_insert(controls.mainSkill.list, { val = i, label = activeSkill.activeGem.grantedEffect.name })
		end
		controls.mainSkill.enabled = #displaySkillList > 1
		controls.mainSkill.selIndex = mainActiveSkill
		controls.mainSkill.shown = true
		controls.mainSkillPart.shown = false
		controls.mainSkillMinion.shown = false
		controls.mainSkillMinionLibrary.shown = false
		controls.mainSkillMinionSkill.shown = false
		if displaySkillList[1] then
			local activeSkill = displaySkillList[mainActiveSkill]
			local activeGem = activeSkill.activeGem
			if activeGem then
				if activeGem.grantedEffect.parts and #activeGem.grantedEffect.parts > 1 then
					controls.mainSkillPart.shown = true
					wipeTable(controls.mainSkillPart.list)
					for i, part in ipairs(activeGem.grantedEffect.parts) do
						t_insert(controls.mainSkillPart.list, { val = i, label = part.name })
					end
					controls.mainSkillPart.selIndex = activeGem.srcGem["skillPart"..suffix] or 1
				elseif not activeSkill.skillFlags.disable and activeGem.grantedEffect.minionList then
					wipeTable(controls.mainSkillMinion.list)
					if activeGem.grantedEffect.minionHasItemSet then
						for _, itemSetId in ipairs(self.itemsTab.itemSetOrderList) do
							local itemSet = self.itemsTab.itemSets[itemSetId]
							t_insert(controls.mainSkillMinion.list, {
								label = itemSet.title or "Default Item Set",
								itemSetId = itemSetId,
							})
						end
						controls.mainSkillMinion:SelByValue(activeGem.srcGem["skillMinionItemSet"..suffix] or 1, "itemSetId")
					else
						local list
						if activeGem.grantedEffect.minionList[1] then
							list = activeGem.grantedEffect.minionList
						else
							list = self.spectreList
							controls.mainSkillMinionLibrary.shown = true
						end
						for _, minionId in ipairs(list) do
							t_insert(controls.mainSkillMinion.list, {
								label = self.data.minions[minionId].name,
								minionId = minionId,
							})
						end
						controls.mainSkillMinion:SelByValue(activeGem.srcGem["skillMinion"..suffix] or controls.mainSkillMinion.list[1], "minionId")
					end
					controls.mainSkillMinion.enabled = #controls.mainSkillMinion.list > 1
					controls.mainSkillMinion.shown = true
					wipeTable(controls.mainSkillMinionSkill.list)
					if activeSkill.minion then
						for _, minionSkill in ipairs(activeSkill.minion.activeSkillList) do
							t_insert(controls.mainSkillMinionSkill.list, minionSkill.activeGem.grantedEffect.name)
						end
						controls.mainSkillMinionSkill.selIndex = activeGem.srcGem["skillMinionSkill"..suffix] or 1
						controls.mainSkillMinionSkill.shown = true
						controls.mainSkillMinionSkill.enabled = #controls.mainSkillMinionSkill.list > 1
					else
						t_insert(controls.mainSkillMinion.list, "<No spectres in build>")
					end
				end
			end
		end
	end
end

function buildMode:FormatStat(statData, statVal)
	local val = statVal * ((statData.pc or statData.mod) and 100 or 1) - (statData.mod and 100 or 0)
	local color = (statVal >= 0 and "^7" or colorCodes.NEGATIVE)
	local valStr = s_format("%"..statData.fmt, val)
	if main.showThousandsSidebar then
		valStr = color .. formatNumSep(valStr)
	else
		valStr = color .. valStr
	end
	self.lastShowThousandsSidebar = main.showThousandsSidebar
	return valStr
end

-- Add stat list for given actor
function buildMode:AddDisplayStatList(statList, actor)
	local statBoxList = self.controls.statBox.list
	for index, statData in ipairs(statList) do
		if statData.stat then
			if not statData.flag or actor.mainSkill.skillFlags[statData.flag] then
				local statVal = actor.output[statData.stat]
				if statVal and ((statData.condFunc and statData.condFunc(statVal,actor.output)) or (not statData.condFunc and statVal ~= 0)) then
					t_insert(statBoxList, {
						height = 16,
						"^7"..statData.label..":",
						self:FormatStat(statData, statVal),
					})
				end
			end
		elseif not statBoxList[#statBoxList] or statBoxList[#statBoxList][1] then
			t_insert(statBoxList, { height = 10 })
		end
	end
end

-- Build list of side bar stats
function buildMode:RefreshStatList()
	local statBoxList = wipeTable(self.controls.statBox.list)
	if self.calcsTab.mainEnv.minion then
		t_insert(statBoxList, { height = 18, "^7Minion:" })
		self:AddDisplayStatList(self.minionDisplayStats, self.calcsTab.mainEnv.minion)
		t_insert(statBoxList, { height = 10 })
		t_insert(statBoxList, { height = 18, "^7Player:" })
	end
	if self.calcsTab.mainEnv.player.mainSkill.skillFlags.disable then
		t_insert(statBoxList, { height = 16, "^7Skill disabled:" })
		t_insert(statBoxList, { height = 14, align = "CENTER_X", x = 140, self.calcsTab.mainEnv.player.mainSkill.disableReason })
	end
	self:AddDisplayStatList(self.displayStats, self.calcsTab.mainEnv.player)
end

function buildMode:CompareStatList(tooltip, statList, actor, baseOutput, compareOutput, header, nodeCount)
	local count = 0
	for _, statData in ipairs(statList) do
		if statData.stat and (not statData.flag or actor.mainSkill.skillFlags[statData.flag]) then
			local statVal1 = compareOutput[statData.stat] or 0
			local statVal2 = baseOutput[statData.stat] or 0
			local diff = statVal1 - statVal2
			if (diff > 0.001 or diff < -0.001) and (not statData.condFunc or statData.condFunc(statVal1,compareOutput) or statData.condFunc(statVal2,baseOutput)) then
				if count == 0 then
					tooltip:AddLine(14, header)
				end
				local color = ((statData.lowerIsBetter and diff < 0) or (not statData.lowerIsBetter and diff > 0)) and colorCodes.POSITIVE or colorCodes.NEGATIVE
				local line = string.format("%s%+"..statData.fmt.." %s", color, diff * ((statData.pc or statData.mod) and 100 or 1), statData.label)
				local pcPerPt = ""
				if statData.compPercent and statVal1 ~= 0 and statVal2 ~= 0 then
					local pc = statVal1 / statVal2 * 100 - 100
					line = line .. string.format(" (%+.1f%%)", pc)
					if nodeCount then
						pcPerPt = string.format(" (%+.1f%%)", pc / nodeCount)
					end
				end
				if nodeCount then
					line = line .. string.format(" ^8[%+"..statData.fmt.."%s per point]", diff * ((statData.pc or statData.mod) and 100 or 1) / nodeCount, pcPerPt)
				end
				tooltip:AddLine(14, line)
				count = count + 1
			end
		end
	end
	return count
end

-- Compare values of all display stats between the two output tables, and add any changed stats to the tooltip
-- Adds the provided header line before the first stat line, if any are added
-- Returns the number of stat lines added
function buildMode:AddStatComparesToTooltip(tooltip, baseOutput, compareOutput, header, nodeCount)
	local count = 0
	if baseOutput.Minion and compareOutput.Minion then
		count = count + self:CompareStatList(tooltip, self.minionDisplayStats, self.calcsTab.mainEnv.minion, baseOutput.Minion, compareOutput.Minion, header.."\n^7Minion:", nodeCount)
		if count > 0 then
			header = "^7Player:"
		else
			header = header.."\n^7Player:"
		end
	end
	count = count + self:CompareStatList(tooltip, self.displayStats, self.calcsTab.mainEnv.player, baseOutput, compareOutput, header, nodeCount)
	return count
end

-- Add requirements to tooltip
do
	local req = { }
	function buildMode:AddRequirementsToTooltip(tooltip, level, str, dex, int, strBase, dexBase, intBase)
		if level and level > 0 then
			t_insert(req, s_format("^x7F7F7FLevel %s%d", main:StatColor(level, nil, self.characterLevel), level))
		end
		if str and (str > 14 or str > self.calcsTab.mainOutput.Str) then
			t_insert(req, s_format("%s%d ^x7F7F7FStr", main:StatColor(str, strBase, self.calcsTab.mainOutput.Str), str))
		end
		if dex and (dex > 14 or dex > self.calcsTab.mainOutput.Dex) then
			t_insert(req, s_format("%s%d ^x7F7F7FDex", main:StatColor(dex, dexBase, self.calcsTab.mainOutput.Dex), dex))
		end
		if int and (int > 14 or int > self.calcsTab.mainOutput.Int) then
			t_insert(req, s_format("%s%d ^x7F7F7FInt", main:StatColor(int, intBase, self.calcsTab.mainOutput.Int), int))
		end
		if req[1] then
			tooltip:AddLine(16, "^x7F7F7FRequires "..table.concat(req, "^x7F7F7F, "))
			tooltip:AddSeparator(10)
		end
		wipeTable(req)
	end
end

function buildMode:LoadDB(xmlText, fileName)
	-- Parse the XML
	local dbXML, errMsg = common.xml.ParseXML(xmlText)
	if not dbXML then
		launch:ShowErrMsg("^1Error loading '%s': %s", fileName, errMsg)
		return true
	elseif dbXML[1].elem ~= "PathOfBuilding" then
		launch:ShowErrMsg("^1Error parsing '%s': 'PathOfBuilding' root element missing", fileName)
		return true
	end

	-- Load Build section first
	for _, node in ipairs(dbXML[1]) do
		if type(node) == "table" and node.elem == "Build" then
			self:Load(node, self.dbFileName)
			break
		end
	end

	-- Store other sections for later processing
	for _, node in ipairs(dbXML[1]) do
		if type(node) == "table" then
			t_insert(self.xmlSectionList, node)
		end
	end
end

function buildMode:LoadDBFile()
	if not self.dbFileName then
		return
	end
	ConPrintf("Loading '%s'...", self.dbFileName)
	local file = io.open(self.dbFileName, "r")
	if not file then
		return true
	end
	local xmlText = file:read("*a")
	file:close()
	return self:LoadDB(xmlText, self.dbFileName)
end

function buildMode:SaveDB(fileName)
	local dbXML = { elem = "PathOfBuilding" }

	-- Save Build section first
	do
		local node = { elem = "Build" }
		self:Save(node)
		t_insert(dbXML, node)
	end

	-- Call on all savers to save their data in their respective sections
	for elem, saver in pairs(self.savers) do
		local node = { elem = elem }
		saver:Save(node)
		t_insert(dbXML, node)
	end

	-- Compose the XML
	local xmlText, errMsg = common.xml.ComposeXML(dbXML)
	if not xmlText then
		launch:ShowErrMsg("Error saving '%s': %s", fileName, errMsg)
	else
		return xmlText
	end
end

function buildMode:SaveDBFile()
	if not self.dbFileName then
		self:OpenSaveAsPopup()
		return
	end
	if self.versionOnSave then
		self.targetVersion = self.versionOnSave
		self.versionOnSave = nil
	end
	local xmlText = self:SaveDB(self.dbFileName)
	if not xmlText then
		return true
	end
	local file = io.open(self.dbFileName, "w+")
	if not file then
		main:OpenMessagePopup("Error", "Couldn't save the build file:\n"..self.dbFileName.."\nMake sure the save folder exists and is writable.")
		return true
	end
	file:write(xmlText)
	file:close()
	local action = self.actionOnSave
	self.actionOnSave = nil
	if action == "LIST" then
		self:CloseBuild()
	elseif action == "EXIT" then
		Exit()
	elseif action == "UPDATE" then
		launch:ApplyUpdate(launch.updateAvailable)
	elseif action == "VERSION" then
		self:Shutdown()
		self:Init(self.dbFileName, self.buildName)
	end
end

return buildMode


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
null

#### Original name
Valsaria saccardiana Speg.

### Remarks
null

redpajama_set_name: RedPajamaGithub


text:
package br.com.delogic.jnerator.impl.generator;

import br.com.delogic.jnerator.AttributeConfiguration;
import br.com.delogic.jnerator.AttributeGenerator;
import br.com.delogic.jnerator.util.Util;

public class IntegerAttributeGenerator implements AttributeGenerator {

    public <T> Integer generate(int index, AttributeConfiguration<T> attributeConfiguration, Object instance) {
        if (attributeConfiguration.getFrom() != null && attributeConfiguration.getTo() != null){
            int from = attributeConfiguration.getFrom().intValue(), to = attributeConfiguration.getTo().intValue();
            if (from >= to){
                throw new IllegalArgumentException("Number From must be less than number To");
            }
            int number = Util.nextInt(to);
            if (number < from){
                number += from;
            }
            return number;
        }
        return index;
    }

}


text:
using KSPPluginFramework;
using OldSimpleJSON;
using System;
using KSP.UI.Screens;

namespace Gameframer
{
    [KSPAddon(KSPAddon.Startup.EveryScene, false)]
    public class RecoveryListener : MonoBehaviourExtended
    {
        internal override void Start()
        {
            GFLogger.Instance.AddDebugLog("RecoveryListener created");
            GameEvents.onVesselRecoveryProcessing.Add(this.onVesselRecoveryProcessing);
            GameEvents.onVesselRecovered.Add(this.onVesselRecovered);
            GameEvents.onVesselTerminated.Add(this.onVesselTerminated);
        }

        internal override void OnDestroy()
        {
            GFLogger.Instance.AddDebugLog("RecoveryListener destroyed");
            GameEvents.onVesselRecoveryProcessing.Remove(this.onVesselRecoveryProcessing);
            GameEvents.onVesselRecovered.Remove(this.onVesselRecovered);
            GameEvents.onVesselTerminated.Remove(this.onVesselTerminated);
        }

        public void onVesselRecoveryProcessing(ProtoVessel pv, MissionRecoveryDialog d, float f)
        {
            if (pv.vesselType == VesselType.Debris || pv.vesselType == VesselType.Flag || pv.vesselType == VesselType.SpaceObject || pv.vesselType == VesselType.Unknown)
            {
                //GFLogger.Instance.AddDebugLog("onVesselRecovered BAILING, VESSEL WAS NOT CONTROLLABLE");
                GFLogger.Instance.AddDebugLog("onVesselRecoveryProcessing BAILING, bad vessel type {0}", pv.vesselType);
                return;
            }
            /*if (!pv.wasControllable)
            {
                GFLogger.Instance.AddDebugLog("onVesselRecoveryProcessing BAILING, VESSEL WAS NOT CONTROLLABLE");
                return;
            }*/

            GFLogger.Instance.AddDebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name + ", name = " + pv.vesselName + ", met = " + pv.missionTime);
            GFLogger.Instance.AddDebugLog("RECOVERY: {0}, {1}, {2}", pv.vesselName, pv.wasControllable, pv.vesselType);
            GFLogger.Instance.AddDebugLog("{0}/{1} recoveryProcessing", pv.protoPartSnapshots[pv.rootIndex].missionID, pv.protoPartSnapshots[pv.rootIndex].flightID);

            var missionID = pv.protoPartSnapshots[pv.rootIndex].missionID;
            var flightID = pv.protoPartSnapshots[pv.rootIndex].flightID;
            GFLogger.Instance.AddDebugLog("Recovery {0}/{1}", missionID, flightID);
            OldJSONNode recoveryPatch = OldJSONNode.Parse("{}");
            recoveryPatch["recoveryFactor"] = d.recoveryFactor;
            recoveryPatch["recoveryLocation"] = d.recoveryLocation;
            recoveryPatch["fundsEarned"].AsDouble = d.fundsEarned;
            recoveryPatch["reputationEarned"].AsDouble = d.reputationEarned;
            recoveryPatch["scienceEarned"].AsDouble = d.scienceEarned;
            OldJSONNode patch = OldJSONNode.Parse("{}");
            patch["recovery"] = recoveryPatch;

            MissionPatchWorker.CreateComponent(this.gameObject, "/missions/" + missionID + "/" + flightID, patch, OnDone, OnFail);
        }

        private void onVesselRecovered(ProtoVessel pv, bool b)
        {
            GFLogger.Instance.AddDebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name + ", name = " + pv.vesselName + ", met = " + pv.missionTime);
            GFLogger.Instance.AddDebugLog("RECOVERED: {0}, {1}, {2}", pv.vesselName, pv.wasControllable, pv.vesselType);

            if (pv == null)
                return;

            if (pv.vesselType == VesselType.Debris || pv.vesselType == VesselType.Flag || pv.vesselType == VesselType.SpaceObject || pv.vesselType == VesselType.Unknown)
            {
                //GFLogger.Instance.AddDebugLog("onVesselRecovered BAILING, VESSEL WAS NOT CONTROLLABLE");
                GFLogger.Instance.AddDebugLog("onVesselRecovered BAILING, bad vessel type {0}", pv.vesselType);
                return;
            }
            /*if(pv.vesselType == VesselType.EVA)
            {
                GFLogger.Instance.AddDebugLog("onVesselRecovered BAILING, EVA");
                return;
            } */
            var missionID = pv.protoPartSnapshots[pv.rootIndex].missionID;
            var flightID = pv.protoPartSnapshots[pv.rootIndex].flightID;

            OldJSONNode patch = OldJSONNode.Parse("{}");
            patch["finalized"].AsBool = true;
            OldJSONNode sitPatch = OldJSONNode.Parse("{}");
            sitPatch["missionTimeInDays"].AsDouble = KSPUtils.GameTimeInDays(pv.missionTime);
            sitPatch["situation"] = "Recovered";
            sitPatch["vesselName"] = pv.vesselName;
            patch["stats"] = sitPatch;

            patch = KSPUtils.GetStatusPatch(pv);
            MissionPatchWorker.CreateComponent(this.gameObject, "/missions/" + missionID + "/" + flightID, patch, OnDone, OnFail);

            var theEvent = GFDataUtils.GetEvent("onRecovered", "Recovered", pv.vesselRef);
            GFLogger.Instance.AddDebugLog("Recovered event: {0}", theEvent.ToString());
            MissionEventWorker.CreateComponent(this.gameObject, missionID, flightID,
                theEvent, VideoOptions.NONE, OnDone, OnFail);
        }

        // deleted from the tracking station
        private void onVesselTerminated(ProtoVessel pv)
        {
            GFLogger.Instance.AddDebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name + ", name = " + pv.vesselName + ", met = " + pv.missionTime);
            GFLogger.Instance.AddDebugLog("{0}/{1} terminated", pv.protoPartSnapshots[pv.rootIndex].missionID, pv.protoPartSnapshots[pv.rootIndex].flightID);
            var missionID = pv.protoPartSnapshots[pv.rootIndex].missionID;
            var flightID = pv.protoPartSnapshots[pv.rootIndex].flightID;

            OldJSONNode patch = OldJSONNode.Parse("{}");
            patch["finalized"].AsBool = true;
            OldJSONNode sitPatch = KSPUtils.GetStatusPatch(pv);
            sitPatch["missionTimeInDays"].AsDouble = KSPUtils.GameTimeInDays(pv.missionTime);
            sitPatch["situation"] = "Terminated";
            sitPatch["vesselName"] = pv.vesselName;
            patch["stats"] = sitPatch;

            MissionPatchWorker.CreateComponent(this.gameObject, "/missions/" + missionID + "/" + flightID, patch, OnDone, OnFail);

            MissionEventWorker.CreateComponent(this.gameObject, missionID, flightID,
                GFDataUtils.GetEvent("onTerminated", "Terminated", pv.vesselRef), VideoOptions.NONE, OnDone, OnFail);
        }

        private void OnDone(OldJSONNode n)
        {
            GFLogger.Instance.AddDebugLog(String.Format("{0}.{1}", this.GetType().Name, System.Reflection.MethodBase.GetCurrentMethod().Name));
        }
        private void OnFail(OldJSONNode n)
        {
            GFLogger.Instance.AddError(String.Format("{0}.{1}", this.GetType().Name, System.Reflection.MethodBase.GetCurrentMethod().Name));
        }
    }
}


text:
package com.jenkov.iap.error;

/**
 * Created by jjenkov on 05-03-2016.
 */
public class IapToolsException extends RuntimeException {

    public IapToolsException() {
        super();
    }

    public IapToolsException(String message) {
        super(message);
    }

    public IapToolsException(String message, Throwable cause) {
        super(message, cause);
    }

    public IapToolsException(Throwable cause) {
        super(cause);
    }

    protected IapToolsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}


text:
SolrJSView is a Java Script/Jquery web front end library for creating user interfaces to [Apache SOLR]. data are presented in table and popup document view.
the application can also be deploy as WAR file in any J2EE web container using Maven.

this application is originally fork from https://github.com/evolvingweb/ajax-solr/
In addition features provided by reuter example,
	*It provide simple and advanced search function for Apache Solr application.
	*It provide a table view of search result.
	*All columns that have matching search text will automatically highlighted, sorted and show in left most columns in the table.
		rest of columns will be sort alphabetically.
	*Individual row could further show in pop up document view when clicked. only applicable fields for the row will be
		show in pop up document view.
	*It provide check box to turn on/off facet view.
	*Configurable facet fields- Automatic create and show facet navigation view base on facet fields configuration.
	*Handle embedded hyper link content column in the table. content that starts with "http" will be handle as a link.
		handled contents include wave file, image file, and html file.
	*embedded facet link in the table header.
	*added function to show/hide columns in a table.
	*added export data as csv/excel function.
	*added offline helpful page to solr query syntax page.
	*handled highlight text in search result, where solr highlight failed due to long text fields.

following fields can be change to  fit your configuration:

solr_ui.js ALLSolrURL:  URL of your solr server instance.
solr_ui.js facetFields:  facet fields your application want to focus on.
solr_ui.js  proxyURL:  proxy server url. this is needed for export of csv/excel function to work. currently comment out
						to run as pure html5 application. uncomment this to support proxy and export function.


text:
package org.apache.apex.malhar.lib.window.accumulation;

import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.apex.malhar.lib.util.KeyValPair;
import org.apache.apex.malhar.lib.window.Accumulation;

/**
 * Generalized TopNByKey accumulation
 *
 * @since 3.5.0
 */
public class TopNByKey<K, V> implements
    Accumulation<KeyValPair<K, V>, Map<K, V>, List<KeyValPair<K, V>>>
{
  int n = 10;

  Comparator<V> comparator;

  public void setN(int n)
  {
    this.n = n;
  }

  public void setComparator(Comparator<V> comparator)
  {
    this.comparator = comparator;
  }

  @Override
  public Map<K, V> defaultAccumulatedValue()
  {
    return new HashMap<>();
  }

  @Override
  public Map<K, V> accumulate(Map<K, V> accumulatedValue, KeyValPair<K, V> input)
  {
    accumulatedValue.put(input.getKey(), input.getValue());
    return accumulatedValue;
  }

  @Override
  public Map<K, V> merge(Map<K, V> accumulatedValue1, Map<K, V> accumulatedValue2)
  {
    for (Map.Entry<K, V> entry : accumulatedValue2.entrySet()) {
      if (!accumulatedValue1.containsKey(entry.getKey())) {
        accumulatedValue1.put(entry.getKey(), entry.getValue());
      } else if (comparator != null) {
        if (comparator.compare(entry.getValue(), accumulatedValue1.get(entry.getKey())) > 0) {
          accumulatedValue1.put(entry.getKey(), entry.getValue());
        }
      } else if (entry.getValue() instanceof Comparable) {
        if (((Comparable<V>)entry.getValue()).compareTo(accumulatedValue1.get(entry.getKey())) > 0) {
          accumulatedValue1.put(entry.getKey(), entry.getValue());
        }
      }
    }
    return accumulatedValue1;
  }

  @Override
  public List<KeyValPair<K, V>> getOutput(Map<K, V> accumulatedValue)
  {
    LinkedList<KeyValPair<K, V>> result = new LinkedList<>();
    for (Map.Entry<K, V> entry : accumulatedValue.entrySet()) {
      int k = 0;
      for (KeyValPair<K, V> inMemory : result) {
        if (comparator != null) {
          if (comparator.compare(entry.getValue(), inMemory.getValue()) > 0) {
            break;
          }
        } else if (entry.getValue() instanceof Comparable) {
          if (((Comparable<V>)entry.getValue()).compareTo(inMemory.getValue()) > 0) {
            break;
          }
        }
        k++;
      }
      result.add(k, new KeyValPair<K, V>(entry.getKey(), entry.getValue()));
      if (result.size() > n) {
        result.remove(result.get(result.size() - 1));
      }
    }
    return result;
  }

  @Override
  public List<KeyValPair<K, V>> getRetraction(List<KeyValPair<K, V>> value)
  {
    // TODO: Need to add implementation for retraction.
    return new LinkedList<>();
  }
}


text:
{% load wagtailcore_tags static i18n %}

{% block content %}
{% for survey in surveys %}

          {{ survey.title }}

{% endfor %}
{% endblock %}


text:
package org.apache.sis.io;

import java.io.Flushable;
import java.io.IOException;
import org.apache.sis.util.Characters;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.internal.util.X364;

/**
 * An {@link Appendable} which can apply different kinds of reformatting that depend on the
 * End Of Line (EOL) occurrences. Available reformatting include inserting a
 * a margin before each line, wrapping to a maximal line length and replacing tabulations or
 * EOL characters. The actual work to be done can be enabled by invoking one or many of the
 * following methods:
 *
 *

 *
{@link #setMaximalLineLength(int)} for wrapping the lines to some maximal line length,
 *       typically 80 Unicode characters (code points).

 *
{@link #setTabulationExpanded(boolean)} for replacing tabulation characters by spaces.

 *
{@link #setLineSeparator(String)} for replacing all occurrences of
 *       {@linkplain Characters#isLineOrParagraphSeparator(int) line separators} by the given string.

 *

 *
 * In addition this class removes trailing {@linkplain Character#isWhitespace(int) whitespaces}
 * before end of lines.
 *
 *
How line lengths are calculated

 * Line length are measured in unit of Unicode code points. This is usually the same
 * than the number of {@code char} primitive values, but not always. Combining characters are not
 * yet recognized by this class, but future versions may improve on that.
 *
 *
For proper line length calculation in presence of tabulation characters ({@code '\t'}),
 * this class needs to known the tabulation width. The default value is 8, but this can be changed
 * by a call to {@link #setTabulationWidth(int)}. Note that invoking that method affects only line
 * length calculation; it does not replace tabulations by spaces. For tabulation expansion, see
 * {@link #setTabulationExpanded(boolean)}.

 *
 * @author  Martin Desruisseaux (Geomatys)
 * @version 0.4
 * @since   0.3
 * @module
 */
public class LineAppender extends Appender implements Flushable {
    /**
     * The line separator, or {@code null} if not yet determined. If {@code null}, then the
     * {@link #append(CharSequence, int, int)} method will try to infer it from the submitted text.
     *
     *
If {@link #isEndOfLineReplaced} is {@code false} (the default), then this line separator
     * will be used only when this class inserts new line separators as a consequence of line wraps;
     * line separators found in the texts given by the user will be passed "as is". If {@code true},
     * then all line separators are replaced.

     */
    private String lineSeparator;

    /**
     * The maximal line length, in units of code points (not {@code char}).
     * Can be set to {@link Integer#MAX_VALUE} if there is no limit.
     *
     * @see #setMaximalLineLength(int)
     */
    private int maximalLineLength;

    /**
     * The length of the current line, in units of code points (not {@code char}).
     * It may be greater than the length of {@link #buffer} because the latter contains only
     * the last word.
     */
    private int codePointCount;

    /**
     * The tabulation width, in number of code points.
     *
     * @see #setTabulationWidth(int)
     */
    private short tabulationWidth = 8;

    /**
     * {@code true} if this formatter shall expands tabulations into spaces.
     *
     * @see #setTabulationExpanded(boolean)
     */
    private boolean isTabulationExpanded;

    /**
     * {@code true} if all occurrences of EOL sequences shall be replaced by
     * the {@link #lineSeparator}, or {@code false} for keeping EOL unchanged.
     */
    private boolean isEndOfLineReplaced;

    /**
     * {@code true} if the next character needs to be skipped if equals to {@code '\n'}.
     * This field is used in order to avoid writing two EOL in place of {@code "\r\n"}.
     */
    private boolean skipLF;

    /**
     * {@code true} if the next character will be at the beginning of a new line.
     * This flag is set to {@code true} only for "real" new lines, as a result of
     * line separator found in the input given to this formatter. The "generated"
     * new lines (resulting from line wrap) will invoke {@link #onLineBegin(boolean)}
     * directly without the help of this temporary variable.
     *
     * @see #transfer(int)
     */
    private boolean isNewLine = true;

    /**
     * {@code true} if an escape sequence is in progress. The escape sequence will stop
     * after the first non-digit character other than {@link X364#BRACKET}.
     */
    private boolean isEscapeSequence;

    /**
     * The buffer for the last word being written.
     * This buffer will also contain trailing whitespace characters. If whitespaces are followed
     * by at least one non-white character, then the whitespaces are written to the underlying
     * stream before the non-ignorable one. Otherwise if whitespaces are followed by a line
     * separator, then they are discarded.
     */
    private final StringBuilder buffer = new StringBuilder();

    /**
     * The number of Java characters (not Unicode code points) in {@link #buffer},
     * ignoring trailing whitespaces.
     */
    private int printableLength;

    /**
     * Constructs a default formatter. Callers should invoke at least one of the following methods
     * after construction in order to perform useful work:
     *
     *

     *
{@link #setMaximalLineLength(int)}

     *
{@link #setTabulationExpanded(boolean)}

     *
{@link #setLineSeparator(String)}

     *

     *
     * @param out  the underlying stream or buffer to write to.
     */
    public LineAppender(final Appendable out) {
        super(out);
        maximalLineLength = Integer.MAX_VALUE;
    }

    /**
     * Constructs a formatter which will replaces line separators by the given string.
     *
     * @param out                   the underlying stream or buffer to write to.
     * @param lineSeparator         the line separator to send to {@code out}, or {@code null}
     *                              for forwarding the EOL sequences unchanged.
     * @param isTabulationExpanded  {@code true} for expanding tabulations into spaces,
     *                              or {@code false} for sending {@code '\t'} characters as-is.
     */
    public LineAppender(final Appendable out, final String lineSeparator, final boolean isTabulationExpanded) {
        super(out);
        maximalLineLength = Integer.MAX_VALUE;
        this.lineSeparator        = lineSeparator;
        this.isEndOfLineReplaced  = (lineSeparator != null);
        this.isTabulationExpanded = isTabulationExpanded;
    }

    /**
     * Constructs a formatter which will wrap the lines at a given maximal length.
     *
     * @param out                   the underlying stream or buffer to write to.
     * @param maximalLineLength     the maximal number of Unicode characters per line,
     *                              or {@link Integer#MAX_VALUE} if there is no limit.
     * @param isTabulationExpanded  {@code true} for expanding tabulations into spaces,
     *                              or {@code false} for forwarding {@code '\t'} characters as-is.
     */
    public LineAppender(final Appendable out, final int maximalLineLength, final boolean isTabulationExpanded) {
        super(out);
        ArgumentChecks.ensureStrictlyPositive("maximalLineLength", maximalLineLength);
        this.maximalLineLength    = maximalLineLength;
        this.isTabulationExpanded = isTabulationExpanded;
    }

    /**
     * Returns the maximal line length, in unit of Unicode characters (code point count).
     * The default value is no limit.
     *
     * @return the current maximal number of Unicode characters per line,
     *         or {@link Integer#MAX_VALUE} if there is no limit.
     */
    public int getMaximalLineLength() {
        return maximalLineLength;
    }

    /**
     * Sets the maximal line length, in units of Unicode characters (code point count).
     *
     * @param  length  the new maximal number of Unicode characters per line,
     *                 or {@link Integer#MAX_VALUE} if there is no limit.
     */
    public void setMaximalLineLength(final int length) {
        ArgumentChecks.ensureStrictlyPositive("length", length);
        maximalLineLength = length;
    }

    /**
     * Returns the current tabulation width, in unit of Unicode characters (code point count).
     * The default value is 8.
     *
     * @return the current tabulation width in number of Unicode characters.
     */
    public int getTabulationWidth() {
        return tabulationWidth;
    }

    /**
     * Sets the tabulation width, in unit of Unicode characters (code point count).
     *
     * @param  width  the new tabulation width. Must be greater than 0.
     * @throws IllegalArgumentException if {@code tabWidth} is not greater than 0
     *         or is unreasonably high.
     */
    public void setTabulationWidth(final int width) {
        ArgumentChecks.ensureStrictlyPositive("width", width);
        ArgumentChecks.ensureBetween("width", 1, Integer.MAX_VALUE, width);
        tabulationWidth = (short) width;
    }

    /**
     * Returns {@code true} if this formatter expands tabulations into spaces.
     * The default value is {@code false}, which means that {@code '\t'} characters
     * are sent to the underlying appendable as-is.
     *
     * @return {@code true} if this formatter expands tabulations into spaces,
     *         or {@code false} if {@code '\t'} characters are forwarded as-is.
     */
    public boolean isTabulationExpanded() {
        return isTabulationExpanded;
    }

    /**
     * Sets whether this class formatter expands tabulations into spaces.
     *
     * @param expanded {@code true} if this class shall expands tabulations into spaces,
     *                 or {@code false} for forwarding {@code '\t'} characters as-is.
     */
    public void setTabulationExpanded(final boolean expanded) {
        isTabulationExpanded = expanded;
    }

    /**
     * Returns the line separator to be sent to the underlying appendable,
     * or {@code null} if EOL sequences are forwarded unchanged.
     *
     * @return the current line separator, or {@code null} if EOL are forwarded as-is.
     */
    public String getLineSeparator() {
        return isEndOfLineReplaced ? lineSeparator : null;
    }

    /**
     * Changes the line separator to be sent to the underlying appendable.
     * This is the string to insert in place of every occurrences of {@code "\r"}, {@code "\n"},
     * {@code "\r\n"} or other {@linkplain Characters#isLineOrParagraphSeparator(int) line separators}.
     * If {@code null} (the default), then the line separators given to the {@code append}
     * methods are forwarded unchanged.
     *
     * @param  lineSeparator  the new line separator, or {@code null} for forwarding EOL as-is.
     *
     * @see Characters#isLineOrParagraphSeparator(int)
     */
    public void setLineSeparator(final String lineSeparator) {
        this.lineSeparator  = lineSeparator;
        isEndOfLineReplaced = (lineSeparator != null);
    }

    /**
     * Writes a line separator to {@link #out}. This method is invoked for new line separators
     * generated by this class, not for the line separators found in the texts supplied by the
     * user, unless {@link #isEndOfLineReplaced} is {@code true}.
     *
     * The {@link #append(CharSequence,int,int)} method tries to detect the line separator used
     * in the text, but if no line separator has been found we have to use some fallback.
     */
    private void writeLineSeparator() throws IOException {
        if (lineSeparator == null) {
            lineSeparator = System.lineSeparator();
        }
        out.append(lineSeparator);
    }

    /**
     * Writes pending non-white characters, discards trailing whitespaces, and resets column
     * position to zero. This method does not write the line separator and
     * does not modify the status of the {@link #skipLF} flag; those tasks are caller's
     * responsibility.
     */
    private void endOfLine() throws IOException {
        buffer.setLength(printableLength);      // Reduce the amount of work for StringBuilder.deleteCharAt(int).
        deleteSoftHyphen(printableLength - 1);
        transfer(printableLength);
        printableLength  = 0;
        codePointCount   = 0;
        isEscapeSequence = false;               // Handle line-breaks as "end of escape sequence".
        isNewLine        = true;
    }

    /**
     * Removes the soft hyphen characters from the given buffer. This is invoked
     * when the buffer is about to be written without being split on two lines.
     *
     * @param i index after the last character to check. This is either {@link printableLength}
     *          for checking all characters, or {@code printableLength-1} for preserving the last
     *          soft hyphen on the line (while removing all others).
     */
    private void deleteSoftHyphen(int i) {
        while (--i >= 0) {
            if (buffer.charAt(i) == Characters.SOFT_HYPHEN) {
                buffer.deleteCharAt(i);
                printableLength--;
            }
        }
    }

    /**
     * Writes the given amount of characters from the {@linkplain #buffer},
     * then removes those characters from the buffer. This method does not
     * adjust {@link #printableLength}; it is caller responsibility to do so.
     */
    private void transfer(final int length) throws IOException {
        if (isNewLine) {
            isNewLine = false;
            onLineBegin(false);
        }
        out.append(buffer, 0, length);
        buffer.delete(0, length);
    }

    /**
     * Writes the specified code point.
     *
     * @throws IOException if an I/O error occurs.
     */
    @SuppressWarnings("fallthrough")
    private void write(final int c) throws IOException {
        final StringBuilder buffer = this.buffer;
        /*
         * If the character to write is a EOL sequence, then:
         *
         *   1) Trim trailing whitespaces in the buffer.
         *   2) Remove unused soft-hyphens (otherwise some consoles display them).
         *   3) Flush the buffer to the underlying appendable.
         *   4) Write the line separator.
         */
        if (Characters.isLineOrParagraphSeparator(c)) {
            final boolean skip;
            switch (c) {
                case '\r': skip = false;  skipLF = true;  break;
                case '\n': skip = skipLF; skipLF = false; break;
                default:   skip = false;  skipLF = false; break;
            }
            if (!skip) {
                endOfLine();
            }
            if (!isEndOfLineReplaced) {
                appendCodePoint(c); // Forward EOL sequences "as-is".
            } else if (!skip) {
                writeLineSeparator(); // Replace EOL sequences by the unique line separator.
            }
            return;
        }
        skipLF = false;
        /*
         * If the character to write is a whitespace, then write any pending characters from
         * the buffer to the underlying appendable since we know that those characters didn't
         * exceeded the line length limit.
         *
         * We use Character.isWhitespace(…) instead of Character.isSpaceChar(…) because
         * the former returns 'true' tabulations (which we want), and returns 'false'
         * for non-breaking spaces (which we also want).
         */
        if (Character.isWhitespace(c)) {
            if (printableLength != 0) {
                deleteSoftHyphen(printableLength);
                transfer(printableLength);
                printableLength = 0;
            }
            if (c != '\t') {
                codePointCount++;
            } else {
                final int width = tabulationWidth - (codePointCount % tabulationWidth);
                codePointCount += width;
                if (isTabulationExpanded) {
                    buffer.append(CharSequences.spaces(width));
                    return;
                }
            }
            buffer.appendCodePoint(c);
            return;
        }
        buffer.appendCodePoint(c);
        printableLength = buffer.length();
        /*
         * Special handling of ANSI X3.64 escape sequences. Since they are not visible
         * characters (they are used for controlling the colors), do not count them in
         * 'codePointCount' (but still count them as "printable" characters, since we
         * don't want to trim them). The sequence pattern is "CSI <digits> <command>"
         * where <command> is a single letter.
         */
        if (c == X364.ESCAPE) {
            isEscapeSequence = true;
            return;
        } else if (isEscapeSequence) {
            final char previous = buffer.charAt(printableLength - 2);
            if (previous != X364.ESCAPE) {
                isEscapeSequence = (c >= '0' && c <= '9');
                return; // The letter after the digits will be the last character to skip.
            } else if (c == X364.BRACKET) {
                return; // Found the second part of the Control Sequence Introducer (CSI).
            }
            // [ESC] was not followed by '['. Proceed as a normal character.
            isEscapeSequence = false;
        }
        /*
         * The remaining of this method is executed only if we exceeded the maximal line length.
         * First, search for the hyphen character, if any. If we find one and if it is preceeded
         * by a letter, split there. The "letter before" condition is a way to avoid to split at
         * the minus sign of negative numbers like "-99", assuming that the minus sign is preceeded
         * by a space. We cannot look at the character after since we may not know it yet.
         */
        if (++codePointCount > maximalLineLength) {
searchHyp:  for (int i=buffer.length(); i>0;) {
                final int b = buffer.codePointBefore(i);
                final int n = Character.charCount(b);
                switch (b) {
                    case '-': {
                        if (i>=n && !Character.isLetter(buffer.codePointBefore(i-n))) {
                            break;                      // Continue searching previous characters.
                        }
                        // fall through
                    }
                    case Characters.HYPHEN:
                    case Characters.SOFT_HYPHEN: {
                        transfer(i);
                        break searchHyp;
                    }
                }
                i -= n;
            }
            /*
             * At this point, all the remaining content of the buffer must move on the next line.
             * Skip the leading whitespaces on the new line.
             */
            writeLineSeparator();
            final int length = buffer.length();
            for (int i=0; i<length;) {
                final int s = buffer.codePointAt(i);
                if (!Character.isWhitespace(s)) {
                    buffer.delete(0, i);
                    break;
                }
                i += Character.charCount(s);
            }
            printableLength = buffer.length();
            codePointCount  = buffer.codePointCount(0, printableLength);
            onLineBegin(true);
        }
    }

    /**
     * Writes a single character.
     *
     * @param  c  the character to append.
     * @return a reference to this {@code Appendable}.
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public Appendable append(final char c) throws IOException {
        final int cp = toCodePoint(c);
        if (cp >= 0) {
            write(cp);
        }
        return this;
    }

    /**
     * Writes a portion of a character sequence.
     *
     * @param  sequence  the character sequence to be written.
     * @param  start     index from which to start reading characters.
     * @param  end       index of the character following the last character to read.
     * @return a reference to this {@code Appendable}.
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public Appendable append(final CharSequence sequence, int start, final int end) throws IOException {
        ArgumentChecks.ensureValidIndexRange(sequence.length(), start, end);
        if (lineSeparator == null) {
            /*
             * Use the line separator found in the submitted document, if possible.
             * If we don't find any line separator in the submitted content, leave
             * the 'lineSeparator' field to null since the 'write' method will set
             * it to the default value only if it really needs it.
             */
            lineSeparator = lineSeparator(sequence, start, end);
        }
        start = appendSurrogate(sequence, start, end);
        while (start < end) {
            final int c = toCodePoint(sequence.charAt(start++));
            if (c >= 0) {
                write(c);
            }
        }
        return this;
    }

    /**
     * Resets the {@code LineAppender} internal state as if a new line was beginning.
     * Trailing whitespaces not yet sent to the {@linkplain #out underlying appendable}
     * are discarded, and the column position (for tabulation expansion calculation) is
     * reset to 0. This method does not write any line separator.
     *
     * @throws IOException if an error occurred while sending the trailing non-white
     *         characters to the underlying stream.
     */
    public void clear() throws IOException {
        endOfLine();
        skipLF = false;
    }

    /**
     * Sends all pending characters to the underlying appendable, including trailing whitespaces.
     * Note that this method should preferably be invoked at the end of a word, sentence or line,
     * since invoking this method may prevent {@code LineAppender} to properly wrap the current
     * line if the current position is in the middle of a word.
     *
     *
Invoking this method also flushes the underlying stream, if {@linkplain Flushable flushable}.
     * A cheaper way to send pending characters is to make sure that the last character is a
     * {@linkplain Characters#isLineOrParagraphSeparator(int) line or paragraph terminator},
     * or to invoke {@link #clear()}.

     *
     * @throws IOException if an I/O error occurs.
     */
    @Override
    public void flush() throws IOException {
        out.append(buffer);
        buffer.setLength(0);
        printableLength = 0;
        IO.flush(out);
    }

    /**
     * Invoked when a new line is beginning. The default implementation does nothing,
     * but subclasses can override this method for example in order to insert a margin
     * on the left side before each line.
     *
     *
If an implementation wishes to write characters, it shall do so by writing
     * directly to {@link #out}, not by invoking the {@code append}
     * methods of this class.

     *
     * @param  isContinuation {@code true} if the new line is the continuation of the previous
     *         line after a "line wrap", or {@code false} if a line or paragraph separator has
     *         been explicitly sent to this formatter.
     * @throws IOException if an error occurred while writing to {@link #out}.
     */
    protected void onLineBegin(boolean isContinuation) throws IOException {
    }
}


text:
Feather efficiently stores pandas DataFrame objects on disk.

## Installing

```shell
pip install feather-format
```

#### Mac notes

Anaconda uses a default 10.5 deployment target which does not have C++11
properly available. This can be fixed by setting:

```
export MACOSX_DEPLOYMENT_TARGET=10.9
```

Deployments targets as early as 10.7 can be used if the compiler supports
C++11 and the correct mode is selected. For example using the following:

```
export MACOSX_DEPLOYMENT_TARGET=10.7
export CFLAGS="${CXXFLAGS} -stdlib=libc++ -std=c++11"
export CXXFLAGS="${CXXFLAGS} -stdlib=libc++ -std=c++11"
```

This may be necessary in some other OS X environments.

## Build

Building Feather requires a C++11 compiler. We've simplified the PyPI packaging
to include libfeather (the C++ core library) to be built statically as part of
the Python extension build, but this may change in the future.

### Static builds for easier packaging

At the moment, the libfeather sources are being built and linked with the
Cython extension, rather than building the `libfeather` shared library and
linking to that.

While we continue to do this, building from source requires you to symlink (or
copy) the C++ sources. See:

```shell
# Symlink the C++ library for the static build
ln -s ../cpp/src src
python setup.py build

# To install it locally
python setup.py install

# Source distribution
python setup.py sdist
```

To change this and instead link to an installed `libfeather.so`, look in
`setup.py` and make the following change:

```python
FEATHER_STATIC_BUILD = False
```

## Limitations

Some features of pandas are not supported in Feather:

* Non-string column names
* Row indexes
* Object-type columns with non-homogeneous data


text:
package org.apache.doris.catalog;

import org.apache.doris.common.DdlException;
import org.apache.doris.common.FeConstants;

import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "javax.management.*" })
@PrepareForTest(Catalog.class)
public class ColumnTest {

    private Catalog catalog;

    @Before
    public void setUp() {
        catalog = EasyMock.createMock(Catalog.class);

        PowerMock.mockStatic(Catalog.class);
        EasyMock.expect(Catalog.getInstance()).andReturn(catalog).anyTimes();
        EasyMock.expect(Catalog.getCurrentCatalogJournalVersion()).andReturn(FeConstants.meta_version).anyTimes();
        PowerMock.replay(Catalog.class);
    }

    @Test
    public void testSerialization() throws Exception {
        // 1. Write objects to file
        File file = new File("./columnTest");
        file.createNewFile();
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));

        Column column1 = new Column("user",
                                ScalarType.createChar(20), false, AggregateType.SUM, "", "");
        column1.write(dos);
        Column column2 = new Column("age",
                                ScalarType.createType(PrimitiveType.INT), false, AggregateType.REPLACE, "20", "");
        column2.write(dos);

        Column column3 = new Column("name", PrimitiveType.BIGINT);
        column3.setIsKey(true);
        column3.write(dos);

        Column column4 = new Column("age",
                                ScalarType.createType(PrimitiveType.INT), false, AggregateType.REPLACE, "20",
                                    "");
        column4.write(dos);

        dos.flush();
        dos.close();

        // 2. Read objects from file
        DataInputStream dis = new DataInputStream(new FileInputStream(file));
        Column rColumn1 = new Column();
        rColumn1.readFields(dis);
        Assert.assertEquals("user", rColumn1.getName());
        Assert.assertEquals(PrimitiveType.CHAR, rColumn1.getDataType());
        Assert.assertEquals(AggregateType.SUM, rColumn1.getAggregationType());
        Assert.assertEquals("", rColumn1.getDefaultValue());
        Assert.assertEquals(0, rColumn1.getScale());
        Assert.assertEquals(0, rColumn1.getPrecision());
        Assert.assertEquals(20, rColumn1.getStrLen());
        Assert.assertFalse(rColumn1.isAllowNull());

        // 3. Test read()
        Column rColumn2 = Column.read(dis);
        Assert.assertEquals("age", rColumn2.getName());
        Assert.assertEquals(PrimitiveType.INT, rColumn2.getDataType());
        Assert.assertEquals(AggregateType.REPLACE, rColumn2.getAggregationType());
        Assert.assertEquals("20", rColumn2.getDefaultValue());

        Column rColumn3 = Column.read(dis);
        Assert.assertTrue(rColumn3.equals(column3));

        Column rColumn4 = Column.read(dis);
        Assert.assertTrue(rColumn4.equals(column4));

        Assert.assertEquals(rColumn2.toString(), column2.toString());
        Assert.assertTrue(column1.equals(column1));
        Assert.assertFalse(column1.equals(this));

        // 4. delete files
        dis.close();
        file.delete();
    }

    @Test(expected = DdlException.class)
    public void testSchemaChangeAllowed() throws DdlException {
        Column oldColumn = new Column("user", ScalarType.createType(PrimitiveType.INT), true, null, true, "0", "");
        Column newColumn = new Column("user", ScalarType.createType(PrimitiveType.INT), true, null, false, "0", "");
        oldColumn.checkSchemaChangeAllowed(newColumn);
        Assert.fail("No exception throws.");
    }

}


text:
"""
Support for the stateful queries
"""

import cache

def save_query(client_id, query):
    """
    Save the last query `query` for the client `client_id`
    """
    cache.put("l:%s" % client_id, query)

def last_query(client_id):
    """
    Return the last query for the client `client_id`
    """
    return cache.get("l:%s" % client_id)


text:
require 'rubygems/command'
require 'rubygems/text'
require 'rubygems/installer'

class Gem::Commands::DumpCommand < Gem::Command
  VERSION = "0.0.1"
  FNAME = "/.gemfile"

  def initialize
    super("dump", "\"Dump\" all your gems to a ~/.gemfile",
          :version => Gem::Requirement.default)
  end

  def arguments # :nodoc:
    'FILE dumps to another file'
  end

  def defaults_str # :nodoc:
    "--version='>= 0'"
  end

  def usage # :nodoc:
    "#{program_name} [PATH]"
  end

  def execute
    path ||= ENV['HOME']
    unless path
      puts "No path to write.."; return
    end
    gems = `gem list --no-version`.split("\n")
    all = nil
    unless File.exists?(fname = path + FNAME)
      file = File.new(fname, "w+")
      file.puts "# Mygems dump #{Time.now}. #{gems.length} gems."
      file.puts
    else
      file = File.new(fname, "a+")
      all = file.readlines.map{ |l| l.match(/"([^\"]*)"/)[1] rescue nil }.reject(&:nil?)
      gems -= all
      return if gems.empty?
      file.puts
      file.puts "# New gems #{Time.now}."
      file.puts
    end
    for gem in gems
      file.puts "gem \"#{gem}\""
    end
    file.close
    puts "Gemfile written. #{fname}"
  end
end


text:
require_relative '../../api2600/synergy/lig_uplink_set'

module OneviewSDK
  module API2800
    module Synergy
      class LIGUplinkSet < OneviewSDK::API2600::Synergy::LIGUplinkSet
      end
    end
  end
end


text:
ACCEPTED

#### According to
International Plant Names Index

#### Published in
null

#### Original name
Cocculus tomentosus Colebr.

### Remarks
null


text:
package com.saasquatch.jsonschemainferrer.annotations;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Indicates that an element is only visible for testing.
 *
 * @author sli
 */
@Retention(RetentionPolicy.SOURCE) // Only used for documentation
@Internal
public @interface VisibleForTesting {

}


text:
package org.apache.hadoop.hdfs.server.common;

public final class Util {
  /**
   * Current system time.
   * @return current time in msec.
   */
  public static long now() {
    return System.currentTimeMillis();
  }
}


text:
<title>Add-on ECB-windows - ECB - the Emacs Code Browser</title>
<meta http-equiv="Content-Type" content="text/html">
<meta name="description" content="ECB - the Emacs Code Browser">
<meta name="generator" content="makeinfo 4.8">
<link title="Top" rel="start" href="ecb.html#Top">
<link rel="up" href="ECB_002dwindows.html#ECB_002dwindows" title="ECB-windows">
<link rel="prev" href="Basic-ECB_002dwindows.html#Basic-ECB_002dwindows" title="Basic ECB-windows">
<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
<meta http-equiv="Content-Style-Type" content="text/css">

Previous: Basic ECB-windows,
Up: ECB-windows

4.2 Add-on ECB-windows of ECB

This chapter gives detailled informations about available add-on
ECB-windows. This includes basic desciptions what they do as well as
how to use them.

Semantic analyser:            Analyses and displays semantic context

Symboldefinition:             Displays definition of current symbol


text:
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

$string['allowattachments'] = 'Allow attachments';
$string['attachmentsoptional'] = 'Attachments are optional';
$string['attachmentsrequired'] = 'Require attachments';
$string['attachmentsrequired_help'] = 'This option specifies the minimum number of attachments required for a response to be considered gradable.';
$string['formateditor'] = 'HTML editor';
$string['formateditorfilepicker'] = 'HTML editor with file picker';
$string['formatmonospaced'] = 'Plain text, monospaced font';
$string['formatnoinline'] = 'No online text';
$string['formatplain'] = 'Plain text';
$string['graderinfo'] = 'Information for graders';
$string['graderinfoheader'] = 'Grader Information';
$string['mustattach'] = 'When "No online text" is selected, or responses are optional, you must allow at least one attachment.';
$string['mustrequire'] = 'When "No online text" is selected, or responses are optional, you must require at least one attachment.';
$string['mustrequirefewer'] = 'You cannot require more attachments than you allow.';
$string['nlines'] = '{$a} lines';
$string['pluginname'] = 'Essay';
$string['pluginname_help'] = 'In response to a question, the respondent may upload one or more files and/or enter text online. A response template may be provided. Responses must be graded manually.';
$string['pluginname_link'] = 'question/type/essay';
$string['pluginnameadding'] = 'Adding an Essay question';
$string['pluginnameediting'] = 'Editing an Essay question';
$string['pluginnamesummary'] = 'Allows a response of a file upload and/or online text. This must then be graded manually.';
$string['responsefieldlines'] = 'Input box size';
$string['responseformat'] = 'Response format';
$string['responseoptions'] = 'Response Options';
$string['responserequired'] = 'Require text';
$string['responsenotrequired'] = 'Text input is optional';
$string['responseisrequired'] = 'Require the student to enter text';
$string['responsetemplate'] = 'Response template';
$string['responsetemplateheader'] = 'Response Template';
$string['responsetemplate_help'] = 'Any text entered here will be displayed in the response input box when a new attempt at the question starts.';