<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            portions copyright @2009 Apple Inc.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/*global module test htmlbody ok equals same stop start */

htmlbody('<style> .sc-static-layout { border: 1px red dotted; } </style>');

//control test pane
var pane = SC.ControlTestPane.design()
     //sample1
    .add("Basic", SC.SelectButtonView, {
       objects: ['None', 'Low', 'Medium', 'High']
    })

    //sample2
    .add("Disabled", SC.SelectButtonView, {
       isEnabled: NO, objects: ['None', 'Low', 'Medium', 'High']
    })

    //sample3
    .add("NotVisible", SC.SelectButtonView, {
      isVisible: NO, objects: ['None', 'Low', 'Medium', 'High']
    })

    //sample4
    .add("SortedObjects", SC.SelectButtonView, {
      objects:['None', 'Low', 'Medium', 'High']
    })

    //sample5
    .add("UnsortedObjects", SC.SelectButtonView, {
      objects:['None', 'Low', 'Medium', 'High'],
      disableSort: YES
    })

    //sample6
    .add("redraw", SC.SelectButtonView, {
      layout: { width: '150', right: '0' }
    })

    //sample7
    .add("SelectButtonWithIcon", SC.SelectButtonView, {
      objects: [{ title: "None", icon: 'select-button-icon' },
        { title: "Low", icon: 'select-button-icon' },
        { title: "Medium", icon: 'select-button-icon' },
        { title: "High", icon: 'select-button-icon' }],
      nameKey: 'title',
      iconKey: 'icon',
      checkboxEnabled: YES
    })

    //sample8
    .add("SortKey", SC.SelectButtonView, {
      objects: [{ title: "None", pos: 3},
        { title: "Low", pos: 1},
        { title: "Medium", pos: 2 },
        { title: "High", pos: 4}],
      nameKey: 'title',
      disableSort: NO,
      sortKey: 'pos',
      checkboxEnabled: YES
    })

    //sample9
    .add("StaticLayout", SC.SelectButtonView, {
      useStaticLayout: YES,
      objects:['None', 'Low', 'Medium', 'High'],
      layout: { width: '150', right: '0' }
    }) ;

    pane.show();

// ..........................................................
// TEST VIEWS
//

module('SC.SelectButtonView ui', pane.standardSetup()) ;

//test1
test("Check the visiblity of the selectButtons", function() {
  ok(pane.view('Basic').get('isVisibleInWindow'), 'Basic.isVisibleInWindow should be YES') ;
  ok(pane.view('Disabled').get('isVisibleInWindow'), 'Disabled.isVisibleInWindow should be YES') ;
  ok(!pane.view('NotVisible').get('isVisibleInWindow'), 'NotVisible.isVisibleInWindow should be NO') ;
  ok(pane.view('SortedObjects').get('isVisibleInWindow'), 'SortedObjects.isVisibleInWindow should be YES') ;
  ok(pane.view('UnsortedObjects').get('isVisibleInWindow'), 'UnsortedObjects.isVisibleInWindow should be YES') ;
  ok(pane.view('redraw').get('isVisibleInWindow'), 'redraw.isVisibleInWindow should be YES') ;
  ok(pane.view('SelectButtonWithIcon').get('isVisibleInWindow'), 'SelectButtonWithIcon.isVisibleInWindow should be YES') ;
  ok(pane.view('StaticLayout').get('isVisibleInWindow'), 'StaticLayout.isVisibleInWindow should be YES') ;
}) ;

//test2
test("Basic", function() {
  var view=pane.view('Basic').$();
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('icon'), 'hasClass(icon) should be NO') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test3
test("Disabled", function() {
  var view=pane.view('Disabled').$() ;
  ok(view.hasClass('disabled'), 'hasClass(disabled) should be YES') ;
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('icon'), 'hasClass(icon) should be NO') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test4
test("NotVisible", function() {
  var view=pane.view('NotVisible').$();
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
  ok(!view.hasClass('sel'), 'should not have sel class') ;
}) ;

//test5
test("SortedObjects", function() {
   var view = pane.view('SortedObjects');
   equals(null,view.get('sortKey'), 'sortkey not specified') ;
   ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
   ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
   ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
   ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
   ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
   ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
   ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test6
test("UnsortedObjects", function() {
   var view = pane.view('UnsortedObjects');
   equals(YES,view.get('disableSort'), 'Sorting disabled') ;

   ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
   ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
   ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
   ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
   ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
   ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
   ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test7
test("redraw", function() {
  var view=pane.view('redraw');
  ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
  ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.$().hasClass('def'), 'hasClass(def) should be NO');

  ok(view.get('objects').length === 0, "Objects should be empty");
  SC.RunLoop.begin();
  view.set('objects', ['Calendar', 'Work', 'Home']);
  SC.RunLoop.end();
  ok(view.get('objects').length === 3, "Objects length should be 3");
}) ;

//test8
test("SelectButtonWithIcon", function() {
  var view=pane.view('SelectButtonWithIcon').$();
  ok(view.hasClass('icon'), 'hasClass(Icon) should be YES') ;
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test9
test("Check if the objects are sorted based on sortKey", function() {
  var view=pane.view('SortKey');
  equals('None',view.get('objects')[2].title, 'Third object should be "None" ') ;
}) ;

//test10
test("StaticLayout", function() {
  var view = pane.view('StaticLayout');
  ok(!view.$().hasClass('disabled'), 'should not have disabled class');
  ok(!view.$().hasClass('sel'), 'should not have sel class');
});
<commit_msg>Made changes to include one more ui case for select button<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            portions copyright @2009 Apple Inc.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/*global module test htmlbody ok equals same stop start */

htmlbody('<style> .sc-static-layout { border: 1px red dotted; } </style>');

//control test pane
var pane = SC.ControlTestPane.design()
     //sample1
    .add("Basic", SC.SelectButtonView, {
       objects: ['None', 'Low', 'Medium', 'High']
    })

    //sample2
    .add("Disabled", SC.SelectButtonView, {
       isEnabled: NO, objects: ['None', 'Low', 'Medium', 'High']
    })

    //sample3
    .add("NotVisible", SC.SelectButtonView, {
      isVisible: NO, objects: ['None', 'Low', 'Medium', 'High']
    })

    //sample4
    .add("SortedObjects", SC.SelectButtonView, {
      objects:['None', 'Low', 'Medium', 'High']
    })

    //sample5
    .add("UnsortedObjects", SC.SelectButtonView, {
      objects:['None', 'Low', 'Medium', 'High'],
      disableSort: YES
    })

    //sample6
    .add("redraw", SC.SelectButtonView, {
      layout: { width: '150', right: '0' }
    })

    //sample7
    .add("SelectButtonWithIcon", SC.SelectButtonView, {
      objects: [{ title: "None", icon: 'select-button-icon' },
        { title: "Low", icon: 'select-button-icon' },
        { title: "Medium", icon: 'select-button-icon' },
        { title: "High", icon: 'select-button-icon' }],
      nameKey: 'title',
      iconKey: 'icon',
      checkboxEnabled: YES
    })

    //sample8
    .add("SortKey", SC.SelectButtonView, {
      objects: [{ title: "None", pos: 3},
        { title: "Low", pos: 1},
        { title: "Medium", pos: 2 },
        { title: "High", pos: 4}],
      nameKey: 'title',
      disableSort: NO,
      sortKey: 'pos',
      checkboxEnabled: YES
    })

    //sample9
    .add("StaticLayout", SC.SelectButtonView, {
      useStaticLayout: YES,
      objects:['None', 'Low', 'Medium', 'High'],
      layout: { width: '150', right: '0' }
    })

    //sample10
    .add("DisableItem", SC.SelectButtonView, {
      objects: [{ title: "None", pos: 3, isEnabled: YES },
        { title: "Low", pos: 1, isEnabled: NO },
        { title: "Medium", pos: 2, isEnabled: YES },
        { title: "High", pos: 4, isEnabled: NO }],
      nameKey: 'title',
      disableSort: NO,
      isEnabledKey: 'isEnabled',
      sortKey: 'pos',
      checkboxEnabled: YES
    }) ;

    pane.show();

// ..........................................................
// TEST VIEWS
//

module('SC.SelectButtonView ui', pane.standardSetup()) ;

//test1
test("Check the visiblity of the selectButtons", function() {
  ok(pane.view('Basic').get('isVisibleInWindow'), 'Basic.isVisibleInWindow should be YES') ;
  ok(pane.view('Disabled').get('isVisibleInWindow'), 'Disabled.isVisibleInWindow should be YES') ;
  ok(!pane.view('NotVisible').get('isVisibleInWindow'), 'NotVisible.isVisibleInWindow should be NO') ;
  ok(pane.view('SortedObjects').get('isVisibleInWindow'), 'SortedObjects.isVisibleInWindow should be YES') ;
  ok(pane.view('UnsortedObjects').get('isVisibleInWindow'), 'UnsortedObjects.isVisibleInWindow should be YES') ;
  ok(pane.view('redraw').get('isVisibleInWindow'), 'redraw.isVisibleInWindow should be YES') ;
  ok(pane.view('SelectButtonWithIcon').get('isVisibleInWindow'), 'SelectButtonWithIcon.isVisibleInWindow should be YES') ;
  ok(pane.view('StaticLayout').get('isVisibleInWindow'), 'StaticLayout.isVisibleInWindow should be YES') ;
}) ;

//test2
test("Basic", function() {
  var view=pane.view('Basic').$();
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('icon'), 'hasClass(icon) should be NO') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test3
test("Disabled", function() {
  var view=pane.view('Disabled').$() ;
  ok(view.hasClass('disabled'), 'hasClass(disabled) should be YES') ;
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('icon'), 'hasClass(icon) should be NO') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test4
test("NotVisible", function() {
  var view=pane.view('NotVisible').$();
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
  ok(!view.hasClass('sel'), 'should not have sel class') ;
}) ;

//test5
test("SortedObjects", function() {
   var view = pane.view('SortedObjects');
   equals(null,view.get('sortKey'), 'sortkey not specified') ;
   ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
   ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
   ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
   ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
   ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
   ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
   ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test6
test("UnsortedObjects", function() {
   var view = pane.view('UnsortedObjects');
   equals(YES,view.get('disableSort'), 'Sorting disabled') ;

   ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
   ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
   ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
   ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
   ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
   ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
   ok(!view.$().hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test7
test("redraw", function() {
  var view=pane.view('redraw');
  ok(view.$().hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.$().hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.$().hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.$().hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.$().hasClass('icon'), 'hasClass(icon) should be NO') ;
  ok(!view.$().hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.$().hasClass('def'), 'hasClass(def) should be NO');

  ok(view.get('objects').length === 0, "Objects should be empty");
  SC.RunLoop.begin();
  view.set('objects', ['Calendar', 'Work', 'Home']);
  SC.RunLoop.end();
  ok(view.get('objects').length === 3, "Objects length should be 3");
}) ;

//test8
test("SelectButtonWithIcon", function() {
  var view=pane.view('SelectButtonWithIcon').$();
  ok(view.hasClass('icon'), 'hasClass(Icon) should be YES') ;
  ok(view.hasClass('sc-view'), 'hasClass(sc-view) should be YES') ;
  ok(view.hasClass('sc-button-view'), 'hasClass(sc-button-view) should be YES') ;
  ok(view.hasClass('sc-regular-size'), 'hasClass(sc-regular-size) should be YES') ;
  ok(!view.hasClass('sel'), 'hasClass(sel) should be NO') ;
  ok(!view.hasClass('disabled'), 'hasClass(disabled) should be NO') ;
  ok(!view.hasClass('def'), 'hasClass(def) should be NO') ;
}) ;

//test9
test("Check if the objects are sorted based on sortKey", function() {
  var view=pane.view('SortKey');
  equals('None',view.get('objects')[2].title, 'Third object should be "None" ') ;
}) ;

//test10
test("StaticLayout", function() {
  var view = pane.view('StaticLayout');
  ok(!view.$().hasClass('disabled'), 'should not have disabled class');
  ok(!view.$().hasClass('sel'), 'should not have sel class');
});
<|endoftext|>


<commit_before>frappe.ui.form.on("Communication", {
	onload: function(frm) {
		if(frm.doc.content) {
			frm.doc.content = frappe.dom.remove_script_and_style(frm.doc.content);
		}
		frm.set_query("reference_doctype", function() {
			return {
				filters: {
					"issingle": 0,
					"istable": 0
				}
			}
		});
	},
	refresh: function(frm) {
		if(frm.is_new()) return;

		frm.convert_to_click && frm.set_convert_button();
		frm.subject_field = "subject";

		if(frm.doc.reference_doctype && frm.doc.reference_name) {
			frm.add_custom_button(__(frm.doc.reference_name), function() {
				frappe.set_route("Form", frm.doc.reference_doctype, frm.doc.reference_name);
			});
		} else {
			// if an unlinked communication, set email field
			if (frm.doc.sent_or_received==="Received") {
				frm.email_field = "sender";
			} else {
				frm.email_field = "recipients";
			}
		}

		if(frm.doc.communication_type == "Feedback") {
			frm.add_custom_button(__("Resend"), function() {
				var feedback = new frappe.utils.Feedback();
				feedback.resend_feedback_request(frm.doc);
			});
		}

		if(frm.doc.status==="Open") {
			frm.add_custom_button(__("Close"), function() {
				frm.set_value("status", "Closed");
				frm.save();
			});
		} else if (frm.doc.status !== "Linked") {
			frm.add_custom_button(__("Reopen"), function() {
				frm.set_value("status", "Open");
				frm.save();
			});
		}

		frm.add_custom_button(__("Relink"), function() {
			frm.trigger('show_relink_dialog');
		});

		if(frm.doc.communication_type=="Communication"
			&& frm.doc.communication_medium == "Email"
			&& frm.doc.sent_or_received == "Received") {

			frm.add_custom_button(__("Reply"), function() {
				frm.trigger('reply');
			});

			frm.add_custom_button(__("Reply All"), function() {
				frm.trigger('reply_all');
			}, "Actions");

			frm.add_custom_button(__("Forward"), function() {
				frm.trigger('forward_mail');
			}, "Actions");

			frm.add_custom_button(__("Mark as {0}", [frm.doc.seen? "Unread": "Read"]), function() {
				frm.trigger('mark_as_read_unread');
			}, "Actions");

			frm.add_custom_button(__("Add Contact"), function() {
				frm.trigger('add_to_contact');
			}, "Actions");

			if(frm.doc.email_status != "Spam")
				frm.add_custom_button(__("Mark as Spam"), function() {
					frm.trigger('mark_as_spam');
				}, "Actions");

			if(frm.doc.email_status != "Trash") {
				frm.add_custom_button(__("Move To Trash"), function() {
					frm.trigger('move_to_trash');
				}, "Actions");
			}
		}

		if(frm.doc.communication_type=="Communication"
			&& frm.doc.communication_medium == "Phone"
			&& frm.doc.sent_or_received == "Received"){

			frm.add_custom_button(__("Add Contact"), function() {
				frm.trigger('add_to_contact');
			}, "Actions");
		}
	},

	show_relink_dialog: function(frm){
		var lib = "frappe.email";
		var d = new frappe.ui.Dialog ({
			title: __("Relink Communication"),
			fields: [{
				"fieldtype": "Link",
				"options": "DocType",
				"label": __("Reference Doctype"),
				"fieldname": "reference_doctype",
				"get_query": function() {return {"query": "frappe.email.get_communication_doctype"}}
			},
			{
				"fieldtype": "Dynamic Link",
				"options": "reference_doctype",
				"label": __("Reference Name"),
				"fieldname": "reference_name"
			}]
		});
		d.set_value("reference_doctype", frm.doc.reference_doctype);
		d.set_value("reference_name", frm.doc.reference_name);
		d.set_primary_action(__("Relink"), function () {
			var values = d.get_values();
			if (values) {
				frappe.confirm(
					__('Are you sure you want to relink this communication to {0}?', [values["reference_name"]]),
					function () {
						d.hide();
						frappe.call({
							method: "frappe.email.relink",
							args: {
								"name": frm.doc.name,
								"reference_doctype": values["reference_doctype"],
								"reference_name": values["reference_name"]
							},
							callback: function () {
								frm.refresh();
							}
						});
					},
					function () {
						frappe.show_alert('Document not Relinked')
					}
				);
			}
		});
		d.show();
	},

	mark_as_read_unread: function(frm) {
		var action = frm.doc.seen? "Unread": "Read";
		var flag = "(\\SEEN)";

		return frappe.call({
			method: "frappe.email.inbox.create_email_flag_queue",
			args: {
				'names': [frm.doc.name],
				'action': action,
				'flag': flag
			},
			freeze: true
		});
	},

	reply: function(frm) {
		var args = frm.events.get_mail_args(frm);
		$.extend(args, {
			subject: __("Re: {0}", [frm.doc.subject]),
			recipients: frm.doc.sender
		})

		new frappe.views.CommunicationComposer(args);
	},

	reply_all: function(frm) {
		var args = frm.events.get_mail_args(frm)
		$.extend(args, {
			subject: __("Re: {0}", [frm.doc.subject]),
			recipients: frm.doc.sender,
			cc: frm.doc.cc
		})
		new frappe.views.CommunicationComposer(args);
	},

	forward_mail: function(frm) {
		var args = frm.events.get_mail_args(frm)
		$.extend(args, {
			forward: true,
			subject: __("Fw: {0}", [frm.doc.subject]),
		})

		new frappe.views.CommunicationComposer(args);
	},

	get_mail_args: function(frm) {
		var sender_email_id = ""
		$.each(frappe.boot.email_accounts, function(idx, account) {
			if(account.email_account == frm.doc.email_account) {
				sender_email_id = account.email_id
				return
			}
		});

		return {
			frm: frm,
			doc: frm.doc,
			last_email: frm.doc,
			sender: sender_email_id,
			attachments: frm.doc.attachments
		}
	},

	add_to_contact: function(frm) {
		var me = this;
		var fullname = frm.doc.sender_full_name || ""

		var names = fullname.split(" ")
		var first_name = names[0]
		var last_name = names.length >= 2? names[names.length - 1]: ""

		frappe.route_options = {
			"email_id": frm.doc.sender || "",
			"first_name": first_name,
			"last_name": last_name,
			"mobile_no": frm.doc.phone_no || ""
		}
		frappe.new_doc("Contact")
	},

	mark_as_spam: function(frm) {
		frappe.call({
			method: "frappe.email.inbox.mark_as_spam",
			args: {
				communication: frm.doc.name,
				sender: frm.doc.sender
			},
			freeze: true,
			callback: function(r) {
				frappe.msgprint(__("Email has been marked as spam"))
			}
		})
	},

	move_to_trash: function(frm) {
		frappe.call({
			method: "frappe.email.inbox.mark_as_trash",
			args: {
				communication: frm.doc.name
			},
			freeze: true,
			callback: function(r) {
				frappe.msgprint(__("Email has been moved to trash"))
			}
		})
	}
});
<commit_msg>fix: Save related drafts when switching between reply and reply-all (#6491)<commit_after>frappe.ui.form.on("Communication", {
	onload: function(frm) {
		if(frm.doc.content) {
			frm.doc.content = frappe.dom.remove_script_and_style(frm.doc.content);
		}
		frm.set_query("reference_doctype", function() {
			return {
				filters: {
					"issingle": 0,
					"istable": 0
				}
			}
		});
	},
	refresh: function(frm) {
		if(frm.is_new()) return;

		frm.convert_to_click && frm.set_convert_button();
		frm.subject_field = "subject";

		if(frm.doc.reference_doctype && frm.doc.reference_name) {
			frm.add_custom_button(__(frm.doc.reference_name), function() {
				frappe.set_route("Form", frm.doc.reference_doctype, frm.doc.reference_name);
			});
		} else {
			// if an unlinked communication, set email field
			if (frm.doc.sent_or_received==="Received") {
				frm.email_field = "sender";
			} else {
				frm.email_field = "recipients";
			}
		}

		if(frm.doc.communication_type == "Feedback") {
			frm.add_custom_button(__("Resend"), function() {
				var feedback = new frappe.utils.Feedback();
				feedback.resend_feedback_request(frm.doc);
			});
		}

		if(frm.doc.status==="Open") {
			frm.add_custom_button(__("Close"), function() {
				frm.set_value("status", "Closed");
				frm.save();
			});
		} else if (frm.doc.status !== "Linked") {
			frm.add_custom_button(__("Reopen"), function() {
				frm.set_value("status", "Open");
				frm.save();
			});
		}

		frm.add_custom_button(__("Relink"), function() {
			frm.trigger('show_relink_dialog');
		});

		if(frm.doc.communication_type=="Communication"
			&& frm.doc.communication_medium == "Email"
			&& frm.doc.sent_or_received == "Received") {

			frm.add_custom_button(__("Reply"), function() {
				frm.trigger('reply');
			});

			frm.add_custom_button(__("Reply All"), function() {
				frm.trigger('reply_all');
			}, "Actions");

			frm.add_custom_button(__("Forward"), function() {
				frm.trigger('forward_mail');
			}, "Actions");

			frm.add_custom_button(__("Mark as {0}", [frm.doc.seen? "Unread": "Read"]), function() {
				frm.trigger('mark_as_read_unread');
			}, "Actions");

			frm.add_custom_button(__("Add Contact"), function() {
				frm.trigger('add_to_contact');
			}, "Actions");

			if(frm.doc.email_status != "Spam")
				frm.add_custom_button(__("Mark as Spam"), function() {
					frm.trigger('mark_as_spam');
				}, "Actions");

			if(frm.doc.email_status != "Trash") {
				frm.add_custom_button(__("Move To Trash"), function() {
					frm.trigger('move_to_trash');
				}, "Actions");
			}
		}

		if(frm.doc.communication_type=="Communication"
			&& frm.doc.communication_medium == "Phone"
			&& frm.doc.sent_or_received == "Received"){

			frm.add_custom_button(__("Add Contact"), function() {
				frm.trigger('add_to_contact');
			}, "Actions");
		}
	},

	show_relink_dialog: function(frm){
		var lib = "frappe.email";
		var d = new frappe.ui.Dialog ({
			title: __("Relink Communication"),
			fields: [{
				"fieldtype": "Link",
				"options": "DocType",
				"label": __("Reference Doctype"),
				"fieldname": "reference_doctype",
				"get_query": function() {return {"query": "frappe.email.get_communication_doctype"}}
			},
			{
				"fieldtype": "Dynamic Link",
				"options": "reference_doctype",
				"label": __("Reference Name"),
				"fieldname": "reference_name"
			}]
		});
		d.set_value("reference_doctype", frm.doc.reference_doctype);
		d.set_value("reference_name", frm.doc.reference_name);
		d.set_primary_action(__("Relink"), function () {
			var values = d.get_values();
			if (values) {
				frappe.confirm(
					__('Are you sure you want to relink this communication to {0}?', [values["reference_name"]]),
					function () {
						d.hide();
						frappe.call({
							method: "frappe.email.relink",
							args: {
								"name": frm.doc.name,
								"reference_doctype": values["reference_doctype"],
								"reference_name": values["reference_name"]
							},
							callback: function () {
								frm.refresh();
							}
						});
					},
					function () {
						frappe.show_alert('Document not Relinked')
					}
				);
			}
		});
		d.show();
	},

	mark_as_read_unread: function(frm) {
		var action = frm.doc.seen? "Unread": "Read";
		var flag = "(\\SEEN)";

		return frappe.call({
			method: "frappe.email.inbox.create_email_flag_queue",
			args: {
				'names': [frm.doc.name],
				'action': action,
				'flag': flag
			},
			freeze: true
		});
	},

	reply: function(frm) {
		var args = frm.events.get_mail_args(frm);
		$.extend(args, {
			subject: __("Re: {0}", [frm.doc.subject]),
			recipients: frm.doc.sender
		})

		new frappe.views.CommunicationComposer(args);
	},

	reply_all: function(frm) {
		var args = frm.events.get_mail_args(frm)
		$.extend(args, {
			subject: __("Res: {0}", [frm.doc.subject]),
			recipients: frm.doc.sender,
			cc: frm.doc.cc
		})
		new frappe.views.CommunicationComposer(args);
	},

	forward_mail: function(frm) {
		var args = frm.events.get_mail_args(frm)
		$.extend(args, {
			forward: true,
			subject: __("Fw: {0}", [frm.doc.subject]),
		})

		new frappe.views.CommunicationComposer(args);
	},

	get_mail_args: function(frm) {
		var sender_email_id = ""
		$.each(frappe.boot.email_accounts, function(idx, account) {
			if(account.email_account == frm.doc.email_account) {
				sender_email_id = account.email_id
				return
			}
		});

		return {
			frm: frm,
			doc: frm.doc,
			last_email: frm.doc,
			sender: sender_email_id,
			attachments: frm.doc.attachments
		}
	},

	add_to_contact: function(frm) {
		var me = this;
		var fullname = frm.doc.sender_full_name || ""

		var names = fullname.split(" ")
		var first_name = names[0]
		var last_name = names.length >= 2? names[names.length - 1]: ""

		frappe.route_options = {
			"email_id": frm.doc.sender || "",
			"first_name": first_name,
			"last_name": last_name,
			"mobile_no": frm.doc.phone_no || ""
		}
		frappe.new_doc("Contact")
	},

	mark_as_spam: function(frm) {
		frappe.call({
			method: "frappe.email.inbox.mark_as_spam",
			args: {
				communication: frm.doc.name,
				sender: frm.doc.sender
			},
			freeze: true,
			callback: function(r) {
				frappe.msgprint(__("Email has been marked as spam"))
			}
		})
	},

	move_to_trash: function(frm) {
		frappe.call({
			method: "frappe.email.inbox.mark_as_trash",
			args: {
				communication: frm.doc.name
			},
			freeze: true,
			callback: function(r) {
				frappe.msgprint(__("Email has been moved to trash"))
			}
		})
	}
});
<|endoftext|>


<commit_before>import React from 'react'
import RichDescription from './richdescription'
import i18n from 'app/utils/i18n'
import plugin from 'app/utils/plugin'
import CodeEdit from 'app/components/genericform/codeedit'

const SEPARATORS=/ \:\n.\[\]\s/

function is_space(c){
  return (c ==" " || c==":" || c == "\n")
}

function prefix_at_cursor(textarea){
  const text = textarea.value
  const end = textarea.selectionStart
  let start
  for (start=end-1; start>0; start--){
    const c = text[start]
    if (is_space(c)){
      start += 1
      break
    }
  }
  return text.slice(start, end)
}

function replace_around_cursor(textarea, value){
  const text = textarea.value
  let end = textarea.selectionStart-1
  let start
  for (start=end; start>0; start--){
    const c = text[start]
    if (is_space(c)){
      start += 1
      break
    }
  }
  for(;end<text.length;end++){
    const c = text[end]
    if (is_space(c)){
      break
    }
  }
  const ntext = text.slice(0, start)+value+text.slice(end)
  textarea.value=ntext
  textarea.selectionStart = start+value.length
}

function cursor_top(ta){
  if (!ta)
    return 0
  const text = ta.value
  const end = ta.selectionStart
  let line=1
  for(let i=0; i<end; i++){
    if (text[i]=='\n')
      line+=1
  }
  return line*18
}

function cursor_left(ta){
  if (!ta)
    return 0
    const text = ta.value
    const end = ta.selectionStart
    let col=1
    for(let i=0; i<end; i++){
      if (text[i]=='\n')
        col=0
      col+=1
    }
    return col*8.5+8
}

class TextArea extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      autocomplete: undefined,
      autocomplete_current: undefined
    }
  }
  componentDidMount(){
    if (this.props.autocomplete)
      this.updateAutocomplete()
  }
  updateAutocomplete(){
    const ac = this.props.autocomplete
    if (!ac)
      return

    const newtext=prefix_at_cursor(this.refs.textarea).toLocaleLowerCase()
    // console.log("Handle change to %o", newtext)

    plugin.start_call_stop( ac.command, ac.call, {"current": newtext, ...this.form_data}).then(
      autocomplete => {
        console.log(autocomplete)
        this.setState({autocomplete})
      })
  }
  insertAtCursor(value){
    replace_around_cursor(this.refs.textarea, value)
    this.refs.textarea.focus()

    this.handleChange({target:{value:this.refs.textarea.value}})
  }
  handleChange(ev){
    if (this.state.autocompletedelay)
      clearTimeout(this.state.autocompletedelay)
    const autocompletedelay = setTimeout(() => {
      this.updateAutocomplete()
    }, 300)

    this.setState({autocompletedelay})
    this.props.onChange(ev)
  }
  is_autocomplete_open(){
    return !!(this.state.autocomplete && this.state.autocomplete.length>0)
  }
  handleKeyboard(ev){
    if (!this.is_autocomplete_open())
      return
    let ignorekey=false
    let autocomplete_current = this.state.autocomplete_current || 0
    console.log(ev.key)

    if (ev.key == "ArrowUp"){
      autocomplete_current = autocomplete_current - 1
      ignorekey=true
    }
    if (ev.key == "ArrowDown"){
      autocomplete_current = autocomplete_current + 1
      ignorekey=true
    }
    if (ev.key == "PageUp"){
      autocomplete_current = autocomplete_current - 3
      ignorekey=true
    }
    if (ev.key == "PageDown"){
      autocomplete_current = autocomplete_current + 3
      ignorekey=true
    }

    if (autocomplete_current != this.state.autocomplete_current){
      if (autocomplete_current>=this.state.autocomplete.length)
        autocomplete_current=0
      if (autocomplete_current<0)
        autocomplete_current=this.state.autocomplete.length-1
      this.setState({autocomplete_current})
      if (this.refs.popup)
        this.refs.popup.scrollTop=autocomplete_current*30
    }

    if (ev.key == "Enter"){
      if (this.state.autocomplete[this.state.autocomplete_current]){
        this.insertAtCursor(this.state.autocomplete[this.state.autocomplete_current])
      }
    }
    if (ev.key == "ArrowLeft"){
      this.setState({cursor: this.refs.textarea.selectionStart})
    }
    if (ev.key == "ArrowRight"){
      this.setState({cursor: this.refs.textarea.selectionStart})
    }
    if (ev.key == "Escape"){
      this.setState({autocomplete:[]})
      ignorekey=true
      // HACK to keep focus.
      setTimeout(() => $(this.refs.textarea).focus(), 500)
    }

    if (ignorekey){
      ev.stopPropagation()
      ev.preventDefault()
      // console.log("Must ignore key", ev, this.state.autocomplete)
    }
  }
  render(){
    const {state, props} = this
    return (
      <div className={`field ${props.className || ""}`}>
        {i18n(props.label)}
        <RichDescription className="ui meta" value={i18n(props.description)} vars={props.vars}/>

          {props.mode ? (
            <CodeEdit
              name={props.name}
              mode={props.mode}
              value={props.value}
              onChange={(value) => props.onChange({target:{value}})}
              />
          ) : (
            <textarea
              ref="textarea"
              className={this.props.autocomplete && "ui fixed text"}
              name={props.name}
              placeholder={i18n(props.placeholder || props.description)}
              value={props.value}
              onChange={this.handleChange.bind(this)}
              onKeyDown={this.handleKeyboard.bind(this)}
              onClick={() => this.setState({autocomplete:[]})}
              />
          )}
          {this.is_autocomplete_open() && (

                {state.autocomplete.map( (i,n) => (
                  <div key={i} className={`item ${ n == state.autocomplete_current ? "selected" : ""} `} onClick={() => this.insertAtCursor(i)}>
                    {i}

                ))}

          )}

    )
  }
}

export default TextArea
<commit_msg>Removed debug log<commit_after>import React from 'react'
import RichDescription from './richdescription'
import i18n from 'app/utils/i18n'
import plugin from 'app/utils/plugin'
import CodeEdit from 'app/components/genericform/codeedit'

const SEPARATORS=/ \:\n.\[\]\s/

function is_space(c){
  return (c ==" " || c==":" || c == "\n")
}

function prefix_at_cursor(textarea){
  const text = textarea.value
  const end = textarea.selectionStart
  let start
  for (start=end-1; start>0; start--){
    const c = text[start]
    if (is_space(c)){
      start += 1
      break
    }
  }
  return text.slice(start, end)
}

function replace_around_cursor(textarea, value){
  const text = textarea.value
  let end = textarea.selectionStart-1
  let start
  for (start=end; start>0; start--){
    const c = text[start]
    if (is_space(c)){
      start += 1
      break
    }
  }
  for(;end<text.length;end++){
    const c = text[end]
    if (is_space(c)){
      break
    }
  }
  const ntext = text.slice(0, start)+value+text.slice(end)
  textarea.value=ntext
  textarea.selectionStart = start+value.length
}

function cursor_top(ta){
  if (!ta)
    return 0
  const text = ta.value
  const end = ta.selectionStart
  let line=1
  for(let i=0; i<end; i++){
    if (text[i]=='\n')
      line+=1
  }
  return line*18
}

function cursor_left(ta){
  if (!ta)
    return 0
    const text = ta.value
    const end = ta.selectionStart
    let col=1
    for(let i=0; i<end; i++){
      if (text[i]=='\n')
        col=0
      col+=1
    }
    return col*8.5+8
}

class TextArea extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      autocomplete: undefined,
      autocomplete_current: undefined
    }
  }
  componentDidMount(){
    if (this.props.autocomplete)
      this.updateAutocomplete()
  }
  updateAutocomplete(){
    const ac = this.props.autocomplete
    if (!ac)
      return

    const newtext=prefix_at_cursor(this.refs.textarea).toLocaleLowerCase()
    // console.log("Handle change to %o", newtext)

    plugin.start_call_stop( ac.command, ac.call, {"current": newtext, ...this.form_data}).then(
      autocomplete => {
        // console.log(autocomplete)
        this.setState({autocomplete})
      })
  }
  insertAtCursor(value){
    replace_around_cursor(this.refs.textarea, value)
    this.refs.textarea.focus()

    this.handleChange({target:{value:this.refs.textarea.value}})
  }
  handleChange(ev){
    if (this.state.autocompletedelay)
      clearTimeout(this.state.autocompletedelay)
    const autocompletedelay = setTimeout(() => {
      this.updateAutocomplete()
    }, 300)

    this.setState({autocompletedelay})
    this.props.onChange(ev)
  }
  is_autocomplete_open(){
    return !!(this.state.autocomplete && this.state.autocomplete.length>0)
  }
  handleKeyboard(ev){
    if (!this.is_autocomplete_open())
      return
    let ignorekey=false
    let autocomplete_current = this.state.autocomplete_current || 0
    // console.log(ev.key)

    if (ev.key == "ArrowUp"){
      autocomplete_current = autocomplete_current - 1
      ignorekey=true
    }
    if (ev.key == "ArrowDown"){
      autocomplete_current = autocomplete_current + 1
      ignorekey=true
    }
    if (ev.key == "PageUp"){
      autocomplete_current = autocomplete_current - 3
      ignorekey=true
    }
    if (ev.key == "PageDown"){
      autocomplete_current = autocomplete_current + 3
      ignorekey=true
    }

    if (autocomplete_current != this.state.autocomplete_current){
      if (autocomplete_current>=this.state.autocomplete.length)
        autocomplete_current=0
      if (autocomplete_current<0)
        autocomplete_current=this.state.autocomplete.length-1
      this.setState({autocomplete_current})
      if (this.refs.popup)
        this.refs.popup.scrollTop=autocomplete_current*30
    }

    if (ev.key == "Enter"){
      if (this.state.autocomplete[this.state.autocomplete_current]){
        this.insertAtCursor(this.state.autocomplete[this.state.autocomplete_current])
      }
    }
    if (ev.key == "ArrowLeft"){
      this.setState({cursor: this.refs.textarea.selectionStart})
    }
    if (ev.key == "ArrowRight"){
      this.setState({cursor: this.refs.textarea.selectionStart})
    }
    if (ev.key == "Escape"){
      this.setState({autocomplete:[]})
      ignorekey=true
      // HACK to keep focus.
      setTimeout(() => $(this.refs.textarea).focus(), 500)
    }

    if (ignorekey){
      ev.stopPropagation()
      ev.preventDefault()
      // console.log("Must ignore key", ev, this.state.autocomplete)
    }
  }
  render(){
    const {state, props} = this
    return (
      <div className={`field ${props.className || ""}`}>
        {i18n(props.label)}
        <RichDescription className="ui meta" value={i18n(props.description)} vars={props.vars}/>

          {props.mode ? (
            <CodeEdit
              name={props.name}
              mode={props.mode}
              value={props.value}
              onChange={(value) => props.onChange({target:{value}})}
              />
          ) : (
            <textarea
              ref="textarea"
              className={this.props.autocomplete && "ui fixed text"}
              name={props.name}
              placeholder={i18n(props.placeholder || props.description)}
              value={props.value}
              onChange={this.handleChange.bind(this)}
              onKeyDown={this.handleKeyboard.bind(this)}
              onClick={() => this.setState({autocomplete:[]})}
              />
          )}
          {this.is_autocomplete_open() && (

                {state.autocomplete.map( (i,n) => (
                  <div key={i} className={`item ${ n == state.autocomplete_current ? "selected" : ""} `} onClick={() => this.insertAtCursor(i)}>
                    {i}

                ))}

          )}

    )
  }
}

export default TextArea
<|endoftext|>


<commit_before>import TextField from 'ember-components/text-field';
import computed from 'ember-computed';
import run from 'ember-runloop';
import {InvokeActionMixin} from 'ember-invoke-action';

let joinUrlParts = function (url, path) {
    if (path[0] !== '/' && url.slice(-1) !== '/') {
        path = `/${path}`;
    } else if (path[0] === '/' && url.slice(-1) === '/') {
        path = path.slice(1);
    }

    return url + path;
};

let isRelative = function (url) {
    // "protocol://", "//example.com", "scheme:", "#anchor", & invalid paths
    // should all be treated as absolute
    return !url.match(/\s/) && !validator.isURL(url) && !url.match(/^(\/\/|#|[a-zA-Z0-9\-]+:)/);
};

export default TextField.extend(InvokeActionMixin, {
    classNames: 'gh-input',

    isBaseUrl: computed('baseUrl', 'value', function () {
        return this.get('baseUrl') === this.get('value');
    }),

    didReceiveAttrs() {
        this._super(...arguments);

        let baseUrl = this.get('baseUrl');
        let url = this.get('url');

        // if we have a relative url, create the absolute url to be displayed in the input
        if (isRelative(url)) {
            url = joinUrlParts(baseUrl, url);
        }

        this.set('value', url);
    },

    focusIn(event) {
        this.set('hasFocus', true);

        if (this.get('isBaseUrl')) {
            // position the cursor at the end of the input
            run.next(function (el) {
                let {length} = el.value;

                el.setSelectionRange(length, length);
            }, event.target);
        }
    },

    keyDown(event) {
        // delete the "placeholder" value all at once
        if (this.get('isBaseUrl') && (event.keyCode === 8 || event.keyCode === 46)) {
            this.set('value', '');

            event.preventDefault();
        }

        // CMD-S
        if (event.keyCode === 83 && event.metaKey) {
            this.notifyUrlChanged();
        }
    },

    keyPress(event) {
        this.invokeAction('clearErrors');

        // enter key
        if (event.keyCode === 13) {
            this.notifyUrlChanged();
        }

        return true;
    },

    focusOut() {
        this.set('hasFocus', false);

        this.notifyUrlChanged();
    },

    notifyUrlChanged() {
        let url = this.get('value').trim();
        let urlParts = document.createElement('a');
        let baseUrl = this.get('baseUrl');
        let baseUrlParts = document.createElement('a');

        // ensure value property is trimmed
        this.set('value', url);

        // leverage the browser's native URI parsing
        urlParts.href = url;
        baseUrlParts.href = baseUrl;

        // if we have an email address, add the mailto:
        if (validator.isEmail(url)) {
            url = `mailto:${url}`;
            this.set('value', url);
        }

        // if we have a relative url, create the absolute url to be displayed in the input
        if (isRelative(url)) {
            url = joinUrlParts(baseUrl, url);
            this.set('value', url);
        }

        // get our baseUrl relativity checks in order
        let isOnSameHost = urlParts.host === baseUrlParts.host;
        let isAnchorLink = url.match(/^#/);
        let isRelativeToBasePath = urlParts.pathname.indexOf(baseUrlParts.pathname) === 0;

        // if our pathname is only missing a trailing / mark it as relative
        if (`${urlParts.pathname}/` === baseUrlParts.pathname) {
            isRelativeToBasePath = true;
        }

        // if relative to baseUrl, remove the base url before sending to action
        if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) {
            url = url.replace(/^[a-zA-Z0-9\-]+:/, '');
            url = url.replace(/^\/\//, '');
            url = url.replace(baseUrlParts.host, '');
            url = url.replace(baseUrlParts.pathname, '');

            // handle case where url path is same as baseUrl path but missing trailing slash
            if (urlParts.pathname.slice(-1) !== '/') {
                url = url.replace(baseUrlParts.pathname.slice(0, -1), '');
            }

            if (url !== '') {
                if (!url.match(/^\//)) {
                    url = `/${url}`;
                }

                if (!url.match(/\/$/) && !url.match(/[\.#\?]/)) {
                    url = `${url}/`;
                }
            }
        }

        this.sendAction('change', url);
    }
});
<commit_msg>Fix new url field focus bug (#194)<commit_after>import TextField from 'ember-components/text-field';
import computed from 'ember-computed';
import run from 'ember-runloop';
import {InvokeActionMixin} from 'ember-invoke-action';

let joinUrlParts = function (url, path) {
    if (path[0] !== '/' && url.slice(-1) !== '/') {
        path = `/${path}`;
    } else if (path[0] === '/' && url.slice(-1) === '/') {
        path = path.slice(1);
    }

    return url + path;
};

let isRelative = function (url) {
    // "protocol://", "//example.com", "scheme:", "#anchor", & invalid paths
    // should all be treated as absolute
    return !url.match(/\s/) && !validator.isURL(url) && !url.match(/^(\/\/|#|[a-zA-Z0-9\-]+:)/);
};

export default TextField.extend(InvokeActionMixin, {
    classNames: 'gh-input',

    isBaseUrl: computed('baseUrl', 'value', function () {
        return this.get('baseUrl') === this.get('value');
    }),

    didReceiveAttrs() {
        this._super(...arguments);

        let baseUrl = this.get('baseUrl');
        let url = this.get('url');

        // if we have a relative url, create the absolute url to be displayed in the input
        if (isRelative(url)) {
            url = joinUrlParts(baseUrl, url);
        }

        this.set('value', url);
    },

    focusIn(event) {
        this.set('hasFocus', true);

        if (this.get('isBaseUrl')) {
            // position the cursor at the end of the input
            run.next(function (el) {
                let {length} = el.value;

                el.setSelectionRange(length, length);
            }, event.target);
        }
    },

    keyDown(event) {
        // delete the "placeholder" value all at once
        if (this.get('isBaseUrl') && (event.keyCode === 8 || event.keyCode === 46)) {
            this.set('value', '');

            event.preventDefault();
        }

        // CMD-S
        if (event.keyCode === 83 && event.metaKey) {
            this.notifyUrlChanged();
        }
    },

    keyPress(event) {
        this.invokeAction('clearErrors');

        // enter key
        if (event.keyCode === 13) {
            this.notifyUrlChanged();
        }

        return true;
    },

    focusOut() {
        this.set('hasFocus', false);

        this.notifyUrlChanged();
    },

    notifyUrlChanged() {
        let url = this.get('value').trim();
        let urlParts = document.createElement('a');
        let baseUrl = this.get('baseUrl');
        let baseUrlParts = document.createElement('a');

        // ensure value property is trimmed
        this.set('value', url);

        // leverage the browser's native URI parsing
        urlParts.href = url;
        baseUrlParts.href = baseUrl;

        // if we have an email address, add the mailto:
        if (validator.isEmail(url)) {
            url = `mailto:${url}`;
            this.set('value', url);
        }

        // if we have a relative url, create the absolute url to be displayed in the input
        if (isRelative(url)) {
            url = joinUrlParts(baseUrl, url);
            this.set('value', url);
        }

        // get our baseUrl relativity checks in order
        let isOnSameHost = urlParts.host === baseUrlParts.host;
        let isAnchorLink = url.match(/^#/);
        let isRelativeToBasePath = urlParts.pathname.indexOf(baseUrlParts.pathname) === 0;

        // if our pathname is only missing a trailing / mark it as relative
        if (`${urlParts.pathname}/` === baseUrlParts.pathname) {
            isRelativeToBasePath = true;
        }

        // if relative to baseUrl, remove the base url before sending to action
        if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) {
            url = url.replace(/^[a-zA-Z0-9\-]+:/, '');
            url = url.replace(/^\/\//, '');
            url = url.replace(baseUrlParts.host, '');
            url = url.replace(baseUrlParts.pathname, '');

            // handle case where url path is same as baseUrl path but missing trailing slash
            if (urlParts.pathname.slice(-1) !== '/') {
                url = url.replace(baseUrlParts.pathname.slice(0, -1), '');
            }

            if (url !== '' || !this.get('isNew')) {
                if (!url.match(/^\//)) {
                    url = `/${url}`;
                }

                if (!url.match(/\/$/) && !url.match(/[\.#\?]/)) {
                    url = `${url}/`;
                }
            }
        }

        this.sendAction('change', url);
    }
});
<|endoftext|>


<commit_before>/* eslint-env jest */

import moment from 'moment'
import tableNames from '../../tables'
import { permissionAuthorizers } from '../../../utils/authorization-helpers'
import User from '../UserModel'
import config from '../../../config'
import {
  DatabaseOperation,
  mockDate,
  getMockUserContext,
  setMockDBResponse,
} from '../../test-utils'
import {
  UnauthorizedQueryException,
  UserDoesNotExistException,
} from '../../../utils/exceptions'

const mediaRoot = config.MEDIA_ENDPOINT

jest.mock('../../databaseClient')

beforeAll(() => {
  mockDate.on()
})

afterAll(() => {
  mockDate.off()
})

describe('UserModel', () => {
  it('implements the name property', () => {
    expect(User.name).toBeDefined()
  })

  it('implements the hashKey property', () => {
    expect(User.hashKey).toBeDefined()
  })

  it('implements the tableName property', () => {
    expect(User.tableName).toBe(tableNames.users)
  })

  it('has the correct get permission', () => {
    expect(User.permissions.get).toBe(
      permissionAuthorizers.userIdMatchesHashKey
    )
  })

  it('has the correct update permission', () => {
    expect(User.permissions.update).toBe(
      permissionAuthorizers.userIdMatchesHashKey
    )
  })

  it('has the correct getAll permission', () => {
    expect(User.permissions.getAll()).toBe(false)
  })

  it('allows create when the user context matches the item to create', () => {
    const userContext = {
      id: 'abc',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: 'foo@bar.com',
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it('does not allow create when user ID is different', () => {
    const userContext = {
      id: 'abcd',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: 'foo@bar.com',
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it("does not allow create when the user's email is different", () => {
    const userContext = {
      id: 'abc',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: 'foo+hi@bar.com',
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('allows create when the user has a null email in both their token and the item to create', () => {
    const userContext = {
      id: 'abc',
      email: null,
      emailVerified: false,
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it('allows create when the user has an undefined email (in their token) and tries to create an item with a null email', () => {
    const userContext = {
      id: 'abc',
      email: undefined,
      emailVerified: false,
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it('allows create when the user does not have an email in their token nor in the item to create', () => {
    const userContext = {
      id: 'abc',
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it("does not allow create when the user's email exists in the token but not in the item", () => {
    const userContext = {
      id: 'abc',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('does not allow create when the user context is not provided', () => {
    const userContext = null
    const item = {
      id: 'abc',
      email: 'foo@bar.com',
      username: 'myOtherName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('does not allow create when the item is not provided', () => {
    const userContext = {
      id: 'abcd',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = null
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('throws an UserDoesNotExistException error when a `get` returns no item', () => {
    const userContext = getMockUserContext()
    const mockItemId = userContext.id

    // Set mock response from DB client.
    setMockDBResponse(DatabaseOperation.GET, {
      Item: null,
    })
    return expect(User.get(userContext, mockItemId)).rejects.toEqual(
      new UserDoesNotExistException()
    )
  })

  it('throws an error when `get` throws an error other than "item does not exist"', () => {
    const userContext = getMockUserContext()

    // Use an unauthorized request to get its error.
    return expect(
      User.get(userContext, 'unauthorized-user-id-here')
    ).rejects.toEqual(new UnauthorizedQueryException())
  })

  it('does not throw an error when a `get` returns an item', () => {
    const userContext = getMockUserContext()
    const mockItemId = userContext.id

    // Set mock response from DB client.
    setMockDBResponse(DatabaseOperation.GET, {
      Item: {
        // Actual item would have more properties
        id: mockItemId,
      },
    })

    return expect(User.get(userContext, mockItemId)).resolves.toBeDefined()
  })

  it('constructs as expected', () => {
    const item = Object.assign(
      {},
      new User({
        id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
        email: 'foo@bar.com',
        username: 'Foo Bar',
      })
    )
    expect(item).toEqual({
      id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
      email: 'foo@bar.com',
      username: 'Foo Bar',
      pendingMissionInvites: [],
      vcCurrent: 0,
      vcAllTime: 0,
      level: 0,
      tabs: 0,
      tabsToday: 0,
      maxTabsDay: {
        maxDay: {
          date: moment.utc().toISOString(),
          numTabs: 0,
        },
        recentDay: {
          date: moment.utc().toISOString(),
          numTabs: 0,
        },
      },
      validTabs: 0,
      heartsUntilNextLevel: 0,
      vcDonatedAllTime: 0,
      numUsersRecruited: 0,
      backgroundImage: {
        id: '9308b921-44c7-4b4e-845d-3b01fa73fa2b',
        image: '94bbd29b17fe4fa3b45777281a392f21.jpg',
        thumbnail: '5d4dfd0b34134879903f0480720bd746.jpg',
        imageURL: `${mediaRoot}/img/backgrounds/94bbd29b17fe4fa3b45777281a392f21.jpg`,
        thumbnailURL: `${mediaRoot}/img/background-thumbnails/5d4dfd0b34134879903f0480720bd746.jpg`,
        timestamp: moment.utc().toISOString(),
      },
      backgroundOption: 'daily',
      searches: 0,
      maxSearchesDay: {
        maxDay: {
          date: moment.utc().toISOString(),
          numSearches: 0,
        },
        recentDay: {
          date: moment.utc().toISOString(),
          numSearches: 0,
        },
      },
      searchesToday: 0,
      v4BetaEnabled: false,
      hasViewedIntroFlow: false,
      deleted: false,
      hasSeenSquads: false,
    })
  })

  it('constructs with the expected "tabsToday" value', () => {
    const item = Object.assign(
      {},
      new User({
        id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
        email: 'foo@bar.com',
        username: 'Foo Bar',
        maxTabsDay: {
          maxDay: {
            date: moment.utc().toISOString(),
            numTabs: 300,
          },
          recentDay: {
            date: moment.utc().toISOString(),
            numTabs: 47,
          },
        },
      })
    )
    expect(item).toMatchObject({
      id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
      tabsToday: 47,
    })
  })

  it('constructs with the expected "searchesToday" value', () => {
    const item = Object.assign(
      {},
      new User({
        id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
        email: 'foo@bar.com',
        username: 'Foo Bar',
        maxSearchesDay: {
          maxDay: {
            date: moment.utc().toISOString(),
            numSearches: 300,
          },
          recentDay: {
            date: moment.utc().toISOString(),
            numSearches: 47,
          },
        },
      })
    )
    expect(item).toMatchObject({
      id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
      searchesToday: 47,
    })
  })
})
<commit_msg>Fix test<commit_after>/* eslint-env jest */

import moment from 'moment'
import tableNames from '../../tables'
import { permissionAuthorizers } from '../../../utils/authorization-helpers'
import User from '../UserModel'
import config from '../../../config'
import {
  DatabaseOperation,
  mockDate,
  getMockUserContext,
  setMockDBResponse,
} from '../../test-utils'
import {
  UnauthorizedQueryException,
  UserDoesNotExistException,
} from '../../../utils/exceptions'

const mediaRoot = config.MEDIA_ENDPOINT

jest.mock('../../databaseClient')

beforeAll(() => {
  mockDate.on()
})

afterAll(() => {
  mockDate.off()
})

describe('UserModel', () => {
  it('implements the name property', () => {
    expect(User.name).toBeDefined()
  })

  it('implements the hashKey property', () => {
    expect(User.hashKey).toBeDefined()
  })

  it('implements the tableName property', () => {
    expect(User.tableName).toBe(tableNames.users)
  })

  it('has the correct get permission', () => {
    expect(User.permissions.get).toBe(
      permissionAuthorizers.userIdMatchesHashKey
    )
  })

  it('has the correct update permission', () => {
    expect(User.permissions.update).toBe(
      permissionAuthorizers.userIdMatchesHashKey
    )
  })

  it('has the correct getAll permission', () => {
    expect(User.permissions.getAll()).toBe(false)
  })

  it('allows create when the user context matches the item to create', () => {
    const userContext = {
      id: 'abc',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: 'foo@bar.com',
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it('does not allow create when user ID is different', () => {
    const userContext = {
      id: 'abcd',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: 'foo@bar.com',
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it("does not allow create when the user's email is different", () => {
    const userContext = {
      id: 'abc',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: 'foo+hi@bar.com',
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('allows create when the user has a null email in both their token and the item to create', () => {
    const userContext = {
      id: 'abc',
      email: null,
      emailVerified: false,
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it('allows create when the user has an undefined email (in their token) and tries to create an item with a null email', () => {
    const userContext = {
      id: 'abc',
      email: undefined,
      emailVerified: false,
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it('allows create when the user does not have an email in their token nor in the item to create', () => {
    const userContext = {
      id: 'abc',
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(true)
  })

  it("does not allow create when the user's email exists in the token but not in the item", () => {
    const userContext = {
      id: 'abc',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = {
      id: 'abc',
      email: null,
      username: 'myName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('does not allow create when the user context is not provided', () => {
    const userContext = null
    const item = {
      id: 'abc',
      email: 'foo@bar.com',
      username: 'myOtherName',
    }
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('does not allow create when the item is not provided', () => {
    const userContext = {
      id: 'abcd',
      email: 'foo@bar.com',
      emailVerified: true,
    }
    const item = null
    expect(User.permissions.create(userContext, null, null, item)).toBe(false)
  })

  it('throws an UserDoesNotExistException error when a `get` returns no item', () => {
    const userContext = getMockUserContext()
    const mockItemId = userContext.id

    // Set mock response from DB client.
    setMockDBResponse(DatabaseOperation.GET, {
      Item: null,
    })
    return expect(User.get(userContext, mockItemId)).rejects.toEqual(
      new UserDoesNotExistException()
    )
  })

  it('throws an error when `get` throws an error other than "item does not exist"', () => {
    const userContext = getMockUserContext()

    // Use an unauthorized request to get its error.
    return expect(
      User.get(userContext, 'unauthorized-user-id-here')
    ).rejects.toEqual(new UnauthorizedQueryException())
  })

  it('does not throw an error when a `get` returns an item', () => {
    const userContext = getMockUserContext()
    const mockItemId = userContext.id

    // Set mock response from DB client.
    setMockDBResponse(DatabaseOperation.GET, {
      Item: {
        // Actual item would have more properties
        id: mockItemId,
      },
    })

    return expect(User.get(userContext, mockItemId)).resolves.toBeDefined()
  })

  it('constructs as expected', () => {
    const item = Object.assign(
      {},
      new User({
        id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
        email: 'foo@bar.com',
        username: 'Foo Bar',
      })
    )
    expect(item).toEqual({
      id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
      email: 'foo@bar.com',
      username: 'Foo Bar',
      pendingMissionInvites: [],
      vcCurrent: 0,
      vcAllTime: 0,
      level: 0,
      tabs: 0,
      tabsToday: 0,
      maxTabsDay: {
        maxDay: {
          date: moment.utc().toISOString(),
          numTabs: 0,
        },
        recentDay: {
          date: moment.utc().toISOString(),
          numTabs: 0,
        },
      },
      validTabs: 0,
      heartsUntilNextLevel: 0,
      vcDonatedAllTime: 0,
      numUsersRecruited: 0,
      backgroundImage: {
        id: '7e73d6d7-b915-4366-b01a-ffc126466d5b',
        image: '3acd54614b1d4d7fbce85d965de3de25.jpg',
        thumbnail: '71a27d6823244354acb85e0806d0dff1.jpg',
        imageURL: `${mediaRoot}/img/backgrounds/3acd54614b1d4d7fbce85d965de3de25.jpg`,
        thumbnailURL: `${mediaRoot}/img/background-thumbnails/71a27d6823244354acb85e0806d0dff1.jpg`,
        timestamp: moment.utc().toISOString(),
      },
      backgroundOption: 'daily',
      searches: 0,
      maxSearchesDay: {
        maxDay: {
          date: moment.utc().toISOString(),
          numSearches: 0,
        },
        recentDay: {
          date: moment.utc().toISOString(),
          numSearches: 0,
        },
      },
      searchesToday: 0,
      v4BetaEnabled: false,
      hasViewedIntroFlow: false,
      deleted: false,
      hasSeenSquads: false,
    })
  })

  it('constructs with the expected "tabsToday" value', () => {
    const item = Object.assign(
      {},
      new User({
        id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
        email: 'foo@bar.com',
        username: 'Foo Bar',
        maxTabsDay: {
          maxDay: {
            date: moment.utc().toISOString(),
            numTabs: 300,
          },
          recentDay: {
            date: moment.utc().toISOString(),
            numTabs: 47,
          },
        },
      })
    )
    expect(item).toMatchObject({
      id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
      tabsToday: 47,
    })
  })

  it('constructs with the expected "searchesToday" value', () => {
    const item = Object.assign(
      {},
      new User({
        id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
        email: 'foo@bar.com',
        username: 'Foo Bar',
        maxSearchesDay: {
          maxDay: {
            date: moment.utc().toISOString(),
            numSearches: 300,
          },
          recentDay: {
            date: moment.utc().toISOString(),
            numSearches: 47,
          },
        },
      })
    )
    expect(item).toMatchObject({
      id: 'bb5082cc-151a-4a9a-9289-06906670fd4e',
      searchesToday: 47,
    })
  })
})
<|endoftext|>


<commit_before>const babel = require('babel-core');
const glob = require('glob');
const fs = require('fs');
const _ = require('async');

const babelrc = {
  presets: ['es2015'],
  plugins: ['add-module-exports'],
};

glob('{index,*/**}.js', { cwd: 'src' }, (globErr, files) => {
  _.each(files, (file, done) => {
    _.waterfall([
      (transformDone) => {
        const options = Object.assign({
          resolveModuleSource(source) {
            return source
              .replace('./util', './lib/util')
              .replace('../util', './util');
          },
        }, babelrc);
        babel.transformFile(`src/${file}`, options, transformDone);
      },
      (result, writeDone) => {
        const dest = file.replace(/^util/, 'lib/util');
        fs.writeFile(dest, result.code, writeDone);
      },
    ], done);
  }, (err) => {
    if (err) return console.error('Error writing lib files:', err);
    console.info('lib files written');
  });
});
<commit_msg>Build script: move resolveModuleSource outside waterfall function<commit_after>const babel = require('babel-core');
const glob = require('glob');
const fs = require('fs');
const _ = require('async');

const babelrc = {
  presets: ['es2015'],
  plugins: ['add-module-exports'],
  resolveModuleSource(source) {
    return source
      .replace('./util', './lib/util')
      .replace('../util', './util');
  },
};

glob('{index,*/**}.js', { cwd: 'src' }, (globErr, files) => {
  _.each(files, (file, done) => {
    _.waterfall([
      (transformDone) => {
        babel.transformFile(`src/${file}`, babelrc, transformDone);
      },
      (result, writeDone) => {
        const dest = file.replace(/^util/, 'lib/util');
        fs.writeFile(dest, result.code, writeDone);
      },
    ], done);
  }, (err) => {
    if (err) return console.error('Error writing lib files:', err);
    console.info('lib files written');
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var objectAssign = require('lodash.assign');
var appRootDir = require('app-root-dir');

function buildConfig(customConfig) {
  var cfg = {};
  var customCfg = customConfig || {};

  cfg.env = {};
  cfg.env.maven = false;

  cfg.appRoot = appRootDir.get() + '/';
  cfg.srcDir = customCfg.srcDir || 'src/';
  cfg.distDir = customCfg.distDir || 'dist/';
  cfg.bowerDir = customCfg.bowerDir || 'bower_components/';
  cfg.npmDir = customCfg.npmDir || 'node_modules/';
  cfg.targetBowerDir = customCfg.targetBowerDir || cfg.distDir + cfg.projectName + cfg.bowerDir;
  cfg.targetNpmDir = customCfg.npmDir || cfg.npmDir + cfg.projectName + +cfg.npmDir;

  cfg.projectName = require(cfg.appRoot + 'package.json').name;

  cfg.src = {};
  cfg.src.styles = cfg.srcDir + 'styles/**/*.scss';
  cfg.src.indexStyles = cfg.srcDir + 'styles/' + cfg.projectName + '.scss';
  cfg.src.images = cfg.srcDir + 'images/**/*.{png,jpg,gif,ico,svg}';
  cfg.src.fonts = cfg.srcDir + 'fonts/**/*';
  cfg.src.indexScript = cfg.srcDir + 'angularjs/' + cfg.projectName + '.js';
  cfg.src.unitTests = cfg.srcDir + '**/*.spec.js';
  cfg.src.scripts = cfg.srcDir + '**/!(*.spec).js';
  cfg.src.templates = cfg.srcDir + '**/!(index).html';
  cfg.src.i18n = cfg.srcDir + 'i18n/**';
  cfg.src.indexHtml = cfg.srcDir + 'index.html';

  cfg.dist = {};
  cfg.dist.indexHtml = cfg.distDir + 'index.html';
  cfg.dist.styles = cfg.distDir + 'styles/';
  cfg.dist.fonts = cfg.distDir + 'fonts/';
  cfg.dist.scripts = cfg.distDir + 'scripts/';
  cfg.dist.indexScript = cfg.distDir + 'scripts/' + cfg.projectName + '.js';
  cfg.dist.images = cfg.distDir + 'images/';
  cfg.dist.i18n = cfg.distDir + 'i18n/';

  cfg.karmaConfig = cfg.appRoot + 'karma.conf.js';
  cfg.bowerAssets = [cfg.bowerDir + 'hippo-theme/dist/**/*.{svg,woff,woff2,ttf,eot,png}'];
  cfg.bowerLinks = [cfg.bowerDir + 'hippo-theme/dist/**'];
  cfg.supportedBrowsers = ['last 1 Chrome versions', 'last 1 Firefox versions', 'Safari >= 7', 'Explorer >= 10'];
  cfg.serverMiddlewares = [];
  cfg.sassLintRules = {
    'force-element-nesting': 0
  };
  cfg.esLintConfig = {
    parser: 'babel-eslint',
    ecmaFeatures: {
      modules: true
    },
    env: {
      browser: true,
      node: true,
      es6: true
    }
  };
  cfg.systemjsOptions = {
    transpiler: 'babel',
    defaultJSExtensions: true
  };
  cfg.serverPort = 9000;

  return objectAssign(cfg, customCfg);
}

module.exports = buildConfig;
<commit_msg>Fix paths for targetBowerDir and targetNpmDit<commit_after>/*
 * Copyright 2015 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var objectAssign = require('lodash.assign');
var appRootDir = require('app-root-dir');

function buildConfig(customConfig) {
  var cfg = {};
  var customCfg = customConfig || {};

  cfg.env = {};
  cfg.env.maven = false;

  cfg.appRoot = appRootDir.get() + '/';
  cfg.srcDir = customCfg.srcDir || 'src/';
  cfg.distDir = customCfg.distDir || 'dist/';
  cfg.bowerDir = customCfg.bowerDir || 'bower_components/';
  cfg.npmDir = customCfg.npmDir || 'node_modules/';
  cfg.targetBowerDir = customCfg.targetBowerDir || cfg.distDir + cfg.bowerDir;
  cfg.targetNpmDir = customCfg.npmDir || cfg.distDir + cfg.npmDir;

  cfg.projectName = require(cfg.appRoot + 'package.json').name;

  cfg.src = {};
  cfg.src.styles = cfg.srcDir + 'styles/**/*.scss';
  cfg.src.indexStyles = cfg.srcDir + 'styles/' + cfg.projectName + '.scss';
  cfg.src.images = cfg.srcDir + 'images/**/*.{png,jpg,gif,ico,svg}';
  cfg.src.fonts = cfg.srcDir + 'fonts/**/*';
  cfg.src.indexScript = cfg.srcDir + 'angularjs/' + cfg.projectName + '.js';
  cfg.src.unitTests = cfg.srcDir + '**/*.spec.js';
  cfg.src.scripts = cfg.srcDir + '**/!(*.spec).js';
  cfg.src.templates = cfg.srcDir + '**/!(index).html';
  cfg.src.i18n = cfg.srcDir + 'i18n/**';
  cfg.src.indexHtml = cfg.srcDir + 'index.html';

  cfg.dist = {};
  cfg.dist.indexHtml = cfg.distDir + 'index.html';
  cfg.dist.styles = cfg.distDir + 'styles/';
  cfg.dist.fonts = cfg.distDir + 'fonts/';
  cfg.dist.scripts = cfg.distDir + 'scripts/';
  cfg.dist.indexScript = cfg.distDir + 'scripts/' + cfg.projectName + '.js';
  cfg.dist.images = cfg.distDir + 'images/';
  cfg.dist.i18n = cfg.distDir + 'i18n/';

  cfg.karmaConfig = cfg.appRoot + 'karma.conf.js';
  cfg.bowerAssets = [cfg.bowerDir + 'hippo-theme/dist/**/*.{svg,woff,woff2,ttf,eot,png}'];
  cfg.bowerLinks = [cfg.bowerDir + 'hippo-theme/dist/**'];
  cfg.supportedBrowsers = ['last 1 Chrome versions', 'last 1 Firefox versions', 'Safari >= 7', 'Explorer >= 10'];
  cfg.serverMiddlewares = [];
  cfg.sassLintRules = {
    'force-element-nesting': 0
  };
  cfg.esLintConfig = {
    parser: 'babel-eslint',
    ecmaFeatures: {
      modules: true
    },
    env: {
      browser: true,
      node: true,
      es6: true
    }
  };
  cfg.systemjsOptions = {
    transpiler: 'babel',
    defaultJSExtensions: true
  };
  cfg.serverPort = 9000;

  return objectAssign(cfg, customCfg);
}

module.exports = buildConfig;
<|endoftext|>


<commit_before>'use strict';

const config = require('config');
const fetch = require('node-fetch');
const path = require('path');
const _ = require('lodash');
const Bluebird = require('bluebird');
const del = require('del');

const fs = Bluebird.promisifyAll(require('fs'));
const childProcess = Bluebird.promisifyAll(require('child_process'));

// Function which returns the raml parser. We don't load this on require-time
// since it has a lot of dependencies and slows down development builds when
// unneeded.
const ramlParser = () => require('raml-1-parser');

/**
 * Creates a new object with all values from the passed object ordered by keys.
 * @param  {Object} obj
 * @return {Object}
 */
function orderObject (obj) {
    const ret = {};
    const orderedKeys = Object.keys(obj).sort();
    for (const key of orderedKeys) {
        ret[key] = obj[key];
    }
    return ret;
}

/**
 * Ensures that the repo cloneable at the provided address is downloaded
 * and up-to-date.
 * @param  {String} addr
 * @return {Promise}
 */
function getRepo (addr) {
    // extract everything after the last slash of the path, excluding .git:
    const name = (/\/([^/]*?)(\.git)?$/).exec(addr)[1];
    const target = path.join(config.src.tmp, name);
    const statPromise = fs.lstatAsync(target);
    // We have a repo override
    if (config.repos && config.repos[name]) {
        return statPromise.tap(stats => {
            // If it's a cloned copy, ripe it
            if (!stats.isSymbolicLink() && stats.isDirectory()) {
                return del(target);
            }
        })
        // if it didn't exist, ignore.
        .catch({ code: 'ENOENT' }, () => { /* do nothing */ })
        .then(stats => {
            if (stats && !stats.isSymbolicLink()) {
                return fs.symlinkAsync(path.join(__dirname, '../', config.repos[name]), target);
            }
        });
    }

    return statPromise.tap(stats => {
        // Delete symbolic if present
        if (stats.isSymbolicLink()) {
            return fs.unlinkAsync(target);
        }
    })
    .tap(stats => {
        // Pull if present
        if (stats.isDirectory()) {
            return childProcess.execAsync(`cd ${target} && git pull`);
        }
    })
    // if the symbolic link did not exist
    .catch({ code: 'ENOENT' }, () => { /* do nothing */ })
    .then(stats => {
        if (stats && stats.isDirectory()) {
            return;
        }
        // Clone if not present or symbolic link was deleted.
        return childProcess.execAsync(`cd ${config.src.tmp} && git clone ${addr}`);
    });
}

/**
 * @callback RAMLTraverseCallback
 * @param {Resource|Method} resource The current resource or method
 * @param {Boolean} isMethod Indicates if node is a method.
 * keep in mind that methods are 'leaves' and have no children.
 * @return {Boolean} If falsy, the entire resource and subresources will be
 * discarded.
 */

/* RAML processing */

// The below code has been taken from
// https://github.com/raml2html/raml2html/blob/raml1.0/index.js
// and has been refactored, license must not be included.

/**
 * Parses the base url by adding the version
 * @param  {RamlJSONObject} ramlObj
 * @return {String}
 */
function parseBaseUri (ramlObj) {
  // I have no clue what kind of variables the RAML spec allows in the baseUri.
  // For now keep it super super simple.
    if (ramlObj.baseUri) {
        ramlObj.baseUri = ramlObj.baseUri.replace('{version}', ramlObj.version);
    }
    return ramlObj;
}

function leftTrim (str, chr) {
    const rgxtrim = !chr ? /^\\s+/ : new RegExp(`^${chr}+`);
    return str.replace(rgxtrim, '');
}

function makeUniqueId (resource) {
    const fullUrl = resource.parentUrl + resource.relativeUri;
    return leftTrim(fullUrl.replace(/\W/g, '_'), '_');
}

function traverseResources (ramlObj, parentUrl, allUriParameters) {
    // Add unique id's and parent URL's plus parent URI parameters to resources
    _.forIn(ramlObj.resources, resource => {
        resource.parentUrl = parentUrl || '';
        resource.uniqueId = makeUniqueId(resource);
        resource.allUriParameters = [];

        if (allUriParameters) {
            resource.allUriParameters.push.apply(resource.allUriParameters, allUriParameters);
        }

        if (resource.uriParameters) {
            _.forIn(resource.uriParameters, parameters => {
                resource.allUriParameters.push(parameters);
            });
        }

        if (resource.methods) {
            _.forIn(resource.methods, method => {
                method.allUriParameters = resource.allUriParameters;
            });
        }
        traverseResources(
            resource,
            resource.parentUrl + resource.relativeUri,
            resource.allUriParameters
        );
    });

    return orderObject(ramlObj);
}

function transverseTypes (ramlObj) {
    if (!ramlObj.hasOwnProperty('types')) {
        return ramlObj;
    }

    const newTypes = {};
    ramlObj.types.forEach(type => _.assign(newTypes, type));
    ramlObj.types = orderObject(newTypes);

    return ramlObj;
}

/**
 * Adds unique ids to each segment.
 * @param {RAMLJSONObject} ramlObj
 */
function addUniqueIdsToDocs (ramlObj) {
    // Add unique id's to top level documentation chapters
    _.forEach(ramlObj.documentation, docSection => {
        docSection.uniqueId = docSection.title.replace(/\W/g, '-');
    });

    return ramlObj;
}

/**
 * Makes the raml object more usable.
 * @param  {RAMLJSONObject} ramlObj
 * @return {RAMLJSONObject}
 */
function enhanceRamlObj (ramlObj) {
    ramlObj = parseBaseUri(ramlObj);
    ramlObj = traverseResources(ramlObj);
    ramlObj = transverseTypes(ramlObj);
    return addUniqueIdsToDocs(ramlObj);
}

/**
 * Generates a custom raml2html config object, injecting some of our own logic
 * to add features raml2html doesn't provide by default.
 * @return {Object}
 */
function traverseRAMLResourceTree (resources, callback, absURL) {
    absURL = absURL || '';

    resources.forEach((resource, idx) => {
        const currUrl = absURL + resource.relativeUri;
        if (!callback(resource, false, absURL)) {
            resources.splice(idx, 1);
            return;
        }
        if (resource.methods) {
            resource.methods.forEach((method, methodIdx) => {
                if (!callback(method, true, currUrl)) {
                    resource.methods.splice(methodIdx, 1);
                    return;
                }
            });
        }
        if (resource.resources) {
            traverseRAMLResourceTree(resource.resources, callback, currUrl);
        }
    });
}

/**
 * Registers a task that compiles
 * @param  {Gulp} gulp
 * @param  {Object} $ plugin loader
 * @return {Stream}
 */
module.exports = (gulp) => {
    gulp.task('java-clone', () => getRepo('git@github.com:WatchBeam/beam-client-java.git'));

    gulp.task('java-mvn-gen', ['java-clone'], (callback) => {
        childProcess.exec(
            `cd ${config.src.tmp}/beam-client-java && mvn clean javadoc:javadoc`,
            callback
        );
    });
    gulp.task('java-doc', ['java-mvn-gen'], () => {
        return gulp.src(path.join(config.src.tmp, 'beam-client-java/target/site/**/*'))
        .pipe(gulp.dest(config.dist.javadoc));
    });

    gulp.task('backend-clone', () => getRepo('git@github.com:WatchBeam/backend.git'));

    gulp.task('backend-doc', ['backend-clone'], () => {
        let docPath;
        if (config.backendRamlPath) {
            docPath = path.join(config.backendRamlPath, 'index.raml');
        } else {
            docPath = path.join(config.src.tmp, 'backend/doc/raml/index.raml');
        }
        return ramlParser().loadApi(docPath, {
            rejectOnErrors: true,
        })
        .catch(error => {
            if (error.parserErrors) {
                const stack = error.parserErrors
                .map((err, idx) => {
                    return `${idx + 1}: ${err.path}@${err.line}:${err.column} ${err.message}`;
                })
                .join('\n');
                error.message += `\n${stack}`;
            }

            throw error;
        })
        .then(api => {
            const tree = enhanceRamlObj(api.expand().toJSON());

            traverseRAMLResourceTree(tree.resources, resource => {
                return !(resource.annotations && resource.annotations.internal);
            });

            fs.writeFileSync(
                path.join(config.src.tmp, 'raml-doc.json'),
                JSON.stringify(tree, null, '   ')
            );
        });
    });

    gulp.task('pull-client-repos', () => {
        const todo = require('./libraries').map(lib => {
            return fetch(`https://api.github.com/repos/${lib.name}`)
            .then(response => {
                if (response.status !== 200) {
                    throw new Error(
                        `Errorful response getting ${lib.name}: ${response.statusText}`
                    );
                }

                return response.json();
            })
            .then(json => ({
                language: lib.language,
                official: lib.official,
                name: lib.alias || lib.name.split('/').pop(),
                updatedAt: new Date(json.pushed_at).toDateString(),
                stars: json.stargazers_count,
                url: json.html_url,
            }));
        });

        return Promise.all(todo).then(result => {
            fs.writeFileSync(
                path.join(config.src.tmp, 'libraries.json'),
                JSON.stringify(_.sortBy(result, 'language'), null, '   ')
            );
        });
    });
};
<commit_msg>use master branch for backend pull<commit_after>'use strict';

const config = require('config');
const fetch = require('node-fetch');
const path = require('path');
const _ = require('lodash');
const Bluebird = require('bluebird');
const del = require('del');

const fs = Bluebird.promisifyAll(require('fs'));
const childProcess = Bluebird.promisifyAll(require('child_process'));

// Function which returns the raml parser. We don't load this on require-time
// since it has a lot of dependencies and slows down development builds when
// unneeded.
const ramlParser = () => require('raml-1-parser');

/**
 * Creates a new object with all values from the passed object ordered by keys.
 * @param  {Object} obj
 * @return {Object}
 */
function orderObject (obj) {
    const ret = {};
    const orderedKeys = Object.keys(obj).sort();
    for (const key of orderedKeys) {
        ret[key] = obj[key];
    }
    return ret;
}

/**
 * Ensures that the repo cloneable at the provided address is downloaded
 * and up-to-date.
 * @param  {String} addr
 * @param  {String} branch Name of the branch to clone
 * @return {Promise}
 */
function getRepo (addr, branch) {
    // extract everything after the last slash of the path, excluding .git:
    const name = (/\/([^/]*?)(\.git)?$/).exec(addr)[1];
    const target = path.join(config.src.tmp, name);
    const statPromise = fs.lstatAsync(target);
    // We have a repo override
    if (config.repos && config.repos[name]) {
        return statPromise.tap(stats => {
            // If it's a cloned copy, ripe it
            if (!stats.isSymbolicLink() && stats.isDirectory()) {
                return del(target);
            }
        })
        // if it didn't exist, ignore.
        .catch({ code: 'ENOENT' }, () => { /* do nothing */ })
        .then(stats => {
            if (stats && !stats.isSymbolicLink()) {
                return fs.symlinkAsync(path.join(__dirname, '../', config.repos[name]), target);
            }
        });
    }

    return statPromise.tap(stats => {
        // Delete symbolic if present
        if (stats.isSymbolicLink()) {
            return fs.unlinkAsync(target);
        }
    })
    .tap(stats => {
        // Pull if present
        if (stats.isDirectory()) {
            return childProcess.execAsync(`cd ${target} && git pull`);
        }
    })
    // if the symbolic link did not exist
    .catch({ code: 'ENOENT' }, () => { /* do nothing */ })
    .then(stats => {
        if (stats && stats.isDirectory()) {
            return;
        }
        const branchString = branch ? `-b ${branch}` : '';
        // Clone if not present or symbolic link was deleted.
        return childProcess.execAsync(`cd ${config.src.tmp} && git clone ${branchString} ${addr}`);
    });
}

/**
 * @callback RAMLTraverseCallback
 * @param {Resource|Method} resource The current resource or method
 * @param {Boolean} isMethod Indicates if node is a method.
 * keep in mind that methods are 'leaves' and have no children.
 * @return {Boolean} If falsy, the entire resource and subresources will be
 * discarded.
 */

/* RAML processing */

// The below code has been taken from
// https://github.com/raml2html/raml2html/blob/raml1.0/index.js
// and has been refactored, license must not be included.

/**
 * Parses the base url by adding the version
 * @param  {RamlJSONObject} ramlObj
 * @return {String}
 */
function parseBaseUri (ramlObj) {
  // I have no clue what kind of variables the RAML spec allows in the baseUri.
  // For now keep it super super simple.
    if (ramlObj.baseUri) {
        ramlObj.baseUri = ramlObj.baseUri.replace('{version}', ramlObj.version);
    }
    return ramlObj;
}

function leftTrim (str, chr) {
    const rgxtrim = !chr ? /^\\s+/ : new RegExp(`^${chr}+`);
    return str.replace(rgxtrim, '');
}

function makeUniqueId (resource) {
    const fullUrl = resource.parentUrl + resource.relativeUri;
    return leftTrim(fullUrl.replace(/\W/g, '_'), '_');
}

function traverseResources (ramlObj, parentUrl, allUriParameters) {
    // Add unique id's and parent URL's plus parent URI parameters to resources
    _.forIn(ramlObj.resources, resource => {
        resource.parentUrl = parentUrl || '';
        resource.uniqueId = makeUniqueId(resource);
        resource.allUriParameters = [];

        if (allUriParameters) {
            resource.allUriParameters.push.apply(resource.allUriParameters, allUriParameters);
        }

        if (resource.uriParameters) {
            _.forIn(resource.uriParameters, parameters => {
                resource.allUriParameters.push(parameters);
            });
        }

        if (resource.methods) {
            _.forIn(resource.methods, method => {
                method.allUriParameters = resource.allUriParameters;
            });
        }
        traverseResources(
            resource,
            resource.parentUrl + resource.relativeUri,
            resource.allUriParameters
        );
    });

    return orderObject(ramlObj);
}

function transverseTypes (ramlObj) {
    if (!ramlObj.hasOwnProperty('types')) {
        return ramlObj;
    }

    const newTypes = {};
    ramlObj.types.forEach(type => _.assign(newTypes, type));
    ramlObj.types = orderObject(newTypes);

    return ramlObj;
}

/**
 * Adds unique ids to each segment.
 * @param {RAMLJSONObject} ramlObj
 */
function addUniqueIdsToDocs (ramlObj) {
    // Add unique id's to top level documentation chapters
    _.forEach(ramlObj.documentation, docSection => {
        docSection.uniqueId = docSection.title.replace(/\W/g, '-');
    });

    return ramlObj;
}

/**
 * Makes the raml object more usable.
 * @param  {RAMLJSONObject} ramlObj
 * @return {RAMLJSONObject}
 */
function enhanceRamlObj (ramlObj) {
    ramlObj = parseBaseUri(ramlObj);
    ramlObj = traverseResources(ramlObj);
    ramlObj = transverseTypes(ramlObj);
    return addUniqueIdsToDocs(ramlObj);
}

/**
 * Generates a custom raml2html config object, injecting some of our own logic
 * to add features raml2html doesn't provide by default.
 * @return {Object}
 */
function traverseRAMLResourceTree (resources, callback, absURL) {
    absURL = absURL || '';

    resources.forEach((resource, idx) => {
        const currUrl = absURL + resource.relativeUri;
        if (!callback(resource, false, absURL)) {
            resources.splice(idx, 1);
            return;
        }
        if (resource.methods) {
            resource.methods.forEach((method, methodIdx) => {
                if (!callback(method, true, currUrl)) {
                    resource.methods.splice(methodIdx, 1);
                    return;
                }
            });
        }
        if (resource.resources) {
            traverseRAMLResourceTree(resource.resources, callback, currUrl);
        }
    });
}

/**
 * Registers a task that compiles
 * @param  {Gulp} gulp
 * @param  {Object} $ plugin loader
 * @return {Stream}
 */
module.exports = (gulp) => {
    gulp.task('java-clone', () => getRepo('git@github.com:WatchBeam/beam-client-java.git'));

    gulp.task('java-mvn-gen', ['java-clone'], (callback) => {
        childProcess.exec(
            `cd ${config.src.tmp}/beam-client-java && mvn clean javadoc:javadoc`,
            callback
        );
    });
    gulp.task('java-doc', ['java-mvn-gen'], () => {
        return gulp.src(path.join(config.src.tmp, 'beam-client-java/target/site/**/*'))
        .pipe(gulp.dest(config.dist.javadoc));
    });

    gulp.task('backend-clone', () => getRepo('git@github.com:WatchBeam/backend.git', 'master'));

    gulp.task('backend-doc', ['backend-clone'], () => {
        let docPath;
        if (config.backendRamlPath) {
            docPath = path.join(config.backendRamlPath, 'index.raml');
        } else {
            docPath = path.join(config.src.tmp, 'backend/doc/raml/index.raml');
        }
        return ramlParser().loadApi(docPath, {
            rejectOnErrors: true,
        })
        .catch(error => {
            if (error.parserErrors) {
                const stack = error.parserErrors
                .map((err, idx) => {
                    return `${idx + 1}: ${err.path}@${err.line}:${err.column} ${err.message}`;
                })
                .join('\n');
                error.message += `\n${stack}`;
            }

            throw error;
        })
        .then(api => {
            const tree = enhanceRamlObj(api.expand().toJSON());

            traverseRAMLResourceTree(tree.resources, resource => {
                return !(resource.annotations && resource.annotations.internal);
            });

            fs.writeFileSync(
                path.join(config.src.tmp, 'raml-doc.json'),
                JSON.stringify(tree, null, '   ')
            );
        });
    });

    gulp.task('pull-client-repos', () => {
        const todo = require('./libraries').map(lib => {
            return fetch(`https://api.github.com/repos/${lib.name}`)
            .then(response => {
                if (response.status !== 200) {
                    throw new Error(
                        `Errorful response getting ${lib.name}: ${response.statusText}`
                    );
                }

                return response.json();
            })
            .then(json => ({
                language: lib.language,
                official: lib.official,
                name: lib.alias || lib.name.split('/').pop(),
                updatedAt: new Date(json.pushed_at).toDateString(),
                stars: json.stargazers_count,
                url: json.html_url,
            }));
        });

        return Promise.all(todo).then(result => {
            fs.writeFileSync(
                path.join(config.src.tmp, 'libraries.json'),
                JSON.stringify(_.sortBy(result, 'language'), null, '   ')
            );
        });
    });
};
<|endoftext|>


<commit_before>/* Nom version 0.0.8, @license MIT, (c) 2015 Vesa Piittinen */
!function(e,n,t){"use strict";function o(){return null}function r(e){e&&e.render&&"value"in e&&"oninput"in e&&(a.node!==e?(a.node=e,a.value=e.value):a.value!==e.value&&(a.value=e.value,e.oninput()))}function i(e,t,o){var c,a,u,d,f,s,h,y=t,m=[];if(null==e)return e;if((n?e instanceof Node:e.nodeType>0)&&!e.render&&(e.render=function(){var n=e.firstChild;for(o||i(e,y);n;)"[object Function]"===Object.prototype.toString.call(n.render)&&n.render(),n=n.nextSibling}),"[object Function]"===Object.prototype.toString.call(t)?t=t.bind(e)():"string"==typeof t&&123===t.charCodeAt(0)&&(t=JSON.parse(t)),"object"!=typeof t)return e;for(a in t)if(t.hasOwnProperty(a))if(u=t[a],!p||"oninput"!==a||"oninput"in e||(e.onpropertychange=function(){"value"===window.event.propertyName&&r(e)}),"children"===a&&e.childNodes){for(f=!0,s=0,h=Array.isArray(u)?Array.prototype.concat.apply([],u):[u],d=e.firstChild;d;){if(f===!0)for(;(f=h[s++])&&"string"!=typeof f&&!(n?f instanceof Node:f.nodeType>0););"string"==typeof f?(3===d.nodeType?(d.nodeValue!==f&&(d.nodeValue=f),d=d.nextSibling):e.insertBefore(document.createTextNode(f),d),f=!0):"[object Function]"===Object.prototype.toString.call(d.render)?f?(d!==f?e.insertBefore(f,d):d=d.nextSibling,f=!0):(m.push(d),d=d.nextSibling):d=d.nextSibling}for(;m.length;)e.removeChild(m.pop());for(;h.length>=s;)e.appendChild("string"==typeof f?document.createTextNode(f):(n?f instanceof Node:f.nodeType>0)?f:l.els(f)),f=h[s++]}else if("[object Function]"===Object.prototype.toString.call(e[a]));else if("object"==typeof u)for(c in u)u.hasOwnProperty(c)&&e[a][c]!==u[c]&&(e[a][c]=u[c]);else e[a]!==u&&(e[a]=u);return e}var l={el:o,els:o,mount:o,supported:e&&t,version:"0.0.8"};if(!e||!t)return l;var c=document.createElement("div"),a={},p="all"in document&&!window.atob;"attachEvent"in document&&p&&document.attachEvent("onselectionchange",function(){r(document.activeElement)}),l.el=function(e,n,t){var o;switch(null==n&&(n={},null==t&&(t=!0)),typeof n){case"function":break;case"boolean":case"number":n=n.toString();case"string":n={children:n},null==t&&(t=!0);break;case"object":Array.isArray(n)&&(n={children:n},null==t&&(t=!0));break;default:n={children:n},null==t&&(t=!0)}return"string"==typeof e&&(-1!==e.indexOf(".")?(o=e.split("."),e=document.createElement(o.shift()),e.className=o.join(" ")):e=document.createElement(e)),i(e,n,!!t)},l.els=function u(e){var t,o,r=document.createDocumentFragment(),i=0;for(e=Array.prototype.concat.apply([],arguments);e.length>i;)if(t=e[i++],n?t instanceof Node:t.nodeType>0)r.appendChild(t);else if("string"==typeof t)for(c.innerHTML=t;c.firstChild;)r.appendChild(c.firstChild);else if(Array.isArray(t))r.appendChild(u.apply(this,t));else if("object"==typeof t)for(o in t)t.hasOwnProperty(o)&&r.appendChild(l.el(o,t[o],"[object Function]"!==Object.prototype.toString.call(t[o])));return r},l.mount=function(e){function t(){for(var e,n=r.length;n;)e=r[--n],e.parentElement?"[object Function]"!==Object.prototype.toString.call(e.parentElement.render)&&e.render():r.splice(n,1);r.length&&requestAnimationFrame(t)}var o,r=[],i=[],c=0;for((n?e instanceof Node&&11===e.nodeType:11===e.nodeType)||(e=l.els.apply(this,arguments));e.childNodes.length>c;)i.push(o=e.childNodes[c++]),"[object Function]"===Object.prototype.toString.call(o.render)&&r.push(o);return e.unmount=function(){for(r.length=0;i.length;)e.appendChild(i.shift());return e},requestAnimationFrame(t),e},"object"==typeof exports?module.exports=l:"function"==typeof define&&define.amd?define(function(){return l}):window.nom=l}("document"in this,"Node"in this,"requestAnimationFrame"in this);<commit_msg>Remove minified file<commit_after><|endoftext|>