<commit_before>(function($) {
	function hide(els) {
		// Hide elements that are to be shown on events
		// els is array
		if(typeof els === "object" && els instanceof Array) {
			for(var i = 0; i < els.length; i++) {
				if(els[i] && els[i].addClass) els[i].addClass("jshidden");
			}
		}
		else if(els && els.addClass) els.addClass("jshidden"); // els is single element
		else return false; // fail
		return true;
	}
	function RichSelect(el) {
		// Object for rich menu functionality
		var menubox = el.find(".menubox");
		var menutext = menubox.prepend("<span />"); // Add empty element for holding option text
		var richopt = menubox.find(".richopt");
		var menu = el.find(".menu");
		menu = menu.size() ? menu : null; // Check for existence of menu
		hide([menubox, richopt, menu]);
		var valstore = menubox.find(".valstore");
		valstore = valstore.size() ? valstore : null; // Check for existence of valstore
		// Public object properties and methods (pretty much everything by now)
		return {
			menu : menu,
			menubox: menubox,
			valstore : valstore,
			name : (valstore ? valstore.attr("name") : menu.attr("name")),
			getVal : function() {
				var val;
				if(valstore) {
					val = valstore.val();
				}
				else {
					var option = menu.children("option:selected");
					if(option.size()) val = option.val();
				}
				return val;
			},
			updateMenubox : function(option) {
				// Shows selected option. If option is a richopt show that element.
				option = option || menu.children("option:selected");
				if(option.hasClass("richopt")) {
					richopt.removeClass("jshidden");
					menubox.removeClass("jshidden");
					var inputs = richopt.find("input");
					// Select form element for added usability
					if(inputs.size()) {
						inputs.first().select();
					}
				}
				else {
					if(!richopt.hasClass("jshidden")) richopt.addClass("jshidden");
					menutext.text(option.text());
					menubox.removeClass("jshidden");
				}
			},
			updateMenu : function() {
				// Update selected menu option from valstore value
				if(!menu) return;
				var option;
				var found = false;
				if(valstore) {
					menu.children().each(function(i, el) {
						option = $(el);
						if(option.val() === valstore.val()) {
							option.attr("selected", "selected");
							found = true;
							return false;
						}
					});
				}
				else {
					option = menu.children("option:selected");
					if(option.size()) found = true;
				}
				if(found) this.updateMenubox(option);
			},
			updateValstore : function() {
				// Update valstore from selected menu option
				var option = menu.children("option:selected");
				if(option.size()) {
					if(valstore) valstore.val(option.val());
					this.updateMenubox(option);
				}
			},
			removeVal : function() {
				// Remove value and collapse menu button to initial form
				menubox.addClass("jshidden");
				menutext.text("");
				if(valstore) valstore.val("");
				if(menu) menu.get(0).selectedIndex = -1;
			},
			showMenu : function() {
				// Show or hide menu
				var el = menu ? menu : menubox;
				el.toggleClass("jshidden");
				return !el.hasClass("jshidden");
			}
		}
	}
	$(function() {
		// init function
		// Add js styles
		$("head").append($("<link />", {
			rel : "Stylesheet",
			type : "text/css",
			href : "js.css"
		}));
		// Array holding all rich menus (currently not used, maybe convenient later - or not)
		var selects = [];
		// Instantiate objects and add event handlers (possibly event handlers should be added inside RichSelect?)
		$(".richselect").each(function(i, el) {
			el = $(el); // jQuerify
			var select = RichSelect(el);
			selects.push(select);
			// Event handler for menu button
			el.click(function(e) {
				var target = $(e.target);
				if(!(target.is(".showmenu") || target.is(".menutext") || target.is(".richselect"))) {
					return;
				}
				var visible = select.showMenu();
				if(!visible && target.is(".showmenu")) {
					e.preventDefault();
				}
			});
			// Make into multiline select
			select.menu.attr("size", select.menu.children().size());
			// Event handlers keep menu and valstore in sync
			select.menu.change(function() {
				select.updateValstore();
				$(this).toggleClass("jshidden");
			});
			select.valstore.change(function() {
				select.updateMenu();
			});
			// Add remove button
			select.menubox.append($("<input />", {
				type : "button",
				value : "<",
				click : function() {
					select.removeVal();
				}
			}));
		});
	});
})(jQuery);<commit_msg>Debugging.<commit_after>(function($) {
	function hide(els) {
		// Hide elements that are to be shown on events
		// els is array
		if(typeof els === "object" && els instanceof Array) {
			for(var i = 0; i < els.length; i++) {
				if(els[i] && els[i].addClass) els[i].addClass("jshidden");
			}
		}
		else if(els && els.addClass) els.addClass("jshidden"); // els is single element
		else return false; // fail
		return true;
	}
	function RichSelect(el) {
		// Object for rich menu functionality
		var showmenu = (function() {
			// Object for manipulating showmenu
			var label = el.find(".showmenu");
			var labeltext;
			function init() {
				labeltext = label.html();
				labeltext = labeltext.slice(0, labeltext.indexOf("<")); // Select text until first HTML element
			}
			return {
				addColon : function() {
					// Show colon in label when expanded
					init();
					if(labeltext.indexOf(":") == -1) {
						label.contents().first().remove(); // Remove text node
						label.prepend(labeltext.trim() + ":");
					}
				},
				removeColon : function() {
					// Remove colon in label when collapsed
					init();
					label.contents().first().remove(); // Remove text node
					label.prepend(labeltext.replace(":", "")); // Add same text without colon
				}
			}
		})();
		showmenu.removeColon();
		var menubox = el.find(".menubox");
		// Add empty element for holding option text
		menubox.prepend("<span />");
		var menutext = menubox.children().first();
		var richopt = menubox.find(".richopt");
		var menu = el.find(".menu");
		menu = menu.size() ? menu : null; // Check for existence of menu
		hide([menubox, richopt, menu]);
		var valstore = menubox.find(".valstore");
		valstore = valstore.size() ? valstore : null; // Check for existence of valstore
		// Public object properties and methods (pretty much everything by now)
		return {
			menu : menu,
			menubox : menubox,
			menutext : menutext,
			valstore : valstore,
			name : (valstore ? valstore.attr("name") : menu.attr("name")),
			getVal : function() {
				var val;
				if(valstore) {
					val = valstore.val();
				}
				else {
					var option = menu.children("option:selected");
					if(option.size()) val = option.val();
				}
				return val;
			},
			updateMenubox : function(option) {
				// Shows selected option. If option is a richopt show that element.
				showmenu.addColon();
				option = option || menu.children("option:selected");
				if(option.hasClass("richopt")) {
					menutext.text("");
					richopt.removeClass("jshidden");
					menubox.removeClass("jshidden");
					var inputs = richopt.find("input");
					// Select form element for added usability
					if(inputs.size()) {
						inputs.first().select();
					}
				}
				else {
					if(!richopt.hasClass("jshidden")) richopt.addClass("jshidden");
					menutext.text(option.text());
					menubox.removeClass("jshidden");
				}
			},
			updateMenu : function() {
				// Update selected menu option from valstore value
				if(!menu) return;
				var option;
				var found = false;
				if(valstore) {
					menu.children().each(function(i, el) {
						option = $(el);
						if(option.val() === valstore.val()) {
							option.attr("selected", "selected");
							found = true;
							return false;
						}
					});
				}
				else {
					option = menu.children("option:selected");
					if(option.size()) found = true;
				}
				if(found) this.updateMenubox(option);
			},
			updateValstore : function() {
				// Update valstore from selected menu option
				var option = menu.children("option:selected");
				if(option.size()) {
					if(valstore) valstore.val(option.val());
					this.updateMenubox(option);
				}
			},
			removeVal : function() {
				// Remove value and collapse menu button to initial form
				showmenu.removeColon();
				menubox.addClass("jshidden");
				menutext.text("");
				if(valstore) valstore.val("");
				if(menu) menu.get(0).selectedIndex = -1;
			},
			showMenu : function() {
				// Show or hide menu
				var el = menu ? menu : menubox;
				el.toggleClass("jshidden");
				return !el.hasClass("jshidden");
			}
		}
	}
	$(function() {
		// init function
		// Add js styles
		$("head").append($("<link />", {
			rel : "Stylesheet",
			type : "text/css",
			href : "js.css"
		}));
		// Array holding all rich menus (currently not used, maybe convenient later - or not)
		var selects = [];
		// Instantiate objects and add event handlers (possibly event handlers should be added inside RichSelect?)
		$(".richselect").each(function(i, el) {
			el = $(el); // jQuerify
			var select = RichSelect(el);
			selects.push(select);
			// Event handler for menu button
			el.click(function(e) {
				var target = $(e.target);
				if(!(target.is(".showmenu") || target.is(".menubox") || target.is(".richselect"))) {
					return;
				}
				var visible = select.showMenu();
				if(!visible && target.is(".showmenu")) {
					e.preventDefault();
				}
			});
			// Make into multiline select
			select.menu.attr("size", select.menu.children().size());
			// Event handlers keep menu and valstore in sync
			select.menu.change(function() {
				select.updateValstore();
				$(this).toggleClass("jshidden");
			});
			select.valstore.change(function() {
				select.updateMenu();
			});
			// Add remove button
			select.menubox.append($("<input />", {
				type : "button",
				value : "<",
				click : function() {
					select.removeVal();
				}
			}));
		});
	});
})(jQuery);<|endoftext|>


<commit_before>module.exports = Object.freeze({
  APP_ID: 'openwmail.openwmail',

  MAILBOX_INDEX_KEY: '__index__',
  MAILBOX_SLEEP_WAIT: 1000 * 30, // 30 seconds

  WEB_URL: 'https://github.com/openwmail/openwmail/',
  GITHUB_URL: 'https://github.com/openwmail/openwmail/',
  GITHUB_ISSUE_URL: 'https://github.com/openwmail/openwmail/issues',
  UPDATE_DOWNLOAD_URL: 'https://github.com/openwmail/openwmail/releases',
  UPDATE_CHECK_URL: 'http://geekgonecrazy.com/misc/wmail/version.json',
  PRIVACY_URL: 'https://github.com/openWMail/openWMail/wiki/Privacy-Policy',
  USER_SCRIPTS_WEB_URL: 'https://github.com/Thomas101/wmail-user-scripts',
  UPDATE_CHECK_INTERVAL: 1000 * 60 * 60 * 24, // 24 hours

  GMAIL_PROFILE_SYNC_MS: 1000 * 60 * 60, // 60 mins
  GMAIL_UNREAD_SYNC_MS: 1000 * 60, // 60 seconds
  GMAIL_NOTIFICATION_MAX_MESSAGE_AGE_MS: 1000 * 60 * 60 * 2, // 2 hours
  GMAIL_NOTIFICATION_FIRST_RUN_GRACE_MS: 1000 * 30, // 30 seconds

  REFOCUS_MAILBOX_INTERVAL_MS: 300,

  DB_EXTENSION: 'wmaildb',
  DB_WRITE_DELAY_MS: 500 // 0.5secs
})
<commit_msg>Update some URLs in the constants with new values<commit_after>module.exports = Object.freeze({
  APP_ID: 'openwmail.openwmail',

  MAILBOX_INDEX_KEY: '__index__',
  MAILBOX_SLEEP_WAIT: 1000 * 30, // 30 seconds

  WEB_URL: 'https://openwmail.github.io/',
  GITHUB_URL: 'https://github.com/openwmail/openwmail/',
  GITHUB_ISSUE_URL: 'https://github.com/openwmail/openwmail/issues',
  UPDATE_DOWNLOAD_URL: 'https://github.com/openwmail/openwmail/releases',
  UPDATE_CHECK_URL: 'https://openwmail.github.io/version.json',
  PRIVACY_URL: 'https://openwmail.github.io/privacy',
  USER_SCRIPTS_WEB_URL: 'https://github.com/Thomas101/wmail-user-scripts',
  UPDATE_CHECK_INTERVAL: 1000 * 60 * 60 * 24, // 24 hours

  GMAIL_PROFILE_SYNC_MS: 1000 * 60 * 60, // 60 mins
  GMAIL_UNREAD_SYNC_MS: 1000 * 60, // 60 seconds
  GMAIL_NOTIFICATION_MAX_MESSAGE_AGE_MS: 1000 * 60 * 60 * 2, // 2 hours
  GMAIL_NOTIFICATION_FIRST_RUN_GRACE_MS: 1000 * 30, // 30 seconds

  REFOCUS_MAILBOX_INTERVAL_MS: 300,

  DB_EXTENSION: 'wmaildb',
  DB_WRITE_DELAY_MS: 500 // 0.5secs
})
<|endoftext|>


<commit_before>var _ = require('../../../lib/alloy/underscore')._,
	U = require('../../../utils'),
	CU = require('../compilerUtils'),
	CONST = require('../../../common/constants');

var PROXY_PROPERTIES = [
	'Ti.UI.TableView.HeaderView',
	'Ti.UI.TableView.HeaderPullView',
	'Ti.UI.TableView.FooterView',
	'Ti.UI.TableView.Search'
];
var VALID = [
	'Ti.UI.TableViewRow',
	'Ti.UI.TableViewSection',
	'Ti.UI.SearchBar'
];
var ALL_VALID = _.union(PROXY_PROPERTIES, VALID);

exports.parse = function(node, state) {
	return require('./base').parse(node, state, parse);
};

function parse(node, state, args) {
	var children = U.XML.getElementsFromNodes(node.childNodes),
		arrayName = CU.generateUniqueId(),
		localModel = CU.generateUniqueId(),
		code = '',
		proxyPropertyCode = '',
		hasRows = false,
		searchBarName,
		proxyProperties = [];

	// are we using collection binding with this table?
	if (args[CONST.BIND_COLLECTION]) {
		var where = args[CONST.BIND_WHERE];
		var transform = args[CONST.BIND_TRANSFORM];
		var tableState = require('./default').parse(node, state);
		code += tableState.code;

		// iterate through all children
		var itemCode = '';
		for (var i = 0, l = children.length; i < l; i++) {
			var child = children[i],
				childArgs = CU.getParserArgs(child);

			// TODO: validate children of tableview

			itemCode += CU.generateNode(child, {
				parent: {},
				local: true,
				model: localModel,
				styles: state.styles,
				post: function(node, state, args) {
					return 'rows.push(' + state.parent.symbol + ');\n';
				}
			});
		}

		// Do we use an instance or singleton collection reference?
		var col;
		if (args[CONST.BIND_COLLECTION].indexOf('$.') === 0) {
			col = args[CONST.BIND_COLLECTION];
		} else {
			col = 'Alloy.Collections[\'' + args[CONST.BIND_COLLECTION] + '\']';
		}

		// create fetch/change handler
		var whereCode = where ? where + "(" + col + ")" : col + ".models";
		var transformCode = transform ? transform + "(" + localModel + ")" : "{}";
		code += col + ".on('fetch destroy change add remove', function(e) { ";
		code += "	var models = " + whereCode + ";";
		code += "	var len = models.length;";
		code += "	var rows = [];";
		code += "	for (var i = 0; i < len; i++) {";
		code += "		var " + localModel + " = models[i];";
		code += "		" + localModel + ".__transform = " + transformCode + ";";
		code += itemCode;
		code += "	}";
		code += tableState.parent.symbol + ".setData(rows);";
		code += "});";
	} else {
		code = 'var ' + arrayName + ' = [];\n';

		// iterate through all children
		for (var i = 0, l = children.length; i < l; i++) {
			var child = children[i],
				childArgs = CU.getParserArgs(child),
				isSearchBar = false,
				isProxyProperty = false;

			// validate the child element and determine if it's part of
			// the table data, a searchbar, or a proxy property assigment
			var theNode = CU.validateNodeName(child, ALL_VALID);
			if (!theNode) {
				U.dieWithNode(child, 'Child element must be on of the following: [' + ALL_VALID.join(',') + ']');
			} else if (theNode === 'Ti.UI.SearchBar') {
				isSearchBar = true;
			} else if (_.contains(PROXY_PROPERTIES, theNode)) {
				isProxyProperty = true;
			}

			if (isProxyProperty) {
				// generate code for proxy property assigments
				proxyPropertyCode += CU.generateNode(child, {
					parent: {
						node: node,
						symbol: '<%= proxyPropertyParent %>'
					},
					styles: state.styles
				});
			} else {
				// generate code for the row/section/searchbar
				code += CU.generateNode(child, {
					parent: {},
					styles: state.styles,
					post: function(node, state, args) {
						if (isSearchBar) {
							searchBarName = state.parent.symbol;
						} else {
							hasRows = true;
							return arrayName + '.push(' + state.parent.symbol + ');\n';
						}
					}
				});
			}
		}

		// Create the initial TableView code
		var extras = [];
		if (hasRows) { extras.push(['data', arrayName]); }
		if (searchBarName) { extras.push(['search', searchBarName]) }
		if (extras.length) { state.extraStyle = CU.createVariableStyle(extras); }

		// generate the code for the table itself
		var tableState = require('./default').parse(node, state);
		code += tableState.code;

		// fill in the proxy property assignment template with the
		// symbol used to represent the table
		code += _.template(proxyPropertyCode, {proxyPropertyParent:tableState.parent.symbol});
	}

	// Update the parsing state
	return {
		parent: {},
		styles: state.styles,
		code: code
	}
};
<commit_msg>major refactor on TableView parser to streamline code and make proxy properties usable with data bound tables (ALOY-420)<commit_after>var _ = require('../../../lib/alloy/underscore')._,
	U = require('../../../utils'),
	CU = require('../compilerUtils'),
	CONST = require('../../../common/constants');

var PROXY_PROPERTIES = [
	'Ti.UI.TableView.HeaderView',
	'Ti.UI.TableView.HeaderPullView',
	'Ti.UI.TableView.FooterView',
	'Ti.UI.TableView.Search'
];
var VALID = [
	'Ti.UI.TableViewRow',
	'Ti.UI.TableViewSection',
	'Ti.UI.SearchBar'
];
var ALL_VALID = _.union(PROXY_PROPERTIES, VALID);

exports.parse = function(node, state) {
	return require('./base').parse(node, state, parse);
};

function parse(node, state, args) {
	var children = U.XML.getElementsFromNodes(node.childNodes),
		code = '',
		proxyPropertyCode = '',
		itemCode = '',
		isDataBound = args[CONST.BIND_COLLECTION] ? true : false,
		searchBarName, localModel, arrayName;

	// iterate through all children of the TableView
	_.each(children, function(child) {
		var childArgs = CU.getParserArgs(child),
			isSearchBar = false,
			isProxyProperty = false;

		// validate the child element and determine if it's part of
		// the table data, a searchbar, or a proxy property assigment
		var theNode = CU.validateNodeName(child, ALL_VALID);
		if (!theNode) {
			U.dieWithNode(child, 'Child element must be on of the following: [' + ALL_VALID.join(',') + ']');
		} else if (theNode === 'Ti.UI.SearchBar') {
			isSearchBar = true;
		} else if (_.contains(PROXY_PROPERTIES, theNode)) {
			isProxyProperty = true;
		}

		// generate code for proxy property assigments
		if (isProxyProperty) {
			proxyPropertyCode += CU.generateNode(child, {
				parent: {
					node: node,
					symbol: '<%= proxyPropertyParent %>'
				},
				styles: state.styles
			});
		// generate code for search bar
		} else if (isSearchBar) {
			code += CU.generateNode(child, {
				parent: {},
				styles: state.styles,
				post: function(node, state, args) {
					searchBarName = state.parent.symbol;
				}
			});
		// generate code for template row for model-view binding
		} else if (isDataBound) {
			localModel || (localModel = CU.generateUniqueId());
			itemCode += CU.generateNode(child, {
				parent: {},
				local: true,
				model: localModel,
				styles: state.styles,
				post: function(node, state, args) {
					return 'rows.push(' + state.parent.symbol + ');\n';
				}
			});
		// generate code for the static row/section/searchbar
		} else {
			code += CU.generateNode(child, {
				parent: {},
				styles: state.styles,
				post: function(node, state, args) {
					var postCode = '';
					if (!arrayName) {
						arrayName = CU.generateUniqueId();
						postCode += 'var ' + arrayName + '=[];';
					}
					postCode += arrayName + '.push(' + state.parent.symbol + ');';
					return postCode;
				}
			});
		}
	});

	// Create the initial TableView code
	var extras = [];
	if (arrayName) { extras.push(['data', arrayName]); }
	if (searchBarName) { extras.push(['search', searchBarName]) }
	if (extras.length) { state.extraStyle = CU.createVariableStyle(extras); }

	// generate the code for the table itself
	var tableState = require('./default').parse(node, state);
	code += tableState.code;

	// fill in the proxy property assignment template with the
	// symbol used to represent the table
	code += _.template(proxyPropertyCode, {
		proxyPropertyParent: tableState.parent.symbol
	});

	// finally, fill in any model-view binding code, if present
	if (isDataBound) {
		localModel || (localModel = CU.generateUniqueId());
		code += _.template(getBindingCode(args), {
			localModel: localModel,
			itemCode: itemCode,
			parentSymbol: tableState.parent.symbol
		});
	}

	// Update the parsing state
	return {
		parent: {},
		styles: state.styles,
		code: code
	}
}

function getBindingCode(args) {
	var code = '';

	// Do we use an instance or singleton collection reference?
	var col;
	if (args[CONST.BIND_COLLECTION].indexOf('$.') === 0) {
		col = args[CONST.BIND_COLLECTION];
	} else {
		col = 'Alloy.Collections[\'' + args[CONST.BIND_COLLECTION] + '\']';
	}

	// create fetch/change handler
	var where = args[CONST.BIND_WHERE];
	var transform = args[CONST.BIND_TRANSFORM];
	var whereCode = where ? where + "(" + col + ")" : col + ".models";
	var transformCode = transform ? transform + "(<%= localModel %>)" : "{}";
	code += col + ".on('fetch destroy change add remove', function(e) { ";
	code += "	var models = " + whereCode + ";";
	code += "	var len = models.length;";
	code += "	var rows = [];";
	code += "	for (var i = 0; i < len; i++) {";
	code += "		var <%= localModel %> = models[i];";
	code += "		<%= localModel %>.__transform = " + transformCode + ";";
	code += "<%= itemCode %>";
	code += "	}";
	code += "<%= parentSymbol %>.setData(rows);";
	code += "});";

	return code;
}

<|endoftext|>


<commit_before>export { PadInfoCalculator };
export { PadInfoCalculatorRoot };

import * as Pool from "../../util/Pool.js";
import * as Recyclable from "../../util/Recyclable.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";

/**
 * According to input image size and depthwise convolution parameters, calculate the padding information of the depthwise convolution.
 *
 * @member {number} inputHeight           Input image height.
 * @member {number} inputWidth            Input image width.
 * @member {number} inputChannelCount     Input image channel count.
 * @member {number} AvgMax_Or_ChannelMultiplier   Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
 * @member {number} filterHeight          The height of the depthwise convolution's filter.
 * @member {number} filterWidth           The width of the depthwise convolution's filter.
 * @member {number} stridesPad            The strides and padding of (depthwise) convolution. (ValueDesc.StridesPad.Singleton.Ids.Xxx)
 *
 * @member {number} channelMultiplier     The channel multiplier of the depthwise operation (according to AvgMax_Or_ChannelMultiplier).
 * @member {number} filterSize            The size of the depthwise convolution's filter. (= filterHeight * filterWidth)
 *
 * @member {number} dilationHeight        The depthwise filters's dilation across height dimension.
 * @member {number} dilationWidth         The depthwise filters's dilation across width dimension.
 *
 * @member {number} effectFilterHeight    The effect height of the depthwise convolution's filter including the dilationHeight.
 * @member {number} effectFilterWidth     The effect width of the depthwise convolution's filter including the dilationWidth.
 * @member {number} effectFilterSize      The effect size of the depthwise convolution's filter. (= effectFilterHeight * effectFilterWidth)
 *
 * @member {ValueDesc.StridesPad.Info} stridesPadInfo  The information getted according to stridesPad.
 *
 * @member {number} strides               The strides along the image's height and width dimension (according to stridesPad).
 * @member {number} stridesHeight         The strides along the image's height dimension (according to stridesPad).
 * @member {number} stridesWidth          The strides along the image's width dimension (according to stridesPad).
 *
 * @member {number} pad                   The padding along the image's height and width dimension (according to stridesPad).
 * @member {number} padHeight             The padding along the input image's height dimension.
 * @member {number} padHeightTop          The padding along the input image's height dimension at the top.
 * @member {number} padHeightBottom       The padding along the input image's height dimension at the bottom.
 * @member {number} padWidth              The padding along the input image's width dimension.
 * @member {number} padWidthLeft          The padding along the input image's width dimension at the left.
 * @member {number} padWidthRight         The padding along the input image's width dimension at the right.
 *
 * @member {number} outputHeight          Output image height.
 * @member {number} outputWidth           Output image width.
 * @member {number} outputChannelCount    Output image channel count.
 * @member {number} outputElementCount    Output image elements count (= ( outputHeight * outputWidth * outputChannelCount ) ).
 */
let PadInfoCalculator = ( ParentClass = Object ) => class PadInfoCalculator extends Recyclable.Base( ParentClass ) {

  /**
   * Used as default Depthwise.PadInfoCalculator provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PadInfoCalculator.Pool", PadInfoCalculator, PadInfoCalculator.setAsConstructor );

  /**
   *
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad, ...restArgs ) {

    super( ...restArgs );
    PadInfoCalculator.setAsConstructor_self.call( this,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /** @override */
  static setAsConstructor(
     inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad, ...restArgs ) {

    super.setAsConstructor.apply( this, restArgs );
    PadInfoCalculator.setAsConstructor_self.call( this,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
    return this;
  }

  /** @override */
  static setAsConstructor_self(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /** @override */
  disposeResources() {
    this.stridesPadInfo = null;
    super.disposeResources();
  }

  /**
   *
   */
  set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.inputHeight = inputHeight;
    this.inputWidth = inputWidth;
    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.filterWidth = filterWidth;
    this.stridesPad = stridesPad;

//!!! ...unfinished... (2021/03/17) What about ( filterHeight <= 0 ) or ( filterWidth <= 0 )?

    this.channelMultiplier = AvgMax_Or_ChannelMultiplier;
    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === AvgMax_Or_ChannelMultiplier ) ) {
      this.channelMultiplier = 1;
    }

    this.filterSize = this.filterHeight * this.filterWidth;

    // Strides and Padding.
    this.stridesPadInfo = ValueDesc.StridesPad.Singleton.getInfoById( stridesPad );
    {
      if ( !this.stridesPadInfo ) { // If not found, using default which could let add-input-to-output possible.
        this.stridesPadInfo = ValueDesc.StridesPad.Singleton.getInfoById( ValueDesc.StridesPad.Singleton.Ids.STRIDES_1_PAD_SAME );
      }

      this.strides = this.stridesPadInfo.strides;
      this.pad = this.stridesPadInfo.pad;
    }

    // Assume strides width equals strides height.
    this.stridesHeight = this.strides;
    this.stridesWidth = this.strides;

    // Currently, we can only handle dilation = 1.
    this.dilationHeight = 1;
    this.dilationWidth = 1;

    // Effect filter size (includes dilation).
    this.effectFilterHeight = this.dilationHeight * ( this.filterHeight - 1 ) + 1;
    this.effectFilterWidth =  this.dilationWidth  * ( this.filterWidth  - 1 ) + 1;
    this.effectFilterSize = this.effectFilterHeight * this.effectFilterWidth;

    // (The following codes for output image height and width and padding calculation are copied from
    // https://github.com/tensorflow/tfjs/blob/tfjs-v3.8.0/tfjs-core/src/ops/conv_util.ts)
    {
      // Determine output image height and width without padding.
      if ( this.stridesPadInfo.pad_isValid() ) {
        this.outputHeight = Math.ceil( ( inputHeight - this.effectFilterHeight + 1 ) / this.stridesHeight );
        this.outputWidth =  Math.ceil( ( inputWidth  - this.effectFilterWidth  + 1 ) / this.stridesWidth  );

        this.padHeight = this.padHeightTop = this.padHeightBottom = this.padWidth = this.padWidthLeft = this.padWidthRight = 0;

      // Determine output image height and width with padding around the input image height and width.
      } else if ( this.stridesPadInfo.pad_isSame() ) {
        this.outputHeight = Math.ceil( inputHeight / this.stridesHeight );
        this.outputWidth =  Math.ceil( inputWidth  / this.stridesWidth  );

        this.padHeight = Math.max( 0, ( this.outputHeight - 1 ) * this.stridesHeight + this.effectFilterHeight - inputHeight );
        this.padWidth =  Math.max( 0, ( this.outputWidth  - 1 ) * this.stridesWidth  + this.effectFilterWidth  - inputWidth  );

        this.padHeightTop =    Math.floor( this.padHeight / 2 );
        this.padHeightBottom = this.padHeight - this.padHeightTop;
        this.padWidthLeft =    Math.floor( this.padWidth /  2 );
        this.padWidthRight =   this.padWidth  - this.padWidthLeft;

      } else {
        throw Error(
          `Depthwise.PadInfoCalculator.set(): `
            + `stridesPadInfo.pad ( ${this.stridesPadInfo.pad} ) is unknown value.`
        );
      }
    }

    this.outputChannelCount = inputChannelCount * this.channelMultiplier;
    this.outputElementCount = ( this.outputHeight * this.outputWidth * this.outputChannelCount );
  }

  /** @return {boolean} If the channel count of this depthwise operation's output is the same as its input, return true. */
  output_channelCount_is_same_as_input() {
    return PadInfoCalculator.output_channelCount_is_same_as_input( this.AvgMax_Or_ChannelMultiplier );
  }

  /** @return {boolean} If the ( height, width ) of this depthwise operation's output is the same as its input, return true. */
  output_height_width_is_same_as_input() {
    return PadInfoCalculator.output_height_width_is_same_as_input( this.inputHeight, this.inputWidth,
      this.AvgMax_Or_ChannelMultiplier, this.filterHeight, this.filterWidth, this.stridesPadInfo );
  }

  /** @return {boolean} If this depthwise operation does not analyze the neighbor in the direction of height and width, return true. */
  output_height_width_is_no_neighbor_analysis() {
    return PadInfoCalculator.output_height_width_is_no_neighbor_analysis( this.inputHeight, this.inputWidth,
      this.AvgMax_Or_ChannelMultiplier, this.filterHeight, this.filterWidth );
  }

  /**
   * @param {number} depthwise_AvgMax_Or_ChannelMultiplier  Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
   *
   * @return {boolean} If the channel count of the depthwise operation's output is the same as its input, return true.
   */
  static output_channelCount_is_same_as_input( depthwise_AvgMax_Or_ChannelMultiplier ) {
    return ( depthwise_AvgMax_Or_ChannelMultiplier <= 1 ); // e.g. avg pooling, or max pooling, or none, or ( channelMultipler == 1 ).
  }

  /**
   * @param {number} depthwise_AvgMax_Or_ChannelMultiplier       Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
   * @param {ValueDesc.StridesPad.Info} depthwiseStridesPadInfo  The information of stridesPad.
   *
   * @return {boolean} If the ( height, width ) of the depthwise operation's output is the same as its input, return true.
   */
  static output_height_width_is_same_as_input(
    inputHeight, inputWidth,
    depthwise_AvgMax_Or_ChannelMultiplier, depthwiseFilterHeight, depthwiseFilterWidth, depthwiseStridesPadInfo
  ) {
    return (
         ( depthwise_AvgMax_Or_ChannelMultiplier == ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE ) // (0), no-op
      || ( ( 1 == inputHeight ) && ( 1 == inputWidth ) )  // input's height and width can not be shrinked any more.
      || (   ( 1 == depthwiseStridesPadInfo.strides )     // Not shrinked by strides.
          && (   ( depthwiseStridesPadInfo.pad_isSame() ) // Not shrinked by filters for ( pad is "same" ).
              || ( depthwiseStridesPadInfo.pad_isValid() && ( 1 == depthwiseFilterHeight ) && ( 1 == depthwiseFilterWidth ) ) // Not shrinked by filters for ( pad is "valid" ). (i.e. ( filter size is 1x1 ) )
             )
         )
    );
  }

  /**
   * @param {number} depthwise_AvgMax_Or_ChannelMultiplier  Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
   *
   * @return {boolean} If the depthwise operation does not analyze the neighbor in the direction of height and width, return true.
   */
  static output_height_width_is_no_neighbor_analysis(
    inputHeight, inputWidth,
    depthwise_AvgMax_Or_ChannelMultiplier, depthwiseFilterHeight, depthwiseFilterWidth
  ) {
    return (
         ( depthwise_AvgMax_Or_ChannelMultiplier == ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE ) // (0), no-op
      || ( ( 1 == inputHeight ) && ( 1 == inputWidth ) )  // input's height and width do not have neighbor to be analyzed.
      || ( ( 1 == depthwiseFilterHeight ) && ( 1 == depthwiseFilterWidth ) ) // The filters do not analyze any neighbor.
    );
  }

}

/**
 * Almost the same as Depthwise.PadInfoCalculator class except its parent class is fixed to Object. In other words, caller can not
 * specify the parent class of Depthwise.PadInfoCalculator.Root (so it is named "Root" which can not have parent class).
 */
class PadInfoCalculatorRoot extends PadInfoCalculator() {
}

<commit_msg>Update Depthwise_PadInfoCalculator.js<commit_after>export { PadInfoCalculator };
export { PadInfoCalculatorRoot };

import * as Pool from "../../util/Pool.js";
import * as Recyclable from "../../util/Recyclable.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";

/**
 * According to input image size and depthwise convolution parameters, calculate the padding information of the depthwise convolution.
 *
 * @member {number} inputHeight           Input image height.
 * @member {number} inputWidth            Input image width.
 * @member {number} inputChannelCount     Input image channel count.
 * @member {number} AvgMax_Or_ChannelMultiplier   Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
 * @member {number} filterHeight          The height of the depthwise convolution's filter.
 * @member {number} filterWidth           The width of the depthwise convolution's filter.
 * @member {number} stridesPad            The strides and padding of (depthwise) convolution. (ValueDesc.StridesPad.Singleton.Ids.Xxx)
 *
 * @member {number} channelMultiplier     The channel multiplier of the depthwise operation (according to AvgMax_Or_ChannelMultiplier).
 * @member {number} filterSize            The size of the depthwise convolution's filter. (= filterHeight * filterWidth)
 *
 * @member {number} dilationHeight        The depthwise filters's dilation across height dimension.
 * @member {number} dilationWidth         The depthwise filters's dilation across width dimension.
 *
 * @member {number} effectFilterHeight    The effect height of the depthwise convolution's filter including the dilationHeight.
 * @member {number} effectFilterWidth     The effect width of the depthwise convolution's filter including the dilationWidth.
 * @member {number} effectFilterSize      The effect size of the depthwise convolution's filter. (= effectFilterHeight * effectFilterWidth)
 *
 * @member {ValueDesc.StridesPad.Info} stridesPadInfo  The information getted according to stridesPad.
 *
 * @member {number} strides               The strides along the image's height and width dimension (according to stridesPad).
 * @member {number} stridesHeight         The strides along the image's height dimension (according to stridesPad).
 * @member {number} stridesWidth          The strides along the image's width dimension (according to stridesPad).
 *
 * @member {number} pad                   The padding along the image's height and width dimension (according to stridesPad).
 * @member {number} padHeight             The padding along the input image's height dimension.
 * @member {number} padHeightTop          The padding along the input image's height dimension at the top.
 * @member {number} padHeightBottom       The padding along the input image's height dimension at the bottom.
 * @member {number} padWidth              The padding along the input image's width dimension.
 * @member {number} padWidthLeft          The padding along the input image's width dimension at the left.
 * @member {number} padWidthRight         The padding along the input image's width dimension at the right.
 *
 * @member {number} outputHeight          Output image height.
 * @member {number} outputWidth           Output image width.
 * @member {number} outputChannelCount    Output image channel count.
 * @member {number} outputElementCount    Output image elements count (= ( outputHeight * outputWidth * outputChannelCount ) ).
 */
let PadInfoCalculator = ( ParentClass = Object ) => class PadInfoCalculator extends Recyclable.Base( ParentClass ) {

  /**
   * Used as default Depthwise.PadInfoCalculator provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "Depthwise.PadInfoCalculator.Pool", PadInfoCalculator, PadInfoCalculator.setAsConstructor );

  /**
   *
   */
  constructor(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad, ...restArgs ) {

    super( ...restArgs );
    PadInfoCalculator.setAsConstructor_self.call( this,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /** @override */
  static setAsConstructor(
     inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad, ...restArgs ) {

    super.setAsConstructor.apply( this, restArgs );
    PadInfoCalculator.setAsConstructor_self.call( this,
      inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
    return this;
  }

  /** @override */
  static setAsConstructor_self(
    inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /** @override */
  disposeResources() {
    this.stridesPadInfo = null;
    super.disposeResources();
  }

  /**
   *
   */
  set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.inputHeight = inputHeight;
    this.inputWidth = inputWidth;
    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.filterWidth = filterWidth;
    this.stridesPad = stridesPad;

//!!! ...unfinished... (2021/03/17) What about ( filterHeight <= 0 ) or ( filterWidth <= 0 )?

    this.channelMultiplier = AvgMax_Or_ChannelMultiplier;
    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === AvgMax_Or_ChannelMultiplier ) ) {
      this.channelMultiplier = 1;
    }

    this.filterSize = this.filterHeight * this.filterWidth;

    // Strides and Padding.
    this.stridesPadInfo = ValueDesc.StridesPad.Singleton.getInfoById( stridesPad );
    {
      if ( !this.stridesPadInfo ) { // If not found, using default which could let add-input-to-output possible.
        this.stridesPadInfo = ValueDesc.StridesPad.Singleton.getInfoById( ValueDesc.StridesPad.Singleton.Ids.STRIDES_1_PAD_SAME );
      }

      this.strides = this.stridesPadInfo.strides;
      this.pad = this.stridesPadInfo.pad;
    }

    // Assume strides width equals strides height.
    this.stridesHeight = this.strides;
    this.stridesWidth = this.strides;

    // Currently, we can only handle dilation = 1.
    this.dilationHeight = 1;
    this.dilationWidth = 1;

    // Effect filter size (includes dilation).
    this.effectFilterHeight = this.dilationHeight * ( this.filterHeight - 1 ) + 1;
    this.effectFilterWidth =  this.dilationWidth  * ( this.filterWidth  - 1 ) + 1;
    this.effectFilterSize = this.effectFilterHeight * this.effectFilterWidth;

    // (The following codes for output image height and width and padding calculation are copied from
    // https://github.com/tensorflow/tfjs/blob/tfjs-v3.8.0/tfjs-core/src/ops/conv_util.ts)
    {
      // Determine output image height and width without padding.
      if ( this.stridesPadInfo.pad_isValid() ) {
        this.outputHeight = Math.ceil( ( inputHeight - this.effectFilterHeight + 1 ) / this.stridesHeight );
        this.outputWidth =  Math.ceil( ( inputWidth  - this.effectFilterWidth  + 1 ) / this.stridesWidth  );

        this.padHeight = this.padHeightTop = this.padHeightBottom = this.padWidth = this.padWidthLeft = this.padWidthRight = 0;

      // Determine output image height and width with padding around the input image height and width.
      } else if ( this.stridesPadInfo.pad_isSame() ) {
        this.outputHeight = Math.ceil( inputHeight / this.stridesHeight );
        this.outputWidth =  Math.ceil( inputWidth  / this.stridesWidth  );

        this.padHeight = Math.max( 0, ( this.outputHeight - 1 ) * this.stridesHeight + this.effectFilterHeight - inputHeight );
        this.padWidth =  Math.max( 0, ( this.outputWidth  - 1 ) * this.stridesWidth  + this.effectFilterWidth  - inputWidth  );

        this.padHeightTop =    Math.floor( this.padHeight / 2 );
        this.padHeightBottom = this.padHeight - this.padHeightTop;
        this.padWidthLeft =    Math.floor( this.padWidth /  2 );
        this.padWidthRight =   this.padWidth  - this.padWidthLeft;

      } else {
        throw Error(
          `Depthwise.PadInfoCalculator.set(): `
            + `stridesPadInfo.pad ( ${this.stridesPadInfo.pad} ) is unknown value.`
        );
      }
    }

    this.outputChannelCount = inputChannelCount * this.channelMultiplier;
    this.outputElementCount = ( this.outputHeight * this.outputWidth * this.outputChannelCount );
  }

  /** @return {boolean} If the channel count of this depthwise operation's output is the same as its input, return true. */
  output_channelCount_is_same_as_input() {
    return PadInfoCalculator.output_channelCount_is_same_as_input( this.AvgMax_Or_ChannelMultiplier );
  }

  /** @return {boolean} If the ( height, width ) of this depthwise operation's output is the same as its input, return true. */
  output_height_width_is_same_as_input() {
    return PadInfoCalculator.output_height_width_is_same_as_input( this.inputHeight, this.inputWidth,
      this.AvgMax_Or_ChannelMultiplier, this.filterHeight, this.filterWidth, this.stridesPadInfo );
  }

  /** @return {boolean} If this depthwise operation does not analyze the neighbor in the direction of height and width, return true. */
  output_height_width_is_no_neighbor_analysis() {
    return PadInfoCalculator.output_height_width_is_no_neighbor_analysis( this.inputHeight, this.inputWidth,
      this.AvgMax_Or_ChannelMultiplier, this.filterHeight, this.filterWidth );
  }

  /**
   * @param {number} depthwise_AvgMax_Or_ChannelMultiplier  Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
   *
   * @return {boolean} If the channel count of the depthwise operation's output is the same as its input, return true.
   */
  static output_channelCount_is_same_as_input( depthwise_AvgMax_Or_ChannelMultiplier ) {
    return ( depthwise_AvgMax_Or_ChannelMultiplier <= 1 ); // e.g. avg pooling, or max pooling, or none, or ( channelMultipler == 1 ).
  }

  /**
   * @param {number} depthwise_AvgMax_Or_ChannelMultiplier       Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
   * @param {ValueDesc.StridesPad.Info} depthwiseStridesPadInfo  The information of stridesPad.
   *
   * @return {boolean} If the ( height, width ) of the depthwise operation's output is the same as its input, return true.
   */
  static output_height_width_is_same_as_input(
    inputHeight, inputWidth,
    depthwise_AvgMax_Or_ChannelMultiplier, depthwiseFilterHeight, depthwiseFilterWidth, depthwiseStridesPadInfo
  ) {
    return (
         ( depthwise_AvgMax_Or_ChannelMultiplier == ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE ) // (0), no-op
      || ( ( 1 == inputHeight ) && ( 1 == inputWidth ) )  // input's height and width can not be shrinked any more.
      || (   ( 1 == depthwiseStridesPadInfo.strides )     // Not shrinked by strides.
          && (   ( depthwiseStridesPadInfo.pad_isSame() ) // Not shrinked by filters for ( pad is "same" ).
              || ( depthwiseStridesPadInfo.pad_isValid() && ( 1 == depthwiseFilterHeight ) && ( 1 == depthwiseFilterWidth ) ) // Not shrinked by filters for ( pad is "valid" ). (i.e. ( filter size is 1x1 ) )
             )
         )
    );
  }

  /**
   * @param {number} depthwise_AvgMax_Or_ChannelMultiplier  Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.Xxx)
   *
   * @return {boolean} If the depthwise operation does not analyze the neighbor in the direction of height and width, return true.
   */
  static output_height_width_is_no_neighbor_analysis(
    inputHeight, inputWidth,
    depthwise_AvgMax_Or_ChannelMultiplier, depthwiseFilterHeight, depthwiseFilterWidth
  ) {
    return (
         ( depthwise_AvgMax_Or_ChannelMultiplier == ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE ) // (0), no-op
      || ( ( 1 == inputHeight ) && ( 1 == inputWidth ) )  // input's height and width do not have neighbor to be analyzed.
      || ( ( 1 == depthwiseFilterHeight ) && ( 1 == depthwiseFilterWidth ) ) // The filters do not analyze any neighbor.
    );
  }

}

/**
 * Almost the same as Depthwise.PadInfoCalculator class except its parent class is fixed to Object. In other words, caller can not
 * specify the parent class of Depthwise.PadInfoCalculatorRoot (so it is named "Root" which can not have parent class).
 */
class PadInfoCalculatorRoot extends PadInfoCalculator() {
}

<|endoftext|>


<commit_before>export { PadInfoCalculator };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";

/**
 * According to input image size and depthwise convolution parameters, calculate the padding information of the depthwise convolution.
 *
 * @member {number} inputHeight           Input image height.
 * @member {number} inputWidth            Input image width.
 * @member {number} inputChannelCount     Input image channel count.
 * @member {number} AvgMax_Or_ChannelMultiplier   Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier)
 * @member {number} filterHeight          The height of the depthwise convolution's filter.
 * @member {number} filterWidth           The width of the depthwise convolution's filter.
 * @member {number} stridesPad            The strides and padding of depthwise convolution. (PointDepthPoint.Params.depthwiseStridesPad)
 *
 * @member {number} channelMultiplier     The channel multiplier of the depthwise operation (according to AvgMax_Or_ChannelMultiplier).
 *
 * @member {number} dilationHeight        The depthwise filters's dilation across height dimension.
 * @member {number} dilationWidth         The depthwise filters's dilation across width dimension.
 *
 * @member {number} effectFilterHeight    The effect height of the depthwise convolution's filter including the dilationHeight.
 * @member {number} effectFilterWidth     The effect width of the depthwise convolution's filter including the dilationWidth.
 * @member {number} effectFilterSize      The effect size of the depthwise convolution's filter. (= effectFilterHeight * effectFilterWidth)
 *
 * @member {number} strides               The strides along the image's height and width dimension (according to stridesPad).
 * @member {number} stridesHeight         The strides along the image's height dimension (according to stridesPad).
 * @member {number} stridesWidth          The strides along the image's width dimension (according to stridesPad).

 * @member {number} pad                   The padding along the image's height and width dimension (according to stridesPad).
 * @member {number} padHeight             The padding along the input image's height dimension.
 * @member {number} padHeightTop          The padding along the input image's height dimension at the top.
 * @member {number} padHeightBottom       The padding along the input image's height dimension at the bottom.
 * @member {number} padWidth              The padding along the input image's width dimension.
 * @member {number} padWidthLeft          The padding along the input image's width dimension at the left.
 * @member {number} padWidthRight         The padding along the input image's width dimension at the right.
 *
 * @member {number} outputHeight          Output image height.
 * @member {number} outputWidth           Output image width.
 * @member {number} outputChannelCount    Output image channel count.
 * @member {number} outputElementCount    Output image elements count (= ( outputHeight * outputWidth * outputChannelCount ) ).
 */
let PadInfoCalculator = ( Base = Object ) => class extends Base {

  constructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    super();
    this.inputHeight = inputHeight;
    this.inputWidth = inputWidth;
    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.filterWidth = filterWidth;
    this.stridesPad = stridesPad;

//!!! ...unfinished... (2021/03/17) What about ( filterHeight <= 0 ) or ( filterWidth <= 0 )?

    this.channelMultiplier = AvgMax_Or_ChannelMultiplier;
    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === AvgMax_Or_ChannelMultiplier ) ) {
      this.channelMultiplier = 1;
    }

    // Strides and Padding.
    switch ( stridesPad ) {
      case 0:  this.strides = 1; this.pad = "valid"; break;
      default:
      case 1:  this.strides = 1; this.pad = "same";  break;
      case 2:  this.strides = 2; this.pad = "same";  break;
    }

    // Assume strides width equals strides height.
    this.stridesHeight = this.strides;
    this.stridesWidth = this.strides;

    // Currently, we can only handle dilation = 1.
    this.dilationHeight = 1;
    this.dilationWidth = 1;

    // Effect filter size (includes dilation).
    this.effectFilterHeight = this.dilationHeight * ( this.filterHeight - 1 ) + 1;
    this.effectFilterWidth =  this.dilationWidth  * ( this.filterWidth  - 1 ) + 1;
    this.effectFilterSize = this.effectFilterHeight * this.effectFilterWidth;

    // (The following codes for output image height and width and padding calculation are copied from
    // https://github.com/tensorflow/tfjs/blob/tfjs-v3.8.0/tfjs-core/src/ops/conv_util.ts)
    {
      // Determine output image height and width without padding.
      if ( this.pad == "valid" ) {
        this.outputHeight = Math.ceil( ( inputHeight - this.effectFilterHeight + 1 ) / this.stridesHeight );
        this.outputWidth =  Math.ceil( ( inputWidth  - this.effectFilterWidth  + 1 ) / this.stridesWidth  );

        this.padHeight = this.padHeightTop = this.padHeightBottom = this.padWidth = this.padWidthLeft = this.padWidthRight = 0;

      // Determine output image height and width with padding around the input image height and width.
      } else if ( this.pad == "same" ) {
        this.outputHeight = Math.ceil( inputHeight / this.stridesHeight );
        this.outputWidth =  Math.ceil( inputWidth  / this.stridesWidth  );

        this.padHeight = Math.max( 0, ( this.outputHeight - 1 ) * this.stridesHeight + this.effectFilterHeight - inputHeight );
        this.padWidth =  Math.max( 0, ( this.outputWidth  - 1 ) * this.stridesWidth  + this.effectFilterWidth  - inputWidth  );

        this.padHeightTop =    Math.floor( this.padHeight / 2 );
        this.padHeightBottom = this.padHeight - this.padHeightTop;
        this.padWidthLeft =    Math.floor( this.padWidth /  2 );
        this.padWidthRight =   this.padWidth  - this.padWidthLeft;
      }
    }

    this.outputChannelCount = inputChannelCount * this.channelMultiplier;
    this.outputElementCount = ( this.outputHeight * this.outputWidth * this.outputChannelCount );
  }

  /**
   * @return {boolean}
   *   If the ( height, width ) of this depthwise operation output is the same as its input, return true.
   */
  is_Output_Same_HeightWidth_As_Input() {

    // If this depthwise operation does not existed, the output will have the same ( height, width ) as input.
    // In fact, they are the same one in this case.
    if ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE === this.AvgMax_Or_ChannelMultiplier )
      return true;

    if ( this.strides != 1 )
      return false; // If strides is not 1, it is impossible to output same ( height, width ) as input.

    if ( this.pad == "same" )
      return true; // If ( strides is 1 ) and ( pad is "same" ), the output will have the same ( height, width ) as input.

    // Or, although ( strides is 1 ) and ( pad is "valid" ) but ( filter size is 1x1 ), the output will have the same ( height, width ) as input.
    if ( ( this.pad == "valid" ) && ( this.filterHeight == 1 ) && ( this.filterWidth == 1 ) )
      return true;

    return false;
  }

  /**
   * @param {number} filterValue
   *   The (non-zero) value used in the pass-through depthwise convolution filter. Default is 1.
   *
   * @return {number[]}
   *   Return the depthwise convolution filters which could pass the input to output unchangely.
   */
  generate_PassThrough_FiltersArray( filterValue = 1 ) {

    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === this.AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === this.AvgMax_Or_ChannelMultiplier ) ) {
      return null; // The depthwise filter of AVG pooling and MAX pooling can not be manipulated.
    }

    // Make up a depthwise convolution filter.
    let depthwiseFiltersArray = new Array( this.filterHeight * this.filterWidth * this.inputChannelCount * this.channelMultiplier );

    // There is only one position (inside the effect depthwise filter) with value one. All other positions of the filter should be zero.
    let oneEffectFilterY = this.padHeightTop;
    let oneEffectFilterX = this.padWidthLeft;

    // Note: Unfortunately, this does not work for ( dilation > 1 ). So, only ( dilation == 1 ) is supported.
    for ( let inChannel = 0; inChannel < this.inputChannelCount; ++inChannel ) {

      for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub ) {

        for ( let filterY = 0, effectFilterY = 0; filterY < this.filterHeight; ++filterY ) {
          for ( let dilationFilterY = 0; dilationFilterY < this.dilationHeight; ++dilationFilterY, ++effectFilterY ) {
            let filterIndexBaseX = ( filterY * this.filterWidth );

            for ( let filterX = 0, effectFilterX = 0; filterX < this.filterWidth; ++filterX ) {
              for ( let dilationFilterX = 0; dilationFilterX < this.dilationWidth; ++dilationFilterX, ++effectFilterX ) {

                // The filter's dilation part can not be manipulated. (They are always zero.)
                if ( ( 0 != dilationFilterY ) || ( 0 != dilationFilterX ) )
                  continue;

                let filterIndexBaseC = ( ( filterIndexBaseX + filterX ) * this.outputChannelCount );
                let filterIndexBaseSubC = filterIndexBaseC + ( inChannel * this.channelMultiplier );

                let filterIndex = filterIndexBaseSubC + outChannelSub;

                if ( ( effectFilterY == oneEffectFilterY ) && ( effectFilterX == oneEffectFilterX ) ) {
                  depthwiseFiltersArray[ filterIndex ] = filterValue; // The only one position with value non-zero.
                  filterValue
                } else {
                  depthwiseFiltersArray[ filterIndex ] = 0; // All other positions of the filter are value zero.
                }
              }
            }
          }
        }
      }
    }

    return depthwiseFiltersArray;
  }

}

<commit_msg>Update Depthwise_PadInfoCalculator.js<commit_after>export { PadInfoCalculator };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";

/**
 * According to input image size and depthwise convolution parameters, calculate the padding information of the depthwise convolution.
 *
 * @member {number} inputHeight           Input image height.
 * @member {number} inputWidth            Input image width.
 * @member {number} inputChannelCount     Input image channel count.
 * @member {number} AvgMax_Or_ChannelMultiplier   Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier)
 * @member {number} filterHeight          The height of the depthwise convolution's filter.
 * @member {number} filterWidth           The width of the depthwise convolution's filter.
 * @member {number} stridesPad            The strides and padding of depthwise convolution. (PointDepthPoint.Params.depthwiseStridesPad)
 *
 * @member {number} channelMultiplier     The channel multiplier of the depthwise operation (according to AvgMax_Or_ChannelMultiplier).
 *
 * @member {number} dilationHeight        The depthwise filters's dilation across height dimension.
 * @member {number} dilationWidth         The depthwise filters's dilation across width dimension.
 *
 * @member {number} effectFilterHeight    The effect height of the depthwise convolution's filter including the dilationHeight.
 * @member {number} effectFilterWidth     The effect width of the depthwise convolution's filter including the dilationWidth.
 * @member {number} effectFilterSize      The effect size of the depthwise convolution's filter. (= effectFilterHeight * effectFilterWidth)
 *
 * @member {number} strides               The strides along the image's height and width dimension (according to stridesPad).
 * @member {number} stridesHeight         The strides along the image's height dimension (according to stridesPad).
 * @member {number} stridesWidth          The strides along the image's width dimension (according to stridesPad).

 * @member {number} pad                   The padding along the image's height and width dimension (according to stridesPad).
 * @member {number} padHeight             The padding along the input image's height dimension.
 * @member {number} padHeightTop          The padding along the input image's height dimension at the top.
 * @member {number} padHeightBottom       The padding along the input image's height dimension at the bottom.
 * @member {number} padWidth              The padding along the input image's width dimension.
 * @member {number} padWidthLeft          The padding along the input image's width dimension at the left.
 * @member {number} padWidthRight         The padding along the input image's width dimension at the right.
 *
 * @member {number} outputHeight          Output image height.
 * @member {number} outputWidth           Output image width.
 * @member {number} outputChannelCount    Output image channel count.
 * @member {number} outputElementCount    Output image elements count (= ( outputHeight * outputWidth * outputChannelCount ) ).
 */
let PadInfoCalculator = ( Base = Object ) => class extends Base {

  constructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    super();
    this.set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /**
   *
   */
  set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.inputHeight = inputHeight;
    this.inputWidth = inputWidth;
    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.filterWidth = filterWidth;
    this.stridesPad = stridesPad;

//!!! ...unfinished... (2021/03/17) What about ( filterHeight <= 0 ) or ( filterWidth <= 0 )?

    this.channelMultiplier = AvgMax_Or_ChannelMultiplier;
    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === AvgMax_Or_ChannelMultiplier ) ) {
      this.channelMultiplier = 1;
    }

    // Strides and Padding.
    switch ( stridesPad ) {
      case 0:  this.strides = 1; this.pad = "valid"; break;
      default:
      case 1:  this.strides = 1; this.pad = "same";  break;
      case 2:  this.strides = 2; this.pad = "same";  break;
    }

    // Assume strides width equals strides height.
    this.stridesHeight = this.strides;
    this.stridesWidth = this.strides;

    // Currently, we can only handle dilation = 1.
    this.dilationHeight = 1;
    this.dilationWidth = 1;

    // Effect filter size (includes dilation).
    this.effectFilterHeight = this.dilationHeight * ( this.filterHeight - 1 ) + 1;
    this.effectFilterWidth =  this.dilationWidth  * ( this.filterWidth  - 1 ) + 1;
    this.effectFilterSize = this.effectFilterHeight * this.effectFilterWidth;

    // (The following codes for output image height and width and padding calculation are copied from
    // https://github.com/tensorflow/tfjs/blob/tfjs-v3.8.0/tfjs-core/src/ops/conv_util.ts)
    {
      // Determine output image height and width without padding.
      if ( this.pad == "valid" ) {
        this.outputHeight = Math.ceil( ( inputHeight - this.effectFilterHeight + 1 ) / this.stridesHeight );
        this.outputWidth =  Math.ceil( ( inputWidth  - this.effectFilterWidth  + 1 ) / this.stridesWidth  );

        this.padHeight = this.padHeightTop = this.padHeightBottom = this.padWidth = this.padWidthLeft = this.padWidthRight = 0;

      // Determine output image height and width with padding around the input image height and width.
      } else if ( this.pad == "same" ) {
        this.outputHeight = Math.ceil( inputHeight / this.stridesHeight );
        this.outputWidth =  Math.ceil( inputWidth  / this.stridesWidth  );

        this.padHeight = Math.max( 0, ( this.outputHeight - 1 ) * this.stridesHeight + this.effectFilterHeight - inputHeight );
        this.padWidth =  Math.max( 0, ( this.outputWidth  - 1 ) * this.stridesWidth  + this.effectFilterWidth  - inputWidth  );

        this.padHeightTop =    Math.floor( this.padHeight / 2 );
        this.padHeightBottom = this.padHeight - this.padHeightTop;
        this.padWidthLeft =    Math.floor( this.padWidth /  2 );
        this.padWidthRight =   this.padWidth  - this.padWidthLeft;
      }
    }

    this.outputChannelCount = inputChannelCount * this.channelMultiplier;
    this.outputElementCount = ( this.outputHeight * this.outputWidth * this.outputChannelCount );
  }

  /**
   * @return {boolean}
   *   If the ( height, width ) of this depthwise operation output is the same as its input, return true.
   */
  is_Output_Same_HeightWidth_As_Input() {

    // If this depthwise operation does not existed, the output will have the same ( height, width ) as input.
    // In fact, they are the same one in this case.
    if ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE === this.AvgMax_Or_ChannelMultiplier )
      return true;

    if ( this.strides != 1 )
      return false; // If strides is not 1, it is impossible to output same ( height, width ) as input.

    if ( this.pad == "same" )
      return true; // If ( strides is 1 ) and ( pad is "same" ), the output will have the same ( height, width ) as input.

    // Or, although ( strides is 1 ) and ( pad is "valid" ) but ( filter size is 1x1 ), the output will have the same ( height, width ) as input.
    if ( ( this.pad == "valid" ) && ( this.filterHeight == 1 ) && ( this.filterWidth == 1 ) )
      return true;

    return false;
  }

  /**
   * @param {number} filterValue
   *   The (non-zero) value used in the pass-through depthwise convolution filter. Default is 1.
   *
   * @return {number[]}
   *   Return the depthwise convolution filters which could pass the input to output unchangely.
   */
  generate_PassThrough_FiltersArray( filterValue = 1 ) {

    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === this.AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === this.AvgMax_Or_ChannelMultiplier ) ) {
      return null; // The depthwise filter of AVG pooling and MAX pooling can not be manipulated.
    }

    // Make up a depthwise convolution filter.
    let depthwiseFiltersArray = new Array( this.filterHeight * this.filterWidth * this.inputChannelCount * this.channelMultiplier );

    // There is only one position (inside the effect depthwise filter) with value one. All other positions of the filter should be zero.
    let oneEffectFilterY = this.padHeightTop;
    let oneEffectFilterX = this.padWidthLeft;

    // Note: Unfortunately, this does not work for ( dilation > 1 ). So, only ( dilation == 1 ) is supported.
    for ( let inChannel = 0; inChannel < this.inputChannelCount; ++inChannel ) {

      for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub ) {

        for ( let filterY = 0, effectFilterY = 0; filterY < this.filterHeight; ++filterY ) {
          for ( let dilationFilterY = 0; dilationFilterY < this.dilationHeight; ++dilationFilterY, ++effectFilterY ) {
            let filterIndexBaseX = ( filterY * this.filterWidth );

            for ( let filterX = 0, effectFilterX = 0; filterX < this.filterWidth; ++filterX ) {
              for ( let dilationFilterX = 0; dilationFilterX < this.dilationWidth; ++dilationFilterX, ++effectFilterX ) {

                // The filter's dilation part can not be manipulated. (They are always zero.)
                if ( ( 0 != dilationFilterY ) || ( 0 != dilationFilterX ) )
                  continue;

                let filterIndexBaseC = ( ( filterIndexBaseX + filterX ) * this.outputChannelCount );
                let filterIndexBaseSubC = filterIndexBaseC + ( inChannel * this.channelMultiplier );

                let filterIndex = filterIndexBaseSubC + outChannelSub;

                if ( ( effectFilterY == oneEffectFilterY ) && ( effectFilterX == oneEffectFilterX ) ) {
                  depthwiseFiltersArray[ filterIndex ] = filterValue; // The only one position with value non-zero.
                  filterValue
                } else {
                  depthwiseFiltersArray[ filterIndex ] = 0; // All other positions of the filter are value zero.
                }
              }
            }
          }
        }
      }
    }

    return depthwiseFiltersArray;
  }

}

<|endoftext|>


<commit_before>var app                 = require('../config/app');
var db                  = require('../config/db');
var mongoose            = require('mongoose');
var request             = require('request');
var Promise             = require('promise');
var achievements        = require('./wow-achievements.json');
var WarcraftProvider    = require('../app/modules/gaming/warcraft/warcraft-provider');
var warcraftProvider    = new WarcraftProvider();

console.log('connecting to db...');
mongoose.connect(db.url);

var profile = {
    "characters": []
};
var promises = [];
app.wow.characters.forEach(function(character) {
    var promise = new Promise(function(resolve) {
        var options = {
            uri: 'http://us.battle.net/api/wow/character/' + character.realm + '/' + character.name,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        };
        if(character.showcase) {
            // just ensure we only pull these once, all characters return account-wide pets and mounts
            options.uri +=  '?fields=feed,items,stats,pvp,progression,pets,mounts,achievements';
        }

        console.log('requesting character data for %s...', character.name);
        request(options, function(err, res, body) {
            if(err) {
                console.err(err);
                mongoose.disconnect();
                return;
            }
            if(!body) {
                console.err('request returned empty');
                mongoose.disconnect();
                return;
            }

            var data = JSON.parse(body);
            if(data) {
                console.log('received info for character: ' + data.name);
                if(character.showcase) {
                    profile.achievementPoints = data.achievementPoints;
                    profile.pets = data.pets;
                    profile.mounts = data.mounts;
                    profile.achievements = parseAchievementObject(achievements.supercats, data);
                    data.showcase = true;
                    parseFeed(data.feed).then(function(feed) {
                        data.feed = feed;
                        profile.characters.push(data);
                        resolve(data);
                    });
                } else {
                    profile.characters.push(data);
                    resolve(data);
                }
            } else {
                console.log('unexpected results...');
                mongoose.disconnect();
            }
        }).on('error', function(e) {
            console.error(e.message);
            mongoose.disconnect();
        });
    });
    promises.push(promise);
});

Promise.all(promises).then(function() {
    console.log('saving profile...');
    warcraftProvider.saveProfile(profile)
        .then(function(res) {
            console.log('profile saved');
            mongoose.disconnect();
        })
        .catch(function(err) {
            console.err(err);
            mongoose.disconnect();
        });
});

function parseFeed(feed) {
    return new Promise(function(resolve, reject){
        var promises = [];
        feed.forEach(function(feedItem) {
            if (feedItem.type === "LOOT") {
                var promise = new Promise(function(resolve, reject) {
                    getItemData(feedItem.itemId).then(function(itemData) {
                        feedItem.item = itemData;
                        resolve();
                    });
                });
                promises.push(promise);
            }
        });

        Promise.all(promises).then(function() {
            console.log('finished parsing activity feed...')
            resolve(feed);
        });
    })
}

function getItemData(itemId, context) {
    return new Promise(function(resolve, reject) {
        var options = {
            uri: 'http://us.battle.net/api/wow/item/' + itemId,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        };
        if (context) {
            options.uri += '/' + context;
        }

        console.log('requesting item data for %s...', itemId);
        request(options, function(err, res, body) {
            if(err) {
                console.err(err);
                mongoose.disconnect();
                return;
            }
            if(!body) {
                console.err('request returned empty');
                mongoose.disconnect();
                return;
            }

            var data = JSON.parse(body);
            if(data) {
                if (!data.name) {
                    if (data.availableContexts) {
                        console.log('require a context for item %s...', data.id);
                        getItemData(itemId, data.availableContexts[0]).then(function(itemData) {
                            resolve(itemData);
                        });
                    } else {
                        console.warn('couldn\'t retrieve data for item %s...', itemId);
                        reject();
                    }
                } else {
                    console.log('received info for item %s...', data.name);
                    resolve(data);
                }
            } else {
                console.log('unexpected results...');
                mongoose.disconnect();
            }
        });
    })
}

function parseAchievementObject(supercats, character) {
    var FAKE_COMPLETION_TIME = new Date('2015-01-01').getTime();
    var IGNORE_ACHIEVEMENTS =
    {
        10050: true, // learn a primary prof
        10051: true  // learn two primary prof
    };

    var obj = {};
    var completed = {};
    var totalPossible = 0;
    var totalCompleted = 0;
    var totalFoS = 0;
    var totalLegacy = 0;
    var found = {};
    console.log('Parsing achievements.json...');

    // Build up lookup for achievements that character has completed
    character.achievements.achievementsCompleted.forEach(function(ach, index) {
        // hash the achievement and its timestamp
        completed[ach] = character.achievements.achievementsCompletedTimestamp[index];

        // FoS will no longer have timestamps, so we need to fake their timestamp
        if (!character.achievements.achievementsCompletedTimestamp[index])
        {
            completed[ach] = FAKE_COMPLETION_TIME;
        }
        found[ach] = false;
    });

    // Lets parse out all the super categories and build out our structure
    supercats.forEach(function(supercat) {
        var possibleCount = 0;
        var completedCount = 0;

        // remove the "." to fix parsing errors
        if (supercat.name === "Player vs. Player") {
            supercat.name = "Player vs Player";
        }

        // Add the supercategory to the object, so we can do quick lookups on category
        obj[supercat.name] = {};
        obj[supercat.name].categories = [];

        supercat.cats.forEach(function(cat) {
            var myCat = {'name': cat.name, 'zones': []};

            cat.zones.forEach(function(zone) {
                var myZone = {'name': zone.name, 'achievements': []};

                zone.achs.forEach(function(ach) {

                    // Mark this achievement in our found tracker
                    found[ach.id] = true;

                    var myAchievement = ach, added = false;
                    myAchievement.completed = completed[ach.id];
                    if (myAchievement.completed) {
                        myAchievement.rel = 'who=' + character.name + '&when=' + myAchievement.completed;
                    }

                    // Hack: until blizz fixes api, don't stamp with date
                    if (myAchievement.completed && myAchievement.completed !== FAKE_COMPLETION_TIME) {
                        myAchievement.rel = 'who=' + character.name + '&when=' + myAchievement.completed;
                    }

                    // Always add it if we've completed it, it should show up regardless if its avaiable
                    if (completed[ach.id]) {
                        added = true;
                        myZone.achievements.push(myAchievement);

                        // if this is feats of strength then I want to keep a seperate count for that
                        // since its not a percentage thing
                        if (supercat.name === 'Feats of Strength') {
                            totalFoS++;
                        } else if (supercat.name === 'Legacy') {
                            totalLegacy++;
                        }
                    }

                    // Update counts proper
                    if (supercat.name !== 'Feats of Strength' && supercat.name !== 'Legacy' && ach.obtainable &&
                        (ach.side === '' || ach.side === character.faction)){
                        possibleCount++;
                        totalPossible++;

                        if (completed[ach.id]) {
                            completedCount++;
                            totalCompleted++;
                        }

                        // if we haven't already added it, then this is one that should show up in the page of achievements
                        // so add it
                        if (!added) {
                            myZone.achievements.push(myAchievement);
                        }
                    }
                });

                if (myZone.achievements.length > 0) {
                    myCat.zones.push(myZone);
                }
            });

            // Add the category to the obj
            obj[supercat.name].categories.push(myCat);
        });

        obj[supercat.name].possible = possibleCount;
        obj[supercat.name].completed = completedCount;

        // Add the FoS count if this is the FoS
        if (supercat.name === 'Feats of Strength') {
            obj[supercat.name].fosTotal = totalFoS;
        } else if (supercat.name === 'Legacy') {
            obj[supercat.name].legacyTotal = totalLegacy;
        }
    });

    for (var achId in found) {
        if (found.hasOwnProperty(achId) && !found[achId] && !IGNORE_ACHIEVEMENTS[achId]) {
            console.log('WARN: Found achievement "' + achId + '" from character but not in db.');
        }
    }

    // Add totals
    obj.possible = totalPossible;
    obj.completed = totalCompleted;

    // Data object we expose externally
    return obj;
}<commit_msg>add missing semicolons<commit_after>var app                 = require('../config/app');
var db                  = require('../config/db');
var mongoose            = require('mongoose');
var request             = require('request');
var Promise             = require('promise');
var achievements        = require('./wow-achievements.json');
var WarcraftProvider    = require('../app/modules/gaming/warcraft/warcraft-provider');
var warcraftProvider    = new WarcraftProvider();

console.log('connecting to db...');
mongoose.connect(db.url);

var profile = {
    "characters": []
};
var promises = [];
app.wow.characters.forEach(function(character) {
    var promise = new Promise(function(resolve) {
        var options = {
            uri: 'http://us.battle.net/api/wow/character/' + character.realm + '/' + character.name,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        };
        if(character.showcase) {
            // just ensure we only pull these once, all characters return account-wide pets and mounts
            options.uri +=  '?fields=feed,items,stats,pvp,progression,pets,mounts,achievements';
        }

        console.log('requesting character data for %s...', character.name);
        request(options, function(err, res, body) {
            if(err) {
                console.err(err);
                mongoose.disconnect();
                return;
            }
            if(!body) {
                console.err('request returned empty');
                mongoose.disconnect();
                return;
            }

            var data = JSON.parse(body);
            if(data) {
                console.log('received info for character: ' + data.name);
                if(character.showcase) {
                    profile.achievementPoints = data.achievementPoints;
                    profile.pets = data.pets;
                    profile.mounts = data.mounts;
                    profile.achievements = parseAchievementObject(achievements.supercats, data);
                    data.showcase = true;
                    parseFeed(data.feed).then(function(feed) {
                        data.feed = feed;
                        profile.characters.push(data);
                        resolve(data);
                    });
                } else {
                    profile.characters.push(data);
                    resolve(data);
                }
            } else {
                console.log('unexpected results...');
                mongoose.disconnect();
            }
        }).on('error', function(e) {
            console.error(e.message);
            mongoose.disconnect();
        });
    });
    promises.push(promise);
});

Promise.all(promises).then(function() {
    console.log('saving profile...');
    warcraftProvider.saveProfile(profile)
        .then(function(res) {
            console.log('profile saved');
            mongoose.disconnect();
        })
        .catch(function(err) {
            console.err(err);
            mongoose.disconnect();
        });
});

function parseFeed(feed) {
    return new Promise(function(resolve, reject){
        var promises = [];
        feed.forEach(function(feedItem) {
            if (feedItem.type === "LOOT") {
                var promise = new Promise(function(resolve, reject) {
                    getItemData(feedItem.itemId).then(function(itemData) {
                        feedItem.item = itemData;
                        resolve();
                    });
                });
                promises.push(promise);
            }
        });

        Promise.all(promises).then(function() {
            console.log('finished parsing activity feed...');
            resolve(feed);
        });
    });
}

function getItemData(itemId, context) {
    return new Promise(function(resolve, reject) {
        var options = {
            uri: 'http://us.battle.net/api/wow/item/' + itemId,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        };
        if (context) {
            options.uri += '/' + context;
        }

        console.log('requesting item data for %s...', itemId);
        request(options, function(err, res, body) {
            if(err) {
                console.err(err);
                mongoose.disconnect();
                return;
            }
            if(!body) {
                console.err('request returned empty');
                mongoose.disconnect();
                return;
            }

            var data = JSON.parse(body);
            if(data) {
                if (!data.name) {
                    if (data.availableContexts) {
                        console.log('require a context for item %s...', data.id);
                        getItemData(itemId, data.availableContexts[0]).then(function(itemData) {
                            resolve(itemData);
                        });
                    } else {
                        console.warn('couldn\'t retrieve data for item %s...', itemId);
                        reject();
                    }
                } else {
                    console.log('received info for item %s...', data.name);
                    resolve(data);
                }
            } else {
                console.log('unexpected results...');
                mongoose.disconnect();
            }
        });
    });
}

function parseAchievementObject(supercats, character) {
    var FAKE_COMPLETION_TIME = new Date('2015-01-01').getTime();
    var IGNORE_ACHIEVEMENTS =
    {
        10050: true, // learn a primary prof
        10051: true  // learn two primary prof
    };

    var obj = {};
    var completed = {};
    var totalPossible = 0;
    var totalCompleted = 0;
    var totalFoS = 0;
    var totalLegacy = 0;
    var found = {};
    console.log('Parsing achievements.json...');

    // Build up lookup for achievements that character has completed
    character.achievements.achievementsCompleted.forEach(function(ach, index) {
        // hash the achievement and its timestamp
        completed[ach] = character.achievements.achievementsCompletedTimestamp[index];

        // FoS will no longer have timestamps, so we need to fake their timestamp
        if (!character.achievements.achievementsCompletedTimestamp[index])
        {
            completed[ach] = FAKE_COMPLETION_TIME;
        }
        found[ach] = false;
    });

    // Lets parse out all the super categories and build out our structure
    supercats.forEach(function(supercat) {
        var possibleCount = 0;
        var completedCount = 0;

        // remove the "." to fix parsing errors
        if (supercat.name === "Player vs. Player") {
            supercat.name = "Player vs Player";
        }

        // Add the supercategory to the object, so we can do quick lookups on category
        obj[supercat.name] = {};
        obj[supercat.name].categories = [];

        supercat.cats.forEach(function(cat) {
            var myCat = {'name': cat.name, 'zones': []};

            cat.zones.forEach(function(zone) {
                var myZone = {'name': zone.name, 'achievements': []};

                zone.achs.forEach(function(ach) {

                    // Mark this achievement in our found tracker
                    found[ach.id] = true;

                    var myAchievement = ach, added = false;
                    myAchievement.completed = completed[ach.id];
                    if (myAchievement.completed) {
                        myAchievement.rel = 'who=' + character.name + '&when=' + myAchievement.completed;
                    }

                    // Hack: until blizz fixes api, don't stamp with date
                    if (myAchievement.completed && myAchievement.completed !== FAKE_COMPLETION_TIME) {
                        myAchievement.rel = 'who=' + character.name + '&when=' + myAchievement.completed;
                    }

                    // Always add it if we've completed it, it should show up regardless if its avaiable
                    if (completed[ach.id]) {
                        added = true;
                        myZone.achievements.push(myAchievement);

                        // if this is feats of strength then I want to keep a seperate count for that
                        // since its not a percentage thing
                        if (supercat.name === 'Feats of Strength') {
                            totalFoS++;
                        } else if (supercat.name === 'Legacy') {
                            totalLegacy++;
                        }
                    }

                    // Update counts proper
                    if (supercat.name !== 'Feats of Strength' && supercat.name !== 'Legacy' && ach.obtainable &&
                        (ach.side === '' || ach.side === character.faction)){
                        possibleCount++;
                        totalPossible++;

                        if (completed[ach.id]) {
                            completedCount++;
                            totalCompleted++;
                        }

                        // if we haven't already added it, then this is one that should show up in the page of achievements
                        // so add it
                        if (!added) {
                            myZone.achievements.push(myAchievement);
                        }
                    }
                });

                if (myZone.achievements.length > 0) {
                    myCat.zones.push(myZone);
                }
            });

            // Add the category to the obj
            obj[supercat.name].categories.push(myCat);
        });

        obj[supercat.name].possible = possibleCount;
        obj[supercat.name].completed = completedCount;

        // Add the FoS count if this is the FoS
        if (supercat.name === 'Feats of Strength') {
            obj[supercat.name].fosTotal = totalFoS;
        } else if (supercat.name === 'Legacy') {
            obj[supercat.name].legacyTotal = totalLegacy;
        }
    });

    for (var achId in found) {
        if (found.hasOwnProperty(achId) && !found[achId] && !IGNORE_ACHIEVEMENTS[achId]) {
            console.log('WARN: Found achievement "' + achId + '" from character but not in db.');
        }
    }

    // Add totals
    obj.possible = totalPossible;
    obj.completed = totalCompleted;

    // Data object we expose externally
    return obj;
}<|endoftext|>