<commit_before>frappe.provide('frappe.views');

frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
	get view_name() {
		return 'Dashboard';
    }

    setup_defaults() {
		return super.setup_defaults()
			.then(() => {
                this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
			});
    }

    render() {

    }

    setup_view() {
        if (this.chart_group || this.number_card_group) {
            return;
        }

        this.setup_dashboard_page();
        this.setup_dashboard_customization();
        this.make_dashboard();
    }

    setup_dashboard_customization() {
        this.$customize = this.$chart_header.find('.customize-dashboard');
        this.$save_or_discard = this.$chart_header.find('.customize-options');
    }

    setup_dashboard_page() {
        const dashboard_name = __('{0} Dashboard', [this.doctype]);
        const chart_wrapper_html = `<div class="dashboard-view"></div>`;

        this.$frappe_list.html(chart_wrapper_html);
        this.page.clear_secondary_action();
        this.$dashboard_page = this.$page.find('.layout-main-section-wrapper').addClass('dashboard-page');
        this.$page.find('.page-form').empty().html(
            `

${dashboard_name}

${__('Customize')}

                        ${__('Reset')}
                     /
                        ${__('Save')}
                     /
                        ${__('Discard')}

`);

        this.$dashboard_wrapper = this.$page.find('.dashboard-view');
        this.$chart_header = this.$page.find('.dashboard-header');

        frappe.utils.bind_actions_with_object(this.$dashboard_page, this);
    }

    make_dashboard() {
        if (this.dashboard_settings) {
            this.charts = this.dashboard_settings.charts;
            this.number_cards = this.dashboard_settings.number_cards;
            this.render_dashboard();
        } else {
            frappe.run_serially([
                () => this.fetch_doctype_number_cards().then(cards => {
                    this.number_cards = cards;
                }),
                () => this.fetch_doctype_charts().then(charts => {
                    this.charts = charts;
                }),
                () => this.render_dashboard()
            ]);
        }
    }

    render_dashboard() {
        this.$dashboard_wrapper.empty();

        frappe.dashboard_utils.get_dashboard_settings().then(settings => {
            this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
            this.charts.map(chart => {
                chart.label = chart.chart_name;
                chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
            });
            this.render_dashboard_charts();
        });

        this.render_number_cards();

        if (!this.charts.length && !this.number_cards.length) {
            this.render_empty_state();
        }
    }

    fetch_doctype_charts() {
        return frappe.db.get_list('Dashboard Chart', {
			filters: {
				chart_type: ['in', ['Count', 'Sum', 'Group By']],
                document_type: this.doctype,
                is_standard: true
            },
            fields: ['*']
		}).then(charts => {
            return charts;
        });
    }

    fetch_doctype_number_cards() {
        return frappe.db.get_list('Number Card', {
			filters: {
                document_type: this.doctype,
                is_standard: true
            },
            fields: ['*']
		}).then(cards => {
            return cards;
        });
    }

    render_number_cards() {
        this.number_card_group = new frappe.widget.WidgetGroup({
            container: this.$dashboard_wrapper,
            type: "number_card",
            columns: 3,
            options: {
                allow_sorting: true,
                allow_create: true,
                allow_delete: true,
                allow_hiding: true,
            },
            default_values: {doctype: this.doctype},
            widgets: this.number_cards || [],
            in_customize_mode: true,
        });

        this.in_customize_mode && this.number_card_group.customize();
    }

    render_dashboard_charts() {
        this.chart_group = new frappe.widget.WidgetGroup({
            container: this.$dashboard_wrapper,
            type: "chart",
            columns: 2,
            options: {
                allow_sorting: true,
                allow_create: true,
                allow_delete: true,
                allow_hiding: true,
            },
            custom_dialog: () => this.show_add_chart_dialog(),
            widgets: this.charts,
            in_customize_mode: this.in_customize_mode || false,
        });

        this.in_customize_mode && this.chart_group.customize();
        this.chart_group.container.find('.widget-group-head').hide();
    }

    render_empty_state() {
        const no_result_message_html =
            `
${__("You haven't added any Dashboard Charts or Number Cards yet.")}

${__("Click On Customize to add your first widget")}
`;

        const customize_button =
	        `
<button class="btn btn-primary btn-sm" data-action="customize">
				${__('Customize')}
            </button>
`;

        const empty_state_image = '/assets/frappe/images/ui-states/empty.png';

        const empty_state_html = `

            ${no_result_message_html}
			${customize_button}

`;

        this.$dashboard_wrapper.append(empty_state_html);

        this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
    }

    customize() {
        if (this.in_customize_mode) {
            return;
        }

        if (this.$empty_state) {
            this.$empty_state.remove();
        }

        this.toggle_customize(true);
        this.chart_group.in_customize_mode = true;
        this.chart_group.customize();
        this.number_cards.in_customize_mode = true;
        this.number_card_group.customize();
    }

    save_dashboard_customization() {
        this.toggle_customize(false);

        const number_card_config = this.number_card_group.get_widget_config();
        let number_cards = [];
        number_card_config.order.map(card_name => {
            number_cards.push(number_card_config.widgets[card_name]);
        });

        const chart_config = this.chart_group.get_widget_config();
        let charts = [];
        chart_config.order.map(chart_name => {
            charts.push(chart_config.widgets[chart_name]);
        });

        // Don't allow duplicates of the same card or chart
        charts = this.remove_duplicates(charts);
        number_cards = this.remove_duplicates(number_cards);

        this.dashboard_settings = {
            charts: charts,
            number_cards: number_cards,
        };

        frappe.model.user_settings.save(this.doctype, 'dashboard_settings', this.dashboard_settings);
        this.make_dashboard();
    }

    discard_dashboard_customization() {
        this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
        this.toggle_customize(false);
        this.render_dashboard();
    }

    reset_dashboard_customization() {
        this.dashboard_settings = null;
        frappe.model.user_settings.save(
            this.doctype, 'dashboard_settings', this.dashboard_settings
        ).then(()=> this.make_dashboard());
        this.toggle_customize(false);
    }

    toggle_customize(show) {
        this.$customize.toggle(!show);
        this.$save_or_discard.toggle(show);
        this.in_customize_mode = show;
    }

    show_add_chart_dialog() {
        let fields = this.get_field_options();
        const dialog = new frappe.ui.Dialog({
            title: __(`Add a ${this.doctype} Chart`),
            fields: [
                {
					fieldname: 'new_or_existing',
					fieldtype: 'Select',
                    label: 'Choose an existing chart or create a new chart',
                    options: ['New Chart', 'Existing Chart'],
                    reqd: 1,
                },
                {
                    label: 'Chart',
                    fieldname: 'chart',
                    fieldtype: 'Link',
                    get_query: () => {
                        return {
                            filters: {
                               document_type: this.doctype,
                               is_standard: 1,
                            }
                        };
                    },
                    options: 'Dashboard Chart',
                    depends_on: 'eval: doc.new_or_existing == "Existing Chart"'
                },
                {
                    fieldname: 'sb_2',
                    fieldtype: 'Section Break',
                    depends_on: 'eval: doc.new_or_existing == "New Chart"'
                },
                {
                    label: 'Chart Label',
                    fieldname: 'label',
                    fieldtype: 'Data',
                    mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"'
                },
                {
                    fieldname: 'cb_1',
                    fieldtype: 'Column Break'
                },
                {
                    label: 'Chart Type',
                    fieldname: 'chart_type',
                    fieldtype: 'Select',
                    options: ['Time Series', 'Group By'],
                    mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"',
                },
                {
                    fieldname: 'sb_2',
                    fieldtype: 'Section Break',
                    label: 'Chart Config',
                    depends_on: 'eval: doc.chart_type == "Time Series" && doc.new_or_existing == "New Chart"',
                },
                {
                    label: 'Function',
                    fieldname: 'chart_function',
                    fieldtype: 'Select',
                    options: ['Count', 'Sum', 'Average'],
                    default: 'Count',
                },
                {
                    label: 'Timespan',
                    fieldtype: 'Select',
                    fieldname: 'timespan',
                    depends_on: 'eval: doc.chart_type == "Time Series"',
                    options: ['Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
                    default: 'Last Year',
                },
                {
                    fieldname: 'cb_2',
                    fieldtype: 'Column Break'
                },
                {
                    label: 'Value Based On',
                    fieldtype: 'Select',
                    fieldname: 'based_on',
                    options: fields.value_fields,
                    depends_on: 'eval: doc.chart_function=="Sum"'
                },
                {
                    label: 'Time Series Based On',
                    fieldtype: 'Select',
                    fieldname: 'based_on',
                    options: fields.date_fields,
                    mandatory_depends_on: 'eval: doc.chart_type == "Time Series"'
                },
                {
                    label: 'Time Interval',
                    fieldname: 'time_interval',
                    fieldtype: 'Select',
                    depends_on: 'eval: doc.chart_type == "Time Series"',
                    options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
                    default: 'Monthly'
                },
                {
                    fieldname: 'sb_2',
                    fieldtype: 'Section Break',
                    label: 'Chart Config',
                    depends_on: 'eval: doc.chart_type == "Group By" && doc.new_or_existing == "New Chart"',
                },
                {
                    label: 'Group By Type',
                    fieldname: 'group_by_type',
                    fieldtype: 'Select',
                    options: ['Count', 'Sum', 'Average'],
                    default: 'Count',
                },
                {
                    label: 'Aggregate Function Based On',
                    fieldtype: 'Select',
                    fieldname: 'aggregate_function_based_on',
                    options: fields.aggregate_function_fields,
                    depends_on: 'eval: ["Sum", "Avergage"].includes(doc.group_by_type)',
                },
                {
                    fieldname: 'cb_2',
                    fieldtype: 'Column Break'
                },
                {
                    label: 'Group By Based On',
                    fieldtype: 'Select',
                    fieldname: 'group_by_based_on',
                    options: fields.group_by_fields,
                    default: 'Last Year',
                },
                {
                    label: 'Number of Groups',
                    fieldtype: 'Int',
                    fieldname: 'number_of_groups',
                    default: 0,
                },
                {
                    fieldname: 'sb_3',
                    fieldtype: 'Section Break',
                    depends_on: 'eval: doc.new_or_existing == "New Chart"'
                },
                {
                    label: 'Chart Type',
                    fieldname: 'type',
                    fieldtype: 'Select',
                    options: ['Line', 'Bar', 'Percentage', 'Pie'],
                    depends_on: 'eval: doc.new_or_existing == "New Chart"'
                },
                {
                    fieldname: 'cb_1',
                    fieldtype: 'Column Break'
                },
                {
                    label: 'Chart Color',
                    fieldname: 'color',
                    fieldtype: 'Color',
                    depends_on: 'eval: doc.new_or_existing == "New Chart"',
                },
            ],
            primary_action_label: __('Add'),
            primary_action: (values) => {
                let chart = values;
                if (chart.new_or_existing == 'New Chart') {
                    chart.chart_name = chart.label;
                    chart.chart_type = chart.chart_type == 'Time Series'? chart.chart_function: chart.chart_type;
                    chart.document_type = this.doctype;
                    chart.filters_json = '[]';
                    frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart', {'args': chart}).then((doc)=> {
                        this.chart_group.new_widget.on_create({'chart_name': doc.chart_name, 'name': doc.chart_name, 'label': chart.label});
                    });
                } else {
                    this.chart_group.new_widget.on_create({'chart_name': chart.chart, 'label': chart.chart, 'name': chart.chart});
                }
                dialog.hide();
            }
        });
        dialog.show();
    }

    get_field_options() {
        let date_fields = [
			{label: __('Created On'), value: 'creation'},
			{label: __('Last Modified On'), value: 'modified'}
		];
		let value_fields = [];
		let group_by_fields = [];
        let aggregate_function_fields = [];

        frappe.get_meta(this.doctype).fields.map(df => {
            if (['Date', 'Datetime'].includes(df.fieldtype)) {
                date_fields.push({label: df.label, value: df.fieldname});
            }
            if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
                value_fields.push({label: df.label, value: df.fieldname});
                aggregate_function_fields.push({label: df.label, value: df.fieldname});
            }
            if (['Link', 'Select'].includes(df.fieldtype)) {
                group_by_fields.push({label: df.label, value: df.fieldname});
            }
        });
        return {
            date_fields: date_fields,
            value_fields: value_fields,
            group_by_fields: group_by_fields,
            aggregate_function_fields: aggregate_function_fields
        }
    }

    remove_duplicates(items) {
        return items.filter((item, index) => items.indexOf(item) === index);
    }

}
<commit_msg>fix: indent using tabs<commit_after>frappe.provide('frappe.views');

frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
	get view_name() {
		return 'Dashboard';
	}

	setup_defaults() {
		return super.setup_defaults()
			.then(() => {
				this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
			});
	}

	render() {

	}

	setup_view() {
		if (this.chart_group || this.number_card_group) {
			return;
		}

		this.setup_dashboard_page();
		this.setup_dashboard_customization();
		this.make_dashboard();
	}

	setup_dashboard_customization() {
		this.$customize = this.$chart_header.find('.customize-dashboard');
		this.$save_or_discard = this.$chart_header.find('.customize-options');
	}

	setup_dashboard_page() {
		const dashboard_name = __('{0} Dashboard', [this.doctype]);
		const chart_wrapper_html = `<div class="dashboard-view"></div>`;

		this.$frappe_list.html(chart_wrapper_html);
		this.page.clear_secondary_action();
		this.$dashboard_page = this.$page.find('.layout-main-section-wrapper').addClass('dashboard-page');
		this.$page.find('.page-form').empty().html(
			`

${dashboard_name}

${__('Customize')}

						${__('Reset')}
					 /
						${__('Save')}
					 /
						${__('Discard')}

`);

		this.$dashboard_wrapper = this.$page.find('.dashboard-view');
		this.$chart_header = this.$page.find('.dashboard-header');

		frappe.utils.bind_actions_with_object(this.$dashboard_page, this);
	}

	make_dashboard() {
		if (this.dashboard_settings) {
			this.charts = this.dashboard_settings.charts;
			this.number_cards = this.dashboard_settings.number_cards;
			this.render_dashboard();
		} else {
			frappe.run_serially([
				() => this.fetch_doctype_number_cards().then(cards => {
					this.number_cards = cards;
				}),
				() => this.fetch_doctype_charts().then(charts => {
					this.charts = charts;
				}),
				() => this.render_dashboard()
			]);
		}
	}

	render_dashboard() {
		this.$dashboard_wrapper.empty();

		frappe.dashboard_utils.get_dashboard_settings().then(settings => {
			this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
			this.charts.map(chart => {
				chart.label = chart.chart_name;
				chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
			});
			this.render_dashboard_charts();
		});

		this.render_number_cards();

		if (!this.charts.length && !this.number_cards.length) {
			this.render_empty_state();
		}
	}

	fetch_doctype_charts() {
		return frappe.db.get_list('Dashboard Chart', {
			filters: {
				chart_type: ['in', ['Count', 'Sum', 'Group By']],
				document_type: this.doctype,
				is_standard: true
			},
			fields: ['*']
		}).then(charts => {
			return charts;
		});
	}

	fetch_doctype_number_cards() {
		return frappe.db.get_list('Number Card', {
			filters: {
				document_type: this.doctype,
				is_standard: true
			},
			fields: ['*']
		}).then(cards => {
			return cards;
		});
	}

	render_number_cards() {
		this.number_card_group = new frappe.widget.WidgetGroup({
			container: this.$dashboard_wrapper,
			type: "number_card",
			columns: 3,
			options: {
				allow_sorting: true,
				allow_create: true,
				allow_delete: true,
				allow_hiding: true,
			},
			default_values: {doctype: this.doctype},
			widgets: this.number_cards || [],
			in_customize_mode: true,
		});

		this.in_customize_mode && this.number_card_group.customize();
	}

	render_dashboard_charts() {
		this.chart_group = new frappe.widget.WidgetGroup({
			container: this.$dashboard_wrapper,
			type: "chart",
			columns: 2,
			options: {
				allow_sorting: true,
				allow_create: true,
				allow_delete: true,
				allow_hiding: true,
			},
			custom_dialog: () => this.show_add_chart_dialog(),
			widgets: this.charts,
			in_customize_mode: this.in_customize_mode || false,
		});

		this.in_customize_mode && this.chart_group.customize();
		this.chart_group.container.find('.widget-group-head').hide();
	}

	render_empty_state() {
		const no_result_message_html =
			`
${__("You haven't added any Dashboard Charts or Number Cards yet.")}

${__("Click On Customize to add your first widget")}
`;

		const customize_button =
			`
<button class="btn btn-primary btn-sm" data-action="customize">
				${__('Customize')}
			</button>
`;

		const empty_state_image = '/assets/frappe/images/ui-states/empty.png';

		const empty_state_html = `

			${no_result_message_html}
			${customize_button}

`;

		this.$dashboard_wrapper.append(empty_state_html);

		this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
	}

	customize() {
		if (this.in_customize_mode) {
			return;
		}

		if (this.$empty_state) {
			this.$empty_state.remove();
		}

		this.toggle_customize(true);
		this.chart_group.in_customize_mode = true;
		this.chart_group.customize();
		this.number_cards.in_customize_mode = true;
		this.number_card_group.customize();
	}

	save_dashboard_customization() {
		this.toggle_customize(false);

		const number_card_config = this.number_card_group.get_widget_config();
		let number_cards = [];
		number_card_config.order.map(card_name => {
			number_cards.push(number_card_config.widgets[card_name]);
		});

		const chart_config = this.chart_group.get_widget_config();
		let charts = [];
		chart_config.order.map(chart_name => {
			charts.push(chart_config.widgets[chart_name]);
		});

		// Don't allow duplicates of the same card or chart
		charts = this.remove_duplicates(charts);
		number_cards = this.remove_duplicates(number_cards);

		this.dashboard_settings = {
			charts: charts,
			number_cards: number_cards,
		};

		frappe.model.user_settings.save(this.doctype, 'dashboard_settings', this.dashboard_settings);
		this.make_dashboard();
	}

	discard_dashboard_customization() {
		this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
		this.toggle_customize(false);
		this.render_dashboard();
	}

	reset_dashboard_customization() {
		this.dashboard_settings = null;
		frappe.model.user_settings.save(
			this.doctype, 'dashboard_settings', this.dashboard_settings
		).then(()=> this.make_dashboard());
		this.toggle_customize(false);
	}

	toggle_customize(show) {
		this.$customize.toggle(!show);
		this.$save_or_discard.toggle(show);
		this.in_customize_mode = show;
	}

	show_add_chart_dialog() {
		let fields = this.get_field_options();
		const dialog = new frappe.ui.Dialog({
			title: __(`Add a ${this.doctype} Chart`),
			fields: [
				{
					fieldname: 'new_or_existing',
					fieldtype: 'Select',
					label: 'Choose an existing chart or create a new chart',
					options: ['New Chart', 'Existing Chart'],
					reqd: 1,
				},
				{
					label: 'Chart',
					fieldname: 'chart',
					fieldtype: 'Link',
					get_query: () => {
						return {
							filters: {
							document_type: this.doctype,
							is_standard: 1,
							}
						};
					},
					options: 'Dashboard Chart',
					depends_on: 'eval: doc.new_or_existing == "Existing Chart"'
				},
				{
					fieldname: 'sb_2',
					fieldtype: 'Section Break',
					depends_on: 'eval: doc.new_or_existing == "New Chart"'
				},
				{
					label: 'Chart Label',
					fieldname: 'label',
					fieldtype: 'Data',
					mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"'
				},
				{
					fieldname: 'cb_1',
					fieldtype: 'Column Break'
				},
				{
					label: 'Chart Type',
					fieldname: 'chart_type',
					fieldtype: 'Select',
					options: ['Time Series', 'Group By'],
					mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"',
				},
				{
					fieldname: 'sb_2',
					fieldtype: 'Section Break',
					label: 'Chart Config',
					depends_on: 'eval: doc.chart_type == "Time Series" && doc.new_or_existing == "New Chart"',
				},
				{
					label: 'Function',
					fieldname: 'chart_function',
					fieldtype: 'Select',
					options: ['Count', 'Sum', 'Average'],
					default: 'Count',
				},
				{
					label: 'Timespan',
					fieldtype: 'Select',
					fieldname: 'timespan',
					depends_on: 'eval: doc.chart_type == "Time Series"',
					options: ['Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
					default: 'Last Year',
				},
				{
					fieldname: 'cb_2',
					fieldtype: 'Column Break'
				},
				{
					label: 'Value Based On',
					fieldtype: 'Select',
					fieldname: 'based_on',
					options: fields.value_fields,
					depends_on: 'eval: doc.chart_function=="Sum"'
				},
				{
					label: 'Time Series Based On',
					fieldtype: 'Select',
					fieldname: 'based_on',
					options: fields.date_fields,
					mandatory_depends_on: 'eval: doc.chart_type == "Time Series"'
				},
				{
					label: 'Time Interval',
					fieldname: 'time_interval',
					fieldtype: 'Select',
					depends_on: 'eval: doc.chart_type == "Time Series"',
					options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
					default: 'Monthly'
				},
				{
					fieldname: 'sb_2',
					fieldtype: 'Section Break',
					label: 'Chart Config',
					depends_on: 'eval: doc.chart_type == "Group By" && doc.new_or_existing == "New Chart"',
				},
				{
					label: 'Group By Type',
					fieldname: 'group_by_type',
					fieldtype: 'Select',
					options: ['Count', 'Sum', 'Average'],
					default: 'Count',
				},
				{
					label: 'Aggregate Function Based On',
					fieldtype: 'Select',
					fieldname: 'aggregate_function_based_on',
					options: fields.aggregate_function_fields,
					depends_on: 'eval: ["Sum", "Avergage"].includes(doc.group_by_type)',
				},
				{
					fieldname: 'cb_2',
					fieldtype: 'Column Break'
				},
				{
					label: 'Group By Based On',
					fieldtype: 'Select',
					fieldname: 'group_by_based_on',
					options: fields.group_by_fields,
					default: 'Last Year',
				},
				{
					label: 'Number of Groups',
					fieldtype: 'Int',
					fieldname: 'number_of_groups',
					default: 0,
				},
				{
					fieldname: 'sb_3',
					fieldtype: 'Section Break',
					depends_on: 'eval: doc.new_or_existing == "New Chart"'
				},
				{
					label: 'Chart Type',
					fieldname: 'type',
					fieldtype: 'Select',
					options: ['Line', 'Bar', 'Percentage', 'Pie'],
					depends_on: 'eval: doc.new_or_existing == "New Chart"'
				},
				{
					fieldname: 'cb_1',
					fieldtype: 'Column Break'
				},
				{
					label: 'Chart Color',
					fieldname: 'color',
					fieldtype: 'Color',
					depends_on: 'eval: doc.new_or_existing == "New Chart"',
				},
			],
			primary_action_label: __('Add'),
			primary_action: (values) => {
				let chart = values;
				if (chart.new_or_existing == 'New Chart') {
					chart.chart_name = chart.label;
					chart.chart_type = chart.chart_type == 'Time Series'? chart.chart_function: chart.chart_type;
					chart.document_type = this.doctype;
					chart.filters_json = '[]';
					frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart', {'args': chart}).then((doc)=> {
						this.chart_group.new_widget.on_create({'chart_name': doc.chart_name, 'name': doc.chart_name, 'label': chart.label});
					});
				} else {
					this.chart_group.new_widget.on_create({'chart_name': chart.chart, 'label': chart.chart, 'name': chart.chart});
				}
				dialog.hide();
			}
		});
		dialog.show();
	}

	get_field_options() {
		let date_fields = [
			{label: __('Created On'), value: 'creation'},
			{label: __('Last Modified On'), value: 'modified'}
		];
		let value_fields = [];
		let group_by_fields = [];
		let aggregate_function_fields = [];

		frappe.get_meta(this.doctype).fields.map(df => {
			if (['Date', 'Datetime'].includes(df.fieldtype)) {
				date_fields.push({label: df.label, value: df.fieldname});
			}
			if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
				value_fields.push({label: df.label, value: df.fieldname});
				aggregate_function_fields.push({label: df.label, value: df.fieldname});
			}
			if (['Link', 'Select'].includes(df.fieldtype)) {
				group_by_fields.push({label: df.label, value: df.fieldname});
			}
		});
		return {
			date_fields: date_fields,
			value_fields: value_fields,
			group_by_fields: group_by_fields,
			aggregate_function_fields: aggregate_function_fields
		}
	}

	remove_duplicates(items) {
		return items.filter((item, index) => items.indexOf(item) === index);
	}

}
<|endoftext|>


<commit_before>'use strict';

var props = require('../');
var gutil = require('gulp-util');
var File = gutil.File;
var fs = require('fs');
var path = require('path');
var es = require('event-stream');

require('should');
require('mocha');

describe('gulp-props2json', function() {

    // Test files

    var validFile;
	var noExtFile;
	var emptyFile;
	var specialFile;
	var nullFile;

    describe('in buffer mode', function() {

        beforeEach(function() {
            validFile = new File({
                path: 'test/valid.properties',
                cwd: 'test',
                contents: fs.readFileSync('test/valid.properties')
            });
            noExtFile = new File({
                path: 'test/noExt.1',
                cwd: 'test',
                contents: new Buffer('test/noExt.1')
            });
            emptyFile = new File({
                path: 'test/empty.properties',
                cwd: 'test',
                contents: new Buffer('')
            });
            specialFile = new File({
                path: 'test/special.properties',
                cwd: 'test',
                contents: fs.readFileSync('test/special.properties')
            });

            nullFile = new File({
                cwd: 'test',
                contents: null
            });
        });

        it('should return JSON when namespace is empty', function(done) {
            var stream = props({ namespace: '' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{"name":"Gulp","message":"Hello!"}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=2 option defined', function(done) {
            var stream = props({ namespace: '', space: 2 });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{\n  "name": "Gulp",\n  "message": "Hello!"\n}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=4 option defined', function(done) {
            var stream = props({ namespace: '', space: 4 });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{\n    "name": "Gulp",\n    "message": "Hello!"\n}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use default namespace when not specified', function(done) {
            var stream = props();

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var config = config || {};\nconfig[\'name\'] = \'Gulp\';\nconfig[\'message\'] = \'Hello!\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use custom namespace', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var state = state || {};\nstate[\'name\'] = \'Gulp\';\nstate[\'message\'] = \'Hello!\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should reject reserved word for namespace', function(done) {
            var stream = props({ namespace: 'void' });

            stream.once('error', function(error) {
                error.message.should.equal('namespace option cannot be a reserved word.');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should rename the namespace if is not a valid identifier', function(done) {
            var stream = props({ namespace: '123' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var _123 = _123 || {};\n_123[\'name\'] = \'Gulp\';\n_123[\'message\'] = \'Hello!\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should append the extension instead of replacing it if appendExt flag is active', function(done) {
            var stream = props({ appendExt: true });

            stream.once('data', function(file) {
                path.basename(file.path).should.equal('noExt.1.js');
                done();
            });

            stream.write(noExtFile);
            stream.end();
        });

        it('should handle special characters properly', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var state = state || {};\nstate[\'a#b!c=d:\'] = \'AAAA#BBBB!CCCC=DDDD:EEEE\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(specialFile);
            stream.end();
        });

        it('should do nothing when contents is null', function(done) {
            var stream = props();

            stream.once('data', function(file) {
                file.isNull().should.equal(true);
                path.extname(file.path).should.equal('');
                done();
            });

            stream.write(nullFile);
            stream.end();
        });
    });

    describe('in stream mode', function() {

        beforeEach(function() {
            validFile = new File({
                path: 'test/valid.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/valid.properties')
            });
            emptyFile = new File({
                path: 'test/empty.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/empty.properties')
            });
            specialFile = new File({
                path: 'test/special.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/special.properties')
            });
        });

        it('should return JSON when namespace is empty', function(done) {
            var stream = props({ namespace: '' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{"name":"Gulp","message":"Hello!"}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=2 option defined', function(done) {
            var stream = props({ namespace: '', space: 2 });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{\n  "name": "Gulp",\n  "message": "Hello!"\n}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=4 option defined', function(done) {
            var stream = props({ namespace: '', space: 4 });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{\n    "name": "Gulp",\n    "message": "Hello!"\n}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use default namespace when not specified', function(done) {
            var stream = props();

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var config = config || {};\nconfig[\'name\'] = \'Gulp\';\nconfig[\'message\'] = \'Hello!\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use custom namespace', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var state = state || {};\nstate[\'name\'] = \'Gulp\';\nstate[\'message\'] = \'Hello!\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should reject reserved word for namespace', function(done) {
            var stream = props({ namespace: 'void' });

            stream.once('error', function(error) {
                error.message.should.equal('namespace option cannot be a reserved word.');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should rename the namespace if is not a valid identifier', function(done) {
            var stream = props({ namespace: '123' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var _123 = _123 || {};\n_123[\'name\'] = \'Gulp\';\n_123[\'message\'] = \'Hello!\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should handle special characters properly', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var state = state || {};\nstate[\'a#b!c=d:\'] = \'AAAA#BBBB!CCCC=DDDD:EEEE\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(specialFile);
            stream.end();
        });
    });
});<commit_msg>json output with nested properties tests<commit_after>'use strict';

var props = require('../');
var gutil = require('gulp-util');
var File = gutil.File;
var fs = require('fs');
var path = require('path');
var es = require('event-stream');

require('should');
require('mocha');

describe('gulp-props2json', function() {

    // Test files

    var validFile;
	var noExtFile;
	var emptyFile;
	var specialFile;
	var nullFile;
	var nestedFile;

    describe('in buffer mode', function() {

        beforeEach(function() {
            validFile = new File({
                path: 'test/valid.properties',
                cwd: 'test',
                contents: fs.readFileSync('test/valid.properties')
            });
            noExtFile = new File({
                path: 'test/noExt.1',
                cwd: 'test',
                contents: new Buffer('test/noExt.1')
            });
            emptyFile = new File({
                path: 'test/empty.properties',
                cwd: 'test',
                contents: new Buffer('')
            });
            specialFile = new File({
                path: 'test/special.properties',
                cwd: 'test',
                contents: fs.readFileSync('test/special.properties')
            });
            nullFile = new File({
                cwd: 'test',
                contents: null
            });
			nestedFile = new File({
                path: 'test/nested.properties',
                cwd: 'test',
                contents: fs.readFileSync('test/nested.properties')
            });
        });

        it('should return JSON when namespace is empty', function(done) {
            var stream = props({ namespace: '' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{"name":"Gulp","message":"Hello!"}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=2 option defined', function(done) {
            var stream = props({ namespace: '', space: 2 });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{\n  "name": "Gulp",\n  "message": "Hello!"\n}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=4 option defined', function(done) {
            var stream = props({ namespace: '', space: 4 });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{\n    "name": "Gulp",\n    "message": "Hello!"\n}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use default namespace when not specified', function(done) {
            var stream = props();

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var config = config || {};\nconfig[\'name\'] = \'Gulp\';\nconfig[\'message\'] = \'Hello!\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use custom namespace', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var state = state || {};\nstate[\'name\'] = \'Gulp\';\nstate[\'message\'] = \'Hello!\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should reject reserved word for namespace', function(done) {
            var stream = props({ namespace: 'void' });

            stream.once('error', function(error) {
                error.message.should.equal('namespace option cannot be a reserved word.');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should rename the namespace if is not a valid identifier', function(done) {
            var stream = props({ namespace: '123' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var _123 = _123 || {};\n_123[\'name\'] = \'Gulp\';\n_123[\'message\'] = \'Hello!\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should append the extension instead of replacing it if appendExt flag is active', function(done) {
            var stream = props({ appendExt: true });

            stream.once('data', function(file) {
                path.basename(file.path).should.equal('noExt.1.js');
                done();
            });

            stream.write(noExtFile);
            stream.end();
        });

        it('should handle special characters properly', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('var state = state || {};\nstate[\'a#b!c=d:\'] = \'AAAA#BBBB!CCCC=DDDD:EEEE\';\n');
                path.extname(file.path).should.equal('.js');
                done();
            });

            stream.write(specialFile);
            stream.end();
        });

        it('should do nothing when contents is null', function(done) {
            var stream = props();

            stream.once('data', function(file) {
                file.isNull().should.equal(true);
                path.extname(file.path).should.equal('');
                done();
            });

            stream.write(nullFile);
            stream.end();
        });

		it('should create JSON object with nested properties', function(done) {
            var stream = props({ namespace: '', createObject: true });

            stream.once('data', function(file) {
                file.contents.toString('utf8').should.equal('{"person":{"firstName":"Peter","lastName":"Parker"}}');
                path.extname(file.path).should.equal('.json');
                done();
            });

            stream.write(nestedFile);
            stream.end();
        });

    });

    describe('in stream mode', function() {

        beforeEach(function() {
            validFile = new File({
                path: 'test/valid.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/valid.properties')
            });
            emptyFile = new File({
                path: 'test/empty.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/empty.properties')
            });
            specialFile = new File({
                path: 'test/special.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/special.properties')
            });
			nestedFile = new File({
                path: 'test/nested.properties',
                cwd: 'test',
                contents: fs.createReadStream('test/nested.properties')
            });
        });

        it('should return JSON when namespace is empty', function(done) {
            var stream = props({ namespace: '' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{"name":"Gulp","message":"Hello!"}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=2 option defined', function(done) {
            var stream = props({ namespace: '', space: 2 });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{\n  "name": "Gulp",\n  "message": "Hello!"\n}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should return pretty JSON when namespace is empty and space=4 option defined', function(done) {
            var stream = props({ namespace: '', space: 4 });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{\n    "name": "Gulp",\n    "message": "Hello!"\n}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use default namespace when not specified', function(done) {
            var stream = props();

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var config = config || {};\nconfig[\'name\'] = \'Gulp\';\nconfig[\'message\'] = \'Hello!\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should use custom namespace', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var state = state || {};\nstate[\'name\'] = \'Gulp\';\nstate[\'message\'] = \'Hello!\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should reject reserved word for namespace', function(done) {
            var stream = props({ namespace: 'void' });

            stream.once('error', function(error) {
                error.message.should.equal('namespace option cannot be a reserved word.');
                done();
            });

            stream.write(validFile);
            stream.end();
        });

        it('should rename the namespace if is not a valid identifier', function(done) {
            var stream = props({ namespace: '123' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var _123 = _123 || {};\n_123[\'name\'] = \'Gulp\';\n_123[\'message\'] = \'Hello!\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(validFile);
            stream.end();
        });

        it('should handle special characters properly', function(done) {
            var stream = props({ namespace: 'state' });

            stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('var state = state || {};\nstate[\'a#b!c=d:\'] = \'AAAA#BBBB!CCCC=DDDD:EEEE\';\n');
                    path.extname(file.path).should.equal('.js');
                    done();
                }));
            });

            stream.write(specialFile);
            stream.end();
        });

		it('should create JSON object with nested properties', function(done) {
            var stream = props({ namespace: '', createObject: true });

			stream.once('data', function(file) {
                file.contents.pipe(es.wait(function(err, data) {
                    data.toString('utf8').should.equal('{"person":{"firstName":"Peter","lastName":"Parker"}}');
                    path.extname(file.path).should.equal('.json');
                    done();
                }));
            });

            stream.write(nestedFile);
            stream.end();
        });

    });

});<|endoftext|>


<commit_before>// Copyright 2015, EMC, Inc.

'use strict';

module.exports = {
    friendlyName: 'Analyze OS Repository',
    injectableName: 'Task.Base.Os.Analyze.Repo',
    runJob: 'Job.Os.Analyze.Repo',
    optionsSchema: {
        properties: {
            version: {
                '$ref': 'types-installos.json#/definitions/Version'
            },
            repo: {
                '$ref': 'types-installos.json#/definitions/Repo'
            },
            osName: {
                'enum': ['ESXi']
            }
        },
        required: ['osName', 'repo', 'version']
    },
    requiredProperties: {
    },
    properties: {
    }
};
<commit_msg>remove "$ref" in build-in task schema to avoid conflict with mongodb keyword<commit_after>// Copyright 2015, EMC, Inc.

'use strict';

module.exports = {
    friendlyName: 'Analyze OS Repository',
    injectableName: 'Task.Base.Os.Analyze.Repo',
    runJob: 'Job.Os.Analyze.Repo',
    optionsSchema: {
        properties: {
            version: {
                "description": "The version of target OS",
                "type": "string",
                "pattern": "^[-0-9a-zA-Z._*+#]+$"
            },
            repo: {
                "description": "The OS http repository for installation",
                "type": "string",
                "pattern": "^http://"
            },
            osName: {
                'enum': ['ESXi']
            }
        },
        required: ['osName', 'repo', 'version']
    },
    requiredProperties: {
    },
    properties: {
    }
};
<|endoftext|>


<commit_before>var mkdirp = require('mkdirp');
var chalk = require('chalk');
var marked = require('marked');
var fs = require('fs');
var slugify = require('slugify');
var path = require('path');
var _nunjucks = require('./nunjucks_config.js');
var bc = require('../blog_config.json');

// inits
var ROOT_DIR = path.resolve('.');
var nunjucks = _nunjucks.init();

module.exports = {

  generatePostTemplate: function(post,labels,dirName){
        var fileName = post.slug+'.html';
        // marked is required for offline support
        // the `body.html` does already exist when using the api
        post.html = marked(post.body);
        var renderContent = nunjucks.render('post_page.html',
          {
            post: post,
            labels: labels,
            comment: bc.comment
          }
        );
        // use `path` for windows support
        // probably will break now
        fs.writeFile(ROOT_DIR+"/"+dirName+"/posts/"+fileName, renderContent, function(err) {
            if(err) { return console.log(err); }
        });
        },

  generateIndexTemplate: function(posts,labels,pagination,dirName,fileName){
        // index template generation
        var renderContent = nunjucks.render('index.html',{posts:posts,labels:labels,pagination:pagination});
        fs.writeFile(ROOT_DIR+"/"+dirName+"/"+fileName, renderContent, function(err) {
            if(err) { return console.log(err); }
        });
        },

  generateCategoryTemplates: function(labels,dirName){
        // takes array of labels
        // creates files with name label.slug.html
        return labels.map(function(label){
                return new Promise(function(resolve,reject){
                        var renderContent = nunjucks.render('category_page.html',{label:label,labels:labels});
                        fs.writeFileSync(ROOT_DIR+"/"+dirName+"/category/"+label.slug+".html", renderContent);
                        resolve();
                });
        });
        },

  generatePageTemplate: function(dirName){
        var pageTemplatesFiles = fs.readdirSync(ROOT_DIR+"/views/pages");
        pageTemplatesFiles.forEach(function(fileName){
          var renderContent = nunjucks.render('pages/'+fileName,{});
          fs.writeFileSync(ROOT_DIR+"/"+dirName+"/"+fileName, renderContent);
        });
        },

  getOfflineFileContents: function(){
        var fileNames = fs.readdirSync(ROOT_DIR+"/content");
        return fileNames.map(fileName=>{
                return new Promise((resolve)=>{
                       let content = fs.readFileSync(ROOT_DIR+"/content/"+fileName,{encoding:"utf8"});
                       let post = {};
                       post.title = content.split("\n")[0].split(" ").slice(1).join(' ').trim();
                       post.body = content.split("\n").slice(1).join("\n");
                       post.slug = slugify(post.title);
                       resolve(post);
                });
        });
        }
};
<commit_msg>added comment in utils<commit_after>var mkdirp = require('mkdirp');
var chalk = require('chalk');
var marked = require('marked');
var fs = require('fs');
var slugify = require('slugify');
var path = require('path');
var _nunjucks = require('./nunjucks_config.js');
var bc = require('../blog_config.json');

// inits
var ROOT_DIR = path.resolve('.');
var nunjucks = _nunjucks.init();

module.exports = {

  generatePostTemplate: function(post,labels,dirName){
        var fileName = post.slug+'.html';
        // marked is required for offline support
        // the `body.html` does already exist when using the api
        post.html = marked(post.body);
        var renderContent = nunjucks.render('post_page.html',
          {
            post: post,
            labels: labels,
            comment: bc.comment
          }
        );
        // use `path` for windows support
        // probably will break now
        fs.writeFile(ROOT_DIR+"/"+dirName+"/posts/"+fileName, renderContent, function(err) {
            if(err) { return console.log(err); }
        });
        },

  generateIndexTemplate: function(posts,labels,pagination,dirName,fileName){
        // index template generation
        var renderContent = nunjucks.render('index.html',{posts:posts,labels:labels,pagination:pagination});
        fs.writeFile(ROOT_DIR+"/"+dirName+"/"+fileName, renderContent, function(err) {
            if(err) { return console.log(err); }
        });
        },

  generateCategoryTemplates: function(labels,dirName){
        // takes array of labels
        // creates files with name label.slug.html
        return labels.map(function(label){
                return new Promise(function(resolve,reject){
                        var renderContent = nunjucks.render('category_page.html',{label:label,labels:labels});
                        fs.writeFileSync(ROOT_DIR+"/"+dirName+"/category/"+label.slug+".html", renderContent);
                        resolve();
                });
        });
        },

  generatePageTemplate: function(dirName){
        var pageTemplatesFiles = fs.readdirSync(ROOT_DIR+"/views/pages");
        pageTemplatesFiles.forEach(function(fileName){
          var renderContent = nunjucks.render('pages/'+fileName,{});
          fs.writeFileSync(ROOT_DIR+"/"+dirName+"/"+fileName, renderContent);
        });
        },

  getOfflineFileContents: function(){
        var fileNames = fs.readdirSync(ROOT_DIR+"/content");
        return fileNames.map(fileName=>{
                return new Promise((resolve)=>{
                       let content = fs.readFileSync(ROOT_DIR+"/content/"+fileName,{encoding:"utf8"});
                       let post = {};
                       // this part really need a good fix
                       post.title = content.split("\n")[0].split(" ").slice(1).join(' ').trim();
                       post.body = content.split("\n").slice(1).join("\n");
                       post.slug = slugify(post.title);
                       resolve(post);
                });
        });
        }
};
<|endoftext|>


<commit_before>/*
 * L.Control.Loading is a control that shows a loading indicator when tiles are
 * loading or when map-related AJAX requests are taking place.
 */

(function () {

    function defineLeafletLoading(L) {
        L.Control.Loading = L.Control.extend({
            options: {
                position: 'topleft',
                separate: false,
                zoomControl: null
            },

            initialize: function(options) {
                L.setOptions(this, options);
                this._dataLoaders = {};

                // Try to set the zoom control this control is attached to from the
                // options
                if (this.options.zoomControl !== null) {
                    this.zoomControl = this.options.zoomControl;
                }
            },

            onAdd: function(map) {
                this._addLayerListeners(map);
                this._addMapListeners(map);

                // Try to set the zoom control this control is attached to from the map
                // the control is being added to
                if (!this.options.separate && !this.zoomControl) {
                    if (map.zoomControl) {
                        this.zoomControl = map.zoomControl;
                    } else if (map.zoomsliderControl) {
                        this.zoomControl = map.zoomsliderControl;
                    }
                }

                // Create the loading indicator
                var classes = 'leaflet-control-loading';
                var container;
                if (this.zoomControl && !this.options.separate) {
                    // If there is a zoom control, hook into the bottom of it
                    container = this.zoomControl._container;
                    // These classes are no longer used as of Leaflet 0.6
                    classes += ' leaflet-bar-part-bottom leaflet-bar-part last';
                }
                else {
                    // Otherwise, create a container for the indicator
                    container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-bar');
                }
                this._indicator = L.DomUtil.create('a', classes, container);
                return container;
            },

            onRemove: function(map) {
                this._removeLayerListeners(map);
                this._removeMapListeners(map);
            },

            removeFrom: function (map) {
                if (this.zoomControl && !this.options.separate) {
                    // Override Control.removeFrom() to avoid clobbering the entire
                    // _container, which is the same as zoomControl's
                    this._container.removeChild(this._indicator);
                    this._map = null;
                    this.onRemove(map);
                    return this;
                }
                else {
                    // If this control is separate from the zoomControl, call the
                    // parent method so we don't leave behind an empty container
                    return L.Control.prototype.removeFrom.call(this, map);
                }
            },

            addLoader: function(id) {
                this._dataLoaders[id] = true;
                this.updateIndicator();
            },

            removeLoader: function(id) {
                delete this._dataLoaders[id];
                this.updateIndicator();
            },

            updateIndicator: function() {
                if (this.isLoading()) {
                    this._showIndicator();
                }
                else {
                    this._hideIndicator();
                }
            },

            isLoading: function() {
                return this._countLoaders() > 0;
            },

            _countLoaders: function() {
                var size = 0, key;
                for (key in this._dataLoaders) {
                    if (this._dataLoaders.hasOwnProperty(key)) size++;
                }
                return size;
            },

            _showIndicator: function() {
                // Show loading indicator
                L.DomUtil.addClass(this._indicator, 'is-loading');

                // If zoomControl exists, make the zoom-out button not last
                if (!this.options.separate) {
                    if (this.zoomControl instanceof L.Control.Zoom) {
                        L.DomUtil.removeClass(this.zoomControl._zoomOutButton, 'leaflet-bar-part-bottom');
                    }
                    else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
                        L.DomUtil.removeClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
                    }
                }
            },

            _hideIndicator: function() {
                // Hide loading indicator
                L.DomUtil.removeClass(this._indicator, 'is-loading');

                // If zoomControl exists, make the zoom-out button last
                if (!this.options.separate) {
                    if (this.zoomControl instanceof L.Control.Zoom) {
                        L.DomUtil.addClass(this.zoomControl._zoomOutButton, 'leaflet-bar-part-bottom');
                    }
                    else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
                        L.DomUtil.addClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
                    }
                }
            },

            _handleLoading: function(e) {
                this.addLoader(this.getEventId(e));
            },

            _handleLoad: function(e) {
                this.removeLoader(this.getEventId(e));
            },

            getEventId: function(e) {
                if (e.id) {
                    return e.id;
                }
                else if (e.layer) {
                    return e.layer._leaflet_id;
                }
                return e.target._leaflet_id;
            },

            _layerAdd: function(e) {
                if (!e.layer || !e.layer.on) return
                e.layer.on({
                    loading: this._handleLoading,
                    load: this._handleLoad
                }, this);
            },

            _addLayerListeners: function(map) {
                // Add listeners for begin and end of load to any layers already on the
                // map
                map.eachLayer(function(layer) {
                    if (!layer.on) return;
                    layer.on({
                        loading: this._handleLoading,
                        load: this._handleLoad
                    }, this);
                }, this);

                // When a layer is added to the map, add listeners for begin and end
                // of load
                map.on('layeradd', this._layerAdd, this);
            },

            _removeLayerListeners: function(map) {
                // Remove listeners for begin and end of load from all layers
                map.eachLayer(function(layer) {
                    if (!layer.off) return;
                    layer.off({
                        loading: this._handleLoading,
                        load: this._handleLoad
                    }, this);
                }, this);

                // Remove layeradd listener from map
                map.off('layeradd', this._layerAdd, this);
            },

            _addMapListeners: function(map) {
                // Add listeners to the map for (custom) dataloading and dataload
                // events, eg, for AJAX calls that affect the map but will not be
                // reflected in the above layer events.
                map.on({
                    dataloading: this._handleLoading,
                    dataload: this._handleLoad,
                    layeradd: this._handleLoading,
                    layerremove: this._handleLoad
                }, this);
            },

            _removeMapListeners: function(map) {
                map.off({
                    dataloading: this._handleLoading,
                    dataload: this._handleLoad,
                    layeradd: this._handleLoading,
                    layerremove: this._handleLoad
                }, this);
            }
        });

        L.Map.addInitHook(function () {
            if (this.options.loadingControl) {
                this.loadingControl = new L.Control.Loading();
                this.addControl(this.loadingControl);
            }
        });

        L.Control.loading = function(options) {
            return new L.Control.Loading(options);
        };
    }

    if (typeof define === 'function' && define.amd) {
        // Try to add leaflet.loading to Leaflet using AMD
        define(['leaflet'], function (L) {
            defineLeafletLoading(L);
        });
    }
    else {
        // Else use the global L
        defineLeafletLoading(L);
    }

})();
<commit_msg>Revert 59f94d7fab6<commit_after>/*
 * L.Control.Loading is a control that shows a loading indicator when tiles are
 * loading or when map-related AJAX requests are taking place.
 */

(function () {

    function defineLeafletLoading(L) {
        L.Control.Loading = L.Control.extend({
            options: {
                position: 'topleft',
                separate: false,
                zoomControl: null
            },

            initialize: function(options) {
                L.setOptions(this, options);
                this._dataLoaders = {};

                // Try to set the zoom control this control is attached to from the
                // options
                if (this.options.zoomControl !== null) {
                    this.zoomControl = this.options.zoomControl;
                }
            },

            onAdd: function(map) {
                this._addLayerListeners(map);
                this._addMapListeners(map);

                // Try to set the zoom control this control is attached to from the map
                // the control is being added to
                if (!this.options.separate && !this.zoomControl) {
                    if (map.zoomControl) {
                        this.zoomControl = map.zoomControl;
                    } else if (map.zoomsliderControl) {
                        this.zoomControl = map.zoomsliderControl;
                    }
                }

                // Create the loading indicator
                var classes = 'leaflet-control-loading';
                var container;
                if (this.zoomControl && !this.options.separate) {
                    // If there is a zoom control, hook into the bottom of it
                    container = this.zoomControl._container;
                    // These classes are no longer used as of Leaflet 0.6
                    classes += ' leaflet-bar-part-bottom leaflet-bar-part last';
                }
                else {
                    // Otherwise, create a container for the indicator
                    container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-bar');
                }
                this._indicator = L.DomUtil.create('a', classes, container);
                return container;
            },

            onRemove: function(map) {
                this._removeLayerListeners(map);
                this._removeMapListeners(map);
            },

            removeFrom: function (map) {
                if (this.zoomControl && !this.options.separate) {
                    // Override Control.removeFrom() to avoid clobbering the entire
                    // _container, which is the same as zoomControl's
                    this._container.removeChild(this._indicator);
                    this._map = null;
                    this.onRemove(map);
                    return this;
                }
                else {
                    // If this control is separate from the zoomControl, call the
                    // parent method so we don't leave behind an empty container
                    return L.Control.prototype.removeFrom.call(this, map);
                }
            },

            addLoader: function(id) {
                this._dataLoaders[id] = true;
                this.updateIndicator();
            },

            removeLoader: function(id) {
                delete this._dataLoaders[id];
                this.updateIndicator();
            },

            updateIndicator: function() {
                if (this.isLoading()) {
                    this._showIndicator();
                }
                else {
                    this._hideIndicator();
                }
            },

            isLoading: function() {
                return this._countLoaders() > 0;
            },

            _countLoaders: function() {
                var size = 0, key;
                for (key in this._dataLoaders) {
                    if (this._dataLoaders.hasOwnProperty(key)) size++;
                }
                return size;
            },

            _showIndicator: function() {
                // Show loading indicator
                L.DomUtil.addClass(this._indicator, 'is-loading');

                // If zoomControl exists, make the zoom-out button not last
                if (!this.options.separate) {
                    if (this.zoomControl instanceof L.Control.Zoom) {
                        L.DomUtil.removeClass(this.zoomControl._zoomOutButton, 'leaflet-bar-part-bottom');
                    }
                    else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
                        L.DomUtil.removeClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
                    }
                }
            },

            _hideIndicator: function() {
                // Hide loading indicator
                L.DomUtil.removeClass(this._indicator, 'is-loading');

                // If zoomControl exists, make the zoom-out button last
                if (!this.options.separate) {
                    if (this.zoomControl instanceof L.Control.Zoom) {
                        L.DomUtil.addClass(this.zoomControl._zoomOutButton, 'leaflet-bar-part-bottom');
                    }
                    else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
                        L.DomUtil.addClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
                    }
                }
            },

            _handleLoading: function(e) {
                this.addLoader(this.getEventId(e));
            },

            _handleLoad: function(e) {
                this.removeLoader(this.getEventId(e));
            },

            getEventId: function(e) {
                if (e.id) {
                    return e.id;
                }
                else if (e.layer) {
                    return e.layer._leaflet_id;
                }
                return e.target._leaflet_id;
            },

            _layerAdd: function(e) {
                if (!e.layer || !e.layer.on) return
                e.layer.on({
                    loading: this._handleLoading,
                    load: this._handleLoad
                }, this);
            },

            _addLayerListeners: function(map) {
                // Add listeners for begin and end of load to any layers already on the
                // map
                map.eachLayer(function(layer) {
                    if (!layer.on) return;
                    layer.on({
                        loading: this._handleLoading,
                        load: this._handleLoad
                    }, this);
                }, this);

                // When a layer is added to the map, add listeners for begin and end
                // of load
                map.on('layeradd', this._layerAdd, this);
            },

            _removeLayerListeners: function(map) {
                // Remove listeners for begin and end of load from all layers
                map.eachLayer(function(layer) {
                    if (!layer.off) return;
                    layer.off({
                        loading: this._handleLoading,
                        load: this._handleLoad
                    }, this);
                }, this);

                // Remove layeradd listener from map
                map.off('layeradd', this._layerAdd, this);
            },

            _addMapListeners: function(map) {
                // Add listeners to the map for (custom) dataloading and dataload
                // events, eg, for AJAX calls that affect the map but will not be
                // reflected in the above layer events.
                map.on({
                    dataloading: this._handleLoading,
                    dataload: this._handleLoad,
                    layerremove: this._handleLoad
                }, this);
            },

            _removeMapListeners: function(map) {
                map.off({
                    dataloading: this._handleLoading,
                    dataload: this._handleLoad,
                    layerremove: this._handleLoad
                }, this);
            }
        });

        L.Map.addInitHook(function () {
            if (this.options.loadingControl) {
                this.loadingControl = new L.Control.Loading();
                this.addControl(this.loadingControl);
            }
        });

        L.Control.loading = function(options) {
            return new L.Control.Loading(options);
        };
    }

    if (typeof define === 'function' && define.amd) {
        // Try to add leaflet.loading to Leaflet using AMD
        define(['leaflet'], function (L) {
            defineLeafletLoading(L);
        });
    }
    else {
        // Else use the global L
        defineLeafletLoading(L);
    }

})();
<|endoftext|>