<commit_before>//=============================================================================
// SnowMV - Simple Gathering
// SnowGather.js
// Version: 1.3.2
//=============================================================================

"use strict";

PluginManager.register("SnowGather", "1.3.2", {
	"email": "",
	"website": "",
	"name": "Sn0wCrack"
}, "2015-10-25")

//=============================================================================
 /*:
 * @plugindesc A simple way to manage "gathering" type events.
 * @author Sn0wCrack
 *
 * @param Successful Harvest Message
 * @desc Message to display upon sucessful harvest, %1 is the amount, %2 is the item name
 * @default You gathered %1 %2.
 *
 * @param Unsuccessful Harvest Message
 * @desc Message to display upon failing to harvest items
 * @default Failed to harvest any resources.
 *
 * @param Item Broken Message
 * @desc Message to display when an item breaks upon harvesting, %1 is the item name
 * @default Your %1 broke while harvesting!
 *
 * @param Incorrect Tools Message
 * @desc Message to display when you don't have the right tools, %1 is the tools formatted for grammar
 * @default You don't have the right tool for the job, you need\n%1\nTo gather items here.
 *
 * @param Respawning Events
 * @desc Override the default option when having a time system installed, true = ON, false = OFF
 * @default true
 *
 * @param Manual Self Switches
 * @desc If you wish to manually set self switches in your event for despawning them, true = ON, false = OFF
 * @default false
 *
 * @param Last Result Store
 * @desc If you want to store the last result of a harvest as a variable, true = ON, false = OFF
 * @default true
 *
 * @param Last Result Variable ID
 * @desc The variable to store the last result in, 0 = success, 1 = failure, 2 = incorrect tools
 * @default 1
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * Welcome to the Simple Gathering System by Sn0wCrack, this script allows you
 * to create gathering events in which you players to search for resources using
 * tools (or not if you want!)
 *
 * Please see https://github.com/Sn0wCrack/SnowMV/blob/master/LICENSE for license
 * details.
 *
 * If you use this script, please give credit!
 *
 * ============================================================================
 * Setup
 * ============================================================================
 * In your items database you can tag any item (this means only normal items,
 * not weapons, armour, etc.) with the following tags:
 *
 * Havesting Tools:
 *
 * <Harvest Chance Boost: x%> - Not Required
 * This tag allows you to set an amount this particular item will boost
 * the chances of you finding items.
 *
 * <Chance Break: x%> - Required
 * This tag allows you to set the percentage of the time the tool will,
 * break after use, e.g. 90% will break 90% of the time, 0% will never break.
 *
 *
 * Harvestables:
 *
 * <Harvest Chance: x%> - Required
 * The percentage chance of finding the item from gathering, e.g
 * 90% wil be found 90% of the time, 100% will always be found.
 *
 * <Harvest Minimum: x> - Required
 * The minimum amount of the item to be found upon the successful harvesting of
 * it.
 *
 * <Harvest Maximum: x> - Required
 * The maximum amount of the item to be found upon the successful harvesting of
 * it.
 *
 * Events:
 *
 * <Repspawn Time: x> - Required if you want respawing events (Requires OrangeTimeSystem)
 * Sets how long in hours you want the event to respawn in
 *
 * ============================================================================
 * Usage
 * ============================================================================
 * To call the script, create an event with a plugin command
 *		SnowGather [x] [y] this
 * Let me explain how this works: replace x with the id of the item you want
 * the player to use, if you want them to use more than one item replace it
 * with something like: [x,y,z] NO SPACES BETWEEN THE ITEMS.
 *
 * y is the id of the item you want the player to harvest from this event,
 * again if you want them to have a chance of getting more than one item
 * use something like this: [x,y,z] NO SPACES BETWEEN THE ITEMS.
 *
 * this is well, always the word this, this is only required if you're using
 * a time system.
 *
 * If you don't want the event to require the usage of a tool, replace [x] with
 * false.
 *
 * Repsawning Events:
 *
 * In order for an event to actually despawn and then respawn afte the allotted
 * time, you must first create second event page, on this page you just have to
 * have the self switch for "A" checked as a condition, leave everything else
 * blank.
 *
 */
//=============================================================================

var Snow = Snow || {};
Snow.Gather = Snow.Gather || {};
Snow.Gather.Parameters = PluginManager.parameters("SnowGather");
Snow.Gather.PopEvents = false;

Snow.Gather.WaitingEvents = [];

Snow.Gather.DataManager_isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function() {
    if (!Snow.Gather.DataManager_isDatabaseLoaded.call(this)) {
		return false;
	}
	this.processNotetagsSnowGather($dataItems);
	return true;
};

if (Imported["OrangeTimeSystem"] && MVC.Boolean(String(Snow.Gather.Parameters["Respawning Events"]))) {
	Snow.Gather.PopEvents = true;
	Snow.Gather.onChangeHour = function() {
		console.log("1 Hour Passess...");
		for (var i = 0; i < Snow.Gather.WaitingEvents.length; i++) {
			Snow.Gather.WaitingEvents[i].timeRemaining -= 1;
			if($dataMap.events[Snow.Gather.WaitingEvents[i].eventData.id] == Snow.Gather.WaitingEvents[i].eventData && Snow.Gather.WaitingEvents[i].timeRemaining == 0)
			{
				var key = [$gameMap.mapId(), Snow.Gather.WaitingEvents[i].eventData.id, "A"];
				$gameSelfSwitches.setValue(key, false);
				Snow.Gather.WaitingEvents.splice(i, 1);
			}
		}
	}
	OrangeTimeSystem.on('changeHour', Snow.Gather.onChangeHour);
}

DataManager.processNotetagsSnowGather = function(group) {
	var note1 = /<(?:HARVEST CHANCE BOOST):[ ](\d+)([%%])>/i;
	var note2 = /<(?:CHANCE BREAK):[ ](\d+)([%%])>/i;
	var note3 = /<(?:HARVEST CHANCE):[ ](\d+)([%%])>/i;
	var note4 = /<(?:HARVEST MINIMUM):[ ](\d+)>/i;
	var note5 = /<(?:HARVEST MAXIMUM):[ ](\d+)>/i;

	for (var i = 1; i < group.length; i++) {
		var obj = group[i];
		var notedata = obj.note.split(/[\r\n]+/);
		for (var n = 0; n < notedata.length; n++) {
			var line = notedata[n];
			if (line.match(note1)) {
				obj.harvestChanceBoost = parseFloat(RegExp.$1 * 0.01);
			}
			if (line.match(note2)) {
				obj.chanceBreak = parseFloat(RegExp.$1 * 0.01);
			}
			if (line.match(note3)) {
				obj.chanceHarvest = parseFloat(RegExp.$1 * 0.01);
			}
			if (line.match(note4)) {
				obj.harvestMinimum = parseInt(RegExp.$1);
			}
			if (line.match(note5)) {
				obj.harvestMaximum = parseInt(RegExp.$1);
			}
		}
	}
}

Snow.Gather.idIntoItem = function(id) {
	return $dataItems[id];
}

Snow.Gather.RandomInt = function() {
	return Math.random();
}

Snow.Gather.RandomIntRange = function(min, max) {
	return Math.floor(Math.random() * (max - min + 1)) + min;
}

Snow.Gather.Round = function(value, decimals) {
	return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
}

Snow.Gather.Gather = function(requiredItems, recievableItems, event) {
	event = event || {_eventId: 0};
	var eventId = event._eventId;
	if (requiredItems == false) {
		var itemisedRecievableItems = [];
		for (var i = 0; i < recievableItems.length; i++) {
			itemisedRecievableItems[i] = Snow.Gather.idIntoItem(recievableItems[i]);
		}

		for (var i = 0; i < itemisedRecievableItems.length; i++) {
			var gen = Snow.Gather.Round(Snow.Gather.RandomInt(), 2);
			if (gen <= itemisedRecievableItems[i].chanceHarvest) {
				var itemGathered = Snow.Gather.RandomIntRange(itemisedRecievableItems[i].harvestMinimum, itemisedRecievableItems[i].harvestMaximum);
				$gameParty.gainItem(itemisedRecievableItems[i], itemGathered);
				if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
					$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 0);
				}
				$gameMessage.add(Snow.Gather.Parameters["Successful Harvest Message"].replace("%1", itemGathered).replace("%2", itemisedRecievableItems[i].name));
			} else {
				if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
					$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 1);
				}
				$gameMessage.add(Snow.Gather.Parameters["Unsuccessful Harvest Message"]);
			}
		}
		if (Snow.Gather.PopEvents) {
			Snow.Gather.WaitingEvents.push({
				eventData: $dataMap.events[eventId],
				timeRemaining: Number($dataMap.events[eventId].meta["Respawn Time"])
			});
			if (!MVC.Boolean(String(Snow.Gather.Parameters["Manual Self Switches"])))
			{
				$gameSelfSwitches.setValue([$gameMap.mapId(), eventId, "A"], true);
			}
		}
	} else {
		var itemisedRequiredItems = [];
		var itemisedRecievableItems = [];
		for (var i = 0; i < requiredItems.length; i++) {
			itemisedRequiredItems[i] = Snow.Gather.idIntoItem(requiredItems[i]);
		}
		for (var i = 0; i < recievableItems.length; i++) {
			itemisedRecievableItems[i] = Snow.Gather.idIntoItem(recievableItems[i]);
		}

		var gotItems = [];
		for (var i = 0; i < requiredItems.length; i++) {
			if ($gameParty.hasItem(itemisedRequiredItems[i])) {
				gotItems[i] = itemisedRequiredItems[i];
			}
		}

		if (gotItems.length == requiredItems.length) {
			var totalHarvestBoost = 0.0;
			for (var i = 0; i < itemisedRequiredItems.length; i++) {
				if (itemisedRequiredItems[i].harvestChanceBoost) {
					totalHarvestBoost += itemisedRequiredItems[i].harvestChanceBoost;
				}
			}

			for (var i = 0; i < itemisedRecievableItems.length; i++) {
				var gen = Snow.Gather.Round(Math.max(0, Snow.Gather.RandomInt() - totalHarvestBoost), 2);
				if (gen <= itemisedRecievableItems[i].chanceHarvest) {
					var itemGathered = Snow.Gather.RandomIntRange(itemisedRecievableItems[i].harvestMinimum, itemisedRecievableItems[i].harvestMaximum);
					$gameParty.gainItem(itemisedRecievableItems[i], itemGathered);
					if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
						$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 0);
					}
					$gameMessage.add(Snow.Gather.Parameters["Successful Harvest Message"].replace("%1", itemGathered).replace("%2", itemisedRecievableItems[i].name));
				} else {
					if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
						$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 1);
					}
					$gameMessage.add(Snow.Gather.Parameters["Unsuccessful Harvest Message"]);
				}
			}

			for (var i = 0; i < itemisedRequiredItems.length; i++) {
				if (Snow.Gather.RandomInt() < itemisedRequiredItems[i].chanceBreak) {
					$gameParty.loseItem(itemisedRequiredItems[i], 1);
					$gameMessage.add(Snow.Gather.Parameters["Item Broken Message"].replace("%1", itemisedRequiredItems[i].name));
				}
			}

			if (Snow.Gather.PopEvents) {
				Snow.Gather.WaitingEvents.push({
					eventData: $dataMap.events[eventId],
					timeRemaining: Number($dataMap.events[eventId].meta["Respawn Time"])
				});
				if (!MVC.Boolean(String(Snow.Gather.Parameters["Manual Self Switches"])))
				{
					$gameSelfSwitches.setValue([$gameMap.mapId(), eventId, "A"], true);
				}
			}

		} else {
			var concatItems = "";
			for (var i = 0; i < itemisedRequiredItems.length; i++)
			{
				if (i == itemisedRequiredItems.length - 1)
				{
					concatItems += "and ";
				}
				concatItems += "a " + itemisedRequiredItems[i].name;
				if (i != itemisedRequiredItems.length - 1)
				{
					concatItems += ", ";
				}
			}
			var lines = String(Snow.Gather.Parameters["Incorrect Tools Message"]).split("\\n");
			if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
				$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 2);
			}
			for (var i = 0; i < lines.length; i++) {
				$gameMessage.add(lines[i].replace("%1", concatItems));
			}
		}
	}
}

var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
	_Game_Interpreter_pluginCommand.call(this, command, args);
	if (command === "SnowGather") {
		if (args[2]) {
			Snow.Gather.Gather(JSON.parse(args[0]), JSON.parse(args[1]), eval(args[2]));
		} else {
			Snow.Gather.Gather(JSON.parse(args[0]), JSON.parse(args[1]))
		}

	}
}<commit_msg>This should stop any collisions with other plugins<commit_after>//=============================================================================
// SnowMV - Simple Gathering
// SnowGather.js
// Version: 1.3.2
//=============================================================================

"use strict";

PluginManager.register("SnowGather", "1.3.2", {
	"email": "",
	"website": "",
	"name": "Sn0wCrack"
}, "2015-10-25")

//=============================================================================
 /*:
 * @plugindesc A simple way to manage "gathering" type events.
 * @author Sn0wCrack
 *
 * @param Successful Harvest Message
 * @desc Message to display upon sucessful harvest, %1 is the amount, %2 is the item name
 * @default You gathered %1 %2.
 *
 * @param Unsuccessful Harvest Message
 * @desc Message to display upon failing to harvest items
 * @default Failed to harvest any resources.
 *
 * @param Item Broken Message
 * @desc Message to display when an item breaks upon harvesting, %1 is the item name
 * @default Your %1 broke while harvesting!
 *
 * @param Incorrect Tools Message
 * @desc Message to display when you don't have the right tools, %1 is the tools formatted for grammar
 * @default You don't have the right tool for the job, you need\n%1\nTo gather items here.
 *
 * @param Respawning Events
 * @desc Override the default option when having a time system installed, true = ON, false = OFF
 * @default true
 *
 * @param Manual Self Switches
 * @desc If you wish to manually set self switches in your event for despawning them, true = ON, false = OFF
 * @default false
 *
 * @param Last Result Store
 * @desc If you want to store the last result of a harvest as a variable, true = ON, false = OFF
 * @default true
 *
 * @param Last Result Variable ID
 * @desc The variable to store the last result in, 0 = success, 1 = failure, 2 = incorrect tools
 * @default 1
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * Welcome to the Simple Gathering System by Sn0wCrack, this script allows you
 * to create gathering events in which you players to search for resources using
 * tools (or not if you want!)
 *
 * Please see https://github.com/Sn0wCrack/SnowMV/blob/master/LICENSE for license
 * details.
 *
 * If you use this script, please give credit!
 *
 * ============================================================================
 * Setup
 * ============================================================================
 * In your items database you can tag any item (this means only normal items,
 * not weapons, armour, etc.) with the following tags:
 *
 * Havesting Tools:
 *
 * <Harvest Chance Boost: x%> - Not Required
 * This tag allows you to set an amount this particular item will boost
 * the chances of you finding items.
 *
 * <Chance Break: x%> - Required
 * This tag allows you to set the percentage of the time the tool will,
 * break after use, e.g. 90% will break 90% of the time, 0% will never break.
 *
 *
 * Harvestables:
 *
 * <Harvest Chance: x%> - Required
 * The percentage chance of finding the item from gathering, e.g
 * 90% wil be found 90% of the time, 100% will always be found.
 *
 * <Harvest Minimum: x> - Required
 * The minimum amount of the item to be found upon the successful harvesting of
 * it.
 *
 * <Harvest Maximum: x> - Required
 * The maximum amount of the item to be found upon the successful harvesting of
 * it.
 *
 * Events:
 *
 * <Repspawn Time: x> - Required if you want respawing events (Requires OrangeTimeSystem)
 * Sets how long in hours you want the event to respawn in
 *
 * ============================================================================
 * Usage
 * ============================================================================
 * To call the script, create an event with a plugin command
 *		SnowGather [x] [y] this
 * Let me explain how this works: replace x with the id of the item you want
 * the player to use, if you want them to use more than one item replace it
 * with something like: [x,y,z] NO SPACES BETWEEN THE ITEMS.
 *
 * y is the id of the item you want the player to harvest from this event,
 * again if you want them to have a chance of getting more than one item
 * use something like this: [x,y,z] NO SPACES BETWEEN THE ITEMS.
 *
 * this is well, always the word this, this is only required if you're using
 * a time system.
 *
 * If you don't want the event to require the usage of a tool, replace [x] with
 * false.
 *
 * Repsawning Events:
 *
 * In order for an event to actually despawn and then respawn afte the allotted
 * time, you must first create second event page, on this page you just have to
 * have the self switch for "A" checked as a condition, leave everything else
 * blank.
 *
 */
//=============================================================================

var Snow = Snow || {};
Snow.Gather = Snow.Gather || {};
Snow.Gather.Parameters = PluginManager.parameters("SnowGather");
Snow.Gather.PopEvents = false;

Snow.Gather.WaitingEvents = [];

Snow.Gather.DataManager_isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function() {
    if (!Snow.Gather.DataManager_isDatabaseLoaded.call(this)) {
		return false;
	}
	this.processNotetagsSnowGather($dataItems);
	return true;
};

if (Imported["OrangeTimeSystem"] && MVC.Boolean(String(Snow.Gather.Parameters["Respawning Events"]))) {
	Snow.Gather.PopEvents = true;
	Snow.Gather.onChangeHour = function() {
		console.log("1 Hour Passess...");
		for (var i = 0; i < Snow.Gather.WaitingEvents.length; i++) {
			Snow.Gather.WaitingEvents[i].timeRemaining -= 1;
			if($dataMap.events[Snow.Gather.WaitingEvents[i].eventData.id] == Snow.Gather.WaitingEvents[i].eventData && Snow.Gather.WaitingEvents[i].timeRemaining == 0)
			{
				var key = [$gameMap.mapId(), Snow.Gather.WaitingEvents[i].eventData.id, "A"];
				$gameSelfSwitches.setValue(key, false);
				Snow.Gather.WaitingEvents.splice(i, 1);
			}
		}
	}
	OrangeTimeSystem.on('changeHour', Snow.Gather.onChangeHour);
}

DataManager.processNotetagsSnowGather = function(group) {
	var note1 = /<(?:HARVEST CHANCE BOOST):[ ](\d+)([%%])>/i;
	var note2 = /<(?:CHANCE BREAK):[ ](\d+)([%%])>/i;
	var note3 = /<(?:HARVEST CHANCE):[ ](\d+)([%%])>/i;
	var note4 = /<(?:HARVEST MINIMUM):[ ](\d+)>/i;
	var note5 = /<(?:HARVEST MAXIMUM):[ ](\d+)>/i;

	for (var i = 1; i < group.length; i++) {
		var obj = group[i];
		var notedata = obj.note.split(/[\r\n]+/);
		for (var n = 0; n < notedata.length; n++) {
			var line = notedata[n];
			if (line.match(note1)) {
				obj.harvestChanceBoost = parseFloat(RegExp.$1 * 0.01);
			}
			if (line.match(note2)) {
				obj.chanceBreak = parseFloat(RegExp.$1 * 0.01);
			}
			if (line.match(note3)) {
				obj.chanceHarvest = parseFloat(RegExp.$1 * 0.01);
			}
			if (line.match(note4)) {
				obj.harvestMinimum = parseInt(RegExp.$1);
			}
			if (line.match(note5)) {
				obj.harvestMaximum = parseInt(RegExp.$1);
			}
		}
	}
}

Snow.Gather.idIntoItem = function(id) {
	return $dataItems[id];
}

Snow.Gather.RandomInt = function() {
	return Math.random();
}

Snow.Gather.RandomIntRange = function(min, max) {
	return Math.floor(Math.random() * (max - min + 1)) + min;
}

Snow.Gather.Round = function(value, decimals) {
	return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
}

Snow.Gather.Gather = function(requiredItems, recievableItems, event) {
	event = event || {_eventId: 0};
	var eventId = event._eventId;
	if (requiredItems == false) {
		var itemisedRecievableItems = [];
		for (var i = 0; i < recievableItems.length; i++) {
			itemisedRecievableItems[i] = Snow.Gather.idIntoItem(recievableItems[i]);
		}

		for (var i = 0; i < itemisedRecievableItems.length; i++) {
			var gen = Snow.Gather.Round(Snow.Gather.RandomInt(), 2);
			if (gen <= itemisedRecievableItems[i].chanceHarvest) {
				var itemGathered = Snow.Gather.RandomIntRange(itemisedRecievableItems[i].harvestMinimum, itemisedRecievableItems[i].harvestMaximum);
				$gameParty.gainItem(itemisedRecievableItems[i], itemGathered);
				if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
					$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 0);
				}
				$gameMessage.add(Snow.Gather.Parameters["Successful Harvest Message"].replace("%1", itemGathered).replace("%2", itemisedRecievableItems[i].name));
			} else {
				if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
					$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 1);
				}
				$gameMessage.add(Snow.Gather.Parameters["Unsuccessful Harvest Message"]);
			}
		}
		if (Snow.Gather.PopEvents) {
			Snow.Gather.WaitingEvents.push({
				eventData: $dataMap.events[eventId],
				timeRemaining: Number($dataMap.events[eventId].meta["Respawn Time"])
			});
			if (!MVC.Boolean(String(Snow.Gather.Parameters["Manual Self Switches"])))
			{
				$gameSelfSwitches.setValue([$gameMap.mapId(), eventId, "A"], true);
			}
		}
	} else {
		var itemisedRequiredItems = [];
		var itemisedRecievableItems = [];
		for (var i = 0; i < requiredItems.length; i++) {
			itemisedRequiredItems[i] = Snow.Gather.idIntoItem(requiredItems[i]);
		}
		for (var i = 0; i < recievableItems.length; i++) {
			itemisedRecievableItems[i] = Snow.Gather.idIntoItem(recievableItems[i]);
		}

		var gotItems = [];
		for (var i = 0; i < requiredItems.length; i++) {
			if ($gameParty.hasItem(itemisedRequiredItems[i])) {
				gotItems[i] = itemisedRequiredItems[i];
			}
		}

		if (gotItems.length == requiredItems.length) {
			var totalHarvestBoost = 0.0;
			for (var i = 0; i < itemisedRequiredItems.length; i++) {
				if (itemisedRequiredItems[i].harvestChanceBoost) {
					totalHarvestBoost += itemisedRequiredItems[i].harvestChanceBoost;
				}
			}

			for (var i = 0; i < itemisedRecievableItems.length; i++) {
				var gen = Snow.Gather.Round(Math.max(0, Snow.Gather.RandomInt() - totalHarvestBoost), 2);
				if (gen <= itemisedRecievableItems[i].chanceHarvest) {
					var itemGathered = Snow.Gather.RandomIntRange(itemisedRecievableItems[i].harvestMinimum, itemisedRecievableItems[i].harvestMaximum);
					$gameParty.gainItem(itemisedRecievableItems[i], itemGathered);
					if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
						$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 0);
					}
					$gameMessage.add(Snow.Gather.Parameters["Successful Harvest Message"].replace("%1", itemGathered).replace("%2", itemisedRecievableItems[i].name));
				} else {
					if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
						$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 1);
					}
					$gameMessage.add(Snow.Gather.Parameters["Unsuccessful Harvest Message"]);
				}
			}

			for (var i = 0; i < itemisedRequiredItems.length; i++) {
				if (Snow.Gather.RandomInt() < itemisedRequiredItems[i].chanceBreak) {
					$gameParty.loseItem(itemisedRequiredItems[i], 1);
					$gameMessage.add(Snow.Gather.Parameters["Item Broken Message"].replace("%1", itemisedRequiredItems[i].name));
				}
			}

			if (Snow.Gather.PopEvents) {
				Snow.Gather.WaitingEvents.push({
					eventData: $dataMap.events[eventId],
					timeRemaining: Number($dataMap.events[eventId].meta["Respawn Time"])
				});
				if (!MVC.Boolean(String(Snow.Gather.Parameters["Manual Self Switches"])))
				{
					$gameSelfSwitches.setValue([$gameMap.mapId(), eventId, "A"], true);
				}
			}

		} else {
			var concatItems = "";
			for (var i = 0; i < itemisedRequiredItems.length; i++)
			{
				if (i == itemisedRequiredItems.length - 1)
				{
					concatItems += "and ";
				}
				concatItems += "a " + itemisedRequiredItems[i].name;
				if (i != itemisedRequiredItems.length - 1)
				{
					concatItems += ", ";
				}
			}
			var lines = String(Snow.Gather.Parameters["Incorrect Tools Message"]).split("\\n");
			if (MVC.Boolean(String(Snow.Gather.Parameters["Last Result Store"]))) {
				$gameVariables.setValue(Number(Snow.Gather.Parameters["Last Result Variable ID"]), 2);
			}
			for (var i = 0; i < lines.length; i++) {
				$gameMessage.add(lines[i].replace("%1", concatItems));
			}
		}
	}
}

Snow.Gather.Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
	Snow.Gather.Game_Interpreter_pluginCommand.call(this, command, args);
	if (command === "SnowGather") {
		if (args[2]) {
			Snow.Gather.Gather(JSON.parse(args[0]), JSON.parse(args[1]), eval(args[2]));
		} else {
			Snow.Gather.Gather(JSON.parse(args[0]), JSON.parse(args[1]))
		}

	}
}<|endoftext|>


<commit_before>import { module, skip } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | cardstack header', function(hooks) {
  setupRenderingTest(hooks);

  skip('it renders', async function(assert) {
    await render(hbs`{{cardstack-header}}`);
    // assert.equal(this.$('label:contains(Editor)').length, 1, 'Found editor switch');
  });
});
<commit_msg>Fix test<commit_after>import { module, skip } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | cardstack header', function(hooks) {
  setupRenderingTest(hooks);

  skip('it renders', async function(assert) {
    await render(hbs`{{cardstack-header}}`);
    assert.equal(this.$('label:contains(Editor)').length, 1, 'Found editor switch');
  });
});
<|endoftext|>


<commit_before>import { Settings } from "api"
import CustomDateInput from "components/CustomDateInput"
import * as FieldHelper from "components/FieldHelper"
import Fieldset from "components/Fieldset"
import LikertScale from "components/graphs/LikertScale"
import Model, {
  CUSTOM_FIELD_TYPE,
  createYupObjectShape
} from "components/Model"
import { FastField, FieldArray } from "formik"
import { JSONPath } from "jsonpath-plus"
import _cloneDeep from "lodash/cloneDeep"
import _debounce from "lodash/debounce"
import _isEmpty from "lodash/isEmpty"
import _isEqualWith from "lodash/isEqualWith"
import _set from "lodash/set"
import _upperFirst from "lodash/upperFirst"
import moment from "moment"
import PropTypes from "prop-types"
import React, { useEffect, useMemo, useRef, useState } from "react"
import { Button, HelpBlock } from "react-bootstrap"
import REMOVE_ICON from "resources/delete.png"
import utils from "utils"

const WIDGETS = {
  likertScale: LikertScale
}
const RENDERERS = {}

const DEFAULT_CUSTOM_FIELDS_PREFIX = "formCustomFields"

const SpecialField = fieldProps => {
  const { widget, ...otherFieldProps } = fieldProps
  const SpecialFieldWidget = WIDGETS[widget]
  return (
    <FastField
      component={FieldHelper.SpecialField}
      widget={<SpecialFieldWidget />}
      {...otherFieldProps}
    />
  )
}

const ReadonlySpecialField = fieldProps =>
  SpecialField({ ...fieldProps, readonly: true })

const TextField = fieldProps => {
  const { onChange, onBlur, ...otherFieldProps } = fieldProps
  return (
    <FastField
      onChange={value => onChange(value, false)}
      component={FieldHelper.InputField}
      {...otherFieldProps}
    />
  )
}

const ReadonlyTextField = fieldProps => {
  const { name, label } = fieldProps
  return (
    <FastField
      name={name}
      label={label}
      component={FieldHelper.ReadonlyField}
    />
  )
}

const DateField = fieldProps => {
  const { name, withTime, ...otherFieldProps } = fieldProps
  return (
    <FastField
      name={name}
      component={FieldHelper.SpecialField}
      widget={<CustomDateInput id={name} withTime={withTime} />}
      {...otherFieldProps}
    />
  )
}

const ReadonlyDateField = fieldProps => {
  const { name, label, withTime } = fieldProps
  return (
    <FastField
      name={name}
      label={label}
      component={FieldHelper.ReadonlyField}
      humanValue={fieldVal =>
        fieldVal &&
        moment(fieldVal).format(
          withTime
            ? Settings.dateFormats.forms.displayShort.withTime
            : Settings.dateFormats.forms.displayShort.date
        )}
    />
  )
}

const DateTimeField = props => <DateField {...props} withTime />

const ReadonlyDateTimeField = props => <ReadonlyDateField {...props} withTime />

const EnumField = fieldProps => {
  const { choices, renderer, ...otherFieldProps } = fieldProps
  return (
    <FastField
      buttons={FieldHelper.customEnumButtons(choices)}
      component={RENDERERS[renderer] || FieldHelper.RadioButtonToggleGroupField}
      {...otherFieldProps}
    />
  )
}

const enumHumanValue = (choices, fieldVal) => {
  if (Array.isArray(fieldVal)) {
    return fieldVal && fieldVal.map(k => choices[k].label).join(", ")
  } else {
    return fieldVal && choices[fieldVal].label
  }
}

const ReadonlyEnumField = fieldProps => {
  const { name, label, choices } = fieldProps
  return (
    <FastField
      name={name}
      label={label}
      component={FieldHelper.ReadonlyField}
      humanValue={fieldVal => enumHumanValue(choices, fieldVal)}
    />
  )
}

const EnumSetField = fieldProps => {
  const { choices, renderer, ...otherFieldProps } = fieldProps
  return (
    <FastField
      buttons={FieldHelper.customEnumButtons(choices)}
      component={
        RENDERERS[renderer] || FieldHelper.CheckboxButtonToggleGroupField
      }
      {...otherFieldProps}
    />
  )
}

const getArrayObjectValue = (values, fieldName) => {
  const nameKeys = fieldName.split(".")
  return nameKeys.reduce((v, key) => (v && v[key] ? v[key] : []), values)
}

const ArrayOfObjectsField = fieldProps => {
  const {
    name,
    fieldConfig,
    formikProps,
    invisibleFields,
    updateInvisibleFields
  } = fieldProps
  const value = useMemo(() => getArrayObjectValue(formikProps.values, name), [
    formikProps.values,
    name
  ])
  const objDefault = useMemo(() => {
    const objDefault = {}
    const objSchema = createYupObjectShape(fieldConfig.objectFields)
    return Model.fillObject(objDefault, objSchema)
  }, [fieldConfig.objectFields])
  const fieldsetTitle = fieldConfig.label || ""
  const addButtonLabel = fieldConfig.addButtonLabel || "Add a new item"
  return (
    <Fieldset title={fieldsetTitle}>
      <FieldArray
        name={name}
        render={arrayHelpers => (

            <Button
              className="pull-right"
              onClick={() => addObject(objDefault, arrayHelpers)}
              bsStyle="primary"
              id="addObjectButton"
            >
              {addButtonLabel}
            </Button>
            {value.map((obj, index) => (
              <ArrayObject
                key={index}
                fieldName={name}
                fieldConfig={fieldConfig}
                formikProps={formikProps}
                invisibleFields={invisibleFields}
                updateInvisibleFields={updateInvisibleFields}
                arrayHelpers={arrayHelpers}
                index={index}
              />
            ))}

        )}
      />
    </Fieldset>
  )
}

const ArrayObject = ({
  fieldName,
  fieldConfig,
  formikProps,
  invisibleFields,
  updateInvisibleFields,
  arrayHelpers,
  index
}) => {
  const objLabel = _upperFirst(fieldConfig.objectLabel || "item")
  return (
    <Fieldset title={`${objLabel} ${index + 1}`}>
      <Button
        className="pull-right"
        title={`Remove this ${objLabel}`}
        onClick={() => arrayHelpers.remove(index)}
      >
        <img src={REMOVE_ICON} height={14} alt={`Remove this ${objLabel}`} />
      </Button>
      <CustomFields
        fieldsConfig={fieldConfig.objectFields}
        formikProps={formikProps}
        invisibleFields={invisibleFields}
        updateInvisibleFields={updateInvisibleFields}
        fieldNamePrefix={`${fieldName}.${index}`}
      />
    </Fieldset>
  )
}
ArrayObject.propTypes = {
  fieldName: PropTypes.string.isRequired,
  fieldConfig: PropTypes.object.isRequired,
  formikProps: PropTypes.object.isRequired,
  invisibleFields: PropTypes.array.isRequired,
  updateInvisibleFields: PropTypes.func.isRequired,
  arrayHelpers: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired
}

const addObject = (objDefault, arrayHelpers) => {
  arrayHelpers.push(objDefault)
}

const ReadonlyArrayOfObjectsField = fieldProps => {
  const { name, fieldConfig, formikProps } = fieldProps
  const value = useMemo(() => getArrayObjectValue(formikProps.values, name), [
    formikProps.values,
    name
  ])
  const fieldsetTitle = fieldConfig.label || ""
  return (
    <Fieldset title={fieldsetTitle}>
      <FieldArray
        name={name}
        render={arrayHelpers => (

            {value.map((obj, index) => (
              <ReadonlyArrayObject
                key={index}
                fieldName={name}
                fieldConfig={fieldConfig}
                formikProps={formikProps}
                index={index}
              />
            ))}

        )}
      />
    </Fieldset>
  )
}

const ReadonlyArrayObject = ({
  fieldName,
  fieldConfig,
  formikProps,
  index
}) => {
  const objLabel = _upperFirst(fieldConfig.objectLabel || "item")
  return (
    <Fieldset title={`${objLabel} ${index + 1}`}>
      <ReadonlyCustomFields
        fieldsConfig={fieldConfig.objectFields}
        formikProps={formikProps}
        fieldNamePrefix={`${fieldName}.${index}`}
      />
    </Fieldset>
  )
}
ReadonlyArrayObject.propTypes = {
  fieldName: PropTypes.string.isRequired,
  fieldConfig: PropTypes.object.isRequired,
  formikProps: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired
}

const FIELD_COMPONENTS = {
  [CUSTOM_FIELD_TYPE.TEXT]: TextField,
  [CUSTOM_FIELD_TYPE.NUMBER]: TextField,
  [CUSTOM_FIELD_TYPE.DATE]: DateField,
  [CUSTOM_FIELD_TYPE.DATETIME]: DateTimeField,
  [CUSTOM_FIELD_TYPE.ENUM]: EnumField,
  [CUSTOM_FIELD_TYPE.ENUMSET]: EnumSetField,
  [CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS]: ArrayOfObjectsField,
  [CUSTOM_FIELD_TYPE.SPECIAL_FIELD]: SpecialField
}

function getInvisibleFields(
  invisibleFields,
  fieldsConfig,
  fieldNamePrefix,
  formikValues
) {
  const prevInvisibleFields = _cloneDeep(invisibleFields)
  const turnedInvisible = []
  const turnedVisible = []
  let curInvisibleFields = []
  Object.keys(fieldsConfig).forEach(key => {
    const fieldConfig = fieldsConfig[key]
    const fieldName = `${fieldNamePrefix}.${key}`
    const isVisible =
      !fieldConfig.visibleWhen ||
      (fieldConfig.visibleWhen &&
        !_isEmpty(JSONPath(fieldConfig.visibleWhen, formikValues)))
    if (!isVisible && !prevInvisibleFields.includes(fieldName)) {
      turnedInvisible.push(fieldName)
    } else if (isVisible && prevInvisibleFields.includes(fieldName)) {
      turnedVisible.push(fieldName)
    }
  })
  if (turnedVisible.length || turnedInvisible.length) {
    curInvisibleFields = prevInvisibleFields.filter(
      x => !turnedVisible.includes(x)
    )
    turnedInvisible.forEach(x => curInvisibleFields.push(x))
    return curInvisibleFields
  }
  return invisibleFields
}

export const CustomFieldsContainer = props => {
  const { fieldNamePrefix, formikProps } = props
  const [invisibleFields, setInvisibleFields] = useState([])
  const { setFieldValue } = formikProps
  const invisibleFieldsFieldName = `${fieldNamePrefix}.invisibleCustomFields`
  useEffect(() => {
    setFieldValue(invisibleFieldsFieldName, invisibleFields, true)
  }, [invisibleFieldsFieldName, invisibleFields, setFieldValue])

  return (
    <>
      <FastField
        type="text"
        value=""
        name={invisibleFieldsFieldName}
        className="hidden"
      />
      <CustomFields
        invisibleFields={invisibleFields}
        updateInvisibleFields={setInvisibleFields}
        {...props}
      />
    </>
  )
}
CustomFieldsContainer.propTypes = {
  fieldsConfig: PropTypes.object,
  formikProps: PropTypes.object,
  fieldNamePrefix: PropTypes.string.isRequired
}
CustomFieldsContainer.defaultProps = {
  fieldNamePrefix: DEFAULT_CUSTOM_FIELDS_PREFIX
}

const CustomField = ({
  fieldConfig,
  fieldName,
  formikProps,
  invisibleFields,
  updateInvisibleFields
}) => {
  const {
    type,
    typeError,
    helpText,
    validations,
    visibleWhen,
    ...fieldProps
  } = fieldConfig
  const { setFieldValue, setFieldTouched, validateForm } = formikProps
  const validateFormDebounced = _debounce(validateForm, 400) // with validateField it somehow doesn't work
  const handleChange = useMemo(
    () => (value, shouldValidate: true) => {
      const val =
        typeof value === "object" && value.target ? value.target.value : value
      const sv = shouldValidate === undefined ? true : shouldValidate
      setFieldTouched(fieldName, true, false)
      setFieldValue(fieldName, val, false)
      if (!sv) {
        validateFormDebounced()
      }
    },
    [fieldName, setFieldTouched, setFieldValue, validateFormDebounced]
  )
  const FieldComponent = FIELD_COMPONENTS[type]
  const extraProps = useMemo(
    () =>
      type !== CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS
        ? {}
        : {
          fieldConfig,
          formikProps,
          invisibleFields,
          updateInvisibleFields
        },
    [fieldConfig, formikProps, invisibleFields, type, updateInvisibleFields]
  )
  return (
    <FieldComponent
      name={fieldName}
      onChange={handleChange}
      {...fieldProps}
      {...extraProps}
    >
      {helpText && (
        <HelpBlock>
          {helpText}
        </HelpBlock>
      )}
    </FieldComponent>
  )
}
CustomField.propTypes = {
  fieldConfig: PropTypes.object,
  fieldName: PropTypes.string.isRequired,
  formikProps: PropTypes.object,
  invisibleFields: PropTypes.array,
  updateInvisibleFields: PropTypes.func
}

const CustomFields = ({
  fieldsConfig,
  formikProps,
  fieldNamePrefix,
  invisibleFields,
  updateInvisibleFields
}) => {
  const formikValues = formikProps.values

  const latestInvisibleFieldsProp = useRef(invisibleFields)
  const invisibleFieldsPropUnchanged = _isEqualWith(
    latestInvisibleFieldsProp.current,
    invisibleFields,
    utils.treatFunctionsAsEqual
  )

  const curInvisibleFields = useMemo(
    () =>
      getInvisibleFields(
        invisibleFields,
        fieldsConfig,
        fieldNamePrefix,
        formikValues
      ),
    [invisibleFields, fieldsConfig, fieldNamePrefix, formikValues]
  )
  const invisibleFieldsUnchanged = _isEqualWith(
    latestInvisibleFieldsProp.current,
    curInvisibleFields,
    utils.treatFunctionsAsEqual
  )

  useEffect(() => {
    if (!invisibleFieldsPropUnchanged) {
      latestInvisibleFieldsProp.current = invisibleFields
    }
  }, [invisibleFieldsPropUnchanged, invisibleFields])

  useEffect(() => {
    if (!invisibleFieldsUnchanged) {
      updateInvisibleFields(curInvisibleFields)
    }
  }, [invisibleFieldsUnchanged, curInvisibleFields, updateInvisibleFields])

  return (
    <>
      {Object.keys(fieldsConfig).map(key => {
        const fieldConfig = fieldsConfig[key]
        const fieldName = `${fieldNamePrefix}.${key}`
        return invisibleFields.includes(fieldName) ? null : (
          <CustomField
            key={key}
            fieldConfig={fieldConfig}
            fieldName={fieldName}
            formikProps={formikProps}
            invisibleFields={invisibleFields}
            updateInvisibleFields={updateInvisibleFields}
          />
        )
      })}
    </>
  )
}
CustomFields.propTypes = {
  fieldsConfig: PropTypes.object,
  formikProps: PropTypes.object,
  fieldNamePrefix: PropTypes.string.isRequired,
  invisibleFields: PropTypes.array,
  updateInvisibleFields: PropTypes.func
}
CustomFields.defaultProps = {
  fieldNamePrefix: DEFAULT_CUSTOM_FIELDS_PREFIX
}

const READONLY_FIELD_COMPONENTS = {
  [CUSTOM_FIELD_TYPE.TEXT]: ReadonlyTextField,
  [CUSTOM_FIELD_TYPE.NUMBER]: ReadonlyTextField,
  [CUSTOM_FIELD_TYPE.DATE]: ReadonlyDateField,
  [CUSTOM_FIELD_TYPE.DATETIME]: ReadonlyDateTimeField,
  [CUSTOM_FIELD_TYPE.ENUM]: ReadonlyEnumField,
  [CUSTOM_FIELD_TYPE.ENUMSET]: ReadonlyEnumField,
  [CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS]: ReadonlyArrayOfObjectsField,
  [CUSTOM_FIELD_TYPE.SPECIAL_FIELD]: ReadonlySpecialField
}

export const ReadonlyCustomFields = ({
  fieldsConfig,
  formikProps,
  fieldNamePrefix
}) => {
  return (
    <>
      {Object.keys(fieldsConfig).map(key => {
        const fieldConfig = fieldsConfig[key]
        const {
          type,
          typeError,
          placeholder,
          helpText,
          validations,
          visibleWhen,
          objectFields,
          ...fieldProps
        } = fieldConfig
        let extraProps = {}
        if (type === CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS) {
          extraProps = {
            fieldConfig,
            formikProps
          }
        }
        const FieldComponent = READONLY_FIELD_COMPONENTS[type]
        return (
          <FieldComponent
            key={key}
            name={`${fieldNamePrefix}.${key}`}
            {...fieldProps}
            {...extraProps}
          />
        )
      })}
    </>
  )
}
ReadonlyCustomFields.propTypes = {
  fieldsConfig: PropTypes.object,
  formikProps: PropTypes.object,
  fieldNamePrefix: PropTypes.string.isRequired
}
ReadonlyCustomFields.defaultProps = {
  fieldNamePrefix: DEFAULT_CUSTOM_FIELDS_PREFIX
}

// customFields should contain the JSON of all the visible custom fields
export const customFieldsJSONString = values => {
  const clonedValues = _cloneDeep(values)
  if (values.formCustomFields.invisibleCustomFields) {
    clonedValues.formCustomFields.invisibleCustomFields.forEach(f => {
      _set(clonedValues, f.split("."), undefined)
    })
  }
  return JSON.stringify(clonedValues.formCustomFields)
}
<commit_msg>NCI-Agency/anet#2579: Fix debounce of custom field validation<commit_after>import { Settings } from "api"
import CustomDateInput from "components/CustomDateInput"
import * as FieldHelper from "components/FieldHelper"
import Fieldset from "components/Fieldset"
import LikertScale from "components/graphs/LikertScale"
import Model, {
  CUSTOM_FIELD_TYPE,
  createYupObjectShape
} from "components/Model"
import { FastField, FieldArray } from "formik"
import { JSONPath } from "jsonpath-plus"
import _cloneDeep from "lodash/cloneDeep"
import _isEmpty from "lodash/isEmpty"
import _isEqualWith from "lodash/isEqualWith"
import _set from "lodash/set"
import _upperFirst from "lodash/upperFirst"
import moment from "moment"
import PropTypes from "prop-types"
import React, { useEffect, useMemo, useRef, useState } from "react"
import { Button, HelpBlock } from "react-bootstrap"
import REMOVE_ICON from "resources/delete.png"
import { useDebouncedCallback } from "use-debounce"
import utils from "utils"

const WIDGETS = {
  likertScale: LikertScale
}
const RENDERERS = {}

const DEFAULT_CUSTOM_FIELDS_PREFIX = "formCustomFields"

const SpecialField = fieldProps => {
  const { widget, ...otherFieldProps } = fieldProps
  const SpecialFieldWidget = WIDGETS[widget]
  return (
    <FastField
      component={FieldHelper.SpecialField}
      widget={<SpecialFieldWidget />}
      {...otherFieldProps}
    />
  )
}

const ReadonlySpecialField = fieldProps =>
  SpecialField({ ...fieldProps, readonly: true })

const TextField = fieldProps => {
  const { onChange, onBlur, ...otherFieldProps } = fieldProps
  return (
    <FastField
      onChange={value => onChange(value, false)}
      component={FieldHelper.InputField}
      {...otherFieldProps}
    />
  )
}

const ReadonlyTextField = fieldProps => {
  const { name, label } = fieldProps
  return (
    <FastField
      name={name}
      label={label}
      component={FieldHelper.ReadonlyField}
    />
  )
}

const DateField = fieldProps => {
  const { name, withTime, ...otherFieldProps } = fieldProps
  return (
    <FastField
      name={name}
      component={FieldHelper.SpecialField}
      widget={<CustomDateInput id={name} withTime={withTime} />}
      {...otherFieldProps}
    />
  )
}

const ReadonlyDateField = fieldProps => {
  const { name, label, withTime } = fieldProps
  return (
    <FastField
      name={name}
      label={label}
      component={FieldHelper.ReadonlyField}
      humanValue={fieldVal =>
        fieldVal &&
        moment(fieldVal).format(
          withTime
            ? Settings.dateFormats.forms.displayShort.withTime
            : Settings.dateFormats.forms.displayShort.date
        )}
    />
  )
}

const DateTimeField = props => <DateField {...props} withTime />

const ReadonlyDateTimeField = props => <ReadonlyDateField {...props} withTime />

const EnumField = fieldProps => {
  const { choices, renderer, ...otherFieldProps } = fieldProps
  return (
    <FastField
      buttons={FieldHelper.customEnumButtons(choices)}
      component={RENDERERS[renderer] || FieldHelper.RadioButtonToggleGroupField}
      {...otherFieldProps}
    />
  )
}

const enumHumanValue = (choices, fieldVal) => {
  if (Array.isArray(fieldVal)) {
    return fieldVal && fieldVal.map(k => choices[k].label).join(", ")
  } else {
    return fieldVal && choices[fieldVal].label
  }
}

const ReadonlyEnumField = fieldProps => {
  const { name, label, choices } = fieldProps
  return (
    <FastField
      name={name}
      label={label}
      component={FieldHelper.ReadonlyField}
      humanValue={fieldVal => enumHumanValue(choices, fieldVal)}
    />
  )
}

const EnumSetField = fieldProps => {
  const { choices, renderer, ...otherFieldProps } = fieldProps
  return (
    <FastField
      buttons={FieldHelper.customEnumButtons(choices)}
      component={
        RENDERERS[renderer] || FieldHelper.CheckboxButtonToggleGroupField
      }
      {...otherFieldProps}
    />
  )
}

const getArrayObjectValue = (values, fieldName) => {
  const nameKeys = fieldName.split(".")
  return nameKeys.reduce((v, key) => (v && v[key] ? v[key] : []), values)
}

const ArrayOfObjectsField = fieldProps => {
  const {
    name,
    fieldConfig,
    formikProps,
    invisibleFields,
    updateInvisibleFields
  } = fieldProps
  const value = useMemo(() => getArrayObjectValue(formikProps.values, name), [
    formikProps.values,
    name
  ])
  const objDefault = useMemo(() => {
    const objDefault = {}
    const objSchema = createYupObjectShape(fieldConfig.objectFields)
    return Model.fillObject(objDefault, objSchema)
  }, [fieldConfig.objectFields])
  const fieldsetTitle = fieldConfig.label || ""
  const addButtonLabel = fieldConfig.addButtonLabel || "Add a new item"
  return (
    <Fieldset title={fieldsetTitle}>
      <FieldArray
        name={name}
        render={arrayHelpers => (

            <Button
              className="pull-right"
              onClick={() => addObject(objDefault, arrayHelpers)}
              bsStyle="primary"
              id="addObjectButton"
            >
              {addButtonLabel}
            </Button>
            {value.map((obj, index) => (
              <ArrayObject
                key={index}
                fieldName={name}
                fieldConfig={fieldConfig}
                formikProps={formikProps}
                invisibleFields={invisibleFields}
                updateInvisibleFields={updateInvisibleFields}
                arrayHelpers={arrayHelpers}
                index={index}
              />
            ))}

        )}
      />
    </Fieldset>
  )
}

const ArrayObject = ({
  fieldName,
  fieldConfig,
  formikProps,
  invisibleFields,
  updateInvisibleFields,
  arrayHelpers,
  index
}) => {
  const objLabel = _upperFirst(fieldConfig.objectLabel || "item")
  return (
    <Fieldset title={`${objLabel} ${index + 1}`}>
      <Button
        className="pull-right"
        title={`Remove this ${objLabel}`}
        onClick={() => arrayHelpers.remove(index)}
      >
        <img src={REMOVE_ICON} height={14} alt={`Remove this ${objLabel}`} />
      </Button>
      <CustomFields
        fieldsConfig={fieldConfig.objectFields}
        formikProps={formikProps}
        invisibleFields={invisibleFields}
        updateInvisibleFields={updateInvisibleFields}
        fieldNamePrefix={`${fieldName}.${index}`}
      />
    </Fieldset>
  )
}
ArrayObject.propTypes = {
  fieldName: PropTypes.string.isRequired,
  fieldConfig: PropTypes.object.isRequired,
  formikProps: PropTypes.object.isRequired,
  invisibleFields: PropTypes.array.isRequired,
  updateInvisibleFields: PropTypes.func.isRequired,
  arrayHelpers: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired
}

const addObject = (objDefault, arrayHelpers) => {
  arrayHelpers.push(objDefault)
}

const ReadonlyArrayOfObjectsField = fieldProps => {
  const { name, fieldConfig, formikProps } = fieldProps
  const value = useMemo(() => getArrayObjectValue(formikProps.values, name), [
    formikProps.values,
    name
  ])
  const fieldsetTitle = fieldConfig.label || ""
  return (
    <Fieldset title={fieldsetTitle}>
      <FieldArray
        name={name}
        render={arrayHelpers => (

            {value.map((obj, index) => (
              <ReadonlyArrayObject
                key={index}
                fieldName={name}
                fieldConfig={fieldConfig}
                formikProps={formikProps}
                index={index}
              />
            ))}

        )}
      />
    </Fieldset>
  )
}

const ReadonlyArrayObject = ({
  fieldName,
  fieldConfig,
  formikProps,
  index
}) => {
  const objLabel = _upperFirst(fieldConfig.objectLabel || "item")
  return (
    <Fieldset title={`${objLabel} ${index + 1}`}>
      <ReadonlyCustomFields
        fieldsConfig={fieldConfig.objectFields}
        formikProps={formikProps}
        fieldNamePrefix={`${fieldName}.${index}`}
      />
    </Fieldset>
  )
}
ReadonlyArrayObject.propTypes = {
  fieldName: PropTypes.string.isRequired,
  fieldConfig: PropTypes.object.isRequired,
  formikProps: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired
}

const FIELD_COMPONENTS = {
  [CUSTOM_FIELD_TYPE.TEXT]: TextField,
  [CUSTOM_FIELD_TYPE.NUMBER]: TextField,
  [CUSTOM_FIELD_TYPE.DATE]: DateField,
  [CUSTOM_FIELD_TYPE.DATETIME]: DateTimeField,
  [CUSTOM_FIELD_TYPE.ENUM]: EnumField,
  [CUSTOM_FIELD_TYPE.ENUMSET]: EnumSetField,
  [CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS]: ArrayOfObjectsField,
  [CUSTOM_FIELD_TYPE.SPECIAL_FIELD]: SpecialField
}

function getInvisibleFields(
  invisibleFields,
  fieldsConfig,
  fieldNamePrefix,
  formikValues
) {
  const prevInvisibleFields = _cloneDeep(invisibleFields)
  const turnedInvisible = []
  const turnedVisible = []
  let curInvisibleFields = []
  Object.keys(fieldsConfig).forEach(key => {
    const fieldConfig = fieldsConfig[key]
    const fieldName = `${fieldNamePrefix}.${key}`
    const isVisible =
      !fieldConfig.visibleWhen ||
      (fieldConfig.visibleWhen &&
        !_isEmpty(JSONPath(fieldConfig.visibleWhen, formikValues)))
    if (!isVisible && !prevInvisibleFields.includes(fieldName)) {
      turnedInvisible.push(fieldName)
    } else if (isVisible && prevInvisibleFields.includes(fieldName)) {
      turnedVisible.push(fieldName)
    }
  })
  if (turnedVisible.length || turnedInvisible.length) {
    curInvisibleFields = prevInvisibleFields.filter(
      x => !turnedVisible.includes(x)
    )
    turnedInvisible.forEach(x => curInvisibleFields.push(x))
    return curInvisibleFields
  }
  return invisibleFields
}

export const CustomFieldsContainer = props => {
  const { fieldNamePrefix, formikProps } = props
  const [invisibleFields, setInvisibleFields] = useState([])
  const { setFieldValue } = formikProps
  const invisibleFieldsFieldName = `${fieldNamePrefix}.invisibleCustomFields`
  useEffect(() => {
    setFieldValue(invisibleFieldsFieldName, invisibleFields, true)
  }, [invisibleFieldsFieldName, invisibleFields, setFieldValue])

  return (
    <>
      <FastField
        type="text"
        value=""
        name={invisibleFieldsFieldName}
        className="hidden"
      />
      <CustomFields
        invisibleFields={invisibleFields}
        updateInvisibleFields={setInvisibleFields}
        {...props}
      />
    </>
  )
}
CustomFieldsContainer.propTypes = {
  fieldsConfig: PropTypes.object,
  formikProps: PropTypes.object,
  fieldNamePrefix: PropTypes.string.isRequired
}
CustomFieldsContainer.defaultProps = {
  fieldNamePrefix: DEFAULT_CUSTOM_FIELDS_PREFIX
}

const CustomField = ({
  fieldConfig,
  fieldName,
  formikProps,
  invisibleFields,
  updateInvisibleFields
}) => {
  const {
    type,
    typeError,
    helpText,
    validations,
    visibleWhen,
    ...fieldProps
  } = fieldConfig
  const { setFieldValue, setFieldTouched, validateForm } = formikProps
  const [validateFormDebounced] = useDebouncedCallback(validateForm, 400) // with validateField it somehow doesn't work
  const handleChange = useMemo(
    () => (value, shouldValidate: true) => {
      const val =
        typeof value === "object" && value.target ? value.target.value : value
      const sv = shouldValidate === undefined ? true : shouldValidate
      setFieldTouched(fieldName, true, false)
      setFieldValue(fieldName, val, false)
      if (!sv) {
        validateFormDebounced()
      }
    },
    [fieldName, setFieldTouched, setFieldValue, validateFormDebounced]
  )
  const FieldComponent = FIELD_COMPONENTS[type]
  const extraProps = useMemo(
    () =>
      type !== CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS
        ? {}
        : {
          fieldConfig,
          formikProps,
          invisibleFields,
          updateInvisibleFields
        },
    [fieldConfig, formikProps, invisibleFields, type, updateInvisibleFields]
  )
  return (
    <FieldComponent
      name={fieldName}
      onChange={handleChange}
      {...fieldProps}
      {...extraProps}
    >
      {helpText && (
        <HelpBlock>
          {helpText}
        </HelpBlock>
      )}
    </FieldComponent>
  )
}
CustomField.propTypes = {
  fieldConfig: PropTypes.object,
  fieldName: PropTypes.string.isRequired,
  formikProps: PropTypes.object,
  invisibleFields: PropTypes.array,
  updateInvisibleFields: PropTypes.func
}

const CustomFields = ({
  fieldsConfig,
  formikProps,
  fieldNamePrefix,
  invisibleFields,
  updateInvisibleFields
}) => {
  const formikValues = formikProps.values

  const latestInvisibleFieldsProp = useRef(invisibleFields)
  const invisibleFieldsPropUnchanged = _isEqualWith(
    latestInvisibleFieldsProp.current,
    invisibleFields,
    utils.treatFunctionsAsEqual
  )

  const curInvisibleFields = useMemo(
    () =>
      getInvisibleFields(
        invisibleFields,
        fieldsConfig,
        fieldNamePrefix,
        formikValues
      ),
    [invisibleFields, fieldsConfig, fieldNamePrefix, formikValues]
  )
  const invisibleFieldsUnchanged = _isEqualWith(
    latestInvisibleFieldsProp.current,
    curInvisibleFields,
    utils.treatFunctionsAsEqual
  )

  useEffect(() => {
    if (!invisibleFieldsPropUnchanged) {
      latestInvisibleFieldsProp.current = invisibleFields
    }
  }, [invisibleFieldsPropUnchanged, invisibleFields])

  useEffect(() => {
    if (!invisibleFieldsUnchanged) {
      updateInvisibleFields(curInvisibleFields)
    }
  }, [invisibleFieldsUnchanged, curInvisibleFields, updateInvisibleFields])

  return (
    <>
      {Object.keys(fieldsConfig).map(key => {
        const fieldConfig = fieldsConfig[key]
        const fieldName = `${fieldNamePrefix}.${key}`
        return invisibleFields.includes(fieldName) ? null : (
          <CustomField
            key={key}
            fieldConfig={fieldConfig}
            fieldName={fieldName}
            formikProps={formikProps}
            invisibleFields={invisibleFields}
            updateInvisibleFields={updateInvisibleFields}
          />
        )
      })}
    </>
  )
}
CustomFields.propTypes = {
  fieldsConfig: PropTypes.object,
  formikProps: PropTypes.object,
  fieldNamePrefix: PropTypes.string.isRequired,
  invisibleFields: PropTypes.array,
  updateInvisibleFields: PropTypes.func
}
CustomFields.defaultProps = {
  fieldNamePrefix: DEFAULT_CUSTOM_FIELDS_PREFIX
}

const READONLY_FIELD_COMPONENTS = {
  [CUSTOM_FIELD_TYPE.TEXT]: ReadonlyTextField,
  [CUSTOM_FIELD_TYPE.NUMBER]: ReadonlyTextField,
  [CUSTOM_FIELD_TYPE.DATE]: ReadonlyDateField,
  [CUSTOM_FIELD_TYPE.DATETIME]: ReadonlyDateTimeField,
  [CUSTOM_FIELD_TYPE.ENUM]: ReadonlyEnumField,
  [CUSTOM_FIELD_TYPE.ENUMSET]: ReadonlyEnumField,
  [CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS]: ReadonlyArrayOfObjectsField,
  [CUSTOM_FIELD_TYPE.SPECIAL_FIELD]: ReadonlySpecialField
}

export const ReadonlyCustomFields = ({
  fieldsConfig,
  formikProps,
  fieldNamePrefix
}) => {
  return (
    <>
      {Object.keys(fieldsConfig).map(key => {
        const fieldConfig = fieldsConfig[key]
        const {
          type,
          typeError,
          placeholder,
          helpText,
          validations,
          visibleWhen,
          objectFields,
          ...fieldProps
        } = fieldConfig
        let extraProps = {}
        if (type === CUSTOM_FIELD_TYPE.ARRAY_OF_OBJECTS) {
          extraProps = {
            fieldConfig,
            formikProps
          }
        }
        const FieldComponent = READONLY_FIELD_COMPONENTS[type]
        return (
          <FieldComponent
            key={key}
            name={`${fieldNamePrefix}.${key}`}
            {...fieldProps}
            {...extraProps}
          />
        )
      })}
    </>
  )
}
ReadonlyCustomFields.propTypes = {
  fieldsConfig: PropTypes.object,
  formikProps: PropTypes.object,
  fieldNamePrefix: PropTypes.string.isRequired
}
ReadonlyCustomFields.defaultProps = {
  fieldNamePrefix: DEFAULT_CUSTOM_FIELDS_PREFIX
}

// customFields should contain the JSON of all the visible custom fields
export const customFieldsJSONString = values => {
  const clonedValues = _cloneDeep(values)
  if (values.formCustomFields.invisibleCustomFields) {
    clonedValues.formCustomFields.invisibleCustomFields.forEach(f => {
      _set(clonedValues, f.split("."), undefined)
    })
  }
  return JSON.stringify(clonedValues.formCustomFields)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Bonnie Schulkin. All Rights Reserved.
 *
 * This file is part of BoxCharter.
 *
 * BoxCharter is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * BoxCharter 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 Affero General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with BoxCharter. If not, see <http://www.gnu.org/licenses/>.
 *
 */

const User = require('./user')
const Key = require('./note-key').Key
const Note = require('./note-key').Note
const ScaleNote = require('./note-key').ScaleNote
const Chord = require('./chord-lyric').Chord
const Lyric = require('./chord-lyric').Lyric
const Measure = require('./measure')
const Section = require('./section')
const Chart = require('./chart')
const ChartUser = require('./chartuser')

//////////
// user
User.belongsToMany(Chart,
  { through: { model: ChartUser, unique: false },
    foreignKey: 'userId'
  })

////////////
// Key
Key.hasMany(ScaleNote, {foreignKey: 'keyCode'})

////////////
// ScaleNote
ScaleNote.belongsTo(Key, {foreignKey: 'keyCode', unique: false, allowNull: false})
ScaleNote.belongsTo(Note, {foreignKey: 'noteCode', unique: false, allowNull: false})

//////////
// chart
Chart.belongsToMany(User,
  { through: { model: ChartUser, unique: false },
    foreignKey: 'chartId'
  })
Chart.belongsTo(Key, {as: 'originalKey', foreignKey: 'keyCode', unique: false})
Chart.belongsTo(Key, {as: 'printKey', foreignKey: 'keyCode', unique: false})
Chart.hasMany(Section, {foreignKey: 'chartId'})

////////////
// section
Section.belongsTo(Chart, {foreignKey: 'chartId', allowNull: false})
Section.belongsTo(Measure,
  { as: 'ending1Start',
    foreignKey: 'measureId',
    unique: false,
    constraints: false
  })
Section.belongsTo(Measure,
  { as: 'ending1End',
    foreignKey: 'measureId',
    unique: false,
    constraints: false
  })
Section.belongsTo(Measure,
  { as: 'ending2Start',
    foreignKey: 'measureId',
    unique: false,
    constraints: false
  })
Section.hasMany(Measure, {foreignKey: 'sectionId'})

///////////////
// measure
Measure.belongsTo(Section, {foreignKey: 'sectionId', allowNull: false})
Measure.hasMany(Chord, {foreignKey: 'measureId'})
Measure.hasMany(Lyric, {foreignKey: 'measureId'})

/////////////
// chords / lyrics
Chord.belongsTo(Measure, {foreignKey: 'measureId', allowNull: false})
Chord.belongsTo(Note, {foreignKey: 'noteCode', allowNull: false})
Lyric.belongsTo(Measure, {foreignKey: 'measureId', allowNull: false})
<commit_msg>Remove 'belongsTo' associations that add extra columns to tables. Unfortunately, this also means I can't enforce not NULL constraints, but I'm okay with that for now.<commit_after>/*
 * Copyright (c) 2017 Bonnie Schulkin. All Rights Reserved.
 *
 * This file is part of BoxCharter.
 *
 * BoxCharter is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * BoxCharter 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 Affero General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with BoxCharter. If not, see <http://www.gnu.org/licenses/>.
 *
 */

const User = require('./user')
const Key = require('./note-key').Key
const Note = require('./note-key').Note
const ScaleNote = require('./note-key').ScaleNote
const Chord = require('./chord-lyric').Chord
const Lyric = require('./chord-lyric').Lyric
const Measure = require('./measure')
const Section = require('./section')
const Chart = require('./chart')
const ChartUser = require('./chartuser')

//////////
// user
User.belongsToMany(Chart,
  { through: { model: ChartUser, unique: false },
    foreignKey: 'userId'
  })

////////////
// Key
Key.hasMany(ScaleNote, {foreignKey: 'keyCode'})

////////////
// ScaleNote
ScaleNote.belongsTo(Key, {foreignKey: 'keyCode', unique: false, allowNull: false})
ScaleNote.belongsTo(Note, {foreignKey: 'noteCode', unique: false, allowNull: false})

//////////
// chart
Chart.belongsToMany(User,
  { through: { model: ChartUser, unique: false },
    foreignKey: 'chartId'
  })
Chart.belongsTo(Key, {
  as: 'originalKey',
  // targetKey: 'originalKey',  // otherwise the column ends up as originalKeyKeyCode
  unique: false
})
Chart.belongsTo(Key, {
  as: 'printKey',
  // targetKey: 'printKey',
  unique: false})
Chart.hasMany(Section, {foreignKey: 'chartId'})

////////////
// section
// Section.belongsTo(Chart, {allowNull: false}) // this adds an extra column: chartChartId
Section.belongsTo(Measure,
  { as: 'ending1Start',
    foreignKey: 'measureId',
    unique: false,
    constraints: false
  })
Section.belongsTo(Measure,
  { as: 'ending1End',
    foreignKey: 'measureId',
    unique: false,
    constraints: false
  })
Section.belongsTo(Measure,
  { as: 'ending2Start',
    foreignKey: 'measureId',
    unique: false,
    constraints: false
  })
Section.hasMany(Measure, {foreignKey: 'sectionId'})

///////////////
// measure
// Measure.belongsTo(Section, {allowNull: false}) // this adds an extra column: sectionSectionId
Measure.hasMany(Chord, {foreignKey: 'measureId'})
Measure.hasMany(Lyric, {foreignKey: 'measureId'})

/////////////
// chords / lyrics
// Chord.belongsTo(Measure, {allowNull: false}) // this adds an extra column
Chord.belongsTo(Note, {foreignKey: 'noteCode', allowNull: false})
// Lyric.belongsTo(Measure, {allowNull: false}) // this adds an extra column
<|endoftext|>


<commit_before>Ext.define('FastUI.view.VTab', {
    extend:'Ext.Panel',
    alias:'widget.vtab',
    title:'All VTabs',
    vfactory:null,
    layout:"card",
    initComponent:function () {
        this.title = this.vfactory.getVData().title;
        this.tbar = Ext.create('Ext.toolbar.Toolbar', {
            items:[
                {   text:'新建',
                    handler:function () {
                        this.loadForm();
                    },
                    scope:this
                },
                { text:'编辑',
                    handler:function () {
                        this.cmdEdit();
                    }, scope:this},
                { text:'列表',
                    handler:function () {
                        this.loadGrid();
                    }, scope:this},
                { text:'保存',
                    handler:function () {
                        this.cmdSave();
                    }, scope:this},
                { text:'删除',
                    handler:function () {
                        this.cmdDelete();
                    }, scope:this},
                { text:'帮助',
                    handler:function () {
                        alert('帮助');
                    }, scope:this}
            ]
        });
        this.callParent();
    },
    listeners:{
        activate:function (tab, opts) {
            this.loadGrid();
        }
    },
    loadGrid:function () {
        if (!this.grid) {
            this.grid = Ext.create('FastUI.view.VGrid', {vfactory:this.vfactory});
            this.add(this.grid);
        }

        this.grid.getStore().reload(); // 不一定每次都要刷新 需修改
        this.getLayout().setActiveItem(this.grid.id);
    },
    loadForm:function () {
        if (!this.form) {
            this.form = Ext.create('FastUI.view.VForm', {vfactory:this.vfactory});
            this.add(this.form);
        }
        this.getLayout().setActiveItem(this.form.id);
        this.form.getForm().reset();
    },
    cmdEdit:function () {
        var record = this.grid.getSelectionModel().getSelection();
        if (record) this.currentRecord = record[0];
        if (!this.form) {
            this.form = Ext.create('FastUI.view.VForm', {vfactory:this.vfactory, title:'编辑'});
            this.add(this.form);
        }
        alert(this.currentRecord.get('id'));
//        this.form.url = "/fastui/m_windows/" + this.currentRecord.get('id') + ".json";
        this.form.url = this.get_url('update');
        this.form.method = 'PUT';
        Ext.Ajax.request({
//            url:"/fastui/m_windows/" + this.currentRecord.get('id') + "/edit.json",
            url:this.get_url('edit'),
            success:function (response) {
                var data = Ext.decode(response.responseText);
                var k, o = {};
                for (k in data) {
                    o['m_window[' + k + ']'] = data[k];
                }
                this.form.getForm().setValues(o);
            }, scope:this
        });
        this.getLayout().setActiveItem(this.form.id);
    },
    cmdDelete:function () {
        var record = this.grid.getSelectionModel().getSelection();
        if (record) this.currentRecord = record[0];
        var opt = {msg:'确定要删除%吗?', method:'DELETE'}
        var msg = (opt.msg || "确定要" + opt.getName() + "%吗?").replace(new RegExp('%'), "tt")
        Ext.MessageBox.confirm('提示', msg, function (btn) {
            if (btn == 'yes') {
                Ext.Ajax.request({
//                    url:"/fastui/m_windows/" + this.currentRecord.get('id') + ".json",
                    url:this.get_url('delete'),
                    method:opt.method || 'GET',
                    success:function () {
                        this.loadGrid();
                        Ext.MessageBox.alert("提示", "操作成功！")
                    },
                    failure:function () {
                        Ext.MessageBox.alert("提示", "操作失败！")
                    },
                    scope:this
                });
            }
        }, this)

    },
    cmdSave:function () {
        if (this.form) {
            var form = this.form.getForm();
            if (form.isValid()) {
                form.submit({
                    url:this.form.url,
                    method:this.form.method,
                    scope:this,
                    success:function (form, action) {
                        Ext.Msg.alert('Success', action.result.msg);
                        this.loadGrid();
                    },
                    failure:function (form, action) {
                        Ext.Msg.alert('Failed', action.result.msg);
                    }
                });
            }
        }
    },
    get_url:function (action, event_name, record) {
        var url = '/fastui/m_windows';
        if (record) this.currentRecord = record;
        switch (action) {
            case "index":
                return url + ".json";
                break;
            case "create":
                return url + ".json";
                break;
            case "update":
                return url + "/" + this.currentRecord.get('id') + ".json";
                break;
            case "edit":
                return url + "/" + this.currentRecord.get('id')  + "/edit.json";
                break;
            case "delete":
                return url + "/" + this.currentRecord.get('id')  + ".json";
                break;
            case "special":
                return url + "/" + this.currentRecord.get('id')  + "/" + event_name + ".json";
                break;
        }
    }
});
<commit_msg>use mtabs each<commit_after>Ext.define('FastUI.view.VTab', {
    extend:'Ext.Panel',
    alias:'widget.vtab',
    title:'All VTabs',
    vfactory:null,
    layout:"card",
    initComponent:function () {
        this.title = this.vfactory.getVData().title;
        this.tbar = Ext.create('Ext.toolbar.Toolbar', {
            items:[
                {   text:'新建',
                    handler:function () {
                        this.loadForm();
                    },
                    scope:this
                },
                { text:'编辑',
                    handler:function () {
                        this.cmdEdit();
                    }, scope:this},
                { text:'列表',
                    handler:function () {
                        this.loadGrid();
                    }, scope:this},
                { text:'保存',
                    handler:function () {
                        this.cmdSave();
                    }, scope:this},
                { text:'删除',
                    handler:function () {
                        this.cmdDelete();
                    }, scope:this},
                { text:'帮助',
                    handler:function () {
                        alert('帮助');
                    }, scope:this}
            ]
        });
        this.callParent();
    },
    listeners:{
        activate:function (tab, opts) {
            this.loadGrid();
        }
    },
    loadGrid:function () {
        if (!this.grid) {
            this.grid = Ext.create('FastUI.view.VGrid', {vfactory:this.vfactory});
            this.add(this.grid);
        }

        this.grid.getStore().reload(); // 不一定每次都要刷新 需修改
        this.getLayout().setActiveItem(this.grid.id);
    },
    loadForm:function () {
        if (!this.form) {
            this.form = Ext.create('FastUI.view.VForm', {vfactory:this.vfactory});
            this.add(this.form);
        }
        this.getLayout().setActiveItem(this.form.id);
        this.form.getForm().reset();
    },
    cmdEdit:function () {
        var record = this.grid.getSelectionModel().getSelection();
        if (record) this.currentRecord = record[0];
        if (!this.form) {
            this.form = Ext.create('FastUI.view.VForm', {vfactory:this.vfactory, title:'编辑'});
            this.add(this.form);
        }
//        alert(this.currentRecord.get('id'));
//        this.form.url = "/fastui/m_windows/" + this.currentRecord.get('id') + ".json";
        this.form.url = this.get_url('update');
        this.form.method = 'PUT';
        Ext.Ajax.request({
//            url:"/fastui/m_windows/" + this.currentRecord.get('id') + "/edit.json",
            url:this.get_url('edit'),
            success:function (response) {
                var data = Ext.decode(response.responseText);
                var k, o = {};
                for (k in data) {
                    o['m_window[' + k + ']'] = data[k];
                }
                this.form.getForm().setValues(o);
            }, scope:this
        });
        this.getLayout().setActiveItem(this.form.id);
    },
    cmdDelete:function () {
        var record = this.grid.getSelectionModel().getSelection();
        if (record) this.currentRecord = record[0];
        var opt = {msg:'确定要删除%吗?', method:'DELETE'}
        var msg = (opt.msg || "确定要" + opt.getName() + "%吗?").replace(new RegExp('%'), "tt")
        Ext.MessageBox.confirm('提示', msg, function (btn) {
            if (btn == 'yes') {
                Ext.Ajax.request({
//                    url:"/fastui/m_windows/" + this.currentRecord.get('id') + ".json",
                    url:this.get_url('delete'),
                    method:opt.method || 'GET',
                    success:function () {
                        this.loadGrid();
                        Ext.MessageBox.alert("提示", "操作成功！")
                    },
                    failure:function () {
                        Ext.MessageBox.alert("提示", "操作失败！")
                    },
                    scope:this
                });
            }
        }, this)

    },
    cmdSave:function () {
        if (this.form) {
            var form = this.form.getForm();
            if (form.isValid()) {
                form.submit({
                    url:this.form.url,
                    method:this.form.method,
                    scope:this,
                    success:function (form, action) {
                        Ext.Msg.alert('Success', action.result.msg);
                        this.loadGrid();
                    },
                    failure:function (form, action) {
                        Ext.Msg.alert('Failed', action.result.msg);
                    }
                });
            }
        }
    },
    get_url:function (action, event_name, record) {
        var url = '/fastui/m_windows';
        if (record) this.currentRecord = record;
        switch (action) {
            case "index":
                return url + ".json";
                break;
            case "create":
                return url + ".json";
                break;
            case "update":
                return url + "/" + this.currentRecord.get('id') + ".json";
                break;
            case "edit":
                return url + "/" + this.currentRecord.get('id')  + "/edit.json";
                break;
            case "delete":
                return url + "/" + this.currentRecord.get('id')  + ".json";
                break;
            case "special":
                return url + "/" + this.currentRecord.get('id')  + "/" + event_name + ".json";
                break;
        }
    }
});
<|endoftext|>