1
|
|
|
/** |
2
|
|
|
* CMS Pico - Create websites using Pico CMS for Nextcloud. |
3
|
|
|
* |
4
|
|
|
* @copyright Copyright (c) 2019, Daniel Rudolf (<[email protected]>) |
5
|
|
|
* |
6
|
|
|
* @license GNU AGPL version 3 or any later version |
7
|
|
|
* |
8
|
|
|
* This program is free software: you can redistribute it and/or modify |
9
|
|
|
* it under the terms of the GNU Affero General Public License as |
10
|
|
|
* published by the Free Software Foundation, either version 3 of the |
11
|
|
|
* License, or (at your option) any later version. |
12
|
|
|
* |
13
|
|
|
* This program is distributed in the hope that it will be useful, |
14
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16
|
|
|
* GNU Affero General Public License for more details. |
17
|
|
|
* |
18
|
|
|
* You should have received a copy of the GNU Affero General Public License |
19
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
20
|
|
|
*/ |
21
|
|
|
|
22
|
|
|
/** global: OC */ |
23
|
|
|
/** global: OCA */ |
24
|
|
|
/** global: jQuery */ |
25
|
|
|
|
26
|
|
|
(function (document, $, OC, OCA) { |
27
|
|
|
'use strict'; |
28
|
|
|
|
29
|
|
|
if (!OCA.CMSPico) { |
30
|
|
|
/** @namespace OCA.CMSPico */ |
31
|
|
|
OCA.CMSPico = {}; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @class |
36
|
|
|
* |
37
|
|
|
* @param {jQuery} $element |
38
|
|
|
* @param {Object} [options] |
39
|
|
|
* @param {string} [options.route] |
40
|
|
|
* @param {jQuery|string} [options.template] |
41
|
|
|
* @param {jQuery|string} [options.loadingTemplate] |
42
|
|
|
* @param {jQuery|string} [options.errorTemplate] |
43
|
|
|
*/ |
44
|
|
|
OCA.CMSPico.List = function ($element, options) { |
45
|
|
|
this.initialize($element, options); |
46
|
|
|
}; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @lends OCA.CMSPico.List.prototype |
50
|
|
|
*/ |
51
|
|
|
OCA.CMSPico.List.prototype = { |
52
|
|
|
/** @member {jQuery} */ |
53
|
|
|
$element: $(), |
54
|
|
|
|
55
|
|
|
/** @member {string} */ |
56
|
|
|
route: '', |
57
|
|
|
|
58
|
|
|
/** @member {jQuery} */ |
59
|
|
|
$template: $(), |
60
|
|
|
|
61
|
|
|
/** @member {jQuery} */ |
62
|
|
|
$loadingTemplate: $(), |
63
|
|
|
|
64
|
|
|
/** @member {jQuery} */ |
65
|
|
|
$errorTemplate: $(), |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @constructs |
69
|
|
|
* |
70
|
|
|
* @param {jQuery} $element |
71
|
|
|
* @param {Object} [options] |
72
|
|
|
* @param {string} [options.route] |
73
|
|
|
* @param {jQuery|string} [options.template] |
74
|
|
|
* @param {jQuery|string} [options.loadingTemplate] |
75
|
|
|
* @param {jQuery|string} [options.errorTemplate] |
76
|
|
|
*/ |
77
|
|
|
initialize: function ($element, options) { |
78
|
|
|
this.$element = $element; |
79
|
|
|
|
80
|
|
|
options = $.extend({ |
81
|
|
|
route: $element.data('route'), |
82
|
|
|
template: $element.data('template'), |
83
|
|
|
loadingTemplate: $element.data('loadingTemplate'), |
84
|
|
|
errorTemplate: $element.data('errorTemplate') |
85
|
|
|
}, options); |
86
|
|
|
|
87
|
|
|
this.route = options.route; |
88
|
|
|
this.$template = $(options.template); |
89
|
|
|
this.$loadingTemplate = $(options.loadingTemplate); |
90
|
|
|
this.$errorTemplate = $(options.errorTemplate); |
91
|
|
|
|
92
|
|
|
var signature = 'OCA.CMSPico.List.initialize()'; |
93
|
|
|
if (!this.route) throw signature + ': No route given'; |
94
|
|
|
if (!this.$template.length) throw signature + ': No valid list template given'; |
95
|
|
|
if (!this.$loadingTemplate.length) throw signature + ': No valid loading template given'; |
96
|
|
|
if (!this.$errorTemplate.length) throw signature + ': No valid error template given'; |
97
|
|
|
}, |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @public |
101
|
|
|
*/ |
102
|
|
|
reload: function () { |
103
|
|
|
this._api('GET'); |
104
|
|
|
}, |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @public |
108
|
|
|
* @abstract |
109
|
|
|
* |
110
|
|
|
* @param {Object} data |
111
|
|
|
*/ |
112
|
|
|
update: function (data) {}, |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @protected |
116
|
|
|
* |
117
|
|
|
* @param {string} method |
118
|
|
|
* @param {string} [item] |
119
|
|
|
* @param {Object} [data] |
120
|
|
|
* @param {function(data)} [callback] |
121
|
|
|
*/ |
122
|
|
|
_api: function (method, item, data, callback) { |
123
|
|
|
var that = this, |
124
|
|
|
url = this.route + (item ? ((this.route.substr(-1) !== '/') ? '/' : '') + item : ''); |
125
|
|
|
|
126
|
|
|
this._content(this.$loadingTemplate); |
127
|
|
|
|
128
|
|
|
$.ajax({ |
129
|
|
|
method: method, |
130
|
|
|
url: OC.generateUrl(url), |
131
|
|
|
data: data || {} |
132
|
|
|
}).done(function (data, textStatus, jqXHR) { |
133
|
|
|
if (callback === undefined) { |
134
|
|
|
that.update(data); |
135
|
|
|
} else if (typeof callback === 'function') { |
136
|
|
|
callback(data); |
137
|
|
|
} |
138
|
|
|
}).fail(function (jqXHR, textStatus, errorThrown) { |
139
|
|
|
that._error((jqXHR.responseJSON || {}).error); |
140
|
|
|
}); |
141
|
|
|
}, |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* @protected |
145
|
|
|
* |
146
|
|
|
* @param {jQuery} $template |
147
|
|
|
* @param {object} [vars] |
148
|
|
|
* @param {boolean} [replaceContent] |
149
|
|
|
* |
150
|
|
|
* @returns {jQuery} |
151
|
|
|
*/ |
152
|
|
|
_content: function ($template, vars, replaceContent) { |
153
|
|
|
var $baseElement = $($template.data('replaces') || $template.data('appendTo') || this.$element), |
154
|
|
|
$content = $template.octemplate(vars || {}) || $(); |
155
|
|
|
|
156
|
|
|
$baseElement.find('.has-tooltip').tooltip('hide'); |
157
|
|
|
$content.find('.has-tooltip').tooltip(); |
158
|
|
|
|
159
|
|
|
if ((replaceContent !== undefined) ? replaceContent : $template.data('replaces')) { |
160
|
|
|
$baseElement.empty(); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$content.appendTo($baseElement); |
164
|
|
|
|
165
|
|
|
return $content; |
166
|
|
|
}, |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @protected |
170
|
|
|
* |
171
|
|
|
* @param {string} [message] |
172
|
|
|
*/ |
173
|
|
|
_error: function (message) { |
174
|
|
|
var $error = this._content(this.$errorTemplate, { message: message || '' }), |
175
|
|
|
that = this; |
176
|
|
|
|
177
|
|
|
if (message) { |
178
|
|
|
$error.find('.error-details').show(); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
$error.find('.action-reload').on('click.CMSPicoAdminList', function (event) { |
182
|
|
|
event.preventDefault(); |
183
|
|
|
that.reload(); |
184
|
|
|
}); |
185
|
|
|
} |
186
|
|
|
}; |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @class |
190
|
|
|
* |
191
|
|
|
* @param {jQuery} $element |
192
|
|
|
* @param {Object} [options] |
193
|
|
|
* @param {string} [options.route] |
194
|
|
|
*/ |
195
|
|
|
OCA.CMSPico.Form = function ($element, options) { |
196
|
|
|
this.initialize($element, options); |
197
|
|
|
}; |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* @lends OCA.CMSPico.Form.prototype |
201
|
|
|
*/ |
202
|
|
|
OCA.CMSPico.Form.prototype = { |
203
|
|
|
/** @member {jQuery} */ |
204
|
|
|
$element: $(), |
205
|
|
|
|
206
|
|
|
/** @member {string} */ |
207
|
|
|
route: '', |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* @constructs |
211
|
|
|
* |
212
|
|
|
* @param {jQuery} $element |
213
|
|
|
* @param {Object} [options] |
214
|
|
|
* @param {string} [options.route] |
215
|
|
|
*/ |
216
|
|
|
initialize: function ($element, options) { |
217
|
|
|
this.$element = $element; |
218
|
|
|
|
219
|
|
|
options = $.extend({ |
220
|
|
|
route: $element.data('route') |
221
|
|
|
}, options); |
222
|
|
|
|
223
|
|
|
this.route = options.route; |
224
|
|
|
|
225
|
|
|
var signature = 'OCA.CMSPico.Form.initialize()'; |
226
|
|
|
if (!this.route) throw signature + ': No route given'; |
227
|
|
|
}, |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* @public |
231
|
|
|
* @abstract |
232
|
|
|
*/ |
233
|
|
|
prepare: function () {}, |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @public |
237
|
|
|
* @abstract |
238
|
|
|
*/ |
239
|
|
|
submit: function () {}, |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* @protected |
243
|
|
|
* |
244
|
|
|
* @param {jQuery} $element |
245
|
|
|
* @param {string|number|Array} [value] |
246
|
|
|
* |
247
|
|
|
* @returns {jQuery|string|number|Array} |
248
|
|
|
*/ |
249
|
|
|
_val: function ($element, value) { |
250
|
|
|
if (value !== undefined) { |
251
|
|
|
return $element.is(':input') ? $element.val(value) : $element.text(value); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
return $element.is(':input') ? $element.val() : $element.text(); |
255
|
|
|
} |
256
|
|
|
}; |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* @class |
260
|
|
|
* |
261
|
|
|
* @param {jQuery} $template |
262
|
|
|
* @param {object} options |
263
|
|
|
* @param {string} options.title |
264
|
|
|
* @param {object} [options.templateData] |
265
|
|
|
* @param {object[]} [options.buttons] |
266
|
|
|
*/ |
267
|
|
|
OCA.CMSPico.Dialog = function ($template, options) { |
268
|
|
|
this.initialize($template, options); |
269
|
|
|
}; |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* @lends OCA.CMSPico.Dialog |
273
|
|
|
*/ |
274
|
|
|
$.extend(OCA.CMSPico.Dialog, { |
275
|
|
|
/** |
276
|
|
|
* @type {number} |
277
|
|
|
* @constant |
278
|
|
|
*/ |
279
|
|
|
BUTTON_ABORT: 1, |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @type {number} |
283
|
|
|
* @constant |
284
|
|
|
*/ |
285
|
|
|
BUTTON_SUBMIT: 2, |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* @type {number} |
289
|
|
|
* @protected |
290
|
|
|
*/ |
291
|
|
|
dialogId: 0 |
292
|
|
|
}); |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* @lends OCA.CMSPico.Dialog.prototype |
296
|
|
|
*/ |
297
|
|
|
OCA.CMSPico.Dialog.prototype = { |
298
|
|
|
/** @member {jQuery} */ |
299
|
|
|
$element: $(), |
300
|
|
|
|
301
|
|
|
/** @member {string} */ |
302
|
|
|
dialogId: '', |
303
|
|
|
|
304
|
|
|
/** @member {jQuery} */ |
305
|
|
|
$template: $(), |
306
|
|
|
|
307
|
|
|
/** @member {string} */ |
308
|
|
|
title: '', |
309
|
|
|
|
310
|
|
|
/** @member {object} */ |
311
|
|
|
templateData: {}, |
312
|
|
|
|
313
|
|
|
/** @member {object[]} */ |
314
|
|
|
buttons: [], |
315
|
|
|
|
316
|
|
|
/** @member {object} */ |
317
|
|
|
events: {}, |
318
|
|
|
|
319
|
|
|
/** @member {boolean} */ |
320
|
|
|
opened: false, |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* @constructs |
324
|
|
|
* |
325
|
|
|
* @param {jQuery} $template |
326
|
|
|
* @param {object} options |
327
|
|
|
* @param {string} options.title |
328
|
|
|
* @param {object} [options.templateData] |
329
|
|
|
* @param {object[]} [options.buttons] |
330
|
|
|
*/ |
331
|
|
|
initialize: function ($template, options) { |
332
|
|
|
this.$template = $template; |
333
|
|
|
|
334
|
|
|
options = $.extend({ |
335
|
|
|
title: '', |
336
|
|
|
templateData: {}, |
337
|
|
|
buttons: [] |
338
|
|
|
}, options); |
339
|
|
|
|
340
|
|
|
this.title = options.title; |
341
|
|
|
this.templateData = options.templateData; |
342
|
|
|
this.buttons = options.buttons; |
343
|
|
|
this.events = {}; |
344
|
|
|
|
345
|
|
|
this.dialogId = 'picocms-dialog-' + ++OCA.CMSPico.Dialog.dialogId; |
346
|
|
|
|
347
|
|
|
var signature = 'OCA.CMSPico.Dialog.initialize()'; |
348
|
|
|
if (!this.title) throw signature + ': No dialog title given'; |
349
|
|
|
}, |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* @protected |
353
|
|
|
* |
354
|
|
|
* @returns {object[]} |
355
|
|
|
*/ |
356
|
|
|
_getButtons: function () { |
357
|
|
|
var buttons = [], |
358
|
|
|
that = this; |
359
|
|
|
|
360
|
|
|
for (var i = 0; i < this.buttons.length; i++) { |
361
|
|
|
if (this.buttons[i].type !== undefined) { |
362
|
|
|
switch (this.buttons[i].type) { |
363
|
|
|
case OCA.CMSPico.Dialog.BUTTON_ABORT: |
364
|
|
|
buttons.push($.extend({ |
365
|
|
|
text: t('cms_pico', 'Abort'), |
366
|
|
|
click: function (event) { |
367
|
|
|
that.close(); |
368
|
|
|
} |
369
|
|
|
}, this.buttons[i])); |
370
|
|
|
break; |
371
|
|
|
|
372
|
|
|
case OCA.CMSPico.Dialog.BUTTON_SUBMIT: |
373
|
|
|
buttons.push($.extend({ |
374
|
|
|
text: t('cms_pico', 'Save'), |
375
|
|
|
defaultButton: true, |
376
|
|
|
click: function (event) { |
377
|
|
|
that.submit(); |
378
|
|
|
that.close(); |
379
|
|
|
} |
380
|
|
|
}, this.buttons[i])); |
381
|
|
|
break; |
382
|
|
|
} |
383
|
|
|
} else { |
384
|
|
|
buttons.push(this.buttons[i]); |
385
|
|
|
} |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
return buttons; |
389
|
|
|
}, |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* @public |
393
|
|
|
*/ |
394
|
|
|
open: function () { |
395
|
|
|
if (this.opened) { |
396
|
|
|
// nothing to do |
397
|
|
|
return; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
this.$element = this.$template.octemplate($.extend({}, this.templateData, { |
401
|
|
|
id: this.dialogId, |
402
|
|
|
title: this.title |
403
|
|
|
})); |
404
|
|
|
|
405
|
|
|
$('#app-content').append(this.$element); |
406
|
|
|
|
407
|
|
|
var that = this; |
408
|
|
|
this.$element.ocdialog({ |
409
|
|
|
buttons: this._getButtons(), |
410
|
|
|
close: function () { |
411
|
|
|
that.opened = false; |
412
|
|
|
that.close(); |
413
|
|
|
} |
414
|
|
|
}); |
415
|
|
|
|
416
|
|
|
this._trigger('open'); |
417
|
|
|
this.opened = true; |
418
|
|
|
}, |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* @public |
422
|
|
|
*/ |
423
|
|
|
submit: function () { |
424
|
|
|
this._trigger('submit'); |
425
|
|
|
}, |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* @public |
429
|
|
|
*/ |
430
|
|
|
close: function () { |
431
|
|
|
if (this.opened) { |
432
|
|
|
this.$element.ocdialog('close'); |
433
|
|
|
return; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
this._trigger('close'); |
437
|
|
|
}, |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* @public |
441
|
|
|
* |
442
|
|
|
* @param {string} eventName |
443
|
|
|
* @param {function} callback |
444
|
|
|
*/ |
445
|
|
|
on: function (eventName, callback) { |
446
|
|
|
var event = this._parseEventName(eventName); |
447
|
|
|
if (event === false) { |
448
|
|
|
$.error('Invalid event name: ' + eventName); |
449
|
|
|
return; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
if (this.events[event[0]] === undefined) { |
453
|
|
|
this.events[event[0]] = {}; |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
this.events[event[0]][event[1]] = callback; |
457
|
|
|
}, |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* @public |
461
|
|
|
* |
462
|
|
|
* @param {string} eventName |
463
|
|
|
*/ |
464
|
|
|
off: function (eventName) { |
465
|
|
|
var event = this._parseEventName(eventName); |
466
|
|
|
if (event === false) { |
467
|
|
|
$.error('Invalid event name: ' + eventName); |
468
|
|
|
return false; |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
if ((this.events[event[0]] !== undefined) && (this.events[event[0]][event[1]] !== undefined)) { |
472
|
|
|
delete this.events[event[0]][event[1]]; |
473
|
|
|
return true; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
return false; |
477
|
|
|
}, |
478
|
|
|
|
479
|
|
|
/** |
480
|
|
|
* @protected |
481
|
|
|
* |
482
|
|
|
* @param {string} eventType |
483
|
|
|
*/ |
484
|
|
|
_trigger: function (eventType) { |
485
|
|
|
if (!this.events[eventType]) { |
486
|
|
|
return; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
var that = this; |
490
|
|
|
$.each(this.events[eventType], function (id, callback) { |
491
|
|
|
callback.apply(that); |
492
|
|
|
}); |
493
|
|
|
}, |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* @protected |
497
|
|
|
* |
498
|
|
|
* @param {string} eventName |
499
|
|
|
* |
500
|
|
|
* @returns {[string, string]|false} |
501
|
|
|
*/ |
502
|
|
|
_parseEventName: function (eventName) { |
503
|
|
|
var pos = eventName.indexOf('.'); |
504
|
|
|
pos = (pos !== -1) ? pos : eventName.length; |
505
|
|
|
|
506
|
|
|
var type = eventName.substr(0, pos), |
507
|
|
|
id = eventName.substr(pos + 1); |
508
|
|
|
|
509
|
|
|
if (!type || !id) { |
510
|
|
|
return false; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
return [ type, id ]; |
514
|
|
|
} |
515
|
|
|
}; |
516
|
|
|
|
517
|
|
|
/** @namespace OCA.CMSPico.Util */ |
518
|
|
|
OCA.CMSPico.Util = { |
519
|
|
|
/** |
520
|
|
|
* @param {string} string |
521
|
|
|
* |
522
|
|
|
* @returns string |
523
|
|
|
*/ |
524
|
|
|
unescape: function (string) { |
525
|
|
|
return string |
526
|
|
|
.replace(/&/g, '&') |
527
|
|
|
.replace(/</g, '<') |
528
|
|
|
.replace(/>/g, '>') |
529
|
|
|
.replace(/"/g, '"') |
530
|
|
|
.replace(/'/g, "'"); |
531
|
|
|
}, |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* @param {jQuery} $element |
535
|
|
|
* |
536
|
|
|
* @returns {object} |
537
|
|
|
*/ |
538
|
|
|
serialize: function ($element) { |
539
|
|
|
var dataArray = $element.serializeArray(), |
540
|
|
|
dataObject = {}; |
541
|
|
|
|
542
|
|
|
$element.find('input[type="button"]').each(function () { |
543
|
|
|
var $button = $(this); |
544
|
|
|
dataArray.push({ name: $button.prop('name'), value: $button.val() }); |
545
|
|
|
}); |
546
|
|
|
|
547
|
|
|
$.each(dataArray, function (_, data) { |
548
|
|
|
var key = data.name, |
549
|
|
|
matches = key.match(/^([a-z_][a-z0-9_]*)\[(\d*|[a-z0-9_]+)\]/i); |
550
|
|
|
|
551
|
|
|
if (matches === null) { |
552
|
|
|
dataObject[key] = data.value; |
553
|
|
|
} else { |
554
|
|
|
if (typeof dataObject[matches[1]] !== 'object') { |
555
|
|
|
dataObject[matches[1]] = {}; |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
var result = dataObject[matches[1]], |
559
|
|
|
subKey = matches[2]; |
560
|
|
|
|
561
|
|
|
key = key.substr(matches[0].length); |
562
|
|
|
matches = key.match(/^\[(\d*|[a-z0-9_]+)\]/i); |
563
|
|
|
|
564
|
|
|
while (matches !== null) { |
565
|
|
|
if (typeof result[matches[1]] !== 'object') { |
566
|
|
|
result[matches[1]] = {}; |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
result = result[subKey]; |
570
|
|
|
subKey = matches[1]; |
571
|
|
|
|
572
|
|
|
key = key.substr(matches[0].length); |
573
|
|
|
matches = key.match(/^\[(\d*|[a-z0-9_]+)\]/i); |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
result[subKey] = data.value; |
577
|
|
|
} |
578
|
|
|
}); |
579
|
|
|
|
580
|
|
|
return dataObject; |
581
|
|
|
} |
582
|
|
|
}; |
583
|
|
|
})(document, jQuery, OC, OCA); |
584
|
|
|
|