docs: function(fields, editing) {
      var f = [];
      _.each(fields, function(field) {
          var nf = {};
          if (!(field === Object(field))) {
              nf = nga.field(field);
          } else {
              switch (field.type) {
                  case 'template':
                      nf = nga.field(field.field, 'template')
                          .template(field.template);
                      break;
                  case 'boolean':
                      nf = nga.field(field.field, field.type)
                          .validation({required: true});
                      break;
                  case 'float':
                  case 'json':
                  case 'text':
                      nf = nga.field(field.field, field.type);
                      break;
                  case 'number':
                      nf = nga.field(field.field, field.type)
                          .format(field.format)
                      break;
                  case 'choice':
                      nf = nga.field(field.field, field.type);
                      if (field.choiceField) {
                          nf.choices(function(entry) {
                              return field.choices.filter(function(choice) {
                                  return choice[field.choiceField] == entry.values[field.choiceField];
                              });
                          });
                      } else {
                          nf.choices(field.choices);
                      }
                      break;
                  case 'string':
                      if (!editing) {
                          switch (field.format) {
                              case 'url':
                                  var caption = field.caption || 'Link';
                                  nf = nga.field(field.field, 'template')
                                      .template('<a href="{{ entry.values.url }}" target="_blank" ng-show="entry.values.url">' + caption + '</a>')
                                  ;
                                  break;
                              case 'image':
                                  nf = nga.field(field.field, 'template')
                                      .template('<img ng-src="' + field.url + '{{ entry.values.name }}"' + ' width="' + field.width + 'px" />')
                                  ;
                                  break;
                              default:
                                  nf = nga.field(field.field);
                          }
                      } else {
                          nf = nga.field(field.field);
                      }
                      break;
                  case 'integer':
                      if (!editing) {
                          switch (field.format) {
                              case 'count':
                                  nf = nga.field(field.field, 'template')
                                      .template('{{ entry.values.' + field.targetField + '.length }}')
                                  ;
                                  break;
                              case 'rating':
                                  nf = nga.field(field.field, 'template')
                                      .template('<star-rating stars="{{ entry.values.rating }}"></star-rating>');
                                  break;
                              default:
                                  nf = nga.field(field.field);
                          }
                      } else {
                          nf = nga.field(field.field);
                      }
                      break;
                  case 'date':
                  case 'datetime':
                      var formatString = field.formatString || '';
                      nf = nga.field(field.field, field.type)
                          .format(formatString);
                      break;
                  case 'reference':
                      var tField = nga.field(field.targetField);
                      if (field.targetFieldMap) {
                          tField.map(field.targetFieldMap);
                      }
                      nf = nga.field(field.field, field.type)
                          .targetEntity(entities[field.targetEntity])
                          .targetField(tField)
                          .detailLinkRoute('show')
                      ;
                      break;
                  case 'referenced_list':
                      var tFields = [];
                      if (field.targetFields) {
                          tFields = ngAdmin.ngaFieldsFromModel(
                              field.targetEntity, field.targetFields
                          );
                      }
                      nf = nga.field(field.field, field.type)
                          .targetEntity(entities[field.targetEntity])
                          .targetReferenceField(field.targetReferenceField)
                          .targetFields(tFields)
                      ;
                      break;
                  case 'reference_many':
                      nf = nga.field(field.field, field.type)
                          .targetEntity(entities[field.targetEntity])
                          .targetField(nga.field(field.targetField))
                          // .detailLinkRoute('show')
                          .perPage(0)
                      ;
                      break;
                  case 'id':
                  // case 'string':
                  default:
                      nf = nga.field(field.field);
                      break;
              };
              // default value
              if (field.defaultValue) {
                  if (typeof field.defaultVale === "function") {
                      nf.defaultValue(field.defaultValue());
                  } else {
                      nf.defaultValue(field.defaultValue);
                  }
              }
              // read-only
              if (field.readOnly) {
                  nf.editable(false);
              }
              // add attributes
              if (field.attributes) {
                  nf.attributes(field.attributes);
              }
              // add filters
              if (field.permanentFilters) {
                  nf.permanentFilters(field.permanentFilters);
              }
              // add map
              if (field.map) {
                  nf.map(field.map);
              }
              // add page
              if (field.perPage) {
                  nf.perPage(field.perPage);
              }
              // add sort
              if (field.sort) {
                  nf.sortField(field.sort.field);
                  nf.sortDir(field.sort.dir);
              }
              // add field label
              if (field.label) {
                  nf.label(field.label);
              }
              // add detail link
              if (field.detailRoute) {
                  nf.isDetailLink(true);
                  nf.detailLinkRoute(field.detailRoute);
              }
              // set field pinned
              if (field.pinned) {
                  nf.pinned(true);
              }
          }
          f.push(nf);
      });
      return f;
  }