@mcpher/gas-fakes 1.0.19 → 1.0.21
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/README.md +63 -30
- package/gasmess/bruce/pbx.js +53 -2
- package/gprompts/gas-inventory.js +92 -0
- package/gprompts/gas-inventory.json +176 -0
- package/gprompts/inventory-list.json +1 -0
- package/gprompts/model.json +34 -0
- package/gprompts/package-lock.json +1103 -0
- package/gprompts/package.json +18 -0
- package/gprompts/temp_fetch.mjs +9 -0
- package/gprompts/update-progress.js +142 -0
- package/package.json +6 -2
- package/setup.sh +147 -0
- package/src/index.js +9 -2
- package/src/services/advdocs/app.js +4 -23
- package/src/services/advdrive/app.js +6 -28
- package/src/services/advforms/app.js +6 -25
- package/src/services/advgmail/app.js +11 -0
- package/src/services/advgmail/fakeadvgmail.js +39 -0
- package/src/services/advgmail/fakeadvgmaillabels.js +119 -0
- package/src/services/advgmail/fakeadvgmailusers.js +23 -0
- package/src/services/advgmail/gmailapis.js +15 -0
- package/src/services/advsheets/app.js +6 -26
- package/src/services/advslides/app.js +6 -28
- package/src/services/common/lazyloader.js +22 -0
- package/src/services/documentapp/app.js +8 -42
- package/src/services/documentapp/appenderhelpers.js +21 -23
- package/src/services/documentapp/elementhelpers.js +0 -1
- package/src/services/documentapp/elementoptions.js +22 -32
- package/src/services/documentapp/fakeelement.js +20 -3
- package/src/services/documentapp/fakelistitem.js +177 -28
- package/src/services/documentapp/fakeparagraph.js +194 -7
- package/src/services/documentapp/faketable.js +16 -0
- package/src/services/documentapp/faketablerow.js +15 -0
- package/src/services/documentapp/nrhelpers.js +1 -0
- package/src/services/documentapp/shadowdocument.js +10 -0
- package/src/services/driveapp/app.js +6 -28
- package/src/services/enums/gmailenums.js +8 -0
- package/src/services/formapp/app.js +5 -40
- package/src/services/gmailapp/app.js +11 -0
- package/src/services/gmailapp/fakegmailapp.js +35 -0
- package/src/services/gmailapp/fakegmaillabel.js +44 -0
- package/src/services/logger/app.js +8 -0
- package/src/services/logger/fakelogger.js +162 -0
- package/src/services/scriptapp/app.js +7 -1
- package/src/services/scriptapp/behavior.js +1 -1
- package/src/services/session/app.js +10 -0
- package/src/services/slidesapp/app.js +5 -40
- package/src/services/spreadsheetapp/app.js +6 -50
- package/src/services/spreadsheetapp/fakeprotection.js +6 -7
- package/src/services/spreadsheetapp/fakesheet.js +3 -4
- package/src/services/stores/app.js +0 -1
- package/src/services/urlfetchapp/app.js +0 -1
- package/src/services/utilities/app.js +6 -20
- package/src/support/gmailcacher.js +7 -0
- package/src/support/helpers.js +2 -2
- package/src/support/proxies.js +20 -1
- package/src/support/sxgmail.js +55 -0
- package/src/support/syncit.js +5 -2
- package/src/support/utils.js +46 -15
- package/src/support/workersync/sxfunctions.js +5 -10
- package/togas.bash +18 -5
- package/ghissues/image-size-inconsistency-issue.sh +0 -46
- package/ghissues/issue-formapp-create-title-inconsistency.sh +0 -51
- package/ghissues/issue-positioned-image.sh +0 -25
- package/ghissues/post-issue.sh +0 -53
- package/ghissues/protection-editors-issue.sh +0 -33
- package/ghissues/review-sandbox-listing-issue.sh +0 -45
- package/ghissues/sandbox-issue.sh +0 -31
- package/ghissues/setup-under-construction.sh +0 -107
- package/src/services/base/app.js +0 -33
- /package/{regenerate-progress-reports.sh → gprompts/regenerate-progress-reports.sh} +0 -0
- /package/src/services/{base → session}/fakesession.js +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FakeAdvResource } from '../common/fakeadvresource.js';
|
|
2
|
+
import { Syncit } from '../../support/syncit.js';
|
|
3
|
+
import { signatureArgs, ssError, gError } from '../../support/helpers.js';
|
|
4
|
+
import { newFakeAdvGmailLabels } from './fakeadvgmaillabels.js';
|
|
5
|
+
|
|
6
|
+
import { Proxies } from '../../support/proxies.js';
|
|
7
|
+
import { Utils } from '../../support/utils.js';
|
|
8
|
+
const { is } = Utils
|
|
9
|
+
|
|
10
|
+
export const newFakeAdvGmailUsers = (...args) => Proxies.guard(new FakeAdvGmailUsers(...args))
|
|
11
|
+
|
|
12
|
+
class FakeAdvGmailUsers extends FakeAdvResource {
|
|
13
|
+
constructor(mainService) {
|
|
14
|
+
super(mainService, 'gmail', Syncit.fxGmail);
|
|
15
|
+
this.gmail = mainService;
|
|
16
|
+
this.__fakeObjectType = 'Gmail.Users';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get Labels() {
|
|
20
|
+
return newFakeAdvGmailLabels(this.gmail);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { Auth } from '../../support/auth.js'
|
|
3
|
+
import { syncLog} from '../../support/workersync/synclogger.js'
|
|
4
|
+
|
|
5
|
+
let __client = null;
|
|
6
|
+
|
|
7
|
+
export const getGmailApiClient = () => {
|
|
8
|
+
const auth = Auth.getAuth()
|
|
9
|
+
if (!__client) {
|
|
10
|
+
syncLog('Creating new Gmail API client');
|
|
11
|
+
__client = google.gmail({ version: 'v1', auth });
|
|
12
|
+
}
|
|
13
|
+
return __client;
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
/**
|
|
3
|
-
* the idea here is to create a global entry for the singleton
|
|
4
|
-
* before we actually have everything we need to create it.
|
|
5
|
-
* We do this by using a proxy, intercepting calls to the
|
|
6
|
-
* initial sigleton and diverting them to a completed one
|
|
7
|
-
*/
|
|
8
|
-
import { newFakeAdvSheets } from './fakeadvsheets.js'
|
|
9
|
-
import { Proxies } from '../../support/proxies.js'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let _app = null
|
|
13
2
|
|
|
14
3
|
/**
|
|
15
|
-
*
|
|
4
|
+
* the idea here is to create an empty global entry for the singleton
|
|
5
|
+
* but only load it when it is actually used.
|
|
16
6
|
*/
|
|
17
|
-
const name = "Sheets"
|
|
18
|
-
if (typeof globalThis[name] === typeof undefined) {
|
|
19
|
-
|
|
20
|
-
const getApp = () => {
|
|
21
|
-
// if it hasne been intialized yet then do that
|
|
22
|
-
if (!_app) {
|
|
23
|
-
console.log('...activating proxy for', name)
|
|
24
|
-
_app = newFakeAdvSheets()
|
|
25
|
-
}
|
|
26
|
-
// this is the actual driveApp we'll return from the proxy
|
|
27
|
-
return _app
|
|
28
|
-
}
|
|
29
7
|
|
|
30
|
-
|
|
8
|
+
import { newFakeAdvSheets as maker} from './fakeadvsheets.js'
|
|
9
|
+
import { lazyLoaderApp } from '../common/lazyloader.js'
|
|
31
10
|
|
|
32
|
-
|
|
11
|
+
let _app = null;
|
|
12
|
+
_app = lazyLoaderApp(_app, 'Sheets', maker)
|
|
@@ -1,32 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
/**
|
|
3
|
-
* the idea here is to create
|
|
4
|
-
*
|
|
5
|
-
* We do this by using a proxy, intercepting calls to the
|
|
6
|
-
* initial sigleton and diverting them to a completed one
|
|
3
|
+
* the idea here is to create an empty global entry for the singleton
|
|
4
|
+
* but only load it when it is actually used.
|
|
7
5
|
*/
|
|
8
|
-
import { newFakeAdvSlides } from './fakeadvslides.js'
|
|
9
|
-
import {
|
|
6
|
+
import { newFakeAdvSlides as maker } from './fakeadvslides.js'
|
|
7
|
+
import { lazyLoaderApp } from '../common/lazyloader.js'
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* adds to global space to mimic Apps Script behavior
|
|
16
|
-
*/
|
|
17
|
-
const name = "Slides"
|
|
18
|
-
if (typeof globalThis[name] === typeof undefined) {
|
|
19
|
-
|
|
20
|
-
const getApp = () => {
|
|
21
|
-
// if it hasne been intialized yet then do that
|
|
22
|
-
if (!_app) {
|
|
23
|
-
console.log('...activating proxy for', name)
|
|
24
|
-
_app = newFakeAdvSlides()
|
|
25
|
-
}
|
|
26
|
-
// this is the actual driveApp we'll return from the proxy
|
|
27
|
-
return _app
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
Proxies.registerProxy(name, getApp)
|
|
31
|
-
|
|
32
|
-
}
|
|
9
|
+
let _app = null;
|
|
10
|
+
_app = lazyLoaderApp(_app, 'Slides', maker)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
export const lazyLoaderApp = (app, name, maker) => {
|
|
4
|
+
|
|
5
|
+
if (typeof globalThis[name] === typeof undefined) {
|
|
6
|
+
|
|
7
|
+
const getApp = () => {
|
|
8
|
+
|
|
9
|
+
// if it hasne been intialized yet then do that
|
|
10
|
+
if (!app) {
|
|
11
|
+
//console.log ('...loading', name)
|
|
12
|
+
app = maker()
|
|
13
|
+
}
|
|
14
|
+
// this is the actual driveApp we'll return from the proxy
|
|
15
|
+
return app
|
|
16
|
+
}
|
|
17
|
+
//console.log ('...registering', name)
|
|
18
|
+
Proxies.registerProxy(name, getApp)
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
return app
|
|
22
|
+
}
|
|
@@ -1,47 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* the idea here is to create
|
|
3
|
-
*
|
|
4
|
-
* We do this by using a proxy, intercepting calls to the
|
|
5
|
-
* initial sigleton and diverting them to a completed one.
|
|
6
|
-
* We also need to make sure all element types are registered.
|
|
4
|
+
* the idea here is to create an empty global entry for the singleton
|
|
5
|
+
* but only load it when it is actually used.
|
|
7
6
|
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
7
|
+
|
|
8
|
+
import { lazyLoaderApp } from '../common/lazyloader.js'
|
|
9
|
+
import { newFakeDocumentApp as maker } from './fakedocumentapp.js';
|
|
10
10
|
import './elements.js'; // This ensures all element types register themselves before DocumentApp is used.
|
|
11
11
|
|
|
12
12
|
let _app = null;
|
|
13
|
-
|
|
14
|
-
const name = 'DocumentApp';
|
|
15
|
-
const serviceName = 'DocumentApp';
|
|
16
|
-
|
|
17
|
-
if (typeof globalThis[name] === typeof undefined) {
|
|
18
|
-
// By importing this, we ensure all element types register themselves.
|
|
19
|
-
const getApp = () => {
|
|
20
|
-
if (!_app) {
|
|
21
|
-
const realApp = newFakeDocumentApp();
|
|
22
|
-
|
|
23
|
-
_app = new Proxy(realApp, {
|
|
24
|
-
get(target, prop, receiver) {
|
|
25
|
-
if (prop === 'toString') {
|
|
26
|
-
return () => name;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const serviceBehavior = ScriptApp.__behavior.sandboxService[serviceName];
|
|
30
|
-
|
|
31
|
-
if (!serviceBehavior.enabled) {
|
|
32
|
-
throw new Error(`${name} service is disabled by sandbox settings.`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const allowedMethods = serviceBehavior.methods;
|
|
36
|
-
if (allowedMethods && typeof target[prop] === 'function' && !allowedMethods.includes(prop)) {
|
|
37
|
-
throw new Error(`Method ${name}.${prop} is not allowed by sandbox settings.`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return Reflect.get(...arguments);
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return _app;
|
|
45
|
-
};
|
|
46
|
-
Proxies.registerProxy(name, getApp);
|
|
47
|
-
}
|
|
13
|
+
_app = lazyLoaderApp(_app, 'DocumentApp', maker)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Utils } from "../../support/utils.js";
|
|
2
2
|
import { ElementType } from '../enums/docsenums.js';
|
|
3
|
-
const { is, isBlob } = Utils
|
|
3
|
+
const { is, isBlob , stringCircular, lobify} = Utils
|
|
4
4
|
import { getElementFactory } from './elementRegistry.js'
|
|
5
5
|
import { signatureArgs, notYetImplemented } from '../../support/helpers.js';
|
|
6
6
|
import { findItem } from './elementhelpers.js';
|
|
@@ -121,7 +121,7 @@ const calculateInsertionPointsAndInitialRequests = (self, childIndex, isAppend,
|
|
|
121
121
|
if (isParaInsert) {
|
|
122
122
|
// Inserting into an existing paragraph. No newlines needed.
|
|
123
123
|
if (childIndex < 0 || childIndex > children.length) {
|
|
124
|
-
throw new Error(`Child index (${childIndex}) must be
|
|
124
|
+
throw new Error(`Child index (${childIndex}) must be less than or equal to the number of child elements (${children.length}).`);
|
|
125
125
|
}
|
|
126
126
|
if (children.length === 0 || childIndex === children.length) {
|
|
127
127
|
// Inserting into an empty paragraph or at the end.
|
|
@@ -136,23 +136,29 @@ const calculateInsertionPointsAndInitialRequests = (self, childIndex, isAppend,
|
|
|
136
136
|
childStartIndex = insertIndex; // The new child will start where we insert it.
|
|
137
137
|
} else {
|
|
138
138
|
// It's an insert operation, creating a new element in a Body/Header/etc.
|
|
139
|
-
if (childIndex < 0 || childIndex
|
|
139
|
+
if (childIndex < 0 || childIndex > children.length) {
|
|
140
140
|
throw new Error(`Child index (${childIndex}) must be less than or equal to the number of child elements (${children.length}).`);
|
|
141
141
|
}
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
// Handle the case where we are inserting at the end (which is like an append).
|
|
143
|
+
if (childIndex === children.length) {
|
|
144
|
+
const lastChildTwig = children.length > 0 ? children[children.length - 1] : null;
|
|
145
|
+
const lastChildItem = lastChildTwig ? elementMap.get(lastChildTwig.name) : item;
|
|
146
|
+
insertIndex = lastChildItem.endIndex - 1;
|
|
147
|
+
newElementStartIndex = lastChildItem.endIndex;
|
|
148
|
+
leading = '\n';
|
|
149
|
+
} else {
|
|
150
|
+
const targetChildTwig = children[childIndex];
|
|
151
|
+
const targetChildItem = elementMap.get(targetChildTwig.name);
|
|
144
152
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
if (targetChildItem.__type === "TABLE") {
|
|
154
|
+
insertIndex = targetChildItem.startIndex - 1; // Insert before the table.
|
|
155
|
+
leading = '\n';
|
|
156
|
+
newElementStartIndex = insertIndex + 1;
|
|
157
|
+
} else {
|
|
158
|
+
insertIndex = targetChildItem.startIndex;
|
|
159
|
+
trailing = '\n';
|
|
160
|
+
newElementStartIndex = insertIndex;
|
|
149
161
|
}
|
|
150
|
-
leading = '\n'
|
|
151
|
-
newElementStartIndex = insertIndex + 1;
|
|
152
|
-
} else {
|
|
153
|
-
insertIndex = targetChildItem.startIndex
|
|
154
|
-
trailing = '\n'
|
|
155
|
-
newElementStartIndex = insertIndex;
|
|
156
162
|
}
|
|
157
163
|
childStartIndex = null; // Child start index is unknown, must be found within container.
|
|
158
164
|
}
|
|
@@ -301,14 +307,6 @@ const elementInserter = (self, elementOrText, childIndex, options) => {
|
|
|
301
307
|
}
|
|
302
308
|
}
|
|
303
309
|
|
|
304
|
-
// After the refresh, the 'self' object is stale. We need to find its new representation
|
|
305
|
-
// in the updated element map and update its internal name. This "revives" the object
|
|
306
|
-
// for subsequent method calls in the user's script.
|
|
307
|
-
const newSelfItem = findItem(shadow.elementMap, selfType, selfStartIndex, segmentId);
|
|
308
|
-
if (newSelfItem && newSelfItem.__name !== selfName) {
|
|
309
|
-
self.__name = newSelfItem.__name;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
310
|
// 6. Handle table content population if necessary. This is a two-phase update
|
|
313
311
|
// because we need the table to exist before we can get the indices to populate its cells.
|
|
314
312
|
if (options.elementType === ElementType.TABLE) {
|
|
@@ -381,11 +381,13 @@ export const imageOptions = {
|
|
|
381
381
|
elementType: ElementType.INLINE_IMAGE,
|
|
382
382
|
insertMethodSignature: 'DocumentApp.Body.insertImage',
|
|
383
383
|
canAcceptText: false,
|
|
384
|
-
|
|
384
|
+
// This internal helper is extracted so it can be used by Paragraph.appendInlineImage
|
|
385
|
+
// without invoking the complex request-building logic of getMainRequest.
|
|
386
|
+
__getImageUriAndSize: (image) => {
|
|
385
387
|
const isDetachedImage = is.object(image) && is.function(image.getType) && image.getType() === ElementType.INLINE_IMAGE;
|
|
386
388
|
const isBlobSource = isBlob(image);
|
|
387
389
|
|
|
388
|
-
let uri, size, fileId;
|
|
390
|
+
let uri, size, fileId, cleanup = null;
|
|
389
391
|
|
|
390
392
|
if (isDetachedImage) {
|
|
391
393
|
if (!image.__isDetached) throw new Error('Element must be detached.');
|
|
@@ -446,13 +448,14 @@ export const imageOptions = {
|
|
|
446
448
|
}
|
|
447
449
|
uri = fileWithLink.webContentLink;
|
|
448
450
|
|
|
449
|
-
|
|
450
|
-
// We can get the byte size, but not the height/width for a blob.
|
|
451
|
-
// The API seems to require an objectSize, so we'll provide a default.
|
|
452
|
-
// This is the desired size in the document, not the intrinsic image size.
|
|
453
451
|
size = {
|
|
454
|
-
height: { magnitude: 100, unit: 'PT' },
|
|
455
|
-
width: { magnitude: 100, unit: 'PT' },
|
|
452
|
+
height: { magnitude: image.getHeight ? image.getHeight() : 100, unit: 'PT' },
|
|
453
|
+
width: { magnitude: image.getWidth ? image.getWidth() : 100, unit: 'PT' },
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
cleanup = () => {
|
|
457
|
+
try { Drive.Files.update({ trashed: true }, fileId); }
|
|
458
|
+
catch (e) { console.warn(`Failed to cleanup temporary image file ${fileId}:`, e.message); }
|
|
456
459
|
};
|
|
457
460
|
} catch (e) {
|
|
458
461
|
// if something fails during setup, trash the file immediately
|
|
@@ -466,9 +469,15 @@ export const imageOptions = {
|
|
|
466
469
|
} else {
|
|
467
470
|
throw new Error('Only inserting a copied (detached) InlineImage or a Blob is supported at this time.');
|
|
468
471
|
}
|
|
472
|
+
return { uri, size, cleanup };
|
|
473
|
+
},
|
|
474
|
+
getMainRequest: ({ content: image, location, isAppend, self, leading, trailing }) => {
|
|
475
|
+
const { uri, size, cleanup } = imageOptions.__getImageUriAndSize(image);
|
|
476
|
+
|
|
469
477
|
if (!uri) {
|
|
470
478
|
throw new Error('Could not determine image URI for insertion.');
|
|
471
479
|
}
|
|
480
|
+
|
|
472
481
|
const imageRequest = {
|
|
473
482
|
insertInlineImage: {
|
|
474
483
|
uri,
|
|
@@ -480,32 +489,13 @@ export const imageOptions = {
|
|
|
480
489
|
imageRequest.insertInlineImage.objectSize = size;
|
|
481
490
|
}
|
|
482
491
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
// The image needs to be inserted into the new paragraph created by the leading newline.
|
|
487
|
-
// The new paragraph will start at location.index + 1.
|
|
488
|
-
const imageLocation = { ...location, index: location.index + 1 };
|
|
489
|
-
const imageRequestWithCorrectedLocation = { ...imageRequest, insertInlineImage: { ...imageRequest.insertInlineImage, location: imageLocation } };
|
|
490
|
-
finalRequests = [textRequest, imageRequestWithCorrectedLocation];
|
|
491
|
-
} else if (trailing) { // Insert case
|
|
492
|
-
const textRequest = { insertText: { text: trailing, location } };
|
|
493
|
-
// Insert a newline, then the image at the same location. The API inserts the image after the newline.
|
|
494
|
-
finalRequests = [textRequest, imageRequest];
|
|
495
|
-
} else {
|
|
496
|
-
// This case is for inserting into an existing paragraph (e.g., Paragraph.appendImage).
|
|
497
|
-
finalRequests = [imageRequest];
|
|
498
|
-
}
|
|
492
|
+
const textRequest = (leading || trailing) ? { insertText: { text: leading || trailing, location } } : null;
|
|
493
|
+
const imageLocation = (leading) ? { ...location, index: location.index + 1 } : location;
|
|
494
|
+
const imageRequestWithCorrectedLocation = { ...imageRequest, insertInlineImage: { ...imageRequest.insertInlineImage, location: imageLocation } };
|
|
499
495
|
|
|
500
|
-
|
|
501
|
-
const cleanup = () => {
|
|
502
|
-
try { Drive.Files.update({ trashed: true }, fileId); }
|
|
503
|
-
catch (e) { console.warn(`Failed to cleanup temporary image file ${fileId}:`, e.message); }
|
|
504
|
-
};
|
|
505
|
-
return { requests: finalRequests, cleanup };
|
|
506
|
-
}
|
|
496
|
+
const finalRequests = textRequest ? [textRequest, imageRequestWithCorrectedLocation] : [imageRequestWithCorrectedLocation];
|
|
507
497
|
|
|
508
|
-
return finalRequests;
|
|
498
|
+
return { requests: finalRequests, cleanup };
|
|
509
499
|
},
|
|
510
500
|
getStyleRequests: null, // Styling is part of the image object itself.
|
|
511
501
|
findType: 'PARAGRAPH', // We are creating a paragraph to hold the image.
|
|
@@ -7,6 +7,7 @@ import { Utils } from '../../support/utils.js';
|
|
|
7
7
|
const { is } = Utils;
|
|
8
8
|
import { ElementType } from '../enums/docsenums.js';
|
|
9
9
|
import { getElementFactory } from './elementRegistry.js';
|
|
10
|
+
import { findItem } from './elementhelpers.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* @typedef {import('./shadow.js').ShadowStructure} ShadowStructure
|
|
@@ -71,7 +72,11 @@ export class FakeElement {
|
|
|
71
72
|
throw new Error(`Invalid arguments for attached FakeElement: ${nameOrItem}. Name must start with '${shadowPrefix}'.`);
|
|
72
73
|
}
|
|
73
74
|
this.__isDetached = false;
|
|
74
|
-
|
|
75
|
+
this.__shadowDocument = shadowDocument;
|
|
76
|
+
// Cache the element's initial position. This is the key to "reviving" the element.
|
|
77
|
+
const initialItem = this.__getElementMapItem(nameOrItem);
|
|
78
|
+
this.__initialStartIndex = initialItem.startIndex;
|
|
79
|
+
this.__initialSegmentId = initialItem.__segmentId;
|
|
75
80
|
this.__name = nameOrItem;
|
|
76
81
|
this.__detachedItem = null;
|
|
77
82
|
} else if (is.object(nameOrItem)) { // Detached
|
|
@@ -105,7 +110,18 @@ export class FakeElement {
|
|
|
105
110
|
if (this.__isDetached) {
|
|
106
111
|
return this.__detachedItem;
|
|
107
112
|
}
|
|
108
|
-
|
|
113
|
+
|
|
114
|
+
// 1. Try the last known name. This is fast if nothing has changed.
|
|
115
|
+
const lastKnownItem = this.__getElementMapItem(this.__name, true); // noThrow = true
|
|
116
|
+
if (lastKnownItem) {
|
|
117
|
+
return lastKnownItem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 2. If the last name was stale, find the element by its initial position.
|
|
121
|
+
const revivedItem = findItem(this.__shadowDocument.elementMap, this.getType().toString(), this.__initialStartIndex, this.__initialSegmentId);
|
|
122
|
+
// 3. Update the internal name so the next lookup is fast again.
|
|
123
|
+
this.__name = revivedItem.__name;
|
|
124
|
+
return revivedItem;
|
|
109
125
|
}
|
|
110
126
|
|
|
111
127
|
/**
|
|
@@ -114,9 +130,10 @@ export class FakeElement {
|
|
|
114
130
|
* @returns {object} The element map item.
|
|
115
131
|
* @private
|
|
116
132
|
*/
|
|
117
|
-
__getElementMapItem(name) {
|
|
133
|
+
__getElementMapItem(name, noThrow = false) {
|
|
118
134
|
const item = this.__shadowDocument.getElement(name);
|
|
119
135
|
if (!item) {
|
|
136
|
+
if (noThrow) return null;
|
|
120
137
|
throw new Error(`element with name ${name} not found`);
|
|
121
138
|
}
|
|
122
139
|
return item;
|