You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

410 lines
16 KiB

/**
* @typedef {object} JQPropertyGridOptions
* @property {object} meta - A metadata object describing the obj properties
*/
/* jshint -W089 */
(function ($) {// jscs:ignore requireNamedUnassignedFunctions
var OTHER_GROUP_NAME = 'Other';
var GET_VALS_FUNC_KEY = 'pg.getValues';
var HIDDEN_GROUP = 'hidden_group';
var NOT_SAVE_GROUP = 'not_save_group';
var pgIdSequence = 0;
/**
* Generates the property grid
* @param {object} obj - The object whose properties we want to display //要显示的对象
* @param {JQPropertyGridOptions} options - Options object for the component //组件的选项对象
*/
$.fn.jqPropertyGrid = function (obj, options) {
// Check if the user called the 'get' function (to get the values back from the grid).
//检查用户是否呼叫了'get' 函数,,从表格中读取数据
if (typeof obj === 'string' && obj === 'get') { //如果第一个参数是字符串类型,并且字面值='get'
if (typeof this.data(GET_VALS_FUNC_KEY) === 'function') { //如果GET_VALS_FUNC_KEY是函数类型
return this.data(GET_VALS_FUNC_KEY)(); //调用这个函数
}
return null; //否则返回null
} else if (typeof obj === 'string') { //字符类型返回错误信息
console.error('jqPropertyGrid got invalid option:', obj);
return;
} else if (typeof obj !== 'object' || obj === null) { //不是对象返回错误误信息
console.error('jqPropertyGrid must get an object in order to initialize the grid.');
return;
}
// Normalize options
options = options && typeof options === 'object' ? options : {}; //如果options不是对象,则返回一个空对象
options.meta = options.meta && typeof options.meta === 'object' ? options.meta : {};//判断 options.met是不是对象,不是对象返回空对象
// 以下为创建属性网格操作
var meta = options.meta; //描述对象属性的元数据
var propertyRowsHTML = {OTHER_GROUP_NAME: ''}; //属性列表的HTML代码
var groupsHeaderRowHTML = {}; //组的标记头
var postCreateInitFuncs = []; //创建初始化函数?
var getValueFuncs = {}; //获取值
var pgId = 'pg' + (pgIdSequence++); //生成属性网格内部使用的id
var customTypes = options.customTypes || {}; //默认类型为object
var currGroup; //当前属性组?
for (var prop in obj) {
// Skip if this is not a direct property, a function, or its meta says it's non browsable
//obj是要显示对象,flow_node对象,如果obj没有这个属性,或者这个属性是个函数,或者这个属性的元数据不存在或可见性为false ,以上不在属性网格显示
if (!obj.hasOwnProperty(prop) || typeof obj[prop] === 'function' || (meta[prop] && meta[prop].browsable === false)) {
continue;
}
// Check what is the group of the current property or use the default 'Other' group
//检查当前属性是否有元数据,没有则归到OTHER组
currGroup = (meta[prop] && meta[prop].group) || OTHER_GROUP_NAME;
// If this is the first time we run into this group create the group row
//如果是第一次向这个组里添加组元素执行以下代码,如果这个组不是Other组,且为空,获取这个组
if (currGroup !== OTHER_GROUP_NAME && currGroup !== NOT_SAVE_GROUP && !groupsHeaderRowHTML[currGroup]) {
groupsHeaderRowHTML[currGroup] = getGroupHeaderRowHtml(currGroup);
}
if (currGroup == 'HIDDEN_GROUP') {
groupsHeaderRowHTML[currGroup] = getGroupHeaderRowHtml(currGroup);
}
// Initialize the group cells html 初始化这个组成员html
propertyRowsHTML[currGroup] = propertyRowsHTML[currGroup] || '';
// 添加html代码当前单元格
// console.log(prop+":"+ obj[prop]);
propertyRowsHTML[currGroup] += getPropertyRowHtml(pgId, prop, obj[prop], meta[prop], postCreateInitFuncs, getValueFuncs, customTypes, currGroup);
}
//以下生成表格
var innerHTML = '<table class="pgTable">';
for (var group in groupsHeaderRowHTML) {
// Add the group row 组列
innerHTML += groupsHeaderRowHTML[group];
// Add the group cells 组单元格
innerHTML += propertyRowsHTML[group];
}
//添加Other组
if (propertyRowsHTML[OTHER_GROUP_NAME]) {
innerHTML += getGroupHeaderRowHtml(OTHER_GROUP_NAME);
innerHTML += propertyRowsHTML[OTHER_GROUP_NAME];
}
// 闭合table标签
innerHTML += '</table>';
this.html(innerHTML);
/* $("pgTextArea").cleditor({
width: 240, // height not including margins, borders or padding
controls: // controls to add to the toolbar
"bold italic underline strikethrough | font size " +
"style | color highlight removeformat | "
}).change(function(){
$('input[id^="pg"]').trigger("keyup");
});*/
$("textarea").change(function () {
$('input[id^="pg"]').trigger("keyup");
});
// Call the post init functions
for (var i = 0; i < postCreateInitFuncs.length; ++i) {
if (typeof postCreateInitFuncs[i] === 'function') {
postCreateInitFuncs[i]();
// just in case make sure we are not holding any reference to the functions
postCreateInitFuncs[i] = null;
}
}
// Create a function that will return tha values back from the property grid
var getValues = function () {
var result = {};
for (var prop in getValueFuncs) {
if (typeof getValueFuncs[prop] !== 'function') {
continue;
}
result[prop] = getValueFuncs[prop]();
}
return result;
};
this.data(GET_VALS_FUNC_KEY, getValues);
};
/**
*获取分组头部信息的html文档
* @param {string} displayName - The group display name
*/
function getGroupHeaderRowHtml(displayName) {
if (displayName != 'hidden_group') {
return '<table class="pgSubTable" ><tr class="pgGroupRow"><td class="pgGroupHeadCell" ><a href="#" onclick="hidde_pgRow(this)">-</a></td><td colspan="2" class="pgGroupCell">' + displayName + '</td></tr>';
} else {
return '<table class="pgSubTable"><tr class="pgGroupRow" style="display:none"><td class="pgGroupHeadCell" ><a href="#" onclick="hidde_pgRow(this)">-</a></td><td colspan="2" class="pgGroupCell" style="display:none">' + displayName + '</td></tr>';
}
}
/**
* Gets the html of a specific property row
* @param {string} pgId - The property-grid id being rendered
* @param {string} name - The property name
* @param {*} value - The current property value
* @param {object} meta - A metadata object describing this property
* @param {function[]} [postCreateInitFuncs] - An array to fill with functions to run after the grid was created
* @param {object.<string, function>} [getValueFuncs] - A dictionary where the key is the property name and the value is a function to retrieve the propery selected value
* @param {object} customTypes - an object describing additionnal types renderers
*/
function getPropertyRowHtml(pgId, name, value, meta, postCreateInitFuncs, getValueFuncs, customTypes, currGroup) {
if (!name) {
return '';
}
meta = meta || {};
// We use the name in the meta if available
var displayName = meta.name || name;
var type = meta.type || '';
var elemId = pgId + name;
var valueHTML;
// check if type is registered in customTypes
var isCustomType = false;
for (var customType in customTypes) {
if (type === customType) {
isCustomType = customTypes[customType];
}
}
// If value was handled by custom type
if (isCustomType !== false) {
valueHTML = isCustomType.html(elemId, name, value, meta);
if (getValueFuncs) {
if (isCustomType.hasOwnProperty('makeValueFn')) {
getValueFuncs[name] = isCustomType.makeValueFn(elemId, name, value, meta);
} else if (isCustomType.hasOwnProperty('valueFn')) {
getValueFuncs[name] = isCustomType.valueFn;
} else {
getValueFuncs[name] = function () {
return $('#' + elemId).val();
};
}
}
}
// If boolean create checkbox
else if (type === 'boolean' || (type === '' && typeof value === 'boolean')) {
valueHTML = '<input type="checkbox" id="' + elemId + '" value="' + name + '"' + (value ? ' checked' : '') + ' />';
if (getValueFuncs) {
getValueFuncs[name] = function () {
return $('#' + elemId).prop('checked');
};
}
// If options create drop-down list
} else if (type === 'options' && Array.isArray(meta.options)) {
valueHTML = getSelectOptionHtml(elemId, value, meta.options);
if (getValueFuncs) {
getValueFuncs[name] = function () {
return $('#' + elemId).val();
};
}
// If number and a jqueryUI spinner is loaded use it
} else if (typeof $.fn.spinner === 'function' && (type === 'number' || (type === '' && typeof value === 'number'))) {
valueHTML = '<input type="text" id="' + elemId + '" value="' + value + '" style="width:50px" />';
if (postCreateInitFuncs) {
postCreateInitFuncs.push(initSpinner(elemId, meta.options));
}
if (getValueFuncs) {
getValueFuncs[name] = function () {
return $('#' + elemId).spinner('value');
};
}
// If color and we have the spectrum color picker use it
} else if (type === 'color' && typeof $.fn.spectrum === 'function') {
valueHTML = '<input type="text" id="' + elemId + '" />';
if (postCreateInitFuncs) {
postCreateInitFuncs.push(initColorPicker(elemId, value, meta.options));
}
if (getValueFuncs) {
getValueFuncs[name] = function () {
return $('#' + elemId).spectrum('get').toHexString();
};
}
// If label (for read-only)
} else if (type === 'label') {
if (typeof meta.description === 'string' && meta.description) {
valueHTML = '<label for="' + elemId + '" title="' + meta.description + '">' + value + '</label>';
} else {
valueHTML = '<label for="' + elemId + '">' + value + '</label>';
}
// If hidden_Item (for read-only)
} else if (currGroup == 'hidden_group') {
valueHTML = '<input type="text" style="display:none" id="' + elemId + '" value="' + value + '"></input>';
if (getValueFuncs) {
getValueFuncs[name] = function () {
return value
};
}
} else if (type == 'text_area') {
valueHTML = '<textarea id="' + elemId + '" class="pgTextArea" rows="10" cols=22 style="resize:none" >' + value + '</textarea >';
if (getValueFuncs) {
getValueFuncs[name] = function () {
return $('#' + elemId).val();
};
}
} else {
whensetassigner = "";
if (elemId.indexOf('assigner') >= 0) {
whensetassigner = "readyonly"
}
valueHTML = '<input type="text" ' + whensetassigner + ' id="' + elemId + '" value="' + value + '" />';
if (getValueFuncs) {
getValueFuncs[name] = function () {
// if (elemId.indexOf('assigner') >= 0) {
// return $('#' + elemId).val();
// }else{
return $('#' + elemId).val();
// }
};
}
}
if (typeof meta.description === 'string' && meta.description &&
(typeof meta.showHelp === 'undefined' || meta.showHelp)) {
displayName += '<span class="pgTooltip" title="' + meta.description + '">[?]</span>';
}
if (currGroup == 'hidden_group') {
return '<tr class="pgRow" style="display:none" ><td class="td_head" ></td>' +
'<td class="pgCell" style="display:none" >' + displayName + '</td>' +
'<td class="pgCell" style="display:none" >' + valueHTML + '</td></tr>';
} else {
return '<tr class="pgRow"><td class="td_head"></td>' +
'<td class="pgCell">' + displayName + '</td>' +
'<td class="pgCell">' + valueHTML + '</td></tr>';
}
}
/**
* Gets a select-option (dropdown) html
* @param {string} id - The select element id
* @param {string} [selectedValue] - The current selected value
* @param {*[]} options - An array of option. An element can be an object with value/text pairs, or just a string which is both the value and text
* @returns {string} The select element html
*/
function getSelectOptionHtml(id, selectedValue, options) {
id = id || '';
console.log('1');
console.log(selectedValue);
selectedValue = selectedValue || '';
options = options || [];
var html = '<select';
if (id) {
html += ' id="' + id + '"';
}
html += ' >';
var text;
var value;
for (var i = 0; i < options.length; i++) {
value = typeof options[i] === 'object' ? options[i].value : options[i];
text = typeof options[i] === 'object' ? options[i].text : options[i];
html += '<option value="' + value + '"' + (selectedValue === value ? ' selected>' : '>');
html += text + '</option>';
}
html += '</select>';
return html;
}
/**
* Gets an init function to a number textbox
* @param {string} id - The number textbox id
* @param {object} [options] - The spinner options
* @returns {function}
*/
function initSpinner(id, options) {
if (!id) {
return null;
}
// Copy the options so we won't change the user "copy"
var opts = {};
$.extend(opts, options);
// Add a handler to the change event to verify the min/max (only if not provided by the user)
opts.change = typeof opts.change === 'undefined' ? onSpinnerChange : opts.change;
return function onSpinnerInit() {
$('#' + id).spinner(opts);
};
}
/**
* Gets an init function to a color textbox
* @param {string} id - The color textbox id
* @param {string} [color] - The current color (e.g #000000)
* @param {object} [options] - The color picker options
* @returns {function}
*/
function initColorPicker(id, color, options) {
if (!id) {
return null;
}
var opts = {};
$.extend(opts, options);
if (typeof color === 'string') {
opts.color = color;
}
return function onColorPickerInit() {
$('#' + id).spectrum(opts);
};
}
/**
* Handler for the spinner change event
*/
function onSpinnerChange() {
var $spinner = $(this);
var value = $spinner.spinner('value');
// If the value is null and the real value in the textbox is string we empty the textbox
if (value === null && typeof $spinner.val() === 'string') {
$spinner.val('');
return;
}
// Now check that the number is in the min/max range.
var min = $spinner.spinner('option', 'min');
var max = $spinner.spinner('option', 'max');
if (typeof min === 'number' && this.value < min) {
this.value = min;
return;
}
if (typeof max === 'number' && this.value > max) {
this.value = max;
}
}
})(window.$);