text: /**
   * The extending functionalities on datagrid columns.
   */
  (function ($) {
  	$.extend($.fn.datagrid.defaults, {
  		onBeforeDragColumn: function (field) {
  		},
  		onStartDragColumn: function (field) {
  		},
  		onStopDragColumn: function (field) {
  		},
  		onBeforeDropColumn: function (toField, fromField, point) {
  		},
  		onDropColumn: function (toField, fromField, point) {
  		}	// point: 'before','after'
  	});
  	$.extend($.fn.treegrid.defaults, {
  		onBeforeDragColumn: function (field) {
  		},
  		onStartDragColumn: function (field) {
  		},
  		onStopDragColumn: function (field) {
  		},
  		onBeforeDropColumn: function (toField, fromField, point) {
  		},
  		onDropColumn: function (toField, fromField, point) {
  		}	// point: 'before','after'
  	});
  	$.extend($.fn.datagrid.methods, {
  		_getPluginName: function (jq) {
  			if (jq.data('treegrid')) {
  				return 'treegrid';
  			} else {
  				return 'datagrid';
  			}
  		},
  		freezeColumn: function (jq, field) {
  			return jq.each(function () {
  				var dg = $(this);
  				var plugin = dg.datagrid('_getPluginName');
  				var fields = dg[plugin]('getColumnFields');
  				var index = $.inArray(field, fields);
  				if (index >= 0) {
  					var col = dg[plugin]('getColumnOption', field);
  					col.originalIndex = $.inArray(field, fields);
  					dg[plugin]('moveColumn', {
  						field: field
  					});
  				}
  			})
  		},
  		unfreezeColumn: function (jq, field) {
  			return jq.each(function () {
  				var dg = $(this);
  				var plugin = dg.datagrid('_getPluginName');
  				var fields = dg[plugin]('getColumnFields', true);
  				var index = $.inArray(field, fields);
  				if (index >= 0) {
  					var col = dg[plugin]('getColumnOption', field);
  					var toFields = dg[plugin]('getColumnFields', false);
  					dg[plugin]('moveColumn', {
  						field: field,
  						before: toFields[col.originalIndex]
  					});
  				}
  			})
  		},
  		/**
  		 * move the column
  		 * $('#dg').datagrid('moveColumn', {
  		 *		field: 'itemid',
  		 *		before: 'listprice'
  		 *		// after: 'listprice'
  		 * })
  		 */
  		moveColumn: function (jq, param) {
  			return jq.each(function () {
  				var dg = $(this);
  				var plugin = dg.datagrid('_getPluginName');
  				var opts = dg[plugin]('options');
  				var toField = param.before || param.after;
  				var col = dg[plugin]('getColumnOption', param.field);
  				if (col) {
  					var index = getIndex(param.field, true);
  					if (index >= 0) {
  						opts.frozenColumns[0].splice(index, 1);
  						if (!toField) {
  							opts.columns[0].push(col);
  							recreate();
  							return;
  						}
  					} else {
  						index = getIndex(param.field, false);
  						opts.columns[0].splice(index, 1);
  						if (!toField) {
  							if (opts.frozenColumns[0]) {
  								opts.frozenColumns[0].push(col);
  							} else {
  								opts.frozenColumns[0] = [col];
  							}
  							recreate();
  							return;
  						}
  					}
  					var toIndex = getIndex(toField, true);
  					if (toIndex >= 0) {
  						opts.frozenColumns[0].splice(toIndex + (param.before ? 0 : 1), 0, col);
  					} else {
  						toIndex = getIndex(toField, false);
  						opts.columns[0].splice(toIndex + (param.before ? 0 : 1), 0, col);
  					}
  					recreate();
  				}
  				function getIndex(field, frozen) {
  					var fields = dg[plugin]('getColumnFields', frozen);
  					return $.inArray(field, fields);
  				}
  
  				function recreate() {
  					var url = opts.url;
  					opts.url = null;
  					var data = dg[plugin]('getData');
  					dg[plugin]({
  						data: null
  					});
  					dg[plugin]('loadData', data);
  					opts.url = url;
  				}
  			})
  		},
  		reorderColumns: function (jq, fields) {
  			return jq.each(function () {
  				var dg = $(this);
  				var plugin = dg.datagrid('_getPluginName');
  				var opts = dg[plugin]('options');
  				var f1 = dg[plugin]('getColumnFields', true);
  				var f2 = dg[plugin]('getColumnFields', false);
  				var ff = f1.concat(f2);
  				var cols = [];
  				for (var i = 0; i < fields.length; i++) {
  					cols.push(dg[plugin]('getColumnOption', fields[i]));
  				}
  				for (var i = 0; i < ff.length; i++) {
  					if ($.inArray(ff[i], fields) == -1) {
  						cols.push(dg[plugin]('getColumnOption', ff[i]));
  					}
  				}
  				var frozenCols = cols.splice(0, f1.length);
  
  				var url = opts.url;
  				opts.url = null;
  				var data = dg[plugin]('getData');
  				dg[plugin]({
  					data: null,
  					frozenColumns: [frozenCols],
  					columns: [cols]
  				});
  				dg[plugin]('loadData', data);
  				opts.url = url;
  			});
  		}
  	})
  })(jQuery);
  
  (function ($) {
  	function initCss() {
  		var css = $('#datagrid-columnmoving');
  		if (!css.length) {
  			$('head').append(
  				'<style id="datagrid-columnmoving">' +
  				'.datagrid-header .datagrid-moving-left{border-left:1px solid red}' +
  				'.datagrid-header .datagrid-moving-left .datagrid-cell{margin-left:-1px}' +
  				'.datagrid-header .datagrid-moving-right{border-right:1px solid red;border-left:0}' +
  				'</style>'
  			);
  		}
  	}
  
  	function moving(target) {
  		var plugin = $(target).datagrid('_getPluginName');
  		var opts = $(target)[plugin]('options');
  		var cells = $(target).datagrid('getPanel').find('div.datagrid-header td[field]:not(:has(.datagrid-filter-c))');
  		cells.draggable({
  			revert: true,
  			cursor: 'pointer',
  			edge: 5,
  			proxy: function (source) {
  				var p = $('<div class="tree-node-proxy tree-dnd-no" style="position:absolute;border:1px solid #ff0000"/>').appendTo('body');
  				p.html($(source).text());
  				p.hide();
  				return p;
  			},
  			onBeforeDrag: function (e) {
  				var field = $(this).attr('field');
  				if (opts.onBeforeDragColumn.call(target, field) == false) {
  					return false;
  				}
  				if (e.which != 1) {
  					return false;
  				}
  				e.data.startLeft = $(this).offset().left;
  				e.data.startTop = $(this).offset().top;
  			},
  			onStartDrag: function (e) {
  				opts.onStartDragColumn.call(target, $(this).attr('field'));
  				$(this).draggable('proxy').css({
  					left: -10000,
  					top: -10000
  				});
  			},
  			onStopDrag: function () {
  				opts.onStopDragColumn.call(target, $(this).attr('field'));
  			},
  			onDrag: function (e) {
  				var x1 = e.pageX, y1 = e.pageY, x2 = e.data.startX, y2 = e.data.startY;
  				var d = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
  				if (d > 3) {	// when drag a little distance, show the proxy object
  					$(this).draggable('proxy').show().css({
  						left: e.pageX + 15,
  						top: e.pageY + 15
  					});
  					this.x = e.pageX;
  					this.y = e.pageY;
  				}
  				return false;
  			}
  		}).droppable({
  			accept: 'td[field]:not(:has(.datagrid-filter-c))',
  			onDragOver: function (e, source) {
  				$(source).draggable('proxy').removeClass('tree-dnd-no').addClass('tree-dnd-yes');
  				var width = $(this).outerWidth();
  				var x = $(this).offset().left;
  				var y = $(this).offset().top;
  				$(this).removeClass('datagrid-moving-left datagrid-moving-right');
  
  				//NOTE: direction
  				if (source.x < x + width / 2) {
  					$(this).addClass('datagrid-moving-left');
  				} else {
  					$(this).addClass('datagrid-moving-right');
  				}
  			},
  			onDragLeave: function (e, source) {
  				$(source).draggable('proxy').removeClass('tree-dnd-yes').addClass('tree-dnd-no');
  				$(this).removeClass('datagrid-moving-left datagrid-moving-right');
  			},
  			onDrop: function (e, source) {
  				var fromField = $(source).attr('field');
  				var toField = $(this).attr('field');
  				var inserted = $(this).hasClass('datagrid-moving-left'); //NOTE: direction
  				$(this).removeClass('datagrid-moving-left datagrid-moving-right');
  
  				var point = inserted ? 'before' : 'after';
  				if (opts.onBeforeDropColumn.call(target, toField, fromField, point) == false) {
  					return;
  				}
  				var param = {field: fromField};
  				param[point] = toField;
  				$(target)[plugin]('moveColumn', param);
  				$(target)[plugin]('columnMoving');
  				opts.onDropColumn.call(target, toField, fromField, point);
  			}
  		})
  	}
  
  	$.extend($.fn.datagrid.methods, {
  		columnMoving: function (jq) {
  			initCss();
  			return jq.each(function () {
  				moving(this);
  			});
  		}
  	});
  
  })(jQuery);
  
  /**
   * The data group functionality on treegrid
   * $('#tg').treegrid('groupData', {
   *		data: data,
   *		fields: ['country','city'],
   *		groupHeader: '#fc'
   * });
   */
  (function ($) {
  	function initCss() {
  		var css = $('#treegrid-groupcolumns');
  		if (!css.length) {
  			$('head').append(
  				'<style id="treegrid-groupcolumns">' +
  				'.tg-group-header{height:36px;box-sizing:border-box;padding:4px;}' +
  				'.tg-group-field{position:relative;display:inline-block;line-height:24px;padding:0 18px 0 4px;border:1px solid #ccc;margin:0 2px;vertical-align:middle;}' +
  				'.tg-group-close{position:absolute;display:inline-block;width:16px;height:16px;line-height:16px;right:0;top:50%;margin-top:-8px;cursor:pointer;text-align:center}' +
  				'</style>'
  			);
  		}
  	}
  
  	function groupData(target, param) {
  		var data = param.data;
  		var fields = param.fields;
  		var opts = $(target).treegrid('options');
  		opts.groupFields = fields || [];
  		opts.groupData = data;
  		opts.groupHeader = param.groupHeader;
  
  		var sindex = 1;
  		var ids = [];
  		if (fields && fields.length) {
  			$(target).treegrid('loadData', _convert(data, fields[0]));
  		} else {
  			$(target).treegrid('loadData', data);
  		}
  
  		if (param.groupHeader) {
  			dnd(target);
  			showGroupHeader(target);
  		}
  
  		function _convert(rows, field) {
  			var srows = [];
  			var keys = _keys(rows, field);
  			for (var i = 0; i < keys.length; i++) {
  				var key = keys[i];
  				var rr = _filter(rows, field, key);
  				var srow = {};
  				var id = 'tg_groupid_' + sindex++;
  				ids.push(id);
  				srow[opts.idField] = id;
  				srow[opts.treeField] = key;
  				srow.children = rr;
  				srows.push(srow);
  				var f = _nextField(field);
  				if (f) {
  					srow.children = _convert(rr, f);
  				}
  			}
  			return srows;
  		}
  
  		function _nextField(field) {
  			for (var i = 0; i < fields.length; i++) {
  				if (fields[i] == field) {
  					return fields[i + 1];
  				}
  			}
  		}
  
  		function _filter(rows, field, value) {
  			return $.grep(rows, function (row) {
  				return row[field] == value;
  			});
  		}
  
  		function _keys(rows, field) {
  			var keys = [];
  			for (var i = 0; i < rows.length; i++) {
  				var key = String(rows[i][field]);
  				if ($.inArray(key, keys) == -1) {
  					keys.push(key);
  				}
  			}
  			return keys;
  		}
  	}
  
  	function dnd(target) {
  		var opts = $(target).treegrid('options');
  		var cells = $(target).treegrid('getPanel').find('.datagrid-header td[field]:not(:has(.datagrid-filter-c))');
  		cells.draggable({
  			revert: true,
  			cursor: 'pointer',
  			edge: 5,
  			proxy: function (source) {
  				var p = $('<div class="tree-node-proxy tree-dnd-no" style="position:absolute;border:1px solid #ff0000"/>').appendTo('body');
  				p.html($(source).text());
  				p.hide();
  				return p;
  			},
  			onBeforeDrag: function (e) {
  				var field = $(this).attr('field');
  				if (e.which != 1) {
  					return false;
  				}
  				e.data.startLeft = $(this).offset().left;
  				e.data.startTop = $(this).offset().top;
  			},
  			onStartDrag: function (e) {
  				$(this).draggable('proxy').css({
  					left: -10000,
  					top: -10000
  				});
  			},
  			onDrag: function (e) {
  				var x1 = e.pageX, y1 = e.pageY, x2 = e.data.startX, y2 = e.data.startY;
  				var d = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
  				if (d > 3) {	// when drag a little distance, show the proxy object
  					$(this).draggable('proxy').show().css({
  						left: e.pageX + 15,
  						top: e.pageY + 15
  					});
  					this.x = e.pageX;
  					this.y = e.pageY;
  				}
  				return false;
  			}
  
  		});
  		var cc = $(opts.groupHeader);
  		cc.droppable({
  			accept: 'td[field]:not(:has(.datagrid-filter-c))',
  			onDragOver: function (e, source) {
  				$(source).draggable('proxy').removeClass('tree-dnd-no').addClass('tree-dnd-yes');
  			},
  			onDragLeave: function (e, source) {
  				$(source).draggable('proxy').removeClass('tree-dnd-yes').addClass('tree-dnd-no');
  			},
  			onDrop: function (e, source) {
  				var field = $(source).attr('field');
  				if ($.inArray(field, opts.groupFields) == -1) {
  					opts.groupFields.push(field);
  				}
  				$(target).treegrid('groupData', {
  					data: opts.groupData,
  					fields: opts.groupFields,
  					groupHeader: opts.groupHeader
  				});
  			}
  		});
  	}
  
  	function showGroupHeader(target) {
  		var dd = [];
  		var opts = $(target).treegrid('options');
  		var fields = $(target).treegrid('options').groupFields;
  		for (var i = 0; i < fields.length; i++) {
  			dd.push('<span class="tg-group-field">');
  			dd.push('<span>' + fields[i] + '</span>');
  			dd.push('<span class="tg-group-close">x</span>');
  			dd.push('</span>');
  		}
  		var cc = $(opts.groupHeader);
  		cc.addClass('tg-group-header').html(dd.join(''));
  		cc.find('.tg-group-close').click(function () {
  			var field = $(this).prev().text();
  			var index = $.inArray(field, fields);
  			if (index >= 0) {
  				fields.splice(index, 1);
  				$(target).treegrid('groupData', {
  					data: opts.groupData,
  					fields: fields,
  					groupHeader: opts.groupHeader
  				});
  			}
  		});
  	}
  
  	$.extend($.fn.treegrid.methods, {
  		groupData: function (jq, param) {
  			initCss();
  			return jq.each(function () {
  				groupData(this, param);
  			});
  		}
  	});
  })(jQuery);
  
  