@mcpher/gas-fakes 2.5.2 → 2.5.4
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 +17 -3
- 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/content/contentservice.js +3 -2
- package/src/services/enums/baseenums.js +47 -0
- package/src/services/enums/contentenums.js +1 -3
- package/src/services/enums/scriptenums.js +31 -4
- package/src/services/enums/slidesenums.js +3 -1
- package/src/services/enums/xmlenums.js +14 -0
- package/src/services/html/serverworker.js +1 -1
- package/src/services/scriptapp/app.js +14 -7
- package/src/services/scriptapp/fakeauthorizationinfo.js +4 -4
- 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
- package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +92 -12
- package/src/services/spreadsheetapp/fakespreadsheet.js +360 -62
- package/src/services/spreadsheetapp/fakespreadsheettheme.js +53 -0
- package/src/services/urlfetchapp/app.js +216 -175
- package/src/services/xmlservice/app.js +3 -78
- package/src/services/xmlservice/fakeattribute.js +15 -0
- package/src/services/xmlservice/fakecdata.js +40 -0
- package/src/services/xmlservice/fakecomment.js +34 -0
- package/src/services/xmlservice/fakecontent.js +51 -0
- package/src/services/xmlservice/fakedoctype.js +68 -0
- package/src/services/xmlservice/fakedocument.js +110 -13
- package/src/services/xmlservice/fakeelement.js +297 -82
- package/src/services/xmlservice/fakeentityref.js +54 -0
- package/src/services/xmlservice/fakeformat.js +67 -22
- package/src/services/xmlservice/fakeprocessinginstruction.js +44 -0
- package/src/services/xmlservice/faketext.js +39 -0
- package/src/services/xmlservice/fakexmlservice.js +118 -0
- package/src/support/sxfetch.js +60 -0
- package/tools/omlx.env.example +6 -0
- package/tools/omlx_mcp_server.cjs +157 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
2
|
import { newFakeSlide } from './fakeslide.js';
|
|
3
3
|
import { newFakeMaster } from './fakemaster.js';
|
|
4
|
+
import { newFakeNotesMaster } from './fakenotesmaster.js';
|
|
5
|
+
import { newFakeLayout } from './fakelayout.js';
|
|
4
6
|
import { newFakeColorScheme } from './fakecolorscheme.js';
|
|
5
7
|
|
|
6
8
|
export const newFakePresentation = (...args) => {
|
|
@@ -28,6 +30,45 @@ export class FakePresentation {
|
|
|
28
30
|
saveAndClose() {
|
|
29
31
|
// this is a no-op in fake environment since it is stateless
|
|
30
32
|
}
|
|
33
|
+
|
|
34
|
+
addEditor(emailAddress) {
|
|
35
|
+
this.__file.addEditor(emailAddress);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
addEditors(emailAddresses) {
|
|
40
|
+
this.__file.addEditors(emailAddresses);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addViewer(emailAddress) {
|
|
45
|
+
this.__file.addViewer(emailAddress);
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
addViewers(emailAddresses) {
|
|
50
|
+
this.__file.addViewers(emailAddresses);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getEditors() {
|
|
55
|
+
return this.__file.getEditors();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getViewers() {
|
|
59
|
+
return this.__file.getViewers();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
removeEditor(emailAddress) {
|
|
63
|
+
this.__file.removeEditor(emailAddress);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
removeViewer(emailAddress) {
|
|
68
|
+
this.__file.removeViewer(emailAddress);
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
31
72
|
/**
|
|
32
73
|
* Gets the ID of the presentation.
|
|
33
74
|
* @returns {string} The presentation ID.
|
|
@@ -44,6 +85,10 @@ export class FakePresentation {
|
|
|
44
85
|
return this.__resource.title;
|
|
45
86
|
}
|
|
46
87
|
|
|
88
|
+
setName(name) {
|
|
89
|
+
this.__file.setName(name);
|
|
90
|
+
}
|
|
91
|
+
|
|
47
92
|
/**
|
|
48
93
|
* Gets the URL of the presentation.
|
|
49
94
|
* @returns {string} The presentation URL.
|
|
@@ -52,6 +97,38 @@ export class FakePresentation {
|
|
|
52
97
|
return `https://docs.google.com/presentation/d/${this.getId()}/edit`;
|
|
53
98
|
}
|
|
54
99
|
|
|
100
|
+
getPageHeight() {
|
|
101
|
+
const size = this.__resource.pageSize;
|
|
102
|
+
return this.__normalize(size?.height);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getPageWidth() {
|
|
106
|
+
const size = this.__resource.pageSize;
|
|
107
|
+
return this.__normalize(size?.width);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getNotesPageHeight() {
|
|
111
|
+
const size = this.__resource.notesMaster?.notesVisualProperties?.pageSize || this.__resource.pageSize;
|
|
112
|
+
return this.__normalize(size?.height);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getNotesPageWidth() {
|
|
116
|
+
const size = this.__resource.notesMaster?.notesVisualProperties?.pageSize || this.__resource.pageSize;
|
|
117
|
+
return this.__normalize(size?.width);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
__normalize(val) {
|
|
121
|
+
if (!val) return 0;
|
|
122
|
+
if (typeof val === 'number') {
|
|
123
|
+
return val > 5000 ? val / 12700 : val;
|
|
124
|
+
}
|
|
125
|
+
if (typeof val.magnitude === 'number') {
|
|
126
|
+
const isEMU = val.unit === 'EMU' || val.magnitude > 5000;
|
|
127
|
+
return isEMU ? val.magnitude / 12700 : val.magnitude;
|
|
128
|
+
}
|
|
129
|
+
return val || 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
55
132
|
/**
|
|
56
133
|
* Gets the masters in the presentation.
|
|
57
134
|
* @returns {FakeMaster[]} The masters.
|
|
@@ -60,6 +137,14 @@ export class FakePresentation {
|
|
|
60
137
|
return (this.__resource.masters || []).map(m => newFakeMaster(m, this));
|
|
61
138
|
}
|
|
62
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Gets the layouts in the presentation.
|
|
142
|
+
* @returns {FakeLayout[]} The layouts.
|
|
143
|
+
*/
|
|
144
|
+
getLayouts() {
|
|
145
|
+
return (this.__resource.layouts || []).map(l => newFakeLayout(l, this));
|
|
146
|
+
}
|
|
147
|
+
|
|
63
148
|
/**
|
|
64
149
|
* Gets a master by its ID.
|
|
65
150
|
* @param {string} id The master ID.
|
|
@@ -69,6 +154,15 @@ export class FakePresentation {
|
|
|
69
154
|
return this.getMasters().find(m => m.getObjectId() === id) || null;
|
|
70
155
|
}
|
|
71
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Gets the notes master in the presentation.
|
|
159
|
+
* @returns {FakeNotesMaster | null} The notes master.
|
|
160
|
+
*/
|
|
161
|
+
getNotesMaster() {
|
|
162
|
+
const notesMaster = this.__resource.notesMaster;
|
|
163
|
+
return notesMaster ? newFakeNotesMaster(notesMaster, this) : null;
|
|
164
|
+
}
|
|
165
|
+
|
|
72
166
|
/**
|
|
73
167
|
* Gets the slides in the presentation.
|
|
74
168
|
* @returns {FakeSlide[]} The slides.
|
|
@@ -86,6 +180,36 @@ export class FakePresentation {
|
|
|
86
180
|
return this.getSlides().find(s => s.getObjectId() === id) || null;
|
|
87
181
|
}
|
|
88
182
|
|
|
183
|
+
getPageElementById(id) {
|
|
184
|
+
const pages = [...this.getSlides(), ...this.getLayouts(), ...this.getMasters()];
|
|
185
|
+
const nm = this.getNotesMaster();
|
|
186
|
+
if (nm) pages.push(nm);
|
|
187
|
+
|
|
188
|
+
for (const page of pages) {
|
|
189
|
+
const el = page.getPageElementById(id);
|
|
190
|
+
if (el) return el;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
getSelection() {
|
|
196
|
+
return null; // Mock
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
replaceAllText(findText, replaceText, matchCase = false) {
|
|
200
|
+
const requests = [{
|
|
201
|
+
replaceAllText: {
|
|
202
|
+
replaceText,
|
|
203
|
+
containsText: {
|
|
204
|
+
text: findText,
|
|
205
|
+
matchCase
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}];
|
|
209
|
+
const response = Slides.Presentations.batchUpdate({ requests }, this.getId());
|
|
210
|
+
return response.replies[0].replaceAllText.occurrencesChanged || 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
89
213
|
/**
|
|
90
214
|
* Appends a new slide to the presentation.
|
|
91
215
|
* @param {string} [layout] The layout to use (optional).
|
|
@@ -100,7 +224,7 @@ export class FakePresentation {
|
|
|
100
224
|
}
|
|
101
225
|
}];
|
|
102
226
|
try {
|
|
103
|
-
Slides.Presentations.batchUpdate(requests, this.getId());
|
|
227
|
+
Slides.Presentations.batchUpdate({ requests }, this.getId());
|
|
104
228
|
} catch (err) {
|
|
105
229
|
// If it already exists, it means a previous attempt succeeded but timed out
|
|
106
230
|
if (!err?.message?.includes('already exists')) throw err;
|
|
@@ -124,7 +248,7 @@ export class FakePresentation {
|
|
|
124
248
|
}
|
|
125
249
|
}];
|
|
126
250
|
try {
|
|
127
|
-
Slides.Presentations.batchUpdate(requests, this.getId());
|
|
251
|
+
Slides.Presentations.batchUpdate({ requests }, this.getId());
|
|
128
252
|
} catch (err) {
|
|
129
253
|
if (!err?.message?.includes('already exists')) throw err;
|
|
130
254
|
}
|
|
@@ -3,6 +3,7 @@ import { newFakeTextRange } from './faketextrange.js';
|
|
|
3
3
|
import { newFakeAutofit } from './fakeautofit.js';
|
|
4
4
|
import { newFakeConnectionSite } from './fakeconnectionsite.js';
|
|
5
5
|
import { FakePageElement, PageElementRegistry } from './fakepageelement.js';
|
|
6
|
+
import { newFakeFill } from './fakefill.js';
|
|
6
7
|
|
|
7
8
|
export const newFakeShape = (...args) => {
|
|
8
9
|
const shape = Proxies.guard(new FakeShape(...args));
|
|
@@ -33,6 +34,14 @@ export class FakeShape extends FakePageElement {
|
|
|
33
34
|
return newFakeAutofit(this);
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Gets the fill of the shape.
|
|
39
|
+
* @returns {FakeFill} The fill object.
|
|
40
|
+
*/
|
|
41
|
+
getFill() {
|
|
42
|
+
return newFakeFill(this);
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
/**
|
|
37
46
|
* Gets the connection sites on the shape.
|
|
38
47
|
* @returns {FakeConnectionSite[]} The connection sites.
|
|
@@ -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
|
+
}
|