<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.treeview_settings");
frappe.provide('frappe.views.trees');
window.cur_tree = null;

frappe.views.TreeFactory = frappe.views.Factory.extend({
	make: function(route) {
		frappe.model.with_doctype(route[1], function() {
			var options = {
				doctype: route[1],
				meta: frappe.get_meta(route[1])
			};

			if (!frappe.treeview_settings[route[1]] && !frappe.meta.get_docfield(route[1], "is_group")) {
				frappe.msgprint(__("Tree view not available for {0}", [route[1]] ));
				return false;
			}
			$.extend(options, frappe.treeview_settings[route[1]] || {});
			frappe.views.trees[options.doctype] = new frappe.views.TreeView(options);
		});
	}
});

frappe.views.TreeView = Class.extend({
	init: function(opts) {
		var me = this;

		this.opts = {};
		this.opts.get_tree_root = true;
		this.opts.show_expand_all = true;
		$.extend(this.opts, opts);
		this.doctype = opts.doctype;
		this.args = {doctype: me.doctype};
		this.page_name = frappe.get_route_str();
		this.get_tree_nodes =  me.opts.get_tree_nodes || "frappe.desk.treeview.get_children";

		this.get_permissions();
		this.make_page();
		this.make_filters();

		if (me.opts.get_tree_root) {
			this.get_root();
		}

		this.onload();
		this.set_menu_item();
		this.set_primary_action();
	},
	get_permissions: function(){
		this.can_read = frappe.model.can_read(this.doctype);
		this.can_create = frappe.boot.user.can_create.indexOf(this.doctype) !== -1 ||
					frappe.boot.user.in_create.indexOf(this.doctype) !== -1;
		this.can_write = frappe.model.can_write(this.doctype);
		this.can_delete = frappe.model.can_delete(this.doctype);
	},
	make_page: function() {
		var me = this;
		this.parent = frappe.container.add_page(this.page_name);
		frappe.ui.make_app_page({parent:this.parent, single_column:true});

		this.page = this.parent.page;
		frappe.container.change_to(this.page_name);
		frappe.breadcrumbs.add(me.opts.breadcrumb || locals.DocType[me.doctype].module);

		this.set_title();

		this.page.main.css({
			"min-height": "300px",
			"padding-bottom": "25px"
		});

		if(this.opts.show_expand_all) {
			this.page.add_inner_button(__('Expand All'), function() {
				me.tree.load_children(me.tree.root_node, true);
			});
		}

		if(this.opts.view_template) {
			var row = $('

').appendTo(this.page.main);
			this.body = $('

').appendTo(row);
			this.node_view = $('

').appendTo(row);
		} else {
			this.body = this.page.main;
		}
	},
	set_title: function() {
		this.page.set_title(this.opts.title || __('{0} Tree', [__(this.doctype)]));
	},
	onload: function() {
		var me = this;
		this.opts.onload && this.opts.onload(me);
	},
	make_filters: function(){
		var me = this;
		frappe.treeview_settings.filters = []
		$.each(this.opts.filters || [], function(i, filter) {
			if(frappe.route_options && frappe.route_options[filter.fieldname]) {
				filter.default = frappe.route_options[filter.fieldname]
			}

			filter.change = function() {
				var val = this.get_value();
				me.args[filter.fieldname] = val;
				if (val) {
					me.root_label = val;
					me.page.set_title(val);
				} else {
					me.root_label = me.opts.root_label;
					me.set_title();
				}
				me.make_tree();
			}

			me.page.add_field(filter);

			if (filter.default) {
				$("[data-fieldname='"+filter.fieldname+"']").trigger("change");
			}
		})
	},
	get_root: function() {
		var me = this;
		frappe.call({
			method: me.get_tree_nodes,
			args: me.args,
			callback: function(r) {
				if (r.message) {
					me.root_label = r.message[0]["value"];
					me.make_tree();
				}
			}
		})
	},
	make_tree: function() {
		$(this.parent).find(".tree").remove();

		this.tree = new frappe.ui.Tree({
			parent: this.body,
			label: this.args[this.opts.root_label] || this.root_label || this.opts.root_label,
			expandable: true,

			args: this.args,
			method: this.get_tree_nodes,

			// array of button props: {label, condition, click, btnClass}
			toolbar: this.get_toolbar(),

			get_label: this.opts.get_label,
			on_render: this.opts.onrender,
			on_click: (node) => { this.select_node(node); },
		});

		cur_tree = this.tree;
	},

	select_node: function(node) {
		var me = this;
		if(this.opts.click) {
			this.opts.click(node);
		}
		if(this.opts.view_template) {
			this.node_view.empty();
			$(frappe.render_template(me.opts.view_template,
				{data:node.data, doctype:me.doctype})).appendTo(this.node_view);
		}
	},
	get_toolbar: function() {
		var me = this;

		var toolbar = [
			{
				label:__(me.can_write? "Edit": "Details"),
				condition: function(node) {
					return !node.is_root && me.can_read;
				},
				click: function(node) {
					frappe.set_route("Form", me.doctype, encodeURIComponent(node.label));
				}
			},
			{
				label:__("Add Child"),
				condition: function(node) {
					return me.can_create && node.expandable && !node.hide_add;
				},
				click: function(node) {
					me.new_node();
				},
				btnClass: "hidden-xs"
			},
			{
				label:__("Rename"),
				condition: function(node) {
					let allow_rename = true;
					if (me.doctype && frappe.get_meta(me.doctype)) {
						if(!frappe.get_meta(me.doctype).allow_rename) allow_rename = false;
					}
					return !node.is_root && me.can_write && allow_rename;
				},
				click: function(node) {
					frappe.model.rename_doc(me.doctype, node.label, function(new_name) {
						node.$tree_link.find('a').text(new_name);
						node.label = new_name;
					});
				},
				btnClass: "hidden-xs"
			},
			{
				label:__("Delete"),
				condition: function(node) { return !node.is_root && me.can_delete; },
				click: function(node) {
					frappe.model.delete_doc(me.doctype, node.label, function() {
						node.parent.remove();
					});
				},
				btnClass: "hidden-xs"
			}
		]

		if(this.opts.toolbar && this.opts.extend_toolbar) {
			return toolbar.concat(this.opts.toolbar)
		} else if (this.opts.toolbar && !this.opts.extend_toolbar) {
			return this.opts.toolbar
		} else {
			return toolbar
		}
	},
	new_node: function() {
		var me = this;
		var node = me.tree.get_selected_node();

		if(!(node && node.expandable)) {
			frappe.msgprint(__("Select a group node first."));
			return;
		}

		this.prepare_fields();

		// the dialog
		var d = new frappe.ui.Dialog({
			title: __('New {0}',[__(me.doctype)]),
			fields: me.fields
		});

		var args = $.extend({}, me.args);
		args["parent_"+me.doctype.toLowerCase().replace(/ /g,'_')] = me.args["parent"];

		d.set_value("is_group", 0);
		d.set_values(args);

		// create
		d.set_primary_action(__("Create New"), function() {
			var btn = this;
			var v = d.get_values();
			if(!v) return;

			var node = me.tree.get_selected_node();
			v.parent = node.label;
			v.doctype = me.doctype;

			if(node.is_root){
				v['is_root'] = node.is_root;
			}
			else{
				v['is_root'] = false;
			}

			$.extend(args, v)
			return frappe.call({
				method: me.opts.add_tree_node || "frappe.desk.treeview.add_node",
				args: args,
				callback: function(r) {
					if(!r.exc) {
						d.hide();
						if(node.expanded) {
							me.tree.toggle_node(node);
						}
						me.tree.load_children(node, true);
					}
				}
			});
		});
		d.show();
	},
	prepare_fields: function(){
		var me = this;

		this.fields = [
			{fieldtype:'Check', fieldname:'is_group', label:__('Group Node'),
				description: __("Further nodes can be only created under 'Group' type nodes")}
		]

		if (this.opts.fields) {
			this.fields = this.opts.fields;
		}

		this.ignore_fields = this.opts.ignore_fields || [];

		var mandatory_fields = $.map(me.opts.meta.fields, function(d) {
			return (d.reqd || d.bold && !d.read_only) ? d : null });

		var opts_field_names = this.fields.map(function(d) {
			return d.fieldname
		})

		mandatory_fields.map(function(d) {
			if($.inArray(d.fieldname, me.ignore_fields) === -1 && $.inArray(d.fieldname, opts_field_names) === -1) {
				me.fields.push(d)
			}
		})
	},
	print_tree: function() {
		if(!frappe.model.can_print(this.doctype)) {
			frappe.msgprint(__("You are not allowed to print this report"));
			return false;
		}
		var tree = $(".tree:visible").html();
		var me = this;
		frappe.ui.get_print_settings(false, function(print_settings) {
			var title =  __(me.docname || me.doctype);
			frappe.render_tree({title: title, tree: tree, print_settings:print_settings});
		});
	},
	set_primary_action: function(){
		var me = this;
		if (!this.opts.disable_add_node && this.can_create) {
			me.page.set_primary_action(__("New"), function() {
				me.new_node();
			}, "octicon octicon-plus")
		}
	},
	set_menu_item: function(){
		var me = this;

		this.menu_items = [
			{
				label: __('View List'),
				action: function() {
					frappe.set_route('List', me.doctype);
				}
			},
			{
				label: __('Print'),
				action: function() {
					me.print_tree();
				}

			},
			{
				label: __('Refresh'),
				action: function() {
					me.make_tree();
				}
			},
		];

		if (me.opts.menu_items) {
			me.menu_items.push.apply(me.menu_items, me.opts.menu_items)
		}

		$.each(me.menu_items, function(i, menu_item){
			var has_perm = true;
			if(menu_item["condition"]) {
				has_perm = eval(menu_item["condition"]);
			}

			if (has_perm) {
				me.page.add_menu_item(menu_item["label"], menu_item["action"]);
			}
		});

		// last menu item
		me.page.add_menu_item(__('Add to Desktop'), () => {
			const label = me.doctype === 'Account' ?
				__('Chart of Accounts') :
				__(me.doctype);
			frappe.add_to_desktop(label, me.doctype);
		});
	}
});

<commit_msg>[Fix] Fix treeview adding child to wrong parent and Freeze screen while adding a node. (#5464)<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.treeview_settings");
frappe.provide('frappe.views.trees');
window.cur_tree = null;

frappe.views.TreeFactory = frappe.views.Factory.extend({
	make: function(route) {
		frappe.model.with_doctype(route[1], function() {
			var options = {
				doctype: route[1],
				meta: frappe.get_meta(route[1])
			};

			if (!frappe.treeview_settings[route[1]] && !frappe.meta.get_docfield(route[1], "is_group")) {
				frappe.msgprint(__("Tree view not available for {0}", [route[1]] ));
				return false;
			}
			$.extend(options, frappe.treeview_settings[route[1]] || {});
			frappe.views.trees[options.doctype] = new frappe.views.TreeView(options);
		});
	}
});

frappe.views.TreeView = Class.extend({
	init: function(opts) {
		var me = this;

		this.opts = {};
		this.opts.get_tree_root = true;
		this.opts.show_expand_all = true;
		$.extend(this.opts, opts);
		this.doctype = opts.doctype;
		this.args = {doctype: me.doctype};
		this.page_name = frappe.get_route_str();
		this.get_tree_nodes =  me.opts.get_tree_nodes || "frappe.desk.treeview.get_children";

		this.get_permissions();
		this.make_page();
		this.make_filters();

		if (me.opts.get_tree_root) {
			this.get_root();
		}

		this.onload();
		this.set_menu_item();
		this.set_primary_action();
	},
	get_permissions: function(){
		this.can_read = frappe.model.can_read(this.doctype);
		this.can_create = frappe.boot.user.can_create.indexOf(this.doctype) !== -1 ||
					frappe.boot.user.in_create.indexOf(this.doctype) !== -1;
		this.can_write = frappe.model.can_write(this.doctype);
		this.can_delete = frappe.model.can_delete(this.doctype);
	},
	make_page: function() {
		var me = this;
		this.parent = frappe.container.add_page(this.page_name);
		frappe.ui.make_app_page({parent:this.parent, single_column:true});

		this.page = this.parent.page;
		frappe.container.change_to(this.page_name);
		frappe.breadcrumbs.add(me.opts.breadcrumb || locals.DocType[me.doctype].module);

		this.set_title();

		this.page.main.css({
			"min-height": "300px",
			"padding-bottom": "25px"
		});

		if(this.opts.show_expand_all) {
			this.page.add_inner_button(__('Expand All'), function() {
				me.tree.load_children(me.tree.root_node, true);
			});
		}

		if(this.opts.view_template) {
			var row = $('

').appendTo(this.page.main);
			this.body = $('

').appendTo(row);
			this.node_view = $('

').appendTo(row);
		} else {
			this.body = this.page.main;
		}
	},
	set_title: function() {
		this.page.set_title(this.opts.title || __('{0} Tree', [__(this.doctype)]));
	},
	onload: function() {
		var me = this;
		this.opts.onload && this.opts.onload(me);
	},
	make_filters: function(){
		var me = this;
		frappe.treeview_settings.filters = []
		$.each(this.opts.filters || [], function(i, filter) {
			if(frappe.route_options && frappe.route_options[filter.fieldname]) {
				filter.default = frappe.route_options[filter.fieldname]
			}

			filter.change = function() {
				var val = this.get_value();
				me.args[filter.fieldname] = val;
				if (val) {
					me.root_label = val;
					me.page.set_title(val);
				} else {
					me.root_label = me.opts.root_label;
					me.set_title();
				}
				me.make_tree();
			}

			me.page.add_field(filter);

			if (filter.default) {
				$("[data-fieldname='"+filter.fieldname+"']").trigger("change");
			}
		})
	},
	get_root: function() {
		var me = this;
		frappe.call({
			method: me.get_tree_nodes,
			args: me.args,
			callback: function(r) {
				if (r.message) {
					me.root_label = r.message[0]["value"];
					me.make_tree();
				}
			}
		})
	},
	make_tree: function() {
		$(this.parent).find(".tree").remove();

		this.tree = new frappe.ui.Tree({
			parent: this.body,
			label: this.args[this.opts.root_label] || this.root_label || this.opts.root_label,
			expandable: true,

			args: this.args,
			method: this.get_tree_nodes,

			// array of button props: {label, condition, click, btnClass}
			toolbar: this.get_toolbar(),

			get_label: this.opts.get_label,
			on_render: this.opts.onrender,
			on_click: (node) => { this.select_node(node); },
		});

		cur_tree = this.tree;
	},

	select_node: function(node) {
		var me = this;
		if(this.opts.click) {
			this.opts.click(node);
		}
		if(this.opts.view_template) {
			this.node_view.empty();
			$(frappe.render_template(me.opts.view_template,
				{data:node.data, doctype:me.doctype})).appendTo(this.node_view);
		}
	},
	get_toolbar: function() {
		var me = this;

		var toolbar = [
			{
				label:__(me.can_write? "Edit": "Details"),
				condition: function(node) {
					return !node.is_root && me.can_read;
				},
				click: function(node) {
					frappe.set_route("Form", me.doctype, encodeURIComponent(node.label));
				}
			},
			{
				label:__("Add Child"),
				condition: function(node) {
					return me.can_create && node.expandable && !node.hide_add;
				},
				click: function(node) {
					me.new_node();
				},
				btnClass: "hidden-xs"
			},
			{
				label:__("Rename"),
				condition: function(node) {
					let allow_rename = true;
					if (me.doctype && frappe.get_meta(me.doctype)) {
						if(!frappe.get_meta(me.doctype).allow_rename) allow_rename = false;
					}
					return !node.is_root && me.can_write && allow_rename;
				},
				click: function(node) {
					frappe.model.rename_doc(me.doctype, node.label, function(new_name) {
						node.$tree_link.find('a').text(new_name);
						node.label = new_name;
					});
				},
				btnClass: "hidden-xs"
			},
			{
				label:__("Delete"),
				condition: function(node) { return !node.is_root && me.can_delete; },
				click: function(node) {
					frappe.model.delete_doc(me.doctype, node.label, function() {
						node.parent.remove();
					});
				},
				btnClass: "hidden-xs"
			}
		]

		if(this.opts.toolbar && this.opts.extend_toolbar) {
			return toolbar.concat(this.opts.toolbar)
		} else if (this.opts.toolbar && !this.opts.extend_toolbar) {
			return this.opts.toolbar
		} else {
			return toolbar
		}
	},
	new_node: function() {
		var me = this;
		var node = me.tree.get_selected_node();

		if(!(node && node.expandable)) {
			frappe.msgprint(__("Select a group node first."));
			return;
		}

		this.prepare_fields();

		// the dialog
		var d = new frappe.ui.Dialog({
			title: __('New {0}',[__(me.doctype)]),
			fields: me.fields
		});

		var args = $.extend({}, me.args);
		args["parent_"+me.doctype.toLowerCase().replace(/ /g,'_')] = me.args["parent"];

		d.set_value("is_group", 0);
		d.set_values(args);

		// create
		d.set_primary_action(__("Create New"), function() {
			var btn = this;
			var v = d.get_values();
			if(!v) return;

			v.parent = node.label;
			v.doctype = me.doctype;

			if(node.is_root){
				v['is_root'] = node.is_root;
			}
			else{
				v['is_root'] = false;
			}

			d.hide();
			frappe.dom.freeze(__('Creating {0}', [me.doctype]));

			$.extend(args, v)
			return frappe.call({
				method: me.opts.add_tree_node || "frappe.desk.treeview.add_node",
				args: args,
				callback: function(r) {
					if(!r.exc) {
						if(node.expanded) {
							me.tree.toggle_node(node);
						}
						me.tree.load_children(node, true);
					}
				},
				always: function() {
					frappe.dom.unfreeze();
				},
			});
		});
		d.show();
	},
	prepare_fields: function(){
		var me = this;

		this.fields = [
			{fieldtype:'Check', fieldname:'is_group', label:__('Group Node'),
				description: __("Further nodes can be only created under 'Group' type nodes")}
		]

		if (this.opts.fields) {
			this.fields = this.opts.fields;
		}

		this.ignore_fields = this.opts.ignore_fields || [];

		var mandatory_fields = $.map(me.opts.meta.fields, function(d) {
			return (d.reqd || d.bold && !d.read_only) ? d : null });

		var opts_field_names = this.fields.map(function(d) {
			return d.fieldname
		})

		mandatory_fields.map(function(d) {
			if($.inArray(d.fieldname, me.ignore_fields) === -1 && $.inArray(d.fieldname, opts_field_names) === -1) {
				me.fields.push(d)
			}
		})
	},
	print_tree: function() {
		if(!frappe.model.can_print(this.doctype)) {
			frappe.msgprint(__("You are not allowed to print this report"));
			return false;
		}
		var tree = $(".tree:visible").html();
		var me = this;
		frappe.ui.get_print_settings(false, function(print_settings) {
			var title =  __(me.docname || me.doctype);
			frappe.render_tree({title: title, tree: tree, print_settings:print_settings});
		});
	},
	set_primary_action: function(){
		var me = this;
		if (!this.opts.disable_add_node && this.can_create) {
			me.page.set_primary_action(__("New"), function() {
				me.new_node();
			}, "octicon octicon-plus")
		}
	},
	set_menu_item: function(){
		var me = this;

		this.menu_items = [
			{
				label: __('View List'),
				action: function() {
					frappe.set_route('List', me.doctype);
				}
			},
			{
				label: __('Print'),
				action: function() {
					me.print_tree();
				}

			},
			{
				label: __('Refresh'),
				action: function() {
					me.make_tree();
				}
			},
		];

		if (me.opts.menu_items) {
			me.menu_items.push.apply(me.menu_items, me.opts.menu_items)
		}

		$.each(me.menu_items, function(i, menu_item){
			var has_perm = true;
			if(menu_item["condition"]) {
				has_perm = eval(menu_item["condition"]);
			}

			if (has_perm) {
				me.page.add_menu_item(menu_item["label"], menu_item["action"]);
			}
		});

		// last menu item
		me.page.add_menu_item(__('Add to Desktop'), () => {
			const label = me.doctype === 'Account' ?
				__('Chart of Accounts') :
				__(me.doctype);
			frappe.add_to_desktop(label, me.doctype);
		});
	}
});

<|endoftext|>


<commit_before>import {
  getBusinessObject,
  is
} from 'bpmn-js/lib/util/ModelUtil';

import {
  SelectEntry,
  TextFieldEntry,
  TextAreaEntry,
  isSelectEntryEdited,
  isTextFieldEntryEdited,
  isTextAreaEntryEdited
} from '@bpmn-io/properties-panel';

import {
  createElement,
  nextId
} from '../../../utils/ElementUtil';

import {
  getExtensionElementsList
} from '../../../utils/ExtensionElementsUtil';

import {
  find,
  isUndefined,
  without
} from 'min-dash';

import {
  useService
} from '../../../hooks';

export function FormProps(props) {
  const {
    element,
    injector
  } = props;

  const formHelper = injector.invoke(FormHelper);

  if (!is(element, 'bpmn:UserTask')) {
    return [];
  }

  const entries = [ {
    id: 'formType',
    component: FormType,
    isEdited: isSelectEntryEdited
  } ];

  if (isCamundaForm(element, formHelper)) {
    entries.push({
      id: 'formConfiguration',
      component: FormConfiguration,
      isEdited: isTextAreaEntryEdited
    });

  } else if (isCustomKey(element, formHelper)) {
    entries.push({
      id: 'customFormKey',
      component: CustomFormKey,
      isEdited: isTextFieldEntryEdited
    });
  }

  return entries;
}

function FormType(props) {

  const {
    element
  } = props;

  const translate = useService('translate');
  const injector = useService('injector');
  const formHelper = injector.invoke(FormHelper);

  const getValue = () => {
    const formDefinition = formHelper.getFormDefinition(element);
    const userTaskForm = formHelper.getUserTaskForm(element);

    if (formDefinition) {

      if (userTaskForm) {
        return 'camundaForm';
      }

      return 'formKey';
    }

    return '';
  };

  const setValue = (value) => {
    formHelper.resetForm(element);

    if (value === 'camundaForm') {
      formHelper.setUserTaskForm(element, '');

    } else if (value === 'formKey') {
      formHelper.setFormDefinition(element, '');
    }
  };

  const getOptions = () => {
    return [
      { value: '', label: translate('<none>') },
      { value: 'camundaForm', label: translate('Camunda forms') },
      { value: 'formKey', label: translate('Custom form key') },
    ];
  };

  return SelectEntry({
    element,
    id: 'formType',
    label: translate('Type'),
    getValue,
    setValue,
    getOptions
  });
}

function FormConfiguration(props) {
  const {
    element
  } = props;

  const injector = useService('injector');
  const debounce = useService('debounceInput');
  const translate = useService('translate');
  const formHelper = injector.invoke(FormHelper);

  const getValue = () => {
    const userTaskForm = formHelper.getUserTaskForm(element);
    return userTaskForm.get('body');
  };

  const setValue = (value) => {
    formHelper.setUserTaskForm(element, value);
  };

  return TextAreaEntry({
    element,
    id: 'formConfiguration',
    label: translate('Form JSON configuration'),
    rows: 4,
    getValue,
    setValue,
    debounce
  });
}

function CustomFormKey(props) {
  const {
    element
  } = props;

  const injector = useService('injector');
  const debounce = useService('debounceInput');
  const translate = useService('translate');
  const formHelper = injector.invoke(FormHelper);

  const getValue = () => {
    const formDefinition = formHelper.getFormDefinition(element);
    return formDefinition.get('formKey');
  };

  const setValue = (value) => {
    formHelper.setFormDefinition(element, value);
  };

  return TextFieldEntry({
    element,
    id: 'customFormKey',
    label: translate('Form Key'),
    getValue,
    setValue,
    debounce
  });
}

const USER_TASK_FORM_PREFIX = 'userTaskForm_';

function FormHelper(bpmnFactory, commandStack) {

  function getFormDefinition(element) {
    const businessObject = getBusinessObject(element);

    const formDefinitions = getExtensionElementsList(businessObject, 'zeebe:FormDefinition');

    return formDefinitions[0];
  }

  function getUserTaskForm(element, parent) {

    const rootElement = parent || getRootElement(element);

    // (1) get form definition from user task
    const formDefinition = getFormDefinition(element);

    if (isUndefined(formDefinition)) {
      return;
    }

    const formKey = formDefinition.get('formKey');

    // (2) retrieve user task form via form key
    const userTaskForm = findUserTaskForm(formKey, rootElement);

    return userTaskForm;
  }

  function ensureTaskForm(element, values) {

    let commands = [];

    const rootElement = getRootElement(element);

    // (1) ensure root element extension elements
    let rootExtensionElements = rootElement.get('extensionElements');

    if (!rootExtensionElements) {
      rootExtensionElements = createElement(
        'bpmn:ExtensionElements',
        { values: [] },
        rootElement,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, rootElement, {
          extensionElements: rootExtensionElements,
        })
      );
    }

    // (2) ensure user task form
    let userTaskForm = getUserTaskForm(element);

    // (2.1) create user task form if doesn't exist
    if (!userTaskForm) {
      userTaskForm = createUserTaskForm(
        values,
        rootExtensionElements,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, rootExtensionElements,{
          values: [ ...rootExtensionElements.get('values'), userTaskForm ]
        })
      );
    }

    commands.push(UpdateModdlePropertiesCmd(element, userTaskForm, values));

    return commands;
  }

  function ensureFormDefinition(element, customFormKey) {
    const businessObject = getBusinessObject(element);

    let commands = [];

    // (1) ensure extension elements
    let extensionElements = businessObject.get('extensionElements');

    if (isUndefined(extensionElements)) {
      extensionElements = createElement(
        'bpmn:ExtensionElements',
        { values: [] },
        businessObject,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, businessObject, {
          extensionElements: extensionElements,
        })
      );
    }

    // (2) ensure form definition
    let formDefinition = getFormDefinition(element);

    // (2.1) create if doesn't exist
    if (!formDefinition) {
      let formKey = customFormKey;

      if (isUndefined(formKey)) {
        const formId = createFormId();
        formKey = createFormKey(formId);
      }

      formDefinition = createFormDefinition(
        {
          formKey
        },
        extensionElements,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, extensionElements, {
          values: [ ...extensionElements.get('values'), formDefinition ]
        })
      );
    }

    // (2.2) update existing form definition with custom key
    else if (customFormKey) {
      commands.push(
        UpdateModdlePropertiesCmd(element, formDefinition, {
          formKey: customFormKey
        })
      );
    }

    return {
      formId: resolveFormId(formDefinition.get('formKey')),
      commands
    };
  }

  function setFormDefinition(element, customFormKey) {

    const {
      commands
    } = ensureFormDefinition(element, customFormKey);

    commandStack.execute('properties-panel.multi-command-executor', commands);
  }

  function setUserTaskForm(element, value) {

    const {
      formId,
      commands: formDefCommands
    } = ensureFormDefinition(element);

    const userTaskCommands = ensureTaskForm(element, { id:formId, body:value });
    const commands = formDefCommands.concat(userTaskCommands);

    commandStack.execute('properties-panel.multi-command-executor', commands);
  }

  function unsetFormDefinition(element) {

    const businessObject = getBusinessObject(element),
          extensionElements = businessObject.get('extensionElements');

    let commands = [];

    const formDefinition = getFormDefinition(element);

    if (!formDefinition) {
      return commands;
    }

    let values = without(extensionElements.get('values'), formDefinition);

    commands.push(
      UpdateModdlePropertiesCmd(element, extensionElements, { values })
    );

    return commands;
  }

  function resetForm(element) {

    const rootElement = getRootElement(element),
          rootExtensionElements = rootElement.get('extensionElements');

    // (1) remove form definition
    const commands = unsetFormDefinition(element);

    // (2) remove referenced user task form
    const userTaskForm = getUserTaskForm(element);

    if (!userTaskForm) {
      commandStack.execute('properties-panel.multi-command-executor', commands);
      return;
    }

    const values = without(rootExtensionElements.get('values'), userTaskForm);

    commands.push(
      UpdateModdlePropertiesCmd(element, rootExtensionElements, { values })
    );

    commandStack.execute('properties-panel.multi-command-executor', commands);

  }

  function createFormKey(formId) {
    return 'camunda-forms:bpmn:' + formId;
  }

  function createFormId() {
    return nextId(USER_TASK_FORM_PREFIX);
  }

  function resolveFormId(formKey) {
    return formKey.split(':')[2];
  }

  function createFormDefinition(properties, extensionElements, bpmnFactory) {
    return createElement(
      'zeebe:FormDefinition',
      properties,
      extensionElements,
      bpmnFactory
    );
  }

  function createUserTaskForm(properties, extensionElements, bpmnFactory) {
    return createElement(
      'zeebe:UserTaskForm',
      properties,
      extensionElements,
      bpmnFactory
    );
  }

  function findUserTaskForm(formKey, rootElement) {
    const forms = getExtensionElementsList(rootElement, 'zeebe:UserTaskForm');

    return find(forms, function(userTaskForm) {
      return createFormKey(userTaskForm.id) === formKey;
    });
  }

  function getRootElement(element) {
    const businessObject = getBusinessObject(element);

    let parent = businessObject;

    while (parent.$parent && !is(parent, 'bpmn:Process')) {
      parent = parent.$parent;
    }

    return parent;
  }

  return {
    getFormDefinition,
    getUserTaskForm,
    setFormDefinition,
    setUserTaskForm,
    resetForm
  };

}

FormHelper.$inject = [ 'bpmnFactory', 'commandStack' ];

// helpers /////////////

function UpdateModdlePropertiesCmd(element, businessObject, newProperties) {
  return {
    cmd: 'element.updateModdleProperties',
    context: {
      element,
      moddleElement: businessObject,
      properties: newProperties
    }
  };
}

function isCamundaForm(element, formHelper) {
  const formDefinition = formHelper.getFormDefinition(element);
  const userTaskForm = formHelper.getUserTaskForm(element);

  return formDefinition && userTaskForm;
}

function isCustomKey(element, formHelper) {
  const formDefinition = formHelper.getFormDefinition(element);
  const userTaskForm = formHelper.getUserTaskForm(element);

  return formDefinition && !userTaskForm;
}<commit_msg>chore: typo<commit_after>import {
  getBusinessObject,
  is
} from 'bpmn-js/lib/util/ModelUtil';

import {
  SelectEntry,
  TextFieldEntry,
  TextAreaEntry,
  isSelectEntryEdited,
  isTextFieldEntryEdited,
  isTextAreaEntryEdited
} from '@bpmn-io/properties-panel';

import {
  createElement,
  nextId
} from '../../../utils/ElementUtil';

import {
  getExtensionElementsList
} from '../../../utils/ExtensionElementsUtil';

import {
  find,
  isUndefined,
  without
} from 'min-dash';

import {
  useService
} from '../../../hooks';

export function FormProps(props) {
  const {
    element,
    injector
  } = props;

  const formHelper = injector.invoke(FormHelper);

  if (!is(element, 'bpmn:UserTask')) {
    return [];
  }

  const entries = [ {
    id: 'formType',
    component: FormType,
    isEdited: isSelectEntryEdited
  } ];

  if (isCamundaForm(element, formHelper)) {
    entries.push({
      id: 'formConfiguration',
      component: FormConfiguration,
      isEdited: isTextAreaEntryEdited
    });

  } else if (isCustomKey(element, formHelper)) {
    entries.push({
      id: 'customFormKey',
      component: CustomFormKey,
      isEdited: isTextFieldEntryEdited
    });
  }

  return entries;
}

function FormType(props) {

  const {
    element
  } = props;

  const translate = useService('translate');
  const injector = useService('injector');
  const formHelper = injector.invoke(FormHelper);

  const getValue = () => {
    const formDefinition = formHelper.getFormDefinition(element);
    const userTaskForm = formHelper.getUserTaskForm(element);

    if (formDefinition) {

      if (userTaskForm) {
        return 'camundaForm';
      }

      return 'formKey';
    }

    return '';
  };

  const setValue = (value) => {
    formHelper.resetForm(element);

    if (value === 'camundaForm') {
      formHelper.setUserTaskForm(element, '');

    } else if (value === 'formKey') {
      formHelper.setFormDefinition(element, '');
    }
  };

  const getOptions = () => {
    return [
      { value: '', label: translate('<none>') },
      { value: 'camundaForm', label: translate('Camunda forms') },
      { value: 'formKey', label: translate('Custom form key') },
    ];
  };

  return SelectEntry({
    element,
    id: 'formType',
    label: translate('Type'),
    getValue,
    setValue,
    getOptions
  });
}

function FormConfiguration(props) {
  const {
    element
  } = props;

  const injector = useService('injector');
  const debounce = useService('debounceInput');
  const translate = useService('translate');
  const formHelper = injector.invoke(FormHelper);

  const getValue = () => {
    const userTaskForm = formHelper.getUserTaskForm(element);
    return userTaskForm.get('body');
  };

  const setValue = (value) => {
    formHelper.setUserTaskForm(element, value);
  };

  return TextAreaEntry({
    element,
    id: 'formConfiguration',
    label: translate('Form JSON configuration'),
    rows: 4,
    getValue,
    setValue,
    debounce
  });
}

function CustomFormKey(props) {
  const {
    element
  } = props;

  const injector = useService('injector');
  const debounce = useService('debounceInput');
  const translate = useService('translate');
  const formHelper = injector.invoke(FormHelper);

  const getValue = () => {
    const formDefinition = formHelper.getFormDefinition(element);
    return formDefinition.get('formKey');
  };

  const setValue = (value) => {
    formHelper.setFormDefinition(element, value);
  };

  return TextFieldEntry({
    element,
    id: 'customFormKey',
    label: translate('Form key'),
    getValue,
    setValue,
    debounce
  });
}

const USER_TASK_FORM_PREFIX = 'userTaskForm_';

function FormHelper(bpmnFactory, commandStack) {

  function getFormDefinition(element) {
    const businessObject = getBusinessObject(element);

    const formDefinitions = getExtensionElementsList(businessObject, 'zeebe:FormDefinition');

    return formDefinitions[0];
  }

  function getUserTaskForm(element, parent) {

    const rootElement = parent || getRootElement(element);

    // (1) get form definition from user task
    const formDefinition = getFormDefinition(element);

    if (isUndefined(formDefinition)) {
      return;
    }

    const formKey = formDefinition.get('formKey');

    // (2) retrieve user task form via form key
    const userTaskForm = findUserTaskForm(formKey, rootElement);

    return userTaskForm;
  }

  function ensureTaskForm(element, values) {

    let commands = [];

    const rootElement = getRootElement(element);

    // (1) ensure root element extension elements
    let rootExtensionElements = rootElement.get('extensionElements');

    if (!rootExtensionElements) {
      rootExtensionElements = createElement(
        'bpmn:ExtensionElements',
        { values: [] },
        rootElement,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, rootElement, {
          extensionElements: rootExtensionElements,
        })
      );
    }

    // (2) ensure user task form
    let userTaskForm = getUserTaskForm(element);

    // (2.1) create user task form if doesn't exist
    if (!userTaskForm) {
      userTaskForm = createUserTaskForm(
        values,
        rootExtensionElements,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, rootExtensionElements,{
          values: [ ...rootExtensionElements.get('values'), userTaskForm ]
        })
      );
    }

    commands.push(UpdateModdlePropertiesCmd(element, userTaskForm, values));

    return commands;
  }

  function ensureFormDefinition(element, customFormKey) {
    const businessObject = getBusinessObject(element);

    let commands = [];

    // (1) ensure extension elements
    let extensionElements = businessObject.get('extensionElements');

    if (isUndefined(extensionElements)) {
      extensionElements = createElement(
        'bpmn:ExtensionElements',
        { values: [] },
        businessObject,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, businessObject, {
          extensionElements: extensionElements,
        })
      );
    }

    // (2) ensure form definition
    let formDefinition = getFormDefinition(element);

    // (2.1) create if doesn't exist
    if (!formDefinition) {
      let formKey = customFormKey;

      if (isUndefined(formKey)) {
        const formId = createFormId();
        formKey = createFormKey(formId);
      }

      formDefinition = createFormDefinition(
        {
          formKey
        },
        extensionElements,
        bpmnFactory
      );

      commands.push(
        UpdateModdlePropertiesCmd(element, extensionElements, {
          values: [ ...extensionElements.get('values'), formDefinition ]
        })
      );
    }

    // (2.2) update existing form definition with custom key
    else if (customFormKey) {
      commands.push(
        UpdateModdlePropertiesCmd(element, formDefinition, {
          formKey: customFormKey
        })
      );
    }

    return {
      formId: resolveFormId(formDefinition.get('formKey')),
      commands
    };
  }

  function setFormDefinition(element, customFormKey) {

    const {
      commands
    } = ensureFormDefinition(element, customFormKey);

    commandStack.execute('properties-panel.multi-command-executor', commands);
  }

  function setUserTaskForm(element, value) {

    const {
      formId,
      commands: formDefCommands
    } = ensureFormDefinition(element);

    const userTaskCommands = ensureTaskForm(element, { id:formId, body:value });
    const commands = formDefCommands.concat(userTaskCommands);

    commandStack.execute('properties-panel.multi-command-executor', commands);
  }

  function unsetFormDefinition(element) {

    const businessObject = getBusinessObject(element),
          extensionElements = businessObject.get('extensionElements');

    let commands = [];

    const formDefinition = getFormDefinition(element);

    if (!formDefinition) {
      return commands;
    }

    let values = without(extensionElements.get('values'), formDefinition);

    commands.push(
      UpdateModdlePropertiesCmd(element, extensionElements, { values })
    );

    return commands;
  }

  function resetForm(element) {

    const rootElement = getRootElement(element),
          rootExtensionElements = rootElement.get('extensionElements');

    // (1) remove form definition
    const commands = unsetFormDefinition(element);

    // (2) remove referenced user task form
    const userTaskForm = getUserTaskForm(element);

    if (!userTaskForm) {
      commandStack.execute('properties-panel.multi-command-executor', commands);
      return;
    }

    const values = without(rootExtensionElements.get('values'), userTaskForm);

    commands.push(
      UpdateModdlePropertiesCmd(element, rootExtensionElements, { values })
    );

    commandStack.execute('properties-panel.multi-command-executor', commands);

  }

  function createFormKey(formId) {
    return 'camunda-forms:bpmn:' + formId;
  }

  function createFormId() {
    return nextId(USER_TASK_FORM_PREFIX);
  }

  function resolveFormId(formKey) {
    return formKey.split(':')[2];
  }

  function createFormDefinition(properties, extensionElements, bpmnFactory) {
    return createElement(
      'zeebe:FormDefinition',
      properties,
      extensionElements,
      bpmnFactory
    );
  }

  function createUserTaskForm(properties, extensionElements, bpmnFactory) {
    return createElement(
      'zeebe:UserTaskForm',
      properties,
      extensionElements,
      bpmnFactory
    );
  }

  function findUserTaskForm(formKey, rootElement) {
    const forms = getExtensionElementsList(rootElement, 'zeebe:UserTaskForm');

    return find(forms, function(userTaskForm) {
      return createFormKey(userTaskForm.id) === formKey;
    });
  }

  function getRootElement(element) {
    const businessObject = getBusinessObject(element);

    let parent = businessObject;

    while (parent.$parent && !is(parent, 'bpmn:Process')) {
      parent = parent.$parent;
    }

    return parent;
  }

  return {
    getFormDefinition,
    getUserTaskForm,
    setFormDefinition,
    setUserTaskForm,
    resetForm
  };

}

FormHelper.$inject = [ 'bpmnFactory', 'commandStack' ];

// helpers /////////////

function UpdateModdlePropertiesCmd(element, businessObject, newProperties) {
  return {
    cmd: 'element.updateModdleProperties',
    context: {
      element,
      moddleElement: businessObject,
      properties: newProperties
    }
  };
}

function isCamundaForm(element, formHelper) {
  const formDefinition = formHelper.getFormDefinition(element);
  const userTaskForm = formHelper.getUserTaskForm(element);

  return formDefinition && userTaskForm;
}

function isCustomKey(element, formHelper) {
  const formDefinition = formHelper.getFormDefinition(element);
  const userTaskForm = formHelper.getUserTaskForm(element);

  return formDefinition && !userTaskForm;
}<|endoftext|>


<commit_before>/* @flow */
/* eslint max-lines: 0 */

import { ZalgoPromise } from 'zalgo-promise/src';
import { info, track, warn, flush as flushLogs, immediateFlush } from 'beaver-logger/client';
import { create, CONSTANTS, PopupOpenError } from 'xcomponent/src';
import { type Component } from 'xcomponent/src/component/component';
import type { CrossDomainWindowType } from 'cross-domain-utils/src';

import { isDevice, request, getQueryParam, redirect as redir, patchMethod,
    setLogLevel, getSessionID, getBrowserLocale, supportsPopups, memoize,
    getDomainSetting, documentReady, getThrottle, getScriptVersion,
    getButtonSessionID, isPayPalDomain } from '../lib';
import { config } from '../config';
import { ENV, FPTI, PAYMENT_TYPE, CHECKOUT_OVERLAY_COLOR } from '../constants';
import { onLegacyPaymentAuthorize } from '../compat';
import { determineParameterFromToken, determineUrl } from '../integrations';

import { containerTemplate, componentTemplate } from './template';

function addHeader(name, value) : void {

    if (!window.$Api) {
        return;
    }

    if (window.$Api.addHeader) {
        return window.$Api.addHeader(name, value);
    }
}

type CheckoutPropsType = {
    payment? : () => ZalgoPromise<string>,
    onAuthorize : ({ returnUrl : string }, { redirect : (?CrossDomainWindowType, ?string) => ZalgoPromise<void> }) => ?ZalgoPromise<void>,
    onCancel? : ({ cancelUrl : string }, { redirect : (?CrossDomainWindowType, ?string) => ZalgoPromise<void> }) => ?ZalgoPromise<void>,
    fallback? : (string) => ?ZalgoPromise<void>,
    fundingSource? : string,
    logLevel? : string,
    env? : string,
    stage? : string,
    stageUrl? : string,
    supplement? : {
        getPaymentOptions : Function,
        addPaymentDetails : Function
    }
};

export let Checkout : Component<CheckoutPropsType> = create({

    tag:  'paypal-checkout',
    name: 'ppcheckout',

    scrolling: true,

    buildUrl(props) : ZalgoPromise<string> {
        let env = props.env || config.env;

        if (!props.payment) {
            throw new Error(`Can not build url without payment prop`);
        }

        return props.payment().then(token => {
            if (!token) {
                throw new Error(`Expected payment id or token to be passed, got ${ token }`);
            }

            return determineUrl(env, props.fundingSource, token);
        });
    },

    get unsafeRenderTo() : boolean {
        return config.env === ENV.LOCAL;
    },

    get domain() : Object {
        return {
            ...config.paypalDomains,
            [ ENV.LOCAL ]: /^http:\/\/localhost.paypal.com:\d+$/
        };
    },

    get bridgeUrl() : Object {
        return config.metaFrameUrls;
    },

    get bridgeDomain() : Object {
        return config.paypalDomains;
    },

    contexts: {
        iframe: (!supportsPopups()),
        popup:  true
    },

    get version() : string {
        return getScriptVersion();
    },

    prerenderTemplate: componentTemplate,
    containerTemplate,

    props: {

        sessionID: {
            type:     'string',
            required: false,
            def() : string {
                return getSessionID();
            },
            queryParam: true
        },

        buttonSessionID: {
            type:     'string',
            required: false,
            def() : ?string {
                return getButtonSessionID();
            },
            queryParam: true
        },

        env: {
            type:       'string',
            required:   false,
            queryParam: true,

            def() : string {
                return config.env;
            },

            validate(env) {
                if (!config.paypalUrls[env]) {
                    throw new Error(`Invalid env: ${ env }`);
                }
            }
        },

        meta: {
            type:     'object',
            required: false,
            def() : Object {
                let meta = window.xprops && window.xprops.meta;
                return meta || {};
            }
        },

        stage: {
            type:       'string',
            required:   false,
            queryParam: true,

            def(props) : ?string {
                let env = props.env || config.env;

                if (env === ENV.STAGE || env === ENV.LOCAL) {
                    return config.stage;
                }
            }
        },

        stageUrl: {
            type:       'string',
            required:   false,
            queryParam: true,

            def(props) : ?string {
                let env = props.env || config.env;

                if (env === ENV.STAGE || env === ENV.LOCAL) {
                    return config.stageUrl;
                }
            }
        },

        locale: {
            type:          'string',
            required:      false,
            queryParam:    'locale.x',
            allowDelegate: true,

            def() : string {
                let { lang, country } = getBrowserLocale();
                return `${ lang }_${ country }`;
            }
        },

        client: {
            type:     'object',
            required: false,
            def() : { [string] : string } {
                return {};
            },
            sendToChild: false,

            validate(client, props) {
                let env = props.env || config.env;

                if (!client[env]) {
                    throw new Error(`Client ID not found for env: ${ env }`);
                }

                if (client[env].match(/^(.)\1+$/)) {
                    throw new Error(`Invalid client ID: ${ client[env] }`);
                }
            }
        },

        payment: {
            type:      'function',
            required:  false,
            memoize:   true,
            promisify: true,
            queryParam(payment) : ZalgoPromise<string> {
                return payment().then(token => {
                    return determineParameterFromToken(token);
                });
            },
            queryValue(payment) : ZalgoPromise<string> {
                return payment();
            },
            childDecorate(payment) : () => ZalgoPromise<string> {
                let token = getQueryParam('token');

                return token
                    ? memoize(() => ZalgoPromise.resolve(token))
                    : payment;
            },
            validate(payment, props) {
                if (!payment && !props.url) {
                    throw new Error(`Expected either props.payment or props.url to be passed`);
                }
            },
            alias: 'billingAgreement'
        },

        style: {
            type:          'object',
            required:      false,
            allowDelegate: true,
            def() : Object {
                return {};
            },
            validate(style) {
                if (style.overlayColor && style.overlayColor !== CHECKOUT_OVERLAY_COLOR.BLACK && style.overlayColor !== CHECKOUT_OVERLAY_COLOR.WHITE) {
                    throw new Error(`Invalid background color: ${ style.overlayColor }`);
                }
            }
        },

        commit: {
            type:     'boolean',
            required: false
        },

        experience: {
            type:     'object',
            required: false,
            def() : Object {
                return {};
            }
        },

        fundingSource: {
            type:       'string',
            required:   false,
            queryParam: true
        },

        onAuthorize: {
            type:     'function',
            required: true,
            once:     true,

            decorate(original) : Function | void {
                if (original) {
                    return function decorateOnAuthorize(data, actions = {}) : ZalgoPromise<void> {

                        if (data && !data.intent) {
                            warn(`checkout_authorize_no_intent`, { paymentID: data.paymentID, token: data.paymentToken });
                        }

                        let close = () => {
                            return ZalgoPromise.try(() => {
                                if (actions.close) {
                                    return actions.close();
                                }
                            }).then(() => {
                                return this.closeComponent();
                            });
                        };

                        let redirect = (win, url) => {
                            return ZalgoPromise.all([
                                redir(win || window.top, url || data.returnUrl),
                                close()
                            ]);
                        };

                        return ZalgoPromise.try(() => {

                            try {
                                let isButton = window.location.href.indexOf('/webapps/hermes/button') !== -1;
                                let isGuest  = this.window.location.href.indexOf('/webapps/xoonboarding') !== -1;

                                if (isButton && isGuest) {
                                    return request({
                                        win:    this.window,
                                        method: 'get',
                                        url:    '/webapps/hermes/api/auth'
                                    }).then(result => {
                                        if (result && result.data && result.data.access_token) {
                                            addHeader('x-paypal-internal-euat', result.data.access_token);
                                        }
                                    }).catch(() => {
                                        // pass
                                    });
                                }

                            } catch (err) {
                                // pass
                            }

                        }).then(() => {
                            return original.call(this, data, { ...actions, close, redirect });
                        }).catch(err => {
                            return this.error(err);
                        }).finally(() => {
                            return this.close();
                        });
                    };
                }
            }
        },

        onAuth: {
            type:       'function',
            required:   false,
            sameDomain: true
        },

        accessToken: {
            type:     'function',
            required: false
        },

        onCancel: {
            type:     'function',
            required: false,
            once:     true,
            noop:     true,

            decorate(original) : Function {
                return function decorateOnCancel(data, actions = {}) : ZalgoPromise<void> {

                    let close = () => {
                        return ZalgoPromise.try(() => {
                            if (actions.close) {
                                return actions.close();
                            }
                        }).then(() => {
                            return this.closeComponent();
                        });
                    };

                    let redirect = (win, url) => {
                        return ZalgoPromise.all([
                            redir(win || window.top, url || data.cancelUrl),
                            close()
                        ]);
                    };

                    return ZalgoPromise.try(() => {
                        return original.call(this, data, { ...actions, close, redirect });
                    }).finally(() => {
                        this.close();
                    });
                };
            }
        },

        init: {
            type:     'function',
            required: false,
            once:     true,
            noop:     true,

            decorate(original) : Function {
                return function decorateInit(data) : void {
                    info('checkout_init');

                    track({
                        [ FPTI.KEY.STATE ]:        FPTI.STATE.CHECKOUT,
                        [ FPTI.KEY.TRANSITION ]:   FPTI.TRANSITION.CHECKOUT_INIT,
                        [ FPTI.KEY.CONTEXT_TYPE ]: FPTI.CONTEXT_TYPE[PAYMENT_TYPE.EC_TOKEN],
                        [ FPTI.KEY.TOKEN ]:        data.paymentToken,
                        [ FPTI.KEY.SELLER_ID ]:    data.merchantID,
                        [ FPTI.KEY.CONTEXT_ID ]:   data.paymentToken
                    });

                    flushLogs();

                    this.paymentToken = data.paymentToken;
                    this.cancelUrl    = data.cancelUrl;

                    return original.apply(this, arguments);
                };
            }
        },

        onClose: {
            type:      'function',
            required:  false,
            once:      true,
            promisify: true,
            noop:      true,

            decorate(original) : Function {
                return function decorateOnClose(reason) : ZalgoPromise<void> {

                    let onClose = original.apply(this, arguments);

                    let CLOSE_REASONS = CONSTANTS.CLOSE_REASONS;

                    let shouldCancel =
                        this.props.onCancel &&
                        [ CLOSE_REASONS.CLOSE_DETECTED, CLOSE_REASONS.USER_CLOSED ].indexOf(reason) !== -1;

                    if (shouldCancel) {
                        info(`close_trigger_cancel`);
                        return this.props.onCancel({
                            paymentToken: this.paymentToken,
                            cancelUrl:    this.cancelUrl
                        }).then(() => onClose);
                    }

                    return onClose;
                };
            }
        },

        onError: {
            type:      'function',
            required:  false,
            promisify: true,
            noop:      true,
            once:      true
        },

        fallback: {
            type:     'function',
            required: false,
            once:     true,

            def() : Function {
                return function defaultFallback(url) : ZalgoPromise<void> {
                    warn('fallback', { url });

                    if (getDomainSetting('allow_full_page_fallback')) {
                        window.top.location = url;
                        return this.close();
                    }

                    return onLegacyPaymentAuthorize(this.props.onAuthorize);
                };
            }
        },

        logLevel: {
            type:     'string',
            required: false,
            get value() : string {
                return config.logLevel;
            }
        },

        supplement: {
            type:     'object',
            required: false,
            get value() : Object {
                // $FlowFixMe
                let value : Object = window.xprops && window.xprops.supplement;
                return value;
            }
        },

        test: {
            type:     'object',
            required: false,
            def() : Object {
                return window.__test__ || { action: 'checkout' };
            }
        }
    },

    autoResize: {
        width:  false,
        height: false
    },

    get dimensions() : { width : string, height : string } {

        if (isDevice()) {
            return {
                width:  '100%',
                height: '535px'
            };
        }

        return {
            width:  '450px',
            height: '535px'
        };
    }
});

if (Checkout.isChild() && Checkout.xchild && Checkout.xprops) {

    if (Checkout.xprops && Checkout.xprops.logLevel) {
        setLogLevel(Checkout.xprops.logLevel);
    }

    Checkout.xchild.onProps(xprops => {
        patchMethod(xprops, 'onAuthorize', ({ callOriginal, args: [ data ] }) => {
            if (data && !data.intent) {
                warn(`hermes_authorize_no_intent`, { paymentID: data.paymentID, token: data.paymentToken });

                try {
                    let intent = window.injector.get('$CheckoutCartModel').instance(data.paymentToken).payment_action;
                    warn(`hermes_intent`, { paymentID: data.paymentID, token: data.paymentToken, intent });
                } catch (err) {
                    // pass
                }

                immediateFlush();
            }
            return callOriginal();
        });
    });

    documentReady.then(() => {

        if (!window.injector) {
            return;
        }

        let $event = window.injector.get('$event');

        if (!$event) {
            return;
        }

        let experimentActive = false;
        let loggedComplete = false;

        $event.on('allLoaded', () => {
            setTimeout(() => {
                let payButton = document.querySelector('.buttons.reviewButton');
                let topPayButton = document.querySelector('.buttons.reviewButton.topReviewButton');
                let reviewSection = document.querySelector('section.review');

                let throttle = getThrottle('top_pay_button', 50);

                let hash = window.location.hash;

                let logComplete = () => {
                    if (experimentActive && !loggedComplete && hash && hash.indexOf('checkout/review') !== -1) {
                        throttle.logComplete({ [ FPTI.KEY.FEED ]: 'hermesnodeweb' });
                        loggedComplete = true;
                    }
                };

                if (payButton) {
                    payButton.addEventListener('click', logComplete);
                }

                if (!reviewSection || !reviewSection.firstChild || !payButton || topPayButton) {
                    return;
                }

                if (payButton.getBoundingClientRect().bottom < window.innerHeight) {
                    return;
                }

                experimentActive = true;
                throttle.logStart({ [ FPTI.KEY.FEED ]: 'hermesnodeweb' });

                if (!throttle.isEnabled()) {
                    return;
                }

                topPayButton = payButton.cloneNode(true);
                topPayButton.className += ' topReviewButton';
                reviewSection.insertBefore(topPayButton, reviewSection.firstChild);

                topPayButton.addEventListener('click', () => {
                    logComplete();
                    let button = payButton && payButton.querySelector('button, input');
                    if (button) {
                        button.click();
                    }
                });
            }, 200);
        });
    });
}

patchMethod(Checkout, 'init', ({ args: [ props, _context ], original, context }) => {
    return original.call(context, props, _context, 'body');
});

patchMethod(Checkout, 'render', ({ args: [ props ], original, context }) => {
    return original.call(context, props, 'body');
});

patchMethod(Checkout, 'renderTo', ({ args: [ win, props ], original, context }) => {

    let payment = props.payment();
    props.payment = () => payment;

    return original.call(context, win, props, 'body').catch(err => {
        if (err instanceof PopupOpenError && isPayPalDomain()) {
            Checkout.contexts.iframe = true;
            return original.call(context, win, props, 'body');
        }
        throw err;
    });
});

<commit_msg>Ramping experiment down to zero (#693)<commit_after>/* @flow */
/* eslint max-lines: 0 */

import { ZalgoPromise } from 'zalgo-promise/src';
import { info, track, warn, flush as flushLogs, immediateFlush } from 'beaver-logger/client';
import { create, CONSTANTS, PopupOpenError } from 'xcomponent/src';
import { type Component } from 'xcomponent/src/component/component';
import type { CrossDomainWindowType } from 'cross-domain-utils/src';

import { isDevice, request, getQueryParam, redirect as redir, patchMethod,
    setLogLevel, getSessionID, getBrowserLocale, supportsPopups, memoize,
    getDomainSetting, documentReady, getThrottle, getScriptVersion,
    getButtonSessionID, isPayPalDomain } from '../lib';
import { config } from '../config';
import { ENV, FPTI, PAYMENT_TYPE, CHECKOUT_OVERLAY_COLOR } from '../constants';
import { onLegacyPaymentAuthorize } from '../compat';
import { determineParameterFromToken, determineUrl } from '../integrations';

import { containerTemplate, componentTemplate } from './template';

function addHeader(name, value) : void {

    if (!window.$Api) {
        return;
    }

    if (window.$Api.addHeader) {
        return window.$Api.addHeader(name, value);
    }
}

type CheckoutPropsType = {
    payment? : () => ZalgoPromise<string>,
    onAuthorize : ({ returnUrl : string }, { redirect : (?CrossDomainWindowType, ?string) => ZalgoPromise<void> }) => ?ZalgoPromise<void>,
    onCancel? : ({ cancelUrl : string }, { redirect : (?CrossDomainWindowType, ?string) => ZalgoPromise<void> }) => ?ZalgoPromise<void>,
    fallback? : (string) => ?ZalgoPromise<void>,
    fundingSource? : string,
    logLevel? : string,
    env? : string,
    stage? : string,
    stageUrl? : string,
    supplement? : {
        getPaymentOptions : Function,
        addPaymentDetails : Function
    }
};

export let Checkout : Component<CheckoutPropsType> = create({

    tag:  'paypal-checkout',
    name: 'ppcheckout',

    scrolling: true,

    buildUrl(props) : ZalgoPromise<string> {
        let env = props.env || config.env;

        if (!props.payment) {
            throw new Error(`Can not build url without payment prop`);
        }

        return props.payment().then(token => {
            if (!token) {
                throw new Error(`Expected payment id or token to be passed, got ${ token }`);
            }

            return determineUrl(env, props.fundingSource, token);
        });
    },

    get unsafeRenderTo() : boolean {
        return config.env === ENV.LOCAL;
    },

    get domain() : Object {
        return {
            ...config.paypalDomains,
            [ ENV.LOCAL ]: /^http:\/\/localhost.paypal.com:\d+$/
        };
    },

    get bridgeUrl() : Object {
        return config.metaFrameUrls;
    },

    get bridgeDomain() : Object {
        return config.paypalDomains;
    },

    contexts: {
        iframe: (!supportsPopups()),
        popup:  true
    },

    get version() : string {
        return getScriptVersion();
    },

    prerenderTemplate: componentTemplate,
    containerTemplate,

    props: {

        sessionID: {
            type:     'string',
            required: false,
            def() : string {
                return getSessionID();
            },
            queryParam: true
        },

        buttonSessionID: {
            type:     'string',
            required: false,
            def() : ?string {
                return getButtonSessionID();
            },
            queryParam: true
        },

        env: {
            type:       'string',
            required:   false,
            queryParam: true,

            def() : string {
                return config.env;
            },

            validate(env) {
                if (!config.paypalUrls[env]) {
                    throw new Error(`Invalid env: ${ env }`);
                }
            }
        },

        meta: {
            type:     'object',
            required: false,
            def() : Object {
                let meta = window.xprops && window.xprops.meta;
                return meta || {};
            }
        },

        stage: {
            type:       'string',
            required:   false,
            queryParam: true,

            def(props) : ?string {
                let env = props.env || config.env;

                if (env === ENV.STAGE || env === ENV.LOCAL) {
                    return config.stage;
                }
            }
        },

        stageUrl: {
            type:       'string',
            required:   false,
            queryParam: true,

            def(props) : ?string {
                let env = props.env || config.env;

                if (env === ENV.STAGE || env === ENV.LOCAL) {
                    return config.stageUrl;
                }
            }
        },

        locale: {
            type:          'string',
            required:      false,
            queryParam:    'locale.x',
            allowDelegate: true,

            def() : string {
                let { lang, country } = getBrowserLocale();
                return `${ lang }_${ country }`;
            }
        },

        client: {
            type:     'object',
            required: false,
            def() : { [string] : string } {
                return {};
            },
            sendToChild: false,

            validate(client, props) {
                let env = props.env || config.env;

                if (!client[env]) {
                    throw new Error(`Client ID not found for env: ${ env }`);
                }

                if (client[env].match(/^(.)\1+$/)) {
                    throw new Error(`Invalid client ID: ${ client[env] }`);
                }
            }
        },

        payment: {
            type:      'function',
            required:  false,
            memoize:   true,
            promisify: true,
            queryParam(payment) : ZalgoPromise<string> {
                return payment().then(token => {
                    return determineParameterFromToken(token);
                });
            },
            queryValue(payment) : ZalgoPromise<string> {
                return payment();
            },
            childDecorate(payment) : () => ZalgoPromise<string> {
                let token = getQueryParam('token');

                return token
                    ? memoize(() => ZalgoPromise.resolve(token))
                    : payment;
            },
            validate(payment, props) {
                if (!payment && !props.url) {
                    throw new Error(`Expected either props.payment or props.url to be passed`);
                }
            },
            alias: 'billingAgreement'
        },

        style: {
            type:          'object',
            required:      false,
            allowDelegate: true,
            def() : Object {
                return {};
            },
            validate(style) {
                if (style.overlayColor && style.overlayColor !== CHECKOUT_OVERLAY_COLOR.BLACK && style.overlayColor !== CHECKOUT_OVERLAY_COLOR.WHITE) {
                    throw new Error(`Invalid background color: ${ style.overlayColor }`);
                }
            }
        },

        commit: {
            type:     'boolean',
            required: false
        },

        experience: {
            type:     'object',
            required: false,
            def() : Object {
                return {};
            }
        },

        fundingSource: {
            type:       'string',
            required:   false,
            queryParam: true
        },

        onAuthorize: {
            type:     'function',
            required: true,
            once:     true,

            decorate(original) : Function | void {
                if (original) {
                    return function decorateOnAuthorize(data, actions = {}) : ZalgoPromise<void> {

                        if (data && !data.intent) {
                            warn(`checkout_authorize_no_intent`, { paymentID: data.paymentID, token: data.paymentToken });
                        }

                        let close = () => {
                            return ZalgoPromise.try(() => {
                                if (actions.close) {
                                    return actions.close();
                                }
                            }).then(() => {
                                return this.closeComponent();
                            });
                        };

                        let redirect = (win, url) => {
                            return ZalgoPromise.all([
                                redir(win || window.top, url || data.returnUrl),
                                close()
                            ]);
                        };

                        return ZalgoPromise.try(() => {

                            try {
                                let isButton = window.location.href.indexOf('/webapps/hermes/button') !== -1;
                                let isGuest  = this.window.location.href.indexOf('/webapps/xoonboarding') !== -1;

                                if (isButton && isGuest) {
                                    return request({
                                        win:    this.window,
                                        method: 'get',
                                        url:    '/webapps/hermes/api/auth'
                                    }).then(result => {
                                        if (result && result.data && result.data.access_token) {
                                            addHeader('x-paypal-internal-euat', result.data.access_token);
                                        }
                                    }).catch(() => {
                                        // pass
                                    });
                                }

                            } catch (err) {
                                // pass
                            }

                        }).then(() => {
                            return original.call(this, data, { ...actions, close, redirect });
                        }).catch(err => {
                            return this.error(err);
                        }).finally(() => {
                            return this.close();
                        });
                    };
                }
            }
        },

        onAuth: {
            type:       'function',
            required:   false,
            sameDomain: true
        },

        accessToken: {
            type:     'function',
            required: false
        },

        onCancel: {
            type:     'function',
            required: false,
            once:     true,
            noop:     true,

            decorate(original) : Function {
                return function decorateOnCancel(data, actions = {}) : ZalgoPromise<void> {

                    let close = () => {
                        return ZalgoPromise.try(() => {
                            if (actions.close) {
                                return actions.close();
                            }
                        }).then(() => {
                            return this.closeComponent();
                        });
                    };

                    let redirect = (win, url) => {
                        return ZalgoPromise.all([
                            redir(win || window.top, url || data.cancelUrl),
                            close()
                        ]);
                    };

                    return ZalgoPromise.try(() => {
                        return original.call(this, data, { ...actions, close, redirect });
                    }).finally(() => {
                        this.close();
                    });
                };
            }
        },

        init: {
            type:     'function',
            required: false,
            once:     true,
            noop:     true,

            decorate(original) : Function {
                return function decorateInit(data) : void {
                    info('checkout_init');

                    track({
                        [ FPTI.KEY.STATE ]:        FPTI.STATE.CHECKOUT,
                        [ FPTI.KEY.TRANSITION ]:   FPTI.TRANSITION.CHECKOUT_INIT,
                        [ FPTI.KEY.CONTEXT_TYPE ]: FPTI.CONTEXT_TYPE[PAYMENT_TYPE.EC_TOKEN],
                        [ FPTI.KEY.TOKEN ]:        data.paymentToken,
                        [ FPTI.KEY.SELLER_ID ]:    data.merchantID,
                        [ FPTI.KEY.CONTEXT_ID ]:   data.paymentToken
                    });

                    flushLogs();

                    this.paymentToken = data.paymentToken;
                    this.cancelUrl    = data.cancelUrl;

                    return original.apply(this, arguments);
                };
            }
        },

        onClose: {
            type:      'function',
            required:  false,
            once:      true,
            promisify: true,
            noop:      true,

            decorate(original) : Function {
                return function decorateOnClose(reason) : ZalgoPromise<void> {

                    let onClose = original.apply(this, arguments);

                    let CLOSE_REASONS = CONSTANTS.CLOSE_REASONS;

                    let shouldCancel =
                        this.props.onCancel &&
                        [ CLOSE_REASONS.CLOSE_DETECTED, CLOSE_REASONS.USER_CLOSED ].indexOf(reason) !== -1;

                    if (shouldCancel) {
                        info(`close_trigger_cancel`);
                        return this.props.onCancel({
                            paymentToken: this.paymentToken,
                            cancelUrl:    this.cancelUrl
                        }).then(() => onClose);
                    }

                    return onClose;
                };
            }
        },

        onError: {
            type:      'function',
            required:  false,
            promisify: true,
            noop:      true,
            once:      true
        },

        fallback: {
            type:     'function',
            required: false,
            once:     true,

            def() : Function {
                return function defaultFallback(url) : ZalgoPromise<void> {
                    warn('fallback', { url });

                    if (getDomainSetting('allow_full_page_fallback')) {
                        window.top.location = url;
                        return this.close();
                    }

                    return onLegacyPaymentAuthorize(this.props.onAuthorize);
                };
            }
        },

        logLevel: {
            type:     'string',
            required: false,
            get value() : string {
                return config.logLevel;
            }
        },

        supplement: {
            type:     'object',
            required: false,
            get value() : Object {
                // $FlowFixMe
                let value : Object = window.xprops && window.xprops.supplement;
                return value;
            }
        },

        test: {
            type:     'object',
            required: false,
            def() : Object {
                return window.__test__ || { action: 'checkout' };
            }
        }
    },

    autoResize: {
        width:  false,
        height: false
    },

    get dimensions() : { width : string, height : string } {

        if (isDevice()) {
            return {
                width:  '100%',
                height: '535px'
            };
        }

        return {
            width:  '450px',
            height: '535px'
        };
    }
});

if (Checkout.isChild() && Checkout.xchild && Checkout.xprops) {

    if (Checkout.xprops && Checkout.xprops.logLevel) {
        setLogLevel(Checkout.xprops.logLevel);
    }

    Checkout.xchild.onProps(xprops => {
        patchMethod(xprops, 'onAuthorize', ({ callOriginal, args: [ data ] }) => {
            if (data && !data.intent) {
                warn(`hermes_authorize_no_intent`, { paymentID: data.paymentID, token: data.paymentToken });

                try {
                    let intent = window.injector.get('$CheckoutCartModel').instance(data.paymentToken).payment_action;
                    warn(`hermes_intent`, { paymentID: data.paymentID, token: data.paymentToken, intent });
                } catch (err) {
                    // pass
                }

                immediateFlush();
            }
            return callOriginal();
        });
    });

    documentReady.then(() => {

        if (!window.injector) {
            return;
        }

        let $event = window.injector.get('$event');

        if (!$event) {
            return;
        }

        let experimentActive = false;
        let loggedComplete = false;

        $event.on('allLoaded', () => {
            setTimeout(() => {
                let payButton = document.querySelector('.buttons.reviewButton');
                let topPayButton = document.querySelector('.buttons.reviewButton.topReviewButton');
                let reviewSection = document.querySelector('section.review');

                let throttle = getThrottle('top_pay_button', 0);

                let hash = window.location.hash;

                let logComplete = () => {
                    if (experimentActive && !loggedComplete && hash && hash.indexOf('checkout/review') !== -1) {
                        throttle.logComplete({ [ FPTI.KEY.FEED ]: 'hermesnodeweb' });
                        loggedComplete = true;
                    }
                };

                if (payButton) {
                    payButton.addEventListener('click', logComplete);
                }

                if (!reviewSection || !reviewSection.firstChild || !payButton || topPayButton) {
                    return;
                }

                if (payButton.getBoundingClientRect().bottom < window.innerHeight) {
                    return;
                }

                experimentActive = true;
                throttle.logStart({ [ FPTI.KEY.FEED ]: 'hermesnodeweb' });

                if (!throttle.isEnabled()) {
                    return;
                }

                topPayButton = payButton.cloneNode(true);
                topPayButton.className += ' topReviewButton';
                reviewSection.insertBefore(topPayButton, reviewSection.firstChild);

                topPayButton.addEventListener('click', () => {
                    logComplete();
                    let button = payButton && payButton.querySelector('button, input');
                    if (button) {
                        button.click();
                    }
                });
            }, 200);
        });
    });
}

patchMethod(Checkout, 'init', ({ args: [ props, _context ], original, context }) => {
    return original.call(context, props, _context, 'body');
});

patchMethod(Checkout, 'render', ({ args: [ props ], original, context }) => {
    return original.call(context, props, 'body');
});

patchMethod(Checkout, 'renderTo', ({ args: [ win, props ], original, context }) => {

    let payment = props.payment();
    props.payment = () => payment;

    return original.call(context, win, props, 'body').catch(err => {
        if (err instanceof PopupOpenError && isPayPalDomain()) {
            Checkout.contexts.iframe = true;
            return original.call(context, win, props, 'body');
        }
        throw err;
    });
});

<|endoftext|>


<commit_before>
var sodaUrls = new Array();
var map;
var heatmap;

var offsetPoints = new Array();
var createPoints = function() {
	offsetPoints.push(0);
	for (var i = 1; i<11; i++) {
		offsetPoints.push(parseInt(i+"000"));
	}
}
createPoints();

// API Call
for (i=0; i < offsetPoints.length; ++i) {
	sodaUrls.push("https://data.seattle.gov/resource/3k2p-39jp.json?$where=within_box(incident_location, 47.605152, -122.341639, 47.585152, -122.3216390)&$offset="+offsetPoints[i]+"&$select=event_clearance_group, latitude, longitude, event_clearance_date, hundred_block_location")
	}

//Draws map
google.maps.event.addDomListener(window, 'load', drawMap);
function drawMap(){
	var styleArray = [
	{
	  featureType: "all",
	  stylers: [
	    { saturation: -80 }
	  ]
	},{
	  featureType: "road.arterial",
	  elementType: "geometry",
	  stylers: [
	    { hue: "#00ffee" },
	    { saturation: 50 }
	  ]
	},{
	  featureType: "poi.business",
	  elementType: "labels",
	  stylers: [
	    { visibility: "off" }
	 	 ]
		}
	];
	var mapOptions = {
	  center: { lat: 47.595152, lng: -122.331639},
	  zoom: 15,
	  styles: styleArray,
	  mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	map = new google.maps.Map(document.getElementById('map-canvas'),
	    mapOptions);
}

//Takes json and parses to push to array
function prepareDataforMap(data) {
	var dataArr = new Array();
  for (i = 0; i < data.length; ++i) {
    dataArr.push([
      parseFloat(data[i].latitude),
      parseFloat(data[i].longitude),
      data[i].hundred_block_location,
      data[i].event_clearance_date,
      data[i].event_clearance_group,
      ])
  };
  return dataArr;
};

//Parses json to add points on map
function parseJsonToAddPoints(urls) {
	for (i=0; i<urls.length; ++i) {
		$.getJSON(urls[i],function(data) {
		var data_to_map = prepareDataforMap(data);
		for (i = 0; i < data_to_map.length; i++) {
			createMarker(data_to_map[i][0], data_to_map[i][1], data_to_map[i][2], data_to_map[i][3],data_to_map[i][4], map)
	  	}
		});
	}
}

function dropPointsonMap() {
	parseJsonToAddPoints(sodaUrls);
}

//Creates and defines markers on map
function createMarker(latitude, longitude, hundred_block_location, event_clearance_date, event_clearance_group, map) {
  var point = new google.maps.LatLng(latitude, longitude);
  var marker = new google.maps.Marker({
      position: point,
      map: map,
      title: event_clearance_group,
  });

   var contentString = '
'+
      '
'+
      '
'+
      '
' + event_clearance_group + '
' +
      '
'+
      '
Location: ' + hundred_block_location + '
' +
      '
Date: ' + event_clearance_date + '
' +
      '
'+
      '
';

  var infowindow = new google.maps.InfoWindow({ content: contentString });
  google.maps.event.addListener(marker, 'click', function() {
      infowindow.setContent(contentString);
      infowindow.open(map, marker);
  });
}

//Parses json to add heat map
function parseJsontoAddHeatMap(urls) {
	var dataArr = new Array();
	for (i=0; i<urls.length; ++i) {
		$.getJSON(urls[i],function(data) {
			var data_to_map = prepareDataforMap(data);
			for (i = 0; i < data_to_map.length; i++) {
		    dataArr.push(
		    	new google.maps.LatLng(parseFloat(data_to_map[i][0]), parseFloat(data_to_map[i][1]))
		    	)
		  }

		});
	}
	var pointArray = new google.maps.MVCArray(dataArr);
		heatmap = new google.maps.visualization.HeatmapLayer({
			data:pointArray,
			opacity: 0.5,
		});
	heatmap.setMap(map);
}

function toggleHeatmap() {
  heatmap.setMap(heatmap.getMap() ? null : map);
}

function changeGradient() {
  var gradient = [
    'rgba(0, 255, 255, 0)',
    'rgba(0, 255, 255, 1)',
    'rgba(0, 191, 255, 1)',
    'rgba(0, 127, 255, 1)',
    'rgba(0, 63, 255, 1)',
    'rgba(0, 0, 255, 1)',
    'rgba(0, 0, 223, 1)',
    'rgba(0, 0, 191, 1)',
    'rgba(0, 0, 159, 1)',
    'rgba(0, 0, 127, 1)',
    'rgba(63, 0, 91, 1)',
    'rgba(127, 0, 63, 1)',
    'rgba(191, 0, 31, 1)',
    'rgba(255, 0, 0, 1)'
  ]
  heatmap.set('gradient', heatmap.get('gradient') ? null : gradient);
}

function changeRadius() {
  heatmap.set('radius', heatmap.get('radius') ? null : 20);
}

function changeOpacity() {
  heatmap.set('opacity', heatmap.get('opacity') ? null : 0.2);
}

//Adds crime Data to map
setTimeout(function(){
	parseJsontoAddHeatMap(sodaUrls);
}, 1000);
<commit_msg>Modify parse data for points on map, fix anonymous function to be able to take a variable that is easy to change to call more data points<commit_after>
var sodaUrls = new Array();
var map;
var heatmap;

var offsetPoints = new Array();
var offsetNum = 11; //Change this number (up to 80) to pull more data points
var createPoints = function() {
	offsetPoints.push(0);
	for (var i = 1; i<offsetNum; i++) {
		offsetPoints.push(parseInt(i+"000"));
	}
}
createPoints();

// API Call
for (i=0; i < offsetPoints.length; ++i) {
	sodaUrls.push("https://data.seattle.gov/resource/3k2p-39jp.json?$where=within_box(incident_location, 47.605152, -122.341639, 47.585152, -122.3216390)&$offset="+offsetPoints[i]+"&$select=event_clearance_group, latitude, longitude, event_clearance_date, hundred_block_location")
	}

//Draws map
function drawMap(){
	var styleArray = [
	{
	  featureType: "all",
	  stylers: [
	    { saturation: -80 }
	  ]
	},{
	  featureType: "road.arterial",
	  elementType: "geometry",
	  stylers: [
	    { hue: "#00ffee" },
	    { saturation: 50 }
	  ]
	},{
	  featureType: "poi.business",
	  elementType: "labels",
	  stylers: [
	    { visibility: "off" }
	 	 ]
		}
	];
	var mapOptions = {
	  center: { lat: 47.595152, lng: -122.331639},
	  zoom: 15,
	  styles: styleArray,
	  mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	map = new google.maps.Map(document.getElementById('map-canvas'),
	    mapOptions);
}
google.maps.event.addDomListener(window, 'load', drawMap);

//Takes json and parses to push to array
function prepareDataforMap(data) {
	var dataArr = new Array();
  for (i = 0; i < data.length; ++i) {
    dataArr.push([
      parseFloat(data[i].latitude),
      parseFloat(data[i].longitude),
      data[i].hundred_block_location,
      data[i].event_clearance_date,
      data[i].event_clearance_group,
      ])
  };
  return dataArr;
};

//Parses json to add points on map
function parseJsonToAddPoints(urls) {
	for (i=0; i<urls.length; ++i) {
		$.getJSON(urls[i],function(data) {
		var data_to_map = prepareDataforMap(data);
		for (i = 0; i < data_to_map.length; i++) {
			createMarker(data_to_map[i][0], data_to_map[i][1], data_to_map[i][2], data_to_map[i][3],data_to_map[i][4], map)
	  	}
		});
	}
}

//Creates and defines markers on map
function createMarker(latitude, longitude, hundred_block_location, event_clearance_date, event_clearance_group, map) {
  var point = new google.maps.LatLng(latitude, longitude);
  var marker = new google.maps.Marker({
      position: point,
      map: map,
      title: event_clearance_group,
  });

   var contentString = '
'+
      '
'+
      '
'+
      '
' + event_clearance_group + '
' +
      '
'+
      '
Location: ' + hundred_block_location + '
' +
      '
Date: ' + event_clearance_date + '
' +
      '
'+
      '
';

  var infowindow = new google.maps.InfoWindow({ content: contentString });
  google.maps.event.addListener(marker, 'click', function() {
      infowindow.setContent(contentString);
      infowindow.open(map, marker);
  });
}

//Drop points on map
function dropPointsonMap() {
	parseJsonToAddPoints(sodaUrls);
}

//Parses json to add heat map
function parseJsontoAddHeatMap(urls) {
	var dataArr = new Array();
	for (i=0; i<urls.length; ++i) {
		$.getJSON(urls[i],function(data) {
			var data_to_map = prepareDataforMap(data);
			for (i = 0; i < data_to_map.length; i++) {
		    dataArr.push(
		    	new google.maps.LatLng(parseFloat(data_to_map[i][0]), parseFloat(data_to_map[i][1]))
		    	)
		  }
		});
	}
	var pointArray = new google.maps.MVCArray(dataArr);
	heatmap = new google.maps.visualization.HeatmapLayer({
		data:pointArray,
		opacity: 0.5,
	});
	heatmap.setMap(map);
}

function toggleHeatmap() {
  heatmap.setMap(heatmap.getMap() ? null : map);
}

function changeGradient() {
  var gradient = [
    'rgba(0, 255, 255, 0)',
    'rgba(0, 255, 255, 1)',
    'rgba(0, 191, 255, 1)',
    'rgba(0, 127, 255, 1)',
    'rgba(0, 63, 255, 1)',
    'rgba(0, 0, 255, 1)',
    'rgba(0, 0, 223, 1)',
    'rgba(0, 0, 191, 1)',
    'rgba(0, 0, 159, 1)',
    'rgba(0, 0, 127, 1)',
    'rgba(63, 0, 91, 1)',
    'rgba(127, 0, 63, 1)',
    'rgba(191, 0, 31, 1)',
    'rgba(255, 0, 0, 1)'
  ]
  heatmap.set('gradient', heatmap.get('gradient') ? null : gradient);
}

function changeRadius() {
  heatmap.set('radius', heatmap.get('radius') ? null : 20);
}

function changeOpacity() {
  heatmap.set('opacity', heatmap.get('opacity') ? null : 0.2);
}

//Add crime data heatmap to map
setTimeout(function(){
	parseJsontoAddHeatMap(sodaUrls);
}, 1000);
<|endoftext|>