text: GrapheneDataGrid = function(options) {
  	if(typeof options.collections == 'object'){
  		this.collections = options.collections;
  	}else{
  		this.collections = gform.collections;
  	}
  	this.methods = options.methods||{};
  
  	var actions = [{type:"danger",name:'delete',min:1,label:'<i class="fa fa-times"></i> Delete' },
  	'|',
  	{type:"primary",name:'edit',min:1,label:'<i class="fa fa-pencil"></i> Edit' },
  	'|',
  	{type:"success",name:'create',min:0,label:'<i class="fa fa-pencil-square-o"></i> New' }];
  	this.resource = options.resource;
  
  	this.eventBus = new gform.eventBus({owner:'grid',item:'model',handlers:options.events||{} }, this)
  	this.on = this.eventBus.on;
  	this.trigger = this.eventBus.dispatch;
  
  	options = _.extend({filter: true,actions:[{'name':'create'},'|',{'name':'edit'},'|',{'name':'delete'}], sort: true, search: true, download: true, upload: true, columns: true, id:gform.getUID()}, options);
  	if(typeof options.actions == 'object' && options.actions.length){
  		options.actions = _.map(options.actions,function(event){
  			// return _.extend({global:false},event)
  			var temp = _.find(actions,{name:event.name})
  			if(typeof temp !== 'undefined'){
  				event = _.defaultsDeep(event,temp);
  			}
  			return event;
  		})
  	}else{
  		options.actions = false;
  	}
  
  	var loaded = false;
  	if (window.localStorage && options.name) {
  		try{
  			loaded = JSON.parse(localStorage.getItem('dg_'+options.name));
  		}catch(e){};
  	}
  	if(typeof options.item_template !== 'string' ){
  		if(window.outerWidth > 767 || window.outerWidth == 0){
  			options.item_template = gform.stencils['data_grid_row'];
  		}else{
  			this.mobile = true;
  			options.item_template = gform.stencils['mobile_row'];
  		}
  	}
  	this.toJSON =function(){
  		return _.map(this.getModels(),'attributes')
  	}
  	this.filterValues = {};
  	this.draw = function() {
  		
  		_.each(this.summary.items, function(item){
  			this.$el.find('.filter #'+item.id+',[data-sort='+item.id+']').toggle(item.isEnabled);
  		}.bind(this))
  
  		// if(this.$el.find('.filter').length){
  		options.search = this.filterValues;
  		_.each(options.search, function(item, index) {
  			if(!item && (item !== 0)) {
  				delete options.search[index];
  			}
  		});
  		var pagebuffer = options.pagebuffer || 2;
  
  		if(this.$el.find('[name="search"]').length && this.$el.find('[name="search"]').val().length){
  			this.searchAll(this.$el.find('[name="search"]').val());
  		}else{
  			this.search(options);
  		}
  
  		var renderObj = {};
  		options.pagecount = Math.ceil(this.lastGrabbed / options.count);
  		renderObj.pages = [];
  
  		if(options.page > options.pagecount){
  				options.page = options.pagecount || 1;
  		}
  		var showing = (this.lastGrabbed>(options.count * options.page))? (options.count * options.page) : this.lastGrabbed;
  
  		var fragment = document.createDocumentFragment();
  		_.each(this.grab(options), function(model) {
  			// var row = document.createElement('tr');
  			// row.innerHTML =gform.renderString(view,model)
  			// row.setAttribute("class", 'filterable grid-row'+(model.waiting() ? " warning" : ""));
  			// row.setAttribute('data-id', model.id);
  			fragment.appendChild(model.draw());
  		});
  		var target =this.$el[0].querySelector('.list-group');
  		if(target !== null){
  			target.innerHTML = '';
  			target.appendChild(fragment)
  		}
  
  
  		var startpage = options.page - pagebuffer;
  		if(startpage < 1){startpage = 1;}
  		var endpage = options.page + pagebuffer;
  		if(endpage >options.pagecount){endpage = options.pagecount}
  
  		for(var i = startpage; i <= endpage; i++){
  			var page = {name: i};
  			if(options.page == i){
  				page.active = 'active';
  			}
  			renderObj.pages.push(page);
  		}
  		renderObj.size = this.lastGrabbed;
  		renderObj.last = showing;
  		renderObj.first = ( (options.count * (options.page-1) ) + 1);
  
  		renderObj.multiPage = (endpage > startpage);
  		renderObj.isFirst = (options.page == 1);
  		renderObj.isLast = (options.page == options.pagecount);
  		renderObj.showLast = (options.pagecount == endpage);
  		renderObj.showFirst = (startpage == 1);
  		renderObj.checked_count = this.getSelected().length;
  
  		renderObj.entries = _.map(options.entries,function(item){
  			return {value:item, selected: (item==options.count)}
  		},options)
  
  		this.renderObj = renderObj;
  		// this.$el.find('.paginate-footer').html(templates['data_grid_footer'].render(this.renderObj, templates));
  		this.updateCount();
  
  		this.$el.find('.paginate-footer').html(gform.render('data_grid_footer',this.renderObj));
  
  		this.fixStyle();
  		if (window.localStorage && options.name) {
  			localStorage.setItem('dg_'+options.name, JSON.stringify(this.state.get())) ;
  		}
  	}
  
  
  	var changePage = function(e) {
  		e.stopPropagation();
  		switch(e.currentTarget.dataset.page){
  			case 'inc':
  				options.page++;
  				if(options.page > options.pagecount){options.page = options.pagecount}
  				break;
  			case 'dec':
  				options.page--;
  				if(options.page < 1){options.page = 1}
  				break;
  			default:
  			options.page = e.currentTarget.dataset.page || options.pagecount;
  
  		}
  		this.draw();
  	}
  
  	var processSort = function(sortField, reverse) {
  		if(typeof sortField == 'undefined' || sortField == true){
  			this.$el.find('.reverse, [data-sort]').removeClass('text-primary').find('i').attr('class', 'fa fa-sort text-muted')
  			this.$el.find('.sortBy').val('true');
  			options.reverse = false;
  		}else{
  			if(typeof reverse == 'undefined') {
  				if(options.sort == sortField) {
  						options.reverse = !options.reverse;
  				}else{
  					options.reverse = false;
  				}
  			}else{
  				options.reverse = reverse;
  			}
  			var current = this.$el.find('.reverse, [data-sort=' + _.find(this.options.filterFields,{search:sortField}).id + ']')
  			if(typeof current !== 'undefined'){
  				if(options.reverse) {
  					current.find('i').attr('class', 'fa fa-sort-asc');
  				}else{
  					current.find('i').attr('class', 'fa fa-sort-desc');
  				}
  				current.siblings('[data-sort]').removeClass('text-primary');
  				current.siblings('[data-sort]').find('i').attr('class', 'fa fa-sort text-muted');
  				current.addClass('text-primary');
  			}
  		}
  		options.sort = sortField;
  		this.draw();
  	}.bind(this)
  
  	var options = _.extend({count: options.count || 25, page: 1, sort: 'createdAt', reverse: false}, options);
  
  	
  
  
  	// if(typeof options.filters !== 'undefined'){
  	// 	options.filters = _.map(options.filters, function(fieldIn){
  	// 		var parent = parent || null;
  	// 		fieldIn.type = fieldIn.type || 'text';
  	// 		//work gform.default in here
  	// 		var field = _.assignIn({
  	// 				name: (gform.renderString(fieldIn.label)||'').toLowerCase().split(' ').join('_'), 
  	// 				id: gform.getUID(), 
  	// 				// type: 'text', 
  	// 				label: fieldIn.legend || fieldIn.name,
  	// 				validate: [],
  	// 				valid: true,
  	// 				parsable:true,
  	// 				visible:true,
  	// 				editable:true,
  	// 				array:false,
  	// 				offset: 0,
  	// 		}, this.opts, gform.default,(gform.types[fieldIn.type]||gform.types['text']).defaults, fieldIn)
  	// 		if(field.name == ''){field.name = field.id;}
  	// 		field.item = fieldIn;
  	// 		return field;
  	// 	})
  	// }
  	options.schema = options.schema || options.form.fields;
  
  	options.filterFields = _.map(_.extend({}, options.schema), function(val){
  		// val = _.omit(gform.normalizeField.call(new gform({options:{default:{type:'text'}}}) ,val),'parent','columns');
  		val = _.omit(gform.field.normalize(new gform({options:{default:{type:'text'}}})).call(null,{},val),'parent','columns');
  		name = val.name;
  		val.value = '';
  		switch(val.type){
  			case 'checkbox':
  				val.format = {label:"{{label}}"}
  
                  var temp = new gform.mapOptions(val)
                  val.options = temp.getoptions();
                  val.options = _.defaults(val.options,[{label: 'False', value: 'false'},{label: 'True', value: 'true'}])
  			case 'radio':
  			case 'smallcombo':
  				val.type = 'select';
  			case 'select':
  				val.placeholder = false;
  				val.multiple = false;
  				var temp = _.pick(val,['options','max','min','path','format'])
  				val = _.omit(val,['options','max','min','path','format'])
  				temp.type = 'optgroup';
  				val.options = [{type:'optgroup',options:[{label:'No Filter',value:''}],format:{label:"{{label}}"}},temp]
  				break;
  
  		case 'fieldset':
  				val.type = 'text';
  
  				if(!val.template){
  					val.template = _.map(val.fields,function(field){
  						return '{{attributes.'+ val.name +'.'+field.name+'}}';
  					})
  					val.template = val.template.join('<br>');
  
  				}
  				val = _.omit(val,['fields'])
  				break;
  
  		case 'smallcombo':
  		case 'hidden':
  				break;
  		default:
  				val.type = 'text';
  		}
  
  		if(val.options && typeof val.options == 'object'){// && !_.isArray(val.options)){
  			val.options = _.map(val.options, function(item){
  				if(typeof item == 'object'){
  					return _.omit(item, 'selected');
  				}else{return item;}
  			})
  		}else{
  		}
  		val.id = val.id || gform.getUID();
  		val.search = val.name;
  		val.name = val.id;
  		val.show = [];
  		val.edit = [];
  		val.show = [];
  		delete val.delete;
  		delete val.size;
  		delete val.pre;
  		delete val.opst;
  		// val.isEnabled = true;
  		val.edit = true;
  		val.help = '';
  		val.array = false;
  		return val;
  	});
  	if(typeof options.columns == 'object'){
  			options.filterFields = _.filter(options.filterFields, function(item){
  					return (_.includes(options.columns, item.name) || _.includes(options.columns,item.id))
  			})
  	}
  
  	var summary = {'[[':'{{', ']]':'}}',checked_count:0,'items': _.map(options.filterFields, function(val){
  		var name = (val.search|| val.label.split(' ').join('_').toLowerCase());
  
  		if(val.template){
  			name = val.template;//.replace(/{{value}}/gi, '{{attributes.'+ name + '}}');
  
  		}else{
  			// name = '{{attributes.'+ name + '}}'
  			name = '{{display.'+ name + '}}';
  
  		}
  		// else{
  		// 	switch(val.type){
  		// 		case 'date':
  		// 			name = '<span data-moment="{{attributes.'+name+'}}" data-format="L"></span>'
  		// 			break;
  		// 		case 'select':
  		// 				if(options.inlineEdit){
  		// 					name = '<span data-popins="'+name+'"></span>';
  		// 				}else{
  		// 					name = '{{display.'+ name + '}}'
  		// 				}
  		// 			break;
  		// 		case 'color':
  		// 			name = '<div class="btn btn-default" style="background-color:{{attributes.'+name+'}}">{{attributes.'+name+'}}</div> {{attributes.'+name+'}}'
  		// 			break;
  		// 		default:
  		// 			// name = '{{attributes.'+ name + '}}'
  		// 			if(options.inlineEdit){
  		// 				name = '<span data-popins="'+name+'"></span>';
  		// 			}else{
  		// 				name = '{{attributes.'+ name + '}}'
  		// 			}
  		// 	}
  		// }
  		return {'isEnabled': (typeof val.showColumn =='undefined' || val.showColumn), 'label': val.label, 'name': name, 'cname': (val.name|| val.label.split(' ').join('_').toLowerCase()), 'id': val.id, 'visible':!(val.type == 'hidden')} 
  	})};
  
  	options.entries = options.entries || [25, 50 ,100];
  	summary.options = options;
  
  	this.summary = summary;
  	var template;
  	if(options.template ){
  		// template= Hogan.compile(Hogan.compile(options.template).render(summary, templates));  
  		template= template = gform.renderString(options.template,summary)
  
  	}else{
  		if(!this.mobile){
  			// template = Hogan.compile(templates['table'].render(summary, templates));
  			template = gform.render('data_grid',summary)
  		}else{
  			// template = Hogan.compile(templates['mobile_data_grid'].render(summary, templates));
  			template = gform.render('mobile_data_grid',summary)
  
  		}
  	}
  	var actions = {
  		'create':function(){
  			var fields = options.schema;
  			if(typeof options.create == 'object'){
  				fields = options.create.fields;
  			}
  			new gform({name:'modal',data:options.defaultData||{},table:this,collections:this.collections,methods:this.methods,events:(options.create||options.form||{}).events, actions:[{type:'cancel',modifiers: "btn btn-danger pull-left"},{type:'save'},{type:'hidden',name:"_method",value:"create",parse:function(){return false}}], legend: '<i class="fa fa-pencil-square-o"></i> Create New', fields:  fields}).on('save', function(e) {
  				if(e.form.validate(true)){
  					this.add(e.form.get(),{validate:false})
  					e.form.trigger('close');
  				}
  			}.bind(this)).on('cancel',function(e){e.form.trigger('close')}).modal()},
  		'edit':function(){
  			if(this.getSelected().length >1) {
  				if(typeof this.options.multiEdit == 'undefined' || this.options.multiEdit.length == 0){return;}
  				var selectedModels = this.getSelected();
  				if(selectedModels.length == 0){ return; }
  				//get the attributes from each model
  				var temp = _.map(selectedModels,function(item){return item.attributes;})//_.pick(item.attributes;})
  				//get the fields that are common between them
  				var common_fields = _.filter(this.options.multiEdit, function(item){return _.uniq(_.map(temp, item)).length == 1});
  				//get the schema fields matching from above
  				// if(common_fields.length == 0) {
  				// 	$(gform.render('modal_container',{title: "Common Field Editor ",footer:'<div class="btn btn-danger" style="margin-right:20px" data-dismiss="modal">Done</div>', body:'<div class="alert alert-warning">No eligible fields have been found for editing.</div>'})).modal();
  				// } else {
  					var newSchema = _.filter(this.options.schema, function(item){return common_fields.indexOf(item.name) >= 0})
  					if(newSchema.length > 0 ){
  						debugger;
  						new gform({collections:this.collections,methods:this.methods,events:(options.edit||options.form||{}).events,legend:'('+selectedModels.length+') Common Field Editor',actions:[{type:'cancel',modifiers: "btn btn-danger pull-left"},{type:'save'},{type:'hidden',name:"_method",value:"edit",parse:function(){return false}}], fields:newSchema, data: _.extend({},_.pick(selectedModels[0].attributes, common_fields))}).on('save', function(selectedModels,e){
  							if(e.form.validate(true)){
  								var newValues = e.form.get();
  								_.map(selectedModels,function(model){
  									model.update(newValues);
  									this.eventBus.dispatch('model:edited',model)
  								}.bind(this))
  				
  								e.form.trigger('close');
  							}
  						}.bind(this,selectedModels)).on('close', function(){
  							this.draw();
  							this.eventBus.dispatch('edited')
  						}.bind(this)).on('cancel',function(e){e.form.trigger('close')}).modal()
  					}else{
  						$(gform.render('modal_container',{title: "Common Field Editor ",footer:'<div class="btn btn-danger" style="margin-right:20px" data-dismiss="modal">Done</div>', body:'<div class="alert alert-warning">No eligible fields have been found for editing.</div>'})).modal();
  					}
  				// }
  			}else{
  				var fields = options.schema;
          if(typeof options.edit == 'object'){
            fields = options.edit.fields;
          }
  				new gform({collections:this.collections,methods:this.methods,events:(options.edit||options.form||{}).events,name:'modal',actions:[{type:'cancel',modifiers: "btn btn-danger pull-left"},{type:'save'},{type:'hidden',name:"_method",value:"edit",parse:function(){return false}}], legend: '<i class="fa fa-pencil-square-o"></i> Edit', data: this.getSelected()[0].attributes,fields:fields} ).on('save', function(e) {
  					if(e.form.validate(true)){
  						// this.getSelected()[0].set(_.extend({}, this.getSelected()[0].attributes, e.form.toJSON()));
  						this.getSelected()[0].set(e.form.toJSON());
  						this.eventBus.dispatch('edited')
  						this.eventBus.dispatch('model:edited',this.getSelected()[0])
  						this.draw();
  						e.form.trigger('close')
  					}
  
  				}.bind(this)).on('cancel',function(e){e.form.trigger('close')}).modal()
  			}
  		},
  		'delete':function(){
  			var checked_models = this.getSelected()
  			if (checked_models.length) {
  				if(confirm("Are you sure you want to delete "+checked_models.length+" records? \nThis operation can not be undone.\n\n" )){
  					_.each(checked_models, function(item){
  							item.delete();
  							this.eventBus.dispatch('model:deleted',item)
  
  					}.bind(this))
  					this.eventBus.dispatch('deleted')
  					this.draw();
  				}
  			}
  		},
  		'mark':function(e){
  			e.model.toggle()
  		}
  	}
  	function handleFiles(table, e) {
  		var files = this.files
      // Check for the various File API support.
      if (window.FileReader) {
          // FileReader are supported.
        (function (fileToRead) {
  		var reader = new FileReader();
  		// Read file into memory as UTF-8      
  		reader.readAsText(fileToRead);
  		reader.onload = function (event) {
  			var csv = event.target.result;
  			var temp = _.csvToArray(csv);
  		//   CSVParser
  			var valid = true;
  
  			// $('#myModal').remove();
  			var ref = $(gform.render('modal_container',{title: "Importing CSV ",footer:'<div class="btn btn-danger" data-dismiss="modal">Cancel</div>', body:'<div class="progress"><div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 0%"><span class="sr-only">50% Complete</span></div></div><div class="status">Validating Items...</div>'}));
  			ref.modal();
  			ref.on('hidden.bs.modal', function () {
  				this.importing = false;
  			}.bind(this));
  
  			var itemCount = temp.length;
  			var totalProgress = itemCount*2;
  			var items = [];
  			this.importing = true;
  
  
  			
  			var keys = _.map(temp[0], function(item,key){
  				var search = _.find(options.schema, {label:key});
  				 return ((search == null) ? {name:key,label:key} : search);
  
  			})
  			for(var i = 0; i<temp.length; i++){
  				if(!this.importing) return false;
  				if(keys.length == _.values(temp[i]).length){
  					var newtemp = {};
  					_.each(keys,function(key){
  						newtemp[key.name] = temp[i][key.label]||"";
  					})
  					valid = table.validate(newtemp);
  					if(!valid){
  						break;
  					}
  					items.push(valid);
  				}else{
  					itemCount--;
  				}
  				ref.find('.progress-bar').width((i/totalProgress)*100 +'%')
  			}
  
  
  
  
  			if(valid){
  				ref.find('.status').html('Adding Items...');
  				for(var i = 0; i<items.length; i++){
  				if(!this.importing) return false;
  					table.add(items[i],{draw:false});
  					ref.find('.progress-bar').width(((i+itemCount)/totalProgress)*100 +'%')
  				}
  			}else{
  				ref.find('.btn').html('Done');
  				ref.find('.status').html('<div class="alert alert-danger">Error(s) in row '+i+ ', failed to validate!<ul><li>'+_.filter(table.errors,function(item){return item;}).join('</li><li>')+'</li></div>')
  				return;
  			}
  			ref.find('.status').html('<div class="alert alert-success">Successfully processed file, '+itemCount+ ' rows were added!</div>')
  			ref.find('.btn').toggleClass('btn-danger btn-success').html('Done');
  			ref.find('.progress').hide();
  
  			if(typeof table.options.sortBy !== 'undefined'){
  				table.models = _.sortBy(table.models, function(obj) { return obj.attributes[this.options.sortBy]; }.bind(table)).reverse();
  			}
  
  			if(typeof table.options.onBulkLoad == 'function'){
  				table.options.onBulkLoad();
  			}
  
  		}
  	      reader.onerror = function (evt) {
  		      if(evt.target.error.name == "NotReadableError") {
  		          alert("Canno't read file !");
  		      }
  		    }
  	    })(files[0]);
  	    e.currentTarget.value = '';
  
      } else {
          alert('FileReader is not supported in this browser.');
  		}
  		table.draw();
    }
  
  	function onload($el){
  		this.$el = $el;
  
  		if($el.find('.filter').length) {
  			this.filter = new gform({actions:[],collections:this.collections,name:'filter'+this.options.id,clear:false, fields: options.filterFields,default:{target:function(){
                  return '[name="'+this.name+'"],[data-inline="'+this.name+'"]'
              },hideLabel:true,type:'text',format:{label: '{{label}}', value: '{{value}}'}} },$el.find('.filter')[0]).on('input', function(){
  				this.$el.find('[name="search"]').val('');
  				this.filterValues = this.filter.toJSON();
  				this.draw();
  			}.bind(this));
  
  			this.filter.set()
  
  		}
  		this.checkForm = new gform(_.assign({},{collections:this.collections,name:'internal'+this.options.id, fields: options.schema }, options.form || {} )).on('change',function(){
  			_.each(this.models,function(model){
  				model.update(null,true)
  			})
  		}.bind(this))
  		this.updateCount =function(count) {
  			var count = count || this.getSelected().length;
  			this.summary.checked_count = count;
  			if(!this.mobile){
  				this.$el.find('[name="actions"]').html(gform.render('actions',this.summary));
  			}
  			_.each(this.$el.find('.grid-action'),function(i){
  				var event = _.assign({max:10000,min:0}, _.find(this.options.actions,{name:i.dataset.event}))
  				if(this.summary.checked_count >= event.min && this.summary.checked_count <= event.max){
  					gform.removeClass(i,'disabled')
  				}else{
  					gform.addClass(i,'disabled')
  				}
  			}.bind(this))
  
  			this.$el.find('[name="count"]').html(gform.render('count',this.summary));
  
  			var checkbox = this.$el.find('[name="select_all"].fa');
  
  			if(count>0 && count == this.getModels().length){
  				checkbox.attr('class', 'fa fa-2x fa-check-square-o');
  			}else if(count == 0){
  				checkbox.attr('class', 'fa fa-2x fa-square-o');
  			}else{
  				checkbox.attr('class', 'fa fa-2x fa-minus-square-o');
  			}
  		}
  		this.load =function(data){
  			_.each(this.models,function(model){
  				model.delete();
  			})
  			this.models = [];
  			if(data) {
  				for(var i in data) {
  					this.models.push(new gridModel(this, data[i],{
  					'*':[function(e){
  					e.model.owner.eventBus.dispatch('model:'+e.event,e.model)
  					}],
  					'check': [function(e){
  						e.model.owner.draw();
  					}]
  					})
  					);
  	
  	
  				}
  			}
  			if(typeof this.options.sortBy !== 'undefined'){
  				this.models = _.sortBy(this.models, function(obj) { return obj.attributes[this.options.sortBy]; }.bind(this)).reverse();
  			}
  			this.draw();
  		}
  
  		this.view = gform.renderString(options.item_template,summary)
  
  		// if(options.data) {
  		// 	for(var i in options.data) {
  		// 		this.models.push(new gridModel(this, options.data[i],{
  		// 		'*':[function(e){
  		// 		e.model.owner.eventBus.dispatch('model:'+e.event,e.model)
  		// 		}],
  		// 		'check': [function(e){
  		// 			e.model.owner.draw();
  		// 		}]
  		// 		})
  		// 		);
  
  
  		// 	}
  		// }
  		// if(typeof this.options.sortBy !== 'undefined'){
  		// 	this.models = _.sortBy(this.models, function(obj) { return obj.attributes[this.options.sortBy]; }.bind(this)).reverse();
  		// }
  
  		this.$el.on('change', '.csvFileInput', _.partial(handleFiles, this));
  		this.$el.on('click','[name="bt-upload"]', function(){
  			this.$el.find('.csvFileInput').click();
  		}.bind(this));
  		this.$el.on('click','[name="bt-download"]', this.getCSV.bind(this));
  		this.$el.on('click','[data-page]', changePage.bind(this));
  
  
  
  		this.$el.on('click', '.columnEnables label', function(e){
  			e.stopPropagation();
  			_.find(this.summary.items, {id:e.currentTarget.dataset.field}).isEnabled = e.currentTarget.childNodes[0].checked;
  			this.view = gform.renderString(options.item_template,summary)
  
  			this.draw();
  		}.bind(this));
  	
  		this.$el.on('click', '.grid-row', function(e) {
  			this.eventBus.dispatch('click',_.find(this.models,{id:e.currentTarget.dataset.id}))
  		}.bind(this));
  
  
  
  		this.$el.on('change', '[name="count"]', function(e) {
  			options.count = parseInt(e.currentTarget.value,10);
  			this.draw();
  		}.bind(this))
  		this.$el.on('input', '[name="search"]', _.debounce(function(e) {
  			this.draw();
  		}.bind(this), 300));
  		
  		this.$el.on('click', '[name="select_all"]', function(e){
  			var checked_models = this.getSelected();
  
  			if (checked_models.length || this.getModels().length == 0) {						
  				_.each(checked_models, function(item){item.toggle(false,true)})			
  			} else {
  				_.each(this.filtered, function(item){item.toggle(true,true)})			
  			}		
  			this.draw();
  		}.bind(this));
  
  		this.$el.on('click','[name="reset-search"]', function(){
  			this.$el.find('[name="search"]').val('');
  
  			//reset filter form if it exists
  			if(this.filter) {this.filter.set()}
  			this.filterValues = {};
  
  			processSort();
  		}.bind(this));
  		
  		// this.$el.on('click', '[data-event="mark"]', function(e) {
  		// 	e.stopPropagation();
  		// }.bind(this));
  
  		this.dispatch = function(event, atts){
  			if(typeof atts == "undefined"){
  				atts = {event:event}
  			}
  			_.each(this.getSelected(),function(model){
  				this.eventBus.dispatch('model:'+atts.event,model)
  			}.bind(this))
  
  			var model;
  			if(typeof atts.id !== 'undefined'){
  				model = _.find(this.models,{id:atts.id})
  			}
  			var result = this.eventBus.dispatch(event,model,atts)
  			if(result.default && typeof actions[event] !== 'undefined'){
  				actions[event].call(this, result)
  			}
  		}
  		this.$el.on('click','[data-event]', function(e){
  			e.stopPropagation();
  			e.preventDefault();
  			var atts = _.assign({},e.currentTarget.dataset);
  			this.dispatch(atts.event,atts);
  		}.bind(this));
  
  		this.$el.on('click','[data-sort]', function(e) {
  			e.stopPropagation();
  			e.preventDefault();
  			var sortField = _.find(this.options.filterFields, {name: e.currentTarget.dataset.sort}).search;
  			if(this.options.reverse && this.options.sort == sortField){
  				processSort();
  			}else{
  				processSort(sortField);
  			}
  		}.bind(this))
  		
  		//Mobile
  		this.$el.on('change', '.sortBy', function(e) {
  			if(e.currentTarget.value !== ''){
  				processSort((_.find(this.options.filterFields, {id:e.currentTarget.value}) || {search:true}).search)
  			}
  		}.bind(this));
  		this.$el.on('click', '.filterForm', function(e) {
  			this.$el.find('[name="search"]').val('');
  			new gform({collections:this.collections,legend:"Filter By" ,name:'modal_filter'+this.options.id,data:this.filterValues, fields: options.filterFields }).on('save', function(){
  				this.filterValues = gform.instances['modal_filter'+this.options.id].toJSON();
  				this.draw();					
  				gform.instances['modal_filter'+this.options.id].trigger('close');
  
  			}.bind(this)).modal();
  
  			// this.filter = new gform({collections:this.collections,name:'filter'+this.options.id,clear:false, fields: this.options.filterFields,default:{hideLabel:true,type:'text',format:{label: '{{label}}', value: '{{value}}'}} },$el.find('.filter')[0]).on('input', function(){
  			// 	this.$el.find('[name="search"]').val('');
  			// 	this.filterValues = this.filter.toJSON();
  			// 	this.draw();
  			// }.bind(this)).modal();
  
  			// this.filter.set()
  
  
  		}.bind(this));	
  		this.$el.on('click', '.reverse', function(e) {
  			processSort(this.options.sort)
  		}.bind(this));
  
  		this.load(options.data);
  	}
  
  
  	this.validate = function(item){
  		var status = false;
  		var tempForm = new gform({collections:this.collections,fields: options.schema,methods:this.methods});
  		tempForm.set(item);
  		if(tempForm.validate()){
  			status = tempForm.toJSON();
  		}else{
  			this.errors = tempForm.errors;
  			console.log('Model not valid');
  		}
  		tempForm.destroy();
  		return status
  	}
  
  
  	this.add = function(item,config){
  		config = config||{validate:false, silent:false}
  		var newModel = new gridModel(this, item,{
  			'*':[function(e){
  			e.model.owner.eventBus.dispatch('model:'+e.event,e.model)
  			}],
  			'check': [function(e){
  				e.model.owner.draw();
  			}]
  			})
  		if(config.validate == false || 	this.validate(item)) {
  			this.models.push(newModel);
  			if(typeof this.options.sortBy !== 'undefined'){
  				this.models = _.sortBy(this.models, function(obj) { return obj.attributes[this.options.sortBy]; }.bind(this)).reverse();
  			}
  			if(config.draw !== false){
  				this.draw();
  			}
  			// this.updateCount(this.summary.checked_count);
  			if(config.silent !== true){
          		this.eventBus.dispatch('created',newModel)
  				this.eventBus.dispatch('model:created',newModel)
  			}
  			// if(typeof this.options.add == 'function'){
  			// 	this.options.add(newModel);
  			// }
  		}else{
  			return false
  		}
  		return newModel;
  		
  		
  	}
  	this.search = function(options) {
  		var ordered = _.sortBy(this.getModels(), function(obj) { return obj.attributes[options.sort]; });
  		if(!options.reverse){
  			ordered = ordered.reverse();
  		}
  		filterMap = this.filterMap;
  		ordered = _.filter(ordered, function(anyModel) {
  
  			var keep = _.isEmpty(options.search);
  			for(var filter in options.search) {
  					var temp;
  					if(_.filter(options.filterFields, {id:filter})[0] && typeof _.filter(options.filterFields, {id:filter})[0].options == 'undefined') {
  						temp = (_.score((anyModel.display[this.filterMap[filter]]+'').replace(/\s+/g, " ").toLowerCase(), (options.search[filter]+'').toLowerCase() ) > 0.40);
  					}else{
                          temp =  (anyModel.display[this.filterMap[filter]]+'' == options.search[filter]+'') || 
                                  (anyModel.attributes[this.filterMap[filter]]+'' == options.search[filter]+'') || 
                                  (typeof anyModel.attributes[this.filterMap[filter]] == "object" &&  (anyModel.attributes[this.filterMap[filter]].indexOf(options.search[filter])!=-1 || 
                                                                                                      anyModel.attributes[this.filterMap[filter]].indexOf(options.search[filter]+"")!=-1)                         
                                  )
  					}
  					keep = temp;
  					if(!keep){break;}
  			}
  			
  			return keep;
  		})
  		this.lastGrabbed = ordered.length;
  		this.filtered = ordered;
  	}
  
  	this.find = function(search) {
  		var keys = _.keys(search)
  		return _.filter(this.getModels(), function(anyModel) {
  			return _.isEqual(search, _.pick(anyModel.attributes, keys));
  		})
  	}
  
  	this.searchAll = function(search) {
  		//reset sorts and filters
  		options.sort = null;
  		this.$el.find('[data-sort]').removeClass('text-primary');
  		this.$el.find('[data-sort]').find('i').attr('class', 'fa fa-sort text-muted');
  		if(this.filter){
  
  			this.filter.set()
  			// silentPopulate.call(this.filter, this.defaults)
  		}
  
  		search = search.toLowerCase()
  		//score each model searching each field and finding a total 
  		_.map(this.getModels(), function(model){
  			model.score = 0;
  			for(var filter in options.filterFields) {
  				model.score += _.score((model.display[options.filterFields[filter].search]+'').replace(/\s+/g, " ").toLowerCase(), search);
  			}
  			
  		})
  
  		//sort by score (highet first) and remove models with no score
  		this.filtered = _.filter(_.sortBy(this.getModels(), 'score'), function(model) {
  				return (model.score > 0);
  		}).reverse();
  
  		this.lastGrabbed = this.filtered.length;
  	}
  
  
  
  	this.fixStyle = function(){
  		if(this.options.autoSize){
  			try{
  				var container = this.$el.find('.table-container > div');
  				var headers = this.$el.find('.table-container > table tr th:visible');
  				var columns = this.$el.find('.list-group-row th');
  				this.$el.find('.table-container table').removeClass('table-fixed')
  				
  				container.css('width', 'auto') 
  				container.css('minWidth', 'auto') 
  				headers.css('width', 'auto')
  				headers.css('minWidth', '85px')
  				this.$el.find('.table-container > table tr th.select-column').css('minWidth', '60px')
  				this.$el.find('.table-container > table tr th.select-column').css('width', '60px')
  				columns.css('width', 'auto')
  				columns.css('minWidth', 'auto')
  
  				container.css('height', $(window).height() - container.offset().top - (88+ this.options.autoSize) +'px');
  				_.each(	columns, function(column, index){
  					if(typeof headers[index] !== 'undefined'){
  
  						column.style.display = 'table-cell';
  						if(headers[index].offsetWidth > column.offsetWidth){
  							$(column).css('width',headers[index].offsetWidth+'px');
  							$(column).css('minWidth',headers[index].offsetWidth+'px');
  							$(headers[index]).css('width',headers[index].offsetWidth+'px')
  							$(headers[index]).css('minWidth',headers[index].offsetWidth+'px')
  						}else{
  							$(headers[index]).css('width',headers[index].offsetWidth+'px')
  							$(headers[index]).css('minWidth',headers[index].offsetWidth+'px')
  
  							$(column).css('width',headers[index].offsetWidth+'px');
  							$(column).css('minWidth',headers[index].offsetWidth+'px');
  							
  						}
  					}else{
  						column.style.display = 'none';
  					}
  				}.bind(this))
  
  				this.$el.find('.table-container table').addClass('table-fixed')
  				
  				var target = this.$el.find('.table-container > div table')[0].offsetWidth;
  				if(this.$el.find('.table-container > table')[0].offsetWidth > target){target = this.$el.find('.table-container > table')[0].offsetWidth;}
  
  				container.css('width', target + 'px') 
  				container.css('minWidth', target + 'px') 
  				if(target > this.$el.find('.table-container')[0].offsetWidth){
  					this.$el.find('.table-container').css('overflow','auto');
  				}else{
  					this.$el.find('.table-container').css('overflow','hidden');					
  				}
  
  			}catch(e){}
  		}
  	}
  	this.visible = [];
  
  	this.grab = function(options) {
  		_.each(this.visible,function(item){
  			item.visible = false;
  		})
  		var temp = this.filtered.slice((options.count * (options.page-1) ), (options.count * (options.page-1) ) + options.count)
  		this.visible = temp
  		_.each(this.visible,function(item){
  			item.visible = true;
  		})
  		return temp;
  	};
  	this.getModels = function(search){
  		var search = search || {deleted: false};
  		return _.filter(this.models, search)
  	}
  	this.getSelected = function(){return this.getModels({checked: true, deleted: false})}//_.filter(this.models, {checked: true, deleted: false})}
  
  
  	this.destroy = function(){
  		this.$el.find('.list-group').empty();
  		this.$el.off();
  		this.$el.empty();
  	}
  
  	this.state = {
  		get:function(){
  			var temp = {count:this.options.count,page:this.options.page};
  			if(this.$el.find('[name="search"]').length && this.$el.find('[name="search"]').val().length){
  				temp.search = this.$el.find('[name="search"]').val();
  			}else{
  				temp.sort = options.sort;
  				temp.reverse = options.reverse;
  				if(typeof this.filter !== 'undefined'){
  					temp.filters = {};
  					_.each(this.options.filterFields, function(item){
  						temp.filters[item.search] = this[item.id]
  					}.bind(this.filter.toJSON()))
  				}
  			}
  			temp.columns = _.map(_.map(_.filter(this.summary.items, function(item){return item.isEnabled}),'id'),function(id){
  				return _.find(this.options.filterFields, {id:id}).search;
  			}.bind(this))
  			return temp;
  		}.bind(this),
  		set: function(settings) {
  
  			if(typeof settings.columns !== 'undefined' && settings.columns.length) {
  				this.summary.items = _.map(this.summary.items, function(item) {
  					item.isEnabled = _.includes(settings.columns, this.filterMap[item.cname])
  					return item;
  				})
  				this.view = gform.renderString(options.item_template,summary)
  
  				this.$el.find('.columnEnables [type="checkbox"]').each(function(e) {
  					this.checked = false
  				})
  				if(options.columns) {
  					_.each(settings.columns, function(item){
  						var temp = this.$el.find('.columnEnables [data-field="'+_.find(this.options.filterFields, {search: item}).id+'"] [type="checkbox"]');
  						if(temp.length)temp[0].checked = true;
  					}.bind(this))
  				}
  			}
  			if(typeof settings.filters !== 'undefined') {
  				this.filterValues = {};
  				_.each(settings.filters, function(item, index) {
  					this.filterValues[_.find(this.options.filterFields, {search: index}).id] = item
  				}.bind(this))
  			}
  
  			if(typeof settings.sort !== 'undefined') {
  					processSort(settings.sort || options.sort, settings.reverse);
  			}
  			
  			if(typeof this.filter !== 'undefined') {
  				this.filter.set(this.filterValues)
  				this.filterValues = this.filter.toJSON();
  
  			}
  			if(typeof settings.search !== 'undefined' && settings.search !== '') {
  				this.$el.find('[name="search"]').val(settings.search)
  			}
  
  			this.options.page = settings.page || this.options.page;
  			this.options.count = settings.count || this.options.count;
  			this.draw();
  		}.bind(this)
  	}
  	this.models = [];
  	this.options = options;
  
  	this.filterMap = {}
  	_.map(options.filterFields, function(item){
  			this.filterMap[item.id] = item.search ;
  	}.bind(this));
  	
  	// var fields = {
  	// 	Title: {},
  	// 	Feed: {type: 'select', label_key: 'title', value_key: '_id', required: true, default: {title: 'Current Collection', _id: 'collection'}},
  	// }
  	
  	this.getCSV = function(title){
  		if(typeof title !== "string") {
  			title =this.options.title
  		}
  
  		_.csvify(
  			_.map(this.filtered, function(item){return item.attributes}),
  			_.map(_.filter(this.summary.items, function(item){return item.isEnabled}) ,function(filterMap,item){
  				return {label:item.label,name:filterMap[item.cname]} 
  			}.bind(null,this.filterMap)),
  			title 
  		)
  	}
  	
  	var container = options.el;
  	if(typeof options.el == 'string'){
  			container = document.querySelector(container);
  	}
  	if(container !== null) {
  		container.innerHTML = gform.renderString(template, summary);
  	
  		onload.call(this, $(container));
  	}
  
  
  	this.$el.find('[name="search"]').focus();
  
  	this.$el.find('.table-container > div').css('overflow', 'auto');
  	$(window).on('resize orientationChange', this.fixStyle.bind(this));
  	if(loaded){
  		this.state.set(loaded);
  	}
  }
  GrapheneDataGrid.version = '0.0.4.2';
  