-
Notifications
You must be signed in to change notification settings - Fork 5
/
Code.gs
196 lines (156 loc) · 5.24 KB
/
Code.gs
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
/*
* MIT License
*
* Copyright (c) 2018 Tech Cooperative (https://techcoop.group)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* Tech Cooperative - json-google-docs
*
* Installation:
* https://github.com/techcoop/json-google-docs
*/
// TODO add authentication support
// TODO will break if you don't go from H1
var DEBUG = true; // Should we log extra information
function test_get() {
if (!DEBUG) {
Logger.log('Warning, you are running test_get with DEBUG turned off!')
}
doGet()
}
function doGet(e) {
try {
// Setup document
var doc = DocumentApp.getActiveDocument()
var body = doc.getBody()
// Init data
var data = {}
// Process document all sections
var searchResult = null
// Use an array to hold the previous nesting, will get reset when content is processed
var tree = []
var content = []
var insideContent = false
var element = null
var previousElement = null
while (searchResult = body.findElement(DocumentApp.ElementType.PARAGRAPH, searchResult)) {
// Get element from search results
previousElement = element
element = searchResult.getElement()
// Get raw text for this element
var elementText = element.getText()
// Add text to tree if this is not a content item
if (element.getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
// When we reach our next non heading, write the previous content
// We will need to write the result on the row after loop as well with this
if (elementText === '') {
continue
}
// Detect empty frame, where previousElement was the same heading
var emptyFrame = false
if (previousElement && previousElement.getHeading() === element.getHeading()) {
emptyFrame = true
}
if (insideContent || emptyFrame) {
// Merge this row into data
data = merge(data, nestedObjectValue(tree, content))
// Reset content array and insideContent flag
content = []
insideContent = false
// Parse what level this heading is at in the tree
var level = parseInt(element.getHeading().toString().split(' ')[1].trim())
// Update tree with new heading state
tree = tree.slice(0, level - 1);
}
// Add heading to tree array
tree.push(elementText)
continue
}
// If we are processing a normal element, we are inside of a content section
insideContent = true
// Add this paragraph to content
content.push(elementText)
}
// Merge last row into data
data = merge(data, nestedObjectValue(tree, content))
var message = 'success'
return handleSuccess(message, data)
} catch(error) {
var message = 'Unknown error ocurred'
if (DEBUG) {
Logger.log(error)
}
return handleError(message, {request: e, error: error})
}
}
// ***** Utility functions *****
function nestedObjectValue(tree, value) {
var obj = {}
for (var i = tree.length; i--;) {
if (i === tree.length - 1) {
obj[tree[i].toLowerCase()] = value
} else {
obj = nest(obj, tree[i].toLowerCase())
}
}
return obj
}
function nest(target, key) {
var obj = {}
obj[key] = target
return obj
}
function merge(target, source) {
for (var item in source) {
if (typeof target[item] === 'object') {
target[item] = merge(target[item], source[item])
} else {
target[item] = source[item]
}
}
return target
}
// Gets response from object
function getResponse(data) {
return ContentService
.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
// Wraps error
function handleError(message, data) {
var error = {status: 'error', message: message}
if (data) {
error['data'] = data
}
if (DEBUG) {
Logger.log(error)
}
return getResponse(error)
}
// Wraps success
function handleSuccess(message, data) {
var success = {status: 'success', message: message}
if (data) {
success['data'] = data
}
if (DEBUG) {
Logger.log(success)
}
return getResponse(success)
}