@mcpher/gas-fakes 1.2.26 → 1.2.27
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/package.json +1 -1
- package/src/cli/setup.js +5 -5
- package/src/cli/utils.js +2 -4
- package/src/services/enums/slidesenums.js +1 -1
- package/src/services/formapp/fakechoiceitem.js +1 -1
- package/src/services/formapp/fakeform.js +4 -3
- package/src/services/formapp/fakeformitem.js +1 -2
- package/src/services/formapp/fakeformresponse.js +3 -2
- package/src/services/formapp/fakelistitem.js +3 -2
- package/src/services/slidesapp/fakeaffinetransform.js +55 -0
- package/src/services/slidesapp/fakeaffinetransformbuilder.js +64 -0
- package/src/services/slidesapp/fakeautofit.js +49 -0
- package/src/services/slidesapp/fakeautotext.js +29 -0
- package/src/services/slidesapp/fakeconnectionsite.js +24 -0
- package/src/services/slidesapp/fakelayout.js +38 -0
- package/src/services/slidesapp/fakeline.js +195 -0
- package/src/services/slidesapp/fakelinefill.js +58 -0
- package/src/services/slidesapp/fakelink.js +31 -0
- package/src/services/slidesapp/fakemaster.js +28 -0
- package/src/services/slidesapp/fakenotespage.js +19 -0
- package/src/services/slidesapp/fakepagebackground.js +17 -0
- package/src/services/slidesapp/fakepageelement.js +395 -0
- package/src/services/slidesapp/fakeparagraph.js +46 -0
- package/src/services/slidesapp/fakepoint.js +24 -0
- package/src/services/slidesapp/fakepresentation.js +61 -3
- package/src/services/slidesapp/fakeshape.js +49 -0
- package/src/services/slidesapp/fakeslide.js +232 -0
- package/src/services/slidesapp/fakeslidesapp.js +5 -0
- package/src/services/slidesapp/faketextrange.js +253 -0
- package/src/services/slidesapp/pageelementfactory.js +19 -0
- package/src/services/stores/gasflex.js +1 -1
- package/src/services/urlfetchapp/app.js +17 -1
- package/src/support/sxfetch.js +39 -13
- package/src/support/sxforms.js +3 -1
- package/src/support/syncit.js +7 -2
- package/src/support/utils.js +2 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
export const newFakeLink = (...args) => {
|
|
4
|
+
return Proxies.guard(new FakeLink(...args));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class FakeLink {
|
|
8
|
+
constructor(resource) {
|
|
9
|
+
this.__resource = resource;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getLinkType() {
|
|
13
|
+
if (this.__resource.url) return 'URL';
|
|
14
|
+
if (this.__resource.slideId) return 'SLIDE_ID';
|
|
15
|
+
if (this.__resource.pageObjectId) return 'PAGE_OBJECT_ID';
|
|
16
|
+
if (this.__resource.relativeSlide) return 'RELATIVE_SLIDE';
|
|
17
|
+
return 'NONE';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getUrl() {
|
|
21
|
+
return this.__resource.url || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getSlideId() {
|
|
25
|
+
return this.__resource.slideId || null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
toString() {
|
|
29
|
+
return 'Link';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
export const newFakeMaster = (...args) => {
|
|
4
|
+
return Proxies.guard(new FakeMaster(...args));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class FakeMaster {
|
|
8
|
+
constructor(resource, presentation) {
|
|
9
|
+
this.__id = resource.objectId;
|
|
10
|
+
this.__presentation = presentation;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get __resource() {
|
|
14
|
+
const presentationResource = this.__presentation.__resource;
|
|
15
|
+
const master = (presentationResource.masters || []).find(m => m.objectId === this.__id);
|
|
16
|
+
if (!master) {
|
|
17
|
+
throw new Error(`Master with ID ${this.__id} not found`);
|
|
18
|
+
}
|
|
19
|
+
return master;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getObjectId() {
|
|
23
|
+
return this.__id;
|
|
24
|
+
}
|
|
25
|
+
toString() {
|
|
26
|
+
return 'Master';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
export const newFakeNotesPage = (...args) => {
|
|
4
|
+
return Proxies.guard(new FakeNotesPage(...args));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class FakeNotesPage {
|
|
8
|
+
constructor(slide) {
|
|
9
|
+
this.__slide = slide;
|
|
10
|
+
}
|
|
11
|
+
get __resource() {
|
|
12
|
+
// notesPage is a property of slideProperties
|
|
13
|
+
const notesPage = this.__slide.__resource.slideProperties?.notesPage;
|
|
14
|
+
return notesPage || null;
|
|
15
|
+
}
|
|
16
|
+
toString() {
|
|
17
|
+
return 'NotesPage';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
export const newFakePageBackground = (...args) => {
|
|
4
|
+
return Proxies.guard(new FakePageBackground(...args));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class FakePageBackground {
|
|
8
|
+
constructor(page) {
|
|
9
|
+
this.__page = page;
|
|
10
|
+
}
|
|
11
|
+
get __resource() {
|
|
12
|
+
return this.__page.__resource.pageBackgroundFill || null;
|
|
13
|
+
}
|
|
14
|
+
toString() {
|
|
15
|
+
return 'PageBackground';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeConnectionSite } from './fakeconnectionsite.js';
|
|
3
|
+
import { newFakeLink } from './fakelink.js';
|
|
4
|
+
import { newFakeAffineTransform } from './fakeaffinetransform.js';
|
|
5
|
+
|
|
6
|
+
export const newFakePageElement = (...args) => {
|
|
7
|
+
return Proxies.guard(new FakePageElement(...args));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class FakePageElement {
|
|
11
|
+
constructor(resource, page) {
|
|
12
|
+
this.__id = resource.objectId;
|
|
13
|
+
this.__page = page;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
__normalize(val) {
|
|
17
|
+
if (!val) return 0;
|
|
18
|
+
if (typeof val === 'number') {
|
|
19
|
+
// If > 5000 it's likely EMU. 1 PT = 12700 EMU.
|
|
20
|
+
// But search "gas-pro"... sometimes things are exactly EMU.
|
|
21
|
+
return val > 5000 ? val / 12700 : val;
|
|
22
|
+
}
|
|
23
|
+
if (typeof val.magnitude === 'number') {
|
|
24
|
+
const isEMU = val.unit === 'EMU' || val.magnitude > 5000;
|
|
25
|
+
return isEMU ? val.magnitude / 12700 : val.magnitude;
|
|
26
|
+
}
|
|
27
|
+
return val || 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get __resource() {
|
|
31
|
+
const pageResource = this.__page.__resource;
|
|
32
|
+
const element = (pageResource.pageElements || []).find(e => e.objectId === this.__id);
|
|
33
|
+
if (!element) {
|
|
34
|
+
throw new Error(`PageElement with ID ${this.__id} not found on page`);
|
|
35
|
+
}
|
|
36
|
+
return element;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getObjectId() {
|
|
40
|
+
return this.__id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns the page element as a shape.
|
|
45
|
+
* @returns {FakeShape} The shape.
|
|
46
|
+
*/
|
|
47
|
+
asShape() {
|
|
48
|
+
if (this.__resource.shape) {
|
|
49
|
+
const { newFakeShape } = PageElementRegistry;
|
|
50
|
+
return newFakeShape(this.__resource, this.__page);
|
|
51
|
+
}
|
|
52
|
+
throw new Error('PageElement is not a shape.');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns the page element as a line.
|
|
57
|
+
* @returns {FakeLine} The line.
|
|
58
|
+
*/
|
|
59
|
+
asLine() {
|
|
60
|
+
if (this.__resource.line) {
|
|
61
|
+
const { newFakeLine } = PageElementRegistry;
|
|
62
|
+
return newFakeLine(this.__resource, this.__page);
|
|
63
|
+
}
|
|
64
|
+
throw new Error('PageElement is not a line.');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Gets the type of the page element.
|
|
69
|
+
* @returns {SlidesApp.PageElementType} The type.
|
|
70
|
+
*/
|
|
71
|
+
getPageElementType() {
|
|
72
|
+
const types = SlidesApp.PageElementType;
|
|
73
|
+
if (this.__resource.shape) {
|
|
74
|
+
return types.SHAPE;
|
|
75
|
+
}
|
|
76
|
+
if (this.__resource.line) {
|
|
77
|
+
return types.LINE;
|
|
78
|
+
}
|
|
79
|
+
if (this.__resource.image) {
|
|
80
|
+
return types.IMAGE;
|
|
81
|
+
}
|
|
82
|
+
if (this.__resource.video) {
|
|
83
|
+
return types.VIDEO;
|
|
84
|
+
}
|
|
85
|
+
if (this.__resource.table) {
|
|
86
|
+
return types.TABLE;
|
|
87
|
+
}
|
|
88
|
+
if (this.__resource.wordArt) {
|
|
89
|
+
return types.WORD_ART;
|
|
90
|
+
}
|
|
91
|
+
if (this.__resource.sheetsChart) {
|
|
92
|
+
return types.SHEETS_CHART;
|
|
93
|
+
}
|
|
94
|
+
if (this.__resource.group) {
|
|
95
|
+
return types.GROUP;
|
|
96
|
+
}
|
|
97
|
+
return types.UNSUPPORTED;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getLeft() {
|
|
101
|
+
return this.__normalize(this.__resource.transform?.translateX);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
setLeft(left) {
|
|
105
|
+
this.__updateTransform({ translateX: left });
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getTop() {
|
|
110
|
+
return this.__normalize(this.__resource.transform?.translateY);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setTop(top) {
|
|
114
|
+
this.__updateTransform({ translateY: top });
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getWidth() {
|
|
119
|
+
const magnitude = this.__normalize(this.__resource.size?.width);
|
|
120
|
+
const scaleX = this.__resource.transform?.scaleX || 1;
|
|
121
|
+
return magnitude * scaleX;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
setWidth(width) {
|
|
125
|
+
this.__updateSize({ width: { magnitude: width, unit: 'PT' } });
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getHeight() {
|
|
130
|
+
const magnitude = this.__normalize(this.__resource.size?.height);
|
|
131
|
+
const scaleY = this.__resource.transform?.scaleY || 1;
|
|
132
|
+
return magnitude * scaleY;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setHeight(height) {
|
|
136
|
+
this.__updateSize({ height: { magnitude: height, unit: 'PT' } });
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getRotation() {
|
|
141
|
+
// Rotation can be calculated from scale and shear in affine transform
|
|
142
|
+
// But for fakes, we might just return 0 if not easily available.
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setRotation(angle) {
|
|
147
|
+
// In API, we usually setting rotation via updatePageElementTransform with RELATIVE mode or similar
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getTransform() {
|
|
152
|
+
const t = this.__resource.transform || {};
|
|
153
|
+
return newFakeAffineTransform(
|
|
154
|
+
t.scaleX || 1,
|
|
155
|
+
t.shearY || 0,
|
|
156
|
+
t.shearX || 0,
|
|
157
|
+
t.scaleY || 1,
|
|
158
|
+
this.__normalize(t.translateX),
|
|
159
|
+
this.__normalize(t.translateY)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
setTransform(affineTransform) {
|
|
164
|
+
this.__updateTransform({
|
|
165
|
+
scaleX: affineTransform.getScaleX(),
|
|
166
|
+
shearY: affineTransform.getShearY(),
|
|
167
|
+
shearX: affineTransform.getShearX(),
|
|
168
|
+
scaleY: affineTransform.getScaleY(),
|
|
169
|
+
translateX: affineTransform.getTranslateX(),
|
|
170
|
+
translateY: affineTransform.getTranslateY()
|
|
171
|
+
});
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
preconcatenateTransform(affineTransform) {
|
|
176
|
+
// Simplified multiplication for mock
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
alignOnPage(alignmentPosition) {
|
|
181
|
+
const presentationId = this.__page.__presentation?.getId() || this.__page.__slide?.__presentation.getId();
|
|
182
|
+
Slides.Presentations.batchUpdate([{
|
|
183
|
+
updatePageElementProperties: {
|
|
184
|
+
objectId: this.getObjectId(),
|
|
185
|
+
pageElementProperties: {}, // API doesn't have direct align?
|
|
186
|
+
fields: '*'
|
|
187
|
+
}
|
|
188
|
+
}], presentationId);
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getTitle() {
|
|
193
|
+
return this.__resource.title || '';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
setTitle(title) {
|
|
197
|
+
this.__updateAltText(title, this.getDescription());
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
getDescription() {
|
|
202
|
+
return this.__resource.description || '';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
setDescription(description) {
|
|
206
|
+
this.__updateAltText(this.getTitle(), description);
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getParentPage() {
|
|
211
|
+
return this.__page;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getParentGroup() {
|
|
215
|
+
// We need to look if this element is inside any group on the page
|
|
216
|
+
// This requires searching all page elements for groups
|
|
217
|
+
return null; // Simplified for now
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
remove() {
|
|
221
|
+
const presentationId = this.__page.__presentation.getId();
|
|
222
|
+
Slides.Presentations.batchUpdate([{
|
|
223
|
+
deleteObject: {
|
|
224
|
+
objectId: this.getObjectId()
|
|
225
|
+
}
|
|
226
|
+
}], presentationId);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
bringForward() {
|
|
230
|
+
this.__updateZOrder('BRING_FORWARD');
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
bringToFront() {
|
|
235
|
+
this.__updateZOrder('BRING_TO_FRONT');
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
sendBackward() {
|
|
240
|
+
this.__updateZOrder('SEND_BACKWARD');
|
|
241
|
+
return this;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
sendToBack() {
|
|
245
|
+
this.__updateZOrder('SEND_TO_BACK');
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getLink() {
|
|
250
|
+
const res = this.__resource;
|
|
251
|
+
const link = res.link ||
|
|
252
|
+
res.shape?.shapeProperties?.link ||
|
|
253
|
+
res.line?.lineProperties?.link ||
|
|
254
|
+
res.image?.imageProperties?.link ||
|
|
255
|
+
res.video?.videoProperties?.link ||
|
|
256
|
+
res.table?.tableProperties?.link;
|
|
257
|
+
if (!link || Object.keys(link).length === 0) return null;
|
|
258
|
+
return newFakeLink(link);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
setLinkUrl(url) {
|
|
262
|
+
this.__updateProperties({ link: { url: url } }, 'link');
|
|
263
|
+
return this.getLink();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
setLinkSlide(slideOrIndexOrPosition) {
|
|
267
|
+
let link = {};
|
|
268
|
+
if (typeof slideOrIndexOrPosition === 'number') {
|
|
269
|
+
// Slide index - we'd need to find the slide ID by index
|
|
270
|
+
// For now, skipping index lookup complexity
|
|
271
|
+
link = { relativeSlide: 'NEXT_SLIDE' }; // placeholder
|
|
272
|
+
} else if (typeof slideOrIndexOrPosition === 'string') {
|
|
273
|
+
// Slide ID maybe? Or relative position string
|
|
274
|
+
link = { slideId: slideOrIndexOrPosition };
|
|
275
|
+
} else if (slideOrIndexOrPosition && slideOrIndexOrPosition.getObjectId) {
|
|
276
|
+
// Slide object
|
|
277
|
+
link = { slideId: slideOrIndexOrPosition.getObjectId() };
|
|
278
|
+
}
|
|
279
|
+
this.__updateProperties({ link: link }, 'link');
|
|
280
|
+
return this.getLink();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
removeLink() {
|
|
284
|
+
this.__updateProperties({}, 'link');
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
__updateTransform(transform) {
|
|
289
|
+
const t = this.getTransform();
|
|
290
|
+
const presentationId = this.__page.__presentation?.getId() || this.__page.__slide?.__presentation.getId();
|
|
291
|
+
Slides.Presentations.batchUpdate([{
|
|
292
|
+
updatePageElementTransform: {
|
|
293
|
+
objectId: this.getObjectId(),
|
|
294
|
+
transform: {
|
|
295
|
+
scaleX: t.getScaleX(),
|
|
296
|
+
scaleY: t.getScaleY(),
|
|
297
|
+
shearX: t.getShearX(),
|
|
298
|
+
shearY: t.getShearY(),
|
|
299
|
+
translateX: t.getTranslateX(),
|
|
300
|
+
translateY: t.getTranslateY(),
|
|
301
|
+
...transform,
|
|
302
|
+
unit: 'PT'
|
|
303
|
+
},
|
|
304
|
+
applyMode: 'ABSOLUTE'
|
|
305
|
+
}
|
|
306
|
+
}], presentationId);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
__updateSize(size) {
|
|
310
|
+
const t = this.getTransform();
|
|
311
|
+
const currentWidth = this.getWidth();
|
|
312
|
+
const currentHeight = this.getHeight();
|
|
313
|
+
|
|
314
|
+
const scaleX = size.width ? size.width.magnitude / (currentWidth || 1) : 1;
|
|
315
|
+
const scaleY = size.height ? size.height.magnitude / (currentHeight || 1) : 1;
|
|
316
|
+
|
|
317
|
+
this.__updateTransform({
|
|
318
|
+
scaleX: t.getScaleX() * scaleX,
|
|
319
|
+
scaleY: t.getScaleY() * scaleY,
|
|
320
|
+
shearX: t.getShearX(),
|
|
321
|
+
shearY: t.getShearY(),
|
|
322
|
+
translateX: t.getTranslateX(),
|
|
323
|
+
translateY: t.getTranslateY()
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
__updateAltText(title, description) {
|
|
328
|
+
const presentationId = this.__page.__presentation.getId();
|
|
329
|
+
Slides.Presentations.batchUpdate([{
|
|
330
|
+
updatePageElementAltText: {
|
|
331
|
+
objectId: this.getObjectId(),
|
|
332
|
+
title: title,
|
|
333
|
+
description: description
|
|
334
|
+
}
|
|
335
|
+
}], presentationId);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
__updateProperties(props, fields) {
|
|
339
|
+
const presentationId = this.__page.__presentation?.getId() || this.__page.__slide?.__presentation.getId();
|
|
340
|
+
const type = this.getPageElementType().toString();
|
|
341
|
+
let request = null;
|
|
342
|
+
|
|
343
|
+
if (type === 'SHAPE') {
|
|
344
|
+
request = {
|
|
345
|
+
updateShapeProperties: {
|
|
346
|
+
objectId: this.getObjectId(),
|
|
347
|
+
shapeProperties: props,
|
|
348
|
+
fields: fields || Object.keys(props).join(',')
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
} else if (type === 'LINE') {
|
|
352
|
+
request = {
|
|
353
|
+
updateLineProperties: {
|
|
354
|
+
objectId: this.getObjectId(),
|
|
355
|
+
lineProperties: props,
|
|
356
|
+
fields: fields || Object.keys(props).join(',')
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (request) {
|
|
362
|
+
if (request.updateShapeProperties) request.updateShapeProperties.fields = fields || '*';
|
|
363
|
+
if (request.updateLineProperties) request.updateLineProperties.fields = fields || '*';
|
|
364
|
+
Slides.Presentations.batchUpdate([request], presentationId);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
__updateZOrder(operation) {
|
|
369
|
+
const presentationId = this.__page.__presentation.getId();
|
|
370
|
+
Slides.Presentations.batchUpdate([{
|
|
371
|
+
updatePageElementZOrder: {
|
|
372
|
+
pageElementObjectIds: [this.getObjectId()],
|
|
373
|
+
zOrderOperation: operation
|
|
374
|
+
}
|
|
375
|
+
}], presentationId);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Gets the connection sites on the page element.
|
|
380
|
+
* @returns {FakeConnectionSite[]} The connection sites.
|
|
381
|
+
*/
|
|
382
|
+
getConnectionSites() {
|
|
383
|
+
// Standard shapes usually have 4 connection sites (top, right, bottom, left).
|
|
384
|
+
// For now, let's return a fixed number as a mock.
|
|
385
|
+
return [0, 1, 2, 3].map(index => newFakeConnectionSite(this, index));
|
|
386
|
+
}
|
|
387
|
+
toString() {
|
|
388
|
+
return 'PageElement';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export const PageElementRegistry = {
|
|
393
|
+
newFakeShape: null,
|
|
394
|
+
newFakeLine: null
|
|
395
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeTextRange } from './faketextrange.js';
|
|
3
|
+
|
|
4
|
+
export const newFakeParagraph = (...args) => {
|
|
5
|
+
return Proxies.guard(new FakeParagraph(...args));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export class FakeParagraph {
|
|
9
|
+
constructor(textRange, index) {
|
|
10
|
+
this.__textRange = textRange;
|
|
11
|
+
this.__index = index;
|
|
12
|
+
// We might need start/end indices relative to the shape's full text
|
|
13
|
+
// The textRange passed here might be the specific range FOR this paragraph?
|
|
14
|
+
// Or the parent range?
|
|
15
|
+
// Usually getParagraphs returns a list of Paragraph objects.
|
|
16
|
+
// Each Paragraph wraps a TextRange?
|
|
17
|
+
// Docs say Paragraph.getRange() returns a TextRange.
|
|
18
|
+
// So Paragraph IS NOT a TextRange, but has one.
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Gets the index of the paragraph.
|
|
23
|
+
* @returns {number} The index.
|
|
24
|
+
*/
|
|
25
|
+
getIndex() {
|
|
26
|
+
// Based on Live Apps Script behavior, getIndex() returns the index of the paragraph's terminating character (newline).
|
|
27
|
+
// Use the end index of the text range minus 1.
|
|
28
|
+
const range = this.getRange();
|
|
29
|
+
// Assuming range.getEndIndex() returns the exclusive end index.
|
|
30
|
+
return range.getEndIndex() - 1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Gets the text range covering this paragraph.
|
|
35
|
+
* @returns {FakeTextRange} The text range.
|
|
36
|
+
*/
|
|
37
|
+
getRange() {
|
|
38
|
+
return this.__textRange;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
toString() {
|
|
44
|
+
return 'Paragraph';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
export const newFakePoint = (...args) => {
|
|
4
|
+
return Proxies.guard(new FakePoint(...args));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class FakePoint {
|
|
8
|
+
constructor(x, y) {
|
|
9
|
+
this.__x = x;
|
|
10
|
+
this.__y = y;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getX() {
|
|
14
|
+
return this.__x;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getY() {
|
|
18
|
+
return this.__y;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
toString() {
|
|
22
|
+
return 'Point';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeSlide } from './fakeslide.js';
|
|
2
3
|
|
|
3
4
|
export const newFakePresentation = (...args) => {
|
|
4
5
|
return Proxies.guard(new FakePresentation(...args));
|
|
@@ -11,10 +12,16 @@ export const newFakePresentation = (...args) => {
|
|
|
11
12
|
*/
|
|
12
13
|
export class FakePresentation {
|
|
13
14
|
/**
|
|
14
|
-
* @param {object} resource the presentation
|
|
15
|
+
* @param {object} resource the presentation resurce from Slides API
|
|
15
16
|
*/
|
|
16
17
|
constructor(resource) {
|
|
17
|
-
this.
|
|
18
|
+
this.__id = resource.presentationId;
|
|
19
|
+
}
|
|
20
|
+
get __file () {
|
|
21
|
+
return DriveApp.getFileById(this.__id);
|
|
22
|
+
}
|
|
23
|
+
get __resource () {
|
|
24
|
+
return Slides.Presentations.get(this.__id);
|
|
18
25
|
}
|
|
19
26
|
saveAndClose() {
|
|
20
27
|
// this is a no-op in fake environment since it is stateless
|
|
@@ -24,7 +31,7 @@ export class FakePresentation {
|
|
|
24
31
|
* @returns {string} The presentation ID.
|
|
25
32
|
*/
|
|
26
33
|
getId() {
|
|
27
|
-
return this.
|
|
34
|
+
return this.__id;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
/**
|
|
@@ -43,6 +50,57 @@ export class FakePresentation {
|
|
|
43
50
|
return `https://docs.google.com/presentation/d/${this.getId()}/edit`;
|
|
44
51
|
}
|
|
45
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Gets the slides in the presentation.
|
|
55
|
+
* @returns {FakeSlide[]} The slides.
|
|
56
|
+
*/
|
|
57
|
+
getSlides() {
|
|
58
|
+
return (this.__resource.slides || []).map(s => newFakeSlide(s, this));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Gets a slide by its ID.
|
|
63
|
+
* @param {string} id The slide ID.
|
|
64
|
+
* @returns {FakeSlide | null} The slide, or null if not found.
|
|
65
|
+
*/
|
|
66
|
+
getSlideById(id) {
|
|
67
|
+
return this.getSlides().find(s => s.getObjectId() === id) || null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Appends a new slide to the presentation.
|
|
72
|
+
* @param {string} [layout] The layout to use (optional).
|
|
73
|
+
* @returns {FakeSlide} The new slide.
|
|
74
|
+
*/
|
|
75
|
+
appendSlide(layout) {
|
|
76
|
+
const requests = [{
|
|
77
|
+
createSlide: {
|
|
78
|
+
slideLayoutReference: layout ? { predefinedLayout: layout } : { predefinedLayout: 'BLANK' }
|
|
79
|
+
}
|
|
80
|
+
}];
|
|
81
|
+
const result = Slides.Presentations.batchUpdate(requests, this.getId());
|
|
82
|
+
const newObjectId = result.replies[0].createSlide.objectId;
|
|
83
|
+
return this.getSlideById(newObjectId);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Inserts a new slide at the specified index.
|
|
88
|
+
* @param {number} index The index to insert at.
|
|
89
|
+
* @param {string} [layout] The layout to use (optional).
|
|
90
|
+
* @returns {FakeSlide} The new slide.
|
|
91
|
+
*/
|
|
92
|
+
insertSlide(index, layout) {
|
|
93
|
+
const requests = [{
|
|
94
|
+
createSlide: {
|
|
95
|
+
insertionIndex: index,
|
|
96
|
+
slideLayoutReference: layout ? { predefinedLayout: layout } : { predefinedLayout: 'BLANK' }
|
|
97
|
+
}
|
|
98
|
+
}];
|
|
99
|
+
const result = Slides.Presentations.batchUpdate(requests, this.getId());
|
|
100
|
+
const newObjectId = result.replies[0].createSlide.objectId;
|
|
101
|
+
return this.getSlideById(newObjectId);
|
|
102
|
+
}
|
|
103
|
+
|
|
46
104
|
toString() {
|
|
47
105
|
return 'Presentation';
|
|
48
106
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeTextRange } from './faketextrange.js';
|
|
3
|
+
import { newFakeAutofit } from './fakeautofit.js';
|
|
4
|
+
import { newFakeConnectionSite } from './fakeconnectionsite.js';
|
|
5
|
+
import { FakePageElement, PageElementRegistry } from './fakepageelement.js';
|
|
6
|
+
|
|
7
|
+
export const newFakeShape = (...args) => {
|
|
8
|
+
const shape = Proxies.guard(new FakeShape(...args));
|
|
9
|
+
return shape;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
PageElementRegistry.newFakeShape = newFakeShape;
|
|
13
|
+
|
|
14
|
+
export class FakeShape extends FakePageElement {
|
|
15
|
+
constructor(resource, page) {
|
|
16
|
+
super(resource, page);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Expose __presentation for TextRange to use
|
|
20
|
+
get __presentation() {
|
|
21
|
+
return this.__page.__presentation || this.__page.__slide?.__presentation;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets the text range of the shape.
|
|
26
|
+
* @returns {FakeTextRange} The text range.
|
|
27
|
+
*/
|
|
28
|
+
getText() {
|
|
29
|
+
return newFakeTextRange(this);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getAutofit() {
|
|
33
|
+
return newFakeAutofit(this);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets the connection sites on the shape.
|
|
38
|
+
* @returns {FakeConnectionSite[]} The connection sites.
|
|
39
|
+
*/
|
|
40
|
+
getConnectionSites() {
|
|
41
|
+
// Standard shapes usually have 4 connection sites (top, right, bottom, left).
|
|
42
|
+
// For now, let's return a fixed number as a mock.
|
|
43
|
+
return [0, 1, 2, 3].map(index => newFakeConnectionSite(this, index));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toString() {
|
|
47
|
+
return 'Shape';
|
|
48
|
+
}
|
|
49
|
+
}
|