text: // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  // MIT License. See license.txt
  
  frappe.views.get_listview = function(doctype, parent) {
  	if(frappe.listviews[doctype]) {
  		var listview = new frappe.listviews[doctype](parent);
  	} else {
  		var listview = new frappe.views.ListView(parent, doctype);
  	}
  	return listview;
  }
  
  // Renders customized list
  // usually based on `in_list_view` property
  
  frappe.views.ListView = Class.extend({
  	init: function(doclistview, doctype) {
  		this.doclistview = doclistview;
  		this.doctype = doctype;
  		this.meta = frappe.get_doc("DocType", this.doctype);
  		this.settings = frappe.listview_settings[this.doctype] || {};
  		if(this.meta.__listview_template) {
  			this.template_name = doctype + "_listview";
  			frappe.templates[this.template_name] = this.meta.__listview_template;
  		}
  		this.set_fields();
  		this.set_columns();
  		this.id_list = [];
  		if(this.settings.group_by)
  			this.group_by = this.settings.group_by;
  
  		var me = this;
  		this.doclistview.onreset = function() {
  			me.id_list = [];
  		}
  		this.order_by = this.settings.order_by;
  		this.group_by = this.settings.group_by;
  	},
  	set_fields: function() {
  		var me = this;
  		var t = "`tab"+this.doctype+"`.";
  		this.fields = [];
  		this.stats = ['_user_tags'];
  
  		var add_field = function(fieldname) {
  			field = t + "`" + fieldname + "`"
  			if(me.fields.indexOf(field)=== -1)
  				me.fields.push(field);
  		}
  
  		$.each(['name', 'owner', 'docstatus', '_user_tags', '_comments', 'modified',
  			'modified_by', '_assign', '_liked_by', '_seen'],
  		function(i, fieldname) { add_field(fieldname); })
  
  		// add title field
  		if(this.meta.title_field) {
  			this.title_field = this.meta.title_field;
  			add_field(this.meta.title_field);
  		}
  
  		// add workflow field (as priority)
  		this.workflow_state_fieldname = frappe.workflow.get_state_fieldname(this.doctype);
  		if(this.workflow_state_fieldname) {
  			add_field(this.workflow_state_fieldname);
  			this.stats.push(this.workflow_state_fieldname);
  		}
  
  		$.each(this.meta.fields, function(i,d) {
  			if(d.in_list_view && frappe.perm.has_perm(me.doctype, d.permlevel, "read")) {
  				if(d.fieldtype=="Image" && d.options) {
  					add_field(d.options);
  				} else {
  					add_field(d.fieldname);
  				}
  
  				if(d.fieldtype=="Select") {
  					if(me.stats.indexOf(d.fieldname)===-1) me.stats.push(d.fieldname);
  				}
  
  				// currency field for symbol (multi-currency)
  				if(d.fieldtype=="Currency" && d.options) {
  					if(d.options.indexOf(":")!=-1) {
  						add_field(d.options.split(":")[1]);
  					} else {
  						add_field(d.options);
  					};
  				}
  			}
  		});
  
  		// additional fields
  		if(this.settings.add_fields) {
  			$.each(this.settings.add_fields, function(i, d) {
  				if(d.indexOf("`tab")===-1) {
  					d = "`tab" + me.doctype + "`." + d;
  				}
  				if(me.fields.indexOf(d)==-1)
  					me.fields.push(d);
  			});
  		}
  	},
  	set_columns: function() {
  		var me = this;
  		this.columns = [];
  		var name_column = {
  			colspan: this.settings.colwidths && this.settings.colwidths.subject || 6,
  			type: "Subject",
  			title: "Name"
  		};
  		if (this.meta.title_field) {
  			name_column.title = frappe.meta.get_docfield(this.doctype, this.meta.title_field).label;
  		}
  		this.columns.push(name_column);
  		this.total_colspans = this.columns[0].colspan;
  
  		if(frappe.model.is_submittable(this.doctype)
  			|| this.settings.get_indicator || this.workflow_state_fieldname) {
  			// indicator
  			this.columns.push({
  				colspan: this.settings.colwidths && this.settings.colwidths.indicator || 3,
  				type: "Indicator",
  				title: "Status"
  			});
  			this.total_colspans += this.columns[1].colspan;
  		}
  
  		// overridden
  		var overridden = $.map(this.settings.add_columns || [], function(d) {
  			return d.content;
  		});
  		var docfields_in_list_view = frappe.get_children("DocType", this.doctype, "fields",
  			{"in_list_view":1}).sort(function(a, b) { return a.idx - b.idx })
  
  		$.each(docfields_in_list_view, function(i,d) {
  			if(in_list(overridden, d.fieldname) || d.fieldname === me.title_field) {
  				return;
  			}
  			if(me.total_colspans < 12) {
  				me.add_column(d);
  			}
  		});
  
  		// additional columns
  		if(this.settings.add_columns) {
  			$.each(this.settings.add_columns, function(i, d) {
  				if(me.total_colspans < 12) {
  					if(typeof d==="string") {
  						me.add_column(frappe.meta.get_docfield(me.doctype, d));
  					} else {
  						me.columns.push(d);
  						me.total_colspans += parseInt(d.colspan);
  					}
  				}
  			});
  		}
  
  		var empty_cols = flt(12 - this.total_colspans);
  		while(empty_cols > 0) {
  			for(var i=0, l=this.columns.length; i < l && empty_cols > 0; i++) {
  				this.columns[i].colspan = cint(this.columns[i].colspan) + 1;
  				empty_cols = empty_cols - 1;
  			}
  		}
  	},
  	add_column: function(df) {
  		// field width
  		var colspan = 3;
  		if(in_list(["Int", "Percent"], df.fieldtype)) {
  			colspan = 2;
  		} else if(in_list(["Check", "Image"], df.fieldtype)) {
  			colspan = 1;
  		} else if(in_list(["name", "subject", "title"], df.fieldname)) { // subjects are longer
  			colspan = 4;
  		} else if(df.fieldtype=="Text Editor" || df.fieldtype=="Text") {
  			colspan = 4;
  		}
  		this.total_colspans += parseInt(colspan);
  		var col = {
  			colspan: colspan,
  			content: df.fieldname,
  			type: df.fieldtype,
  			df:df,
  			fieldtype: df.fieldtype,
  			fieldname: df.fieldname,
  			title:__(df.label)
  		};
  		if(this.settings.column_render && this.settings.column_render[df.fieldname]) {
  			col.render = this.settings.column_render[df.fieldname];
  		}
  		this.columns.push(col);
  
  	},
  	render: function(row, data) {
  		this.prepare_data(data);
  
  		// maintain id_list to avoid duplication incase
  		// of filtering by child table
  		if(in_list(this.id_list, data.name)) {
  			$(row).toggle(false);
  			return;
  		} else {
  			this.id_list.push(data.name);
  		}
  
  		if(this.meta && this.meta.image_view == 0){
  			this.render_list_row(row, data);
  		}
  		else{
  			this.render_list_image(row, data);
  		}
  
  		if(this.settings.post_render_item) {
  			this.settings.post_render_item(this, row, data);
  		}
  
  		this.render_tags(row, data);
  
  	},
  	render_list_row: function(row, data) {
  		var main = frappe.render_template("list_item_main", {
  			data: data,
  			columns: this.columns,
  			subject: this.get_avatar_and_id(data, true),
  			list: this,
  			right_column: this.settings.right_column
  		});
  
  		$(frappe.render_template("list_item_row", {
  			data: data,
  			main: main,
  			list: this,
  			right_column: this.settings.right_column
  		})).appendTo(row);
  	},
  	render_list_image: function(row, data) {
  		this.allowed_type = [
  			"Check", "Currency", "Data", "Date",
  			"Datetime", "Float", "Int", "Link",
  			"Percent", "Select", "Read Only", "Time"
  		];
  		img_col = $(frappe.render_template("image_view_item_row", {
  			data: data,
  			list: this,
  			columns: this.columns,
  			allowed_type: this.allowed_type,
  			item_image: data.image ? "url('" + data.image + "')" : null,
  			color: frappe.get_palette(data.item_name),
  			subject: this.get_avatar_and_id(data, true),
  			right_column: this.settings.right_column
  		}))
  		.data("data", data)
  		.appendTo($(row).find(".image-view-marker"));
  	},
  	render_tags: function(row, data) {
  		var me = this;
  		var row2 = $('<div class="tag-row">\
  			<div class="list-tag xs-hidden"></div>\
  			<div class="clearfix"></div>\
  		</div>').appendTo(row);
  
  		if(!me.doclistview.tags_shown) {
  			row2.addClass("hide");
  		}
  
  		// add tags
  		var tag_editor = new frappe.ui.TagEditor({
  			parent: row2.find(".list-tag"),
  			frm: {
  				doctype: this.doctype,
  				docname: data.name
  			},
  			user_tags: data._user_tags,
  			on_change: function(user_tags) {
  				data._user_tags = user_tags;
  				//me.render_timestamp_and_comments(row, data);
  			}
  		});
  		tag_editor.$w.on("click", ".tagit-label", function() {
  			me.doclistview.set_filter("_user_tags",
  				$(this).text());
  		});
  	},
  
  	get_avatar_and_id: function(data, without_workflow) {
  		data._without_workflow = without_workflow;
  		data.css_seen = '';
  
  		if(data._seen) {
  			var seen = JSON.parse(data._seen);
  			if(seen && seen.indexOf(frappe.session.user) !== -1) {
  				data.css_seen = 'seen'
  			}
  		}
  
  		return frappe.render_template("list_item_subject", data);
  	},
  
  	get_indicator: function(doc) {
          var indicator = frappe.get_indicator(doc, this.doctype);
  		if(indicator) {
  	        return '<span class="indicator '+indicator[1]+' filterable" data-filter="'
  				+indicator[2]+'">'+__(indicator[0])+'<span>';
  		} else {
  			return "";
  		}
  	},
  
  	get_indicator_dot: function(doc) {
  		var indicator = frappe.get_indicator(doc, this.doctype);
  		if (!indicator) {
  			return "";
  		}
  		return '<span class="indicator '+indicator[1]+'" title="'+__(indicator[0])+'"></span>';
  	},
  
  	prepare_data: function(data) {
  		if(data.modified)
  			this.prepare_when(data, data.modified);
  
  		data._liked_by = data._liked_by ?
  			JSON.parse(data._liked_by) : [];
  
  		data._checkbox = (frappe.model.can_delete(this.doctype) || this.settings.selectable) && !this.no_delete
  
  		data._doctype_encoded = encodeURIComponent(data.doctype);
  		data._name = data.name.replace(/"/g, '\"');
  		data._name_encoded = encodeURIComponent(data.name);
  		data._submittable = frappe.model.is_submittable(this.doctype);
  
  		data._title = strip_html(data[this.title_field || "name"] || data["name"]);
  		data._full_title = data._title;
  
  		if(data._title.length > 40) {
  			data._title = data._title.slice(0, 40) + "...";
  		}
  
  		data._workflow = null;
  		if(this.workflow_state_fieldname) {
  			data._workflow = {
  				fieldname: this.workflow_state_fieldname,
  				value: data[this.workflow_state_fieldname],
  				style: frappe.utils.guess_style(data[this.workflow_state_fieldname])
  			}
  		}
  		data._user = user;
  
  		data._tags = $.map((data._user_tags || "").split(","),
  			function(v) { return v ? v : null; });
  		data._assign_list = data._assign ? JSON.parse(data._assign) : [];
  
  		// nulls as strings
  		for(key in data) {
  			if(data[key]==null) {
  				data[key]='';
  			}
  		}
  
  		// prepare data in settings
  		if(this.settings.prepare_data)
  			this.settings.prepare_data(data);
  	},
  
  	prepare_when: function(data, date_str) {
  		if (!date_str) date_str = data.modified;
  		// when
  		data.when = (dateutil.str_to_user(date_str)).split(' ')[0];
  		var diff = dateutil.get_diff(dateutil.get_today(), date_str.split(' ')[0]);
  		if(diff==0) {
  			data.when = comment_when(date_str);
  		}
  		if(diff == 1) {
  			data.when = __('Yesterday')
  		}
  		if(diff == 2) {
  			data.when = __('2 days ago')
  		}
  	},
  
  	render_bar_graph: function(parent, data, field, label) {
  		var args = {
  			percent: data[field],
  			label: __(label)
  		}
  		$(parent).append(repl('<span class="progress" style="width: 100%; float: left; margin: 5px 0px;"> \
  			<span class="progress-bar" title="%(percent)s% %(label)s" \
  				style="width: %(percent)s%;"></span>\
  		</span>', args));
  	},
  	render_icon: function(parent, icon_class, label) {
  		var icon_html = "<i class='%(icon_class)s' title='%(label)s'></i>";
  		$(parent).append(repl(icon_html, {icon_class: icon_class, label: __(label) || ''}));
  	}
  });
  