1
|
|
|
/** |
2
|
|
|
* Symphony backend views |
3
|
|
|
* |
4
|
|
|
* @package assets |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
(function($, Symphony) { |
8
|
|
|
'use strict'; |
9
|
|
|
|
10
|
|
|
/*-------------------------------------------------------------------------- |
11
|
|
|
General backend view |
12
|
|
|
--------------------------------------------------------------------------*/ |
13
|
|
|
|
14
|
|
|
Symphony.View.add('/:context*:', function() { |
15
|
|
|
|
16
|
|
|
// Initialise core plugins |
17
|
|
|
Symphony.Elements.contents.find('select.picker[data-interactive]').symphonyPickable(); |
18
|
|
|
Symphony.Elements.contents.find('ul.orderable[data-interactive]').symphonyOrderable(); |
19
|
|
|
Symphony.Elements.contents.find('table.selectable[data-interactive]').symphonySelectable(); |
20
|
|
|
Symphony.Elements.wrapper.find('.filters-duplicator[data-interactive]').symphonyDuplicator(); |
21
|
|
|
Symphony.Elements.wrapper.find('.tags[data-interactive]').symphonyTags(); |
22
|
|
|
Symphony.Elements.wrapper.find('div.drawer').symphonyDrawer(); |
23
|
|
|
Symphony.Elements.header.symphonyNotify(); |
24
|
|
|
|
25
|
|
|
// Fix for Webkit browsers to initially show the options. #2127 |
26
|
|
|
$('select[multiple=multiple]').scrollTop(0); |
27
|
|
|
|
28
|
|
|
// Initialise plugins inside duplicators |
29
|
|
|
Symphony.Elements.contents.find('.duplicator').on('constructshow.duplicator', '.instance', function() { |
30
|
|
|
// Enable tag lists inside duplicators |
31
|
|
|
$(this).find('.tags[data-interactive]').symphonyTags(); |
32
|
|
|
// Enable parameter suggestions |
33
|
|
|
Symphony.Interface.Suggestions.init($(this), 'input[type="text"]'); |
34
|
|
|
}); |
35
|
|
|
|
36
|
|
|
// Navigation sizing |
37
|
|
|
Symphony.Elements.window.on('resize.admin nav.admin', function() { |
38
|
|
|
var content = Symphony.Elements.nav.find('ul.content'), |
39
|
|
|
structure = Symphony.Elements.nav.find('ul.structure'), |
40
|
|
|
width = content.width() + structure.width() + 20; |
41
|
|
|
|
42
|
|
|
// Compact mode |
43
|
|
|
if(width > window.innerWidth) { |
44
|
|
|
Symphony.Elements.nav.removeClass('wide'); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
// Wide mode |
48
|
|
|
else { |
49
|
|
|
Symphony.Elements.nav.addClass('wide'); |
50
|
|
|
} |
51
|
|
|
}).trigger('nav.admin'); |
52
|
|
|
|
53
|
|
|
// Accessible navigation |
54
|
|
|
Symphony.Elements.nav.on('focus.admin blur.admin', 'a', function() { |
55
|
|
|
$(this).parents('li').eq(1).toggleClass('current'); |
56
|
|
|
}); |
57
|
|
|
|
58
|
|
|
// Notifier sizing |
59
|
|
|
Symphony.Elements.window.on('resize.admin', function() { |
60
|
|
|
Symphony.Elements.header.find('.notifier').trigger('resize.notify'); |
61
|
|
|
}); |
62
|
|
|
|
63
|
|
|
// Table sizing |
64
|
|
|
Symphony.Elements.window.on('resize.admin table.admin', function() { |
65
|
|
|
var table = Symphony.Elements.contents.find('table:first'); |
66
|
|
|
|
67
|
|
|
// Fix table size, if width exceeds the visibile viewport area. |
68
|
|
|
if(table.width() > Symphony.Elements.html.width()){ |
69
|
|
|
table.addClass('fixed'); |
70
|
|
|
} |
71
|
|
|
else { |
72
|
|
|
table.removeClass('fixed'); |
73
|
|
|
} |
74
|
|
|
}).trigger('table.admin'); |
75
|
|
|
|
76
|
|
|
// Orderable tables |
77
|
|
|
var oldSorting = null, |
78
|
|
|
orderable = Symphony.Elements.contents.find('table.orderable[data-interactive]'); |
79
|
|
|
|
80
|
|
|
// Ignore tables with less than two rows |
81
|
|
|
orderable = orderable.filter(function() { |
82
|
|
|
return ($(this).find('tbody tr').length > 1); |
83
|
|
|
}); |
84
|
|
|
|
85
|
|
|
// Initalise ordering |
86
|
|
|
orderable.symphonyOrderable({ |
87
|
|
|
items: 'tr', |
88
|
|
|
handles: 'td' |
89
|
|
|
}) |
90
|
|
|
.on('orderstart.orderable', function() { |
91
|
|
|
// Store current sort order |
92
|
|
|
oldSorting = orderable.find('input').map(function(e) { return this.name + '=' + (e + 1); }).get().join('&'); |
93
|
|
|
}) |
94
|
|
|
.on('orderstop.orderable', function() { |
95
|
|
|
var newSorting = orderable.find('input').map(function(e) { return this.name + '=' + (e + 1); }).get().join('&'); |
96
|
|
|
|
97
|
|
|
// Store sort order, if changed |
98
|
|
|
if(oldSorting !== null && newSorting !== oldSorting) { |
99
|
|
|
// Update UI |
100
|
|
|
orderable.addClass('busy'); |
101
|
|
|
|
102
|
|
|
// Update items |
103
|
|
|
orderable.trigger('orderupdate.admin'); |
104
|
|
|
|
105
|
|
|
// Update old value |
106
|
|
|
oldSorting = newSorting; |
107
|
|
|
|
108
|
|
|
// Add XSRF token |
109
|
|
|
newSorting += '&' + Symphony.Utilities.getXSRF(true); |
110
|
|
|
|
111
|
|
|
// Send request |
112
|
|
|
$.ajax({ |
113
|
|
|
type: 'POST', |
114
|
|
|
url: Symphony.Context.get('symphony') + '/ajax/reorder' + Symphony.Context.get('route'), |
115
|
|
|
data: newSorting, |
116
|
|
|
error: function() { |
117
|
|
|
Symphony.Message.post(Symphony.Language.get('Reordering was unsuccessful.'), 'error'); |
118
|
|
|
}, |
119
|
|
|
complete: function() { |
120
|
|
|
orderable.removeClass('busy').find('tr').removeClass('selected'); |
121
|
|
|
} |
122
|
|
|
}); |
123
|
|
|
} |
124
|
|
|
}); |
125
|
|
|
|
126
|
|
|
// Suggest |
127
|
|
|
if(orderable.length) { |
128
|
|
|
Symphony.Elements.breadcrumbs.append('<p class="inactive"><span> – ' + Symphony.Language.get('drag to reorder') + '</span></p>'); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
// With Selected |
132
|
|
|
Symphony.Elements.contents.find('fieldset.apply').each(function() { |
133
|
|
|
var applicable = $(this), |
134
|
|
|
selection = Symphony.Elements.contents.find('table.selectable'), |
135
|
|
|
select = applicable.find('select'), |
136
|
|
|
button = applicable.find('button'); |
137
|
|
|
|
138
|
|
|
// Set menu status |
139
|
|
|
if(selection.length > 0) { |
140
|
|
|
selection.on('select.selectable deselect.selectable check.selectable', 'tbody tr', function() { |
141
|
|
|
|
142
|
|
|
// Activate menu |
143
|
|
|
if(selection.has('.selected').length > 0) { |
144
|
|
|
applicable.removeClass('inactive'); |
145
|
|
|
select.removeAttr('disabled'); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
// Deactivate menu |
149
|
|
|
else { |
150
|
|
|
applicable.addClass('inactive'); |
151
|
|
|
select.attr('disabled', 'disabled'); |
152
|
|
|
} |
153
|
|
|
}); |
154
|
|
|
|
155
|
|
|
selection.find('tbody tr:first').trigger('check'); |
156
|
|
|
|
157
|
|
|
// Respect menu state |
158
|
|
|
button.on('click.admin', function() { |
159
|
|
|
if(applicable.is('.inactive')) { |
|
|
|
|
160
|
|
|
return false; |
161
|
|
|
} |
162
|
|
|
}); |
163
|
|
|
} |
164
|
|
|
}); |
165
|
|
|
|
166
|
|
|
// Confirm actions |
167
|
|
|
Symphony.Elements.contents.add(Symphony.Elements.context).on('click.admin', 'button.confirm', function() { |
168
|
|
|
var message = $(this).attr('data-message') || Symphony.Language.get('Are you sure you want to proceed?'); |
169
|
|
|
|
170
|
|
|
return confirm(message); |
171
|
|
|
}); |
172
|
|
|
|
173
|
|
|
// Confirm with selected actions |
174
|
|
|
Symphony.Elements.contents.find('> form').on('submit.admin', function() { |
175
|
|
|
var select = $('select[name="with-selected"]'), |
176
|
|
|
option = select.find('option:selected'), |
177
|
|
|
message = option.attr('data-message') || Symphony.Language.get('Are you sure you want to proceed?'); |
178
|
|
|
|
179
|
|
|
// Needs confirmation |
180
|
|
|
if(option.is('.confirm')) { |
|
|
|
|
181
|
|
|
return confirm(message); |
182
|
|
|
} |
183
|
|
|
}); |
184
|
|
|
|
185
|
|
|
// Timestamp validation overwrite |
186
|
|
|
Symphony.Elements.header.find('.notifier .js-tv-overwrite').on('click.admin', function (e){ |
187
|
|
|
var action = $(this).attr('data-action') || 'save'; |
188
|
|
|
var hidden = Symphony.Elements.contents.find('input[name="action[ignore-timestamp]"]'); |
189
|
|
|
hidden.prop('checked', true); |
190
|
|
|
e.preventDefault(); |
191
|
|
|
e.stopPropagation(); |
192
|
|
|
e.stopImmediatePropagation(); |
193
|
|
|
// Click on the action button: |
194
|
|
|
// This is needed since we need to send the button's data in the POST |
195
|
|
|
Symphony.Elements.contents.find('> form').find('input, button') |
196
|
|
|
.filter('[name="action[' + action + ']"]').first().trigger('click'); |
197
|
|
|
return false; |
198
|
|
|
}); |
199
|
|
|
|
200
|
|
|
// Catch all JavaScript errors and write them to the Symphony Log |
201
|
|
|
Symphony.Elements.window.on('error.admin', function(event) { |
202
|
|
|
$.ajax({ |
203
|
|
|
type: 'POST', |
204
|
|
|
url: Symphony.Context.get('symphony') + '/ajax/log/', |
205
|
|
|
data: { |
206
|
|
|
'error': event.originalEvent.message, |
207
|
|
|
'url': event.originalEvent.filename, |
208
|
|
|
'line': event.originalEvent.lineno, |
209
|
|
|
'xsrf': Symphony.Utilities.getXSRF() |
210
|
|
|
} |
211
|
|
|
}); |
212
|
|
|
}); |
213
|
|
|
}); |
214
|
|
|
|
215
|
|
|
Symphony.View.add('/publish/:context*:', function() { |
216
|
|
|
|
217
|
|
|
// Filtering |
218
|
|
|
Symphony.Interface.Filtering.init(); |
219
|
|
|
|
220
|
|
|
// Pagination |
221
|
|
|
Symphony.Elements.contents.find('.page').each(function() { |
222
|
|
|
var pagination = $(this), |
223
|
|
|
form = pagination.find('form'), |
224
|
|
|
jump = form.find('input'), |
225
|
|
|
active = jump.attr('data-active'), |
226
|
|
|
inactive = jump.attr('data-inactive'), |
227
|
|
|
helper = $('<span />').appendTo(form), |
228
|
|
|
width; |
229
|
|
|
|
230
|
|
|
// Measure placeholder text |
231
|
|
|
width = Math.max(helper.text(active).width(), helper.text(inactive).width()); |
232
|
|
|
jump.width(width + 20); |
233
|
|
|
helper.remove(); |
234
|
|
|
|
235
|
|
|
// Set current page |
236
|
|
|
jump.val(inactive); |
237
|
|
|
|
238
|
|
|
// Display "Go to page …" placeholder |
239
|
|
|
form.on('mouseover.admin', function() { |
240
|
|
|
if(!form.is('.active') && jump.val() == inactive) { |
241
|
|
|
jump.val(active); |
242
|
|
|
} |
243
|
|
|
}); |
244
|
|
|
|
245
|
|
|
// Display current page placeholder |
246
|
|
|
form.on('mouseout.admin', function() { |
247
|
|
|
if(!form.is('.active') && jump.val() == active) { |
248
|
|
|
jump.val(inactive); |
249
|
|
|
} |
250
|
|
|
}); |
251
|
|
|
|
252
|
|
|
// Edit page number |
253
|
|
|
jump.on('focus.admin', function() { |
254
|
|
|
if(jump.val() == active) { |
255
|
|
|
jump.val(''); |
256
|
|
|
} |
257
|
|
|
form.addClass('active'); |
258
|
|
|
}); |
259
|
|
|
|
260
|
|
|
// Stop editing page number |
261
|
|
|
jump.on('blur.admin', function() { |
262
|
|
|
|
263
|
|
|
// Clear errors |
264
|
|
|
if(form.is('.invalid') || jump.val() === '') { |
265
|
|
|
form.removeClass('invalid'); |
266
|
|
|
jump.val(inactive); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
// Deactivate |
270
|
|
|
if(jump.val() == inactive) { |
271
|
|
|
form.removeClass('active'); |
272
|
|
|
} |
273
|
|
|
}); |
274
|
|
|
|
275
|
|
|
// Validate page number |
276
|
|
|
form.on('submit.admin', function() { |
277
|
|
|
if(parseInt(jump.val(), 10) > parseInt(jump.attr('data-max'), 10)) { |
|
|
|
|
278
|
|
|
form.addClass('invalid'); |
279
|
|
|
return false; |
280
|
|
|
} |
281
|
|
|
}); |
282
|
|
|
}); |
283
|
|
|
|
284
|
|
|
// Upload field destructors |
285
|
|
|
$('<em />', { |
286
|
|
|
text: Symphony.Language.get('Remove File'), |
287
|
|
|
on: { |
288
|
|
|
click: function(event) { |
289
|
|
|
event.preventDefault(); |
290
|
|
|
|
291
|
|
|
var span = $(this).parent(), |
292
|
|
|
name = span.find('input').attr('name'); |
293
|
|
|
|
294
|
|
|
span.empty().append('<input name="' + name + '" type="file">'); |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
}).appendTo('label.file:has(a) span.frame'); |
298
|
|
|
|
299
|
|
|
// Calendars |
300
|
|
|
$('.field-date').each(function() { |
301
|
|
|
var field = $(this), |
302
|
|
|
datetime = Symphony.Context.get('datetime'), |
303
|
|
|
calendar; |
304
|
|
|
|
305
|
|
|
// Add calendar widget |
306
|
|
|
if(field.attr('data-interactive')) { |
307
|
|
|
calendar = new Symphony.Interface.Calendar(); |
308
|
|
|
calendar.init(this); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
// Add timezone offset information |
312
|
|
|
if(moment().utcOffset() !== datetime['timezone-offset']) { |
313
|
|
|
field.addClass('show-timezone'); |
314
|
|
|
} |
315
|
|
|
}); |
316
|
|
|
}); |
317
|
|
|
|
318
|
|
|
Symphony.View.add('/:context*:/new', function() { |
319
|
|
|
Symphony.Elements.contents.find('input[type="text"], textarea').first().focus(); |
320
|
|
|
}); |
321
|
|
|
|
322
|
|
|
/*-------------------------------------------------------------------------- |
323
|
|
|
Blueprints - Pages Editor |
324
|
|
|
--------------------------------------------------------------------------*/ |
325
|
|
|
|
326
|
|
|
Symphony.View.add('/blueprints/pages/:action:/:id:/:status:', function() { |
327
|
|
|
// No core interactions yet |
328
|
|
|
}); |
329
|
|
|
|
330
|
|
|
/*-------------------------------------------------------------------------- |
331
|
|
|
Blueprints - Sections |
332
|
|
|
--------------------------------------------------------------------------*/ |
333
|
|
|
|
334
|
|
|
Symphony.View.add('/blueprints/sections/:action:/:id:/:status:', function(action, id, status) { |
335
|
|
|
var duplicator = $('#fields-duplicator'), |
336
|
|
|
legend = $('#fields-legend'), |
337
|
|
|
expand, collapse, toggle; |
338
|
|
|
|
339
|
|
|
// Create toggle controls |
340
|
|
|
expand = $('<a />', { |
341
|
|
|
'class': 'expand', |
342
|
|
|
'text': Symphony.Language.get('Expand all') |
343
|
|
|
}); |
344
|
|
|
collapse = $('<a />', { |
345
|
|
|
'class': 'collapse', |
346
|
|
|
'text': Symphony.Language.get('Collapse all') |
347
|
|
|
}); |
348
|
|
|
toggle = $('<p />', { |
349
|
|
|
'class': 'help toggle' |
350
|
|
|
}); |
351
|
|
|
|
352
|
|
|
// Add toggle controls |
353
|
|
|
toggle.append(expand).append('<br />').append(collapse).insertAfter(legend); |
354
|
|
|
|
355
|
|
|
// Toggle fields |
356
|
|
|
toggle.on('click.admin', 'a.expand, a.collapse', function toggleFields() { |
357
|
|
|
|
358
|
|
|
// Expand |
359
|
|
|
if($(this).is('.expand')) { |
360
|
|
|
duplicator.trigger('expandall.collapsible'); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
// Collapse |
364
|
|
|
else { |
365
|
|
|
duplicator.trigger('collapseall.collapsible'); |
366
|
|
|
} |
367
|
|
|
}); |
368
|
|
|
|
369
|
|
|
// Affix for toggle |
370
|
|
|
$('fieldset.settings > legend + .help').symphonyAffix(); |
371
|
|
|
|
372
|
|
|
// Initialise field editor |
373
|
|
|
duplicator.symphonyDuplicator({ |
374
|
|
|
orderable: true, |
375
|
|
|
collapsible: true, |
376
|
|
|
preselect: 'input' |
377
|
|
|
}); |
378
|
|
|
|
379
|
|
|
// Load section list |
380
|
|
|
duplicator.on('constructshow.duplicator', '.instance', function() { |
381
|
|
|
var instance = $(this), |
382
|
|
|
sections = instance.find('.js-fetch-sections'), |
383
|
|
|
sectionsParent = sections.parent(), |
384
|
|
|
selected = [], |
385
|
|
|
options; |
386
|
|
|
|
387
|
|
|
if(sections.length) { |
388
|
|
|
options = sections.find('option').each(function() { |
|
|
|
|
389
|
|
|
selected.push(this.value); |
390
|
|
|
|
391
|
|
|
if(!isNaN(this.value)) { |
392
|
|
|
$(this).remove(); |
393
|
|
|
} |
394
|
|
|
}); |
395
|
|
|
|
396
|
|
|
$.ajax({ |
397
|
|
|
type: 'GET', |
398
|
|
|
dataType: 'json', |
399
|
|
|
url: Symphony.Context.get('symphony') + '/ajax/sections/', |
400
|
|
|
success: function(result) { |
401
|
|
|
// offline DOM manipulation |
402
|
|
|
sections.detach(); |
403
|
|
|
|
404
|
|
|
if(result.sections.length) { |
405
|
|
|
sections.prop('disabled', false); |
406
|
|
|
} |
407
|
|
|
var options = $(); |
408
|
|
|
|
409
|
|
|
if (!sections.attr('data-required')) { |
410
|
|
|
// Allow de-selection, if permitted |
411
|
|
|
options = options.add($('<option />', { |
412
|
|
|
text: Symphony.Language.get('None'), |
413
|
|
|
value: '' |
414
|
|
|
})); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
// Append sections |
418
|
|
|
$.each(result.sections, function(index, section) { |
419
|
|
|
var optgroup = $('<optgroup />', { |
420
|
|
|
label: section.name |
421
|
|
|
}); |
422
|
|
|
options = options.add(optgroup); |
423
|
|
|
// Append fields |
424
|
|
|
$.each(section.fields, function(index, field) { |
425
|
|
|
var option = $('<option />', { |
426
|
|
|
value: field.id, |
427
|
|
|
text: field.name |
428
|
|
|
}).appendTo(optgroup); |
429
|
|
|
|
430
|
|
|
if($.inArray(field.id, selected) > -1) { |
431
|
|
|
option.prop('selected', true); |
432
|
|
|
} |
433
|
|
|
}); |
434
|
|
|
}); |
435
|
|
|
sections.append(options); |
436
|
|
|
sectionsParent.append(sections); |
437
|
|
|
sections.trigger('change.admin'); |
438
|
|
|
} |
439
|
|
|
}); |
440
|
|
|
} |
441
|
|
|
}); |
442
|
|
|
duplicator.find('.instance').trigger('constructshow.duplicator'); |
443
|
|
|
|
444
|
|
|
// Focus first input |
445
|
|
|
duplicator.on('constructshow.duplicator expandstop.collapsible', '.instance', function() { |
446
|
|
|
var item = $(this); |
447
|
|
|
if (!item.hasClass('js-animate-all')) { |
448
|
|
|
$(this).find('input:visible:first').trigger('focus.admin'); |
449
|
|
|
} |
450
|
|
|
}); |
451
|
|
|
|
452
|
|
|
// Update name |
453
|
|
|
duplicator.on('blur.admin input.admin', '.instance input[name*="[label]"]', function() { |
454
|
|
|
var label = $(this), |
455
|
|
|
value = label.val(); |
456
|
|
|
|
457
|
|
|
// Empty label |
458
|
|
|
if(value === '') { |
459
|
|
|
value = Symphony.Language.get('Untitled Field'); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
// Update title |
463
|
|
|
label.parents('.instance').find('.frame-header strong').text(value); |
464
|
|
|
}); |
465
|
|
|
|
466
|
|
|
// Update location |
467
|
|
|
duplicator.on('change.admin', '.instance select[name*="[location]"]', function() { |
468
|
|
|
var select = $(this); |
469
|
|
|
|
470
|
|
|
select.parents('.instance').find('.frame-header').removeClass('main').removeClass('sidebar').addClass(select.val()); |
471
|
|
|
}); |
472
|
|
|
|
473
|
|
|
// Update requirements |
474
|
|
|
duplicator.on('change.admin', '.instance input[name*="[required]"]', function() { |
475
|
|
|
var checkbox = $(this), |
476
|
|
|
headline = checkbox.parents('.instance').find('.frame-header h4'); |
477
|
|
|
|
478
|
|
|
// Is required |
479
|
|
|
if(checkbox.is(':checked')) { |
480
|
|
|
$('<span />', { |
481
|
|
|
class: 'required', |
482
|
|
|
text: '— ' + Symphony.Language.get('required') |
483
|
|
|
}).appendTo(headline); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
// Is not required |
487
|
|
|
else { |
488
|
|
|
headline.find('.required').remove(); |
489
|
|
|
} |
490
|
|
|
}); |
491
|
|
|
duplicator.find('.instance input[name*="[required]"]').trigger('change.admin'); |
492
|
|
|
|
493
|
|
|
// Update select field |
494
|
|
|
duplicator.on('change.admin', '.instance select[name*="[dynamic_options]"]', function() { |
495
|
|
|
$(this).parents('.instance').find('[data-condition=associative]').toggle($.isNumeric(this.value)); |
496
|
|
|
}).trigger('change.admin'); |
497
|
|
|
|
498
|
|
|
// Update tag field |
499
|
|
|
duplicator.on('change.admin', '.instance select[name*="[pre_populate_source]"]', function() { |
500
|
|
|
var selected = $(this).val(), |
501
|
|
|
show = false; |
502
|
|
|
|
503
|
|
|
if(selected) { |
504
|
|
|
selected = jQuery.grep(selected, function(value) { |
505
|
|
|
return value != 'existing'; |
506
|
|
|
}); |
507
|
|
|
|
508
|
|
|
show = (selected.length > 0); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
$(this).parents('.instance').find('[data-condition=associative]').toggle(show); |
512
|
|
|
}).trigger('change.admin'); |
513
|
|
|
|
514
|
|
|
// Remove field |
515
|
|
|
duplicator.on('destructstart.duplicator', function(event) { |
516
|
|
|
var target = $(event.target), |
517
|
|
|
item = target.clone(), |
518
|
|
|
title = item.find('.frame-header strong').text(), |
519
|
|
|
type = item.find('.frame-header span').text(), |
520
|
|
|
index = target.index(), |
521
|
|
|
id = new Date().getTime(); |
522
|
|
|
|
523
|
|
|
// Offer undo option after removing a field |
524
|
|
|
Symphony.Elements.header.find('div.notifier').trigger('attach.notify', [ |
525
|
|
|
Symphony.Language.get('The field “{$title}” ({$type}) has been removed.', { |
526
|
|
|
title: title, |
527
|
|
|
type: type |
528
|
|
|
}) + '<a id="' + id + '">' + Symphony.Language.get('Undo?') + '</a>', 'protected undo'] |
529
|
|
|
); |
530
|
|
|
|
531
|
|
|
// Prepare field recovery |
532
|
|
|
$('#' + id).data('field', item).data('preceding', index - 1).on('click.admin', function() { |
533
|
|
|
var undo = $(this), |
534
|
|
|
message = undo.parent(), |
535
|
|
|
field = undo.data('field').hide(), |
536
|
|
|
list = $('#fields-duplicator'); |
537
|
|
|
|
538
|
|
|
// Add field |
539
|
|
|
list.parent().removeClass('empty'); |
540
|
|
|
field.trigger('constructstart.duplicator'); |
541
|
|
|
list.find('.instance:eq(' + undo.data('preceding') + ')').after(field); |
542
|
|
|
field.trigger('constructshow.duplicator'); |
543
|
|
|
field.slideDown('fast', function() { |
544
|
|
|
field.trigger('constructstop.duplicator'); |
545
|
|
|
}); |
546
|
|
|
|
547
|
|
|
// Clear system message |
548
|
|
|
message.trigger('detach.notify'); |
549
|
|
|
}); |
550
|
|
|
}); |
551
|
|
|
|
552
|
|
|
// Discard undo options because the field context changed |
553
|
|
|
duplicator.on('orderstop.orderable', function() { |
554
|
|
|
Symphony.Elements.header.find('.undo').trigger('detach.notify'); |
555
|
|
|
}); |
556
|
|
|
|
557
|
|
|
// Highlight instances with the same location when ordering fields |
558
|
|
|
duplicator.on('orderstart.orderable', function(event, item) { |
559
|
|
|
var duplicator = $(this), |
560
|
|
|
header = item.find('.frame-header'), |
561
|
|
|
position = (header.is('.main') ? 'main' : 'sidebar'); |
562
|
|
|
|
563
|
|
|
duplicator.find('li:has(.' + position + ')').not(item).addClass('highlight'); |
564
|
|
|
}); |
565
|
|
|
|
566
|
|
|
duplicator.on('orderstop.orderable', function() { |
567
|
|
|
$(this).find('li.highlight').removeClass('highlight'); |
568
|
|
|
}); |
569
|
|
|
|
570
|
|
|
// Restore collapsible states for new sections |
571
|
|
|
if(status === 'created') { |
572
|
|
|
var storageId = Symphony.Context.get('context-id'); |
573
|
|
|
storageId = storageId.split('.'); |
574
|
|
|
storageId.pop(); |
575
|
|
|
storageId = 'symphony.collapsible.' + storageId.join('.') + '.0.collapsed'; |
576
|
|
|
|
577
|
|
|
if(Symphony.Support.localStorage === true && window.localStorage[storageId]) { |
578
|
|
|
$.each(window.localStorage[storageId].split(','), function(index, value) { |
579
|
|
|
var collapsed = duplicator.find('.instance').eq(value); |
580
|
|
|
if(collapsed.has('.invalid').length == 0) { |
|
|
|
|
581
|
|
|
collapsed.trigger('collapse.collapsible', [0]); |
582
|
|
|
} |
583
|
|
|
}); |
584
|
|
|
|
585
|
|
|
window.localStorage.removeItem(storageId); |
586
|
|
|
} |
587
|
|
|
} |
588
|
|
|
}); |
589
|
|
|
|
590
|
|
|
/*-------------------------------------------------------------------------- |
591
|
|
|
Blueprints - Datasource Editor |
592
|
|
|
--------------------------------------------------------------------------*/ |
593
|
|
|
|
594
|
|
|
Symphony.View.add('/blueprints/datasources/:action:/:id:/:status:/:*:', function(action) { |
595
|
|
|
if(!action) { |
596
|
|
|
return; |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
var context = $('#ds-context'), |
600
|
|
|
source = $('#ds-source'), |
601
|
|
|
name = Symphony.Elements.contents.find('input[name="fields[name]"]').attr('data-updated', 0), |
602
|
|
|
nameChangeCount = 0, |
603
|
|
|
params = Symphony.Elements.contents.find('select[name="fields[param][]"]'), |
604
|
|
|
pagination = Symphony.Elements.contents.find('.pagination'), |
605
|
|
|
paginationInput = pagination.find('input'); |
606
|
|
|
|
607
|
|
|
// Update data source handle |
608
|
|
|
name.on('blur.admin input.admin', function updateDsHandle() { |
609
|
|
|
var current = (nameChangeCount++), |
610
|
|
|
value = name.val(); |
611
|
|
|
|
612
|
|
|
setTimeout(function fetchDsHandle(nameChangeCount, current, value) { |
613
|
|
|
if(nameChangeCount == current) { |
614
|
|
|
$.ajax({ |
615
|
|
|
type: 'GET', |
616
|
|
|
data: { 'string': value }, |
617
|
|
|
dataType: 'json', |
618
|
|
|
url: Symphony.Context.get('symphony') + '/ajax/handle/', |
619
|
|
|
success: function(result) { |
620
|
|
|
if(nameChangeCount == current) { |
621
|
|
|
name.data('handle', result.handle); |
622
|
|
|
params.trigger('update.admin'); |
623
|
|
|
} |
624
|
|
|
} |
625
|
|
|
}); |
626
|
|
|
} |
627
|
|
|
}, 500, nameChangeCount, current, value); |
628
|
|
|
}) |
629
|
|
|
// Enable the default value for Data Source name |
630
|
|
|
.symphonyDefaultValue({ |
631
|
|
|
sourceElement: context |
632
|
|
|
}); |
633
|
|
|
|
634
|
|
|
// Update output parameters |
635
|
|
|
params.on('update.admin', function updateDsParams() { |
636
|
|
|
var handle = name.data('handle') || Symphony.Language.get('untitled'); |
637
|
|
|
|
638
|
|
|
// Process parameters |
639
|
|
|
if(parseInt(name.attr('data-updated')) !== 0) { |
640
|
|
|
params.find('option').each(function updateDsParam() { |
641
|
|
|
var param = $(this), |
642
|
|
|
field = param.attr('data-handle'); |
643
|
|
|
|
644
|
|
|
// Set parameter |
645
|
|
|
param.text('$ds-' + handle + '.' + field); |
646
|
|
|
}); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
// Updated |
650
|
|
|
name.attr('data-updated', 1); |
651
|
|
|
}).trigger('update.admin'); |
652
|
|
|
|
653
|
|
|
// Data source manager options |
654
|
|
|
Symphony.Elements.contents.find('.contextual select optgroup').each(function() { |
655
|
|
|
var optgroup = $(this), |
656
|
|
|
select = optgroup.parents('select'), |
657
|
|
|
label = optgroup.attr('data-label'), |
658
|
|
|
options = optgroup.remove().find('option').addClass('optgroup'); |
659
|
|
|
|
660
|
|
|
// Show only relevant options based on context |
661
|
|
|
context.on('change.admin', function() { |
662
|
|
|
var option = $(this).find('option:selected'), |
663
|
|
|
context = option.attr('data-context') || 'section-' + option.val(); |
664
|
|
|
|
665
|
|
|
if(context == label) { |
666
|
|
|
select.find('option.optgroup').remove(); |
667
|
|
|
select.append(options.clone(true)); |
668
|
|
|
} |
669
|
|
|
}); |
670
|
|
|
}); |
671
|
|
|
|
672
|
|
|
// Data source manager context |
673
|
|
|
context.on('change.admin', function() { |
674
|
|
|
var optgroup = context.find('option:selected').parent(), |
675
|
|
|
label = optgroup.attr('data-label') || optgroup.attr('label'), |
676
|
|
|
reference = context.find('option:selected').attr('data-context') || 'section-' + context.val(), |
677
|
|
|
components = Symphony.Elements.contents.find('.contextual'); |
678
|
|
|
|
679
|
|
|
// Store context |
680
|
|
|
source.val(context.val()); |
681
|
|
|
|
682
|
|
|
// Show only relevant interface components based on context |
683
|
|
|
components.addClass('irrelevant'); |
684
|
|
|
components.filter('[data-context~=' + label + ']').removeClass('irrelevant'); |
685
|
|
|
components.filter('[data-context~=' + reference + ']').removeClass('irrelevant'); |
686
|
|
|
|
687
|
|
|
// Make sure parameter names are up-to-date |
688
|
|
|
Symphony.Elements.contents.find('input[name="fields[name]"]').trigger('blur.admin'); |
689
|
|
|
}).trigger('change.admin'); |
690
|
|
|
|
691
|
|
|
// Toggle pagination |
692
|
|
|
Symphony.Elements.contents.find('input[name*=paginate_results]').on('change.admin', function() { |
693
|
|
|
var disabled = !$(this).is(':checked'); |
694
|
|
|
paginationInput.prop('disabled', disabled); |
695
|
|
|
}).trigger('change.admin'); |
696
|
|
|
|
697
|
|
|
// Enabled fields on submit |
698
|
|
|
Symphony.Elements.contents.find('> form').on('submit.admin', function() { |
699
|
|
|
paginationInput.prop('disabled', false); |
700
|
|
|
}); |
701
|
|
|
|
702
|
|
|
// Enable parameter suggestions |
703
|
|
|
Symphony.Elements.contents.find('.ds-param').each(function() { |
704
|
|
|
Symphony.Interface.Suggestions.init(this, 'input[type="text"]'); |
705
|
|
|
}); |
706
|
|
|
|
707
|
|
|
// Toggle filter help |
708
|
|
|
Symphony.Elements.contents.find('.filters-duplicator').on('input.admin change.admin', 'input', function toggleFilterHelp(event) { |
709
|
|
|
var item = $(event.target).parents('.instance'), |
710
|
|
|
value = event.target.value, |
711
|
|
|
filters = item.data('filters'), |
712
|
|
|
help = item.find('.help'); |
713
|
|
|
|
714
|
|
|
// Handle values that don't contain predicates |
715
|
|
|
var filter = value.search(/:/) |
716
|
|
|
? $.trim(value.split(':')[0]) |
717
|
|
|
: $.trim(value); |
718
|
|
|
|
719
|
|
|
// Store filters |
720
|
|
|
if(!filters) { |
721
|
|
|
filters = {}; |
722
|
|
|
item.find('.tags li').each(function() { |
723
|
|
|
var val = $.trim(this.getAttribute('data-value')); |
724
|
|
|
if (val.search(/:/)) { |
725
|
|
|
val = val.slice(0, -1); |
726
|
|
|
} |
727
|
|
|
filters[val] = this.getAttribute('data-help'); |
728
|
|
|
}); |
729
|
|
|
|
730
|
|
|
item.data('filters', filters); |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
// Filter help |
734
|
|
|
if(filters[filter]) { |
735
|
|
|
help.html(filters[filter]); |
736
|
|
|
} |
737
|
|
|
}); |
738
|
|
|
}); |
739
|
|
|
|
740
|
|
|
/*-------------------------------------------------------------------------- |
741
|
|
|
Blueprints - Event Editor |
742
|
|
|
--------------------------------------------------------------------------*/ |
743
|
|
|
|
744
|
|
|
Symphony.View.add('/blueprints/events/:action:/:name:/:status:/:*:', function() { |
745
|
|
|
var context = $('#event-context'), |
746
|
|
|
source = $('#event-source'), |
747
|
|
|
filters = $('#event-filters'), |
748
|
|
|
name = Symphony.Elements.contents.find('input[name="fields[name]"]').attr('data-updated', 0), |
749
|
|
|
nameChangeCount = 0; |
750
|
|
|
|
751
|
|
|
// Update event handle |
752
|
|
|
name.on('blur.admin input.admin', function updateEventHandle() { |
753
|
|
|
var current = (nameChangeCount++); |
754
|
|
|
|
755
|
|
|
setTimeout(function checkEventHandle(nameChangeCount, current) { |
756
|
|
|
if(nameChangeCount == current) { |
757
|
|
|
Symphony.Elements.contents.trigger('update.admin'); |
758
|
|
|
} |
759
|
|
|
}, 500, nameChangeCount, current); |
760
|
|
|
}) |
761
|
|
|
// Enable the default value for Event name |
762
|
|
|
.symphonyDefaultValue({ |
763
|
|
|
sourceElement: context |
764
|
|
|
}); |
765
|
|
|
|
766
|
|
|
// Change context |
767
|
|
|
context.on('change.admin', function changeEventContext() { |
768
|
|
|
source.val(context.val()); |
769
|
|
|
Symphony.Elements.contents.trigger('update.admin'); |
770
|
|
|
}).trigger('change.admin'); |
771
|
|
|
|
772
|
|
|
// Change filters |
773
|
|
|
filters.on('change.admin', function changeEventFilters() { |
774
|
|
|
Symphony.Elements.contents.trigger('update.admin'); |
775
|
|
|
}); |
776
|
|
|
|
777
|
|
|
// Update documentation |
778
|
|
|
Symphony.Elements.contents.on('update.admin', function updateEventDocumentation() { |
779
|
|
|
if(name.val() == '') { |
780
|
|
|
$('#event-documentation').empty(); |
781
|
|
|
} |
782
|
|
|
else { |
783
|
|
|
$.ajax({ |
784
|
|
|
type: 'GET', |
785
|
|
|
data: { |
786
|
|
|
'section': context.val(), |
787
|
|
|
'filters': filters.serializeArray(), |
788
|
|
|
'name': name.val() |
789
|
|
|
}, |
790
|
|
|
dataType: 'html', |
791
|
|
|
url: Symphony.Context.get('symphony') + '/ajax/eventdocumentation/', |
792
|
|
|
success: function(documentation) { |
793
|
|
|
$('#event-documentation').replaceWith(documentation); |
794
|
|
|
} |
795
|
|
|
}); |
796
|
|
|
} |
797
|
|
|
}); |
798
|
|
|
}); |
799
|
|
|
|
800
|
|
|
/*-------------------------------------------------------------------------- |
801
|
|
|
System - Authors |
802
|
|
|
--------------------------------------------------------------------------*/ |
803
|
|
|
|
804
|
|
|
Symphony.View.add('/system/authors/:action:/:id:/:status:', function(action, id, status) { |
805
|
|
|
var password = $('#password'); |
806
|
|
|
|
807
|
|
|
// Add change password overlay |
808
|
|
|
if(!password.has('.invalid').length && id) { |
809
|
|
|
var overlay = $('<div class="password" />'), |
810
|
|
|
frame = $('<span class="frame centered" />'), |
811
|
|
|
button = $('<button />', { |
812
|
|
|
text: Symphony.Language.get('Change Password'), |
813
|
|
|
on: { |
814
|
|
|
click: function(event) { |
815
|
|
|
event.preventDefault(); |
816
|
|
|
overlay.hide(); |
817
|
|
|
} |
818
|
|
|
} |
819
|
|
|
}).attr('type', 'button'); |
820
|
|
|
|
821
|
|
|
frame.append(button); |
822
|
|
|
overlay.append(frame); |
823
|
|
|
overlay.insertBefore(password); |
824
|
|
|
} |
825
|
|
|
|
826
|
|
|
// Focussed UI for password reset |
827
|
|
|
if(status == 'reset-password') { |
828
|
|
|
var fieldsets = Symphony.Elements.contents.find('fieldset'), |
829
|
|
|
essentials = fieldsets.eq(0), |
830
|
|
|
login = fieldsets.eq(1), |
831
|
|
|
legend = login.find('> legend'); |
832
|
|
|
|
833
|
|
|
essentials.hide(); |
834
|
|
|
login.children().not('legend, #password').hide(); |
835
|
|
|
|
836
|
|
|
$('<p />', { |
837
|
|
|
class: 'help', |
838
|
|
|
text: Symphony.Language.get('Please reset your password') |
839
|
|
|
}).insertAfter(legend); |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
// Highlight confirmation promt |
843
|
|
|
Symphony.Elements.contents.find('input, select').on('change.admin input.admin', function() { |
844
|
|
|
$('#confirmation').addClass('highlight'); |
845
|
|
|
}); |
846
|
|
|
}); |
847
|
|
|
|
848
|
|
|
/*-------------------------------------------------------------------------- |
849
|
|
|
System - Extensions |
850
|
|
|
--------------------------------------------------------------------------*/ |
851
|
|
|
Symphony.View.add('/system/extensions/:context*:', function() { |
852
|
|
|
Symphony.Language.add({ |
853
|
|
|
'Enable': false, |
854
|
|
|
'Install': false, |
855
|
|
|
'Update': false |
856
|
|
|
}); |
857
|
|
|
|
858
|
|
|
// Update controls contextually |
859
|
|
|
Symphony.Elements.contents.find('.actions select').on('focus.admin', function() { |
860
|
|
|
var selected = Symphony.Elements.contents.find('tr.selected'), |
861
|
|
|
canUpdate = selected.filter('.extension-can-update').length, |
862
|
|
|
canInstall = selected.filter('.extension-can-install').length, |
863
|
|
|
canEnable = selected.length - canUpdate - canInstall, |
864
|
|
|
control = Symphony.Elements.contents.find('.actions option[value="enable"]'), |
865
|
|
|
label = []; |
866
|
|
|
|
867
|
|
|
if(canEnable) { |
868
|
|
|
label.push(Symphony.Language.get('Enable')); |
869
|
|
|
} |
870
|
|
|
if(canUpdate) { |
871
|
|
|
label.push(Symphony.Language.get('Update')); |
872
|
|
|
} |
873
|
|
|
if(canInstall) { |
874
|
|
|
label.push(Symphony.Language.get('Install')); |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
control.text(label.join('/')); |
878
|
|
|
}); |
879
|
|
|
}); |
880
|
|
|
|
881
|
|
|
})(window.jQuery, window.Symphony); |
882
|
|
|
|
This check looks for functions where a
return
statement is found in some execution paths, but not in all.Consider this little piece of code
The function
isBig
will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly returnundefined
.This behaviour may not be what you had intended. In any case, you can add a
return undefined
to the other execution path to make the return value explicit.