app/assets/javascripts/issueEditor.js
/*global jQuery, HDO, Handlebars */
(function ($, HDO, Handlebars) {
HDO.issueEditor = {
create: function (opts) {
var instance = Object.create(this);
instance.root = $(opts.root);
instance.url = opts.url;
instance.issueId = opts.id;
return instance;
},
init: function () {
this.setupTemplates();
this.renderForm();
HDO.markdownEditor();
this.form = $('form.edit_issue, form.new_issue');
this.saveButton = this.root.find('button[name=save]');
this.editorSelect = this.root.find('select#issue_editor_id');
this.categorySelect = this.root.find('select#issue_category_ids');
this.positionPartySelects = this.root.find('select.position-parties');
this.newPositionButton = this.root.find('#new-position');
this.newPartyCommentButton = this.root.find('#new-party-comment');
this.tagList = this.root.find('input[name=tags]');
this.promiseSearchTab = this.root.find('#promise-search-tab');
this.promiseSpinner = this.root.find('#promise-spinner');
this.propositionConnectTab = this.root.find('#proposition-connections-tab');
this.propositionSearchTab = this.root.find('#proposition-search-tab');
this.propositionSpinner = this.root.find('#proposition-spinner');
this.errorElement = this.root.find('.error-message');
this.saveButton.click(this.save.bind(this));
this.newPositionButton.click(this.newPosition.bind(this));
this.newPartyCommentButton.click(this.newPartyComment.bind(this));
this.root.delegate('[data-expands]', 'click', this.toggleRow.bind(this));
this.editorSelect.chosen();
this.categorySelect.chosen();
this.positionPartySelects.chosen();
this.root.find('.position-form').hide();
this.root.delegate('.position-remove', 'click', this.removePosition.bind(this));
this.setupTagList();
this.setupCarts();
this.facetSearch({
baseUrl: '/promises',
root: this.promiseSearchTab,
spinner: this.promiseSpinner,
template: this.templates['promise-search-template'],
cart: this.promiseCart,
issueId: this.issueId
});
this.facetSearch({
baseUrl: '/propositions',
root: this.propositionSearchTab,
spinner: this.propositionSpinner,
template: this.templates['proposition-search-template'],
cart: this.propositionCart,
issueId: this.issueId
});
},
save: function (e) {
e.preventDefault();
this.toggleSpin();
var self, currentSection;
self = this;
currentSection = this.root.find('.in').data('key');
$.ajax({
url: this.url,
method: 'POST',
data: this.form.serialize(),
success: function (data) {
// trololol
window.location.href = currentSection ?
data.location + ('?section=' + encodeURIComponent(currentSection)) : data.location;
},
error: function (xhr) {
self.toggleSpin();
self.errorElement.html(xhr.responseText).show();
}
});
},
toggleSpin: function () {
this.root.find('.accordion-group').toggle();
$('#spinner').toggle();
},
newPosition: function (e) {
e.preventDefault();
var position, created, template;
this.newPositionId = this.newPositionId || 0;
position = {
id: --this.newPositionId,
party_ids: [],
priority: 0
};
template = this.templates['position-template'];
created = $(template(position)).prependTo('.positions');
created.addClass('new');
created.find('.expandable, .expanded').toggleClass('expandable expanded');
created.find('.position-parties').chosen();
HDO.markdownEditor({root: created});
},
newPartyComment: function (e) {
e.preventDefault();
var comment, created, template;
this.newPartyCommentId = this.newPartyCommentId || 0;
comment = {
id: --this.newPartyCommentId
};
template = this.templates['party-comment-template'];
created = $(template(comment)).prependTo('.party-comments');
created.addClass('new');
HDO.markdownEditor({root: created});
},
removePosition: function (e) {
e.preventDefault();
var target = $(e.target),
id = target.data('id'),
parties = target.data('parties'),
title = target.data('title'),
container = target.closest('.position');
container.html('<del>' + parties + '<strong>' + title + '</strong>' + '</del>');
container.append(
$('<input/>')
.attr('type', 'hidden')
.attr('name', 'positions[' + id + '][deleted]')
.val('true')
);
},
setupTagList: function () {
var el = this.tagList;
el.tagsManager({
prefilled: el.data('current-tags').split(','),
preventSubmitOnEnter: true,
typeahead: true,
typeaheadSource: el.data('all-tags').split(','),
hiddenTagListName: 'issue[tag_list]'
});
},
setupTemplates: function () {
var templates;
this.templates = templates = {};
$('script[type="text/x-handlebars-template"]').each(function () {
var el, name, partialName;
el = $(this);
name = el.data('name');
partialName = name.match(/^(\w+?)-partial$/);
partialName = partialName && partialName[1];
if (partialName) {
Handlebars.registerPartial(partialName, el.html());
} else {
templates[name] = Handlebars.compile(el.html());
}
});
Handlebars.registerHelper('selectedIfEqual', function (a, b) {
return a === b && 'selected';
});
Handlebars.registerHelper('selectedIfInclude', function (a, b) {
if (b.indexOf(a) !== -1) {
return 'selected';
}
});
},
// TODO: get rid of all the duplication
setupCarts: function () {
var toggleRow, promiseTemplate, propositionTemplate;
this.promiseCart = this.createCart($('.cart[data-type=promises]'));
this.propositionCart = this.createCart($('.cart[data-type=propositions]'));
toggleRow = this.toggleRow;
promiseTemplate = this.templates['promise-connection-template'];
propositionTemplate = this.templates['proposition-connection-template'];
this.promiseCart.on('use', function (items) {
if (items.length === 0) {
return;
}
$('a[href=#promise-connections-tab]').click();
$.ajax({
url: '/admin/issues/promises/' + items.join(','),
dataType: 'json',
success: function (data) {
$.each(data, function () {
this.status = 'related';
var created = $(promiseTemplate(this)).prependTo('#promise-connections-tab');
created.addClass('new');
});
},
error: function (xhr) {
window.alert('Uffda, noe gikk helt galt ' + xhr.status);
},
complete: function () {
// TODO: spinner
}
});
});
this.propositionCart.on('use', function (items) {
if (items.length === 0) {
return;
}
$('a[href=#proposition-connections-tab]').click();
$.ajax({
url: '/admin/issues/propositions/' + items.join(','),
dataType: 'json',
success: function (data) {
$.each(data, function () {
var created = $(propositionTemplate(this)).prependTo('#proposition-connections-tab');
created.addClass('new');
HDO.markdownEditor({root: created});
});
},
error: function (xhr) {
window.alert('Uffda, noe gikk helt galt ' + xhr.status);
},
complete: function () {
// TODO: spinner
}
});
});
},
toggleRow: function (e) {
var target, el;
target = $(e.target);
if (target.hasClass('permalink') || e.originalEvent.defaultPrevented) {
return;
}
el = target.parents('.row-fluid:first');
el.find('.expandable, .expanded').toggleClass('expandable expanded');
$(el.data('expands')).slideToggle('fast');
},
notImplemented: function (e) {
e.preventDefault();
window.alert('ikke enda');
},
renderForm: function () {
function render(template, i, e) {
var el = $(e);
el.html(template(el.data('context')));
}
$('.proposition-connection').each(render.bind(null, this.templates['proposition-connection-template']));
$('.promise-connection').each(render.bind(null, this.templates['promise-connection-template']));
$('.position').each(render.bind(null, this.templates['position-template']));
},
facetSearch: function (opts) {
var baseUrl, root, template, spinner, query, cart, issueId;
baseUrl = opts.baseUrl;
root = opts.root;
template = opts.template;
spinner = opts.spinner || $('#spinner');
query = opts.root.find('input[name=q]');
cart = opts.cart;
issueId = opts.issueId;
function prepareData(data) {
$.each(data.results, function (i, e) {
e.selected = cart.isSelected(Number(e.id));
e.connected = e.issue_ids.indexOf(issueId) !== -1;
});
return data;
}
function render(url) {
spinner.toggleClass('hidden');
$.ajax({
url: url || baseUrl,
dataType: 'json',
success: function (data) { root.html(template(prepareData(data))); },
error: function (xhr) { window.alert('Uffda, noe gikk helt galt ' + xhr.status); },
complete: function () { spinner.toggleClass('hidden'); }
});
}
function filterHandler(e) {
e.preventDefault();
render(e.target.href);
}
function queryHandler(e) {
if (e.which === 13) {
e.preventDefault();
var target = $(this),
url = decodeURI(target.data('url-template'));
url = url.replace('{query}', encodeURIComponent(target.val()));
render(url);
}
}
function toggleResult() {
var el = $(this), id;
if (el.hasClass('connected')) {
return;
}
id = el.data('id');
el.toggleClass('selected');
if (el.hasClass('selected')) {
cart.add(id);
} else {
cart.remove(id);
}
}
cart.on('clear', function () {
root.find('.search-result.selected').removeClass('selected');
});
root.delegate('.navigators a, a[data-xhr]', 'click', filterHandler);
root.delegate('input[name=q]', 'keypress', queryHandler);
root.delegate('.search-result', 'click', toggleResult);
render();
},
createCart: function (el) {
var items, template, callbacks;
items = [];
template = this.templates['shopping-cart-template'];
callbacks = {clear: [], use: []};
function invokeCallbacks(type) {
$(callbacks[type]).each(function (i, cb) {
cb(items);
});
}
function render() {
el.html(template({items: items}));
}
function isSelected(id) {
return items.indexOf(id) !== -1;
}
function add(id) {
items.push(id);
render();
}
function remove(id) {
var idx = items.indexOf(id);
if (idx > -1) {
items.splice(idx, 1);
render();
}
}
function clear() {
items = [];
invokeCallbacks('clear');
render();
}
function use() {
invokeCallbacks('use');
clear();
}
function addCallback(type, cb) {
if (typeof cb !== 'function') {
throw new TypeError('expected function, got ' + typeof cb);
}
if (Object.keys(callbacks).indexOf(type) === -1) {
throw new Error('invalid callback type ' + type);
}
callbacks[type].push(cb);
}
el.delegate('a[data-action=use]', 'click', function (e) {
e.preventDefault();
use();
});
el.delegate('a[data-action=clear]', 'click', function (e) {
e.preventDefault();
clear();
});
render();
return {
isSelected: isSelected,
add: add,
remove: remove,
on: addCallback
};
}
};
}(jQuery, HDO, Handlebars));