diff --git a/package.json b/package.json index 8d638a5928..ed12e6a5e4 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,9 @@ "scripts": { "reskindex": "node scripts/reskindex.js -h header", "reskindex:watch": "node scripts/reskindex.js -h header -w", - "build": "npm run reskindex && babel src -d lib --source-maps", - "build:watch": "babel src -w -d lib --source-maps", + "build": "npm run reskindex && babel src -d lib --source-maps --copy-files", + "build:watch": "babel src -w -d lib --source-maps --copy-files", + "emoji-data-strip": "node scripts/emoji-data-strip.js", "start": "parallelshell \"npm run build:watch\" \"npm run reskindex:watch\"", "lint": "eslint src/", "lintall": "eslint src/ test/", diff --git a/scripts/emoji-data-strip.js b/scripts/emoji-data-strip.js new file mode 100644 index 0000000000..d7c80e7e37 --- /dev/null +++ b/scripts/emoji-data-strip.js @@ -0,0 +1,23 @@ +#!/usr/bin/env node +const EMOJI_DATA = require('emojione/emoji.json'); +const fs = require('fs'); + +const output = Object.keys(EMOJI_DATA).map( + (key) => { + const datum = EMOJI_DATA[key]; + const newDatum = { + name: datum.name, + shortname: datum.shortname, + category: datum.category, + emoji_order: datum.emoji_order, + }; + if (datum.aliases_ascii.length > 0) { + newDatum.aliases_ascii = datum.aliases_ascii; + } + return newDatum; + } +); + +// Write to a file in src. Changes should be checked into git. This file is copied by +// babel using --copy-files +fs.writeFileSync('./src/stripped-emoji.json', JSON.stringify(output)); diff --git a/src/ComposerHistoryManager.js b/src/ComposerHistoryManager.js index 3e19a78bfe..1ae836574b 100644 --- a/src/ComposerHistoryManager.js +++ b/src/ComposerHistoryManager.js @@ -52,21 +52,19 @@ export default class ComposerHistoryManager { history: Array = []; prefix: string; lastIndex: number = 0; - currentIndex: number = -1; + currentIndex: number = 0; constructor(roomId: string, prefix: string = 'mx_composer_history_') { this.prefix = prefix + roomId; // TODO: Performance issues? - for(; sessionStorage.getItem(`${this.prefix}[${this.lastIndex}]`); this.lastIndex++, this.currentIndex++) { + let item; + for(; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) { this.history.push( - Object.assign( - new HistoryItem(), - JSON.parse(sessionStorage.getItem(`${this.prefix}[${this.lastIndex}]`)), - ), + Object.assign(new HistoryItem(), JSON.parse(item)), ); } - this.currentIndex--; + this.lastIndex = this.currentIndex; } addItem(message: string, format: MessageFormat) { diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 5c1c2881e5..8c7e397fce 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -84,7 +84,7 @@ export function charactersToImageNode(alt, useSvg, ...unicode) { } -export function stripParagraphs(html: string): string { +export function processHtmlForSending(html: string): string { const contentDiv = document.createElement('div'); contentDiv.innerHTML = html; @@ -93,10 +93,21 @@ export function stripParagraphs(html: string): string { } let contentHTML = ""; - for (let i=0; i'; + contentHTML += element.innerHTML; + // Don't add a
for the last

+ if (i !== contentDiv.children.length - 1) { + contentHTML += '
'; + } + } else if (element.tagName.toLowerCase() === 'pre') { + // Replace "
\n" with "\n" within `

` tags because the 
is + // redundant. This is a workaround for a bug in draft-js-export-html: + // https://github.com/sstur/draft-js-export-html/issues/62 + contentHTML += '
' +
+                element.innerHTML.replace(/
\n/g, '\n').trim() + + '
'; } else { const temp = document.createElement('div'); temp.appendChild(element.cloneNode(true)); @@ -134,6 +145,7 @@ const sanitizeHtmlParams = { // would make sense if we did img: ['src'], ol: ['start'], + code: ['class'], // We don't actually allow all classes, we filter them in transformTags }, // Lots of these won't come up by default because we don't allow them selfClosing: ['img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta'], @@ -175,6 +187,19 @@ const sanitizeHtmlParams = { attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/ return { tagName: tagName, attribs : attribs }; }, + 'code': function(tagName, attribs) { + if (typeof attribs.class !== 'undefined') { + // Filter out all classes other than ones starting with language- for syntax highlighting. + let classes = attribs.class.split(/\s+/).filter(function(cl) { + return cl.startsWith('language-'); + }); + attribs.class = classes.join(' '); + } + return { + tagName: tagName, + attribs: attribs, + }; + }, '*': function(tagName, attribs) { // Delete any style previously assigned, style is an allowedTag for font and span // because attributes are stripped after transforming diff --git a/src/KeyCode.js b/src/KeyCode.js index 28aafc00cb..90c2caeb0e 100644 --- a/src/KeyCode.js +++ b/src/KeyCode.js @@ -30,7 +30,30 @@ module.exports = { RIGHT: 39, DOWN: 40, DELETE: 46, + KEY_A: 65, + KEY_B: 66, + KEY_C: 67, KEY_D: 68, KEY_E: 69, + KEY_F: 70, + KEY_G: 71, + KEY_H: 72, + KEY_I: 73, + KEY_J: 74, + KEY_K: 75, + KEY_L: 76, KEY_M: 77, + KEY_N: 78, + KEY_O: 79, + KEY_P: 80, + KEY_Q: 81, + KEY_R: 82, + KEY_S: 83, + KEY_T: 84, + KEY_U: 85, + KEY_V: 86, + KEY_W: 87, + KEY_X: 88, + KEY_Y: 89, + KEY_Z: 90, }; diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index c1b975e8e8..1104458f22 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -109,6 +110,76 @@ Example: response: 78 } +set_widget +---------- +Set a new widget in the room. Clobbers based on the ID. + +Request: + - `room_id` (String) is the room to set the widget in. + - `widget_id` (String) is the ID of the widget to add (or replace if it already exists). + It can be an arbitrary UTF8 string and is purely for distinguishing between widgets. + - `url` (String) is the URL that clients should load in an iframe to run the widget. + All widgets must have a valid URL. If the URL is `null` (not `undefined`), the + widget will be removed from the room. + - `type` (String) is the type of widget, which is provided as a hint for matrix clients so they + can configure/lay out the widget in different ways. All widgets must have a type. + - `name` (String) is an optional human-readable string about the widget. + - `data` (Object) is some optional data about the widget, and can contain arbitrary key/value pairs. +Response: +{ + success: true +} +Example: +{ + action: "set_widget", + room_id: "!foo:bar", + widget_id: "abc123", + url: "http://widget.url", + type: "example", + response: { + success: true + } +} + +get_widgets +----------- +Get a list of all widgets in the room. The response is the `content` field +of the state event. + +Request: + - `room_id` (String) is the room to get the widgets in. +Response: +{ + $widget_id: { + type: "example", + url: "http://widget.url", + name: "Example Widget", + data: { + key: "val" + } + }, + $widget_id: { ... } +} +Example: +{ + action: "get_widgets", + room_id: "!foo:bar", + widget_id: "abc123", + url: "http://widget.url", + type: "example", + response: { + $widget_id: { + type: "example", + url: "http://widget.url", + name: "Example Widget", + data: { + key: "val" + } + }, + $widget_id: { ... } + } +} + membership_state AND bot_options -------------------------------- @@ -191,6 +262,84 @@ function inviteUser(event, roomId, userId) { }); } +function setWidget(event, roomId) { + const widgetId = event.data.widget_id; + const widgetType = event.data.type; + const widgetUrl = event.data.url; + const widgetName = event.data.name; // optional + const widgetData = event.data.data; // optional + + const client = MatrixClientPeg.get(); + if (!client) { + sendError(event, _t('You need to be logged in.')); + return; + } + + // both adding/removing widgets need these checks + if (!widgetId || widgetUrl === undefined) { + sendError(event, _t("Unable to create widget."), new Error("Missing required widget fields.")); + return; + } + + if (widgetUrl !== null) { // if url is null it is being deleted, don't need to check name/type/etc + // check types of fields + if (widgetName !== undefined && typeof widgetName !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Optional field 'name' must be a string.")); + return; + } + if (widgetData !== undefined && !(widgetData instanceof Object)) { + sendError(event, _t("Unable to create widget."), new Error("Optional field 'data' must be an Object.")); + return; + } + if (typeof widgetType !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Field 'type' must be a string.")); + return; + } + if (typeof widgetUrl !== 'string') { + sendError(event, _t("Unable to create widget."), new Error("Field 'url' must be a string or null.")); + return; + } + } + + // TODO: same dance we do for power levels. It'd be nice if the JS SDK had helper methods to do this. + client.getStateEvent(roomId, "im.vector.modular.widgets", "").then((widgets) => { + if (widgetUrl === null) { + delete widgets[widgetId]; + } + else { + widgets[widgetId] = { + type: widgetType, + url: widgetUrl, + name: widgetName, + data: widgetData, + }; + } + return client.sendStateEvent(roomId, "im.vector.modular.widgets", widgets); + }, (err) => { + if (err.errcode === "M_NOT_FOUND") { + return client.sendStateEvent(roomId, "im.vector.modular.widgets", { + [widgetId]: { + type: widgetType, + url: widgetUrl, + name: widgetName, + data: widgetData, + } + }); + } + throw err; + }).done(() => { + sendResponse(event, { + success: true, + }); + }, (err) => { + sendError(event, _t('Failed to send request.'), err); + }); +} + +function getWidgets(event, roomId) { + returnStateEvent(event, roomId, "im.vector.modular.widgets", ""); +} + function setPlumbingState(event, roomId, status) { if (typeof status !== 'string') { throw new Error('Plumbing state status should be a string'); @@ -367,7 +516,7 @@ const onMessage = function(event) { return; } - // Getting join rules does not require userId + // These APIs don't require userId if (event.data.action === "join_rules_state") { getJoinRules(event, roomId); return; @@ -377,6 +526,12 @@ const onMessage = function(event) { } else if (event.data.action === "get_membership_count") { getMembershipCount(event, roomId); return; + } else if (event.data.action === "set_widget") { + setWidget(event, roomId); + return; + } else if (event.data.action === "get_widgets") { + getWidgets(event, roomId); + return; } if (!userId) { @@ -409,12 +564,27 @@ const onMessage = function(event) { }); }; +let listenerCount = 0; module.exports = { startListening: function() { - window.addEventListener("message", onMessage, false); + if (listenerCount === 0) { + window.addEventListener("message", onMessage, false); + } + listenerCount += 1; }, stopListening: function() { - window.removeEventListener("message", onMessage); + listenerCount -= 1; + if (listenerCount === 0) { + window.removeEventListener("message", onMessage); + } + if (listenerCount < 0) { + // Make an error so we get a stack trace + const e = new Error( + "ScalarMessaging: mismatched startListening / stopListening detected." + + " Negative count" + ); + console.error(e); + } }, }; diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 84d85e7565..009fdabb53 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -30,11 +30,17 @@ export default { id: 'rich_text_editor', default: false, }, + { + name: "-", + id: 'matrix_apps', + default: false, + }, ], // horrible but it works. The locality makes this somewhat more palatable. doTranslations: function() { this.LABS_FEATURES[0].name = _t("New Composer & Autocomplete"); + this.LABS_FEATURES[1].name = _t("Matrix Apps"); }, loadProfileInfo: function() { diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js index 416855ca09..36aad68639 100644 --- a/src/autocomplete/EmojiProvider.js +++ b/src/autocomplete/EmojiProvider.js @@ -18,16 +18,42 @@ limitations under the License. import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; -import {emojioneList, shortnameToImage, shortnameToUnicode} from 'emojione'; +import {emojioneList, shortnameToImage, shortnameToUnicode, asciiRegexp} from 'emojione'; import FuzzyMatcher from './FuzzyMatcher'; import sdk from '../index'; import {PillCompletion} from './Components'; import type {SelectionRange, Completion} from './Autocompleter'; -const EMOJI_REGEX = /:\w*:?/g; -const EMOJI_SHORTNAMES = Object.keys(emojioneList).map(shortname => { +import EmojiData from '../stripped-emoji.json'; + +const LIMIT = 20; +const CATEGORY_ORDER = [ + 'people', + 'food', + 'objects', + 'activity', + 'nature', + 'travel', + 'flags', + 'symbols', + 'unicode9', + 'modifier', +]; + +// Match for ":wink:" or ascii-style ";-)" provided by emojione +const EMOJI_REGEX = new RegExp('(:\\w*:?|' + asciiRegexp + ')', 'g'); +const EMOJI_SHORTNAMES = Object.keys(EmojiData).map((key) => EmojiData[key]).sort( + (a, b) => { + if (a.category === b.category) { + return a.emoji_order - b.emoji_order; + } + return CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category); + }, +).map((a) => { return { - shortname, + name: a.name, + shortname: a.shortname, + aliases_ascii: a.aliases_ascii ? a.aliases_ascii.join(' ') : '', }; }); @@ -37,7 +63,9 @@ export default class EmojiProvider extends AutocompleteProvider { constructor() { super(EMOJI_REGEX); this.matcher = new FuzzyMatcher(EMOJI_SHORTNAMES, { - keys: 'shortname', + keys: ['aliases_ascii', 'shortname', 'name'], + // For matching against ascii equivalents + shouldMatchWordsOnly: false, }); } @@ -57,7 +85,7 @@ export default class EmojiProvider extends AutocompleteProvider { ), range, }; - }).slice(0, 8); + }).slice(0, LIMIT); } return completions; } diff --git a/src/autocomplete/QueryMatcher.js b/src/autocomplete/QueryMatcher.js index 01fc251318..1b2ee1bc0d 100644 --- a/src/autocomplete/QueryMatcher.js +++ b/src/autocomplete/QueryMatcher.js @@ -63,6 +63,12 @@ export default class QueryMatcher { this.options = options; this.keys = options.keys; this.setObjects(objects); + + // By default, we remove any non-alphanumeric characters ([^A-Za-z0-9_]) from the + // query and the value being queried before matching + if (this.options.shouldMatchWordsOnly === undefined) { + this.options.shouldMatchWordsOnly = true; + } } setObjects(objects: Array) { @@ -70,9 +76,16 @@ export default class QueryMatcher { } match(query: String): Array { - query = query.toLowerCase().replace(/[^\w]/g, ''); + query = query.toLowerCase(); + if (this.options.shouldMatchWordsOnly) { + query = query.replace(/[^\w]/g, ''); + } const results = _sortedUniq(_sortBy(_flatMap(this.keyMap.keys, (key) => { - return key.toLowerCase().replace(/[^\w]/g, '').indexOf(query) >= 0 ? this.keyMap.objectMap[key] : []; + let resultKey = key.toLowerCase(); + if (this.options.shouldMatchWordsOnly) { + resultKey = resultKey.replace(/[^\w]/g, ''); + } + return resultKey.indexOf(query) !== -1 ? this.keyMap.objectMap[key] : []; }), (candidate) => this.keyMap.priorityMap.get(candidate))); return results; } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 67b523bfaf..ffb0c1243c 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -47,13 +47,12 @@ import UserProvider from '../../autocomplete/UserProvider'; import RoomViewStore from '../../stores/RoomViewStore'; -var DEBUG = false; +let DEBUG = false; +let debuglog = function() {}; if (DEBUG) { // using bind means that we get to keep useful line numbers in the console - var debuglog = console.log.bind(console); -} else { - var debuglog = function() {}; + debuglog = console.log.bind(console); } module.exports = React.createClass({ @@ -113,6 +112,7 @@ module.exports = React.createClass({ callState: null, guestsCanJoin: false, canPeek: false, + showApps: false, // error object, as from the matrix client/server API // If we failed to load information about the room, @@ -236,6 +236,7 @@ module.exports = React.createClass({ if (room) { this.setState({ unsentMessageError: this._getUnsentMessageError(room), + showApps: this._shouldShowApps(room), }); this._onRoomLoaded(room); } @@ -273,6 +274,11 @@ module.exports = React.createClass({ } }, + _shouldShowApps: function(room) { + const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets', ''); + return appsStateEvents && Object.keys(appsStateEvents.getContent()).length > 0; + }, + componentDidMount: function() { var call = this._getCallForRoom(); var callState = call ? call.call_state : "ended"; @@ -453,9 +459,14 @@ module.exports = React.createClass({ this._updateConfCallNotification(); this.setState({ - callState: callState + callState: callState, }); + break; + case 'appsDrawer': + this.setState({ + showApps: payload.show, + }); break; } }, @@ -1604,11 +1615,13 @@ module.exports = React.createClass({ var auxPanel = ( + onResize={this.onChildResize} + showApps={this.state.showApps && !this.state.editingRoomSettings} > { aux } ); @@ -1621,8 +1634,14 @@ module.exports = React.createClass({ if (canSpeak) { messageComposer = ; + room={this.state.room} + onResize={this.onChildResize} + uploadFile={this.uploadFile} + callState={this.state.callState} + tabComplete={this.tabComplete} + opacity={ this.props.opacity } + showApps={ this.state.showApps } + />; } // TODO: Why aren't we storing the term/scope/count in this format diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index ef574d2ed6..bfbb9831b0 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -93,6 +93,10 @@ const SETTINGS_LABELS = [ id: 'disableMarkdown', label: 'Disable markdown formatting', }, + { + id: 'enableSyntaxHighlightLanguageDetection', + label: 'Enable automatic language detection for syntax highlighting', + }, /* { id: 'useFixedWidthFont', diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js new file mode 100644 index 0000000000..6f4c931ab7 --- /dev/null +++ b/src/components/views/elements/AppTile.js @@ -0,0 +1,102 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +import React from 'react'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import { _t } from '../../../languageHandler'; + +export default React.createClass({ + displayName: 'AppTile', + + propTypes: { + id: React.PropTypes.string.isRequired, + url: React.PropTypes.string.isRequired, + name: React.PropTypes.string.isRequired, + room: React.PropTypes.object.isRequired, + }, + + getDefaultProps: function() { + return { + url: "", + }; + }, + + _onEditClick: function() { + console.log("Edit widget %s", this.props.id); + }, + + _onDeleteClick: function() { + console.log("Delete widget %s", this.props.id); + const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + if (!appsStateEvents) { + return; + } + const appsStateEvent = appsStateEvents.getContent(); + if (appsStateEvent[this.props.id]) { + delete appsStateEvent[this.props.id]; + MatrixClientPeg.get().sendStateEvent( + this.props.room.roomId, + 'im.vector.modular.widgets', + appsStateEvent, + '', + ).then(() => { + console.log('Deleted widget'); + }, (e) => { + console.error('Failed to delete widget', e); + }); + } + }, + + formatAppTileName: function() { + let appTileName = "No name"; + if(this.props.name && this.props.name.trim()) { + appTileName = this.props.name.trim(); + appTileName = appTileName[0].toUpperCase() + appTileName.slice(1).toLowerCase(); + } + return appTileName; + }, + + render: function() { + return ( +
+
+ {this.formatAppTileName()} + + {/* Edit widget */} + {/* Edit */} + + {/* Delete widget */} + {_t("Cancel")} + +
+
+ +
+
+ ); + }, +}); diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index 46d8366592..f31b94df80 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -79,7 +79,7 @@ module.exports = React.createClass({ const content = this.props.mxEvent.getContent(); if (content.file !== undefined) { return this.state.decryptedThumbnailUrl; - } else if (content.info.thumbnail_url) { + } else if (content.info && content.info.thumbnail_url) { return MatrixClientPeg.get().mxcUrlToHttp(content.info.thumbnail_url); } else { return null; diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index d5a1977cdd..190b1341c3 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -29,6 +29,7 @@ import Modal from '../../../Modal'; import SdkConfig from '../../../SdkConfig'; import dis from '../../../dispatcher'; import { _t } from '../../../languageHandler'; +import UserSettingsStore from "../../../UserSettingsStore"; linkifyMatrix(linkify); @@ -90,7 +91,18 @@ module.exports = React.createClass({ setTimeout(() => { if (this._unmounted) return; for (let i = 0; i < blocks.length; i++) { - highlight.highlightBlock(blocks[i]); + if (UserSettingsStore.getSyncedSetting("enableSyntaxHighlightLanguageDetection", false)) { + highlight.highlightBlock(blocks[i]) + } else { + // Only syntax highlight if there's a class starting with language- + let classes = blocks[i].className.split(/\s+/).filter(function (cl) { + return cl.startsWith('language-'); + }); + + if (classes.length != 0) { + highlight.highlightBlock(blocks[i]); + } + } } }, 10); } diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js new file mode 100644 index 0000000000..b535b148ac --- /dev/null +++ b/src/components/views/rooms/AppsDrawer.js @@ -0,0 +1,218 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +import React from 'react'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import AppTile from '../elements/AppTile'; +import Modal from '../../../Modal'; +import dis from '../../../dispatcher'; +import sdk from '../../../index'; +import SdkConfig from '../../../SdkConfig'; +import ScalarAuthClient from '../../../ScalarAuthClient'; +import ScalarMessaging from '../../../ScalarMessaging'; +import { _t } from '../../../languageHandler'; + + +module.exports = React.createClass({ + displayName: 'AppsDrawer', + + propTypes: { + room: React.PropTypes.object.isRequired, + }, + + getInitialState: function() { + return { + apps: this._getApps(), + }; + }, + + componentWillMount: function() { + ScalarMessaging.startListening(); + MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents); + }, + + componentDidMount: function() { + this.scalarClient = null; + if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) { + this.scalarClient = new ScalarAuthClient(); + this.scalarClient.connect().done(() => { + this.forceUpdate(); + if (this.state.apps && this.state.apps.length < 1) { + this.onClickAddWidget(); + } + // TODO -- Handle Scalar errors + // }, + // (err) => { + // this.setState({ + // scalar_error: err, + // }); + }); + } + }, + + componentWillUnmount: function() { + ScalarMessaging.stopListening(); + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); + } + }, + + /** + * Encodes a URI according to a set of template variables. Variables will be + * passed through encodeURIComponent. + * @param {string} pathTemplate The path with template variables e.g. '/foo/$bar'. + * @param {Object} variables The key/value pairs to replace the template + * variables with. E.g. { "$bar": "baz" }. + * @return {string} The result of replacing all template variables e.g. '/foo/baz'. + */ + encodeUri: function(pathTemplate, variables) { + for (const key in variables) { + if (!variables.hasOwnProperty(key)) { + continue; + } + pathTemplate = pathTemplate.replace( + key, encodeURIComponent(variables[key]), + ); + } + return pathTemplate; + }, + + _initAppConfig: function(appId, app) { + const user = MatrixClientPeg.get().getUser(this.props.userId); + const params = { + '$matrix_user_id': this.props.userId, + '$matrix_room_id': this.props.room.roomId, + '$matrix_display_name': user ? user.displayName : this.props.userId, + '$matrix_avatar_url': user ? MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl) : '', + }; + + if(app.data) { + Object.keys(app.data).forEach((key) => { + params['$' + key] = app.data[key]; + }); + } + + app.id = appId; + app.name = app.name || app.type; + app.url = this.encodeUri(app.url, params); + + // switch(app.type) { + // case 'etherpad': + // app.queryParams = '?userName=' + this.props.userId + + // '&padId=' + this.props.room.roomId; + // break; + // case 'jitsi': { + // + // app.queryParams = '?confId=' + app.data.confId + + // '&displayName=' + encodeURIComponent(user.displayName) + + // '&avatarUrl=' + encodeURIComponent(MatrixClientPeg.get().mxcUrlToHttp(user.avatarUrl)) + + // '&email=' + encodeURIComponent(this.props.userId) + + // '&isAudioConf=' + app.data.isAudioConf; + // + // break; + // } + // case 'vrdemo': + // app.queryParams = '?roomAlias=' + encodeURIComponent(app.data.roomAlias); + // break; + // } + + return app; + }, + + onRoomStateEvents: function(ev, state) { + if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'im.vector.modular.widgets') { + return; + } + this._updateApps(); + }, + + _getApps: function() { + const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', ''); + if (!appsStateEvents) { + return []; + } + const appsStateEvent = appsStateEvents.getContent(); + if (Object.keys(appsStateEvent).length < 1) { + return []; + } + + return Object.keys(appsStateEvent).map((appId) => { + return this._initAppConfig(appId, appsStateEvent[appId]); + }); + }, + + _updateApps: function() { + const apps = this._getApps(); + if (apps.length < 1) { + dis.dispatch({ + action: 'appsDrawer', + show: false, + }); + } + this.setState({ + apps: apps, + }); + }, + + onClickAddWidget: function(e) { + if (e) { + e.preventDefault(); + } + + const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); + const src = (this.scalarClient !== null && this.scalarClient.hasCredentials()) ? + this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId) : + null; + Modal.createDialog(IntegrationsManager, { + src: src, + }, "mx_IntegrationsManager"); + }, + + render: function() { + const apps = this.state.apps.map( + (app, index, arr) => { + return ; + }); + + const addWidget = this.state.apps && this.state.apps.length < 2 && + (
+ [+] {_t('Add a widget')} +
); + + return ( +
+
+ {apps} +
+ {addWidget} +
+ ); + }, +}); diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index d591e4f6c2..dd6d9d1ae9 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -40,25 +40,51 @@ export default class Autocomplete extends React.Component { }; } - async componentWillReceiveProps(props, state) { - if (props.query === this.props.query) { - return null; - } - - return await this.complete(props.query, props.selection); - } - - async complete(query, selection) { - let forceComplete = this.state.forceComplete; - const completionPromise = getCompletions(query, selection, forceComplete); - this.completionPromise = completionPromise; - const completions = await this.completionPromise; - - // There's a newer completion request, so ignore results. - if (completionPromise !== this.completionPromise) { + componentWillReceiveProps(newProps, state) { + // Query hasn't changed so don't try to complete it + if (newProps.query === this.props.query) { return; } + this.complete(newProps.query, newProps.selection); + } + + complete(query, selection) { + if (this.debounceCompletionsRequest) { + clearTimeout(this.debounceCompletionsRequest); + } + if (query === "") { + this.setState({ + // Clear displayed completions + completions: [], + completionList: [], + // Reset selected completion + selectionOffset: COMPOSER_SELECTED, + // Hide the autocomplete box + hide: true, + }); + return Q(null); + } + let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200); + + // Don't debounce if we are already showing completions + if (this.state.completions.length > 0) { + autocompleteDelay = 0; + } + + const deferred = Q.defer(); + this.debounceCompletionsRequest = setTimeout(() => { + getCompletions( + query, selection, this.state.forceComplete, + ).then((completions) => { + this.processCompletions(completions); + deferred.resolve(); + }); + }, autocompleteDelay); + return deferred.promise; + } + + processCompletions(completions) { const completionList = flatMap(completions, (provider) => provider.completions); // Reset selection when completion list becomes empty. @@ -88,23 +114,13 @@ export default class Autocomplete extends React.Component { hide = false; } - const autocompleteDelay = UserSettingsStore.getSyncedSetting('autocompleteDelay', 200); - - // We had no completions before, but do now, so we should apply our display delay here - if (this.state.completionList.length === 0 && completionList.length > 0 && - !forceComplete && autocompleteDelay > 0) { - await Q.delay(autocompleteDelay); - } - - // Force complete is turned off each time since we can't edit the query in that case - forceComplete = false; - this.setState({ completions, completionList, selectionOffset, hide, - forceComplete, + // Force complete is turned off each time since we can't edit the query in that case + forceComplete: false, }); } diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index acb9c76aa0..a50743a25d 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -19,7 +19,9 @@ import MatrixClientPeg from "../../../MatrixClientPeg"; import sdk from '../../../index'; import dis from "../../../dispatcher"; import ObjectUtils from '../../../ObjectUtils'; -import { _t, _tJsx} from '../../../languageHandler'; +import AppsDrawer from './AppsDrawer'; +import { _t, _tJsx} from '../../../languageHandler'; +import UserSettingsStore from '../../../UserSettingsStore'; module.exports = React.createClass({ @@ -28,6 +30,8 @@ module.exports = React.createClass({ propTypes: { // js-sdk room object room: React.PropTypes.object.isRequired, + userId: React.PropTypes.string.isRequired, + showApps: React.PropTypes.bool, // Conference Handler implementation conferenceHandler: React.PropTypes.object, @@ -70,10 +74,10 @@ module.exports = React.createClass({ }, render: function() { - var CallView = sdk.getComponent("voip.CallView"); - var TintableSvg = sdk.getComponent("elements.TintableSvg"); + const CallView = sdk.getComponent("voip.CallView"); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); - var fileDropTarget = null; + let fileDropTarget = null; if (this.props.draggingFile) { fileDropTarget = (
@@ -87,14 +91,13 @@ module.exports = React.createClass({ ); } - var conferenceCallNotification = null; + let conferenceCallNotification = null; if (this.props.displayConfCallNotification) { let supportedText = ''; let joinNode; if (!MatrixClientPeg.get().supportsVoip()) { supportedText = _t(" (unsupported)"); - } - else { + } else { joinNode = ( {_tJsx( "Join as voice or video.", @@ -105,7 +108,6 @@ module.exports = React.createClass({ ] )} ); - } // XXX: the translation here isn't great: appending ' (unsupported)' is likely to not make sense in many languages, // but there are translations for this in the languages we do have so I'm leaving it for now. @@ -118,7 +120,7 @@ module.exports = React.createClass({ ); } - var callView = ( + const callView = ( ); + let appsDrawer = null; + if(UserSettingsStore.isFeatureEnabled('matrix_apps') && this.props.showApps) { + appsDrawer = ; + } + return (
+ { appsDrawer } { fileDropTarget } { callView } { conferenceCallNotification } diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 434685e8e1..c83e32d9a8 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -13,16 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -var React = require('react'); +import React from 'react'; import { _t } from '../../../languageHandler'; -var CallHandler = require('../../../CallHandler'); -var MatrixClientPeg = require('../../../MatrixClientPeg'); -var Modal = require('../../../Modal'); -var sdk = require('../../../index'); -var dis = require('../../../dispatcher'); +import CallHandler from '../../../CallHandler'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import Modal from '../../../Modal'; +import sdk from '../../../index'; +import dis from '../../../dispatcher'; import Autocomplete from './Autocomplete'; import classNames from 'classnames'; - import UserSettingsStore from '../../../UserSettingsStore'; @@ -32,6 +31,8 @@ export default class MessageComposer extends React.Component { this.onCallClick = this.onCallClick.bind(this); this.onHangupClick = this.onHangupClick.bind(this); this.onUploadClick = this.onUploadClick.bind(this); + this.onShowAppsClick = this.onShowAppsClick.bind(this); + this.onHideAppsClick = this.onHideAppsClick.bind(this); this.onUploadFileSelected = this.onUploadFileSelected.bind(this); this.uploadFiles = this.uploadFiles.bind(this); this.onVoiceCallClick = this.onVoiceCallClick.bind(this); @@ -57,7 +58,6 @@ export default class MessageComposer extends React.Component { }, showFormatting: UserSettingsStore.getSyncedSetting('MessageComposer.showFormatting', false), }; - } componentDidMount() { @@ -127,7 +127,7 @@ export default class MessageComposer extends React.Component { if(shouldUpload) { // MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file if (files) { - for(var i=0; i console.log('Sent state'), (e) => console.error(e)); + // } + // } + onCallClick(ev) { + // NOTE -- Will be replaced by Jitsi code (currently commented) dis.dispatch({ action: 'place_call', type: ev.shiftKey ? "screensharing" : "video", room_id: this.props.room.roomId, }); + // this._startCallApp(false); } onVoiceCallClick(ev) { + // NOTE -- Will be replaced by Jitsi code (currently commented) dis.dispatch({ action: 'place_call', - type: 'voice', + type: "voice", room_id: this.props.room.roomId, }); + // this._startCallApp(true); + } + + onShowAppsClick(ev) { + dis.dispatch({ + action: 'appsDrawer', + show: true, + }); + } + + onHideAppsClick(ev) { + dis.dispatch({ + action: 'appsDrawer', + show: false, + }); } onInputContentChanged(content: string, selection: {start: number, end: number}) { @@ -216,19 +264,19 @@ export default class MessageComposer extends React.Component { } render() { - var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); - var uploadInputStyle = {display: 'none'}; - var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); - var TintableSvg = sdk.getComponent("elements.TintableSvg"); - var MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput" + + const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); + const uploadInputStyle = {display: 'none'}; + const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); + const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput" + (UserSettingsStore.isFeatureEnabled('rich_text_editor') ? "" : "Old")); - var controls = []; + const controls = []; controls.push(
-
+
, ); let e2eImg, e2eTitle, e2eClass; @@ -247,16 +295,15 @@ export default class MessageComposer extends React.Component { controls.push( {e2eTitle} + />, ); - var callButton, videoCallButton, hangupButton; + let callButton, videoCallButton, hangupButton, showAppsButton, hideAppsButton; if (this.props.callState && this.props.callState !== 'ended') { hangupButton =
{
; - } - else { + } else { callButton =
@@ -267,14 +314,29 @@ export default class MessageComposer extends React.Component {
; } - var canSendMessages = this.props.room.currentState.maySendMessage( + // Apps + if (UserSettingsStore.isFeatureEnabled('matrix_apps')) { + if (this.props.showApps) { + hideAppsButton = +
+ +
; + } else { + showAppsButton = +
+ +
; + } + } + + const canSendMessages = this.props.room.currentState.maySendMessage( MatrixClientPeg.get().credentials.userId); if (canSendMessages) { // This also currently includes the call buttons. Really we should // check separately for whether we can call, but this is slightly // complex because of conference calls. - var uploadButton = ( + const uploadButton = (
@@ -300,7 +362,7 @@ export default class MessageComposer extends React.Component { controls.push( this.messageComposerInput = c} + ref={(c) => this.messageComposerInput = c} key="controls_input" onResize={this.props.onResize} room={this.props.room} @@ -316,13 +378,15 @@ export default class MessageComposer extends React.Component { uploadButton, hangupButton, callButton, - videoCallButton + videoCallButton, + showAppsButton, + hideAppsButton, ); } else { controls.push(
{ _t('You do not have permission to post to this room') } -
+
, ); } @@ -340,7 +404,7 @@ export default class MessageComposer extends React.Component { const {style, blockType} = this.state.inputState; const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map( - name => { + (name) => { const active = style.includes(name) || blockType === name; const suffix = active ? '-o-n' : ''; const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name); @@ -403,5 +467,8 @@ MessageComposer.propTypes = { uploadFile: React.PropTypes.func.isRequired, // opacity for dynamic UI fading effects - opacity: React.PropTypes.number + opacity: React.PropTypes.number, + + // string representing the current room app drawer state + showApps: React.PropTypes.bool, }; diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 5ea92d18ce..aae91620d8 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -87,6 +87,13 @@ export default class MessageComposerInput extends React.Component { return 'toggle-mode'; } + // Allow opening of dev tools. getDefaultKeyBinding would be 'italic' for KEY_I + if (e.keyCode === KeyCode.KEY_I && e.shiftKey && e.ctrlKey) { + // When null is returned, draft-js will NOT preventDefault, allowing dev tools + // to be toggled when the editor is focussed + return null; + } + return getDefaultKeyBinding(e); } @@ -114,6 +121,7 @@ export default class MessageComposerInput extends React.Component { this.onEscape = this.onEscape.bind(this); this.setDisplayedCompletion = this.setDisplayedCompletion.bind(this); this.onMarkdownToggleClicked = this.onMarkdownToggleClicked.bind(this); + this.onTextPasted = this.onTextPasted.bind(this); const isRichtextEnabled = UserSettingsStore.getSyncedSetting('MessageComposerInput.isRichTextEnabled', false); @@ -126,6 +134,10 @@ export default class MessageComposerInput extends React.Component { // the original editor state, before we started tabbing through completions originalEditorState: null, + + // the virtual state "above" the history stack, the message currently being composed that + // we want to persist whilst browsing history + currentlyComposedEditorState: null, }; // bit of a hack, but we need to do this here since createEditorState needs isRichtextEnabled @@ -217,7 +229,8 @@ export default class MessageComposerInput extends React.Component { if (this.state.isRichtextEnabled) { contentState = Modifier.setBlockType(contentState, startSelection, 'blockquote'); } - const editorState = EditorState.push(this.state.editorState, contentState, 'insert-characters'); + let editorState = EditorState.push(this.state.editorState, contentState, 'insert-characters'); + editorState = EditorState.moveSelectionToEnd(editorState); this.onEditorContentChanged(editorState); editor.focus(); } @@ -425,6 +438,29 @@ export default class MessageComposerInput extends React.Component { return false; } + onTextPasted(text: string, html?: string) { + const currentSelection = this.state.editorState.getSelection(); + const currentContent = this.state.editorState.getCurrentContent(); + + let contentState = null; + + if (html) { + contentState = Modifier.replaceWithFragment( + currentContent, + currentSelection, + RichText.htmlToContentState(html).getBlockMap(), + ); + } else { + contentState = Modifier.replaceText(currentContent, currentSelection, text); + } + + let newEditorState = EditorState.push(this.state.editorState, contentState, 'insert-characters'); + + newEditorState = EditorState.forceSelection(newEditorState, contentState.getSelectionAfter()); + this.onEditorContentChanged(newEditorState); + return true; + } + handleReturn(ev) { if (ev.shiftKey) { this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState)); @@ -476,9 +512,30 @@ export default class MessageComposerInput extends React.Component { } if (this.state.isRichtextEnabled) { - contentHTML = HtmlUtils.stripParagraphs( - RichText.contentStateToHTML(contentState), - ); + // We should only send HTML if any block is styled or contains inline style + let shouldSendHTML = false; + const blocks = contentState.getBlocksAsArray(); + if (blocks.some((block) => block.getType() !== 'unstyled')) { + shouldSendHTML = true; + } else { + const characterLists = blocks.map((block) => block.getCharacterList()); + // For each block of characters, determine if any inline styles are applied + // and if yes, send HTML + characterLists.forEach((characters) => { + const numberOfStylesForCharacters = characters.map( + (character) => character.getStyle().toArray().length, + ).toArray(); + // If any character has more than 0 inline styles applied, send HTML + if (numberOfStylesForCharacters.some((styles) => styles > 0)) { + shouldSendHTML = true; + } + }); + } + if (shouldSendHTML) { + contentHTML = HtmlUtils.processHtmlForSending( + RichText.contentStateToHTML(contentState), + ); + } } else { const md = new Markdown(contentText); if (md.isPlainText()) { @@ -499,9 +556,15 @@ export default class MessageComposerInput extends React.Component { sendTextFn = this.client.sendEmoteMessage; } - this.historyManager.addItem( - this.state.isRichtextEnabled ? contentHTML : contentState.getPlainText(), - this.state.isRichtextEnabled ? 'html' : 'markdown'); + if (this.state.isRichtextEnabled) { + this.historyManager.addItem( + contentHTML ? contentHTML : contentText, + contentHTML ? 'html' : 'markdown', + ); + } else { + // Always store MD input as input history + this.historyManager.addItem(contentText, 'markdown'); + } let sendMessagePromise; if (contentHTML) { @@ -525,49 +588,108 @@ export default class MessageComposerInput extends React.Component { this.autocomplete.hide(); return true; + } + + onUpArrow = (e) => { + this.onVerticalArrow(e, true); }; - onUpArrow = async (e) => { - const completion = this.autocomplete.onUpArrow(); - if (completion == null) { - const newContent = this.historyManager.getItem(-1, this.state.isRichtextEnabled ? 'html' : 'markdown'); - if (!newContent) return false; - const editorState = EditorState.push(this.state.editorState, - newContent, - 'insert-characters'); - this.setState({editorState}); - return true; - } - e.preventDefault(); - return await this.setDisplayedCompletion(completion); + onDownArrow = (e) => { + this.onVerticalArrow(e, false); }; - onDownArrow = async (e) => { - const completion = this.autocomplete.onDownArrow(); - if (completion == null) { - const newContent = this.historyManager.getItem(+1, this.state.isRichtextEnabled ? 'html' : 'markdown'); - if (!newContent) return false; - const editorState = EditorState.push(this.state.editorState, - newContent, - 'insert-characters'); - this.setState({editorState}); - return true; - } - e.preventDefault(); - return await this.setDisplayedCompletion(completion); - }; - - // tab and shift-tab are mapped to down and up arrow respectively - onTab = async (e) => { - e.preventDefault(); // we *never* want tab's default to happen, but we do want up/down sometimes + onVerticalArrow = (e, up) => { + // Select history only if we are not currently auto-completing if (this.autocomplete.state.completionList.length === 0) { - await this.autocomplete.forceComplete(); - this.onDownArrow(e); + // Don't go back in history if we're in the middle of a multi-line message + const selection = this.state.editorState.getSelection(); + const blockKey = selection.getStartKey(); + const firstBlock = this.state.editorState.getCurrentContent().getFirstBlock(); + const lastBlock = this.state.editorState.getCurrentContent().getLastBlock(); + + const selectionOffset = selection.getAnchorOffset(); + let canMoveUp = false; + let canMoveDown = false; + if (blockKey === firstBlock.getKey()) { + const textBeforeCursor = firstBlock.getText().slice(0, selectionOffset); + canMoveUp = textBeforeCursor.indexOf('\n') === -1; + } + + if (blockKey === lastBlock.getKey()) { + const textAfterCursor = lastBlock.getText().slice(selectionOffset); + canMoveDown = textAfterCursor.indexOf('\n') === -1; + } + + if ((up && !canMoveUp) || (!up && !canMoveDown)) return; + + const selected = this.selectHistory(up); + if (selected) { + // We're selecting history, so prevent the key event from doing anything else + e.preventDefault(); + } } else { - await (e.shiftKey ? this.onUpArrow : this.onDownArrow)(e); + this.moveAutocompleteSelection(up); } }; + selectHistory = async (up) => { + const delta = up ? -1 : 1; + + // True if we are not currently selecting history, but composing a message + if (this.historyManager.currentIndex === this.historyManager.history.length) { + // We can't go any further - there isn't any more history, so nop. + if (!up) { + return; + } + this.setState({ + currentlyComposedEditorState: this.state.editorState, + }); + } else if (this.historyManager.currentIndex + delta === this.historyManager.history.length) { + // True when we return to the message being composed currently + this.setState({ + editorState: this.state.currentlyComposedEditorState, + }); + this.historyManager.currentIndex = this.historyManager.history.length; + return; + } + + const newContent = this.historyManager.getItem(delta, this.state.isRichtextEnabled ? 'html' : 'markdown'); + if (!newContent) return false; + let editorState = EditorState.push( + this.state.editorState, + newContent, + 'insert-characters', + ); + + // Move selection to the end of the selected history + let newSelection = SelectionState.createEmpty(newContent.getLastBlock().getKey()); + newSelection = newSelection.merge({ + focusOffset: newContent.getLastBlock().getLength(), + anchorOffset: newContent.getLastBlock().getLength(), + }); + editorState = EditorState.forceSelection(editorState, newSelection); + + this.setState({editorState}); + return true; + }; + + onTab = async (e) => { + e.preventDefault(); + if (this.autocomplete.state.completionList.length === 0) { + // Force completions to show for the text currently entered + await this.autocomplete.forceComplete(); + // Select the first item by moving "down" + await this.moveAutocompleteSelection(false); + } else { + await this.moveAutocompleteSelection(e.shiftKey); + } + }; + + moveAutocompleteSelection = (up) => { + const completion = up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow(); + return this.setDisplayedCompletion(completion); + }; + onEscape = async (e) => { e.preventDefault(); if (this.autocomplete) { @@ -706,6 +828,7 @@ export default class MessageComposerInput extends React.Component { keyBindingFn={MessageComposerInput.getKeyBinding} handleKeyCommand={this.handleKeyCommand} handleReturn={this.handleReturn} + handlePastedText={this.onTextPasted} handlePastedFiles={this.props.onFilesPasted} stripPastedStyles={!this.state.isRichtextEnabled} onTab={this.onTab} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7de380e1d5..2b0f15703c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1,944 +1,950 @@ { - "af":"Afrikaans", - "ar-ae":"Arabic (U.A.E.)", - "ar-bh":"Arabic (Bahrain)", - "ar-dz":"Arabic (Algeria)", - "ar-eg":"Arabic (Egypt)", - "ar-iq":"Arabic (Iraq)", - "ar-jo":"Arabic (Jordan)", - "ar-kw":"Arabic (Kuwait)", - "ar-lb":"Arabic (Lebanon)", - "ar-ly":"Arabic (Libya)", - "ar-ma":"Arabic (Morocco)", - "ar-om":"Arabic (Oman)", - "ar-qa":"Arabic (Qatar)", - "ar-sa":"Arabic (Saudi Arabia)", - "ar-sy":"Arabic (Syria)", - "ar-tn":"Arabic (Tunisia)", - "ar-ye":"Arabic (Yemen)", - "be":"Belarusian", - "bg":"Bulgarian", - "ca":"Catalan", - "cs":"Czech", - "da":"Danish", - "de-at":"German (Austria)", - "de-ch":"German (Switzerland)", - "de":"German", - "de-li":"German (Liechtenstein)", - "de-lu":"German (Luxembourg)", - "el":"Greek", - "en-au":"English (Australia)", - "en-bz":"English (Belize)", - "en-ca":"English (Canada)", - "en":"English", - "en-gb":"English (United Kingdom)", - "en-ie":"English (Ireland)", - "en-jm":"English (Jamaica)", - "en-nz":"English (New Zealand)", - "en-tt":"English (Trinidad)", - "en-us":"English (United States)", - "en-za":"English (South Africa)", - "es-ar":"Spanish (Argentina)", - "es-bo":"Spanish (Bolivia)", - "es-cl":"Spanish (Chile)", - "es-co":"Spanish (Colombia)", - "es-cr":"Spanish (Costa Rica)", - "es-do":"Spanish (Dominican Republic)", - "es-ec":"Spanish (Ecuador)", - "es-gt":"Spanish (Guatemala)", - "es-hn":"Spanish (Honduras)", - "es-mx":"Spanish (Mexico)", - "es-ni":"Spanish (Nicaragua)", - "es-pa":"Spanish (Panama)", - "es-pe":"Spanish (Peru)", - "es-pr":"Spanish (Puerto Rico)", - "es-py":"Spanish (Paraguay)", - "es":"Spanish (Spain)", - "es-sv":"Spanish (El Salvador)", - "es-uy":"Spanish (Uruguay)", - "es-ve":"Spanish (Venezuela)", - "et":"Estonian", - "eu":"Basque (Basque)", - "fa":"Farsi", - "fi":"Finnish", - "fo":"Faeroese", - "fr-be":"French (Belgium)", - "fr-ca":"French (Canada)", - "fr-ch":"French (Switzerland)", - "fr":"French", - "fr-lu":"French (Luxembourg)", - "ga":"Irish", - "gd":"Gaelic (Scotland)", - "he":"Hebrew", - "hi":"Hindi", - "hr":"Croatian", - "hu":"Hungarian", - "id":"Indonesian", - "is":"Icelandic", - "it-ch":"Italian (Switzerland)", - "it":"Italian", - "ja":"Japanese", - "ji":"Yiddish", - "ko":"Korean", - "lt":"Lithuanian", - "lv":"Latvian", - "mk":"Macedonian (FYROM)", - "ms":"Malaysian", - "mt":"Maltese", - "nl-be":"Dutch (Belgium)", - "nl":"Dutch", - "no":"Norwegian", - "pl":"Polish", - "pt-br":"Brazilian Portuguese", - "pt":"Portuguese", - "rm":"Rhaeto-Romanic", - "ro-mo":"Romanian (Republic of Moldova)", - "ro":"Romanian", - "ru-mo":"Russian (Republic of Moldova)", - "ru":"Russian", - "sb":"Sorbian", - "sk":"Slovak", - "sl":"Slovenian", - "sq":"Albanian", - "sr":"Serbian", - "sv-fi":"Swedish (Finland)", - "sv":"Swedish", - "sx":"Sutu", - "sz":"Sami (Lappish)", - "th":"Thai", - "tn":"Tswana", - "tr":"Turkish", - "ts":"Tsonga", - "uk":"Ukrainian", - "ur":"Urdu", - "ve":"Venda", - "vi":"Vietnamese", - "xh":"Xhosa", - "zh-cn":"Chinese (PRC)", - "zh-hk":"Chinese (Hong Kong SAR)", - "zh-sg":"Chinese (Singapore)", - "zh-tw":"Chinese (Taiwan)", - "zu":"Zulu", - "a room": "a room", - "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", - "Accept": "Accept", - "%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.", - "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", - "Account": "Account", - "Access Token:": "Access Token:", - "Active call (%(roomName)s)": "Active call (%(roomName)s)", - "Add": "Add", - "Add a topic": "Add a topic", - "Add email address": "Add email address", - "Add phone number": "Add phone number", - "Admin": "Admin", - "Admin tools": "Admin tools", - "And %(count)s more...": "And %(count)s more...", - "VoIP": "VoIP", - "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", - "No Microphones detected": "No Microphones detected", - "No Webcams detected": "No Webcams detected", - "No media permissions": "No media permissions", - "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", - "Default Device": "Default Device", - "Microphone": "Microphone", - "Camera": "Camera", - "Advanced": "Advanced", - "Algorithm": "Algorithm", - "Hide removed messages": "Hide removed messages", - "Always show message timestamps": "Always show message timestamps", - "Authentication": "Authentication", - "Alias (optional)": "Alias (optional)", - "all room members": "all room members", - "all room members, from the point they are invited": "all room members, from the point they are invited", - "all room members, from the point they joined": "all room members, from the point they joined", - "and": "and", - "%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others", - "%(items)s and one other": "%(items)s and one other", - "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", - "and %(overflowCount)s others...": "and %(overflowCount)s others...", - "and one other...": "and one other...", - "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", - "%(names)s and one other are typing": "%(names)s and one other are typing", - "%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing", - "An email has been sent to": "An email has been sent to", - "A new password must be entered.": "A new password must be entered.", - "%(senderName)s answered the call.": "%(senderName)s answered the call.", - "anyone": "anyone", - "An error has occurred.": "An error has occurred.", - "Anyone": "Anyone", - "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests", - "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests", - "Are you sure?": "Are you sure?", - "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", - "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", - "Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?", - "Attachment": "Attachment", - "Autoplay GIFs and videos": "Autoplay GIFs and videos", - "%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.", - "Ban": "Ban", - "Banned users": "Banned users", - "Bans user with given id": "Bans user with given id", - "Blacklisted": "Blacklisted", - "Bug Report": "Bug Report", - "Bulk Options": "Bulk Options", - "Call Timeout": "Call Timeout", - "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", - "Can't load user settings": "Can't load user settings", - "Change Password": "Change Password", - "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.", - "%(senderName)s changed their profile picture.": "%(senderName)s changed their profile picture.", - "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", - "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s changed the room name to %(roomName)s.", - "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s removed the room name.", - "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".", - "Changes to who can read history will only apply to future messages in this room": "Changes to who can read history will only apply to future messages in this room", - "Changes your display nickname": "Changes your display nickname", - "changing room on a RoomView is not supported": "changing room on a RoomView is not supported", - "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", - "Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key", - "Clear Cache and Reload": "Clear Cache and Reload", - "Clear Cache": "Clear Cache", - "Click here to join the discussion!": "Click here to join the discussion!", - "Click here to fix": "Click here to fix", - "Click to mute audio": "Click to mute audio", - "Click to mute video": "Click to mute video", - "click to reveal": "click to reveal", - "Click to unmute video": "Click to unmute video", - "Click to unmute audio": "Click to unmute audio", - "Close": "Close", - "Command error": "Command error", - "Commands": "Commands", - "Conference call failed.": "Conference call failed.", - "Conference calling is in development and may not be reliable.": "Conference calling is in development and may not be reliable.", - "Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms", - "Conference calls are not supported in this client": "Conference calls are not supported in this client", - "Confirm password": "Confirm password", - "Confirm your new password": "Confirm your new password", - "Continue": "Continue", - "Could not connect to the integration server": "Could not connect to the integration server", - "%(count)s new messages": { - "one": "%(count)s new message", - "other": "%(count)s new messages" - }, - "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", - "Create an account": "Create an account", - "Create Room": "Create Room", - "Cryptography": "Cryptography", - "Current password": "Current password", - "Curve25519 identity key": "Curve25519 identity key", - "Custom": "Custom", - "Custom level": "Custom level", - "/ddg is not a command": "/ddg is not a command", - "Deactivate Account": "Deactivate Account", - "Deactivate my account": "Deactivate my account", - "Decline": "Decline", - "Decrypt %(text)s": "Decrypt %(text)s", - "Decryption error": "Decryption error", - "Delete": "Delete", - "demote": "demote", - "Deops user with given id": "Deops user with given id", - "Default": "Default", - "Define the power level of a user": "Define the power level of a user", - "Device already verified!": "Device already verified!", - "Device ID": "Device ID", - "Device ID:": "Device ID:", - "device id: ": "device id: ", - "Device key:": "Device key:", - "Devices": "Devices", - "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room", - "Direct Chat": "Direct Chat", - "Direct chats": "Direct chats", - "Disable Notifications": "Disable Notifications", - "disabled": "disabled", - "Disable inline URL previews by default": "Disable inline URL previews by default", - "Disable markdown formatting": "Disable markdown formatting", - "Disinvite": "Disinvite", - "Display name": "Display name", - "Displays action": "Displays action", - "Don't send typing notifications": "Don't send typing notifications", - "Download %(text)s": "Download %(text)s", - "Drop File Here": "Drop File Here", - "Drop here %(toAction)s": "Drop here %(toAction)s", - "Drop here to tag %(section)s": "Drop here to tag %(section)s", - "Ed25519 fingerprint": "Ed25519 fingerprint", - "Email": "Email", - "Email address": "Email address", - "Email address (optional)": "Email address (optional)", - "Email, name or matrix ID": "Email, name or matrix ID", - "Emoji": "Emoji", - "Enable encryption": "Enable encryption", - "Enable Notifications": "Enable Notifications", - "enabled": "enabled", - "Encrypted by a verified device": "Encrypted by a verified device", - "Encrypted by an unverified device": "Encrypted by an unverified device", - "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption", - "Encrypted room": "Encrypted room", - "Encryption is enabled in this room": "Encryption is enabled in this room", - "Encryption is not enabled in this room": "Encryption is not enabled in this room", - "%(senderName)s ended the call.": "%(senderName)s ended the call.", - "End-to-end encryption information": "End-to-end encryption information", - "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable", - "Enter Code": "Enter Code", - "Enter passphrase": "Enter passphrase", - "Error": "Error", - "Error decrypting attachment": "Error decrypting attachment", - "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", - "Event information": "Event information", - "Existing Call": "Existing Call", - "Export": "Export", - "Export E2E room keys": "Export E2E room keys", - "Failed to ban user": "Failed to ban user", - "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", - "Failed to change power level": "Failed to change power level", - "Failed to delete device": "Failed to delete device", - "Failed to fetch avatar URL": "Failed to fetch avatar URL", - "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", - "Failed to join room": "Failed to join room", - "Failed to join the room": "Failed to join the room", - "Failed to kick": "Failed to kick", - "Failed to leave room": "Failed to leave room", - "Failed to load timeline position": "Failed to load timeline position", - "Failed to lookup current room": "Failed to lookup current room", - "Failed to mute user": "Failed to mute user", - "Failed to register as guest:": "Failed to register as guest:", - "Failed to reject invite": "Failed to reject invite", - "Failed to reject invitation": "Failed to reject invitation", - "Failed to save settings": "Failed to save settings", - "Failed to send email": "Failed to send email", - "Failed to send request.": "Failed to send request.", - "Failed to set avatar.": "Failed to set avatar.", - "Failed to set display name": "Failed to set display name", - "Failed to set up conference call": "Failed to set up conference call", - "Failed to toggle moderator status": "Failed to toggle moderator status", - "Failed to unban": "Failed to unban", - "Failed to upload file": "Failed to upload file", - "Failed to upload profile picture!": "Failed to upload profile picture!", - "Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email", - "Failure to create room": "Failure to create room", - "Favourite": "Favourite", - "favourite": "favourite", - "Favourites": "Favourites", - "Fill screen": "Fill screen", - "Filter room members": "Filter room members", - "Forget room": "Forget room", - "Forgot your password?": "Forgot your password?", - "For security, this session has been signed out. Please sign in again.": "For security, this session has been signed out. Please sign in again.", - "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.", - "Found a bug?": "Found a bug?", - "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", - "Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.", - "Guests can't set avatars. Please register.": "Guests can't set avatars. Please register.", - "Guest users can't create new rooms. Please register to create room and start a chat.": "Guest users can't create new rooms. Please register to create room and start a chat.", - "Guest users can't upload files. Please register to upload.": "Guest users can't upload files. Please register to upload.", - "Guests can't use labs features. Please register.": "Guests can't use labs features. Please register.", - "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", - "had": "had", - "Hangup": "Hangup", - "Hide read receipts": "Hide read receipts", - "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", - "Historical": "Historical", - "Home": "Home", - "Homeserver is": "Homeserver is", - "Identity Server is": "Identity Server is", - "I have verified my email address": "I have verified my email address", - "Import": "Import", - "Import E2E room keys": "Import E2E room keys", - "Incoming call from %(name)s": "Incoming call from %(name)s", - "Incoming video call from %(name)s": "Incoming video call from %(name)s", - "Incoming voice call from %(name)s": "Incoming voice call from %(name)s", - "Incorrect username and/or password.": "Incorrect username and/or password.", - "Incorrect verification code": "Incorrect verification code", - "Interface Language": "Interface Language", - "Invalid alias format": "Invalid alias format", - "Invalid address format": "Invalid address format", - "Invalid Email Address": "Invalid Email Address", - "Invalid file%(extra)s": "Invalid file%(extra)s", - "%(senderName)s invited %(targetName)s.": "%(senderName)s invited %(targetName)s.", - "Invite new room members": "Invite new room members", - "Invited": "Invited", - "Invites": "Invites", - "Invites user with given id to current room": "Invites user with given id to current room", - "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", - "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", - "%(displayName)s is typing": "%(displayName)s is typing", - "Sign in with": "Sign in with", - "Join as voice or video.": "Join as voice or video.", - "Join Room": "Join Room", - "joined and left": "joined and left", - "joined": "joined", - "%(targetName)s joined the room.": "%(targetName)s joined the room.", - "Joins room with given alias": "Joins room with given alias", - "Jump to first unread message.": "Jump to first unread message.", - "%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.", - "Kick": "Kick", - "Kicks user with given id": "Kicks user with given id", - "Labs": "Labs", - "Last seen": "Last seen", - "Leave room": "Leave room", - "left and rejoined": "left and rejoined", - "left": "left", - "%(targetName)s left the room.": "%(targetName)s left the room.", - "Level:": "Level:", - "List this room in %(domain)s's room directory?": "List this room in %(domain)s's room directory?", - "Local addresses for this room:": "Local addresses for this room:", - "Logged in as:": "Logged in as:", - "Login as guest": "Login as guest", - "Logout": "Logout", - "Low priority": "Low priority", - "%(senderName)s made future room history visible to": "%(senderName)s made future room history visible to", - "Manage Integrations": "Manage Integrations", - "Markdown is disabled": "Markdown is disabled", - "Markdown is enabled": "Markdown is enabled", - "matrix-react-sdk version:": "matrix-react-sdk version:", - "Members only": "Members only", - "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", - "Missing room_id in request": "Missing room_id in request", - "Missing user_id in request": "Missing user_id in request", - "Mobile phone number": "Mobile phone number", - "Mobile phone number (optional)": "Mobile phone number (optional)", - "Moderator": "Moderator", - "Must be viewing a room": "Must be viewing a room", - "Mute": "Mute", - "my Matrix ID": "my Matrix ID", - "Name": "Name", - "Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device", - "Never send encrypted messages to unverified devices in this room": "Never send encrypted messages to unverified devices in this room", - "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", - "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", - "New Composer & Autocomplete": "New Composer & Autocomplete", - "New password": "New password", - "New passwords don't match": "New passwords don't match", - "New passwords must match each other.": "New passwords must match each other.", - "none": "none", - "not set": "not set", - "not specified": "not specified", - "Notifications": "Notifications", - "(not supported by this browser)": "(not supported by this browser)", - "": "", - "NOT verified": "NOT verified", - "No devices with registered encryption keys": "No devices with registered encryption keys", - "No display name": "No display name", - "No more results": "No more results", - "No results": "No results", - "No users have specific privileges in this room": "No users have specific privileges in this room", - "OK": "OK", - "olm version:": "olm version:", - "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)", - "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", - "Only people who have been invited": "Only people who have been invited", - "Operation failed": "Operation failed", - "Otherwise, click here to send a bug report.": "Otherwise, click here to send a bug report.", - "Password": "Password", - "Password:": "Password:", - "Passwords can't be empty": "Passwords can't be empty", - "People": "People", - "Permissions": "Permissions", - "Phone": "Phone", - "%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.", - "Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.", - "Please Register": "Please Register", - "Power level must be positive integer.": "Power level must be positive integer.", - "Press": "Press", - "Press to start a chat with someone": "Press to start a chat with someone", - "Privacy warning": "Privacy warning", - "Private Chat": "Private Chat", - "Privileged Users": "Privileged Users", - "Profile": "Profile", - "Public Chat": "Public Chat", - "Reason": "Reason", - "Reason: %(reasonText)s": "Reason: %(reasonText)s", - "Revoke Moderator": "Revoke Moderator", - "Refer a friend to Riot:": "Refer a friend to Riot:", - "Register": "Register", - "rejected": "rejected", - "%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.", - "Reject invitation": "Reject invitation", - "Rejoin": "Rejoin", - "Remote addresses for this room:": "Remote addresses for this room:", - "Remove Contact Information?": "Remove Contact Information?", - "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).", - "%(senderName)s removed their profile picture.": "%(senderName)s removed their profile picture.", - "Remove": "Remove", - "Remove %(threePid)s?": "Remove %(threePid)s?", - "%(senderName)s requested a VoIP conference.": "%(senderName)s requested a VoIP conference.", - "Report it": "Report it", - "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", - "restore": "restore", - "Results from DuckDuckGo": "Results from DuckDuckGo", - "Return to app": "Return to app", - "Return to login screen": "Return to login screen", - "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings", - "Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again", - "riot-web version:": "riot-web version:", - "Room %(roomId)s not visible": "Room %(roomId)s not visible", - "Room Colour": "Room Colour", - "Room contains unknown devices": "Room contains unknown devices", - "Room name (optional)": "Room name (optional)", - "%(roomName)s does not exist.": "%(roomName)s does not exist.", - "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", - "Rooms": "Rooms", - "Save": "Save", - "Scroll to bottom of page": "Scroll to bottom of page", - "Scroll to unread messages": "Scroll to unread messages", - "Search": "Search", - "Search failed": "Search failed", - "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", - "Searching known users": "Searching known users", - "Seen by %(userName)s at %(dateTime)s": "Seen by %(userName)s at %(dateTime)s", - "Send a message (unencrypted)": "Send a message (unencrypted)", - "Send an encrypted message": "Send an encrypted message", - "Send anyway": "Send anyway", - "Sender device information": "Sender device information", - "Send Invites": "Send Invites", - "Send Reset Email": "Send Reset Email", - "sent an image": "sent an image", - "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s sent an image.", - "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.", - "sent a video": "sent a video", - "Server error": "Server error", - "Server may be unavailable or overloaded": "Server may be unavailable or overloaded", - "Server may be unavailable, overloaded, or search timed out :(": "Server may be unavailable, overloaded, or search timed out :(", - "Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big", - "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", - "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", - "Session ID": "Session ID", - "%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.", - "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", - "Set": "Set", - "Settings": "Settings", - "Show panel": "Show panel", - "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", - "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", - "Signed Out": "Signed Out", - "Sign in": "Sign in", - "Sign out": "Sign out", - "since the point in time of selecting this option": "since the point in time of selecting this option", - "since they joined": "since they joined", - "since they were invited": "since they were invited", - "Some of your messages have not been sent.": "Some of your messages have not been sent.", - "Someone": "Someone", - "Sorry, this homeserver is using a login which is not recognised ": "Sorry, this homeserver is using a login which is not recognised ", - "Start a chat": "Start a chat", - "Start authentication": "Start authentication", - "Start Chat": "Start Chat", - "Submit": "Submit", - "Success": "Success", - "tag as %(tagName)s": "tag as %(tagName)s", - "tag direct chat": "tag direct chat", - "Tagged as: ": "Tagged as: ", - "The default role for new room members is": "The default role for new room members is", - "The main address for this room is": "The main address for this room is", - "The phone number entered looks invalid": "The phone number entered looks invalid", - "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", - "This action cannot be performed by a guest user. Please register to be able to do this.": "This action cannot be performed by a guest user. Please register to be able to do this.", - "This email address is already in use": "This email address is already in use", - "This email address was not found": "This email address was not found", - "%(actionVerb)s this person?": "%(actionVerb)s this person?", - "The email address linked to your account must be entered.": "The email address linked to your account must be entered.", - "The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads", - "The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload", - "The remote side failed to pick up": "The remote side failed to pick up", - "This Home Server does not support login using email address.": "This Home Server does not support login using email address.", - "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", - "There was a problem logging in.": "There was a problem logging in.", - "This room has no local addresses": "This room has no local addresses", - "This room is not recognised.": "This room is not recognised.", - "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", - "The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged", - "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address", - "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", - "This phone number is already in use": "This phone number is already in use", - "This room": "This room", - "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", - "This room's internal ID is": "This room's internal ID is", - "times": "times", - "To ban users": "To ban users", - "to browse the directory": "to browse the directory", - "To configure the room": "To configure the room", - "to demote": "to demote", - "to favourite": "to favourite", - "To invite users into the room": "To invite users into the room", - "To kick users": "To kick users", - "To link to a room it must have an address.": "To link to a room it must have an address.", - "to make a room or": "to make a room or", - "To remove other users' messages": "To remove other users' messages", - "To reset your password, enter the email address linked to your account": "To reset your password, enter the email address linked to your account", - "to restore": "to restore", - "To send events of type": "To send events of type", - "To send messages": "To send messages", - "to start a chat with someone": "to start a chat with someone", - "to tag as %(tagName)s": "to tag as %(tagName)s", - "to tag direct chat": "to tag direct chat", - "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", - "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", - "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", - "Turn Markdown off": "Turn Markdown off", - "Turn Markdown on": "Turn Markdown on", - "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).", - "Unable to add email address": "Unable to add email address", - "Unable to remove contact information": "Unable to remove contact information", - "Unable to restore previous session": "Unable to restore previous session", - "Unable to verify email address.": "Unable to verify email address.", - "Unban": "Unban", - "Unbans user with given id": "Unbans user with given id", - "%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.", - "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", - "Unable to capture screen": "Unable to capture screen", - "Unable to enable Notifications": "Unable to enable Notifications", - "Unable to load device list": "Unable to load device list", - "Undecryptable": "Undecryptable", - "Unencrypted room": "Unencrypted room", - "unencrypted": "unencrypted", - "Unencrypted message": "Unencrypted message", - "unknown caller": "unknown caller", - "Unknown command": "Unknown command", - "unknown device": "unknown device", - "unknown error code": "unknown error code", - "Unknown room %(roomId)s": "Unknown room %(roomId)s", - "Unknown (user, device) pair:": "Unknown (user, device) pair:", - "unknown": "unknown", - "Unmute": "Unmute", - "Unnamed Room": "Unnamed Room", - "Unrecognised command:": "Unrecognised command:", - "Unrecognised room alias:": "Unrecognised room alias:", - "Unverified": "Unverified", - "Uploading %(filename)s and %(count)s others": { - "zero": "Uploading %(filename)s", - "one": "Uploading %(filename)s and %(count)s other", - "other": "Uploading %(filename)s and %(count)s others" - }, - "uploaded a file": "uploaded a file", - "Upload avatar": "Upload avatar", - "Upload Failed": "Upload Failed", - "Upload Files": "Upload Files", - "Upload file": "Upload file", - "Upload new:": "Upload new:", - "Usage": "Usage", - "Use compact timeline layout": "Use compact timeline layout", - "Use with caution": "Use with caution", - "User ID": "User ID", - "User Interface": "User Interface", - "%(user)s is a": "%(user)s is a", - "User name": "User name", - "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)", - "Username invalid: %(errMessage)s": "Username invalid: %(errMessage)s", - "Users": "Users", - "User": "User", - "Verification Pending": "Verification Pending", - "Verification": "Verification", - "verified": "verified", - "Verified": "Verified", - "Verified key": "Verified key", - "Video call": "Video call", - "Voice call": "Voice call", - "VoIP conference finished.": "VoIP conference finished.", - "VoIP conference started.": "VoIP conference started.", - "VoIP is unsupported": "VoIP is unsupported", - "(could not connect media)": "(could not connect media)", - "(no answer)": "(no answer)", - "(unknown failure: %(reason)s)": "(unknown failure: %(reason)s)", - "(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)", - "Warning!": "Warning!", - "WARNING: Device already verified, but keys do NOT MATCH!": "WARNING: Device already verified, but keys do NOT MATCH!", - "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!", - "Who can access this room?": "Who can access this room?", - "Who can read history?": "Who can read history?", - "Who would you like to add to this room?": "Who would you like to add to this room?", - "Who would you like to communicate with?": "Who would you like to communicate with?", - "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", - "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", - "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", - "You are already in a call.": "You are already in a call.", - "You're not in any rooms yet! Press to make a room or to browse the directory": "You're not in any rooms yet! Press to make a room or to browse the directory", - "You are trying to access %(roomName)s.": "You are trying to access %(roomName)s.", - "You cannot place a call with yourself.": "You cannot place a call with yourself.", - "You cannot place VoIP calls in this browser.": "You cannot place VoIP calls in this browser.", - "You do not have permission to post to this room": "You do not have permission to post to this room", - "You have been banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", - "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s", - "You have been kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)s.", - "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device", - "You have disabled URL previews by default.": "You have disabled URL previews by default.", - "You have enabled URL previews by default.": "You have enabled URL previews by default.", - "You have entered an invalid contact. Try using their Matrix ID or email address.": "You have entered an invalid contact. Try using their Matrix ID or email address.", - "You have no visible notifications": "You have no visible notifications", - "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", - "you must be a": "you must be a", - "You must register to use this functionality": "You must register to use this functionality", - "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", - "You need to be logged in.": "You need to be logged in.", - "You need to enter a user name.": "You need to enter a user name.", - "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", - "Your password has been reset": "Your password has been reset", - "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them", - "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?", - "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?", - "You should not yet trust it to secure data": "You should not yet trust it to secure data", - "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.", - "Your home server does not support device management.": "Your home server does not support device management.", - "Sun": "Sun", - "Mon": "Mon", - "Tue": "Tue", - "Wed": "Wed", - "Thu": "Thu", - "Fri": "Fri", - "Sat": "Sat", - "Jan": "Jan", - "Feb": "Feb", - "Mar": "Mar", - "Apr": "Apr", - "May": "May", - "Jun": "Jun", - "Jul": "Jul", - "Aug": "Aug", - "Sep": "Sep", - "Oct": "Oct", - "Nov": "Nov", - "Dec": "Dec", - "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s", - "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", - "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", - "Set a display name:": "Set a display name:", - "Set a Display Name": "Set a Display Name", - "Upload an avatar:": "Upload an avatar:", - "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", - "Missing password.": "Missing password.", - "Passwords don't match.": "Passwords don't match.", - "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Password too short (min %(MIN_PASSWORD_LENGTH)s).", - "This doesn't look like a valid email address.": "This doesn't look like a valid email address.", - "This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.", - "User names may only contain letters, numbers, dots, hyphens and underscores.": "User names may only contain letters, numbers, dots, hyphens and underscores.", - "An unknown error occurred.": "An unknown error occurred.", - "I already have an account": "I already have an account", - "An error occurred: %(error_string)s": "An error occurred: %(error_string)s", - "Topic": "Topic", - "Make Moderator": "Make Moderator", - "Make this room private": "Make this room private", - "Share message history with new users": "Share message history with new users", - "Encrypt room": "Encrypt room", - "There are no visible files in this room": "There are no visible files in this room", - "Room": "Room", - "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", - "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.", - "Auto-complete": "Auto-complete", - "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Resend all or cancel all now. You can also select individual messages to resend or cancel.", - "(~%(count)s results)": { - "one": "(~%(count)s result)", - "other": "(~%(count)s results)" - }, - "Cancel": "Cancel", - "or": "or", - "Active call": "Active call", - "Monday": "Monday", - "Tuesday": "Tuesday", - "Wednesday": "Wednesday", - "Thursday": "Thursday", - "Friday": "Friday", - "Saturday": "Saturday", - "Sunday": "Sunday", - "bold": "bold", - "italic": "italic", - "strike": "strike", - "underline": "underline", - "code":"code", - "quote":"quote", - "bullet":"bullet", - "numbullet":"numbullet", - "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times", - "%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times", - "%(severalUsers)sjoined": "%(severalUsers)sjoined", - "%(oneUser)sjoined": "%(oneUser)sjoined", - "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times", - "%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times", - "%(severalUsers)sleft": "%(severalUsers)sleft", - "%(oneUser)sleft": "%(oneUser)sleft", - "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times", - "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times", - "%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left", - "%(oneUser)sjoined and left": "%(oneUser)sjoined and left", - "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times", - "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times", - "%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined", - "%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined", - "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times", - "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times", - "%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations", - "%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation", - "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times", - "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times", - "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn", - "%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn", - "were invited %(repeats)s times": "were invited %(repeats)s times", - "was invited %(repeats)s times": "was invited %(repeats)s times", - "were invited": "were invited", - "was invited": "was invited", - "were banned %(repeats)s times": "were banned %(repeats)s times", - "was banned %(repeats)s times": "was banned %(repeats)s times", - "were banned": "were banned", - "was banned": "was banned", - "were unbanned %(repeats)s times": "were unbanned %(repeats)s times", - "was unbanned %(repeats)s times": "was unbanned %(repeats)s times", - "were unbanned": "were unbanned", - "was unbanned": "was unbanned", - "were kicked %(repeats)s times": "were kicked %(repeats)s times", - "was kicked %(repeats)s times": "was kicked %(repeats)s times", - "were kicked": "were kicked", - "was kicked": "was kicked", - "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times", - "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times", - "%(severalUsers)schanged their name": "%(severalUsers)schanged their name", - "%(oneUser)schanged their name": "%(oneUser)schanged their name", - "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times", - "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times", - "%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar", - "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar", - "Please select the destination room for this message": "Please select the destination room for this message", - "Create new room": "Create new room", - "Welcome page": "Welcome page", - "Room directory": "Room directory", - "Start chat": "Start chat", - "New Password": "New Password", - "Start automatically after system login": "Start automatically after system login", - "Desktop specific": "Desktop specific", - "Analytics": "Analytics", - "Opt out of analytics": "Opt out of analytics", - "Options": "Options", - "Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.", - "Passphrases must match": "Passphrases must match", - "Passphrase must not be empty": "Passphrase must not be empty", - "Export room keys": "Export room keys", - "Confirm passphrase": "Confirm passphrase", - "Import room keys": "Import room keys", - "File to import": "File to import", - "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.", - "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.", - "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.", - "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.", - "You must join the room to see its files": "You must join the room to see its files", - "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", - "Start new chat": "Start new chat", - "Guest users can't invite users. Please register.": "Guest users can't invite users. Please register.", - "Failed to invite": "Failed to invite", - "Failed to invite user": "Failed to invite user", - "Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:", - "Confirm Removal": "Confirm Removal", - "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", - "Unknown error": "Unknown error", - "Incorrect password": "Incorrect password", - "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "This will make your account permanently unusable. You will not be able to re-register the same user ID.", - "This action is irreversible.": "This action is irreversible.", - "To continue, please enter your password.": "To continue, please enter your password.", - "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:", - "Device name": "Device name", - "Device Name": "Device Name", - "Device key": "Device key", - "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.": "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.", - "In future this verification process will be more sophisticated.": "In future this verification process will be more sophisticated.", - "Verify device": "Verify device", - "I verify that the keys match": "I verify that the keys match", - "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.", - "Unable to restore session": "Unable to restore session", - "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.", - "Continue anyway": "Continue anyway", - "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?", - "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.", - "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.", - "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", - "Unknown devices": "Unknown devices", - "Unknown Address": "Unknown Address", - "Unblacklist": "Unblacklist", - "Blacklist": "Blacklist", - "Unverify": "Unverify", - "Verify...": "Verify...", - "ex. @bob:example.com": "ex. @bob:example.com", - "Add User": "Add User", - "This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot", - "Sign in with CAS": "Sign in with CAS", - "Custom Server Options": "Custom Server Options", - "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.", - "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.", - "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.", - "Dismiss": "Dismiss", - "Please check your email to continue registration.": "Please check your email to continue registration.", - "Token incorrect": "Token incorrect", - "A text message has been sent to": "A text message has been sent to", - "Please enter the code it contains:": "Please enter the code it contains:", - "powered by Matrix": "powered by Matrix", - "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", - "You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s", - "Default server": "Default server", - "Custom server": "Custom server", - "Home server URL": "Home server URL", - "Identity server URL": "Identity server URL", - "What does this mean?": "What does this mean?", - "Error decrypting audio": "Error decrypting audio", - "Error decrypting image": "Error decrypting image", - "Image '%(Body)s' cannot be displayed.": "Image '%(Body)s' cannot be displayed.", - "This image cannot be displayed.": "This image cannot be displayed.", - "Error decrypting video": "Error decrypting video", - "Add an Integration": "Add an Integration", - "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", - "Removed or unknown message type": "Removed or unknown message type", - "Disable URL previews by default for participants in this room": "Disable URL previews by default for participants in this room", - "Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)", - "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", - "URL Previews": "URL Previews", - "Enable URL previews for this room (affects only you)": "Enable URL previews for this room (affects only you)", - "Drop file here to upload": "Drop file here to upload", - " (unsupported)": " (unsupported)", - "Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)s.", - "for %(amount)ss": "for %(amount)ss", - "for %(amount)sm": "for %(amount)sm", - "for %(amount)sh": "for %(amount)sh", - "for %(amount)sd": "for %(amount)sd", - "Online": "Online", - "Idle": "Idle", - "Offline": "Offline", - "Updates": "Updates", - "Check for update": "Check for update", - "Start chatting": "Start chatting", - "Start Chatting": "Start Chatting", - "Click on the button below to start chatting!": "Click on the button below to start chatting!", - "$senderDisplayName changed the room avatar to ": "$senderDisplayName changed the room avatar to ", - "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.", - "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s", - "Username available": "Username available", - "Username not available": "Username not available", - "Something went wrong!": "Something went wrong!", - "This will be your account name on the homeserver, or you can pick a different server.": "This will be your account name on the homeserver, or you can pick a different server.", - "If you already have a Matrix account you can log in instead.": "If you already have a Matrix account you can log in instead.", - "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", - "Not a valid Riot keyfile": "Not a valid Riot keyfile", - "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", - "Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls", - "Do you want to set an email address?": "Do you want to set an email address?", - "This will allow you to reset your password and receive notifications.": "This will allow you to reset your password and receive notifications.", - "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", - "Skip":"Skip", - "Start verification": "Start verification", - "Share without verifying": "Share without verifying", - "Ignore request": "Ignore request", - "You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.", - "Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.", - "Encryption key request": "Encryption key request", - "Autocomplete Delay (ms):": "Autocomplete Delay (ms):", - "This Home server does not support groups": "This Home server does not support groups", - "Loading device info...": "Loading device info...", - "Groups": "Groups", - "Create a new group": "Create a new group", - "Create Group": "Create Group", - "Group Name": "Group Name", - "Example": "Example", - "Create": "Create", - "Group ID": "Group ID", - "+example:%(domain)s": "+example:%(domain)s", - "Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s", - "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", - "Room creation failed": "Room creation failed", - "You are a member of these groups": "You are a member of these groups", - "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.", - "Join an existing group": "Join an existing group", - "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.": "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org." + "Add a widget": "Add a widget", + "af": "Afrikaans", + "ar-ae": "Arabic (U.A.E.)", + "ar-bh": "Arabic (Bahrain)", + "ar-dz": "Arabic (Algeria)", + "ar-eg": "Arabic (Egypt)", + "ar-iq": "Arabic (Iraq)", + "ar-jo": "Arabic (Jordan)", + "ar-kw": "Arabic (Kuwait)", + "ar-lb": "Arabic (Lebanon)", + "ar-ly": "Arabic (Libya)", + "ar-ma": "Arabic (Morocco)", + "ar-om": "Arabic (Oman)", + "ar-qa": "Arabic (Qatar)", + "ar-sa": "Arabic (Saudi Arabia)", + "ar-sy": "Arabic (Syria)", + "ar-tn": "Arabic (Tunisia)", + "ar-ye": "Arabic (Yemen)", + "be": "Belarusian", + "bg": "Bulgarian", + "ca": "Catalan", + "cs": "Czech", + "da": "Danish", + "de-at": "German (Austria)", + "de-ch": "German (Switzerland)", + "de": "German", + "de-li": "German (Liechtenstein)", + "de-lu": "German (Luxembourg)", + "el": "Greek", + "en-au": "English (Australia)", + "en-bz": "English (Belize)", + "en-ca": "English (Canada)", + "en": "English", + "en-gb": "English (United Kingdom)", + "en-ie": "English (Ireland)", + "en-jm": "English (Jamaica)", + "en-nz": "English (New Zealand)", + "en-tt": "English (Trinidad)", + "en-us": "English (United States)", + "en-za": "English (South Africa)", + "es-ar": "Spanish (Argentina)", + "es-bo": "Spanish (Bolivia)", + "es-cl": "Spanish (Chile)", + "es-co": "Spanish (Colombia)", + "es-cr": "Spanish (Costa Rica)", + "es-do": "Spanish (Dominican Republic)", + "es-ec": "Spanish (Ecuador)", + "es-gt": "Spanish (Guatemala)", + "es-hn": "Spanish (Honduras)", + "es-mx": "Spanish (Mexico)", + "es-ni": "Spanish (Nicaragua)", + "es-pa": "Spanish (Panama)", + "es-pe": "Spanish (Peru)", + "es-pr": "Spanish (Puerto Rico)", + "es-py": "Spanish (Paraguay)", + "es": "Spanish (Spain)", + "es-sv": "Spanish (El Salvador)", + "es-uy": "Spanish (Uruguay)", + "es-ve": "Spanish (Venezuela)", + "et": "Estonian", + "eu": "Basque (Basque)", + "fa": "Farsi", + "fi": "Finnish", + "fo": "Faeroese", + "fr-be": "French (Belgium)", + "fr-ca": "French (Canada)", + "fr-ch": "French (Switzerland)", + "fr": "French", + "fr-lu": "French (Luxembourg)", + "ga": "Irish", + "gd": "Gaelic (Scotland)", + "he": "Hebrew", + "hi": "Hindi", + "hr": "Croatian", + "hu": "Hungarian", + "id": "Indonesian", + "is": "Icelandic", + "it-ch": "Italian (Switzerland)", + "it": "Italian", + "ja": "Japanese", + "ji": "Yiddish", + "ko": "Korean", + "lt": "Lithuanian", + "lv": "Latvian", + "mk": "Macedonian (FYROM)", + "ms": "Malaysian", + "mt": "Maltese", + "nl-be": "Dutch (Belgium)", + "nl": "Dutch", + "no": "Norwegian", + "pl": "Polish", + "pt-br": "Brazilian Portuguese", + "pt": "Portuguese", + "rm": "Rhaeto-Romanic", + "ro-mo": "Romanian (Republic of Moldova)", + "ro": "Romanian", + "ru-mo": "Russian (Republic of Moldova)", + "ru": "Russian", + "sb": "Sorbian", + "sk": "Slovak", + "sl": "Slovenian", + "sq": "Albanian", + "sr": "Serbian", + "sv-fi": "Swedish (Finland)", + "sv": "Swedish", + "sx": "Sutu", + "sz": "Sami (Lappish)", + "th": "Thai", + "tn": "Tswana", + "tr": "Turkish", + "ts": "Tsonga", + "uk": "Ukrainian", + "ur": "Urdu", + "ve": "Venda", + "vi": "Vietnamese", + "xh": "Xhosa", + "zh-cn": "Chinese (PRC)", + "zh-hk": "Chinese (Hong Kong SAR)", + "zh-sg": "Chinese (Singapore)", + "zh-tw": "Chinese (Taiwan)", + "zu": "Zulu", + "a room": "a room", + "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", + "Accept": "Accept", + "%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.", + "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", + "Account": "Account", + "Access Token:": "Access Token:", + "Active call (%(roomName)s)": "Active call (%(roomName)s)", + "Add": "Add", + "Add a topic": "Add a topic", + "Add email address": "Add email address", + "Add phone number": "Add phone number", + "Admin": "Admin", + "Admin tools": "Admin tools", + "And %(count)s more...": "And %(count)s more...", + "VoIP": "VoIP", + "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", + "No Microphones detected": "No Microphones detected", + "No Webcams detected": "No Webcams detected", + "No media permissions": "No media permissions", + "You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam", + "Default Device": "Default Device", + "Microphone": "Microphone", + "Camera": "Camera", + "Advanced": "Advanced", + "Algorithm": "Algorithm", + "Hide removed messages": "Hide removed messages", + "Always show message timestamps": "Always show message timestamps", + "Authentication": "Authentication", + "Alias (optional)": "Alias (optional)", + "all room members": "all room members", + "all room members, from the point they are invited": "all room members, from the point they are invited", + "all room members, from the point they joined": "all room members, from the point they joined", + "and": "and", + "%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others", + "%(items)s and one other": "%(items)s and one other", + "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", + "and %(overflowCount)s others...": "and %(overflowCount)s others...", + "and one other...": "and one other...", + "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", + "%(names)s and one other are typing": "%(names)s and one other are typing", + "%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing", + "An email has been sent to": "An email has been sent to", + "A new password must be entered.": "A new password must be entered.", + "%(senderName)s answered the call.": "%(senderName)s answered the call.", + "anyone": "anyone", + "An error has occurred.": "An error has occurred.", + "Anyone": "Anyone", + "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests", + "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests", + "Are you sure?": "Are you sure?", + "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", + "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", + "Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?", + "Attachment": "Attachment", + "Autoplay GIFs and videos": "Autoplay GIFs and videos", + "%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.", + "Ban": "Ban", + "Banned users": "Banned users", + "Bans user with given id": "Bans user with given id", + "Blacklisted": "Blacklisted", + "Bug Report": "Bug Report", + "Bulk Options": "Bulk Options", + "Call Timeout": "Call Timeout", + "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", + "Can't load user settings": "Can't load user settings", + "Change Password": "Change Password", + "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.", + "%(senderName)s changed their profile picture.": "%(senderName)s changed their profile picture.", + "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", + "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s changed the room name to %(roomName)s.", + "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s removed the room name.", + "%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s changed the topic to \"%(topic)s\".", + "Changes to who can read history will only apply to future messages in this room": "Changes to who can read history will only apply to future messages in this room", + "Changes your display nickname": "Changes your display nickname", + "changing room on a RoomView is not supported": "changing room on a RoomView is not supported", + "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Changing password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", + "Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key", + "Clear Cache and Reload": "Clear Cache and Reload", + "Clear Cache": "Clear Cache", + "Click here to join the discussion!": "Click here to join the discussion!", + "Click here to fix": "Click here to fix", + "Click to mute audio": "Click to mute audio", + "Click to mute video": "Click to mute video", + "click to reveal": "click to reveal", + "Click to unmute video": "Click to unmute video", + "Click to unmute audio": "Click to unmute audio", + "Close": "Close", + "Command error": "Command error", + "Commands": "Commands", + "Conference call failed.": "Conference call failed.", + "Conference calling is in development and may not be reliable.": "Conference calling is in development and may not be reliable.", + "Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms", + "Conference calls are not supported in this client": "Conference calls are not supported in this client", + "Confirm password": "Confirm password", + "Confirm your new password": "Confirm your new password", + "Continue": "Continue", + "Could not connect to the integration server": "Could not connect to the integration server", + "%(count)s new messages": { + "one": "%(count)s new message", + "other": "%(count)s new messages" + }, + "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", + "Create an account": "Create an account", + "Create Room": "Create Room", + "Cryptography": "Cryptography", + "Current password": "Current password", + "Curve25519 identity key": "Curve25519 identity key", + "Custom": "Custom", + "Custom level": "Custom level", + "/ddg is not a command": "/ddg is not a command", + "Deactivate Account": "Deactivate Account", + "Deactivate my account": "Deactivate my account", + "Decline": "Decline", + "Decrypt %(text)s": "Decrypt %(text)s", + "Decryption error": "Decryption error", + "Delete": "Delete", + "demote": "demote", + "Deops user with given id": "Deops user with given id", + "Default": "Default", + "Define the power level of a user": "Define the power level of a user", + "Device already verified!": "Device already verified!", + "Device ID": "Device ID", + "Device ID:": "Device ID:", + "device id: ": "device id: ", + "Device key:": "Device key:", + "Devices": "Devices", + "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room", + "Direct Chat": "Direct Chat", + "Direct chats": "Direct chats", + "Disable Notifications": "Disable Notifications", + "disabled": "disabled", + "Disable inline URL previews by default": "Disable inline URL previews by default", + "Disable markdown formatting": "Disable markdown formatting", + "Disinvite": "Disinvite", + "Display name": "Display name", + "Displays action": "Displays action", + "Don't send typing notifications": "Don't send typing notifications", + "Download %(text)s": "Download %(text)s", + "Drop File Here": "Drop File Here", + "Drop here %(toAction)s": "Drop here %(toAction)s", + "Drop here to tag %(section)s": "Drop here to tag %(section)s", + "Ed25519 fingerprint": "Ed25519 fingerprint", + "Email": "Email", + "Email address": "Email address", + "Email address (optional)": "Email address (optional)", + "Email, name or matrix ID": "Email, name or matrix ID", + "Emoji": "Emoji", + "Enable automatic language detection for syntax highlighting": "Enable automatic language detection for syntax highlighting", + "Enable encryption": "Enable encryption", + "Enable Notifications": "Enable Notifications", + "enabled": "enabled", + "Encrypted by a verified device": "Encrypted by a verified device", + "Encrypted by an unverified device": "Encrypted by an unverified device", + "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption", + "Encrypted room": "Encrypted room", + "Encryption is enabled in this room": "Encryption is enabled in this room", + "Encryption is not enabled in this room": "Encryption is not enabled in this room", + "%(senderName)s ended the call.": "%(senderName)s ended the call.", + "End-to-end encryption information": "End-to-end encryption information", + "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable", + "Enter Code": "Enter Code", + "Enter passphrase": "Enter passphrase", + "Error": "Error", + "Error decrypting attachment": "Error decrypting attachment", + "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", + "Event information": "Event information", + "Existing Call": "Existing Call", + "Export": "Export", + "Export E2E room keys": "Export E2E room keys", + "Failed to ban user": "Failed to ban user", + "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", + "Failed to change power level": "Failed to change power level", + "Failed to delete device": "Failed to delete device", + "Failed to fetch avatar URL": "Failed to fetch avatar URL", + "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", + "Failed to join room": "Failed to join room", + "Failed to join the room": "Failed to join the room", + "Failed to kick": "Failed to kick", + "Failed to leave room": "Failed to leave room", + "Failed to load timeline position": "Failed to load timeline position", + "Failed to lookup current room": "Failed to lookup current room", + "Failed to mute user": "Failed to mute user", + "Failed to register as guest:": "Failed to register as guest:", + "Failed to reject invite": "Failed to reject invite", + "Failed to reject invitation": "Failed to reject invitation", + "Failed to save settings": "Failed to save settings", + "Failed to send email": "Failed to send email", + "Failed to send request.": "Failed to send request.", + "Failed to set avatar.": "Failed to set avatar.", + "Failed to set display name": "Failed to set display name", + "Failed to set up conference call": "Failed to set up conference call", + "Failed to toggle moderator status": "Failed to toggle moderator status", + "Failed to unban": "Failed to unban", + "Failed to upload file": "Failed to upload file", + "Failed to upload profile picture!": "Failed to upload profile picture!", + "Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email", + "Failure to create room": "Failure to create room", + "Favourite": "Favourite", + "favourite": "favourite", + "Favourites": "Favourites", + "Fill screen": "Fill screen", + "Filter room members": "Filter room members", + "Forget room": "Forget room", + "Forgot your password?": "Forgot your password?", + "For security, this session has been signed out. Please sign in again.": "For security, this session has been signed out. Please sign in again.", + "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.", + "Found a bug?": "Found a bug?", + "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", + "Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.", + "Guests can't set avatars. Please register.": "Guests can't set avatars. Please register.", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Guest users can't create new rooms. Please register to create room and start a chat.", + "Guest users can't upload files. Please register to upload.": "Guest users can't upload files. Please register to upload.", + "Guests can't use labs features. Please register.": "Guests can't use labs features. Please register.", + "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", + "had": "had", + "Hangup": "Hangup", + "Hide Apps": "Hide Apps", + "Hide read receipts": "Hide read receipts", + "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", + "Historical": "Historical", + "Home": "Home", + "Homeserver is": "Homeserver is", + "Identity Server is": "Identity Server is", + "I have verified my email address": "I have verified my email address", + "Import": "Import", + "Import E2E room keys": "Import E2E room keys", + "Incoming call from %(name)s": "Incoming call from %(name)s", + "Incoming video call from %(name)s": "Incoming video call from %(name)s", + "Incoming voice call from %(name)s": "Incoming voice call from %(name)s", + "Incorrect username and/or password.": "Incorrect username and/or password.", + "Incorrect verification code": "Incorrect verification code", + "Interface Language": "Interface Language", + "Invalid alias format": "Invalid alias format", + "Invalid address format": "Invalid address format", + "Invalid Email Address": "Invalid Email Address", + "Invalid file%(extra)s": "Invalid file%(extra)s", + "%(senderName)s invited %(targetName)s.": "%(senderName)s invited %(targetName)s.", + "Invite new room members": "Invite new room members", + "Invited": "Invited", + "Invites": "Invites", + "Invites user with given id to current room": "Invites user with given id to current room", + "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address", + "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", + "%(displayName)s is typing": "%(displayName)s is typing", + "Sign in with": "Sign in with", + "Join as voice or video.": "Join as voice or video.", + "Join Room": "Join Room", + "joined and left": "joined and left", + "joined": "joined", + "%(targetName)s joined the room.": "%(targetName)s joined the room.", + "Joins room with given alias": "Joins room with given alias", + "Jump to first unread message.": "Jump to first unread message.", + "%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.", + "Kick": "Kick", + "Kicks user with given id": "Kicks user with given id", + "Labs": "Labs", + "Last seen": "Last seen", + "Leave room": "Leave room", + "left and rejoined": "left and rejoined", + "left": "left", + "%(targetName)s left the room.": "%(targetName)s left the room.", + "Level:": "Level:", + "List this room in %(domain)s's room directory?": "List this room in %(domain)s's room directory?", + "Local addresses for this room:": "Local addresses for this room:", + "Logged in as:": "Logged in as:", + "Login as guest": "Login as guest", + "Logout": "Logout", + "Low priority": "Low priority", + "%(senderName)s made future room history visible to": "%(senderName)s made future room history visible to", + "Manage Integrations": "Manage Integrations", + "Markdown is disabled": "Markdown is disabled", + "Markdown is enabled": "Markdown is enabled", + "matrix-react-sdk version:": "matrix-react-sdk version:", + "Matrix Apps": "Matrix Apps", + "Members only": "Members only", + "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", + "Missing room_id in request": "Missing room_id in request", + "Missing user_id in request": "Missing user_id in request", + "Mobile phone number": "Mobile phone number", + "Mobile phone number (optional)": "Mobile phone number (optional)", + "Moderator": "Moderator", + "Must be viewing a room": "Must be viewing a room", + "Mute": "Mute", + "my Matrix ID": "my Matrix ID", + "Name": "Name", + "Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device", + "Never send encrypted messages to unverified devices in this room": "Never send encrypted messages to unverified devices in this room", + "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device", + "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)", + "New Composer & Autocomplete": "New Composer & Autocomplete", + "New password": "New password", + "New passwords don't match": "New passwords don't match", + "New passwords must match each other.": "New passwords must match each other.", + "none": "none", + "not set": "not set", + "not specified": "not specified", + "Notifications": "Notifications", + "(not supported by this browser)": "(not supported by this browser)", + "": "", + "NOT verified": "NOT verified", + "No devices with registered encryption keys": "No devices with registered encryption keys", + "No display name": "No display name", + "No more results": "No more results", + "No results": "No results", + "No users have specific privileges in this room": "No users have specific privileges in this room", + "OK": "OK", + "olm version:": "olm version:", + "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)", + "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below", + "Only people who have been invited": "Only people who have been invited", + "Operation failed": "Operation failed", + "Otherwise, click here to send a bug report.": "Otherwise, click here to send a bug report.", + "Password": "Password", + "Password:": "Password:", + "Passwords can't be empty": "Passwords can't be empty", + "People": "People", + "Permissions": "Permissions", + "Phone": "Phone", + "%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.", + "Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.", + "Please Register": "Please Register", + "Power level must be positive integer.": "Power level must be positive integer.", + "Press": "Press", + "Press to start a chat with someone": "Press to start a chat with someone", + "Privacy warning": "Privacy warning", + "Private Chat": "Private Chat", + "Privileged Users": "Privileged Users", + "Profile": "Profile", + "Public Chat": "Public Chat", + "Reason": "Reason", + "Reason: %(reasonText)s": "Reason: %(reasonText)s", + "Revoke Moderator": "Revoke Moderator", + "Refer a friend to Riot:": "Refer a friend to Riot:", + "Register": "Register", + "rejected": "rejected", + "%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.", + "Reject invitation": "Reject invitation", + "Rejoin": "Rejoin", + "Remote addresses for this room:": "Remote addresses for this room:", + "Remove Contact Information?": "Remove Contact Information?", + "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).", + "%(senderName)s removed their profile picture.": "%(senderName)s removed their profile picture.", + "Remove": "Remove", + "Remove %(threePid)s?": "Remove %(threePid)s?", + "%(senderName)s requested a VoIP conference.": "%(senderName)s requested a VoIP conference.", + "Report it": "Report it", + "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.", + "restore": "restore", + "Results from DuckDuckGo": "Results from DuckDuckGo", + "Return to app": "Return to app", + "Return to login screen": "Return to login screen", + "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings", + "Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again", + "riot-web version:": "riot-web version:", + "Room %(roomId)s not visible": "Room %(roomId)s not visible", + "Room Colour": "Room Colour", + "Room contains unknown devices": "Room contains unknown devices", + "Room name (optional)": "Room name (optional)", + "%(roomName)s does not exist.": "%(roomName)s does not exist.", + "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", + "Rooms": "Rooms", + "Save": "Save", + "Scroll to bottom of page": "Scroll to bottom of page", + "Scroll to unread messages": "Scroll to unread messages", + "Search": "Search", + "Search failed": "Search failed", + "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", + "Searching known users": "Searching known users", + "Seen by %(userName)s at %(dateTime)s": "Seen by %(userName)s at %(dateTime)s", + "Send a message (unencrypted)": "Send a message (unencrypted)", + "Send an encrypted message": "Send an encrypted message", + "Send anyway": "Send anyway", + "Sender device information": "Sender device information", + "Send Invites": "Send Invites", + "Send Reset Email": "Send Reset Email", + "sent an image": "sent an image", + "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s sent an image.", + "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.", + "sent a video": "sent a video", + "Server error": "Server error", + "Server may be unavailable or overloaded": "Server may be unavailable or overloaded", + "Server may be unavailable, overloaded, or search timed out :(": "Server may be unavailable, overloaded, or search timed out :(", + "Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big", + "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", + "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.", + "Session ID": "Session ID", + "%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.", + "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", + "Set": "Set", + "Settings": "Settings", + "Show Apps": "Show Apps", + "Show panel": "Show panel", + "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", + "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", + "Signed Out": "Signed Out", + "Sign in": "Sign in", + "Sign out": "Sign out", + "since the point in time of selecting this option": "since the point in time of selecting this option", + "since they joined": "since they joined", + "since they were invited": "since they were invited", + "Some of your messages have not been sent.": "Some of your messages have not been sent.", + "Someone": "Someone", + "Sorry, this homeserver is using a login which is not recognised ": "Sorry, this homeserver is using a login which is not recognised ", + "Start a chat": "Start a chat", + "Start authentication": "Start authentication", + "Start Chat": "Start Chat", + "Submit": "Submit", + "Success": "Success", + "tag as %(tagName)s": "tag as %(tagName)s", + "tag direct chat": "tag direct chat", + "Tagged as: ": "Tagged as: ", + "The default role for new room members is": "The default role for new room members is", + "The main address for this room is": "The main address for this room is", + "The phone number entered looks invalid": "The phone number entered looks invalid", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", + "This action cannot be performed by a guest user. Please register to be able to do this.": "This action cannot be performed by a guest user. Please register to be able to do this.", + "This email address is already in use": "This email address is already in use", + "This email address was not found": "This email address was not found", + "%(actionVerb)s this person?": "%(actionVerb)s this person?", + "The email address linked to your account must be entered.": "The email address linked to your account must be entered.", + "The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads", + "The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload", + "The remote side failed to pick up": "The remote side failed to pick up", + "This Home Server does not support login using email address.": "This Home Server does not support login using email address.", + "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", + "There was a problem logging in.": "There was a problem logging in.", + "This room has no local addresses": "This room has no local addresses", + "This room is not recognised.": "This room is not recognised.", + "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways", + "The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged", + "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address", + "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", + "This phone number is already in use": "This phone number is already in use", + "This room": "This room", + "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", + "This room's internal ID is": "This room's internal ID is", + "times": "times", + "To ban users": "To ban users", + "to browse the directory": "to browse the directory", + "To configure the room": "To configure the room", + "to demote": "to demote", + "to favourite": "to favourite", + "To invite users into the room": "To invite users into the room", + "To kick users": "To kick users", + "To link to a room it must have an address.": "To link to a room it must have an address.", + "to make a room or": "to make a room or", + "To remove other users' messages": "To remove other users' messages", + "To reset your password, enter the email address linked to your account": "To reset your password, enter the email address linked to your account", + "to restore": "to restore", + "To send events of type": "To send events of type", + "To send messages": "To send messages", + "to start a chat with someone": "to start a chat with someone", + "to tag as %(tagName)s": "to tag as %(tagName)s", + "to tag direct chat": "to tag direct chat", + "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", + "Turn Markdown off": "Turn Markdown off", + "Turn Markdown on": "Turn Markdown on", + "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).", + "Unable to add email address": "Unable to add email address", + "Unable to create widget.": "Unable to create widget.", + "Unable to remove contact information": "Unable to remove contact information", + "Unable to restore previous session": "Unable to restore previous session", + "Unable to verify email address.": "Unable to verify email address.", + "Unban": "Unban", + "Unbans user with given id": "Unbans user with given id", + "%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.", + "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", + "Unable to capture screen": "Unable to capture screen", + "Unable to enable Notifications": "Unable to enable Notifications", + "Unable to load device list": "Unable to load device list", + "Undecryptable": "Undecryptable", + "Unencrypted room": "Unencrypted room", + "unencrypted": "unencrypted", + "Unencrypted message": "Unencrypted message", + "unknown caller": "unknown caller", + "Unknown command": "Unknown command", + "unknown device": "unknown device", + "unknown error code": "unknown error code", + "Unknown room %(roomId)s": "Unknown room %(roomId)s", + "Unknown (user, device) pair:": "Unknown (user, device) pair:", + "unknown": "unknown", + "Unmute": "Unmute", + "Unnamed Room": "Unnamed Room", + "Unrecognised command:": "Unrecognised command:", + "Unrecognised room alias:": "Unrecognised room alias:", + "Unverified": "Unverified", + "Uploading %(filename)s and %(count)s others": { + "zero": "Uploading %(filename)s", + "one": "Uploading %(filename)s and %(count)s other", + "other": "Uploading %(filename)s and %(count)s others" + }, + "uploaded a file": "uploaded a file", + "Upload avatar": "Upload avatar", + "Upload Failed": "Upload Failed", + "Upload Files": "Upload Files", + "Upload file": "Upload file", + "Upload new:": "Upload new:", + "Usage": "Usage", + "Use compact timeline layout": "Use compact timeline layout", + "Use with caution": "Use with caution", + "User ID": "User ID", + "User Interface": "User Interface", + "%(user)s is a": "%(user)s is a", + "User name": "User name", + "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)", + "Username invalid: %(errMessage)s": "Username invalid: %(errMessage)s", + "Users": "Users", + "User": "User", + "Verification Pending": "Verification Pending", + "Verification": "Verification", + "verified": "verified", + "Verified": "Verified", + "Verified key": "Verified key", + "Video call": "Video call", + "Voice call": "Voice call", + "VoIP conference finished.": "VoIP conference finished.", + "VoIP conference started.": "VoIP conference started.", + "VoIP is unsupported": "VoIP is unsupported", + "(could not connect media)": "(could not connect media)", + "(no answer)": "(no answer)", + "(unknown failure: %(reason)s)": "(unknown failure: %(reason)s)", + "(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)", + "Warning!": "Warning!", + "WARNING: Device already verified, but keys do NOT MATCH!": "WARNING: Device already verified, but keys do NOT MATCH!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!", + "Who can access this room?": "Who can access this room?", + "Who can read history?": "Who can read history?", + "Who would you like to add to this room?": "Who would you like to add to this room?", + "Who would you like to communicate with?": "Who would you like to communicate with?", + "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", + "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", + "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", + "You are already in a call.": "You are already in a call.", + "You're not in any rooms yet! Press to make a room or to browse the directory": "You're not in any rooms yet! Press to make a room or to browse the directory", + "You are trying to access %(roomName)s.": "You are trying to access %(roomName)s.", + "You cannot place a call with yourself.": "You cannot place a call with yourself.", + "You cannot place VoIP calls in this browser.": "You cannot place VoIP calls in this browser.", + "You do not have permission to post to this room": "You do not have permission to post to this room", + "You have been banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", + "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s", + "You have been kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)s.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device", + "You have disabled URL previews by default.": "You have disabled URL previews by default.", + "You have enabled URL previews by default.": "You have enabled URL previews by default.", + "You have entered an invalid contact. Try using their Matrix ID or email address.": "You have entered an invalid contact. Try using their Matrix ID or email address.", + "You have no visible notifications": "You have no visible notifications", + "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", + "you must be a": "you must be a", + "You must register to use this functionality": "You must register to use this functionality", + "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", + "You need to be logged in.": "You need to be logged in.", + "You need to enter a user name.": "You need to enter a user name.", + "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", + "Your password has been reset": "Your password has been reset", + "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them", + "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?", + "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?", + "You should not yet trust it to secure data": "You should not yet trust it to secure data", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.", + "Your home server does not support device management.": "Your home server does not support device management.", + "Sun": "Sun", + "Mon": "Mon", + "Tue": "Tue", + "Wed": "Wed", + "Thu": "Thu", + "Fri": "Fri", + "Sat": "Sat", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "May", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Oct", + "Nov": "Nov", + "Dec": "Dec", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s", + "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", + "Set a display name:": "Set a display name:", + "Set a Display Name": "Set a Display Name", + "Upload an avatar:": "Upload an avatar:", + "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", + "Missing password.": "Missing password.", + "Passwords don't match.": "Passwords don't match.", + "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Password too short (min %(MIN_PASSWORD_LENGTH)s).", + "This doesn't look like a valid email address.": "This doesn't look like a valid email address.", + "This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.", + "User names may only contain letters, numbers, dots, hyphens and underscores.": "User names may only contain letters, numbers, dots, hyphens and underscores.", + "An unknown error occurred.": "An unknown error occurred.", + "I already have an account": "I already have an account", + "An error occurred: %(error_string)s": "An error occurred: %(error_string)s", + "Topic": "Topic", + "Make Moderator": "Make Moderator", + "Make this room private": "Make this room private", + "Share message history with new users": "Share message history with new users", + "Encrypt room": "Encrypt room", + "There are no visible files in this room": "There are no visible files in this room", + "Room": "Room", + "Connectivity to the server has been lost.": "Connectivity to the server has been lost.", + "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.", + "Auto-complete": "Auto-complete", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Resend all or cancel all now. You can also select individual messages to resend or cancel.", + "(~%(count)s results)": { + "one": "(~%(count)s result)", + "other": "(~%(count)s results)" + }, + "Cancel": "Cancel", + "or": "or", + "Active call": "Active call", + "Monday": "Monday", + "Tuesday": "Tuesday", + "Wednesday": "Wednesday", + "Thursday": "Thursday", + "Friday": "Friday", + "Saturday": "Saturday", + "Sunday": "Sunday", + "bold": "bold", + "italic": "italic", + "strike": "strike", + "underline": "underline", + "code": "code", + "quote": "quote", + "bullet": "bullet", + "numbullet": "numbullet", + "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times", + "%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times", + "%(severalUsers)sjoined": "%(severalUsers)sjoined", + "%(oneUser)sjoined": "%(oneUser)sjoined", + "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times", + "%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times", + "%(severalUsers)sleft": "%(severalUsers)sleft", + "%(oneUser)sleft": "%(oneUser)sleft", + "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times", + "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times", + "%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left", + "%(oneUser)sjoined and left": "%(oneUser)sjoined and left", + "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times", + "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times", + "%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined", + "%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined", + "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times", + "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times", + "%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations", + "%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation", + "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times", + "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times", + "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn", + "%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn", + "were invited %(repeats)s times": "were invited %(repeats)s times", + "was invited %(repeats)s times": "was invited %(repeats)s times", + "were invited": "were invited", + "was invited": "was invited", + "were banned %(repeats)s times": "were banned %(repeats)s times", + "was banned %(repeats)s times": "was banned %(repeats)s times", + "were banned": "were banned", + "was banned": "was banned", + "were unbanned %(repeats)s times": "were unbanned %(repeats)s times", + "was unbanned %(repeats)s times": "was unbanned %(repeats)s times", + "were unbanned": "were unbanned", + "was unbanned": "was unbanned", + "were kicked %(repeats)s times": "were kicked %(repeats)s times", + "was kicked %(repeats)s times": "was kicked %(repeats)s times", + "were kicked": "were kicked", + "was kicked": "was kicked", + "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times", + "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times", + "%(severalUsers)schanged their name": "%(severalUsers)schanged their name", + "%(oneUser)schanged their name": "%(oneUser)schanged their name", + "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times", + "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times", + "%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar", + "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar", + "Please select the destination room for this message": "Please select the destination room for this message", + "Create new room": "Create new room", + "Welcome page": "Welcome page", + "Room directory": "Room directory", + "Start chat": "Start chat", + "New Password": "New Password", + "Start automatically after system login": "Start automatically after system login", + "Desktop specific": "Desktop specific", + "Analytics": "Analytics", + "Opt out of analytics": "Opt out of analytics", + "Options": "Options", + "Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.", + "Passphrases must match": "Passphrases must match", + "Passphrase must not be empty": "Passphrase must not be empty", + "Export room keys": "Export room keys", + "Confirm passphrase": "Confirm passphrase", + "Import room keys": "Import room keys", + "File to import": "File to import", + "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.", + "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.", + "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.", + "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.", + "You must join the room to see its files": "You must join the room to see its files", + "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", + "Start new chat": "Start new chat", + "Guest users can't invite users. Please register.": "Guest users can't invite users. Please register.", + "Failed to invite": "Failed to invite", + "Failed to invite user": "Failed to invite user", + "Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:", + "Confirm Removal": "Confirm Removal", + "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.", + "Unknown error": "Unknown error", + "Incorrect password": "Incorrect password", + "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "This will make your account permanently unusable. You will not be able to re-register the same user ID.", + "This action is irreversible.": "This action is irreversible.", + "To continue, please enter your password.": "To continue, please enter your password.", + "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:": "To verify that this device can be trusted, please contact its owner using some other means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings for this device matches the key below:", + "Device name": "Device name", + "Device Name": "Device Name", + "Device key": "Device key", + "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.": "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.", + "In future this verification process will be more sophisticated.": "In future this verification process will be more sophisticated.", + "Verify device": "Verify device", + "I verify that the keys match": "I verify that the keys match", + "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.", + "Unable to restore session": "Unable to restore session", + "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.", + "Continue anyway": "Continue anyway", + "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?", + "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.", + "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.", + "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", + "Unknown devices": "Unknown devices", + "Unknown Address": "Unknown Address", + "Unblacklist": "Unblacklist", + "Blacklist": "Blacklist", + "Unverify": "Unverify", + "Verify...": "Verify...", + "ex. @bob:example.com": "ex. @bob:example.com", + "Add User": "Add User", + "This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot", + "Sign in with CAS": "Sign in with CAS", + "Custom Server Options": "Custom Server Options", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.", + "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.", + "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.", + "Dismiss": "Dismiss", + "Please check your email to continue registration.": "Please check your email to continue registration.", + "Token incorrect": "Token incorrect", + "A text message has been sent to": "A text message has been sent to", + "Please enter the code it contains:": "Please enter the code it contains:", + "powered by Matrix": "powered by Matrix", + "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", + "You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s", + "Default server": "Default server", + "Custom server": "Custom server", + "Home server URL": "Home server URL", + "Identity server URL": "Identity server URL", + "What does this mean?": "What does this mean?", + "Error decrypting audio": "Error decrypting audio", + "Error decrypting image": "Error decrypting image", + "Image '%(Body)s' cannot be displayed.": "Image '%(Body)s' cannot be displayed.", + "This image cannot be displayed.": "This image cannot be displayed.", + "Error decrypting video": "Error decrypting video", + "Add an Integration": "Add an Integration", + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", + "Removed or unknown message type": "Removed or unknown message type", + "Disable URL previews by default for participants in this room": "Disable URL previews by default for participants in this room", + "Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)", + "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", + "URL Previews": "URL Previews", + "Enable URL previews for this room (affects only you)": "Enable URL previews for this room (affects only you)", + "Drop file here to upload": "Drop file here to upload", + " (unsupported)": " (unsupported)", + "Ongoing conference call%(supportedText)s.": "Ongoing conference call%(supportedText)s.", + "for %(amount)ss": "for %(amount)ss", + "for %(amount)sm": "for %(amount)sm", + "for %(amount)sh": "for %(amount)sh", + "for %(amount)sd": "for %(amount)sd", + "Online": "Online", + "Idle": "Idle", + "Offline": "Offline", + "Updates": "Updates", + "Check for update": "Check for update", + "Start chatting": "Start chatting", + "Start Chatting": "Start Chatting", + "Click on the button below to start chatting!": "Click on the button below to start chatting!", + "$senderDisplayName changed the room avatar to ": "$senderDisplayName changed the room avatar to ", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.", + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s", + "Username available": "Username available", + "Username not available": "Username not available", + "Something went wrong!": "Something went wrong!", + "This will be your account name on the homeserver, or you can pick a different server.": "This will be your account name on the homeserver, or you can pick a different server.", + "If you already have a Matrix account you can log in instead.": "If you already have a Matrix account you can log in instead.", + "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", + "Not a valid Riot keyfile": "Not a valid Riot keyfile", + "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", + "Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls", + "Do you want to set an email address?": "Do you want to set an email address?", + "This will allow you to reset your password and receive notifications.": "This will allow you to reset your password and receive notifications.", + "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", + "Skip": "Skip", + "Start verification": "Start verification", + "Share without verifying": "Share without verifying", + "Ignore request": "Ignore request", + "You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.", + "Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.", + "Encryption key request": "Encryption key request", + "Autocomplete Delay (ms):": "Autocomplete Delay (ms):", + "This Home server does not support groups": "This Home server does not support groups", + "Loading device info...": "Loading device info...", + "Groups": "Groups", + "Create a new group": "Create a new group", + "Create Group": "Create Group", + "Group Name": "Group Name", + "Example": "Example", + "Create": "Create", + "Group ID": "Group ID", + "+example:%(domain)s": "+example:%(domain)s", + "Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s", + "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", + "Room creation failed": "Room creation failed", + "You are a member of these groups": "You are a member of these groups", + "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.", + "Join an existing group": "Join an existing group", + "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.": "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org." } diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index e060add84f..4b8b8a5d3c 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -1,4 +1,5 @@ { + "Add a widget": "Add a widget", "af": "Afrikaans", "ar-ae": "Arabic (U.A.E.)", "ar-bh": "Arabic (Bahrain)", @@ -311,6 +312,7 @@ "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", "had": "had", "Hangup": "Hangup", + "Hide Apps": "Hide Apps", "Hide read receipts": "Hide read receipts", "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", "Historical": "Historical", @@ -362,6 +364,7 @@ "Markdown is disabled": "Markdown is disabled", "Markdown is enabled": "Markdown is enabled", "matrix-react-sdk version:": "matrix-react-sdk version:", + "Matrix Apps": "Matrix Apps", "Members only": "Members only", "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present", "Missing room_id in request": "Missing room_id in request", @@ -464,6 +467,7 @@ "%(senderName)s set a profile picture.": "%(senderName)s set a profile picture.", "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s set their display name to %(displayName)s.", "Settings": "Settings", + "Show Apps": "Show Apps", "Show panel": "Show panel", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)", "Signed Out": "Signed Out", diff --git a/src/stripped-emoji.json b/src/stripped-emoji.json new file mode 100644 index 0000000000..fc72dd3441 --- /dev/null +++ b/src/stripped-emoji.json @@ -0,0 +1 @@ +[{"name":"modern pentathlon","shortname":"","category":"unicode9","emoji_order":"67"},{"name":"hundred points symbol","shortname":":100:","category":"symbols","emoji_order":"856"},{"name":"input symbol for numbers","shortname":":1234:","category":"symbols","emoji_order":"913"},{"name":"grinning face","shortname":":grinning:","category":"people","emoji_order":"1"},{"name":"grimacing face","shortname":":grimacing:","category":"people","emoji_order":"2"},{"name":"grinning face with smiling eyes","shortname":":grin:","category":"people","emoji_order":"3"},{"name":"face with tears of joy","shortname":":joy:","category":"people","emoji_order":"4","aliases_ascii":[":')",":'-)"]},{"name":"smiling face with open mouth","shortname":":smiley:","category":"people","emoji_order":"5","aliases_ascii":[":D",":-D","=D"]},{"name":"smiling face with open mouth and smiling eyes","shortname":":smile:","category":"people","emoji_order":"6"},{"name":"smiling face with open mouth and cold sweat","shortname":":sweat_smile:","category":"people","emoji_order":"7","aliases_ascii":["':)","':-)","'=)","':D","':-D","'=D"]},{"name":"smiling face with open mouth and tightly-closed eyes","shortname":":laughing:","category":"people","emoji_order":"8","aliases_ascii":[">:)",">;)",">:-)",">=)"]},{"name":"smiling face with halo","shortname":":innocent:","category":"people","emoji_order":"9","aliases_ascii":["O:-)","0:-3","0:3","0:-)","0:)","0;^)","O:)","O;-)","O=)","0;-)","O:-3","O:3"]},{"name":"winking face","shortname":":wink:","category":"people","emoji_order":"10","aliases_ascii":[";)",";-)","*-)","*)",";-]",";]",";D",";^)"]},{"name":"smiling face with smiling eyes","shortname":":blush:","category":"people","emoji_order":"11"},{"name":"slightly smiling face","shortname":":slight_smile:","category":"people","emoji_order":"12","aliases_ascii":[":)",":-)","=]","=)",":]"]},{"name":"upside-down face","shortname":":upside_down:","category":"people","emoji_order":"13"},{"name":"white smiling face","shortname":":relaxed:","category":"people","emoji_order":"14"},{"name":"face savouring delicious food","shortname":":yum:","category":"people","emoji_order":"15"},{"name":"relieved face","shortname":":relieved:","category":"people","emoji_order":"16"},{"name":"smiling face with heart-shaped eyes","shortname":":heart_eyes:","category":"people","emoji_order":"17"},{"name":"face throwing a kiss","shortname":":kissing_heart:","category":"people","emoji_order":"18","aliases_ascii":[":*",":-*","=*",":^*"]},{"name":"kissing face","shortname":":kissing:","category":"people","emoji_order":"19"},{"name":"kissing face with smiling eyes","shortname":":kissing_smiling_eyes:","category":"people","emoji_order":"20"},{"name":"kissing face with closed eyes","shortname":":kissing_closed_eyes:","category":"people","emoji_order":"21"},{"name":"face with stuck-out tongue and winking eye","shortname":":stuck_out_tongue_winking_eye:","category":"people","emoji_order":"22","aliases_ascii":[">:P","X-P","x-p"]},{"name":"face with stuck-out tongue and tightly-closed eyes","shortname":":stuck_out_tongue_closed_eyes:","category":"people","emoji_order":"23"},{"name":"face with stuck-out tongue","shortname":":stuck_out_tongue:","category":"people","emoji_order":"24","aliases_ascii":[":P",":-P","=P",":-p",":p","=p",":-Þ",":Þ",":þ",":-þ",":-b",":b","d:"]},{"name":"money-mouth face","shortname":":money_mouth:","category":"people","emoji_order":"25"},{"name":"nerd face","shortname":":nerd:","category":"people","emoji_order":"26"},{"name":"smiling face with sunglasses","shortname":":sunglasses:","category":"people","emoji_order":"27","aliases_ascii":["B-)","B)","8)","8-)","B-D","8-D"]},{"name":"hugging face","shortname":":hugging:","category":"people","emoji_order":"28"},{"name":"smirking face","shortname":":smirk:","category":"people","emoji_order":"29"},{"name":"face without mouth","shortname":":no_mouth:","category":"people","emoji_order":"30","aliases_ascii":[":-X",":X",":-#",":#","=X","=x",":x",":-x","=#"]},{"name":"neutral face","shortname":":neutral_face:","category":"people","emoji_order":"31"},{"name":"expressionless face","shortname":":expressionless:","category":"people","emoji_order":"32","aliases_ascii":["-_-","-__-","-___-"]},{"name":"unamused face","shortname":":unamused:","category":"people","emoji_order":"33"},{"name":"face with rolling eyes","shortname":":rolling_eyes:","category":"people","emoji_order":"34"},{"name":"thinking face","shortname":":thinking:","category":"people","emoji_order":"35"},{"name":"flushed face","shortname":":flushed:","category":"people","emoji_order":"36","aliases_ascii":[":$","=$"]},{"name":"disappointed face","shortname":":disappointed:","category":"people","emoji_order":"37","aliases_ascii":[">:[",":-(",":(",":-[",":[","=("]},{"name":"worried face","shortname":":worried:","category":"people","emoji_order":"38"},{"name":"angry face","shortname":":angry:","category":"people","emoji_order":"39","aliases_ascii":[">:(",">:-(",":@"]},{"name":"pouting face","shortname":":rage:","category":"people","emoji_order":"40"},{"name":"pensive face","shortname":":pensive:","category":"people","emoji_order":"41"},{"name":"confused face","shortname":":confused:","category":"people","emoji_order":"42","aliases_ascii":[">:\\",">:/",":-/",":-.",":/",":\\","=/","=\\",":L","=L"]},{"name":"slightly frowning face","shortname":":slight_frown:","category":"people","emoji_order":"43"},{"name":"white frowning face","shortname":":frowning2:","category":"people","emoji_order":"44"},{"name":"persevering face","shortname":":persevere:","category":"people","emoji_order":"45","aliases_ascii":[">.<"]},{"name":"confounded face","shortname":":confounded:","category":"people","emoji_order":"46"},{"name":"tired face","shortname":":tired_face:","category":"people","emoji_order":"47"},{"name":"weary face","shortname":":weary:","category":"people","emoji_order":"48"},{"name":"face with look of triumph","shortname":":triumph:","category":"people","emoji_order":"49"},{"name":"face with open mouth","shortname":":open_mouth:","category":"people","emoji_order":"50","aliases_ascii":[":-O",":O",":-o",":o","O_O",">:O"]},{"name":"face screaming in fear","shortname":":scream:","category":"people","emoji_order":"51"},{"name":"fearful face","shortname":":fearful:","category":"people","emoji_order":"52","aliases_ascii":["D:"]},{"name":"face with open mouth and cold sweat","shortname":":cold_sweat:","category":"people","emoji_order":"53"},{"name":"hushed face","shortname":":hushed:","category":"people","emoji_order":"54"},{"name":"frowning face with open mouth","shortname":":frowning:","category":"people","emoji_order":"55"},{"name":"anguished face","shortname":":anguished:","category":"people","emoji_order":"56"},{"name":"crying face","shortname":":cry:","category":"people","emoji_order":"57","aliases_ascii":[":'(",":'-(",";(",";-("]},{"name":"disappointed but relieved face","shortname":":disappointed_relieved:","category":"people","emoji_order":"58"},{"name":"sleepy face","shortname":":sleepy:","category":"people","emoji_order":"59"},{"name":"face with cold sweat","shortname":":sweat:","category":"people","emoji_order":"60","aliases_ascii":["':(","':-(","'=("]},{"name":"loudly crying face","shortname":":sob:","category":"people","emoji_order":"61"},{"name":"dizzy face","shortname":":dizzy_face:","category":"people","emoji_order":"62","aliases_ascii":["#-)","#)","%-)","%)","X)","X-)"]},{"name":"astonished face","shortname":":astonished:","category":"people","emoji_order":"63"},{"name":"zipper-mouth face","shortname":":zipper_mouth:","category":"people","emoji_order":"64"},{"name":"face with medical mask","shortname":":mask:","category":"people","emoji_order":"65"},{"name":"face with thermometer","shortname":":thermometer_face:","category":"people","emoji_order":"66"},{"name":"face with head-bandage","shortname":":head_bandage:","category":"people","emoji_order":"67"},{"name":"sleeping face","shortname":":sleeping:","category":"people","emoji_order":"68"},{"name":"sleeping symbol","shortname":":zzz:","category":"people","emoji_order":"69"},{"name":"pile of poo","shortname":":poop:","category":"people","emoji_order":"70"},{"name":"smiling face with horns","shortname":":smiling_imp:","category":"people","emoji_order":"71"},{"name":"imp","shortname":":imp:","category":"people","emoji_order":"72"},{"name":"japanese ogre","shortname":":japanese_ogre:","category":"people","emoji_order":"73"},{"name":"japanese goblin","shortname":":japanese_goblin:","category":"people","emoji_order":"74"},{"name":"skull","shortname":":skull:","category":"people","emoji_order":"75"},{"name":"ghost","shortname":":ghost:","category":"people","emoji_order":"76"},{"name":"extraterrestrial alien","shortname":":alien:","category":"people","emoji_order":"77"},{"name":"robot face","shortname":":robot:","category":"people","emoji_order":"78"},{"name":"smiling cat face with open mouth","shortname":":smiley_cat:","category":"people","emoji_order":"79"},{"name":"grinning cat face with smiling eyes","shortname":":smile_cat:","category":"people","emoji_order":"80"},{"name":"cat face with tears of joy","shortname":":joy_cat:","category":"people","emoji_order":"81"},{"name":"smiling cat face with heart-shaped eyes","shortname":":heart_eyes_cat:","category":"people","emoji_order":"82"},{"name":"cat face with wry smile","shortname":":smirk_cat:","category":"people","emoji_order":"83"},{"name":"kissing cat face with closed eyes","shortname":":kissing_cat:","category":"people","emoji_order":"84"},{"name":"weary cat face","shortname":":scream_cat:","category":"people","emoji_order":"85"},{"name":"crying cat face","shortname":":crying_cat_face:","category":"people","emoji_order":"86"},{"name":"pouting cat face","shortname":":pouting_cat:","category":"people","emoji_order":"87"},{"name":"person raising both hands in celebration","shortname":":raised_hands:","category":"people","emoji_order":"88"},{"name":"clapping hands sign","shortname":":clap:","category":"people","emoji_order":"89"},{"name":"waving hand sign","shortname":":wave:","category":"people","emoji_order":"90"},{"name":"thumbs up sign","shortname":":thumbsup:","category":"people","emoji_order":"91"},{"name":"thumbs down sign","shortname":":thumbsdown:","category":"people","emoji_order":"92"},{"name":"fisted hand sign","shortname":":punch:","category":"people","emoji_order":"93"},{"name":"raised fist","shortname":":fist:","category":"people","emoji_order":"94"},{"name":"victory hand","shortname":":v:","category":"people","emoji_order":"95"},{"name":"ok hand sign","shortname":":ok_hand:","category":"people","emoji_order":"96"},{"name":"raised hand","shortname":":raised_hand:","category":"people","emoji_order":"97"},{"name":"open hands sign","shortname":":open_hands:","category":"people","emoji_order":"98"},{"name":"flexed biceps","shortname":":muscle:","category":"people","emoji_order":"99"},{"name":"person with folded hands","shortname":":pray:","category":"people","emoji_order":"100"},{"name":"white up pointing index","shortname":":point_up:","category":"people","emoji_order":"101"},{"name":"white up pointing backhand index","shortname":":point_up_2:","category":"people","emoji_order":"102"},{"name":"white down pointing backhand index","shortname":":point_down:","category":"people","emoji_order":"103"},{"name":"white left pointing backhand index","shortname":":point_left:","category":"people","emoji_order":"104"},{"name":"white right pointing backhand index","shortname":":point_right:","category":"people","emoji_order":"105"},{"name":"reversed hand with middle finger extended","shortname":":middle_finger:","category":"people","emoji_order":"106"},{"name":"raised hand with fingers splayed","shortname":":hand_splayed:","category":"people","emoji_order":"107"},{"name":"sign of the horns","shortname":":metal:","category":"people","emoji_order":"108"},{"name":"raised hand with part between middle and ring fingers","shortname":":vulcan:","category":"people","emoji_order":"109"},{"name":"writing hand","shortname":":writing_hand:","category":"people","emoji_order":"110"},{"name":"nail polish","shortname":":nail_care:","category":"people","emoji_order":"111"},{"name":"mouth","shortname":":lips:","category":"people","emoji_order":"112"},{"name":"tongue","shortname":":tongue:","category":"people","emoji_order":"113"},{"name":"ear","shortname":":ear:","category":"people","emoji_order":"114"},{"name":"nose","shortname":":nose:","category":"people","emoji_order":"115"},{"name":"eye","shortname":":eye:","category":"people","emoji_order":"116"},{"name":"eyes","shortname":":eyes:","category":"people","emoji_order":"117"},{"name":"bust in silhouette","shortname":":bust_in_silhouette:","category":"people","emoji_order":"118"},{"name":"busts in silhouette","shortname":":busts_in_silhouette:","category":"people","emoji_order":"119"},{"name":"speaking head in silhouette","shortname":":speaking_head:","category":"people","emoji_order":"120"},{"name":"baby","shortname":":baby:","category":"people","emoji_order":"121"},{"name":"boy","shortname":":boy:","category":"people","emoji_order":"122"},{"name":"girl","shortname":":girl:","category":"people","emoji_order":"123"},{"name":"man","shortname":":man:","category":"people","emoji_order":"124"},{"name":"woman","shortname":":woman:","category":"people","emoji_order":"125"},{"name":"person with blond hair","shortname":":person_with_blond_hair:","category":"people","emoji_order":"126"},{"name":"older man","shortname":":older_man:","category":"people","emoji_order":"127"},{"name":"older woman","shortname":":older_woman:","category":"people","emoji_order":"128"},{"name":"man with gua pi mao","shortname":":man_with_gua_pi_mao:","category":"people","emoji_order":"129"},{"name":"man with turban","shortname":":man_with_turban:","category":"people","emoji_order":"130"},{"name":"police officer","shortname":":cop:","category":"people","emoji_order":"131"},{"name":"construction worker","shortname":":construction_worker:","category":"people","emoji_order":"132"},{"name":"guardsman","shortname":":guardsman:","category":"people","emoji_order":"133"},{"name":"sleuth or spy","shortname":":spy:","category":"people","emoji_order":"134"},{"name":"father christmas","shortname":":santa:","category":"people","emoji_order":"135"},{"name":"baby angel","shortname":":angel:","category":"people","emoji_order":"136"},{"name":"princess","shortname":":princess:","category":"people","emoji_order":"137"},{"name":"bride with veil","shortname":":bride_with_veil:","category":"people","emoji_order":"138"},{"name":"pedestrian","shortname":":walking:","category":"people","emoji_order":"139"},{"name":"runner","shortname":":runner:","category":"people","emoji_order":"140"},{"name":"dancer","shortname":":dancer:","category":"people","emoji_order":"141"},{"name":"woman with bunny ears","shortname":":dancers:","category":"people","emoji_order":"142"},{"name":"man and woman holding hands","shortname":":couple:","category":"people","emoji_order":"143"},{"name":"two men holding hands","shortname":":two_men_holding_hands:","category":"people","emoji_order":"144"},{"name":"two women holding hands","shortname":":two_women_holding_hands:","category":"people","emoji_order":"145"},{"name":"person bowing deeply","shortname":":bow:","category":"people","emoji_order":"146"},{"name":"information desk person","shortname":":information_desk_person:","category":"people","emoji_order":"147"},{"name":"face with no good gesture","shortname":":no_good:","category":"people","emoji_order":"148"},{"name":"face with ok gesture","shortname":":ok_woman:","category":"people","emoji_order":"149","aliases_ascii":["*\\0/*","\\0/","*\\O/*","\\O/"]},{"name":"happy person raising one hand","shortname":":raising_hand:","category":"people","emoji_order":"150"},{"name":"person with pouting face","shortname":":person_with_pouting_face:","category":"people","emoji_order":"151"},{"name":"person frowning","shortname":":person_frowning:","category":"people","emoji_order":"152"},{"name":"haircut","shortname":":haircut:","category":"people","emoji_order":"153"},{"name":"face massage","shortname":":massage:","category":"people","emoji_order":"154"},{"name":"couple with heart","shortname":":couple_with_heart:","category":"people","emoji_order":"155"},{"name":"couple (woman,woman)","shortname":":couple_ww:","category":"people","emoji_order":"156"},{"name":"couple (man,man)","shortname":":couple_mm:","category":"people","emoji_order":"157"},{"name":"kiss","shortname":":couplekiss:","category":"people","emoji_order":"158"},{"name":"kiss (woman,woman)","shortname":":kiss_ww:","category":"people","emoji_order":"159"},{"name":"kiss (man,man)","shortname":":kiss_mm:","category":"people","emoji_order":"160"},{"name":"family","shortname":":family:","category":"people","emoji_order":"161"},{"name":"family (man,woman,girl)","shortname":":family_mwg:","category":"people","emoji_order":"162"},{"name":"family (man,woman,girl,boy)","shortname":":family_mwgb:","category":"people","emoji_order":"163"},{"name":"family (man,woman,boy,boy)","shortname":":family_mwbb:","category":"people","emoji_order":"164"},{"name":"family (man,woman,girl,girl)","shortname":":family_mwgg:","category":"people","emoji_order":"165"},{"name":"family (woman,woman,boy)","shortname":":family_wwb:","category":"people","emoji_order":"166"},{"name":"family (woman,woman,girl)","shortname":":family_wwg:","category":"people","emoji_order":"167"},{"name":"family (woman,woman,girl,boy)","shortname":":family_wwgb:","category":"people","emoji_order":"168"},{"name":"family (woman,woman,boy,boy)","shortname":":family_wwbb:","category":"people","emoji_order":"169"},{"name":"family (woman,woman,girl,girl)","shortname":":family_wwgg:","category":"people","emoji_order":"170"},{"name":"family (man,man,boy)","shortname":":family_mmb:","category":"people","emoji_order":"171"},{"name":"family (man,man,girl)","shortname":":family_mmg:","category":"people","emoji_order":"172"},{"name":"family (man,man,girl,boy)","shortname":":family_mmgb:","category":"people","emoji_order":"173"},{"name":"family (man,man,boy,boy)","shortname":":family_mmbb:","category":"people","emoji_order":"174"},{"name":"family (man,man,girl,girl)","shortname":":family_mmgg:","category":"people","emoji_order":"175"},{"name":"womans clothes","shortname":":womans_clothes:","category":"people","emoji_order":"176"},{"name":"t-shirt","shortname":":shirt:","category":"people","emoji_order":"177"},{"name":"jeans","shortname":":jeans:","category":"people","emoji_order":"178"},{"name":"necktie","shortname":":necktie:","category":"people","emoji_order":"179"},{"name":"dress","shortname":":dress:","category":"people","emoji_order":"180"},{"name":"bikini","shortname":":bikini:","category":"people","emoji_order":"181"},{"name":"kimono","shortname":":kimono:","category":"people","emoji_order":"182"},{"name":"lipstick","shortname":":lipstick:","category":"people","emoji_order":"183"},{"name":"kiss mark","shortname":":kiss:","category":"people","emoji_order":"184"},{"name":"footprints","shortname":":footprints:","category":"people","emoji_order":"185"},{"name":"high-heeled shoe","shortname":":high_heel:","category":"people","emoji_order":"186"},{"name":"womans sandal","shortname":":sandal:","category":"people","emoji_order":"187"},{"name":"womans boots","shortname":":boot:","category":"people","emoji_order":"188"},{"name":"mans shoe","shortname":":mans_shoe:","category":"people","emoji_order":"189"},{"name":"athletic shoe","shortname":":athletic_shoe:","category":"people","emoji_order":"190"},{"name":"womans hat","shortname":":womans_hat:","category":"people","emoji_order":"191"},{"name":"top hat","shortname":":tophat:","category":"people","emoji_order":"192"},{"name":"helmet with white cross","shortname":":helmet_with_cross:","category":"people","emoji_order":"193"},{"name":"graduation cap","shortname":":mortar_board:","category":"people","emoji_order":"194"},{"name":"crown","shortname":":crown:","category":"people","emoji_order":"195"},{"name":"school satchel","shortname":":school_satchel:","category":"people","emoji_order":"196"},{"name":"pouch","shortname":":pouch:","category":"people","emoji_order":"197"},{"name":"purse","shortname":":purse:","category":"people","emoji_order":"198"},{"name":"handbag","shortname":":handbag:","category":"people","emoji_order":"199"},{"name":"briefcase","shortname":":briefcase:","category":"people","emoji_order":"200"},{"name":"eyeglasses","shortname":":eyeglasses:","category":"people","emoji_order":"201"},{"name":"dark sunglasses","shortname":":dark_sunglasses:","category":"people","emoji_order":"202"},{"name":"ring","shortname":":ring:","category":"people","emoji_order":"203"},{"name":"closed umbrella","shortname":":closed_umbrella:","category":"people","emoji_order":"204"},{"name":"dog face","shortname":":dog:","category":"nature","emoji_order":"205"},{"name":"cat face","shortname":":cat:","category":"nature","emoji_order":"206"},{"name":"mouse face","shortname":":mouse:","category":"nature","emoji_order":"207"},{"name":"hamster face","shortname":":hamster:","category":"nature","emoji_order":"208"},{"name":"rabbit face","shortname":":rabbit:","category":"nature","emoji_order":"209"},{"name":"bear face","shortname":":bear:","category":"nature","emoji_order":"210"},{"name":"panda face","shortname":":panda_face:","category":"nature","emoji_order":"211"},{"name":"koala","shortname":":koala:","category":"nature","emoji_order":"212"},{"name":"tiger face","shortname":":tiger:","category":"nature","emoji_order":"213"},{"name":"lion face","shortname":":lion_face:","category":"nature","emoji_order":"214"},{"name":"cow face","shortname":":cow:","category":"nature","emoji_order":"215"},{"name":"pig face","shortname":":pig:","category":"nature","emoji_order":"216"},{"name":"pig nose","shortname":":pig_nose:","category":"nature","emoji_order":"217"},{"name":"frog face","shortname":":frog:","category":"nature","emoji_order":"218"},{"name":"octopus","shortname":":octopus:","category":"nature","emoji_order":"219"},{"name":"monkey face","shortname":":monkey_face:","category":"nature","emoji_order":"220"},{"name":"see-no-evil monkey","shortname":":see_no_evil:","category":"nature","emoji_order":"221"},{"name":"hear-no-evil monkey","shortname":":hear_no_evil:","category":"nature","emoji_order":"222"},{"name":"speak-no-evil monkey","shortname":":speak_no_evil:","category":"nature","emoji_order":"223"},{"name":"monkey","shortname":":monkey:","category":"nature","emoji_order":"224"},{"name":"chicken","shortname":":chicken:","category":"nature","emoji_order":"225"},{"name":"penguin","shortname":":penguin:","category":"nature","emoji_order":"226"},{"name":"bird","shortname":":bird:","category":"nature","emoji_order":"227"},{"name":"baby chick","shortname":":baby_chick:","category":"nature","emoji_order":"228"},{"name":"hatching chick","shortname":":hatching_chick:","category":"nature","emoji_order":"229"},{"name":"front-facing baby chick","shortname":":hatched_chick:","category":"nature","emoji_order":"230"},{"name":"wolf face","shortname":":wolf:","category":"nature","emoji_order":"231"},{"name":"boar","shortname":":boar:","category":"nature","emoji_order":"232"},{"name":"horse face","shortname":":horse:","category":"nature","emoji_order":"233"},{"name":"unicorn face","shortname":":unicorn:","category":"nature","emoji_order":"234"},{"name":"honeybee","shortname":":bee:","category":"nature","emoji_order":"235"},{"name":"bug","shortname":":bug:","category":"nature","emoji_order":"236"},{"name":"snail","shortname":":snail:","category":"nature","emoji_order":"237"},{"name":"lady beetle","shortname":":beetle:","category":"nature","emoji_order":"238"},{"name":"ant","shortname":":ant:","category":"nature","emoji_order":"239"},{"name":"spider","shortname":":spider:","category":"nature","emoji_order":"240"},{"name":"scorpion","shortname":":scorpion:","category":"nature","emoji_order":"241"},{"name":"crab","shortname":":crab:","category":"nature","emoji_order":"242"},{"name":"snake","shortname":":snake:","category":"nature","emoji_order":"243"},{"name":"turtle","shortname":":turtle:","category":"nature","emoji_order":"244"},{"name":"tropical fish","shortname":":tropical_fish:","category":"nature","emoji_order":"245"},{"name":"fish","shortname":":fish:","category":"nature","emoji_order":"246"},{"name":"blowfish","shortname":":blowfish:","category":"nature","emoji_order":"247"},{"name":"dolphin","shortname":":dolphin:","category":"nature","emoji_order":"248"},{"name":"spouting whale","shortname":":whale:","category":"nature","emoji_order":"249"},{"name":"whale","shortname":":whale2:","category":"nature","emoji_order":"250"},{"name":"crocodile","shortname":":crocodile:","category":"nature","emoji_order":"251"},{"name":"leopard","shortname":":leopard:","category":"nature","emoji_order":"252"},{"name":"tiger","shortname":":tiger2:","category":"nature","emoji_order":"253"},{"name":"water buffalo","shortname":":water_buffalo:","category":"nature","emoji_order":"254"},{"name":"ox","shortname":":ox:","category":"nature","emoji_order":"255"},{"name":"cow","shortname":":cow2:","category":"nature","emoji_order":"256"},{"name":"dromedary camel","shortname":":dromedary_camel:","category":"nature","emoji_order":"257"},{"name":"bactrian camel","shortname":":camel:","category":"nature","emoji_order":"258"},{"name":"elephant","shortname":":elephant:","category":"nature","emoji_order":"259"},{"name":"goat","shortname":":goat:","category":"nature","emoji_order":"260"},{"name":"ram","shortname":":ram:","category":"nature","emoji_order":"261"},{"name":"sheep","shortname":":sheep:","category":"nature","emoji_order":"262"},{"name":"horse","shortname":":racehorse:","category":"nature","emoji_order":"263"},{"name":"pig","shortname":":pig2:","category":"nature","emoji_order":"264"},{"name":"rat","shortname":":rat:","category":"nature","emoji_order":"265"},{"name":"mouse","shortname":":mouse2:","category":"nature","emoji_order":"266"},{"name":"rooster","shortname":":rooster:","category":"nature","emoji_order":"267"},{"name":"turkey","shortname":":turkey:","category":"nature","emoji_order":"268"},{"name":"dove of peace","shortname":":dove:","category":"nature","emoji_order":"269"},{"name":"dog","shortname":":dog2:","category":"nature","emoji_order":"270"},{"name":"poodle","shortname":":poodle:","category":"nature","emoji_order":"271"},{"name":"cat","shortname":":cat2:","category":"nature","emoji_order":"272"},{"name":"rabbit","shortname":":rabbit2:","category":"nature","emoji_order":"273"},{"name":"chipmunk","shortname":":chipmunk:","category":"nature","emoji_order":"274"},{"name":"paw prints","shortname":":feet:","category":"nature","emoji_order":"275"},{"name":"dragon","shortname":":dragon:","category":"nature","emoji_order":"276"},{"name":"dragon face","shortname":":dragon_face:","category":"nature","emoji_order":"277"},{"name":"cactus","shortname":":cactus:","category":"nature","emoji_order":"278"},{"name":"christmas tree","shortname":":christmas_tree:","category":"nature","emoji_order":"279"},{"name":"evergreen tree","shortname":":evergreen_tree:","category":"nature","emoji_order":"280"},{"name":"deciduous tree","shortname":":deciduous_tree:","category":"nature","emoji_order":"281"},{"name":"palm tree","shortname":":palm_tree:","category":"nature","emoji_order":"282"},{"name":"seedling","shortname":":seedling:","category":"nature","emoji_order":"283"},{"name":"herb","shortname":":herb:","category":"nature","emoji_order":"284"},{"name":"shamrock","shortname":":shamrock:","category":"nature","emoji_order":"285"},{"name":"four leaf clover","shortname":":four_leaf_clover:","category":"nature","emoji_order":"286"},{"name":"pine decoration","shortname":":bamboo:","category":"nature","emoji_order":"287"},{"name":"tanabata tree","shortname":":tanabata_tree:","category":"nature","emoji_order":"288"},{"name":"leaf fluttering in wind","shortname":":leaves:","category":"nature","emoji_order":"289"},{"name":"fallen leaf","shortname":":fallen_leaf:","category":"nature","emoji_order":"290"},{"name":"maple leaf","shortname":":maple_leaf:","category":"nature","emoji_order":"291"},{"name":"ear of rice","shortname":":ear_of_rice:","category":"nature","emoji_order":"292"},{"name":"hibiscus","shortname":":hibiscus:","category":"nature","emoji_order":"293"},{"name":"sunflower","shortname":":sunflower:","category":"nature","emoji_order":"294"},{"name":"rose","shortname":":rose:","category":"nature","emoji_order":"295"},{"name":"tulip","shortname":":tulip:","category":"nature","emoji_order":"296"},{"name":"blossom","shortname":":blossom:","category":"nature","emoji_order":"297"},{"name":"cherry blossom","shortname":":cherry_blossom:","category":"nature","emoji_order":"298"},{"name":"bouquet","shortname":":bouquet:","category":"nature","emoji_order":"299"},{"name":"mushroom","shortname":":mushroom:","category":"nature","emoji_order":"300"},{"name":"chestnut","shortname":":chestnut:","category":"nature","emoji_order":"301"},{"name":"jack-o-lantern","shortname":":jack_o_lantern:","category":"nature","emoji_order":"302"},{"name":"spiral shell","shortname":":shell:","category":"nature","emoji_order":"303"},{"name":"spider web","shortname":":spider_web:","category":"nature","emoji_order":"304"},{"name":"earth globe americas","shortname":":earth_americas:","category":"nature","emoji_order":"305"},{"name":"earth globe europe-africa","shortname":":earth_africa:","category":"nature","emoji_order":"306"},{"name":"earth globe asia-australia","shortname":":earth_asia:","category":"nature","emoji_order":"307"},{"name":"full moon symbol","shortname":":full_moon:","category":"nature","emoji_order":"308"},{"name":"waning gibbous moon symbol","shortname":":waning_gibbous_moon:","category":"nature","emoji_order":"309"},{"name":"last quarter moon symbol","shortname":":last_quarter_moon:","category":"nature","emoji_order":"310"},{"name":"waning crescent moon symbol","shortname":":waning_crescent_moon:","category":"nature","emoji_order":"311"},{"name":"new moon symbol","shortname":":new_moon:","category":"nature","emoji_order":"312"},{"name":"waxing crescent moon symbol","shortname":":waxing_crescent_moon:","category":"nature","emoji_order":"313"},{"name":"first quarter moon symbol","shortname":":first_quarter_moon:","category":"nature","emoji_order":"314"},{"name":"waxing gibbous moon symbol","shortname":":waxing_gibbous_moon:","category":"nature","emoji_order":"315"},{"name":"new moon with face","shortname":":new_moon_with_face:","category":"nature","emoji_order":"316"},{"name":"full moon with face","shortname":":full_moon_with_face:","category":"nature","emoji_order":"317"},{"name":"first quarter moon with face","shortname":":first_quarter_moon_with_face:","category":"nature","emoji_order":"318"},{"name":"last quarter moon with face","shortname":":last_quarter_moon_with_face:","category":"nature","emoji_order":"319"},{"name":"sun with face","shortname":":sun_with_face:","category":"nature","emoji_order":"320"},{"name":"crescent moon","shortname":":crescent_moon:","category":"nature","emoji_order":"321"},{"name":"white medium star","shortname":":star:","category":"nature","emoji_order":"322"},{"name":"glowing star","shortname":":star2:","category":"nature","emoji_order":"323"},{"name":"dizzy symbol","shortname":":dizzy:","category":"nature","emoji_order":"324"},{"name":"sparkles","shortname":":sparkles:","category":"nature","emoji_order":"325"},{"name":"comet","shortname":":comet:","category":"nature","emoji_order":"326"},{"name":"black sun with rays","shortname":":sunny:","category":"nature","emoji_order":"327"},{"name":"white sun with small cloud","shortname":":white_sun_small_cloud:","category":"nature","emoji_order":"328"},{"name":"sun behind cloud","shortname":":partly_sunny:","category":"nature","emoji_order":"329"},{"name":"white sun behind cloud","shortname":":white_sun_cloud:","category":"nature","emoji_order":"330"},{"name":"white sun behind cloud with rain","shortname":":white_sun_rain_cloud:","category":"nature","emoji_order":"331"},{"name":"cloud","shortname":":cloud:","category":"nature","emoji_order":"332"},{"name":"cloud with rain","shortname":":cloud_rain:","category":"nature","emoji_order":"333"},{"name":"thunder cloud and rain","shortname":":thunder_cloud_rain:","category":"nature","emoji_order":"334"},{"name":"cloud with lightning","shortname":":cloud_lightning:","category":"nature","emoji_order":"335"},{"name":"high voltage sign","shortname":":zap:","category":"nature","emoji_order":"336"},{"name":"fire","shortname":":fire:","category":"nature","emoji_order":"337"},{"name":"collision symbol","shortname":":boom:","category":"nature","emoji_order":"338"},{"name":"snowflake","shortname":":snowflake:","category":"nature","emoji_order":"339"},{"name":"cloud with snow","shortname":":cloud_snow:","category":"nature","emoji_order":"340"},{"name":"snowman","shortname":":snowman2:","category":"nature","emoji_order":"341"},{"name":"snowman without snow","shortname":":snowman:","category":"nature","emoji_order":"342"},{"name":"wind blowing face","shortname":":wind_blowing_face:","category":"nature","emoji_order":"343"},{"name":"dash symbol","shortname":":dash:","category":"nature","emoji_order":"344"},{"name":"cloud with tornado","shortname":":cloud_tornado:","category":"nature","emoji_order":"345"},{"name":"fog","shortname":":fog:","category":"nature","emoji_order":"346"},{"name":"umbrella","shortname":":umbrella2:","category":"nature","emoji_order":"347"},{"name":"umbrella with rain drops","shortname":":umbrella:","category":"nature","emoji_order":"348"},{"name":"droplet","shortname":":droplet:","category":"nature","emoji_order":"349"},{"name":"splashing sweat symbol","shortname":":sweat_drops:","category":"nature","emoji_order":"350"},{"name":"water wave","shortname":":ocean:","category":"nature","emoji_order":"351"},{"name":"green apple","shortname":":green_apple:","category":"food","emoji_order":"352"},{"name":"red apple","shortname":":apple:","category":"food","emoji_order":"353"},{"name":"pear","shortname":":pear:","category":"food","emoji_order":"354"},{"name":"tangerine","shortname":":tangerine:","category":"food","emoji_order":"355"},{"name":"lemon","shortname":":lemon:","category":"food","emoji_order":"356"},{"name":"banana","shortname":":banana:","category":"food","emoji_order":"357"},{"name":"watermelon","shortname":":watermelon:","category":"food","emoji_order":"358"},{"name":"grapes","shortname":":grapes:","category":"food","emoji_order":"359"},{"name":"strawberry","shortname":":strawberry:","category":"food","emoji_order":"360"},{"name":"melon","shortname":":melon:","category":"food","emoji_order":"361"},{"name":"cherries","shortname":":cherries:","category":"food","emoji_order":"362"},{"name":"peach","shortname":":peach:","category":"food","emoji_order":"363"},{"name":"pineapple","shortname":":pineapple:","category":"food","emoji_order":"364"},{"name":"tomato","shortname":":tomato:","category":"food","emoji_order":"365"},{"name":"aubergine","shortname":":eggplant:","category":"food","emoji_order":"366"},{"name":"hot pepper","shortname":":hot_pepper:","category":"food","emoji_order":"367"},{"name":"ear of maize","shortname":":corn:","category":"food","emoji_order":"368"},{"name":"roasted sweet potato","shortname":":sweet_potato:","category":"food","emoji_order":"369"},{"name":"honey pot","shortname":":honey_pot:","category":"food","emoji_order":"370"},{"name":"bread","shortname":":bread:","category":"food","emoji_order":"371"},{"name":"cheese wedge","shortname":":cheese:","category":"food","emoji_order":"372"},{"name":"poultry leg","shortname":":poultry_leg:","category":"food","emoji_order":"373"},{"name":"meat on bone","shortname":":meat_on_bone:","category":"food","emoji_order":"374"},{"name":"fried shrimp","shortname":":fried_shrimp:","category":"food","emoji_order":"375"},{"name":"egg","shortname":":egg:","category":"unicode9","emoji_order":"75"},{"name":"hamburger","shortname":":hamburger:","category":"food","emoji_order":"377"},{"name":"french fries","shortname":":fries:","category":"food","emoji_order":"378"},{"name":"hot dog","shortname":":hotdog:","category":"food","emoji_order":"379"},{"name":"slice of pizza","shortname":":pizza:","category":"food","emoji_order":"380"},{"name":"spaghetti","shortname":":spaghetti:","category":"food","emoji_order":"381"},{"name":"taco","shortname":":taco:","category":"food","emoji_order":"382"},{"name":"burrito","shortname":":burrito:","category":"food","emoji_order":"383"},{"name":"steaming bowl","shortname":":ramen:","category":"food","emoji_order":"384"},{"name":"pot of food","shortname":":stew:","category":"food","emoji_order":"385"},{"name":"fish cake with swirl design","shortname":":fish_cake:","category":"food","emoji_order":"386"},{"name":"sushi","shortname":":sushi:","category":"food","emoji_order":"387"},{"name":"bento box","shortname":":bento:","category":"food","emoji_order":"388"},{"name":"curry and rice","shortname":":curry:","category":"food","emoji_order":"389"},{"name":"rice ball","shortname":":rice_ball:","category":"food","emoji_order":"390"},{"name":"cooked rice","shortname":":rice:","category":"food","emoji_order":"391"},{"name":"rice cracker","shortname":":rice_cracker:","category":"food","emoji_order":"392"},{"name":"oden","shortname":":oden:","category":"food","emoji_order":"393"},{"name":"dango","shortname":":dango:","category":"food","emoji_order":"394"},{"name":"shaved ice","shortname":":shaved_ice:","category":"food","emoji_order":"395"},{"name":"ice cream","shortname":":ice_cream:","category":"food","emoji_order":"396"},{"name":"soft ice cream","shortname":":icecream:","category":"food","emoji_order":"397"},{"name":"shortcake","shortname":":cake:","category":"food","emoji_order":"398"},{"name":"birthday cake","shortname":":birthday:","category":"food","emoji_order":"399"},{"name":"custard","shortname":":custard:","category":"food","emoji_order":"400"},{"name":"candy","shortname":":candy:","category":"food","emoji_order":"401"},{"name":"lollipop","shortname":":lollipop:","category":"food","emoji_order":"402"},{"name":"chocolate bar","shortname":":chocolate_bar:","category":"food","emoji_order":"403"},{"name":"popcorn","shortname":":popcorn:","category":"food","emoji_order":"404"},{"name":"doughnut","shortname":":doughnut:","category":"food","emoji_order":"405"},{"name":"cookie","shortname":":cookie:","category":"food","emoji_order":"406"},{"name":"beer mug","shortname":":beer:","category":"food","emoji_order":"407"},{"name":"clinking beer mugs","shortname":":beers:","category":"food","emoji_order":"408"},{"name":"wine glass","shortname":":wine_glass:","category":"food","emoji_order":"409"},{"name":"cocktail glass","shortname":":cocktail:","category":"food","emoji_order":"410"},{"name":"tropical drink","shortname":":tropical_drink:","category":"food","emoji_order":"411"},{"name":"bottle with popping cork","shortname":":champagne:","category":"food","emoji_order":"412"},{"name":"sake bottle and cup","shortname":":sake:","category":"food","emoji_order":"413"},{"name":"teacup without handle","shortname":":tea:","category":"food","emoji_order":"414"},{"name":"hot beverage","shortname":":coffee:","category":"food","emoji_order":"415"},{"name":"baby bottle","shortname":":baby_bottle:","category":"food","emoji_order":"416"},{"name":"fork and knife","shortname":":fork_and_knife:","category":"food","emoji_order":"417"},{"name":"fork and knife with plate","shortname":":fork_knife_plate:","category":"food","emoji_order":"418"},{"name":"soccer ball","shortname":":soccer:","category":"activity","emoji_order":"419"},{"name":"basketball and hoop","shortname":":basketball:","category":"activity","emoji_order":"420"},{"name":"american football","shortname":":football:","category":"activity","emoji_order":"421"},{"name":"baseball","shortname":":baseball:","category":"activity","emoji_order":"422"},{"name":"tennis racquet and ball","shortname":":tennis:","category":"activity","emoji_order":"423"},{"name":"volleyball","shortname":":volleyball:","category":"activity","emoji_order":"424"},{"name":"rugby football","shortname":":rugby_football:","category":"activity","emoji_order":"425"},{"name":"billiards","shortname":":8ball:","category":"activity","emoji_order":"426"},{"name":"flag in hole","shortname":":golf:","category":"activity","emoji_order":"427"},{"name":"golfer","shortname":":golfer:","category":"activity","emoji_order":"428"},{"name":"table tennis paddle and ball","shortname":":ping_pong:","category":"activity","emoji_order":"429"},{"name":"badminton racquet","shortname":":badminton:","category":"activity","emoji_order":"430"},{"name":"ice hockey stick and puck","shortname":":hockey:","category":"activity","emoji_order":"431"},{"name":"field hockey stick and ball","shortname":":field_hockey:","category":"activity","emoji_order":"432"},{"name":"cricket bat and ball","shortname":":cricket:","category":"activity","emoji_order":"433"},{"name":"ski and ski boot","shortname":":ski:","category":"activity","emoji_order":"434"},{"name":"skier","shortname":":skier:","category":"activity","emoji_order":"435"},{"name":"snowboarder","shortname":":snowboarder:","category":"activity","emoji_order":"436"},{"name":"ice skate","shortname":":ice_skate:","category":"activity","emoji_order":"437"},{"name":"bow and arrow","shortname":":bow_and_arrow:","category":"activity","emoji_order":"438"},{"name":"fishing pole and fish","shortname":":fishing_pole_and_fish:","category":"activity","emoji_order":"439"},{"name":"rowboat","shortname":":rowboat:","category":"activity","emoji_order":"440"},{"name":"swimmer","shortname":":swimmer:","category":"activity","emoji_order":"441"},{"name":"surfer","shortname":":surfer:","category":"activity","emoji_order":"442"},{"name":"bath","shortname":":bath:","category":"activity","emoji_order":"443"},{"name":"person with ball","shortname":":basketball_player:","category":"activity","emoji_order":"444"},{"name":"weight lifter","shortname":":lifter:","category":"activity","emoji_order":"445"},{"name":"bicyclist","shortname":":bicyclist:","category":"activity","emoji_order":"446"},{"name":"mountain bicyclist","shortname":":mountain_bicyclist:","category":"activity","emoji_order":"447"},{"name":"horse racing","shortname":":horse_racing:","category":"activity","emoji_order":"448"},{"name":"man in business suit levitating","shortname":":levitate:","category":"activity","emoji_order":"449"},{"name":"trophy","shortname":":trophy:","category":"activity","emoji_order":"450"},{"name":"running shirt with sash","shortname":":running_shirt_with_sash:","category":"activity","emoji_order":"451"},{"name":"sports medal","shortname":":medal:","category":"activity","emoji_order":"452"},{"name":"military medal","shortname":":military_medal:","category":"activity","emoji_order":"453"},{"name":"reminder ribbon","shortname":":reminder_ribbon:","category":"activity","emoji_order":"454"},{"name":"rosette","shortname":":rosette:","category":"activity","emoji_order":"455"},{"name":"ticket","shortname":":ticket:","category":"activity","emoji_order":"456"},{"name":"admission tickets","shortname":":tickets:","category":"activity","emoji_order":"457"},{"name":"performing arts","shortname":":performing_arts:","category":"activity","emoji_order":"458"},{"name":"artist palette","shortname":":art:","category":"activity","emoji_order":"459"},{"name":"circus tent","shortname":":circus_tent:","category":"activity","emoji_order":"460"},{"name":"microphone","shortname":":microphone:","category":"activity","emoji_order":"461"},{"name":"headphone","shortname":":headphones:","category":"activity","emoji_order":"462"},{"name":"musical score","shortname":":musical_score:","category":"activity","emoji_order":"463"},{"name":"musical keyboard","shortname":":musical_keyboard:","category":"activity","emoji_order":"464"},{"name":"saxophone","shortname":":saxophone:","category":"activity","emoji_order":"465"},{"name":"trumpet","shortname":":trumpet:","category":"activity","emoji_order":"466"},{"name":"guitar","shortname":":guitar:","category":"activity","emoji_order":"467"},{"name":"violin","shortname":":violin:","category":"activity","emoji_order":"468"},{"name":"clapper board","shortname":":clapper:","category":"activity","emoji_order":"469"},{"name":"video game","shortname":":video_game:","category":"activity","emoji_order":"470"},{"name":"alien monster","shortname":":space_invader:","category":"activity","emoji_order":"471"},{"name":"direct hit","shortname":":dart:","category":"activity","emoji_order":"472"},{"name":"game die","shortname":":game_die:","category":"activity","emoji_order":"473"},{"name":"slot machine","shortname":":slot_machine:","category":"activity","emoji_order":"474"},{"name":"bowling","shortname":":bowling:","category":"activity","emoji_order":"475"},{"name":"automobile","shortname":":red_car:","category":"travel","emoji_order":"476"},{"name":"taxi","shortname":":taxi:","category":"travel","emoji_order":"477"},{"name":"recreational vehicle","shortname":":blue_car:","category":"travel","emoji_order":"478"},{"name":"bus","shortname":":bus:","category":"travel","emoji_order":"479"},{"name":"trolleybus","shortname":":trolleybus:","category":"travel","emoji_order":"480"},{"name":"racing car","shortname":":race_car:","category":"travel","emoji_order":"481"},{"name":"police car","shortname":":police_car:","category":"travel","emoji_order":"482"},{"name":"ambulance","shortname":":ambulance:","category":"travel","emoji_order":"483"},{"name":"fire engine","shortname":":fire_engine:","category":"travel","emoji_order":"484"},{"name":"minibus","shortname":":minibus:","category":"travel","emoji_order":"485"},{"name":"delivery truck","shortname":":truck:","category":"travel","emoji_order":"486"},{"name":"articulated lorry","shortname":":articulated_lorry:","category":"travel","emoji_order":"487"},{"name":"tractor","shortname":":tractor:","category":"travel","emoji_order":"488"},{"name":"racing motorcycle","shortname":":motorcycle:","category":"travel","emoji_order":"489"},{"name":"bicycle","shortname":":bike:","category":"travel","emoji_order":"490"},{"name":"police cars revolving light","shortname":":rotating_light:","category":"travel","emoji_order":"491"},{"name":"oncoming police car","shortname":":oncoming_police_car:","category":"travel","emoji_order":"492"},{"name":"oncoming bus","shortname":":oncoming_bus:","category":"travel","emoji_order":"493"},{"name":"oncoming automobile","shortname":":oncoming_automobile:","category":"travel","emoji_order":"494"},{"name":"oncoming taxi","shortname":":oncoming_taxi:","category":"travel","emoji_order":"495"},{"name":"aerial tramway","shortname":":aerial_tramway:","category":"travel","emoji_order":"496"},{"name":"mountain cableway","shortname":":mountain_cableway:","category":"travel","emoji_order":"497"},{"name":"suspension railway","shortname":":suspension_railway:","category":"travel","emoji_order":"498"},{"name":"railway car","shortname":":railway_car:","category":"travel","emoji_order":"499"},{"name":"tram car","shortname":":train:","category":"travel","emoji_order":"500"},{"name":"monorail","shortname":":monorail:","category":"travel","emoji_order":"501"},{"name":"high-speed train","shortname":":bullettrain_side:","category":"travel","emoji_order":"502"},{"name":"high-speed train with bullet nose","shortname":":bullettrain_front:","category":"travel","emoji_order":"503"},{"name":"light rail","shortname":":light_rail:","category":"travel","emoji_order":"504"},{"name":"mountain railway","shortname":":mountain_railway:","category":"travel","emoji_order":"505"},{"name":"steam locomotive","shortname":":steam_locomotive:","category":"travel","emoji_order":"506"},{"name":"train","shortname":":train2:","category":"travel","emoji_order":"507"},{"name":"metro","shortname":":metro:","category":"travel","emoji_order":"508"},{"name":"tram","shortname":":tram:","category":"travel","emoji_order":"509"},{"name":"station","shortname":":station:","category":"travel","emoji_order":"510"},{"name":"helicopter","shortname":":helicopter:","category":"travel","emoji_order":"511"},{"name":"small airplane","shortname":":airplane_small:","category":"travel","emoji_order":"512"},{"name":"airplane","shortname":":airplane:","category":"travel","emoji_order":"513"},{"name":"airplane departure","shortname":":airplane_departure:","category":"travel","emoji_order":"514"},{"name":"airplane arriving","shortname":":airplane_arriving:","category":"travel","emoji_order":"515"},{"name":"sailboat","shortname":":sailboat:","category":"travel","emoji_order":"516"},{"name":"motorboat","shortname":":motorboat:","category":"travel","emoji_order":"517"},{"name":"speedboat","shortname":":speedboat:","category":"travel","emoji_order":"518"},{"name":"ferry","shortname":":ferry:","category":"travel","emoji_order":"519"},{"name":"passenger ship","shortname":":cruise_ship:","category":"travel","emoji_order":"520"},{"name":"rocket","shortname":":rocket:","category":"travel","emoji_order":"521"},{"name":"satellite","shortname":":satellite_orbital:","category":"travel","emoji_order":"522"},{"name":"seat","shortname":":seat:","category":"travel","emoji_order":"523"},{"name":"anchor","shortname":":anchor:","category":"travel","emoji_order":"524"},{"name":"construction sign","shortname":":construction:","category":"travel","emoji_order":"525"},{"name":"fuel pump","shortname":":fuelpump:","category":"travel","emoji_order":"526"},{"name":"bus stop","shortname":":busstop:","category":"travel","emoji_order":"527"},{"name":"vertical traffic light","shortname":":vertical_traffic_light:","category":"travel","emoji_order":"528"},{"name":"horizontal traffic light","shortname":":traffic_light:","category":"travel","emoji_order":"529"},{"name":"chequered flag","shortname":":checkered_flag:","category":"travel","emoji_order":"530"},{"name":"ship","shortname":":ship:","category":"travel","emoji_order":"531"},{"name":"ferris wheel","shortname":":ferris_wheel:","category":"travel","emoji_order":"532"},{"name":"roller coaster","shortname":":roller_coaster:","category":"travel","emoji_order":"533"},{"name":"carousel horse","shortname":":carousel_horse:","category":"travel","emoji_order":"534"},{"name":"building construction","shortname":":construction_site:","category":"travel","emoji_order":"535"},{"name":"foggy","shortname":":foggy:","category":"travel","emoji_order":"536"},{"name":"tokyo tower","shortname":":tokyo_tower:","category":"travel","emoji_order":"537"},{"name":"factory","shortname":":factory:","category":"travel","emoji_order":"538"},{"name":"fountain","shortname":":fountain:","category":"travel","emoji_order":"539"},{"name":"moon viewing ceremony","shortname":":rice_scene:","category":"travel","emoji_order":"540"},{"name":"mountain","shortname":":mountain:","category":"travel","emoji_order":"541"},{"name":"snow capped mountain","shortname":":mountain_snow:","category":"travel","emoji_order":"542"},{"name":"mount fuji","shortname":":mount_fuji:","category":"travel","emoji_order":"543"},{"name":"volcano","shortname":":volcano:","category":"travel","emoji_order":"544"},{"name":"silhouette of japan","shortname":":japan:","category":"travel","emoji_order":"545"},{"name":"camping","shortname":":camping:","category":"travel","emoji_order":"546"},{"name":"tent","shortname":":tent:","category":"travel","emoji_order":"547"},{"name":"national park","shortname":":park:","category":"travel","emoji_order":"548"},{"name":"motorway","shortname":":motorway:","category":"travel","emoji_order":"549"},{"name":"railway track","shortname":":railway_track:","category":"travel","emoji_order":"550"},{"name":"sunrise","shortname":":sunrise:","category":"travel","emoji_order":"551"},{"name":"sunrise over mountains","shortname":":sunrise_over_mountains:","category":"travel","emoji_order":"552"},{"name":"desert","shortname":":desert:","category":"travel","emoji_order":"553"},{"name":"beach with umbrella","shortname":":beach:","category":"travel","emoji_order":"554"},{"name":"desert island","shortname":":island:","category":"travel","emoji_order":"555"},{"name":"sunset over buildings","shortname":":city_sunset:","category":"travel","emoji_order":"556"},{"name":"cityscape at dusk","shortname":":city_dusk:","category":"travel","emoji_order":"557"},{"name":"cityscape","shortname":":cityscape:","category":"travel","emoji_order":"558"},{"name":"night with stars","shortname":":night_with_stars:","category":"travel","emoji_order":"559"},{"name":"bridge at night","shortname":":bridge_at_night:","category":"travel","emoji_order":"560"},{"name":"milky way","shortname":":milky_way:","category":"travel","emoji_order":"561"},{"name":"shooting star","shortname":":stars:","category":"travel","emoji_order":"562"},{"name":"firework sparkler","shortname":":sparkler:","category":"travel","emoji_order":"563"},{"name":"fireworks","shortname":":fireworks:","category":"travel","emoji_order":"564"},{"name":"rainbow","shortname":":rainbow:","category":"travel","emoji_order":"565"},{"name":"house buildings","shortname":":homes:","category":"travel","emoji_order":"566"},{"name":"european castle","shortname":":european_castle:","category":"travel","emoji_order":"567"},{"name":"japanese castle","shortname":":japanese_castle:","category":"travel","emoji_order":"568"},{"name":"stadium","shortname":":stadium:","category":"travel","emoji_order":"569"},{"name":"statue of liberty","shortname":":statue_of_liberty:","category":"travel","emoji_order":"570"},{"name":"house building","shortname":":house:","category":"travel","emoji_order":"571"},{"name":"house with garden","shortname":":house_with_garden:","category":"travel","emoji_order":"572"},{"name":"derelict house building","shortname":":house_abandoned:","category":"travel","emoji_order":"573"},{"name":"office building","shortname":":office:","category":"travel","emoji_order":"574"},{"name":"department store","shortname":":department_store:","category":"travel","emoji_order":"575"},{"name":"japanese post office","shortname":":post_office:","category":"travel","emoji_order":"576"},{"name":"european post office","shortname":":european_post_office:","category":"travel","emoji_order":"577"},{"name":"hospital","shortname":":hospital:","category":"travel","emoji_order":"578"},{"name":"bank","shortname":":bank:","category":"travel","emoji_order":"579"},{"name":"hotel","shortname":":hotel:","category":"travel","emoji_order":"580"},{"name":"convenience store","shortname":":convenience_store:","category":"travel","emoji_order":"581"},{"name":"school","shortname":":school:","category":"travel","emoji_order":"582"},{"name":"love hotel","shortname":":love_hotel:","category":"travel","emoji_order":"583"},{"name":"wedding","shortname":":wedding:","category":"travel","emoji_order":"584"},{"name":"classical building","shortname":":classical_building:","category":"travel","emoji_order":"585"},{"name":"church","shortname":":church:","category":"travel","emoji_order":"586"},{"name":"mosque","shortname":":mosque:","category":"travel","emoji_order":"587"},{"name":"synagogue","shortname":":synagogue:","category":"travel","emoji_order":"588"},{"name":"kaaba","shortname":":kaaba:","category":"travel","emoji_order":"589"},{"name":"shinto shrine","shortname":":shinto_shrine:","category":"travel","emoji_order":"590"},{"name":"watch","shortname":":watch:","category":"objects","emoji_order":"591"},{"name":"mobile phone","shortname":":iphone:","category":"objects","emoji_order":"592"},{"name":"mobile phone with rightwards arrow at left","shortname":":calling:","category":"objects","emoji_order":"593"},{"name":"personal computer","shortname":":computer:","category":"objects","emoji_order":"594"},{"name":"keyboard","shortname":":keyboard:","category":"objects","emoji_order":"595"},{"name":"desktop computer","shortname":":desktop:","category":"objects","emoji_order":"596"},{"name":"printer","shortname":":printer:","category":"objects","emoji_order":"597"},{"name":"three button mouse","shortname":":mouse_three_button:","category":"objects","emoji_order":"598"},{"name":"trackball","shortname":":trackball:","category":"objects","emoji_order":"599"},{"name":"joystick","shortname":":joystick:","category":"objects","emoji_order":"600"},{"name":"compression","shortname":":compression:","category":"objects","emoji_order":"601"},{"name":"minidisc","shortname":":minidisc:","category":"objects","emoji_order":"602"},{"name":"floppy disk","shortname":":floppy_disk:","category":"objects","emoji_order":"603"},{"name":"optical disc","shortname":":cd:","category":"objects","emoji_order":"604"},{"name":"dvd","shortname":":dvd:","category":"objects","emoji_order":"605"},{"name":"videocassette","shortname":":vhs:","category":"objects","emoji_order":"606"},{"name":"camera","shortname":":camera:","category":"objects","emoji_order":"607"},{"name":"camera with flash","shortname":":camera_with_flash:","category":"objects","emoji_order":"608"},{"name":"video camera","shortname":":video_camera:","category":"objects","emoji_order":"609"},{"name":"movie camera","shortname":":movie_camera:","category":"objects","emoji_order":"610"},{"name":"film projector","shortname":":projector:","category":"objects","emoji_order":"611"},{"name":"film frames","shortname":":film_frames:","category":"objects","emoji_order":"612"},{"name":"telephone receiver","shortname":":telephone_receiver:","category":"objects","emoji_order":"613"},{"name":"black telephone","shortname":":telephone:","category":"objects","emoji_order":"614"},{"name":"pager","shortname":":pager:","category":"objects","emoji_order":"615"},{"name":"fax machine","shortname":":fax:","category":"objects","emoji_order":"616"},{"name":"television","shortname":":tv:","category":"objects","emoji_order":"617"},{"name":"radio","shortname":":radio:","category":"objects","emoji_order":"618"},{"name":"studio microphone","shortname":":microphone2:","category":"objects","emoji_order":"619"},{"name":"level slider","shortname":":level_slider:","category":"objects","emoji_order":"620"},{"name":"control knobs","shortname":":control_knobs:","category":"objects","emoji_order":"621"},{"name":"stopwatch","shortname":":stopwatch:","category":"objects","emoji_order":"622"},{"name":"timer clock","shortname":":timer:","category":"objects","emoji_order":"623"},{"name":"alarm clock","shortname":":alarm_clock:","category":"objects","emoji_order":"624"},{"name":"mantlepiece clock","shortname":":clock:","category":"objects","emoji_order":"625"},{"name":"hourglass with flowing sand","shortname":":hourglass_flowing_sand:","category":"objects","emoji_order":"626"},{"name":"hourglass","shortname":":hourglass:","category":"objects","emoji_order":"627"},{"name":"satellite antenna","shortname":":satellite:","category":"objects","emoji_order":"628"},{"name":"battery","shortname":":battery:","category":"objects","emoji_order":"629"},{"name":"electric plug","shortname":":electric_plug:","category":"objects","emoji_order":"630"},{"name":"electric light bulb","shortname":":bulb:","category":"objects","emoji_order":"631"},{"name":"electric torch","shortname":":flashlight:","category":"objects","emoji_order":"632"},{"name":"candle","shortname":":candle:","category":"objects","emoji_order":"633"},{"name":"wastebasket","shortname":":wastebasket:","category":"objects","emoji_order":"634"},{"name":"oil drum","shortname":":oil:","category":"objects","emoji_order":"635"},{"name":"money with wings","shortname":":money_with_wings:","category":"objects","emoji_order":"636"},{"name":"banknote with dollar sign","shortname":":dollar:","category":"objects","emoji_order":"637"},{"name":"banknote with yen sign","shortname":":yen:","category":"objects","emoji_order":"638"},{"name":"banknote with euro sign","shortname":":euro:","category":"objects","emoji_order":"639"},{"name":"banknote with pound sign","shortname":":pound:","category":"objects","emoji_order":"640"},{"name":"money bag","shortname":":moneybag:","category":"objects","emoji_order":"641"},{"name":"credit card","shortname":":credit_card:","category":"objects","emoji_order":"642"},{"name":"gem stone","shortname":":gem:","category":"objects","emoji_order":"643"},{"name":"scales","shortname":":scales:","category":"objects","emoji_order":"644"},{"name":"wrench","shortname":":wrench:","category":"objects","emoji_order":"645"},{"name":"hammer","shortname":":hammer:","category":"objects","emoji_order":"646"},{"name":"hammer and pick","shortname":":hammer_pick:","category":"objects","emoji_order":"647"},{"name":"hammer and wrench","shortname":":tools:","category":"objects","emoji_order":"648"},{"name":"pick","shortname":":pick:","category":"objects","emoji_order":"649"},{"name":"nut and bolt","shortname":":nut_and_bolt:","category":"objects","emoji_order":"650"},{"name":"gear","shortname":":gear:","category":"objects","emoji_order":"651"},{"name":"chains","shortname":":chains:","category":"objects","emoji_order":"652"},{"name":"pistol","shortname":":gun:","category":"objects","emoji_order":"653"},{"name":"bomb","shortname":":bomb:","category":"objects","emoji_order":"654"},{"name":"hocho","shortname":":knife:","category":"objects","emoji_order":"655"},{"name":"dagger knife","shortname":":dagger:","category":"objects","emoji_order":"656"},{"name":"crossed swords","shortname":":crossed_swords:","category":"objects","emoji_order":"657"},{"name":"shield","shortname":":shield:","category":"objects","emoji_order":"658"},{"name":"smoking symbol","shortname":":smoking:","category":"objects","emoji_order":"659"},{"name":"skull and crossbones","shortname":":skull_crossbones:","category":"objects","emoji_order":"660"},{"name":"coffin","shortname":":coffin:","category":"objects","emoji_order":"661"},{"name":"funeral urn","shortname":":urn:","category":"objects","emoji_order":"662"},{"name":"amphora","shortname":":amphora:","category":"objects","emoji_order":"663"},{"name":"crystal ball","shortname":":crystal_ball:","category":"objects","emoji_order":"664"},{"name":"prayer beads","shortname":":prayer_beads:","category":"objects","emoji_order":"665"},{"name":"barber pole","shortname":":barber:","category":"objects","emoji_order":"666"},{"name":"alembic","shortname":":alembic:","category":"objects","emoji_order":"667"},{"name":"telescope","shortname":":telescope:","category":"objects","emoji_order":"668"},{"name":"microscope","shortname":":microscope:","category":"objects","emoji_order":"669"},{"name":"hole","shortname":":hole:","category":"objects","emoji_order":"670"},{"name":"pill","shortname":":pill:","category":"objects","emoji_order":"671"},{"name":"syringe","shortname":":syringe:","category":"objects","emoji_order":"672"},{"name":"thermometer","shortname":":thermometer:","category":"objects","emoji_order":"673"},{"name":"label","shortname":":label:","category":"objects","emoji_order":"674"},{"name":"bookmark","shortname":":bookmark:","category":"objects","emoji_order":"675"},{"name":"toilet","shortname":":toilet:","category":"objects","emoji_order":"676"},{"name":"shower","shortname":":shower:","category":"objects","emoji_order":"677"},{"name":"bathtub","shortname":":bathtub:","category":"objects","emoji_order":"678"},{"name":"key","shortname":":key:","category":"objects","emoji_order":"679"},{"name":"old key","shortname":":key2:","category":"objects","emoji_order":"680"},{"name":"couch and lamp","shortname":":couch:","category":"objects","emoji_order":"681"},{"name":"sleeping accommodation","shortname":":sleeping_accommodation:","category":"objects","emoji_order":"682"},{"name":"bed","shortname":":bed:","category":"objects","emoji_order":"683"},{"name":"door","shortname":":door:","category":"objects","emoji_order":"684"},{"name":"bellhop bell","shortname":":bellhop:","category":"objects","emoji_order":"685"},{"name":"frame with picture","shortname":":frame_photo:","category":"objects","emoji_order":"686"},{"name":"world map","shortname":":map:","category":"objects","emoji_order":"687"},{"name":"umbrella on ground","shortname":":beach_umbrella:","category":"objects","emoji_order":"688"},{"name":"moyai","shortname":":moyai:","category":"objects","emoji_order":"689"},{"name":"shopping bags","shortname":":shopping_bags:","category":"objects","emoji_order":"690"},{"name":"balloon","shortname":":balloon:","category":"objects","emoji_order":"691"},{"name":"carp streamer","shortname":":flags:","category":"objects","emoji_order":"692"},{"name":"ribbon","shortname":":ribbon:","category":"objects","emoji_order":"693"},{"name":"wrapped present","shortname":":gift:","category":"objects","emoji_order":"694"},{"name":"confetti ball","shortname":":confetti_ball:","category":"objects","emoji_order":"695"},{"name":"party popper","shortname":":tada:","category":"objects","emoji_order":"696"},{"name":"japanese dolls","shortname":":dolls:","category":"objects","emoji_order":"697"},{"name":"wind chime","shortname":":wind_chime:","category":"objects","emoji_order":"698"},{"name":"crossed flags","shortname":":crossed_flags:","category":"objects","emoji_order":"699"},{"name":"izakaya lantern","shortname":":izakaya_lantern:","category":"objects","emoji_order":"700"},{"name":"envelope","shortname":":envelope:","category":"objects","emoji_order":"701"},{"name":"envelope with downwards arrow above","shortname":":envelope_with_arrow:","category":"objects","emoji_order":"702"},{"name":"incoming envelope","shortname":":incoming_envelope:","category":"objects","emoji_order":"703"},{"name":"e-mail symbol","shortname":":e-mail:","category":"objects","emoji_order":"704"},{"name":"love letter","shortname":":love_letter:","category":"objects","emoji_order":"705"},{"name":"postbox","shortname":":postbox:","category":"objects","emoji_order":"706"},{"name":"closed mailbox with lowered flag","shortname":":mailbox_closed:","category":"objects","emoji_order":"707"},{"name":"closed mailbox with raised flag","shortname":":mailbox:","category":"objects","emoji_order":"708"},{"name":"open mailbox with raised flag","shortname":":mailbox_with_mail:","category":"objects","emoji_order":"709"},{"name":"open mailbox with lowered flag","shortname":":mailbox_with_no_mail:","category":"objects","emoji_order":"710"},{"name":"package","shortname":":package:","category":"objects","emoji_order":"711"},{"name":"postal horn","shortname":":postal_horn:","category":"objects","emoji_order":"712"},{"name":"inbox tray","shortname":":inbox_tray:","category":"objects","emoji_order":"713"},{"name":"outbox tray","shortname":":outbox_tray:","category":"objects","emoji_order":"714"},{"name":"scroll","shortname":":scroll:","category":"objects","emoji_order":"715"},{"name":"page with curl","shortname":":page_with_curl:","category":"objects","emoji_order":"716"},{"name":"bookmark tabs","shortname":":bookmark_tabs:","category":"objects","emoji_order":"717"},{"name":"bar chart","shortname":":bar_chart:","category":"objects","emoji_order":"718"},{"name":"chart with upwards trend","shortname":":chart_with_upwards_trend:","category":"objects","emoji_order":"719"},{"name":"chart with downwards trend","shortname":":chart_with_downwards_trend:","category":"objects","emoji_order":"720"},{"name":"page facing up","shortname":":page_facing_up:","category":"objects","emoji_order":"721"},{"name":"calendar","shortname":":date:","category":"objects","emoji_order":"722"},{"name":"tear-off calendar","shortname":":calendar:","category":"objects","emoji_order":"723"},{"name":"spiral calendar pad","shortname":":calendar_spiral:","category":"objects","emoji_order":"724"},{"name":"card index","shortname":":card_index:","category":"objects","emoji_order":"725"},{"name":"card file box","shortname":":card_box:","category":"objects","emoji_order":"726"},{"name":"ballot box with ballot","shortname":":ballot_box:","category":"objects","emoji_order":"727"},{"name":"file cabinet","shortname":":file_cabinet:","category":"objects","emoji_order":"728"},{"name":"clipboard","shortname":":clipboard:","category":"objects","emoji_order":"729"},{"name":"spiral note pad","shortname":":notepad_spiral:","category":"objects","emoji_order":"730"},{"name":"file folder","shortname":":file_folder:","category":"objects","emoji_order":"731"},{"name":"open file folder","shortname":":open_file_folder:","category":"objects","emoji_order":"732"},{"name":"card index dividers","shortname":":dividers:","category":"objects","emoji_order":"733"},{"name":"rolled-up newspaper","shortname":":newspaper2:","category":"objects","emoji_order":"734"},{"name":"newspaper","shortname":":newspaper:","category":"objects","emoji_order":"735"},{"name":"notebook","shortname":":notebook:","category":"objects","emoji_order":"736"},{"name":"closed book","shortname":":closed_book:","category":"objects","emoji_order":"737"},{"name":"green book","shortname":":green_book:","category":"objects","emoji_order":"738"},{"name":"blue book","shortname":":blue_book:","category":"objects","emoji_order":"739"},{"name":"orange book","shortname":":orange_book:","category":"objects","emoji_order":"740"},{"name":"notebook with decorative cover","shortname":":notebook_with_decorative_cover:","category":"objects","emoji_order":"741"},{"name":"ledger","shortname":":ledger:","category":"objects","emoji_order":"742"},{"name":"books","shortname":":books:","category":"objects","emoji_order":"743"},{"name":"open book","shortname":":book:","category":"objects","emoji_order":"744"},{"name":"link symbol","shortname":":link:","category":"objects","emoji_order":"745"},{"name":"paperclip","shortname":":paperclip:","category":"objects","emoji_order":"746"},{"name":"linked paperclips","shortname":":paperclips:","category":"objects","emoji_order":"747"},{"name":"black scissors","shortname":":scissors:","category":"objects","emoji_order":"748"},{"name":"triangular ruler","shortname":":triangular_ruler:","category":"objects","emoji_order":"749"},{"name":"straight ruler","shortname":":straight_ruler:","category":"objects","emoji_order":"750"},{"name":"pushpin","shortname":":pushpin:","category":"objects","emoji_order":"751"},{"name":"round pushpin","shortname":":round_pushpin:","category":"objects","emoji_order":"752"},{"name":"triangular flag on post","shortname":":triangular_flag_on_post:","category":"objects","emoji_order":"753"},{"name":"waving white flag","shortname":":flag_white:","category":"objects","emoji_order":"754"},{"name":"waving black flag","shortname":":flag_black:","category":"objects","emoji_order":"755"},{"name":"closed lock with key","shortname":":closed_lock_with_key:","category":"objects","emoji_order":"756"},{"name":"lock","shortname":":lock:","category":"objects","emoji_order":"757"},{"name":"open lock","shortname":":unlock:","category":"objects","emoji_order":"758"},{"name":"lock with ink pen","shortname":":lock_with_ink_pen:","category":"objects","emoji_order":"759"},{"name":"lower left ballpoint pen","shortname":":pen_ballpoint:","category":"objects","emoji_order":"760"},{"name":"lower left fountain pen","shortname":":pen_fountain:","category":"objects","emoji_order":"761"},{"name":"black nib","shortname":":black_nib:","category":"objects","emoji_order":"762"},{"name":"memo","shortname":":pencil:","category":"objects","emoji_order":"763"},{"name":"pencil","shortname":":pencil2:","category":"objects","emoji_order":"764"},{"name":"lower left crayon","shortname":":crayon:","category":"objects","emoji_order":"765"},{"name":"lower left paintbrush","shortname":":paintbrush:","category":"objects","emoji_order":"766"},{"name":"left-pointing magnifying glass","shortname":":mag:","category":"objects","emoji_order":"767"},{"name":"right-pointing magnifying glass","shortname":":mag_right:","category":"objects","emoji_order":"768"},{"name":"heavy black heart","shortname":":heart:","category":"symbols","emoji_order":"769","aliases_ascii":["<3"]},{"name":"yellow heart","shortname":":yellow_heart:","category":"symbols","emoji_order":"770"},{"name":"green heart","shortname":":green_heart:","category":"symbols","emoji_order":"771"},{"name":"blue heart","shortname":":blue_heart:","category":"symbols","emoji_order":"772"},{"name":"purple heart","shortname":":purple_heart:","category":"symbols","emoji_order":"773"},{"name":"broken heart","shortname":":broken_heart:","category":"symbols","emoji_order":"774","aliases_ascii":[" { }); it('should not change content unnecessarily on Markdown -> RTE conversion', () => { - const spy = sinon.spy(client, 'sendHtmlMessage'); + const spy = sinon.spy(client, 'sendTextMessage'); mci.enableRichtext(false); addTextToDraft('a'); mci.handleKeyCommand('toggle-mode'); @@ -109,8 +109,8 @@ describe('MessageComposerInput', () => { expect(spy.args[0][1]).toEqual('a'); }); - it('should send emoji messages in rich text', () => { - const spy = sinon.spy(client, 'sendHtmlMessage'); + it('should send emoji messages when rich text is enabled', () => { + const spy = sinon.spy(client, 'sendTextMessage'); mci.enableRichtext(true); addTextToDraft('☹'); mci.handleReturn(sinon.stub()); @@ -118,7 +118,7 @@ describe('MessageComposerInput', () => { expect(spy.calledOnce).toEqual(true, 'should send message'); }); - it('should send emoji messages in Markdown', () => { + it('should send emoji messages when Markdown is enabled', () => { const spy = sinon.spy(client, 'sendTextMessage'); mci.enableRichtext(false); addTextToDraft('☹');