(function ($) { /** * One executor is created for each object containing inline fields. * When the field is modified, the executor is triggered. * * As a delayed executor, a period of grace is given for the field to * be corrected or other fields to be modified. Each modification resets * the counter. * * When modifications stop happening, the entire object is stored in a * single AJAX request. */ var executors = {}, obtainExecutor, saveFields; obtainExecutor = function (container, delay) { if (isNaN(delay)) delay = 5000; var url = $.serviceUrl($(container).data('object-store-url')); var id = url; if ($(container).find('[data-field-type="DUR"]').length > 0) { id = id + 'DUR'; } if (executors[id]) { return executors[id]; } return executors[id] = delayedExecutor(delay, function (cancelled) { if (cancelled) { return; } var parts = []; var containers = []; $('.editable-inline.modified :input, .editable-dialog.modified :input').each(function () { var container = $(this).closest('.editable-inline, .editable-dialog')[0]; var ownership = $.serviceUrl($(container).data('object-store-url')); if (ownership === url) { parts.push($(this).serialize()); if (-1 === containers.indexOf(container)) { containers.push(container); } } }); $(containers).each(function () { $(this).tikiModal($(this).width() > 30 ? tr("Saving...") : " "); }); $.post(url, parts.join('&'), 'json') .success(function () { $(containers). removeClass('modified'). removeClass('unsaved'). trigger('changed.inline.tiki'). trigger('saved.tiki'). filter(function () { // The post-save value application is only for cases where the field was initially fetched return $(this).data('field-fetch-url'); }). each(function () { var $this = $(this), obj = $.extend($(this).data('field-fetch-url'), { mode: "output" }); // use the url for the field input in output mode $.get($.serviceUrl(obj)) .success(function (data) { $this.removeClass("loaded") .tikiModal() .html($.trim(data.replace("", ""))) .attr("title", $(this).data("saved_title") || "") .removeData("saved_title"); if( $this.data('saved_overflow') ) { $this.closest('td').css('overflow', $this.data('saved_overflow')); } var editIcon = $.fn.getIcon('edit'); $(editIcon).addClass('ml-2'); $this.append(editIcon); }); }); }) .error(function () { $(containers).filter('.modified'). addClass('unsaved'). trigger('changed.inline.tiki'); $.getJSON($.service('object', 'report_error')); }) ; }); }; /** * Returns a function which delays inline-edit fields saving. * @param {number} delay Time to delay save. In milliseconds. */ saveFields = function ($el, delay) { var container, executor; container = $el.closest('.editable-inline')[0]; executor = obtainExecutor(container, delay); $(container). data("saved_title", $(container).attr("title")). attr("title", tr("Queued for saving")). addClass('modified'). trigger('changed.inline.tiki'); executor(); }; $(document).on('click', '.editable-inline:not(.loaded)', function (e) { if ($(e.target).is("a")) { return true; } var container = this, url = $.serviceUrl($(this).data('field-fetch-url')), overflow = $(container).closest('td').css('overflow'); $(container). addClass('loaded'). data("saved_html", $(container).parent().html()). data("saved_text", $(container).text()). data("saved_overflow", overflow). closest('td').css('overflow', 'visible'); if ($(container).data('group')) { $('.editable-inline:not(.loaded)') .filter(function () { return $(this).data('group') === $(container).data('group'); }) .not(container) .click(); } if (url) { url = url.replace(/mode=output/, "mode=input"); $.get(url) .success(function (data) { var tdInnerWidth = $(container).parent().innerWidth(); // td width // Puts only duration field in popover var $el = $('
').html(data).find('[data-field-type="DUR"]'); if ($el.length > 0) { $el.append('Save and exit'); $(container).popover({ html: true, container: $(container), content: $el, trigger: 'hover', delay: { "show": 0, "hide": 90000 } }).popover('show'); } else { $(container).html(data); } $("input, select", container).each(function () { $(this).keydown(function (e) { if (e.which === 13) { // enter $(this).blur(); return false; } else if (e.which === 9) { // tab $(this).blur(); if (e.shiftKey) { $(this).parents("td:first").prev().find(".editable-inline:first").click(); } else { $(this).parents("td:first").next().find(".editable-inline:first").click(); } return false; } else if (e.which === 27) { // escape $(this).off('change'); var url = $.serviceUrl($(container).data('object-store-url')); var id = url; if ($(container).find('[data-field-type="DUR"]').length > 0) { id = id + 'DUR'; } var executor = executors[id]; executor.apply(document, [true]); $(container).parent().html( $(container).data("saved_html") ).find(".editable-inline:first").removeClass("loaded"); if( $(container).data('saved_overflow') ) { $(container).closest('td').css('overflow', $(container).data('saved_overflow')); } } else { return true; } }).width( Math.max( Math.min($(this).width(), tdInnerWidth), parseFloat(getComputedStyle(document.documentElement).fontSize) * 2 // 2 chars width minumum ) ); $(this).focus(); }); var $select = $("select", container).css("width", "auto"); if (jqueryTiki.select2) { if ($select.length) { $select.tiki("select2"); } } // radio buttons need to have different name attributes per item var $radios = $("input[type=radio]", container); if ($radios.length) { var itemId = $(container).data("field-fetch-url").itemId; $radios.each(function () { $(this).attr("name", $(this).attr("name") + "~item" + itemId); }); } }) .error(function (jqxhr) { $(container).showError(jqxhr); $(container).addClass('failure'); }) ; } }); $(document).on('change', '.editable-inline.loaded :input:not(.isDatepicker):not(.duration-field)', function() { saveFields($(this), 5000); }); $(document).on('click', '.editable-inline.loaded .inline-edit-save-fields', function() { saveFields($(this), 0); }); $(document).on('click', '.editable-dialog:not(.loaded)', function () { var container = this, fields = {}; $('.editable-dialog:not(.loaded)') .filter(function () { return $(this).data('group') == $(container).data('group'); }) .each(function (k) { fields['fields[' + k + '][label]'] = $(this).data('label'); fields['fields[' + k + '][fetch]'] = $(this).data('field-fetch-url'); fields['fields[' + k + '][store]'] = $(this).data('object-store-url'); }); $('#bootstrap-modal').modal('show'); $('#bootstrap-modal .modal-content') .load( $.service('edit', 'inline_dialog', { modal: 1 }), fields, function () { $('#bootstrap-modal').trigger('tiki.modal.redraw'); } ); }); $(document).on('submit', '.inline-edit-dialog', function (e) { e.preventDefault(); var reload = delayedExecutor(500, function () { document.location.reload(); }); $('#boostrap-modal').tikiModal(tr('Loading...')); $('.editable-dialog.loaded', this) .addClass('modified') .one('saved.tiki', function () { if ($('.editable-dialog.loaded.modified', this).size() === 0) { reload(); } }) .each(function () { var executor = obtainExecutor(this, 100); executor(); }); }); $(function () { if (jqueryTiki.ui) { $('.inline-sort-handle') .next().children().on('changed.inline.tiki', function () { $(this).parent().prev() .toggleClass('text-warning', $(this).hasClass('modified')) .toggleClass('text-danger', $(this).hasClass('unsaved')); }) .closest('tbody, ul, ol') .each(function () { var $list = $('.inline-sort-handle', this); var first = $list.eq(0).data('current-value') || 1; var second = $list.eq(1).data('current-value') || 2; this.first = first; this.increment = (second - first) || 1; }) .sortable({ handle: '.inline-sort-handle', stop: function () { var first = this.first, increment = this.increment; $('.inline-sort-handle', this).next().find(':input:first').each(function (position) { var val = $(this).val(), target = first + position * increment; if (val != target) { $(this).val(target).change(); } }); } }); } }); // After page reload start duration pickers with pending chronometers or unsaved duration drafts triggerVueDurationFields(); function triggerVueDurationFields() { var $durationEls = $('[data-field-type="DUR"]'); if ($durationEls.length > 0) { $.ajax({ method: "get", url: $.serviceUrl({controller: 'tracker_duration', action: 'drafts'}), dataType: 'json' }).done(function (durationDrafts) { $durationEls.each(function() { var id = $(this).data('field-id'); if (durationDrafts[id]) $(this).click(); }); }); } } })(jQuery);