1 | /*!
|
2 | * OpenUI5
|
3 | * (c) Copyright 2009-2022 SAP SE or an SAP affiliate company.
|
4 | * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
|
5 | */
|
6 | // Provides miscellaneous utility functions that might be useful for any script
|
7 | sap.ui.define([
|
8 | 'jquery.sap.global',
|
9 | 'sap/base/util/uid',
|
10 | 'sap/base/strings/hash',
|
11 | 'sap/base/util/array/uniqueSort',
|
12 | 'sap/base/util/deepEqual',
|
13 | 'sap/base/util/each',
|
14 | 'sap/base/util/array/diff',
|
15 | 'sap/base/util/JSTokenizer',
|
16 | 'sap/base/util/merge',
|
17 | 'sap/base/util/UriParameters'
|
18 | ], function(jQuery, uid, hash, uniqueSort, deepEqual, each, diff, JSTokenizer, merge, UriParameters) {
|
19 | ;
|
20 |
|
21 | /**
|
22 | * Creates and returns a pseudo-unique id.
|
23 | *
|
24 | * No means for detection of overlap with already present or future UIDs.
|
25 | *
|
26 | * @return {string} A pseudo-unique id.
|
27 | * @public
|
28 | * @function
|
29 | * @deprecated since 1.58 use {@link module:sap/base/util/uid} instead
|
30 | */
|
31 | jQuery.sap.uid = uid;
|
32 |
|
33 | /**
|
34 | * This function generates a hash-code from a string
|
35 | * @param {string} sString The string to generate the hash-code from
|
36 | * @return {int} The generated hash-code
|
37 | * @since 1.39
|
38 | * @private
|
39 | * @ui5-restricted sap.ui.core
|
40 | * @function
|
41 | * @deprecated since 1.58 use {@link module:sap/base/strings/hash} instead
|
42 | */
|
43 | jQuery.sap.hashCode = hash;
|
44 |
|
45 |
|
46 | /**
|
47 | * Sorts the given array in-place and removes any duplicates (identified by "===").
|
48 | *
|
49 | * Use <code>jQuery.uniqueSort()</code> for arrays of DOMElements.
|
50 | *
|
51 | * @param {Array} a An Array of any type
|
52 | * @return {Array} Same array as given (for chaining)
|
53 | * @public
|
54 | * @function
|
55 | * @deprecated since 1.58 use {@link module:sap/base/util/array/uniqueSort} instead
|
56 | */
|
57 | jQuery.sap.unique = uniqueSort;
|
58 |
|
59 | /**
|
60 | * Compares the two given values for equality, especially takes care not to compare
|
61 | * arrays and objects by reference, but compares their content.
|
62 | * Note: function does not work with comparing XML objects
|
63 | *
|
64 | * @param {any} a A value of any type
|
65 | * @param {any} b A value of any type
|
66 | * @param {int} [maxDepth=10] Maximum recursion depth
|
67 | * @param {boolean} [contains] Whether all existing properties in a are equal as in b
|
68 | *
|
69 | * @return {boolean} Whether a and b are equal
|
70 | * @public
|
71 | * @function
|
72 | * @deprecated since 1.58 use {@link module:sap/base/util/deepEqual} instead
|
73 | */
|
74 | jQuery.sap.equal = deepEqual;
|
75 |
|
76 | /**
|
77 | * Iterates over elements of the given object or array.
|
78 | *
|
79 | * Works similar to <code>jQuery.each</code>, but a numeric index is only used for
|
80 | * instances of <code>Array</code>. For all other objects, including those with a numeric
|
81 | * <code>length</code> property, the properties are iterated by name.
|
82 | *
|
83 | * The contract for the <code>fnCallback</code> is the same as for <code>jQuery.each</code>,
|
84 | * when it returns <code>false</code>, then the iteration stops (break).
|
85 | *
|
86 | * @param {object|any[]} oObject object or array to enumerate the properties of
|
87 | * @param {function} fnCallback function to call for each property name
|
88 | * @return {object|any[]} the given <code>oObject</code>
|
89 | * @since 1.11
|
90 | * @function
|
91 | * @deprecated since 1.58 use {@link module:sap/base/util/each} instead
|
92 | */
|
93 | jQuery.sap.each = each;
|
94 |
|
95 | /**
|
96 | * Calculate delta of old list and new list.
|
97 | *
|
98 | * This function implements the algorithm described in "A Technique for Isolating Differences Between Files"
|
99 | * (Commun. ACM, April 1978, Volume 21, Number 4, Pages 264-268).
|
100 | *
|
101 | * Items in the arrays are not compared directly. Instead, a substitute symbol is determined for each item
|
102 | * by applying the provided function <code>fnSymbol</code> to it. Items with strictly equal symbols are
|
103 | * assumed to represent the same logical item:
|
104 | * <pre>
|
105 | * fnSymbol(a) === fnSymbol(b) <=> a 'is logically the same as' b
|
106 | * </pre>
|
107 | * As an additional constraint, casting the symbols to string should not modify the comparison result.
|
108 | * If this second constraint is not met, this method might report more diffs than necessary.
|
109 | *
|
110 | * If no symbol function is provided, a default implementation is used which applies <code>JSON.stringify</code>
|
111 | * to non-string items and reduces the strings to a hash code. It is not guaranteed that this default
|
112 | * implementation fulfills the above constraint in all cases, but it is a compromise between implementation
|
113 | * effort, generality and performance. If items are known to be non-stringifiable (e.g. because they may
|
114 | * contain cyclic references) or when hash collisions are likely, an own <code>fnSymbol</code> function
|
115 | * must be provided.
|
116 | *
|
117 | * The result of the diff is a sequence of update operations, each consisting of a <code>type</code>
|
118 | * (either <code>"insert"</code> or <code>"delete"</code>) and an <code>index</code>.
|
119 | * By applying the operations one after the other to the old array, it can be transformed to an
|
120 | * array whose items are equal to the new array.
|
121 | *
|
122 | * Sample implementation of the update
|
123 | * <pre>
|
124 | *
|
125 | * function update(aOldArray, aNewArray) {
|
126 | *
|
127 | * // calculate the diff
|
128 | * var aDiff = jQuery.sap.arraySymbolDiff(aOldArray, aNewArray, __provide_your_symbol_function_here__);
|
129 | *
|
130 | * // apply update operations
|
131 | * aDiff.forEach( function(op) {
|
132 | *
|
133 | * // invariant: aOldArray and aNewArray now are equal up to (excluding) op.index
|
134 | *
|
135 | * switch ( op.type ) {
|
136 | * case 'insert':
|
137 | * // new array contains a new (or otherwise unmapped) item, add it here
|
138 | * aOldArray.splice(op.index, 0, aNewArray[op.index]);
|
139 | * break;
|
140 | * case 'delete':
|
141 | * // an item is no longer part of the array (or has been moved to another position), remove it
|
142 | * aOldArray.splice(op.index, 1);
|
143 | * break;
|
144 | * default:
|
145 | * throw new Error('unexpected diff operation type');
|
146 | * }
|
147 | *
|
148 | * });
|
149 | * }
|
150 | *
|
151 | * </pre>
|
152 | *
|
153 | * @param {Array} aOld Old Array
|
154 | * @param {Array} aNew New Array
|
155 | * @param {function} [fnSymbol] Function to calculate substitute symbols for array items
|
156 | * @return {Array.<{type:string,index:int}>} List of update operations
|
157 | * @public
|
158 | * @function
|
159 | * @deprecated since 1.58 use {@link module:sap/base/util/array/diff} instead
|
160 | */
|
161 | jQuery.sap.arraySymbolDiff = diff;
|
162 |
|
163 |
|
164 | /**
|
165 | * A factory returning a tokenizer object for JS values.
|
166 | * Contains functions to consume tokens on an input string.
|
167 | * @function
|
168 | * @private
|
169 | * @returns {object} - the tokenizer
|
170 | * @deprecated since 1.58 use {@link module:sap/base/util/JSTokenizer} instead
|
171 | */
|
172 | jQuery.sap._createJSTokenizer = function() {
|
173 | return new JSTokenizer();
|
174 | };
|
175 |
|
176 | /**
|
177 | * Parse simple JS objects.
|
178 | *
|
179 | * A parser for JS object literals. This is different from a JSON parser, as it does not have
|
180 | * the JSON specification as a format description, but a subset of the JavaScript language.
|
181 | * The main difference is, that keys in objects do not need to be quoted and strings can also
|
182 | * be defined using apostrophes instead of quotation marks.
|
183 | *
|
184 | * The parser does not support functions, but only boolean, number, string, object and array.
|
185 | *
|
186 | * @function
|
187 | * @param {string} The string containing the JS objects
|
188 | * @throws an error, if the string does not contain a valid JS object
|
189 | * @returns {object} the JS object
|
190 | *
|
191 | * @private
|
192 | * @since 1.11
|
193 | * @deprecated since 1.58 use {@link module:sap/base/util/JSTokenizer.parseJS} instead
|
194 | */
|
195 | jQuery.sap.parseJS = JSTokenizer.parseJS;
|
196 |
|
197 | /**
|
198 | * Merge the contents of two or more objects together into the first object.
|
199 | * Usage is the same as jQuery.extend, but Arguments that are null or undefined are NOT ignored.
|
200 | *
|
201 | * @deprecated since 1.58. For shallow extend use <code>Object.assign</code> (polyfilled), for deep extend use <code>sap/base/util/merge</code>.
|
202 | * @function
|
203 | * @since 1.26
|
204 | * @private
|
205 | */
|
206 | jQuery.sap.extend = function () {
|
207 | var args = arguments,
|
208 | deep = false;
|
209 |
|
210 | // Check whether the first argument is the deep-flag
|
211 | if (typeof arguments[0] === "boolean") {
|
212 | deep = arguments[0];
|
213 |
|
214 | // skip the first argument while creating a shallow copy of arguments
|
215 | args = Array.prototype.slice.call(arguments, 1);
|
216 | }
|
217 |
|
218 | if (deep) {
|
219 | return merge.apply(this, args);
|
220 | } else {
|
221 | /*
|
222 | * The code in this function is taken from jQuery 3.6.0 "jQuery.extend" and got modified.
|
223 | *
|
224 | * jQuery JavaScript Library v3.6.0
|
225 | * https://jquery.com/
|
226 | *
|
227 | * Copyright OpenJS Foundation and other contributors
|
228 | * Released under the MIT license
|
229 | * https://jquery.org/license
|
230 | */
|
231 | var copy, name, options,
|
232 | target = arguments[0] || {},
|
233 | i = 1,
|
234 | length = arguments.length;
|
235 |
|
236 | // Handle case when target is a string or something (possible in deep copy)
|
237 | if (typeof target !== "object" && typeof target !== "function") {
|
238 | target = {};
|
239 | }
|
240 |
|
241 | for (; i < length; i++) {
|
242 |
|
243 | options = arguments[i];
|
244 |
|
245 | // Extend the base object
|
246 | for (name in options) {
|
247 | copy = options[name];
|
248 |
|
249 | // Prevent never-ending loop
|
250 | if (name === "__proto__" || target === copy) {
|
251 | continue;
|
252 | }
|
253 |
|
254 | target[name] = copy;
|
255 | }
|
256 | }
|
257 |
|
258 | // Return the modified object
|
259 | return target;
|
260 | }
|
261 | };
|
262 |
|
263 | // Javadoc for private inner class "UriParams" - this list of comments is intentional!
|
264 | /**
|
265 | * @interface Encapsulates all URI parameters of the current windows location (URL).
|
266 | *
|
267 | * Use {@link jQuery.sap.getUriParameters} to create an instance of jQuery.sap.util.UriParameters.
|
268 | *
|
269 | * @author SAP SE
|
270 | * @version 1.90.11
|
271 | * @since 0.9.0
|
272 | * @name jQuery.sap.util.UriParameters
|
273 | * @public
|
274 | */
|
275 |
|
276 | /**
|
277 | * Returns the value(s) of the URI parameter with the given name sName.
|
278 | *
|
279 | * If the boolean parameter bAll is <code>true</code>, an array of string values of all
|
280 | * occurrences of the URI parameter with the given name is returned. This array is empty
|
281 | * if the URI parameter is not contained in the windows URL.
|
282 | *
|
283 | * If the boolean parameter bAll is <code>false</code> or is not specified, the value of the first
|
284 | * occurrence of the URI parameter with the given name is returned. Might be <code>null</code>
|
285 | * if the URI parameter is not contained in the windows URL.
|
286 | *
|
287 | * @public
|
288 | * @param {string} sUri The name of the URI parameter.
|
289 | * @return {string|array} The value(s) of the URI parameter with the given name
|
290 | * @SecSource {return|XSS} Return value contains URL parameters
|
291 | * @function
|
292 | * @name jQuery.sap.util.UriParameters.prototype.get
|
293 | */
|
294 |
|
295 | /**
|
296 | * Creates and returns a new instance of {@link jQuery.sap.util.UriParameters}.
|
297 | *
|
298 | * Example for reading a single URI parameter (or the value of the first
|
299 | * occurrence of the URI parameter):
|
300 | * <pre>
|
301 | * var sValue = jQuery.sap.getUriParameters().get("myUriParam");
|
302 | * </pre>
|
303 | *
|
304 | * Example for reading the values of the first of the URI parameter
|
305 | * (with multiple occurrences):
|
306 | * <pre>
|
307 | * var aValues = jQuery.sap.getUriParameters().get("myUriParam", true);
|
308 | * for(i in aValues){
|
309 | * var sValue = aValues[i];
|
310 | * }
|
311 | * </pre>
|
312 | *
|
313 | * @public
|
314 | * @param {string} sUri Uri to determine the parameters for
|
315 | * @return {jQuery.sap.util.UriParameters} A new URI parameters instance
|
316 | * @deprecated As of 1.68, use {@link module:sap/base/util/UriParameters.fromQuery UriParameters.fromQuery}
|
317 | * or {@link module:sap/base/util/UriParameters.fromURL UriParameters.fromURL} instead.
|
318 | */
|
319 | jQuery.sap.getUriParameters = function getUriParameters(sUri) {
|
320 | return UriParameters.fromURL(sUri || window.location.href);
|
321 | };
|
322 |
|
323 | /**
|
324 | * Calls a method after a given delay and returns an id for this timer
|
325 | *
|
326 | * @param {int} iDelay Delay time in milliseconds
|
327 | * @param {object} oObject Object from which the method should be called
|
328 | * @param {string|object} method function pointer or name of the method
|
329 | * @param {array} [aParameters] Method parameters
|
330 | * @return {string} Id which can be used to cancel the timer with clearDelayedCall
|
331 | * @public
|
332 | * @deprecated since 1.58 use native <code>setTimeout</code> instead
|
333 | */
|
334 | jQuery.sap.delayedCall = function delayedCall(iDelay, oObject, method, aParameters) {
|
335 | return setTimeout(function(){
|
336 | if (typeof method === "string") {
|
337 | method = oObject[method];
|
338 | }
|
339 | method.apply(oObject, aParameters || []);
|
340 | }, iDelay);
|
341 | };
|
342 |
|
343 | /**
|
344 | * Stops the delayed call.
|
345 | *
|
346 | * The function given when calling delayedCall is not called anymore.
|
347 | *
|
348 | * @param {string} sDelayedCallId The id returned, when calling delayedCall
|
349 | * @public
|
350 | * @deprecated since 1.58 use native <code>clearTimeout</code> instead
|
351 | */
|
352 | jQuery.sap.clearDelayedCall = function clearDelayedCall(sDelayedCallId) {
|
353 | clearTimeout(sDelayedCallId);
|
354 | return this;
|
355 | };
|
356 |
|
357 | /**
|
358 | * Calls a method after a given interval and returns an id for this interval.
|
359 | *
|
360 | * @param {int} iInterval Interval time in milliseconds
|
361 | * @param {object} oObject Object from which the method should be called
|
362 | * @param {string|object} method function pointer or name of the method
|
363 | * @param {array} [aParameters] Method parameters
|
364 | * @return {string} Id which can be used to cancel the interval with clearIntervalCall
|
365 | * @public
|
366 | * @deprecated since 1.58 use native <code>setInterval</code> instead
|
367 | */
|
368 | jQuery.sap.intervalCall = function intervalCall(iInterval, oObject, method, aParameters) {
|
369 | return setInterval(function(){
|
370 | if (typeof method === "string") {
|
371 | method = oObject[method];
|
372 | }
|
373 | method.apply(oObject, aParameters || []);
|
374 | }, iInterval);
|
375 | };
|
376 |
|
377 | /**
|
378 | * Stops the interval call.
|
379 | *
|
380 | * The function given when calling intervalCall is not called anymore.
|
381 | *
|
382 | * @param {string} sIntervalCallId The id returned, when calling intervalCall
|
383 | * @public
|
384 | * @deprecated since 1.58 use native <code>clearInterval</code> instead
|
385 | */
|
386 | jQuery.sap.clearIntervalCall = function clearIntervalCall(sIntervalCallId) {
|
387 | clearInterval(sIntervalCallId);
|
388 | return this;
|
389 | };
|
390 |
|
391 | /**
|
392 | * Substitute for <code>for(n in o)</code> loops.
|
393 | * This function is just a wrapper around the native for-in loop.
|
394 | *
|
395 | * Iterates over all enumerable properties of the given object and calls the
|
396 | * given callback function for each of them. The assumed signature of the
|
397 | * callback function is
|
398 | *
|
399 | * fnCallback(name, value)
|
400 | *
|
401 | * where name is the name of the property and value is its value.
|
402 | *
|
403 | * @param {object} oObject object to enumerate the properties of
|
404 | * @param {function} fnCallback function to call for each property name
|
405 | * @deprecated since 1.48.0. Use native for-in loop instead.
|
406 | * @since 1.7.1
|
407 | */
|
408 | jQuery.sap.forIn = each;
|
409 |
|
410 | /**
|
411 | * Calculate delta of old list and new list.
|
412 | *
|
413 | * This partly implements the algorithm described in "A Technique for Isolating Differences Between Files"
|
414 | * but instead of working with hashes, it does compare each entry of the old list with each entry of the new
|
415 | * list, which causes terrible performance on large datasets.
|
416 | *
|
417 | * @deprecated As of 1.38, use {@link module:sap/base/util/array/diff} instead if applicable
|
418 | * @public
|
419 | * @param {Array} aOld Old Array
|
420 | * @param {Array} aNew New Array
|
421 | * @param {function} [fnCompare] Function to compare list entries
|
422 | * @param {boolean} [bUniqueEntries] Whether entries are unique, so no duplicate entries exist
|
423 | * @return {Array} List of changes
|
424 | */
|
425 | jQuery.sap.arrayDiff = function(aOld, aNew, fnCompare, bUniqueEntries){
|
426 | fnCompare = fnCompare || function(vValue1, vValue2) {
|
427 | return deepEqual(vValue1, vValue2);
|
428 | };
|
429 |
|
430 | var aOldRefs = [];
|
431 | var aNewRefs = [];
|
432 |
|
433 | //Find references
|
434 | var aMatches = [];
|
435 | for (var i = 0; i < aNew.length; i++) {
|
436 | var oNewEntry = aNew[i];
|
437 | var iFound = 0;
|
438 | var iTempJ;
|
439 | // if entries are unique, first check for whether same index is same entry
|
440 | // and stop searching as soon the first matching entry is found
|
441 | if (bUniqueEntries && fnCompare(aOld[i], oNewEntry)) {
|
442 | iFound = 1;
|
443 | iTempJ = i;
|
444 | } else {
|
445 | for (var j = 0; j < aOld.length; j++) {
|
446 | if (fnCompare(aOld[j], oNewEntry)) {
|
447 | iFound++;
|
448 | iTempJ = j;
|
449 | if (bUniqueEntries || iFound > 1) {
|
450 | break;
|
451 | }
|
452 | }
|
453 | }
|
454 | }
|
455 | if (iFound == 1) {
|
456 | var oMatchDetails = {
|
457 | oldIndex: iTempJ,
|
458 | newIndex: i
|
459 | };
|
460 | if (aMatches[iTempJ]) {
|
461 | delete aOldRefs[iTempJ];
|
462 | delete aNewRefs[aMatches[iTempJ].newIndex];
|
463 | } else {
|
464 | aNewRefs[i] = {
|
465 | data: aNew[i],
|
466 | row: iTempJ
|
467 | };
|
468 | aOldRefs[iTempJ] = {
|
469 | data: aOld[iTempJ],
|
470 | row: i
|
471 | };
|
472 | aMatches[iTempJ] = oMatchDetails;
|
473 | }
|
474 | }
|
475 | }
|
476 |
|
477 | //Pass 4: Find adjacent matches in ascending order
|
478 | for (var i = 0; i < aNew.length - 1; i++) {
|
479 | if (aNewRefs[i] &&
|
480 | !aNewRefs[i + 1] &&
|
481 | aNewRefs[i].row + 1 < aOld.length &&
|
482 | !aOldRefs[aNewRefs[i].row + 1] &&
|
483 | fnCompare(aOld[ aNewRefs[i].row + 1 ], aNew[i + 1])) {
|
484 |
|
485 | aNewRefs[i + 1] = {
|
486 | data: aNew[i + 1],
|
487 | row: aNewRefs[i].row + 1
|
488 | };
|
489 | aOldRefs[aNewRefs[i].row + 1] = {
|
490 | data: aOldRefs[aNewRefs[i].row + 1],
|
491 | row: i + 1
|
492 | };
|
493 |
|
494 | }
|
495 | }
|
496 |
|
497 | //Pass 5: Find adjacent matches in descending order
|
498 | for (var i = aNew.length - 1; i > 0; i--) {
|
499 | if (aNewRefs[i] &&
|
500 | !aNewRefs[i - 1] &&
|
501 | aNewRefs[i].row > 0 &&
|
502 | !aOldRefs[aNewRefs[i].row - 1] &&
|
503 | fnCompare(aOld[aNewRefs[i].row - 1], aNew[i - 1])) {
|
504 |
|
505 | aNewRefs[i - 1] = {
|
506 | data: aNew[i - 1],
|
507 | row: aNewRefs[i].row - 1
|
508 | };
|
509 | aOldRefs[aNewRefs[i].row - 1] = {
|
510 | data: aOldRefs[aNewRefs[i].row - 1],
|
511 | row: i - 1
|
512 | };
|
513 |
|
514 | }
|
515 | }
|
516 |
|
517 | //Pass 6: Generate diff data
|
518 | var aDiff = [];
|
519 |
|
520 | if (aNew.length == 0) {
|
521 | //New list is empty, all items were deleted
|
522 | for (var i = 0; i < aOld.length; i++) {
|
523 | aDiff.push({
|
524 | index: 0,
|
525 | type: 'delete'
|
526 | });
|
527 | }
|
528 | } else {
|
529 | var iNewListIndex = 0;
|
530 | if (!aOldRefs[0]) {
|
531 | //Detect all deletions at the beginning of the old list
|
532 | for (var i = 0; i < aOld.length && !aOldRefs[i]; i++) {
|
533 | aDiff.push({
|
534 | index: 0,
|
535 | type: 'delete'
|
536 | });
|
537 | iNewListIndex = i + 1;
|
538 | }
|
539 | }
|
540 |
|
541 | for (var i = 0; i < aNew.length; i++) {
|
542 | if (!aNewRefs[i] || aNewRefs[i].row > iNewListIndex) {
|
543 | //Entry doesn't exist in old list = insert
|
544 | aDiff.push({
|
545 | index: i,
|
546 | type: 'insert'
|
547 | });
|
548 | } else {
|
549 | iNewListIndex = aNewRefs[i].row + 1;
|
550 | for (var j = aNewRefs[i].row + 1; j < aOld.length && (!aOldRefs[j] || aOldRefs[j].row < i); j++) {
|
551 | aDiff.push({
|
552 | index: i + 1,
|
553 | type: 'delete'
|
554 | });
|
555 | iNewListIndex = j + 1;
|
556 | }
|
557 | }
|
558 | }
|
559 | }
|
560 |
|
561 | return aDiff;
|
562 | };
|
563 |
|
564 | return jQuery;
|
565 | });
|