PageSourceSearch

https://erus.ru/Content/js/jq.jr.request-1.0.js

js erus.ru collected 2026-10-07 19:34:11 UTC 18,771 bytes, 633 lines download raw bytes

1(function ($) {
2	$.ETC = $.ETC || {};
3	$.ETC.JR = $.ETC.JR || {};
4	// Редактор JSON запроса для отчётов
5	$.ETC.JR.RequestEditor = function(frm, args)
6	{
7		jsonEnumAction = args.JsonEnumAction;
8		jsonFieldLookupUrl = args.JsonFieldLookupUrl;
9
10		var objectPluralName = args.ObjectPluralName;
11		nextone = nextone.replace(/{name}/, objectPluralName);
12		nextf = nextf.replace(/{name}/, objectPluralName);
13		nextm = nextm.replace(/{name}/, objectPluralName);
14
15		var table = $(args.TableId);
16		var td = $('TD.next.add', table)[0];
17
18		$.ajax({
19			url:		'/Content/htm/jr.ceditor.t.htm',
20			success:	function(data, textStatus, jqXHR){ et = data; templateLoaded = true; },
21			error:		function(jqXHR, textStatus, errorThrown){ loadError = 'Шаблон не загружен: '+textStatus+' - '+errorThrown;},
22			dataType:	'text'
23		});
24		// Загружает реестр всех полей
25		$.ajax({
26			url:		args.JsonFieldsAction,
27			success:	function(data, textStatus, jqXHR) {jrFields = $.parseJSON(data); fieldsLoaded = true;},
28			error:		function(jqXHR, textStatus, errorThrown) {loadError = 'Реестр полей не загружен: '+textStatus+' - '+errorThrown;}
29		});
30		waitAndInit(frm, args);
31	}
32
33	function waitAndInit ( frm, args )
34	{
35		var table = $(args.TableId);
36		var td = $('TD.next.add', table)[0];
37
38		if (loadError == null && (!templateLoaded || !fieldsLoaded))
39		{
40			td.innerHTML = '<b>Ожидание загрузки...</b>';
41			setTimeout(function () { waitAndInit(frm,args); }, 500);
42			return;
43		}
44		else if (loadError != null)
45		{
46			$.Dm.Notify({ html: 'Работа с редактором невозможна: ' + loadError, timeout: 30000, css: 'error' });
47			return;
48		}
49		else
50		{
51			td.innerHTML = nextone;
52		}
53
54		frm.container.on('FieldLookupChanged', OnFieldLookupChanged);
55		var id = 0;
56		jsonInputId = args.JsonInputId;
57		var jsonRequest = $(jsonInputId).val();
58		if (jsonRequest)
59		{
60			var model = $.parseJSON(jsonRequest);
61			if (model)
62			{
63				for (var a = 0; a < model.length; a++)
64					id = addAndBefore(id, td, model[a]);
65			}
66		}
67		// Обработка добавления/удаления условия
68		table.click(function (e) {
69			e.preventDefault();
70			e.stopPropagation();
71
72			var td = findAddKey(e.target);
73
74			if (td != null && td.length == 1) {
75				$(document).click();
76				$.Dm.Notify.close();
77
78				var ecnt = _getemptycount(table);
79				if (ecnt > 0) {
80					$.Dm.Notify({ html: ecnt > 1 ? 'В запросе уже есть незаполненные условия поиска.' : 'В запросе уже есть незаполненное условие поиска.', css: 'error' });
81				} else if (td.hasClass('next'))
82					id = addAndBefore(id, td[0], null);
83				else if (td.hasClass('or'))
84					id = addOrBefore(id, td[0], null);
85			}
86		});
87
88		var serializeButton = $(args.ButtonSelector);
89		serializeButton.click(function (e) {
90			if (!generateJson(table[0], $(e.target))) {
91				$(document).click();
92				e.preventDefault();
93				e.stopPropagation();
94				return false;
95			}
96		});
97		serializeButton.attr('disabled', null);
98	}
99
100	// URL для Json запросов
101	var jsonEnumAction;
102	var jsonInputId;
103	var jsonFieldLookupUrl;
104	var loadError			= null;
105	// Шаблон редактора
106	var et					= 'Шаблон еще не загружен...';
107	var templateLoaded		= false;
108
109	// реестр всех полей
110	var fieldsLoaded		= false;
111	var jrFields			= [];
112
113	var nextone		= '<img src="/Content/img/arr-d-dn.png"/>Выбрать все {name}<button title="Добавить этап выбора">+</button>';
114	var nextf		= '<img src="/Content/img/arr-d-dn.png"/>Выбрать {name}, у которых<button title="Добавить этап выбора">+</button>';
115	var nextm		= '<img src="/Content/img/arr-d-dn.png"/>из них выбрать {name}, у которых<button title="Добавить этап выбора">+</button>';
116	var nextl		= '<img src="/Content/img/arr-d-dn.png"/><button title="Добавить этап выбора">+</button>';
117
118	// При выборе нового поля из реестра
119	function OnFieldLookupChanged(e, lookup)
120	{
121		var conditionSpan = $(e.target).siblings('span');
122		fieldTypeEditor(conditionSpan, lookup.Path);
123	}
124
125	// Функция поиска дескриптора поля по полному пути
126	function findFieldDescriptor(path)
127	{
128		for (var i = 0; i < jrFields.length; i++)
129		{
130			if (jrFields[i].Path == path)
131				return jrFields[i];
132		}
133		return null;
134	}
135
136	// Найти кнопку добавления условия
137	function findAddKey(el)
138	{
139		var frombutton			= false;
140		while(el!=null)
141		{
142			if(el.tagName == 'TABLE' || el.tagName == 'BODY')
143				return null;
144			if(!frombutton && el.tagName == 'BUTTON')
145			{
146				frombutton		= true;
147			} else if(frombutton && el.tagName == 'TD')
148			{
149				var $el			= $(el);
150				if($el.hasClass('add'))
151					return $el;
152			}
153			el					= el.parentNode;
154		}
155
156		return null;
157	}
158
159	// Проверить количество незаполненных условий
160	function _getemptycount(table)
161	{
162		var pa					= $('INPUT.forcountcheck' , table);
163		var res					= 0;
164		for(var i=0; i<pa.length; i++)
165		{
166			if($(pa[i]).val()=='')
167				res++;
168		}
169
170		return res;
171	}
172
173	// Добавить шаг поиска перед указанной ячейкой таблицы
174	function addAndBefore(id, td, modelAnd)
175	{
176		var row					= td.parentNode;
177		var table				= row.parentNode.parentNode;
178
179		if(row.rowIndex == table.rows.length)
180			row.cells[0].innerHTML	= nextl;
181		else
182			row.cells[0].innerHTML	= nextm;
183
184		var result				= {
185			crow				: null,
186			ccell				: null,
187			nrow				: null,
188			ncell				: null
189		};
190
191		// Добавляем ячейку со стрелкой НАД ячейкой с новым критерием
192		result.nrow				= table.insertRow(row.rowIndex);
193		result.ncell			= result.nrow.insertCell(td.cellIndex);
194		result.ncell.className		= 'next add';
195
196		if(row.rowIndex==1)
197			result.ncell.innerHTML	= nextf;
198		else
199			result.ncell.innerHTML	= nextm;
200
201		// Добавляем ячейку с редактором критерия НАД ячейкой со стрелкой
202		result.crow				= table.insertRow(row.rowIndex);
203		result.ccell			= result.crow.insertCell(td.cellIndex);
204		result.ccell.innerHTML	= '<table class="single"><tr><td class="or add"><button title="Добавить условие">+</button></td></tr></table>';
205
206		// Добавляем редактор условия
207		var cc					= $('TD', result.ccell)[0];
208		if(modelAnd==null)
209		{
210			id					= addOrBefore(id, cc, null);
211		} else
212		{
213			for (var o = 0; o < modelAnd.length; o++)
214			{
215				id			= addOrBefore(id, cc, modelAnd[o]);
216			}
217		}
218		return id;
219	}
220
221	// Добавить вариант поиска перед указанной ячейкой таблицы
222	function addOrBefore(id, td, condition)
223	{
224		var row				= td.parentNode;
225		var table			= row.parentNode.parentNode;
226		// Для начала вставим ячейку - пригодится
227		var cc				= table.rows[0].insertCell(td.cellIndex);
228
229		if(td.cellIndex>1)
230		{
231			// если у нас уже есть условия, то нужно написать "или" в отдельной ячейке
232			table.className	= 'complex';
233
234			cc.innerHTML = 'или';
235			cc.className	= 'space';
236			cc				= table.rows[0].insertCell(td.cellIndex);
237		}
238
239		// Добавляем редактор
240		return createEditor(id, cc, condition);
241	}
242
243	// Правит и заполняет шаблон редактора для выбранного типа поля
244	function fieldTypeEditor ( conditionSpan, path, condition )
245	{
246		var descriptor = findFieldDescriptor(path);
247		var typeClass = descriptor.Type.replace(/\?/, '');
248		conditionSpan.removeClass().addClass(typeClass);
249		switch(typeClass)
250		{
251		case 'string':
252			if ( condition )
253			{
254				conditionSpan.find('.for.string select').val(condition.Operator);
255				conditionSpan.find('.for.string #stringValue' + condition.id).val(condition.Argument);
256			}
257			break;
258
259		case 'decimal':
260		case 'int':
261		case 'long':
262			var numberSpan = conditionSpan.find('.for.int');
263			numberEditor(numberSpan, descriptor, condition, false);
264			break;
265
266		case 'DateTime':
267			var dateTimeSpan = conditionSpan.find('.for.DateTime');
268			numberEditor(dateTimeSpan, descriptor, condition, true);
269			break;
270
271		case 'bool':
272			if ( condition )
273				conditionSpan.find('.for.bool select').val(condition.Argument ? 1 : 0);
274			break;
275
276		case 'enum':
277			{
278			    var select = conditionSpan.find('span.for.enum select');
279			    var selected = condition ? condition.Argument : null;
280				$.ajax({
281					url:		jsonEnumAction + "?enumName=" + descriptor.Model,
282					success:	function(values, textStatus, jqXHR) {enumDropbox(select, values, selected);}
283				});
284			}
285			break;
286
287		case 'ETCDictionary':
288		case 'CodeDictionary':
289			dictionaryEditor(conditionSpan, descriptor, condition);
290			break;
291		}
292	}
293
294	// Правит и заполняет редактор числа
295	function numberEditor ( numberSpan, descriptor, condition, isDate )
296	{
297		if ( condition )
298		{
299			if ( condition.Operator == 'Between' )
300			{
301				var from = condition.From;
302				var to = condition.To;
303				if (isDate)
304				{
305					if (from != null)
306					{
307						var fromDate = new Date(from);
308						from = fromDate.toLocaleDateString("ru-Ru").replace(/[^ -~]/g,'');
309					}
310					if (to != null)
311					{
312						var toDate = new Date(to);
313						to = toDate.toLocaleDateString("ru-Ru").replace(/[^ -~]/g,'');
314					}
315				}
316				numberSpan.find('#from').val(from);
317				numberSpan.find('#to').val(to);
318			}
319		}
320		else
321		{	// пустая модель условия
322			numberSpan.find('#from').val(null);
323			numberSpan.find('#to').val(null);
324		}
325	}
326
327	// Правит и заполняет редактор справочника
328	function dictionaryEditor ( conditionSpan, descriptor, condition )
329	{
330		var dicLookup = conditionSpan.find('.for.ETCDictionary .lookup2');
331		var dicNameRE = /^[^,]+\.(\w+)(,|$)/;
332		var dicName = dicNameRE.exec(descriptor.Model)[1];
333		var lookupUrl = '/DIC/' + dicName + 'List/LookupTable';
334		if (descriptor.LookupParam != null)
335			lookupUrl += '?' + descriptor.LookupParam;
336		dicLookup.DmLookup2({
337			clear: null,
338			setUrl: lookupUrl});
339		// заполнение lookup значением из модели
340		if ( condition )
341			dicLookup.DmLookup2({val: condition.Argument});
342	}
343
344	// Отрисовывает dropbox для enum
345	function enumDropbox ( select, values, selected )
346	{
347		var options = [];
348		for (var item in values)
349		{
350			options.push('<option value="', item, '">', values[item], '</option>');
351		}
352		select.html(options.join('')).val(selected);
353	}
354
355	// Добавить редактор условия в указанную ячейку таблицы
356	function createEditor(id, td, condition)
357	{
358		id++;
359		td.className = 'condition';
360
361		// Возможно к нам может придти заполненная модель, а возможно не придет
362		condition			= condition || {};
363		condition.id		= id;
364		if(condition.Path)
365		{
366			// Если есть заполненная модель, то получаем нужные параметры
367			var field			= findFieldDescriptor(condition.Path);
368			if(!field)
369			{
370				$.Dm.Notify({ html: 'Нет описания для пути '+condition.Path, css: 'error' });
371			} else
372			{
373				condition.Text			= field.Name;
374				condition.Type			= field.Type;
375				condition.Model			= field.Model;
376			}
377		}
378
379		// Строим HTML по шаблону с заполнением нужных полей по моделе
380		td.innerHTML			= et.replace(/\{([a-z]+)\}/gi, function(m, p)
381		{
382			return condition[p] || '';
383		});
384		$("#FieldLookup" + id, td).data("url", jsonFieldLookupUrl);
385
386		// Инициализация календаря
387		$('.for.DateTime INPUT', td).DmCalendar();
388		if(condition.Path)
389			fieldTypeEditor($('#conditions' + id, td), condition.Path, condition);
390
391		// Операция удаления
392		$('.del', td).click(function() {delOr(td);});
393
394		return id;
395	}
396
397	function delOr(td)
398	{
399		$(document).click();
400		var row				= td.parentNode;
401		if(row.cells.length==2)
402		{
403			// Удаление последнего условия - удаление всей строки
404			delAnd(row.parentNode.parentNode.parentNode);
405			return;
406		}
407		var orindex			= td.cellIndex+(td.cellIndex==0?1:-1);
408		$(row.cells[orindex]).remove();
409		$(td).remove();
410	}
411
412	function delAnd(td)
413	{
414		var row				= td.parentNode;
415		var table			= row.parentNode.parentNode;
416		var delindex		= row.rowIndex;
417
418		$(table.rows[delindex+1]).remove();
419		$(table.rows[delindex]).remove();
420		row					= table.rows[delindex-1]
421		var cell			= row.cells[0];
422
423		if(table.rows.length==4)
424			cell.innerHTML	= nextone;
425		else if(row.rowIndex == table.rows.length-2)
426			cell.innerHTML	= nextl;
427	}
428
429	// Сериализует весь фильтр в json
430	function generateJson(table, button)
431	{
432		$.Dm.Notify.close();
433
434		var lastand				= table.rows.length;
435		var andindex			= 0;
436		var tor, lastor, orindex;
437		var valid				= true;
438		var serialRequest = [];
439
440		for(var a=1; a<lastand; a+=2)
441		{
442			tor					= table.rows[a].cells[0].childNodes[0];
443			lastor				= tor.rows[0].cells.length-1;
444			var conditionList	= [];
445			for(var o=0; o<lastor; o+=2)
446			{
447				valid = conditionJson(conditionList, tor.rows[0].cells[o]) && valid;
448			}
449			serialRequest.push(conditionList);
450			andindex++;
451		}
452
453		if (valid)
454		{
455			var json = JSON.stringify(serialRequest, null, '\t');
456			$(jsonInputId).val(json);
457		}
458		else
459		{
460			$.Dm.Notify({ html: 'В запросе есть некорректные элементы.', timeout: 30000, css: 'error' });
461		}
462
463		return valid;
464	}
465
466	// Сериализует условие в объект и добавляет в список
467	function conditionJson(conditionList, td)
468	{
469		td.className			= '';
470		var nid					= $('#nodeid', td).val();
471		var type				= $('#conditions'+nid, td).attr('class');
472
473		if(!type)
474		{
475			td.className		= 'error';
476			return false;
477		}
478
479		var values				= $('.for.'+type, td);
480		var serialCondition = {};
481		serialCondition.Path = $('#FieldPath'+nid, td).val();
482
483		switch(type)
484		{
485			case 'string':
486				var operator	= $('select', values).val();
487				var val			= $('#stringValue' + nid, values).val();
488				if(val=='')
489				{
490					td.className		= 'error';
491					return false;
492				}
493				serialCondition.Operator = operator;
494				serialCondition.Argument = val;
495				break;
496
497			case 'DateTime':
498			case 'long':
499			case 'int':
500			case 'decimal':
501				var fromValue = $('#from', values).val();
502				var toValue = $('#to', values).val();
503				if(fromValue=='' && toValue=='')
504				{
505					td.className		= 'error';
506					return false;
507				}
508				if(!_iscorrect(type, fromValue) || !_iscorrect(type, toValue))
509				{
510					td.className		= 'error';
511					return false;
512				}
513				if(!_checkinterval(fromValue, toValue, type=='DateTime'))
514				{
515					td.className		= 'error';
516					return false;
517				}
518				serialCondition.Operator = "Between";
519				if(fromValue!='')
520					serialCondition.From = type=='DateTime' ? _serializeRuDate(fromValue) : fromValue;
521				if(toValue!='')
522					serialCondition.To = type=='DateTime' ? _serializeRuDate(toValue) : toValue;
523				break;
524
525			case 'bool':
526				var val			= $('SELECT', values).val();
527				if(val!='0' && val!='1')
528				{
529					td.className		= 'error';
530					return false;
531				}
532
533				serialCondition.Operator = "Equal";
534				boolValue = val =='1' ? true : false;
535				serialCondition.Argument = boolValue;
536				break;
537
538			case 'enum':
539				var option = $('select :selected', values);
540				if(option.val()=='')
541				{
542					td.className		= 'error';
543					return false;
544				}
545
546				serialCondition.Operator = "Equal";
547				serialCondition.Argument = option.val();
548				break;
549
550			case 'ETCDictionary':
551			case 'CodeDictionary':
552				serialCondition.Operator = "Equal";
553				var lookup = {};
554				lookup.Key = $('#DictionaryKey' + nid, td).val();
555				lookup.Version = $('#DictionaryVersion' + nid, td).val();
556				lookup.DisplayText = $('#DictionaryDisplayText' + nid, td).val();
557				if (type == 'CodeDictionary')
558					lookup.Code = $('#DictionaryCode' + nid, td).val();
559				if (lookup.Key == '' || (type == 'CodeDictionary' && lookup.Code == ''))
560				{
561					td.className		= 'error';
562					return false;
563				}
564				serialCondition.Argument = lookup;
565				break;
566		}
567
568		conditionList.push(serialCondition);
569		return true;
570	}
571
572	function _iscorrect(type, val)
573	{
574		if(val=='')
575			return true;
576		switch(type)
577		{
578			case 'DateTime':
579				return (/^\d{1,2}\.\d{1,2}\.\d\d\d\d$/).test(val);
580			case 'long':
581			case 'decimal':
582				return (/^[-+]?\d+(\.\d+)?$/).test(val);
583		}
584		return true;
585	}
586
587	function _checkinterval(fromValue, toValue, isdate)
588	{
589		if(fromValue=='' || toValue=='')
590			return true;
591		fromValue = isdate?_date2num(fromValue):Number(fromValue);
592		toValue	= isdate?_date2num(toValue):Number(toValue);
593		return fromValue < toValue;
594	}
595
596	// Разделяет русскую дату на день, месяц, год
597	function _splitRuDate(ruDate)
598	{
599		if(ruDate == null)
600			return null;
601		var splitDate = ruDate.split('.');
602		if(splitDate.length!=3)
603			return null;
604		return splitDate;
605	}
606
607	// Сериализует русскую дату в строку международного формата с локальной полуночью,
608	// чтобы не было смещения часового пояса
609	function _serializeRuDate(ruDate)
610	{
611		var splitDate = _splitRuDate(ruDate);
612		if (splitDate == null)
613			return null;
614		return splitDate[2] + '-' + splitDate[1] + '-' + splitDate[0] + 'T00:00:00';
615	}
616
617	// Конвертирует русскую дату в дату (со смещением часового пояса)
618	function _ruDate2Date(ruDate)
619	{
620		var splitDate = _splitRuDate(ruDate);
621		if(splitDate == null)
622			return null;
623		return new Date(splitDate[2], Number(splitDate[1])-1, splitDate[0]);
624	}
625
626	function _date2num(ruDate)
627	{
628		date	= _ruDate2Date(ruDate);
629		if(date == null)
630			return null;
631		return date.getTime();
632	}
633})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.