|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<base target="_top" /> |
|
<link |
|
rel="stylesheet" |
|
href="https://ssl.gstatic.com/docs/script/css/add-ons1.css" |
|
/> |
|
<script src="https://cdn.jsdelivr.net/npm/mithril/mithril.min.js"></script> |
|
<script> |
|
const isValidString = param => |
|
typeof param === 'string' && param.length > 0 |
|
|
|
const startsWith = (string, start) => string[0] === start |
|
|
|
const isSelector = param => |
|
isValidString(param) && |
|
(startsWith(param, '.') || startsWith(param, '#')) |
|
|
|
const helpers = (h, tags) => |
|
tags.reduce((res, tagName) => { |
|
res[tagName] = (first, ...rest) => { |
|
if (isSelector(first)) { |
|
return h(tagName + first, ...rest) |
|
} else if (typeof first === 'undefined') { |
|
return h(tagName) |
|
} else { |
|
return h(tagName, first, ...rest) |
|
} |
|
} |
|
|
|
return res |
|
}, {}) |
|
</script> |
|
<style> |
|
body { |
|
margin: 10px; |
|
} |
|
|
|
.branding-below { |
|
bottom: 56px; |
|
top: 0; |
|
} |
|
|
|
.form-group label { |
|
font-weight: bolder; |
|
} |
|
|
|
#status { |
|
font-weight: bold; |
|
} |
|
|
|
.ok { |
|
color: green; |
|
} |
|
|
|
.error { |
|
color: red; |
|
} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
<div id="root" class="branding-below"></div> |
|
|
|
<div class="bottom"> |
|
<span class="gray"> |
|
Введенные данные будут сохранены в настройках пользователя внутри вашего |
|
аккаунта Google. |
|
</span> |
|
</div> |
|
|
|
<script> |
|
const { div, p, input, span, label, button } = helpers(m, [ |
|
'div', |
|
'p', |
|
'input', |
|
'span', |
|
'label', |
|
'button' |
|
]) |
|
|
|
const STATUS = { |
|
LOADING: 'LOADING', |
|
READY: 'READY', |
|
SAVING: 'SAVING', |
|
ERROR: 'ERROR', |
|
OK: 'OK' |
|
} |
|
|
|
let state = { |
|
status: null, |
|
statusMsg: null, |
|
properties: {} |
|
} |
|
|
|
const isPassword = name => { |
|
const val = name.toLowerCase() |
|
return ['password', 'secret', 'token'].some(k => val.includes(k)) |
|
} |
|
|
|
function handlePropertyInput(name, ev) { |
|
state = { |
|
...state, |
|
properties: { |
|
...state.properties, |
|
[name]: ev.target.value |
|
} |
|
} |
|
} |
|
|
|
function getDocumentProperties() { |
|
return new Promise((resolve, reject) => { |
|
google.script.run |
|
.withSuccessHandler(resolve) |
|
.withFailureHandler(reject) |
|
.getDocumentProperties() |
|
}) |
|
} |
|
|
|
function handleSaveClick() { |
|
state = { |
|
...state, |
|
status: STATUS.SAVING, |
|
statusMsg: 'Обновление ...' |
|
} |
|
|
|
google.script.run |
|
.withSuccessHandler(() => { |
|
m.redraw() |
|
state = { |
|
...state, |
|
status: STATUS.OK, |
|
statusMsg: 'Параметры сохранены' |
|
} |
|
}) |
|
.withFailureHandler(err => { |
|
m.redraw() |
|
state = { |
|
...state, |
|
status: STATUS.ERROR, |
|
statusMsg: err.message |
|
} |
|
}) |
|
.setDocumentProperties(state.properties) |
|
} |
|
|
|
// function handleCancelClick() { |
|
// google.script.host.close() |
|
// } |
|
|
|
function getStatusClass() { |
|
switch (state.status) { |
|
case STATUS.ERROR: |
|
return '.error' |
|
case STATUS.OK: |
|
return '.ok' |
|
default: |
|
return '' |
|
} |
|
} |
|
|
|
const render = () => () => { |
|
return [ |
|
div('.block.form-group', [ |
|
...Object.keys(state.properties).map(key => { |
|
const prop = state.properties[key] |
|
|
|
return div('.block.form-group', [ |
|
label({ for: key }, key), |
|
input(`#${key}`, { |
|
type: isPassword(key) ? 'password' : 'text', |
|
style: 'width: 250px;', |
|
value: prop != null ? prop : '', |
|
oninput: ev => handlePropertyInput(key, ev), |
|
disabled: |
|
state.status === STATUS.SAVING ? 'disabled' : undefined |
|
}) |
|
]) |
|
}), |
|
|
|
state.status !== STATUS.LOADING |
|
? div('.block', [ |
|
button( |
|
'#save-btn.blue', |
|
{ |
|
onclick: handleSaveClick, |
|
disabled: |
|
state.status === STATUS.SAVING ? 'disabled' : undefined |
|
}, |
|
['Сохранить'] |
|
) |
|
// button( |
|
// '#cancel-btn', |
|
// { |
|
// onclick: handleCancelClick |
|
// }, |
|
// ['Отменить'] |
|
// ) |
|
]) |
|
: null, |
|
|
|
div('.block', [ |
|
span(`#status${getStatusClass()}`, [state.statusMsg]) |
|
]) |
|
]) |
|
] |
|
} |
|
|
|
const rootNode = document.getElementById('root') |
|
|
|
state.status = STATUS.LOADING |
|
state.statusMsg = 'Загрузка свойств документа ...' |
|
|
|
getDocumentProperties({ isPassword }).then(properties => { |
|
m.redraw() |
|
state.properties = properties |
|
state.status = STATUS.READY |
|
state.statusMsg = null |
|
}) |
|
|
|
m.mount(rootNode, { |
|
view: render() |
|
}) |
|
</script> |
|
</body> |
|
</html> |