@mcpher/gas-fakes 2.3.11 → 2.3.13
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 +14 -14
- package/gas-fakes.js +1 -0
- package/gf_agent/scripts/builder.js +26 -1
- package/package.json +1 -1
- package/src/cli/lib-manager.js +14 -4
- package/src/cli/setup.js +17 -4
- package/src/services/chartsapp/fakechartsapp.js +6 -1
- package/src/services/driveapp/driveiterators.js +53 -8
- package/src/services/driveapp/fakedriveapp.js +49 -15
- package/src/services/driveapp/fakedrivefile.js +105 -0
- package/src/services/driveapp/fakedrivefolder.js +8 -0
- package/src/services/driveapp/fakedrivemeta.js +68 -16
- package/src/services/driveapp/fakefolderapp.js +19 -6
- package/src/services/enums/chartsenums.js +53 -20
- package/src/services/enums/driveenums.js +1 -0
- package/src/services/slidesapp/fakeautofit.js +23 -15
- package/src/services/slidesapp/fakecolorscheme.js +160 -0
- package/src/services/slidesapp/fakelayout.js +11 -1
- package/src/services/slidesapp/fakemaster.js +10 -0
- package/src/services/slidesapp/fakepresentation.js +27 -0
- package/src/services/slidesapp/fakeslide.js +9 -0
- package/src/services/slidesapp/faketextrange.js +6 -11
- package/src/services/spreadsheetapp/chartenummapping.js +15 -0
- package/src/services/spreadsheetapp/fakebooleancondition.js +119 -0
- package/src/services/spreadsheetapp/fakecellimage.js +42 -0
- package/src/services/spreadsheetapp/fakecellimagebuilder.js +59 -0
- package/src/services/spreadsheetapp/fakeconditionalformatrule.js +55 -0
- package/src/services/spreadsheetapp/fakeconditionalformatrulebuilder.js +330 -0
- package/src/services/spreadsheetapp/fakedevelopermetadata.js +32 -4
- package/src/services/spreadsheetapp/fakedevelopermetadatalocation.js +27 -5
- package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +155 -21
- package/src/services/spreadsheetapp/fakegradientcondition.js +71 -0
- package/src/services/spreadsheetapp/fakesheet.js +63 -0
- package/src/services/spreadsheetapp/fakesheetrange.js +12 -1
- package/src/services/spreadsheetapp/fakespreadsheet.js +30 -11
- package/src/services/spreadsheetapp/fakespreadsheetapp.js +21 -3
- package/src/services/urlfetchapp/app.js +33 -1
- package/src/support/fileiterators.js +3 -1
- package/src/support/filesharers.js +7 -3
- package/src/support/peeker.js +8 -2
- package/src/support/sheetutils.js +1 -0
- package/src/support/sxdrive.js +26 -15
- package/src/support/syncit.js +4 -6
- package/src/support/workersync/synchronizer.js +24 -4
- package/src/support/workersync/worker.js +13 -2
- package/summarize_advanced.js +0 -69
|
@@ -88,7 +88,7 @@ export class FakeDriveMeta {
|
|
|
88
88
|
return this
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
const newMeta = this.__withPlatform(() => Drive.Files.get(this.getId(), { fields }, { allow404: false }))
|
|
91
|
+
console.log("CHECK", fields, Reflect.has(this.meta, fields), Object.keys(this.meta)); const newMeta = this.__withPlatform(() => Drive.Files.get(this.getId(), { fields }, { allow404: false }))
|
|
92
92
|
// need to merge this with already known fields
|
|
93
93
|
this.meta = { ...this.meta, ...newMeta }
|
|
94
94
|
improveFileCache(this.getId(), this.meta, fields)
|
|
@@ -207,10 +207,10 @@ export class FakeDriveMeta {
|
|
|
207
207
|
|
|
208
208
|
/**
|
|
209
209
|
* get the file viewers
|
|
210
|
-
* @returns {FakeUser} the file viewers
|
|
210
|
+
* @returns {FakeUser[]} the file viewers and commenters
|
|
211
211
|
*/
|
|
212
212
|
getViewers() {
|
|
213
|
-
return getSharers(this.getId(), 'reader')
|
|
213
|
+
return getSharers(this.getId(), ['reader', 'commenter'])
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
/**
|
|
@@ -399,7 +399,8 @@ export class FakeDriveMeta {
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
__managePermissions(emails, role, add = true) {
|
|
402
|
-
const
|
|
402
|
+
const arr = is.array(emails) ? emails : [emails];
|
|
403
|
+
const emailAddresses = arr.map(e => is.string(e) ? e : e?.getEmail?.() || e?.emailAddress || e);
|
|
403
404
|
|
|
404
405
|
if (add) {
|
|
405
406
|
// In Apps Script, this is not atomic. It just loops.
|
|
@@ -499,59 +500,110 @@ export class FakeDriveMeta {
|
|
|
499
500
|
setSecurityUpdateEnabled() {
|
|
500
501
|
return notYetImplemented('setSecurityUpdateEnabled')
|
|
501
502
|
}
|
|
502
|
-
getAccess() {
|
|
503
|
-
|
|
503
|
+
getAccess(user) {
|
|
504
|
+
const email = is.string(user) ? user : user?.getEmail?.() || user?.emailAddress || user;
|
|
505
|
+
const { permissions } = this.__withPlatform(() => Drive.Permissions.list(this.getId(), {
|
|
506
|
+
fields: 'permissions(role,emailAddress,type)'
|
|
507
|
+
}));
|
|
508
|
+
const p = permissions.find(p => p.emailAddress === email && p.type === 'user');
|
|
509
|
+
if (!p) return Permission.NONE;
|
|
510
|
+
if (p.role === 'owner') return Permission.OWNER;
|
|
511
|
+
if (p.role === 'writer') return Permission.EDIT;
|
|
512
|
+
if (p.role === 'commenter') return Permission.COMMENT;
|
|
513
|
+
if (p.role === 'reader') return Permission.VIEW;
|
|
514
|
+
return Permission.NONE;
|
|
504
515
|
}
|
|
505
516
|
|
|
506
517
|
|
|
507
|
-
revokePermissions() {
|
|
508
|
-
|
|
518
|
+
revokePermissions(user) {
|
|
519
|
+
const email = is.string(user) ? user : user?.getEmail?.() || user?.emailAddress || user;
|
|
520
|
+
const { permissions } = this.__withPlatform(() => Drive.Permissions.list(this.getId(), {
|
|
521
|
+
fields: 'permissions(id,emailAddress,type)'
|
|
522
|
+
}));
|
|
523
|
+
// Revoke all permissions for this user
|
|
524
|
+
const userPermissions = permissions.filter(p => p.emailAddress === email && p.type === 'user');
|
|
525
|
+
userPermissions.forEach(p => {
|
|
526
|
+
this.__withPlatform(() => Drive.Permissions.delete(this.getId(), p.id));
|
|
527
|
+
});
|
|
528
|
+
if (userPermissions.length > 0) {
|
|
529
|
+
improveFileCache(this.getId(), null);
|
|
530
|
+
}
|
|
531
|
+
return this;
|
|
509
532
|
}
|
|
510
533
|
|
|
511
534
|
|
|
512
|
-
setOwner() {
|
|
513
|
-
|
|
535
|
+
setOwner(user) {
|
|
536
|
+
const email = is.string(user) ? user : user?.getEmail?.() || user?.emailAddress || user;
|
|
537
|
+
this.__withPlatform(() => Drive.Permissions.create({
|
|
538
|
+
role: 'owner',
|
|
539
|
+
type: 'user',
|
|
540
|
+
emailAddress: email
|
|
541
|
+
}, this.getId(), { transferOwnership: true }));
|
|
542
|
+
|
|
543
|
+
improveFileCache(this.getId(), null);
|
|
544
|
+
return this;
|
|
514
545
|
}
|
|
515
546
|
|
|
516
547
|
addViewers() {
|
|
517
548
|
const { nargs, matchThrow } = signatureArgs(arguments, "addViewers");
|
|
518
549
|
const [emailAddresses] = arguments;
|
|
519
|
-
if (nargs !== 1 || !is.array(emailAddresses)
|
|
550
|
+
if (nargs !== 1 || !is.array(emailAddresses)) matchThrow();
|
|
520
551
|
return this.__managePermissions(emailAddresses, 'reader', true);
|
|
521
552
|
}
|
|
522
553
|
|
|
523
554
|
addViewer() {
|
|
524
555
|
const { nargs, matchThrow } = signatureArgs(arguments, "addViewer");
|
|
525
556
|
const [emailAddress] = arguments;
|
|
526
|
-
if (nargs !== 1
|
|
557
|
+
if (nargs !== 1) matchThrow();
|
|
527
558
|
return this.__managePermissions(emailAddress, 'reader', true);
|
|
528
559
|
}
|
|
529
560
|
|
|
530
561
|
removeEditor() {
|
|
531
562
|
const { nargs, matchThrow } = signatureArgs(arguments, "removeEditor");
|
|
532
563
|
const [emailAddress] = arguments;
|
|
533
|
-
if (nargs !== 1
|
|
564
|
+
if (nargs !== 1) matchThrow();
|
|
534
565
|
return this.__managePermissions(emailAddress, 'writer', false);
|
|
535
566
|
}
|
|
536
567
|
|
|
537
568
|
addEditor() {
|
|
538
569
|
const { nargs, matchThrow } = signatureArgs(arguments, "addEditor");
|
|
539
570
|
const [emailAddress] = arguments;
|
|
540
|
-
if (nargs !== 1
|
|
571
|
+
if (nargs !== 1) matchThrow();
|
|
541
572
|
return this.__managePermissions(emailAddress, 'writer', true);
|
|
542
573
|
}
|
|
543
574
|
|
|
544
575
|
removeViewer() {
|
|
545
576
|
const { nargs, matchThrow } = signatureArgs(arguments, "removeViewer");
|
|
546
577
|
const [emailAddress] = arguments;
|
|
547
|
-
if (nargs !== 1
|
|
578
|
+
if (nargs !== 1) matchThrow();
|
|
548
579
|
return this.__managePermissions(emailAddress, 'reader', false);
|
|
549
580
|
}
|
|
550
581
|
|
|
551
582
|
addEditors() {
|
|
552
583
|
const { nargs, matchThrow } = signatureArgs(arguments, "addEditors");
|
|
553
584
|
const [emailAddresses] = arguments;
|
|
554
|
-
if (nargs !== 1 || !is.array(emailAddresses)
|
|
585
|
+
if (nargs !== 1 || !is.array(emailAddresses)) matchThrow();
|
|
555
586
|
return this.__managePermissions(emailAddresses, 'writer', true);
|
|
556
587
|
}
|
|
588
|
+
|
|
589
|
+
addCommenter() {
|
|
590
|
+
const { nargs, matchThrow } = signatureArgs(arguments, "addCommenter");
|
|
591
|
+
const [emailAddress] = arguments;
|
|
592
|
+
if (nargs !== 1) matchThrow();
|
|
593
|
+
return this.__managePermissions(emailAddress, 'commenter', true);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
addCommenters() {
|
|
597
|
+
const { nargs, matchThrow } = signatureArgs(arguments, "addCommenters");
|
|
598
|
+
const [emailAddresses] = arguments;
|
|
599
|
+
if (nargs !== 1 || !is.array(emailAddresses)) matchThrow();
|
|
600
|
+
return this.__managePermissions(emailAddresses, 'commenter', true);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
removeCommenter() {
|
|
604
|
+
const { nargs, matchThrow } = signatureArgs(arguments, "removeCommenter");
|
|
605
|
+
const [emailAddress] = arguments;
|
|
606
|
+
if (nargs !== 1) matchThrow();
|
|
607
|
+
return this.__managePermissions(emailAddress, 'commenter', false);
|
|
608
|
+
}
|
|
557
609
|
}
|
|
@@ -61,8 +61,25 @@ class FakeFolderApp {
|
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
createShortcut({ targetId, resourceKey, file = {} }) {
|
|
65
|
+
const target = Drive.Files.get(targetId, { fields: 'name' })
|
|
66
|
+
return this.createFile({
|
|
67
|
+
nargs: 2,
|
|
68
|
+
name: target.name,
|
|
69
|
+
content: "",
|
|
70
|
+
file: {
|
|
71
|
+
mimeType: 'application/vnd.google-apps.shortcut',
|
|
72
|
+
shortcutDetails: {
|
|
73
|
+
targetId,
|
|
74
|
+
targetResourceKey: resourceKey
|
|
75
|
+
},
|
|
76
|
+
...file
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
createShortcutForTargetIdAndResourceKey({ targetId, resourceKey, file = {} }) {
|
|
82
|
+
return this.createShortcut({ targetId, resourceKey, file })
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
|
|
@@ -92,10 +109,6 @@ class FakeFolderApp {
|
|
|
92
109
|
return notYetImplemented('removeFile')
|
|
93
110
|
}
|
|
94
111
|
|
|
95
|
-
createShortcut() {
|
|
96
|
-
return notYetImplemented('createShortcut')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
112
|
/**
|
|
100
113
|
* the args are a bit flexible
|
|
101
114
|
* we can have 1 arg which mucst be ablob
|
|
@@ -1,24 +1,57 @@
|
|
|
1
1
|
import { newFakeGasenum } from "@mcpher/fake-gasenum";
|
|
2
2
|
|
|
3
3
|
export const ChartType = newFakeGasenum([
|
|
4
|
-
"AREA", //
|
|
5
|
-
"BAR", //
|
|
6
|
-
"COLUMN", //
|
|
7
|
-
"COMBO", //
|
|
8
|
-
"HISTOGRAM", //
|
|
9
|
-
"LINE", //
|
|
10
|
-
"PIE", //
|
|
11
|
-
"SCATTER", //
|
|
12
|
-
"STEPPED_AREA", //
|
|
13
|
-
"WATERFALL", //
|
|
14
|
-
"SCORECARD", //
|
|
15
|
-
"RADAR", //
|
|
16
|
-
"GAUGE", //
|
|
17
|
-
"ORG", //
|
|
18
|
-
"TIMELINE", //
|
|
19
|
-
"TREE_MAP", //
|
|
20
|
-
"TABLE", //
|
|
21
|
-
"CANDLESTICK", //
|
|
22
|
-
"GEOMAP", //
|
|
23
|
-
"BUBBLE", //
|
|
4
|
+
"AREA", // Enum Area chart.
|
|
5
|
+
"BAR", // Enum Bar chart.
|
|
6
|
+
"COLUMN", // Enum Column chart.
|
|
7
|
+
"COMBO", // Enum Combo chart.
|
|
8
|
+
"HISTOGRAM", // Enum Histogram chart.
|
|
9
|
+
"LINE", // Enum Line chart.
|
|
10
|
+
"PIE", // Enum Pie chart.
|
|
11
|
+
"SCATTER", // Enum Scatter chart.
|
|
12
|
+
"STEPPED_AREA", // Enum Stepped area chart.
|
|
13
|
+
"WATERFALL", // Enum Waterfall chart.
|
|
14
|
+
"SCORECARD", // Enum Scorecard chart.
|
|
15
|
+
"RADAR", // Enum Radar chart.
|
|
16
|
+
"GAUGE", // Enum Gauge chart.
|
|
17
|
+
"ORG", // Enum Org chart.
|
|
18
|
+
"TIMELINE", // Enum Timeline chart.
|
|
19
|
+
"TREE_MAP", // Enum Tree map chart.
|
|
20
|
+
"TABLE", // Enum Table chart.
|
|
21
|
+
"CANDLESTICK", // Enum Candlestick chart.
|
|
22
|
+
"GEOMAP", // Enum Geo map chart.
|
|
23
|
+
"BUBBLE", // Enum Bubble chart.
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export const ChartHiddenDimensionStrategy = newFakeGasenum([
|
|
27
|
+
"IGNORE_BOTH", // Enum Ignore both hidden rows and columns.
|
|
28
|
+
"IGNORE_COLUMNS", // Enum Ignore hidden columns.
|
|
29
|
+
"IGNORE_ROWS", // Enum Ignore hidden rows.
|
|
30
|
+
"SHOW_BOTH", // Enum Show all hidden rows and columns.
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
export const ChartMergeStrategy = newFakeGasenum([
|
|
34
|
+
"MERGE_COLUMNS", // Enum Merge columns across multiple ranges.
|
|
35
|
+
"MERGE_ROWS", // Enum Merge rows across multiple ranges.
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
export const CurveStyle = newFakeGasenum([
|
|
39
|
+
"NORMAL", // Enum Straight lines without curve.
|
|
40
|
+
"SMOOTH", // Enum Smooth curves.
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
export const PointStyle = newFakeGasenum([
|
|
44
|
+
"NONE", // Enum No point style.
|
|
45
|
+
"TINY", // Enum Tiny points.
|
|
46
|
+
"MEDIUM", // Enum Medium points.
|
|
47
|
+
"LARGE", // Enum Large points.
|
|
48
|
+
"HUGE", // Enum Huge points.
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
export const Position = newFakeGasenum([
|
|
52
|
+
"TOP", // Enum Position at the top.
|
|
53
|
+
"BOTTOM", // Enum Position at the bottom.
|
|
54
|
+
"RIGHT", // Enum Position on the right.
|
|
55
|
+
"LEFT", // Enum Position on the left.
|
|
56
|
+
"NONE", // Enum No position.
|
|
24
57
|
]);
|
|
@@ -8,39 +8,47 @@ export const newFakeAutofit = (...args) => {
|
|
|
8
8
|
export class FakeAutofit {
|
|
9
9
|
constructor(shape) {
|
|
10
10
|
this.__shape = shape;
|
|
11
|
-
// Store autofit type on the shape resource or locally?
|
|
12
|
-
// Ideally on the resource so it persists if we reload the shape.
|
|
13
|
-
// But for now, let's look at the shape's property if meaningful or just a local property default.
|
|
14
|
-
// The resource might have 'text.autoFit'.
|
|
15
11
|
}
|
|
16
12
|
|
|
17
13
|
get __resource() {
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
14
|
+
const shapeResource = this.__shape.__resource;
|
|
15
|
+
if (shapeResource && shapeResource.shape && shapeResource.shape.shapeProperties && shapeResource.shape.shapeProperties.autofit) {
|
|
16
|
+
return shapeResource.shape.shapeProperties.autofit;
|
|
21
17
|
}
|
|
22
|
-
return
|
|
18
|
+
return {};
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
getAutofitType() {
|
|
26
|
-
const type =
|
|
22
|
+
const type = this.__resource.autofitType || 'NONE';
|
|
27
23
|
return AutofitType[type] || AutofitType.NONE;
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
disableAutofit() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
const objectId = this.__shape.getObjectId();
|
|
28
|
+
const presentationId = this.__shape.__presentation.getId();
|
|
29
|
+
|
|
30
|
+
const requests = [{
|
|
31
|
+
updateShapeProperties: {
|
|
32
|
+
objectId: objectId,
|
|
33
|
+
shapeProperties: {
|
|
34
|
+
autofit: {
|
|
35
|
+
autofitType: 'NONE'
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
fields: 'autofit.autofitType'
|
|
39
|
+
}
|
|
40
|
+
}];
|
|
41
|
+
|
|
42
|
+
Slides.Presentations.batchUpdate(requests, presentationId);
|
|
35
43
|
return this;
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
getFontScale() {
|
|
39
|
-
return
|
|
47
|
+
return this.__resource.fontScale !== undefined ? this.__resource.fontScale : 1;
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
getLineSpacingReduction() {
|
|
43
|
-
return
|
|
51
|
+
return this.__resource.lineSpacingReduction !== undefined ? this.__resource.lineSpacingReduction : 0;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
toString() {
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeColor } from '../common/fakecolor.js';
|
|
3
|
+
import { newFakeColorBuilder } from '../common/fakecolorbuilder.js';
|
|
4
|
+
import { ThemeColorType } from '../enums/slidesenums.js';
|
|
5
|
+
import { signatureArgs } from '../../support/helpers.js';
|
|
6
|
+
|
|
7
|
+
export const newFakeColorScheme = (...args) => {
|
|
8
|
+
return Proxies.guard(new FakeColorScheme(...args));
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @class FakeColorScheme
|
|
13
|
+
*/
|
|
14
|
+
export class FakeColorScheme {
|
|
15
|
+
constructor(parent) {
|
|
16
|
+
this.__parent = parent;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get __masterPage() {
|
|
20
|
+
const parent = this.__parent;
|
|
21
|
+
const type = parent.toString();
|
|
22
|
+
|
|
23
|
+
if (type === 'Master') return parent;
|
|
24
|
+
if (type === 'Slide' || type === 'Layout') {
|
|
25
|
+
// Find the master for this slide/layout
|
|
26
|
+
if (type === 'Slide') {
|
|
27
|
+
const layout = parent.getLayout();
|
|
28
|
+
return layout ? layout.getMaster() : null;
|
|
29
|
+
} else {
|
|
30
|
+
return parent.getMaster();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (type === 'Presentation') {
|
|
34
|
+
// Use the first master of the presentation
|
|
35
|
+
const masters = parent.getMasters();
|
|
36
|
+
if (masters.length > 0) {
|
|
37
|
+
return masters[0];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get __resource() {
|
|
44
|
+
const master = this.__masterPage;
|
|
45
|
+
return master?.__resource?.pageProperties?.colorScheme || { colors: [] };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* getConcreteColor(themeColorType) https://developers.google.com/apps-script/reference/slides/color-scheme#getconcretecolorthemecolortype
|
|
50
|
+
* @param {ThemeColorType} themeColorType
|
|
51
|
+
* @returns {FakeColor}
|
|
52
|
+
*/
|
|
53
|
+
getConcreteColor(themeColorType) {
|
|
54
|
+
const { nargs, matchThrow } = signatureArgs(arguments, "ColorScheme.getConcreteColor");
|
|
55
|
+
if (nargs !== 1) matchThrow();
|
|
56
|
+
|
|
57
|
+
const colors = this.__resource.colors || [];
|
|
58
|
+
const typeStr = themeColorType.toString();
|
|
59
|
+
const pair = colors.find(c => c.type === typeStr);
|
|
60
|
+
|
|
61
|
+
if (pair && pair.color) {
|
|
62
|
+
return makeColorFromApiForSlides(pair.color);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* getThemeColors() https://developers.google.com/apps-script/reference/slides/color-scheme#getthemecolors
|
|
70
|
+
* @returns {ThemeColorType[]}
|
|
71
|
+
*/
|
|
72
|
+
getThemeColors() {
|
|
73
|
+
return Object.keys(ThemeColorType)
|
|
74
|
+
.filter(k => k !== 'UNSUPPORTED')
|
|
75
|
+
.map(k => ThemeColorType[k]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* setConcreteColor(themeColorType, color) https://developers.google.com/apps-script/reference/slides/color-scheme#setconcretecolorthemecolortype,color
|
|
80
|
+
* @param {ThemeColorType} themeColorType
|
|
81
|
+
* @param {FakeColor} color
|
|
82
|
+
* @returns {FakeColorScheme} self
|
|
83
|
+
*/
|
|
84
|
+
setConcreteColor(themeColorType, color) {
|
|
85
|
+
const { nargs, matchThrow } = signatureArgs(arguments, "ColorScheme.setConcreteColor");
|
|
86
|
+
if (nargs !== 2) matchThrow();
|
|
87
|
+
|
|
88
|
+
const master = this.__masterPage;
|
|
89
|
+
if (!master) throw new Error('Could not find master page for color scheme');
|
|
90
|
+
|
|
91
|
+
const presentationId = master.__presentation.getId();
|
|
92
|
+
const typeStr = themeColorType.toString();
|
|
93
|
+
|
|
94
|
+
// The API requires ALL theme colors to be present when updating the color scheme.
|
|
95
|
+
// So we fetch the current colors, update the one we want, and send them all back.
|
|
96
|
+
const currentResource = this.__resource;
|
|
97
|
+
const colors = [...(currentResource.colors || [])];
|
|
98
|
+
|
|
99
|
+
// Convert the new Color to API format
|
|
100
|
+
const apiColor = {};
|
|
101
|
+
if (color.getColorType().toString() === 'RGB') {
|
|
102
|
+
const rgb = color.asRgbColor();
|
|
103
|
+
apiColor.red = rgb.getRed() / 255;
|
|
104
|
+
apiColor.green = rgb.getGreen() / 255;
|
|
105
|
+
apiColor.blue = rgb.getBlue() / 255;
|
|
106
|
+
} else if (color.getColorType().toString() === 'THEME') {
|
|
107
|
+
apiColor.themeColor = color.asThemeColor().getThemeColorType().toString();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Find and update or add the color pair
|
|
111
|
+
const index = colors.findIndex(c => c.type === typeStr);
|
|
112
|
+
if (index !== -1) {
|
|
113
|
+
colors[index] = { type: typeStr, color: apiColor };
|
|
114
|
+
} else {
|
|
115
|
+
colors.push({ type: typeStr, color: apiColor });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Ensure we have exactly the right set of colors if possible?
|
|
119
|
+
// The API error said "must have 12 theme colors pairs".
|
|
120
|
+
// Usually these are the standard 12 types.
|
|
121
|
+
|
|
122
|
+
const requests = [{
|
|
123
|
+
updatePageProperties: {
|
|
124
|
+
objectId: master.getObjectId(),
|
|
125
|
+
pageProperties: {
|
|
126
|
+
colorScheme: {
|
|
127
|
+
colors: colors
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
fields: 'colorScheme'
|
|
131
|
+
}
|
|
132
|
+
}];
|
|
133
|
+
|
|
134
|
+
Slides.Presentations.batchUpdate(requests, presentationId);
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
toString() {
|
|
139
|
+
return 'ColorScheme';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Helper to avoid circular dependency or reusing the spreadsheet-specific one if it differs
|
|
144
|
+
const makeColorFromApiForSlides = (apiColor) => {
|
|
145
|
+
const builder = newFakeColorBuilder();
|
|
146
|
+
const rgb = apiColor.rgbColor || (apiColor.red !== undefined ? apiColor : null);
|
|
147
|
+
|
|
148
|
+
if (rgb) {
|
|
149
|
+
const hex = '#' + [rgb.red, rgb.green, rgb.blue].map(v => {
|
|
150
|
+
const val = Math.round((v || 0) * 255);
|
|
151
|
+
const hex = val.toString(16);
|
|
152
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
153
|
+
}).join('');
|
|
154
|
+
builder.setRgbColor(hex);
|
|
155
|
+
} else if (apiColor.themeColor) {
|
|
156
|
+
const tc = apiColor.themeColor;
|
|
157
|
+
builder.setThemeColor(ThemeColorType[tc]);
|
|
158
|
+
}
|
|
159
|
+
return builder.build();
|
|
160
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
2
|
import { newFakeMaster } from './fakemaster.js';
|
|
3
|
+
import { newFakeColorScheme } from './fakecolorscheme.js';
|
|
3
4
|
|
|
4
5
|
export const newFakeLayout = (...args) => {
|
|
5
6
|
return Proxies.guard(new FakeLayout(...args));
|
|
@@ -21,7 +22,7 @@ export class FakeLayout {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
getMaster() {
|
|
24
|
-
const masterId = this.__resource.masterObjectId;
|
|
25
|
+
const masterId = this.__resource.layoutProperties?.masterObjectId;
|
|
25
26
|
if (!masterId) return null;
|
|
26
27
|
|
|
27
28
|
const presentationResource = this.__presentation.__resource;
|
|
@@ -32,6 +33,15 @@ export class FakeLayout {
|
|
|
32
33
|
getObjectId() {
|
|
33
34
|
return this.__id;
|
|
34
35
|
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gets the color scheme of the layout.
|
|
39
|
+
* @returns {FakeColorScheme} The color scheme.
|
|
40
|
+
*/
|
|
41
|
+
getColorScheme() {
|
|
42
|
+
return newFakeColorScheme(this);
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
toString() {
|
|
36
46
|
return 'Layout';
|
|
37
47
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeColorScheme } from './fakecolorscheme.js';
|
|
2
3
|
|
|
3
4
|
export const newFakeMaster = (...args) => {
|
|
4
5
|
return Proxies.guard(new FakeMaster(...args));
|
|
@@ -22,6 +23,15 @@ export class FakeMaster {
|
|
|
22
23
|
getObjectId() {
|
|
23
24
|
return this.__id;
|
|
24
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets the color scheme of the master.
|
|
29
|
+
* @returns {FakeColorScheme} The color scheme.
|
|
30
|
+
*/
|
|
31
|
+
getColorScheme() {
|
|
32
|
+
return newFakeColorScheme(this);
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
toString() {
|
|
26
36
|
return 'Master';
|
|
27
37
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
2
|
import { newFakeSlide } from './fakeslide.js';
|
|
3
|
+
import { newFakeMaster } from './fakemaster.js';
|
|
4
|
+
import { newFakeColorScheme } from './fakecolorscheme.js';
|
|
3
5
|
|
|
4
6
|
export const newFakePresentation = (...args) => {
|
|
5
7
|
return Proxies.guard(new FakePresentation(...args));
|
|
@@ -50,6 +52,31 @@ export class FakePresentation {
|
|
|
50
52
|
return `https://docs.google.com/presentation/d/${this.getId()}/edit`;
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Gets the color scheme of the presentation.
|
|
57
|
+
* @returns {FakeColorScheme} The color scheme.
|
|
58
|
+
*/
|
|
59
|
+
getColorScheme() {
|
|
60
|
+
return newFakeColorScheme(this);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Gets the masters in the presentation.
|
|
65
|
+
* @returns {FakeMaster[]} The masters.
|
|
66
|
+
*/
|
|
67
|
+
getMasters() {
|
|
68
|
+
return (this.__resource.masters || []).map(m => newFakeMaster(m, this));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets a master by its ID.
|
|
73
|
+
* @param {string} id The master ID.
|
|
74
|
+
* @returns {FakeMaster | null} The master, or null if not found.
|
|
75
|
+
*/
|
|
76
|
+
getMasterById(id) {
|
|
77
|
+
return this.getMasters().find(m => m.getObjectId() === id) || null;
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
/**
|
|
54
81
|
* Gets the slides in the presentation.
|
|
55
82
|
* @returns {FakeSlide[]} The slides.
|
|
@@ -4,6 +4,7 @@ import { newFakeLayout } from './fakelayout.js';
|
|
|
4
4
|
import { newFakeMaster } from './fakemaster.js';
|
|
5
5
|
import { newFakePageElement } from './fakepageelement.js';
|
|
6
6
|
import { newFakePageBackground } from './fakepagebackground.js';
|
|
7
|
+
import { newFakeColorScheme } from './fakecolorscheme.js';
|
|
7
8
|
import { asSpecificPageElement } from './pageelementfactory.js';
|
|
8
9
|
|
|
9
10
|
export const newFakeSlide = (...args) => {
|
|
@@ -86,6 +87,14 @@ export class FakeSlide {
|
|
|
86
87
|
return background ? newFakePageBackground(this) : null;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Gets the color scheme of the slide.
|
|
92
|
+
* @returns {FakeColorScheme} The color scheme.
|
|
93
|
+
*/
|
|
94
|
+
getColorScheme() {
|
|
95
|
+
return newFakeColorScheme(this);
|
|
96
|
+
}
|
|
97
|
+
|
|
89
98
|
/**
|
|
90
99
|
* Gets the list of tables on the slide.
|
|
91
100
|
* @returns {FakeTable[]} The tables.
|
|
@@ -17,10 +17,11 @@ export class FakeTextRange {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
get __resource() {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const shapeResource = this.__shape.__resource;
|
|
21
|
+
if (shapeResource && shapeResource.shape && shapeResource.shape.text) {
|
|
22
|
+
return shapeResource.shape.text;
|
|
22
23
|
}
|
|
23
|
-
return
|
|
24
|
+
return {};
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
getStartIndex() {
|
|
@@ -42,7 +43,7 @@ export class FakeTextRange {
|
|
|
42
43
|
* @returns {string} The text.
|
|
43
44
|
*/
|
|
44
45
|
asString() {
|
|
45
|
-
const textElements = this.__resource
|
|
46
|
+
const textElements = this.__resource?.textElements || [];
|
|
46
47
|
let fullText = textElements.map(te => {
|
|
47
48
|
if (te.textRun) return te.textRun.content;
|
|
48
49
|
if (te.autoText) return te.autoText.content || '[AutoText]'; // Use placeholder
|
|
@@ -114,12 +115,6 @@ export class FakeTextRange {
|
|
|
114
115
|
|
|
115
116
|
if (requests.length > 0) {
|
|
116
117
|
Slides.Presentations.batchUpdate(requests, presentationId);
|
|
117
|
-
|
|
118
|
-
// REST API documentation: "The field is automatically set to NONE if a request is made that might affect text fitting within its bounding text box."
|
|
119
|
-
if (!this.__resource.autoFit) {
|
|
120
|
-
this.__resource.autoFit = {};
|
|
121
|
-
}
|
|
122
|
-
this.__resource.autoFit.autofitType = AutofitType.NONE;
|
|
123
118
|
}
|
|
124
119
|
|
|
125
120
|
return this;
|
|
@@ -176,7 +171,7 @@ export class FakeTextRange {
|
|
|
176
171
|
* @returns {FakeAutoText[]} The auto texts.
|
|
177
172
|
*/
|
|
178
173
|
getAutoTexts() {
|
|
179
|
-
const textElements = this.__resource
|
|
174
|
+
const textElements = this.__resource?.textElements || [];
|
|
180
175
|
const autoTexts = [];
|
|
181
176
|
let charIndex = 0;
|
|
182
177
|
let autoTextIndex = 0;
|