import Page from './Page';

class MainContent extends Page {

	get mainContent() { return browser.element('.main-content'); }

	// Main Content Header (Channel Title Area)
	get emptyFavoriteStar() { return browser.element('.rc-header__toggle-favorite.empty'); }
	get favoriteStar() { return browser.element('.rc-header__toggle-favorite.favorite-room'); }
	get channelTitle() { return browser.element('.rc-header__name'); }

	// Main Content Footer (Message Input Area)
	get messageInput() { return browser.element('.js-input-message'); }
	get sendBtn() { return browser.element('.rc-message-box__icon.js-send'); }
	get messageBoxActions() { return browser.element('.rc-message-box__icon'); }
	get recordBtn() { return browser.element('.message-buttons .icon-mic'); }
	get videoCamBtn() { return browser.element('.message-buttons .icon-videocam'); }
	get emojiBtn() { return browser.element('.rc-message-box__icon.emoji-picker-icon'); }
	get messagePopUp() { return browser.element('.message-popup'); }
	get messagePopUpTitle() { return browser.element('.message-popup-title'); }
	get messagePopUpItems() { return browser.element('.message-popup-items'); }
	get messagePopUpFirstItem() { return browser.element('.popup-item.selected'); }
	get mentionAllPopUp() { return browser.element('.popup-item[data-id="all"]'); }
	get joinChannelBtn() { return browser.element('.button.join'); }

	// Messages
	get lastMessageUser() { return browser.element('.message:last-child .user-card-message:nth-of-type(2)'); }
	get lastMessage() { return browser.element('.message:last-child .body'); }
	get lastMessageDesc() { return browser.element('.message:last-child .body .attachment-description'); }
	get lastMessageRoleAdded() { return browser.element('.message:last-child.subscription-role-added .body'); }
	get beforeLastMessage() { return browser.element('.message:nth-last-child(2) .body'); }
	get lastMessageUserTag() { return browser.element('.message:last-child .role-tag'); }
	get lastMessageImg() { return browser.element('.message:last-child .attachment-image img'); }
	get lastMessageTextAttachment() { return browser.element('.message:last-child .attachment-text'); }
	get messageOptionsBtn() { return browser.element('.message:last-child .message-actions__menu'); }
	get messageActionMenu() { return browser.element('.rc-popover .rc-popover__content'); }
	get messageReply() { return browser.element('[data-id="reply-message"][data-type="message-action"]'); }
	get messageEdit() { return browser.element('[data-id="edit-message"][data-type="message-action"]'); }
	get messageDelete() { return browser.element('[data-id="delete-message"][data-type="message-action"]'); }
	get messagePermalink() { return browser.element('[data-id="permalink"][data-type="message-action"]'); }
	get messageCopy() { return browser.element('[data-id="copy"][data-type="message-action"]'); }
	get messageQuote() { return browser.element('[data-id="quote-message"][data-type="message-action"]'); }
	get messageStar() { return browser.element('[data-id="star-message"][data-type="message-action"]'); }
	get messageUnread() { return browser.element('[data-id="mark-message-as-unread"][data-type="message-action"]'); }
	// get messageReaction() { return browser.element('.message-actions__button[data-message-action="reaction-message"]'); }
	get messagePin() { return browser.element('[data-id="pin-message"][data-type="message-action"]'); }
	// get messageClose() { return browser.element('[data-id="rc-popover-close"][data-type="message-action"]'); }

	// Emojis
	get emojiPickerMainScreen() { return browser.element('.emoji-picker'); }
	get emojiPickerPeopleIcon() { return browser.element('.emoji-picker .icon-people'); }
	get emojiPickerNatureIcon() { return browser.element('.emoji-picker .icon-nature'); }
	get emojiPickerFoodIcon() { return browser.element('.emoji-picker .icon-food'); }
	get emojiPickerActivityIcon() { return browser.element('.emoji-picker .icon-activity'); }
	get emojiPickerTravelIcon() { return browser.element('.emoji-picker .icon-travel'); }
	get emojiPickerObjectsIcon() { return browser.element('.emoji-picker .icon-objects'); }
	get emojiPickerSymbolsIcon() { return browser.element('.emoji-picker .icon-symbols'); }
	get emojiPickerFlagsIcon() { return browser.element('.emoji-picker .icon-flags'); }
	get emojiPickerModifierIcon() { return browser.element('.emoji-picker .icon-symbols'); }
	get emojiPickerChangeTone() { return browser.element('.emoji-picker .change-tone'); }
	get emojiPickerCustomIcon() { return browser.element('.emoji-picker .icon-rocket'); }
	get emojiPickerRecentIcon() { return browser.element('.emoji-picker .icon-recent'); }
	get emojiPickerFilter() { return browser.element('.emoji-picker .js-emojipicker-search'); }
	get emojiPickerEmojiContainer() { return browser.element('.emoji-picker .emojis'); }
	get emojiGrinning() { return browser.element('.emoji-picker .emoji-grinning'); }
	get emojiSmile() { return browser.element('.emoji-picker .emoji-smile'); }

	// Popover
	get popoverWrapper() { return browser.element('.rc-popover'); }

	// Sends a message and wait for the message to equal the text sent
	sendMessage(text) {
		this.setTextToInput(text);
		this.sendBtn.click();
		browser.waitUntil(function() {
			browser.waitForVisible('.message:last-child .body', 5000);
			return browser.getText('.message:last-child .body') === text;
		}, 5000);
	}

	// adds text to the input
	addTextToInput(text) {
		this.messageInput.waitForVisible(5000);
		this.messageInput.addValue(text);
	}

	// Clear and sets the text to the input
	setTextToInput(text) {
		this.messageInput.waitForVisible(5000);
		this.messageInput.clearElement();
		this.messageInput.addValue(text);
	}

	// uploads a file in the given filepath (url).
	fileUpload(filePath) {
		this.sendMessage('Prepare for the file');
		this.fileAttachment.chooseFile(filePath);
	}

	waitForLastMessageEqualsText(text) {
		browser.waitUntil(function() {
			browser.waitForVisible('.message:last-child .body', 5000);
			return browser.getText('.message:last-child .body') === text;
		}, 5000);
	}

	waitForLastMessageTextAttachmentEqualsText(text) {
		browser.waitUntil(function() {
			browser.waitForVisible('.message:last-child .attachment-text', 5000);
			return browser.getText('.message:last-child .attachment-text') === text;
		}, 5000);
	}

	// Wait for the last message author username to equal the provided text
	waitForLastMessageUserEqualsText(text) {
		browser.waitUntil(function() {
			browser.waitForVisible('.message:last-child .user-card-message:nth-of-type(2)', 5000);
			return browser.getText('.message:last-child .user-card-message:nth-of-type(2)') === text;
		}, 5000);
	}

	openMessageActionMenu() {
		this.lastMessage.moveToObject();
		this.messageOptionsBtn.waitForVisible(5000);
		this.messageOptionsBtn.click();
		this.messageActionMenu.waitForVisible(5000);
		browser.pause(100);
	}

	setLanguageToEnglish() {
		this.settingLanguageSelect.click();
		this.settingLanguageEnglish.click();
		this.settingSaveBtn.click();
	}

	tryToMentionAll() {
		this.addTextToInput('@all');
		this.sendBtn.click();
		this.waitForLastMessageEqualsText('Notify all in this room is not allowed');
	}

	// Do one of the message actions, based on the "action" parameter inserted.
	selectAction(action) {
		switch (action) {
			case 'edit':
				this.messageEdit.waitForVisible(5000);
				this.messageEdit.click();
				this.messageInput.addValue('this message was edited');
				break;
			case 'reply':
				this.messageReply.waitForVisible(5000);
				this.messageReply.click();
				this.messageInput.addValue(' this is a reply message');
				break;
			case 'delete':
				this.messageDelete.waitForVisible(5000);
				this.messageDelete.click();
				break;
			case 'permalink':
				this.messagePermalink.waitForVisible(5000);
				this.messagePermalink.click();
				break;
			case 'copy':
				this.messageCopy.waitForVisible(5000);
				this.messageCopy.click();
				break;
			case 'quote':
				this.messageQuote.waitForVisible(5000);
				this.messageQuote.click();
				this.messageInput.addValue(' this is a quote message');
				break;
			case 'star':
				this.messageStar.waitForVisible(5000);
				this.messageStar.click();
				break;
			case 'unread':
				this.messageUnread.waitForVisible(5000);
				this.messageUnread.click();
				break;
			case 'reaction':
				this.messageReply.waitForVisible(5000);
				this.messageReply.click();
				this.emojiPickerMainScreen.waitForVisible(5000);
				this.emojiPickerPeopleIcon.click();
				this.emojiGrinning.waitForVisible(5000);
				this.emojiGrinning.click();
				break;
			case 'close':
				this.messageClose.waitForVisible(5000);
				this.messageClose.click();
				break;
		}
	}
}

module.exports = new MainContent();


image_url: attachment.imageUrl,
audio_url: attachment.audioUrl,

image_type: attachment.imageType,
image_size: attachment.imageSize,

RocketChat.addUserToDefaultChannels = function(user, silenced) {
	RocketChat.callbacks.run('beforeJoinDefaultChannels', user);
	const defaultRooms = RocketChat.models.Rooms.findByDefaultAndTypes(true, ['c', 'p'], { fields: { usernames: 0 } }).fetch();
	defaultRooms.forEach((room) => {

		// put user in default rooms
		const muted = room.ro && !RocketChat.authz.hasPermission(user._id, 'post-readonly');
		if (muted) {
			RocketChat.models.Rooms.muteUsernameByRoomId(room._id, user.username);
		}

		if (!RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(room._id, user._id)) {

			// Add a subscription to this user
			RocketChat.models.Subscriptions.createWithRoomAndUser(room, user, {
				ts: new Date(),
				open: true,
				alert: true,
				unread: 1,
				userMentions: 1,
				groupMentions: 0,
			});

			// Insert user joined message
			if (!silenced) {
				RocketChat.models.Messages.createUserJoinWithRoomIdAndUser(room._id, user);
			}
		}
	});
};


import Notifications from './lib/Notifications';

export {
	Notifications,
};


audio_type: attachment.audioType,
audio_size: attachment.audioSize,

import { Random } from 'meteor/random';

export class AppMessagesConverter {
	constructor(orch) {
		this.orch = orch;
	}

	convertById(msgId) {
		const msg = RocketChat.models.Messages.getOneById(msgId);

		return this.convertMessage(msg);
	}

	convertMessage(msgObj) {
		if (!msgObj) {
			return undefined;
		}

		const room = this.orch.getConverters().get('rooms').convertById(msgObj.rid);

		let sender;
		if (msgObj.u && msgObj.u._id) {
			sender = this.orch.getConverters().get('users').convertById(msgObj.u._id);

			if (!sender) {
				sender = this.orch.getConverters().get('users').convertToApp(msgObj.u);
			}
		}

		let editor;
		if (msgObj.editedBy) {
			editor = this.orch.getConverters().get('users').convertById(msgObj.editedBy._id);
		}

		const attachments = this._convertAttachmentsToApp(msgObj.attachments);

		return {
			id: msgObj._id,
			room,
			sender,
			text: msgObj.msg,
			createdAt: msgObj.ts,
			updatedAt: msgObj._updatedAt,
			editor,
			editedAt: msgObj.editedAt,
			emoji: msgObj.emoji,
			avatarUrl: msgObj.avatar,
			alias: msgObj.alias,
			customFields: msgObj.customFields,
			groupable: msgObj.groupable,
			attachments,
			reactions: msgObj.reactions,
			parseUrls: msgObj.parseUrls,
		};
	}

	convertAppMessage(message) {
		if (!message) {
			return undefined;
		}

		const room = RocketChat.models.Rooms.findOneById(message.room.id);

		if (!room) {
			throw new Error('Invalid room provided on the message.');
		}

		let u;
		if (message.sender && message.sender.id) {
			const user = RocketChat.models.Users.findOneById(message.sender.id);

			if (user) {
				u = {
					_id: user._id,
					username: user.username,
					name: user.name,
				};
			} else {
				u = {
					_id: message.sender.id,
					username: message.sender.username,
					name: message.sender.name,
				};
			}
		}

		let editedBy;
		if (message.editor) {
			const editor = RocketChat.models.Users.findOneById(message.editor.id);
			editedBy = {
				_id: editor._id,
				username: editor.username,
			};
		}

		const attachments = this._convertAppAttachments(message.attachments);

		return {
			_id: message.id || Random.id(),
			rid: room._id,
			u,
			msg: message.text,
			ts: message.createdAt || new Date(),
			_updatedAt: message.updatedAt || new Date(),
			editedBy,
			editedAt: message.editedAt,
			emoji: message.emoji,
			avatar: message.avatarUrl,
			alias: message.alias,
			customFields: message.customFields,
			groupable: message.groupable,
			attachments,
			reactions: message.reactions,
			parseUrls: message.parseUrls,
		};
	}

	_convertAppAttachments(attachments) {
		if (typeof attachments === 'undefined' || !Array.isArray(attachments)) {
			return undefined;
		}

		return attachments.map((attachment) => ({
			collapsed: attachment.collapsed,
			color: attachment.color,
			text: attachment.text,
			ts: attachment.timestamp ? attachment.timestamp.toJSON() : attachment.timestamp,
			message_link: attachment.timestampLink,
			thumb_url: attachment.thumbnailUrl,
			author_name: attachment.author ? attachment.author.name : undefined,
			author_link: attachment.author ? attachment.author.link : undefined,
			author_icon: attachment.author ? attachment.author.icon : undefined,
			title: attachment.title ? attachment.title.value : undefined,
			title_link: attachment.title ? attachment.title.link : undefined,
			title_link_download: attachment.title ? attachment.title.displayDownloadLink : undefined,
			image_url: attachment.imageUrl,
			audio_url: attachment.audioUrl,
			video_url: attachment.videoUrl,
			fields: attachment.fields,
			button_alignment: attachment.actionButtonsAlignment,
			actions: attachment.actions,
			type: attachment.type,
			description: attachment.description,
		})).map((a) => {
			Object.keys(a).forEach((k) => {
				if (typeof a[k] === 'undefined') {
					delete a[k];
				}
			});

			return a;
		});
	}

	_convertAttachmentsToApp(attachments) {
		if (typeof attachments === 'undefined' || !Array.isArray(attachments)) {
			return undefined;
		}

		return attachments.map((attachment) => {
			let author;
			if (attachment.author_name || attachment.author_link || attachment.author_icon) {
				author = {
					name: attachment.author_name,
					link: attachment.author_link,
					icon: attachment.author_icon,
				};
			}

			let title;
			if (attachment.title || attachment.title_link || attachment.title_link_download) {
				title = {
					value: attachment.title,
					link: attachment.title_link,
					displayDownloadLink: attachment.title_link_download,
				};
			}

			return {
				collapsed: attachment.collapsed,
				color: attachment.color,
				text: attachment.text,
				timestamp: new Date(attachment.ts),
				timestampLink: attachment.message_link,
				thumbnailUrl: attachment.thumb_url,
				author,
				title,
				imageUrl: attachment.image_url,
				audioUrl: attachment.audio_url,
				videoUrl: attachment.video_url,
				fields: attachment.fields,
				actionButtonsAlignment: attachment.button_alignment,
				actions: attachment.actions,
				type: attachment.type,
				description: attachment.description,
			};
		});
	}
}


Package.describe({
	name: 'rocketchat:ui',
	version: '0.1.0',
	// Brief, one-line summary of the package.
	summary: '',
	// URL to the Git repository containing the source code for this package.
	git: '',
	// By default, Meteor will default to using README.md for documentation.
	// To avoid submitting documentation, set this field to null.
	documentation: 'README.md',
});

Package.onUse(function(api) {
	api.use([
		'accounts-base',
		'mongo',
		'session',
		'jquery',
		'tracker',
		'reactive-var',
		'ecmascript',
		'templating',
		'kadira:flow-router',
		'kadira:blaze-layout',
		'rocketchat:ui-master',
		'rocketchat:push',
		'rocketchat:utils',
		'rocketchat:emoji',
		'rocketchat:notifications',
		'rocketchat:ui-utils',
		'rocketchat:models',
		'raix:ui-dropped-event',
		'rocketchat:lazy-load',
		'rocketchat:e2e',
		'mizzao:autocomplete',
		'rocketchat:file-upload',
		'konecty:user-presence',
		'rocketchat:webrtc',
	]);
	api.mainModule('client/index.js', 'client');
	api.mainModule('server/index.js', 'server');
});


import { metrics } from './lib/metrics';
import StatsTracker from './lib/statsTracker';

import './callbacksMetrics';

export {
	metrics,
	StatsTracker,
};


import { Meteor } from 'meteor/meteor';
import { roomFiles } from '../lib/roomFiles';

Meteor.publish('roomFilesWithSearchText', function(rid, searchText, limit = 50) {
	return roomFiles(this, { rid, searchText, limit });
});


video_type: attachment.videoType,
video_size: attachment.videoSize,


Config:

Sending Message
* Send slack msg to rocket
* Send rocket msg to slack
* Repeat on a channel that rocketbot isn't a member

Edit Message:
* Edit slack msg from slack
* Edit rocket msg from rocket
* Edit slack msg from rocket - Expected fail, slack not updated (slack says can't update message)
* Edit rocket msg from slack - Expected fail, can't even edit
* Repeat on a channel that rocketbot isn't a member

Delete Message:
* Delete slack message in slack
* Delete rocket message in rocket
* Delete rocket message in slack
* Delete slack message in rocket - Expected fail, slack not updated, deleted in rocket
* Repeat on a channel that rocketbot isn't a member

React to a Message:
* React to a slack message in slack
* React to a rocket message in slack
* React to a rocket message in rocket
* React to a slack message in rocket
* Repeat on a channel that rocketbot isn't a member

Channels:
Channel exists on both, but no rocketbot.
* Add rocket bot to slack, send messages from both

Channel exists on rocket only
* Create slack channel, add rocket bot:
** Send message from rocket
** Send message from slack

Channel exists on slack only
* Add rocket bot to slack:
** Send message from slack
** Send message from rocket

Channel exists on both w/ rocketbot
* Remove rocket channel, send messages from slack (same as ch exists on slack only)
* Remove rocketbot from slack:
** Send message from slack
** Send message from rocket

Reactions:
* Disabled (default)
** Try to react to Slack and Rocket messages
* Enable it
** Try to react to Slack and Rocket messages


//! moment.js locale configuration
//! locale : Chuvash [cv]
//! author : Anatoly Mironov : https://github.com/mirontoli

;(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined'
       && typeof require === 'function' ? factory(require('../moment')) :
   typeof define === 'function' && define.amd ? define(['../moment'], factory) :
   factory(global.moment)
}(this, function (moment) { 'use strict';

    var cv = moment.defineLocale('cv', {
        months : 'кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав'.split('_'),
        monthsShort : 'кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш'.split('_'),
        weekdays : 'вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун'.split('_'),
        weekdaysShort : 'выр_тун_ытл_юн_кӗҫ_эрн_шӑм'.split('_'),
        weekdaysMin : 'вр_тн_ыт_юн_кҫ_эр_шм'.split('_'),
        longDateFormat : {
            LT : 'HH:mm',
            LTS : 'HH:mm:ss',
            L : 'DD-MM-YYYY',
            LL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]',
            LLL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm',
            LLLL : 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm'
        },
        calendar : {
            sameDay: '[Паян] LT [сехетре]',
            nextDay: '[Ыран] LT [сехетре]',
            lastDay: '[Ӗнер] LT [сехетре]',
            nextWeek: '[Ҫитес] dddd LT [сехетре]',
            lastWeek: '[Иртнӗ] dddd LT [сехетре]',
            sameElse: 'L'
        },
        relativeTime : {
            future : function (output) {
                var affix = /сехет$/i.exec(output) ? 'рен' : /ҫул$/i.exec(output) ? 'тан' : 'ран';
                return output + affix;
            },
            past : '%s каялла',
            s : 'пӗр-ик ҫеккунт',
            m : 'пӗр минут',
            mm : '%d минут',
            h : 'пӗр сехет',
            hh : '%d сехет',
            d : 'пӗр кун',
            dd : '%d кун',
            M : 'пӗр уйӑх',
            MM : '%d уйӑх',
            y : 'пӗр ҫул',
            yy : '%d ҫул'
        },
        ordinalParse: /\d{1,2}-мӗш/,
        ordinal : '%d-мӗш',
        week : {
            dow : 1, // Monday is the first day of the week.
            doy : 7  // The week that contains Jan 1st is the first week of the year.
        }
    });

    return cv;

}));


import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import OmniChannel from '../lib/OmniChannel';

Meteor.methods({
	'livechat:facebook'(options) {
		if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'view-livechat-manager')) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:addAgent' });
		}

		try {
			switch (options.action) {
				case 'initialState': {
					return {
						enabled: RocketChat.settings.get('Livechat_Facebook_Enabled'),
						hasToken: !!RocketChat.settings.get('Livechat_Facebook_API_Key'),
					};
				}

				case 'enable': {
					const result = OmniChannel.enable();

					if (!result.success) {
						return result;
					}

					return RocketChat.settings.updateById('Livechat_Facebook_Enabled', true);
				}

				case 'disable': {
					OmniChannel.disable();

					return RocketChat.settings.updateById('Livechat_Facebook_Enabled', false);
				}

				case 'list-pages': {
					return OmniChannel.listPages();
				}

				case 'subscribe': {
					return OmniChannel.subscribe(options.page);
				}

				case 'unsubscribe': {
					return OmniChannel.unsubscribe(options.page);
				}
			}
		} catch (e) {
			if (e.response && e.response.data && e.response.data.error) {
				if (e.response.data.error.error) {
					throw new Meteor.Error(e.response.data.error.error, e.response.data.error.message);
				}
				if (e.response.data.error.response) {
					throw new Meteor.Error('integration-error', e.response.data.error.response.error.message);
				}
				if (e.response.data.error.message) {
					throw new Meteor.Error('integration-error', e.response.data.error.message);
				}
			}
			console.error('Error contacting omni.rocket.chat:', e);
			throw new Meteor.Error('integration-error', e.error);
		}
	},
});


import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';

const objectMaybeIncluding = (types) => Match.Where((value) => {
	Object.keys(types).forEach((field) => {
		if (value[field] != null) {
			try {
				check(value[field], types[field]);
			} catch (error) {
				error.path = field;
				throw error;
			}
		}
	});

	return true;
});

const validateAttachmentsFields = (attachmentField) => {
	check(attachmentField, objectMaybeIncluding({
		short: Boolean,
		title: String,
		value: Match.OneOf(String, Match.Integer, Boolean),
	}));

	if (typeof attachmentField.value !== 'undefined') {
		attachmentField.value = String(attachmentField.value);
	}

};

const validateAttachmentsActions = (attachmentActions) => {
	check(attachmentActions, objectMaybeIncluding({
		type: String,
		text: String,
		url: String,
		image_url: String,
		is_webview: Boolean,
		webview_height_ratio: String,
		msg: String,
		msg_in_chat_window: Boolean,
	}));
};

const validateAttachment = (attachment) => {
	check(attachment, objectMaybeIncluding({
		color: String,
		text: String,
		ts: Match.OneOf(String, Match.Integer),
		thumb_url: String,
		button_alignment: String,
		actions: [Match.Any],
		message_link: String,
		collapsed: Boolean,
		author_name: String,
		author_link: String,
		author_icon: String,
		title: String,
		title_link: String,
		title_link_download: Boolean,
		image_url: String,
		audio_url: String,
		video_url: String,
		fields: [Match.Any],
	}));

	if (attachment.fields && attachment.fields.length) {
		attachment.fields.map(validateAttachmentsFields);
	}

	if (attachment.actions && attachment.actions.length) {
		attachment.actions.map(validateAttachmentsActions);
	}
};

const validateBodyAttachments = (attachments) => attachments.map(validateAttachment);

RocketChat.sendMessage = function(user, message, room, upsert = false) {
	if (!user || !message || !room._id) {
		return false;
	}

	check(message, objectMaybeIncluding({
		_id: String,
		msg: String,
		text: String,
		alias: String,
		emoji: String,
		avatar: String,
		attachments: [Match.Any],
	}));

	if (Array.isArray(message.attachments) && message.attachments.length) {
		validateBodyAttachments(message.attachments);
	}

	if (!message.ts) {
		message.ts = new Date();
	}
	const { _id, username, name } = user;
	message.u = {
		_id,
		username,
		name,
	};
	message.rid = room._id;

	if (!Match.test(message.msg, String)) {
		message.msg = '';
	}

	if (message.ts == null) {
		message.ts = new Date();
	}

	if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) {
		message.unread = true;
	}

	// For the Rocket.Chat Apps :)
	if (message && Apps && Apps.isLoaded()) {
		const prevent = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentPrevent', message));
		if (prevent) {
			throw new Meteor.Error('error-app-prevented-sending', 'A Rocket.Chat App prevented the message sending.');
		}

		let result;
		result = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentExtend', message));
		result = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentModify', result));

		if (typeof result === 'object') {
			message = Object.assign(message, result);
		}
	}

	if (message.parseUrls !== false) {
		message.html = message.msg;
		message = RocketChat.Markdown.code(message);

		const urls = message.html.match(/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\(\)\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g);
		if (urls) {
			message.urls = urls.map((url) => ({ url }));
		}

		message = RocketChat.Markdown.mountTokensBack(message, false);
		message.msg = message.html;
		delete message.html;
		delete message.tokens;
	}

	message = RocketChat.callbacks.run('beforeSaveMessage', message);
	if (message) {
		// Avoid saving sandstormSessionId to the database
		let sandstormSessionId = null;
		if (message.sandstormSessionId) {
			sandstormSessionId = message.sandstormSessionId;
			delete message.sandstormSessionId;
		}

		if (message._id && upsert) {
			const { _id } = message;
			delete message._id;
			RocketChat.models.Messages.upsert({
				_id,
				'u._id': message.u._id,
			}, message);
			message._id = _id;
		} else {
			message._id = RocketChat.models.Messages.insert(message);
		}

		if (Apps && Apps.isLoaded()) {
			// This returns a promise, but it won't mutate anything about the message
			// so, we don't really care if it is successful or fails
			Apps.getBridges().getListenerBridge().messageEvent('IPostMessageSent', message);
		}

		/*
		Defer other updates as their return is not interesting to the user
		*/
		Meteor.defer(() => {
			// Execute all callbacks
			message.sandstormSessionId = sandstormSessionId;
			return RocketChat.callbacks.run('afterSaveMessage', message, room, user._id);
		});
		return message;
	}
};


import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';

Meteor.methods({
	'livechat:getAnalyticsOverviewData'(options) {
		if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'view-livechat-manager')) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'livechat:getAnalyticsOverviewData',
			});
		}

		if (!(options.analyticsOptions && options.analyticsOptions.name)) {
			console.log('Incorrect analytics options');
			return;
		}

		return RocketChat.Livechat.Analytics.getAnalyticsOverviewData(options);
	},
});


<template name="emojiPicker">

							{{> icon block="rc-input__icon-svg" icon="magnifier" }}

						<input name="name" type="text" class="rc-input__element js-emojipicker-search" placeholder="{{_ "Search"}}" autocomplete="off">

				{{#each category}}

				{{/each}}

			{{#each category}}

					{{{emojiList .}}}

			{{/each}}

</template>


/* globals Department, Livechat, LivechatVideoCall */
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { TAPi18n } from 'meteor/tap:i18n';
import visitor from '../../imports/client/visitor';
import _ from 'underscore';
import s from 'underscore.string';

Template.register.helpers({
	error() {
		return Template.instance().error.get();
	},
	welcomeMessage() {
		return Livechat.registrationFormMessage;
	},
	showDepartments() {
		return Department.find({ showOnRegistration: true }).count() > 1;
	},
	departments() {
		return Department.find({ showOnRegistration: true });
	},
	videoCallEnabled() {
		return Livechat.videoCall;
	},
	selectedDepartment() {
		return this._id === Livechat.department;
	},
	showNameFieldRegisterForm() {
		return Livechat.nameFieldRegistrationForm;
	},
	showEmailFieldRegisterForm() {
		return Livechat.emailFieldRegistrationForm;
	},
	getName() {
		return Livechat.guestName;
	},
	getEmail() {
		return Livechat.guestEmail;
	},
});

Template.register.events({
	'submit #livechat-registration'(e, instance) {
		e.preventDefault();

		const start = () => {
			instance.hideError();
			if (instance.request === 'video') {
				LivechatVideoCall.request();
			}
		};
		const form = e.currentTarget;

		const fields = [];
		let name = Livechat.guestName;
		let email = Livechat.guestEmail;

		if (Livechat.nameFieldRegistrationForm) {
			fields.push('name');
			name = instance.$('input[name=name]').val();
		}

		if (Livechat.emailFieldRegistrationForm) {
			fields.push('email');
			email = instance.$('input[name=email]').val();
		}

		if (!instance.validateForm(form, fields)) {
			return instance.showError(TAPi18n.__('You_must_complete_all_fields'));
		} else {
			let departmentId = instance.$('select[name=department]').val();
			if (!departmentId) {
				const department = Department.findOne({ showOnRegistration: true });
				if (department) {
					departmentId = department._id;
				}
			}

			if (departmentId) {
				Livechat.department = departmentId;
			}

			const guest = {
				token: visitor.getToken(),
				name,
				email,
				department: Livechat.department,
			};

			Meteor.call('livechat:registerGuest', guest, function(error, result) {
				if (error != null) {
					return instance.showError(error.reason);
				}
				parentCall('callback', ['pre-chat-form-submit', _.omit(guest, 'token')]);
				visitor.setId(result.userId);
				visitor.setData(result.visitor);
				start();
			});
		}
	},
	'click .error'(e, instance) {
		return instance.hideError();
	},
	'click .request-chat'(e, instance) {
		instance.request = 'chat';
	},
	'click .request-video'(e, instance) {
		instance.request = 'video';
	},
});

Template.register.onCreated(function() {
	this.error = new ReactiveVar();
	this.request = '';

	this.validateForm = (form, fields) => {
		const valid = fields.every((field) => !_.isEmpty(s.trim(form.elements[field].value)));

		return valid;
	};

	this.showError = (msg) => {
		$('.error').addClass('show');
		this.error.set(msg);
	};
	this.hideError = () => {
		$('.error').removeClass('show');
		this.error.set();
	};
});


imageUrl: attachment.image_url,
audioUrl: attachment.audio_url,

imageType: attachment.image_type,
imageSize: attachment.image_size,


import { Template } from 'meteor/templating';

Template.slider.onRendered(function() {
	const params = this.data;

	const rangeSlider = function() {

		const range = $(`#${ params.id }`);
		const labelValue = $(`#${ params.id }_value`);

		labelValue.html(params.value);

		range.on('input', function() {
			labelValue.html(this.value);
		});
	};

	rangeSlider();
});


import _ from 'underscore';
import { settings } from 'meteor/rocketchat:settings';
import { FileUploadClass } from '../lib/FileUpload';
import { FileUpload } from '../lib/FileUpload';
import '../../ufs/AmazonS3/server.js';
import http from 'http';
import https from 'https';

const get = function(file, req, res) {
	const fileUrl = this.store.getRedirectURL(file);

	if (fileUrl) {
		const storeType = file.store.split(':').pop();
		if (settings.get(`FileUpload_S3_Proxy_${ storeType }`)) {
			const request = /^https:/.test(fileUrl) ? https : http;
			request.get(fileUrl, (fileRes) => fileRes.pipe(res));
		} else {
			res.removeHeader('Content-Length');
			res.setHeader('Location', fileUrl);
			res.writeHead(302);
			res.end();
		}
	} else {
		res.end();
	}
};

const copy = function(file, out) {
	const fileUrl = this.store.getRedirectURL(file);

	if (fileUrl) {
		const request = /^https:/.test(fileUrl) ? https : http;
		request.get(fileUrl, (fileRes) => fileRes.pipe(out));
	} else {
		out.end();
	}
};

const AmazonS3Uploads = new FileUploadClass({
	name: 'AmazonS3:Uploads',
	get,
	copy,
	// store setted bellow
});

const AmazonS3Avatars = new FileUploadClass({
	name: 'AmazonS3:Avatars',
	get,
	copy,
	// store setted bellow
});

const AmazonS3UserDataFiles = new FileUploadClass({
	name: 'AmazonS3:UserDataFiles',
	get,
	copy,
	// store setted bellow
});

const configure = _.debounce(function() {
	const Bucket = settings.get('FileUpload_S3_Bucket');
	const Acl = settings.get('FileUpload_S3_Acl');
	const AWSAccessKeyId = settings.get('FileUpload_S3_AWSAccessKeyId');
	const AWSSecretAccessKey = settings.get('FileUpload_S3_AWSSecretAccessKey');
	const URLExpiryTimeSpan = settings.get('FileUpload_S3_URLExpiryTimeSpan');
	const Region = settings.get('FileUpload_S3_Region');
	const SignatureVersion = settings.get('FileUpload_S3_SignatureVersion');
	const ForcePathStyle = settings.get('FileUpload_S3_ForcePathStyle');
	// const CDN = RocketChat.settings.get('FileUpload_S3_CDN');
	const BucketURL = settings.get('FileUpload_S3_BucketURL');

	if (!Bucket) {
		return;
	}

	const config = {
		connection: {
			signatureVersion: SignatureVersion,
			s3ForcePathStyle: ForcePathStyle,
			params: {
				Bucket,
				ACL: Acl,
			},
			region: Region,
		},
		URLExpiryTimeSpan,
	};

	if (AWSAccessKeyId) {
		config.connection.accessKeyId = AWSAccessKeyId;
	}

	if (AWSSecretAccessKey) {
		config.connection.secretAccessKey = AWSSecretAccessKey;
	}

	if (BucketURL) {
		config.connection.endpoint = BucketURL;
	}

	AmazonS3Uploads.store = FileUpload.configureUploadsStore('AmazonS3', AmazonS3Uploads.name, config);
	AmazonS3Avatars.store = FileUpload.configureUploadsStore('AmazonS3', AmazonS3Avatars.name, config);
	AmazonS3UserDataFiles.store = FileUpload.configureUploadsStore('AmazonS3', AmazonS3UserDataFiles.name, config);
}, 500);

settings.get(/^FileUpload_S3_/, configure);


import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';

Meteor.methods({
	addRoomOwner(rid, userId) {
		check(rid, String);
		check(userId, String);

		if (!Meteor.userId()) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'addRoomOwner',
			});
		}

		if (!RocketChat.authz.hasPermission(Meteor.userId(), 'set-owner', rid)) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'addRoomOwner',
			});
		}

		const user = RocketChat.models.Users.findOneById(userId);

		if (!user || !user.username) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'addRoomOwner',
			});
		}

		const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, user._id);

		if (!subscription) {
			throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', {
				method: 'addRoomOwner',
			});
		}

		if (Array.isArray(subscription.roles) === true && subscription.roles.includes('owner') === true) {
			throw new Meteor.Error('error-user-already-owner', 'User is already an owner', {
				method: 'addRoomOwner',
			});
		}

		RocketChat.models.Subscriptions.addRoleById(subscription._id, 'owner');

		const fromUser = RocketChat.models.Users.findOneById(Meteor.userId());

		RocketChat.models.Messages.createSubscriptionRoleAddedWithRoomIdAndUser(rid, user, {
			u: {
				_id: fromUser._id,
				username: fromUser.username,
			},
			role: 'owner',
		});

		if (RocketChat.settings.get('UI_DisplayRoles')) {
			RocketChat.Notifications.notifyLogged('roles-change', {
				type: 'added',
				_id: 'owner',
				u: {
					_id: user._id,
					username: user.username,
					name: user.name,
				},
				scope: rid,
			});
		}

		return true;
	},
});


audioUrl: attachment.audio_url,
videoUrl: attachment.video_url,

audioType: attachment.audio_type,
audioSize: attachment.audio_size,


#! /bin/bash

caddy_version="v0.11.0"

caddy_bin="caddy"
caddy_dl_ext=".tar.gz"

# NOTE: `uname -m` is more accurate and universal than `arch`
# See https://en.wikipedia.org/wiki/Uname
unamem="$(uname -m)"
if [[ $unamem == *aarch64* ]]; then
        caddy_arch="arm64"
elif [[ $unamem == *64* ]]; then
        caddy_arch="amd64"
elif [[ $unamem == *86* ]]; then
        caddy_arch="386"
elif [[ $unamem == *armv5* ]]; then
        caddy_arch="arm"
        caddy_arm="5"
elif [[ $unamem == *armv6l* ]]; then
        caddy_arch="arm"
        caddy_arm="6"
elif [[ $unamem == *armv7l* ]]; then
        caddy_arch="arm"
        caddy_arm="7"
else
        echo "Aborted, unsupported or unknown architecture: $unamem"
        return 2
fi

caddy_arch=_linux_$caddy_arch

echo "Downloading Caddy for $caddy_os/$caddy_arch$caddy_arm..."
caddy_file="caddy_linux_$caddy_arch${caddy_arm}_custom$caddy_dl_ext"
caddy_url="https://github.com/mholt/caddy/releases/download/$caddy_version/caddy_$caddy_version$caddy_arch$caddy_arm.tar.gz"
echo "$caddy_url"

curl -L "$caddy_url" -o "$caddy_file"
tar -xzf $caddy_file -C . "$caddy_bin"
chmod +x $caddy_bin


<template name="oembedFrameWidget">
{{#if parsedUrl}}

		{{#if meta.oembedProviderName}}
			{{#if meta.oembedProviderUrl}}
				{{meta.oembedProviderName}}
			{{/if}}
		{{/if}}
		{{#if meta.oembedAuthorName}}
			{{#if meta.oembedAuthorUrl}}

				{{meta.oembedAuthorName}}
			{{/if}}
		{{/if}}
		{{#if meta.oembedTitle}}
			{{#if meta.oembedUrl}}

				{{meta.oembedTitle}}
			{{/if}}
		{{/if}}
		{{#if collapsed}}

		{{else}}

			{{#if meta.oembedDescription}}

{{meta.oembedDescription}}

			{{/if}}
			{{{meta.oembedHtml}}}
		{{/if}}

{{/if}}
</template>


import s from 'underscore.string';

RocketChat.Migrations.add({
	version: 64,
	up() {
		RocketChat.models.Messages.find({ t: 'room_changed_topic', msg: /</ }, { msg: 1 }).forEach(function(message) {
			const msg = s.escapeHTML(message.msg);
			RocketChat.models.Messages.update({ _id: message._id }, { $set: { msg } });
		});
	},
});


videoType: attachment.video_type,
videoSize: attachment.video_size,


Package.describe({
	name: 'rocketchat:slashcommands-leave',
	version: '0.0.1',
	summary: 'Message pre-processor that will translate /leave commands',
	git: '',
});

Package.onUse(function(api) {
	api.use([
		'ecmascript',
		'rocketchat:lib',
	]);
	api.mainModule('server/index.js', 'server');
});


import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';
import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { t, getUserPreference } from 'meteor/rocketchat:utils';
import { Session } from 'meteor/session';
import { Template } from 'meteor/templating';
import { mainReady, Layout, iframeLogin, modal, popover, menu, fireGlobalEvent } from 'meteor/rocketchat:ui-utils';
import { toolbarSearch } from 'meteor/rocketchat:ui-sidenav';
import { settings } from 'meteor/rocketchat:settings';
import { CachedChatSubscription, Roles, ChatSubscription } from 'meteor/rocketchat:models';
import { CachedCollectionManager } from 'meteor/rocketchat:ui-cached-collection';
import { hasRole } from 'meteor/rocketchat:authorization';
import { tooltip } from 'meteor/rocketchat:tooltip';
import Clipboard from 'clipboard';
import s from 'underscore.string';

settings.collection.find({ _id:/theme-color-rc/i }, { fields:{ value: 1 } }).observe({ changed: () => { DynamicCss.run(true); } });

Template.body.onRendered(function() {
	new Clipboard('.clipboard');

	$(document.body).on('keydown', function(e) {
		if ((e.keyCode === 80 || e.keyCode === 75) && (e.ctrlKey === true || e.metaKey === true) && e.shiftKey === false) {
			e.preventDefault();
			e.stopPropagation();
			toolbarSearch.show(true);
		}
		const unread = Session.get('unread');
		if (e.keyCode === 27 && (e.shiftKey === true || e.ctrlKey === true) && (unread != null) && unread !== '') {
			e.preventDefault();
			e.stopPropagation();
			modal.open({
				title: t('Clear_all_unreads_question'),
				type: 'warning',
				confirmButtonText: t('Yes_clear_all'),
				showCancelButton: true,
				cancelButtonText: t('Cancel'),
				confirmButtonColor: '#DD6B55',
			}, function() {
				const subscriptions = ChatSubscription.find({
					open: true,
				}, {
					fields: {
						unread: 1,
						alert: 1,
						rid: 1,
						t: 1,
						name: 1,
						ls: 1,
					},
				});

				subscriptions.forEach((subscription) => {
					if (subscription.alert || subscription.unread > 0) {
						Meteor.call('readMessages', subscription.rid);
					}
				});
			});
		}
	});

	$(document.body).on('keydown', function(e) {
		const { target } = e;
		if (e.ctrlKey === true || e.metaKey === true) {
			popover.close();
			return;
		}
		if (!((e.keyCode > 45 && e.keyCode < 91) || e.keyCode === 8)) {
			return;
		}

		popover.close();

		if (/input|textarea|select/i.test(target.tagName)) {
			return;
		}
		if (target.id === 'pswp') {
			return;
		}
		const inputMessage = $('.rc-message-box__textarea');
		if (inputMessage.length === 0) {
			return;
		}
		inputMessage.focus();
	});

	$(document.body).on('click', function(e) {
		if (e.target.tagName === 'A') {
			const link = e.currentTarget;
			if (link.origin === s.rtrim(Meteor.absoluteUrl(), '/') && /msg=([a-zA-Z0-9]+)/.test(link.search)) {
				e.preventDefault();
				e.stopPropagation();
				if (Layout.isEmbedded()) {
					return fireGlobalEvent('click-message-link', {
						link: link.pathname + link.search,
					});
				}
				return FlowRouter.go(link.pathname + link.search, null, FlowRouter.current().queryParams);
			}
		}
	});

	Tracker.autorun(function(c) {
		const w = window;
		const d = document;
		const script = 'script';
		const l = 'dataLayer';
		const i = settings.get('GoogleTagManager_id');
		if (Match.test(i, String) && i.trim() !== '') {
			c.stop();
			return (function(w, d, s, l, i) {
				w[l] = w[l] || [];
				w[l].push({
					'gtm.start': new Date().getTime(),
					event: 'gtm.js',
				});
				const f = d.getElementsByTagName(s)[0];
				const j = d.createElement(s);
				const dl = l !== 'dataLayer' ? `&l=${ l }` : '';
				j.async = true;
				j.src = `//www.googletagmanager.com/gtm.js?id=${ i }${ dl }`;
				return f.parentNode.insertBefore(j, f);
			}(w, d, script, l, i));
		}
	});
});

Template.main.onCreated(function() {
	tooltip.init();
});

Template.main.helpers({
	removeSidenav() {
		const { modal } = this;
		return (modal || typeof modal === 'function' ? modal() : modal); // || RocketChat.Layout.isEmbedded();
	},
	siteName() {
		return settings.get('Site_Name');
	},
	logged() {
		if (Meteor.userId() != null || (settings.get('Accounts_AllowAnonymousRead') === true && Session.get('forceLogin') !== true)) {
			$('html').addClass('noscroll').removeClass('scroll');
			return true;
		} else {
			$('html').addClass('scroll').removeClass('noscroll');
			return false;
		}
	},
	useIframe() {
		const iframeEnabled = typeof iframeLogin !== 'undefined';
		return iframeEnabled && iframeLogin.reactiveEnabled.get();
	},
	iframeUrl() {
		const iframeEnabled = typeof iframeLogin !== 'undefined';
		return iframeEnabled && iframeLogin.reactiveIframeUrl.get();
	},
	subsReady() {
		const routerReady = FlowRouter.subsReady('userData', 'activeUsers');
		const subscriptionsReady = CachedChatSubscription.ready.get();
		const settingsReady = settings.cachedCollection.ready.get();
		const ready = (Meteor.userId() == null) || (routerReady && subscriptionsReady && settingsReady);
		CachedCollectionManager.syncEnabled = ready;
		Meteor.defer(() => {
			mainReady.set(ready);
		});
		return ready;
	},
	hasUsername() {
		return (Meteor.userId() != null && Meteor.user().username != null) || (Meteor.userId() == null && settings.get('Accounts_AllowAnonymousRead') === true);
	},
	requirePasswordChange() {
		const user = Meteor.user();
		return user && user.requirePasswordChange === true;
	},
	require2faSetup() {
		const user = Meteor.user();

		// User is already using 2fa
		if (user.services.totp !== undefined && user.services.totp.enabled) {
			return false;
		}

		const mandatoryRole = Roles.findOne({ _id: { $in: user.roles }, mandatory2fa: true });
		return mandatoryRole !== undefined;
	},
	CustomScriptLoggedOut() {
		const script = settings.get('Custom_Script_Logged_Out') || '';
		if (script.trim()) {
			eval(script);//eslint-disable-line
		}
	},
	CustomScriptLoggedIn() {
		const script = settings.get('Custom_Script_Logged_In') || '';
		if (script.trim()) {
			eval(script);//eslint-disable-line
		}
	},
	embeddedVersion() {
		if (Layout.isEmbedded()) {
			return 'embedded-view';
		}
	},
	showSetupWizard() {
		const userId = Meteor.userId();
		const Show_Setup_Wizard = settings.get('Show_Setup_Wizard');

		return (!userId && Show_Setup_Wizard === 'pending') || (userId && hasRole(userId, 'admin') && Show_Setup_Wizard === 'in_progress');
	},
});

Template.main.events({
	'click .burger'() {
		return menu.toggle();
	},
});

Template.main.onRendered(function() {
	$('#initial-page-loading').remove();
	window.addEventListener('focus', function() {
		return Meteor.setTimeout(function() {
			if (!$(':focus').is('INPUT,TEXTAREA')) {
				return $('.input-message').focus();
			}
		}, 100);
	});
	return Tracker.autorun(function() {
		const userId = Meteor.userId();
		const Show_Setup_Wizard = settings.get('Show_Setup_Wizard');

		if ((!userId && Show_Setup_Wizard === 'pending') || (userId && hasRole(userId, 'admin') && Show_Setup_Wizard === 'in_progress')) {
			FlowRouter.go('setup-wizard');
		}
		if (getUserPreference(userId, 'hideUsernames')) {
			$(document.body).on('mouseleave', 'button.thumb', function() {
				return tooltip.hide();
			});
			return $(document.body).on('mouseenter', 'button.thumb', function(e) {
				const avatarElem = $(e.currentTarget);
				const username = avatarElem.attr('data-username');
				if (username) {
					e.stopPropagation();
					return tooltip.showElement($('<span>').text(username), avatarElem);
				}
			});
		} else {
			$(document.body).off('mouseenter', 'button.thumb');
			return $(document.body).off('mouseleave', 'button.thumb');
		}
	});
});

Meteor.startup(function() {
	return fireGlobalEvent('startup', true);
});


Sind Sie sich sicher, dass Sie diesen Chat beenden wollen?

Sind Sie sich sicher, dass Sie diesen Chat beenden und die Abteilung wechseln wollen?

Sind Sie sich sicher, das Sie die Abteilung wechseln möchten?

Wie reaktionsschnell war der Chat-Agent?

Wie zufrieden sind Sie mit diesem Gespräch?

Bitte nehmen Sie sich einen Moment Zeit, um kurz einige Fragen zu dem Gespräch zu beantworten.

Bitte gib Sie Ihren Namen und Ihre E-Mail-Adresse ein

Bewerte jede Frage: 1 bedeutet, "total unzufrieden", 5 "vollständig zufrieden".

Vielen Dank! Wir kommen zeitnah auf Sie zurück.

Wir sind momentan nicht online. Bitte hinterlassen Sie eine Nachricht.


author_link: String,
author_icon: String,
title: String,
title_link: String,
title_link_download: Boolean,
image_url: String,

import { Random } from 'meteor/random';

export class AppMessagesConverter {
	constructor(orch) {
		this.orch = orch;
	}

	convertById(msgId) {
		const msg = RocketChat.models.Messages.getOneById(msgId);

		return this.convertMessage(msg);
	}

	convertMessage(msgObj) {
		if (!msgObj) {
			return undefined;
		}

		const room = this.orch.getConverters().get('rooms').convertById(msgObj.rid);

		let sender;
		if (msgObj.u && msgObj.u._id) {
			sender = this.orch.getConverters().get('users').convertById(msgObj.u._id);

			if (!sender) {
				sender = this.orch.getConverters().get('users').convertToApp(msgObj.u);
			}
		}

		let editor;
		if (msgObj.editedBy) {
			editor = this.orch.getConverters().get('users').convertById(msgObj.editedBy._id);
		}

		const attachments = this._convertAttachmentsToApp(msgObj.attachments);

		return {
			id: msgObj._id,
			room,
			sender,
			text: msgObj.msg,
			createdAt: msgObj.ts,
			updatedAt: msgObj._updatedAt,
			editor,
			editedAt: msgObj.editedAt,
			emoji: msgObj.emoji,
			avatarUrl: msgObj.avatar,
			alias: msgObj.alias,
			customFields: msgObj.customFields,
			groupable: msgObj.groupable,
			attachments,
			reactions: msgObj.reactions,
			parseUrls: msgObj.parseUrls,
		};
	}

	convertAppMessage(message) {
		if (!message) {
			return undefined;
		}

		const room = RocketChat.models.Rooms.findOneById(message.room.id);

		if (!room) {
			throw new Error('Invalid room provided on the message.');
		}

		let u;
		if (message.sender && message.sender.id) {
			const user = RocketChat.models.Users.findOneById(message.sender.id);

			if (user) {
				u = {
					_id: user._id,
					username: user.username,
					name: user.name,
				};
			} else {
				u = {
					_id: message.sender.id,
					username: message.sender.username,
					name: message.sender.name,
				};
			}
		}

		let editedBy;
		if (message.editor) {
			const editor = RocketChat.models.Users.findOneById(message.editor.id);
			editedBy = {
				_id: editor._id,
				username: editor.username,
			};
		}

		const attachments = this._convertAppAttachments(message.attachments);

		return {
			_id: message.id || Random.id(),
			rid: room._id,
			u,
			msg: message.text,
			ts: message.createdAt || new Date(),
			_updatedAt: message.updatedAt || new Date(),
			editedBy,
			editedAt: message.editedAt,
			emoji: message.emoji,
			avatar: message.avatarUrl,
			alias: message.alias,
			customFields: message.customFields,
			groupable: message.groupable,
			attachments,
			reactions: message.reactions,
			parseUrls: message.parseUrls,
		};
	}

	_convertAppAttachments(attachments) {
		if (typeof attachments === 'undefined' || !Array.isArray(attachments)) {
			return undefined;
		}

		return attachments.map((attachment) => ({
			collapsed: attachment.collapsed,
			color: attachment.color,
			text: attachment.text,
			ts: attachment.timestamp ? attachment.timestamp.toJSON() : attachment.timestamp,
			message_link: attachment.timestampLink,
			thumb_url: attachment.thumbnailUrl,
			author_name: attachment.author ? attachment.author.name : undefined,
			author_link: attachment.author ? attachment.author.link : undefined,
			author_icon: attachment.author ? attachment.author.icon : undefined,
			title: attachment.title ? attachment.title.value : undefined,
			title_link: attachment.title ? attachment.title.link : undefined,
			title_link_download: attachment.title ? attachment.title.displayDownloadLink : undefined,
			image_url: attachment.imageUrl,
			audio_url: attachment.audioUrl,
			video_url: attachment.videoUrl,
			fields: attachment.fields,
			button_alignment: attachment.actionButtonsAlignment,
			actions: attachment.actions,
			type: attachment.type,
			description: attachment.description,
		})).map((a) => {
			Object.keys(a).forEach((k) => {
				if (typeof a[k] === 'undefined') {
					delete a[k];
				}
			});

			return a;
		});
	}

	_convertAttachmentsToApp(attachments) {
		if (typeof attachments === 'undefined' || !Array.isArray(attachments)) {
			return undefined;
		}

		return attachments.map((attachment) => {
			let author;
			if (attachment.author_name || attachment.author_link || attachment.author_icon) {
				author = {
					name: attachment.author_name,
					link: attachment.author_link,
					icon: attachment.author_icon,
				};
			}

			let title;
			if (attachment.title || attachment.title_link || attachment.title_link_download) {
				title = {
					value: attachment.title,
					link: attachment.title_link,
					displayDownloadLink: attachment.title_link_download,
				};
			}

			return {
				collapsed: attachment.collapsed,
				color: attachment.color,
				text: attachment.text,
				timestamp: new Date(attachment.ts),
				timestampLink: attachment.message_link,
				thumbnailUrl: attachment.thumb_url,
				author,
				title,
				imageUrl: attachment.image_url,
				audioUrl: attachment.audio_url,
				videoUrl: attachment.video_url,
				fields: attachment.fields,
				actionButtonsAlignment: attachment.button_alignment,
				actions: attachment.actions,
				type: attachment.type,
				description: attachment.description,
			};
		});
	}
}


import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
import { RocketChat } from 'meteor/rocketchat:lib';

Meteor.startup(function() {
	Tracker.autorun(function() {
		if (RocketChat.settings.get('Message_AllowSnippeting')) {
			RocketChat.TabBar.addButton({
				groups: ['channel', 'group', 'direct'],
				id: 'snippeted-messages',
				i18nTitle: 'snippet-message',
				icon: 'code',
				template: 'snippetedMessages',
				order: 20,
			});
		} else {
			RocketChat.TabBar.removeButton('snippeted-messages');
		}
	});
});


/*
	What is this file? Great question! To make Rocket.Chat more "modular"
	and to make the "rocketchat:lib" package more of a core package
	with the libraries, this index file contains the exported members
	for the *server* pieces of code which does include the shared
	library files.
*/

import { RoomSettingsEnum, RoomTypeConfig, RoomTypeRouteConfig } from '../../lib/RoomTypeConfig';
import { sendNotification } from './sendNotificationsOnMessage.js';

export {
	RoomSettingsEnum,
	RoomTypeConfig,
	RoomTypeRouteConfig,
	sendNotification,
};


import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';
import session from 'express-session';
import Grant from 'grant-express';
import fiber from 'fibers';

import { GrantError } from './error';
import { generateConfig } from './grant';
import { path, generateCallback, generateAppCallback } from './routes';
import { middleware as redirect } from './redirect';
import Providers, { middleware as providers } from './providers';
import Settings from './settings';

let grant;

WebApp.connectHandlers.use(session({
	secret: 'grant',
	resave: true,
	saveUninitialized: true,
}));

// grant
WebApp.connectHandlers.use(path, (req, res, next) => {
	if (grant) {
		grant(req, res, next);
	} else {
		next();
	}
});

// callbacks
WebApp.connectHandlers.use((req, res, next) => {
	fiber(() => {
		redirect(req, res, next);
	}).run();
});

// providers
WebApp.connectHandlers.use((req, res, next) => {
	fiber(() => {
		providers(req, res, next);
	}).run();
});

Meteor.startup(() => {
	const config = generateConfig();

	grant = new Grant(config);
});

export {
	path,
	generateCallback,
	generateAppCallback,
	Providers,
	Settings,
	GrantError,
};


image_preview: String,
image_type: String,
image_size: Number,


RocketChat.Migrations.add({
	version: 23,
	up() {
		RocketChat.models.Settings.remove({
			_id: 'Accounts_denyUnverifiedEmails',
		});

		return console.log('Deleting not used setting Accounts_denyUnverifiedEmails');
	},
});


import schema from '../../schemas/users/UserStatus-enum.graphqls';

export {
	schema,
};


/**
 * Tries to retrieve the user preference falling back to a default system
 * value or to a default value if it is passed as argument
*/
RocketChat.getUserPreference = function(user, key, defaultValue = undefined) {
	let preference;
	if (typeof user === typeof '') {
		user = RocketChat.models.Users.findOne(user, { fields: { [`settings.preferences.${ key }`]: 1 } });
	}
	if (user && user.settings && user.settings.preferences &&
		user.settings.preferences.hasOwnProperty(key)) {
		preference = user.settings.preferences[key];
	} else if (defaultValue === undefined) {
		preference = RocketChat.settings.get(`Accounts_Default_User_Preferences_${ key }`);
	}

	return preference !== undefined ? preference : defaultValue;
};


RocketChat.Migrations.add({
	version: 117,
	up() {
		if (RocketChat.authz &&
		RocketChat.models &&
		RocketChat.models.Settings &&
		RocketChat.authz.getUsersInRole('admin').count()) {
			RocketChat.models.Settings.upsert(
				{
					_id: 'Show_Setup_Wizard',
				}, {
					$set: { value: 'completed' },
				}
			);
		}
	},
});


import flexTab from '../../pageobjects/flex-tab.page';
import mainContent from '../../pageobjects/main-content.page';
import sideNav from '../../pageobjects/side-nav.page';

// test data imports
import { checkIfUserIsValid } from '../../data/checks';
import { username, email, password } from '../../data/user.js';

describe('[Main Elements Render]', function() {
	before(() => {
		checkIfUserIsValid(username, email, password);
		sideNav.sidebarHeader.waitForVisible(10000);
		sideNav.spotlightSearchIcon.click();
		sideNav.spotlightSearch.waitForVisible(10000);
		sideNav.searchChannel('general');
	});

	describe('[Side Nav Bar]', () => {
		describe('render:', () => {
			before(() => {
				sideNav.sidebarHeader.waitForVisible(10000);
			});

			it('it should show the new channel button', () => {
				sideNav.newChannelBtn.isVisible().should.be.true;
			});

			it('it should show "general" channel', () => {
				sideNav.general.isVisible().should.be.true;
			});
		});

		describe('spotlight search render:', () => {
			after(() => {
				mainContent.messageInput.click();
			});
			it('it should show spotlight search bar', () => {
				sideNav.spotlightSearchIcon.click();
				sideNav.spotlightSearch.isVisible().should.be.true;
			});

			it('it should click the spotlight and show the channel list', () => {
				sideNav.spotlightSearch.waitForVisible(5000);
				sideNav.spotlightSearch.click();
				sideNav.spotlightSearchPopUp.waitForVisible(5000);
				sideNav.spotlightSearchPopUp.isVisible().should.be.true;
			});

			it.skip('it should remove the list when the spotlight loses focus', () => {
				sideNav.spotlightSearchPopUp.waitForVisible(5000);
				sideNav.spotlightSearchPopUp.isVisible().should.be.true;
				mainContent.messageInput.click();
				mainContent.lastMessage.click();
				sideNav.spotlightSearchPopUp.waitForVisible(5000, true);
				sideNav.spotlightSearchPopUp.isVisible().should.be.false;
			});

			it('it should add text to the spotlight and show the channel list', () => {
				sideNav.spotlightSearch.waitForVisible(5000);
				sideNav.spotlightSearch.setValue('rocket.cat');
				sideNav.spotlightSearchPopUp.waitForVisible(5000);
				sideNav.spotlightSearchPopUp.isVisible().should.be.true;
			});

			it.skip('it should remove the text on the spotlight and the list when lost focus', () => {
				sideNav.spotlightSearchPopUp.waitForVisible(5000);
				sideNav.spotlightSearchPopUp.isVisible().should.be.true;
				mainContent.messageInput.click();
				sideNav.spotlightSearchPopUp.waitForVisible(5000, true);
				sideNav.spotlightSearchPopUp.isVisible().should.be.false;
				sideNav.spotlightSearch.getText().should.equal('');
			});
		});
	});

	describe('[User Options]', () => {
		describe('render:', () => {
			before(() => {
				sideNav.sidebarUserMenu.click();
				sideNav.getPopOverContent().waitForVisible(10000);
			});

			after(() => {
				mainContent.popoverWrapper.click();
			});

			it('it should show online button', () => {
				sideNav.statusOnline.isVisible().should.be.true;
			});

			it('it should show away button', () => {
				sideNav.statusAway.isVisible().should.be.true;
			});

			it('it should show busy button', () => {
				sideNav.statusBusy.isVisible().should.be.true;
			});

			it('it should show offline button', () => {
				sideNav.statusOffline.isVisible().should.be.true;
			});

			it('it should show settings button', () => {
				sideNav.account.isVisible().should.be.true;
			});

			it('it should show logout button', () => {
				sideNav.logout.isVisible().should.be.true;
			});
		});
	});

	describe('[Main Content]', () => {
		describe('render:', () => {
			before(() => {
				sideNav.logout.waitForVisible(5000, true);
				sideNav.getChannelFromList('general').waitForVisible(5000);
				sideNav.openChannel('general');
			});

			it('it should show the title of the channel', () => {
				mainContent.channelTitle.isVisible().should.be.true;
			});

			it('it should show the empty favorite star', () => {
				mainContent.emptyFavoriteStar.isVisible().should.be.true;
			});

			it('it should click the star', () => {
				mainContent.emptyFavoriteStar.click();
			});

			it('it should not show the empty favorite star', () => {
				mainContent.favoriteStar.isVisible().should.be.true;
			});

			it.skip('it should click the star', () => {
				mainContent.favoriteStar.click();
			});

			it('it should show the message input bar', () => {
				mainContent.messageInput.isVisible().should.be.true;
			});

			it('it should show the message box actions button', () => {
				mainContent.messageBoxActions.isVisible().should.be.true;
			});

			// issues with the new message box action button and the no animations on tests

			it.skip('it should show the audio recording button', () => {
				mainContent.recordBtn.isVisible().should.be.true;
			});

			it.skip('it should show the video call button', () => {
				mainContent.videoCamBtn.isVisible().should.be.true;
			});

			it('it should show the emoji button', () => {
				mainContent.emojiBtn.isVisible().should.be.true;
			});

			it('it should show the last message', () => {
				mainContent.lastMessage.isVisible().should.be.true;
			});

			it('it should be that the last message is from the loged user', () => {
				mainContent.lastMessageUser.getText().should.equal(username);
			});

			it('it should not show the Admin tag', () => {
				mainContent.lastMessageUserTag.isVisible().should.be.false;
			});
		});
	});

	describe('[Flextab]', () => {
		describe('[Render]', () => {
			before(() => {
				sideNav.getChannelFromList('general').waitForVisible(5000);
				sideNav.openChannel('general');
			});

			after(() => {
				flexTab.operateFlexTab('info', false);
			});
			describe('Room Info Tab:', () => {
				before(() => {
					flexTab.operateFlexTab('info', true);
				});

				after(() => {
					flexTab.operateFlexTab('info', false);
				});

				it('it should show the room info button', () => {
					flexTab.channelTab.isVisible().should.be.true;
				});

				it('it should show the room info tab content', () => {
					flexTab.channelSettings.waitForVisible(5000);
					flexTab.channelSettings.isVisible().should.be.true;
				});

				it('it should show the room name', () => {
					flexTab.channelSettingName.waitForVisible();
					flexTab.channelSettingName.getAttribute('title').should.equal('general');
				});

			});

			describe('Search Tab:', () => {
				before(() => {
					flexTab.operateFlexTab('search', true);
				});

				after(() => {
					flexTab.operateFlexTab('search', false);
				});

				it('it should show the message search  button', () => {
					flexTab.searchTab.isVisible().should.be.true;
				});

				it('it should show the message tab content', () => {
					flexTab.searchTabContent.isVisible().should.be.true;
				});
			});

			describe.skip('Members Tab:', () => {
				before(() => {
					flexTab.operateFlexTab('members', true);
				});

				after(() => {
					flexTab.operateFlexTab('members', false);
				});

				it('it should show the members tab button', () => {
					flexTab.membersTab.waitForVisible(5000);
					flexTab.membersTab.isVisible().should.be.true;
				});

				it('it should show the members content', () => {
					flexTab.membersTabContent.waitForVisible(5000);
					flexTab.membersTabContent.isVisible().should.be.true;
				});

				it('it should show the show all link', () => {
					flexTab.showAll.isVisible().should.be.true;
				});
			});

			describe('Notifications Tab:', () => {
				before(() => {
					flexTab.moreActions.click();
					flexTab.operateFlexTab('notifications', true);
				});

				after(() => {
					flexTab.moreActions.click();
					flexTab.operateFlexTab('notifications', false);
				});

				it('it should not show the notifications button', () => {
					flexTab.notificationsTab.isVisible().should.be.false;
				});

				it('it should show the notifications Tab content', () => {
					flexTab.notificationsSettings.isVisible().should.be.true;
				});
			});

			describe('Files Tab:', () => {
				before(() => {
					if (flexTab.filesTab.isVisible()) {
						this.shouldClose = undefined;
						return flexTab.filesTab.click();
					}
					this.shouldClose = true;
					flexTab.moreActions.click();
					flexTab.operateFlexTab('files', true);
				});

				after(() => {
					if (!this.shouldClose) {
						return;
					}
					flexTab.moreActions.click();
					flexTab.operateFlexTab('files', false);
				});

				it('it should show the files Tab content', () => {
					flexTab.filesTabContent.waitForVisible(5000);
					flexTab.filesTabContent.isVisible().should.be.true;
				});
			});

			describe('Mentions Tab:', () => {

				before(() => {
					if (flexTab.mentionsTab.isVisible()) {
						this.shouldClose = undefined;
						return flexTab.mentionsTab.click();
					}
					this.shouldClose = true;
					flexTab.moreActions.click();
					flexTab.operateFlexTab('mentions', true);
				});

				after(() => {
					if (!this.shouldClose) {
						return;
					}
					flexTab.moreActions.click();
					flexTab.operateFlexTab('mentions', false);
				});

				it('it should show the mentions Tab content', () => {
					flexTab.mentionsTabContent.isVisible().should.be.true;
				});
			});

			describe('Starred Messages Tab:', () => {
				before(() => {
					if (flexTab.starredTab.isVisible()) {
						this.shouldClose = undefined;
						return flexTab.starredTab.click();
					}
					this.shouldClose = true;
					flexTab.moreActions.click();
					flexTab.operateFlexTab('starred', true);
				});

				after(() => {
					if (!this.shouldClose) {
						return;
					}
					flexTab.moreActions.click();
					flexTab.operateFlexTab('starred', false);
				});

				it('it should show the starred messages Tab content', () => {
					flexTab.starredTabContent.isVisible().should.be.true;
				});
			});

			describe('Pinned Messages Tab:', () => {
				before(() => {
					if (flexTab.pinnedTab.isVisible()) {
						this.shouldClose = undefined;
						return flexTab.pinnedTab.click();
					}
					this.shouldClose = true;
					flexTab.moreActions.click();
					flexTab.operateFlexTab('pinned', true);
				});

				after(() => {
					if (!this.shouldClose) {
						return;
					}
					flexTab.moreActions.click();
					flexTab.operateFlexTab('pinned', false);
				});

				it('it should show the pinned messages Tab content', () => {
					flexTab.pinnedTabContent.isVisible().should.be.true;
				});
			});
		});
	});
});


import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';

const objectMaybeIncluding = (types) => Match.Where((value) => {
	Object.keys(types).forEach((field) => {
		if (value[field] != null) {
			try {
				check(value[field], types[field]);
			} catch (error) {
				error.path = field;
				throw error;
			}
		}
	});

	return true;
});

const validateAttachmentsFields = (attachmentField) => {
	check(attachmentField, objectMaybeIncluding({
		short: Boolean,
		title: String,
		value: Match.OneOf(String, Match.Integer, Boolean),
	}));

	if (typeof attachmentField.value !== 'undefined') {
		attachmentField.value = String(attachmentField.value);
	}

};

const validateAttachmentsActions = (attachmentActions) => {
	check(attachmentActions, objectMaybeIncluding({
		type: String,
		text: String,
		url: String,
		image_url: String,
		is_webview: Boolean,
		webview_height_ratio: String,
		msg: String,
		msg_in_chat_window: Boolean,
	}));
};

const validateAttachment = (attachment) => {
	check(attachment, objectMaybeIncluding({
		color: String,
		text: String,
		ts: Match.OneOf(String, Match.Integer),
		thumb_url: String,
		button_alignment: String,
		actions: [Match.Any],
		message_link: String,
		collapsed: Boolean,
		author_name: String,
		author_link: String,
		author_icon: String,
		title: String,
		title_link: String,
		title_link_download: Boolean,
		image_url: String,
		audio_url: String,
		video_url: String,
		fields: [Match.Any],
	}));

	if (attachment.fields && attachment.fields.length) {
		attachment.fields.map(validateAttachmentsFields);
	}

	if (attachment.actions && attachment.actions.length) {
		attachment.actions.map(validateAttachmentsActions);
	}
};

const validateBodyAttachments = (attachments) => attachments.map(validateAttachment);

RocketChat.sendMessage = function(user, message, room, upsert = false) {
	if (!user || !message || !room._id) {
		return false;
	}

	check(message, objectMaybeIncluding({
		_id: String,
		msg: String,
		text: String,
		alias: String,
		emoji: String,
		avatar: String,
		attachments: [Match.Any],
	}));

	if (Array.isArray(message.attachments) && message.attachments.length) {
		validateBodyAttachments(message.attachments);
	}

	if (!message.ts) {
		message.ts = new Date();
	}
	const { _id, username, name } = user;
	message.u = {
		_id,
		username,
		name,
	};
	message.rid = room._id;

	if (!Match.test(message.msg, String)) {
		message.msg = '';
	}

	if (message.ts == null) {
		message.ts = new Date();
	}

	if (RocketChat.settings.get('Message_Read_Receipt_Enabled')) {
		message.unread = true;
	}

	// For the Rocket.Chat Apps :)
	if (message && Apps && Apps.isLoaded()) {
		const prevent = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentPrevent', message));
		if (prevent) {
			throw new Meteor.Error('error-app-prevented-sending', 'A Rocket.Chat App prevented the message sending.');
		}

		let result;
		result = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentExtend', message));
		result = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentModify', result));

		if (typeof result === 'object') {
			message = Object.assign(message, result);
		}
	}

	if (message.parseUrls !== false) {
		message.html = message.msg;
		message = RocketChat.Markdown.code(message);

		const urls = message.html.match(/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\(\)\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g);
		if (urls) {
			message.urls = urls.map((url) => ({ url }));
		}

		message = RocketChat.Markdown.mountTokensBack(message, false);
		message.msg = message.html;
		delete message.html;
		delete message.tokens;
	}

	message = RocketChat.callbacks.run('beforeSaveMessage', message);
	if (message) {
		// Avoid saving sandstormSessionId to the database
		let sandstormSessionId = null;
		if (message.sandstormSessionId) {
			sandstormSessionId = message.sandstormSessionId;
			delete message.sandstormSessionId;
		}

		if (message._id && upsert) {
			const { _id } = message;
			delete message._id;
			RocketChat.models.Messages.upsert({
				_id,
				'u._id': message.u._id,
			}, message);
			message._id = _id;
		} else {
			message._id = RocketChat.models.Messages.insert(message);
		}

		if (Apps && Apps.isLoaded()) {
			// This returns a promise, but it won't mutate anything about the message
			// so, we don't really care if it is successful or fails
			Apps.getBridges().getListenerBridge().messageEvent('IPostMessageSent', message);
		}

		/*
		Defer other updates as their return is not interesting to the user
		*/
		Meteor.defer(() => {
			// Execute all callbacks
			message.sandstormSessionId = sandstormSessionId;
			return RocketChat.callbacks.run('afterSaveMessage', message, room, user._id);
		});
		return message;
	}
};


export { AccountBox } from './lib/accountBox';
import './lib/accounts';
export { getAvatarAsPng, updateAvatarOfUsername } from './lib/avatar';
export { ChatMessages } from './lib/chatMessages';
export { RoomManager } from './lib/RoomManager';
export { RoomHistoryManager } from './lib/RoomHistoryManager';
export {
	ChatMessage,
	CachedChatRoom,
	ChatRoom,
	CachedChatSubscription,
	ChatSubscription,
	UserRoles,
	RoomRoles,
	UserAndRoom,
	CachedChannelList,
	CachedUserList,
} from './lib/collections';
import './lib/customEventPolyfill';
export { fileUpload } from './lib/fileUpload';
export { fireGlobalEvent } from './lib/fireEvent';
import './lib/iframeCommands';
export { menu } from './lib/menu';
import './lib/Modernizr';
export { MsgTyping } from './lib/msgTyping';
export { KonchatNotification } from './lib/notification';
import './lib/parentTemplate';
export { readMessage } from './lib/readMessages';
export { Login, animationSupport, animeBack, Button, preLoadImgs } from './lib/rocket';
export { SideNav } from './lib/sideNav';
import './lib/textarea-autogrow';
import './lib/codeMirror/codeMirror';
export { AudioRecorder } from './lib/recorderjs/audioRecorder';
export { VideoRecorder } from './lib/recorderjs/videoRecorder';
import './lib/recorderjs/recorder';
import './lib/textarea-cursor/set-cursor-position';
import './views/cmsPage.html';
import './views/fxos.html';
import './views/modal.html';
import './views/404/roomNotFound.html';
import './views/404/invalidSecretURL.html';
import './views/app/audioNotification.html';
import './views/app/burger.html';
import './views/app/createChannel.html';
import './views/app/fullModal.html';
import './views/app/home.html';
import './views/app/directory.html';
import './views/app/notAuthorized.html';
import './views/app/pageContainer.html';
import './views/app/pageSettingsContainer.html';
import './views/app/room.html';
import './views/app/roomSearch.html';
import './views/app/secretURL.html';
import './views/app/userSearch.html';
import './views/app/videoCall/videoButtons.html';
import './views/app/videoCall/videoCall.html';
import './views/app/photoswipe.html';
import './views/cmsPage';
import './views/fxos';
import './views/modal';
import './views/404/roomNotFound';
import './views/app/burger';
import './views/app/createChannel';
import './views/app/fullModal';
import './views/app/home';
import './views/app/directory';
export { chatMessages } from './views/app/room';
import './views/app/roomSearch';
import './views/app/secretURL';
import './views/app/videoCall/videoButtons';
import './views/app/videoCall/videoCall';
export { alerts } from './views/app/alerts';
export { popover } from './views/app/popover';
export { popout } from './views/app/popout';
export { modal } from './views/app/modal';
import './views/app/photoswipe';
import './components/icon.html';
import './components/icon';
import './components/table.html';
import './components/table';
import './components/tabs.html';
import './components/tabs';
import './components/popupList.html';
import './components/popupList';
import './components/selectDropdown.html';
import './components/selectDropdown';
import './components/header/header.html';
import './components/header/header';
import './components/contextualBar.html';
import './components/contextualBar';
export { getAvatarUrFromUsername } from 'meteor/rocketchat:utils';


//! moment.js locale configuration
//! locale : Chinese (China) [zh-cn]
//! author : suupic : https://github.com/suupic
//! author : Zeno Zeng : https://github.com/zenozeng

;(function (global, factory) {
   typeof exports === 'object' && typeof module !== 'undefined'
       && typeof require === 'function' ? factory(require('../moment')) :
   typeof define === 'function' && define.amd ? define(['../moment'], factory) :
   factory(global.moment)
}(this, function (moment) { 'use strict';

    var zh_cn = moment.defineLocale('zh-cn', {
        months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
        monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
        weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
        weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'),
        weekdaysMin : '日_一_二_三_四_五_六'.split('_'),
        longDateFormat : {
            LT : 'Ah点mm分',
            LTS : 'Ah点m分s秒',
            L : 'YYYY-MM-DD',
            LL : 'YYYY年MMMD日',
            LLL : 'YYYY年MMMD日Ah点mm分',
            LLLL : 'YYYY年MMMD日ddddAh点mm分',
            l : 'YYYY-MM-DD',
            ll : 'YYYY年MMMD日',
            lll : 'YYYY年MMMD日Ah点mm分',
            llll : 'YYYY年MMMD日ddddAh点mm分'
        },
        meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
        meridiemHour: function (hour, meridiem) {
            if (hour === 12) {
                hour = 0;
            }
            if (meridiem === '凌晨' || meridiem === '早上' ||
                    meridiem === '上午') {
                return hour;
            } else if (meridiem === '下午' || meridiem === '晚上') {
                return hour + 12;
            } else {
                // '中午'
                return hour >= 11 ? hour : hour + 12;
            }
        },
        meridiem : function (hour, minute, isLower) {
            var hm = hour * 100 + minute;
            if (hm < 600) {
                return '凌晨';
            } else if (hm < 900) {
                return '早上';
            } else if (hm < 1130) {
                return '上午';
            } else if (hm < 1230) {
                return '中午';
            } else if (hm < 1800) {
                return '下午';
            } else {
                return '晚上';
            }
        },
        calendar : {
            sameDay : function () {
                return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
            },
            nextDay : function () {
                return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
            },
            lastDay : function () {
                return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
            },
            nextWeek : function () {
                var startOfWeek, prefix;
                startOfWeek = moment().startOf('week');
                prefix = this.diff(startOfWeek, 'days') >= 7 ? '[下]' : '[本]';
                return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
            },
            lastWeek : function () {
                var startOfWeek, prefix;
                startOfWeek = moment().startOf('week');
                prefix = this.unix() < startOfWeek.unix()  ? '[上]' : '[本]';
                return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
            },
            sameElse : 'LL'
        },
        ordinalParse: /\d{1,2}(日|月|周)/,
        ordinal : function (number, period) {
            switch (period) {
                case 'd':
                case 'D':
                case 'DDD':
                    return number + '日';
                case 'M':
                    return number + '月';
                case 'w':
                case 'W':
                    return number + '周';
                default:
                    return number;
            }
        },
        relativeTime : {
            future : '%s内',
            past : '%s前',
            s : '几秒',
            m : '1 分钟',
            mm : '%d 分钟',
            h : '1 小时',
            hh : '%d 小时',
            d : '1 天',
            dd : '%d 天',
            M : '1 个月',
            MM : '%d 个月',
            y : '1 年',
            yy : '%d 年'
        },
        week : {
            // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
            dow : 1, // Monday is the first day of the week.
            doy : 4  // The week that contains Jan 4th is the first week of the year.
        }
    });

    return zh_cn;

}));


<template name="home">

		{{> header sectionName=title}}

			{{{body}}}

</template>


keep keep keep keep keep replace keep keep

// Styles
import '../../stylus/components/_text-fields.styl'

// Extensions
import VInput from '../VInput'

// Components
import VCounter from '../VCounter'
import VLabel from '../VLabel'

// Mixins
import Maskable from '../../mixins/maskable'

// Directives
import Ripple from '../../directives/ripple'

// Utilities
import {
  keyCodes
} from '../../util/helpers'

const dirtyTypes = ['color', 'file', 'time', 'date', 'datetime-local', 'week', 'month']

export default {
  name: 'v-text-field',

  extends: VInput,

  mixins: [Maskable],

  directives: { Ripple },

  inheritAttrs: false,

  data: () => ({
    badInput: false,
    initialValue: null,
    internalChange: false,
    isClearing: false
  }),

  props: {
    appendOuterIcon: String,
    autofocus: Boolean,
    box: Boolean,
    browserAutocomplete: String,
    clearable: Boolean,
    clearIcon: {
      type: String,
      default: 'clear'
    },
    clearIconCb: Function,
    color: {
      type: String,
      default: 'primary'
    },
    counter: [Boolean, Number, String],
    flat: Boolean,
    fullWidth: Boolean,
    label: String,
    outline: Boolean,
    placeholder: String,
    prefix: String,
    reverse: Boolean,
    singleLine: Boolean,
    solo: Boolean,
    soloInverted: Boolean,
    suffix: String,
    textarea: Boolean, // TODO: Deprecate
    type: {
      type: String,
      default: 'text'
    }
  },

  computed: {
    classes () {
      return {
        'v-text-field': true,
        'v-text-field--prefix': this.prefix,
        'v-text-field--single-line': this.isSingle,
        'v-text-field--solo': this.isSolo,
        'v-text-field--solo-inverted': this.soloInverted,
        'v-text-field--box': this.box,
        'v-text-field--enclosed': this.isEnclosed,
        'v-text-field--reverse': this.reverse,
        'v-text-field--outline': this.hasOutline,
        'elevation-0': this.flat
      }
    },
    directivesInput () {
      return this.box
        ? [{ name: 'ripple', value: true }]
        : []
    },
    // TODO: Deprecate
    hasOutline () {
      return this.outline || this.textarea
    },
    internalValue: {
      get () {
        return this.lazyValue
      },
      set (val) {
        if (this.mask) {
          this.lazyValue = this.unmaskText(this.maskText(this.unmaskText(val)))
          this.setSelectionRange()
        } else {
          this.lazyValue = val
          this.$emit('input', this.lazyValue)
        }
      }
    },
    isDirty () {
      return (this.lazyValue != null &&
        this.lazyValue.toString().length > 0) ||
        this.badInput
    },
    isEnclosed () {
      return this.isSolo || this.hasOutline
    },
    isLabelActive () {
      return this.isDirty || dirtyTypes.includes(this.type)
    },
    isSingle () {
      return this.isSolo || this.singleLine
    },
    isSolo () {
      return this.solo || this.soloInverted
    }
  },

  watch: {
    isFocused (val) {
      // Sets validationState from validatable
      this.hasColor = val

      if (val) {
        this.initialValue = this.lazyValue
      } else if (this.initialValue !== this.lazyValue) {
        this.$emit('change', this.lazyValue)
      }
    },
    value (val) {
      if (this.mask && !this.internalChange) {
        const masked = this.maskText(this.unmaskText(val))
        this.lazyValue = this.unmaskText(masked)

        // Emit when the externally set value was modified internally
        String(val) !== this.lazyValue && this.$nextTick(() => {
          this.$refs.input.value = masked
          this.$emit('input', this.lazyValue)
        })
      } else this.lazyValue = val

      if (this.internalChange) this.internalChange = false
    }
  },

  mounted () {
    this.autofocus && this.onFocus()
  },

  methods: {
    clearableCallback () {
      this.internalValue = null
      this.$nextTick(() => this.$refs.input.focus())
    },
    genAppendSlot () {
      const slot = []

      if (this.$slots['append-outer']) {
        slot.push(this.$slots['append-outer'])
      } else if (this.$slots['append-outer-icon']) {
        slot.push(this.$slots['append-outer-icon'])
      } else if (this.appendOuterIcon) {
        slot.push(this.genIcon('appendOuter'))
      }

      return this.genSlot('append', 'outer', slot)
    },
    genCounter () {
      if (this.counter === false) return null

      const value = (this.internalValue || '').length
      const max = this.counter === true ? this.$attrs.maxlength : this.counter

      return this.$createElement(VCounter, {
        props: {
          value,
          max
        }
      })
    },
    genDefaultSlot () {
      return [
        this.genTextFieldSlot(),
        this.genIconSlot()
      ]
    },
    genLabel () {
      if (this.isSingle &&
        (this.isDirty || !!this.placeholder)
      ) return null

      const isSingleLine = this.isSingle
      let value = 0
      let left = 'auto'
      let right = 'auto'

      // Create spacing
      if ((this.prefix || this.reverse) &&
        (isSingleLine || !this.isFocused) &&
        !this.isDirty
      ) value = 16

      // Check if RTL
      if (this.$vuetify.rtl) right = value
      else left = value

      // Check if reversed
      if (this.reverse) {
        const direction = right
        right = left
        left = direction
      }

      const data = {
        props: {
          absolute: true,
          color: this.validationState,
          disabled: this.disabled,
          focused: !isSingleLine && (this.isFocused || !!this.validationState),
          left,
          right,
          value: Boolean(!isSingleLine &&
            (this.isFocused || this.isDirty || this.placeholder))
        }
      }

      if (this.$attrs.id) data.props.for = this.$attrs.id

      return this.$createElement(VLabel, data, this.$slots.label || this.label)
    },
    genIconSlot () {
      const slot = []

      if (this.$slots['append']) {
        slot.push(this.$slots['append'])
      } else if (this.$slots['append-icon']) {
        slot.push(this.$slots['append-icon'])
      } else if (this.appendIcon) {
        slot.push(this.genIcon('append'))
      } else if (this.clearable && this.isDirty) {
        slot.push(this.genIcon('clear',
          this.clearIconCb || this.clearableCallback
        ))
      }

      return this.genSlot('append', 'inner', slot)
    },
    genInput () {
      const listeners = Object.assign({}, this.$listeners)
      delete listeners['change'] // Change should not be bound externally

      const data = {
        style: {},
        domProps: {
          value: this.maskText(this.lazyValue)
        },
        attrs: {
          ...this.$attrs,
          autofocus: this.autofocus,
          disabled: this.disabled,
          required: this.required,
          readonly: this.readonly,
          tabindex: this.tabindex,
          type: this.type,
          'aria-label': (!this.$attrs || !this.$attrs.id) && this.label // Label `for` will be set if we have an id
        },
        on: Object.assign(listeners, {
          blur: this.onBlur,
          input: this.onInput,
          focus: this.onFocus,
          keydown: this.onKeyDown
        }),
        ref: 'input'
      }

      if (this.placeholder) data.attrs.placeholder = this.placeholder
      if (this.mask) data.attrs.maxlength = this.masked.length
      if (this.browserAutocomplete) data.attrs.autocomplete = this.browserAutocomplete

      return this.$createElement('input', data)
    },
    genMessages () {
      return this.$createElement('div', {
        staticClass: 'v-text-field__details'
      }, [
        VInput.methods.genMessages.call(this),
        this.genCounter()
      ])
    },
    genTextFieldSlot () {
      return this.$createElement('div', {
        staticClass: 'v-text-field__slot'
      }, [
        this.genLabel(),
        this.prefix ? this.genAffix('prefix') : null,
        this.genInput(),
        this.suffix ? this.genAffix('suffix') : null
      ])
    },
    genAffix (type) {
      return this.$createElement('div', {
        'class': `v-text-field__${type}`,
        ref: type
      }, this[type])
    },
    onBlur (e) {
      this.isFocused = false
      // Reset internalChange state
      // to allow external change
      // to persist
      this.internalChange = false

      this.$emit('blur', e)
    },
    onClick () {
      if (this.isFocused || this.disabled) return

      this.$refs.input.focus()
    },
    onFocus (e) {
      if (!this.$refs.input) return

      if (document.activeElement !== this.$refs.input) {
        return this.$refs.input.focus()
      }

      this.isFocused = true
      this.$emit('focus', e)
    },
    onInput (e) {
      this.mask && this.resetSelections(e.target)
      this.internalValue = e.target.value
      this.badInput = e.target.validity && e.target.validity.badInput
    },
    onKeyDown (e) {
      this.internalChange = true

      if (e.keyCode === keyCodes.enter) this.$emit('change', this.internalValue)
    },
    onMouseDown (e) {
      // Prevent input from being blurred
      if (e.target !== this.$refs.input) {
        e.preventDefault()
        e.stopPropagation()
      }

      VInput.methods.onMouseDown.call(this, e)
    },
    onMouseUp (e) {
      // Default click handler is on slot,
      // Mouse events are to enable specific
      // input types when clicked
      if (
        (this.isSolo || this.hasOutline) &&
        document.activeElement !== this.$refs.input
      ) {
        this.$refs.input.focus()
      }

      VInput.methods.onMouseUp.call(this, e)
    }
  }
}


import '../../stylus/components/_carousel.styl'

import VBtn from '../VBtn'
import VIcon from '../VIcon'

import Bootable from '../../mixins/bootable'
import Themeable from '../../mixins/themeable'
import { provide as RegistrableProvide } from '../../mixins/registrable'

import Touch from '../../directives/touch'

export default {
  name: 'v-carousel',

  mixins: [Bootable, Themeable, RegistrableProvide('carousel')],

  directives: { Touch },

  data () {
    return {
      inputValue: null,
      items: [],
      slideTimeout: null,
      reverse: false
    }
  },

  props: {
    cycle: {
      type: Boolean,
      default: true
    },
    delimiterIcon: {
      type: String,
      default: '$vuetify.icons.delimiter'
    },
    hideControls: Boolean,
    hideDelimiters: Boolean,
    interval: {
      type: [Number, String],
      default: 6000,
      validator: value => value > 0
    },
    nextIcon: {
      type: [Boolean, String],
      default: '$vuetify.icons.next'
    },
    prevIcon: {
      type: [Boolean, String],
      default: '$vuetify.icons.prev'
    },
    value: Number
  },

  watch: {
    items () {
      if (this.inputValue >= this.items.length) {
        this.inputValue = this.items.length - 1
      }
    },
    inputValue () {
      // Evaluates items when inputValue changes to
      // account for dynamic changing of children

      const uid = (this.items[this.inputValue] || {}).uid
      for (let index = this.items.length; --index >= 0;) {
        this.items[index].open(uid, this.reverse)
      }

      this.$emit('input', this.inputValue)
      this.restartTimeout()
    },
    value (val) {
      this.inputValue = val
    },
    interval () {
      this.restartTimeout()
    },
    cycle (val) {
      if (val) {
        this.restartTimeout()
      } else {
        clearTimeout(this.slideTimeout)
        this.slideTimeout = null
      }
    }
  },

  mounted () {
    this.init()
  },

  methods: {
    genDelimiters () {
      return this.$createElement('div', {
        staticClass: 'v-carousel__controls'
      }, this.genItems())
    },
    genIcon (direction, icon, fn) {
      if (!icon) return null

      return this.$createElement('div', {
        staticClass: `v-carousel__${direction}`
      }, [
        this.$createElement(VBtn, {
          props: {
            icon: true,
            dark: this.dark || !this.light,
            light: this.light
          },
          on: { click: fn }
        }, [
          this.$createElement(VIcon, {
            props: { 'size': '46px' }
          }, icon)
        ])
      ])
    },
    genItems () {
      return this.items.map((item, index) => {
        return this.$createElement(VBtn, {
          class: {
            'v-carousel__controls__item': true,
            'v-carousel__controls__item--active': index === this.inputValue
          },
          props: {
            icon: true,
            small: true,
            dark: this.dark || !this.light,
            light: this.light
          },
          key: index,
          on: { click: this.select.bind(this, index) }
        }, [this.$createElement(VIcon, {
          props: { size: '18px' }
        }, this.delimiterIcon)])
      })
    },
    restartTimeout () {
      this.slideTimeout && clearTimeout(this.slideTimeout)
      this.slideTimeout = null

      const raf = requestAnimationFrame || setTimeout
      raf(this.startTimeout)
    },
    init () {
      this.inputValue = this.value || 0
    },
    next () {
      this.reverse = false
      this.inputValue = (this.inputValue + 1) % this.items.length
    },
    prev () {
      this.reverse = true
      this.inputValue = (this.inputValue + this.items.length - 1) % this.items.length
    },
    select (index) {
      this.reverse = index < this.inputValue
      this.inputValue = index
    },
    startTimeout () {
      if (!this.cycle) return

      this.slideTimeout = setTimeout(() => this.next(), this.interval > 0 ? this.interval : 6000)
    },
    register (uid, open) {
      this.items.push({ uid, open })
    },
    unregister (uid) {
      this.items = this.items.filter(i => i.uid !== uid)
    }
  },

  render (h) {
    return h('div', {
      staticClass: 'v-carousel',
      directives: [{
        name: 'touch',
        value: {
          left: this.next,
          right: this.prev
        }
      }]
    }, [
      this.hideControls ? null : this.genIcon('left', this.prevIcon, this.prev),
      this.hideControls ? null : this.genIcon('right', this.nextIcon, this.next),
      this.hideDelimiters ? null : this.genDelimiters(),
      this.$slots.default
    ])
  }
}


@import '../bootstrap'

.v-time-picker-title
  color: #fff
  display: flex
  line-height: 1
  justify-content: flex-end

.v-time-picker-title__time
  white-space: nowrap

  .v-picker__title__btn,
  span
    align-items: center
    display: inline-flex
    height: 70px
    font-size: 70px
    justify-content: center

.v-time-picker-title__ampm
  align-self: flex-end
  display: flex
  flex-direction: column
  font-size: 16px
  margin: 8px 0 6px 8px
  text-transform: uppercase

  div:only-child
    flex-direction: row

.v-picker__title--landscape
  .v-time-picker-title
    flex-direction: column
    justify-content: center
    height: 100%

  .v-time-picker-title__time
    text-align: right

    .v-picker__title__btn,
    span
      height: 55px
      font-size: 55px

  .v-time-picker-title__ampm
    margin: 16px 0 0
    align-self: initial
    text-align: center


@import '../bootstrap'
@import '../theme'

v-autocomplete($material)
  //

theme(v-autocomplete, 'v-autocomplete')

.v-autocomplete
  input
    align-self: center

  &--is-selecting-index
    input
      opacity: 0

  &.v-select.v-text-field
    input
      pointer-events: auto

  &__content.v-menu__content
    border-radius: 0

    .v-card
      border-radius: 0