func (f *FieldSetType) AddTag(tag string) *FieldSetType {
	f.Tags.Add(tag)
	return f
}
AddTag adds a no-value parameter (e.g.: "disabled", "checked") to the fieldset.
package forms
import (
	"bytes"
	"html/template"
	"strconv"
	"strings"
	"github.com/coscms/forms/common"
	"github.com/coscms/forms/config"
	"github.com/coscms/forms/fields"
)
type FieldSetType struct {
	OrigName   string                  `json:"origName" xml:"origName"`
	CurrName   string                  `json:"currName" xml:"currName"`
	Label      string                  `json:"label" xml:"label"`
	LabelCols  int                     `json:"labelCols" xml:"labelCols"`
	FieldCols  int                     `json:"fieldCols" xml:"fieldCols"`
	Classes    common.HTMLAttrValues   `json:"classes" xml:"classes"`
	Tags       common.HTMLAttrValues   `json:"tags" xml:"tags"`
	FieldList  []fields.FieldInterface `json:"fieldList" xml:"fieldList"`
	AppendData map[string]interface{}  `json:"appendData,omitempty" xml:"appendData,omitempty"`
	FormTheme  string                  `json:"formTheme" xml:"formTheme"`
	Language   string                  `json:"language,omitempty" xml:"language,omitempty"`
	Template   string                  `json:"template" xml:"template"`
	fieldMap   map[string]int
	data       map[string]interface{}
}
func (f *FieldSetType) SetData(key string, value interface{}) {
	f.AppendData[key] = value
}
func (f *FieldSetType) SetLabelCols(cols int) {
	f.LabelCols = cols
}
func (f *FieldSetType) SetFieldCols(cols int) {
	f.FieldCols = cols
}
func (f *FieldSetType) SetName(name string) {
	f.CurrName = name
}
func (f *FieldSetType) OriginalName() string {
	return f.OrigName
}
func (f *FieldSetType) Data() map[string]interface{} {
	if len(f.data) > 0 {
		return f.data
	}
	f.data = map[string]interface{}{
		"container": "fieldset",
		"name":      f.CurrName,
		"label":     f.Label,
		"labelCols": f.LabelCols,
		"fieldCols": f.FieldCols,
		"fields":    f.FieldList,
		"classes":   f.Classes,
		"tags":      f.Tags,
	}
	for k, v := range f.AppendData {
		f.data[k] = v
	}
	return f.data
}
func (f *FieldSetType) render() string {
	buf := bytes.NewBuffer(nil)
	tpf := common.TmplDir(f.FormTheme) + "/" + f.FormTheme + "/" + f.Template + ".html"
	tpl, err := common.GetOrSetCachedTemplate(tpf, func() (*template.Template, error) {
		return common.ParseFiles(common.LookupPath(tpf))
	})
	if err != nil {
		return err.Error()
	}
	err = tpl.Execute(buf, f.Data())
	if err != nil {
		return err.Error()
	}
	return buf.String()
}
func (f *FieldSetType) Render() template.HTML {
	return template.HTML(f.render())
}
func (f *FieldSetType) String() string {
	return f.render()
}
func (f *FieldSetType) SetLang(lang string) {
	f.Language = lang
}
func (f *FieldSetType) Lang() string {
	return f.Language
}
func (f *FieldSetType) Clone() config.FormElement {
	fc := *f
	return &fc
}
func (f *FieldSetType) SetTemplate(tmpl string) *FieldSetType {
	f.Template = tmpl
	return f
}
func FieldSet(name string, label string, theme string, elems ...fields.FieldInterface) *FieldSetType {
	ret := &FieldSetType{
		Template:   "fieldset",
		CurrName:   name,
		OrigName:   name,
		Label:      label,
		Classes:    common.HTMLAttrValues{},
		Tags:       common.HTMLAttrValues{},
		FieldList:  elems,
		fieldMap:   map[string]int{},
		AppendData: map[string]interface{}{},
		FormTheme:  theme,
	}
	for i, elem := range elems {
		ret.fieldMap[elem.OriginalName()] = i
	}
	return ret
}
func (f *FieldSetType) SortAll(sortList ...string) *FieldSetType {
	elem := f.FieldList
	size := len(elem)
	f.FieldList = make([]fields.FieldInterface, size)
	var sortSlice []string
	if len(sortList) == 1 {
		sortSlice = strings.Split(sortList[0], ",")
	} else {
		sortSlice = sortList
	}
	for k, fieldName := range sortSlice {
		if oldIndex, ok := f.fieldMap[fieldName]; ok {
			f.FieldList[k] = elem[oldIndex]
			f.fieldMap[fieldName] = k
		}
	}
	return f
}
func (f *FieldSetType) Elements(elems ...config.FormElement) *FieldSetType {
	for _, e := range elems {
		switch v := e.(type) {
		case fields.FieldInterface:
			f.addField(v)
		}
	}
	return f
}
func (f *FieldSetType) addField(field fields.FieldInterface) *FieldSetType {
	field.SetTheme(f.FormTheme)
	field.SetData(`container`, `fieldset`)
	f.FieldList = append(f.FieldList, field)
	f.fieldMap[field.OriginalName()] = len(f.FieldList) - 1
	return f
}
func (f *FieldSetType) Sort(sortList ...string) *FieldSetType {
	size := len(f.FieldList)
	endIdx := size - 1
	var sortSlice []string
	if len(sortList) == 1 {
		sortSlice = strings.Split(sortList[0], ",")
	} else {
		sortSlice = sortList
	}
	var index int
	for _, nameIndex := range sortSlice {
		ni := strings.Split(nameIndex, ":")
		fieldName := ni[0]
		if len(ni) > 1 {
			if ni[1] == "last" {
				index = endIdx
			} else if idx, err := strconv.Atoi(ni[1]); err != nil {
				continue
			} else {
				if idx >= 0 {
					index = idx
				} else {
					index = endIdx + idx
				}
			}
		}
		if oldIndex, ok := f.fieldMap[fieldName]; ok {
			if oldIndex != index && size > index {
				f.sortFields(index, oldIndex, endIdx, size)
			}
		}
		index++
	}
	return f
}
func (f *FieldSetType) Sort2Last(fieldsName ...string) *FieldSetType {
	size := len(f.FieldList)
	endIdx := size - 1
	index := endIdx
	for n := len(fieldsName) - 1; n >= 0; n-- {
		fieldName := fieldsName[n]
		if oldIndex, ok := f.fieldMap[fieldName]; ok {
			if oldIndex != index && index >= 0 {
				f.sortFields(index, oldIndex, endIdx, size)
			}
		}
		index--
	}
	return f
}
func (f *FieldSetType) Field(name string) fields.FieldInterface {
	ind, ok := f.fieldMap[name]
	if !ok {
		return &fields.Field{}
	}
	return f.FieldList[ind].(fields.FieldInterface)
}
func (f *FieldSetType) Name() string {
	return f.CurrName
}
func (f *FieldSetType) AddClass(class string) *FieldSetType {
	f.Classes.Add(class)
	return f
}
func (f *FieldSetType) RemoveClass(class string) *FieldSetType {
	f.Classes.Remove(class)
	return f
}