@crowdin/app-project-module 0.50.0 → 0.50.1
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/out/static/css/styles.css +235 -0
- package/out/static/js/dependent.js +306 -0
- package/out/static/js/form.js +112 -0
- package/out/static/js/main.js +94 -0
- package/out/static/js/polyfills/fetch.js +494 -0
- package/out/static/js/polyfills/promise.js +375 -0
- package/out/views/error.handlebars +31 -0
- package/out/views/form.handlebars +30 -0
- package/out/views/install.handlebars +16 -0
- package/out/views/login.handlebars +250 -0
- package/out/views/main.handlebars +1158 -0
- package/out/views/oauth.handlebars +4 -0
- package/out/views/partials/head.handlebars +48 -0
- package/out/views/subscription.handlebars +26 -0
- package/package.json +4 -1
- package/.release-it.json +0 -17
- package/CODE_OF_CONDUCT.md +0 -76
- package/CONTRIBUTING.md +0 -92
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* setImmediate polyfill v1.0.1, supports IE9+
|
|
3
|
+
* © 2014–2015 Dmitry Korobkin
|
|
4
|
+
* Released under the MIT license
|
|
5
|
+
* github.com/Octane/setImmediate
|
|
6
|
+
*/
|
|
7
|
+
window.setImmediate ||
|
|
8
|
+
(function () {
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
var uid = 0;
|
|
12
|
+
var storage = {};
|
|
13
|
+
var firstCall = true;
|
|
14
|
+
var slice = Array.prototype.slice;
|
|
15
|
+
var message = 'setImmediatePolyfillMessage';
|
|
16
|
+
|
|
17
|
+
function fastApply(args) {
|
|
18
|
+
var func = args[0];
|
|
19
|
+
switch (args.length) {
|
|
20
|
+
case 1:
|
|
21
|
+
return func();
|
|
22
|
+
case 2:
|
|
23
|
+
return func(args[1]);
|
|
24
|
+
case 3:
|
|
25
|
+
return func(args[1], args[2]);
|
|
26
|
+
}
|
|
27
|
+
return func.apply(window, slice.call(args, 1));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function callback(event) {
|
|
31
|
+
var key = event.data;
|
|
32
|
+
var data;
|
|
33
|
+
if (typeof key == 'string' && key.indexOf(message) == 0) {
|
|
34
|
+
data = storage[key];
|
|
35
|
+
if (data) {
|
|
36
|
+
delete storage[key];
|
|
37
|
+
fastApply(data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
window.setImmediate = function setImmediate() {
|
|
43
|
+
var id = uid++;
|
|
44
|
+
var key = message + id;
|
|
45
|
+
var i = arguments.length;
|
|
46
|
+
var args = new Array(i);
|
|
47
|
+
while (i--) {
|
|
48
|
+
args[i] = arguments[i];
|
|
49
|
+
}
|
|
50
|
+
storage[key] = args;
|
|
51
|
+
if (firstCall) {
|
|
52
|
+
firstCall = false;
|
|
53
|
+
window.addEventListener('message', callback);
|
|
54
|
+
}
|
|
55
|
+
window.postMessage(key, '*');
|
|
56
|
+
return id;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
window.clearImmediate = function clearImmediate(id) {
|
|
60
|
+
delete storage[message + id];
|
|
61
|
+
};
|
|
62
|
+
})();
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Promise polyfill v1.0.10
|
|
66
|
+
* requires setImmediate
|
|
67
|
+
*
|
|
68
|
+
* © 2014–2015 Dmitry Korobkin
|
|
69
|
+
* Released under the MIT license
|
|
70
|
+
* github.com/Octane/Promise
|
|
71
|
+
*/
|
|
72
|
+
(function (global) {
|
|
73
|
+
'use strict';
|
|
74
|
+
|
|
75
|
+
var STATUS = '[[PromiseStatus]]';
|
|
76
|
+
var VALUE = '[[PromiseValue]]';
|
|
77
|
+
var ON_FUlFILLED = '[[OnFulfilled]]';
|
|
78
|
+
var ON_REJECTED = '[[OnRejected]]';
|
|
79
|
+
var ORIGINAL_ERROR = '[[OriginalError]]';
|
|
80
|
+
var PENDING = 'pending';
|
|
81
|
+
var INTERNAL_PENDING = 'internal pending';
|
|
82
|
+
var FULFILLED = 'fulfilled';
|
|
83
|
+
var REJECTED = 'rejected';
|
|
84
|
+
var NOT_ARRAY = 'not an array.';
|
|
85
|
+
var REQUIRES_NEW = 'constructor Promise requires "new".';
|
|
86
|
+
var CHAINING_CYCLE = 'then() cannot return same Promise that it resolves.';
|
|
87
|
+
|
|
88
|
+
var setImmediate = global.setImmediate || require('timers').setImmediate;
|
|
89
|
+
var isArray =
|
|
90
|
+
Array.isArray ||
|
|
91
|
+
function (anything) {
|
|
92
|
+
return Object.prototype.toString.call(anything) == '[object Array]';
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function InternalError(originalError) {
|
|
96
|
+
this[ORIGINAL_ERROR] = originalError;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isInternalError(anything) {
|
|
100
|
+
return anything instanceof InternalError;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isObject(anything) {
|
|
104
|
+
//Object.create(null) instanceof Object → false
|
|
105
|
+
return Object(anything) === anything;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isCallable(anything) {
|
|
109
|
+
return typeof anything == 'function';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isPromise(anything) {
|
|
113
|
+
return anything instanceof Promise;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function identity(value) {
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function thrower(reason) {
|
|
121
|
+
throw reason;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function enqueue(promise, onFulfilled, onRejected) {
|
|
125
|
+
if (!promise[ON_FUlFILLED]) {
|
|
126
|
+
promise[ON_FUlFILLED] = [];
|
|
127
|
+
promise[ON_REJECTED] = [];
|
|
128
|
+
}
|
|
129
|
+
promise[ON_FUlFILLED].push(onFulfilled);
|
|
130
|
+
promise[ON_REJECTED].push(onRejected);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function clearAllQueues(promise) {
|
|
134
|
+
delete promise[ON_FUlFILLED];
|
|
135
|
+
delete promise[ON_REJECTED];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function callEach(queue) {
|
|
139
|
+
var i;
|
|
140
|
+
var length = queue.length;
|
|
141
|
+
for (i = 0; i < length; i++) {
|
|
142
|
+
queue[i]();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function call(resolve, reject, value) {
|
|
147
|
+
var anything = toPromise(value);
|
|
148
|
+
if (isPromise(anything)) {
|
|
149
|
+
anything.then(resolve, reject);
|
|
150
|
+
} else if (isInternalError(anything)) {
|
|
151
|
+
reject(anything[ORIGINAL_ERROR]);
|
|
152
|
+
} else {
|
|
153
|
+
resolve(value);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function toPromise(anything) {
|
|
158
|
+
var then;
|
|
159
|
+
if (isPromise(anything)) {
|
|
160
|
+
return anything;
|
|
161
|
+
}
|
|
162
|
+
if (isObject(anything)) {
|
|
163
|
+
try {
|
|
164
|
+
then = anything.then;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return new InternalError(error);
|
|
167
|
+
}
|
|
168
|
+
if (isCallable(then)) {
|
|
169
|
+
return new Promise(function (resolve, reject) {
|
|
170
|
+
setImmediate(function () {
|
|
171
|
+
try {
|
|
172
|
+
then.call(anything, resolve, reject);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
reject(error);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function resolvePromise(promise, resolver) {
|
|
184
|
+
function resolve(value) {
|
|
185
|
+
if (promise[STATUS] == PENDING) {
|
|
186
|
+
fulfillPromise(promise, value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function reject(reason) {
|
|
190
|
+
if (promise[STATUS] == PENDING) {
|
|
191
|
+
rejectPromise(promise, reason);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
resolver(resolve, reject);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
reject(error);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function fulfillPromise(promise, value) {
|
|
202
|
+
var queue;
|
|
203
|
+
var anything = toPromise(value);
|
|
204
|
+
if (isPromise(anything)) {
|
|
205
|
+
promise[STATUS] = INTERNAL_PENDING;
|
|
206
|
+
anything.then(
|
|
207
|
+
function (value) {
|
|
208
|
+
fulfillPromise(promise, value);
|
|
209
|
+
},
|
|
210
|
+
function (reason) {
|
|
211
|
+
rejectPromise(promise, reason);
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
} else if (isInternalError(anything)) {
|
|
215
|
+
rejectPromise(promise, anything[ORIGINAL_ERROR]);
|
|
216
|
+
} else {
|
|
217
|
+
promise[STATUS] = FULFILLED;
|
|
218
|
+
promise[VALUE] = value;
|
|
219
|
+
queue = promise[ON_FUlFILLED];
|
|
220
|
+
if (queue && queue.length) {
|
|
221
|
+
clearAllQueues(promise);
|
|
222
|
+
callEach(queue);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function rejectPromise(promise, reason) {
|
|
228
|
+
var queue = promise[ON_REJECTED];
|
|
229
|
+
promise[STATUS] = REJECTED;
|
|
230
|
+
promise[VALUE] = reason;
|
|
231
|
+
if (queue && queue.length) {
|
|
232
|
+
clearAllQueues(promise);
|
|
233
|
+
callEach(queue);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function Promise(resolver) {
|
|
238
|
+
var promise = this;
|
|
239
|
+
if (!isPromise(promise)) {
|
|
240
|
+
throw new TypeError(REQUIRES_NEW);
|
|
241
|
+
}
|
|
242
|
+
promise[STATUS] = PENDING;
|
|
243
|
+
promise[VALUE] = undefined;
|
|
244
|
+
resolvePromise(promise, resolver);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Promise.prototype.then = function (onFulfilled, onRejected) {
|
|
248
|
+
var promise = this;
|
|
249
|
+
var nextPromise;
|
|
250
|
+
onFulfilled = isCallable(onFulfilled) ? onFulfilled : identity;
|
|
251
|
+
onRejected = isCallable(onRejected) ? onRejected : thrower;
|
|
252
|
+
nextPromise = new Promise(function (resolve, reject) {
|
|
253
|
+
function tryCall(func) {
|
|
254
|
+
var value;
|
|
255
|
+
try {
|
|
256
|
+
value = func(promise[VALUE]);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
reject(error);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (value === nextPromise) {
|
|
262
|
+
reject(new TypeError(CHAINING_CYCLE));
|
|
263
|
+
} else {
|
|
264
|
+
call(resolve, reject, value);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function asyncOnFulfilled() {
|
|
268
|
+
setImmediate(tryCall, onFulfilled);
|
|
269
|
+
}
|
|
270
|
+
function asyncOnRejected() {
|
|
271
|
+
setImmediate(tryCall, onRejected);
|
|
272
|
+
}
|
|
273
|
+
switch (promise[STATUS]) {
|
|
274
|
+
case FULFILLED:
|
|
275
|
+
asyncOnFulfilled();
|
|
276
|
+
break;
|
|
277
|
+
case REJECTED:
|
|
278
|
+
asyncOnRejected();
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
enqueue(promise, asyncOnFulfilled, asyncOnRejected);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
return nextPromise;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
Promise.prototype['catch'] = function (onRejected) {
|
|
288
|
+
return this.then(identity, onRejected);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
Promise.resolve = function (value) {
|
|
292
|
+
var anything = toPromise(value);
|
|
293
|
+
if (isPromise(anything)) {
|
|
294
|
+
return anything;
|
|
295
|
+
}
|
|
296
|
+
return new Promise(function (resolve, reject) {
|
|
297
|
+
if (isInternalError(anything)) {
|
|
298
|
+
reject(anything[ORIGINAL_ERROR]);
|
|
299
|
+
} else {
|
|
300
|
+
resolve(value);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
Promise.reject = function (reason) {
|
|
306
|
+
return new Promise(function (resolve, reject) {
|
|
307
|
+
reject(reason);
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
Promise.race = function (values) {
|
|
312
|
+
return new Promise(function (resolve, reject) {
|
|
313
|
+
var i;
|
|
314
|
+
var length;
|
|
315
|
+
if (isArray(values)) {
|
|
316
|
+
length = values.length;
|
|
317
|
+
for (i = 0; i < length; i++) {
|
|
318
|
+
call(resolve, reject, values[i]);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
reject(new TypeError(NOT_ARRAY));
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
Promise.all = function (values) {
|
|
327
|
+
return new Promise(function (resolve, reject) {
|
|
328
|
+
var fulfilledCount = 0;
|
|
329
|
+
var promiseCount = 0;
|
|
330
|
+
var anything;
|
|
331
|
+
var length;
|
|
332
|
+
var value;
|
|
333
|
+
var i;
|
|
334
|
+
if (isArray(values)) {
|
|
335
|
+
values = values.slice(0);
|
|
336
|
+
length = values.length;
|
|
337
|
+
for (i = 0; i < length; i++) {
|
|
338
|
+
value = values[i];
|
|
339
|
+
anything = toPromise(value);
|
|
340
|
+
if (isPromise(anything)) {
|
|
341
|
+
promiseCount++;
|
|
342
|
+
anything.then(
|
|
343
|
+
(function (index) {
|
|
344
|
+
return function (value) {
|
|
345
|
+
values[index] = value;
|
|
346
|
+
fulfilledCount++;
|
|
347
|
+
if (fulfilledCount == promiseCount) {
|
|
348
|
+
resolve(values);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
})(i),
|
|
352
|
+
reject,
|
|
353
|
+
);
|
|
354
|
+
} else if (isInternalError(anything)) {
|
|
355
|
+
reject(anything[ORIGINAL_ERROR]);
|
|
356
|
+
} else {
|
|
357
|
+
//[1, , 3] → [1, undefined, 3]
|
|
358
|
+
values[i] = value;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (!promiseCount) {
|
|
362
|
+
resolve(values);
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
reject(new TypeError(NOT_ARRAY));
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
if (typeof module != 'undefined' && module.exports) {
|
|
371
|
+
module.exports = global.Promise || Promise;
|
|
372
|
+
} else if (!global.Promise) {
|
|
373
|
+
global.Promise = Promise;
|
|
374
|
+
}
|
|
375
|
+
})(this);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
{{> head}}
|
|
4
|
+
|
|
5
|
+
<body>
|
|
6
|
+
<div class="i_w">
|
|
7
|
+
<div class="error-page">
|
|
8
|
+
<div>
|
|
9
|
+
<div class="error-page-message">
|
|
10
|
+
<crowdin-h3>{{ message }}</crowdin-h3>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="error-page-action">
|
|
13
|
+
<crowdin-button outlined onclick="integrationLogout()">Log out</crowdin-button>
|
|
14
|
+
<span> or <crowdin-a href="https://crowdin.com/contacts" target="_blank">contact us</crowdin-a> for help</span>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</body>
|
|
20
|
+
|
|
21
|
+
</html>
|
|
22
|
+
<script>
|
|
23
|
+
function integrationLogout() {
|
|
24
|
+
checkOrigin()
|
|
25
|
+
.then(queryParams => fetch(`api/logout${queryParams}`, { method: 'POST' }))
|
|
26
|
+
.then(checkResponse)
|
|
27
|
+
.then(reloadLocation)
|
|
28
|
+
.then(localStorage.removeItem('revised_{{name}}'))
|
|
29
|
+
.catch(e => catchRejection(e, 'Looks like you are not logged in'));
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
{{> head}}
|
|
4
|
+
<body>
|
|
5
|
+
<div
|
|
6
|
+
class="i_w"
|
|
7
|
+
style='max-width:680px; position: relative;'
|
|
8
|
+
>
|
|
9
|
+
<crowdin-card
|
|
10
|
+
id="card"
|
|
11
|
+
is-shadowed
|
|
12
|
+
>
|
|
13
|
+
<div id="form"></div>
|
|
14
|
+
<div id="form-loading">
|
|
15
|
+
<crowdin-progress-indicator />
|
|
16
|
+
</div>
|
|
17
|
+
</crowdin-card>
|
|
18
|
+
</div>
|
|
19
|
+
<crowdin-toasts></crowdin-toasts>
|
|
20
|
+
<script>
|
|
21
|
+
/*<!--*/
|
|
22
|
+
var formGetDataUrl = '{{formGetDataUrl}}';
|
|
23
|
+
var formPostDataUrl = '{{formPostDataUrl}}';
|
|
24
|
+
var formSchema = {{{formSchema}}};
|
|
25
|
+
var formUiSchema = {{{formUiSchema}}};
|
|
26
|
+
/*-->*/
|
|
27
|
+
</script>
|
|
28
|
+
<script src="/assets/js/form.js"></script>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
{{> head}}
|
|
4
|
+
|
|
5
|
+
<body>
|
|
6
|
+
<div class="i_w center">
|
|
7
|
+
<div>
|
|
8
|
+
<h1>Looks like {{ name }} is not installed yet!</h1>
|
|
9
|
+
<p>Contact your organization administrator to install it. More info on <a
|
|
10
|
+
href='https://support.crowdin.com'>Link
|
|
11
|
+
to how to install</a></p>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</body>
|
|
15
|
+
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
{{> head}}
|
|
4
|
+
|
|
5
|
+
<body>
|
|
6
|
+
<div class="i_w center">
|
|
7
|
+
<div>
|
|
8
|
+
<crowdin-card is-shadowed is-padding-lg class="login">
|
|
9
|
+
<img alt='{{ name }} logo' src='logo.png' />
|
|
10
|
+
<crowdin-h4 id="integration-name">{{ name }}</crowdin-h4>
|
|
11
|
+
<div class="inputs">
|
|
12
|
+
{{#each loginFields}}
|
|
13
|
+
{{#if key}}
|
|
14
|
+
{{#ifeq type "checkbox"}}
|
|
15
|
+
<crowdin-checkbox
|
|
16
|
+
id="{{key}}"
|
|
17
|
+
label="{{label}}"
|
|
18
|
+
value="false"
|
|
19
|
+
use-switch
|
|
20
|
+
{{#if helpText}}
|
|
21
|
+
help-text="{{helpText}}"
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{#if helpTextHtml}}
|
|
24
|
+
help-text-html="{{helpTextHtml}}"
|
|
25
|
+
{{/if}}
|
|
26
|
+
{{#ifeq defaultValue true}}
|
|
27
|
+
checked="{{defaultValue}}"
|
|
28
|
+
{{/ifeq}}
|
|
29
|
+
>
|
|
30
|
+
</crowdin-checkbox>
|
|
31
|
+
{{else}}
|
|
32
|
+
{{#ifeq type "select"}}
|
|
33
|
+
<crowdin-select
|
|
34
|
+
{{#if isMulti}}
|
|
35
|
+
is-multi
|
|
36
|
+
close-on-select="false"
|
|
37
|
+
{{/if}}
|
|
38
|
+
{{#if isSearchable}}
|
|
39
|
+
is-searchable
|
|
40
|
+
{{/if}}
|
|
41
|
+
id="{{key}}"
|
|
42
|
+
label="{{label}}"
|
|
43
|
+
{{#if helpText}}
|
|
44
|
+
help-text="{{helpText}}"
|
|
45
|
+
{{/if}}
|
|
46
|
+
{{#if helpTextHtml}}
|
|
47
|
+
help-text-html="{{helpTextHtml}}"
|
|
48
|
+
{{/if}}
|
|
49
|
+
>
|
|
50
|
+
{{#each options}}
|
|
51
|
+
<option {{#ifeq ../defaultValue value}} selected {{/ifeq}} value="{{value}}">{{label}}</option>
|
|
52
|
+
{{/each}}
|
|
53
|
+
</crowdin-select>
|
|
54
|
+
{{else}}
|
|
55
|
+
{{#ifeq type "textarea"}}
|
|
56
|
+
<crowdin-textarea
|
|
57
|
+
id="{{key}}"
|
|
58
|
+
label="{{label}}"
|
|
59
|
+
{{#if helpText}}
|
|
60
|
+
help-text="{{helpText}}"
|
|
61
|
+
{{/if}}
|
|
62
|
+
{{#if helpTextHtml}}
|
|
63
|
+
help-text-html="{{helpTextHtml}}"
|
|
64
|
+
{{/if}}
|
|
65
|
+
value="{{#if defaultValue}}{{defaultValue}}{{/if}}">
|
|
66
|
+
</crowdin-textarea>
|
|
67
|
+
{{else}}
|
|
68
|
+
{{#ifeq type "file"}}
|
|
69
|
+
<div class="file-field">
|
|
70
|
+
{{#if helpText}}<div class="help-text">{{helpText}}</div>{{/if}}
|
|
71
|
+
{{#if helpTextHtml}}<div class="help-text">{{helpTextHtml}}</div>{{/if}}
|
|
72
|
+
<div class="upload">
|
|
73
|
+
<crowdin-button outlined onclick="uploadFiles('{{key}}');">{{label}}</crowdin-button>
|
|
74
|
+
<textarea hidden id="{{key}}"></textarea>
|
|
75
|
+
<input
|
|
76
|
+
id="store_{{key}}"
|
|
77
|
+
data-id="{{key}}"
|
|
78
|
+
{{#if accept}}
|
|
79
|
+
accept="{{accept}}"
|
|
80
|
+
{{/if}}
|
|
81
|
+
onchange="readFileData(event)"
|
|
82
|
+
hidden
|
|
83
|
+
type="{{type}}"
|
|
84
|
+
>
|
|
85
|
+
<div class="uploaded-file"><i>No file</i></div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
{{else}}
|
|
89
|
+
<crowdin-input
|
|
90
|
+
id="{{key}}"
|
|
91
|
+
label="{{label}}"
|
|
92
|
+
{{#if helpText}}
|
|
93
|
+
help-text="{{helpText}}"
|
|
94
|
+
{{/if}}
|
|
95
|
+
{{#if helpTextHtml}}
|
|
96
|
+
help-text-html="{{helpTextHtml}}"
|
|
97
|
+
{{/if}}
|
|
98
|
+
{{#if type}}
|
|
99
|
+
type="{{type}}"
|
|
100
|
+
{{/if}}
|
|
101
|
+
value="{{#if defaultValue}}{{defaultValue}}{{/if}}">
|
|
102
|
+
</crowdin-input>
|
|
103
|
+
{{/ifeq}}
|
|
104
|
+
{{/ifeq}}
|
|
105
|
+
{{/ifeq}}
|
|
106
|
+
{{/ifeq}}
|
|
107
|
+
{{else}}
|
|
108
|
+
{{#if labelHtml}}
|
|
109
|
+
<crowdin-p>{{{labelHtml}}}</crowdin-p>
|
|
110
|
+
{{else}}
|
|
111
|
+
<crowdin-p>{{label}}</crowdin-p>
|
|
112
|
+
{{/if}}
|
|
113
|
+
{{/if}}
|
|
114
|
+
<div style="padding: 8px"></div>
|
|
115
|
+
{{/each}}
|
|
116
|
+
</div>
|
|
117
|
+
<crowdin-button
|
|
118
|
+
id="login-button"
|
|
119
|
+
outlined icon-after="arrow_forward"
|
|
120
|
+
{{#if oauthLogin}}
|
|
121
|
+
onclick="oauthLogin()"
|
|
122
|
+
{{else}}
|
|
123
|
+
onclick="integrationLogin()"
|
|
124
|
+
{{/if}}
|
|
125
|
+
>
|
|
126
|
+
Log In With {{ name }}
|
|
127
|
+
</crowdin-button>
|
|
128
|
+
</crowdin-card>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<crowdin-toasts></crowdin-toasts>
|
|
132
|
+
</body>
|
|
133
|
+
<script type="text/javascript">
|
|
134
|
+
const loginButton = document.querySelector('#login-button');
|
|
135
|
+
|
|
136
|
+
function oauthLogin() {
|
|
137
|
+
{{#if oauthUrl}}
|
|
138
|
+
const url = '{{{ oauthUrl }}}';
|
|
139
|
+
{{else}}
|
|
140
|
+
const url = undefined;
|
|
141
|
+
{{/if}}
|
|
142
|
+
if (url) {
|
|
143
|
+
openOAuthPopup(url);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
loginButton.setAttribute('disabled', true);
|
|
147
|
+
loginButton.setAttribute('is-loading', true);
|
|
148
|
+
checkOrigin()
|
|
149
|
+
.then(queryParams =>
|
|
150
|
+
fetch(`api/oauth-url${queryParams}`, {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
headers: { 'Content-Type': 'application/json' },
|
|
153
|
+
body: JSON.stringify({ loginForm: getLoginForm() })
|
|
154
|
+
})
|
|
155
|
+
)
|
|
156
|
+
.then(checkResponse)
|
|
157
|
+
.then(res => openOAuthPopup(res.url))
|
|
158
|
+
.catch(e => {
|
|
159
|
+
loginButton.setAttribute('disabled', false);
|
|
160
|
+
loginButton.setAttribute('is-loading', false);
|
|
161
|
+
catchRejection(e, 'Can\'t get oauth token');
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function openOAuthPopup(url) {
|
|
166
|
+
const oauthWindow = window.open(url, '{{ name }}', 'location=0,status=0,width=800,height=400');
|
|
167
|
+
postPromises['oauth_popup'] = {
|
|
168
|
+
resolve: (data) => {
|
|
169
|
+
if (data.error) {
|
|
170
|
+
showToast(data.error);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
integrationLogin({
|
|
174
|
+
refreshToken: data.refreshToken,
|
|
175
|
+
accessToken: data.accessToken,
|
|
176
|
+
expireIn: data.expireIn,
|
|
177
|
+
timestamp: data.timestamp
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function integrationLogin(oauthCredentials) {
|
|
184
|
+
const credentials = oauthCredentials || getLoginForm();
|
|
185
|
+
loginButton.setAttribute('disabled', true);
|
|
186
|
+
loginButton.setAttribute('is-loading', true);
|
|
187
|
+
checkOrigin()
|
|
188
|
+
.then(queryParams =>
|
|
189
|
+
fetch(`api/login${queryParams}`, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: { 'Content-Type': 'application/json' },
|
|
192
|
+
body: JSON.stringify({ credentials })
|
|
193
|
+
})
|
|
194
|
+
)
|
|
195
|
+
.then(checkResponse)
|
|
196
|
+
.then(localStorage.removeItem('revised_{{name}}'))
|
|
197
|
+
.then(reloadLocation)
|
|
198
|
+
.catch(e => {
|
|
199
|
+
loginButton.setAttribute('disabled', false);
|
|
200
|
+
loginButton.setAttribute('is-loading', false);
|
|
201
|
+
catchRejection(e, 'Credentials are not stored');
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function getLoginForm() {
|
|
206
|
+
return {
|
|
207
|
+
{{#each loginFields}}
|
|
208
|
+
{{#if key}}
|
|
209
|
+
{{#ifeq type "checkbox"}}
|
|
210
|
+
'{{key}}': !!document.querySelector('#{{key}}').checked,
|
|
211
|
+
{{else}}
|
|
212
|
+
'{{key}}': document.querySelector('#{{key}}').getAttribute('value') || document.querySelector('#{{key}}').value,
|
|
213
|
+
{{/ifeq}}
|
|
214
|
+
{{/if}}
|
|
215
|
+
{{/each}}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function uploadFiles(id) {
|
|
220
|
+
const input = document.querySelector(`#store_${id}`);
|
|
221
|
+
input.value = '';
|
|
222
|
+
input.click();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function readFileData(event) {
|
|
226
|
+
const reader = new FileReader();
|
|
227
|
+
const identifier = event.target.getAttribute('data-id');
|
|
228
|
+
const fileName = document.querySelector('.uploaded-file');
|
|
229
|
+
const input = document.querySelector(`#${identifier}`);
|
|
230
|
+
const file = event.target.files[0];
|
|
231
|
+
fileName.innerText = file.name;
|
|
232
|
+
|
|
233
|
+
reader.onloadstart = function() {
|
|
234
|
+
loginButton.setAttribute('disabled', true);
|
|
235
|
+
loginButton.setAttribute('is-loading', true);
|
|
236
|
+
};
|
|
237
|
+
reader.onloadend = function() {
|
|
238
|
+
loginButton.setAttribute('disabled', false);
|
|
239
|
+
loginButton.setAttribute('is-loading', false);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
reader.onload = () => {
|
|
243
|
+
input.value = reader.result;
|
|
244
|
+
};
|
|
245
|
+
reader.readAsText(file);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
</script>
|
|
249
|
+
|
|
250
|
+
</html>
|