sdc_client 0.57.11 → 0.57.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/misc.xml +2 -2
- package/.idea/workspace.xml +125 -40
- package/SimpleDomControlClient.iml +5 -2
- package/dist/index.js +8 -8
- package/dist/ugly.index.js +1 -1
- package/package.json +1 -1
- package/src/simpleDomControl/AbstractSDC.js +9 -4
- package/src/simpleDomControl/sdc_controller.js +69 -17
- package/src/simpleDomControl/sdc_dom_events.js +1 -1
- package/src/simpleDomControl/sdc_main.js +361 -365
- package/src/simpleDomControl/sdc_socket.js +31 -10
- package/src/simpleDomControl/sdc_test_utils.js +34 -10
- package/src/simpleDomControl/sdc_utils.js +46 -24
- package/src/simpleDomControl/sdc_view.js +382 -206
- package/test/controller.test.js +1 -41
- package/test/dist.test.js +1 -1
- package/test/utils.js +89 -0
- package/test/view.test.js +158 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
controllerFactory,
|
|
3
|
+
prepareRefreshProcess,
|
|
4
|
+
runControlFlowFunctions,
|
|
5
|
+
updateEventAndTriggerOnRefresh
|
|
6
|
+
} from "./sdc_controller.js";
|
|
2
7
|
import {getUrlParam} from "./sdc_params.js";
|
|
3
8
|
import {app} from "./sdc_main.js";
|
|
4
9
|
import {trigger} from "./sdc_events.js";
|
|
@@ -14,7 +19,7 @@ export const CONTROLLER_CLASS = '_sdc_controller_';
|
|
|
14
19
|
|
|
15
20
|
|
|
16
21
|
export function cleanCache() {
|
|
17
|
-
|
|
22
|
+
htmlFiles = {};
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/**
|
|
@@ -29,29 +34,29 @@ export function cleanCache() {
|
|
|
29
34
|
* @return {Array} - a array of objects with all register tags found
|
|
30
35
|
*/
|
|
31
36
|
function findSdcTgs($container, tagNameList, parentController) {
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
if (!$container) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
let $children = $container.children();
|
|
41
|
+
let emptyList = [];
|
|
42
|
+
$children.each(function (_, element) {
|
|
43
|
+
let $element = $(element);
|
|
44
|
+
let tagName = $element.prop('tagName').toLowerCase().split('_');
|
|
45
|
+
if ($.inArray(tagName[0], tagNameList) >= 0) {
|
|
46
|
+
emptyList.push({
|
|
47
|
+
tag: tagName[0],
|
|
48
|
+
super: tagName.splice(1) || [],
|
|
49
|
+
dom: $element
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
} else if (tagName[0].startsWith('this.')) {
|
|
53
|
+
$element.addClass(`_bind_to_update_handler sdc_uuid_${parentController._uuid}`)
|
|
54
|
+
} else {
|
|
55
|
+
emptyList = emptyList.concat(findSdcTgs($element, tagNameList, parentController))
|
|
34
56
|
}
|
|
35
|
-
|
|
36
|
-
let emptyList = [];
|
|
37
|
-
$children.each(function (_, element) {
|
|
38
|
-
let $element = $(element);
|
|
39
|
-
let tagName = $element.prop('tagName').toLowerCase().split('_');
|
|
40
|
-
if ($.inArray(tagName[0], tagNameList) >= 0) {
|
|
41
|
-
emptyList.push({
|
|
42
|
-
tag: tagName[0],
|
|
43
|
-
super: tagName.splice(1) || [],
|
|
44
|
-
dom: $element
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
} else if (tagName[0].startsWith('this.')) {
|
|
48
|
-
$element.addClass(`_bind_to_update_handler sdc_uuid_${parentController._uuid}`)
|
|
49
|
-
} else {
|
|
50
|
-
emptyList = emptyList.concat(findSdcTgs($element, tagNameList, parentController))
|
|
51
|
-
}
|
|
52
|
-
});
|
|
57
|
+
});
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
return emptyList;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
/**
|
|
@@ -64,15 +69,15 @@ function findSdcTgs($container, tagNameList, parentController) {
|
|
|
64
69
|
* @returns {string} - the correct URL with prefix.
|
|
65
70
|
*/
|
|
66
71
|
function replacePlaceholderController(controller, url, urlValues) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
72
|
+
for (let key_idx in controller._urlParams) {
|
|
73
|
+
if (controller._urlParams.hasOwnProperty(key_idx)) {
|
|
74
|
+
let key = controller._urlParams[key_idx];
|
|
75
|
+
let re = RegExp("%\\(" + key + "\\)\\w", "gm");
|
|
76
|
+
url = url.replace(re, "" + urlValues.shift());
|
|
73
77
|
}
|
|
78
|
+
}
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
return url;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
/**
|
|
@@ -87,30 +92,30 @@ function replacePlaceholderController(controller, url, urlValues) {
|
|
|
87
92
|
* @returns {Promise<Boolean>} - waits for the file to be loaded.
|
|
88
93
|
*/
|
|
89
94
|
function loadHTMLFile(path, args, tag, hardReload) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
if (!path) {
|
|
96
|
+
return Promise.resolve(false);
|
|
97
|
+
} else if (htmlFiles[tag]) {
|
|
98
|
+
return Promise.resolve(htmlFiles[tag])
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
args.VERSION = app.VERSION;
|
|
102
|
+
args._method = 'content';
|
|
103
|
+
|
|
104
|
+
return $.get(path, args).then(function (data) {
|
|
105
|
+
if (!hardReload) {
|
|
106
|
+
htmlFiles[tag] = data;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return data;
|
|
105
|
-
}).catch(function (err) {
|
|
106
|
-
if (err.status === 301) {
|
|
107
|
-
const data = err.responseJSON;
|
|
108
|
-
trigger('_RedirectOnView', data['url-link']);
|
|
109
|
-
}
|
|
110
|
-
trigger('navLoaded', {'controller_name': () => err.status});
|
|
109
|
+
return data;
|
|
110
|
+
}).catch(function (err) {
|
|
111
|
+
if (err.status === 301) {
|
|
112
|
+
const data = err.responseJSON;
|
|
113
|
+
trigger('_RedirectOnView', data['url-link']);
|
|
114
|
+
}
|
|
115
|
+
trigger('navLoaded', {'controller_name': () => err.status});
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
throw `<sdc-error data-code="${err.status}">${err.responseText}</sdc-error>`;
|
|
118
|
+
});
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
/**
|
|
@@ -119,10 +124,11 @@ function loadHTMLFile(path, args, tag, hardReload) {
|
|
|
119
124
|
*
|
|
120
125
|
* @param {jquery} $container - given container
|
|
121
126
|
* @param {AbstractSDC} parentController - parent contoller surrounded the container
|
|
127
|
+
* @param {Object} process - Process object containing the refresh process
|
|
122
128
|
*/
|
|
123
|
-
function replaceAllTagElementsInContainer($container, parentController) {
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
function replaceAllTagElementsInContainer($container, parentController, process = null) {
|
|
130
|
+
parentController = parentController || $container.data(DATA_CONTROLLER_KEY);
|
|
131
|
+
return replaceTagElementsInContainer(app.tagNames, $container, parentController, process);
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
/**
|
|
@@ -133,25 +139,25 @@ function replaceAllTagElementsInContainer($container, parentController) {
|
|
|
133
139
|
* @returns {string} - the correct URL with prefix.
|
|
134
140
|
*/
|
|
135
141
|
function parseContentUrl(controller) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
142
|
+
let url = controller.contentUrl;
|
|
143
|
+
if (controller && controller._urlParams.length === 0) {
|
|
144
|
+
let re = /%\(([^)]+)\)\w/gm;
|
|
145
|
+
let matches;
|
|
146
|
+
controller._urlParams = [];
|
|
147
|
+
while ((matches = re.exec(url))) {
|
|
148
|
+
controller._urlParams.push(matches[1]);
|
|
149
|
+
controller.contentReload = true;
|
|
145
150
|
}
|
|
151
|
+
}
|
|
146
152
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
let params = getUrlParam(controller, controller.$container);
|
|
154
|
+
if (controller._urlParams.length) {
|
|
155
|
+
url = replacePlaceholderController(controller, url, params);
|
|
156
|
+
}
|
|
151
157
|
|
|
152
|
-
|
|
158
|
+
controller.parsedContentUrl = url;
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
return {url: url, args: params[params.length - 1]};
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
|
|
@@ -161,10 +167,10 @@ function parseContentUrl(controller) {
|
|
|
161
167
|
* @return {AbstractSDC}
|
|
162
168
|
*/
|
|
163
169
|
export function getController($elem) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
170
|
+
if ($elem.hasClass(CONTROLLER_CLASS)) {
|
|
171
|
+
return $elem.data(`${DATA_CONTROLLER_KEY}`);
|
|
172
|
+
}
|
|
173
|
+
return $elem.closest(`.${CONTROLLER_CLASS}`).data(`${DATA_CONTROLLER_KEY}`);
|
|
168
174
|
}
|
|
169
175
|
|
|
170
176
|
/**
|
|
@@ -179,26 +185,26 @@ export function getController($elem) {
|
|
|
179
185
|
* @returns {Promise<jQuery>} - the promise waits to the files are loaded. it returns the jQuery object.
|
|
180
186
|
*/
|
|
181
187
|
export function loadFilesFromController(controller) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
188
|
+
let getElements = {args: {}};
|
|
189
|
+
if (controller.contentUrl) {
|
|
190
|
+
getElements = parseContentUrl(controller, controller.contentUrl);
|
|
191
|
+
controller.contentUrl = getElements.url;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return Promise.all([
|
|
195
|
+
loadHTMLFile(controller.contentUrl, getElements.args, controller._tagName, controller.contentReload)
|
|
196
|
+
]).then(function (results) {
|
|
197
|
+
let htmlFile = results[0];
|
|
198
|
+
if (htmlFile) {
|
|
199
|
+
try {
|
|
200
|
+
return $(htmlFile);
|
|
201
|
+
} catch {
|
|
202
|
+
return $('<div></div>').append(htmlFile);
|
|
203
|
+
}
|
|
186
204
|
}
|
|
187
205
|
|
|
188
|
-
return
|
|
189
|
-
|
|
190
|
-
]).then(function (results) {
|
|
191
|
-
let htmlFile = results[0];
|
|
192
|
-
if (htmlFile) {
|
|
193
|
-
try {
|
|
194
|
-
return $(htmlFile);
|
|
195
|
-
} catch {
|
|
196
|
-
return $('<div></div>').append(htmlFile);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return null;
|
|
201
|
-
});
|
|
206
|
+
return null;
|
|
207
|
+
});
|
|
202
208
|
}
|
|
203
209
|
|
|
204
210
|
/**
|
|
@@ -212,15 +218,15 @@ export function loadFilesFromController(controller) {
|
|
|
212
218
|
* @returns {Promise<jQuery>} - the promise waits to the files are loaded. it returns the jQuery object.
|
|
213
219
|
*/
|
|
214
220
|
export function reloadHTMLController(controller) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
if (controller.contentUrl) {
|
|
222
|
+
let getElements = parseContentUrl(controller, controller.contentUrl);
|
|
223
|
+
controller.contentUrl = getElements.url;
|
|
224
|
+
return loadHTMLFile(controller.contentUrl, getElements.args, controller._tagName, controller.contentReload);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return new Promise(resolve => {
|
|
228
|
+
resolve($());
|
|
229
|
+
});
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
/**
|
|
@@ -229,18 +235,19 @@ export function reloadHTMLController(controller) {
|
|
|
229
235
|
* @param {string} tagName
|
|
230
236
|
* @param {Array<string>} superTagNameList
|
|
231
237
|
* @param {AbstractSDC} parentController
|
|
232
|
-
* @
|
|
238
|
+
* @param {Object} process - Process object containing the refresh process
|
|
239
|
+
* @returns {Promise}
|
|
233
240
|
*/
|
|
234
|
-
function runReplaceTagElementsInContainer($element, tagName, superTagNameList, parentController) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
function runReplaceTagElementsInContainer($element, tagName, superTagNameList, parentController, process) {
|
|
242
|
+
let controller = $element.data(DATA_CONTROLLER_KEY);
|
|
243
|
+
if (controller) {
|
|
244
|
+
return replaceAllTagElementsInContainer($element, controller, process);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
controller = controllerFactory(parentController, $element, tagName, superTagNameList);
|
|
248
|
+
$element.data(DATA_CONTROLLER_KEY, controller);
|
|
249
|
+
$element.addClass(CONTROLLER_CLASS);
|
|
250
|
+
return runControlFlowFunctions(controller, process);
|
|
244
251
|
}
|
|
245
252
|
|
|
246
253
|
|
|
@@ -250,19 +257,20 @@ function runReplaceTagElementsInContainer($element, tagName, superTagNameList, p
|
|
|
250
257
|
*
|
|
251
258
|
* @param {AbstractSDC} controller - js controller instance
|
|
252
259
|
* @param {jquery} $html - jQuery loaded content
|
|
260
|
+
* @param {Object} process - Process object containing the refresh process
|
|
253
261
|
* @return {Promise}
|
|
254
262
|
*/
|
|
255
|
-
export function runControllerFillContent(controller, $html) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
controller.$container.append($html);
|
|
263
|
+
export function runControllerFillContent(controller, $html, process = null) {
|
|
264
|
+
if ($html && $html.length > 0) {
|
|
265
|
+
controller.$container.empty();
|
|
266
|
+
controller.$container.attr(controller._tagName, '');
|
|
267
|
+
for (let mixinKey in controller._mixins) {
|
|
268
|
+
controller.$container.attr(controller._mixins[mixinKey]._tagName, '');
|
|
263
269
|
}
|
|
270
|
+
controller.$container.append($html);
|
|
271
|
+
}
|
|
264
272
|
|
|
265
|
-
|
|
273
|
+
return replaceAllTagElementsInContainer(controller.$container, controller, process);
|
|
266
274
|
}
|
|
267
275
|
|
|
268
276
|
|
|
@@ -275,123 +283,291 @@ export function runControllerFillContent(controller, $html) {
|
|
|
275
283
|
* @param {Array<string>} tagList - list of all registered tags
|
|
276
284
|
* @param {jquery} $container - jQuery container to find the tags
|
|
277
285
|
* @param {AbstractSDC} parentController - controller in surrounding
|
|
286
|
+
* @param {Object} process - Process object containing the refresh process
|
|
278
287
|
*/
|
|
279
|
-
export function replaceTagElementsInContainer(tagList, $container, parentController) {
|
|
280
|
-
|
|
288
|
+
export function replaceTagElementsInContainer(tagList, $container, parentController, process) {
|
|
289
|
+
return new Promise((resolve) => {
|
|
281
290
|
|
|
282
|
-
|
|
283
|
-
|
|
291
|
+
let tagDescriptionElements = findSdcTgs($container, tagList, parentController);
|
|
292
|
+
let tagCount = tagDescriptionElements.length;
|
|
284
293
|
|
|
294
|
+
if (tagCount === 0) {
|
|
295
|
+
return resolve();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (let elementIndex = 0; elementIndex < tagDescriptionElements.length; elementIndex++) {
|
|
299
|
+
runReplaceTagElementsInContainer(tagDescriptionElements[elementIndex].dom,
|
|
300
|
+
tagDescriptionElements[elementIndex].tag,
|
|
301
|
+
tagDescriptionElements[elementIndex].super,
|
|
302
|
+
parentController, process).then(() => {
|
|
303
|
+
tagCount--;
|
|
285
304
|
if (tagCount === 0) {
|
|
286
|
-
|
|
305
|
+
return resolve();
|
|
287
306
|
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
288
311
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
312
|
+
export function reloadMethodHTML(controller, $container, process) {
|
|
313
|
+
return _reloadMethodHTML(controller, $container ?? controller.$container, process)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function _reloadMethodHTML(controller, $dom, process) {
|
|
317
|
+
const plist = [];
|
|
318
|
+
|
|
319
|
+
$dom.find(`._bind_to_update_handler.sdc_uuid_${controller._uuid}`).each(function () {
|
|
320
|
+
const $this = $(this);
|
|
321
|
+
let result = undefined;
|
|
322
|
+
if ($this.hasClass(`_with_handler`)) {
|
|
323
|
+
result = $this.data('handler');
|
|
324
|
+
} else {
|
|
325
|
+
let controller_handler = this.tagName.toLowerCase().replace(/^this./, '');
|
|
326
|
+
if (controller[controller_handler]) {
|
|
327
|
+
result = controller[controller_handler];
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
if (typeof result === 'function') {
|
|
333
|
+
result = result.bind(controller)($this.data());
|
|
334
|
+
}
|
|
335
|
+
if (result !== undefined) {
|
|
336
|
+
plist.push(Promise.resolve(result).then((x) => {
|
|
337
|
+
let $newContent = $(`<div></div>`);
|
|
338
|
+
$newContent.append(x);
|
|
339
|
+
$newContent = $this.clone().empty().append($newContent);
|
|
340
|
+
return app.reconcile(controller, $newContent, $this, process);
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return Promise.all(plist);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
function getNodeKey(node) {
|
|
351
|
+
if (node[0].nodeType === 3) {
|
|
352
|
+
return `TEXT__${node[0].nodeValue}`;
|
|
353
|
+
}
|
|
354
|
+
const res = [node[0].tagName];
|
|
355
|
+
if (node[0].nodeName === 'INPUT') {
|
|
356
|
+
[['name', ''], ['type', 'text'], ['id', '']].forEach(([key, defaultValue]) => {
|
|
357
|
+
const attr = node.attr(key) ?? defaultValue;
|
|
358
|
+
if (attr) {
|
|
359
|
+
res.push(attr);
|
|
360
|
+
}
|
|
300
361
|
});
|
|
362
|
+
}
|
|
363
|
+
return res.join('__');
|
|
301
364
|
}
|
|
302
365
|
|
|
303
|
-
|
|
304
|
-
|
|
366
|
+
function reconcileTree($element, id = [], parent = null) {
|
|
367
|
+
id.push(getNodeKey($element));
|
|
368
|
+
const obj = {
|
|
369
|
+
$element,
|
|
370
|
+
id: id.join('::'),
|
|
371
|
+
depth: id.length,
|
|
372
|
+
idx: 0,
|
|
373
|
+
getRealParent: () => parent,
|
|
374
|
+
getIdx: function () {
|
|
375
|
+
this.idx = (this.getRealParent()?.getIdx() ?? -1) + $element.index() + 1;
|
|
376
|
+
return this.idx;
|
|
377
|
+
},
|
|
378
|
+
op: null,
|
|
379
|
+
parent
|
|
380
|
+
};
|
|
381
|
+
obj.getIdx.bind(obj);
|
|
382
|
+
return [obj].concat($element.contents().toArray().map((x) => reconcileTree($(x), id.slice(), obj)).flat());
|
|
383
|
+
|
|
305
384
|
}
|
|
306
385
|
|
|
307
|
-
function _reloadMethodHTML(controller, $dom) {
|
|
308
|
-
const plist = [];
|
|
309
386
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
387
|
+
export function reconcile($virtualNode, $realNode) {
|
|
388
|
+
const $old = reconcileTree($realNode);
|
|
389
|
+
const $new = reconcileTree($virtualNode);
|
|
390
|
+
$old.map((x, i) => x.idx = i);
|
|
391
|
+
$new.map((x, i) => x.idx = i);
|
|
392
|
+
const depth = Math.max(...$new.concat($old).map(x => x.depth));
|
|
393
|
+
const op_steps = lcbDiff($old, $new, depth);
|
|
394
|
+
let toRemove = [];
|
|
395
|
+
window.MAIN = $realNode;
|
|
396
|
+
window.OPS = op_steps;
|
|
397
|
+
|
|
398
|
+
op_steps.forEach((op_step, i) => {
|
|
399
|
+
const {op, $element, idx} = op_step;
|
|
400
|
+
|
|
401
|
+
if (op.type === 'keep_counterpart') {
|
|
402
|
+
let cIdx = op.counterpart.getIdx();
|
|
403
|
+
if (cIdx !== idx) {
|
|
404
|
+
const elemBefore = op_step.getBefore();
|
|
405
|
+
if (!elemBefore) {
|
|
406
|
+
op_step.getRealParent().$element.prepend(op.counterpart.$element);
|
|
315
407
|
} else {
|
|
316
|
-
|
|
317
|
-
if (controller[controller_handler]) {
|
|
318
|
-
result = controller[controller_handler];
|
|
319
|
-
}
|
|
408
|
+
op.counterpart.$element.insertAfter(elemBefore.$element);
|
|
320
409
|
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
syncAttributes(op.counterpart.$element, $element);
|
|
413
|
+
if ($element.hasClass(CONTROLLER_CLASS)) {
|
|
414
|
+
$element.data(DATA_CONTROLLER_KEY).$container = op.counterpart.$element;
|
|
415
|
+
$element.data(DATA_CONTROLLER_KEY, null);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
toRemove.push($element);
|
|
419
|
+
} else if (op.type === 'delete') {
|
|
420
|
+
$element.safeRemove();
|
|
421
|
+
} else if (op.type === 'insert') {
|
|
422
|
+
const {after, target} = op_step.op;
|
|
423
|
+
if (after) {
|
|
424
|
+
$element.insertAfter(after.$element);
|
|
425
|
+
} else if (target) {
|
|
426
|
+
target.$element.prepend($element);
|
|
427
|
+
}
|
|
321
428
|
|
|
429
|
+
}
|
|
430
|
+
});
|
|
322
431
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
if (result !== undefined) {
|
|
327
|
-
plist.push(Promise.resolve(result).then((x) => {
|
|
328
|
-
let $newContent = $(`<div></div>`);
|
|
329
|
-
$newContent.append(x);
|
|
330
|
-
$newContent = $this.clone().empty().append($newContent);
|
|
331
|
-
return controller.reconcile($newContent, $this);
|
|
332
|
-
}));
|
|
333
|
-
}
|
|
432
|
+
toRemove.forEach(($element) => $element.safeRemove());
|
|
433
|
+
}
|
|
334
434
|
|
|
335
|
-
|
|
435
|
+
function syncAttributes($real, $virtual) {
|
|
436
|
+
const realAttrs = $real[0].attributes ?? [];
|
|
437
|
+
const virtualAttrs = $virtual[0].attributes ?? [];
|
|
438
|
+
// Remove missing attrs
|
|
439
|
+
[...realAttrs].forEach(attr => {
|
|
440
|
+
if (!$virtual.is(`[${attr.name}]`)) {
|
|
441
|
+
$real.removeAttr(attr.name);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Add or update
|
|
446
|
+
[...virtualAttrs].forEach(attr => {
|
|
447
|
+
if (!attr.name.startsWith(`data`) && $real.attr(attr.name) !== attr.value) {
|
|
448
|
+
$real.attr(attr.name, attr.value);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
336
451
|
|
|
337
|
-
|
|
452
|
+
$real.removeData();
|
|
453
|
+
Object.entries($virtual.data()).forEach(([key, value]) => {
|
|
454
|
+
$real.data(key, value);
|
|
455
|
+
});
|
|
338
456
|
}
|
|
339
457
|
|
|
458
|
+
/**
|
|
459
|
+
* LCB (Longest Common Branch) finds matching branches and reserves them!
|
|
460
|
+
*
|
|
461
|
+
* @param oldNodes
|
|
462
|
+
* @param newNodes
|
|
463
|
+
* @param depth
|
|
464
|
+
* @returns {*|*[]}
|
|
465
|
+
*/
|
|
466
|
+
function lcbDiff(oldNodes, newNodes, depth) {
|
|
467
|
+
newNodes.filter(x => x.depth === depth && !x.op).forEach((newNode) => {
|
|
468
|
+
const oldNode = oldNodes.find((tempOldNode) => {
|
|
469
|
+
return !tempOldNode.op && tempOldNode.id === newNode.id;
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
if (oldNode) {
|
|
473
|
+
const keepTreeBranch = (oldNode, newNode) => {
|
|
474
|
+
oldNode.op = {type: 'keep', idx: newNode.idx};
|
|
475
|
+
newNode.op = {type: 'keep_counterpart', counterpart: oldNode};
|
|
476
|
+
oldNode = oldNode.parent;
|
|
477
|
+
newNode = newNode.parent;
|
|
478
|
+
if (!oldNode || oldNode.op || newNode?.op) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
keepTreeBranch(oldNode, newNode);
|
|
340
482
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
483
|
+
}
|
|
484
|
+
keepTreeBranch(oldNode, newNode);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
if (depth > 1) {
|
|
488
|
+
return lcbDiff(oldNodes, newNodes, depth - 1);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
oldNodes.forEach((x, i) => {
|
|
492
|
+
if (!x.op) {
|
|
493
|
+
const idx = (oldNodes[i - 1]?.op.idx ?? -1) + 1;
|
|
494
|
+
x.op = {type: 'delete', idx}
|
|
347
495
|
}
|
|
496
|
+
});
|
|
348
497
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
498
|
+
function getRealParent(element) {
|
|
499
|
+
if (!element.parent) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
return element.parent.op.type === 'keep_counterpart' ? element.parent.op.counterpart : element.parent;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function getBefore(element, idx) {
|
|
506
|
+
const startDepth = element.depth;
|
|
507
|
+
while (idx >= 0 && element.depth >= startDepth) {
|
|
508
|
+
idx -= 1;
|
|
509
|
+
element = newNodes[idx];
|
|
510
|
+
if (element.depth === startDepth) {
|
|
511
|
+
return element.op.type === 'keep_counterpart' ? element.op.counterpart : element;
|
|
512
|
+
}
|
|
354
513
|
}
|
|
355
514
|
|
|
356
|
-
|
|
357
|
-
syncAttributes($realNode, $virtualNode);
|
|
515
|
+
return null
|
|
358
516
|
|
|
359
|
-
|
|
360
|
-
const virtualChildren = $virtualNode.contents();
|
|
361
|
-
const realChildren = $realNode.contents();
|
|
517
|
+
}
|
|
362
518
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
519
|
+
newNodes.forEach((x, i) => {
|
|
520
|
+
x.getBefore = () => getBefore(x, i);
|
|
521
|
+
x.getRealParent = () => getRealParent(x);
|
|
522
|
+
|
|
523
|
+
if (!x.op) {
|
|
524
|
+
const target = x.getRealParent();
|
|
525
|
+
const type = target?.op.type === 'insert' ? 'insert_ignore' : 'insert';
|
|
526
|
+
x.op = {type, target, after: x.getBefore()}
|
|
372
527
|
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const tagged = [
|
|
531
|
+
...oldNodes,
|
|
532
|
+
...newNodes,
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
return tagged.sort((a, b) => {
|
|
537
|
+
const aVal = a.op?.idx ?? a.idx;
|
|
538
|
+
const bVal = b.op?.idx ?? b.idx;
|
|
539
|
+
|
|
540
|
+
return aVal - bVal;
|
|
541
|
+
});
|
|
373
542
|
}
|
|
374
543
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
});
|
|
544
|
+
/**
|
|
545
|
+
*
|
|
546
|
+
* @param {jquery} $dom
|
|
547
|
+
* @param {AbstractSDC} leafController
|
|
548
|
+
* @param {Object} process - Process object containing the refresh process
|
|
549
|
+
* @return {Promise<void>}
|
|
550
|
+
*/
|
|
384
551
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
});
|
|
552
|
+
export function refresh($dom, leafController, process = null) {
|
|
553
|
+
if (!leafController) {
|
|
554
|
+
leafController = getController($dom);
|
|
555
|
+
}
|
|
391
556
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
557
|
+
if (!leafController) {
|
|
558
|
+
return Promise.resolve();
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const {refreshProcess, isRunningProcess} = prepareRefreshProcess(process, leafController);
|
|
562
|
+
|
|
563
|
+
$dom ??= leafController.$container;
|
|
564
|
+
|
|
565
|
+
return replaceTagElementsInContainer(app.tagNames, $dom, leafController, process).then(() => {
|
|
566
|
+
reloadMethodHTML(leafController, $dom, refreshProcess).then(() => {
|
|
567
|
+
if (!isRunningProcess) {
|
|
568
|
+
updateEventAndTriggerOnRefresh(refreshProcess);
|
|
569
|
+
}
|
|
396
570
|
});
|
|
571
|
+
|
|
572
|
+
});
|
|
397
573
|
}
|