@mcpher/gas-fakes 2.5.2 → 2.5.3
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 +8 -1
- package/package.json +1 -1
- package/pngs/srv.jpg +0 -0
- package/src/cli/app.js +1 -0
- package/src/cli/togas.js +23 -14
- package/src/index.js +1 -0
- package/src/services/advslides/fakeadvslides.js +11 -5
- package/src/services/base/app.js +9 -0
- package/src/services/base/fakebase.js +28 -0
- package/src/services/common/fakeadvresource.js +3 -2
- package/src/services/enums/baseenums.js +47 -0
- package/src/services/enums/slidesenums.js +3 -1
- package/src/services/html/serverworker.js +1 -1
- package/src/services/slidesapp/app.js +5 -0
- package/src/services/slidesapp/fakeautofit.js +1 -1
- package/src/services/slidesapp/fakeborder.js +106 -0
- package/src/services/slidesapp/fakecolorscheme.js +1 -1
- package/src/services/slidesapp/fakefill.js +216 -0
- package/src/services/slidesapp/fakegroup.js +35 -0
- package/src/services/slidesapp/fakeimage.js +118 -0
- package/src/services/slidesapp/fakelayout.js +351 -0
- package/src/services/slidesapp/fakeline.js +2 -2
- package/src/services/slidesapp/fakelinefill.js +15 -16
- package/src/services/slidesapp/fakelink.js +20 -3
- package/src/services/slidesapp/fakelist.js +36 -0
- package/src/services/slidesapp/fakeliststyle.js +105 -0
- package/src/services/slidesapp/fakemaster.js +358 -0
- package/src/services/slidesapp/fakenotesmaster.js +125 -0
- package/src/services/slidesapp/fakenotespage.js +102 -2
- package/src/services/slidesapp/fakepagebackground.js +109 -1
- package/src/services/slidesapp/fakepageelement.js +157 -18
- package/src/services/slidesapp/fakepageelementrange.js +28 -0
- package/src/services/slidesapp/fakepagerange.js +28 -0
- package/src/services/slidesapp/fakeparagraphstyle.js +139 -0
- package/src/services/slidesapp/fakepicturefill.js +32 -0
- package/src/services/slidesapp/fakepresentation.js +126 -2
- package/src/services/slidesapp/fakeshape.js +9 -0
- package/src/services/slidesapp/fakeslide.js +216 -24
- package/src/services/slidesapp/fakesolidfill.js +45 -0
- package/src/services/slidesapp/fakespeakerspotlight.js +18 -0
- package/src/services/slidesapp/faketable.js +55 -9
- package/src/services/slidesapp/faketablecell.js +141 -12
- package/src/services/slidesapp/faketablecellrange.js +28 -0
- package/src/services/slidesapp/faketablecolumn.js +72 -0
- package/src/services/slidesapp/faketablerow.js +31 -0
- package/src/services/slidesapp/faketextrange.js +179 -135
- package/src/services/slidesapp/faketextstyle.js +158 -0
- package/src/services/slidesapp/fakevideo.js +35 -0
- package/src/services/slidesapp/fakewordart.js +22 -0
- package/src/services/slidesapp/pageelementfactory.js +24 -1
|
@@ -5,7 +5,6 @@ import { newFakeMaster } from './fakemaster.js';
|
|
|
5
5
|
import { newFakePageElement } from './fakepageelement.js';
|
|
6
6
|
import { newFakePageBackground } from './fakepagebackground.js';
|
|
7
7
|
import { newFakeColorScheme } from './fakecolorscheme.js';
|
|
8
|
-
import { asSpecificPageElement } from './pageelementfactory.js';
|
|
9
8
|
|
|
10
9
|
export const newFakeSlide = (...args) => {
|
|
11
10
|
return Proxies.guard(new FakeSlide(...args));
|
|
@@ -75,7 +74,11 @@ export class FakeSlide {
|
|
|
75
74
|
* @returns {FakePageElement[]} The page elements.
|
|
76
75
|
*/
|
|
77
76
|
getPageElements() {
|
|
78
|
-
return (this.__resource.pageElements || []).map(pe =>
|
|
77
|
+
return (this.__resource.pageElements || []).map(pe => newFakePageElement(pe, this));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getPageElementById(id) {
|
|
81
|
+
return this.getPageElements().find(pe => pe.getObjectId() === id) || null;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
/**
|
|
@@ -83,8 +86,7 @@ export class FakeSlide {
|
|
|
83
86
|
* @returns {FakePageBackground} The background.
|
|
84
87
|
*/
|
|
85
88
|
getBackground() {
|
|
86
|
-
|
|
87
|
-
return background ? newFakePageBackground(this) : null;
|
|
89
|
+
return newFakePageBackground(this);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
/**
|
|
@@ -101,7 +103,7 @@ export class FakeSlide {
|
|
|
101
103
|
*/
|
|
102
104
|
getTables() {
|
|
103
105
|
return this.getPageElements()
|
|
104
|
-
.filter(pe => pe.toString() === '
|
|
106
|
+
.filter(pe => pe.getPageElementType().toString() === 'TABLE')
|
|
105
107
|
.map(pe => pe.asTable());
|
|
106
108
|
}
|
|
107
109
|
|
|
@@ -111,21 +113,186 @@ export class FakeSlide {
|
|
|
111
113
|
*/
|
|
112
114
|
getShapes() {
|
|
113
115
|
return this.getPageElements()
|
|
114
|
-
.filter(pe => pe.toString() === '
|
|
116
|
+
.filter(pe => pe.getPageElementType().toString() === 'SHAPE')
|
|
115
117
|
.map(pe => pe.asShape());
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Gets the list of images on the slide.
|
|
122
|
+
* @returns {FakeImage[]} The images.
|
|
123
|
+
*/
|
|
124
|
+
getImages() {
|
|
125
|
+
return this.getPageElements()
|
|
126
|
+
.filter(pe => pe.getPageElementType().toString() === 'IMAGE')
|
|
127
|
+
.map(pe => pe.asImage());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Inserts a Google Sheets chart on the slide.
|
|
132
|
+
* @param {EmbeddedChart} chart The chart.
|
|
133
|
+
* @returns {SheetsChart} The inserted chart.
|
|
134
|
+
*/
|
|
135
|
+
insertSheetsChart(chart) {
|
|
136
|
+
const presentationId = this.__presentation.getId();
|
|
137
|
+
const objectId = `chart_${Math.random().toString(36).substring(2, 11)}`;
|
|
138
|
+
const spreadsheetId = chart.getSpreadsheetId ? chart.getSpreadsheetId() : '';
|
|
139
|
+
const chartId = chart.getChartId ? chart.getChartId() : 0;
|
|
140
|
+
|
|
141
|
+
const requests = [{
|
|
142
|
+
createSheetsChart: {
|
|
143
|
+
objectId,
|
|
144
|
+
spreadsheetId,
|
|
145
|
+
chartId,
|
|
146
|
+
linkingMode: 'LINKED',
|
|
147
|
+
elementProperties: {
|
|
148
|
+
pageObjectId: this.getObjectId(),
|
|
149
|
+
size: {
|
|
150
|
+
width: { magnitude: 400, unit: 'PT' },
|
|
151
|
+
height: { magnitude: 300, unit: 'PT' }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}];
|
|
156
|
+
|
|
157
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
158
|
+
return this.getPageElementById(objectId).asSheetsChart();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Inserts a Google Sheets chart as an image on the slide.
|
|
163
|
+
* @param {EmbeddedChart} chart The chart.
|
|
164
|
+
* @returns {Image} The inserted image.
|
|
165
|
+
*/
|
|
166
|
+
insertSheetsChartAsImage(chart) {
|
|
167
|
+
// In GAS, this is usually implemented as a static image capture.
|
|
168
|
+
// For the fake, we'll just insert it as a non-linked chart or a placeholder image.
|
|
169
|
+
const presentationId = this.__presentation.getId();
|
|
170
|
+
const objectId = `chart_img_${Math.random().toString(36).substring(2, 11)}`;
|
|
171
|
+
|
|
172
|
+
const requests = [{
|
|
173
|
+
createImage: {
|
|
174
|
+
objectId,
|
|
175
|
+
url: 'https://via.placeholder.com/400x300?text=Sheets+Chart',
|
|
176
|
+
elementProperties: {
|
|
177
|
+
pageObjectId: this.getObjectId()
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}];
|
|
181
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
182
|
+
return this.getPageElementById(objectId).asImage();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Inserts a video at the top left corner of the page with a default size from the provided URL.
|
|
187
|
+
* @param {string} videoUrl The video URL.
|
|
188
|
+
* @param {number} [left]
|
|
189
|
+
* @param {number} [top]
|
|
190
|
+
* @param {number} [width]
|
|
191
|
+
* @param {number} [height]
|
|
192
|
+
* @returns {FakeVideo} The inserted video.
|
|
193
|
+
*/
|
|
194
|
+
insertVideo(videoUrl, left = 0, top = 0, width = 300, height = 200) {
|
|
195
|
+
const presentationId = this.__presentation.getId();
|
|
196
|
+
const objectId = `video_${Math.random().toString(36).substring(2, 11)}`;
|
|
197
|
+
|
|
198
|
+
// Standard YouTube URL parsing
|
|
199
|
+
let videoId = videoUrl;
|
|
200
|
+
if (videoUrl.includes('v=')) {
|
|
201
|
+
videoId = videoUrl.split('v=')[1].split('&')[0];
|
|
202
|
+
} else if (videoUrl.includes('youtu.be/')) {
|
|
203
|
+
videoId = videoUrl.split('youtu.be/')[1].split('?')[0];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const requests = [{
|
|
207
|
+
createVideo: {
|
|
208
|
+
objectId,
|
|
209
|
+
source: 'YOUTUBE',
|
|
210
|
+
id: videoId,
|
|
211
|
+
elementProperties: {
|
|
212
|
+
pageObjectId: this.getObjectId(),
|
|
213
|
+
size: {
|
|
214
|
+
width: { magnitude: width, unit: 'PT' },
|
|
215
|
+
height: { magnitude: height, unit: 'PT' }
|
|
216
|
+
},
|
|
217
|
+
transform: {
|
|
218
|
+
scaleX: 1,
|
|
219
|
+
scaleY: 1,
|
|
220
|
+
translateX: left,
|
|
221
|
+
translateY: top,
|
|
222
|
+
unit: 'PT'
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}];
|
|
227
|
+
|
|
228
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
229
|
+
return this.getPageElementById(objectId).asVideo();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Inserts an image.
|
|
234
|
+
* @param {string|FakeBlob|FakeImage} urlOrBlobOrImage The image to insert.
|
|
235
|
+
* @param {number} [left]
|
|
236
|
+
* @param {number} [top]
|
|
237
|
+
* @param {number} [width]
|
|
238
|
+
* @param {number} [height]
|
|
239
|
+
* @returns {FakeImage} The new image.
|
|
240
|
+
*/
|
|
241
|
+
insertImage(urlOrBlobOrImage, left = 0, top = 0, width = 300, height = 300) {
|
|
242
|
+
const presentationId = this.__presentation.getId();
|
|
243
|
+
const objectId = `image_${Math.random().toString(36).substring(2, 11)}`;
|
|
244
|
+
let sourceUrl = '';
|
|
245
|
+
|
|
246
|
+
if (typeof urlOrBlobOrImage === 'string') {
|
|
247
|
+
sourceUrl = urlOrBlobOrImage;
|
|
248
|
+
} else if (urlOrBlobOrImage && urlOrBlobOrImage.getSourceUrl) {
|
|
249
|
+
sourceUrl = urlOrBlobOrImage.getSourceUrl();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const requests = [{
|
|
253
|
+
createImage: {
|
|
254
|
+
objectId,
|
|
255
|
+
url: sourceUrl || 'https://via.placeholder.com/150', // Fallback URL
|
|
256
|
+
elementProperties: {
|
|
257
|
+
pageObjectId: this.getObjectId(),
|
|
258
|
+
size: {
|
|
259
|
+
width: { magnitude: width, unit: 'PT' },
|
|
260
|
+
height: { magnitude: height, unit: 'PT' }
|
|
261
|
+
},
|
|
262
|
+
transform: {
|
|
263
|
+
scaleX: 1,
|
|
264
|
+
scaleY: 1,
|
|
265
|
+
translateX: left,
|
|
266
|
+
translateY: top,
|
|
267
|
+
unit: 'PT'
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}];
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
if (!err?.message?.includes('already exists')) throw err;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const elements = this.getPageElements();
|
|
280
|
+
const newElement = elements.find(e => e.getObjectId() === objectId);
|
|
281
|
+
if (!newElement) throw new Error('New image not found');
|
|
282
|
+
return newElement.asImage();
|
|
283
|
+
}
|
|
284
|
+
|
|
118
285
|
/**
|
|
119
286
|
* Deletes the slide.
|
|
120
287
|
*/
|
|
121
288
|
remove() {
|
|
122
289
|
const presentationId = this.__presentation.getId();
|
|
123
290
|
try {
|
|
124
|
-
Slides.Presentations.batchUpdate([{
|
|
291
|
+
Slides.Presentations.batchUpdate({ requests: [{
|
|
125
292
|
deleteObject: {
|
|
126
293
|
objectId: this.getObjectId()
|
|
127
294
|
}
|
|
128
|
-
}], presentationId);
|
|
295
|
+
}] }, presentationId);
|
|
129
296
|
} catch (err) {
|
|
130
297
|
// If not found, it's already deleted (perhaps on a previous timeouted attempt)
|
|
131
298
|
if (!err?.message?.includes('not found')) throw err;
|
|
@@ -168,7 +335,7 @@ export class FakeSlide {
|
|
|
168
335
|
}];
|
|
169
336
|
|
|
170
337
|
try {
|
|
171
|
-
Slides.Presentations.batchUpdate(requests, presentationId);
|
|
338
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
172
339
|
} catch (err) {
|
|
173
340
|
if (!err?.message?.includes('already exists')) throw err;
|
|
174
341
|
}
|
|
@@ -196,6 +363,31 @@ export class FakeSlide {
|
|
|
196
363
|
return newElement.asShape();
|
|
197
364
|
}
|
|
198
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Groups all the specified page elements.
|
|
368
|
+
* @param {FakePageElement[]} elements The elements to group.
|
|
369
|
+
* @returns {FakeGroup} The new group.
|
|
370
|
+
*/
|
|
371
|
+
group(elements) {
|
|
372
|
+
const presentationId = this.__presentation.getId();
|
|
373
|
+
const objectId = `group_${Math.random().toString(36).substring(2, 11)}`;
|
|
374
|
+
const childrenObjectIds = elements.map(e => e.getObjectId());
|
|
375
|
+
|
|
376
|
+
const requests = [{
|
|
377
|
+
groupObjects: {
|
|
378
|
+
groupObjectId: objectId,
|
|
379
|
+
childrenObjectIds
|
|
380
|
+
}
|
|
381
|
+
}];
|
|
382
|
+
|
|
383
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
384
|
+
|
|
385
|
+
const allElements = this.getPageElements();
|
|
386
|
+
const newElement = allElements.find(e => e.getObjectId() === objectId);
|
|
387
|
+
if (!newElement) throw new Error('New group not found after batchUpdate');
|
|
388
|
+
return newElement.asGroup();
|
|
389
|
+
}
|
|
390
|
+
|
|
199
391
|
/**
|
|
200
392
|
* Inserts a text box.
|
|
201
393
|
* @param {string} text The text to insert.
|
|
@@ -247,7 +439,7 @@ export class FakeSlide {
|
|
|
247
439
|
}];
|
|
248
440
|
|
|
249
441
|
try {
|
|
250
|
-
Slides.Presentations.batchUpdate(requests, presentationId);
|
|
442
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
251
443
|
} catch (err) {
|
|
252
444
|
if (!err?.message?.includes('already exists')) throw err;
|
|
253
445
|
}
|
|
@@ -324,7 +516,7 @@ export class FakeSlide {
|
|
|
324
516
|
}
|
|
325
517
|
|
|
326
518
|
try {
|
|
327
|
-
Slides.Presentations.batchUpdate([request], presentationId);
|
|
519
|
+
Slides.Presentations.batchUpdate({ requests: [request] }, presentationId);
|
|
328
520
|
} catch (err) {
|
|
329
521
|
if (!err?.message?.includes('already exists')) throw err;
|
|
330
522
|
}
|
|
@@ -339,16 +531,16 @@ export class FakeSlide {
|
|
|
339
531
|
if (typeof rowsOrTable !== 'number') {
|
|
340
532
|
const sourceTable = rowsOrTable;
|
|
341
533
|
const targetTable = newTable;
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
for (let c = 0; c <
|
|
349
|
-
const text =
|
|
534
|
+
const numRows = sourceTable.getNumRows();
|
|
535
|
+
|
|
536
|
+
for (let r = 0; r < numRows; r++) {
|
|
537
|
+
const sourceRow = sourceTable.getRow(r);
|
|
538
|
+
const targetRow = targetTable.getRow(r);
|
|
539
|
+
const numCells = sourceRow.getNumCells();
|
|
540
|
+
for (let c = 0; c < numCells; c++) {
|
|
541
|
+
const text = sourceRow.getCell(c).getText().asString();
|
|
350
542
|
if (text) {
|
|
351
|
-
|
|
543
|
+
targetRow.getCell(c).getText().setText(text);
|
|
352
544
|
}
|
|
353
545
|
}
|
|
354
546
|
}
|
|
@@ -370,7 +562,7 @@ export class FakeSlide {
|
|
|
370
562
|
}];
|
|
371
563
|
|
|
372
564
|
try {
|
|
373
|
-
Slides.Presentations.batchUpdate(requests, presentationId);
|
|
565
|
+
Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
374
566
|
} catch (err) {
|
|
375
567
|
if (!err?.message?.includes('already exists')) throw err;
|
|
376
568
|
}
|
|
@@ -388,12 +580,12 @@ export class FakeSlide {
|
|
|
388
580
|
move(index) {
|
|
389
581
|
const presentationId = this.__presentation.getId();
|
|
390
582
|
|
|
391
|
-
Slides.Presentations.batchUpdate([{
|
|
583
|
+
Slides.Presentations.batchUpdate({ requests: [{
|
|
392
584
|
updateSlidesPosition: {
|
|
393
585
|
slideObjectIds: [this.getObjectId()],
|
|
394
586
|
insertionIndex: index
|
|
395
587
|
}
|
|
396
|
-
}], presentationId);
|
|
588
|
+
}] }, presentationId);
|
|
397
589
|
}
|
|
398
590
|
|
|
399
591
|
/**
|
|
@@ -416,7 +608,7 @@ export class FakeSlide {
|
|
|
416
608
|
}
|
|
417
609
|
}];
|
|
418
610
|
|
|
419
|
-
const response = Slides.Presentations.batchUpdate(requests, presentationId);
|
|
611
|
+
const response = Slides.Presentations.batchUpdate({ requests }, presentationId);
|
|
420
612
|
return response.replies[0].replaceAllText.occurrencesChanged || 0;
|
|
421
613
|
}
|
|
422
614
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeColorBuilder } from '../common/fakecolorbuilder.js';
|
|
3
|
+
import { ThemeColorType } from '../enums/slidesenums.js';
|
|
4
|
+
|
|
5
|
+
export const newFakeSolidFill = (...args) => {
|
|
6
|
+
return Proxies.guard(new FakeSolidFill(...args));
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export class FakeSolidFill {
|
|
10
|
+
constructor(fill) {
|
|
11
|
+
this.__fill = fill;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get __solidFill() {
|
|
15
|
+
return this.__fill.__fill.solidFill || {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getAlpha() {
|
|
19
|
+
return this.__solidFill.alpha !== undefined ? this.__solidFill.alpha : 1.0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getColor() {
|
|
23
|
+
const apiColor = this.__solidFill.color || {};
|
|
24
|
+
const builder = newFakeColorBuilder();
|
|
25
|
+
|
|
26
|
+
const rgb = apiColor.rgbColor;
|
|
27
|
+
if (rgb) {
|
|
28
|
+
const hex = '#' + [rgb.red, rgb.green, rgb.blue].map(v => {
|
|
29
|
+
const val = Math.round((v || 0) * 255);
|
|
30
|
+
const hex = val.toString(16);
|
|
31
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
32
|
+
}).join('');
|
|
33
|
+
builder.setRgbColor(hex);
|
|
34
|
+
} else if (apiColor.themeColor) {
|
|
35
|
+
builder.setThemeColor(ThemeColorType[apiColor.themeColor]);
|
|
36
|
+
} else {
|
|
37
|
+
builder.setRgbColor('#FFFFFF');
|
|
38
|
+
}
|
|
39
|
+
return builder.build();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
toString() {
|
|
43
|
+
return 'SolidFill';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { FakePageElement, PageElementRegistry } from './fakepageelement.js';
|
|
3
|
+
|
|
4
|
+
export const newFakeSpeakerSpotlight = (...args) => {
|
|
5
|
+
return Proxies.guard(new FakeSpeakerSpotlight(...args));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
PageElementRegistry.newFakeSpeakerSpotlight = newFakeSpeakerSpotlight;
|
|
9
|
+
|
|
10
|
+
export class FakeSpeakerSpotlight extends FakePageElement {
|
|
11
|
+
constructor(resource, page) {
|
|
12
|
+
super(resource, page);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
toString() {
|
|
16
|
+
return 'SpeakerSpotlight';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
2
|
import { FakePageElement, PageElementRegistry } from './fakepageelement.js';
|
|
3
3
|
import { newFakeTableRow } from './faketablerow.js';
|
|
4
|
+
import { newFakeTableColumn } from './faketablecolumn.js';
|
|
4
5
|
|
|
5
6
|
export const newFakeTable = (...args) => {
|
|
6
7
|
return Proxies.guard(new FakeTable(...args));
|
|
@@ -18,13 +19,41 @@ export class FakeTable extends FakePageElement {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @returns {
|
|
22
|
+
* Appends a new column to the right of the last column of the table.
|
|
23
|
+
* @returns {FakeTableColumn} The new appended column.
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
-
return (this.
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
appendColumn() {
|
|
26
|
+
return this.insertColumn(this.getNumColumns());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Appends a new row below the last row of the table.
|
|
31
|
+
* @returns {FakeTableRow} The new appended row.
|
|
32
|
+
*/
|
|
33
|
+
appendRow() {
|
|
34
|
+
return this.insertRow(this.getNumRows());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gets a column by its index.
|
|
39
|
+
* @param {number} index The column index.
|
|
40
|
+
* @returns {FakeTableColumn} The column.
|
|
41
|
+
*/
|
|
42
|
+
getColumn(index) {
|
|
43
|
+
if (index < 0 || index >= this.getNumColumns()) {
|
|
44
|
+
throw new Error(`Column index ${index} out of bounds`);
|
|
45
|
+
}
|
|
46
|
+
return newFakeTableColumn(this, index);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Gets a cell by its row and column index.
|
|
51
|
+
* @param {number} rowIndex The row index.
|
|
52
|
+
* @param {number} colIndex The column index.
|
|
53
|
+
* @returns {FakeTableCell} The cell.
|
|
54
|
+
*/
|
|
55
|
+
getCell(rowIndex, colIndex) {
|
|
56
|
+
return this.getRow(rowIndex).getCell(colIndex);
|
|
28
57
|
}
|
|
29
58
|
|
|
30
59
|
/**
|
|
@@ -33,11 +62,10 @@ export class FakeTable extends FakePageElement {
|
|
|
33
62
|
* @returns {FakeTableRow} The row.
|
|
34
63
|
*/
|
|
35
64
|
getRow(index) {
|
|
36
|
-
|
|
37
|
-
if (index < 0 || index >= rows.length) {
|
|
65
|
+
if (index < 0 || index >= this.getNumRows()) {
|
|
38
66
|
throw new Error(`Row index ${index} out of bounds`);
|
|
39
67
|
}
|
|
40
|
-
return
|
|
68
|
+
return newFakeTableRow(this, index);
|
|
41
69
|
}
|
|
42
70
|
|
|
43
71
|
/**
|
|
@@ -57,6 +85,24 @@ export class FakeTable extends FakePageElement {
|
|
|
57
85
|
return rows.length > 0 ? (rows[0].tableCells || []).length : 0;
|
|
58
86
|
}
|
|
59
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Inserts a new column at the specified index of the table.
|
|
90
|
+
* @param {number} index The index.
|
|
91
|
+
* @returns {FakeTableColumn} The new column.
|
|
92
|
+
*/
|
|
93
|
+
insertColumn(index) {
|
|
94
|
+
throw new Error('Table.insertColumn() not yet implemented');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Inserts a new row at the specified index of the table.
|
|
99
|
+
* @param {number} index The index.
|
|
100
|
+
* @returns {FakeTableRow} The new row.
|
|
101
|
+
*/
|
|
102
|
+
insertRow(index) {
|
|
103
|
+
throw new Error('Table.insertRow() not yet implemented');
|
|
104
|
+
}
|
|
105
|
+
|
|
60
106
|
toString() {
|
|
61
107
|
return 'Table';
|
|
62
108
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
2
|
import { newFakeTextRange } from './faketextrange.js';
|
|
3
|
+
import { newFakeFill } from './fakefill.js';
|
|
4
|
+
import { ContentAlignment, CellMergeState } from '../enums/slidesenums.js';
|
|
3
5
|
|
|
4
6
|
export const newFakeTableCell = (...args) => {
|
|
5
7
|
return Proxies.guard(new FakeTableCell(...args));
|
|
@@ -12,30 +14,157 @@ export class FakeTableCell {
|
|
|
12
14
|
this.__colIndex = colIndex;
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Returns the unique ID for this object.
|
|
19
|
+
* @returns {string} The ID.
|
|
20
|
+
*/
|
|
21
|
+
getObjectId() {
|
|
22
|
+
return this.__table.getObjectId();
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
get __resource() {
|
|
16
26
|
return this.__table.__resource.table?.tableRows?.[this.__rowIndex]?.tableCells?.[this.__colIndex];
|
|
17
27
|
}
|
|
18
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Returns the 0-based column index of the table cell.
|
|
31
|
+
* @returns {number} The column index.
|
|
32
|
+
*/
|
|
33
|
+
getColumnIndex() {
|
|
34
|
+
return this.__colIndex;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns the column span of the table cell.
|
|
39
|
+
* @returns {number} The column span.
|
|
40
|
+
*/
|
|
41
|
+
getColumnSpan() {
|
|
42
|
+
return this.__resource?.columnSpan || 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns the ContentAlignment of the text in the table cell.
|
|
47
|
+
* @returns {ContentAlignment} The content alignment.
|
|
48
|
+
*/
|
|
49
|
+
getContentAlignment() {
|
|
50
|
+
const alignment = this.__resource?.tableCellProperties?.contentAlignment;
|
|
51
|
+
return ContentAlignment[alignment || 'TOP'];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Returns the fill of the table cell.
|
|
56
|
+
* @returns {FakeFill} The fill.
|
|
57
|
+
*/
|
|
58
|
+
getFill() {
|
|
59
|
+
return newFakeFill(this);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns the head cell of this table cell.
|
|
64
|
+
* @returns {FakeTableCell | null} The head cell or null.
|
|
65
|
+
*/
|
|
66
|
+
getHeadCell() {
|
|
67
|
+
// Basic implementation: if this is merged, find its head.
|
|
68
|
+
// For now, return null as we don't have full merge tracking.
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Returns the merge state of the table cell.
|
|
74
|
+
* @returns {CellMergeState} The merge state.
|
|
75
|
+
*/
|
|
76
|
+
getMergeState() {
|
|
77
|
+
const rowSpan = this.getRowSpan();
|
|
78
|
+
const colSpan = this.getColumnSpan();
|
|
79
|
+
if (rowSpan > 1 || colSpan > 1) {
|
|
80
|
+
return CellMergeState.HEAD;
|
|
81
|
+
}
|
|
82
|
+
// TODO: Detect MERGED state if we can find the head.
|
|
83
|
+
return CellMergeState.NORMAL;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns the table column containing the current cell.
|
|
88
|
+
* @returns {FakeTableColumn} The column.
|
|
89
|
+
*/
|
|
90
|
+
getParentColumn() {
|
|
91
|
+
return this.__table.getColumn(this.__colIndex);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns the table row containing the current cell.
|
|
96
|
+
* @returns {FakeTableRow} The row.
|
|
97
|
+
*/
|
|
98
|
+
getParentRow() {
|
|
99
|
+
return this.__table.getRow(this.__rowIndex);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Returns the table containing the current cell.
|
|
104
|
+
* @returns {FakeTable} The table.
|
|
105
|
+
*/
|
|
106
|
+
getParentTable() {
|
|
107
|
+
return this.__table;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Returns the 0-based row index of the table cell.
|
|
112
|
+
* @returns {number} The row index.
|
|
113
|
+
*/
|
|
114
|
+
getRowIndex() {
|
|
115
|
+
return this.__rowIndex;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the row span of the table cell.
|
|
120
|
+
* @returns {number} The row span.
|
|
121
|
+
*/
|
|
122
|
+
getRowSpan() {
|
|
123
|
+
return this.__resource?.rowSpan || 1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sets the ContentAlignment of the text in the table cell.
|
|
128
|
+
* @param {ContentAlignment} alignment The alignment.
|
|
129
|
+
* @returns {FakeTableCell} This cell.
|
|
130
|
+
*/
|
|
131
|
+
setContentAlignment(alignment) {
|
|
132
|
+
const presentationId = this.__table.__presentation.getId();
|
|
133
|
+
Slides.Presentations.batchUpdate({ requests: [{
|
|
134
|
+
updateTableCellProperties: {
|
|
135
|
+
objectId: this.__table.getObjectId(),
|
|
136
|
+
tableCellProperties: {
|
|
137
|
+
contentAlignment: alignment.toString()
|
|
138
|
+
},
|
|
139
|
+
fields: 'contentAlignment',
|
|
140
|
+
tableRange: {
|
|
141
|
+
location: {
|
|
142
|
+
rowIndex: this.__rowIndex,
|
|
143
|
+
columnIndex: this.__colIndex
|
|
144
|
+
},
|
|
145
|
+
rowSpan: 1,
|
|
146
|
+
columnSpan: 1
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}] }, presentationId);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
19
153
|
/**
|
|
20
154
|
* Gets the text in the cell.
|
|
21
155
|
* @returns {FakeTextRange} The text range.
|
|
22
156
|
*/
|
|
23
157
|
getText() {
|
|
24
|
-
// FakeTableCell in Slides API doesn't have a direct shape resource to pass to TextRange?
|
|
25
|
-
// Actually, TableCell has text property in Slides API.
|
|
26
|
-
// Wait, let's check Slides API TableCell resource.
|
|
27
|
-
// It has `text` field of type `TextContent`.
|
|
28
|
-
// FakeTextRange expects a `shape` with `__resource.shape.text`.
|
|
29
|
-
// We might need to adapt FakeTextRange or mock a shape-like object.
|
|
30
|
-
|
|
31
|
-
// Let's create a proxy for shape so FakeTextRange can work.
|
|
32
158
|
const mockShape = {
|
|
33
159
|
getObjectId: () => this.__table.getObjectId(),
|
|
34
|
-
__resource
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
160
|
+
get __resource() {
|
|
161
|
+
return {
|
|
162
|
+
shape: {
|
|
163
|
+
text: this.__target.__resource?.text || { textElements: [] }
|
|
164
|
+
}
|
|
165
|
+
};
|
|
38
166
|
},
|
|
167
|
+
__target: this,
|
|
39
168
|
__cellLocation: {
|
|
40
169
|
rowIndex: this.__rowIndex,
|
|
41
170
|
columnIndex: this.__colIndex
|