frappe.provide('frappe.views');

frappe.views.ListView = class ListView extends frappe.views.BaseList {
	static load_last_view() {
		const route = frappe.get_route();
		const doctype = route[1];

		if (route.length === 2) {
			// List/{doctype} => List/{doctype}/{last_view} or List
			const user_settings = frappe.get_user_settings(doctype);
			frappe.set_route('List', doctype, user_settings.last_view || 'List');
			return true;
		}
		return false;
	}

	get view_name() {
		// ListView -> List
		return this.constructor.name.split('View')[0];
	}

	show() {
		this.init().then(() => {
			if (frappe.route_options) {
				this.set_filters_from_route_options();
				return;
			} else {
				this.refresh();
			}
		});
	}

	get view_user_settings() {
		return this.user_settings[this.view_name] || {};
	}

	setup_defaults() {
		super.setup_defaults();
		// initialize with saved filters
		const saved_filters = this.view_user_settings.filters;
		if (saved_filters) {
			this.filters = saved_filters;
		} else {
			// filters in listview_settings
			const filters = (this.settings.filters || []).map(f => {
				if (f.length === 3) {
					f = [this.doctype, f[0], f[1], f[2]];
				}
				return f;
			});

			this.filters = filters;
		}
		// initialize with saved order by
		this.order_by = this.view_user_settings.order_by || 'modified desc';
		// buld menu items)
		this.menu_items = this.menu_items.concat(this.get_menu_items());

		this.patch_refresh_and_load_lib();
	}

	patch_refresh_and_load_lib() {
		// throttle refresh for 1s
		this.refresh = this.refresh.bind(this);
		this.refresh = frappe.utils.throttle(this.refresh, 1000);
		this.load_lib = new Promise(resolve => {
			if (this.required_libs) {
				frappe.require(this.required_libs, resolve);
			} else {
				resolve();
			}
		});
		// call refresh every 5 minutes
		const interval = 5 * 60 * 1000;
		setInterval(this.refresh, interval);
	}

	setup_page_head() {
		super.setup_page_head();
		this.set_primary_action();
	}

	set_primary_action() {
		if (this.can_create) {
			this.page.set_primary_action(__('New'), () => {
				this.make_new_doc();
			}, 'octicon octicon-plus');
		} else {
			this.page.clear_primary_action();
		}
	}

	make_new_doc() {
		const doctype = this.doctype;
		const options = {};
		this.filter_area.get().forEach(f => {
			if (f[2] === "=" && frappe.model.is_non_std_field(f[1])) {
				options[f[1]] = f[3];
			}
		});
		frappe.new_doc(doctype, options);
	}

	setup_view() {
		this.setup_columns();
		this.setup_events();
		this.settings.onload && this.settings.onload(this);
	}

	setup_footnote_area() {
		const match_rules_list = frappe.perm.get_match_rules(this.doctype);

		if (match_rules_list.length) {
			this.$footnote_area =
				frappe.utils.set_footnote(this.$footnote_area, this.$frappe_list,
					frappe.render_template('list_permission_footer', {
						condition_list: match_rules_list
					}));
		}
	}

	setup_columns() {
		// setup columns for list view
		this.columns = [];

		const get_df = frappe.meta.get_docfield.bind(null, this.doctype);

		// 1st column: title_field or name
		if (this.meta.title_field) {
			this.columns.push({
				type: 'Subject',
				df: get_df(this.meta.title_field)
			});
		} else {
			this.columns.push({
				type: 'Subject',
				df: {
					label: __('Name'),
					fieldname: 'name'
				}
			});
		}

		// 2nd column: Status indicator
		if (frappe.has_indicator(this.doctype)) {
			// indicator
			this.columns.push({
				type: 'Status'
			});
		}

		const fields_in_list_view = this.get_fields_in_list_view();
		// Add rest from in_list_view docfields
		this.columns = this.columns.concat(
			fields_in_list_view
				.filter(df => {
					if (frappe.has_indicator(this.doctype) && df.fieldname === 'status') {
						return false;
					}
					if (!df.in_list_view) {
						return false;
					}
					return df.fieldname !== this.meta.title_field;
				})
				.map(df => ({
					type: 'Field',
					df
				}))
		);

		// limit to 4 columns
		this.columns = this.columns.slice(0, 4);
	}

	get_no_result_message() {
		const new_button = this.can_create ?
			`<p><button class="btn btn-primary btn-sm btn-new-doc">
				${__('Make a new {0}', [__(this.doctype)])}
			</button></p>` : '';

		return `<div class="msg-box no-border">
			<p>${__('No {0} found', [__(this.doctype)])}</p>
			${new_button}
		</div>`;
	}

	get_args() {
		const args = super.get_args();

		return Object.assign(args, {
			with_comment_count: true
		});
	}

	// freeze(toggle) {
	// 	if (this.view_name !== 'List') return;

	// 	this.$freeze.toggle(toggle);
	// 	this.$result.toggle(!toggle);

	// 	const columns = this.columns;
	// 	if (toggle) {
	// 		if (this.$freeze.find('.freeze-row').length > 0) return;

	// 		const html = `
	// 			${this.get_header_html()}
	// 			${Array.from(new Array(10)).map(loading_row).join('')}
	// 		`;
	// 		this.$freeze.html(html);
	// 	}

	// 	function loading_row() {
	// 		return `
	// 			<div class="list-row freeze-row level">
	// 				<div class="level-left">
	// 					<div class="list-row-col list-subject"></div>
	// 					${columns.slice(1).map(c => `<div class="list-row-col"></div>`).join('')}
	// 				</div>
	// 				<div class="level-right"></div>
	// 			</div>
	// 		`;
	// 	}
	// }

	toggle_result_area() {
		super.toggle_result_area();
		this.toggle_delete_button(
			this.$result.find('.list-row-check:checked').length > 0
		);
	}

	before_render() {
		this.settings.before_render && this.settings.before_render();
		frappe.model.user_settings.save(this.doctype, 'last_view', this.view_name);
		this.save_view_user_settings({
			filters: this.filter_area.get(),
			order_by: this.sort_selector.get_sql_string()
		});
	}

	render() {
		if (this.data.length > 0) {
			const html = `
				${this.get_header_html()}
				${this.data.map(doc => this.get_list_row_html(doc)).join('')}
			`;
			this.$result.html(html);
		}
		this.render_count();
		this.render_tags();
	}

	render_count() {
		this.get_count_html()
			.then(html => {
				this.$result.find('.list-count').html(html);
			});
	}

	render_tags() {
		const $list_rows = this.$result.find('.list-row-container');

		this.data.forEach((d, i) => {
			let tag_html = $(`<div class='tag-row'>
				<div class='list-tag hidden-xs'></div>
			</div>`).appendTo($list_rows.get(i));

			// add tags
			let tag_editor = new frappe.ui.TagEditor({
				parent: tag_html.find('.list-tag'),
				frm: {
					doctype: this.doctype,
					docname: d.name
				},
				list_sidebar: this.list_sidebar,
				user_tags: d._user_tags,
				on_change: function (user_tags) {
					d._user_tags = user_tags;
				}
			});

			tag_editor.wrapper.on('click', '.tagit-label', (e) => {
				const $this = $(e.currentTarget);
				this.filter_area.add(this.doctype, '_user_tags', '=', $this.text());
			});
		});
	}

	get_header_html() {
		const subject_field = this.columns[0].df;
		let subject_html = `
			<input class="level-item list-check-all hidden-xs" type="checkbox" title="${__("Select All")}">
			<span class="level-item list-liked-by-me">
				<i class="octicon octicon-heart text-extra-muted" title="${__("Likes")}"></i>
			</span>
			<span class="level-item">${__(subject_field.label)}</span>
		`;
		const $columns = this.columns.map(col => {
			let classes = [
				'list-row-col ellipsis',
				col.type == 'Subject' ? 'list-subject level' : 'hidden-xs',
				frappe.model.is_numeric_field(col.df) ? 'text-right' : ''
			].join(' ');

			return `
				<div class="${classes}">
					${col.type === 'Subject' ? subject_html : `
					<span>${__(col.df && col.df.label || col.type)}</span>`}
				</div>
			`;
		}).join('');

		return this.get_header_html_skeleton($columns, '<span class="list-count"></span>');
	}

	get_header_html_skeleton(left = '', right = '') {
		return `
			<header class="level list-row list-row-head text-muted small">
				<div class="level-left list-header-subject">
					${left}
				</div>
				<div class="level-left checkbox-actions">
					<div class="level list-subject">
						<input class="level-item list-check-all hidden-xs" type="checkbox" title="${__("Select All")}">
						<span class="level-item list-header-meta"></span>
					</div>
				</div>
				<div class="level-right">
					${right}
				</div>
			</header>
		`;
	}

	get_left_html(doc) {
		return this.columns.map(col => this.get_column_html(col, doc)).join('');
	}

	get_right_html(doc) {
		return this.get_meta_html(doc);
	}

	get_list_row_html(doc) {
		return this.get_list_row_html_skeleton(this.get_left_html(doc), this.get_right_html(doc));
	}

	get_list_row_html_skeleton(left = '', right = '') {
		return `
			<div class="list-row-container">
				<div class="level list-row small">
					<div class="level-left ellipsis">
						${left}
					</div>
					<div class="level-right text-muted ellipsis">
						${right}
					</div>
				</div>
			</div>
		`;
	}

	get_column_html(col, doc) {
		if (col.type === 'Status') {
			return `
				<div class="list-row-col hidden-xs ellipsis">
					${this.get_indicator_html(doc)}
				</div>
			`;
		}

		const df = col.df || {};
		const label = df.label;
		const fieldname = df.fieldname;
		const value = doc[fieldname] || '';

		// listview_setting formatter
		const formatters = this.settings.formatters;

		const format = () => {
			if (formatters && formatters[fieldname]) {
				return formatters[fieldname](value, df, doc);
			} else if (df.fieldtype === 'Code') {
				return value;
			} else {
				return frappe.format(value, df, null, doc);
			}
		};

		const field_html = () => {
			let html;
			const _value = typeof value === 'string' ? frappe.utils.escape_html(value) : value;

			if (df.fieldtype === 'Image') {
				html = df.options ?
					`<img src="${doc[df.options]}" style="max-height: 30px; max-width: 100%;">` :
					`<div class="missing-image small">
						<span class="octicon octicon-circle-slash"></span>
					</div>`;
			} else if (df.fieldtype === 'Select') {
				html = `<span class="filterable indicator ${frappe.utils.guess_colour(_value)} ellipsis"
					data-filter="${fieldname},=,${value}">
					${__(_value)}
				</span>`;
			} else if (df.fieldtype === 'Link') {
				html = `<a class="filterable text-muted ellipsis"
					data-filter="${fieldname},=,${value}">
					${_value}
				</a>`;
			} else if (df.fieldtype === 'Text Editor') {
				html = `<span class="text-muted ellipsis">
					${_value}
				</span>`;
			} else {
				html = `<a class="filterable text-muted ellipsis"
					data-filter="${fieldname},=,${value}">
					${format()}
				</a>`;
			}

			return `<span class="ellipsis"
				title="${__(label) + ': ' + _value}">
				${html}
			</span>`;
		};

		const class_map = {
			Subject: 'list-subject level',
			Field: 'hidden-xs'
		};
		const css_class = [
			'list-row-col ellipsis',
			class_map[col.type],
			frappe.model.is_numeric_field(df) ? 'text-right' : ''
		].join(' ');

		const html_map = {
			Subject: this.get_subject_html(doc),
			Field: field_html()
		};
		const column_html = html_map[col.type];

		return `
			<div class="${css_class}">
				${column_html}
			</div>
		`;
	}

	get_meta_html(doc) {
		let html = '';
		if (doc[this.meta.title_field || ''] !== doc.name) {
			html += `
				<div class="level-item hidden-xs hidden-sm ellipsis">
					<a class="text-muted ellipsis" href="${this.get_form_link(doc)}">
						${doc.name}
					</a>
				</div>
			`;
		}
		const modified = comment_when(doc.modified, true);

		const last_assignee = JSON.parse(doc._assign || '[]').slice(-1)[0];
		const assigned_to = last_assignee ?
			`<span class="filterable"
				data-filter="_assign,like,%${last_assignee}%">
				${frappe.avatar(last_assignee)}
			</span>` :
			`<span class="avatar avatar-small avatar-empty"></span>`;

		const comment_count =
			`<span class="${!doc._comment_count ? 'text-extra-muted' : ''} comment-count">
				<i class="octicon octicon-comment-discussion"></i>
				${doc._comment_count > 99 ? "99+" : doc._comment_count}
			</span>`;

		html += `
			<div class="level-item hidden-xs list-row-activity">
				${modified}
				${assigned_to}
				${comment_count}
			</div>
			<div class="level-item visible-xs text-right">
				${this.get_indicator_dot(doc)}
			</div>
		`;

		return html;
	}

	get_count_html() {
		const current_count = this.data.length;

		return frappe.call({
			type: 'GET',
			method: this.method,
			args: {
				doctype: this.doctype,
				filters: this.get_filters_for_args(),
				fields: [`count(${frappe.model.get_full_column_name('name', this.doctype)}) as total_count`]
			}
		}).then(r => {
			const count = r.message.values[0][0] || current_count;
			const str = __('{0} of {1}', [current_count, count]);
			const html = `<span>${str}</span>`;
			return html;
		});
	}

	get_form_link(doc) {
		const docname = doc.name.match(/[%'"]/)
			? encodeURIComponent(doc.name)
			: doc.name;

		return '#Form/' + this.doctype + '/' + docname;
	}

	get_subject_html(doc) {
		let user = frappe.session.user;
		let subject_field = this.columns[0].df;
		let value = doc[subject_field.fieldname] || doc.name;
		let subject = strip_html(value);
		let escaped_subject = frappe.utils.escape_html(value);

		const liked_by = JSON.parse(doc._liked_by || '[]');
		let heart_class = liked_by.includes(user) ?
			'liked-by' : 'text-extra-muted not-liked';

		const seen = JSON.parse(doc._seen || '[]')
			.includes(user) ? 'seen' : '';

		let subject_html = `
			<input class="level-item list-row-checkbox hidden-xs" type="checkbox" data-name="${doc.name}">
			<span class="level-item" style="margin-bottom: 1px;">
				<i class="octicon octicon-heart like-action ${heart_class}"
					data-name="${doc.name}" data-doctype="${this.doctype}"
					data-liked-by="${encodeURI(doc._liked_by) || '[]'}"
				>
				</i>
				<span class="likes-count">
					${ liked_by.length > 99 ? __("99") + '+' : __(liked_by.length || '')}
				</span>
			</span>
			<span class="level-item ${seen} ellipsis" title="${escaped_subject}">
				<a class="ellipsis" href="${this.get_form_link(doc)}" title="${escaped_subject}">
				${subject}
				</a>
			</span>
		`;

		return subject_html;
	}

	get_indicator_html(doc) {
		const indicator = frappe.get_indicator(doc, this.doctype);
		if (indicator) {
			return `<span class="indicator ${indicator[1]} filterable"
				data-filter='${indicator[2]}'>
				${__(indicator[0])}
			<span>`;
		}
		return '';
	}

	get_indicator_dot(doc) {
		const indicator = frappe.get_indicator(doc, this.doctype);
		if (!indicator) return '';
		return `<span class='indicator ${indicator[1]}' title='${__(indicator[0])}'></span>`;
	}

	setup_events() {
		// filterable events
		this.$result.on('click', '.filterable', e => {
			if (e.metaKey || e.ctrlKey) return;
			e.stopPropagation();
			const $this = $(e.currentTarget);
			const filters = $this.attr('data-filter').split('|');

			const filters_to_apply = filters.map(f => {
				f = f.split(',');
				if (f[2] === 'Today') {
					f[2] = frappe.datetime.get_today();
				} else if (f[2] == 'User') {
					f[2] = frappe.session.user;
				}
				return [this.doctype, f[0], f[1], f.slice(2).join(',')];
			});
			this.filter_area.add(filters_to_apply);
		});

		this.$result.on('click', '.list-row', (e) => {
			const $target = $(e.target);

			// tick checkbox if Ctrl/Meta key is pressed
			if (e.ctrlKey || e.metaKey && !$target.is('a')) {
				const $list_row = $(e.currentTarget);
				const $check = $list_row.find('.list-row-checkbox');
				$check.prop('checked', !$check.prop('checked'));
				e.preventDefault();
				this.on_row_checked();
				return;
			}

			// don't open form when checkbox, like, filterable are clicked
			if ($target.hasClass('filterable') ||
				$target.hasClass('octicon-heart') ||
				$target.is(':checkbox') ||
				$target.is('a')
			) {
				return;
			}

			// open form
			const $row = $(e.currentTarget);
			const link = $row.find('.list-subject a').get(0);
			if (link) {
				window.location.href = link.href;
				return false;
			}
		});

		// toggle tags
		this.list_sidebar.parent.on('click', '.list-tag-preview', () => {
			this.toggle_tags();
		});

		this.$no_result.find('.btn-new-doc').click(() => this.make_new_doc());

		this.setup_check_events();
		this.setup_like();
	}

	setup_check_events() {
		this.$result.on('change', 'input[type=checkbox]', e => {
			const $target = $(e.currentTarget);

			if ($target.is('.list-header-subject .list-check-all')) {
				const $check = this.$result.find('.checkbox-actions .list-check-all');
				$check.prop('checked', $target.prop('checked'));
				$check.trigger('change');
			} else if ($target.is('.checkbox-actions .list-check-all')) {
				const $check = this.$result.find('.list-header-subject .list-check-all');
				$check.prop('checked', $target.prop('checked'));

				this.$result.find('.list-row-checkbox')
					.prop('checked', $target.prop('checked'));
			}

			this.on_row_checked();
		});

		this.$result.on('click', '.list-row-checkbox', e => {
			const $target = $(e.currentTarget);

			// shift select checkboxes
			if (e.shiftKey && this.$checkbox_cursor && !$target.is(this.$checkbox_cursor)) {
				const name_1 = this.$checkbox_cursor.data().name;
				const name_2 = $target.data().name;
				const index_1 = this.data.findIndex(d => d.name === name_1);
				const index_2 = this.data.findIndex(d => d.name === name_2);
				let [min_index, max_index] = [index_1, index_2];

				if (min_index > max_index) {
					[min_index, max_index] = [max_index, min_index];
				}

				let docnames = this.data.slice(min_index + 1, max_index).map(d => d.name);
				const selector = docnames.map(name => `.list-row-checkbox[data-name="${name}"]`).join(',');
				this.$result.find(selector).prop('checked', true);
			}

			this.$checkbox_cursor = $target;
		});
	}

	setup_like() {
		this.$result.on('click', '.like-action', frappe.ui.click_toggle_like);
		this.$result.on('click', '.list-liked-by-me', e => {
			const $this = $(e.currentTarget);
			$this.toggleClass('active');

			if ($this.hasClass('active')) {
				this.filter_area.add(this.doctype, '_liked_by', 'like', '%' + frappe.session.user + '%');
			} else {
				this.filter_area.remove('_liked_by');
			}
		});

		frappe.ui.setup_like_popover(this.$result, '.liked-by');
	}

	on_row_checked() {
		this.$list_head_subject = this.$list_head_subject || this.$result.find('header .list-header-subject');
		this.$checkbox_actions = this.$checkbox_actions || this.$result.find('header .checkbox-actions');

		this.$checks = this.$result.find('.list-row-checkbox:checked');

		this.$list_head_subject.toggle(this.$checks.length === 0);
		this.$checkbox_actions.toggle(this.$checks.length > 0);

		if (this.$checks.length === 0) {
			this.$list_head_subject.find('.list-select-all').prop('checked', false);
		} else {
			this.$checkbox_actions.find('.list-header-meta').html(
				__('{0} items selected', [this.$checks.length])
			);
			this.$checkbox_actions.show();
			this.$list_head_subject.hide();
		}

		if (this.can_delete) {
			this.toggle_delete_button(this.$checks.length > 0);
		}
	}

	toggle_delete_button(toggle) {
		if (toggle) {
			this.page.set_primary_action(__('Delete'),
				() => this.delete_items(),
				'octicon octicon-trashcan'
			).addClass('btn-danger');
		} else {
			this.page.btn_primary.removeClass('btn-danger');
			this.set_primary_action();
		}
	}

	toggle_tags() {
		this.$result.toggleClass('tags-shown');
	}

	delete_items() {
		const docnames = this.get_checked_items(true);

		frappe.confirm(__('Delete {0} items permanently?', [docnames.length]),
			() => {
				frappe.call({
					method: 'frappe.desk.reportview.delete_items',
					freeze: true,
					args: {
						items: docnames,
						doctype: this.doctype
					}
				}).then((r) => {
					let failed = r.message;
					if (!failed) failed = [];

					if (failed.length && !r._server_messages) {
						frappe.throw(__('Cannot delete {0}', [failed.map(f => f.bold()).join(', ')]));
					}
					if (failed.length < docnames.length) {
						frappe.utils.play_sound('delete');
						this.refresh(true);
					}
				});
			}
		);
	}

	get_checked_items(only_docnames) {
		const docnames = Array.from(this.$checks || [])
			.map(check => $(check).data().name);

		if (only_docnames) return docnames;

		return this.data.filter(d => docnames.includes(d.name));
	}

	save_view_user_settings(obj) {
		return frappe.model.user_settings.save(this.doctype, this.view_name, obj);
	}

	on_update(data) {
		if (data.doctype === this.doctype) {
			this.refresh();
		}
	}

	get_menu_items() {
		const doctype = this.doctype;
		const items = [];

		if (frappe.model.can_import(doctype)) {
			items.push({
				label: __('Import'),
				action: () => frappe.set_route('List', 'Data Import', {
					reference_doctype: doctype
				}),
				standard: true
			});
		}
		if (frappe.model.can_set_user_permissions(doctype)) {
			items.push({
				label: __('User Permissions'),
				action: () => frappe.set_route('List', 'User Permission', {
					allow: doctype
				}),
				standard: true
			});
		}
		if (frappe.user_roles.includes('System Manager')) {
			items.push({
				label: __('Role Permissions Manager'),
				action: () => frappe.set_route('permission-manager', {
					doctype
				}),
				standard: true
			});

			items.push({
				label: __('Customize'),
				action: () => frappe.set_route('Form', 'Customize Form', {
					doc_type: doctype
				}),
				standard: true
			});
		}

		// utility
		const bulk_assignment = () => {
			return {
				label: __('Assign To'),
				action: () => {
					const docnames = this.get_checked_items(true);
					if (docnames.length > 0) {
						const dialog = new frappe.ui.form.AssignToDialog({
							obj: this,
							method: 'frappe.desk.form.assign_to.add_multiple',
							doctype: this.doctype,
							docname: docnames,
							bulk_assign: true,
							re_assign: true,
							callback: () => this.refresh(true)
						});
						dialog.clear();
						dialog.show();
					} else {
						frappe.msgprint(__('Select records for assignment'));
					}
				},
				standard: true
			};
		};

		const bulk_printing = () => {
			const print_settings = frappe.model.get_doc(':Print Settings', 'Print Settings');
			const allow_print_for_draft = cint(print_settings.allow_print_for_draft);
			const is_submittable = frappe.model.is_submittable(this.doctype);
			const allow_print_for_cancelled = cint(print_settings.allow_print_for_cancelled);

			return {
				label: __('Print'),
				action: () => {
					const items = this.get_checked_items();

					const valid_docs = items.filter(doc => {
						return !is_submittable || doc.docstatus === 1 ||
							(allow_print_for_cancelled && doc.docstatus == 2) ||
							(allow_print_for_draft && doc.docstatus == 0) ||
							frappe.user.has_role('Administrator');
					}).map(doc => doc.name);

					var invalid_docs = items.filter(doc => !valid_docs.includes(doc.name));

					if (invalid_docs.length > 0) {
						frappe.msgprint(__('You selected Draft or Cancelled documents'));
						return;
					}

					if (valid_docs.length > 0) {
						const dialog = new frappe.ui.Dialog({
							title: __('Print Documents'),
							fields: [{
								'fieldtype': 'Check',
								'label': __('With Letterhead'),
								'fieldname': 'with_letterhead'
							},
							{
								'fieldtype': 'Select',
								'label': __('Print Format'),
								'fieldname': 'print_sel',
								options: frappe.meta.get_print_formats(this.doctype)
							}]
						});

						dialog.set_primary_action(__('Print'), args => {
							if (!args) return;
							const default_print_format = frappe.get_meta(this.doctype).default_print_format;
							const with_letterhead = args.with_letterhead ? 1 : 0;
							const print_format = args.print_sel ? args.print_sel : default_print_format;
							const json_string = JSON.stringify(valid_docs);

							const w = window.open('/api/method/frappe.utils.print_format.download_multi_pdf?' +
								'doctype=' + encodeURIComponent(this.doctype) +
								'&name=' + encodeURIComponent(json_string) +
								'&format=' + encodeURIComponent(print_format) +
								'&no_letterhead=' + (with_letterhead ? '0' : '1'));
							if (!w) {
								frappe.msgprint(__('Please enable pop-ups'));
								return;
							}
						});

						dialog.show();
					} else {
						frappe.msgprint(__('Select atleast 1 record for printing'));
					}
				},
				standard: true
			};
		};

		// bulk assignment
		items.push(bulk_assignment());

		if (frappe.model.can_print(doctype)) {
			items.push(bulk_printing());
		}

		// add to desktop
		items.push({
			label: __('Add to Desktop'),
			action: () => frappe.add_to_desktop(doctype, doctype),
			standard: true
		});

		if (frappe.user.has_role('System Manager') && frappe.boot.developer_mode === 1) {
			// edit doctype
			items.push({
				label: __('Edit DocType'),
				action: () => frappe.set_route('Form', 'DocType', doctype),
				standard: true
			});
		}

		return items;
	}

	set_filters_from_route_options() {
		const filters = [];
		for (let field in frappe.route_options) {
			var value = frappe.route_options[field];
			var doctype = null;

			// if `Child DocType.fieldname`
			if (field.includes('.')) {
				doctype = field.split('.')[0];
				field = field.split('.')[1];
			}

			// find the table in which the key exists
			// for example the filter could be {"item_code": "X"}
			// where item_code is in the child table.

			// we can search all tables for mapping the doctype
			if (!doctype) {
				doctype = frappe.meta.get_doctype_for_field(this.doctype, field);
			}

			if (doctype) {
				if ($.isArray(value)) {
					filters.push([doctype, field, value[0], value[1]]);
				} else {
					filters.push([doctype, field, "=", value]);
				}
			}
		}
		frappe.route_options = null;

		this.filter_area.add(filters);
	}

	static trigger_list_update(data) {
		const doctype = data.doctype;
		if (!doctype) return;
		frappe.provide('frappe.views.trees');

		// refresh tree view
		if (frappe.views.trees[doctype]) {
			frappe.views.trees[doctype].tree.refresh();
			return;
		}

		// refresh list view
		const page_name = frappe.get_route_str();
		const list_view = frappe.views.list_view[page_name];
		list_view && list_view.on_update(data);
	}
};

$(document).on('save', function (event, doc) {
	frappe.views.ListView.trigger_list_update(doc);
});