api/js/etemplate/et2_widget_historylog.js   F
last analyzed

Complexity

Total Complexity 111
Complexity/F 4.11

Size

Lines of Code 689
Function Count 27

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 397
dl 0
loc 689
rs 2
c 0
b 0
f 0
wmc 111
mnd 84
bc 84
fnc 27
bpm 3.1111
cpm 4.1111
noi 2

How to fix   Complexity   

Complexity

Complex classes like api/js/etemplate/et2_widget_historylog.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * EGroupware eTemplate2 - JS History log
3
 *
4
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
5
 * @package etemplate
6
 * @subpackage api
7
 * @link http://www.egroupware.org
8
 * @author Nathan Gray
9
 * @copyright 2012 Nathan Gray
10
 */
11
12
/*egw:uses
13
	/vendor/bower-asset/jquery/dist/jquery.js;
14
	/vendor/bower-asset/jquery-ui/jquery-ui.js;
15
	et2_core_valueWidget;
16
17
	// Include the grid classes
18
	et2_dataview;
19
*/
20
21
/**
22
 * eTemplate history log widget displays a list of changes to the current record.
23
 * The widget is encapsulated, and only needs the record's ID, and a map of
24
 * fields:widgets for display.
25
 *
26
 * It defers its initialization until the tab that it's on is selected, to avoid
27
 * wasting time if the user never looks at it.
28
 *
29
 * @augments et2_valueWidget
30
 */
31
var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable],
32
{
33
	createNamespace: true,
34
	attributes: {
35
		"value": {
36
			"name": "Value",
37
			"type": "any",
38
			"description": "Object {app: ..., id: ..., status-widgets: {}} where status-widgets is a map of fields to widgets used to display those fields"
39
		},
40
		"status_id":{
41
			"name": "status_id",
42
			"type": "string",
43
			"default": "status",
44
			"description": "The history widget is traditionally named 'status'.  If you name another widget in the same template 'status', you can use this attribute to re-name the history widget.  "
45
		},
46
		"columns": {
47
			"name": "columns",
48
			"type": "string",
49
			"default": "user_ts,owner,status,new_value,old_value",
50
			"description": "Columns to display.  Default is user_ts,owner,status,new_value,old_value"
51
		},
52
		"get_rows": {
53
			"name": "get_rows",
54
			"type": "string",
55
			"default": "EGroupware\\Api\\Storage\\History::get_rows",
56
			"description": "Method to get rows"
57
		}
58
	},
59
60
	legacyOptions: ["status_id"],
61
	columns: [
62
		{'id': 'user_ts', caption: 'Date', 'width': '120px', widget_type: 'date-time'},
63
		{'id': 'owner', caption: 'User', 'width': '150px', widget_type: 'select-account'},
64
		{'id': 'status', caption: 'Changed', 'width': '120px', widget_type: 'select'},
65
		{'id': 'new_value', caption: 'New Value', 'width': '50%'},
66
		{'id': 'old_value', caption: 'Old Value', 'width': '50%'}
67
	],
68
69
	TIMESTAMP: 0, OWNER: 1, FIELD: 2, NEW_VALUE: 3, OLD_VALUE: 4,
70
71
	/**
72
	 * Constructor
73
	 *
74
	 * @memberOf et2_historylog
75
	 */
76
	init: function() {
77
		this._super.apply(this, arguments);
78
		this.div = jQuery(document.createElement("div"))
79
			.addClass("et2_historylog");
80
81
		this.innerDiv = jQuery(document.createElement("div"))
82
			.appendTo(this.div);
83
	},
84
85
	set_status_id: function(_new_id) {
86
		this.options.status_id = _new_id;
87
	},
88
89
	doLoadingFinished: function() {
90
		this._super.apply(this, arguments);
91
92
		// Find the tab widget, if there is one
93
		var tabs = this;
94
		do {
95
			tabs = tabs._parent;
96
		} while (tabs != this.getRoot() && tabs._type != 'tabbox');
97
		if(tabs != this.getRoot())
98
		{
99
			// Find the tab index
100
			for(var i = 0; i < tabs.tabData.length; i++)
101
			{
102
				// Find the tab
103
				if(tabs.tabData[i].contentDiv.has(this.div).length)
104
				{
105
					// Bind the action to when the tab is selected
106
					var handler = function(e) {
107
						e.data.div.unbind("click.history");
108
						// Bind on click tap, because we need to update history size
109
						// after a rezise happend and history log was not the active tab
110
						e.data.div.bind("click.history",{"history": e.data.history, div: tabs.tabData[i].flagDiv}, function(e){
0 ignored issues
show
Bug introduced by
The variable i is changed as part of the for loop for example by i++ on line 100. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
111
							if(e.data.history && e.data.history.dynheight)
112
							{
113
								e.data.history.dynheight.update(function(_w, _h)
114
								{
115
									e.data.history.dataview.resize(_w, _h);
116
								});
117
							}
118
						});
119
120
						if (typeof e.data.history.dataview == "undefined")
121
						{
122
							e.data.history.finishInit();
123
							if(e.data.history.dynheight)
124
							{
125
								e.data.history.dynheight.update(function(_w, _h) {
126
									e.data.history.dataview.resize(_w, _h);
127
								});
128
						}
129
						}
130
131
					};
132
					tabs.tabData[i].flagDiv.bind("click.history",{"history": this, div: tabs.tabData[i].flagDiv}, handler);
133
134
					// Display if history tab is selected
135
					if(i == tabs.get_active_tab() && typeof this.dataview == 'undefined')
136
					{
137
						tabs.tabData[i].flagDiv.trigger("click.history");
138
					}
139
					break;
140
				}
141
			}
142
		}
143
		else
144
		{
145
			this.finishInit();
146
		}
147
	},
148
149
	/**
150
	 * Finish initialization which was skipped until tab was selected
151
	 */
152
	finishInit: function() {
153
		// No point with no ID
154
		if(!this.options.value || !this.options.value.id)
155
		{
156
			return;
157
		}
158
		this._filters = {
159
			record_id: this.options.value.id,
160
			appname: this.options.value.app,
161
			get_rows: this.options.get_rows
162
		};
163
164
		// Warn if status_id is the same as history id, that causes overlap and missing labels
165
		if(this.options.status_id === this.id)
166
		{
167
			this.egw().debug("warn", "status_id attribute should not be the same as historylog ID");
168
		}
169
		var _columns = typeof this.options.columns === "string" ?
170
			this.options.columns.split(',') : this.options.columns;
171
172
		// Create the dynheight component which dynamically scales the inner
173
		// container.
174
		this.div.parentsUntil('.et2_tabs').height('100%');
175
		var parent = this.get_tab_info();
176
		this.dynheight = new et2_dynheight(parent ? parent.contentDiv : this.div.parent(),
177
				this.innerDiv, 250
178
		);
179
180
		// Create the outer grid container
181
		this.dataview = new et2_dataview(this.innerDiv, this.egw());
182
		var dataview_columns = [];
183
		var _columns = typeof this.options.columns === "string" ?
184
			this.options.columns.split(',') : this.options.columns;
185
		for (var i = 0; i < this.columns.length; i++)
186
		{
187
			dataview_columns[i] = {
188
					"id": this.columns[i].id,
189
					"caption": this.columns[i].caption,
190
					"width":this.columns[i].width,
191
					"visibility":_columns.indexOf(this.columns[i].id) < 0 ?
192
						ET2_COL_VISIBILITY_INVISIBLE : ET2_COL_VISIBILITY_VISIBLE
193
			};
194
		}
195
		this.dataview.setColumns(dataview_columns);
196
197
		// Create widgets for columns that stay the same, and set up varying widgets
198
		this.createWidgets();
199
200
		// Create the gridview controller
201
		var linkCallback = function() {};
202
		this.controller = new et2_dataview_controller(null, this.dataview.grid,
203
			this, this.rowCallback, linkCallback, this,
204
			null
205
		);
206
207
		var total = typeof this.options.value.total !== "undefined" ?
208
			this.options.value.total : 0;
209
210
		// This triggers an invalidate, which updates the grid
211
		this.dataview.grid.setTotalCount(total);
212
213
		// Insert any data sent from server, so invalidate finds data already
214
		if(this.options.value.rows && this.options.value.num_rows)
215
		{
216
			this.controller.loadInitialData(
217
				this.options.value.dataStorePrefix,
218
				this.options.value.row_id,
219
				this.options.value.rows
220
			);
221
			// Remove, to prevent duplication
222
			delete this.options.value.rows;
223
			// This triggers an invalidate, which updates the grid
224
			this.dataview.grid.setTotalCount(total);
225
		}
226
		else
227
		{
228
			// Trigger the initial update
229
			this.controller.update();
230
		}
231
232
		// Write something inside the column headers
233
		for (var i = 0; i < this.columns.length; i++)
234
		{
235
			jQuery(this.dataview.getHeaderContainerNode(i)).text(this.columns[i].caption);
236
		}
237
238
		// Register a resize callback
239
		var self = this;
240
		jQuery(window).on('resize.' +this.options.value.app + this.options.value.id, function() {
241
			if (self && typeof self.dynheight != 'undefined') self.dynheight.update(function(_w, _h) {
242
				self.dataview.resize(_w, _h);
243
			});
244
		});
245
	},
246
247
	/**
248
	 * Destroys all
249
	 */
250
	destroy: function() {
251
		// Unbind, if bound
252
		if(this.options.value && !this.options.value.id)
253
		{
254
			jQuery(window).off('.' +this.options.value.app + this.options.value.id);
255
		}
256
257
		// Free the widgets
258
		for(var i = 0; i < this.columns.length; i++)
259
		{
260
			if(this.columns[i].widget) this.columns[i].widget.destroy();
261
		}
262
		for(var key in this.fields)
263
		{
264
			this.fields[key].widget.destroy();
265
		}
266
		if(this.diff) this.diff.widget.destroy();
267
268
		// Free the grid components
269
		if(this.dataview) this.dataview.free();
270
		if(this.rowProvider) this.rowProvider.free();
271
		if(this.controller) this.controller.free();
272
		if(this.dynheight) this.dynheight.free();
273
274
		this._super.apply(this, arguments);
275
	},
276
277
	/**
278
	 * Create all needed widgets for new / old values
279
	 */
280
	createWidgets: function() {
281
282
		// Constant widgets - first 3 columns
283
		for(var i = 0; i < this.columns.length; i++)
284
		{
285
			if(this.columns[i].widget_type)
286
			{
287
				// Status ID is allowed to be remapped to something else.  Only affects the widget ID though
288
				var attrs = {'readonly': true, 'id': (i == this.FIELD ? this.options.status_id : this.columns[i].id)};
289
				this.columns[i].widget = et2_createWidget(this.columns[i].widget_type, attrs, this);
290
				this.columns[i].widget.transformAttributes(attrs);
291
				this.columns[i].nodes = jQuery(this.columns[i].widget.getDetachedNodes());
292
			}
293
		}
294
295
		// Add in handling for links
296
		if(typeof this.options.value['status-widgets']['~link~'] == 'undefined')
297
		{
298
			this.columns[this.FIELD].widget.optionValues['~link~'] = this.egw().lang('link');
299
			this.options.value['status-widgets']['~link~'] = 'link';
300
		}
301
302
		// Add in handling for files
303
		if(typeof this.options.value['status-widgets']['~file~'] == 'undefined')
304
		{
305
			this.columns[this.FIELD].widget.optionValues['~file~'] = this.egw().lang('File');
306
			this.options.value['status-widgets']['~file~'] = 'vfs';
307
		}
308
309
		// Add in handling for user-agent & action
310
		if(typeof this.options.value['status-widgets']['user_agent_action'] == 'undefined')
311
		{
312
			this.columns[this.FIELD].widget.optionValues['user_agent_action'] = this.egw().lang('User-agent & action');
313
		}
314
315
		// Per-field widgets - new value & old value
316
		this.fields = {};
317
318
		var labels = this.columns[this.FIELD].widget.optionValues;
319
320
		// Custom fields - Need to create one that's all read-only for proper display
321
		var cf_widget = et2_createWidget('customfields', {'readonly':true}, this);
322
		cf_widget.loadFields();
323
		// Override this or it may damage the real values
324
		cf_widget.getValue = function() {return null;};
325
		for(var key in cf_widget.widgets)
326
		{
327
			// Add label
328
			labels[cf_widget.prefix + key] = cf_widget.options.customfields[key].label;
329
330
			// If it doesn't support detached nodes, just treat it as text
331
			if(cf_widget.widgets[key].getDetachedNodes)
332
			{
333
				var nodes = cf_widget.widgets[key].getDetachedNodes();
334
				for(var i = 0; i < nodes.length; i++)
335
				{
336
					if(nodes[i] == null) nodes.splice(i,1);
337
				}
338
339
				// Save to use for each row
340
				this.fields[cf_widget.prefix + key] = {
341
					attrs: cf_widget.widgets[key].options,
342
					widget: cf_widget.widgets[key],
343
					nodes: jQuery(nodes)
344
				};
345
			}
346
		}
347
		// Add all cf labels
348
		this.columns[this.FIELD].widget.set_select_options(labels);
349
350
		// From app
351
		for(var key in this.options.value['status-widgets'])
352
		{
353
			var attrs = jQuery.extend({'readonly': true, 'id': key}, this.getArrayMgr('modifications').getEntry(key));
354
			var field = attrs.type || this.options.value['status-widgets'][key];
355
			var options = null;
356
			var widget = this._create_widget(key, field, attrs, options);
357
			if(widget === null)
358
			{
359
				continue;
360
			}
361
			if(widget.instanceOf(et2_selectbox)) widget.options.multiple = true;
362
			widget.transformAttributes(attrs);
363
364
			// Save to use for each row
365
			var nodes = widget._children.length ? [] : jQuery(widget.getDetachedNodes());
366
			for(var i = 0; i < widget._children.length; i++)
367
			{
368
				nodes.push(jQuery(widget._children[i].getDetachedNodes()));
369
			}
370
			this.fields[key] = {
371
				attrs: attrs,
372
				widget: widget,
373
				nodes: nodes
374
			};
375
		}
376
		// Widget for text diffs
377
		var diff = et2_createWidget('diff', {}, this);
378
		this.diff = {
379
			widget: diff,
380
			nodes: jQuery(diff.getDetachedNodes())
381
		};
382
	},
383
384
	_create_widget(key, field, attrs, options)
385
	{
386
		var widget = null;
387
388
		// If field has multiple parts (is object) and isn't an obvious select box
389
		if(typeof field === 'object')
390
		{
391
			// Check for multi-part statuses needing multiple widgets
392
			var need_box = false;//!this.getArrayMgr('sel_options').getEntry(key);
393
			for(var j in field)
394
			{
395
				// Require widget to be a widget, to avoid invalid widgets
396
				// (and template, which is a widget and an infolog todo status)
397
				if(et2_registry[field[j]] && ['template'].indexOf(field[j]) < 0)// && (et2_registry[field[j]].prototype.instanceOf(et2_valueWidget))
398
				{
399
					need_box = true;
400
					break;
401
				}
402
			}
403
404
			if(need_box)
405
			{
406
				// Multi-part value needs multiple widgets
407
				widget = et2_createWidget('vbox', attrs, this);
408
				for(var i in field)
409
				{
410
					var type = field[i];
411
					var child_attrs = jQuery.extend({}, attrs);
412
					if(typeof type === 'object')
413
					{
414
						child_attrs['select_options'] = field[i];
415
						type = 'select';
416
					}
417
					else
418
					{
419
						delete child_attrs['select_options'];
420
					}
421
					child_attrs.id = i;
422
					var child = this._create_widget(i, type, child_attrs, options)
423
					widget.addChild(child);
424
					child.transformAttributes(child_attrs);
425
				}
426
			}
427
			else
428
			{
429
				attrs['select_options'] = field;
430
			}
431
		}
432
		// Check for options after the type, ex: link-entry:infolog
433
		else if (field.indexOf(':') > 0)
434
		{
435
			var options = field.split(':');
436
			field = options.shift();
437
		}
438
439
		if(widget === null)
440
		{
441
			widget = et2_createWidget(typeof field === 'string' ? field : 'select', attrs, this);
442
		}
443
444
		if(!widget.instanceOf(et2_IDetachedDOM))
445
		{
446
			this.egw().debug("warn", this, "Invalid widget " + field + " for " + key + ".  Status widgets must implement et2_IDetachedDOM.");
447
			return null;
448
		}
449
450
		// Parse / set legacy options
451
		if(options)
452
		{
453
			var mgr = this.getArrayMgr("content");
454
			for(var i = 0; i < options.length && i < widget.legacyOptions.length; i++)
455
			{
456
				// Not set
457
				if(options[i] === "") continue;
458
459
				var attr = widget.attributes[widget.legacyOptions[i]];
460
				var attrValue = options[i];
461
462
				// If the attribute is marked as boolean, parse the
463
				// expression as bool expression.
464
				if (attr.type === "boolean")
465
				{
466
					attrValue = mgr.parseBoolExpression(attrValue);
467
				}
468
				else
469
				{
470
					attrValue = mgr.expandName(attrValue);
471
				}
472
				attrs[widget.legacyOptions[i]] = attrValue;
473
				if(typeof widget['set_'+widget.legacyOptions[i]] === 'function')
474
				{
475
					widget['set_'+widget.legacyOptions[i]].call(widget, attrValue);
476
				}
477
				else
478
				{
479
					widget.options[widget.legacyOptions[i]] = attrValue;
480
				}
481
			}
482
		}
483
		return widget;
484
	},
485
486
	getDOMNode: function(_sender) {
487
		if (_sender == this)
488
		{
489
				return this.div[0];
490
		}
491
492
		for (var i = 0; i < this.columns.length; i++)
493
		{
494
			if (_sender == this.columns[i].widget)
495
			{
496
				return this.dataview.getHeaderContainerNode(i);
497
			}
498
		}
499
		return null;
500
	},
501
502
503
	dataFetch: function (_queriedRange, _callback, _context) {
504
		// Skip getting data if there's no ID
505
		if(!this.value.id) return;
506
507
		// Set num_rows to fetch via nextmatch
508
		if ( this.options.value['num_rows'] )
509
			_queriedRange['num_rows'] = this.options.value['num_rows'];
510
511
		var historylog = this;
512
		// Pass the fetch call to the API
513
		this.egw().dataFetch(
514
			this.getInstanceManager().etemplate_exec_id,
515
			_queriedRange,
516
			this._filters,
517
			this.id,
518
			function(_response) {
519
				_callback.call(this,_response);
520
				// This seems to prevent unwanted scrollbars
521
				historylog.div.hide();
522
				window.setTimeout(function() {
523
					historylog.div.show();
524
				}.bind(historylog),100);
0 ignored issues
show
unused-code introduced by
The call to bind does not seem necessary since the function does not use this. Consider calling it directly.
Loading history...
525
			},
526
			_context,
527
			[]
528
		);
529
	},
530
531
532
	// Needed by interface
533
	dataRegisterUID: function (_uid, _callback, _context) {
534
		this.egw().dataRegisterUID(_uid, _callback, _context, this.getInstanceManager().etemplate_exec_id,
535
                                this.id);
536
	},
537
538
	dataUnregisterUID: function (_uid, _callback, _context) {
539
		// Needed by interface
540
	},
541
542
	/**
543
	 * The row callback gets called by the gridview controller whenever
544
	 * the actual DOM-Nodes for a node with the given data have to be
545
	 * created.
546
	 *
547
	 * @param {type} _data
548
	 * @param {type} _row
549
	 * @param {type} _idx
550
	 * @param {type} _entry
551
	 */
552
	rowCallback: function(_data, _row, _idx, _entry) {
553
		var tr = _row.getDOMNode();
554
		jQuery(tr).attr("valign","top");
555
556
		var row = this.dataview.rowProvider.getPrototype("default");
557
		var self = this;
558
		jQuery("div", row).each(function (i) {
559
			var nodes = [];
560
			var widget = self.columns[i].widget;
561
			var value = _data[self.columns[i].id];
562
			if(self.OWNER === i && _data['share_email'])
563
			{
564
				// Show share email instead of owner
565
				widget = undefined;
566
				value = _data['share_email'];
567
			}
568
			// Get widget from list, unless it needs a diff widget
569
			if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined' && (
570
					i < self.NEW_VALUE ||
571
					i >= self.NEW_VALUE && (
572
						self.fields[_data.status].nodes || !self._needsDiffWidget(_data['status'], _data[self.columns[self.OLD_VALUE].id])
573
					)
574
			))
575
			{
576
				widget = self.fields[_data.status].widget;
577
				if(!widget._children.length)
578
				{
579
					nodes = self.fields[_data.status].nodes.clone();
580
				}
581
				for(var j = 0; j < widget._children.length; j++)
582
				{
583
					nodes.push(self.fields[_data.status].nodes[j].clone());
584
					if(widget._children[j].instanceOf(et2_diff))
585
					{
586
						self._spanValueColumns(jQuery(this));
587
					}
588
				}
589
			}
590
			else if (widget)
591
			{
592
				nodes = self.columns[i].nodes.clone();
593
			}
594
			else if ((
595
				// Already parsed & cached
596
				typeof _data[self.columns[self.NEW_VALUE].id] == "object" &&
597
				typeof _data[self.columns[self.NEW_VALUE].id] != "undefined" &&
598
				_data[self.columns[self.NEW_VALUE].id] !== null) ||	// typeof null === 'object'
599
				// Large old value
600
				self._needsDiffWidget(_data['status'], _data[self.columns[self.OLD_VALUE].id]) ||
601
				// Large new value
602
				self._needsDiffWidget(_data['status'], _data[self.columns[self.NEW_VALUE].id]))
603
			{
604
				// Large text value - span both columns, and show a nice diff
605
				var jthis = jQuery(this);
606
				if(i === self.NEW_VALUE)
607
				{
608
					// Diff widget
609
					widget = self.diff.widget;
610
					nodes = self.diff.nodes.clone();
611
612
					if(widget) widget.setDetachedAttributes(nodes, {
613
						value: value,
614
						label: jthis.parents("td").prev().text()
615
					});
616
617
					self._spanValueColumns(jthis);
618
				}
619
			}
620
			else
621
			{
622
				// No widget fallback - display actual value
623
				nodes = jQuery('<span>').text(value === null ? '' : value);
624
			}
625
			if(widget)
626
			{
627
				if(widget._children.length)
628
				{
629
					// Multi-part values
630
					var box = jQuery(widget.getDOMNode()).clone();
631
					for(var j = 0; j < widget._children.length; j++)
632
					{
633
						var id = widget._children[j].id;
634
						var widget_value = value ? value[id] || "" : "";
635
						widget._children[j].setDetachedAttributes(nodes[j], {value:widget_value});
636
						box.append(nodes[j]);
637
					}
638
					nodes = box;
639
				}
640
				else
641
				{
642
					widget.setDetachedAttributes(nodes, {value:value});
643
				}
644
			}
645
			jQuery(this).append(nodes);
646
		});
647
		jQuery(tr).append(row.children());
648
649
		return tr;
650
	},
651
652
	/**
653
	 * How to tell if the row needs a diff widget or not
654
	 *
655
	 * @param {string} columnName
656
	 * @param {string} value
657
	 * @returns {Boolean}
658
	 */
659
	_needsDiffWidget: function(columnName, value) {
660
		if(typeof value !== "string" && value)
661
		{
662
			this.egw().debug("warn", "Crazy diff value", value);
663
			return false;
664
		}
665
		return value === '***diff***';
666
	},
667
668
	/**
669
	 * Make a single row's new value cell span across both new value and old value
670
	 * columns.  Used for diff widget.
671
	 *
672
	 * @param {jQuery} row jQuery wrapped row node
673
	 */
674
	_spanValueColumns: function(row)
675
	{
676
		// Stretch column 4
677
		row.parents("td").attr("colspan", 2)
678
			.css("border-right", "none");
679
		row.css("width", (
680
				this.dataview.columnMgr.columnWidths[this.NEW_VALUE] +
681
				this.dataview.columnMgr.columnWidths[this.OLD_VALUE]-10)+'px');
682
683
		// Skip column 5
684
		row.parents("td").next().remove();
685
	},
686
687
	resize: function (_height)
688
	{
689
		if (typeof this.options != 'undefined' && _height
690
				&& typeof this.options.resize_ratio != 'undefined')
691
		{
692
			// apply the ratio
693
			_height = (this.options.resize_ratio != '')? _height * this.options.resize_ratio: _height;
694
			if (_height != 0)
695
			{
696
				// 250px is the default value for history widget
697
				// if it's not loaded yet and window is resized
698
				// then add the default height with excess_height
699
				if (this.div.height() == 0) _height += 250;
700
				this.div.height(this.div.height() + _height);
701
702
				// trigger the history registered resize
703
				// in order to update the height with new value
704
				this.div.trigger('resize.' +this.options.value.app + this.options.value.id);
705
			}
706
		}
707
		if(this.dynheight)
708
		{
709
			this.dynheight.update();
710
		}
711
		// Resize diff widgets to match new space
712
		if(this.dataview)
713
		{
714
			var columns = this.dataview.getColumnMgr().columnWidths;
715
			jQuery('.et2_diff', this.div).closest('.innerContainer').width(columns[this.NEW_VALUE] + columns[this.OLD_VALUE]);
716
		}
717
	}
718
});}).call(this);
719
et2_register_widget(et2_historylog, ['historylog']);
720