if (await this.isEmitAllowed(subscription, eventName, ...args)) {

await new Promise<void>((resolve, reject) => {

const frame = data[subscription.client.meteorClient ? 'meteor' : 'normal'];

subscription.client.ws._sender.sendFrame(frame, (err: unknown) => {

if (!(await this.isEmitAllowed(subscription, eventName, ...args))) {


if (this.retransmitToSelf === false && origin && origin === subscription.connection) {

keep keep keep keep replace keep keep keep keep

import { escapeRegExp } from '@rocket.chat/string-helpers';
import type { ILivechatBusinessHour } from '@rocket.chat/core-typings';
import { LivechatBusinessHours } from '@rocket.chat/models';

import { hasPermissionAsync } from '../../../../../../app/authorization/server/functions/hasPermission';
import type { IPaginatedResponse, IPagination } from '../../api/lib/definition';

interface IResponse extends IPaginatedResponse {
	businessHours: ILivechatBusinessHour[];
}

export async function findBusinessHours(userId: string, { offset, count, sort }: IPagination, name?: string): Promise<IResponse> {
	if (!(await hasPermissionAsync(userId, 'view-livechat-business-hours'))) {
		throw new Error('error-not-authorized');
	}
	const query = {};
	if (name) {
		const filterReg = new RegExp(escapeRegExp(name), 'i');
		Object.assign(query, { name: filterReg });
	}
	const { cursor, totalCount } = LivechatBusinessHours.findPaginated(query, {
		sort: sort || { name: 1 },
		skip: offset,
		limit: count,
	});

	const [businessHours, total] = await Promise.all([cursor.toArray(), totalCount]);

	return {
		businessHours,
		count: businessHours.length,
		offset,
		total,
	};
}


import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';

import { getWorkspaceAccessToken } from '../../app/cloud/server';
import { hasPermission } from '../../app/authorization/server';
import { settings } from '../../app/settings/server';
import { appTokensCollection, Push } from '../../app/push/server';

Meteor.methods({
	push_test() {
		const user = Meteor.user();

		if (!user) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'push_test',
			});
		}

		if (!hasPermission(user._id, 'test-admin-options')) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'push_test',
			});
		}

		if (settings.get('Push_enable') !== true) {
			throw new Meteor.Error('error-push-disabled', 'Push is disabled', {
				method: 'push_test',
			});
		}

		const query = {
			$and: [
				{
					userId: user._id,
				},
				{
					$or: [
						{
							'token.apn': {
								$exists: true,
							},
						},
						{
							'token.gcm': {
								$exists: true,
							},
						},
					],
				},
			],
		};

		const tokens = appTokensCollection.find(query).count();

		if (tokens === 0) {
			throw new Meteor.Error('error-no-tokens-for-this-user', 'There are no tokens for this user', {
				method: 'push_test',
			});
		}

		Push.send({
			from: 'push',
			title: `@${user.username}`,
			text: TAPi18n.__('This_is_a_push_test_messsage'),
			apn: {
				text: `@${user.username}:\n${TAPi18n.__('This_is_a_push_test_messsage')}`,
			},
			sound: 'default',
			userId: user._id,
		});

		return {
			message: 'Your_push_was_sent_to_s_devices',
			params: [tokens],
		};
	},
});

settings.watch<boolean>('Push_enable', async function (enabled) {
	if (!enabled) {
		return;
	}
	const gateways =
		settings.get('Push_enable_gateway') && settings.get('Register_Server') && settings.get('Cloud_Service_Agree_PrivacyTerms')
			? settings.get<string>('Push_gateway').split('\n')
			: undefined;

	let apn:
		| {
				apiKey?: string;
				passphrase: string;
				key: string;
				cert: string;
				gateway?: string;
		  }
		| undefined;
	let gcm:
		| {
				apiKey: string;
				projectNumber: string;
		  }
		| undefined;

	if (!gateways) {
		gcm = {
			apiKey: settings.get('Push_gcm_api_key'),
			projectNumber: settings.get('Push_gcm_project_number'),
		};

		apn = {
			passphrase: settings.get('Push_apn_passphrase'),
			key: settings.get('Push_apn_key'),
			cert: settings.get('Push_apn_cert'),
		};

		if (settings.get('Push_production') !== true) {
			apn = {
				passphrase: settings.get('Push_apn_dev_passphrase'),
				key: settings.get('Push_apn_dev_key'),
				cert: settings.get('Push_apn_dev_cert'),
				gateway: 'gateway.sandbox.push.apple.com',
			};
		}

		if (!apn.key || apn.key.trim() === '' || !apn.cert || apn.cert.trim() === '') {
			apn = undefined;
		}

		if (!gcm.apiKey || gcm.apiKey.trim() === '' || !gcm.projectNumber || gcm.projectNumber.trim() === '') {
			gcm = undefined;
		}
	}

	Push.configure({
		apn,
		gcm,
		production: settings.get('Push_production'),
		gateways,
		uniqueId: settings.get('uniqueID'),
		getAuthorization() {
			return `Bearer ${Promise.await(getWorkspaceAccessToken())}`;
		},
	});
});


import type { IRoom } from '@rocket.chat/core-typings';
import type { Dispatch, SetStateAction } from 'react';
import { useCallback, useMemo, useState } from 'react';

import { RoomManager, RoomHistoryManager } from '../../../../../app/ui-utils/client';
import { useReactiveValue } from '../../../../hooks/useReactiveValue';

export const useUnreadMessages = (
	room: IRoom,
): readonly [
	data:
		| {
				count: number;
				since: Date;
		  }
		| undefined,
	setUnreadCount: Dispatch<SetStateAction<number>>,
] => {
	const notLoadedCount = useReactiveValue(useCallback(() => RoomHistoryManager.getRoom(room._id).unreadNotLoaded.get(), [room._id]));
	const [loadedCount, setLoadedCount] = useState(0);

	const count = useMemo(() => notLoadedCount + loadedCount, [notLoadedCount, loadedCount]);

	const since = useReactiveValue(useCallback(() => RoomManager.getOpenedRoomByRid(room._id)?.unreadSince.get(), [room._id]));

	return useMemo(() => {
		if (count && since) {
			return [{ count, since }, setLoadedCount];
		}

		return [undefined, setLoadedCount];
	}, [count, since]);
};


return reject(err);
}
resolve();
});
});
}
}
}
}

keep keep keep keep add keep keep keep keep

if (error.code === 'ERR_STREAM_DESTROYED') {

console.warn('Trying to send data to destroyed stream, closing connection.');

// if we still tried to send data to a destroyed stream, we'll try again to close the connection

if (subscription.client.ws.readyState !== WebSocket.OPEN) {

console.error('Error trying to send data to stream.', error);

import { EventEmitter } from 'events';

import type WebSocket from 'ws';
import ejson from 'ejson';
import { v1 as uuidv1 } from 'uuid';
import { MeteorService, isMeteorError, MeteorError } from '@rocket.chat/core-services';

import { DDP_EVENTS } from './constants';
import { Publication } from './Publication';
import type { Client } from './Client';
import type { IPacket } from './types/IPacket';
import { Logger } from '../../../../apps/meteor/server/lib/logger/Logger';

const logger = new Logger('DDP-Streamer');

type SubscriptionFn = (this: Publication, eventName: string, options: object) => void;
type MethodFn = (this: Client, ...args: any[]) => any;
type Methods = {
	[k: string]: MethodFn;
};

const handleInternalException = (err: unknown, msg: string): MeteorError => {
	if (err instanceof MeteorError) {
		return err;
	}

	// default errors are logged to the console and redacted from the client
	// TODO switch to using the logger (ideally broker.logger)
	logger.error({ msg, err });

	return new MeteorError(500, 'Internal server error');
};

export const SERVER_ID = ejson.stringify({ server_id: '0' });

export class Server extends EventEmitter {
	private _subscriptions = new Map<string, SubscriptionFn>();

	private _methods = new Map<string, MethodFn>();

	public readonly id = uuidv1();

	serialize = ejson.stringify;

	parse = (data: WebSocket.Data, isBinary: boolean): IPacket => {
		if (isBinary) {
			throw new MeteorError(500, 'Binary data not supported');
		}
		const packet = data.toString();

		const payload = packet.startsWith('[') ? JSON.parse(packet)[0] : packet;
		return ejson.parse(payload);
	};

	async call(client: Client, packet: IPacket): Promise<void> {
		try {
			// if method was not defined on DDP Streamer we fall back to Meteor
			if (!this._methods.has(packet.method)) {
				const result = await MeteorService.callMethodWithToken(client.userId, client.userToken, packet.method, packet.params);
				if (result?.result) {
					return this.result(client, packet, result.result);
				}

				throw new MeteorError(404, `Method '${packet.method}' not found`);
			}

			const fn = this._methods.get(packet.method);
			if (!fn) {
				throw new MeteorError(404, `Method '${packet.method}' not found`);
			}

			const result = await fn.apply(client, packet.params);
			return this.result(client, packet, result);
		} catch (err: unknown) {
			return this.result(client, packet, null, handleInternalException(err, 'Method call error'));
		}
	}

	methods(obj: Methods): void {
		Object.entries(obj).forEach(([name, fn]) => {
			if (this._methods.has(name)) {
				return;
			}
			this._methods.set(name, fn);
		});
	}

	async subscribe(client: Client, packet: IPacket): Promise<void> {
		try {
			if (!this._subscriptions.has(packet.name)) {
				throw new MeteorError(404, `Subscription '${packet.name}' not found`);
			}
			const fn = this._subscriptions.get(packet.name);
			if (!fn) {
				throw new MeteorError(404, `Subscription '${packet.name}' not found`);
			}

			const publication = new Publication(client, packet, this);
			const [eventName, options] = packet.params;
			await fn.call(publication, eventName, options);
		} catch (err: unknown) {
			return this.nosub(client, packet, handleInternalException(err, 'Subscription error'));
		}
	}

	publish(name: string, fn: SubscriptionFn): void {
		if (this._subscriptions.has(name)) {
			return;
		}
		this._subscriptions.set(name, fn);
	}

	stream(stream: string, fn: SubscriptionFn): void {
		return this.publish(`stream-${stream}`, fn);
	}

	result(client: Client, { id }: IPacket, result?: any, error?: Error | MeteorError): void {
		client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.RESULT,
				id,
				...(result && { result }),
				...(error && { error: isMeteorError(error) ? error.toJSON() : error }),
			}),
		);
		return client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.UPDATED,
				[DDP_EVENTS.METHODS]: [id],
			}),
		);
	}

	nosub(client: Client, { id }: IPacket, error?: Error | MeteorError): void {
		return client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.NO_SUBSCRIBE,
				id,
				...(error && { error: isMeteorError(error) ? error.toJSON() : error }),
			}),
		);
	}

	ready(client: Client, packet: IPacket): void {
		return client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.READY,
				[DDP_EVENTS.SUBSCRIPTIONS]: [packet.id],
			}),
		);
	}

	added(client: Client, collection: string, id: string, fields: any): void {
		return client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.ADDED,
				[DDP_EVENTS.COLLECTION]: collection,
				[DDP_EVENTS.ID]: id,
				[DDP_EVENTS.FIELDS]: fields,
			}),
		);
	}

	changed(client: Client, collection: string, id: string, fields: any): void {
		return client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.CHANGED,
				[DDP_EVENTS.COLLECTION]: collection,
				[DDP_EVENTS.ID]: id,
				[DDP_EVENTS.FIELDS]: fields,
			}),
		);
	}

	removed(client: Client, collection: string, id: string): void {
		return client.send(
			this.serialize({
				[DDP_EVENTS.MSG]: DDP_EVENTS.REMOVED,
				[DDP_EVENTS.COLLECTION]: collection,
				[DDP_EVENTS.ID]: id,
			}),
		);
	}
}


import type { IRoom } from '@rocket.chat/core-typings';
import { useRoute } from '@rocket.chat/ui-contexts';
import type { ReactNode, ContextType, ReactElement } from 'react';
import React, { useMemo, memo, useEffect, useCallback } from 'react';

import { RoomHistoryManager } from '../../../../app/ui-utils/client';
import { UserAction } from '../../../../app/ui/client';
import { useReactiveQuery } from '../../../hooks/useReactiveQuery';
import { useReactiveValue } from '../../../hooks/useReactiveValue';
import { RoomManager } from '../../../lib/RoomManager';
import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
import RoomNotFound from '../RoomNotFound';
import RoomSkeleton from '../RoomSkeleton';
import { useRoomRolesManagement } from '../components/body/useRoomRolesManagement';
import { RoomAPIContext } from '../contexts/RoomAPIContext';
import { RoomContext } from '../contexts/RoomContext';
import ToolboxProvider from './ToolboxProvider';

type RoomProviderProps = {
	children: ReactNode;
	rid: IRoom['_id'];
};

const RoomProvider = ({ rid, children }: RoomProviderProps): ReactElement => {
	useRoomRolesManagement(rid);

	const roomQuery = useReactiveQuery(['rooms', rid], ({ rooms }) => rooms.findOne({ _id: rid }));

	// TODO: the following effect is a workaround while we don't have a general and definitive solution for it
	const homeRoute = useRoute('home');
	useEffect(() => {
		if (roomQuery.isSuccess && !roomQuery.data) {
			homeRoute.push();
		}
	}, [roomQuery.isSuccess, roomQuery.data, homeRoute]);

	const subscriptionQuery = useReactiveQuery(['subscriptions', { rid }], ({ subscriptions }) => subscriptions.findOne({ rid }) ?? null);

	const pseudoRoom = useMemo(() => {
		if (!roomQuery.data) {
			return null;
		}

		return {
			...subscriptionQuery.data,
			...roomQuery.data,
			name: roomCoordinator.getRoomName(roomQuery.data.t, roomQuery.data),
		};
	}, [roomQuery.data, subscriptionQuery.data]);

	const { hasMorePreviousMessages, hasMoreNextMessages, isLoadingMoreMessages } = useReactiveValue(
		useCallback(() => {
			const { hasMore, hasMoreNext, isLoading } = RoomHistoryManager.getRoom(rid);

			return {
				hasMorePreviousMessages: hasMore.get(),
				hasMoreNextMessages: hasMoreNext.get(),
				isLoadingMoreMessages: isLoading.get(),
			};
		}, [rid]),
	);

	const context = useMemo((): ContextType<typeof RoomContext> => {
		if (!pseudoRoom) {
			return null;
		}

		return {
			rid,
			room: pseudoRoom,
			subscription: subscriptionQuery.data ?? undefined,
			hasMorePreviousMessages,
			hasMoreNextMessages,
			isLoadingMoreMessages,
		};
	}, [hasMoreNextMessages, hasMorePreviousMessages, isLoadingMoreMessages, pseudoRoom, rid, subscriptionQuery.data]);

	useEffect(() => {
		RoomManager.open(rid);
		return (): void => {
			RoomManager.back(rid);
		};
	}, [rid]);

	const subscribed = !!subscriptionQuery.data;

	useEffect(() => {
		if (!subscribed) {
			return;
		}

		UserAction.addStream(rid);
		return (): void => {
			try {
				UserAction.cancel(rid);
			} catch (error) {
				// Do nothing
			}
		};
	}, [rid, subscribed]);

	const api = useMemo(() => ({}), []);

	if (!pseudoRoom) {
		return roomQuery.isSuccess ? <RoomNotFound /> : <RoomSkeleton />;
	}

	return (
		<RoomAPIContext.Provider value={api}>
			<RoomContext.Provider value={context}>
				<ToolboxProvider room={pseudoRoom}>{children}</ToolboxProvider>
			</RoomContext.Provider>
		</RoomAPIContext.Provider>
	);
};

export default memo(RoomProvider);


import { Box, Skeleton, Margins } from '@rocket.chat/fuselage';
import type { FC } from 'react';
import React from 'react';

const AccordionLoading: FC = () => (
	<Box w='full' alignSelf='center'>
		<Margins block='x2'>
			<Skeleton variant='rect' width='100%' height='x80' />
			<Skeleton variant='rect' width='100%' height='x80' />
			<Skeleton variant='rect' width='100%' height='x80' />
		</Margins>
	</Box>
);

export default AccordionLoading;


import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { LivechatVisitors } from '@rocket.chat/models';

import { hasPermission } from '../../../authorization/server';
import { LivechatRooms, Subscriptions, Users } from '../../../models/server';
import { Livechat } from '../lib/Livechat';
import { normalizeTransferredByData } from '../lib/Helper';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';

// Deprecated in favor of "livechat/room.forward" endpoint
// TODO: Deprecated: Remove in v6.0.0
Meteor.methods({
	async 'livechat:transfer'(transferData) {
		methodDeprecationLogger.warn('livechat:transfer method is deprecated in favor of "livechat/room.forward" endpoint');
		if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'view-l-room')) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:transfer' });
		}

		check(transferData, {
			roomId: String,
			userId: Match.Optional(String),
			departmentId: Match.Optional(String),
			comment: Match.Optional(String),
			clientAction: Match.Optional(Boolean),
		});

		const room = LivechatRooms.findOneById(transferData.roomId);
		if (!room || room.t !== 'l') {
			throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'livechat:transfer' });
		}

		if (!room.open) {
			throw new Meteor.Error('room-closed', 'Room closed', { method: 'livechat:transfer' });
		}

		const subscription = Subscriptions.findOneByRoomIdAndUserId(room._id, Meteor.userId(), {
			fields: { _id: 1 },
		});
		if (!subscription && !hasPermission(Meteor.userId(), 'transfer-livechat-guest')) {
			throw new Meteor.Error('error-not-authorized', 'Not authorized', {
				method: 'livechat:transfer',
			});
		}

		const guest = await LivechatVisitors.findOneById(room.v && room.v._id);
		transferData.transferredBy = normalizeTransferredByData(Meteor.user() || {}, room);
		if (transferData.userId) {
			const userToTransfer = Users.findOneById(transferData.userId);
			transferData.transferredTo = {
				_id: userToTransfer._id,
				username: userToTransfer.username,
				name: userToTransfer.name,
			};
		}

		return Livechat.transfer(room, guest, transferData);
	},
});


"alt": "Attaches an alt property to the parallax image",

keep keep keep keep keep replace keep keep

// Style
import './VParallax.sass'

// Mixins
import Translatable from '../../mixins/translatable'

// Types
import { VNode, VNodeData } from 'vue/types/vnode'
import mixins from '../../util/mixins'

const baseMixins = mixins(
  Translatable
)
interface options extends InstanceType<typeof baseMixins> {
  $refs: {
    img: HTMLImageElement
  }
}

/* @vue/component */
export default baseMixins.extend<options>().extend({
  name: 'v-parallax',

  props: {
    alt: {
      type: String,
      default: '',
    },
    height: {
      type: [String, Number],
      default: 500,
    },
    src: String,
  },

  data: () => ({
    isBooted: false,
  }),

  computed: {
    styles (): object {
      return {
        display: 'block',
        opacity: this.isBooted ? 1 : 0,
        transform: `translate(-50%, ${this.parallax}px)`,
      }
    },
  },

  mounted () {
    this.init()
  },

  methods: {
    init () {
      const img = this.$refs.img

      if (!img) return

      if (img.complete) {
        this.translate()
        this.listeners()
      } else {
        img.addEventListener('load', () => {
          this.translate()
          this.listeners()
        }, false)
      }

      this.isBooted = true
    },
    objHeight () {
      return this.$refs.img.naturalHeight
    },
  },

  render (h): VNode {
    const imgData: VNodeData = {
      staticClass: 'v-parallax__image',
      style: this.styles,
      attrs: {
        src: this.src,
        alt: this.alt,
      },
      ref: 'img',
    }

    const container = h('div', {
      staticClass: 'v-parallax__image-container',
    }, [
      h('img', imgData),
    ])

    const content = h('div', {
      staticClass: 'v-parallax__content',
    }, this.$slots.default)

    return h('div', {
      staticClass: 'v-parallax',
      style: {
        height: `${this.height}px`,
      },
      on: this.$listeners,
    }, [container, content])
  },
})


# Обзор API компонентов

Быстрый поиск и навигация всех компонентов в спецификации Material Design, а также много другое! Выберите компонент и быстро просмотрите его API и пример использования

Выберите компонент для просмотра его API

Поиск компонента

Обзор Категорий


# Vuetify pour entreprises

### Disponible dans le cadre de l'abonnement Tidelift

Vuetify et les développeurs de milliers d'autres packages travaillent avec Tidelift pour fournir un abonnement d'entreprise qui couvre tous les logiciels libres que vous utilisez.

Si vous voulez la flexibilité de l'open source et la confiance des logiciels de qualité commerciale, c'est pour vous.

### L'abonnement Tidelift gère vos dépendances pour vous

Obtenez les outils dont vous avez besoin pour cataloguer et comprendre le logiciel open source dont dépend votre application.

Votre abonnement aide à payer les responsables de la communauté open source et maintenir les packages que vous utilisez afin qu'ils répondent à vos standards

Traiter les problèmes de manière proactive, avec des outils qui analysent les nouveaux problèmes de sécurité, de licence et de maintenance, et alertent nos responsables Open Source pour qu'ils puissent les résoudre en votre nom.

Mesurez et améliorez la santé de vos dépendances open source - ce qui améliore la santé de votre application - et obtenez une courte liste de mesures à fort impact que votre équipe peut prendre pour les améliorer encore plus.

Obtenez des garanties commerciales qui ne sont pas offertes gratuitement avec les logiciels libres, comme l'indemnisation de la propriété intellectuelle et le soutien dans le cadre d'un contrat de niveau de service. Vous attendez ces garanties des logiciels propriétaires, et vous pouvez les obtenir en utilisant l'open source.

Le résultat final ? Toutes les fonctionnalités que vous attendez d'un logiciel de qualité commerciale, pour l'ensemble des logiciels libres que vous utilisez. Cela signifie moins de temps à se débattre avec des quiz ésotériques open source, et plus de temps à construire vos propres applications - et votre entreprise.


height: {
type: [String, Number],
default: 500,
},
src: String,
},
data: () => ({
isBooted: false,


<template>
  <v-card
    class="mx-auto"
    color="#26c6da"
    dark
    max-width="400"
  >
    <v-card-title>
      <v-icon
        large
        left
      >
        mdi-twitter
      </v-icon>
      Twitter
    </v-card-title>

    <v-card-text class="headline font-weight-bold">
      "Turns out semicolon-less style is easier and safer in TS because most gotcha edge cases are type invalid as well."
    </v-card-text>

    <v-card-actions>
      <v-list-item class="grow">
        <v-list-item-avatar color="grey darken-3">
          <v-img
            class="elevation-6"
            src="https://avataaars.io/?avatarStyle=Transparent&topType=ShortHairShortCurly&accessoriesType=Prescription02&hairColor=Black&facialHairType=Blank&clotheType=Hoodie&clotheColor=White&eyeType=Default&eyebrowType=DefaultNatural&mouthType=Default&skinColor=Light"
          ></v-img>
        </v-list-item-avatar>

        <v-list-item-content>
          <v-list-item-title>Evan You</v-list-item-title>
        </v-list-item-content>

        <v-row
          align="center"
          justify="end"
        >
          <v-icon class="mr-1">mdi-heart</v-icon>
          256
          ·
          <v-icon class="mr-1">mdi-share-variant</v-icon>
          45
        </v-row>
      </v-list-item>
    </v-card-actions>
  </v-card>
</template>


const spawn = require('cross-spawn')

let target = process.argv[2]
const alias = {
  docs: 'vuetifyjs.com',
}
target = alias[target] || target

if (!target) {
  spawn('yarn', ['lerna', 'run', 'dev', '--scope', 'vuetify', '--stream'], { stdio: 'inherit' })
} else {
  spawn('yarn', ['lerna', 'run', 'dev', '--scope', target, '--stream'], { stdio: 'inherit' })
}


Zero Theme Pro

Zero is a carefully crafted multi-purpose, responsive, and gorgeous theme that delivers a clean user experience.

Material Dashboard Pro

Vuetify Material Dashboard PRO is a beautiful theme built over Vuetify, Vuex and Vuejs.

Material Kit

A complete set of Material Inspired themes built with Vuetify on top of Vue CLI.

Alpha Theme

Complete theme experience including enhanced Vue CLI, full documentation, 5 custom components and much more!

Zero Theme Free

The Free version of the Zero theme.

Material Dashboard Free

Vuetify Material Dashboard is a beautiful resource built over Vuetify, Vuex and Vue.js.

Freelancer

A single page Material inspired theme for Freelancers.

Parallax

This beautiful single page parallax is a great home page for any application.

Blog

A simple template that features a clean interface for creating a blog or blog-like application.


@import './_variables.scss'
@import './_mixins.sass'

// Theme
+theme(v-treeview) using ($material)
  color: map-deep-get($material, 'text', 'primary')

  &--hoverable .v-treeview-node__root,
  .v-treeview-node--click > .v-treeview-node__root
    +states($material)

  .v-treeview-node__root.v-treeview-node--active
    +active-states($material)

  .v-treeview-node--disabled
    > .v-treeview-node__root > .v-treeview-node__content
      color: map-deep-get($material, 'text', 'disabled') !important

.v-treeview-node
  &.v-treeview-node--shaped
    +treeview-shaped($treeview-node-height, $treeview-node-shaped-margin)

  &.v-treeview-node--rounded
    +treeview-rounded($treeview-node-height, $treeview-node-shaped-margin)

  &--excluded
    display: none

  &--click
    > .v-treeview-node__root,
    > .v-treeview-node__root > .v-treeview-node__content > *
      cursor: pointer
      user-select: none

  &.v-treeview-node--active .v-treeview-node__content .v-icon
    color: inherit

.v-treeview-node__root
  display: flex
  align-items: center
  min-height: $treeview-node-height
  padding-left: $treeview-node-padding
  padding-right: $treeview-node-padding
  position: relative

  &::before
    background-color: currentColor
    bottom: 0
    content: ''
    left: 0
    opacity: 0
    pointer-events: none
    position: absolute
    right: 0
    top: 0
    transition: $primary-transition

  // Fix for IE11 where min-height does not work with
  // align-items: center in flex containers
  // https://github.com/philipwalton/flexbugs/issues/231
  &::after
    content: ''
    font-size: 0
    min-height: inherit

.v-treeview-node__children
  transition: all $treeview-transition

.v-treeview--dense
  .v-treeview-node__root
    min-height: $treeview-node-height-dense

  &.v-treeview-node--shaped
    +treeview-shaped($treeview-node-height-dense, $treeview-node-shaped-margin)

  &.v-treeview-node--rounded
    +treeview-rounded($treeview-node-height-dense, $treeview-node-shaped-margin)

.v-treeview-node__checkbox
  width: $treeview-node-level-width
  user-select: none

  +ltr()
    margin-left: $treeview-node-margin
  +rtl()
    margin-right: $treeview-node-margin

.v-treeview-node__toggle
  width: $treeview-node-level-width
  user-select: none

  &--loading
    animation: progress-circular-rotate 1s linear infinite

  +ltr()
    transform: rotate(-90deg)
    &--open
      transform: none

  +rtl()
    transform: rotate(90deg)
    &--open
      transform: none

.v-treeview-node__prepend
  min-width: $treeview-node-level-width

  +ltr()
    margin-right: $treeview-node-margin
  +rtl()
    margin-left: $treeview-node-margin

.v-treeview-node__append
  min-width: $treeview-node-level-width

  +ltr()
    margin-left: $treeview-node-margin
  +rtl()
    margin-right: $treeview-node-margin

.v-treeview-node__level
  width: $treeview-node-level-width

.v-treeview-node__label
  flex: 1
  font-size: $treeview-label-font-size
  overflow: hidden
  text-overflow: ellipsis
  white-space: nowrap

.v-treeview-node__content
  align-items: center
  display: flex
  flex-basis: 0%
  flex-grow: 1
  flex-shrink: 0
  min-width: 0

  // TODO: this is temporary fix for d-flex * shenanigans
  .v-btn
    flex-grow: 0 !important
    flex-shrink: 1 !important

  +ltr()
    margin-left: $treeview-node-margin
  +rtl()
    margin-right: $treeview-node-margin


Legt den Übergangsmodus fest (gilt nicht für Übergangs-Gruppen). Weitere Informationen finden Sie in der Vue Dokumentation [für Übergangsmodi](https://vuejs.org/v2/api/#transition).

Legt den Übergangspunkt des Elements fest. Mehr Informationen bezüglich [des Herkunfts-Übergangs](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin) finden Sie in der MDN-Dokumentation.

Legt den Komponenten-Übergang fest. Kann einer der [eingebauten Übergänge](/styles/transitions) oder ein eigener sein.


<!-- Vue HTML Markup -->

<template>
  <v-menu>
    <template v-slot:activator="{ on, attrs }">
      <v-btn
        v-bind="attrs"
        v-on="on"
      >
        Click me
      </v-btn>
    </template>

    <v-list>
      <v-list-item @click="method">
        <v-list-item-title>Option 1</v-list-item-title>
      </v-list-item>
      <v-list-item disabled>
        <v-list-item-title>Option 2</v-list-item-title>
      </v-list-item>
      <v-list-item @click="method">
        <v-list-item-title>Option 3</v-list-item-title>
      </v-list-item>
    </v-list>
  </v-menu>
</template>


const initFixture = require("@lerna-test/init-fixture")(__dirname);

keep keep keep add keep keep keep keep keep

const collectUpdates = require("@lerna/collect-updates");

"use strict";

const Command = require("@lerna/command");
const listable = require("@lerna/listable");
const output = require("@lerna/output");
const { getFilteredPackages } = require("@lerna/filter-options");

module.exports = factory;

function factory(argv) {
  return new ListCommand(argv);
}

class ListCommand extends Command {
  get requiresGit() {
    return false;
  }

  initialize() {
    let chain = Promise.resolve();

    chain = chain.then(() => getFilteredPackages(this.packageGraph, this.execOpts, this.options));
    chain = chain.then(filteredPackages => {
      this.result = listable.format(filteredPackages, this.options);
    });

    return chain;
  }

  execute() {
    output(this.result.text);

    this.logger.success(
      "found",
      "%d %s",
      this.result.count,
      this.result.count === 1 ? "package" : "packages"
    );
  }
}

module.exports.ListCommand = ListCommand;


@lerna/filter-options

Options for lerna sub-commands that need filtering


it("does not emit empty stdout", async () => {

expect.objectContaining({ since: "deadbeef" })

"use strict";

// mocked modules
const output = require("@lerna/output");

// helpers
const initFixture = require("@lerna-test/init-fixture")(__dirname);

// file under test
const lernaLs = require("@lerna-test/command-runner")(require("../command"));

// remove quotes around top-level strings
expect.addSnapshotSerializer({
  test(val) {
    return typeof val === "string";
  },
  serialize(val, config, indentation, depth) {
    // top-level strings don't need quotes, but nested ones do (object properties, etc)
    return depth ? `"${val}"` : val;
  },
});

// normalize temp directory paths in snapshots
expect.addSnapshotSerializer(require("@lerna-test/serialize-windows-paths"));
expect.addSnapshotSerializer(require("@lerna-test/serialize-tempdir"));

describe("lerna ls", () => {
  describe("in a basic repo", () => {
    let testDir;

    beforeAll(async () => {
      testDir = await initFixture("basic");
    });

    it("should list public packages", async () => {
      await lernaLs(testDir)();
      expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
package-3
package-4
`);
    });

    it("should also list private packages with --all", async () => {
      await lernaLs(testDir)("--all");
      expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
package-3
package-4
package-5 (PRIVATE)
`);
    });

    it("lists public package versions and relative paths with --long", async () => {
      await lernaLs(testDir)("--long");
      expect(output.logged()).toMatchInlineSnapshot(`
package-1 v1.0.0 packages/package-1
package-2 v1.0.0 packages/package-2
package-3 v1.0.0 packages/package-3
package-4 v1.0.0 packages/package-4
`);
    });

    it("lists all package versions and relative paths with --long --all", async () => {
      await lernaLs(testDir)("-la");
      expect(output.logged()).toMatchInlineSnapshot(`
package-1 v1.0.0 packages/package-1
package-2 v1.0.0 packages/package-2
package-3 v1.0.0 packages/package-3
package-4 v1.0.0 packages/package-4
package-5 v1.0.0 packages/package-5 (PRIVATE)
`);
    });

    it("lists public package locations with --parseable", async () => {
      await lernaLs(testDir)("--parseable");
      expect(output.logged()).toMatchInlineSnapshot(`
__TEST_ROOTDIR__/packages/package-1
__TEST_ROOTDIR__/packages/package-2
__TEST_ROOTDIR__/packages/package-3
__TEST_ROOTDIR__/packages/package-4
`);
    });

    it("lists all package locations with --parseable --all", async () => {
      await lernaLs(testDir)("-pa");
      expect(output.logged()).toMatchInlineSnapshot(`
__TEST_ROOTDIR__/packages/package-1
__TEST_ROOTDIR__/packages/package-2
__TEST_ROOTDIR__/packages/package-3
__TEST_ROOTDIR__/packages/package-4
__TEST_ROOTDIR__/packages/package-5
`);
    });

    it("lists public package locations with --parseable --long", async () => {
      await lernaLs(testDir)("--parseable", "--long");
      expect(output.logged()).toMatchInlineSnapshot(`
__TEST_ROOTDIR__/packages/package-1:package-1:1.0.0
__TEST_ROOTDIR__/packages/package-2:package-2:1.0.0
__TEST_ROOTDIR__/packages/package-3:package-3:1.0.0
__TEST_ROOTDIR__/packages/package-4:package-4:1.0.0
`);
    });

    it("lists all package locations with --parseable --long --all", async () => {
      await lernaLs(testDir)("-pal");
      expect(output.logged()).toMatchInlineSnapshot(`
__TEST_ROOTDIR__/packages/package-1:package-1:1.0.0
__TEST_ROOTDIR__/packages/package-2:package-2:1.0.0
__TEST_ROOTDIR__/packages/package-3:package-3:1.0.0
__TEST_ROOTDIR__/packages/package-4:package-4:1.0.0
__TEST_ROOTDIR__/packages/package-5:package-5:1.0.0:PRIVATE
`);
    });

    it("lists packages matching --scope", async () => {
      await lernaLs(testDir)("--scope", "package-1");
      expect(output.logged()).toMatchInlineSnapshot(`package-1`);
    });

    it("does not list packages matching --ignore", async () => {
      await lernaLs(testDir)("--ignore", "package-@(2|3|4|5)");
      expect(output.logged()).toMatchInlineSnapshot(`package-1`);
    });

    it("does not list private packages with --no-private", async () => {
      await lernaLs(testDir)("--no-private");
      expect(output.logged()).not.toMatch("package-5 v1.0.0 (private)");
    });
  });

  describe("in a repo with packages outside of packages/", () => {
    it("should list packages", async () => {
      const testDir = await initFixture("extra");
      await lernaLs(testDir)();
      expect(output.logged()).toMatchInlineSnapshot(`
package-3
package-1
package-2
`);
    });
  });

  describe("--include-filtered-dependencies", () => {
    it("should list packages, including filtered ones", async () => {
      const testDir = await initFixture("include-filtered-dependencies");
      await lernaLs(testDir)("--scope", "@test/package-2", "--include-filtered-dependencies");
      expect(output.logged()).toMatchInlineSnapshot(`
@test/package-2
@test/package-1
`);
    });
  });

  describe("--include-filtered-dependents", () => {
    it("should list packages, including filtered ones", async () => {
      const testDir = await initFixture("include-filtered-dependencies");
      await lernaLs(testDir)("--scope", "@test/package-1", "--include-filtered-dependents");
      expect(output.logged()).toMatchInlineSnapshot(`
@test/package-1
@test/package-2
`);
    });
  });

  describe("with an undefined version", () => {
    it("replaces version with MISSING", async () => {
      const testDir = await initFixture("undefined-version");
      await lernaLs(testDir)("--long");
      expect(output.logged()).toMatchInlineSnapshot(`package-1 MISSING packages/package-1`);
    });

    it("appends MISSING flag to long parseable output", async () => {
      const testDir = await initFixture("undefined-version");
      await lernaLs(testDir)("--long", "--parseable");
      expect(output.logged()).toMatchInlineSnapshot(`__TEST_ROOTDIR__/packages/package-1:package-1:MISSING`);
    });
  });

  describe("--json", () => {
    it("should list packages as json objects", async () => {
      const testDir = await initFixture("basic");
      await lernaLs(testDir)("--json", "-a");

      // Output should be a parseable string
      const jsonOutput = JSON.parse(output.logged());
      expect(jsonOutput).toEqual([
        {
          location: expect.stringContaining("package-1"),
          name: "package-1",
          private: false,
          version: "1.0.0",
        },
        {
          location: expect.stringContaining("package-2"),
          name: "package-2",
          private: false,
          version: "1.0.0",
        },
        {
          location: expect.stringContaining("package-3"),
          name: "package-3",
          private: false,
          version: "1.0.0",
        },
        {
          location: expect.stringContaining("package-4"),
          name: "package-4",
          private: false,
          version: "1.0.0",
        },
        {
          location: expect.stringContaining("package-5"),
          name: "package-5",
          private: true,
          version: "1.0.0",
        },
      ]);
    });
  });

  describe("in a Yarn workspace", () => {
    it("should use package.json/workspaces setting", async () => {
      const testDir = await initFixture("yarn-workspaces");
      await lernaLs(testDir)();
      expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
`);
    });
  });

  describe("with terribly complicated dependency cycles", () => {
    // for reference: 1->2, 1->3, 1->4, 2->4, 2->5, 3->4, 3->6, 4->1, 4->4,  5->4, 6->4, 7->4
    // We design the package tree in a very specific way. We want to test several different things
    // * A package depending on itself isn't added twice (package 4)
    // * A package being added twice in the same stage of the expansion isn't added twice (package 4)
    // * A package that has already been processed wont get added twice (package 1)
    it("should list all packages with no repeats", async () => {
      const testDir = await initFixture("cycles-and-repeated-deps");
      await lernaLs(testDir)("--scope", "package-1", "--include-filtered-dependencies");

      // should follow all transitive deps and pass all packages except 7 with no repeats
      expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
package-3
package-4
package-5
package-6
`);
    });
  });

  describe("with fancy 'packages' configuration", () => {
    it("lists globstar-nested packages", async () => {
      const testDir = await initFixture("globstar");
      await lernaLs(testDir)();
      expect(output.logged()).toMatchInlineSnapshot(`
globstar
package-2
package-4
package-1
package-3
package-5
`);
    });

    it("lists packages under explicitly configured node_modules directories", async () => {
      const testDir = await initFixture("explicit-node-modules");
      await lernaLs(testDir)();
      expect(output.logged()).toMatchInlineSnapshot(`
alle-pattern-root
package-1
package-2
package-3
package-4
@scoped/package-5
`);
    });

    it("throws an error when globstars and explicit node_modules configs are mixed", async () => {
      expect.assertions(1);

      const testDir = await initFixture("mixed-globstar");

      try {
        await lernaLs(testDir)();
      } catch (err) {
        expect(err.message).toMatch("An explicit node_modules package path does not allow globstars");
      }
    });
  });
});


# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [3.11.0](https://github.com/lerna/lerna/compare/v3.10.8...v3.11.0) (2019-02-08)

### Bug Fixes

* **deps:** Explicit libnpmpublish ^1.1.1 ([a506d96](https://github.com/lerna/lerna/commit/a506d96))
* **deps:** Explicit npmlog ^4.1.2 ([571c2e2](https://github.com/lerna/lerna/commit/571c2e2))
* **deps:** Explicit read-package-json ^2.0.13 ([2695a90](https://github.com/lerna/lerna/commit/2695a90))
* **deps:** Remove unused libnpm (replaced by direct sub-packages) ([1caeb28](https://github.com/lerna/lerna/commit/1caeb28))

## [3.10.7](https://github.com/lerna/lerna/compare/v3.10.6...v3.10.7) (2019-01-22)

### Bug Fixes

* **npm-publish:** Ensure process exits non-zero when `libnpm/publish` fails ([9e9ce08](https://github.com/lerna/lerna/commit/9e9ce08))

## [3.10.5](https://github.com/lerna/lerna/compare/v3.10.4...v3.10.5) (2019-01-11)

**Note:** Version bump only for package @lerna/npm-publish

# [3.10.0](https://github.com/lerna/lerna/compare/v3.9.1...v3.10.0) (2019-01-08)

**Note:** Version bump only for package @lerna/npm-publish

# [3.9.0](https://github.com/lerna/lerna/compare/v3.8.5...v3.9.0) (2019-01-08)

**Note:** Version bump only for package @lerna/npm-publish

## [3.8.5](https://github.com/lerna/lerna/compare/v3.8.4...v3.8.5) (2019-01-05)

### Bug Fixes

* **npm-publish:** Ensure pkg.publishConfig is handled correctly ([1877def](https://github.com/lerna/lerna/commit/1877def)), closes [#1498](https://github.com/lerna/lerna/issues/1498)

## [3.8.2](https://github.com/lerna/lerna/compare/v3.8.1...v3.8.2) (2019-01-03)

**Note:** Version bump only for package @lerna/npm-publish

## [3.8.1](https://github.com/lerna/lerna/compare/v3.8.0...v3.8.1) (2018-12-31)

### Bug Fixes

* **npm-publish:** Pass normalized manifest to libnpm/publish ([8e59950](https://github.com/lerna/lerna/commit/8e59950)), closes [#1843](https://github.com/lerna/lerna/issues/1843)

## [3.7.1](https://github.com/lerna/lerna/compare/v3.7.0...v3.7.1) (2018-12-20)

### Bug Fixes

* **npm-publish:** Accept opts.log, defaulting to libnpm/log ([a1d61f6](https://github.com/lerna/lerna/commit/a1d61f6))

# [3.7.0](https://github.com/lerna/lerna/compare/v3.6.0...v3.7.0) (2018-12-19)

### Features

* **npm-publish:** Use libnpm/publish instead of subprocess execution ([433275e](https://github.com/lerna/lerna/commit/433275e))
* **publish:** Use libnpm/publish instead of subprocess execution ([58fda8d](https://github.com/lerna/lerna/commit/58fda8d))

# [3.6.0](https://github.com/lerna/lerna/compare/v3.5.1...v3.6.0) (2018-12-07)

### Features

* Migrate existing usage to libnpm ([0d3a786](https://github.com/lerna/lerna/commit/0d3a786)), closes [#1767](https://github.com/lerna/lerna/issues/1767)

## [3.3.1](https://github.com/lerna/lerna/compare/v3.3.0...v3.3.1) (2018-09-11)

### Bug Fixes

* **publish:** Tell yarn to stop creating git tags ([2a6f0a4](https://github.com/lerna/lerna/commit/2a6f0a4)), closes [#1662](https://github.com/lerna/lerna/issues/1662)

# [3.3.0](https://github.com/lerna/lerna/compare/v3.2.1...v3.3.0) (2018-09-06)

### Features

* **deps:** Upgrade fs-extra to ^7.0.0 ([042b1a3](https://github.com/lerna/lerna/commit/042b1a3))

# [3.2.0](https://github.com/lerna/lerna/compare/v3.1.4...v3.2.0) (2018-08-28)

### Features

* **npm-publish:** Resolve target package to aid chaining ([928a707](https://github.com/lerna/lerna/commit/928a707))
* **npm-publish:** Store entire tarball metadata object on Package instances ([063d743](https://github.com/lerna/lerna/commit/063d743))

## [3.0.6](https://github.com/lerna/lerna/compare/v3.0.5...v3.0.6) (2018-08-16)

### Bug Fixes

* **npm-publish:** Tip-toe around logging when emitting chunk to stdout ([c027246](https://github.com/lerna/lerna/commit/c027246))

## [3.0.4](https://github.com/lerna/lerna/compare/v3.0.3...v3.0.4) (2018-08-14)

### Bug Fixes

* **publish:** Only pass `--json` to `npm pack` when npm >= 5.10.0 ([71babce](https://github.com/lerna/lerna/commit/71babce)), closes [#1558](https://github.com/lerna/lerna/issues/1558)
* **publish:** Run publish from leaf nodes again ([3d348ec](https://github.com/lerna/lerna/commit/3d348ec)), closes [#1560](https://github.com/lerna/lerna/issues/1560)

# [3.0.0](https://github.com/lerna/lerna/compare/v3.0.0-rc.0...v3.0.0) (2018-08-10)

### Bug Fixes

* **publish:** Improve `npm pack` experience ([627cfc2](https://github.com/lerna/lerna/commit/627cfc2))

### Features

* **npm-publish:** Add npmPack export ([088ea54](https://github.com/lerna/lerna/commit/088ea54))
* **publish:** Run `npm pack` before `npm publish` ([8d80b2c](https://github.com/lerna/lerna/commit/8d80b2c))

# [3.0.0-rc.0](https://github.com/lerna/lerna/compare/v3.0.0-beta.21...v3.0.0-rc.0) (2018-07-27)

**Note:** Version bump only for package @lerna/npm-publish

# [3.0.0-beta.21](https://github.com/lerna/lerna/compare/v3.0.0-beta.20...v3.0.0-beta.21) (2018-05-12)

### Bug Fixes

* **child-process:** Prevent duplicate logs when any package-oriented execution fails ([d3a8128](https://github.com/lerna/lerna/commit/d3a8128))

# [3.0.0-beta.20](https://github.com/lerna/lerna/compare/v3.0.0-beta.19...v3.0.0-beta.20) (2018-05-07)

**Note:** Version bump only for package @lerna/npm-publish

# [3.0.0-beta.13](https://github.com/lerna/lerna/compare/v3.0.0-beta.12...v3.0.0-beta.13) (2018-03-31)

**Note:** Version bump only for package @lerna/npm-publish

# [3.0.0-beta.12](https://github.com/lerna/lerna/compare/v3.0.0-beta.11...v3.0.0-beta.12) (2018-03-30)

**Note:** Version bump only for package @lerna/npm-publish

# [3.0.0-beta.11](https://github.com/lerna/lerna/compare/v3.0.0-beta.10...v3.0.0-beta.11) (2018-03-29)

### Features

* Execute atomic publish lifecycle during lerna publish ([#1348](https://github.com/lerna/lerna/issues/1348)) ([45efa24](https://github.com/lerna/lerna/commit/45efa24))

# [3.0.0-beta.1](https://github.com/lerna/lerna/compare/v3.0.0-beta.0...v3.0.0-beta.1) (2018-03-09)

### Bug Fixes

* **publish:** guard against undefined tag ([d8ce253](https://github.com/lerna/lerna/commit/d8ce253)), closes [#1311](https://github.com/lerna/lerna/issues/1311)
* **publish:** Respect pkg.publishConfig.tag ([04b256a](https://github.com/lerna/lerna/commit/04b256a)), closes [#1311](https://github.com/lerna/lerna/issues/1311)


return chain;
}
execute() {
output(this.result.text);
this.logger.success(
"found",

keep keep keep keep keep replace keep keep keep

// piping to `wc -l` should not yield 1 when no packages matched


root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


if (isMaximumHeightReached && isTextLengthOutdated && !wasWidthChanged) {

const shadowText =			text.replace(/</g, '<')

keep keep keep keep keep replace keep keep keep keep keep

import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { Blaze } from 'meteor/blaze';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import _ from 'underscore';

import { fireGlobalEvent } from './fireGlobalEvent';
import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager';
import { mainReady } from './mainReady';
import { roomTypes } from '../../../utils';
import { call } from '../..';
import { callbacks } from '../../../callbacks';
import { Notifications } from '../../../notifications';
import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription } from '../../../models';
import { CachedCollectionManager } from '../../../ui-cached-collection';
import { getConfig } from '../config';

const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen')) || 5;

const onDeleteMessageStream = (msg) => {
	ChatMessage.remove({ _id: msg._id });

	// remove thread refenrece from deleted message
	ChatMessage.update({ tmid: msg._id }, { $unset: { tmid: 1 } }, { multi: true });
};
const onDeleteMessageBulkStream = ({ rid, ts, excludePinned, ignoreDiscussion, users }) => {
	const query = { rid, ts };
	if (excludePinned) {
		query.pinned = { $ne: true };
	}
	if (ignoreDiscussion) {
		query.drid = { $exists: false };
	}
	if (users && users.length) {
		query['u.username'] = { $in: users };
	}
	ChatMessage.remove(query);
};

export const RoomManager = new function() {
	const openedRooms = {};
	const msgStream = new Meteor.Streamer('room-messages');
	const onlineUsers = new ReactiveVar({});
	const Dep = new Tracker.Dependency();
	const Cls = class {
		static initClass() {
			this.prototype.openedRooms = openedRooms;
			this.prototype.onlineUsers = onlineUsers;
			this.prototype.computation = Tracker.autorun(() => {
				const ready = CachedChatRoom.ready.get() && mainReady.get();
				if (ready !== true) { return; }
				const user = Meteor.user();
				Tracker.nonreactive(() => Object.entries(openedRooms).forEach(([typeName, record]) => {
					if (record.active !== true || record.ready === true) { return; }

					const type = typeName.substr(0, 1);
					const name = typeName.substr(1);

					const room = roomTypes.findRoom(type, name, user);

					if (room != null) {
						record.rid = room._id;
						RoomHistoryManager.getMoreIfIsEmpty(room._id);

						if (record.streamActive !== true) {
							record.streamActive = true;
							msgStream.on(record.rid, async (msg) => {
								// Should not send message to room if room has not loaded all the current messages
								if (RoomHistoryManager.hasMoreNext(record.rid) !== false) {
									return;
								}
								// Do not load command messages into channel
								if (msg.t !== 'command') {
									const subscription = ChatSubscription.findOne({ rid: record.rid }, { reactive: false });
									upsertMessage({ msg, subscription });
									msg.room = {
										type,
										name,
									};
								}
								msg.name = room.name;
								RoomManager.updateMentionsMarksOfRoom(typeName);

								callbacks.run('streamMessage', msg);

								return fireGlobalEvent('new-message', msg);
							});

							Notifications.onRoom(record.rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define
							Notifications.onRoom(record.rid, 'deleteMessageBulk', onDeleteMessageBulkStream); // eslint-disable-line no-use-before-define
						}
					}

					record.ready = true;
				}));
				Dep.changed();
			});
		}

		getOpenedRoomByRid(rid) {
			return Object.keys(openedRooms).map((typeName) => openedRooms[typeName]).find((openedRoom) => openedRoom.rid === rid);
		}

		getDomOfRoom(typeName, rid) {
			const room = openedRooms[typeName];
			if (room == null) {
				return;
			}

			if ((room.dom == null) && (rid != null)) {
				room.dom = document.createElement('div');
				room.dom.classList.add('room-container');
				const contentAsFunc = (content) => () => content;

				room.template = Blaze._TemplateWith({ _id: rid }, contentAsFunc(Template.room));
				Blaze.render(room.template, room.dom); // , nextNode, parentView
			}

			return room.dom;
		}

		close(typeName) {
			if (openedRooms[typeName]) {
				if (openedRooms[typeName].rid != null) {
					msgStream.removeAllListeners(openedRooms[typeName].rid);
					Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define
					Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessageBulk', onDeleteMessageBulkStream); // eslint-disable-line no-use-before-define
				}

				openedRooms[typeName].ready = false;
				openedRooms[typeName].active = false;
				if (openedRooms[typeName].template != null) {
					Blaze.remove(openedRooms[typeName].template);
				}
				delete openedRooms[typeName].dom;
				delete openedRooms[typeName].template;

				const { rid } = openedRooms[typeName];
				delete openedRooms[typeName];

				if (rid != null) {
					return RoomHistoryManager.clear(rid);
				}
			}
		}

		closeOlderRooms() {
			if (Object.keys(openedRooms).length <= maxRoomsOpen) {
				return;
			}

			const roomsToClose = _.sortBy(_.values(openedRooms), 'lastSeen').reverse().slice(maxRoomsOpen);
			return Array.from(roomsToClose).map((roomToClose) =>
				this.close(roomToClose.typeName));
		}

		closeAllRooms() {
			Object.keys(openedRooms).forEach((key) => {
				const openedRoom = openedRooms[key];
				this.close(openedRoom.typeName);
			});
		}

		open(typeName) {
			if (openedRooms[typeName] == null) {
				openedRooms[typeName] = {
					typeName,
					active: false,
					ready: false,
					unreadSince: new ReactiveVar(undefined),
				};
			}

			openedRooms[typeName].lastSeen = new Date();

			if (openedRooms[typeName].ready) {
				this.closeOlderRooms();
			}

			if (CachedChatSubscription.ready.get() === true) {
				if (openedRooms[typeName].active !== true) {
					openedRooms[typeName].active = true;
					if (this.computation) {
						this.computation.invalidate();
					}
				}
			}

			return {
				ready() {
					Dep.depend();
					return openedRooms[typeName].ready;
				},
			};
		}

		existsDomOfRoom(typeName) {
			const room = openedRooms[typeName];
			return (room != null ? room.dom : undefined) != null;
		}

		updateUserStatus(user, status, utcOffset) {
			const onlineUsersValue = onlineUsers.curValue;

			if (status === 'offline') {
				delete onlineUsersValue[user.username];
			} else {
				onlineUsersValue[user.username] = {
					_id: user._id,
					status,
					utcOffset,
				};
			}

			return onlineUsers.set(onlineUsersValue);
		}

		updateMentionsMarksOfRoom(typeName) {
			const dom = this.getDomOfRoom(typeName);
			if (!dom) {
				return;
			}

			const [ticksBar] = dom.getElementsByClassName('ticks-bar');
			const [messagesBox] = dom.getElementsByClassName('messages-box');
			const scrollTop = $('> .wrapper', messagesBox).scrollTop() - 50;
			const totalHeight = $(' > .wrapper > ul', messagesBox).height() + 40;

			// TODO: thread quotes should NOT have mention links at all
			const mentionsSelector = '.message .body .mention-link--me, .message .body .mention-link--group';
			ticksBar.innerHTML = Array.from(messagesBox.querySelectorAll(mentionsSelector))
				.map((mentionLink) => {
					const topOffset = $(mentionLink).offset().top + scrollTop;
					const percent = (100 / totalHeight) * topOffset;
					const className = [
						'tick',
						mentionLink.classList.contains('mention-link--me') && 'tick--me',
						mentionLink.classList.contains('mention-link--group') && 'tick--group',
					].filter(Boolean).join(' ');
					return `<div class="${ className }" style="top: ${ percent }%;"></div>`;
				})
				.join('');
		}
	};
	Cls.initClass();
	return new Cls();
}();

const loadMissedMessages = async function(rid) {
	const lastMessage = ChatMessage.findOne({ rid, _hidden: { $ne: true }, temp: { $exists: false } }, { sort: { ts: -1 }, limit: 1 });

	if (lastMessage == null) {
		return;
	}

	try {
		const result = await call('loadMissedMessages', rid, lastMessage.ts);
		if (result) {
			const subscription = ChatSubscription.findOne({ rid });
			return Promise.all(Array.from(result).map((msg) => upsertMessage({ msg, subscription })));
		}
		return [];
	} catch (error) {
		return [];
	}
};

let connectionWasOnline = true;
Tracker.autorun(function() {
	const { connected } = Meteor.connection.status();

	if (connected === true && connectionWasOnline === false && RoomManager.openedRooms != null) {
		Object.keys(RoomManager.openedRooms).forEach((key) => {
			const value = RoomManager.openedRooms[key];
			if (value.rid != null) {
				loadMissedMessages(value.rid);
			}
		});
	}
	connectionWasOnline = connected;
});

Meteor.startup(() => {
	// Reload rooms after login
	let currentUsername = undefined;
	Tracker.autorun(() => {
		const user = Meteor.user();
		if ((currentUsername === undefined) && ((user != null ? user.username : undefined) != null)) {
			currentUsername = user.username;
			RoomManager.closeAllRooms();
			const { roomTypes: types } = roomTypes;

			// Reload only if the current route is a channel route
			const roomType = Object.keys(types).find((key) => types[key].route && types[key].route.name === FlowRouter.current().route.name);
			if (roomType) {
				FlowRouter.reload();
			}
		}
	});

	ChatMessage.find().observe({
		removed(record) {
			if (RoomManager.getOpenedRoomByRid(record.rid) != null) {
				const recordBefore = ChatMessage.findOne({ ts: { $lt: record.ts } }, { sort: { ts: -1 } });
				if (recordBefore != null) {
					ChatMessage.update({ _id: recordBefore._id }, { $set: { tick: new Date() } });
				}

				const recordAfter = ChatMessage.findOne({ ts: { $gt: record.ts } }, { sort: { ts: 1 } });
				if (recordAfter != null) {
					return ChatMessage.update({ _id: recordAfter._id }, { $set: { tick: new Date() } });
				}
			}
		},
	});
});

Tracker.autorun(function() {
	if (Meteor.userId()) {
		return Notifications.onUser('message', function(msg) {
			msg.u = msg.u || { username: 'rocket.cat' };
			msg.private = true;

			return ChatMessage.upsert({ _id: msg._id }, msg);
		});
	}
});

callbacks.add('afterLogoutCleanUp', () => RoomManager.closeAllRooms(), callbacks.priority.MEDIUM, 'roommanager-after-logout-cleanup');

CachedCollectionManager.onLogin(() => {
	Notifications.onUser('subscriptions-changed', (action, sub) => {
		const ignored = sub && sub.ignored ? { $nin: sub.ignored } : { $exists: true };

		ChatMessage.update({ rid: sub.rid, ignored }, { $unset: { ignored: true } }, { multi: true });
		if (sub && sub.ignored) {
			ChatMessage.update({ rid: sub.rid, t: { $ne: 'command' }, 'u._id': { $in: sub.ignored } }, { $set: { ignored: true } }, { multi: true });
		}
	});
});


import { Migrations } from '../../../app/migrations';
import { Messages } from '../../../app/models';

Migrations.add({
	version: 71,
	up() {
		// Removes the reactions on messages which are the system type "rm" ;)
		Messages.find({ t: 'rm', reactions: { $exists: true, $not: { $size: 0 } } }, { t: 1 }).forEach(function(message) {
			Messages.update({ _id: message._id }, { $set: { reactions: [] } });
		});
	},
});


import './lib/startup';
import './views/cleanHistory.html';
import './views/cleanHistory';


//! moment.js locale configuration
//! locale : Malay [ms-my]
//! note : DEPRECATED, the correct one is [ms]
//! author : Weldan Jamili : https://github.com/weldan

;(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 ms_my = moment.defineLocale('ms-my', {
        months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'),
        monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'),
        weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'),
        weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'),
        weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'),
        longDateFormat : {
            LT : 'HH.mm',
            LTS : 'HH.mm.ss',
            L : 'DD/MM/YYYY',
            LL : 'D MMMM YYYY',
            LLL : 'D MMMM YYYY [pukul] HH.mm',
            LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm'
        },
        meridiemParse: /pagi|tengahari|petang|malam/,
        meridiemHour: function (hour, meridiem) {
            if (hour === 12) {
                hour = 0;
            }
            if (meridiem === 'pagi') {
                return hour;
            } else if (meridiem === 'tengahari') {
                return hour >= 11 ? hour : hour + 12;
            } else if (meridiem === 'petang' || meridiem === 'malam') {
                return hour + 12;
            }
        },
        meridiem : function (hours, minutes, isLower) {
            if (hours < 11) {
                return 'pagi';
            } else if (hours < 15) {
                return 'tengahari';
            } else if (hours < 19) {
                return 'petang';
            } else {
                return 'malam';
            }
        },
        calendar : {
            sameDay : '[Hari ini pukul] LT',
            nextDay : '[Esok pukul] LT',
            nextWeek : 'dddd [pukul] LT',
            lastDay : '[Kelmarin pukul] LT',
            lastWeek : 'dddd [lepas pukul] LT',
            sameElse : 'L'
        },
        relativeTime : {
            future : 'dalam %s',
            past : '%s yang lepas',
            s : 'beberapa saat',
            m : 'seminit',
            mm : '%d minit',
            h : 'sejam',
            hh : '%d jam',
            d : 'sehari',
            dd : '%d hari',
            M : 'sebulan',
            MM : '%d bulan',
            y : 'setahun',
            yy : '%d tahun'
        },
        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 ms_my;

}));


const overflow = height === maxHeight ? 'hidden' : '';

const overflow = height < maxHeight ? 'hidden' : '';


// Templates
import './views/creationDialog/CreateDiscussion';
import './views/DiscussionList';
import './views/DiscussionTabbar';

// Other UI extensions
import './lib/messageTypes/discussionMessage';
import './lib/discussionsOfRoom';
import './createDiscussionMessageAction';
import './discussionFromMessageBox';
import './tabBar';

import '../lib/discussionRoomType';

// Style
import './public/stylesheets/discussion.css';


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

import { Subscriptions } from '../../../models';

Meteor.methods({
	unblockUser({ rid, blocked }) {
		check(rid, String);
		check(blocked, String);

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

		const subscription = Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId());
		const subscription2 = Subscriptions.findOneByRoomIdAndUserId(rid, blocked);

		if (!subscription || !subscription2) {
			throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'blockUser' });
		}

		Subscriptions.unsetBlockedByRoomId(rid, blocked, Meteor.userId());

		return true;
	},
});


.page-not-found-container {
	width: 100%;
	height: 100%;
	padding: 10%;

	text-align: center;

	color: white;
	background-image: url("/images/404.svg");
	background-repeat: no-repeat;
	background-position: center;
	background-size: cover;

	.error-404 {
		display: block;

		padding: 10px;

		font-size: 4em;
		font-weight: bold;
	}

	.page-not-found-message {
		display: block;

		padding: 10px;

		font-size: 2em;
		font-weight: bold;
	}

	.page-not-found-description {
		display: block;

		padding: 10px;

		font-size: 1em;
	}

	.page-not-found-button {
		display: inline-block;

		margin: 20px 10px 0;
		padding: 8px 16px;

		font-size: 1em;
	}
}


import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager';

import { mainReady } from './mainReady';

import { roomTypes } from '../../../utils';

import { callbacks } from '../../../callbacks';

import { Notifications } from '../../../notifications';

import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription } from '../../../models';

import _ from 'underscore';

const replaceWhitespaces = (whitespaces: string) => `${ ' '.repeat(whitespaces.length - 1) } `;

export const setupAutogrow = (textarea: HTMLTextAreaElement, shadow: HTMLDivElement, callback: () => void) => {
	const width = textarea.clientWidth;
	const height = textarea.clientHeight;
	const { font, lineHeight, maxHeight: maxHeightPx } = window.getComputedStyle(textarea);

	shadow.style.position = 'fixed';
	shadow.style.top = '-10000px';
	shadow.style.left = '-10000px';
	shadow.style.width = `${ width }px`;
	shadow.style.font = font;
	shadow.style.lineHeight = lineHeight;
	shadow.style.resize = 'none';
	shadow.style.wordWrap = 'break-word';

	const minHeight = height;
	const maxHeight = parseInt(maxHeightPx, 10);

	let lastWidth = width;
	let lastHeight = minHeight;
	let textLenght = 0;

	const update = () => {
		const { clientWidth: width, value: text } = textarea;

		const isMaximumHeightReached = lastHeight >= maxHeight;
		const isTextLengthOutdated = textLenght && textLenght < text.length;
		const wasWidthChanged = width !== lastWidth;

		if (isMaximumHeightReached && isTextLengthOutdated && !wasWidthChanged) {
			return true;
		}

		const shadowText =			text.replace(/</g, '<')
			.replace(/>/g, '>')
			.replace(/&/g, '&')
			.replace(/\n$/, '
 ')
			.replace(/\n/g, '
')
			.replace(/ {2,}/g, replaceWhitespaces);
		if (wasWidthChanged) {
			shadow.style.width = `${ width }px`;
			lastWidth = width;
		}

		shadow.innerHTML = shadowText;

		const shadowHeight = Math.max(shadow.clientHeight + 1, minHeight) + 1;
		const height = Math.min(shadowHeight, maxHeight);

		if (height === lastHeight) {
			return true;
		}

		lastHeight = height;

		const overflow = height === maxHeight ? 'hidden' : '';

		if (height < maxHeight) {
			textLenght = text.length;
		}

		textarea.style.overflow = overflow;
		textarea.style.height = `${ height }px`;

		callback && callback();
	};

	const updateThrottled = _.throttle(update, 300);

	const $textarea = $(textarea);
	$textarea.on('focus', update);
	$textarea.on('change input', updateThrottled);
	window.addEventListener('resize', updateThrottled, false);

	const destroy = () => {
		$textarea.off('focus', update);
		$textarea.off('change input', updateThrottled);
		window.removeEventListener('resize', updateThrottled);
	};

	update();

	return {
		update,
		destroy,
	};
};


import { getURL } from './getURL';

export const getAvatarURL = ({ username, roomId, cache }) => {
	if (username) {
		return getURL(`/avatar/${ encodeURIComponent(username) }${ cache ? `?_dc=${ cache }` : '' }`);
	}
	if (roomId) {
		return getURL(`/avatar/room/${ encodeURIComponent(roomId) }${ cache ? `?_dc=${ cache }` : '' }`);
	}
};


.flex-tab {
	& .push-notifications {
		& .input.checkbox.toggle {
			width: 40px;
		}

		& ul {
			& li {
				margin-bottom: 20px;
			}
		}

		& form {
			& label {
				display: block;

				margin-bottom: 5px;

				font-weight: bold;
			}

			& div span {
				font-size: 14px;

				& i.icon-pencil {
					margin-left: 3px;

					font-size: 12px;
				}
			}
		}

		& .submit {
			margin-top: 30px;

			text-align: center;
		}

		& [data-edit],
		& [data-play] {
			cursor: pointer;
		}

		& .disable-notifications,
		& .unread-room-status {
			display: flex;
			align-items: center;

			& label {
				flex: 1 1;
			}
		}

		& .alert {
			padding: 0.5rem;
		}
	}
}


@keyframes loading {
	0% {

		transform: scale(0.7);

		opacity: 0;
	}

	50% {

		transform: scale(1);

		opacity: 1;
	}

	100% {

		transform: scale(0.7);

		opacity: 0;
	}
}

.rc-popout {
	min-width: 380px;
	max-width: 800px;

	transition: all 0.3s cubic-bezier(0.45, 0.05, 0.55, 0.95);

	color: var(--rc-color-primary-lightest);

	border: solid 4px var(--rc-color-primary-darkest);
	background: var(--rc-color-primary-darkest);

	&-wrapper {
		position: absolute;
		z-index: 10;

		top: calc(var(--header-min-height) + 60px);
		left: calc(100% - 395px);

		display: flex;

		animation: dropdown-show 0.3s cubic-bezier(0.45, 0.05, 0.55, 0.95);

		box-shadow: 3px 3px 5px 1px rgba(47, 52, 61, 0.08), 0 0 12px 0 rgba(47, 52, 61, 0.12);

		align-items: center;
		justify-content: center;

		-webkit-user-drag: element;
		-khtml-user-drag: element;
	}

	&__title {
		flex: 1 1 auto;

		font-size: 16px;
	}

	&--open {
		& .rc-popout__header {
			padding: 5px 10px 8px;

			& .rc-popout__controls {
				display: none;
			}
		}
	}

	&__close {
		cursor: pointer;
		transform: rotate(45deg);

		font-size: 20px;
	}

	&__minimize {
		cursor: pointer;
		transition: transform linear 0.2s;

		font-size: 20px;
	}

	&__header {
		display: flex;
		flex-direction: row;

		padding: 5px 10px;

		font-size: 21px;
		justify-content: flex-end;

		& .rc-popout__controls {
			padding: 0;
		}
	}

	&__content {
		display: flex;

		flex-direction: column;

		animation: dropdown-show 0.1s cubic-bezier(0.45, 0.05, 0.55, 0.95);
		align-items: stretch;

		& .youtube-player {
			width: inherit;
			height: inherit;
		}
	}

	&__controls {
		display: flex;
		flex: 10 1 auto;

		padding: 8px 10px 5px;

		& button {
			margin: 0 5px;

			font-size: 16px;

			&.preparing {
				animation: loading 2s infinite;
				pointer-events: none;
			}

			&.broadcasting {
				color: red;
			}
		}

		& span {
			text-transform: capitalize;

			font-size: 15px;
			line-height: 18px;
		}
	}

	&__content-icon {
		margin: 1.2rem auto;

		font-size: 6rem;

		&--popout-warning {
			color: var(--rc-color-alert);
		}

		&--popout-success {
			color: var(--rc-color-success-light);
		}

		&--popout-info {
			color: var(--rc-color-alert);
		}

		&--popout-error {
			color: var(--rc-color-error-light);
		}
	}

	&__content-text {
		text-align: center;
		word-break: break-word;

		font-size: 1rem;
		line-height: 1.5;
	}

	&__content-error {
		display: none;

		width: 100%;
		margin: 0.5rem 0;

		text-align: center;
		word-break: break-word;

		color: var(--rc-color-primary-lightest);
		background: var(--rc-color-error);

		line-height: 2;
	}

	&--closed {
		& .rc-popout__minimize {
			transform: rotate(180deg);
		}

		&.rc-popout__content {
			overflow: hidden;

			height: 0;

			& .streaming-object {
				visibility: 'hidden';

				height: 0;
			}
		}
	}

	& svg {
		fill: var(--rc-color-primary-lightest);
	}
}

@media (width <= 400px) {
	.rc-popout {
		top: initial !important;
		bottom: 0;
		left: 0 !important;

		width: 100%;
		min-width: 100%;
		max-width: 100%;
		margin: 0 16px;

		animation: dropup-show 0.3s cubic-bezier(0.45, 0.05, 0.55, 0.95);
	}
}


import { CachedCollectionManager } from '../../../ui-cached-collection';

const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen')) || 5;


import { Migrations } from '../../../app/migrations';
import { Integrations } from '../../../app/models';

Migrations.add({
	version: 79,
	up() {
		const integrations = Integrations.find({ type: 'webhook-incoming' }).fetch();

		for (const integration of integrations) {
			if (typeof integration.channel === 'string') {
				Integrations.update({ _id: integration._id }, {
					$set: {
						channel: integration.channel.split(',').map((channel) => channel.trim()),
					},
				});
			}
		}
	},
});


¿Estás seguro de que quieres terminar este chat?

¿Estás seguro de que quieres terminar este chat y cambiar de departamento?

¿Está seguro que quiere cambiar el departamento?

¿Qué tan amigable fue el agente de chat?

¿Qué tan conocedor fue el agente de chat?

¿Qué tan receptivo fue el agente de chat?

¿Qué tan satisfecho estuviste con este chat?

Por favor, tómese un momento para responder una breve encuesta sobre este chat

Por favor introduzca su nombre y correo electrónico.

Califique cada pregunta de acuerdo a su nivel de satisfacción, 1 para completamente insatisfecho y 5 para completamente satisfecho.

¡Gracias! Pronto nos pondremos en contacto con usted.

No estamos en línea en este momento. Por favor, deje un mensaje.

Estamos fuera de línea. Disculpe las molestias.


return RocketChat.API.v1.failure('Please verify the parameters');

keep keep keep keep replace replace keep keep keep keep keep

offset: Math.max(0, offset),
limit: Math.max(0, count),

import s from 'underscore.string';

const sortChannels = function(field, direction) {
	switch (field) {
		case 'createdAt':
			return {
				ts: direction === 'asc' ? 1 : -1,
			};
		default:
			return {
				[field]: direction === 'asc' ? 1 : -1,
			};
	}
};

const sortUsers = function(field, direction) {
	switch (field) {
		default:
			return {
				[field]: direction === 'asc' ? 1 : -1,
			};
	}
};

Meteor.methods({
	browseChannels({ text = '', type = 'channels', sortBy = 'name', sortDirection = 'asc', page = 0, limit = 10 }) {
		const regex = new RegExp(s.trim(s.escapeRegExp(text)), 'i');

		if (!['channels', 'users'].includes(type)) {
			return;
		}

		if (!['asc', 'desc'].includes(sortDirection)) {
			return;
		}

		if (!['name', 'createdAt', 'usersCount', ...type === 'channels' ? ['usernames'] : [], ...type === 'users' ? ['username'] : []].includes(sortBy)) {
			return;
		}

		page = page > -1 ? page : 0;

		limit = limit > 0 ? limit : 10;

		const options = {
			skip: limit * page,
			limit,
		};

		const user = Meteor.user();
		if (type === 'channels') {

			const sort = sortChannels(sortBy, sortDirection);
			if (!RocketChat.authz.hasPermission(user._id, 'view-c-room')) {
				return;
			}
			return {
				results: RocketChat.models.Rooms.findByNameAndType(regex, 'c', {
					...options,
					sort,
					fields: {
						description: 1,
						topic: 1,
						name: 1,
						lastMessage: 1,
						ts: 1,
						archived: 1,
						usersCount: 1,
					},
				}).fetch(),
				total: RocketChat.models.Rooms.findByNameAndType(regex, 'c').count(),
			};
		}

		// type === users
		if (!RocketChat.authz.hasPermission(user._id, 'view-outside-room') || !RocketChat.authz.hasPermission(user._id, 'view-d-room')) {
			return;
		}
		const sort = sortUsers(sortBy, sortDirection);
		return {
			results: RocketChat.models.Users.findByActiveUsersExcept(text, [user.username], {
				...options,
				sort,
				fields: {
					username: 1,
					name: 1,
					createdAt: 1,
					emails: 1,
				},
			}).fetch(),
			total: RocketChat.models.Users.findByActiveUsersExcept(text, [user.username]).count(),
		};
	},
});

DDPRateLimiter.addRule({
	type: 'method',
	name: 'browseChannels',
	userId(/* userId*/) {
		return true;
	},
}, 100, 100000);


Meteor.startup(function() {
	RocketChat.MessageTypes.registerType({
		id: 'jitsi_call_started',
		system: true,
		message: TAPi18n.__('Started_a_video_call'),
	});
});


<template name="lazyloadImage">
	<img data-src="{{srcUrl}}" src="{{lazySrcUrl}}" height="{{height}}" class="{{class}}"
	 data-title="{{title}}" data-description="{{description}}">
</template>


//! moment.js locale configuration
//! locale : English (Ireland) [en-ie]
//! author : Chris Cartlidge : https://github.com/chriscartlidge

;(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 en_ie = moment.defineLocale('en-ie', {
        months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
        monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
        weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
        weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
        weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
        longDateFormat : {
            LT : 'HH:mm',
            LTS : 'HH:mm:ss',
            L : 'DD-MM-YYYY',
            LL : 'D MMMM YYYY',
            LLL : 'D MMMM YYYY HH:mm',
            LLLL : 'dddd D MMMM YYYY HH:mm'
        },
        calendar : {
            sameDay : '[Today at] LT',
            nextDay : '[Tomorrow at] LT',
            nextWeek : 'dddd [at] LT',
            lastDay : '[Yesterday at] LT',
            lastWeek : '[Last] dddd [at] LT',
            sameElse : 'L'
        },
        relativeTime : {
            future : 'in %s',
            past : '%s ago',
            s : 'a few seconds',
            m : 'a minute',
            mm : '%d minutes',
            h : 'an hour',
            hh : '%d hours',
            d : 'a day',
            dd : '%d days',
            M : 'a month',
            MM : '%d months',
            y : 'a year',
            yy : '%d years'
        },
        ordinalParse: /\d{1,2}(st|nd|rd|th)/,
        ordinal : function (number) {
            var b = number % 10,
                output = (~~(number % 100 / 10) === 1) ? 'th' :
                (b === 1) ? 'st' :
                (b === 2) ? 'nd' :
                (b === 3) ? 'rd' : 'th';
            return number + output;
        },
        week : {
            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 en_ie;

}));


browseChannels({ text = '', type = 'channels', sortBy = 'name', sortDirection = 'asc', page = 0, limit = 10 }) {

const regex = new RegExp(s.trim(s.escapeRegExp(text)), 'i');

if (!['channels', 'users'].includes(type)) {

keep keep keep replace keep keep keep keep

browseChannels({ text = '', type = 'channels', sortBy = 'name', sortDirection = 'asc', page, offset, limit = 10 }) {

RocketChat.API.v1.addRoute('info', { authRequired: false }, {
	get() {
		const user = this.getLoggedInUser();

		if (user && RocketChat.authz.hasRole(user._id, 'admin')) {
			return RocketChat.API.v1.success({
				info: RocketChat.Info,
			});
		}

		return RocketChat.API.v1.success({
			info: {
				version: RocketChat.Info.version,
			},
		});
	},
});

RocketChat.API.v1.addRoute('me', { authRequired: true }, {
	get() {
		return RocketChat.API.v1.success(this.getUserInfo(RocketChat.models.Users.findOneById(this.userId)));
	},
});

let onlineCache = 0;
let onlineCacheDate = 0;
const cacheInvalid = 60000; // 1 minute
RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, {
	get() {
		const { type, channel, name, icon } = this.queryParams;
		if (!RocketChat.settings.get('API_Enable_Shields')) {
			throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', { route: '/api/v1/shield.svg' });
		}

		const types = RocketChat.settings.get('API_Shield_Types');
		if (type && (types !== '*' && !types.split(',').map((t) => t.trim()).includes(type))) {
			throw new Meteor.Error('error-shield-disabled', 'This shield type is disabled', { route: '/api/v1/shield.svg' });
		}

		const hideIcon = icon === 'false';
		if (hideIcon && (!name || !name.trim())) {
			return RocketChat.API.v1.failure('Name cannot be empty when icon is hidden');
		}

		let text;
		let backgroundColor = '#4c1';
		switch (type) {
			case 'online':
				if (Date.now() - onlineCacheDate > cacheInvalid) {
					onlineCache = RocketChat.models.Users.findUsersNotOffline().count();
					onlineCacheDate = Date.now();
				}

				text = `${ onlineCache } ${ TAPi18n.__('Online') }`;
				break;
			case 'channel':
				if (!channel) {
					return RocketChat.API.v1.failure('Shield channel is required for type "channel"');
				}

				text = `#${ channel }`;
				break;
			case 'user':
				const user = this.getUserFromParams();

				// Respect the server's choice for using their real names or not
				if (user.name && RocketChat.settings.get('UI_Use_Real_Name')) {
					text = `${ user.name }`;
				} else {
					text = `@${ user.username }`;
				}

				switch (user.status) {
					case 'online':
						backgroundColor = '#1fb31f';
						break;
					case 'away':
						backgroundColor = '#dc9b01';
						break;
					case 'busy':
						backgroundColor = '#bc2031';
						break;
					case 'offline':
						backgroundColor = '#a5a1a1';
				}
				break;
			default:
				text = TAPi18n.__('Join_Chat').toUpperCase();
		}

		const iconSize = hideIcon ? 7 : 24;
		const leftSize = name ? name.length * 6 + 7 + iconSize : iconSize;
		const rightSize = text.length * 6 + 20;
		const width = leftSize + rightSize;
		const height = 20;
		return {
			headers: { 'Content-Type': 'image/svg+xml;charset=utf-8' },
			body: `
				<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${ width }" height="${ height }">
				  <linearGradient id="b" x2="0" y2="100%">
				    <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
				    <stop offset="1" stop-opacity=".1"/>
				  </linearGradient>
				  <mask id="a">
				    <rect width="${ width }" height="${ height }" rx="3" fill="#fff"/>
				  </mask>
				  <g mask="url(#a)">
				    <path fill="#555" d="M0 0h${ leftSize }v${ height }H0z"/>
				    <path fill="${ backgroundColor }" d="M${ leftSize } 0h${ rightSize }v${ height }H${ leftSize }z"/>
				    <path fill="url(#b)" d="M0 0h${ width }v${ height }H0z"/>
				  </g>
				    ${ hideIcon ? '' : '<image x="5" y="3" width="14" height="14" xlink:href="/assets/favicon.svg"/>' }
				  <g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
						${ name ? `<text x="${ iconSize }" y="15" fill="#010101" fill-opacity=".3">${ name }</text>
				    <text x="${ iconSize }" y="14">${ name }</text>` : '' }
				    <text x="${ leftSize + 7 }" y="15" fill="#010101" fill-opacity=".3">${ text }</text>
				    <text x="${ leftSize + 7 }" y="14">${ text }</text>
				  </g>
				</svg>
			`.trim().replace(/\>[\s]+\</gm, '><'),
		};
	},
});

RocketChat.API.v1.addRoute('spotlight', { authRequired: true }, {
	get() {
		check(this.queryParams, {
			query: String,
		});

		const { query } = this.queryParams;

		const result = Meteor.runAsUser(this.userId, () =>
			Meteor.call('spotlight', query)
		);

		return RocketChat.API.v1.success(result);
	},
});

RocketChat.API.v1.addRoute('directory', { authRequired: true }, {
	get() {
		const { offset, count } = this.getPaginationItems();
		const { sort, query } = this.parseJsonQuery();

		const { text, type } = query;
		if (sort && Object.keys(sort).length > 1) {
			return RocketChat.API.v1.failure('This method support only one "sort" parameter');
		}
		const sortBy = sort ? Object.keys(sort)[0] : undefined;
		const sortDirection = sort && Object.values(sort)[0] === 1 ? 'asc' : 'desc';

		const result = Meteor.runAsUser(this.userId, () => Meteor.call('browseChannels', {
			text,
			type,
			sortBy,
			sortDirection,
			page: offset,
			limit: count,
		}));

		if (!result) {
			return RocketChat.API.v1.failure('Please verify the parameters');
		}
		return RocketChat.API.v1.success({
			result: result.results,
			count: result.results.length,
			offset,
			total: result.total,
		});
	},
});


Meteor.startup(() =>
	RocketChat.CachedCollectionManager.onLogin(() =>
		RocketChat.Notifications.onAll('deleteCustomSound', (data) => RocketChat.CustomSounds.remove(data.soundData))
	)
);


.full-modal {
	position: absolute;
	top: 0;
	left: 0;

	display: flex;

	width: 100%;
	height: 100%;
	justify-content: center;

	&__wrapper {
		position: relative;

		display: flex;

		width: var(--modal-wrapper-width);
		margin: var(--modal-wrapper-margin) 0;
	}

	&__back-button {
		position: absolute;
		top: 0;
		right: 0;

		display: flex;

		margin: 0 30px;

		color: var(--modal-back-button-color);
		align-items: center;
	}

	&__back-text {
		margin-right: 2px;

		line-height: 22px;
	}

	&__back-icon {
		transform: rotate(45deg);

		font-size: 1rem;
	}
}

@media (width < 1024px) {
	.full-modal {
		padding: 50px;

		&__wrapper {
			margin: 0;
		}
	}
}

@media (width <= 400px) {
	.full-modal {
		&__back-button {
			top: -1.5rem;
		}
	}
}

.rtl .full-modal {
	&__back-button {
	  right: auto;
	  left: 0;
	}

	&__back-text {
		margin-left: 0.375rem;
	}
}


<template name="integrationsNew">

		{{> header sectionName=pageTitle}}

			 {{_ "Back_to_integrations"}}

			{{#if hasPermission}}

											{{_ "Incoming_WebHook"}}

											{{_ "Send_data_into_RocketChat_in_realtime"}}

											{{_ "Outgoing_WebHook"}}

											{{_ "Outgoing_WebHook_Description"}}

			{{else}}
				{{_ "Not_authorized"}}
			{{/if}}

</template>


if (!['asc', 'desc'].includes(sortDirection)) {

if (!['name', 'createdAt', 'usersCount', ...type === 'channels' ? ['usernames'] : [], ...type === 'users' ? ['username'] : []].includes(sortBy)) {


{{#if isPatch~}}
  ##
{{~else~}}
  #
{{~/if}} {{#if @root.linkCompare~}}
  [{{version}}](
  {{~#if @root.repository~}}
    {{~#if @root.host}}
      {{~@root.host}}/
    {{~/if}}
    {{~#if @root.owner}}
      {{~@root.owner}}/
    {{~/if}}
    {{~@root.repository}}
  {{~else}}
    {{~@root.repoUrl}}
  {{~/if~}}
  /compare/{{previousTag}}...{{currentTag}})
{{~else}}
  {{~version}}
{{~/if}}
{{~#if title}} "{{title}}"
{{~/if}}
{{~#if date}} ({{date}})
{{/if}}


/* globals WebApp:true */
import _ from 'underscore';
import url from 'url';

const { WebApp } = Package.webapp;
const { Autoupdate } = Package.autoupdate;

WebApp.connectHandlers.use('/livechat', Meteor.bindEnvironment((req, res, next) => {
	const reqUrl = url.parse(req.url);
	if (reqUrl.pathname !== '/') {
		return next();
	}
	res.setHeader('content-type', 'text/html; charset=utf-8');

	let domainWhiteList = RocketChat.settings.get('Livechat_AllowedDomainsList');
	if (req.headers.referer && !_.isEmpty(domainWhiteList.trim())) {
		domainWhiteList = _.map(domainWhiteList.split(','), function(domain) {
			return domain.trim();
		});

		const referer = url.parse(req.headers.referer);
		if (!_.contains(domainWhiteList, referer.host)) {
			res.setHeader('X-FRAME-OPTIONS', 'DENY');
			return next();
		}

		res.setHeader('X-FRAME-OPTIONS', `ALLOW-FROM ${ referer.protocol }//${ referer.host }`);
	}

	const head = Assets.getText('public/head.html');

	let baseUrl;
	if (__meteor_runtime_config__.ROOT_URL_PATH_PREFIX && __meteor_runtime_config__.ROOT_URL_PATH_PREFIX.trim() !== '') {
		baseUrl = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX;
	} else {
		baseUrl = '/';
	}
	if (/\/$/.test(baseUrl) === false) {
		baseUrl += '/';
	}

	const html = `<html>
		<head>
			<link rel="stylesheet" type="text/css" class="__meteor-css__" href="${ baseUrl }livechat/livechat.css?_dc=${ Autoupdate.autoupdateVersion }">
			<script type="text/javascript">
				__meteor_runtime_config__ = ${ JSON.stringify(__meteor_runtime_config__) };
			</script>

			<base href="${ baseUrl }">

			${ head }
		</head>
		<body>
			<script type="text/javascript" src="${ baseUrl }livechat/livechat.js?_dc=${ Autoupdate.autoupdateVersion }"></script>
		</body>
	</html>`;

	res.write(html);
	res.end();
}));


RocketChat.getUserNotificationPreference = function _getUserNotificationPreference(user, pref) {
	if (typeof user === 'string') {
		user = RocketChat.models.Users.findOneById(user);
	}

	let preferenceKey;
	switch (pref) {
		case 'desktop': preferenceKey = 'desktopNotifications'; break;
		case 'mobile': preferenceKey = 'mobileNotifications'; break;
		case 'email': preferenceKey = 'emailNotificationMode'; break;
	}

	if (user && user.settings && user.settings.preferences && user.settings.preferences[preferenceKey] !== 'default') {
		return {
			value: user.settings.preferences[preferenceKey],
			origin: 'user',
		};
	}
	const serverValue = RocketChat.settings.get(`Accounts_Default_User_Preferences_${ preferenceKey }`);
	if (serverValue) {
		return {
			value: serverValue,
			origin: 'server',
		};
	}

	return null;
};


keep keep keep keep replace keep keep keep

const skip = Math.max(0, offset || (page > -1 ? limit * page : 0));