Files
x-editable/src/inputs/list.js

289 lines
10 KiB
JavaScript

/**
List - abstract class for inputs that have source option loaded from js array or via ajax
@class list
@extends abstractinput
**/
(function ($) {
var List = function (options) {
};
$.fn.editableutils.inherit(List, $.fn.editabletypes.abstractinput);
$.extend(List.prototype, {
render: function () {
var deferred = $.Deferred();
this.error = null;
this.sourceData = null;
this.prependData = null;
this.onSourceReady(function () {
this.renderList();
deferred.resolve();
}, function () {
this.error = this.options.sourceError;
deferred.resolve();
});
return deferred.promise();
},
html2value: function (html) {
return null; //can't set value by text
},
value2html: function (value, element, display, response) {
var deferred = $.Deferred(),
success = function () {
if(typeof display === 'function') {
//custom display method
display.call(element, value, this.sourceData, response);
} else {
this.value2htmlFinal(value, element);
}
deferred.resolve();
};
//for null value just call success without loading source
if(value === null) {
success.call(this);
} else {
this.onSourceReady(success, function () { deferred.resolve(); });
}
return deferred.promise();
},
// ------------- additional functions ------------
onSourceReady: function (success, error) {
//if allready loaded just call success
if($.isArray(this.sourceData)) {
success.call(this);
return;
}
// try parse json in single quotes (for double quotes jquery does automatically)
try {
this.options.source = $.fn.editableutils.tryParseJson(this.options.source, false);
} catch (e) {
error.call(this);
return;
}
//loading from url
if (typeof this.options.source === 'string') {
//try to get from cache
if(this.options.sourceCache) {
var cacheID = this.options.source,
cache;
if (!$(document).data(cacheID)) {
$(document).data(cacheID, {});
}
cache = $(document).data(cacheID);
//check for cached data
if (cache.loading === false && cache.sourceData) { //take source from cache
this.sourceData = cache.sourceData;
this.doPrepend();
success.call(this);
return;
} else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
cache.callbacks.push($.proxy(function () {
this.sourceData = cache.sourceData;
this.doPrepend();
success.call(this);
}, this));
//also collecting error callbacks
cache.err_callbacks.push($.proxy(error, this));
return;
} else { //no cache yet, activate it
cache.loading = true;
cache.callbacks = [];
cache.err_callbacks = [];
}
}
//loading sourceData from server
$.ajax({
url: this.options.source,
type: 'get',
cache: false,
dataType: 'json',
success: $.proxy(function (data) {
if(cache) {
cache.loading = false;
}
this.sourceData = this.makeArray(data);
if($.isArray(this.sourceData)) {
if(cache) {
//store result in cache
cache.sourceData = this.sourceData;
//run success callbacks for other fields waiting for this source
$.each(cache.callbacks, function () { this.call(); });
}
this.doPrepend();
success.call(this);
} else {
error.call(this);
if(cache) {
//run error callbacks for other fields waiting for this source
$.each(cache.err_callbacks, function () { this.call(); });
}
}
}, this),
error: $.proxy(function () {
error.call(this);
if(cache) {
cache.loading = false;
//run error callbacks for other fields
$.each(cache.err_callbacks, function () { this.call(); });
}
}, this)
});
} else { //options as json/array/function
if (typeof this.options.source === 'function') {
this.sourceData = this.makeArray(this.options.source());
} else {
this.sourceData = this.makeArray(this.options.source);
}
if($.isArray(this.sourceData)) {
this.doPrepend();
success.call(this);
} else {
error.call(this);
}
}
},
doPrepend: function () {
if(this.options.prepend === null || this.options.prepend === undefined) {
return;
}
if(!$.isArray(this.prependData)) {
//try parse json in single quotes
this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true);
if (typeof this.options.prepend === 'string') {
this.options.prepend = {'': this.options.prepend};
}
if (typeof this.options.prepend === 'function') {
this.prependData = this.makeArray(this.options.prepend());
} else {
this.prependData = this.makeArray(this.options.prepend);
}
}
if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
this.sourceData = this.prependData.concat(this.sourceData);
}
},
/*
renders input list
*/
renderList: function() {
// this method should be overwritten in child class
},
/*
set element's html by value
*/
value2htmlFinal: function(value, element) {
// this method should be overwritten in child class
},
/**
* convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
*/
makeArray: function(data) {
var count, obj, result = [], iterateEl;
if(!data || typeof data === 'string') {
return null;
}
if($.isArray(data)) { //array
iterateEl = function (k, v) {
obj = {value: k, text: v};
if(count++ >= 2) {
return false;// exit each if object has more than one value
}
};
for(var i = 0; i < data.length; i++) {
if(typeof data[i] === 'object') {
count = 0;
$.each(data[i], iterateEl);
if(count === 1) {
result.push(obj);
} else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) {
result.push(data[i]);
} else {
//data contains incorrect objects
}
} else {
result.push({value: data[i], text: data[i]});
}
}
} else { //object
$.each(data, function (k, v) {
result.push({value: k, text: v});
});
}
return result;
}
});
List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
/**
Source data for list.
If **array** - it should be in format: `[{value: 1, text: "text1"}, {...}]`
For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order.
If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option.
If **function**, it should return data in format above (since 1.4.0).
@property source
@type string | array | object | function
@default null
**/
source:null,
/**
Data automatically prepended to the beginning of dropdown list.
@property prepend
@type string | array | object | function
@default false
**/
prepend:false,
/**
Error message when list cannot be loaded (e.g. ajax error)
@property sourceError
@type string
@default Error when loading list
**/
sourceError: 'Error when loading list',
/**
if <code>true</code> and source is **string url** - results will be cached for fields with the same source and name.
Usefull for editable grids.
@property sourceCache
@type boolean
@default true
@since 1.2.0
**/
sourceCache: true
});
$.fn.editabletypes.list = List;
}(window.jQuery));