@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.
Files changed (50) hide show
  1. package/README.md +8 -1
  2. package/package.json +1 -1
  3. package/pngs/srv.jpg +0 -0
  4. package/src/cli/app.js +1 -0
  5. package/src/cli/togas.js +23 -14
  6. package/src/index.js +1 -0
  7. package/src/services/advslides/fakeadvslides.js +11 -5
  8. package/src/services/base/app.js +9 -0
  9. package/src/services/base/fakebase.js +28 -0
  10. package/src/services/common/fakeadvresource.js +3 -2
  11. package/src/services/enums/baseenums.js +47 -0
  12. package/src/services/enums/slidesenums.js +3 -1
  13. package/src/services/html/serverworker.js +1 -1
  14. package/src/services/slidesapp/app.js +5 -0
  15. package/src/services/slidesapp/fakeautofit.js +1 -1
  16. package/src/services/slidesapp/fakeborder.js +106 -0
  17. package/src/services/slidesapp/fakecolorscheme.js +1 -1
  18. package/src/services/slidesapp/fakefill.js +216 -0
  19. package/src/services/slidesapp/fakegroup.js +35 -0
  20. package/src/services/slidesapp/fakeimage.js +118 -0
  21. package/src/services/slidesapp/fakelayout.js +351 -0
  22. package/src/services/slidesapp/fakeline.js +2 -2
  23. package/src/services/slidesapp/fakelinefill.js +15 -16
  24. package/src/services/slidesapp/fakelink.js +20 -3
  25. package/src/services/slidesapp/fakelist.js +36 -0
  26. package/src/services/slidesapp/fakeliststyle.js +105 -0
  27. package/src/services/slidesapp/fakemaster.js +358 -0
  28. package/src/services/slidesapp/fakenotesmaster.js +125 -0
  29. package/src/services/slidesapp/fakenotespage.js +102 -2
  30. package/src/services/slidesapp/fakepagebackground.js +109 -1
  31. package/src/services/slidesapp/fakepageelement.js +157 -18
  32. package/src/services/slidesapp/fakepageelementrange.js +28 -0
  33. package/src/services/slidesapp/fakepagerange.js +28 -0
  34. package/src/services/slidesapp/fakeparagraphstyle.js +139 -0
  35. package/src/services/slidesapp/fakepicturefill.js +32 -0
  36. package/src/services/slidesapp/fakepresentation.js +126 -2
  37. package/src/services/slidesapp/fakeshape.js +9 -0
  38. package/src/services/slidesapp/fakeslide.js +216 -24
  39. package/src/services/slidesapp/fakesolidfill.js +45 -0
  40. package/src/services/slidesapp/fakespeakerspotlight.js +18 -0
  41. package/src/services/slidesapp/faketable.js +55 -9
  42. package/src/services/slidesapp/faketablecell.js +141 -12
  43. package/src/services/slidesapp/faketablecellrange.js +28 -0
  44. package/src/services/slidesapp/faketablecolumn.js +72 -0
  45. package/src/services/slidesapp/faketablerow.js +31 -0
  46. package/src/services/slidesapp/faketextrange.js +179 -135
  47. package/src/services/slidesapp/faketextstyle.js +158 -0
  48. package/src/services/slidesapp/fakevideo.js +35 -0
  49. package/src/services/slidesapp/fakewordart.js +22 -0
  50. 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 => asSpecificPageElement(newFakePageElement(pe, this)));
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
- const background = this.__resource.pageBackgroundFill;
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() === 'Table')
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() === 'Shape')
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 rows = sourceTable.getRows();
343
- const targetRows = targetTable.getRows();
344
-
345
- for (let r = 0; r < rows.length; r++) {
346
- const cells = rows[r].getCells();
347
- const targetCells = targetRows[r].getCells();
348
- for (let c = 0; c < cells.length; c++) {
349
- const text = cells[c].getText().asString();
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
- targetCells[c].getText().setText(text);
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
- * Gets the rows in the table.
22
- * @returns {FakeTableRow[]} The rows.
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
- getRows() {
25
- return (this.__resource.table?.tableRows || []).map((_, index) =>
26
- newFakeTableRow(this, index)
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
- const rows = this.getRows();
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 rows[index];
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
- shape: {
36
- text: this.__resource?.text || { textElements: [] }
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