From 8525477b2670db6da831f33f53ff6af427844859 Mon Sep 17 00:00:00 2001
From: nathangray <nathangray.bsc+github@gmail.com>
Date: Wed, 12 Feb 2020 13:21:05 -0700
Subject: [PATCH] Avoid error if AJAX call returns after destruction

---
 api/js/etemplate/et2_widget_link.js |  7 ++-
 api/js/etemplate/et2_widget_link.ts | 95 ++++++++++++++++++-----------
 2 files changed, 64 insertions(+), 38 deletions(-)

diff --git a/api/js/etemplate/et2_widget_link.js b/api/js/etemplate/et2_widget_link.js
index 7dd37fba91..04f7ee3ed0 100644
--- a/api/js/etemplate/et2_widget_link.js
+++ b/api/js/etemplate/et2_widget_link.js
@@ -1247,8 +1247,11 @@ var et2_link_string = /** @class */ (function (_super) {
     };
     et2_link_string.prototype.set_value = function (_value) {
         // Get data
-        if (!_value || _value == null) {
-            this.list.empty();
+        if (!_value || _value == null || !this.list) {
+            // List can be missing if the AJAX call returns after the form is destroyed
+            if (this.list) {
+                this.list.empty();
+            }
             return;
         }
         if (typeof _value == "string" && _value.indexOf(',') > 0) {
diff --git a/api/js/etemplate/et2_widget_link.ts b/api/js/etemplate/et2_widget_link.ts
index ffe763bb52..a4c8fb394d 100644
--- a/api/js/etemplate/et2_widget_link.ts
+++ b/api/js/etemplate/et2_widget_link.ts
@@ -10,23 +10,23 @@
  * @version $Id$
  */
 
-/*egw:uses
-	/vendor/bower-asset/jquery/dist/jquery.js;
-	/vendor/bower-asset/jquery-ui/jquery-ui.js;
-	et2_core_inputWidget;
-	et2_core_valueWidget;
+	/*egw:uses
+		/vendor/bower-asset/jquery/dist/jquery.js;
+		/vendor/bower-asset/jquery-ui/jquery-ui.js;
+		et2_core_inputWidget;
+		et2_core_valueWidget;
 
-	// Include menu system for list context menu
-	egw_action.egw_menu_dhtmlx;
-*/
+		// Include menu system for list context menu
+		egw_action.egw_menu_dhtmlx;
+	*/
 
-import {WidgetConfig, et2_widget, et2_register_widget} from "./et2_core_widget";
-import {ClassWithAttributes} from "./et2_core_inheritance";
-import {et2_valueWidget} from "./et2_core_valueWidget";
-import {et2_inputWidget} from "./et2_core_inputWidget";
-import {et2_selectbox} from "./et2_widget_selectbox";
-import {et2_button} from "./et2_widget_button";
-import {et2_vfs_select} from "./et2_widget_vfs";
+	import {et2_register_widget, et2_widget, WidgetConfig} from "./et2_core_widget";
+	import {ClassWithAttributes} from "./et2_core_inheritance";
+	import {et2_valueWidget} from "./et2_core_valueWidget";
+	import {et2_inputWidget} from "./et2_core_inputWidget";
+	import {et2_selectbox} from "./et2_widget_selectbox";
+	import {et2_button} from "./et2_widget_button";
+	import {et2_vfs_select} from "./et2_widget_vfs";
 
 	/**
  * UI widgets for Egroupware linking system
@@ -1553,9 +1553,13 @@ export class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM
 	set_value( _value)
 	{
 		// Get data
-		if(!_value || _value == null)
+		if(!_value || _value == null || !this.list)
 		{
-			this.list.empty();
+			// List can be missing if the AJAX call returns after the form is destroyed
+			if (this.list)
+			{
+				this.list.empty();
+			}
 			return;
 		}
 		if(typeof _value == "string" && _value.indexOf(',') > 0)
@@ -1574,7 +1578,8 @@ export class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM
 			return;
 		}
 		this.list.empty();
-		if(typeof _value == 'object' && _value.length > 0) {
+		if(typeof _value == 'object' && _value.length > 0)
+		{
 			// Have full info
 			// Don't store new value, just update display
 
@@ -1666,7 +1671,8 @@ export class et2_link_string extends et2_valueWidget implements et2_IDetachedDOM
 		if(_link_data.title) link.text(_link_data.title);
 
 		// Now that link is created, get title from server & update
-		if(!_link_data.title) {
+		if(!_link_data.title)
+		{
 			this.egw().link_title(_link_data.app, _link_data.id, function(title) {
 				if (title)
 					this.removeClass("loading").text(title);
@@ -1792,11 +1798,13 @@ export class et2_link_list extends et2_link_string
 		// Set up context menu
 		var self = this;
 		this.context = new egwMenu();
-		this.context.addItem("comment", this.egw().lang("Comment"), "", function() {
+		this.context.addItem("comment", this.egw().lang("Comment"), "", function()
+		{
 			var link_id = typeof self.context.data.link_id == 'number' ? self.context.data.link_id : self.context.data.link_id.replace(/[:\.]/g,'_');
 
 			et2_dialog.show_prompt(
-				function(button, comment) {
+				function(button, comment)
+				{
 					if(button != et2_dialog.OK_BUTTON) return;
 					var remark = jQuery('#link_'+(self.context.data.dom_id ? self.context.data.dom_id : link_id), self.list).children('.remark');
 					if(isNaN(self.context.data.link_id))	// new entry, not yet stored
@@ -1807,8 +1815,10 @@ export class et2_link_list extends et2_link_string
 						{
 							var _widget = link_id.widget || null;
 							self.getRoot().iterateOver(
-								function(widget) {
-									if(widget.id == self.id) {
+								function(widget)
+								{
+									if(widget.id == self.id)
+									{
 										_widget = widget;
 									}
 								},
@@ -1825,7 +1835,8 @@ export class et2_link_list extends et2_link_string
 					remark.addClass("loading");
 					var request = egw.json("EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_comment",
 						[link_id, comment],
-						function() {
+						function()
+						{
 							if(remark)
 							{
 								// Append "" to make sure it's a string, not undefined
@@ -1841,7 +1852,8 @@ export class et2_link_list extends et2_link_string
 			);
 
 		});
-		this.context.addItem("file_info", this.egw().lang("File information"), this.egw().image("edit"), function(menu_item) {
+		this.context.addItem("file_info", this.egw().lang("File information"), this.egw().image("edit"), function(menu_item)
+		{
 			var link_data = self.context.data;
 			if(link_data.app == 'file')
 			{
@@ -1856,7 +1868,8 @@ export class et2_link_list extends et2_link_string
 			}
 		});
 		this.context.addItem("-", "-");
-		this.context.addItem("save", this.egw().lang("Save as"), this.egw().image('save'), function(menu_item) {
+		this.context.addItem("save", this.egw().lang("Save as"), this.egw().image('save'), function(menu_item)
+		{
 			var link_data = self.context.data;
 			// Download file
 			if(link_data.download_url)
@@ -1886,7 +1899,8 @@ export class et2_link_list extends et2_link_string
 
 			self.egw().open(link_data, "", "view",'download',link_data.target ? link_data.target : link_data.app,link_data.app);
 		});
-		this.context.addItem("zip", this.egw().lang("Save as Zip"), this.egw().image('save_zip'), function(menu_item) {
+		this.context.addItem("zip", this.egw().lang("Save as Zip"), this.egw().image('save_zip'), function(menu_item)
+		{
 			// Highlight files for nice UI indicating what will be in the zip.
 			// Files have negative IDs.
 			jQuery('[id^="link_-"]',this.list).effect('highlight',{},2000);
@@ -1899,7 +1913,8 @@ export class et2_link_list extends et2_link_string
 			});
 		});
 		this.context.addItem("-", "-");
-		this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), function(menu_item) {
+		this.context.addItem("delete", this.egw().lang("Delete link"), this.egw().image("delete"), function(menu_item)
+		{
 			var link_id = isNaN(self.context.data.link_id) ? self.context.data : self.context.data.link_id;
 			var row = jQuery('#link_'+(self.context.data.dom_id ? self.context.data.dom_id : self.context.data.link_id), self.list);
 			et2_dialog.show_dialog(
@@ -2025,7 +2040,8 @@ export class et2_link_list extends et2_link_string
 		var columns = ['title','remark'];
 
 		var self = this;
-		for(var i = 0; i < columns.length; i++) {
+		for(var i = 0; i < columns.length; i++)
+		{
 			var $td = jQuery(document.createElement("td"))
 				.appendTo(row)
 				.addClass(columns[i])
@@ -2050,7 +2066,8 @@ export class et2_link_list extends et2_link_string
 					}
 					else
 					{
-						if( !self.options.target_app ){
+						if( !self.options.target_app )
+						{
 							self.options.target_app = _link_data.app;
 						}
 						self.egw().open(_link_data, "", "view",null,_link_data.target ? _link_data.target : _link_data.app,self.options.target_app);
@@ -2063,7 +2080,8 @@ export class et2_link_list extends et2_link_string
 		{
 			// Title will be fetched from server and then set
 			jQuery('td.title',row).addClass("loading");
-			var title = this.egw().link_title(_link_data.app, _link_data.id, function(title) {
+			var title = this.egw().link_title(_link_data.app, _link_data.id, function(title)
+			{
 				jQuery('td.title',this).removeClass("loading").text(title+"");
 			}, row);
 		}
@@ -2089,7 +2107,8 @@ export class et2_link_list extends et2_link_string
 				.appendTo(delete_button)
 				// We don't use ui-icon because it assigns a bg image
 				.addClass("delete icon")
-				.bind( 'click', function() {
+				.bind( 'click', function()
+				{
 					et2_dialog.show_dialog(
 						function(button) {
 							if(button == et2_dialog.YES_BUTTON)
@@ -2105,7 +2124,8 @@ export class et2_link_list extends et2_link_string
 				});
 		}
 		// Context menu
-		row.bind("contextmenu", function(e) {
+		row.bind("contextmenu", function(e)
+		{
 			// Comment only available if link_id is there and not readonly
 			self.context.getItem("comment").set_enabled(typeof _link_data.link_id != 'undefined' && !self.options.readonly);
 			// File info only available for existing files
@@ -2127,7 +2147,8 @@ export class et2_link_list extends et2_link_string
 		// // Unfortunately, dragging files is currently only supported by Chrome
 		if(navigator && navigator.userAgent.indexOf('Chrome') >= 0)
 		{
-			row.on("dragstart", _link_data, function(event) {
+			row.on("dragstart", _link_data, function(event)
+			{
 				if(event.dataTransfer == null) {
 					return;
 				}
@@ -2175,7 +2196,8 @@ export class et2_link_list extends et2_link_string
 
 				event.dataTransfer.setDragImage(div.get(0),0,0);
 			})
-			.on('drag', function() {
+			.on('drag', function()
+			{
 				jQuery('#drag_helper',self.list).remove();
 			});
 		}
@@ -2211,7 +2233,8 @@ export class et2_link_list extends et2_link_string
 				var _widget = link_id.widget || null;
 				this.getRoot().iterateOver(
 					function(widget) {
-						if(widget.id == self.id) {
+						if(widget.id == self.id)
+						{
 							_widget = widget;
 						}
 					},