-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.js
206 lines (182 loc) · 5.49 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
var _URL_MAX_LENGTH = 2000;
/**
* Get an Image-Charts graph image URL, usage:
*
* `=image(DEPENDENCY_GRAPH_URL('Feuille 1'!B2:B23; 'Feuille 1'!F2:F23; "ortho"); 2)`
*
* @param {A1:A} itemRows the items column range
* @param {C1:C} dependsOnRows the dependencies column range (each cell must contain comma separated item name)
* @param {"line"} edgeRepresentation could be either: line, polyline, curved, ortho, spline
* @return The graph dependency image
* @customfunction
*/
function DEPENDENCY_GRAPH_URL(itemRows, dependsOnRows, edgeRepresentation) {
// items = [row1 'item name 1', row2 'item name 2', row3 'item name 3']
// dependsOn = [row1 '', row2 'item name 1,item name 3', row3 'item name 2']
// edgeRepresentation = 'line'
if (!Array.isArray(itemRows)) {
throw new Error("`itemRows` must be specified and a range");
}
if (!Array.isArray(dependsOnRows)) {
throw new Error("`dependsOnRows` must be specified and a range");
}
const edgeLinesAllowed = "line,polyline,curved,ortho,spline".split(",");
if (!edgeRepresentation) {
edgeRepresentation = edgeLinesAllowed[0];
}
if (!inArray(edgeLinesAllowed, edgeRepresentation)) {
throw new Error(
"`edgeRepresentation` must be specified one of:" +
JSON.stringify(edgeLinesAllowed)
);
}
// String => Array[String]
// 'item name 1,item name 3' => ['item name 1','item name 3']
function getParentDependencies(dependsOn) {
return String(dependsOn || "")
.split(",")
.map(function(a) {
return a.trim();
});
}
/**
*
* @param {Array<String>} itemRows
* @param {Array<String>} dependsOnRows
* @return {Object}
* {
* 'item name 1': ['item name 2'],
* // item name 1 & item name 3 depends on item name 2
* 'item name 2': ['item name 1', 'item name 3'],
* 'item name 3': ['item name 2']
*
* }
*/
function getItemsAndDependencies(itemRows, dependsOnRows) {
return dependsOnRows.reduce(function(nodes, dependsOn, i) {
const to = itemRows[i][0];
getParentDependencies(dependsOn).forEach(function(parentDependency) {
if (!parentDependency) {
nodes[to] = [];
return;
}
if (!nodes[parentDependency]) {
nodes[parentDependency] = [];
}
nodes[parentDependency].push(to);
});
return nodes;
}, {});
}
/**
* [dotGraph description]
* @param {Object} itemsAndDependencies
* @return {String} graph representation in dot language
*/
function dotGraph(itemsAndDependencies, edgeRepresentation) {
function dependenciesSet(dependencies) {
return (
"{" +
dependencies
.map(function(dep) {
return JSON.stringify(dep);
})
.join(" ") +
"}"
);
}
return (
'digraph {splines="' +
edgeRepresentation +
'";rankdir=LR;' +
reduce(
itemsAndDependencies,
function(m, dependencies, item, i) {
m.push(
[JSON.stringify(item), dependenciesSet(dependencies)].join("->")
);
return m;
},
[]
).join(";") +
"}"
);
}
function reduce(obj, callback, memo) {
"use strict";
var key;
for (key in obj) {
if (!obj.hasOwnProperty(key)) continue;
memo = callback(memo, obj[key], key, obj);
}
return memo;
}
function inArray(arr, value) {
return arr.some(function(item) {
return item === value;
});
}
const url =
"https://image-charts.com/chart?cht=gv&chl=" +
encodeURIComponent(
dotGraph(
getItemsAndDependencies(itemRows, dependsOnRows),
edgeRepresentation
)
);
if (url.length > _URL_MAX_LENGTH) {
throw new Error(
"URL_MAX_LENGTH: Google sheet does not allow URL larger than " +
_URL_MAX_LENGTH +
" chars."
);
}
return url;
}
if (typeof module !== "undefined") {
// Google script is ES5 :(
module.exports.DEPENDENCY_GRAPH_URL = DEPENDENCY_GRAPH_URL;
module.exports.URL_MAX_LENGTH = _URL_MAX_LENGTH;
}
/**
* Runs when the add-on is installed.
*/
function onInstall() {
onOpen();
}
var MESSAGES = {
HOW_TO_USE: "Watch step-by-step guide",
REPORT_ISSUE: "Report an issue",
IMAGE_CHARTS: "Learn more about Image-Charts",
IMAGE_CHARTS_WATERMARK: "Learn how to remove the watermark"
};
/**
* Runs when the document is opened, creating the add-on's menu. Custom function
* add-ons need at least one menu item, since the add-on is only enabled in the
* current spreadsheet when a function is run.
*/
function onOpen() {
SpreadsheetApp.getUi()
.createAddonMenu()
.addItem(MESSAGES.HOW_TO_USE, "openScreencast")
.addItem(MESSAGES.IMAGE_CHARTS, "openImageCharts")
.addItem(MESSAGES.IMAGE_CHARTS_WATERMARK, "openImageChartsWatermark")
.addItem(MESSAGES.REPORT_ISSUE, "openIssue")
.addToUi();
}
function openScreencast() {
var html = HtmlService.createHtmlOutputFromFile("screencast");
SpreadsheetApp.getUi().showModalDialog(html, MESSAGES.HOW_TO_USE);
}
function openIssue() {
var html = HtmlService.createHtmlOutputFromFile("issues");
SpreadsheetApp.getUi().showModalDialog(html, MESSAGES.REPORT_ISSUE);
}
function openImageCharts() {
var html = HtmlService.createHtmlOutputFromFile("website");
SpreadsheetApp.getUi().showModalDialog(html, MESSAGES.IMAGE_CHARTS);
}
function openImageChartsWatermark() {
var html = HtmlService.createHtmlOutputFromFile("watermark");
SpreadsheetApp.getUi().showModalDialog(html, MESSAGES.IMAGE_CHARTS_WATERMARK);
}