<commit_before>(function(){

var ui = angular.module('axelor.ui');

ui.directive('uiHelpPopover', function() {
	
	function addRow(table, label, text, klass) {
		var tr = $('<tr></tr>').appendTo(table);
		if (label) {
			$('<th></th>').text(label + ':').appendTo(tr);
		}
		if (klass == null) {
			text = '<code>' + text + '</code>';
		}
		var td = $('<td></td>').html(text).addClass(klass).appendTo(tr);
		if (!label) {
			td.attr('colspan', 2);
		}
		return table;
	}
	
	function getHelp(scope, element, field, mode) {
		
		var text = field.help;
		var table = $('<table class="field-details"></table>');

		if (text) {
			text = text.replace(/\\n/g, '<br>');
			addRow(table, null, text, 'help-text');
		}
		
		if (mode != 'dev') {
			return table;
		}

		if (text) {
			addRow(table, null, '<hr noshade>', 'help-text');
		}
		
		var model = scope._model;
		if (model === field.target) {
			model = scope.$parent._model;
		}

		addRow(table, _t('Object'), model);
		addRow(table, _t('Field Name'), field.name);
		addRow(table, _t('Field Type'), field.serverType);
		
		if (field.type == 'text') {
			return table;
		}
		
		if (field.domain) {
			addRow(table, _t('Filter'), field.domain);
		}
		
		if (field.target) {
			addRow(table, _t('Reference'), field.target);
		}

		var value = scope.$eval('$$original.' + field.name);
		if (value && field.type === 'many-to-one') {
			value = value.id;
		}
		if (value && /-many$/.test(field.type)) {
			var length = value.length;
			value = _.first(value, 5);
			value = _.map(value, function(v){
				return v.id;
			});
			if (length > 5) {
				value.push('...');
			}
			value = value.join(', ');
		}

		addRow(table, _t('Orig. Value'), value);

		return table;
	}

	return function(scope, element, attrs) {
		var forWidget = attrs.forWidget || element.parents('[x-field]:first').attr('id');
		var field = scope.getViewDef(forWidget);
		if (field == null) {
			return;
		}
		var mode = scope.$eval('app.mode') || 'dev';
		if (!field.help && mode != 'dev') {
			return;
		}

		element.popover({
			html: true,
			delay: { show: 1000, hide: 100 },
			animate: true,
			trigger: 'hover',
			title: function() {
				return element.text();
			},
			content: function() {
				return getHelp(scope, element, field, mode);
			}
		});
	};
});

/**
 * The Label widget.
 *
 */
var LabelItem = {
	css: 'label-item',
	cellCss: 'form-label',
	transclude: true,
	template: '<label ui-help-popover ng-transclude></label>',
	link: function(scope, element, attrs, controller) {
			var field = scope.getViewDef(attrs.forWidget);
			if (field && field.required) {
				element.addClass('required');
			}
	}
};

/**
 * The Spacer widget.
 *
 */
var SpacerItem = {
	css: 'spacer-item',
	template: '<div>&nbsp;</div>'
};

/**
 * The Separator widget.
 *
 */
var SeparatorItem = {
	css: 'separator-item',
	showTitle: false,
	scope: {
		title: '@'
	},
	template: '<div><span style="padding-left: 4px;">{{title}}</span><hr style="margin: 4px 0;"></div>'
};

/**
 * The Static Text widget.
 *
 */
var StaticItem = {
	css: 'static-item',
	transclude: true,
	template: '<label ng-transclude></label>'
};

/**
 * The button widget.
 */
var ButtonItem = {
	css: 'button-item',
	transclude: true,
	template: '<button class="btn" type="button" ng-transclude></button>'
};

/**
 * The String widget.
 */
var StringItem = {
	css: 'string-item',
	template: '<input type="text">'
};

/**
 * The Email input widget.
 */
var EmailItem = {
	css: 'email-item',
	template: '<input type="email">'
};

/**
 * The Phone input widget.
 */
var PhoneItem = {
	css: 'phone-item',
	template: '<input type="tel">'
};

/**
 * The Integer input widget.
 */
var IntegerItem = {
	css: 'integer-item',
	require: '?ngModel',
	link: function(scope, element, attrs, model) {

		var props = scope.getViewDef(element),
			scale = props.scale || this.scale,
			format = "n" + (_.isUndefined(scale) ? 0 : scale);
		
		var options = {
			step: 1,
			numberFormat: format,
			spin: onSpin
		};
		
		model.$parsers.unshift(function(viewValue) {
            var isNumber = _.isNumber(viewValue) || isValid(viewValue);
            model.$setValidity('format', isNumber);
            return isNumber ? viewValue : undefined;
        });

		function isValid(text) {
			return _.isEmpty(text) || /^(-)?\d+(\.\d+)?$/.test(text);
		}
		
		function onSpin(event, ui) {
			
			var text = this.value,
				value = ui.value,
				orig = element.spinner('value'),
				parts, integer, decimal, min, max, dir = 0;

			event.preventDefault();
			
			if (!isValid(text)) {
				return false;
			}

			if (value < orig)
				dir = -1;
			if (value > orig)
				dir = 1;

			parts = text.split(/\./);
			integer = +parts[0];
			decimal = +parts[1];
			
			integer += dir;
			if (parts.length > 1) {
				value = integer + '.' + decimal;
			}
			
			value = +value;
			min = options.min;
			max = options.max;

			if (_.isNumber(min) && value < min)
				value = min;
			if (_.isNumber(max) && value > max)
				value = max;

			element.val(value);
			setTimeout(function(){
				scope.$apply(function(){
					model.$setViewValue(value);
				});
			});
		}

		if (props.minSize !== undefined)
			options.min = +props.minSize;
		if (props.maxSize !== undefined)
			options.max = +props.maxSize;

		setTimeout(function(){
			element.spinner(options);
			if (scope.isReadonly(element)) {
				element.spinner("disable");
			}
			element.on("on:attrs-change", function(event, data) {
				element.spinner(data.readonly ? "disable" : "enable");
			});
		});
	},
	template: '<input type="text">'
};

/**
 * The Decimal input widget.
 */
var DecimalItem = _.extend({}, IntegerItem, {
	css: 'decimal-item',
	step: 2
});

/**
 * The Boolean input widget.<commit_msg>Refactoring Integer and Decimal Item<commit_after>(function(){

var ui = angular.module('axelor.ui');

ui.directive('uiHelpPopover', function() {
	
	function addRow(table, label, text, klass) {
		var tr = $('<tr></tr>').appendTo(table);
		if (label) {
			$('<th></th>').text(label + ':').appendTo(tr);
		}
		if (klass == null) {
			text = '<code>' + text + '</code>';
		}
		var td = $('<td></td>').html(text).addClass(klass).appendTo(tr);
		if (!label) {
			td.attr('colspan', 2);
		}
		return table;
	}
	
	function getHelp(scope, element, field, mode) {
		
		var text = field.help;
		var table = $('<table class="field-details"></table>');

		if (text) {
			text = text.replace(/\\n/g, '<br>');
			addRow(table, null, text, 'help-text');
		}
		
		if (mode != 'dev') {
			return table;
		}

		if (text) {
			addRow(table, null, '<hr noshade>', 'help-text');
		}
		
		var model = scope._model;
		if (model === field.target) {
			model = scope.$parent._model;
		}

		addRow(table, _t('Object'), model);
		addRow(table, _t('Field Name'), field.name);
		addRow(table, _t('Field Type'), field.serverType);
		
		if (field.type == 'text') {
			return table;
		}
		
		if (field.domain) {
			addRow(table, _t('Filter'), field.domain);
		}
		
		if (field.target) {
			addRow(table, _t('Reference'), field.target);
		}

		var value = scope.$eval('$$original.' + field.name);
		if (value && field.type === 'many-to-one') {
			value = value.id;
		}
		if (value && /-many$/.test(field.type)) {
			var length = value.length;
			value = _.first(value, 5);
			value = _.map(value, function(v){
				return v.id;
			});
			if (length > 5) {
				value.push('...');
			}
			value = value.join(', ');
		}

		addRow(table, _t('Orig. Value'), value);

		return table;
	}

	return function(scope, element, attrs) {
		var forWidget = attrs.forWidget || element.parents('[x-field]:first').attr('id');
		var field = scope.getViewDef(forWidget);
		if (field == null) {
			return;
		}
		var mode = scope.$eval('app.mode') || 'dev';
		if (!field.help && mode != 'dev') {
			return;
		}

		element.popover({
			html: true,
			delay: { show: 1000, hide: 100 },
			animate: true,
			trigger: 'hover',
			title: function() {
				return element.text();
			},
			content: function() {
				return getHelp(scope, element, field, mode);
			}
		});
	};
});

/**
 * The Label widget.
 *
 */
var LabelItem = {
	css: 'label-item',
	cellCss: 'form-label',
	transclude: true,
	template: '<label ui-help-popover ng-transclude></label>',
	link: function(scope, element, attrs, controller) {
			var field = scope.getViewDef(attrs.forWidget);
			if (field && field.required) {
				element.addClass('required');
			}
	}
};

/**
 * The Spacer widget.
 *
 */
var SpacerItem = {
	css: 'spacer-item',
	template: '<div>&nbsp;</div>'
};

/**
 * The Separator widget.
 *
 */
var SeparatorItem = {
	css: 'separator-item',
	showTitle: false,
	scope: {
		title: '@'
	},
	template: '<div><span style="padding-left: 4px;">{{title}}</span><hr style="margin: 4px 0;"></div>'
};

/**
 * The Static Text widget.
 *
 */
var StaticItem = {
	css: 'static-item',
	transclude: true,
	template: '<label ng-transclude></label>'
};

/**
 * The button widget.
 */
var ButtonItem = {
	css: 'button-item',
	transclude: true,
	template: '<button class="btn" type="button" ng-transclude></button>'
};

/**
 * The String widget.
 */
var StringItem = {
	css: 'string-item',
	template: '<input type="text">'
};

/**
 * The Email input widget.
 */
var EmailItem = {
	css: 'email-item',
	template: '<input type="email">'
};

/**
 * The Phone input widget.
 */
var PhoneItem = {
	css: 'phone-item',
	template: '<input type="tel">'
};

/**
 * The Integer input widget.
 */
var IntegerItem = {
	css: 'integer-item',
	require: '?ngModel',
	link: function(scope, element, attrs, model) {

		var props = scope.getViewDef(element),
			scale = props.scale || this.scale,
			format = "n" + (_.isUndefined(scale) ? 0 : scale),
			self = this;
		
		var options = {
			step: 1,
			numberFormat: format,
			spin: onSpin
		};
		
		model.$parsers.unshift(function(viewValue) {
            var isNumber = _.isNumber(viewValue) || isValid(viewValue);
            model.$setValidity('format', isNumber);
            return isNumber ? viewValue : undefined;
        });
		
		element.change(function(e, ui) {
			updateModel(element.val())
		});

		function isValid(text) {

			if(self.isDecimal) { return  _.isEmpty(text) || /^(-)?\d+(\.\d+)?$/.test(text); }
			return  _.isEmpty(text) || /^\s*-?[0-9]*\s*$/.test(text);
			
		}
		
		function updateModel(value){
			
			if(!isValid(value)){
	            return ;
            }

			//Formatting value of decimal field : Apply scale
			if(self.isDecimal){
				
				parts = value.toString().split(/\./);
				integer = +parts[0];
				decimal = +parts[1];
				decimal_points = scale || 2; //Default scale : 2
				
				if(value == 0) {
					var decimals = "";
					for(var i=0;i<decimal_points;i++) decimals += "0";
					value = "0."+decimals;
				}
				else{
					var exponent = Math.pow(10,decimal_points);
					var num = Math.round((value * exponent)).toString();
					if(integer == 0){
						value = "0." + num.slice(-1*decimal_points)
					}
					else{
						value = num.slice(0,-1*decimal_points) + "." + num.slice(-1*decimal_points)
					}
				}
			
			}
			
			if (model.$viewValue === value)
				return;
			
			element.val(value);
			setTimeout(function(){
				scope.$apply(function(){
					model.$setViewValue(value);
				});
			});
			
		}
		
		function onSpin(event, ui) {
			
			var text = this.value,
				value = ui.value,
				orig = element.spinner('value'),
				parts, integer, decimal, min, max, dir = 0;

			event.preventDefault();
			
			if (!isValid(text)) {
				return false;
			}

			if (value < orig)
				dir = -1;
			if (value > orig)
				dir = 1;

			parts = text.split(/\./);
			integer = +parts[0];
			decimal = +parts[1];
			
			integer += dir;
			if (parts.length > 1) {
				value = integer + '.' + decimal;
			}
			
			value = +value;
			min = options.min;
			max = options.max;

			if (_.isNumber(min) && value < min)
				value = min;
			if (_.isNumber(max) && value > max)
				value = max;

			updateModel(value);
		}

		if (props.minSize !== undefined)
			options.min = +props.minSize;
		if (props.maxSize !== undefined)
			options.max = +props.maxSize;

		setTimeout(function(){
			element.spinner(options);
			if (scope.isReadonly(element)) {
				element.spinner("disable");
			}
			element.on("on:attrs-change", function(event, data) {
				element.spinner(data.readonly ? "disable" : "enable");
			});
		});
	},
	template: '<input type="text">'
};

/**
 * The Decimal input widget.
 */
var DecimalItem = _.extend({}, IntegerItem, {
	css: 'decimal-item',
	step: 2,
	isDecimal: true
});

/**
 * The Boolean input widget.
{'fraction_non_alphanumeric': 0.19768, 'fraction_numerical': 0.00496, 'mean_word_length': 2.9151268399624177, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6712619', 'n_tokens_mistral': 5217, 'n_tokens_neox': 4430, 'n_words': 980}