311 lines
12 KiB
JavaScript
311 lines
12 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.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 ($.isFunction(this.options.source)) {
|
|
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 = [], item, iterateItem;
|
|
if(!data || typeof data === 'string') {
|
|
return null;
|
|
}
|
|
|
|
if($.isArray(data)) { //array
|
|
/*
|
|
function to iterate inside item of array if item is object.
|
|
Caclulates count of keys in item and store in obj.
|
|
*/
|
|
iterateItem = function (k, v) {
|
|
obj = {value: k, text: v};
|
|
if(count++ >= 2) {
|
|
return false;// exit from `each` if item has more than one key.
|
|
}
|
|
};
|
|
|
|
for(var i = 0; i < data.length; i++) {
|
|
item = data[i];
|
|
if(typeof item === 'object') {
|
|
count = 0; //count of keys inside item
|
|
$.each(item, iterateItem);
|
|
//case: [{val1: 'text1'}, {val2: 'text2} ...]
|
|
if(count === 1) {
|
|
result.push(obj);
|
|
//case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]
|
|
} else if(count > 1) {
|
|
//removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text')
|
|
if(item.children) {
|
|
item.children = this.makeArray(item.children);
|
|
}
|
|
result.push(item);
|
|
}
|
|
} else {
|
|
//case: ['text1', 'text2' ...]
|
|
result.push({value: item, text: item});
|
|
}
|
|
}
|
|
} else { //case: {val1: 'text1', val2: 'text2, ...}
|
|
$.each(data, function (k, v) {
|
|
result.push({value: k, text: v});
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
|
|
option: function(key, value) {
|
|
this.options[key] = value;
|
|
if(key === 'source') {
|
|
this.sourceData = null;
|
|
}
|
|
if(key === 'prepend') {
|
|
this.prependData = null;
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
|
/**
|
|
Source data for list.
|
|
If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`
|
|
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).
|
|
|
|
Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).
|
|
`[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`
|
|
|
|
|
|
@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.
|
|
Usefull for editable column in grid to prevent extra requests.
|
|
|
|
@property sourceCache
|
|
@type boolean
|
|
@default true
|
|
@since 1.2.0
|
|
**/
|
|
sourceCache: true
|
|
});
|
|
|
|
$.fn.editabletypes.list = List;
|
|
|
|
}(window.jQuery));
|