<commit_before>var parseContext = require('./context');
var nameRegex = /[a-zA-Z_][\w:-]*/;

function readAttribute(context) {
	var name = context.readRegex(nameRegex);
	var value = null;
	if (context.current === '=' || context.peekIgnoreWhitespace() === '=') {
		context.readRegex(/\s*=\s*/);
		var quote = /['"]/.test(context.current) ? context.current : '';
		var attributeValueRegex = !quote
			? /(.*?)(?=[\s>])/
			: new RegExp(quote + '(.*?)' + quote);

		var match = attributeValueRegex.exec(context.substring) || [0, ''];
		value = match[1];
		context.read(match[0].length);
	}

	context.callbacks.attribute(name, value);
}

function readAttributes(context, isXml) {
	function isClosingToken() {
		if (isXml) {
			return context.current === '?' && context.peek() === '>';
		}

		return context.current === '>' || (context.current === '/' && context.peekIgnoreWhitespace() === '>');
	}
	var next = context.current;
	while (!context.isEof() && !isClosingToken()) {
		if (nameRegex.test(next)) {
			readAttribute(context);
			next = context.current;
		}
		else {
			next = context.read();
		}
	}
}

function readCloserForOpenedElement(context, name) {
	if (context.current === '/') {
		//self closing tag "/>"
		context.readUntilNonWhitespace();
		context.read();
		context.callbacks.closeOpenedElement(name, '/>');
	}
	else if (context.current === '?') {
		//xml closing "?>"
		context.read(2);
		context.callbacks.closeOpenedElement(name, '?>');
	}
	else {
		//normal closing ">"
		context.read();
		context.callbacks.closeOpenedElement(name, '>');
	}
}

function parseOpenElement(context) {
	var name = context.readRegex(nameRegex);
	context.callbacks.openElement(name);
	readAttributes(context, false);
	readCloserForOpenedElement(context, name);

	if (!/^(script|xmp)$/i.test(name)) {
		return;
	}

	//just read until the closing tags for elements that allow cdata
	var regex = new RegExp('^([\\s\\S]*?)(?:$|</(' + name + ')\\s*>)', 'i');
	var match = regex.exec(context.substring);
	context.read(match[0].length);
	if (match[1]) {
		context.callbacks.cdata(match[1]);
	}
	if (match[2]) {
		context.callbacks.closeElement(match[2]);
	}
}

function parseEndElement(context) {
	var name = context.readRegex(nameRegex);
	context.callbacks.closeElement(name);
	context.readRegex(/.*?(?:>|$)/);
}

function parseCData(context) {
	//read "![CDATA["
	context.read(8);

	var match = /^([\s\S]*?)(?:$|]]>)/.exec(context.substring);
	var value = match[1];
	context.read(match[0].length);
	context.callbacks.cdata(value);
}

function parseComment(context) {
	//read "!--"
	context.read(3);

	var match = /^([\s\S]*?)(?:$|-->)/.exec(context.substring);
	var value = match[1];
	context.read(match[0].length);
	context.callbacks.comment(value);
}

function parseDocType(context) {
	//read "!doctype"
	context.read(8);

	var match = /^\s*([\s\S]*?)(?:$|>)/.exec(context.substring);
	var value = match[1];
	context.read(match[0].length);
	context.callbacks.docType(value);
}

function parseXmlProlog(context) {
	//read "?xml"
	context.read(4);
	context.callbacks.xmlProlog();
	readAttributes(context, true);
	readCloserForOpenedElement(context, '?xml');
}

function appendText(value, context) {
	context.text += value;
}

function callbackText(context) {
	if (context.text) {
		context.callbacks.text(context.text);
		context.text = '';
	}
}

function parseNext(context) {
	var current = context.current, buffer = current;
	if (current == '<') {
		buffer += context.read();
		if (context.current === '/') {
			buffer += context.read();
			if (nameRegex.test(context.current)) {
				callbackText(context);
				parseEndElement(context);
			} else {
				//malformed html
				context.read();
				appendText(buffer, context);
			}
		} else if (context.current === '!') {
			if (/^!\[CDATA\[/.test(context.substring)) {
				callbackText(context);
				parseCData(context);
			} else if (/^!--/.test(context.substring)) {
				callbackText(context);
				parseComment(context);
			} else if (/^!doctype/i.test(context.substring)) {
				callbackText(context);
				parseDocType(context);
			} else {
				//malformed html
				context.read();
				appendText(buffer, context);
			}
		} else if (context.current === '?') {
			if (/^\?xml/.test(context.substring)) {
				callbackText(context);
				parseXmlProlog(context);
			} else {
				//malformed xml prolog
				context.read();
				appendText(buffer, context);
			}
		} else if (nameRegex.test(context.current)) {
			callbackText(context);
			parseOpenElement(context);
		} else {
			//malformed html
			context.read();
			appendText(buffer, context);
		}
	} else {
		appendText(context.current, context);
		context.read();
	}
}

/**
 * Parses the given string o' HTML, executing each callback when it
 * encounters a token.
 *
 * @param {String} htmlString A string o' HTML
 * @param {Object} [callbacks] Callbacks for each token
 * @param {Function} [callbacks.attribute] Takes the name of the attribute and its value
 * @param {Function} [callbacks.openElement] Takes the tag name of the element
 * @param {Function} [callbacks.closeOpenedElement] Takes the tag name of the element and the token used to
 * close it (">", "/>", "?>")
 * @param {Function} [callbacks.closeElement] Takes the name of the element
 * @param {Function} [callbacks.comment] Takes the content of the comment
 * @param {Function} [callbacks.docType] Takes the content of the document type declaration
 * @param {Function} [callbacks.cdata] Takes the content of the CDATA
 * @param {Function} [callbacks.xmlProlog] Takes no arguments
 * @param {Function} [callbacks.text] Takes the value of the text node
 */
exports.parse = function(htmlString, callbacks) {
	htmlString = htmlString.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
	var context = parseContext.create(htmlString, callbacks);
	do {
		parseNext(context);
	} while (!context.isEof());

	callbackText(context);
};

/**
 * Parses the HTML contained in the given file asynchronously.
 *
 * Note that this is merely a convenience function, it will still read the entire
 * contents of the file into memory.
 *
 * @param {String} fileName Name of the file to parse
 * @param {String} [encoding] Optional encoding to read the file in, defaults to utf8
 * @param {Object} [callbacks] Callbacks to pass to parse()
 * @param {Function} [callback]
 */
exports.parseFile = function(fileName, encoding, callbacks, callback) {
	var fs = require('fs');
	fs.readFile(fileName, encoding || 'utf8', function(err, contents) {
		if (err) {
			callback && callback(err);
			return;
		}

		exports.parse(contents, callbacks);
		callback && callback();
	});
};

/**
 * Sanitizes an HTML string.
 *
 * If removalCallbacks is not given, it will simply reformat the HTML
 * (i.e. converting all tags to lowercase, etc.). Note that this function
 * assumes that the HTML is decently formatted and kind of valid. It
 * may exhibit undefined or unexpected behavior if your HTML is trash.
 *
 * @param {String} htmlString A string o' HTML
 * @param {Object} [removalCallbacks] Callbacks for each token type
 * @param {Function|Array} [removalCallbacks.attributes] Callback or array of specific attributes to strip
 * @param {Function|Array} [removalCallbacks.elements] Callback or array of specific elements to strip
 * @param {Function|Boolean} [removalCallbacks.comments] Callback or boolean indicating to strip comments
 * @param {Function|Boolean} [removalCallbacks.docTypes] Callback or boolean indicating to strip doc type declarations
 * @return {String} The sanitized HTML
 */
exports.sanitize = function(htmlString, removalCallbacks) {
	removalCallbacks = removalCallbacks || {};

	function createArrayCallback(index) {
		var callbackOrArray = removalCallbacks[index] || [];
		if (typeof(callbackOrArray) === 'function') {
			return function() {
				return callbackOrArray.apply(null, arguments);
			}
		} else {
			return function(value) {
				return callbackOrArray.indexOf(value) !== -1;
			}
		}
	}

	function createBoolCallback(index) {
		var callbackOrBool = removalCallbacks[index] || false;
		if (typeof(callbackOrBool) === 'function') {
			return function() {
				return callbackOrBool.apply(null, arguments);
			}
		} else {
			return function() {
				return callbackOrBool;
			}
		}
	}

	function last(arr) {
		return arr[arr.length - 1];
	}

	var toRemove = {
		attributes: createArrayCallback('attributes'),
		elements: createArrayCallback('elements'),
		comments: createBoolCallback('comments'),
		docTypes: createBoolCallback('docTypes')
	};

	var sanitized = '', tagStack = [];
	var ignoreStack = [];
	var selfClosingTags = {
		meta: 1,
		br: 1,
		link: 1,
		area: 1,
		base :1,
		col: 1,
		command: 1,
		embed: 1,
		hr: 1,
		img: 1,
		input: 1,
		param: 1,
		source: 1
	};
	var callbacks = {
		docType: function(value) {
			if (toRemove.docTypes(value)) {
				return;
			}
			sanitized += '<!doctype ' + value + '>';
		},

		openElement: function(name) {
			name = name.toLowerCase();
			//if there is an unclosed self-closing tag in the stack, then
			//pop it off (assumed to be malformed html).
			if (tagStack.length) {
				var scope = last(tagStack);
				if (selfClosingTags[scope]) {
					tagStack.pop();
					if (scope === last(ignoreStack)) {
						ignoreStack.pop();
					}
				}
			}

			if (ignoreStack.length) {
				return;
			}

			tagStack.push(name);
			if (toRemove.elements(name)) {
				ignoreStack.push(name);
				return;
			}
			sanitized += '<' + name;
		},

		closeOpenedElement: function(name, token) {
			name = name.toLowerCase();
			if (token.length === 2) {
				//self closing
				var scope = tagStack.pop();
				if (scope === last(ignoreStack)) {
					ignoreStack.pop();
				}
			}
			if (ignoreStack.length || toRemove.elements(name)) {
				return;
			}
			sanitized += token;
		},

		closeElement: function(name) {
			name = name.toLowerCase();
			if (tagStack.length && last(tagStack) === name) {
				if (tagStack.pop() === last(ignoreStack)) {
					ignoreStack.pop();
				}
			}
			if (ignoreStack.length || toRemove.elements(name)) {
				return;
			}
			sanitized += '</' + name + '>';
		},

		attribute: function(name, value) {
			if (ignoreStack.length) {
				return;
			}

			name = name.toLowerCase();
			if (toRemove.attributes(name)) {
				return;
			}

			sanitized += ' ' + name;
			if (value) {
				sanitized += '="' + value.replace(/"/g, '"') + '"';
			}
		},

		text: function(value) {
			if (ignoreStack.length) {
				return;
			}
			sanitized += value;
		},

		comment: function(value) {
			if (ignoreStack.length || toRemove.comments(value)) {
				return;
			}
			sanitized += '<!--' + value + '-->';
		},

		cdata: function(value) {
			if (ignoreStack.length) {
				return;
			}

			for (var i = tagStack.length - 1; i >= 0; i--) {
				if (tagStack[i] === 'script' || tagStack[i] === 'xmp') {
					sanitized += value;
					return;
				}
			}

			sanitized += '<![CDATA[' + value + ']]>';
		}
	};

	exports.parse(htmlString, callbacks);
	return sanitized;
};<commit_msg>fixed #1 issue<commit_after>var parseContext = require('./context');
var nameRegex = /[a-zA-Z_][\w:-]*/;

function readAttribute(context) {
	var name = context.readRegex(nameRegex);
	var value = null;
	if (context.current === '=' || context.peekIgnoreWhitespace() === '=') {
		context.readRegex(/\s*=\s*/);
		var quote = /['"]/.test(context.current) ? context.current : '';
		var attributeValueRegex = !quote
			? /(.*?)(?=[\s>])/
			: new RegExp(quote + '(.*?)' + quote);

		var match = attributeValueRegex.exec(context.substring) || [0, ''];
		value = match[1];
		context.read(match[0].length);
	}

	context.callbacks.attribute(name, value);
}

function readAttributes(context, isXml) {
	function isClosingToken() {
		if (isXml) {
			return context.current === '?' && context.peek() === '>';
		}

		return context.current === '>' || (context.current === '/' && context.peekIgnoreWhitespace() === '>');
	}
	var next = context.current;
	while (!context.isEof() && !isClosingToken()) {
		if (nameRegex.test(next)) {
			readAttribute(context);
			next = context.current;
		}
		else {
			next = context.read();
		}
	}
}

function readCloserForOpenedElement(context, name) {
	if (context.current === '/') {
		//self closing tag "/>"
		context.readUntilNonWhitespace();
		context.read();
		context.callbacks.closeOpenedElement(name, '/>');
	}
	else if (context.current === '?') {
		//xml closing "?>"
		context.read(2);
		context.callbacks.closeOpenedElement(name, '?>');
	}
	else {
		//normal closing ">"
		context.read();
		context.callbacks.closeOpenedElement(name, '>');
	}
}

function parseOpenElement(context) {
	var name = context.readRegex(nameRegex);
	context.callbacks.openElement(name);
	readAttributes(context, false);
	readCloserForOpenedElement(context, name);

	if (!/^(script|xmp)$/i.test(name)) {
		return;
	}

	//just read until the closing tags for elements that allow cdata
	var regex = new RegExp('^([\\s\\S]*?)(?:$|</(' + name + ')\\s*>)', 'i');
	var match = regex.exec(context.substring);
	context.read(match[0].length);
	if (match[1]) {
		context.callbacks.cdata(match[1]);
	}
	if (match[2]) {
		context.callbacks.closeElement(match[2]);
	}
}

function parseEndElement(context) {
	var name = context.readRegex(nameRegex);
	context.callbacks.closeElement(name);
	context.readRegex(/.*?(?:>|$)/);
}

function parseCData(context) {
	//read "![CDATA["
	context.read(8);

	var match = /^([\s\S]*?)(?:$|]]>)/.exec(context.substring);
	var value = match[1];
	context.read(match[0].length);
	context.callbacks.cdata(value);
}

function parseComment(context) {
	//read "!--"
	context.read(3);

	var match = /^([\s\S]*?)(?:$|-->)/.exec(context.substring);
	var value = match[1];
	context.read(match[0].length);
	context.callbacks.comment(value);
}

function parseDocType(context) {
	//read "!doctype"
	context.read(8);

	var match = /^\s*([\s\S]*?)(?:$|>)/.exec(context.substring);
	var value = match[1];
	context.read(match[0].length);
	context.callbacks.docType(value);
}

function parseXmlProlog(context) {
	//read "?xml"
	context.read(4);
	context.callbacks.xmlProlog();
	readAttributes(context, true);
	readCloserForOpenedElement(context, '?xml');
}

function appendText(value, context) {
	context.text += value;
}

function callbackText(context) {
	if (context.text) {
		context.callbacks.text(context.text);
		context.text = '';
	}
}

function parseNext(context) {
	var current = context.current, buffer = current;
	if (current == '<') {
		buffer += context.read();
		if (context.current === '/') {
			buffer += context.read();
			if (nameRegex.test(context.current)) {
				callbackText(context);
				parseEndElement(context);
			} else {
				//malformed html
				context.read();
				appendText(buffer, context);
			}
		} else if (context.current === '!') {
			if (/^!\[CDATA\[/.test(context.substring)) {
				callbackText(context);
				parseCData(context);
			} else if (/^!--/.test(context.substring)) {
				callbackText(context);
				parseComment(context);
			} else if (/^!doctype/i.test(context.substring)) {
				callbackText(context);
				parseDocType(context);
			} else {
				//malformed html
				context.read();
				appendText(buffer, context);
			}
		} else if (context.current === '?') {
			if (/^\?xml/.test(context.substring)) {
				callbackText(context);
				parseXmlProlog(context);
			} else {
				//malformed xml prolog
				context.read();
				appendText(buffer, context);
			}
		} else if (nameRegex.test(context.current)) {
			callbackText(context);
			parseOpenElement(context);
		} else {
			//malformed html
			context.read();
			appendText(buffer, context);
		}
	} else {
		appendText(context.current, context);
		context.read();
	}
}

/**
 * Parses the given string o' HTML, executing each callback when it
 * encounters a token.
 *
 * @param {String} htmlString A string o' HTML
 * @param {Object} [callbacks] Callbacks for each token
 * @param {Function} [callbacks.attribute] Takes the name of the attribute and its value
 * @param {Function} [callbacks.openElement] Takes the tag name of the element
 * @param {Function} [callbacks.closeOpenedElement] Takes the tag name of the element and the token used to
 * close it (">", "/>", "?>")
 * @param {Function} [callbacks.closeElement] Takes the name of the element
 * @param {Function} [callbacks.comment] Takes the content of the comment
 * @param {Function} [callbacks.docType] Takes the content of the document type declaration
 * @param {Function} [callbacks.cdata] Takes the content of the CDATA
 * @param {Function} [callbacks.xmlProlog] Takes no arguments
 * @param {Function} [callbacks.text] Takes the value of the text node
 */
exports.parse = function(htmlString, callbacks) {
	htmlString = htmlString.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
	var context = parseContext.create(htmlString, callbacks);
	do {
		parseNext(context);
	} while (!context.isEof());

	callbackText(context);
};

/**
 * Parses the HTML contained in the given file asynchronously.
 *
 * Note that this is merely a convenience function, it will still read the entire
 * contents of the file into memory.
 *
 * @param {String} fileName Name of the file to parse
 * @param {String} [encoding] Optional encoding to read the file in, defaults to utf8
 * @param {Object} [callbacks] Callbacks to pass to parse()
 * @param {Function} [callback]
 */
exports.parseFile = function(fileName, encoding, callbacks, callback) {
	var fs = require('fs');
	fs.readFile(fileName, encoding || 'utf8', function(err, contents) {
		if (err) {
			callback && callback(err);
			return;
		}

		exports.parse(contents, callbacks);
		callback && callback();
	});
};

/**
 * Sanitizes an HTML string.
 *
 * If removalCallbacks is not given, it will simply reformat the HTML
 * (i.e. converting all tags to lowercase, etc.). Note that this function
 * assumes that the HTML is decently formatted and kind of valid. It
 * may exhibit undefined or unexpected behavior if your HTML is trash.
 *
 * @param {String} htmlString A string o' HTML
 * @param {Object} [removalCallbacks] Callbacks for each token type
 * @param {Function|Array} [removalCallbacks.attributes] Callback or array of specific attributes to strip
 * @param {Function|Array} [removalCallbacks.elements] Callback or array of specific elements to strip
 * @param {Function|Boolean} [removalCallbacks.comments] Callback or boolean indicating to strip comments
 * @param {Function|Boolean} [removalCallbacks.docTypes] Callback or boolean indicating to strip doc type declarations
 * @return {String} The sanitized HTML
 */
exports.sanitize = function(htmlString, removalCallbacks) {
	removalCallbacks = removalCallbacks || {};

	function createArrayCallback(index) {
		var callbackOrArray = removalCallbacks[index] || [];
		if (typeof(callbackOrArray) === 'function') {
			return function() {
				return callbackOrArray.apply(null, arguments);
			}
		} else {
			return function(value) {
				return callbackOrArray.indexOf(value) !== -1;
			}
		}
	}

	function createBoolCallback(index) {
		var callbackOrBool = removalCallbacks[index] || false;
		if (typeof(callbackOrBool) === 'function') {
			return function() {
				return callbackOrBool.apply(null, arguments);
			}
		} else {
			return function() {
				return callbackOrBool;
			}
		}
	}

	function last(arr) {
		return arr[arr.length - 1];
	}

	var toRemove = {
		attributes: createArrayCallback('attributes'),
		elements: createArrayCallback('elements'),
		comments: createBoolCallback('comments'),
		docTypes: createBoolCallback('docTypes')
	};

	var sanitized = '', tagStack = [];
	var ignoreStack = [];
	var selfClosingTags = {
		meta: 1,
		br: 1,
		link: 1,
		area: 1,
		base :1,
		col: 1,
		command: 1,
		embed: 1,
		hr: 1,
		img: 1,
		input: 1,
		param: 1,
		source: 1
	};
	var callbacks = {
		docType: function(value) {
			if (toRemove.docTypes(value)) {
				return;
			}
			sanitized += '<!doctype ' + value + '>';
		},

		openElement: function(name) {
			name = name.toLowerCase();
			//if there is an unclosed self-closing tag in the stack, then
			//pop it off (assumed to be malformed html).
			if (tagStack.length) {
				var scope = last(tagStack);
				if (selfClosingTags[scope]) {
					tagStack.pop();
					if (scope === last(ignoreStack)) {
						ignoreStack.pop();
					}
				}
			}

			if (ignoreStack.length) {
				return;
			}

			tagStack.push(name);
			if (toRemove.elements(name)) {
				ignoreStack.push(name);
				return;
			}
			sanitized += '<' + name;
		},

		closeOpenedElement: function(name, token) {
			name = name.toLowerCase();
			if (token.length === 2) {
				//self closing
				var scope = tagStack.pop();
				if (scope === last(ignoreStack)) {
					ignoreStack.pop();
				}
			}
			if (ignoreStack.length || toRemove.elements(name)) {
				return;
			}
			sanitized += token;
		},

		closeElement: function(name) {
			name = name.toLowerCase();
			if (tagStack.length && last(tagStack) === name) {
				if (tagStack.pop() === last(ignoreStack)) {
					ignoreStack.pop();
				}
			}
			if (ignoreStack.length || toRemove.elements(name)) {
				return;
			}
			sanitized += '</' + name + '>';
		},

		attribute: function(name, value) {
			if (ignoreStack.length) {
				return;
			}

			name = name.toLowerCase();
			if (toRemove.attributes(name,value)) {
				return;
			}

			sanitized += ' ' + name;
			if (value) {
				sanitized += '="' + value.replace(/"/g, '"') + '"';
			}
		},

		text: function(value) {
			if (ignoreStack.length) {
				return;
			}
			sanitized += value;
		},

		comment: function(value) {
			if (ignoreStack.length || toRemove.comments(value)) {
				return;
			}
			sanitized += '<!--' + value + '-->';
		},

		cdata: function(value) {
			if (ignoreStack.length) {
				return;
			}

			for (var i = tagStack.length - 1; i >= 0; i--) {
				if (tagStack[i] === 'script' || tagStack[i] === 'xmp') {
					sanitized += value;
					return;
				}
			}

			sanitized += '<![CDATA[' + value + ']]>';
		}
	};

	exports.parse(htmlString, callbacks);
	return sanitized;
};
<|endoftext|>


<commit_before>import { toJS } from 'mobx';
import _ from 'lodash';

const fieldPropsToPick = [
  'path',
  'bindings',
  'default',
  'initial',
  'value',
  'label',
  'placeholder',
  'validating',
  'disabled',
  'focus',
  'touched',
  'changed',
  'hasError',
  'isValid',
  'isEmpty',
  'isDefault',
  'isPristine',
  'isDirty',
  'error',
  'related',
  'rules',
  'validators',
];

const parseFormData = form =>
  toJS(_.pick(form, [
    'validating',
    'hasError',
    'isValid',
    'isDirty',
    'isPristine',
    'isDefault',
    'isEmpty',
    'focus',
    'touched',
    'changed',
  ]));

const parseFieldsData = fields =>
  _.reduce(fields.values(), (obj, field) => {
    const $nested = $fields => ($fields.size !== 0)
      ? parseFieldsData($fields)
      : undefined;

    const data = toJS(_.pick(field, fieldPropsToPick));

    Object.assign(obj, {
      [field.key]: Object.assign(data, {
        fields: $nested(field.fields),
      }),
    });

    return obj;
  }, {});

export default {
  parseFormData,
  parseFieldsData,
};
<commit_msg>feat: Introduced submitting<commit_after>import { toJS } from 'mobx';
import _ from 'lodash';

const fieldPropsToPick = [
  'path',
  'bindings',
  'default',
  'initial',
  'value',
  'label',
  'placeholder',
  'submitting',
  'validating',
  'disabled',
  'focus',
  'touched',
  'changed',
  'hasError',
  'isValid',
  'isEmpty',
  'isDefault',
  'isPristine',
  'isDirty',
  'error',
  'related',
  'rules',
  'validators',
];

const parseFormData = form =>
  toJS(_.pick(form, [
    'submitting',
    'validating',
    'hasError',
    'isValid',
    'isDirty',
    'isPristine',
    'isDefault',
    'isEmpty',
    'focus',
    'touched',
    'changed',
  ]));

const parseFieldsData = fields =>
  _.reduce(fields.values(), (obj, field) => {
    const $nested = $fields => ($fields.size !== 0)
      ? parseFieldsData($fields)
      : undefined;

    const data = toJS(_.pick(field, fieldPropsToPick));

    Object.assign(obj, {
      [field.key]: Object.assign(data, {
        fields: $nested(field.fields),
      }),
    });

    return obj;
  }, {});

export default {
  parseFormData,
  parseFieldsData,
};
<|endoftext|>


<commit_before>/* eslint no-console: 0 */
import _ from 'lodash';
import fs from 'fs';
import util from 'util';

const defaults = {
    debug: false, // verbose logging

    sort: false, // sort keys in alphabetical order

    attr: { // HTML attributes to parse
        list: ['data-i18n'],
        extensions: ['.html', '.htm']
    },

    func: { // function names to parse
        list: ['i18next.t', 'i18n.t'],
        extensions: ['.js', '.jsx']
    },

    lngs: ['en'], // array of supported languages
    fallbackLng: 'en', // language to lookup key if not found while calling `parser.get(key, { lng: '' })`

    ns: [], // string or array of namespaces

    defaultNs: 'translation', // default namespace used if not passed to translation function

    defaultValue: '', // default value used if not passed to `parser.set`

    // resource
    resource: {
        loadPath: 'i18n/{{lng}}/{{ns}}.json', // the source resource path (relative to current working directory)
        savePath: 'i18n/{{lng}}/{{ns}}.json', // the target resource path (relative to the path specified with `gulp.dest(path)`)
        jsonIndent: 2
    },

    keySeparator: '.', // char to separate keys
    nsSeparator: ':', // char to split namespace from key

    // interpolation options
    interpolation: {
        prefix: '{{', // prefix for interpolation
        suffix: '}}' // suffix for interpolation
    }
};

const transformOptions = (options) => {
    // Attribute
    if (_.isUndefined(_.get(options, 'attr.list'))) {
        _.set(options, 'attr.list', defaults.attr.list);
    }
    if (_.isUndefined(_.get(options, 'attr.extensions'))) {
        _.set(options, 'attr.extensions', defaults.attr.extensions);
    }

    // Function
    if (_.isUndefined(_.get(options, 'func.list'))) {
        _.set(options, 'func.list', defaults.func.list);
    }

    // Resource
    if (_.isUndefined(_.get(options, 'func.extensions'))) {
        _.set(options, 'func.extensions', defaults.func.extensions);
    }
    if (_.isUndefined(_.get(options, 'resource.loadPath'))) {
        _.set(options, 'resource.loadPath', defaults.resource.loadPath);
    }
    if (_.isUndefined(_.get(options, 'resource.savePath'))) {
        _.set(options, 'resource.savePath', defaults.resource.savePath);
    }
    if (_.isUndefined(_.get(options, 'resource.jsonIndent'))) {
        _.set(options, 'resource.jsonIndent', defaults.resource.jsonIndent);
    }

    // Accept both nsseparator or nsSeparator
    if (!_.isUndefined(options.nsseparator)) {
        options.nsSeparator = options.nsseparator;
        delete options.nsseparator;
    }
    // Allowed only string or false
    if (!_.isString(options.nsSeparator)) {
        options.nsSeparator = false;
    }

    // Accept both keyseparator or keySeparator
    if (!_.isUndefined(options.keyseparator)) {
        options.keySeparator = options.keyseparator;
        delete options.keyseparator;
    }
    // Allowed only string or false
    if (!_.isString(options.keySeparator)) {
        options.keySeparator = false;
    }

    if (!_.isArray(options.ns)) {
        options.ns = [options.ns];
    }

    options.ns = _(options.ns.concat(options.defaultNs))
        .flatten()
        .union()
        .value();

    return options;
};

/**
* Creates a new parser
* @constructor
*/
class Parser {
    options = Object.assign({}, defaults);
    resStore = {};

    constructor(options) {
        this.options = transformOptions(_.extend({}, this.options, options));

        const lngs = this.options.lngs;
        const namespaces = this.options.ns;

        lngs.forEach((lng) => {
            this.resStore[lng] = this.resStore[lng] || {};
            namespaces.forEach((ns) => {
                const resPath = this.formatResourceLoadPath(lng, ns);
                this.resStore[lng][ns] = {};
                try {
                    const stat = fs.statSync(resPath);
                    if (stat.isFile()) {
                        this.resStore[lng][ns] = JSON.parse(fs.readFileSync(resPath, 'utf-8'));
                    }
                } catch (err) {
                    this.debuglog('Unable to load ' + JSON.stringify(resPath));
                }
            });
        });

        this.debuglog('[i18next-scanner] Parser(options): ' + JSON.stringify(this.options));
    }
    debuglog(...args) {
        const { debug } = this.options;
        if (!!debug) {
            console.log.apply(this, args);
        }
    }
    formatResourceLoadPath(lng, ns) {
        const options = this.options;
        const regex = {
            lng: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'lng' + options.interpolation.suffix), 'g'),
            ns: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'ns' + options.interpolation.suffix), 'g')
        };
        return options.resource.loadPath
            .replace(regex.lng, lng)
            .replace(regex.ns, ns);
    }
    formatResourceSavePath(lng, ns) {
        const options = this.options;
        const regex = {
            lng: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'lng' + options.interpolation.suffix), 'g'),
            ns: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'ns' + options.interpolation.suffix), 'g')
        };
        return options.resource.savePath
            .replace(regex.lng, lng)
            .replace(regex.ns, ns);
    }
    // i18next.t('ns:foo.bar') // matched
    // i18next.t("ns:foo.bar") // matched
    // i18next.t('ns:foo.bar') // matched
    // i18next.t("ns:foo.bar", { count: 1 }); // matched
    // i18next.t("ns:foo.bar" + str); // not matched
    parseFuncFromString(content, opts = {}, customHandler = null) {
        if (_.isFunction(opts)) {
            customHandler = opts;
            opts = {};
        }

        const funcs = opts.list || this.options.func.list;
        const matchPattern = _(funcs)
            .map((func) => ('(?:' + func + ')'))
            .value()
            .join('|')
            .replace(/\./g, '\\.');
        const pattern = '(?:(?:^[\s]*)|[^a-zA-Z0-9_])(?:' + matchPattern + ')\\(("(?:[^"\\\\]|\\\\.)*"|\'(?:[^\'\\\\]|\\\\.)*\')\\s*[\\,\\)]';
        const results = content.match(new RegExp(pattern, 'gim')) || [];
        results.forEach((result) => {
            const r = result.match(new RegExp(pattern));
            if (!r) {
                return;
            }

            const key = _.trim(r[1], '\'"');

            if (customHandler) {
                customHandler(key);
                return;
            }

            this.set(key);
        });

        return this;
    }
    // Parses translation keys from `data-i18n` attribute in HTML
    // <div data-i18n="[attr]ns:foo.bar;[attr]ns:foo.baz">
    // </div>
    parseAttrFromString(content, opts = {}, customHandler = null) {
        if (_.isFunction(opts)) {
            customHandler = opts;
            opts = {};
        }

        const attrs = opts.list || this.options.attr.list;
        const matchPattern = _(attrs)
            .map((attr) => ('(?:' + attr + ')'))
            .value()
            .join('|')
            .replace(/\./g, '\\.');
        const pattern = '(?:(?:^[\s]*)|[^a-zA-Z0-9_])(?:' + matchPattern + ')=("[^"]*"|\'[^\']*\')';
        const results = content.match(new RegExp(pattern, 'gim')) || [];
        results.forEach((result) => {
            const r = result.match(new RegExp(pattern));
            if (!r) {
                return;
            }

            const attr = _.trim(r[1], '\'"');
            const keys = (attr.indexOf(';') >= 0) ? attr.split(';') : [attr];
            keys.forEach((key) => {
                //let attr = 'text';
                key = _.trim(key);
                if (key.length === 0) {
                    return;
                }
                if (key.indexOf('[') === 0) {
                    let parts = key.split(']');
                    key = parts[1];
                    //attr = parts[0].substr(1, parts[0].length - 1);
                }
                if (key.indexOf(';') === (key.length - 1)) {
                    key = key.substr(0, key.length - 2);
                }

                if (customHandler) {
                    customHandler(key);
                    return;
                }

                this.set(key);
            });
        });

        return this;
    }
    // Get the value of a translation key or the whole resource store containing translation information
    // @param {string} [key] The translation key
    // @param {object} [opts] The opts object
    // @param {boolean} [opts.sort] True to sort object by key
    // @param {boolean} [opts.lng] The language to use
    // @return {object}
    get(key, opts = {}) {
        if (_.isObject(key)) {
            opts = key;
            key = undefined;
        }

        const resStore = Object.assign({}, this.resStore);

        if (!!opts.sort) { // sort by key
            Object.keys(resStore).forEach((lng) => {
                const namespaces = resStore[lng];

                Object.keys(namespaces).forEach((ns) => {
                    resStore[lng][ns] = _(namespaces[ns])
                        .toPairs()
                        .sortBy((pair) => pair[0])
                        .reduce((memo, pair) => {
                            const _key = pair[0];
                            const _value = pair[1];
                            memo[_key] = _value;
                            return memo;
                        }, {});

                        // Note. The reduce method is not chainable by default
                });
            });
        }

        if (!_.isUndefined(key)) {
            const options = this.options;
            let ns = options.defaultNs;

            // http://i18next.com/translate/keyBasedFallback/
            // Set nsSeparator and keySeparator to false if you prefer
            // having keys as the fallback for translation.
            // i18next.init({
            //   nsSeparator: false,
            //   keySeparator: false
            // })

            if (_.isString(options.nsSeparator) && (key.indexOf(options.nsSeparator) > -1)) {
                const parts = key.split(options.nsSeparator);
                ns = parts[0];
                key = parts[1];
            }

            const keys = _.isString(options.keySeparator) ? key.split(options.keySeparator) : [key];
            const lng = opts.lng ? opts.lng : options.fallbackLng;
            const namespaces = resStore[lng] || {};

            let value = namespaces[ns];
            let x = 0;
            while (keys[x]) {
                value = value && value[keys[x]];
                x++;
            }

            return value;
        }

        return resStore;
    }
    // Set translation key with an optional defaultValue to i18n resource store
    // @param {string} key The translation key
    // @param {string} [defaultValue] The key's value
    set(key, defaultValue) {
        const options = this.options;

        let ns = options.defaultNs;
        console.assert(_.isString(ns) && !!ns.length, 'ns is not a valid string', ns);

        // http://i18next.com/translate/keyBasedFallback/
        // Set nsSeparator and keySeparator to false if you prefer
        // having keys as the fallback for translation.
        // i18next.init({
        //   nsSeparator: false,
        //   keySeparator: false
        // })

        if (_.isString(options.nsSeparator) && (key.indexOf(options.nsSeparator) > -1)) {
            const parts = key.split(options.nsSeparator);
            ns = parts[0];
            key = parts[1];
        }

        const keys = _.isString(options.keySeparator) ? key.split(options.keySeparator) : [key];
        options.lngs.forEach((lng) => {
            let value = this.resStore[lng] && this.resStore[lng][ns];
            let x = 0;

            while (keys[x]) {
                value = value && value[keys[x]];
                x++;
            }

            if (!_.isUndefined(value)) {
                // Found a value associated with the key
                let lookupKey = '[' + lng + '][' + ns + '][' + keys.join('][') + ']';
                this.debuglog('Found a value %s associated with the key %s in %s.',
                    JSON.stringify(_.get(this.resStore, lookupKey)),
                    JSON.stringify(keys.join(options.keySeparator || '')),
                    JSON.stringify(this.formatResourceLoadPath(lng, ns))
                );
            } else if (_.isObject(this.resStore[lng][ns])) {
                // Adding a new entry
                let res = this.resStore[lng][ns];
                Object.keys(keys).forEach((index) => {
                    const elem = keys[index];
                    if (index >= (keys.length - 1)) {
                        if (options.keySeparator === false) {
                            res[elem] = elem;
                        } else {
                            res[elem] = _.isUndefined(defaultValue) ? options.defaultValue : defaultValue;
                        }
                    } else {
                        res[elem] = res[elem] || {};
                        res = res[elem];
                    }
                });
                let lookupKey = '[' + lng + '][' + ns + '][' + keys.join('][') + ']';
                this.debuglog('Adding a new entry {%s:%s} to %s.',
                    JSON.stringify(keys.join(options.keySeparator || '')),
                    JSON.stringify(_.get(this.resStore, lookupKey)),
                    JSON.stringify(this.formatResourceLoadPath(lng, ns))
                );
            } else { // skip the namespace that is not defined in the i18next options
                console.log('The namespace "' + ns + '" does not exist:', { key, defaultValue });
            }
        });
    }
    // Returns a JSON string containing translation information
    // @param {object} [options] The options object
    // @param {boolean} [options.sort] True to sort object by key
    // @param {function|string[]|number[]} [options.replacer] The same as the JSON.stringify()
    // @param {string|number} [options.space] The same as the JSON.stringify() method
    // @return {string}
    toJSON(options = {}) {
        const { replacer, space } = options;

        return JSON.stringify(this.getResourceStore(options), replacer, space);
    }
}

export default Parser;
<commit_msg>Remove unused vars<commit_after>/* eslint no-console: 0 */
import _ from 'lodash';
import fs from 'fs';

const defaults = {
    debug: false, // verbose logging

    sort: false, // sort keys in alphabetical order

    attr: { // HTML attributes to parse
        list: ['data-i18n'],
        extensions: ['.html', '.htm']
    },

    func: { // function names to parse
        list: ['i18next.t', 'i18n.t'],
        extensions: ['.js', '.jsx']
    },

    lngs: ['en'], // array of supported languages
    fallbackLng: 'en', // language to lookup key if not found while calling `parser.get(key, { lng: '' })`

    ns: [], // string or array of namespaces

    defaultNs: 'translation', // default namespace used if not passed to translation function

    defaultValue: '', // default value used if not passed to `parser.set`

    // resource
    resource: {
        loadPath: 'i18n/{{lng}}/{{ns}}.json', // the source resource path (relative to current working directory)
        savePath: 'i18n/{{lng}}/{{ns}}.json', // the target resource path (relative to the path specified with `gulp.dest(path)`)
        jsonIndent: 2
    },

    keySeparator: '.', // char to separate keys
    nsSeparator: ':', // char to split namespace from key

    // interpolation options
    interpolation: {
        prefix: '{{', // prefix for interpolation
        suffix: '}}' // suffix for interpolation
    }
};

const transformOptions = (options) => {
    // Attribute
    if (_.isUndefined(_.get(options, 'attr.list'))) {
        _.set(options, 'attr.list', defaults.attr.list);
    }
    if (_.isUndefined(_.get(options, 'attr.extensions'))) {
        _.set(options, 'attr.extensions', defaults.attr.extensions);
    }

    // Function
    if (_.isUndefined(_.get(options, 'func.list'))) {
        _.set(options, 'func.list', defaults.func.list);
    }

    // Resource
    if (_.isUndefined(_.get(options, 'func.extensions'))) {
        _.set(options, 'func.extensions', defaults.func.extensions);
    }
    if (_.isUndefined(_.get(options, 'resource.loadPath'))) {
        _.set(options, 'resource.loadPath', defaults.resource.loadPath);
    }
    if (_.isUndefined(_.get(options, 'resource.savePath'))) {
        _.set(options, 'resource.savePath', defaults.resource.savePath);
    }
    if (_.isUndefined(_.get(options, 'resource.jsonIndent'))) {
        _.set(options, 'resource.jsonIndent', defaults.resource.jsonIndent);
    }

    // Accept both nsseparator or nsSeparator
    if (!_.isUndefined(options.nsseparator)) {
        options.nsSeparator = options.nsseparator;
        delete options.nsseparator;
    }
    // Allowed only string or false
    if (!_.isString(options.nsSeparator)) {
        options.nsSeparator = false;
    }

    // Accept both keyseparator or keySeparator
    if (!_.isUndefined(options.keyseparator)) {
        options.keySeparator = options.keyseparator;
        delete options.keyseparator;
    }
    // Allowed only string or false
    if (!_.isString(options.keySeparator)) {
        options.keySeparator = false;
    }

    if (!_.isArray(options.ns)) {
        options.ns = [options.ns];
    }

    options.ns = _(options.ns.concat(options.defaultNs))
        .flatten()
        .union()
        .value();

    return options;
};

/**
* Creates a new parser
* @constructor
*/
class Parser {
    options = Object.assign({}, defaults);
    resStore = {};

    constructor(options) {
        this.options = transformOptions(_.extend({}, this.options, options));

        const lngs = this.options.lngs;
        const namespaces = this.options.ns;

        lngs.forEach((lng) => {
            this.resStore[lng] = this.resStore[lng] || {};
            namespaces.forEach((ns) => {
                const resPath = this.formatResourceLoadPath(lng, ns);
                this.resStore[lng][ns] = {};
                try {
                    const stat = fs.statSync(resPath);
                    if (stat.isFile()) {
                        this.resStore[lng][ns] = JSON.parse(fs.readFileSync(resPath, 'utf-8'));
                    }
                } catch (err) {
                    this.debuglog('Unable to load ' + JSON.stringify(resPath));
                }
            });
        });

        this.debuglog('[i18next-scanner] Parser(options): ' + JSON.stringify(this.options));
    }
    debuglog(...args) {
        const { debug } = this.options;
        if (!!debug) {
            console.log.apply(this, args);
        }
    }
    formatResourceLoadPath(lng, ns) {
        const options = this.options;
        const regex = {
            lng: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'lng' + options.interpolation.suffix), 'g'),
            ns: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'ns' + options.interpolation.suffix), 'g')
        };
        return options.resource.loadPath
            .replace(regex.lng, lng)
            .replace(regex.ns, ns);
    }
    formatResourceSavePath(lng, ns) {
        const options = this.options;
        const regex = {
            lng: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'lng' + options.interpolation.suffix), 'g'),
            ns: new RegExp(_.escapeRegExp(options.interpolation.prefix + 'ns' + options.interpolation.suffix), 'g')
        };
        return options.resource.savePath
            .replace(regex.lng, lng)
            .replace(regex.ns, ns);
    }
    // i18next.t('ns:foo.bar') // matched
    // i18next.t("ns:foo.bar") // matched
    // i18next.t('ns:foo.bar') // matched
    // i18next.t("ns:foo.bar", { count: 1 }); // matched
    // i18next.t("ns:foo.bar" + str); // not matched
    parseFuncFromString(content, opts = {}, customHandler = null) {
        if (_.isFunction(opts)) {
            customHandler = opts;
            opts = {};
        }

        const funcs = opts.list || this.options.func.list;
        const matchPattern = _(funcs)
            .map((func) => ('(?:' + func + ')'))
            .value()
            .join('|')
            .replace(/\./g, '\\.');
        const pattern = '(?:(?:^[\s]*)|[^a-zA-Z0-9_])(?:' + matchPattern + ')\\(("(?:[^"\\\\]|\\\\.)*"|\'(?:[^\'\\\\]|\\\\.)*\')\\s*[\\,\\)]';
        const results = content.match(new RegExp(pattern, 'gim')) || [];
        results.forEach((result) => {
            const r = result.match(new RegExp(pattern));
            if (!r) {
                return;
            }

            const key = _.trim(r[1], '\'"');

            if (customHandler) {
                customHandler(key);
                return;
            }

            this.set(key);
        });

        return this;
    }
    // Parses translation keys from `data-i18n` attribute in HTML
    // <div data-i18n="[attr]ns:foo.bar;[attr]ns:foo.baz">
    // </div>
    parseAttrFromString(content, opts = {}, customHandler = null) {
        if (_.isFunction(opts)) {
            customHandler = opts;
            opts = {};
        }

        const attrs = opts.list || this.options.attr.list;
        const matchPattern = _(attrs)
            .map((attr) => ('(?:' + attr + ')'))
            .value()
            .join('|')
            .replace(/\./g, '\\.');
        const pattern = '(?:(?:^[\s]*)|[^a-zA-Z0-9_])(?:' + matchPattern + ')=("[^"]*"|\'[^\']*\')';
        const results = content.match(new RegExp(pattern, 'gim')) || [];
        results.forEach((result) => {
            const r = result.match(new RegExp(pattern));
            if (!r) {
                return;
            }

            const attr = _.trim(r[1], '\'"');
            const keys = (attr.indexOf(';') >= 0) ? attr.split(';') : [attr];
            keys.forEach((key) => {
                //let attr = 'text';
                key = _.trim(key);
                if (key.length === 0) {
                    return;
                }
                if (key.indexOf('[') === 0) {
                    let parts = key.split(']');
                    key = parts[1];
                    //attr = parts[0].substr(1, parts[0].length - 1);
                }
                if (key.indexOf(';') === (key.length - 1)) {
                    key = key.substr(0, key.length - 2);
                }

                if (customHandler) {
                    customHandler(key);
                    return;
                }

                this.set(key);
            });
        });

        return this;
    }
    // Get the value of a translation key or the whole resource store containing translation information
    // @param {string} [key] The translation key
    // @param {object} [opts] The opts object
    // @param {boolean} [opts.sort] True to sort object by key
    // @param {boolean} [opts.lng] The language to use
    // @return {object}
    get(key, opts = {}) {
        if (_.isObject(key)) {
            opts = key;
            key = undefined;
        }

        const resStore = Object.assign({}, this.resStore);

        if (!!opts.sort) { // sort by key
            Object.keys(resStore).forEach((lng) => {
                const namespaces = resStore[lng];

                Object.keys(namespaces).forEach((ns) => {
                    resStore[lng][ns] = _(namespaces[ns])
                        .toPairs()
                        .sortBy((pair) => pair[0])
                        .reduce((memo, pair) => {
                            const _key = pair[0];
                            const _value = pair[1];
                            memo[_key] = _value;
                            return memo;
                        }, {});

                        // Note. The reduce method is not chainable by default
                });
            });
        }

        if (!_.isUndefined(key)) {
            const options = this.options;
            let ns = options.defaultNs;

            // http://i18next.com/translate/keyBasedFallback/
            // Set nsSeparator and keySeparator to false if you prefer
            // having keys as the fallback for translation.
            // i18next.init({
            //   nsSeparator: false,
            //   keySeparator: false
            // })

            if (_.isString(options.nsSeparator) && (key.indexOf(options.nsSeparator) > -1)) {
                const parts = key.split(options.nsSeparator);
                ns = parts[0];
                key = parts[1];
            }

            const keys = _.isString(options.keySeparator) ? key.split(options.keySeparator) : [key];
            const lng = opts.lng ? opts.lng : options.fallbackLng;
            const namespaces = resStore[lng] || {};

            let value = namespaces[ns];
            let x = 0;
            while (keys[x]) {
                value = value && value[keys[x]];
                x++;
            }

            return value;
        }

        return resStore;
    }
    // Set translation key with an optional defaultValue to i18n resource store
    // @param {string} key The translation key
    // @param {string} [defaultValue] The key's value
    set(key, defaultValue) {
        const options = this.options;

        let ns = options.defaultNs;
        console.assert(_.isString(ns) && !!ns.length, 'ns is not a valid string', ns);

        // http://i18next.com/translate/keyBasedFallback/
        // Set nsSeparator and keySeparator to false if you prefer
        // having keys as the fallback for translation.
        // i18next.init({
        //   nsSeparator: false,
        //   keySeparator: false
        // })

        if (_.isString(options.nsSeparator) && (key.indexOf(options.nsSeparator) > -1)) {
            const parts = key.split(options.nsSeparator);
            ns = parts[0];
            key = parts[1];
        }

        const keys = _.isString(options.keySeparator) ? key.split(options.keySeparator) : [key];
        options.lngs.forEach((lng) => {
            let value = this.resStore[lng] && this.resStore[lng][ns];
            let x = 0;

            while (keys[x]) {
                value = value && value[keys[x]];
                x++;
            }

            if (!_.isUndefined(value)) {
                // Found a value associated with the key
                let lookupKey = '[' + lng + '][' + ns + '][' + keys.join('][') + ']';
                this.debuglog('Found a value %s associated with the key %s in %s.',
                    JSON.stringify(_.get(this.resStore, lookupKey)),
                    JSON.stringify(keys.join(options.keySeparator || '')),
                    JSON.stringify(this.formatResourceLoadPath(lng, ns))
                );
            } else if (_.isObject(this.resStore[lng][ns])) {
                // Adding a new entry
                let res = this.resStore[lng][ns];
                Object.keys(keys).forEach((index) => {
                    const elem = keys[index];
                    if (index >= (keys.length - 1)) {
                        if (options.keySeparator === false) {
                            res[elem] = elem;
                        } else {
                            res[elem] = _.isUndefined(defaultValue) ? options.defaultValue : defaultValue;
                        }
                    } else {
                        res[elem] = res[elem] || {};
                        res = res[elem];
                    }
                });
                let lookupKey = '[' + lng + '][' + ns + '][' + keys.join('][') + ']';
                this.debuglog('Adding a new entry {%s:%s} to %s.',
                    JSON.stringify(keys.join(options.keySeparator || '')),
                    JSON.stringify(_.get(this.resStore, lookupKey)),
                    JSON.stringify(this.formatResourceLoadPath(lng, ns))
                );
            } else { // skip the namespace that is not defined in the i18next options
                console.log('The namespace "' + ns + '" does not exist:', { key, defaultValue });
            }
        });
    }
    // Returns a JSON string containing translation information
    // @param {object} [options] The options object
    // @param {boolean} [options.sort] True to sort object by key
    // @param {function|string[]|number[]} [options.replacer] The same as the JSON.stringify()
    // @param {string|number} [options.space] The same as the JSON.stringify() method
    // @return {string}
    toJSON(options = {}) {
        const { replacer, space } = options;

        return JSON.stringify(this.getResourceStore(options), replacer, space);
    }
}

export default Parser;
<|endoftext|>


<commit_before>// --
// Core.Agent.Stats.js - provides the special module functions for AgentStats
// Copyright (C) 2001-2011 OTRS AG, http://otrs.org/
// --
// $Id: Core.Agent.Stats.js,v 1.3 2011-10-31 10:58:10 mg Exp $
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var Core = Core || {};
Core.Agent = Core.Agent || {};

/**
 * @namespace
 * @exports TargetNS as Core.Agent.Stats
 * @description
 *      This namespace contains the special module functions for the Dashboard.
 */
Core.Agent.Stats = (function (TargetNS) {

    /**
     * @function
     * @return nothing
     * @description
     *      Activates the graph size menu if a GD element is selected.
     */
    TargetNS.FormatGraphSizeRelation = function () {
        var $Format = $('#Format'),
            Flag = false,
            Reg = /^GD::/;

        // find out if a GD element is used
        $.each($Format.children('option'), function () {
            if ($(this).attr('selected') === 'selected') {
                if (Reg.test($(this).val()) === true) {
                    Flag = true;
                }
            }
        });

        // activate or deactivate the Graphsize menu
        $('#GraphSize').attr('disabled', Flag ? false : true);
    };

    /**
     * @function
     * @return nothing
     *      Selects a checbox by name
     * @param {Object} The name of the radio button to be selected
     */
    TargetNS.SelectCheckbox = function (Name) {
        $('input:checkbox[name=' + Name + ']').attr('checked', true);
    };

    /**
     * @function
     * @return nothing
     *      Selects a radio button by name and value
     * @param {Value} The value attribute of the radio button to be selected
     * @param {Object} The name of the radio button to be selected
     */

    TargetNS.SelectRadiobutton = function (Value, Name) {
        $('input:radio[name=' + Name + '][value=' + Value + ']').attr('checked', true);
    };

    return TargetNS;
}(Core.Agent.Stats || {}));<commit_msg>Fix for jQuery 1.6 compat.<commit_after>// --
// Core.Agent.Stats.js - provides the special module functions for AgentStats
// Copyright (C) 2001-2011 OTRS AG, http://otrs.org/
// --
// $Id: Core.Agent.Stats.js,v 1.4 2011-10-31 11:02:27 mg Exp $
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var Core = Core || {};
Core.Agent = Core.Agent || {};

/**
 * @namespace
 * @exports TargetNS as Core.Agent.Stats
 * @description
 *      This namespace contains the special module functions for the Dashboard.
 */
Core.Agent.Stats = (function (TargetNS) {

    /**
     * @function
     * @return nothing
     * @description
     *      Activates the graph size menu if a GD element is selected.
     */
    TargetNS.FormatGraphSizeRelation = function () {
        var $Format = $('#Format'),
            Flag = false,
            Reg = /^GD::/;

        // find out if a GD element is used
        $.each($Format.children('option'), function () {
            if ($(this).attr('selected') === 'selected') {
                if (Reg.test($(this).val()) === true) {
                    Flag = true;
                }
            }
        });

        // activate or deactivate the Graphsize menu
        $('#GraphSize').attr('disabled', Flag ? false : true);
    };

    /**
     * @function
     * @return nothing
     *      Selects a checbox by name
     * @param {Object} The name of the radio button to be selected
     */
    TargetNS.SelectCheckbox = function (Name) {
        $('input:checkbox[name=' + Name + ']').attr('checked', 'checked');
    };

    /**
     * @function
     * @return nothing
     *      Selects a radio button by name and value
     * @param {Value} The value attribute of the radio button to be selected
     * @param {Object} The name of the radio button to be selected
     */

    TargetNS.SelectRadiobutton = function (Value, Name) {
        $('input:radio[name=' + Name + '][value=' + Value + ']').attr('checked', 'checked');
    };

    return TargetNS;
}(Core.Agent.Stats || {}));<|endoftext|>


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

// Usage: The passed in function is called when the page is ready.
// CouchApp passes in the app object, which takes care of linking to
// the proper database, and provides access to the CouchApp helpers.
// $.couch.app(function(app) {
//    app.db.view(...)
//    ...
// });

(function($) {

  function Design(db, name) {
    this.doc_id = "_design/"+name;
    this.view = function(view, opts) {
      db.view(name+'/'+view, opts);
    };
  }

  $.couch.app = $.couch.app || function(appFun) {
    $(function() {
      var dbname = document.location.href.split('/')[3];
      var dname = unescape(document.location.href).split('/')[5];
      var db = $.couch.db(dbname);
      var design = new Design(db, dname);

      // docForm applies CouchDB behavior to HTML forms.
      // todo make this a couch.app plugin
      function docForm(formSelector, opts) {
        var localFormDoc = {};
        opts = opts || {};
        opts.fields = opts.fields || [];

        // turn the form into deep json
        // field names like 'author-email' get turned into json like
        // {"author":{"email":"quentin@example.com"}}
        function formToDeepJSON(form, fields, doc) {
          form = $(form);
          opts.fields.forEach(function(field) {
            var val = form.find("[name="+field+"]").val();
            if (!val) {return;}
            var parts = field.split('-');
            var frontObj = doc, frontName = parts.shift();
            while (parts.length > 0) {
              frontObj[frontName] = frontObj[frontName] || {};
              frontObj = frontObj[frontName];
              frontName = parts.shift();
            }
            frontObj[frontName] = val;
          });
        }

        // Apply the behavior
        $(formSelector).submit(function(e) {
          e.preventDefault();
          // formToDeepJSON acts on localFormDoc by reference
          formToDeepJSON(this, opts.fields, localFormDoc);
          if (opts.beforeSave) {opts.beforeSave(localFormDoc);}
          db.saveDoc(localFormDoc, {
            success : function(resp) {
              if (opts.success) {opts.success(resp, localFormDoc);}
            }
          });

          return false;
        });

        // populate form from an existing doc
        function docToForm(doc) {
          var form = $(formSelector);
          // fills in forms
          opts.fields.forEach(function(field) {
            var parts = field.split('-');
            var run = true, frontObj = doc, frontName = parts.shift();
            while (frontObj && parts.length > 0) {
              frontObj = frontObj[frontName];
              frontName = parts.shift();
            }
            if (frontObj && frontObj[frontName]) {
              form.find("[name="+field+"]").val(frontObj[frontName]);
            }
          });
        }

        if (opts.id) {
          db.openDoc(opts.id, {
            success: function(doc) {
              if (opts.onLoad) {opts.onLoad(doc);}
              localFormDoc = doc;
              docToForm(doc);
          }});
        } else if (opts.template) {
          if (opts.onLoad) {opts.onLoad(opts.template);}
          localFormDoc = opts.template;
          docToForm(localFormDoc);
        }
        var instance = {
          deleteDoc : function(opts) {
            opts = opts || {};
            if (confirm("Really delete this document?")) {
              db.removeDoc(localFormDoc, opts);
            }
          },
          localDoc : function() {
            formToDeepJSON(formSelector, opts.fields, localFormDoc);
            return localFormDoc;
          }
        };
        return instance;
      }

      function resolveModule(names, parent, current) {
        if (names.length === 0) {
          if (typeof current != "string") {
            throw ["error","invalid_require_path",
              'Must require a JavaScript string, not: '+(typeof current)];
          }
          return [current, parent];
        }
        // we need to traverse the path
        var n = names.shift();
        if (n == '..') {
          if (!(parent && parent.parent)) {
            throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(current)];
          }
          return resolveModule(names, parent.parent.parent, parent.parent);
        } else if (n == '.') {
          if (!parent) {
            throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(current)];
          }
          return resolveModule(names, parent.parent, parent);
        }
        if (!current[n]) {
          throw ["error", "invalid_require_path", 'Object has no property "'+n+'". '+JSON.stringify(current)];
        }
        var p = current;
        current = current[n];
        current.parent = p;
        return resolveModule(names, p, current);
      }

      var p = document.location.pathname.split('/');
      p.shift();
      var qs = document.location.search.replace(/^\?/,'').split('&');
      var q = {};
      qs.forEach(function(param) {
        var ps = param.split('=');
        var k = decodeURIComponent(ps[0]);
        var v = decodeURIComponent(ps[1]);
        if (["startkey", "endkey", "key"].indexOf(k) != -1) {
          q[k] = JSON.parse(v);
        } else {
          q[k] = v;
        }
      });
      var mockReq = {
        path : p,
        query : q
      };

      var appExports = $.extend({
        db : db,
        design : design,
        view : design.view,
        docForm : docForm,
        req : mockReq
      }, $.couch.app.app);

      function handleDDoc(ddoc) {
        if (ddoc) {
          var require = function(name, parent) {
            var exports = {};
            var resolved = resolveModule(name.split('/'), parent, ddoc);
            var source = resolved[0];
            parent = resolved[1];
            var s = "var func = function (exports, require) { " + source + " };";
            try {
              eval(s);
              func.apply(ddoc, [exports, function(name) {return require(name, parent, source)}]);
            } catch(e) {
              throw ["error","compilation_error","Module require('"+name+"') raised error "+e.toSource()];
            }
            return exports;
          }
          appExports.ddoc = ddoc;
          appExports.require = require;
        }
        // todo make app-exports the this in the execution context?
        appFun.apply(appExports, [appExports]);
      }

    if ($.couch.app.ddocs[design.doc_id]) {
      handleDDoc($.couch.app.ddocs[design.doc_id])
    } else {
      // only open 1 connection for this ddoc
      if ($.couch.app.ddoc_handlers[design.doc_id]) {
        // we are already fetching, just wait
        $.couch.app.ddoc_handlers[design.doc_id].push(handleDDoc);
      } else {
        $.couch.app.ddoc_handlers[design.doc_id] = [handleDDoc];
        db.openDoc(design.doc_id, {
          success : function(doc) {
            $.couch.app.ddocs[design.doc_id] = doc;
            $.couch.app.ddoc_handlers[design.doc_id].forEach(function(h) {
              h(doc);
            });
            $.couch.app.ddoc_handlers[design.doc_id] = null;
          },
          error : function() {
            $.couch.app.ddoc_handlers[design.doc_id].forEach(function(h) {
              h();
            });
            $.couch.app.ddoc_handlers[design.doc_id] = null;
          }
        });
      }
    }

    });
  };
  $.couch.app.ddocs = {};
  $.couch.app.ddoc_handlers = {};
  // legacy support. $.CouchApp is deprecated, please use $.couch.app
  $.CouchApp = $.couch.app;
})(jQuery);
<commit_msg>adding compatiblity code so that jquery.couch.app.js will work in IE7/IE8<commit_after>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations under
// the License.

// Usage: The passed in function is called when the page is ready.
// CouchApp passes in the app object, which takes care of linking to
// the proper database, and provides access to the CouchApp helpers.
// $.couch.app(function(app) {
//    app.db.view(...)
//    ...
// });

(function($) {

  function Design(db, name) {
    this.doc_id = "_design/"+name;
    this.view = function(view, opts) {
      db.view(name+'/'+view, opts);
    };
  }

  $.couch.app = $.couch.app || function(appFun) {
    $(function() {
      var dbname = document.location.href.split('/')[3];
      var dname = unescape(document.location.href).split('/')[5];
      var db = $.couch.db(dbname);
      var design = new Design(db, dname);

      // docForm applies CouchDB behavior to HTML forms.
      // todo make this a couch.app plugin
      function docForm(formSelector, opts) {
        var localFormDoc = {};
        opts = opts || {};
        opts.fields = opts.fields || [];

        // turn the form into deep json
        // field names like 'author-email' get turned into json like
        // {"author":{"email":"quentin@example.com"}}
        function formToDeepJSON(form, fields, doc) {
          form = $(form);
          opts.fields.forEach(function(field) {
            var val = form.find("[name="+field+"]").val();
            if (!val) {return;}
            var parts = field.split('-');
            var frontObj = doc, frontName = parts.shift();
            while (parts.length > 0) {
              frontObj[frontName] = frontObj[frontName] || {};
              frontObj = frontObj[frontName];
              frontName = parts.shift();
            }
            frontObj[frontName] = val;
          });
        }

        // Apply the behavior
        $(formSelector).submit(function(e) {
          e.preventDefault();
          // formToDeepJSON acts on localFormDoc by reference
          formToDeepJSON(this, opts.fields, localFormDoc);
          if (opts.beforeSave) {opts.beforeSave(localFormDoc);}
          db.saveDoc(localFormDoc, {
            success : function(resp) {
              if (opts.success) {opts.success(resp, localFormDoc);}
            }
          });

          return false;
        });

        // populate form from an existing doc
        function docToForm(doc) {
          var form = $(formSelector);
          // fills in forms
          opts.fields.forEach(function(field) {
            var parts = field.split('-');
            var run = true, frontObj = doc, frontName = parts.shift();
            while (frontObj && parts.length > 0) {
              frontObj = frontObj[frontName];
              frontName = parts.shift();
            }
            if (frontObj && frontObj[frontName]) {
              form.find("[name="+field+"]").val(frontObj[frontName]);
            }
          });
        }

        if (opts.id) {
          db.openDoc(opts.id, {
            success: function(doc) {
              if (opts.onLoad) {opts.onLoad(doc);}
              localFormDoc = doc;
              docToForm(doc);
          }});
        } else if (opts.template) {
          if (opts.onLoad) {opts.onLoad(opts.template);}
          localFormDoc = opts.template;
          docToForm(localFormDoc);
        }
        var instance = {
          deleteDoc : function(opts) {
            opts = opts || {};
            if (confirm("Really delete this document?")) {
              db.removeDoc(localFormDoc, opts);
            }
          },
          localDoc : function() {
            formToDeepJSON(formSelector, opts.fields, localFormDoc);
            return localFormDoc;
          }
        };
        return instance;
      }

      function resolveModule(names, parent, current) {
        if (names.length === 0) {
          if (typeof current != "string") {
            throw ["error","invalid_require_path",
              'Must require a JavaScript string, not: '+(typeof current)];
          }
          return [current, parent];
        }
        // we need to traverse the path
        var n = names.shift();
        if (n == '..') {
          if (!(parent && parent.parent)) {
            throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(current)];
          }
          return resolveModule(names, parent.parent.parent, parent.parent);
        } else if (n == '.') {
          if (!parent) {
            throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(current)];
          }
          return resolveModule(names, parent.parent, parent);
        }
        if (!current[n]) {
          throw ["error", "invalid_require_path", 'Object has no property "'+n+'". '+JSON.stringify(current)];
        }
        var p = current;
        current = current[n];
        current.parent = p;
        return resolveModule(names, p, current);
      }

      var p = document.location.pathname.split('/');
      p.shift();
      var qs = document.location.search.replace(/^\?/,'').split('&');
      var q = {};
      qs.forEach(function(param) {
        var ps = param.split('=');
        var k = decodeURIComponent(ps[0]);
        var v = decodeURIComponent(ps[1]);
        if (["startkey", "endkey", "key"].indexOf(k) != -1) {
          q[k] = JSON.parse(v);
        } else {
          q[k] = v;
        }
      });
      var mockReq = {
        path : p,
        query : q
      };

      var appExports = $.extend({
        db : db,
        design : design,
        view : design.view,
        docForm : docForm,
        req : mockReq
      }, $.couch.app.app);

      function handleDDoc(ddoc) {
        if (ddoc) {
          var require = function(name, parent) {
            var exports = {};
            var resolved = resolveModule(name.split('/'), parent, ddoc);
            var source = resolved[0];
            parent = resolved[1];
            var s = "var func = function (exports, require) { " + source + " };";
            try {
              eval(s);
              func.apply(ddoc, [exports, function(name) {return require(name, parent, source)}]);
            } catch(e) {
              throw ["error","compilation_error","Module require('"+name+"') raised error "+e.toSource()];
            }
            return exports;
          }
          appExports.ddoc = ddoc;
          appExports.require = require;
        }
        // todo make app-exports the this in the execution context?
        appFun.apply(appExports, [appExports]);
      }

    if ($.couch.app.ddocs[design.doc_id]) {
      handleDDoc($.couch.app.ddocs[design.doc_id])
    } else {
      // only open 1 connection for this ddoc
      if ($.couch.app.ddoc_handlers[design.doc_id]) {
        // we are already fetching, just wait
        $.couch.app.ddoc_handlers[design.doc_id].push(handleDDoc);
      } else {
        $.couch.app.ddoc_handlers[design.doc_id] = [handleDDoc];
        db.openDoc(design.doc_id, {
          success : function(doc) {
            $.couch.app.ddocs[design.doc_id] = doc;
            $.couch.app.ddoc_handlers[design.doc_id].forEach(function(h) {
              h(doc);
            });
            $.couch.app.ddoc_handlers[design.doc_id] = null;
          },
          error : function() {
            $.couch.app.ddoc_handlers[design.doc_id].forEach(function(h) {
              h();
            });
            $.couch.app.ddoc_handlers[design.doc_id] = null;
          }
        });
      }
    }

    });
  };
  $.couch.app.ddocs = {};
  $.couch.app.ddoc_handlers = {};
  // legacy support. $.CouchApp is deprecated, please use $.couch.app
  $.CouchApp = $.couch.app;
})(jQuery);

// JavaScript 1.6 compatibility functions that are missing from IE7/IE8

if (!Array.prototype.forEach)
{
    Array.prototype.forEach = function(fun /*, thisp*/)
    {
        var len = this.length >>> 0;
        if (typeof fun != "function")
            throw new TypeError();

        var thisp = arguments[1];
        for (var i = 0; i < len; i++)
        {
            if (i in this)
                fun.call(thisp, this[i], i, this);
        }
    };
}

if (!Array.prototype.indexOf)
{
    Array.prototype.indexOf = function(elt)
    {
        var len = this.length >>> 0;

        var from = Number(arguments[1]) || 0;
        from = (from < 0)
                ? Math.ceil(from)
                : Math.floor(from);
        if (from < 0)
            from += len;

        for (; from < len; from++)
        {
            if (from in this &&
                this[from] === elt)
                return from;
        }
        return -1;
    };
}
<|endoftext|>


<commit_before>var Alloy = require('alloy'),
	_ = Alloy._,
	Backbone = Alloy.Backbone;

/**
 * @class Framework
 * @singleton
 *
 * RebelFrame's general functions, defined as singleton
 */
var Framework = module.exports = {
	/**
	 * @property {String} LOGGEDOUT User has installed App, but not yet loggedin
	 */
	LOGGEDOUT: 'loggedout',
	/**
	 * @property {String} LOGGEDOUT User is loggedin, but has not yet activated
	 */
	LOGGEDIN: 'loggedin',
	/**
	 * @property {String} ACTIVATED User is loggedin, activated and ready to use all the features
	 */
	ACTIVATED: 'activated',

	/**
	 * @property {Number} deviceWidth Width of the device in dip. We need to devide by the logicalDensityFactor on Android to get the dip
	 */
	deviceWidth: OS_IOS ? Ti.Platform.displayCaps.platformWidth : Ti.Platform.displayCaps.platformWidth / Ti.Platform.displayCaps.logicalDensityFactor,

	/**
	 * @property {Number} deviceHeight Height of the device in dip. We need to devide by the logicalDensityFactor on Android to get the dip
	 */
	deviceHeight: OS_IOS ? Ti.Platform.displayCaps.platformHeight : Ti.Platform.displayCaps.platformHeight / Ti.Platform.displayCaps.logicalDensityFactor,

	/**
	 * Sets the status of this app
	 *
	 * @param {String} status The status to set
	 */
	setStatus: function(status) {
		_status = status;
		Ti.App.Properties.setString(Ti.App.id + '.status', status);
	},

	/**
	 * Returns the status of this app
	 *
	 * @return {String} The status
	 */
	getStatus: function() {
		if (_status)
			return _status;

		return Ti.App.Properties.getString(Ti.App.id + '.status', Framework.LOGGEDOUT);
	},

	/**
	 * Extend Alloy's createController en createWidget functions to call construct function when controller is initialized.
	 */
	extendAlloy: function() {
		// Wrap some Alloy functions, so they call construct and destruct methods.
		var _alloy_createController = Alloy.createController,
			_alloy_createModel = Alloy.createModel,
			_alloy_createCollection = Alloy.createCollection,
			_alloy_createWidget = Alloy.createWidget,
			WM = require('RebelFrame/WindowManager');

		/**
		 * Adds openWin en closeWin functions to Alloy Controllers. This way it is easy to call $.openWin() and $.closeWin and are needed close eventlisteners automatically added and removed.
		 *
		 * @param {Alloy.Controller} controller Controller to add the functions to
		 */
		var addFunctions = function(controller, config) {
			controller.openWin = function(win) {
				win = win || controller.getView();

				if (!_.isUndefined(config.showSideMenu))
					win.showSideMenu = config.showSideMenu;

				// Open the window
				WM.openWin(win);

				/**
				 * Handle `close` event of Window. Removes eventlistener and calls both RebelFrame's destruct as Alloy's destroy functions.
				 *
				 * @param  {Object} evt Event details
				 */
				function onCloseWin(evt) {
					this.removeEventListener('close', onCloseWin);

					if (controller.destruct) {
						Ti.API.debug('destruct() called');
						controller.destruct.call(controller, evt);
					} else
						Ti.API.warn('destruct() NOT called');

					controller.destroy.call(controller, evt);

					// Cleanup possible panning:
					evt.source.keyboardPanning = false;
				}

				win.addEventListener('close', onCloseWin);
			};

			/**
			 * Close the Window
			 *
			 * @param  {Ti.UI.Window} [win] Window to close. If not provided, the Controller's top level view is used.
			 */
			controller.closeWin = function(win) {
				win = win || controller.getView();

				WM.closeWin(win);
			};
		};

		/**
		 * Call original Alloy.createController function and then construct is it exists. Also track this new screen in Google Analytics
		 *
		 * @param  {String} name Controller name
		 * @param  {Object} config Controller configuration
		 */
		Alloy.createController = function(name, config) {
			config = config || {};

			var controller = _alloy_createController(name, config);

			addFunctions(controller, config);

			if (controller.construct)
				controller.construct.call(controller, config || {});

			// Track screen
			if (name !== 'index')
				tracker.trackScreen(name);

			return controller;
		};

		/**
		 * Call original Alloy.createWidget function and then construct is it exists. Also track this new screen in Google Analytics
		 *
		 * @param  {String} name Controller name
		 * @param  {Object} config Controller configuration
		 */
		Alloy.createWidget = function(name, controller, config) {
			config = config || {};

			var widget = _alloy_createWidget(name, controller, config);

			// Also support name, config as arguments, leaving out controller, but do this only after calling the original method.
			// Copied from Alloy.js definition
			if ("undefined" != typeof controller && null !== controller && _.isObject(controller) && !_.isString(controller)) {
				config = controller;
			}

			if (widget.construct)
				widget.construct.call(widget, config || {});

			addFunctions(widget, config);

			return widget;
		};
	},

	/**
	 * Scan the contents of the supplied object
	 *
	 * For debugging purposes only
	 *
	 * @param {Object} obj The object to scan
	 * @private
	 */
	scan: function(obj) {
		var key, type;

		Ti.API.error('Contents of object:');
		for (key in obj) {
			type = typeof(obj[key]);
			if (type != 'object' && type != 'function')
				Ti.API.error(' - ' + key + ': ' + type + ' (' + obj[key] + ')');
			else
				Ti.API.error(' - ' + key + ': ' + type);
		}
	}
};

/**
 * {String} The status of this app (i.e. loggedout, loggedin, activated)
 * @private
 */
var _status;

// Create some basic globals that can be used in TSS
Alloy.Globals.screenHeight = Framework.deviceHeight - 64;
Alloy.Globals.screenWidth = Framework.deviceWidth;<commit_msg>Improved extending the Alloy controllers<commit_after>var Alloy = require('alloy'),
	_ = Alloy._,
	Backbone = Alloy.Backbone;

/**
 * @class Framework
 * @singleton
 *
 * RebelFrame's general functions, defined as singleton
 */
var Framework = module.exports = {
	/**
	 * @property {String} LOGGEDOUT User has installed App, but not yet loggedin
	 */
	LOGGEDOUT: 'loggedout',
	/**
	 * @property {String} LOGGEDOUT User is loggedin, but has not yet activated
	 */
	LOGGEDIN: 'loggedin',
	/**
	 * @property {String} ACTIVATED User is loggedin, activated and ready to use all the features
	 */
	ACTIVATED: 'activated',

	/**
	 * @property {Number} deviceWidth Width of the device in dip. We need to devide by the logicalDensityFactor on Android to get the dip
	 */
	deviceWidth: OS_IOS ? Ti.Platform.displayCaps.platformWidth : Ti.Platform.displayCaps.platformWidth / Ti.Platform.displayCaps.logicalDensityFactor,

	/**
	 * @property {Number} deviceHeight Height of the device in dip. We need to devide by the logicalDensityFactor on Android to get the dip
	 */
	deviceHeight: OS_IOS ? Ti.Platform.displayCaps.platformHeight : Ti.Platform.displayCaps.platformHeight / Ti.Platform.displayCaps.logicalDensityFactor,

	/**
	 * Sets the status of this app
	 *
	 * @param {String} status The status to set
	 */
	setStatus: function(status) {
		_status = status;
		Ti.App.Properties.setString(Ti.App.id + '.status', status);
	},

	/**
	 * Returns the status of this app
	 *
	 * @return {String} The status
	 */
	getStatus: function() {
		if (_status)
			return _status;

		return Ti.App.Properties.getString(Ti.App.id + '.status', Framework.LOGGEDOUT);
	},

	/**
	 * Extend Alloy's createController en createWidget functions to call construct function when controller is initialized.
	 */
	extendAlloy: function() {
		// Wrap some Alloy functions, so they call construct and destruct methods.
		var _alloy_createController = Alloy.createController,
			_alloy_createModel = Alloy.createModel,
			_alloy_createCollection = Alloy.createCollection,
			_alloy_createWidget = Alloy.createWidget,
			WM = require('RebelFrame/WindowManager');

		/**
		 * Adds openWin en closeWin functions to Alloy Controllers. This way it is easy to call $.openWin() and $.closeWin and are needed close eventlisteners automatically added and removed.
		 *
		 * @param {Alloy.Controller} controller Controller to add the functions to
		 */
		var addFunctions = function(controller, config) {

			_.extend(controller, {
				/**
				 * Opens the Window. Also adds a `clse` eventListener, to clean up the controller when the Window is closed.
				 *
				 * @param  {Ti.UI.Window} [win] Window to open. If not provided, the Controller's top level view is used.
				 */
				openWin: function(win) {
					win = win || controller.getView();

					if (!_.isUndefined(config.showSideMenu))
						win.showSideMenu = config.showSideMenu;

					// Open the window
					WM.openWin(win);

					/**
					 * Handle `close` event of Window. Removes eventlistener and calls both RebelFrame's destruct as Alloy's destroy functions.
					 *
					 * @param  {Object} evt Event details
					 */
					function onCloseWin(evt) {
						this.removeEventListener('close', onCloseWin);

						if (controller.destruct) {
							Ti.API.debug('destruct() called');
							controller.destruct.call(controller, evt);
						} else
							Ti.API.warn('destruct() NOT called');

						controller.destroy.call(controller, evt);

						// Cleanup possible panning:
						evt.source.keyboardPanning = false;
					}

					win.addEventListener('close', onCloseWin);
				},

				/**
				 * Close the Window
				 *
				 * @param  {Ti.UI.Window} [win] Window to close. If not provided, the Controller's top level view is used.
				 */
				closeWin: function(win) {
					win = win || controller.getView();

					WM.closeWin(win);
				}
			});
		};

		/**
		 * Call original Alloy.createController function and then construct is it exists. Also track this new screen in Google Analytics
		 *
		 * @param  {String} name Controller name
		 * @param  {Object} config Controller configuration
		 *
		 * @return {Alloy.controller} Created controller, extended with RebelFrame fucntions
		 */
		Alloy.createController = function(name, config) {
			config = config || {};

			// Create controller using Alloy's original function
			var controller = _alloy_createController(name, config);

			// Add custom RebelFrame functions to controller
			addFunctions(controller, config);

			// Call constructor, if exists
			if (controller.construct)
				controller.construct.call(controller, config || {});

			// Track screen
			if (name !== 'index')
				tracker.trackScreen(name);

			return controller;
		};

		/**
		 * Call original Alloy.createWidget function and then construct is it exists. Also track this new screen in Google Analytics
		 *
		 * @param  {String} name Controller name
		 * @param  {Object} config Controller configuration
		 *
		 * @return {Alloy.controller} Created controller, extended with RebelFrame fucntions
		 */
		Alloy.createWidget = function(name, controller, config) {
			config = config || {};

			// Create controller using Alloy's original function
			var widget = _alloy_createWidget(name, controller, config);

			// Also support name, config as arguments, leaving out controller, but do this only after calling the original method.
			// Copied from Alloy.js definition
			if ("undefined" != typeof controller && null !== controller && _.isObject(controller) && !_.isString(controller)) {
				config = controller;
			}

			// Add custom RebelFrame functions to controller
			addFunctions(widget, config);

			// Call constructor, if exists
			if (widget.construct)
				widget.construct.call(widget, config || {});

			return widget;
		};
	},

	/**
	 * Scan the contents of the supplied object
	 *
	 * For debugging purposes only
	 *
	 * @param {Object} obj The object to scan
	 * @private
	 */
	scan: function(obj) {
		var key, type;

		Ti.API.error('Contents of object:');
		for (key in obj) {
			type = typeof(obj[key]);
			if (type != 'object' && type != 'function')
				Ti.API.error(' - ' + key + ': ' + type + ' (' + obj[key] + ')');
			else
				Ti.API.error(' - ' + key + ': ' + type);
		}
	}
};

/**
 * {String} The status of this app (i.e. loggedout, loggedin, activated)
 * @private
 */
var _status;

// Create some basic globals that can be used in TSS
Alloy.Globals.screenHeight = Framework.deviceHeight - 64;
Alloy.Globals.screenWidth = Framework.deviceWidth;<|endoftext|>