Files
attract/attract/static/js/tasks.js

301 lines
10 KiB
JavaScript

(function ( $ ) {
$.fn.flashOnce = function() {
var target = this;
this
.addClass('flash-on')
.delay(1000) // this delay is linked to the transition in the flash-on CSS class.
.queue(function() {
target
.removeClass('flash-on')
.addClass('flash-off')
.dequeue()
;})
.delay(1000) // this delay is just to clean up the flash-X classes.
.queue(function() {
target
.removeClass('flash-on flash-off')
.dequeue()
;})
;
return this;
};
/**
* Fades out the element, then erases its contents and shows the now-empty element again.
*/
$.fn.fadeOutAndClear = function(fade_speed) {
var target = this;
this
.fadeOut(fade_speed, function() {
target
.html('')
.show();
});
}
$.fn.hideAndRemove = function(hide_speed, finished) {
this.slideUp(hide_speed, function() {
this.remove();
if (typeof finished !== 'undefined') finished();
});
}
$.fn.removeClassPrefix = function(prefix) {
this.each(function(i, el) {
var classes = el.className.split(" ").filter(function(c) {
return c.lastIndexOf(prefix, 0) !== 0;
});
el.className = $.trim(classes.join(" "));
});
return this;
};
}(jQuery));
/**
* Removes the task from the task list and shot list, and show the 'task-add-link'
* when this was the last task in its category.
*/
function _remove_task_from_list(task_id) {
var $task_link = $('#task-' + task_id)
var $task_link_parent = $task_link.parent();
$task_link.hideAndRemove(300, function() {
if ($task_link_parent.children('.task-link').length == 0) {
$task_link_parent.find('.task-add-link').removeClass('hidden');
}
});
}
/**
* Shows a task in the #task-details div.
*/
function task_open(task_id) {
if (task_id === undefined) {
throw new ReferenceError("task_open(" + task_id + ") called.");
}
var project_url = ProjectUtils.projectUrl();
if (typeof project_url === 'undefined') {
throw new ReferenceError("ProjectUtils.projectUrl() undefined");
}
$('#col_right .col_header span.header_text').text('Task details');
$('[id^="task-"]').removeClass('active');
$('#task-' + task_id).addClass('active');
var task_url = '/attract/' + project_url + '/tasks/' + task_id;
$.get(task_url, function(task_data) {
$('#task-details').html(task_data);
}).fail(function(xhr) {
if (console) {
console.log('Error fetching task', task_id, 'from', task_url);
console.log('XHR:', xhr);
}
$('#task-details').html(xhr.responseText);
});
}
/**
* Shows a shot in the #task-details div.
*/
function shot_open(shot_id) {
if (shot_id === undefined) {
throw new ReferenceError("shot_open(" + shot_id + ") called.");
}
var project_url = ProjectUtils.projectUrl();
if (typeof project_url === 'undefined') {
throw new ReferenceError("ProjectUtils.projectUrl() undefined");
}
$('#col_right .col_header span.header_text').text('Shot details');
$('[id^="shot-"]').removeClass('active');
$('#shot-' + shot_id).addClass('active');
var shot_url = '/attract/' + project_url + '/shots/' + shot_id;
console.log('shot_url is ' + shot_url);
$.get(shot_url, function(shot_data) {
$('#task-details').html(shot_data);
}).fail(function(xhr) {
if (console) {
console.log('Error fetching shot', shot_id, 'from', shot_url);
console.log('XHR:', xhr);
}
$('#task-details').html(xhr.responseText);
});
}
/**
* Create a task and show it in the #task-details div.
*/
function task_create(shot_id, project_url, task_type) {
if (shot_id === undefined || project_url === undefined || task_type === undefined) {
throw new ReferenceError("task_create(" + shot_id + ", " + project_url+ ", " + task_type + ") called.");
}
var url = '/attract/' + project_url + '/tasks/create';
data = {
task_type: task_type,
parent: shot_id,
};
$.post(url, data, function(task_data) {
task_open(task_data.task_id, project_url);
})
.fail(function(xhr) {
if (console) {
console.log('Error creating task');
console.log('XHR:', xhr);
}
$('#task-details').html(xhr.responseText);
});
}
function attract_form_save(form_id, item_id, item_save_url, options={})
{
// Mandatory option.
if (typeof options.type === 'undefined') {
throw new ReferenceError('attract_form_save(): options.type is mandatory.');
}
var $form = $('#' + form_id);
var $button = $form.find("button[type='submit']");
var payload = $form.serialize();
var $item = $('#' + item_id);
$button.attr('disabled', true);
$item.addClass('processing');
$('#status-bar').text('Saving ' + options.type + '...');
if (console) console.log('Sending:', payload);
$.post(item_save_url, payload)
.done(function(saved_item) {
if (console) console.log('Done saving', saved_item);
$('#status-bar')
.text('Saved ' + options.type + '. ' + saved_item._updated);
$form.find("input[name='_etag']").val(saved_item._etag);
if (options.done) options.done($item, saved_item);
})
.fail(function(xhr_or_response_data) {
// jQuery sends the response data (if JSON), or an XHR object (if not JSON).
if (console) console.log('Failed saving', options.type, xhr_or_response_data);
$button.removeClass('btn-default').addClass('btn-danger');
$('#status-bar').text('Failed saving. ' + xhr_or_response_data.status);
if (options.fail) options.fail($item, xhr_or_response_data);
})
.always(function() {
$button.attr('disabled', false);
$item.removeClass('processing');
if (options.always) options.always($item);
})
;
return false; // prevent synchronous POST to current page.
}
function task_save(task_id, task_url) {
return attract_form_save('task_form', 'task-' + task_id, task_url, {
done: function($task, saved_task) {
// Update the task list.
// NOTE: this is tightly linked to the HTML of the task list in for_project.jade.
$('.task-name-' + saved_task._id).text(saved_task.name).flashOnce();
$task.find('span.name').text(saved_task.name);
$task.find('span.type').text(saved_task.task_type);
$task.find('span.status').text(saved_task.properties.status.replace('_', ' '));
$task
.removeClassPrefix('status-')
.addClass('status-' + saved_task.properties.status)
.flashOnce()
;
},
fail: function($item, xhr_or_response_data) {
if (xhr_or_response_data.status == 412) {
// TODO: implement something nice here. Just make sure we don't throw
// away the user's edits. It's up to the user to handle this.
} else {
$('#task-details').html(xhr_or_response_data.responseText);
}
},
type: 'task'
});
}
function shot_save(shot_id, shot_url) {
return attract_form_save('shot_form', 'shot-' + shot_id, shot_url, {
done: function($shot, saved_shot) {
// Update the shot list.
$('.shot-name-' + saved_shot._id).text(saved_shot.name);
$shot
.removeClassPrefix('status-')
.addClass('status-' + saved_shot.properties.status)
.flashOnce()
;
},
fail: function($item, xhr_or_response_data) {
if (xhr_or_response_data.status == 412) {
// TODO: implement something nice here. Just make sure we don't throw
// away the user's edits. It's up to the user to handle this.
} else {
$('#task-details').html(xhr_or_response_data.responseText);
}
},
type: 'shot'
});
}
function task_delete(task_id, task_etag, task_delete_url) {
if (task_id === undefined || task_etag === undefined || task_delete_url === undefined) {
throw new ReferenceError("task_delete(" + task_id + ", " + task_etag + ", " + task_delete_url + ") called.");
}
$.ajax({
type: 'DELETE',
url: task_delete_url,
data: {'etag': task_etag}
})
.done(function(e) {
if (console) console.log('Task', task_id, 'was deleted.');
$('#task-details').fadeOutAndClear();
_remove_task_from_list(task_id);
})
.fail(function(xhr) {
$('#status-bar').text('Unable to delete task, code ' + xhr.status);
if (xhr.status == 412) {
alert('Someone else edited this task before you deleted it; refresh to try again.');
// TODO: implement something nice here. Just make sure we don't throw
// away the user's edits. It's up to the user to handle this.
// TODO: refresh activity feed and point user to it.
} else {
// TODO: find a better place to put this error message, without overwriting the
// task the user is looking at in-place.
$('#task-view-feed').html(xhr.responseText);
}
});
}
$(function() {
$("a.shot-link[data-shot-id]").click(function(e) {
e.preventDefault();
// delegateTarget is the thing the event hander was attached to,
// rather than the thing we clicked on.
var shot_id = e.delegateTarget.dataset.shotId;
shot_open(shot_id);
});
$("a.task-link[data-task-id]").click(function(e) {
e.preventDefault();
var task_id = e.delegateTarget.dataset.taskId;
task_open(task_id);
});
});