/** * @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 = ''; 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 += '
'; 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 ''; } else { return '
-' + displayName + '
'; } } /** * 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.} [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 = ''; 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 = ''; 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 = ''; 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 = ''; } else { valueHTML = ''; } // If hidden_Item (for read-only) } else if (currGroup == 'hidden_group') { valueHTML = ''; if (getValueFuncs) { getValueFuncs[name] = function () { return value }; } } else if (type == 'text_area') { valueHTML = ''; if (getValueFuncs) { getValueFuncs[name] = function () { return $('#' + elemId).val(); }; } } else { whensetassigner = ""; if (elemId.indexOf('assigner') >= 0) { whensetassigner = "readyonly" } valueHTML = ''; 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 += '[?]'; } if (currGroup == 'hidden_group') { return '' + '' + ''; } else { return '' + '' + ''; } } /** * 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 = '' : '>'); html += text + ''; } html += ''; 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.$);
' + displayName + '' + valueHTML + '