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

frappe.ui.Notifications = class Notifications {
	constructor() {
		this.tabs = {};
		frappe.model
			.with_doc('Notification Settings', frappe.session.user)
			.then(doc => {
				this.notifications_settings = doc;
				this.make();
			});
	}

	make() {
		this.dropdown = $('.navbar').find('.dropdown-notifications');
		this.dropdown.removeClass("hidden")
		this.dropdown_list = this.dropdown.find('.notifications-list');
		this.header_items = this.dropdown_list.find('.header-items');
		this.header_actions = this.dropdown_list.find('.header-actions');
		this.body = this.dropdown_list.find('.notification-list-body');
		this.panel_events = this.dropdown_list.find('.panel-events');
		this.panel_notifications = this.dropdown_list.find('.panel-notifications');

		this.user = frappe.session.user;

		this.setup_headers();
		let me = this;
		// frappe.search.utils.make_function_searchable(
		// 	frappe.set_route('Form', 'Notification Settings', frappe.session.user),
		// 	__('Notification Settings'),
		// );

		// this.setup_notifications();
		this.setup_dropdown_events();
		// this.bind_events();
	}

	setup_headers() {
		// Add header actions
		$(`
			${frappe.utils.icon('setting-gear')}
		`)
			.on('click', (e) => {
				e.stopImmediatePropagation();
				this.dropdown.dropdown('hide');
				frappe.set_route('Form', 'Notification Settings', frappe.session.user);
			}).appendTo(this.header_actions);

		$(`
			${frappe.utils.icon('mark-as-read')}
		`)
			.on('click', (e) => this.mark_all_as_read(e))
			.appendTo(this.header_actions);

		this.categories = [
			{
				label: __("Notifications"),
				id: "notifications",
				view: NotificationsView,
				el: this.panel_notifications,
			},
			{
				label: __("Today's Events"),
				id: "todays_events",
				view: EventsView,
				el: this.panel_events,
			}
		];

		let get_headers_html = (item) => {
			let active = item.id == "notifications" ? 'active' : ''

			let html = `
${item.label}
`;

			return html;
		};

		let navitem = $(`<ul class="notification-item-tabs nav nav-tabs" role="tablist"></ul>`);
		this.categories = this.categories.map(item => {
			item.$tab = $(get_headers_html(item));
			item.$tab.on('click', (e) => {
				e.stopImmediatePropagation();
				this.switch_tab(item);
			})
			navitem.append(item.$tab);

			return item
		})
		navitem.appendTo(this.header_items);
		this.categories.forEach(category => {
			this.make_tab_view(category);
		})
		this.switch_tab(this.categories[0]);
	}

	switch_tab(item) {
		// Set active tab
		this.categories.forEach((item) => {
			item.$tab.removeClass("active");
		});

		item.$tab.addClass("active");

		// Hide other tabs
		Object.keys(this.tabs).forEach(tab_name => this.tabs[tab_name].hide());
		this.tabs[item.id].show();
	}

	make_tab_view(item) {
		let tabView = new item.view(
			item.el,
			this.dropdown,
			this.notifications_settings
		);
		this.tabs[item.id] = tabView;
	}

	mark_all_as_read(e) {
		e.stopImmediatePropagation();
		this.dropdown_list.find('.unread').removeClass('unread');
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_all_as_read',
		);
	}

	setup_dropdown_events() {
		this.dropdown.on('hide.bs.dropdown', e => {
			let hide = $(e.currentTarget).data('closable');
			$(e.currentTarget).data('closable', true);
			return hide;
		});

		this.dropdown.on('click', e => {
			$(e.currentTarget).data('closable', true);
		});
	}
};

frappe.ui.notifications = {
	get_notification_config() {
		return frappe.xcall('frappe.desk.notifications.get_notification_info').then(r => {
			frappe.ui.notifications.config = r;
			return r;
		});
	},

	show_open_count_list(doctype) {
		if (!frappe.ui.notifications.config) {
			this.get_notification_config().then(() => {
				this.route_to_list_with_filters(doctype);
			});
		} else {
			this.route_to_list_with_filters(doctype);
		}
	},

	route_to_list_with_filters(doctype) {
		let filters = frappe.ui.notifications.config['conditions'][doctype];
		if (filters && $.isPlainObject(filters)) {
			if (!frappe.route_options) {
				frappe.route_options = {};
			}
			$.extend(frappe.route_options, filters);
		}
		frappe.set_route('List', doctype);
	}
};

class BaseNotificaitonsView {
	constructor(wrapper, parent, settings) {
		// wrapper, max_length
		this.wrapper = wrapper;
		this.parent = parent;
		this.settings = settings;
		this.max_length = 20;
		this.container = $(`<div></div>`).appendTo(this.wrapper);
		this.make();
	}

	show() {
		this.container.show()
	}

	hide() {
		this.container.hide()
	}
}

class NotificationsView extends BaseNotificaitonsView {
	make() {
		this.notifications_icon = this.parent.find('.notifications-icon');

		this.setup_notification_listeners();
		this.get_notifications_list(this.max_length).then(list => {
			this.dropdown_items = list;
			this.render_notifications_dropdown();
			if (this.settings.seen == 0) {
				this.toggle_notification_icon(false);
			}
		});

	}

	update_dropdown() {
		this.get_notifications_list(1).then(r => {
			let new_item = r[0];
			this.dropdown_items.unshift(new_item);
			if (this.dropdown_items.length > this.max_length) {
				this.container
					.find('.recent-notification')
					.last()
					.remove();
				this.dropdown_items.pop();
			}

			this.insert_into_dropdown();
		});
	}

	change_activity_status() {
		if (this.container.find('.activity-status')) {
			this.container.find('.activity-status').replaceWith(
				`

${__('View Full Log')}

				`
			);
		}
	}

	mark_as_read(docname, $el) {
		console.log("SCAM")
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_as_read',
			{ docname: docname }
		).then(() => {
			$el.removeClass('unread');
		});
	}

	insert_into_dropdown() {
		let new_item = this.dropdown_items[0];
		let new_item_html = this.get_dropdown_item_html(new_item);
		$(new_item_html).prependTo(this.container);
		this.change_activity_status();
	}

	get_dropdown_item_html(field) {
		let doc_link = this.get_item_link(field);

		// let read_class = field.read ? '' : 'unread';
		let read_class = 'unread';
		let message = field.subject;

		let title = message.match(/(.*?)<\/b>/);
		message = title ? message.replace(title[1], frappe.ellipsis(strip_html(title[1]), 100)) : message;

		let timestamp = frappe.datetime.comment_when(field.creation);
		let message_html = `

${message}

				${timestamp}

`;

		let user = field.from_user;
		let user_avatar = frappe.avatar(user, 'avatar-medium user-avatar');

		let item_html =
			$(`

					${user_avatar}
					${message_html}

			`);

		item_html.find('.mark-as-read').on('click', (e) => {
			e.preventDefault();
			e.stopImmediatePropagation();
			if (!field.read) {
				this.mark_as_read(field.name, item_html);
			}
		});

		item_html.on('click', (e) => {
			if (!field.read) {
				this.mark_as_read(field.name, item_html);
			}
		});

		return item_html;
	}

	render_notifications_dropdown() {
		if (this.settings && !this.settings.enabled) {
			this.container.html(`

					${__('Notifications Disabled')}

`);
		} else {
			if (this.dropdown_items.length) {
				this.container.empty();
				this.dropdown_items.forEach(field => {
					this.container.append(this.get_dropdown_item_html(field));
				});
				this.container.append(`

${__('See all Activity')}

					`);
			} else {
				this.container.append($(`

No New notifications

							${__('Looks like you haven’t received any notifications.')}

`));
			}
		}
	}

	get_notifications_list(limit) {
		return frappe.db.get_list('Notification Log', {
			fields: ['*'],
			limit: limit,
			order_by: 'creation desc'
		});
	}

	get_item_link(notification_doc) {
		const link_doctype =
			notification_doc.type == 'Alert' ? 'Notification Log' : notification_doc.document_type;
		const link_docname =
			notification_doc.type == 'Alert' ? notification_doc.name : notification_doc.document_name;
		return frappe.utils.get_form_link(
			link_doctype,
			link_docname
		);
	}

	toggle_notification_icon(seen) {
		this.notifications_icon.find('.notifications-seen').toggle(seen);
		this.notifications_icon.find('.notifications-unseen').toggle(!seen);
	}

	toggle_seen(flag) {
		frappe.call(
			'frappe.desk.doctype.notification_settings.notification_settings.set_seen_value',
			{
				value: cint(flag),
				user: frappe.session.user
			}
		);
	}

	setup_notification_listeners() {
		frappe.realtime.on('notification', () => {
			this.toggle_notification_icon(false)
			this.update_dropdown();
		});

		frappe.realtime.on('indicator_hide', () => {
			this.toggle_notification_icon(true)
		});

		this.parent.on('show.bs.dropdown', () => {
			this.toggle_seen(true);
			if (this.notifications_icon.find('.notifications-unseen').is(':visible')) {
				this.toggle_notification_icon(true);
				frappe.call(
					'frappe.desk.doctype.notification_log.notification_log.trigger_indicator_hide'
				);
			}
		});

	}
}

class EventsView extends BaseNotificaitonsView {
	make() {
		let today = frappe.datetime.get_today();
		frappe.xcall('frappe.desk.doctype.event.event.get_events', {
			start: today,
			end: today
		}).then(event_list => {
			this.render_events_html(event_list);
		});
	}

	render_events_html(event_list) {
		let html = '';
		if (event_list.length) {
			let get_event_html = (event) => {
				let time = __("All Day");
				if (!event.all_day) {
					let start_time = frappe.datetime.get_time(event.starts_on);
					let days_diff = frappe.datetime.get_day_diff(event.ends_on, event.starts_on)
					let end_time = frappe.datetime.get_time(event.ends_on);
					if (days_diff > 1) {
						end_time = __("Rest of the day");
					}
					time = `${start_time} - ${end_time}`;
				}

				// REDESIGN-TODO: Add Participants to get_events query
				let particpants = '';
				if (event.particpants) {
					particpants = frappe.avatar_group(event.particpants, 3);
				}

				// REDESIGN-TODO: Add location to calendar field
				let location = '';
				if (event.location) {
					location = `, ${event.location}`;
				}

				return `

${event.subject}

${time}${location}

						${particpants}

				`;
			};
			html = event_list.map(get_event_html).join('');
		} else {
			html = `

No Upcoming Events

						${__('There are no upcoming events for you.')}

			`;
		}

		this.container.html(html);
	}
}<commit_msg>chore: cleanup<commit_after>frappe.provide('frappe.search');

frappe.ui.Notifications = class Notifications {
	constructor() {
		this.tabs = {};
		frappe.model
			.with_doc('Notification Settings', frappe.session.user)
			.then(doc => {
				this.notifications_settings = doc;
				this.make();
			});
	}

	make() {
		this.dropdown = $('.navbar').find('.dropdown-notifications');
		this.dropdown.removeClass("hidden")
		this.dropdown_list = this.dropdown.find('.notifications-list');
		this.header_items = this.dropdown_list.find('.header-items');
		this.header_actions = this.dropdown_list.find('.header-actions');
		this.body = this.dropdown_list.find('.notification-list-body');
		this.panel_events = this.dropdown_list.find('.panel-events');
		this.panel_notifications = this.dropdown_list.find('.panel-notifications');

		this.user = frappe.session.user;

		this.setup_headers();
		let me = this;
		// frappe.search.utils.make_function_searchable(
		// 	frappe.set_route('Form', 'Notification Settings', frappe.session.user),
		// 	__('Notification Settings'),
		// );

		// this.setup_notifications();
		this.setup_dropdown_events();
		// this.bind_events();
	}

	setup_headers() {
		// Add header actions
		$(`
			${frappe.utils.icon('setting-gear')}
		`)
			.on('click', (e) => {
				e.stopImmediatePropagation();
				this.dropdown.dropdown('hide');
				frappe.set_route('Form', 'Notification Settings', frappe.session.user);
			}).appendTo(this.header_actions);

		$(`
			${frappe.utils.icon('mark-as-read')}
		`)
			.on('click', (e) => this.mark_all_as_read(e))
			.appendTo(this.header_actions);

		this.categories = [
			{
				label: __("Notifications"),
				id: "notifications",
				view: NotificationsView,
				el: this.panel_notifications,
			},
			{
				label: __("Today's Events"),
				id: "todays_events",
				view: EventsView,
				el: this.panel_events,
			}
		];

		let get_headers_html = (item) => {
			let active = item.id == "notifications" ? 'active' : ''

			let html = `
${item.label}
`;

			return html;
		};

		let navitem = $(`<ul class="notification-item-tabs nav nav-tabs" role="tablist"></ul>`);
		this.categories = this.categories.map(item => {
			item.$tab = $(get_headers_html(item));
			item.$tab.on('click', (e) => {
				e.stopImmediatePropagation();
				this.switch_tab(item);
			})
			navitem.append(item.$tab);

			return item
		})
		navitem.appendTo(this.header_items);
		this.categories.forEach(category => {
			this.make_tab_view(category);
		})
		this.switch_tab(this.categories[0]);
	}

	switch_tab(item) {
		// Set active tab
		this.categories.forEach((item) => {
			item.$tab.removeClass("active");
		});

		item.$tab.addClass("active");

		// Hide other tabs
		Object.keys(this.tabs).forEach(tab_name => this.tabs[tab_name].hide());
		this.tabs[item.id].show();
	}

	make_tab_view(item) {
		let tabView = new item.view(
			item.el,
			this.dropdown,
			this.notifications_settings
		);
		this.tabs[item.id] = tabView;
	}

	mark_all_as_read(e) {
		e.stopImmediatePropagation();
		this.dropdown_list.find('.unread').removeClass('unread');
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_all_as_read',
		);
	}

	setup_dropdown_events() {
		this.dropdown.on('hide.bs.dropdown', e => {
			let hide = $(e.currentTarget).data('closable');
			$(e.currentTarget).data('closable', true);
			return hide;
		});

		this.dropdown.on('click', e => {
			$(e.currentTarget).data('closable', true);
		});
	}
};

frappe.ui.notifications = {
	get_notification_config() {
		return frappe.xcall('frappe.desk.notifications.get_notification_info').then(r => {
			frappe.ui.notifications.config = r;
			return r;
		});
	},

	show_open_count_list(doctype) {
		if (!frappe.ui.notifications.config) {
			this.get_notification_config().then(() => {
				this.route_to_list_with_filters(doctype);
			});
		} else {
			this.route_to_list_with_filters(doctype);
		}
	},

	route_to_list_with_filters(doctype) {
		let filters = frappe.ui.notifications.config['conditions'][doctype];
		if (filters && $.isPlainObject(filters)) {
			if (!frappe.route_options) {
				frappe.route_options = {};
			}
			$.extend(frappe.route_options, filters);
		}
		frappe.set_route('List', doctype);
	}
};

class BaseNotificaitonsView {
	constructor(wrapper, parent, settings) {
		// wrapper, max_length
		this.wrapper = wrapper;
		this.parent = parent;
		this.settings = settings;
		this.max_length = 20;
		this.container = $(`<div></div>`).appendTo(this.wrapper);
		this.make();
	}

	show() {
		this.container.show()
	}

	hide() {
		this.container.hide()
	}
}

class NotificationsView extends BaseNotificaitonsView {
	make() {
		this.notifications_icon = this.parent.find('.notifications-icon');

		this.setup_notification_listeners();
		this.get_notifications_list(this.max_length).then(list => {
			this.dropdown_items = list;
			this.render_notifications_dropdown();
			if (this.settings.seen == 0) {
				this.toggle_notification_icon(false);
			}
		});

	}

	update_dropdown() {
		this.get_notifications_list(1).then(r => {
			let new_item = r[0];
			this.dropdown_items.unshift(new_item);
			if (this.dropdown_items.length > this.max_length) {
				this.container
					.find('.recent-notification')
					.last()
					.remove();
				this.dropdown_items.pop();
			}

			this.insert_into_dropdown();
		});
	}

	change_activity_status() {
		if (this.container.find('.activity-status')) {
			this.container.find('.activity-status').replaceWith(
				`

${__('View Full Log')}

				`
			);
		}
	}

	mark_as_read(docname, $el) {
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_as_read',
			{ docname: docname }
		).then(() => {
			$el.removeClass('unread');
		});
	}

	insert_into_dropdown() {
		let new_item = this.dropdown_items[0];
		let new_item_html = this.get_dropdown_item_html(new_item);
		$(new_item_html).prependTo(this.container);
		this.change_activity_status();
	}

	get_dropdown_item_html(field) {
		let doc_link = this.get_item_link(field);

		let read_class = field.read ? '' : 'unread';
		let message = field.subject;

		let title = message.match(/(.*?)<\/b>/);
		message = title ? message.replace(title[1], frappe.ellipsis(strip_html(title[1]), 100)) : message;

		let timestamp = frappe.datetime.comment_when(field.creation);
		let message_html = `

${message}

				${timestamp}

`;

		let user = field.from_user;
		let user_avatar = frappe.avatar(user, 'avatar-medium user-avatar');

		let item_html =
			$(`

					${user_avatar}
					${message_html}

			`);

		item_html.find('.mark-as-read').on('click', (e) => {
			e.preventDefault();
			e.stopImmediatePropagation();
			if (!field.read) {
				this.mark_as_read(field.name, item_html);
			}
		});

		item_html.on('click', (e) => {
			if (!field.read) {
				this.mark_as_read(field.name, item_html);
			}
		});

		return item_html;
	}

	render_notifications_dropdown() {
		if (this.settings && !this.settings.enabled) {
			this.container.html(`

					${__('Notifications Disabled')}

`);
		} else {
			if (this.dropdown_items.length) {
				this.container.empty();
				this.dropdown_items.forEach(field => {
					this.container.append(this.get_dropdown_item_html(field));
				});
				this.container.append(`

${__('See all Activity')}

					`);
			} else {
				this.container.append($(`

No New notifications

							${__('Looks like you haven’t received any notifications.')}

`));
			}
		}
	}

	get_notifications_list(limit) {
		return frappe.db.get_list('Notification Log', {
			fields: ['*'],
			limit: limit,
			order_by: 'creation desc'
		});
	}

	get_item_link(notification_doc) {
		const link_doctype =
			notification_doc.type == 'Alert' ? 'Notification Log' : notification_doc.document_type;
		const link_docname =
			notification_doc.type == 'Alert' ? notification_doc.name : notification_doc.document_name;
		return frappe.utils.get_form_link(
			link_doctype,
			link_docname
		);
	}

	toggle_notification_icon(seen) {
		this.notifications_icon.find('.notifications-seen').toggle(seen);
		this.notifications_icon.find('.notifications-unseen').toggle(!seen);
	}

	toggle_seen(flag) {
		frappe.call(
			'frappe.desk.doctype.notification_settings.notification_settings.set_seen_value',
			{
				value: cint(flag),
				user: frappe.session.user
			}
		);
	}

	setup_notification_listeners() {
		frappe.realtime.on('notification', () => {
			this.toggle_notification_icon(false)
			this.update_dropdown();
		});

		frappe.realtime.on('indicator_hide', () => {
			this.toggle_notification_icon(true)
		});

		this.parent.on('show.bs.dropdown', () => {
			this.toggle_seen(true);
			if (this.notifications_icon.find('.notifications-unseen').is(':visible')) {
				this.toggle_notification_icon(true);
				frappe.call(
					'frappe.desk.doctype.notification_log.notification_log.trigger_indicator_hide'
				);
			}
		});

	}
}

class EventsView extends BaseNotificaitonsView {
	make() {
		let today = frappe.datetime.get_today();
		frappe.xcall('frappe.desk.doctype.event.event.get_events', {
			start: today,
			end: today
		}).then(event_list => {
			this.render_events_html(event_list);
		});
	}

	render_events_html(event_list) {
		let html = '';
		if (event_list.length) {
			let get_event_html = (event) => {
				let time = __("All Day");
				if (!event.all_day) {
					let start_time = frappe.datetime.get_time(event.starts_on);
					let days_diff = frappe.datetime.get_day_diff(event.ends_on, event.starts_on)
					let end_time = frappe.datetime.get_time(event.ends_on);
					if (days_diff > 1) {
						end_time = __("Rest of the day");
					}
					time = `${start_time} - ${end_time}`;
				}

				// REDESIGN-TODO: Add Participants to get_events query
				let particpants = '';
				if (event.particpants) {
					particpants = frappe.avatar_group(event.particpants, 3);
				}

				// REDESIGN-TODO: Add location to calendar field
				let location = '';
				if (event.location) {
					location = `, ${event.location}`;
				}

				return `

${event.subject}

${time}${location}

						${particpants}

				`;
			};
			html = event_list.map(get_event_html).join('');
		} else {
			html = `

No Upcoming Events

						${__('There are no upcoming events for you.')}

			`;
		}

		this.container.html(html);
	}
}<|endoftext|>


<commit_before>(function(window, document) {
  'use strict';

  function Pin(container, options) {
    this.el = document.querySelector(container);
    this.parent = this.el.parentNode;

    this.setOptions(options);

    this.init();
  }

  Pin.prototype.setOptions = function(options) {
    this.options = {
      onPin: (options.onPin) ? options.onPin : function() {},
      onUnpin: (options.onUnpin) ? options.onUnpin : function() {},
      onTouchBottom: (options.onTouchBottom) ? options.onTouchBottom : function() {}
    };
  };

  Pin.prototype.init = function() {
    this.createHelperElement();
    this.bind();
    this.calcPositions();

    // simulates the window scroll, in case the user is already
    // in the middle of the scroll bar when the page loads (default browser action)
    this.onWindowScroll();
  };

  Pin.prototype.createHelperElement = function() {
    this.helperDiv = document.createElement(this.el.tagName);
    setStyle(this.helperDiv, {
      position: getStyle(this.el, 'position'),
      width: getStyle(this.el, 'width'),
      height: getStyle(this.el, 'height'),
      float: getStyle(this.el, 'float'),
      display: 'none',
      visibility: 'hidden'
    });

    this.parent.insertBefore(this.helperDiv, this.el);
  };

  Pin.prototype.showHelperElement = function(show) {
    setStyle(this.helperDiv, {
      display: (show === false) ? 'none' : 'block'
    });
  };

  Pin.prototype.destroy = function() {
    setStyle(this.el);
    this.unbind();
  };

  Pin.prototype.calcPositions = function() {
    // if parent does not have a position
    // sets a position relative, that's because
    // the pinned element gets position absolute
    if(getStyle(this.parent, 'position') === 'static') {
      setStyle(this.parent, {
        position: 'relative'
      });
    }

    this.positions = {
      offset: this.getOffset(this.el),
      parentOffset: this.getParentOffset(),
      stopTop: (this.parent.offsetHeight + this.getOffset(this.parent).top) - this.el.offsetHeight
    };
  };

  Pin.prototype.getOffset = function(element) {
    var de = document.documentElement;
    var box = element.getBoundingClientRect();

    return {
      top: box.top + window.pageYOffset - de.clientTop,
      left: box.left + window.pageXOffset - de.clientLeft
    };
  };

  Pin.prototype.getParentOffset = function() {
    return {
      left: this.el.offsetLeft
    };
  };

  Pin.prototype.bind = function() {
    // need to attach to a seperate variable, since bind
    // creates another wraped function, and I can't unbind
    // it in the destroy method.
    this.reloadBind = this.reload.bind(this);
    this.onWindowScrollBind = this.onWindowScroll.bind(this);

    window.addEventListener('resize', this.reloadBind);
    window.addEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.unbind = function() {
    window.removeEventListener('resize', this.reloadBind);
    window.removeEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.reload = function() {
    this.showHelperElement(false);
    setStyle(this.el);
    this.calcPositions();
    this.onWindowScroll();
  };

  /**
  * Lots of things happens on this method
  * It checks the window size, the position of the scroll
  * If the container is touching the bottom and other
  * stuff. That's why it looks like a mess
  * :|
  **/
  Pin.prototype.onWindowScroll = function() {
    var newTop;

    // if the window is smaller then it won't stick
    if(windowIsSmaller(this.el)) return;

    // if the window got to the bottom of the parent element
    // of the container element, it stops the element
    if(this.touchBottom()) return;
    newTop = window.pageYOffset - this.positions.offset.top;

    // some checks to stop unecessary code repetition
    if(newTop > 0 && getStyle(this.el, 'position') === 'fixed') return;
    if(newTop <= 0 && (getStyle(this.el, 'position') === 'relative' || getStyle(this.el, 'position') === 'static')) return;

    if(newTop > 0) {
      setStyle(this.el, {
        position: 'fixed',
        // adds the left and top property, minus the margins,
        // so the element sticks in the same position it was before
        left: toPx(this.positions.offset.left),
        top: 0,
        marginLeft: 0,
        marginTop: 0,
        bottom: ''
      });

      this.options.onPin(this);
      this.showHelperElement();

      return;
    }

    setStyle(this.el);
    this.options.onUnpin(this);
    this.showHelperElement(false);
  };

  Pin.prototype.touchBottom = function() {
    // if the scroll passed the end of the parent
    if(window.pageYOffset > this.positions.stopTop) {
      if(getStyle(this.el, 'position') === 'absolute') return true;

      setStyle(this.el, {
        top: '',
        marginLeft: '',
        bottom: 0,
        position: 'absolute',
        left: toPx(this.positions.parentOffset.left)
      });

      this.options.onTouchBottom(this);
      this.showHelperElement();

      return true;
    }

    return false;
  };

  /**
   * Private functions
   *
   * The functions below are not supposed to be used by the dev
   */

  function setStyle(el, properties) {
    if(!properties) {
      el.removeAttribute('style');
      return;
    }

    for(var property in properties) {
      el.style[property] = properties[property];
    }
  }

  function getStyle(el, property) {
    return (property) ? window.getComputedStyle(el)[property] : window.getComputedStyle(el);
  }

  function toPx(n) {
    return n + 'px';
  }

  function windowIsSmaller(el) {
    return window.innerHeight < el.offsetHeight;
  }

  window.Pin = Pin;

} (window, document));
<commit_msg>export private functions as a Pin property, so it can be tested<commit_after>(function(window, document) {
  'use strict';

  function Pin(container, options) {
    this.el = document.querySelector(container);
    this.parent = this.el.parentNode;

    this.setOptions(options);

    this.init();
  }

  Pin.prototype.setOptions = function(options) {
    this.options = {
      onPin: (options.onPin) ? options.onPin : function() {},
      onUnpin: (options.onUnpin) ? options.onUnpin : function() {},
      onTouchBottom: (options.onTouchBottom) ? options.onTouchBottom : function() {}
    };
  };

  Pin.prototype.init = function() {
    this.createHelperElement();
    this.bind();
    this.calcPositions();

    // simulates the window scroll, in case the user is already
    // in the middle of the scroll bar when the page loads (default browser action)
    this.onWindowScroll();
  };

  Pin.prototype.createHelperElement = function() {
    this.helperDiv = document.createElement(this.el.tagName);
    Pin.setStyle(this.helperDiv, {
      position: Pin.getStyle(this.el, 'position'),
      width: Pin.getStyle(this.el, 'width'),
      height: Pin.getStyle(this.el, 'height'),
      float: Pin.getStyle(this.el, 'float'),
      display: 'none',
      visibility: 'hidden'
    });

    this.parent.insertBefore(this.helperDiv, this.el);
  };

  Pin.prototype.showHelperElement = function(show) {
    Pin.setStyle(this.helperDiv, {
      display: (show === false) ? 'none' : 'block'
    });
  };

  Pin.prototype.destroy = function() {
    Pin.setStyle(this.el);
    this.unbind();
  };

  Pin.prototype.calcPositions = function() {
    // if parent does not have a position
    // sets a position relative, that's because
    // the pinned element gets position absolute
    if(Pin.getStyle(this.parent, 'position') === 'static') {
      Pin.setStyle(this.parent, {
        position: 'relative'
      });
    }

    this.positions = {
      offset: this.getOffset(this.el),
      parentOffset: this.getParentOffset(),
      stopTop: (this.parent.offsetHeight + this.getOffset(this.parent).top) - this.el.offsetHeight
    };
  };

  Pin.prototype.getOffset = function(element) {
    var de = document.documentElement;
    var box = element.getBoundingClientRect();

    return {
      top: box.top + window.pageYOffset - de.clientTop,
      left: box.left + window.pageXOffset - de.clientLeft
    };
  };

  Pin.prototype.getParentOffset = function() {
    return {
      left: this.el.offsetLeft
    };
  };

  Pin.prototype.bind = function() {
    // need to attach to a seperate variable, since bind
    // creates another wraped function, and I can't unbind
    // it in the destroy method.
    this.reloadBind = this.reload.bind(this);
    this.onWindowScrollBind = this.onWindowScroll.bind(this);

    window.addEventListener('resize', this.reloadBind);
    window.addEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.unbind = function() {
    window.removeEventListener('resize', this.reloadBind);
    window.removeEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.reload = function() {
    this.showHelperElement(false);
    Pin.setStyle(this.el);
    this.calcPositions();
    this.onWindowScroll();
  };

  /**
  * Lots of things happens on this method
  * It checks the window size, the position of the scroll
  * If the container is touching the bottom and other
  * stuff. That's why it looks like a mess
  * :|
  **/
  Pin.prototype.onWindowScroll = function() {
    var newTop;

    // if the window is smaller then it won't stick
    if(Pin.windowIsSmaller(this.el)) return;

    // if the window got to the bottom of the parent element
    // of the container element, it stops the element
    if(this.touchBottom()) return;
    newTop = window.pageYOffset - this.positions.offset.top;

    // some checks to stop unecessary code repetition
    if(newTop > 0 && Pin.getStyle(this.el, 'position') === 'fixed') return;
    if(newTop <= 0 && (Pin.getStyle(this.el, 'position') === 'relative' || Pin.getStyle(this.el, 'position') === 'static')) return;

    if(newTop > 0) {
      Pin.setStyle(this.el, {
        position: 'fixed',
        // adds the left and top property, minus the margins,
        // so the element sticks in the same position it was before
        left: Pin.toPx(this.positions.offset.left),
        top: 0,
        marginLeft: 0,
        marginTop: 0,
        bottom: ''
      });

      this.options.onPin(this);
      this.showHelperElement();

      return;
    }

    Pin.setStyle(this.el);
    this.options.onUnpin(this);
    this.showHelperElement(false);
  };

  Pin.prototype.touchBottom = function() {
    // if the scroll passed the end of the parent
    if(window.pageYOffset > this.positions.stopTop) {
      if(Pin.getStyle(this.el, 'position') === 'absolute') return true;

      Pin.setStyle(this.el, {
        top: '',
        marginLeft: '',
        bottom: 0,
        position: 'absolute',
        left: Pin.toPx(this.positions.parentOffset.left)
      });

      this.options.onTouchBottom(this);
      this.showHelperElement();

      return true;
    }

    return false;
  };

  /**
   * "Private" functions
   *
   * The functions below are not supposed to be used by the dev
   */

  Pin.setStyle = function(el, properties) {
    if(!properties) {
      el.removeAttribute('style');
      return;
    }

    for(var property in properties) {
      el.style[property] = properties[property];
    }
  };

  Pin.getStyle = function(el, property) {
    return (property) ? window.getComputedStyle(el)[property] : window.getComputedStyle(el);
  };

  Pin.toPx = function(n) {
    return n + 'px';
  };

  Pin.windowIsSmaller = function(el) {
    return window.innerHeight < el.offsetHeight;
  };

  Pin.init = function() {

  };

  window.Pin = Pin;

  /**
   * Initialization
   *
   * Automatically searches for elements in
   * the DOM with the data-pin attribute
   */

  if(typeof Document !== 'undefined') {
    document.addEventListener('DOMContentLoaded', Pin.init);
  }

} (window, document));
<|endoftext|>


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

frappe.ui.Notifications = class Notifications {
	constructor() {
		this.tabs = {};
		frappe.model
			.with_doc('Notification Settings', frappe.session.user)
			.then(doc => {
				this.notifications_settings = doc;
				this.make();
			});
	}

	make() {
		this.dropdown = $('.navbar').find('.dropdown-notifications');
		this.dropdown_list = this.dropdown.find('.notifications-list');
		this.header_items = this.dropdown_list.find('.header-items');
		this.header_actions = this.dropdown_list.find('.header-actions');
		this.body = this.dropdown_list.find('.notification-list-body');

		this.notification_indicator = this.dropdown.find(
			'.notifications-indicator'
		);

		this.user = frappe.session.user;

		this.setup_headers();
		let me = this;
		frappe.search.utils.make_function_searchable(
			me.route_to_settings,
			__('Notification Settings'),
		);

		// this.setup_notifications();
		this.setup_dropdown_events();
		// this.bind_events();
	}

	setup_headers() {
		// Add header actions
		$(`
			${frappe.utils.icon('setting-gear')}
		`)
			.on('click', (e) => this.go_to_settings(e))
			.appendTo(this.header_actions);

		$(`
			${frappe.utils.icon('mark-as-read')}
		`)
			.on('click', (e) => this.mark_all_as_read(e))
			.appendTo(this.header_actions);

		this.categories = [
			{ label: __("Notifications"), id: "notifications", view: NotificationsView },
			{ label: __("Today's Events"), id: "todays_events", view: EventsView }
		];

		let get_headers_html = (item) => {
			let active = item.id == "notifications" ? 'active' : ''

			let html = `
${item.label}
`;

			return html;
		};

		let navitem = $(`<ul class="notification-item-tabs nav nav-tabs" role="tablist"></ul>`);
		this.categories = this.categories.map(item => {
			item.dom = $(get_headers_html(item));
			item.dom.on('click', (e) => {
				e.stopImmediatePropagation();
				this.switch_tab(item);
			})
			navitem.append(item.dom);

			return item
		})
		navitem.appendTo(this.header_items);
		this.switch_tab(this.categories[0]);
	}

	switch_tab(item) {
		this.categories.forEach((item) => {
			item.dom.removeClass("active");
		});
		item.dom.addClass("active");
		this.current_tab && this.current_tab.hide();
		if (this.tabs[item.id]) {
			this.tabs[item.id].show()
			this.current_tab = this.tabs[item.id]
		} else {
			this.make_tab_view(item);
		}
	}

	make_tab_view(item) {
		let tabView = new item.view(this.body);
		this.tabs[item.id] = tabView;
		this.current_tab = tabView;
		tabView.show();
	}

	go_to_settings(e) {
		e.stopImmediatePropagation();
		this.dropdown.removeClass('open');
		this.dropdown.trigger('hide.bs.dropdown');
		this.route_to_settings();
	}

	route_to_settings() {
		frappe.set_route(`#Form/Notification Settings/${frappe.session.user}`);
	}

	mark_all_as_read(e) {
		e.stopImmediatePropagation();
		this.dropdown_list.find('.unread').removeClass('unread');
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_all_as_read',
		);
	}

	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------

	change_activity_status() {
		if (this.dropdown_list.find('.activity-status')) {
			this.dropdown_list.find('.activity-status').replaceWith(
				`

${__('View Full Log')}

				`
			);
		}
	}

	set_field_as_read(docname, $el) {
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_as_read',
			{ docname: docname }
		).then(() => {
			$el.removeClass('unread');
		});
	}

	explicitly_mark_as_read(e, $target) {
		e.preventDefault();
		e.stopImmediatePropagation();
		let docname = $target.parents('.unread').attr('data-name');
		this.set_field_as_read(docname, $target.parents('.unread'));
	}

	mark_as_read(e, $target) {
		let docname = $target.attr('data-name');
		let df = this.dropdown_items.filter(f => docname.includes(f.name))[0];
		this.set_field_as_read(df.name, $target);
	}

	toggle_seen(flag) {
		frappe.call(
			'frappe.desk.doctype.notification_settings.notification_settings.set_seen_value',
			{
				value: cint(flag),
				user: frappe.session.user
			}
		);
	}

	setup_dropdown_events() {
		this.dropdown.on('hide.bs.dropdown', e => {
			let hide = $(e.currentTarget).data('closable');
			$(e.currentTarget).data('closable', true);
			return hide;
		});

		this.dropdown.on('show.bs.dropdown', () => {
			this.toggle_seen(true);
			if (this.notification_indicator.is(':visible')) {
				this.notification_indicator.hide();
				frappe.call(
					'frappe.desk.doctype.notification_log.notification_log.trigger_indicator_hide'
				);
			}
		});

		this.dropdown.on('click', e => {
			if ($(e.target).closest('.dropdown-toggle').length) {
				$(e.currentTarget).data('closable', true);
			} else {
				$(e.currentTarget).data('closable', false);
			}
		});
	}
};

frappe.ui.notifications = {
	get_notification_config() {
		return frappe.xcall('frappe.desk.notifications.get_notification_info').then(r => {
			frappe.ui.notifications.config = r;
			return r;
		});
	},

	show_open_count_list(doctype) {
		if (!frappe.ui.notifications.config) {
			this.get_notification_config().then(() => {
				this.route_to_list_with_filters(doctype);
			});
		} else {
			this.route_to_list_with_filters(doctype);
		}
	},

	route_to_list_with_filters(doctype) {
		let filters = frappe.ui.notifications.config['conditions'][doctype];
		if (filters && $.isPlainObject(filters)) {
			if (!frappe.route_options) {
				frappe.route_options = {};
			}
			$.extend(frappe.route_options, filters);
		}
		frappe.set_route('List', doctype);
	}
};

class BaseNotificaitonsView {
	constructor(wrapper) {
		// wrapper, max_length
		this.wrapper = wrapper;
		this.max_length = 20;
		this.container = $(`<div></div>`).appendTo(this.wrapper);
		this.make();
	}

	show() {
		this.container.show()
	}

	hide() {
		this.container.hide()
	}
}

class NotificationsView extends BaseNotificaitonsView {
	make() {
		this.setup_notification_listeners();
		this.get_notifications_list(this.max_length).then(list => {
			this.dropdown_items = list;
			this.render_notifications_dropdown();
		});
	}

	update_dropdown() {
		this.get_notifications_list(1).then(r => {
			let new_item = r[0];
			this.dropdown_items.unshift(new_item);
			if (this.dropdown_items.length > this.max_length) {
				this.dropdown_list
					.find('.recent-notification')
					.last()
					.remove();
				this.dropdown_items.pop();
			}

			this.insert_into_dropdown();
		});
	}

	insert_into_dropdown() {
		let new_item = this.dropdown_items[0];
		let new_item_html = this.get_dropdown_item_html(new_item);
		$(new_item_html).prependTo(this.dropdown_list.find(this.$notifications));
		this.change_activity_status();
	}

	get_dropdown_item_html(field) {
		let doc_link = this.get_item_link(field);

		let read_class = field.read ? '' : 'unread';
		let message = field.subject;

		let title = message.match(/(.*?)<\/b>/);
		message = title ? message.replace(title[1], frappe.ellipsis(title[1], 100)) : message;

		let timestamp = frappe.datetime.comment_when(field.creation);
		let message_html = `

${message}

				${timestamp}

`;

		let user = field.from_user;
		let user_avatar = frappe.avatar(user, 'avatar-medium user-avatar');

		let item_html =
			`
				${user_avatar}
				${message_html}
			`;

		return item_html;
	}

	render_notifications_dropdown() {
		let body_html = '';
		let view_full_log_html = '';
		let dropdown_html;

		if (this.notifications_settings && !this.notifications_settings.enabled) {
			dropdown_html = `

					${__('Notifications Disabled')}

`;
		} else {
			if (this.dropdown_items.length) {
				this.dropdown_items.forEach(field => {
					let item_html = this.get_dropdown_item_html(field);
					if (item_html) body_html += item_html;
				});
				view_full_log_html = `

${__('See all Activity')}

					`;
			} else {
				body_html += `

						${__('No activity')}

`;
			}
			dropdown_html = body_html + view_full_log_html;
		}

		this.container.html(dropdown_html);
	}

	get_notifications_list(limit) {
		return frappe.db.get_list('Notification Log', {
			fields: ['*'],
			limit: limit,
			order_by: 'creation desc'
		});
	}

	get_item_link(notification_doc) {
		const link_doctype =
			notification_doc.type == 'Alert' ? 'Notification Log' : notification_doc.document_type;
		const link_docname =
			notification_doc.type == 'Alert' ? notification_doc.name : notification_doc.document_name;
		return frappe.utils.get_form_link(
			link_doctype,
			link_docname
		);
	}

	setup_notification_listeners() {
		frappe.realtime.on('notification', () => {
			this.dropdown.find('.notifications-indicator').show();
			this.update_dropdown();
		});

		frappe.realtime.on('indicator_hide', () => {
			this.dropdown.find('.notifications-indicator').hide();
		});
	}
}

class EventsView extends BaseNotificaitonsView {
	make() {
		let today = frappe.datetime.get_today();
		frappe.xcall('frappe.desk.doctype.event.event.get_events', {
			start: today,
			end: today
		}).then(event_list => {
			this.render_events_html(event_list);
		});
	}

	render_events_html(event_list) {
		let html = '';
		if (event_list.length) {
			let get_event_html = (event) => {
				let time = __("All Day");
				if (!event.all_day) {
					let start_time = frappe.datetime.get_time(event.starts_on);
					let days_diff = frappe.datetime.get_day_diff(event.ends_on, event.starts_on)
					let end_time = frappe.datetime.get_time(event.ends_on);
					if (days_diff > 1) {
						end_time = __("Rest of the day");
					}
					time = `${start_time} - ${end_time}`;
				}

				// REDESIGN-TODO: Add Participants to get_events query
				let particpants = '';
				if (event.particpants) {
					particpants = frappe.avatar_group(event.particpants, 3)
				}

				// REDESIGN-TODO: Add location to calendar field
				let location = '';
				if (event.location) {
					location = `, ${event.location}`;
				}

				return `

${event.subject}

${time}${location}

						${particpants}

				`;
			};
			html = event_list.map(get_event_html).join('');
		} else {
			html = `

					${__('No Events Today')}

`;
		}

		this.container.html(html);
	}
}<commit_msg>fix: hide dropdown on opening settings<commit_after>frappe.provide('frappe.search');

frappe.ui.Notifications = class Notifications {
	constructor() {
		window.not = this;
		this.tabs = {};
		frappe.model
			.with_doc('Notification Settings', frappe.session.user)
			.then(doc => {
				this.notifications_settings = doc;
				this.make();
			});
	}

	make() {
		this.dropdown = $('.navbar').find('.dropdown-notifications');
		this.dropdown_list = this.dropdown.find('.notifications-list');
		this.header_items = this.dropdown_list.find('.header-items');
		this.header_actions = this.dropdown_list.find('.header-actions');
		this.body = this.dropdown_list.find('.notification-list-body');

		this.notification_indicator = this.dropdown.find(
			'.notifications-indicator'
		);

		this.user = frappe.session.user;

		this.setup_headers();
		let me = this;
		frappe.search.utils.make_function_searchable(
			me.route_to_settings,
			__('Notification Settings'),
		);

		// this.setup_notifications();
		this.setup_dropdown_events();
		// this.bind_events();
	}

	setup_headers() {
		// Add header actions
		$(`
			${frappe.utils.icon('setting-gear')}
		`)
			.on('click', (e) => this.go_to_settings(e))
			.appendTo(this.header_actions);

		$(`
			${frappe.utils.icon('mark-as-read')}
		`)
			.on('click', (e) => this.mark_all_as_read(e))
			.appendTo(this.header_actions);

		this.categories = [
			{ label: __("Notifications"), id: "notifications", view: NotificationsView },
			{ label: __("Today's Events"), id: "todays_events", view: EventsView }
		];

		let get_headers_html = (item) => {
			let active = item.id == "notifications" ? 'active' : ''

			let html = `
${item.label}
`;

			return html;
		};

		let navitem = $(`<ul class="notification-item-tabs nav nav-tabs" role="tablist"></ul>`);
		this.categories = this.categories.map(item => {
			item.dom = $(get_headers_html(item));
			item.dom.on('click', (e) => {
				e.stopImmediatePropagation();
				this.switch_tab(item);
			})
			navitem.append(item.dom);

			return item
		})
		navitem.appendTo(this.header_items);
		this.switch_tab(this.categories[0]);
	}

	switch_tab(item) {
		this.categories.forEach((item) => {
			item.dom.removeClass("active");
		});
		item.dom.addClass("active");
		this.current_tab && this.current_tab.hide();
		if (this.tabs[item.id]) {
			this.tabs[item.id].show()
			this.current_tab = this.tabs[item.id]
		} else {
			this.make_tab_view(item);
		}
	}

	make_tab_view(item) {
		let tabView = new item.view(this.body);
		this.tabs[item.id] = tabView;
		this.current_tab = tabView;
		tabView.show();
	}

	go_to_settings(e) {
		e.stopImmediatePropagation();
		this.dropdown.dropdown('hide');
		this.route_to_settings();
	}

	route_to_settings() {
		frappe.set_route(`#Form/Notification Settings/${frappe.session.user}`);
	}

	mark_all_as_read(e) {
		e.stopImmediatePropagation();
		this.dropdown_list.find('.unread').removeClass('unread');
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_all_as_read',
		);
	}

	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------------------------------------------

	change_activity_status() {
		if (this.dropdown_list.find('.activity-status')) {
			this.dropdown_list.find('.activity-status').replaceWith(
				`

${__('View Full Log')}

				`
			);
		}
	}

	set_field_as_read(docname, $el) {
		frappe.call(
			'frappe.desk.doctype.notification_log.notification_log.mark_as_read',
			{ docname: docname }
		).then(() => {
			$el.removeClass('unread');
		});
	}

	explicitly_mark_as_read(e, $target) {
		e.preventDefault();
		e.stopImmediatePropagation();
		let docname = $target.parents('.unread').attr('data-name');
		this.set_field_as_read(docname, $target.parents('.unread'));
	}

	mark_as_read(e, $target) {
		let docname = $target.attr('data-name');
		let df = this.dropdown_items.filter(f => docname.includes(f.name))[0];
		this.set_field_as_read(df.name, $target);
	}

	toggle_seen(flag) {
		frappe.call(
			'frappe.desk.doctype.notification_settings.notification_settings.set_seen_value',
			{
				value: cint(flag),
				user: frappe.session.user
			}
		);
	}

	setup_dropdown_events() {
		this.dropdown.on('hide.bs.dropdown', e => {
			let hide = $(e.currentTarget).data('closable');
			$(e.currentTarget).data('closable', true);
			return hide;
		});

		this.dropdown.on('show.bs.dropdown', () => {
			this.toggle_seen(true);
			if (this.notification_indicator.is(':visible')) {
				this.notification_indicator.hide();
				frappe.call(
					'frappe.desk.doctype.notification_log.notification_log.trigger_indicator_hide'
				);
			}
		});

		this.dropdown.on('click', e => {
			$(e.currentTarget).data('closable', true);
		});
	}
};

frappe.ui.notifications = {
	get_notification_config() {
		return frappe.xcall('frappe.desk.notifications.get_notification_info').then(r => {
			frappe.ui.notifications.config = r;
			return r;
		});
	},

	show_open_count_list(doctype) {
		if (!frappe.ui.notifications.config) {
			this.get_notification_config().then(() => {
				this.route_to_list_with_filters(doctype);
			});
		} else {
			this.route_to_list_with_filters(doctype);
		}
	},

	route_to_list_with_filters(doctype) {
		let filters = frappe.ui.notifications.config['conditions'][doctype];
		if (filters && $.isPlainObject(filters)) {
			if (!frappe.route_options) {
				frappe.route_options = {};
			}
			$.extend(frappe.route_options, filters);
		}
		frappe.set_route('List', doctype);
	}
};

class BaseNotificaitonsView {
	constructor(wrapper) {
		// wrapper, max_length
		this.wrapper = wrapper;
		this.max_length = 20;
		this.container = $(`<div></div>`).appendTo(this.wrapper);
		this.make();
	}

	show() {
		this.container.show()
	}

	hide() {
		this.container.hide()
	}
}

class NotificationsView extends BaseNotificaitonsView {
	make() {
		this.setup_notification_listeners();
		this.get_notifications_list(this.max_length).then(list => {
			this.dropdown_items = list;
			this.render_notifications_dropdown();
		});
	}

	update_dropdown() {
		this.get_notifications_list(1).then(r => {
			let new_item = r[0];
			this.dropdown_items.unshift(new_item);
			if (this.dropdown_items.length > this.max_length) {
				this.dropdown_list
					.find('.recent-notification')
					.last()
					.remove();
				this.dropdown_items.pop();
			}

			this.insert_into_dropdown();
		});
	}

	insert_into_dropdown() {
		let new_item = this.dropdown_items[0];
		let new_item_html = this.get_dropdown_item_html(new_item);
		$(new_item_html).prependTo(this.dropdown_list.find(this.$notifications));
		this.change_activity_status();
	}

	get_dropdown_item_html(field) {
		let doc_link = this.get_item_link(field);

		let read_class = field.read ? '' : 'unread';
		let message = field.subject;

		let title = message.match(/(.*?)<\/b>/);
		message = title ? message.replace(title[1], frappe.ellipsis(title[1], 100)) : message;

		let timestamp = frappe.datetime.comment_when(field.creation);
		let message_html = `

${message}

				${timestamp}

`;

		let user = field.from_user;
		let user_avatar = frappe.avatar(user, 'avatar-medium user-avatar');

		let item_html =
			`
				${user_avatar}
				${message_html}
			`;

		return item_html;
	}

	render_notifications_dropdown() {
		let body_html = '';
		let view_full_log_html = '';
		let dropdown_html;

		if (this.notifications_settings && !this.notifications_settings.enabled) {
			dropdown_html = `

					${__('Notifications Disabled')}

`;
		} else {
			if (this.dropdown_items.length) {
				this.dropdown_items.forEach(field => {
					let item_html = this.get_dropdown_item_html(field);
					if (item_html) body_html += item_html;
				});
				view_full_log_html = `

${__('See all Activity')}

					`;
			} else {
				body_html += `

						${__('No activity')}

`;
			}
			dropdown_html = body_html + view_full_log_html;
		}

		this.container.html(dropdown_html);
	}

	get_notifications_list(limit) {
		return frappe.db.get_list('Notification Log', {
			fields: ['*'],
			limit: limit,
			order_by: 'creation desc'
		});
	}

	get_item_link(notification_doc) {
		const link_doctype =
			notification_doc.type == 'Alert' ? 'Notification Log' : notification_doc.document_type;
		const link_docname =
			notification_doc.type == 'Alert' ? notification_doc.name : notification_doc.document_name;
		return frappe.utils.get_form_link(
			link_doctype,
			link_docname
		);
	}

	setup_notification_listeners() {
		frappe.realtime.on('notification', () => {
			this.dropdown.find('.notifications-indicator').show();
			this.update_dropdown();
		});

		frappe.realtime.on('indicator_hide', () => {
			this.dropdown.find('.notifications-indicator').hide();
		});
	}
}

class EventsView extends BaseNotificaitonsView {
	make() {
		let today = frappe.datetime.get_today();
		frappe.xcall('frappe.desk.doctype.event.event.get_events', {
			start: today,
			end: today
		}).then(event_list => {
			this.render_events_html(event_list);
		});
	}

	render_events_html(event_list) {
		let html = '';
		if (event_list.length) {
			let get_event_html = (event) => {
				let time = __("All Day");
				if (!event.all_day) {
					let start_time = frappe.datetime.get_time(event.starts_on);
					let days_diff = frappe.datetime.get_day_diff(event.ends_on, event.starts_on)
					let end_time = frappe.datetime.get_time(event.ends_on);
					if (days_diff > 1) {
						end_time = __("Rest of the day");
					}
					time = `${start_time} - ${end_time}`;
				}

				// REDESIGN-TODO: Add Participants to get_events query
				let particpants = '';
				if (event.particpants) {
					particpants = frappe.avatar_group(event.particpants, 3)
				}

				// REDESIGN-TODO: Add location to calendar field
				let location = '';
				if (event.location) {
					location = `, ${event.location}`;
				}

				return `

${event.subject}

${time}${location}

						${particpants}

				`;
			};
			html = event_list.map(get_event_html).join('');
		} else {
			html = `

					${__('No Events Today')}

`;
		}

		this.container.html(html);
	}
}<|endoftext|>


<commit_before>
(function (context, factory) {
    'use strict';
    /* globals define: true, module: true */

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else {
        context.Pin = factory();

        if (context.$ === undefined) {
            context.$ = context.Pin;
        }
    }
})(this, function () {
    'use strict';
    /* jshint laxbreak: true */

    var $,
        pin   = {},
        div   = document.createElement('div'),
        table = document.createElement('table'),
        tbody = document.createElement('tbody'),
        tr    = document.createElement('tr'),
        containers = {
            'thead': table,
            'tbody': table,
            'tfoot': table,
            'tr':    tbody,
            'td':    tr,
            'th':    tr,
            '*':     div
        },
        tagRe = /^\s*<(\w+|!)[^>]*>/;

    /* extended-code */
    var cssStyles = getCssStyles(),
        cssPrefix = getCssPrefix();
    /* end-extended-code */

    pin.init = function (selector, context) {
        var elmts;

        if (!selector) {
            return new pin.Collection();
        }
        else if (selector._pin) {
            return selector;
        }
        else if (selector instanceof Function) {
            return document.addEventListener('DOMContentLoaded', selector);
        }
        else if (selector instanceof Array) {
            elmts = selector;
        }
        else if (typeof selector === 'string') {
            selector = selector.trim();

            if (selector[0] === '<') {
                elmts = pin.fragment(selector);
            }
            else if (context) {
                return $(context).find(selector);
            }
            else {
                elmts = document.querySelectorAll(selector);
            }
        }
        else if (selector instanceof NodeList) {
            elmts = selector;
        }
        else if (typeof selector === 'object') {
            elmts = [ selector ];
        }

        return new pin.Collection(elmts);
    };

    pin.Collection = function (elmts) {
        elmts = [].slice.call(elmts || []);
        elmts._pin = true;

        $.each($.fn, function (i) {
            elmts[i] = $.fn[i];
        });

        return elmts;
    };

    pin.fragment = function (html) {
        var container,
            elmts,
            name = html.match(tagRe)[1];

        if (!containers[name]) {
            name = '*';
        }

        container = containers[name];
        container.innerHTML = html;

        elmts = $(container.childNodes).each(function () {
            container.removeChild(this);
        });

        return elmts;
    };

    $ = function (selector, context) {
        return pin.init(selector, context);
    };

    $.each = function (elmts, callback) {
        var i, k;

        if (typeof elmts.length === 'number') {
            for (i = 0; i < elmts.length; i++) {
                if (callback.call(elmts[i], i, elmts[i]) === false) {
                    return elmts;
                }
            }
        } else {
            for (k in elmts) {
                if (elmts.hasOwnProperty(k)) {
                    if (callback.call(elmts[k], k, elmts[k]) === false) {
                        return elmts;
                    }
                }
            }
        }

        return elmts;
    };

    $.map = function (elmts, callback) {
        var values = [],
            value;

        $(elmts).each(function (i) {
            value = callback.call(this, this, i);

            if (value !== null) {
                values.push(value);
            }
        });

        return values;
    };

    /* extended-code */
    $.uniq = function (elmts) {
        return [].filter.call(elmts, function (elmt, idx) {
            return elmts.indexOf(elmt) === idx;
        });
    };

    $.extend = function (deep) {
        var obj  = {},
            args = [].slice.call(arguments),
            i, k;

        if (typeof deep === 'boolean') {
            args.shift();
        }

        for (i = 0; i < args.length; i++) {
            for (k in args[i]) {
                if (args[i].hasOwnProperty(k)) {
                    if (deep === true && typeof args[i][k] === 'object') {
                        obj[k] = $.extend(deep, obj[k], args[i][k]);
                    } else {
                        obj[k] = args[i][k];
                    }
                }
            }
        }

        return obj;
    };
    /* end-extended-code */

    $.fn = {
        /* extended-code */
        slice: function () {
            return $([].slice.apply(this, arguments));
        },

        parent: function (selector) {
            return this.parents(selector, true);
        },

        closest: function (selector) {
            return $.uniq(this.map(function () {
                if ($(this).is(selector)) {
                    return this;
                }

                return $(this).parent(selector)[0];
            }));
        },

        parents: function (selector, firstOnly){
            var elmts   = this,
                parents = [],
                parent;

            if (typeof selector === 'boolean')  {
                firstOnly = selector;
                selector  = null;
            }

            function findParents (elmts) {
                return $.map(elmts, function () {
                    parent = this.parentNode;

                    if (parent && parent.nodeType !== 9 && parents.indexOf(parent) < 0) {
                        if (!selector || $(parent).is(selector)) {
                            parents.push(parent);
                        }

                        return parent;
                    }

                    return null;
                });
            }

            while (elmts.length > 0) {
                elmts = findParents(elmts);

                if (firstOnly && parents.length) {
                    return $(parents);
                }
            }

            return $(parents);
        },

        eq: function (idx) {
            return this.slice(idx, idx + 1);
        },

        index: function (elmt) {
            if (elmt) {
                return this.indexOf($(elmt)[0]);
            }

            return this.parent.childNodes.indexOf(this[0]);
        },

        has: function (selector) {
            return !!this.find(selector).length;
        },

        is: function (selector) {
            return !!(this.map(function () {
                if (this !== selector && !(
                    this.webkitMatchesSelector
                    || this.mozMatchesSelector
                    || this.msMatchesSelector
                    || this.oMatchesSelector
                    || this.matchesSelector
                ).call(this, selector)) {
                    return null;
                }
            })).length;
        },

        replaceWith: function (html) {
            var elmt = $(html)[0];

            return this.map(function () {
                $(this).before(elmt).remove();

                return elmt;
            });
        },

        wrapWith: function (html) {
            var elmt = $(html)[0],
                last = elmt;

            return this.map(function () {
                $(this).before(elmt);

                while (last.children.length) {
                    last = last.children[0];
                }

                $(last).append(this);

                return elmt;
            });
        },

        remove: function () {
            return this.each(function () {
                if (this.parentNode) {
                    this.parentNode.removeChild(this);
                }
            });
        },

        on: function (name, handler, capture) {
            var evt = getEventInfo(name),
                key = evt.name + '.' + evt.ns,
                handlerList,
                handlerProxy,
                args;

            return this.each(function () {
                handlerProxy = function (e) {
                    args = e._args || [];
                    args.unshift(e);

                    handler.apply(this, args);
                };

                handlerList = this._handlers || {};
                handlerList[key] = handlerList[key] || [];
                handlerList[key].push(handlerProxy);

                this._handlers = handlerList;
                this.addEventListener(evt.name, handlerProxy, capture);
            });
        },

        off: function (name, capture) {
            var evt = getEventInfo(name);

            return this.each(function () {
                var k, i, x,
                    handlers;

                handlers = this._handlers;

                for (k in handlers) {
                    if (handlers.hasOwnProperty(k)) {
                        i = getEventInfo(k);

                        if ((evt.name === '*' || evt.name === i.name ) && (evt.ns === '*' || evt.ns === i.ns)) {

                            for (x = 0; x < handlers[k].length; x++) {
                                this.removeEventListener(i.name, handlers[k][x], capture);
                            }

                            delete handlers[k];
                        }
                    }
                }
            });
        },

        trigger: function (name, args) {
            var evt = document.createEvent('HTMLEvents');

            evt.initEvent(name, true, true);
            evt._args = args;

            return this.each(function () {
                this.dispatchEvent(evt);
            });
        },

        set: function (key, value) {
            return this.each(function (i) {
                var values = key,
                    elmt = this;

                if (typeof key !== 'object') {
                    values = {};
                    values[key] = value;
                }

                $.each(values, function (key, value) {
                    var sign = key[0],
                        shortKey = key.slice(1);

                    if (typeof value === 'function') {
                        value = value.call(elmt, i);
                    }

                    if (sign === '@') {
                        if (value === undefined) {
                            elmt.removeAttribute(shortKey);
                        } else {
                            elmt.setAttribute(shortKey, value);
                        }
                    }

                    if (sign === '.') {
                        if (value === 'toggle') {
                            toggleClass(elmt, shortKey);
                        } else if (value === 'remove') {
                            removeClass(elmt, shortKey);
                        } else {
                            addClass(elmt, shortKey);
                        }
                    }

                    if (sign === '+') {
                        shortKey = camelize(getCssProperty(shortKey));

                        elmt.style[shortKey] = value;
                    } else {
                        elmt[key] = value;
                    }
                });
            });
        },

        get: function (key) {
            var elmt = this[0],
                sign = key[0],
                shortKey = key.slice(1);

            if (sign === '@') {
                return elmt.getAttribute(shortKey);
            }

            if (sign === '.') {
                return hasClass(elmt, shortKey);
            }

            if (sign === '+') {
                shortKey = camelize(getCssProperty(shortKey));

                return elmt.style[shortKey] || getComputedStyle(elmt).getPropertyValue(shortKey);
            }

            return elmt[key];
        },
        /* end-extended-code */

        each: function (callback) {
            return $.each(this, callback);
        },

        map: function (callback) {
            return $($.map(this, callback));
        },

        find: function (selector) {
            var elmts;

            if (!selector) {
                elmts = [];
            } else if (this.length === 1) {
                elmts = this[0].querySelectorAll(selector);
            } else {
                elmts = this.map(function () {
                    return this.querySelectorAll(selector);
                });
            }

            return $(elmts);
        }
    };

    /* extended-code */
    [ 'prepend', 'append', 'before', 'after' ].forEach(function (name, i) {
        $.fn[name] = function (html) {
            var elmt = $(html)[0];

            return this.each(function () {
                if (i === 0) {
                    return this.insertBefore(elmt, this.firstChild);
                }

                if (i === 1) {
                    return this.appendChild(elmt);
                }

                if (i === 2) {
                    return this.parentNode.insertBefore(elmt, this);
                }

                return this.parentNode.insertBefore(elmt, this.nextSibling);
            });
        };
    });

    function getEventInfo (name) {
        var splits = name.split('.');

        return {
            name: splits[0] || '*',
            ns:   splits[1] || '*'
        };
    }

    function getClassRe (className) {
        return new RegExp('(^|\\s+)' + className + '(\\s+|$)');
    }

    function hasClass(elmt, className) {
        return getClassRe(className).test(elmt.className);
    }

    function addClass (elmt, className) {
        if (!hasClass(elmt, className)) {
            elmt.className = elmt.className + ' ' + className;
        }
    }

    function removeClass (elmt, className) {
         elmt.className = elmt.className.replace(getClassRe(className), '');
    }

    function toggleClass (elmt, className) {
        if (hasClass(elmt, className)) {
            removeClass(elmt, className);
        } else {
            addClass(elmt, className);
        }
    }

    function getComputedStyle (elmt) {
        return window.getComputedStyle(elmt, '');
    }

    function getCssStyles () {
        return [].slice.call(getComputedStyle(document.documentElement));
    }

    function getCssPrefix () {
        var prefix = (cssStyles.join('').match(/-(moz|webkit|ms)-/) || (cssStyles.OLink === '' && ['', 'o']))[1];

        return prefix === 'ms' ? prefix : prefix[0].toUpperCase() + prefix.slice(1) ;
    }

    function getCssProperty (property) {
        var prefixed = '';

        if (cssPrefix) {
             prefixed = '-' + cssPrefix + '-' + property;
        }

        if (cssStyles.indexOf(prefixed.toLowerCase()) !== -1) {
            return prefixed;
        }

        return property;
    }

    function camelize (string) {
        return string.replace(/-+(.)?/g, function (x, chr) {
            return chr ? chr.toUpperCase() : '';
        });
    }
    /* end-extended-code */

    return $;
});
<commit_msg>Simplify classname regexp<commit_after>
(function (context, factory) {
    'use strict';
    /* globals define: true, module: true */

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else {
        context.Pin = factory();

        if (context.$ === undefined) {
            context.$ = context.Pin;
        }
    }
})(this, function () {
    'use strict';
    /* jshint laxbreak: true */

    var $,
        pin   = {},
        div   = document.createElement('div'),
        table = document.createElement('table'),
        tbody = document.createElement('tbody'),
        tr    = document.createElement('tr'),
        containers = {
            'thead': table,
            'tbody': table,
            'tfoot': table,
            'tr':    tbody,
            'td':    tr,
            'th':    tr,
            '*':     div
        },
        tagRe = /^\s*<(\w+|!)[^>]*>/;

    /* extended-code */
    var cssStyles = getCssStyles(),
        cssPrefix = getCssPrefix();
    /* end-extended-code */

    pin.init = function (selector, context) {
        var elmts;

        if (!selector) {
            return new pin.Collection();
        }
        else if (selector._pin) {
            return selector;
        }
        else if (selector instanceof Function) {
            return document.addEventListener('DOMContentLoaded', selector);
        }
        else if (selector instanceof Array) {
            elmts = selector;
        }
        else if (typeof selector === 'string') {
            selector = selector.trim();

            if (selector[0] === '<') {
                elmts = pin.fragment(selector);
            }
            else if (context) {
                return $(context).find(selector);
            }
            else {
                elmts = document.querySelectorAll(selector);
            }
        }
        else if (selector instanceof NodeList) {
            elmts = selector;
        }
        else if (typeof selector === 'object') {
            elmts = [ selector ];
        }

        return new pin.Collection(elmts);
    };

    pin.Collection = function (elmts) {
        elmts = [].slice.call(elmts || []);
        elmts._pin = true;

        $.each($.fn, function (i) {
            elmts[i] = $.fn[i];
        });

        return elmts;
    };

    pin.fragment = function (html) {
        var container,
            elmts,
            name = html.match(tagRe)[1];

        if (!containers[name]) {
            name = '*';
        }

        container = containers[name];
        container.innerHTML = html;

        elmts = $(container.childNodes).each(function () {
            container.removeChild(this);
        });

        return elmts;
    };

    $ = function (selector, context) {
        return pin.init(selector, context);
    };

    $.each = function (elmts, callback) {
        var i, k;

        if (typeof elmts.length === 'number') {
            for (i = 0; i < elmts.length; i++) {
                if (callback.call(elmts[i], i, elmts[i]) === false) {
                    return elmts;
                }
            }
        } else {
            for (k in elmts) {
                if (elmts.hasOwnProperty(k)) {
                    if (callback.call(elmts[k], k, elmts[k]) === false) {
                        return elmts;
                    }
                }
            }
        }

        return elmts;
    };

    $.map = function (elmts, callback) {
        var values = [],
            value;

        $(elmts).each(function (i) {
            value = callback.call(this, this, i);

            if (value !== null) {
                values.push(value);
            }
        });

        return values;
    };

    /* extended-code */
    $.uniq = function (elmts) {
        return [].filter.call(elmts, function (elmt, idx) {
            return elmts.indexOf(elmt) === idx;
        });
    };

    $.extend = function (deep) {
        var obj  = {},
            args = [].slice.call(arguments),
            i, k;

        if (typeof deep === 'boolean') {
            args.shift();
        }

        for (i = 0; i < args.length; i++) {
            for (k in args[i]) {
                if (args[i].hasOwnProperty(k)) {
                    if (deep === true && typeof args[i][k] === 'object') {
                        obj[k] = $.extend(deep, obj[k], args[i][k]);
                    } else {
                        obj[k] = args[i][k];
                    }
                }
            }
        }

        return obj;
    };
    /* end-extended-code */

    $.fn = {
        /* extended-code */
        slice: function () {
            return $([].slice.apply(this, arguments));
        },

        parent: function (selector) {
            return this.parents(selector, true);
        },

        closest: function (selector) {
            return $.uniq(this.map(function () {
                if ($(this).is(selector)) {
                    return this;
                }

                return $(this).parent(selector)[0];
            }));
        },

        parents: function (selector, firstOnly){
            var elmts   = this,
                parents = [],
                parent;

            if (typeof selector === 'boolean')  {
                firstOnly = selector;
                selector  = null;
            }

            function findParents (elmts) {
                return $.map(elmts, function () {
                    parent = this.parentNode;

                    if (parent && parent.nodeType !== 9 && parents.indexOf(parent) < 0) {
                        if (!selector || $(parent).is(selector)) {
                            parents.push(parent);
                        }

                        return parent;
                    }

                    return null;
                });
            }

            while (elmts.length > 0) {
                elmts = findParents(elmts);

                if (firstOnly && parents.length) {
                    return $(parents);
                }
            }

            return $(parents);
        },

        eq: function (idx) {
            return this.slice(idx, idx + 1);
        },

        index: function (elmt) {
            if (elmt) {
                return this.indexOf($(elmt)[0]);
            }

            return this.parent.childNodes.indexOf(this[0]);
        },

        has: function (selector) {
            return !!this.find(selector).length;
        },

        is: function (selector) {
            return !!(this.map(function () {
                if (this !== selector && !(
                    this.webkitMatchesSelector
                    || this.mozMatchesSelector
                    || this.msMatchesSelector
                    || this.oMatchesSelector
                    || this.matchesSelector
                ).call(this, selector)) {
                    return null;
                }
            })).length;
        },

        replaceWith: function (html) {
            var elmt = $(html)[0];

            return this.map(function () {
                $(this).before(elmt).remove();

                return elmt;
            });
        },

        wrapWith: function (html) {
            var elmt = $(html)[0],
                last = elmt;

            return this.map(function () {
                $(this).before(elmt);

                while (last.children.length) {
                    last = last.children[0];
                }

                $(last).append(this);

                return elmt;
            });
        },

        remove: function () {
            return this.each(function () {
                if (this.parentNode) {
                    this.parentNode.removeChild(this);
                }
            });
        },

        on: function (name, handler, capture) {
            var evt = getEventInfo(name),
                key = evt.name + '.' + evt.ns,
                handlerList,
                handlerProxy,
                args;

            return this.each(function () {
                handlerProxy = function (e) {
                    args = e._args || [];
                    args.unshift(e);

                    handler.apply(this, args);
                };

                handlerList = this._handlers || {};
                handlerList[key] = handlerList[key] || [];
                handlerList[key].push(handlerProxy);

                this._handlers = handlerList;
                this.addEventListener(evt.name, handlerProxy, capture);
            });
        },

        off: function (name, capture) {
            var evt = getEventInfo(name);

            return this.each(function () {
                var k, i, x,
                    handlers;

                handlers = this._handlers;

                for (k in handlers) {
                    if (handlers.hasOwnProperty(k)) {
                        i = getEventInfo(k);

                        if ((evt.name === '*' || evt.name === i.name ) && (evt.ns === '*' || evt.ns === i.ns)) {

                            for (x = 0; x < handlers[k].length; x++) {
                                this.removeEventListener(i.name, handlers[k][x], capture);
                            }

                            delete handlers[k];
                        }
                    }
                }
            });
        },

        trigger: function (name, args) {
            var evt = document.createEvent('HTMLEvents');

            evt.initEvent(name, true, true);
            evt._args = args;

            return this.each(function () {
                this.dispatchEvent(evt);
            });
        },

        set: function (key, value) {
            return this.each(function (i) {
                var values = key,
                    elmt = this;

                if (typeof key !== 'object') {
                    values = {};
                    values[key] = value;
                }

                $.each(values, function (key, value) {
                    var sign = key[0],
                        shortKey = key.slice(1);

                    if (typeof value === 'function') {
                        value = value.call(elmt, i);
                    }

                    if (sign === '@') {
                        if (value === undefined) {
                            elmt.removeAttribute(shortKey);
                        } else {
                            elmt.setAttribute(shortKey, value);
                        }
                    }

                    if (sign === '.') {
                        if (value === 'toggle') {
                            toggleClass(elmt, shortKey);
                        } else if (value === 'remove') {
                            removeClass(elmt, shortKey);
                        } else {
                            addClass(elmt, shortKey);
                        }
                    }

                    if (sign === '+') {
                        shortKey = camelize(getCssProperty(shortKey));

                        elmt.style[shortKey] = value;
                    } else {
                        elmt[key] = value;
                    }
                });
            });
        },

        get: function (key) {
            var elmt = this[0],
                sign = key[0],
                shortKey = key.slice(1);

            if (sign === '@') {
                return elmt.getAttribute(shortKey);
            }

            if (sign === '.') {
                return hasClass(elmt, shortKey);
            }

            if (sign === '+') {
                shortKey = camelize(getCssProperty(shortKey));

                return elmt.style[shortKey] || getComputedStyle(elmt).getPropertyValue(shortKey);
            }

            return elmt[key];
        },
        /* end-extended-code */

        each: function (callback) {
            return $.each(this, callback);
        },

        map: function (callback) {
            return $($.map(this, callback));
        },

        find: function (selector) {
            var elmts;

            if (!selector) {
                elmts = [];
            } else if (this.length === 1) {
                elmts = this[0].querySelectorAll(selector);
            } else {
                elmts = this.map(function () {
                    return this.querySelectorAll(selector);
                });
            }

            return $(elmts);
        }
    };

    /* extended-code */
    [ 'prepend', 'append', 'before', 'after' ].forEach(function (name, i) {
        $.fn[name] = function (html) {
            var elmt = $(html)[0];

            return this.each(function () {
                if (i === 0) {
                    return this.insertBefore(elmt, this.firstChild);
                }

                if (i === 1) {
                    return this.appendChild(elmt);
                }

                if (i === 2) {
                    return this.parentNode.insertBefore(elmt, this);
                }

                return this.parentNode.insertBefore(elmt, this.nextSibling);
            });
        };
    });

    function getEventInfo (name) {
        var splits = name.split('.');

        return {
            name: splits[0] || '*',
            ns:   splits[1] || '*'
        };
    }

    function getClassRe (className) {
        return new RegExp('\\b' + className + '\\b');
    }

    function hasClass(elmt, className) {
        return getClassRe(className).test(elmt.className);
    }

    function addClass (elmt, className) {
        if (!hasClass(elmt, className)) {
            elmt.className = elmt.className + ' ' + className;
        }
    }

    function removeClass (elmt, className) {
         elmt.className = elmt.className.replace(getClassRe(className), '');
    }

    function toggleClass (elmt, className) {
        if (hasClass(elmt, className)) {
            removeClass(elmt, className);
        } else {
            addClass(elmt, className);
        }
    }

    function getComputedStyle (elmt) {
        return window.getComputedStyle(elmt, '');
    }

    function getCssStyles () {
        return [].slice.call(getComputedStyle(document.documentElement));
    }

    function getCssPrefix () {
        var prefix = (cssStyles.join('').match(/-(moz|webkit|ms)-/) || (cssStyles.OLink === '' && ['', 'o']))[1];

        return prefix === 'ms' ? prefix : prefix[0].toUpperCase() + prefix.slice(1) ;
    }

    function getCssProperty (property) {
        var prefixed = '';

        if (cssPrefix) {
             prefixed = '-' + cssPrefix + '-' + property;
        }

        if (cssStyles.indexOf(prefixed.toLowerCase()) !== -1) {
            return prefixed;
        }

        return property;
    }

    function camelize (string) {
        return string.replace(/-+(.)?/g, function (x, chr) {
            return chr ? chr.toUpperCase() : '';
        });
    }
    /* end-extended-code */

    return $;
});
<|endoftext|>


<commit_before>(function(window, document) {
  'use strict';

  function Pin(container, options) {
    this.el = document.querySelector(container);
    this.parent = this.el.parentNode;

    this.setOptions(options);

    this.init();
  }

  Pin.prototype.setOptions = function(options) {
    this.options = {
      onPin: (options.onPin) ? options.onPin : function() {},
      onUnpin: (options.onUnpin) ? options.onUnpin : function() {},
      onTouchBottom: (options.onTouchBottom) ? options.onTouchBottom : function() {}
    };
  };

  Pin.prototype.init = function() {
    this.bind();
    this.calcPositions();

    // simulates the window scroll, in case the user is already
    // in the middle of the scroll bar when the page loads (default browser action)
    this.onWindowScroll();
  };

  Pin.prototype.destroy = function() {
    this.setCss();
    this.unbind();
  };

  Pin.prototype.calcPositions = function() {
    // if parent does not have a position
    // sets a position relative, that's because
    // the pinned element gets position absolute
    if(getStyle(this.parent, 'position') === 'static') {
      this.parent.style.position = 'relative';
    }

    this.positions = {
      offset: this.getOffset(this.el),
      parentOffset: this.getParentOffset(),
      stopTop: (this.parent.offsetHeight + this.getOffset(this.parent).top) - this.el.offsetHeight
    };
  };

  Pin.prototype.getOffset = function(element) {
    var de = document.documentElement;
    var box = element.getBoundingClientRect();

    return {
      top: box.top + window.pageYOffset - de.clientTop,
      left: box.left + window.pageXOffset - de.clientLeft
    };
  };

  Pin.prototype.getParentOffset = function() {
    return {
      left: this.el.offsetLeft
    };
  };

  Pin.prototype.bind = function() {
    // need to attach to a seperate variable, since bind
    // creates another wraped function, and I can't unbind
    // it in the destroy method.
    this.reloadBind = this.reload.bind(this);
    this.onWindowScrollBind = this.onWindowScroll.bind(this);

    window.addEventListener('resize', this.reloadBind);
    window.addEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.unbind = function() {
    window.removeEventListener('resize', this.reloadBind);
    window.removeEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.reload = function() {
    this.setCss();
    this.calcPositions();
    this.onWindowScroll();
  };

  /**
  * Lots of things happens on this method
  * It checks the window size, the position of the scroll
  * If the container is touching the bottom and other
  * stuff. That's why it looks like a mess
  * :|
  **/
  Pin.prototype.onWindowScroll = function() {
    var newTop;

    // if the window is smaller then it won't stick
    if(windowIsSmaller(this.el)) return;

    // if the window got to the bottom of the parent element
    // of the container element, it stops the element
    if(this.touchBottom()) return;
    newTop = window.pageYOffset - this.positions.offset.top;

    // some checks to stop unecessary code repetition
    if(newTop > 0 && getStyle(this.el, 'position') === 'fixed') return;
    if(newTop <= 0 && (getStyle(this.el, 'position') === 'relative' || getStyle(this.el, 'position') === 'static')) return;

    if(newTop > 0) {
      this.setCss({
        position: 'fixed',
        // adds the left and top property, minus the margins,
        // so the element sticks in the same position it was before
        left: toPx(this.positions.offset.left),
        top: 0,
        marginLeft: 0,
        marginTop: 0,
        bottom: ''
      });

      this.options.onPin(this);

      return;
    }

    this.setCss();
    this.options.onUnpin(this);
  };

  Pin.prototype.touchBottom = function() {
    // if the scroll passed the end of the parent
    if(window.pageYOffset > this.positions.stopTop) {
      if(getStyle(this.el, 'position') === 'absolute') return true;

      this.setCss({
        top: '',
        marginLeft: '',
        bottom: 0,
        position: 'absolute',
        left: toPx(this.positions.parentOffset.left)
      });

      this.options.onTouchBottom(this);

      return true;
    }

    return false;
  };

  Pin.prototype.setCss = function(properties) {
    if(!properties) {
      this.el.removeAttribute('style');
      return;
    }

    for(var property in properties) {
      this.el.style[property] = properties[property];
    }
  };

  /**
   * Private functions
   *
   * The functions below are not supposed to be used by the dev
   */

  function getStyle(el, property) {
    return window.getComputedStyle(el)[property];
  }

  function toPx(n) {
    return n + 'px';
  }

  function isMobile() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  }

  function windowIsSmaller(el) {
    return window.innerHeight < el.offsetHeight;
  }

  window.Pin = Pin;

} (window, document));
<commit_msg>update getStyle function, now it returns all styles if the property is not defined<commit_after>(function(window, document) {
  'use strict';

  function Pin(container, options) {
    this.el = document.querySelector(container);
    this.parent = this.el.parentNode;

    this.setOptions(options);

    this.init();
  }

  Pin.prototype.setOptions = function(options) {
    this.options = {
      onPin: (options.onPin) ? options.onPin : function() {},
      onUnpin: (options.onUnpin) ? options.onUnpin : function() {},
      onTouchBottom: (options.onTouchBottom) ? options.onTouchBottom : function() {}
    };
  };

  Pin.prototype.init = function() {
    this.createHelperElement();
    this.bind();
    this.calcPositions();

    // simulates the window scroll, in case the user is already
    // in the middle of the scroll bar when the page loads (default browser action)
    this.onWindowScroll();
  };

  Pin.prototype.createHelperElement = function() {
    debugger;
  };

  Pin.prototype.destroy = function() {
    this.setCss();
    this.unbind();
  };

  Pin.prototype.calcPositions = function() {
    // if parent does not have a position
    // sets a position relative, that's because
    // the pinned element gets position absolute
    if(getStyle(this.parent, 'position') === 'static') {
      this.parent.style.position = 'relative';
    }

    this.positions = {
      offset: this.getOffset(this.el),
      parentOffset: this.getParentOffset(),
      stopTop: (this.parent.offsetHeight + this.getOffset(this.parent).top) - this.el.offsetHeight
    };
  };

  Pin.prototype.getOffset = function(element) {
    var de = document.documentElement;
    var box = element.getBoundingClientRect();

    return {
      top: box.top + window.pageYOffset - de.clientTop,
      left: box.left + window.pageXOffset - de.clientLeft
    };
  };

  Pin.prototype.getParentOffset = function() {
    return {
      left: this.el.offsetLeft
    };
  };

  Pin.prototype.bind = function() {
    // need to attach to a seperate variable, since bind
    // creates another wraped function, and I can't unbind
    // it in the destroy method.
    this.reloadBind = this.reload.bind(this);
    this.onWindowScrollBind = this.onWindowScroll.bind(this);

    window.addEventListener('resize', this.reloadBind);
    window.addEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.unbind = function() {
    window.removeEventListener('resize', this.reloadBind);
    window.removeEventListener('scroll', this.onWindowScrollBind);
  };

  Pin.prototype.reload = function() {
    this.setCss();
    this.calcPositions();
    this.onWindowScroll();
  };

  /**
  * Lots of things happens on this method
  * It checks the window size, the position of the scroll
  * If the container is touching the bottom and other
  * stuff. That's why it looks like a mess
  * :|
  **/
  Pin.prototype.onWindowScroll = function() {
    var newTop;

    // if the window is smaller then it won't stick
    if(windowIsSmaller(this.el)) return;

    // if the window got to the bottom of the parent element
    // of the container element, it stops the element
    if(this.touchBottom()) return;
    newTop = window.pageYOffset - this.positions.offset.top;

    // some checks to stop unecessary code repetition
    if(newTop > 0 && getStyle(this.el, 'position') === 'fixed') return;
    if(newTop <= 0 && (getStyle(this.el, 'position') === 'relative' || getStyle(this.el, 'position') === 'static')) return;

    if(newTop > 0) {
      this.setCss({
        position: 'fixed',
        // adds the left and top property, minus the margins,
        // so the element sticks in the same position it was before
        left: toPx(this.positions.offset.left),
        top: 0,
        marginLeft: 0,
        marginTop: 0,
        bottom: ''
      });

      this.options.onPin(this);

      return;
    }

    this.setCss();
    this.options.onUnpin(this);
  };

  Pin.prototype.touchBottom = function() {
    // if the scroll passed the end of the parent
    if(window.pageYOffset > this.positions.stopTop) {
      if(getStyle(this.el, 'position') === 'absolute') return true;

      this.setCss({
        top: '',
        marginLeft: '',
        bottom: 0,
        position: 'absolute',
        left: toPx(this.positions.parentOffset.left)
      });

      this.options.onTouchBottom(this);

      return true;
    }

    return false;
  };

  Pin.prototype.setCss = function(properties) {
    if(!properties) {
      this.el.removeAttribute('style');
      return;
    }

    for(var property in properties) {
      this.el.style[property] = properties[property];
    }
  };

  /**
   * Private functions
   *
   * The functions below are not supposed to be used by the dev
   */

  function getStyle(el, property) {
    return (property) ? window.getComputedStyle(el)[property] : window.getComputedStyle(el);
  }

  function toPx(n) {
    return n + 'px';
  }

  function isMobile() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  }

  function windowIsSmaller(el) {
    return window.innerHeight < el.offsetHeight;
  }

  window.Pin = Pin;

} (window, document));
<|endoftext|>