PluginProbe
UpStream: a Project Management Plugin for WordPress / 2.1.0
UpStream: a Project Management Plugin for WordPress v2.1.0
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
upstream / includes / admin / assets / js / edit-project.js

edit-project.js in UpStream: a Project Management Plugin for WordPress 2.1.0, at includes/admin/assets/js/edit-project.js

1,740 lines 69.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function ($, $data) {
2 $('#upstream_mimlestone_data select').select2({
3 allowClear: true
4 });
5
6
7 function randomString(length) {
8 var result = '';
9 var chars = '0123456789abcdefghijklmnopqrstuvwxyz';
10 for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
11 return result;
12 }
13
14 function initProject () {
15 var $box = $(document.getElementById('post-body'));
16
17 var groups = [
18 '#_upstream_project_milestones',
19 '#_upstream_project_tasks',
20 '#_upstream_project_bugs',
21 '#_upstream_project_files'
22 ];
23
24 $(groups).each(function (index, element) {
25
26 var $group = $box.find(element);
27 var $items = $group.find('.cmb-repeatable-grouping');
28
29 // UI stuff
30 $items.addClass('closed');
31 hideFirstItemIfEmpty($group);
32 hideFieldWrap($group);
33
34 // add dynamic data into group row title
35 replaceTitles($group);
36 addAvatars($group);
37
38 // permissions
39 publishPermissions($group);
40 deletePermissions($group);
41 fileFieldPermissions($group);
42
43 // when we do something
44 $group
45 .on('cmb2_add_row', function (evt) {
46 addRow($group);
47 })
48 .on('change cmb2_add_row cmb2_shift_rows_complete', function (evt) {
49 resetGroup($group);
50 })
51 .on('click button.cmb-remove-group-row', function (evt) {
52 if ($(evt.target).hasClass('cmb-remove-group-row')) {
53 $($group).each(function (i, e) {
54 var e = $(e);
55 var e_id = e.attr('id');
56
57 //resetGroup(e);
58
59 $(groups).each(function (i, e) {
60 var $g = $box.find(e);
61
62 resetGroup($g);
63
64 if ($g.attr('id') === '_upstream_project_tasks' || $g.attr('id') === '_upstream_project_bugs') {
65 displayEndDate($g);
66 }
67 });
68
69 var $m = $('#_upstream_project_milestones');
70 displayMilestoneProgress($m);
71 displayMilestoneIcon($m);
72
73 var $t = $('#_upstream_project_tasks');
74 displayStatusColor($t);
75 displayMilestoneIcon($t);
76 displayProgress($t);
77
78 displayStatusColor($('#_upstream_project_bugs'));
79 });
80 }
81 })
82 .on('keyup', titleOnKeyUp);
83
84 // milestone specific
85 if ($group.attr('id') == '_upstream_project_milestones') {
86
87 displayMilestoneProgress($group);
88 displayMilestoneIcon($group);
89
90 $group
91 .on('change cmb2_add_row cmb2_shift_rows_complete', function (evt) {
92 displayMilestoneProgress($group);
93 displayMilestoneIcon($group);
94 });
95
96 }
97
98 // task specific
99 if ($group.attr('id') == '_upstream_project_tasks') {
100
101 displayStatusColor($group);
102 displayMilestoneIcon($group);
103 displayProgress($group);
104 displayEndDate($group);
105
106 $group
107 .on('change cmb2_add_row cmb2_shift_rows_complete', function (evt) {
108 displayStatusColor($group);
109 displayMilestoneIcon($group);
110 displayProgress($group);
111 displayEndDate($group);
112 });
113 }
114
115 // bug specific
116 if ($group.attr('id') == '_upstream_project_bugs') {
117
118 displayStatusColor($group);
119 displayEndDate($group);
120
121 $group
122 .on('change cmb2_add_row cmb2_shift_rows_complete', function (evt) {
123 displayStatusColor($group);
124 displayEndDate($group);
125 });
126 }
127
128 });
129 }
130
131 function resetGroup ($group) {
132 replaceTitles($group);
133 addAvatars($group);
134 }
135
136 /*
137 * Disable 'add new' button if permissions don't allow it.
138 * Used in all groups.
139 */
140 function publishPermissions ($group) {
141 if (!$group.find('.inside .hidden').attr('data-publish')) {
142 $group.find('.inside .cmb-add-row button').prop('disabled', true).prop('title', 'You do not have permission for this');
143 }
144 };
145
146 /*
147 * Disable 'delete' button if permissions don't allow it.
148 * Used in all groups.
149 */
150 function deletePermissions ($group) {
151 $group.find('.cmb-repeatable-grouping').each(function () {
152 var isOwner = $(this).find('[data-owner]').attr('data-owner');
153 if (isOwner != 'true') {
154 $(this).find('button.cmb-remove-group-row').prop('disabled', true).prop('title', 'You do not have permission for this');
155 }
156 });
157 };
158
159 /*
160 * Disable 'upload file' button if permissions don't allow it.
161 * Used in bugs and files.
162 */
163 function fileFieldPermissions ($group) {
164 $group.find('.cmb-repeatable-grouping').each(function () {
165 var file = $(this).find('.cmb-type-file');
166 var disabled = $(file).find('[data-disabled]').attr('data-disabled');
167 if (disabled == 'true') {
168 $(this).on('click', '.cmb-attach-list li, .cmb2-media-status .img-status img, .cmb2-media-status .file-status > span', function () {
169 return false;
170 });
171 $(file).find('input.cmb2-upload-button').prop('disabled', true).prop('title', 'You do not have permission for this');
172 $(file).find('.cmb2-remove-file-button').hide();
173 }
174 });
175 };
176
177 /*
178 * Hides the row if there is only 1 and it is empty.
179 *
180 */
181 function hideFirstItemIfEmpty ($group) {
182 if ($group.attr('id') == '_upstream_project_milestones') {
183 var $items = $group.find('.cmb-repeatable-grouping').first();
184 $items.removeClass('closed');
185 return;
186 }
187
188 if ($group.find('.hidden').attr('data-empty') == '1') {
189 if ($group.find('.cmb-repeatable-grouping').length == 1) {
190 $group.find('.cmb-repeatable-grouping').hide();
191 }
192 }
193 };
194
195 /*
196 * Hide the field wrapping row if an input field has been hidden.
197 * Via a filter such as add_filter( 'upstream_bug_metabox_fields', 'upstream_bugs_hide_field_for_role', 99, 3 );
198 */
199 function hideFieldWrap ($group) {
200 $group.find('input, textarea, select').each(function () {
201 if ($(this).hasClass('hidden')) {
202 $(this).parents('.cmb-repeat-group-field').addClass('hidden');
203 }
204 });
205 };
206
207 /*
208 * Displays the avatar in the title.
209 * Used in all groups.
210 */
211 function addAvatars ($group) {
212
213 $group.find('.cmb-repeatable-grouping').each(function () {
214 var $this = $(this);
215 var user_created = $this.find('[data-user_created_by]').attr('data-user_created_by');
216 var av_created = $this.find('[data-avatar_created_by]').attr('data-avatar_created_by');
217
218 // create the boxes to hold the images first
219 $this.find('h3 span.title').prepend('<div class="av-created"></div><div class="av-assigned"></div>');
220
221 if (av_created) {
222 $this.find('.av-created').html('<img title="Created by: ' + user_created + '" src="' + av_created + '" height="25" width="25" />').show();
223 } else {
224 $this.find('.av-created').hide();
225 }
226
227 var assigneesWrapper = $this.find('.av-assigned');
228 assigneesWrapper.html('');
229
230 var assignees = $this.find('[data-assignees]').attr('data-assignees');
231 if (assignees) {
232 try {
233 var assigneesNames = [];
234
235 assignees = JSON.parse(assignees);
236 if (assignees
237 && assignees.data
238 && assignees.data.length > 0
239 ) {
240 var assignee = assignees.data[0];
241 assigneesWrapper.html('<img src="' + assignee.avatar + '" height="25" width="25" />');
242
243 if (assignees.data.length > 1) {
244 assigneesWrapper.append($('<span class="o-badge">+' + (assignees.data.length - 1) + '</span>'));
245 }
246
247 for (var assigneeIndex = 0; assigneeIndex < assignees.data.length; assigneeIndex++) {
248 assigneesNames.push(assignees.data[assigneeIndex].name);
249 }
250 }
251
252 assigneesWrapper.attr('title', $data.l.LB_ASSIGNED_TO + ': ' + assigneesNames.join(', ')).show();
253 } catch (e) {
254 // Do nothing.
255 }
256 }
257 });
258 };
259
260 /*
261 * Displays the title in the title.
262 * Used in all groups.
263 */
264 function replaceTitles ($group) {
265
266 if ($group && $group.attr('id') == '_upstream_project_milestones') {
267 $group.find('.cmb-group-title').each(function () {
268 var $this = $(this);
269 var title = $this.next().find('[id$="milestone"]').val();
270 var id = $this.next().find('[id$="id"]').val();
271 var start = $this.next().find('[id$="start_date"]').val();
272 var end = $this.next().find('[id$="end_date"]').val();
273 var dates = '<div class="dates">' + start + ' - ' + end + '</div>';
274 if (title) {
275 $this.html('<span class="title">' + title + '</span>' + '<div class="dates">ID: ' + id + '</div>'+ dates);
276 }
277 });
278
279 } else {
280
281 $group.find('.cmb-group-title').each(function () {
282 var $this = $(this);
283 var title = $this.next().find('[id$="title"]').val();
284 var id = $this.next().find('[id$="id"]').val();
285 var grouptitle = $group.find('[data-grouptitle]').data('grouptitle');
286 if (!title) {
287 var $row = $this.parents('.cmb-row.cmb-repeatable-grouping');
288 var rowindex = $row.data('iterator');
289 var newtitle = grouptitle.replace('{#}', (rowindex + 1));
290 $this.html('<span class="title">' + newtitle + '</span>');
291 } else {
292 $this.html('<span class="title">' + title + '</span>' + '<div class="dates">ID: ' + id + '</div>');
293 }
294 if (grouptitle == 'Task {#}')
295 displayProgress($group);
296 });
297
298 }
299 };
300
301 function titleOnKeyUp (evt) {
302 var $group = $(evt.target).parents('.cmb2-postbox');
303 replaceTitles($group);
304 addAvatars($group);
305 };
306
307 /*
308 * Displays the total milestone progress in the title.
309 * Only used on the Milestones group.
310 */
311 function displayMilestoneProgress ($group) {
312 $group.find('.cmb-repeatable-grouping').each(function () {
313 var $this = $(this);
314 var title = $this.find('.cmb-group-title .title').text();
315 if (title) {
316 var progress = $('ul.milestones li .title:contains(' + title + ')').next().next().text();
317 } else {
318 var progress = '0';
319 }
320 progress = progress ? progress : '0';
321 $this.find('.progress').remove();
322 $this.append('<span class="progress"><progress value="' + progress + '" max="100"></progress></span>');
323 });
324 };
325
326 /*
327 * Displays the milestone icon in the title.
328 * Used in tasks and bugs.
329 */
330 function displayMilestoneIcon ($group) {
331 $group.find('.cmb-repeatable-grouping').each(function () {
332 var $this = $(this);
333 var milestone = $this.find('[id$="milestone"]').val();
334
335 if (milestone) {
336 $this.find('.on-title.dashicons').remove();
337 var color = $('ul.milestones .title:contains(' + milestone + ')').next().text();
338
339 $this.find('button.cmb-remove-group-row.dashicons-before').after('<span style="color: ' + color + '" class="dashicons dashicons-flag on-title"></span> ');
340 }
341 });
342 };
343
344 /*
345 * Displays the status in the title.
346 * Used in bugs and tasks.
347 */
348 function displayStatusColor ($group) {
349 $group.find('.cmb-group-title').each(function () {
350 var $this = $(this);
351 var status = $this.next().find('[id$="status"] option:selected').text();
352 if (status) {
353 var $parent = $this.parents('.cmb2-wrap.form-table');
354 color = $parent.find('ul.statuses li .status:contains(' + status + ')').next().text();
355 color = color ? color : 'transparent';
356 $this.append('<span class="status" style="background: ' + color + '">' + status + '</span>');
357 }
358 });
359 };
360
361 /*
362 * Displays the task end date in the title.
363 */
364 function displayEndDate ($group) {
365 $group.find('.cmb-group-title').each(function () {
366 var $this = $(this);
367 var date = $this.next().find('[id$="end_date"], [id$="due_date"]').val();
368 if (date) {
369 $this.append('<span class="dates">End: ' + date + '</span>');
370 }
371 });
372 };
373
374 /*
375 * Displays the currently selected progress in the title.
376 * Only used on the Tasks group.
377 */
378 function displayProgress ($group) {
379 $group.find('.cmb-repeatable-grouping').each(function () {
380 var $this = $(this);
381 var progress = $this.find('[id$="progress"]').val();
382 progress = progress ? progress : '0';
383 $this.find('.progress').remove();
384 $this.append('<span class="progress"><progress value="' + progress + '" max="100"></progress></span>');
385 });
386 };
387
388 var emptyClickEvent = function (e) {
389 e.preventDefault();
390 e.stopPropagation();
391 };
392
393 $(document).ready(function () {
394 $('.postbox.cmb-row.cmb-repeatable-grouping[data-iterator] button.cmb-remove-group-row').each(function () {
395 var self = $(this);
396
397 if (self.attr('disabled') === 'disabled') {
398 self.attr('data-disabled', 'disabled');
399 self.on('click', emptyClickEvent);
400 }
401 });
402
403 $('div[data-groupid]').on('click', 'button.cmb-remove-group-row', function (e) {
404 var self = $(this);
405 var groupWrapper = $(self.parents('div[data-groupid].cmb-nested.cmb-field-list.cmb-repeatable-group'));
406
407 setTimeout(function () {
408 $('.postbox.cmb-row.cmb-repeatable-grouping .cmb-remove-group-row[data-disabled]', groupWrapper).attr('disabled', 'disabled');
409 }, 50);
410 });
411 });
412
413 /*
414 * When adding a new row
415 *
416 */
417 function addRow ($group) {
418 // if first item is hidden, then show it
419 var first = $group.find('.cmb-nested .cmb-row')[0];
420 if ($(first).is(':hidden')) {
421 $(first).show();
422 $(first).removeClass('closed');
423 $(first).next().remove();
424 }
425
426 // enable all fields in this row and reset them
427 var $row = $group.find('.cmb-repeatable-grouping').last();
428 $row.addClass('is-new');
429 $row.find('input:not([type="checkbox"]), textarea, select').not(':button,:hidden').val('');
430 $row.find(':input').prop({'disabled': false, 'readonly': false});
431 $row.find('[data-user_assigned]').attr('data-user_assigned', '');
432 $row.find('[data-user_created_by]').attr('data-user_created_by', '');
433 $row.find('[data-avatar_assigned]').attr('data-avatar_assigned', '');
434 $row.find('[data-avatar_created_by]').attr('data-avatar_created_by', '');
435 $row.find('.up-autoincrement').html('---');
436 $row.find('.up-o-tab').removeClass('nav-tab-active');
437 $row.find('.dashicons-flag').removeAttr('style');
438 $row.find('.up-o-tab[data-target=".up-c-tab-content-data"]').addClass('nav-tab-active');
439
440 // upfs
441 $row.find('.upfs-hidden').each(function() {
442 $(this).attr('type', 'file');
443 $(this).parent().children('a').remove();
444 });
445
446 // Check if we have a defined default value in the attributes.
447 $row.find('input, textarea, select').each(function () {
448 var $self = $(this);
449 var $wrapper = $self.closest('.cmb-row,.form-group');
450
451 if (this.tagName === 'INPUT') {
452 var r = $(this);
453 var st = randomString(10);
454 // for datepicker duplication
455 if (r.attr('name') === 'chooseadate2') {
456 r.attr('id', 'upstream_chooseadate2' + st);
457 r.siblings('[name="chooseadate2_timestamp"]').attr('id', 'upstream_chooseadate2' + st + '_timestamp');
458 }
459 }
460 if ($wrapper.data('default')) {
461 var defaultValue = $wrapper.data('default');
462
463 // Is this element a select field?
464 if (this.tagName === 'SELECT') {
465 defaultValue = defaultValue.split(',');
466
467 $.each(defaultValue, function (i, value) {
468 $self.find('option').each(function (i, option) {
469 if ($(option).prop('value') === value) {
470 $(option).prop('selected', true);
471 }
472 });
473 });
474 } else if (this.tagName === 'INPUT' && $(this).prop('type') === 'radio') {
475 if ($self.prop('value') === defaultValue) {
476 $self.prop('checked', true);
477 }
478 } else {
479 $self.val(defaultValue);
480 }
481 }
482 });
483
484 setTimeout(function () {
485 $row.find('.up-o-tab[data-target=".up-c-tab-content-comments"]').remove();
486 $row.find('.up-c-tabs-header').remove();
487 $row.find('.cmb-row[data-fieldtype="comments"]').remove();
488 // Recreate select2 fields.
489 $row.find('.select2-container').remove();
490 $row.find('.o-select2').select2();
491 }, 25);
492
493 $group.find('.cmb-add-row span').remove();
494
495 window.wp.autosave.server.triggerSave();
496
497 $('.cmb-remove-group-row[data-disabled]', $row).attr('data-disabled', null);
498 $('.cmb-remove-group-row[data-disabled]', $group).each(function () {
499 $(this).attr('disabled', 'disabled');
500 });
501 }
502
503 /**
504 * Add or remove users to "Assigned To" fields dynamically.
505 */
506 function toggleClientUsersFromAssignedToFields (e) {
507 var self = $(this);
508 var isChecked = self.is(':checked');
509
510 var user_id = self.val();
511
512 var fields = $('#post select.o-select2[name$="[assigned_to][]"]');
513 var existentOptions = fields.find('option[value="' + user_id + '"]');
514
515 if (isChecked) {
516 if (existentOptions.length === 0) {
517 var user_name = $('label', self.parent()).text().trim();
518
519 var newOption = new Option(user_name, user_id, false, false);
520
521 fields.append(newOption).trigger('change');
522 }
523 } else {
524 if (existentOptions.length > 0) {
525 existentOptions.remove();
526 fields.trigger('change');
527 }
528 }
529 }
530
531 $('.cmb-row.cmb2-id--upstream-project-client-users').on('change', 'input[type="checkbox"]', toggleClientUsersFromAssignedToFields);
532
533 /*
534 * Shows a clients users dynamically via AJAX
535 */
536 function showClientUsers () {
537
538 var $box = $(document.getElementById('_upstream_project_details'));
539 var $ul = $box.find('.cmb2-id--upstream-project-client-users ul');
540
541 getUsers = function (evt) {
542
543 var $this = $(evt.target);
544 var client_id = $this.val();
545
546 $.ajax({
547 url: ajaxurl,
548 type: 'post',
549 data: {
550 action: 'upstream_admin_ajax_get_clients_users',
551 client_id: client_id,
552 project_id: $('#post_ID').val(),
553 nonce: $('#upstream_admin_project_form_nonce').val()
554 },
555 success: function (response) {
556 $ul.empty();
557
558 if (typeof response.data === 'string' && response.data) {
559 $ul.append(response.data);
560 } else if (response.data.msg) {
561 $ul.append('<li>' + response.data.msg + '</li>');
562 }
563 }
564 });
565
566 return false;
567
568 };
569
570 noUsers = function () {
571 if ($ul.find('li').length == 0) {
572 $ul.append('<li>' + upstream_project.l.MSG_NO_CLIENT_SELECTED + '</li>');
573 }
574 };
575
576 noUsers();
577
578 $box
579 .on('keyup change', '#_upstream_project_client', function (evt) {
580 getUsers(evt);
581 });
582
583 }
584
585 // kick it all off
586 initProject();
587 showClientUsers();
588
589 var showValidationError = function (errorMessage) {
590 if ($('.has-error').length == 0 || errorMessage == '') {
591 return;
592 }
593
594 var box = $('<div>')
595 .addClass('upstream_validation_error_box')
596 .text(errorMessage)
597 .appendTo($('#submitdiv'));
598
599 var icon = $('<span>')
600 .addClass('dashicons dashicons-warning')
601 .appendTo(box);
602
603 window.setTimeout(function () {
604 hideValidationError();
605 }, 8000);
606 };
607
608 var hideValidationError = function () {
609 var box = $('.upstream_validation_error_box');
610 var removeBox = function () {
611 box.remove();
612 };
613
614 box.fadeOut(400, removeBox);
615 };
616
617 /*
618 * Deal with invalid fields to make sure they are visible when there is an error. If hidden, there will be an error:
619 * "An invalid form control with name.... is not focusable."
620 * It will happen on Chrome if the field is invalid and not visible.
621 */
622 $('form#post').find('input,textarea,select').on('invalid', function (e) {
623 // Check if its container is closed and open it.
624 if ($(this).closest('.postbox.closed')) {
625 $(this).closest('.postbox.closed').removeClass('closed');
626 }
627 });
628
629 $('form#post').on('submit', function (e) {
630 var tasksWrapper = $('#_upstream_project_tasks_repeat');
631 var tasks = $('.postbox.cmb-row.cmb-repeatable-grouping', tasksWrapper);
632
633 for (var t = 0; t < tasks.length; t++) {
634 var taskWrapper = $(tasks[t]);
635 if (taskWrapper.css('display') !== 'none') {
636 var taskTitleField = $('input.task-title', taskWrapper);
637 if (taskTitleField.val().trim().length === 0) {
638 taskTitleField.addClass('has-error');
639
640 $(taskTitleField.parents('.postbox.cmb-row.cmb-repeatable-grouping')).removeClass('closed');
641 $(taskTitleField.parents('.postbox.cmb2-postbox')).removeClass('closed');
642
643 e.preventDefault();
644 e.stopPropagation();
645
646 taskTitleField.focus();
647 showValidationError(upstream_project.l.MSG_TITLE_CANT_BE_EMPTY);
648
649 return false;
650 }
651 }
652 }
653
654 $('input.task-title.has-error', tasksWrapper).removeClass('has-error');
655
656 var wrapperMilestones = $('#_upstream_project_milestones_repeat, #_upstream_project_tasks_repeat, #_upstream_project_bugs_repeat');
657 if (wrapperMilestones.length) {
658 $('.postbox.cmb-row.cmb-repeatable-grouping .cmb-row *:disabled', wrapperMilestones).filter(function () {
659 var self = $(this);
660 if (['INPUT', 'SELECT', 'TEXTAREA'].indexOf(self.prop('tagName')) >= 0) {
661 $(this).prop({
662 'disabled': '',
663 'data-disabled': '',
664 'readonly': ''
665 });
666 }
667 });
668 }
669
670 // Check if Start/End dates intervals are valid.
671 var stopSubmit = false;
672
673 var validateIntervalBetweenDatesFields = function (startDateEl, endDateEl) {
674 if (!startDateEl || !endDateEl) return true;
675
676 var startDate = new Date(startDateEl.val());
677 var endDate = new Date(endDateEl.val());
678
679 if (!startDate
680 || !endDate
681 || !startDate.toJSON()
682 || !endDate.toJSON()
683 ) {
684 return true;
685 }
686
687 return (+startDate) <= (+endDate);
688 };
689
690 var validateIntervalBetweenDatesFieldsInSection = function (section) {
691 var isValid = true;
692
693 if (section.length > 0) {
694 var rowsList = $('> .postbox.cmb-row[data-iterator]', section);
695 if (rowsList.length) {
696 rowsList.each(function () {
697 var row = $(this);
698
699 var startDateEl = $('[name$="[start_date]"]', row);
700 var endDateEl = $('[name$="[end_date]"]', row);
701
702 if (!validateIntervalBetweenDatesFields(startDateEl, endDateEl)) {
703 isValid = false;
704
705 startDateEl.addClass('has-error');
706 endDateEl.addClass('has-error');
707
708 row.removeClass('closed');
709 $('.up-o-tab', row).removeClass('nav-tab-active');
710 $('.up-o-tab.up-o-tab-data', row).addClass('nav-tab-active');
711
712 $('.up-o-tab-content', row).removeClass('is-active');
713 $('.up-o-tab-content.up-c-tab-content-data').addClass('is-active');
714
715 startDateEl.focus();
716
717 showValidationError(upstream_project.l.MSG_INVALID_INTERVAL_BETWEEN_DATE);
718
719 return false;
720 } else {
721 startDateEl.removeClass('has-error');
722 endDateEl.removeClass('has-error');
723 }
724 });
725 }
726 }
727
728 return isValid;
729 };
730
731 // Check if Milestones section exists.
732 if (!validateIntervalBetweenDatesFieldsInSection($('#_upstream_project_milestones_repeat'))
733 || !validateIntervalBetweenDatesFieldsInSection($('#_upstream_project_tasks_repeat'))
734 ) {
735 stopSubmit = true;
736 }
737
738 if (stopSubmit) {
739 e.preventDefault();
740 e.stopPropagation();
741
742 return false;
743 }
744 });
745
746 var titleHasFocus = false;
747 $(document)
748 .on('before-autosave.update-post-slug', function () {
749 titleHasFocus = document.activeElement && document.activeElement.id === 'title';
750 })
751 .on('after-autosave.update-post-slug', function (e, data) {
752 if (!$('#edit-slug-box > *').length && !titleHasFocus) {
753 $.post(ajaxurl, {
754 action: 'sample-permalink',
755 post_id: $('#post_ID').val(),
756 new_title: $('#title').val(),
757 samplepermalinknonce: $('#samplepermalinknonce').val()
758 },
759 function (data) {
760 if (data != '-1') {
761 $('#edit-slug-box').html(data);
762 }
763 }
764 );
765 }
766 });
767 })(jQuery, upstream_project);
768
769 (function (window, document, $, upstream_project, undefined) {
770 $(document).ready(function () {
771 var newMessageLabel = $('#_upstream_project_discussions label[for="_upstream_project_new_message"]');
772 var newMessageLabelText = newMessageLabel.text();
773
774 function getCommentEditor (editor_id) {
775 var TinyMceSingleton = window.tinyMCE ? window.tinyMCE : (window.tinymce ? window.tinymce : null);
776
777 // The editor can be disabled by the user in his profile, so we return null.
778 if (TinyMceSingleton === null) {
779 return null;
780 }
781
782 var theEditor = TinyMceSingleton.get(editor_id);
783
784 return theEditor;
785 }
786
787 function getCommentEditorTextarea (editor_id) {
788 return $('#' + editor_id);
789 }
790
791 function getEditorContent (editor_id, asHtml) {
792 asHtml = typeof asHtml === 'undefined' ? true : (asHtml ? true : false);
793
794 var theEditor = getCommentEditor(editor_id);
795 var content = '';
796
797 var isEditorInVisualMode = theEditor ? !theEditor.isHidden() : false;
798 if (isEditorInVisualMode) {
799 if (asHtml) {
800 content = (theEditor.getContent() || '').trim();
801 } else {
802 content = (theEditor.getContent({format: 'text'}) || '').trim();
803 }
804 } else {
805 theEditor = getCommentEditorTextarea(editor_id);
806 content = theEditor.val().trim();
807 }
808
809 return content;
810 }
811
812 function isEditorEmpty (editor_id) {
813 var theEditor = getCommentEditor(editor_id),
814 content;
815
816 var isEditorInVisualMode = theEditor ? !theEditor.isHidden() : false;
817
818 if (isEditorInVisualMode) {
819 content = theEditor.getContent();
820 } else {
821 theEditor = getCommentEditorTextarea(editor_id);
822 content = theEditor.val().trim();
823 }
824
825 // Check if maybe we have images in the content (those are not identified on the text format).
826 // Replace images with placeholders
827 content = content.replace(/<img.*\/>/g, '[image]');
828 // Remove tags and special chars.
829 content = content.replace(/<[^>]+>|&[a-z]+;/g, '');
830 // Remove spaces.
831 content = content.trim();
832
833 return content === '';
834 }
835
836 function disableCommentArea (editor_id) {
837 var theEditor = getCommentEditor(editor_id);
838
839 if (theEditor) {
840 theEditor.getDoc().designMode = 'off';
841
842 var theEditorBody = theEditor.getBody();
843 theEditorBody.setAttribute('contenteditable', 'false');
844 theEditorBody.setAttribute('readonly', '1');
845 theEditorBody.style.background = '#ECF0F1';
846 theEditorBody.style.cursor = 'progress';
847 }
848
849 var theEditorTextarea = getCommentEditorTextarea(editor_id);
850 theEditorTextarea.attr('disabled', 'disabled');
851 theEditorTextarea.addClass('disabled');
852
853 $('#wp-' + editor_id + '-wrap').css('cursor', 'progress');
854 $('#insert-media-button').attr('disabled', 'disabled');
855 $('button[data-action^="comment."]').attr('disabled', 'disabled');
856 $('button[data-action^="comments."]').attr('disabled', 'disabled');
857 $('button[data-editor="' + editor_id + '"]').attr('disabled', 'disabled');
858 }
859
860 function enableCommentArea (editor_id) {
861 var theEditor = getCommentEditor(editor_id);
862
863 if (theEditor) {
864 theEditor.getDoc().designMode = 'on';
865
866 var theEditorBody = theEditor.getBody();
867 theEditorBody.setAttribute('contenteditable', 'true');
868 theEditorBody.setAttribute('readonly', '0');
869 theEditorBody.style.background = null;
870 theEditorBody.style.cursor = null;
871 }
872
873 var theEditorTextarea = getCommentEditorTextarea(editor_id);
874 theEditorTextarea.attr('disabled', null);
875 theEditorTextarea.removeClass('disabled');
876
877 $('#wp-' + editor_id + '-wrap').css('cursor', '');
878 $('#insert-media-button').attr('disabled', null);
879 $('button[data-action^="comment."]').attr('disabled', null);
880 $('button[data-action^="comments."]').attr('disabled', null);
881 $('button[data-editor="' + editor_id + '"]').attr('disabled', null);
882 }
883
884 function resetCommentEditorContent (editor_id) {
885 var theEditor = getCommentEditor(editor_id);
886 if (theEditor) {
887 theEditor.setContent('');
888 }
889
890 var theEditorTextarea = getCommentEditorTextarea(editor_id);
891 theEditorTextarea.val('');
892 }
893
894 function appendCommentHtmlToDiscussion (commentHtml, wrapper) {
895 var comment = $(commentHtml);
896 comment.hide();
897
898 commentHtml = comment.html()
899 .replace(/\\'/g, '\'')
900 .replace(/\\"/g, '"');
901
902 comment.html(commentHtml);
903
904 comment.prependTo(wrapper);
905
906 comment.slideDown();
907 }
908
909 function replyCancelButtonClickCallback (editor_id, wrapper) {
910 if (editor_id === '_upstream_project_new_message') {
911 $('label[for="_upstream_project_new_message"]').text(upstream_project.l.LB_ADD_NEW_COMMENT);
912 }
913
914 $('.button.u-to-be-removed', wrapper).remove();
915 $('.button[data-action="comments.add_comment"]', wrapper).show();
916
917 $('.o-comment[data-id]', wrapper).removeClass('is-mouse-over is-disabled is-being-replied');
918
919 resetCommentEditorContent(editor_id);
920 }
921
922 function replySendButtonCallback (e) {
923 e.preventDefault();
924
925 var self = $(this);
926 var parent = $(self.parent().parent());
927 var commentsWrapper;
928
929 var editor_id = $('textarea.wp-editor-area', parent).attr('id');
930
931 if (isEditorEmpty(editor_id)) {
932 setFocus(editor_id);
933 return;
934 }
935
936 var commentContentHtml = getEditorContent(editor_id);
937
938 var item_type, item_id, item_index;
939 var itemsWrapper = $(self.parents('.cmb-nested.cmb-field-list[data-groupid]'));
940
941 if (itemsWrapper.length > 0) {
942 commentsWrapper = $('.c-comments', parent);
943 var itemWrapper = $(self.parents('.cmb-row[data-iterator]'));
944
945 item_index = itemWrapper.attr('data-iterator');
946
947 var group_id = itemsWrapper.attr('data-groupid');
948 var item_type_plural = group_id.replace('_upstream_project_', '');
949 item_type = item_type_plural.substring(0, item_type_plural.length - 1);
950
951 var prefix = group_id + '_' + item_index;
952 item_id = $('#' + prefix + '_id').val();
953 } else {
954 item_type = 'project';
955 commentsWrapper = $('.c-comments', self.parents('.cmb2-metabox'));
956 }
957
958 var errorCallback = function () {
959 self.text(upstream_project.l.LB_SEND_REPLY);
960 $('.button.u-to-be-removed', parent).attr('disabled', null);
961 $('.o-comment.is-being-replied a[data-action="comment.reply"]', commentsWrapper).text(upstream_project.l.LB_REPLY);
962 };
963
964 var theCommentBeingReplied = $('.o-comment.is-being-replied', commentsWrapper);
965
966 $.ajax({
967 type: 'POST',
968 url: ajaxurl,
969 data: {
970 action: 'upstream:project.add_comment_reply',
971 nonce: self.data('nonce'),
972 project_id: $('#post_ID').val(),
973 parent_id: self.attr('data-id'),
974 content: commentContentHtml,
975 item_type: item_type || null,
976 item_id: item_id || null
977 },
978 beforeSend: function () {
979 disableCommentArea(editor_id);
980 self.text(upstream_project.l.LB_REPLYING);
981 $('.button.u-to-be-removed', parent).attr('disabled', 'disabled');
982 $('a[data-action="comment.reply"]', theCommentBeingReplied).text(upstream_project.l.LB_REPLYING);
983 },
984 success: function (response) {
985 if (response.error) {
986 errorCallback();
987 console.error(response.error);
988 alert(response.error);
989 } else {
990 if (!response.success) {
991 errorCallback();
992 console.error('Something went wrong.');
993 } else {
994 resetCommentEditorContent(editor_id);
995 replyCancelButtonClickCallback(editor_id, parent);
996
997 appendCommentHtmlToDiscussion(response.comment_html, theCommentBeingReplied.find('.o-comment-replies').get(0));
998
999 $('a[data-action="comment.reply"]', theCommentBeingReplied).text(upstream_project.l.LB_REPLY);
1000 $('.o-comment', commentsWrapper).removeClass('is-disabled is-mouse-over is-being-replied');
1001 }
1002 }
1003 },
1004 error: function (jqXHR, textStatus, errorThrown) {
1005 errorCallback();
1006
1007 var response = {
1008 text_status: textStatus,
1009 errorThrown: errorThrown
1010 };
1011
1012 console.error(response);
1013 },
1014 complete: function () {
1015 enableCommentArea(editor_id);
1016 }
1017 });
1018 }
1019
1020 function setFocus (editor_id) {
1021 var theEditor = getCommentEditor(editor_id);
1022 var isEditorInVisualMode = theEditor ? !theEditor.isHidden() : false;
1023 if (isEditorInVisualMode) {
1024 theEditor.execCommand('mceFocus', false);
1025 } else {
1026 theEditor = getCommentEditorTextarea(editor_id);
1027 theEditor.focus();
1028 }
1029 }
1030
1031 $('label[for="_upstream_project_new_message"]').on('click', function (e) {
1032 e.preventDefault();
1033
1034 setFocus('_upstream_project_new_message');
1035 });
1036
1037 $('.cmb2-wrap').on('click', '.c-comments .o-comment[data-id] a[data-action="comment.reply"]', function (e) {
1038 e.preventDefault();
1039
1040 var self = $(this);
1041 var commentWrapper = $(self.parents('.o-comment[data-id]').get(0));
1042 var comment_id = commentWrapper.attr('data-id');
1043
1044 commentWrapper.addClass('is-mouse-over is-being-replied');
1045 var parent = $(commentWrapper.parents('.c-comments').parent());
1046
1047 $('.o-comment[data-id!="' + comment_id + '"]', parent).addClass('is-disabled');
1048
1049 var editor_id = $('textarea.wp-editor-area', parent).attr('id');
1050
1051 var addCommentBtn = $('.button[data-action="comments.add_comment"]', parent);
1052 addCommentBtn.hide();
1053 var controlsWrapper = $(addCommentBtn.parent());
1054
1055 $('.button.u-to-be-removed', parent).remove();
1056
1057 var cancelButton = $('<button></button>', {
1058 type: 'button',
1059 class: 'button button-secondary u-to-be-removed'
1060 })
1061 .text(upstream_project.l.LB_CANCEL);
1062 cancelButton.on('click', function (e) {
1063 e.preventDefault();
1064
1065 replyCancelButtonClickCallback(editor_id, parent);
1066 });
1067 controlsWrapper.append(cancelButton);
1068
1069 var sendButton = $('<button></button>', {
1070 type: 'button',
1071 class: 'button button-primary u-to-be-removed',
1072 'data-id': comment_id,
1073 'data-nonce': self.data('nonce')
1074 })
1075 .text(upstream_project.l.LB_SEND_REPLY)
1076 .css('margin-left', '10px');
1077 sendButton.on('click', replySendButtonCallback);
1078 controlsWrapper.append(sendButton);
1079
1080 resetCommentEditorContent(editor_id);
1081
1082 if (editor_id === '_upstream_project_new_message') {
1083 $('label[for="_upstream_project_new_message"]').text(upstream_project.l.LB_ADD_NEW_REPLY);
1084 }
1085
1086 var finished = false;
1087 $('html, body').animate({
1088 scrollTop: $(editor_id === '_upstream_project_new_message' ? '#_upstream_project_discussions' : commentWrapper.parents('.postbox.cmb-row[data-iterator]')).offset().top
1089 }, {
1090 complete: function (e) {
1091 if (!finished) {
1092 setFocus(editor_id);
1093 finished = true;
1094 }
1095 }
1096 });
1097 });
1098
1099 $('.cmb2-wrap').on('click', '.o-comment[data-id] a[data-action="comment.trash"]', function (e) {
1100 e.preventDefault();
1101
1102 var self = $(this);
1103
1104 var comment = $(self.parents('.o-comment[data-id]').get(0));
1105 if (!comment.length) {
1106 console.error('Comment wrapper not found.');
1107 return;
1108 }
1109
1110 if (!confirm(upstream_project.l.MSG_ARE_YOU_SURE)) return;
1111
1112 var errorCallback = function () {
1113 comment.removeClass('is-loading is-mouse-over is-being-removed');
1114 self.text(upstream_project.l.LB_DELETE);
1115 };
1116
1117 $.ajax({
1118 type: 'POST',
1119 url: ajaxurl,
1120 data: {
1121 action: 'upstream:project.trash_comment',
1122 nonce: self.data('nonce'),
1123 project_id: $('#post_ID').val(),
1124 comment_id: comment.attr('data-id')
1125 },
1126 beforeSend: function () {
1127 comment.addClass('is-loading is-mouse-over is-being-removed');
1128 self.text(upstream_project.l.LB_DELETING);
1129 },
1130 success: function (response) {
1131 if (response.error) {
1132 errorCallback();
1133
1134 console.error(response.error);
1135 alert(response.error);
1136 } else {
1137 if (!response.success) {
1138 console.error('Something went wrong.');
1139
1140 errorCallback();
1141 } else {
1142 comment.css('background-color', '#E74C3C');
1143
1144 comment.slideUp({
1145 complete: function () {
1146 comment.remove();
1147 }
1148 });
1149 }
1150 }
1151 },
1152 error: function (jqXHR, textStatus, errorThrown) {
1153 errorCallback();
1154
1155 var response = {
1156 text_status: textStatus,
1157 errorThrown: errorThrown
1158 };
1159
1160 console.error(response);
1161 }
1162 });
1163 });
1164
1165 function sendNewCommentRequest (self, content, nonce, item_type, item_id, item_index, editor_id, commentsWrapper, item_title) {
1166 item_type = typeof item_type === 'undefined' ? 'project' : item_type;
1167 item_id = typeof item_id === 'undefined' ? null : item_id;
1168 item_index = typeof item_index === 'undefined' ? null : item_index;
1169 item_title = typeof item_title === 'undefined' ? null : item_title;
1170
1171 $.ajax({
1172 type: 'POST',
1173 url: ajaxurl,
1174 data: {
1175 action: 'upstream:project.add_comment',
1176 nonce: nonce,
1177 project_id: $('#post_ID').val(),
1178 item_type: item_type,
1179 item_index: item_index,
1180 item_id: item_id,
1181 item_title: item_title,
1182 content: content
1183 },
1184 beforeSend: function () {
1185 disableCommentArea(editor_id);
1186 self.text(upstream_project.l.LB_ADDING);
1187 self.attr('disabled', 'disabled');
1188 },
1189 success: function (response) {
1190 if (response.error) {
1191 console.error(response.error);
1192 alert(response.error);
1193 } else {
1194 if (!response.success) {
1195 console.error('Something went wrong.');
1196 } else {
1197 resetCommentEditorContent(editor_id);
1198
1199 appendCommentHtmlToDiscussion(response.comment_html, commentsWrapper);
1200 }
1201 }
1202 },
1203 error: function (jqXHR, textStatus, errorThrown) {
1204 errorCallback();
1205
1206 var response = {
1207 text_status: textStatus,
1208 errorThrown: errorThrown
1209 };
1210
1211 console.error(response);
1212 },
1213 complete: function () {
1214 enableCommentArea(editor_id);
1215 self.text(upstream_project.l.LB_ADD_COMMENT);
1216 self.attr('disabled', null);
1217 }
1218 });
1219 }
1220
1221 $('#_upstream_project_discussions .button[data-action="comments.add_comment"]').on('click', function (e) {
1222 e.preventDefault();
1223
1224 var self = $(this);
1225
1226 var editor_id = '_upstream_project_new_message';
1227 if (isEditorEmpty(editor_id)) {
1228 setFocus(editor_id);
1229 return;
1230 }
1231
1232 var commentHtml = getEditorContent(editor_id);
1233
1234 sendNewCommentRequest(
1235 self,
1236 commentHtml,
1237 self.attr('data-nonce'),
1238 'project',
1239 null,
1240 null,
1241 editor_id,
1242 $('#_upstream_project_discussions .c-comments')
1243 );
1244 });
1245
1246 function sendToggleApprovalStateRequest (self, isApproved) {
1247 var comment = $(self.parents('.o-comment[data-id]').get(0));
1248 if (!comment.length) {
1249 console.error('Comment wrapper not found.');
1250 return;
1251 }
1252
1253 var errorCallback = function () {
1254 comment
1255 .removeClass('is-loading is-mouse-over is-being-' + (isApproved ? 'approved' : 'unapproved'))
1256 .css('background-color', '');
1257 self.text(isApproved ? upstream_project.l.LB_APPROVE : upstream_project.l.LB_UNAPPROVE);
1258 };
1259
1260 $.ajax({
1261 type: 'POST',
1262 url: ajaxurl,
1263 data: {
1264 action: 'upstream:project.' + (isApproved ? 'approve' : 'unapprove') + '_comment',
1265 nonce: self.data('nonce'),
1266 project_id: $('#post_ID').val(),
1267 comment_id: comment.attr('data-id')
1268 },
1269 beforeSend: function () {
1270 comment.addClass('is-loading is-mouse-over is-being-' + (isApproved ? 'approved' : 'unapproved'));
1271 self.text(isApproved ? upstream_project.l.LB_APPROVING : upstream_project.l.LB_UNAPPROVING);
1272 },
1273 success: function (response) {
1274 if (response.error) {
1275 errorCallback();
1276 console.error(response.error);
1277 alert(response.error);
1278 } else {
1279 if (!response.success) {
1280 errorCallback();
1281 console.error('Something went wrong.');
1282 } else {
1283 comment.removeClass('s-status-' + (!isApproved ? 'approved' : 'unapproved') + ' is-being-' + (!isApproved ? 'approved' : 'unapproved'))
1284 .addClass('s-status-' + (isApproved ? 'approved' : 'unapproved'));
1285
1286 var newComment = $(response.comment_html);
1287 var newCommentBody = $('.o-comment__body', newComment);
1288
1289 $(comment.find('.o-comment__body').get(0)).replaceWith(newCommentBody);
1290 }
1291 }
1292 },
1293 error: function (jqXHR, textStatus, errorThrown) {
1294 errorCallback();
1295
1296 var response = {
1297 text_status: textStatus,
1298 errorThrown: errorThrown
1299 };
1300
1301 console.error(response);
1302 },
1303 complete: function () {
1304 comment.removeClass('is-loading is-mouse-over is-being-approved is-being-unapproved');
1305 }
1306 });
1307 }
1308
1309 $('.cmb2-wrap').on('click', '.o-comment[data-id] a[data-action="comment.unapprove"]', function (e) {
1310 e.preventDefault();
1311 sendToggleApprovalStateRequest($(this), false);
1312 });
1313
1314 $('.cmb2-wrap').on('click', '.o-comment[data-id] a[data-action="comment.approve"]', function (e) {
1315 e.preventDefault();
1316 sendToggleApprovalStateRequest($(this), true);
1317 });
1318
1319 $('.cmb2-wrap').on('click', '.c-comments .o-comment[data-id] a[data-action="comment.go_to_reply"]', function (e) {
1320 e.preventDefault();
1321
1322 var targetComment = $($(this).attr('href'));
1323 if (!targetComment.length) {
1324 console.error('Comment not found.');
1325 return;
1326 }
1327
1328 var wrapper = $(targetComment.parents('.c-comments'));
1329 var wrapperOffset = wrapper.offset();
1330
1331 if (!wrapperOffset) return;
1332
1333 var offset = targetComment.offset() || null;
1334 if (!offset) return;
1335
1336 var targetCommentOffsetTop = offset.top - wrapperOffset.top;
1337
1338 wrapper.animate({
1339 scrollTop: targetCommentOffsetTop
1340 }, function () {
1341 targetComment.addClass('s-highlighted');
1342 setTimeout(function () {
1343 targetComment.removeClass('s-highlighted');
1344 }, 750);
1345 });
1346 });
1347
1348 $('.cmb2-wrap').on('click', '.up-o-tab[role="tab"][data-target]', function (e) {
1349 e.preventDefault();
1350
1351 var self = $(this);
1352 var wrapper = $(self.parents('.cmb-row[data-iterator]'));
1353
1354 $('.up-o-tab', wrapper).removeClass('nav-tab-active');
1355 self.addClass('nav-tab-active');
1356
1357 var target = $('.up-o-tab-content' + self.attr('data-target'), wrapper);
1358 if (target.length > 0) {
1359 $('.up-o-tab-content', wrapper).removeClass('is-active');
1360 target.addClass('is-active');
1361 }
1362 });
1363
1364 function fetchAllComments () {
1365 // Fix misplaced comments wrappers.
1366 $('.up-o-tab-content.up-c-tab-content-data .up-c-tab-content-comments').each(function () {
1367 var self = $(this);
1368 var tabsWrapper = $(self.parents('.up-c-tabs-content'));
1369
1370 if (tabsWrapper.length > 0) {
1371 tabsWrapper.append(self);
1372 }
1373 });
1374
1375 $.ajax({
1376 type: 'GET',
1377 url: ajaxurl,
1378 data: {
1379 action: 'upstream:project.get_all_items_comments',
1380 nonce: $('#project_all_items_comments_nonce').val(),
1381 project_id: $('#post_ID').val()
1382 },
1383 success: function (response) {
1384 if (response.success) {
1385 var itemsTypes = ['milestones', 'tasks', 'bugs', 'files'];
1386 for (var itemTypeIndex = 0; itemTypeIndex < itemsTypes.length; itemTypeIndex++) {
1387 var itemType = itemsTypes[itemTypeIndex];
1388 var rowset = response.data[itemType];
1389
1390 $('input.hidden[type="text"][id^="_upstream_project_' + itemType + '_"][id$="_id"]').each(function () {
1391 var wrapper = $($(this).parents('.up-c-tabs-content'));
1392 if ($('up-c-tab-content-comments .c-comments', wrapper).length === 0) {
1393 $('.up-c-tab-content-comments', wrapper).append($('.c-comments', wrapper));
1394 }
1395 });
1396
1397 if (!rowset || rowset.length === 0) {
1398 continue;
1399 }
1400
1401 for (var item_id in rowset) {
1402 var commentsList = rowset[item_id];
1403 var itemEl = $('input.hidden[type="text"][id^="_upstream_project_' + itemType + '_"][id$="_id"][value="' + item_id + '"]');
1404
1405 var wrapper = $(itemEl.parents('.up-c-tabs-content'));
1406 if ($('up-c-tab-content-comments .c-comments', wrapper).length === 0) {
1407 $('.up-c-tab-content-comments', wrapper).append($('.c-comments', wrapper));
1408 }
1409
1410 var commentsWrapper = $('.up-c-tab-content-comments .c-comments', wrapper);
1411 if (commentsList.length > 0) {
1412 for (var commentIndex = 0; commentIndex < commentsList.length; commentIndex++) {
1413 commentsWrapper.append($(commentsList[commentIndex]));
1414 }
1415 }
1416 }
1417 }
1418 }
1419 },
1420 error: function () {},
1421 complete: function () {}
1422 });
1423 }
1424
1425 fetchAllComments();
1426
1427 $('.cmb-row.cmb-type-comments[data-fieldtype="comments"]').each(function () {
1428 var self = $(this);
1429
1430 var itemsWrapper = $(self.parents('.cmb-nested.cmb-field-list[data-groupid]'));
1431 var itemWrapper = $(self.parents('.postbox.cmb-row[data-iterator]'));
1432
1433 var group_id = itemsWrapper.attr('data-groupid');
1434
1435 var parent = $(self.parents('.up-c-tabs-content'));
1436 var wrapper = $('.up-c-tab-content-comments', parent);
1437
1438 var prefix = group_id + '_' + itemWrapper.attr('data-iterator');
1439
1440 var div = $('<div></div>');
1441
1442 var addCommentButton = $('<button></button>', {
1443 'type': 'button',
1444 'class': 'button button-primary',
1445 'data-nonce': $('#' + prefix + '_comments_add_comment_nonce', parent).val(),
1446 'data-action': 'comments.add_comment'
1447 })
1448 .text(upstream_project.l.LB_ADD_COMMENT)
1449 .on('click', function (e) {
1450 e.preventDefault();
1451
1452 var self = $(this);
1453
1454 var editor_id = prefix + '_comments_editor';
1455 if (isEditorEmpty(editor_id)) {
1456 setFocus(editor_id);
1457 return;
1458 }
1459
1460 var commentHtml = getEditorContent(editor_id);
1461
1462 var item_type_plural = group_id.replace('_upstream_project_', '');
1463 var item_id = $('#' + prefix + '_id').val();
1464 var item_title = '';
1465
1466 if (item_type_plural === 'milestones') {
1467 item_title = $('#' + prefix + '_milestone').val();
1468 } else {
1469 item_title = $('#' + prefix + '_title').val();
1470 }
1471
1472 sendNewCommentRequest(
1473 self,
1474 commentHtml,
1475 self.attr('data-nonce'),
1476 item_type_plural.substring(0, item_type_plural.length - 1),
1477 item_id,
1478 itemWrapper.attr('data-iterator'),
1479 editor_id,
1480 $('.c-comments', itemWrapper),
1481 item_title
1482 );
1483 });
1484
1485 div.append(addCommentButton);
1486 wrapper.prepend(div);
1487
1488 wrapper.prepend($('.cmb-td > div.wp-editor-wrap', self));
1489 });
1490
1491 $('.up-o-filter-date').datepicker({
1492 dateFormat: 'yy-mm-dd',
1493 beforeShow: function (input, instance) {
1494 $('#ui-datepicker-div').addClass('cmb2-element');
1495 }
1496 });
1497
1498 $('select.up-o-filter:not(.up-o-filter-date)').on('change', function (e) {
1499 e.preventDefault();
1500 e.stopPropagation();
1501
1502 var self = $(this);
1503 var filterColumn = self.attr('data-column');
1504 var filterValue = self.val();
1505
1506 var metabox = $(self.parents('.cmb2-metabox').get(0));
1507
1508 filterMetaboxTableBy(metabox, filterColumn, filterValue, 'contains');
1509 });
1510
1511 $('.up-o-filter-date.up-o-filter').on('change', function (e) {
1512 e.preventDefault();
1513 e.stopPropagation();
1514
1515 var self = $(this);
1516 var filterColumn = self.attr('data-column');
1517 var filterValue = self.val();
1518
1519 if (filterValue.length > 0) {
1520 filterValue = +new Date(filterValue);
1521 }
1522
1523 var metabox = $(self.parents('.cmb2-metabox').get(0));
1524
1525 filterMetaboxTableBy(metabox, filterColumn, filterValue, self.attr('data-compare-operator'));
1526 });
1527
1528 function filterMetaboxTableBy (metabox, columnName, filterValue, operator) {
1529 var table = metabox.find('.cmb-nested.cmb-field-list.cmb-repeatable-group');
1530 var filtersWrapper = metabox.find('.up-c-filters');
1531 var filtersMap = [];
1532
1533 var filters = $('[data-column]', filtersWrapper);
1534 filters.each(function () {
1535 var self = $(this);
1536
1537 var value = self.val();
1538 if (typeof value === 'string') {
1539 value = value.trim();
1540 } else if (value) {
1541 value = value.filter(function (elValue) { return elValue.length > 0; });
1542 } else {
1543 value = '';
1544 }
1545
1546 filtersMap[self.attr('data-column')] = value.length > 0 ? value : null;
1547 filtersMap.push({
1548 column: self.attr('data-column'),
1549 value: value.length > 0 ? value : null,
1550 comparator: self.attr('data-compare-operator') || 'exact'
1551 });
1552 });
1553
1554 $('.cmb-row.postbox[data-empty-row]', table).remove();
1555
1556 var filtersHasChanged = false;
1557 var rows = $('.cmb-row.postbox[data-iterator]', table);
1558
1559 rows.removeClass('is-filtered');
1560
1561 rows.each(function (trIndex) {
1562 var tr = $(this);
1563 var shouldDisplay = false;
1564
1565 var filter, filterIndex, filterColumnValue, columnValue, comparator, theColumn;
1566 for (filterIndex = 0; filterIndex < filtersMap.length; filterIndex++) {
1567 filter = filtersMap[filterIndex];
1568 if (filter.value === null) {
1569 continue;
1570 }
1571
1572 filtersHasChanged = true;
1573
1574 theColumn = $('[name$="[' + filter.column + ']"]', tr);
1575 if (theColumn.length === 0) {
1576 theColumn = $('[name$="[' + filter.column + '][]"]', tr);
1577 }
1578
1579 if (filter.comparator === 'contains' && theColumn.prop('tagName') === 'SELECT') {
1580 columnValue = [];
1581
1582 var values = $('option[selected]', theColumn);
1583 for (var valueIndex = 0; valueIndex < values.length; valueIndex++) {
1584 columnValue.push($(values[valueIndex]).text());
1585 columnValue.push($(values[valueIndex]).attr('value'));
1586 }
1587 } else {
1588 columnValue = theColumn.val() || '';
1589 }
1590
1591 if (theColumn.hasClass('hasDatepicker') && columnValue.length > 0) {
1592 filter.value = +new Date(filter.value);
1593 columnValue = +new Date(columnValue);
1594 }
1595
1596 if (filter.comparator === 'contains') {
1597 if (typeof filter.value === 'string') {
1598 comparator = new RegExp(filter.value, 'i');
1599 shouldDisplay = comparator.test(columnValue);
1600 } else {
1601 for (var valueIndex in filter.value) {
1602 var theValue = filter.value[valueIndex];
1603 theValue = theValue.length > 0 && theValue !== '__none__' ? theValue : '';
1604
1605 if (typeof columnValue === 'string') {
1606 if (theValue === columnValue) {
1607 shouldDisplay = true;
1608 break;
1609 } else if (theValue !== '') {
1610 comparator = new RegExp(theValue, 'i');
1611 if (comparator.test(columnValue)) {
1612 shouldDisplay = true;
1613 break;
1614 }
1615 }
1616 } else {
1617 for (var columnValueIndex = 0; columnValueIndex < columnValue.length; columnValueIndex++) {
1618 var columnValueItem = columnValue[columnValueIndex];
1619 if (theValue === columnValue) {
1620 shouldDisplay = true;
1621 break;
1622 } else if (theValue !== '') {
1623 comparator = new RegExp(theValue, 'i');
1624 if (comparator.test(columnValue)) {
1625 shouldDisplay = true;
1626 break;
1627 }
1628 }
1629 }
1630
1631 if (shouldDisplay) {
1632 break;
1633 }
1634 }
1635 }
1636 }
1637 } else if (filter.comparator === 'exact') {
1638 if (typeof filter.value === 'string') {
1639 if (filter.value === '__none__') {
1640 shouldDisplay = !columnValue || columnValue === '__none__';
1641 } else {
1642 shouldDisplay = columnValue === filter.value;
1643 }
1644 } else {
1645 for (var valueIndex in filter.value) {
1646 if (filter.value[valueIndex] === '__none__') {
1647 shouldDisplay = !columnValue || columnValue === '__none__';
1648 } else {
1649 shouldDisplay = columnValue === filter.value[valueIndex];
1650 }
1651
1652 if (shouldDisplay) {
1653 break;
1654 }
1655 }
1656 }
1657 } else if (filter.comparator === '>') {
1658 shouldDisplay = columnValue > filter.value;
1659 } else if (filter.comparator === '>=') {
1660 shouldDisplay = columnValue >= filter.value;
1661 } else if (filter.comparator === '<') {
1662 shouldDisplay = columnValue < filter.value;
1663 } else if (filter.comparator === '<=') {
1664 shouldDisplay = columnValue <= filter.value;
1665 } else if (filter.value === '__none__') {
1666 shouldDisplay = !columnValue || columnValue === '__none__';
1667 } else {
1668 if (typeof filter.value === 'string') {
1669 shouldDisplay = columnValue.localeCompare(filter.value) === 0;
1670 } else {
1671 for (var valueIndex in filter.value) {
1672 if (filter.value[valueIndex] === '__none__') {
1673 shouldDisplay = !columnValue || columnValue === '__none__';
1674 } else {
1675 shouldDisplay = columnValue.localeCompare(filter.value[valueIndex]) === 0;
1676 }
1677
1678 if (shouldDisplay) {
1679 break;
1680 }
1681 }
1682 }
1683 }
1684
1685 if (filtersHasChanged && !shouldDisplay) {
1686 break;
1687 }
1688 }
1689
1690 if (shouldDisplay) {
1691 tr.addClass('is-filtered').show();
1692 } else {
1693 tr.hide();
1694 }
1695 });
1696
1697 if (!filtersHasChanged) {
1698 rows.show();
1699 }
1700
1701 var filteredRows = $('.cmb-row.postbox[data-iterator]:visible', table);
1702 if (filteredRows.length === 0) {
1703 $('.cmb-row:last-child', table).prepend($('<div class="postbox cmb-row cmb-repeatable-grouping" data-empty-row><p>' + upstream_project.l.MSG_NO_RESULTS + '</p></div>'));
1704 } else {
1705 $('.cmb-row.postbox:visible', table).addClass('is-filtered');
1706 }
1707 }
1708
1709 $('.up-o-filter[data-trigger_on="keyup"]').on('keyup', function (e) {
1710 e.preventDefault();
1711 e.stopPropagation();
1712
1713 var self = $(this);
1714 var filterColumn = self.attr('data-column');
1715 var filterValue = self.val().trim();
1716
1717 var metabox = $(self.parents('.cmb2-metabox').get(0));
1718
1719 filterMetaboxTableBy(metabox, filterColumn, filterValue, 'contains');
1720 });
1721
1722 $('.o-select2').select2();
1723
1724 $('.cmb-add-group-row').on('click', function (e) {
1725 var self = $(this);
1726
1727 setTimeout(function () {
1728 var wrapper = $(self.parents('div.cmb-repeatable-group[data-groupid]'));
1729
1730 var dataWrapper = $('.up-c-tab-content-data', $('.postbox.cmb-row[data-iterator]', wrapper));
1731 dataWrapper = $(dataWrapper.get(dataWrapper.length - 1));
1732
1733 $('input[type="text"],select', dataWrapper).val('').trigger('change');
1734 $('.select2.select2-container', dataWrapper).remove();
1735 $('select.o-select2', dataWrapper).select2();
1736 }, 100);
1737 });
1738 });
1739 })(window, window.document, jQuery, upstream_project || {});
1740