;(function() {
var Form = Backbone.Form,
Base = Form.editors.Base,
createTemplate = Form.helpers.createTemplate,
triggerCancellableEvent = Form.helpers.triggerCancellableEvent,
exports = {};
/**
* Additional editors that depend on jQuery UI
*/
exports.Date = Base.extend({
className: 'bbf-date',
initialize: function(options) {
Base.prototype.initialize.call(this, options);
if (!this.value) {
var date = new Date();
date.setSeconds(0);
date.setMilliseconds(0);
this.value = date;
}
},
render: function() {
var el = $(this.el);
el.html('');
var input = $('input', el);
input.datepicker({
dateFormat: 'dd/mm/yy',
showButtonPanel: true
});
//Make sure setValue of this object is called, not of any objects extending it (e.g. DateTime)
exports.Date.prototype.setValue.call(this, this.value);
return this;
},
/**
* @return {Date} Selected date
*/
getValue: function() {
var input = $('input', this.el),
date = input.datepicker('getDate');
return date;
},
setValue: function(value) {
$('input', this.el).datepicker('setDate', value);
}
});
exports.DateTime = exports.Date.extend({
className: 'bbf-datetime',
template: createTemplate(' : '),
render: function() {
function pad(n) {
return n < 10 ? '0' + n : n
}
//Render the date element first
exports.Date.prototype.render.call(this);
//Setup hour options
var hours = _.range(0, 24),
hoursOptions = [];
_.each(hours, function(hour) {
hoursOptions.push('');
});
//Setup minute options
var minsInterval = this.schema.minsInterval || 15,
mins = _.range(0, 60, minsInterval),
minsOptions = [];
_.each(mins, function(min) {
minsOptions.push('');
});
//Render time selects
$(this.el).append(this.template({
hours: hoursOptions.join(),
mins: minsOptions.join()
}));
//Store references to selects
this.$hours = $('select:eq(0)', this.el);
this.$mins = $('select:eq(1)', this.el);
//Set time
this.setValue(this.value);
return this;
},
/**
* @return {Date} Selected datetime
*/
getValue: function() {
var input = $('input', this.el),
date = input.datepicker('getDate');
date.setHours(this.$hours.val());
date.setMinutes(this.$mins.val());
date.setMilliseconds(0);
return date;
},
setValue: function(date) {
exports.Date.prototype.setValue.call(this, date);
this.$hours.val(date.getHours());
this.$mins.val(date.getMinutes());
}
});
exports.List = Base.extend({
className: 'bbf-list',
//Note: The extra div around the
is used to limit the drag area
template: createTemplate('\
\
\
'),
itemTemplate: createTemplate('\
- \
{{text}}\
\
\
\
\
\
'),
editorTemplate: createTemplate('\
\
'),
events: {
'click .bbf-list-add': 'addNewItem',
'click .bbf-list-edit': 'editItem',
'click .bbf-list-del': 'deleteItem'
},
initialize: function(options) {
Base.prototype.initialize.call(this, options);
if (!this.schema) throw "Missing required option 'schema'";
this.schema.listType = this.schema.listType || 'Text';
if (this.schema.listType == 'NestedModel' && !this.schema.model)
throw "Missing required option 'schema.model'";
},
render: function() {
var el = $(this.el);
//Main element
el.html(this.template());
//Create list
var self = this,
el = $(this.el),
data = this.value || [],
schema = this.schema,
itemToString = this.itemToString,
itemTemplate = this.itemTemplate,
listEl = $('ul', el);
_.each(data, function(itemData) {
var text = itemToString.call(self, itemData);
//Create DOM element
var li = $(itemTemplate({
id: itemData.id || '',
text: text
}));
//Attach data
$.data(li[0], 'data', itemData);
listEl.append(li);
});
//Make sortable
if (schema.sortable !== false) {
listEl.sortable({
axis: 'y',
cursor: 'move',
containment: 'parent'
});
el.addClass('bbf-list-sortable');
}
//jQuery UI buttonize
$('button.bbf-list-add', el).button({
text: false,
icons: { primary: 'ui-icon-plus' }
});
$('button.bbf-list-edit', el).button({
text: false,
icons: { primary: 'ui-icon-pencil' }
});
$('button.bbf-list-del', el).button({
text: false,
icons: { primary: 'ui-icon-trash' }
});
return this;
},
/**
* Formats an item for display in the list
* For example objects, dates etc. can have a custom
* itemToString method which says how it should be formatted.
*/
itemToString: function(data) {
if (!data) return data;
var schema = this.schema;
//If there's a specified toString use that
if (schema.itemToString)
return schema.itemToString(data);
//Otherwise check if it's NestedModel with it's own toString() method
if (this.schema.listType == 'NestedModel') {
var model = new (this.schema.model)(data);
return model.toString();
}
//Last resort, just return the data as is
return data;
},
/**
* Add a new item to the list if it is completed in the editor
*/
addNewItem: function(event) {
event.preventDefault();
var self = this;
this.openEditor(null, function(value) {
//Fire 'addItem' cancellable event
triggerCancellableEvent(self, 'addItem', [value], function() {
var text = self.itemToString(value);
//Create DOM element
var li = $(self.itemTemplate({
id: value.id || '',
text: text
}));
//Store data
$.data(li[0], 'data', value);
$('ul', self.el).append(li);
//jQuery UI buttonize
$('button.bbf-list-edit', this.el).button({
text: false,
icons: { primary: 'ui-icon-pencil' }
});
$('button.bbf-list-del', this.el).button({
text: false,
icons: { primary: 'ui-icon-trash' }
});
});
});
},
/**
* Edit an existing item in the list
*/
editItem: function(event) {
event.preventDefault();
var self = this,
li = $(event.target).closest('li'),
originalValue = $.data(li[0], 'data');
this.openEditor(originalValue, function(newValue) {
//Fire 'editItem' cancellable event
triggerCancellableEvent(self, 'editItem', [newValue], function() {
//Update display
$('.bbf-list-text', li).html(self.itemToString(newValue));
//Store data
$.data(li[0], 'data', newValue);
});
});
},
deleteItem: function(event) {
event.preventDefault();
var self = this,
li = $(event.target).closest('li'),
data = $.data(li[0], 'data');
var confirmDelete = (this.schema.confirmDelete) ? this.schema.confirmDelete : false,
confirmMsg = this.schema.confirmDeleteMsg || 'Are you sure?';
function remove() {
triggerCancellableEvent(self, 'removeItem', [data], function() {
li.remove();
});
}
if (this.schema.confirmDelete) {
if (confirm(confirmMsg)) remove();
} else {
remove();
}
},
/**
* Opens the sub editor dialog
* @param {Mixed} Data (if editing existing list item, null otherwise)
* @param {Function} Save callback. receives: value
*/
openEditor: function(data, callback) {
var self = this,
schema = this.schema,
listType = schema.listType || 'Text';
var editor = Form.helpers.createEditor(listType, {
key: '',
schema: schema,
value: data
}).render();
var container = $(this.editorTemplate());
$('.bbf-list-editor', container).html(editor.el);
var close = function() {
container.dialog('close');
editor.remove();
container.remove();
};
$(container).dialog({
resizable: false,
modal: true,
width: 500,
title: data ? 'Edit item' : 'New item',
buttons: {
'OK': function() {
callback(editor.getValue());
close();
},
'Cancel': close
}
});
},
getValue: function() {
var data = [];
$('li', this.el).each(function(index, li) {
data.push($.data(li, 'data'));
});
return data;
},
setValue: function(value) {
this.value = value;
this.render();
}
});
//Exports
_.extend(Form.editors, exports);
})();