diff options
Diffstat (limited to 'web/react/utils')
-rw-r--r-- | web/react/utils/async_client.jsx | 1085 | ||||
-rw-r--r-- | web/react/utils/channel_intro_messages.jsx | 253 | ||||
-rw-r--r-- | web/react/utils/client.jsx | 1650 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 520 | ||||
-rw-r--r-- | web/react/utils/delayed_action.jsx | 27 | ||||
-rw-r--r-- | web/react/utils/emoticons.jsx | 161 | ||||
-rw-r--r-- | web/react/utils/markdown.jsx | 575 | ||||
-rw-r--r-- | web/react/utils/text_formatting.jsx | 402 | ||||
-rw-r--r-- | web/react/utils/utils.jsx | 1416 |
9 files changed, 0 insertions, 6089 deletions
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx deleted file mode 100644 index b9770a6e9..000000000 --- a/web/react/utils/async_client.jsx +++ /dev/null @@ -1,1085 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import * as client from './client.jsx'; -import * as GlobalActions from '../action_creators/global_actions.jsx'; -import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; -import BrowserStore from '../stores/browser_store.jsx'; -import ChannelStore from '../stores/channel_store.jsx'; -import PreferenceStore from '../stores/preference_store.jsx'; -import PostStore from '../stores/post_store.jsx'; -import UserStore from '../stores/user_store.jsx'; -import * as utils from './utils.jsx'; - -import Constants from './constants.jsx'; -const ActionTypes = Constants.ActionTypes; -const StatTypes = Constants.StatTypes; - -// Used to track in progress async calls -const callTracker = {}; - -export function dispatchError(err, method) { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ERROR, - err, - method - }); -} - -function isCallInProgress(callName) { - if (!(callName in callTracker)) { - return false; - } - - if (callTracker[callName] === 0) { - return false; - } - - if (utils.getTimestamp() - callTracker[callName] > 5000) { - //console.log('AsyncClient call ' + callName + ' expired after more than 5 seconds'); - return false; - } - - return true; -} - -export function getChannels(checkVersion) { - if (isCallInProgress('getChannels')) { - return null; - } - - callTracker.getChannels = utils.getTimestamp(); - - return client.getChannels( - (data, textStatus, xhr) => { - callTracker.getChannels = 0; - - if (xhr.status === 304 || !data) { - return; - } - - if (checkVersion) { - var serverVersion = xhr.getResponseHeader('X-Version-ID'); - - if (serverVersion !== BrowserStore.getLastServerVersion()) { - if (!BrowserStore.getLastServerVersion() || BrowserStore.getLastServerVersion() === '') { - BrowserStore.setLastServerVersion(serverVersion); - } else { - BrowserStore.setLastServerVersion(serverVersion); - window.location.reload(true); - console.log('Detected version update refreshing the page'); //eslint-disable-line no-console - } - } - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_CHANNELS, - channels: data.channels, - members: data.members - }); - }, - (err) => { - callTracker.getChannels = 0; - dispatchError(err, 'getChannels'); - } - ); -} - -export function getChannel(id) { - if (isCallInProgress('getChannel' + id)) { - return; - } - - callTracker['getChannel' + id] = utils.getTimestamp(); - - client.getChannel(id, - (data, textStatus, xhr) => { - callTracker['getChannel' + id] = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_CHANNEL, - channel: data.channel, - member: data.member - }); - }, - (err) => { - callTracker['getChannel' + id] = 0; - dispatchError(err, 'getChannel'); - } - ); -} - -export function updateLastViewedAt(id) { - let channelId; - if (id) { - channelId = id; - } else { - channelId = ChannelStore.getCurrentId(); - } - - if (channelId == null) { - return; - } - - if (isCallInProgress(`updateLastViewed${channelId}`)) { - return; - } - - callTracker[`updateLastViewed${channelId}`] = utils.getTimestamp(); - client.updateLastViewedAt( - channelId, - () => { - callTracker.updateLastViewed = 0; - }, - (err) => { - callTracker.updateLastViewed = 0; - dispatchError(err, 'updateLastViewedAt'); - } - ); -} - -export function getMoreChannels(force) { - if (isCallInProgress('getMoreChannels')) { - return; - } - - if (ChannelStore.getMoreAll().loading || force) { - callTracker.getMoreChannels = utils.getTimestamp(); - client.getMoreChannels( - function getMoreChannelsSuccess(data, textStatus, xhr) { - callTracker.getMoreChannels = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_MORE_CHANNELS, - channels: data.channels, - members: data.members - }); - }, - function getMoreChannelsFailure(err) { - callTracker.getMoreChannels = 0; - dispatchError(err, 'getMoreChannels'); - } - ); - } -} - -export function getChannelExtraInfo(id, memberLimit) { - let channelId; - if (id) { - channelId = id; - } else { - channelId = ChannelStore.getCurrentId(); - } - - if (channelId != null) { - if (isCallInProgress('getChannelExtraInfo_' + channelId)) { - return; - } - - callTracker['getChannelExtraInfo_' + channelId] = utils.getTimestamp(); - - client.getChannelExtraInfo( - channelId, - memberLimit, - (data, textStatus, xhr) => { - callTracker['getChannelExtraInfo_' + channelId] = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_CHANNEL_EXTRA_INFO, - extra_info: data - }); - }, - (err) => { - callTracker['getChannelExtraInfo_' + channelId] = 0; - dispatchError(err, 'getChannelExtraInfo'); - } - ); - } -} - -export function getProfiles() { - if (isCallInProgress('getProfiles')) { - return; - } - - callTracker.getProfiles = utils.getTimestamp(); - client.getProfiles( - function getProfilesSuccess(data, textStatus, xhr) { - callTracker.getProfiles = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_PROFILES, - profiles: data - }); - }, - function getProfilesFailure(err) { - callTracker.getProfiles = 0; - dispatchError(err, 'getProfiles'); - } - ); -} - -export function getSessions() { - if (isCallInProgress('getSessions')) { - return; - } - - callTracker.getSessions = utils.getTimestamp(); - client.getSessions( - UserStore.getCurrentId(), - function getSessionsSuccess(data, textStatus, xhr) { - callTracker.getSessions = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_SESSIONS, - sessions: data - }); - }, - function getSessionsFailure(err) { - callTracker.getSessions = 0; - dispatchError(err, 'getSessions'); - } - ); -} - -export function getAudits() { - if (isCallInProgress('getAudits')) { - return; - } - - callTracker.getAudits = utils.getTimestamp(); - client.getAudits( - UserStore.getCurrentId(), - function getAuditsSuccess(data, textStatus, xhr) { - callTracker.getAudits = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_AUDITS, - audits: data - }); - }, - function getAuditsFailure(err) { - callTracker.getAudits = 0; - dispatchError(err, 'getAudits'); - } - ); -} - -export function getLogs() { - if (isCallInProgress('getLogs')) { - return; - } - - callTracker.getLogs = utils.getTimestamp(); - client.getLogs( - (data, textStatus, xhr) => { - callTracker.getLogs = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_LOGS, - logs: data - }); - }, - (err) => { - callTracker.getLogs = 0; - dispatchError(err, 'getLogs'); - } - ); -} - -export function getServerAudits() { - if (isCallInProgress('getServerAudits')) { - return; - } - - callTracker.getServerAudits = utils.getTimestamp(); - client.getServerAudits( - (data, textStatus, xhr) => { - callTracker.getServerAudits = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_SERVER_AUDITS, - audits: data - }); - }, - (err) => { - callTracker.getServerAudits = 0; - dispatchError(err, 'getServerAudits'); - } - ); -} - -export function getConfig() { - if (isCallInProgress('getConfig')) { - return; - } - - callTracker.getConfig = utils.getTimestamp(); - client.getConfig( - (data, textStatus, xhr) => { - callTracker.getConfig = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_CONFIG, - config: data - }); - }, - (err) => { - callTracker.getConfig = 0; - dispatchError(err, 'getConfig'); - } - ); -} - -export function getAllTeams() { - if (isCallInProgress('getAllTeams')) { - return; - } - - callTracker.getAllTeams = utils.getTimestamp(); - client.getAllTeams( - (data, textStatus, xhr) => { - callTracker.getAllTeams = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ALL_TEAMS, - teams: data - }); - }, - (err) => { - callTracker.getAllTeams = 0; - dispatchError(err, 'getAllTeams'); - } - ); -} - -export function search(terms) { - if (isCallInProgress('search_' + String(terms))) { - return; - } - - callTracker['search_' + String(terms)] = utils.getTimestamp(); - client.search( - terms, - function searchSuccess(data, textStatus, xhr) { - callTracker['search_' + String(terms)] = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_SEARCH, - results: data - }); - }, - function searchFailure(err) { - callTracker['search_' + String(terms)] = 0; - dispatchError(err, 'search'); - } - ); -} - -export function getPostsPage(id, maxPosts) { - let channelId = id; - if (channelId == null) { - channelId = ChannelStore.getCurrentId(); - if (channelId == null) { - return; - } - } - - if (isCallInProgress('getPostsPage_' + channelId)) { - return; - } - - var postList = PostStore.getAllPosts(id); - - var max = maxPosts; - if (max == null) { - max = Constants.POST_CHUNK_SIZE * Constants.MAX_POST_CHUNKS; - } - - // if we already have more than POST_CHUNK_SIZE posts, - // let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE, - // with a max at maxPosts - var numPosts = Math.min(max, Constants.POST_CHUNK_SIZE); - if (postList && postList.order.length > 0) { - numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE)); - } - - if (channelId != null) { - callTracker['getPostsPage_' + channelId] = utils.getTimestamp(); - - client.getPostsPage( - channelId, - 0, - numPosts, - (data, textStatus, xhr) => { - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: true, - numRequested: numPosts, - post_list: data - }); - - getProfiles(); - }, - (err) => { - dispatchError(err, 'getPostsPage'); - }, - () => { - callTracker['getPostsPage_' + channelId] = 0; - } - ); - } -} - -export function getPosts(id) { - let channelId = id; - if (channelId == null) { - channelId = ChannelStore.getCurrentId(); - if (channelId == null) { - return; - } - } - - if (isCallInProgress('getPosts_' + channelId)) { - return; - } - - const postList = PostStore.getAllPosts(channelId); - - if ($.isEmptyObject(postList) || postList.order.length < Constants.POST_CHUNK_SIZE) { - getPostsPage(channelId, Constants.POST_CHUNK_SIZE); - return; - } - - const latestPost = PostStore.getLatestPost(channelId); - let latestPostTime = 0; - - if (latestPost != null && latestPost.update_at != null) { - latestPostTime = latestPost.create_at; - } - - callTracker['getPosts_' + channelId] = utils.getTimestamp(); - - client.getPosts( - channelId, - latestPostTime, - (data, textStatus, xhr) => { - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: true, - numRequested: 0, - post_list: data - }); - - getProfiles(); - }, - (err) => { - dispatchError(err, 'getPosts'); - }, - () => { - callTracker['getPosts_' + channelId] = 0; - } - ); -} - -export function getPostsBefore(postId, offset, numPost) { - const channelId = ChannelStore.getCurrentId(); - if (channelId == null) { - return; - } - - if (isCallInProgress('getPostsBefore_' + channelId)) { - return; - } - - client.getPostsBefore( - channelId, - postId, - offset, - numPost, - (data, textStatus, xhr) => { - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: true, - numRequested: numPost, - post_list: data - }); - - getProfiles(); - }, - (err) => { - dispatchError(err, 'getPostsBefore'); - }, - () => { - callTracker['getPostsBefore_' + channelId] = 0; - } - ); -} - -export function getPostsAfter(postId, offset, numPost) { - const channelId = ChannelStore.getCurrentId(); - if (channelId == null) { - return; - } - - if (isCallInProgress('getPostsAfter_' + channelId)) { - return; - } - - client.getPostsAfter( - channelId, - postId, - offset, - numPost, - (data, textStatus, xhr) => { - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_POSTS, - id: channelId, - before: false, - numRequested: numPost, - post_list: data - }); - - getProfiles(); - }, - (err) => { - dispatchError(err, 'getPostsAfter'); - }, - () => { - callTracker['getPostsAfter_' + channelId] = 0; - } - ); -} - -export function getMe() { - if (isCallInProgress('getMe')) { - return null; - } - - callTracker.getMe = utils.getTimestamp(); - return client.getMe( - (data, textStatus, xhr) => { - callTracker.getMe = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ME, - me: data - }); - - GlobalActions.newLocalizationSelected(data.locale); - }, - (err) => { - callTracker.getMe = 0; - dispatchError(err, 'getMe'); - } - ); -} - -export function getStatuses() { - const preferences = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW); - - const teammateIds = []; - for (const preference of preferences) { - if (preference.value === 'true') { - teammateIds.push(preference.name); - } - } - - if (isCallInProgress('getStatuses') || teammateIds.length === 0) { - return; - } - - callTracker.getStatuses = utils.getTimestamp(); - client.getStatuses(teammateIds, - (data, textStatus, xhr) => { - callTracker.getStatuses = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_STATUSES, - statuses: data - }); - }, - (err) => { - callTracker.getStatuses = 0; - dispatchError(err, 'getStatuses'); - } - ); -} - -export function getMyTeam() { - if (isCallInProgress('getMyTeam')) { - return null; - } - - callTracker.getMyTeam = utils.getTimestamp(); - return client.getMyTeam( - function getMyTeamSuccess(data, textStatus, xhr) { - callTracker.getMyTeam = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_MY_TEAM, - team: data - }); - }, - function getMyTeamFailure(err) { - callTracker.getMyTeam = 0; - dispatchError(err, 'getMyTeam'); - } - ); -} - -export function getAllPreferences() { - if (isCallInProgress('getAllPreferences')) { - return; - } - - callTracker.getAllPreferences = utils.getTimestamp(); - client.getAllPreferences( - (data, textStatus, xhr) => { - callTracker.getAllPreferences = 0; - - if (xhr.status === 304 || !data) { - return; - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_PREFERENCES, - preferences: data - }); - }, - (err) => { - callTracker.getAllPreferences = 0; - dispatchError(err, 'getAllPreferences'); - } - ); -} - -export function savePreferences(preferences, success, error) { - client.savePreferences( - preferences, - (data, textStatus, xhr) => { - if (xhr.status !== 304) { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_PREFERENCES, - preferences - }); - } - - if (success) { - success(data); - } - }, - (err) => { - dispatchError(err, 'savePreferences'); - - if (error) { - error(); - } - } - ); -} - -export function getSuggestedCommands(command, suggestionId, component) { - client.listCommands( - (data) => { - var matches = []; - data.forEach((cmd) => { - if (('/' + cmd.trigger).indexOf(command) === 0) { - let s = '/' + cmd.trigger; - let hint = ''; - if (cmd.auto_complete_hint && cmd.auto_complete_hint.length !== 0) { - hint = cmd.auto_complete_hint; - } - matches.push({ - suggestion: s, - hint, - description: cmd.auto_complete_desc - }); - } - }); - - matches = matches.sort((a, b) => a.suggestion.localeCompare(b.suggestion)); - - // pull out the suggested commands from the returned data - const terms = matches.map((suggestion) => suggestion.suggestion); - - if (terms.length > 0) { - AppDispatcher.handleServerAction({ - type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS, - id: suggestionId, - matchedPretext: command, - terms, - items: matches, - component - }); - } - }, - (err) => { - dispatchError(err, 'getCommandSuggestions'); - } - ); -} - -export function getFileInfo(filename) { - const callName = 'getFileInfo' + filename; - - if (isCallInProgress(callName)) { - return; - } - - callTracker[callName] = utils.getTimestamp(); - - client.getFileInfo( - filename, - (data) => { - callTracker[callName] = 0; - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_FILE_INFO, - filename, - info: data - }); - }, - (err) => { - callTracker[callName] = 0; - - dispatchError(err, 'getFileInfo'); - } - ); -} - -export function getStandardAnalytics(teamId) { - const callName = 'getStandardAnaytics' + teamId; - - if (isCallInProgress(callName)) { - return; - } - - callTracker[callName] = utils.getTimestamp(); - - client.getAnalytics( - 'standard', - teamId, - (data) => { - callTracker[callName] = 0; - - const stats = {}; - - for (const index in data) { - if (data[index].name === 'channel_open_count') { - stats[StatTypes.TOTAL_PUBLIC_CHANNELS] = data[index].value; - } - - if (data[index].name === 'channel_private_count') { - stats[StatTypes.TOTAL_PRIVATE_GROUPS] = data[index].value; - } - - if (data[index].name === 'post_count') { - stats[StatTypes.TOTAL_POSTS] = data[index].value; - } - - if (data[index].name === 'unique_user_count') { - stats[StatTypes.TOTAL_USERS] = data[index].value; - } - - if (data[index].name === 'team_count' && teamId == null) { - stats[StatTypes.TOTAL_TEAMS] = data[index].value; - } - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ANALYTICS, - teamId, - stats - }); - }, - (err) => { - callTracker[callName] = 0; - - dispatchError(err, 'getStandardAnalytics'); - } - ); -} - -export function getAdvancedAnalytics(teamId) { - const callName = 'getAdvancedAnalytics' + teamId; - - if (isCallInProgress(callName)) { - return; - } - - callTracker[callName] = utils.getTimestamp(); - - client.getAnalytics( - 'extra_counts', - teamId, - (data) => { - callTracker[callName] = 0; - - const stats = {}; - - for (const index in data) { - if (data[index].name === 'file_post_count') { - stats[StatTypes.TOTAL_FILE_POSTS] = data[index].value; - } - - if (data[index].name === 'hashtag_post_count') { - stats[StatTypes.TOTAL_HASHTAG_POSTS] = data[index].value; - } - - if (data[index].name === 'incoming_webhook_count') { - stats[StatTypes.TOTAL_IHOOKS] = data[index].value; - } - - if (data[index].name === 'outgoing_webhook_count') { - stats[StatTypes.TOTAL_OHOOKS] = data[index].value; - } - - if (data[index].name === 'command_count') { - stats[StatTypes.TOTAL_COMMANDS] = data[index].value; - } - - if (data[index].name === 'session_count') { - stats[StatTypes.TOTAL_SESSIONS] = data[index].value; - } - } - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ANALYTICS, - teamId, - stats - }); - }, - (err) => { - callTracker[callName] = 0; - - dispatchError(err, 'getAdvancedAnalytics'); - } - ); -} - -export function getPostsPerDayAnalytics(teamId) { - const callName = 'getPostsPerDayAnalytics' + teamId; - - if (isCallInProgress(callName)) { - return; - } - - callTracker[callName] = utils.getTimestamp(); - - client.getAnalytics( - 'post_counts_day', - teamId, - (data) => { - callTracker[callName] = 0; - - data.reverse(); - - const stats = {}; - stats[StatTypes.POST_PER_DAY] = data; - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ANALYTICS, - teamId, - stats - }); - }, - (err) => { - callTracker[callName] = 0; - - dispatchError(err, 'getPostsPerDayAnalytics'); - } - ); -} - -export function getUsersPerDayAnalytics(teamId) { - const callName = 'getUsersPerDayAnalytics' + teamId; - - if (isCallInProgress(callName)) { - return; - } - - callTracker[callName] = utils.getTimestamp(); - - client.getAnalytics( - 'user_counts_with_posts_day', - teamId, - (data) => { - callTracker[callName] = 0; - - data.reverse(); - - const stats = {}; - stats[StatTypes.USERS_WITH_POSTS_PER_DAY] = data; - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ANALYTICS, - teamId, - stats - }); - }, - (err) => { - callTracker[callName] = 0; - - dispatchError(err, 'getUsersPerDayAnalytics'); - } - ); -} - -export function getRecentAndNewUsersAnalytics(teamId) { - const callName = 'getRecentAndNewUsersAnalytics' + teamId; - - if (isCallInProgress(callName)) { - return; - } - - callTracker[callName] = utils.getTimestamp(); - - client.getProfilesForTeam( - teamId, - (users) => { - const stats = {}; - - const usersList = []; - for (const id in users) { - if (users.hasOwnProperty(id)) { - usersList.push(users[id]); - } - } - - usersList.sort((a, b) => { - if (a.last_activity_at < b.last_activity_at) { - return 1; - } - - if (a.last_activity_at > b.last_activity_at) { - return -1; - } - - return 0; - }); - - const recentActive = []; - for (let i = 0; i < usersList.length; i++) { - if (usersList[i].last_activity_at == null) { - continue; - } - - recentActive.push(usersList[i]); - if (i >= Constants.STAT_MAX_ACTIVE_USERS) { - break; - } - } - - stats[StatTypes.RECENTLY_ACTIVE_USERS] = recentActive; - - usersList.sort((a, b) => { - if (a.create_at < b.create_at) { - return 1; - } - - if (a.create_at > b.create_at) { - return -1; - } - - return 0; - }); - - var newlyCreated = []; - for (let i = 0; i < usersList.length; i++) { - newlyCreated.push(usersList[i]); - if (i >= Constants.STAT_MAX_NEW_USERS) { - break; - } - } - - stats[StatTypes.NEWLY_CREATED_USERS] = newlyCreated; - - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_ANALYTICS, - teamId, - stats - }); - }, - (err) => { - callTracker[callName] = 0; - - dispatchError(err, 'getRecentAndNewUsersAnalytics'); - } - ); -} diff --git a/web/react/utils/channel_intro_messages.jsx b/web/react/utils/channel_intro_messages.jsx deleted file mode 100644 index 94f3f0ce0..000000000 --- a/web/react/utils/channel_intro_messages.jsx +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import * as Utils from './utils.jsx'; -import ChannelInviteModal from '../components/channel_invite_modal.jsx'; -import EditChannelHeaderModal from '../components/edit_channel_header_modal.jsx'; -import ToggleModalButton from '../components/toggle_modal_button.jsx'; -import UserProfile from '../components/user_profile.jsx'; -import ChannelStore from '../stores/channel_store.jsx'; -import Constants from '../utils/constants.jsx'; -import * as GlobalActions from '../action_creators/global_actions.jsx'; - -import {FormattedMessage, FormattedHTMLMessage, FormattedDate} from 'mm-intl'; - -export function createChannelIntroMessage(channel) { - if (channel.type === 'D') { - return createDMIntroMessage(channel); - } else if (ChannelStore.isDefault(channel)) { - return createDefaultIntroMessage(channel); - } else if (channel.name === Constants.OFFTOPIC_CHANNEL) { - return createOffTopicIntroMessage(channel); - } else if (channel.type === 'O' || channel.type === 'P') { - return createStandardIntroMessage(channel); - } - return null; -} - -export function createDMIntroMessage(channel) { - var teammate = Utils.getDirectTeammate(channel.id); - - if (teammate) { - var teammateName = teammate.username; - if (teammate.nickname.length > 0) { - teammateName = teammate.nickname; - } - - return ( - <div className='channel-intro'> - <div className='post-profile-img__container channel-intro-img'> - <img - className='post-profile-img' - src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at} - height='50' - width='50' - /> - </div> - <div className='channel-intro-profile'> - <strong> - <UserProfile user={teammate}/> - </strong> - </div> - <p className='channel-intro-text'> - <FormattedHTMLMessage - id='intro_messages.DM' - defaultMessage='This is the start of your direct message history with {teammate}.<br />Direct messages and files shared here are not shown to people outside this area.' - values={{ - teammate: teammateName - }} - /> - </p> - {createSetHeaderButton(channel)} - </div> - ); - } - - return ( - <div className='channel-intro'> - <p className='channel-intro-text'> - <FormattedMessage - id='intro_messages.teammate' - defaultMessage='This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.' - /> - </p> - </div> - ); -} - -export function createOffTopicIntroMessage(channel) { - return ( - <div className='channel-intro'> - <FormattedHTMLMessage - id='intro_messages.offTopic' - defaultMessage='<h4 class="channel-intro__title">Beginning of {display_name}</h4><p class="channel-intro__content">This is the start of {display_name}, a channel for non-work-related conversations.<br/></p>' - values={{ - display_name: channel.display_name - }} - /> - {createSetHeaderButton(channel)} - {createInviteChannelMemberButton(channel, 'channel')} - </div> - ); -} - -export function createDefaultIntroMessage(channel) { - const inviteModalLink = ( - <a - className='intro-links' - href='#' - onClick={GlobalActions.showGetTeamInviteLinkModal} - > - <i className='fa fa-user-plus'></i> - <FormattedMessage - id='intro_messages.inviteOthers' - defaultMessage='Invite others to this team' - /> - </a> - ); - - return ( - <div className='channel-intro'> - <FormattedHTMLMessage - id='intro_messages.default' - defaultMessage="<h4 class='channel-intro__title'>Beginning of {display_name}</h4><p class='channel-intro__content'><strong>Welcome to {display_name}!</strong><br/><br/>This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.</p>" - values={{ - display_name: channel.display_name - }} - /> - {inviteModalLink} - {createSetHeaderButton(channel)} - <br/> - </div> - ); -} - -export function createStandardIntroMessage(channel) { - var uiName = channel.display_name; - var creatorName = ''; - - var uiType; - var memberMessage; - if (channel.type === 'P') { - uiType = ( - <FormattedMessage - id='intro_messages.group' - defaultMessage='private group' - /> - ); - memberMessage = ( - <FormattedMessage - id='intro_messages.onlyInvited' - defaultMessage=' Only invited members can see this private group.' - /> - ); - } else { - uiType = ( - <FormattedMessage - id='intro_messages.channel' - defaultMessage='channel' - /> - ); - memberMessage = ( - <FormattedMessage - id='intro_messages.anyMember' - defaultMessage=' Any member can join and read this channel.' - /> - ); - } - - const date = ( - <FormattedDate - value={channel.create_at} - month='long' - day='2-digit' - year='numeric' - /> - ); - - var createMessage; - if (creatorName === '') { - createMessage = ( - <FormattedMessage - id='intro_messages.noCreator' - defaultMessage='This is the start of the {name} {type}, created on {date}.' - values={{ - name: (uiName), - type: (uiType), - date: (date) - }} - /> - ); - } else { - createMessage = ( - <span> - <FormattedHTMLMessage - id='intro_messages.creator' - defaultMessage='This is the start of the <strong>{name}</strong> {type}, created by <strong>{creator}</strong> on <strong>{date}</strong>' - values={{ - name: (uiName), - type: (uiType), - date: (date), - creator: creatorName - }} - /> - </span> - ); - } - - return ( - <div className='channel-intro'> - <h4 className='channel-intro__title'> - <FormattedMessage - id='intro_messages.beginning' - defaultMessage='Beginning of {name}' - values={{ - name: (uiName) - }} - /> - </h4> - <p className='channel-intro__content'> - {createMessage} - {memberMessage} - <br/> - </p> - {createSetHeaderButton(channel)} - {createInviteChannelMemberButton(channel, uiType)} - </div> - ); -} - -function createInviteChannelMemberButton(channel, uiType) { - return ( - <ToggleModalButton - className='intro-links' - dialogType={ChannelInviteModal} - dialogProps={{channel}} - > - <i className='fa fa-user-plus'></i> - <FormattedMessage - id='intro_messages.invite' - defaultMessage='Invite others to this {type}' - values={{ - type: (uiType) - }} - /> - </ToggleModalButton> - ); -} - -function createSetHeaderButton(channel) { - return ( - <ToggleModalButton - className='intro-links' - dialogType={EditChannelHeaderModal} - dialogProps={{channel}} - > - <i className='fa fa-pencil'></i> - <FormattedMessage - id='intro_messages.setHeader' - defaultMessage='Set a Header' - /> - </ToggleModalButton> - ); -} diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx deleted file mode 100644 index e00f28a14..000000000 --- a/web/react/utils/client.jsx +++ /dev/null @@ -1,1650 +0,0 @@ -// See License.txt for license information. - -import BrowserStore from '../stores/browser_store.jsx'; - -import {browserHistory} from 'react-router'; - -let translations = { - connectionError: 'There appears to be a problem with your internet connection.', - unknownError: 'We received an unexpected status code from the server.' -}; - -export function setTranslations(messages) { - translations = messages; -} - -export function track(category, action, label, property, value) { - global.window.analytics.track(action, {category, label, property, value}); -} - -export function trackPage() { - global.window.analytics.page(); -} - -function handleError(methodName, xhr, status, err) { - var e = null; - try { - e = JSON.parse(xhr.responseText); - } catch (parseError) { - e = null; - } - - var msg = ''; - - if (e) { - msg = 'method=' + methodName + ' msg=' + e.message + ' detail=' + e.detailed_error + ' rid=' + e.request_id; - } else { - msg = 'method=' + methodName + ' status=' + status + ' statusCode=' + xhr.status + ' err=' + err; - - if (xhr.status === 0) { - e = {message: translations.connectionError}; - } else { - e = {message: translations.unknownError + ' (' + xhr.status + ')'}; - } - } - - console.error(msg); //eslint-disable-line no-console - console.error(e); //eslint-disable-line no-console - - track('api', 'api_weberror', methodName, 'message', msg); - - if (xhr.status === 401) { - if (window.location.href.indexOf('/channels') === 0) { - browserHistory.push('/login?extra=expired&redirect=' + encodeURIComponent(window.location.pathname + window.location.search)); - } else { - var teamURL = window.location.pathname.split('/channels')[0]; - browserHistory.push(teamURL + '/login?extra=expired&redirect=' + encodeURIComponent(window.location.pathname + window.location.search)); - } - } - - return e; -} - -export function getTranslations(locale, success, error) { - $.ajax({ - url: '/static/i18n/' + locale + '.json', - dataType: 'json', - success, - error: function onError(xhr, status, err) { - var e = handleError('getTranslations', xhr, status, err); - error(e); - } - }); -} - -export function createTeamFromSignup(teamSignup, success, error) { - $.ajax({ - url: '/api/v1/teams/create_from_signup', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(teamSignup), - success, - error: function onError(xhr, status, err) { - var e = handleError('createTeamFromSignup', xhr, status, err); - error(e); - } - }); -} - -export function createTeamWithLdap(teamSignup, success, error) { - $.ajax({ - url: '/api/v1/teams/create_with_ldap', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(teamSignup), - success, - error: function onError(xhr, status, err) { - var e = handleError('createTeamFromSignup', xhr, status, err); - error(e); - } - }); -} - -export function createTeamWithSSO(team, service, success, error) { - $.ajax({ - url: '/api/v1/teams/create_with_sso/' + service, - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(team), - success, - error: function onError(xhr, status, err) { - var e = handleError('createTeamWithSSO', xhr, status, err); - error(e); - } - }); -} - -export function createUser(user, data, emailHash, success, error) { - $.ajax({ - url: '/api/v1/users/create?d=' + encodeURIComponent(data) + '&h=' + encodeURIComponent(emailHash), - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(user), - success, - error: function onError(xhr, status, err) { - var e = handleError('createUser', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_create', user.team_id, 'email', user.email); -} - -export function updateUser(user, success, error) { - $.ajax({ - url: '/api/v1/users/update', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(user), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateUser', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_update'); -} - -export function updatePassword(data, success, error) { - $.ajax({ - url: '/api/v1/users/newpassword', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('newPassword', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_newpassword'); -} - -export function updateUserNotifyProps(data, success, error) { - $.ajax({ - url: '/api/v1/users/update_notify', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateUserNotifyProps', xhr, status, err); - error(e); - } - }); -} - -export function updateRoles(data, success, error) { - $.ajax({ - url: '/api/v1/users/update_roles', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateRoles', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_update_roles'); -} - -export function updateActive(userId, active, success, error) { - var data = {}; - data.user_id = userId; - data.active = '' + active; - - $.ajax({ - url: '/api/v1/users/update_active', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateActive', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_update_roles'); -} - -export function sendPasswordReset(data, success, error) { - $.ajax({ - url: '/api/v1/users/send_password_reset', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('sendPasswordReset', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_send_password_reset'); -} - -export function resetPassword(data, success, error) { - $.ajax({ - url: '/api/v1/users/reset_password', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('resetPassword', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_reset_password'); -} - -export function switchToSSO(data, success, error) { - $.ajax({ - url: '/api/v1/users/switch_to_sso', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('switchToSSO', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_switch_to_sso'); -} - -export function switchToEmail(data, success, error) { - $.ajax({ - url: '/api/v1/users/switch_to_email', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('switchToEmail', xhr, status, err); - error(e); - } - }); - - track('api', 'api_users_switch_to_email'); -} - -export function logout(success, error) { - track('api', 'api_users_logout'); - $.ajax({ - url: '/api/v1/users/logout', - type: 'POST', - success, - error: function onError(xhr, status, err) { - var e = handleError('logout', xhr, status, err); - error(e); - } - }); -} - -export function loginByEmail(name, email, password, success, error) { - $.ajax({ - url: '/api/v1/users/login', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({name, email, password}), - success: function onSuccess(data, textStatus, xhr) { - track('api', 'api_users_login_success', data.team_id, 'email', data.email); - sessionStorage.removeItem(data.id + '_last_error'); - BrowserStore.signalLogin(); - success(data, textStatus, xhr); - }, - error: function onError(xhr, status, err) { - track('api', 'api_users_login_fail', name, 'email', email); - - var e = handleError('loginByEmail', xhr, status, err); - error(e); - } - }); -} - -export function loginByUsername(name, username, password, success, error) { - $.ajax({ - url: '/api/v1/users/login', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({name, username, password}), - success: function onSuccess(data, textStatus, xhr) { - track('api', 'api_users_login_success', data.team_id, 'username', data.username); - sessionStorage.removeItem(data.id + '_last_error'); - BrowserStore.signalLogin(); - success(data, textStatus, xhr); - }, - error: function onError(xhr, status, err) { - track('api', 'api_users_login_fail', name, 'username', username); - - var e = handleError('loginByUsername', xhr, status, err); - error(e); - } - }); -} - -export function loginByLdap(teamName, id, password, success, error) { - $.ajax({ - url: '/api/v1/users/login_ldap', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({teamName, id, password}), - success: function onSuccess(data, textStatus, xhr) { - track('api', 'api_users_loginLdap_success', data.team_id, 'id', id); - sessionStorage.removeItem(data.id + '_last_error'); - BrowserStore.signalLogin(); - success(data, textStatus, xhr); - }, - error: function onError(xhr, status, err) { - track('api', 'api_users_loginLdap_fail', teamName, 'id', id); - - var e = handleError('loginByLdap', xhr, status, err); - error(e); - } - }); -} - -export function revokeSession(altId, success, error) { - $.ajax({ - url: '/api/v1/users/revoke_session', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({id: altId}), - success, - error: function onError(xhr, status, err) { - var e = handleError('revokeSession', xhr, status, err); - error(e); - } - }); -} - -export function getSessions(userId, success, error) { - $.ajax({ - cache: false, - url: '/api/v1/users/' + userId + '/sessions', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getSessions', xhr, status, err); - error(e); - } - }); -} - -export function getAudits(userId, success, error) { - $.ajax({ - url: '/api/v1/users/' + userId + '/audits', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getAudits', xhr, status, err); - error(e); - } - }); -} - -export function getLogs(success, error) { - $.ajax({ - url: '/api/v1/admin/logs', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getLogs', xhr, status, err); - error(e); - } - }); -} - -export function getServerAudits(success, error) { - $.ajax({ - url: '/api/v1/admin/audits', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getServerAudits', xhr, status, err); - error(e); - } - }); -} - -export function getConfig(success, error) { - return $.ajax({ - url: '/api/v1/admin/config', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getConfig', xhr, status, err); - error(e); - } - }); -} - -export function getAnalytics(name, teamId, success, error) { - let url = '/api/v1/admin/analytics/'; - if (teamId == null) { - url += name; - } else { - url += teamId + '/' + name; - } - $.ajax({ - url, - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('getSystemAnalytics', xhr, status, err); - error(e); - } - }); -} - -export function getClientConfig(success, error) { - return $.ajax({ - url: '/api/v1/admin/client_props', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getClientConfig', xhr, status, err); - error(e); - } - }); -} - -export function getTeamAnalytics(teamId, name, success, error) { - $.ajax({ - url: '/api/v1/admin/analytics/' + teamId + '/' + name, - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('getTeamAnalytics', xhr, status, err); - error(e); - } - }); -} - -export function saveConfig(config, success, error) { - $.ajax({ - url: '/api/v1/admin/save_config', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(config), - success, - error: function onError(xhr, status, err) { - var e = handleError('saveConfig', xhr, status, err); - error(e); - } - }); -} - -export function logClientError(msg) { - var l = {}; - l.level = 'ERROR'; - l.message = msg; - - $.ajax({ - url: '/api/v1/admin/log_client', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(l) - }); -} - -export function testEmail(config, success, error) { - $.ajax({ - url: '/api/v1/admin/test_email', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(config), - success, - error: function onError(xhr, status, err) { - var e = handleError('testEmail', xhr, status, err); - error(e); - } - }); -} - -export function getAllTeams(success, error) { - $.ajax({ - url: '/api/v1/teams/all', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getAllTeams', xhr, status, err); - error(e); - } - }); -} - -export function getMeLoggedIn(success, error) { - return $.ajax({ - cache: false, - url: '/api/v1/users/me_logged_in', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getMeLoggedIn', xhr, status, err); - error(e); - } - }); -} - -export function getMe(success, error) { - var currentUser = null; - $.ajax({ - cache: false, - url: '/api/v1/users/me', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success: function gotUser(data, textStatus, xhr) { - currentUser = data; - if (success) { - success(data, textStatus, xhr); - } - }, - error: function onError(xhr, status, err) { - if (error) { - var e = handleError('getMe', xhr, status, err); - error(e); - } - } - }); - - return currentUser; -} - -export function inviteMembers(data, success, error) { - $.ajax({ - url: '/api/v1/teams/invite_members', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('inviteMembers', xhr, status, err); - error(e); - } - }); - - track('api', 'api_teams_invite_members'); -} - -export function updateTeam(team, success, error) { - $.ajax({ - url: '/api/v1/teams/update', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(team), - success, - error: (xhr, status, err) => { - var e = handleError('updateTeam', xhr, status, err); - error(e); - } - }); - - track('api', 'api_teams_update_name'); -} - -export function signupTeam(email, success, error) { - $.ajax({ - url: '/api/v1/teams/signup', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({email: email}), - success, - error: function onError(xhr, status, err) { - var e = handleError('singupTeam', xhr, status, err); - error(e); - } - }); - - track('api', 'api_teams_signup'); -} - -export function createTeam(team, success, error) { - $.ajax({ - url: '/api/v1/teams/create', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(team), - success, - error: function onError(xhr, status, err) { - var e = handleError('createTeam', xhr, status, err); - error(e); - } - }); -} - -export function findTeamByName(teamName, success, error) { - $.ajax({ - url: '/api/v1/teams/find_team_by_name', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({name: teamName}), - success, - error: function onError(xhr, status, err) { - var e = handleError('findTeamByName', xhr, status, err); - error(e); - } - }); -} - -export function createChannel(channel, success, error) { - $.ajax({ - url: '/api/v1/channels/create', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(channel), - success, - error: function onError(xhr, status, err) { - var e = handleError('createChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_create', channel.type, 'name', channel.name); -} - -export function createDirectChannel(channel, userId, success, error) { - $.ajax({ - url: '/api/v1/channels/create_direct', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({user_id: userId}), - success, - error: function onError(xhr, status, err) { - var e = handleError('createDirectChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_create_direct', channel.type, 'name', channel.name); -} - -export function updateChannel(channel, success, error) { - $.ajax({ - url: '/api/v1/channels/update', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(channel), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_update'); -} - -export function updateChannelHeader(channelId, header, success, error) { - const data = { - channel_id: channelId, - channel_header: header - }; - - $.ajax({ - url: '/api/v1/channels/update_header', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateChannelHeader', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_header'); -} - -export function updateChannelPurpose(data, success, error) { - $.ajax({ - url: '/api/v1/channels/update_purpose', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateChannelPurpose', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_purpose'); -} - -export function updateNotifyProps(data, success, error) { - $.ajax({ - url: '/api/v1/channels/update_notify_props', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('updateNotifyProps', xhr, status, err); - error(e); - } - }); -} - -export function joinChannel(id, success, error) { - $.ajax({ - url: '/api/v1/channels/' + id + '/join', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - success, - error: function onError(xhr, status, err) { - var e = handleError('joinChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_join'); -} - -export function leaveChannel(id, success, error) { - $.ajax({ - url: '/api/v1/channels/' + id + '/leave', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - success, - error: function onError(xhr, status, err) { - var e = handleError('leaveChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_leave'); -} - -export function deleteChannel(id, success, error) { - $.ajax({ - url: '/api/v1/channels/' + id + '/delete', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - success, - error: function onError(xhr, status, err) { - var e = handleError('deleteChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_delete'); -} - -export function updateLastViewedAt(channelId, success, error) { - $.ajax({ - url: '/api/v1/channels/' + channelId + '/update_last_viewed_at', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - success, - error: function onError(xhr, status, err) { - var e = handleError('updateLastViewedAt', xhr, status, err); - error(e); - } - }); -} - -export function getChannels(success, error) { - return $.ajax({ - cache: false, - url: '/api/v1/channels/', - dataType: 'json', - type: 'GET', - success, - ifModified: true, - error: function onError(xhr, status, err) { - var e = handleError('getChannels', xhr, status, err); - error(e); - } - }); -} - -export function getChannel(id, success, error) { - $.ajax({ - cache: false, - url: '/api/v1/channels/' + id + '/', - dataType: 'json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getChannel', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channel_get'); -} - -export function getMoreChannels(success, error) { - $.ajax({ - url: '/api/v1/channels/more', - dataType: 'json', - type: 'GET', - success, - ifModified: true, - error: function onError(xhr, status, err) { - var e = handleError('getMoreChannels', xhr, status, err); - error(e); - } - }); -} - -export function getChannelCounts(success, error) { - $.ajax({ - cache: false, - url: '/api/v1/channels/counts', - dataType: 'json', - type: 'GET', - success, - ifModified: true, - error: function onError(xhr, status, err) { - var e = handleError('getChannelCounts', xhr, status, err); - error(e); - } - }); -} - -export function getChannelExtraInfo(id, memberLimit, success, error) { - let url = '/api/v1/channels/' + id + '/extra_info'; - - if (memberLimit) { - url += '/' + memberLimit; - } - - return $.ajax({ - url, - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getChannelExtraInfo', xhr, status, err); - error(e); - } - }); -} - -export function executeCommand(channelId, command, suggest, success, error) { - $.ajax({ - url: '/api/v1/commands/execute', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify({channelId, command, suggest: '' + suggest}), - success, - error: function onError(xhr, status, err) { - var e = handleError('executeCommand', xhr, status, err); - error(e); - } - }); -} - -export function addCommand(cmd, success, error) { - $.ajax({ - url: '/api/v1/commands/create', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(cmd), - success, - error: (xhr, status, err) => { - var e = handleError('addCommand', xhr, status, err); - error(e); - } - }); -} - -export function deleteCommand(data, success, error) { - $.ajax({ - url: '/api/v1/commands/delete', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: (xhr, status, err) => { - var e = handleError('deleteCommand', xhr, status, err); - error(e); - } - }); -} - -export function listTeamCommands(success, error) { - $.ajax({ - url: '/api/v1/commands/list_team_commands', - dataType: 'json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('listTeamCommands', xhr, status, err); - error(e); - } - }); -} - -export function regenCommandToken(data, success, error) { - $.ajax({ - url: '/api/v1/commands/regen_token', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: (xhr, status, err) => { - var e = handleError('regenCommandToken', xhr, status, err); - error(e); - } - }); -} - -export function listCommands(success, error) { - $.ajax({ - url: '/api/v1/commands/list', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('listCommands', xhr, status, err); - error(e); - } - }); -} - -export function getPostsPage(channelId, offset, limit, success, error, complete) { - $.ajax({ - cache: false, - url: '/api/v1/channels/' + channelId + '/posts/' + offset + '/' + limit, - dataType: 'json', - type: 'GET', - ifModified: true, - success, - error: function onError(xhr, status, err) { - var e = handleError('getPosts', xhr, status, err); - error(e); - }, - complete: complete - }); -} - -export function getPosts(channelId, since, success, error, complete) { - return $.ajax({ - url: '/api/v1/channels/' + channelId + '/posts/' + since, - dataType: 'json', - type: 'GET', - ifModified: true, - success, - error: function onError(xhr, status, err) { - var e = handleError('getPosts', xhr, status, err); - error(e); - }, - complete: complete - }); -} - -export function getPostsBefore(channelId, post, offset, numPost, success, error, complete) { - $.ajax({ - url: '/api/v1/channels/' + channelId + '/post/' + post + '/before/' + offset + '/' + numPost, - dataType: 'json', - type: 'GET', - ifModified: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('getPostsBefore', xhr, status, err); - error(e); - }, - complete: complete - }); -} - -export function getPostsAfter(channelId, post, offset, numPost, success, error, complete) { - $.ajax({ - url: '/api/v1/channels/' + channelId + '/post/' + post + '/after/' + offset + '/' + numPost, - dataType: 'json', - type: 'GET', - ifModified: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('getPostsAfter', xhr, status, err); - error(e); - }, - complete: complete - }); -} - -export function getPost(channelId, postId, success, error, complete) { - $.ajax({ - cache: false, - url: '/api/v1/channels/' + channelId + '/post/' + postId, - dataType: 'json', - type: 'GET', - ifModified: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('getPost', xhr, status, err); - error(e); - }, - complete - }); -} - -export function getPostById(postId, success, error, complete) { - $.ajax({ - cache: false, - url: '/api/v1/posts/' + postId, - dataType: 'json', - type: 'GET', - ifModified: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('getPostById', xhr, status, err); - error(e); - }, - complete - }); -} - -export function search(terms, success, error) { - $.ajax({ - url: '/api/v1/posts/search', - dataType: 'json', - type: 'GET', - data: {terms: terms}, - success, - error: function onError(xhr, status, err) { - var e = handleError('search', xhr, status, err); - error(e); - } - }); - - track('api', 'api_posts_search'); -} - -export function deletePost(channelId, id, success, error) { - $.ajax({ - url: '/api/v1/channels/' + channelId + '/post/' + id + '/delete', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - success, - error: function onError(xhr, status, err) { - var e = handleError('deletePost', xhr, status, err); - error(e); - } - }); - - track('api', 'api_posts_delete'); -} - -export function createPost(post, channel, success, error) { - $.ajax({ - url: '/api/v1/channels/' + post.channel_id + '/create', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(post), - success, - error: function onError(xhr, status, err) { - var e = handleError('createPost', xhr, status, err); - error(e); - } - }); - - track('api', 'api_posts_create', channel.name, 'length', post.message.length); - - // global.window.analytics.track('api_posts_create', { - // category: 'api', - // channel_name: channel.name, - // channel_type: channel.type, - // length: post.message.length, - // files: (post.filenames || []).length, - // mentions: (post.message.match('/<mention>/g') || []).length - // }); -} - -export function updatePost(post, success, error) { - $.ajax({ - url: '/api/v1/channels/' + post.channel_id + '/update', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(post), - success, - error: function onError(xhr, status, err) { - var e = handleError('updatePost', xhr, status, err); - error(e); - } - }); - - track('api', 'api_posts_update'); -} - -export function addChannelMember(id, data, success, error) { - $.ajax({ - url: '/api/v1/channels/' + id + '/add', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('addChannelMember', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_add_member'); -} - -export function removeChannelMember(id, data, success, error) { - $.ajax({ - url: '/api/v1/channels/' + id + '/remove', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('removeChannelMember', xhr, status, err); - error(e); - } - }); - - track('api', 'api_channels_remove_member'); -} - -export function getProfiles(success, error) { - $.ajax({ - cache: false, - url: '/api/v1/users/profiles', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - ifModified: true, - error: function onError(xhr, status, err) { - var e = handleError('getProfiles', xhr, status, err); - error(e); - } - }); -} - -export function getProfilesForTeam(teamId, success, error) { - $.ajax({ - cache: false, - url: '/api/v1/users/profiles/' + teamId, - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getProfilesForTeam', xhr, status, err); - error(e); - } - }); -} - -export function uploadFile(formData, success, error) { - var request = $.ajax({ - url: '/api/v1/files/upload', - type: 'POST', - data: formData, - cache: false, - contentType: false, - processData: false, - success, - error: function onError(xhr, status, err) { - if (err !== 'abort') { - var e = handleError('uploadFile', xhr, status, err); - error(e); - } - } - }); - - track('api', 'api_files_upload'); - - return request; -} - -export function getFileInfo(filename, success, error) { - $.ajax({ - url: '/api/v1/files/get_info' + filename, - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success: (data) => { - success(data); - }, - error: function onError(xhr, status, err) { - var e = handleError('getFileInfo', xhr, status, err); - error(e); - } - }); -} - -export function getPublicLink(data, success, error) { - $.ajax({ - url: '/api/v1/files/get_public_link', - dataType: 'json', - type: 'POST', - data: JSON.stringify(data), - success, - error: function onError(xhr, status, err) { - var e = handleError('getPublicLink', xhr, status, err); - error(e); - } - }); -} - -export function uploadProfileImage(imageData, success, error) { - $.ajax({ - url: '/api/v1/users/newimage', - type: 'POST', - data: imageData, - cache: false, - contentType: false, - processData: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('uploadProfileImage', xhr, status, err); - error(e); - } - }); -} - -export function importSlack(fileData, success, error) { - $.ajax({ - url: '/api/v1/teams/import_team', - type: 'POST', - data: fileData, - cache: false, - contentType: false, - processData: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('importTeam', xhr, status, err); - error(e); - } - }); -} - -export function exportTeam(success, error) { - $.ajax({ - url: '/api/v1/teams/export_team', - type: 'GET', - dataType: 'json', - success, - error: function onError(xhr, status, err) { - var e = handleError('exportTeam', xhr, status, err); - error(e); - } - }); -} - -export function getStatuses(ids, success, error) { - $.ajax({ - url: '/api/v1/users/status', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(ids), - success, - error: function onError(xhr, status, err) { - var e = handleError('getStatuses', xhr, status, err); - error(e); - } - }); -} - -export function getMyTeam(success, error) { - return $.ajax({ - url: '/api/v1/teams/me', - dataType: 'json', - type: 'GET', - success, - ifModified: true, - error: function onError(xhr, status, err) { - var e = handleError('getMyTeam', xhr, status, err); - error(e); - } - }); -} - -export function registerOAuthApp(app, success, error) { - $.ajax({ - url: '/api/v1/oauth/register', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(app), - success: success, - error: (xhr, status, err) => { - const e = handleError('registerApp', xhr, status, err); - error(e); - } - }); - - module.exports.track('api', 'api_apps_register'); -} - -export function allowOAuth2(responseType, clientId, redirectUri, state, scope, success, error) { - $.ajax({ - url: '/api/v1/oauth/allow?response_type=' + responseType + '&client_id=' + clientId + '&redirect_uri=' + redirectUri + '&scope=' + scope + '&state=' + state, - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: (xhr, status, err) => { - const e = handleError('allowOAuth2', xhr, status, err); - error(e); - } - }); - - module.exports.track('api', 'api_users_allow_oauth2'); -} - -export function addIncomingHook(hook, success, error) { - $.ajax({ - url: '/api/v1/hooks/incoming/create', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(hook), - success, - error: (xhr, status, err) => { - var e = handleError('addIncomingHook', xhr, status, err); - error(e); - } - }); -} - -export function deleteIncomingHook(data, success, error) { - $.ajax({ - url: '/api/v1/hooks/incoming/delete', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: (xhr, status, err) => { - var e = handleError('deleteIncomingHook', xhr, status, err); - error(e); - } - }); -} - -export function listIncomingHooks(success, error) { - $.ajax({ - url: '/api/v1/hooks/incoming/list', - dataType: 'json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('listIncomingHooks', xhr, status, err); - error(e); - } - }); -} - -export function getAllPreferences(success, error) { - return $.ajax({ - url: '/api/v1/preferences/', - dataType: 'json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('getAllPreferences', xhr, status, err); - error(e); - } - }); -} - -export function getPreferenceCategory(category, success, error) { - $.ajax({ - url: `/api/v1/preferences/${category}`, - dataType: 'json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('getPreferenceCategory', xhr, status, err); - error(e); - } - }); -} - -export function savePreferences(preferences, success, error) { - $.ajax({ - url: '/api/v1/preferences/save', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(preferences), - success, - error: (xhr, status, err) => { - var e = handleError('savePreferences', xhr, status, err); - error(e); - } - }); -} - -export function addOutgoingHook(hook, success, error) { - $.ajax({ - url: '/api/v1/hooks/outgoing/create', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(hook), - success, - error: (xhr, status, err) => { - var e = handleError('addOutgoingHook', xhr, status, err); - error(e); - } - }); -} - -export function deleteOutgoingHook(data, success, error) { - $.ajax({ - url: '/api/v1/hooks/outgoing/delete', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: (xhr, status, err) => { - var e = handleError('deleteOutgoingHook', xhr, status, err); - error(e); - } - }); -} - -export function listOutgoingHooks(success, error) { - $.ajax({ - url: '/api/v1/hooks/outgoing/list', - dataType: 'json', - type: 'GET', - success, - error: (xhr, status, err) => { - var e = handleError('listOutgoingHooks', xhr, status, err); - error(e); - } - }); -} - -export function regenOutgoingHookToken(data, success, error) { - $.ajax({ - url: '/api/v1/hooks/outgoing/regen_token', - dataType: 'json', - contentType: 'application/json', - type: 'POST', - data: JSON.stringify(data), - success, - error: (xhr, status, err) => { - var e = handleError('regenOutgoingHookToken', xhr, status, err); - error(e); - } - }); -} - -export function uploadLicenseFile(formData, success, error) { - $.ajax({ - url: '/api/v1/license/add', - type: 'POST', - data: formData, - cache: false, - contentType: false, - processData: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('uploadLicenseFile', xhr, status, err); - error(e); - } - }); - - track('api', 'api_license_upload'); -} - -export function removeLicenseFile(success, error) { - $.ajax({ - url: '/api/v1/license/remove', - type: 'POST', - cache: false, - contentType: false, - processData: false, - success, - error: function onError(xhr, status, err) { - var e = handleError('removeLicenseFile', xhr, status, err); - error(e); - } - }); - - track('api', 'api_license_upload'); -} - -export function getClientLicenceConfig(success, error) { - return $.ajax({ - url: '/api/v1/license/client_config', - dataType: 'json', - contentType: 'application/json', - type: 'GET', - success, - error: function onError(xhr, status, err) { - var e = handleError('getClientLicenceConfig', xhr, status, err); - error(e); - } - }); -} - -export function getInviteInfo(success, error, id) { - $.ajax({ - url: '/api/v1/teams/get_invite_info', - type: 'POST', - dataType: 'json', - contentType: 'application/json', - data: JSON.stringify({invite_id: id}), - success, - error: function onError(xhr, status, err) { - var e = handleError('getInviteInfo', xhr, status, err); - if (error) { - error(e); - } - } - }); -} - -export function verifyEmail(success, error, uid, hid) { - $.ajax({ - url: '/api/v1/users/verify_email', - type: 'POST', - contentType: 'application/json', - dataType: 'text', - data: JSON.stringify({uid, hid}), - success, - error: function onError(xhr, status, err) { - var e = handleError('verifyEmail', xhr, status, err); - if (error) { - error(e); - } - } - }); -} - -export function resendVerification(success, error, teamName, email) { - $.ajax({ - url: '/api/v1/users/resend_verification', - type: 'POST', - contentType: 'application/json', - dataType: 'text', - data: JSON.stringify({team_name: teamName, email}), - success, - error: function onError(xhr, status, err) { - var e = handleError('resendVerification', xhr, status, err); - if (error) { - error(e); - } - } - }); -} diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx deleted file mode 100644 index 3de562b7b..000000000 --- a/web/react/utils/constants.jsx +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import keyMirror from 'keymirror'; - -export default { - ActionTypes: keyMirror({ - RECEIVED_ERROR: null, - - CLICK_CHANNEL: null, - CREATE_CHANNEL: null, - LEAVE_CHANNEL: null, - CREATE_POST: null, - POST_DELETED: null, - REMOVE_POST: null, - - RECEIVED_CHANNELS: null, - RECEIVED_CHANNEL: null, - RECEIVED_MORE_CHANNELS: null, - RECEIVED_CHANNEL_EXTRA_INFO: null, - - FOCUS_POST: null, - RECEIVED_POSTS: null, - RECEIVED_FOCUSED_POST: null, - RECEIVED_POST: null, - RECEIVED_EDIT_POST: null, - RECEIVED_SEARCH: null, - RECEIVED_SEARCH_TERM: null, - RECEIVED_POST_SELECTED: null, - RECEIVED_MENTION_DATA: null, - RECEIVED_ADD_MENTION: null, - - RECEIVED_PROFILES: null, - RECEIVED_ME: null, - RECEIVED_SESSIONS: null, - RECEIVED_AUDITS: null, - RECEIVED_TEAMS: null, - RECEIVED_STATUSES: null, - RECEIVED_PREFERENCE: null, - RECEIVED_PREFERENCES: null, - RECEIVED_FILE_INFO: null, - - RECEIVED_MSG: null, - - RECEIVED_MY_TEAM: null, - - RECEIVED_CONFIG: null, - RECEIVED_LOGS: null, - RECEIVED_SERVER_AUDITS: null, - RECEIVED_ALL_TEAMS: null, - - RECEIVED_LOCALE: null, - - SHOW_SEARCH: null, - - TOGGLE_IMPORT_THEME_MODAL: null, - TOGGLE_INVITE_MEMBER_MODAL: null, - TOGGLE_DELETE_POST_MODAL: null, - TOGGLE_GET_POST_LINK_MODAL: null, - TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null, - TOGGLE_REGISTER_APP_MODAL: null, - - SUGGESTION_PRETEXT_CHANGED: null, - SUGGESTION_RECEIVED_SUGGESTIONS: null, - SUGGESTION_CLEAR_SUGGESTIONS: null, - SUGGESTION_COMPLETE_WORD: null, - SUGGESTION_SELECT_NEXT: null, - SUGGESTION_SELECT_PREVIOUS: null - }), - - PayloadSources: keyMirror({ - SERVER_ACTION: null, - VIEW_ACTION: null - }), - - StatTypes: keyMirror({ - TOTAL_USERS: null, - TOTAL_PUBLIC_CHANNELS: null, - TOTAL_PRIVATE_GROUPS: null, - TOTAL_POSTS: null, - TOTAL_TEAMS: null, - TOTAL_FILE_POSTS: null, - TOTAL_HASHTAG_POSTS: null, - TOTAL_IHOOKS: null, - TOTAL_OHOOKS: null, - TOTAL_COMMANDS: null, - TOTAL_SESSIONS: null, - POST_PER_DAY: null, - USERS_WITH_POSTS_PER_DAY: null, - RECENTLY_ACTIVE_USERS: null, - NEWLY_CREATED_USERS: null - }), - STAT_MAX_ACTIVE_USERS: 20, - STAT_MAX_NEW_USERS: 20, - - SocketEvents: { - POSTED: 'posted', - POST_EDITED: 'post_edited', - POST_DELETED: 'post_deleted', - CHANNEL_VIEWED: 'channel_viewed', - NEW_USER: 'new_user', - USER_ADDED: 'user_added', - USER_REMOVED: 'user_removed', - TYPING: 'typing', - PREFERENCE_CHANGED: 'preference_changed', - EPHEMERAL_MESSAGE: 'ephemeral_message' - }, - - //SPECIAL_MENTIONS: ['all', 'channel'], - SPECIAL_MENTIONS: ['channel'], - CHARACTER_LIMIT: 4000, - IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png', 'jpeg'], - AUDIO_TYPES: ['mp3', 'wav', 'wma', 'm4a', 'flac', 'aac', 'ogg'], - VIDEO_TYPES: ['mp4', 'avi', 'webm', 'mkv', 'wmv', 'mpg', 'mov', 'flv'], - PRESENTATION_TYPES: ['ppt', 'pptx'], - SPREADSHEET_TYPES: ['xlsx', 'csv'], - WORD_TYPES: ['doc', 'docx'], - CODE_TYPES: ['css', 'html', 'js', 'php', 'rb'], - PDF_TYPES: ['pdf'], - PATCH_TYPES: ['patch'], - ICON_FROM_TYPE: { - audio: 'audio', - video: 'video', - spreadsheet: 'excel', - presentation: 'ppt', - pdf: 'pdf', - code: 'code', - word: 'word', - patch: 'patch', - other: 'generic' - }, - MAX_DISPLAY_FILES: 5, - MAX_UPLOAD_FILES: 5, - MAX_FILE_SIZE: 50000000, // 50 MB - THUMBNAIL_WIDTH: 128, - THUMBNAIL_HEIGHT: 100, - WEB_VIDEO_WIDTH: 640, - WEB_VIDEO_HEIGHT: 480, - MOBILE_VIDEO_WIDTH: 480, - MOBILE_VIDEO_HEIGHT: 360, - DEFAULT_CHANNEL: 'town-square', - OFFTOPIC_CHANNEL: 'off-topic', - GITLAB_SERVICE: 'gitlab', - GOOGLE_SERVICE: 'google', - EMAIL_SERVICE: 'email', - SIGNIN_CHANGE: 'signin_change', - SIGNIN_VERIFIED: 'verified', - SESSION_EXPIRED: 'expired', - POST_CHUNK_SIZE: 60, - MAX_POST_CHUNKS: 3, - POST_FOCUS_CONTEXT_RADIUS: 10, - POST_LOADING: 'loading', - POST_FAILED: 'failed', - POST_DELETED: 'deleted', - POST_TYPE_EPHEMERAL: 'system_ephemeral', - POST_TYPE_JOIN_LEAVE: 'system_join_leave', - SYSTEM_MESSAGE_PREFIX: 'system_', - SYSTEM_MESSAGE_PROFILE_NAME: 'System', - SYSTEM_MESSAGE_PROFILE_IMAGE: '/static/images/logo_compact.png', - RESERVED_TEAM_NAMES: [ - 'www', - 'web', - 'admin', - 'support', - 'notify', - 'test', - 'demo', - 'mail', - 'team', - 'channel', - 'internal', - 'localhost', - 'dockerhost', - 'stag', - 'post', - 'cluster', - 'api' - ], - RESERVED_USERNAMES: [ - 'valet', - 'all', - 'channel' - ], - MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], - MAX_DMS: 20, - MAX_CHANNEL_POPOVER_COUNT: 100, - DM_CHANNEL: 'D', - OPEN_CHANNEL: 'O', - PRIVATE_CHANNEL: 'P', - INVITE_TEAM: 'I', - OPEN_TEAM: 'O', - MAX_POST_LEN: 4000, - EMOJI_SIZE: 16, - ONLINE_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-243 245 12 12'style='enable-background:new -243 245 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <path class='online--icon' d='M-236,250.5C-236,250.5-236,250.5-236,250.5C-236,250.5-236,250.5-236,250.5C-236,250.5-236,250.5-236,250.5z'/> <ellipse class='online--icon' cx='-238.5' cy='248' rx='2.5' ry='2.5'/> </g> <path class='online--icon' d='M-238.9,253.8c0-0.4,0.1-0.9,0.2-1.3c-2.2-0.2-2.2-2-2.2-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5c0,0.1-0.1,0.5,0,0.6 c0.2,1.3,2.2,2.3,4.4,2.4c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0C-238.7,255.7-238.9,254.8-238.9,253.8z'/> <g> <g> <path class='online--icon' d='M-232.3,250.1l1.3,1.3c0,0,0,0.1,0,0.1l-4.1,4.1c0,0,0,0-0.1,0c0,0,0,0,0,0l-2.7-2.7c0,0,0-0.1,0-0.1l1.2-1.2 c0,0,0.1,0,0.1,0l1.4,1.4l2.9-2.9C-232.4,250.1-232.3,250.1-232.3,250.1z'/> </g> </g> </svg>", - AWAY_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-299 391 12 12'style='enable-background:new -299 391 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <ellipse class='away--icon' cx='-294.6' cy='394' rx='2.5' ry='2.5'/> <path class='away--icon' d='M-293.8,399.4c0-0.4,0.1-0.7,0.2-1c-0.3,0.1-0.6,0.2-1,0.2c-2.5,0-2.5-2-2.5-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5 c0,0.1-0.1,0.5,0,0.6c0.2,1.3,2.2,2.3,4.4,2.4c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0.7,0,1.4-0.1,2-0.3 C-293.3,401.5-293.8,400.5-293.8,399.4z'/> </g> <path class='away--icon' d='M-287,400c0,0.1-0.1,0.1-0.1,0.1l-4.9,0c-0.1,0-0.1-0.1-0.1-0.1v-1.6c0-0.1,0.1-0.1,0.1-0.1l4.9,0c0.1,0,0.1,0.1,0.1,0.1 V400z'/> </svg>", - OFFLINE_ICON_SVG: "<svg version='1.1'id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:cc='http://creativecommons.org/ns#' inkscape:version='0.48.4 r9939' sodipodi:docname='TRASH_1_4.svg'xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='-299 391 12 12'style='enable-background:new -299 391 12 12;' xml:space='preserve'> <sodipodi:namedview inkscape:cx='26.358185' inkscape:zoom='1.18' bordercolor='#666666' pagecolor='#ffffff' borderopacity='1' objecttolerance='10' inkscape:cy='139.7898' gridtolerance='10' guidetolerance='10' showgrid='false' showguides='true' id='namedview6' inkscape:pageopacity='0' inkscape:pageshadow='2' inkscape:guide-bbox='true' inkscape:window-width='1366' inkscape:current-layer='Layer_1' inkscape:window-height='705' inkscape:window-y='-8' inkscape:window-maximized='1' inkscape:window-x='-8'> <sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide> <sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide> </sodipodi:namedview> <g> <g> <ellipse class='offline--icon' cx='-294.5' cy='394' rx='2.5' ry='2.5'/> <path class='offline--icon' d='M-294.3,399.7c0-0.4,0.1-0.8,0.2-1.2c-0.1,0-0.2,0-0.4,0c-2.5,0-2.5-2-2.5-2s-1,0.1-1.2,0.5c-0.4,0.6-0.6,1.7-0.7,2.5 c0,0.1-0.1,0.5,0,0.6c0.2,1.3,2.2,2.3,4.4,2.4h0.1h0.1c0.3,0,0.7,0,1-0.1C-293.9,401.6-294.3,400.7-294.3,399.7z'/> </g> </g> <g> <path class='offline--icon' d='M-288.9,399.4l1.8-1.8c0.1-0.1,0.1-0.3,0-0.3l-0.7-0.7c-0.1-0.1-0.3-0.1-0.3,0l-1.8,1.8l-1.8-1.8c-0.1-0.1-0.3-0.1-0.3,0 l-0.7,0.7c-0.1,0.1-0.1,0.3,0,0.3l1.8,1.8l-1.8,1.8c-0.1,0.1-0.1,0.3,0,0.3l0.7,0.7c0.1,0.1,0.3,0.1,0.3,0l1.8-1.8l1.8,1.8 c0.1,0.1,0.3,0.1,0.3,0l0.7-0.7c0.1-0.1,0.1-0.3,0-0.3L-288.9,399.4z'/> </g> </svg>", - MENU_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>", - COMMENT_ICON: "<svg version='1.1' id='Layer_2' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='15px' height='15px' viewBox='1 1.5 15 15' enable-background='new 1 1.5 15 15' xml:space='preserve'> <g> <g> <path fill='#211B1B' d='M14,1.5H3c-1.104,0-2,0.896-2,2v8c0,1.104,0.896,2,2,2h1.628l1.884,3l1.866-3H14c1.104,0,2-0.896,2-2v-8 C16,2.396,15.104,1.5,14,1.5z M15,11.5c0,0.553-0.447,1-1,1H8l-1.493,2l-1.504-1.991L5,12.5H3c-0.552,0-1-0.447-1-1v-8 c0-0.552,0.448-1,1-1h11c0.553,0,1,0.448,1,1V11.5z'/> </g> </g> </svg>", - REPLY_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'viewBox='-158 242 18 18' style='enable-background:new -158 242 18 18;' xml:space='preserve'> <path d='M-142.2,252.6c-2-3-4.8-4.7-8.3-4.8v-3.3c0-0.2-0.1-0.3-0.2-0.3s-0.3,0-0.4,0.1l-6.9,6.2c-0.1,0.1-0.1,0.2-0.1,0.3 c0,0.1,0,0.2,0.1,0.3l6.9,6.4c0.1,0.1,0.3,0.1,0.4,0.1c0.1-0.1,0.2-0.2,0.2-0.4v-3.8c4.2,0,7.4,0.4,9.6,4.4c0.1,0.1,0.2,0.2,0.3,0.2 c0,0,0.1,0,0.1,0c0.2-0.1,0.3-0.3,0.2-0.4C-140.2,257.3-140.6,255-142.2,252.6z M-150.8,252.5c-0.2,0-0.4,0.2-0.4,0.4v3.3l-6-5.5 l6-5.3v2.8c0,0.2,0.2,0.4,0.4,0.4c3.3,0,6,1.5,8,4.5c0.5,0.8,0.9,1.6,1.2,2.3C-144,252.8-147.1,252.5-150.8,252.5z'/> </svg>", - SCROLL_BOTTOM_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'viewBox='-239 239 21 23' style='enable-background:new -239 239 21 23;' xml:space='preserve'> <path d='M-239,241.4l2.4-2.4l8.1,8.2l8.1-8.2l2.4,2.4l-10.5,10.6L-239,241.4z M-228.5,257.2l8.1-8.2l2.4,2.4l-10.5,10.6l-10.5-10.6 l2.4-2.4L-228.5,257.2z'/> </svg>", - UPDATE_TYPING_MS: 5000, - THEMES: { - default: { - type: 'Organization', - sidebarBg: '#2071a7', - sidebarText: '#fff', - sidebarUnreadText: '#fff', - sidebarTextHoverBg: '#136197', - sidebarTextActiveBorder: '#7AB0D6', - sidebarTextActiveColor: '#FFFFFF', - sidebarHeaderBg: '#2f81b7', - sidebarHeaderTextColor: '#FFFFFF', - onlineIndicator: '#7DBE00', - awayIndicator: '#DCBD4E', - mentionBj: '#FBFBFB', - mentionColor: '#2071A7', - centerChannelBg: '#f2f4f8', - centerChannelColor: '#333333', - newMessageSeparator: '#FF8800', - linkColor: '#2f81b7', - buttonBg: '#1dacfc', - buttonColor: '#FFFFFF', - mentionHighlightBg: '#fff2bb', - mentionHighlightLink: '#2f81b7', - codeTheme: 'github' - }, - mattermost: { - type: 'Mattermost', - sidebarBg: '#fafafa', - sidebarText: '#333333', - sidebarUnreadText: '#333333', - sidebarTextHoverBg: '#e6f2fa', - sidebarTextActiveBorder: '#378FD2', - sidebarTextActiveColor: '#111111', - sidebarHeaderBg: '#2389d7', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#7DBE00', - awayIndicator: '#DCBD4E', - mentionBj: '#2389d7', - mentionColor: '#ffffff', - centerChannelBg: '#ffffff', - centerChannelColor: '#333333', - newMessageSeparator: '#FF8800', - linkColor: '#2389d7', - buttonBg: '#2389d7', - buttonColor: '#FFFFFF', - mentionHighlightBg: '#fff2bb', - mentionHighlightLink: '#2f81b7', - codeTheme: 'github' - }, - mattermostDark: { - type: 'Mattermost Dark', - sidebarBg: '#1B2C3E', - sidebarText: '#fff', - sidebarUnreadText: '#fff', - sidebarTextHoverBg: '#4A5664', - sidebarTextActiveBorder: '#39769C', - sidebarTextActiveColor: '#FFFFFF', - sidebarHeaderBg: '#1B2C3E', - sidebarHeaderTextColor: '#FFFFFF', - onlineIndicator: '#55C5B2', - awayIndicator: '#A9A14C', - mentionBj: '#B74A4A', - mentionColor: '#FFFFFF', - centerChannelBg: '#2F3E4E', - centerChannelColor: '#DDDDDD', - newMessageSeparator: '#5de5da', - linkColor: '#A4FFEB', - buttonBg: '#4CBBA4', - buttonColor: '#FFFFFF', - mentionHighlightBg: '#984063', - mentionHighlightLink: '#A4FFEB', - codeTheme: 'solarized-dark' - }, - windows10: { - type: 'Windows Dark', - sidebarBg: '#171717', - sidebarText: '#fff', - sidebarUnreadText: '#fff', - sidebarTextHoverBg: '#302e30', - sidebarTextActiveBorder: '#196CAF', - sidebarTextActiveColor: '#FFFFFF', - sidebarHeaderBg: '#1f1f1f', - sidebarHeaderTextColor: '#FFFFFF', - onlineIndicator: '#0177e7', - awayIndicator: '#A9A14C', - mentionBj: '#0177e7', - mentionColor: '#FFFFFF', - centerChannelBg: '#1F1F1F', - centerChannelColor: '#DDDDDD', - newMessageSeparator: '#CC992D', - linkColor: '#0D93FF', - buttonBg: '#0177e7', - buttonColor: '#FFFFFF', - mentionHighlightBg: '#784098', - mentionHighlightLink: '#A4FFEB', - codeTheme: 'monokai' - } - }, - THEME_ELEMENTS: [ - { - group: 'sidebarElements', - id: 'sidebarBg', - uiName: 'Sidebar BG' - }, - { - group: 'sidebarElements', - id: 'sidebarText', - uiName: 'Sidebar Text' - }, - { - group: 'sidebarElements', - id: 'sidebarHeaderBg', - uiName: 'Sidebar Header BG' - }, - { - group: 'sidebarElements', - id: 'sidebarHeaderTextColor', - uiName: 'Sidebar Header Text' - }, - { - group: 'sidebarElements', - id: 'sidebarUnreadText', - uiName: 'Sidebar Unread Text' - }, - { - group: 'sidebarElements', - id: 'sidebarTextHoverBg', - uiName: 'Sidebar Text Hover BG' - }, - { - group: 'sidebarElements', - id: 'sidebarTextActiveBorder', - uiName: 'Sidebar Text Active Border' - }, - { - group: 'sidebarElements', - id: 'sidebarTextActiveColor', - uiName: 'Sidebar Text Active Color' - }, - { - group: 'sidebarElements', - id: 'onlineIndicator', - uiName: 'Online Indicator' - }, - { - group: 'sidebarElements', - id: 'awayIndicator', - uiName: 'Away Indicator' - }, - { - group: 'sidebarElements', - id: 'mentionBj', - uiName: 'Mention Jewel BG' - }, - { - group: 'sidebarElements', - id: 'mentionColor', - uiName: 'Mention Jewel Text' - }, - { - group: 'centerChannelElements', - id: 'centerChannelBg', - uiName: 'Center Channel BG' - }, - { - group: 'centerChannelElements', - id: 'centerChannelColor', - uiName: 'Center Channel Text' - }, - { - group: 'centerChannelElements', - id: 'newMessageSeparator', - uiName: 'New Message Separator' - }, - { - group: 'centerChannelElements', - id: 'mentionHighlightBg', - uiName: 'Mention Highlight BG' - }, - { - group: 'centerChannelElements', - id: 'mentionHighlightLink', - uiName: 'Mention Highlight Link' - }, - { - group: 'centerChannelElements', - id: 'codeTheme', - uiName: 'Code Theme', - themes: [ - { - id: 'solarized-dark', - uiName: 'Solarized Dark' - }, - { - id: 'solarized-light', - uiName: 'Solarized Light' - }, - { - id: 'github', - uiName: 'GitHub' - }, - { - id: 'monokai', - uiName: 'Monokai' - } - ] - }, - { - group: 'linkAndButtonElements', - id: 'linkColor', - uiName: 'Link Color' - }, - { - group: 'linkAndButtonElements', - id: 'buttonBg', - uiName: 'Button BG' - }, - { - group: 'linkAndButtonElements', - id: 'buttonColor', - uiName: 'Button Text' - } - ], - DEFAULT_CODE_THEME: 'github', - FONTS: { - 'Droid Serif': 'font--droid_serif', - 'Roboto Slab': 'font--roboto_slab', - Lora: 'font--lora', - Arvo: 'font--arvo', - 'Open Sans': 'font--open_sans', - Roboto: 'font--roboto', - 'PT Sans': 'font--pt_sans', - Lato: 'font--lato', - 'Source Sans Pro': 'font--source_sans_pro', - 'Exo 2': 'font--exo_2', - Ubuntu: 'font--ubuntu' - }, - DEFAULT_FONT: 'Open Sans', - Preferences: { - CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show', - CATEGORY_DISPLAY_SETTINGS: 'display_settings', - DISPLAY_PREFER_NICKNAME: 'nickname_full_name', - DISPLAY_PREFER_FULL_NAME: 'full_name', - CATEGORY_ADVANCED_SETTINGS: 'advanced_settings', - TUTORIAL_STEP: 'tutorial_step' - }, - TutorialSteps: { - INTRO_SCREENS: 0, - POST_POPOVER: 1, - CHANNEL_POPOVER: 2, - MENU_POPOVER: 3 - }, - KeyCodes: { - UP: 38, - DOWN: 40, - LEFT: 37, - RIGHT: 39, - BACKSPACE: 8, - ENTER: 13, - ESCAPE: 27, - SPACE: 32, - TAB: 9 - }, - HighlightedLanguages: { - diff: 'Diff', - apache: 'Apache', - makefile: 'Makefile', - http: 'HTTP', - json: 'JSON', - markdown: 'Markdown', - javascript: 'JavaScript', - css: 'CSS', - nginx: 'nginx', - objectivec: 'Objective-C', - python: 'Python', - xml: 'XML', - perl: 'Perl', - bash: 'Bash', - php: 'PHP', - coffeescript: 'CoffeeScript', - cs: 'C#', - cpp: 'C++', - sql: 'SQL', - go: 'Go', - ruby: 'Ruby', - java: 'Java', - ini: 'ini' - }, - PostsViewJumpTypes: { - BOTTOM: 1, - POST: 2, - SIDEBAR_OPEN: 3 - }, - NotificationPrefs: { - MENTION: 'mention' - }, - FeatureTogglePrefix: 'feature_enabled_', - PRE_RELEASE_FEATURES: { - MARKDOWN_PREVIEW: { - label: 'markdown_preview', // github issue: https://github.com/mattermost/platform/pull/1389 - description: 'Show markdown preview option in message input box' - }, - EMBED_PREVIEW: { - label: 'embed_preview', - description: 'Show preview snippet of links below message' - }, - EMBED_TOGGLE: { - label: 'embed_toggle', - description: 'Show toggle for all embed previews' - } - }, - OVERLAY_TIME_DELAY: 400, - MIN_USERNAME_LENGTH: 3, - MAX_USERNAME_LENGTH: 64, - MIN_PASSWORD_LENGTH: 5, - MAX_PASSWORD_LENGTH: 50, - TIME_SINCE_UPDATE_INTERVAL: 30000, - MIN_HASHTAG_LINK_LENGTH: 3 -}; diff --git a/web/react/utils/delayed_action.jsx b/web/react/utils/delayed_action.jsx deleted file mode 100644 index 4f6239ad0..000000000 --- a/web/react/utils/delayed_action.jsx +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -export default class DelayedAction { - constructor(action) { - this.action = action; - - this.timer = -1; - - // bind fire since it doesn't get passed the correct this value with setTimeout - this.fire = this.fire.bind(this); - } - - fire() { - this.action(); - - this.timer = -1; - } - - fireAfter(timeout) { - if (this.timer >= 0) { - window.clearTimeout(this.timer); - } - - this.timer = window.setTimeout(this.fire, timeout); - } -} diff --git a/web/react/utils/emoticons.jsx b/web/react/utils/emoticons.jsx deleted file mode 100644 index bed798b3e..000000000 --- a/web/react/utils/emoticons.jsx +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -const emoticonPatterns = { - slightly_smiling_face: /(^|\s)(:-?\))(?=$|\s)/g, // :) - wink: /(^|\s)(;-?\))(?=$|\s)/g, // ;) - open_mouth: /(^|\s)(:o)(?=$|\s)/gi, // :o - scream: /(^|\s)(:-o)(?=$|\s)/gi, // :-o - smirk: /(^|\s)(:-?])(?=$|\s)/g, // :] - smile: /(^|\s)(:-?d)(?=$|\s)/gi, // :D - stuck_out_tongue_closed_eyes: /(^|\s)(x-d)(?=$|\s)/gi, // x-d - stuck_out_tongue: /(^|\s)(:-?p)(?=$|\s)/gi, // :p - rage: /(^|\s)(:-?[\[@])(?=$|\s)/g, // :@ - slightly_frowning_face: /(^|\s)(:-?\()(?=$|\s)/g, // :( - cry: /(^|\s)(:['ā]-?\(|:'\(|:'\()(?=$|\s)/g, // :`( - confused: /(^|\s)(:-?\/)(?=$|\s)/g, // :/ - confounded: /(^|\s)(:-?s)(?=$|\s)/gi, // :s - neutral_face: /(^|\s)(:-?\|)(?=$|\s)/g, // :| - flushed: /(^|\s)(:-?\$)(?=$|\s)/g, // :$ - mask: /(^|\s)(:-x)(?=$|\s)/gi, // :-x - heart: /(^|\s)(<3|<3)(?=$|\s)/g, // <3 - broken_heart: /(^|\s)(<\/3|</3)(?=$|\s)/g, // </3 - thumbsup: /(^|\s)(:\+1:)(?=$|\s)/g, // :+1: - thumbsdown: /(^|\s)(:\-1:)(?=$|\s)/g // :-1: -}; - -function initializeEmoticonMap() { - const emoticonNames = - ('+1,-1,100,1234,8ball,a,ab,abc,abcd,accept,aerial_tramway,airplane,alarm_clock,alien,ambulance,anchor,angel,' + - 'anger,angry,anguished,ant,apple,aquarius,aries,arrow_backward,arrow_double_down,arrow_double_up,arrow_down,' + - 'arrow_down_small,arrow_forward,arrow_heading_down,arrow_heading_up,arrow_left,arrow_lower_left,' + - 'arrow_lower_right,arrow_right,arrow_right_hook,arrow_up,arrow_up_down,arrow_up_small,arrow_upper_left,' + - 'arrow_upper_right,arrows_clockwise,arrows_counterclockwise,art,articulated_lorry,astonished,atm,b,baby,' + - 'baby_bottle,baby_chick,baby_symbol,back,baggage_claim,balloon,ballot_box_with_check,bamboo,banana,bangbang,' + - 'bank,bar_chart,barber,baseball,basketball,bath,bathtub,battery,bear,bee,beer,beers,beetle,beginner,bell,bento,' + - 'bicyclist,bike,bikini,bird,birthday,black_circle,black_joker,black_medium_small_square,black_medium_square,' + - 'black_large_square,black_nib,black_small_square,black_square,black_square_button,blossom,blowfish,blue_book,' + - 'blue_car,blue_heart,blush,boar,boat,bomb,book,bookmark,bookmark_tabs,books,boom,boot,bouquet,bow,bowling,bowtie,' + - 'boy,bread,bride_with_veil,bridge_at_night,briefcase,broken_heart,bug,bulb,bullettrain_front,bullettrain_side,bus,' + - 'busstop,bust_in_silhouette,busts_in_silhouette,cactus,cake,calendar,calling,camel,camera,cancer,candy,capital_abcd,' + - 'capricorn,car,card_index,carousel_horse,cat,cat2,cd,chart,chart_with_downwards_trend,chart_with_upwards_trend,' + - 'checkered_flag,cherries,cherry_blossom,chestnut,chicken,children_crossing,chocolate_bar,christmas_tree,church,' + - 'cinema,circus_tent,city_sunrise,city_sunset,cl,clap,clapper,clipboard,clock1,clock10,clock1030,clock11,' + - 'clock1130,clock12,clock1230,clock130,clock2,clock230,clock3,clock330,clock4,clock430,clock5,clock530,clock6,' + - 'clock630,clock7,clock730,clock8,clock830,clock9,clock930,closed_book,closed_lock_with_key,closed_umbrella,cloud,' + - 'clubs,cn,cocktail,coffee,cold_sweat,collision,computer,confetti_ball,confounded,confused,congratulations,' + - 'construction,construction_worker,convenience_store,cookie,cool,cop,copyright,corn,couple,couple_with_heart,' + - 'couplekiss,cow,cow2,credit_card,crescent_moon,crocodile,crossed_flags,crown,cry,crying_cat_face,crystal_ball,' + - 'cupid,curly_loop,currency_exchange,curry,custard,customs,cyclone,dancer,dancers,dango,dart,dash,date,de,' + - 'deciduous_tree,department_store,diamond_shape_with_a_dot_inside,diamonds,disappointed,disappointed_relieved,' + - 'dizzy,dizzy_face,do_not_litter,dog,dog2,dollar,dolls,dolphin,donut,door,doughnut,dragon,dragon_face,dress,' + - 'dromedary_camel,droplet,dvd,e-mail,ear,ear_of_rice,earth_africa,earth_americas,earth_asia,egg,eggplant,eight,' + - 'eight_pointed_black_star,eight_spoked_asterisk,electric_plug,elephant,email,end,envelope,es,euro,' + - 'european_castle,european_post_office,evergreen_tree,exclamation,expressionless,eyeglasses,eyes,facepunch,' + - 'factory,fallen_leaf,family,fast_forward,fax,fearful,feelsgood,feet,ferris_wheel,file_folder,finnadie,fire,' + - 'fire_engine,fireworks,first_quarter_moon,first_quarter_moon_with_face,fish,fish_cake,fishing_pole_and_fish,fist,' + - 'five,flags,flashlight,floppy_disk,flower_playing_cards,flushed,foggy,football,fork_and_knife,fountain,four,' + - 'four_leaf_clover,fr,free,fried_shrimp,fries,frog,frowning,fu,fuelpump,full_moon,full_moon_with_face,game_die,gb,' + - 'gem,gemini,ghost,gift,gift_heart,girl,globe_with_meridians,goat,goberserk,godmode,golf,grapes,green_apple,' + - 'green_book,green_heart,grey_exclamation,grey_question,grimacing,grin,grinning,guardsman,guitar,gun,haircut,' + - 'hamburger,hammer,hamster,hand,handbag,hankey,hash,hatched_chick,hatching_chick,headphones,hear_no_evil,heart,' + - 'heart_decoration,heart_eyes,heart_eyes_cat,heartbeat,heartpulse,hearts,heavy_check_mark,heavy_division_sign,' + - 'heavy_dollar_sign,heavy_exclamation_mark,heavy_minus_sign,heavy_multiplication_x,heavy_plus_sign,helicopter,' + - 'herb,hibiscus,high_brightness,high_heel,hocho,honey_pot,honeybee,horse,horse_racing,hospital,hotel,hotsprings,' + - 'hourglass,hourglass_flowing_sand,house,house_with_garden,hurtrealbad,hushed,ice_cream,icecream,id,' + - 'ideograph_advantage,imp,inbox_tray,incoming_envelope,information_desk_person,information_source,innocent,' + - 'interrobang,iphone,it,izakaya_lantern,jack_o_lantern,japan,japanese_castle,japanese_goblin,japanese_ogre,jeans,' + - 'joy,joy_cat,jp,key,keycap_ten,kimono,kiss,kissing,kissing_cat,kissing_closed_eyes,kissing_face,kissing_heart,' + - 'kissing_smiling_eyes,koala,koko,kr,large_blue_circle,large_blue_diamond,large_orange_diamond,last_quarter_moon,' + - 'last_quarter_moon_with_face,laughing,leaves,ledger,left_luggage,left_right_arrow,leftwards_arrow_with_hook,' + - 'lemon,leo,leopard,libra,light_rail,link,lips,lipstick,lock,lock_with_ink_pen,lollipop,loop,loudspeaker,' + - 'love_hotel,love_letter,low_brightness,m,mag,mag_right,mahjong,mailbox,mailbox_closed,mailbox_with_mail,' + - 'mailbox_with_no_mail,man,man_with_gua_pi_mao,man_with_turban,mans_shoe,maple_leaf,mask,massage,meat_on_bone,' + - 'mega,melon,memo,mens,metal,metro,microphone,microscope,milky_way,minibus,minidisc,mobile_phone_off,' + - 'money_with_wings,moneybag,monkey,monkey_face,monorail,mortar_board,mount_fuji,mountain_bicyclist,' + - 'mountain_cableway,mountain_railway,mouse,mouse2,movie_camera,moyai,muscle,mushroom,musical_keyboard,' + - 'musical_note,musical_score,mute,nail_care,name_badge,neckbeard,necktie,negative_squared_cross_mark,' + - 'neutral_face,new,new_moon,new_moon_with_face,newspaper,ng,nine,no_bell,no_bicycles,no_entry,no_entry_sign,' + - 'no_good,no_mobile_phones,no_mouth,no_pedestrians,no_smoking,non-potable_water,nose,notebook,' + - 'notebook_with_decorative_cover,notes,nut_and_bolt,o,o2,ocean,octocat,octopus,oden,office,ok,ok_hand,' + - 'ok_woman,older_man,older_woman,on,oncoming_automobile,oncoming_bus,oncoming_police_car,oncoming_taxi,one,' + - 'open_file_folder,open_hands,open_mouth,ophiuchus,orange_book,outbox_tray,ox,package,page_facing_up,' + - 'page_with_curl,pager,palm_tree,panda_face,paperclip,parking,part_alternation_mark,partly_sunny,' + - 'passport_control,paw_prints,peach,pear,pencil,pencil2,penguin,pensive,performing_arts,persevere,' + - 'person_frowning,person_with_blond_hair,person_with_pouting_face,phone,pig,pig2,pig_nose,pill,pineapple,pisces,' + - 'pizza,plus1,point_down,point_left,point_right,point_up,point_up_2,police_car,poodle,poop,post_office,' + - 'postal_horn,postbox,potable_water,pouch,poultry_leg,pound,pouting_cat,pray,princess,punch,purple_heart,purse,' + - 'pushpin,put_litter_in_its_place,question,rabbit,rabbit2,racehorse,radio,radio_button,rage,rage1,rage2,rage3,' + - 'rage4,railway_car,rainbow,raised_hand,raised_hands,raising_hand,ram,ramen,rat,recycle,red_car,red_circle,' + - 'registered,relaxed,relieved,repeat,repeat_one,restroom,revolving_hearts,rewind,ribbon,rice,rice_ball,' + - 'rice_cracker,rice_scene,ring,rocket,roller_coaster,rooster,rose,rotating_light,round_pushpin,rowboat,ru,' + - 'rugby_football,runner,running,running_shirt_with_sash,sa,sagittarius,sailboat,sake,sandal,santa,satellite,' + - 'satisfied,saxophone,school,school_satchel,scissors,scorpius,scream,scream_cat,scroll,seat,secret,see_no_evil,' + - 'seedling,seven,shaved_ice,sheep,shell,ship,shipit,shirt,shit,shoe,shower,signal_strength,six,six_pointed_star,' + - 'ski,skull,sleeping,sleepy,slightly_smiling_face,slightly_frowning_face,slot_machine,small_blue_diamond,' + - 'small_orange_diamond,small_red_triangle,small_red_triangle_down,smile,smile_cat,smiley,smiley_cat,smiling_imp,' + - 'smirk,smirk_cat,smoking,snail,snake,snowboarder,snowflake,snowman,sob,soccer,soon,sos,sound,space_invader,spades,' + - 'spaghetti,sparkle,sparkler,sparkles,sparkling_heart,speak_no_evil,speaker,speech_balloon,speedboat,squirrel,star,' + - 'star2,stars,station,statue_of_liberty,steam_locomotive,stew,straight_ruler,strawberry,stuck_out_tongue,' + - 'stuck_out_tongue_closed_eyes,stuck_out_tongue_winking_eye,sun_with_face,sunflower,sunglasses,sunny,sunrise,' + - 'sunrise_over_mountains,surfer,sushi,suspect,suspension_railway,sweat,sweat_drops,sweat_smile,sweet_potato,swimmer,' + - 'symbols,syringe,tada,tanabata_tree,tangerine,taurus,taxi,tea,telephone,telephone_receiver,telescope,tennis,tent,' + - 'thought_balloon,three,thumbsdown,thumbsup,ticket,tiger,tiger2,tired_face,tm,toilet,tokyo_tower,tomato,tongue,top,' + - 'tophat,tractor,traffic_light,train,train2,tram,triangular_flag_on_post,triangular_ruler,trident,triumph,trolleybus,' + - 'trollface,trophy,tropical_drink,tropical_fish,truck,trumpet,tshirt,tulip,turtle,tv,twisted_rightwards_arrows,' + - 'two,two_hearts,two_men_holding_hands,two_women_holding_hands,u5272,u5408,u55b6,u6307,u6708,u6709,u6e80,u7121,' + - 'u7533,u7981,u7a7a,uk,umbrella,unamused,underage,unlock,up,us,v,vertical_traffic_light,vhs,vibration_mode,' + - 'video_camera,video_game,violin,virgo,volcano,vs,walking,waning_crescent_moon,waning_gibbous_moon,warning,watch,' + - 'water_buffalo,watermelon,wave,wavy_dash,waxing_crescent_moon,waxing_gibbous_moon,wc,weary,wedding,whale,whale2,' + - 'wheelchair,white_check_mark,white_circle,white_flower,white_large_square,white_medium_small_square,' + - 'white_medium_square,white_small_square,white_square_button,wind_chime,wine_glass,wink,wolf,woman,' + - 'womans_clothes,womans_hat,womens,worried,wrench,x,yellow_heart,yen,yum,zap,zero,zzz').split(','); - - // use a map to help make lookups faster instead of having to use indexOf on an array - const out = new Map(); - - for (let i = 0; i < emoticonNames.length; i++) { - out.set(emoticonNames[i], true); - } - - return out; -} - -export const emoticonMap = initializeEmoticonMap(); - -export function handleEmoticons(text, tokens) { - let output = text; - - function replaceEmoticonWithToken(fullMatch, prefix, matchText, name) { - if (emoticonMap.has(name)) { - const index = tokens.size; - const alias = `MM_EMOTICON${index}`; - - tokens.set(alias, { - value: `<img align="absmiddle" alt="${matchText}" class="emoticon" src="${getImagePathForEmoticon(name)}" title="${matchText}" />`, - originalText: fullMatch - }); - - return prefix + alias; - } - - return fullMatch; - } - - output = output.replace(/(^|\s)(:([a-zA-Z0-9_-]+):)(?=$|\s)/g, (fullMatch, prefix, matchText, name) => replaceEmoticonWithToken(fullMatch, prefix, matchText, name)); - - $.each(emoticonPatterns, (name, pattern) => { - // this might look a bit funny, but since the name isn't contained in the actual match - // like with the named emoticons, we need to add it in manually - output = output.replace(pattern, (fullMatch, prefix, matchText) => replaceEmoticonWithToken(fullMatch, prefix, matchText, name)); - }); - - return output; -} - -export function getImagePathForEmoticon(name) { - if (name) { - return `/static/images/emoji/${name}.png`; - } - return '/static/images/emoji'; -} diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx deleted file mode 100644 index 2b1aed9c0..000000000 --- a/web/react/utils/markdown.jsx +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import highlightJs from 'highlight.js/lib/highlight.js'; -import highlightJsDiff from 'highlight.js/lib/languages/diff.js'; -import highlightJsApache from 'highlight.js/lib/languages/apache.js'; -import highlightJsMakefile from 'highlight.js/lib/languages/makefile.js'; -import highlightJsHttp from 'highlight.js/lib/languages/http.js'; -import highlightJsJson from 'highlight.js/lib/languages/json.js'; -import highlightJsMarkdown from 'highlight.js/lib/languages/markdown.js'; -import highlightJsJavascript from 'highlight.js/lib/languages/javascript.js'; -import highlightJsCss from 'highlight.js/lib/languages/css.js'; -import highlightJsNginx from 'highlight.js/lib/languages/nginx.js'; -import highlightJsObjectivec from 'highlight.js/lib/languages/objectivec.js'; -import highlightJsPython from 'highlight.js/lib/languages/python.js'; -import highlightJsXml from 'highlight.js/lib/languages/xml.js'; -import highlightJsPerl from 'highlight.js/lib/languages/perl.js'; -import highlightJsBash from 'highlight.js/lib/languages/bash.js'; -import highlightJsPhp from 'highlight.js/lib/languages/php.js'; -import highlightJsCoffeescript from 'highlight.js/lib/languages/coffeescript.js'; -import highlightJsCs from 'highlight.js/lib/languages/cs.js'; -import highlightJsCpp from 'highlight.js/lib/languages/cpp.js'; -import highlightJsSql from 'highlight.js/lib/languages/sql.js'; -import highlightJsGo from 'highlight.js/lib/languages/go.js'; -import highlightJsRuby from 'highlight.js/lib/languages/ruby.js'; -import highlightJsJava from 'highlight.js/lib/languages/java.js'; -import highlightJsIni from 'highlight.js/lib/languages/ini.js'; - -highlightJs.registerLanguage('diff', highlightJsDiff); -highlightJs.registerLanguage('apache', highlightJsApache); -highlightJs.registerLanguage('makefile', highlightJsMakefile); -highlightJs.registerLanguage('http', highlightJsHttp); -highlightJs.registerLanguage('json', highlightJsJson); -highlightJs.registerLanguage('markdown', highlightJsMarkdown); -highlightJs.registerLanguage('javascript', highlightJsJavascript); -highlightJs.registerLanguage('css', highlightJsCss); -highlightJs.registerLanguage('nginx', highlightJsNginx); -highlightJs.registerLanguage('objectivec', highlightJsObjectivec); -highlightJs.registerLanguage('python', highlightJsPython); -highlightJs.registerLanguage('xml', highlightJsXml); -highlightJs.registerLanguage('perl', highlightJsPerl); -highlightJs.registerLanguage('bash', highlightJsBash); -highlightJs.registerLanguage('php', highlightJsPhp); -highlightJs.registerLanguage('coffeescript', highlightJsCoffeescript); -highlightJs.registerLanguage('cs', highlightJsCs); -highlightJs.registerLanguage('cpp', highlightJsCpp); -highlightJs.registerLanguage('sql', highlightJsSql); -highlightJs.registerLanguage('go', highlightJsGo); -highlightJs.registerLanguage('ruby', highlightJsRuby); -highlightJs.registerLanguage('java', highlightJsJava); -highlightJs.registerLanguage('ini', highlightJsIni); - -import * as TextFormatting from './text_formatting.jsx'; -import * as Utils from './utils.jsx'; - -import marked from 'marked'; - -import Constants from '../utils/constants.jsx'; -const HighlightedLanguages = Constants.HighlightedLanguages; - -function markdownImageLoaded(image) { - image.style.height = 'auto'; -} -window.markdownImageLoaded = markdownImageLoaded; - -class MattermostInlineLexer extends marked.InlineLexer { - constructor(links, options) { - super(links, options); - - this.rules = Object.assign({}, this.rules); - - // modified version of the regex that allows for links starting with www and those surrounded by parentheses - // the original is /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/| {2,}\n|$)/ - this.rules.text = /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/|www\.|\(| {2,}\n|$)/; - - // modified version of the regex that allows links starting with www and those surrounded by parentheses - // the original is /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/ - this.rules.url = /^(\(?(?:https?:\/\/|www\.)[^\s<.][^\s<]*[^<.,:;"'\]\s])/; - - // modified version of the regex that allows <links> starting with www. - // the original is /^<([^ >]+(@|:\/)[^ >]+)>/ - this.rules.autolink = /^<((?:[^ >]+(@|:\/)|www\.)[^ >]+)>/; - } -} - -class MattermostParser extends marked.Parser { - parse(src) { - this.inline = new MattermostInlineLexer(src.links, this.options, this.renderer); - this.tokens = src.reverse(); - - var out = ''; - while (this.next()) { - out += this.tok(); - } - - return out; - } -} - -class MattermostMarkdownRenderer extends marked.Renderer { - constructor(options, formattingOptions = {}) { - super(options); - - this.heading = this.heading.bind(this); - this.paragraph = this.paragraph.bind(this); - this.text = this.text.bind(this); - - this.formattingOptions = formattingOptions; - } - - code(code, language, escaped) { - let usedLanguage = language || ''; - usedLanguage = usedLanguage.toLowerCase(); - - // treat html as xml to prevent injection attacks - if (usedLanguage === 'html') { - usedLanguage = 'xml'; - } - - if (HighlightedLanguages[usedLanguage]) { - const parsed = highlightJs.highlight(usedLanguage, code); - - return ( - '<div class="post-body--code">' + - '<span class="post-body--code__language">' + - HighlightedLanguages[usedLanguage] + - '</span>' + - '<pre>' + - '<code class="hljs">' + - parsed.value + - '</code>' + - '</pre>' + - '</div>' - ); - } else if (usedLanguage === 'tex' || usedLanguage === 'latex') { - try { - const html = katex.renderToString(code, {throwOnError: false, displayMode: true}); - - return '<div class="post-body--code tex">' + html + '</div>'; - } catch (e) { - // fall through if latex parsing fails and handle below - } - } - - return ( - '<pre>' + - '<code class="hljs">' + - (escaped ? code : TextFormatting.sanitizeHtml(code)) + '\n' + - '</code>' + - '</pre>' - ); - } - - codespan(text) { - return '<span class="codespan__pre-wrap">' + super.codespan(text) + '</span>'; - } - - br() { - if (this.formattingOptions.singleline) { - return ' '; - } - - return super.br(); - } - - image(href, title, text) { - let out = '<img src="' + href + '" alt="' + text + '"'; - if (title) { - out += ' title="' + title + '"'; - } - out += ' onload="window.markdownImageLoaded(this)" onerror="window.markdownImageLoaded(this)" class="markdown-inline-img"'; - out += this.options.xhtml ? '/>' : '>'; - return out; - } - - heading(text, level, raw) { - const id = `${this.options.headerPrefix}${raw.toLowerCase().replace(/[^\w]+/g, '-')}`; - return `<h${level} id="${id}" class="markdown__heading">${text}</h${level}>`; - } - - link(href, title, text) { - let outHref = href; - let outText = text; - let prefix = ''; - let suffix = ''; - - // some links like https://en.wikipedia.org/wiki/Rendering_(computer_graphics) contain brackets - // and we try our best to differentiate those from ones just wrapped in brackets when autolinking - if (outHref.startsWith('(') && outHref.endsWith(')') && text === outHref) { - prefix = '('; - suffix = ')'; - outText = text.substring(1, text.length - 1); - outHref = outHref.substring(1, outHref.length - 1); - } - - try { - const unescaped = decodeURIComponent(unescape(href)).replace(/[^\w:]/g, '').toLowerCase(); - - if (unescaped.indexOf('javascript:') === 0 || unescaped.indexOf('vbscript:') === 0) { // eslint-disable-line no-script-url - return ''; - } - } catch (e) { - return ''; - } - - if (!(/[a-z+.-]+:/i).test(outHref)) { - outHref = `http://${outHref}`; - } - - let output = '<a class="theme markdown__link" href="' + outHref + '"'; - if (title) { - output += ' title="' + title + '"'; - } - - if (outHref.lastIndexOf(Utils.getTeamURLFromAddressBar(), 0) === 0) { - output += '>'; - } else { - output += ' target="_blank">'; - } - - output += outText + '</a>'; - - return prefix + output + suffix; - } - - paragraph(text) { - if (this.formattingOptions.singleline) { - return `<p class="markdown__paragraph-inline">${text}</p>`; - } - - return super.paragraph(text); - } - - table(header, body) { - return `<div class="table-responsive"><table class="markdown__table"><thead>${header}</thead><tbody>${body}</tbody></table></div>`; - } - - listitem(text) { - const taskListReg = /^\[([ |xX])\] /; - const isTaskList = taskListReg.exec(text); - - if (isTaskList) { - return `<li class="list-item--task-list">${'<input type="checkbox" disabled="disabled" ' + (isTaskList[1] === ' ' ? '' : 'checked="checked" ') + '/> '}${text.replace(taskListReg, '')}</li>`; - } - return `<li>${text}</li>`; - } - - text(txt) { - return TextFormatting.doFormatText(txt, this.formattingOptions); - } -} - -class MattermostLexer extends marked.Lexer { - token(originalSrc, top, bq) { - let src = originalSrc.replace(/^ +$/gm, ''); - - while (src) { - // newline - let cap = this.rules.newline.exec(src); - if (cap) { - src = src.substring(cap[0].length); - if (cap[0].length > 1) { - this.tokens.push({ - type: 'space' - }); - } - } - - // code - cap = this.rules.code.exec(src); - if (cap) { - src = src.substring(cap[0].length); - cap = cap[0].replace(/^ {4}/gm, ''); - this.tokens.push({ - type: 'code', - text: this.options.pedantic ? cap : cap.replace(/\n+$/, '') - }); - continue; - } - - // fences (gfm) - cap = this.rules.fences.exec(src); - if (cap) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'code', - lang: cap[2], - text: cap[3] || '' - }); - continue; - } - - // heading - cap = this.rules.heading.exec(src); - if (cap) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'heading', - depth: cap[1].length, - text: cap[2] - }); - continue; - } - - // table no leading pipe (gfm) - cap = this.rules.nptable.exec(src); - if (top && cap) { - src = src.substring(cap[0].length); - - const item = { - type: 'table', - header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3].replace(/\n$/, '').split('\n') - }; - - for (let i = 0; i < item.align.length; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - for (let i = 0; i < item.cells.length; i++) { - item.cells[i] = item.cells[i].split(/ *\| */); - } - - this.tokens.push(item); - - continue; - } - - // lheading - cap = this.rules.lheading.exec(src); - if (cap) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'heading', - depth: cap[2] === '=' ? 1 : 2, - text: cap[1] - }); - continue; - } - - // hr - cap = this.rules.hr.exec(src); - if (cap) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'hr' - }); - continue; - } - - // blockquote - cap = this.rules.blockquote.exec(src); - if (cap) { - src = src.substring(cap[0].length); - - this.tokens.push({ - type: 'blockquote_start' - }); - - cap = cap[0].replace(/^ *> ?/gm, ''); - - // Pass `top` to keep the current - // "toplevel" state. This is exactly - // how markdown.pl works. - this.token(cap, top, true); - - this.tokens.push({ - type: 'blockquote_end' - }); - - continue; - } - - // list - cap = this.rules.list.exec(src); - if (cap) { - src = src.substring(cap[0].length); - const bull = cap[2]; - - this.tokens.push({ - type: 'list_start', - ordered: bull.length > 1 - }); - - // Get each top-level item. - cap = cap[0].match(this.rules.item); - - let next = false; - const l = cap.length; - let i = 0; - - for (; i < l; i++) { - let item = cap[i]; - - // Remove the list item's bullet - // so it is seen as the next token. - let space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) +/, ''); - - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = this.options.pedantic ? - item.replace(/^ {1,4}/gm, '') : - item.replace(new RegExp('^ {1,' + space + '}', 'gm'), ''); - } - - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (this.options.smartLists && i !== l - 1) { - const b = this.rules.bullet.exec(cap[i + 1])[0]; - if (bull !== b && !(bull.length > 1 && b.length > 1)) { - src = cap.slice(i + 1).join('\n') + src; - i = l - 1; - } - } - - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - let loose = next || (/\n\n(?!\s*$)/).test(item); - if (i !== l - 1) { - next = item.charAt(item.length - 1) === '\n'; - if (!loose) { - loose = next; - } - } - - this.tokens.push({ - type: loose ? - 'loose_item_start' : - 'list_item_start' - }); - - // Recurse. - this.token(item, false, bq); - - this.tokens.push({ - type: 'list_item_end' - }); - } - - this.tokens.push({ - type: 'list_end' - }); - - continue; - } - - // html - cap = this.rules.html.exec(src); - if (cap) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: this.options.sanitize ? 'paragraph' : 'html', - pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: cap[0] - }); - continue; - } - - // def - cap = this.rules.def.exec(src); - if ((!bq && top) && cap) { - src = src.substring(cap[0].length); - this.tokens.links[cap[1].toLowerCase()] = { - href: cap[2], - title: cap[3] - }; - continue; - } - - // table (gfm) - cap = this.rules.table.exec(src); - if (top && cap) { - src = src.substring(cap[0].length); - - const item = { - type: 'table', - header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') - }; - - for (let i = 0; i < item.align.length; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - for (let i = 0; i < item.cells.length; i++) { - item.cells[i] = item.cells[i].replace(/^ *\| *| *\| *$/g, '').split(/ *\| */); - } - - this.tokens.push(item); - - continue; - } - - // top-level paragraph - cap = this.rules.paragraph.exec(src); - if (top && cap) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'paragraph', - text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1] - }); - continue; - } - - // text - cap = this.rules.text.exec(src); - if (cap) { - // Top-level should never reach here. - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'text', - text: cap[0] - }); - continue; - } - - if (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); - } - } - - return this.tokens; - } -} - -export function format(text, options) { - const markdownOptions = { - renderer: new MattermostMarkdownRenderer(null, options), - sanitize: true, - gfm: true, - tables: true - }; - - const tokens = new MattermostLexer(markdownOptions).lex(text); - - return new MattermostParser(markdownOptions).parse(tokens); -} - -// Marked helper functions that should probably just be exported - -function unescape(html) { - return html.replace(/&([#\w]+);/g, (_, m) => { - const n = m.toLowerCase(); - if (n === 'colon') { - return ':'; - } else if (n.charAt(0) === '#') { - return n.charAt(1) === 'x' ? - String.fromCharCode(parseInt(n.substring(2), 16)) : - String.fromCharCode(+n.substring(1)); - } - return ''; - }); -} diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx deleted file mode 100644 index 552d93fac..000000000 --- a/web/react/utils/text_formatting.jsx +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import Autolinker from 'autolinker'; -import Constants from './constants.jsx'; -import * as Emoticons from './emoticons.jsx'; -import * as Markdown from './markdown.jsx'; -import UserStore from '../stores/user_store.jsx'; -import * as Utils from './utils.jsx'; - -// Performs formatting of user posts including highlighting mentions and search terms and converting urls, hashtags, and -// @mentions to links by taking a user's message and returning a string of formatted html. Also takes a number of options -// as part of the second parameter: -// - searchTerm - If specified, this word is highlighted in the resulting html. Defaults to nothing. -// - mentionHighlight - Specifies whether or not to highlight mentions of the current user. Defaults to true. -// - singleline - Specifies whether or not to remove newlines. Defaults to false. -// - emoticons - Enables emoticon parsing. Defaults to true. -// - markdown - Enables markdown parsing. Defaults to true. -export function formatText(text, options = {}) { - let output; - - if (!('markdown' in options) || options.markdown) { - // the markdown renderer will call doFormatText as necessary - output = Markdown.format(text, options); - } else { - output = sanitizeHtml(text); - output = doFormatText(output, options); - } - - // replace newlines with spaces if necessary - if (options.singleline) { - output = replaceNewlines(output); - } - - return output; -} - -// Performs most of the actual formatting work for formatText. Not intended to be called normally. -export function doFormatText(text, options) { - let output = text; - - const tokens = new Map(); - - // replace important words and phrases with tokens - output = autolinkAtMentions(output, tokens); - output = autolinkEmails(output, tokens); - output = autolinkHashtags(output, tokens); - - if (!('emoticons' in options) || options.emoticon) { - output = Emoticons.handleEmoticons(output, tokens); - } - - if (options.searchTerm) { - output = highlightSearchTerm(output, tokens, options.searchTerm); - } - - if (!('mentionHighlight' in options) || options.mentionHighlight) { - output = highlightCurrentMentions(output, tokens); - } - - // reinsert tokens with formatted versions of the important words and phrases - output = replaceTokens(output, tokens); - - return output; -} - -export function sanitizeHtml(text) { - let output = text; - - // normal string.replace only does a single occurrance so use a regex instead - output = output.replace(/&/g, '&'); - output = output.replace(/</g, '<'); - output = output.replace(/>/g, '>'); - output = output.replace(/'/g, '''); - output = output.replace(/"/g, '"'); - - return output; -} - -// Convert emails into tokens -function autolinkEmails(text, tokens) { - function replaceEmailWithToken(autolinker, match) { - const linkText = match.getMatchedText(); - let url = linkText; - - if (match.getType() === 'email') { - url = `mailto:${url}`; - } - - const index = tokens.size; - const alias = `MM_EMAIL${index}`; - - tokens.set(alias, { - value: `<a class="theme" href="${url}">${linkText}</a>`, - originalText: linkText - }); - - return alias; - } - - // we can't just use a static autolinker because we need to set replaceFn - const autolinker = new Autolinker({ - urls: false, - email: true, - phone: false, - twitter: false, - hashtag: false, - replaceFn: replaceEmailWithToken - }); - - return autolinker.link(text); -} - -function autolinkAtMentions(text, tokens) { - // Return true if provided character is punctuation - function isPunctuation(character) { - const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/g; - return re.test(character); - } - - // Test if provided text needs to be highlighted, special mention or current user - function mentionExists(u) { - return (Constants.SPECIAL_MENTIONS.indexOf(u) !== -1 || UserStore.getProfileByUsername(u)); - } - - function addToken(username, mention) { - const index = tokens.size; - const alias = `MM_ATMENTION${index}`; - - tokens.set(alias, { - value: `<a class='mention-link' href='#' data-mention='${username}'>${mention}</a>`, - originalText: mention - }); - return alias; - } - - function replaceAtMentionWithToken(fullMatch, mention, username) { - let usernameLower = username.toLowerCase(); - - if (mentionExists(usernameLower)) { - // Exact match - const alias = addToken(usernameLower, mention, ''); - return alias; - } - - // Not an exact match, attempt to truncate any punctuation to see if we can find a user - const originalUsername = usernameLower; - - for (let c = usernameLower.length; c > 0; c--) { - if (isPunctuation(usernameLower[c - 1])) { - usernameLower = usernameLower.substring(0, c - 1); - - if (mentionExists(usernameLower)) { - const suffix = originalUsername.substr(c - 1); - const alias = addToken(usernameLower, '@' + usernameLower); - return alias + suffix; - } - } else { - // If the last character is not punctuation, no point in going any further - break; - } - } - - return fullMatch; - } - - let output = text; - output = output.replace(/(@([a-z0-9.\-_]*))/gi, replaceAtMentionWithToken); - - return output; -} - -function escapeRegex(text) { - return text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); -} - -function highlightCurrentMentions(text, tokens) { - let output = text; - - const mentionKeys = UserStore.getCurrentMentionKeys(); - - // look for any existing tokens which are self mentions and should be highlighted - var newTokens = new Map(); - for (const [alias, token] of tokens) { - if (mentionKeys.indexOf(token.originalText) !== -1) { - const index = tokens.size + newTokens.size; - const newAlias = `MM_SELFMENTION${index}`; - - newTokens.set(newAlias, { - value: `<span class='mention--highlight'>${alias}</span>`, - originalText: token.originalText - }); - output = output.replace(alias, newAlias); - } - } - - // the new tokens are stashed in a separate map since we can't add objects to a map during iteration - for (const newToken of newTokens) { - tokens.set(newToken[0], newToken[1]); - } - - // look for self mentions in the text - function replaceCurrentMentionWithToken(fullMatch, prefix, mention) { - const index = tokens.size; - const alias = `MM_SELFMENTION${index}`; - - tokens.set(alias, { - value: `<span class='mention--highlight'>${mention}</span>`, - originalText: mention - }); - - return prefix + alias; - } - - for (const mention of UserStore.getCurrentMentionKeys()) { - output = output.replace(new RegExp(`(^|\\W)(${escapeRegex(mention)})\\b`, 'gi'), replaceCurrentMentionWithToken); - } - - return output; -} - -function autolinkHashtags(text, tokens) { - let output = text; - - var newTokens = new Map(); - for (const [alias, token] of tokens) { - if (token.originalText.lastIndexOf('#', 0) === 0) { - const index = tokens.size + newTokens.size; - const newAlias = `MM_HASHTAG${index}`; - - newTokens.set(newAlias, { - value: `<a class='mention-link' href='#' data-hashtag='${token.originalText}'>${token.originalText}</a>`, - originalText: token.originalText - }); - - output = output.replace(alias, newAlias); - } - } - - // the new tokens are stashed in a separate map since we can't add objects to a map during iteration - for (const newToken of newTokens) { - tokens.set(newToken[0], newToken[1]); - } - - // look for hashtags in the text - function replaceHashtagWithToken(fullMatch, prefix, hashtag) { - const index = tokens.size; - const alias = `MM_HASHTAG${index}`; - - let value = hashtag; - - if (hashtag.length > Constants.MIN_HASHTAG_LINK_LENGTH) { - value = `<a class='mention-link' href='#' data-hashtag='${hashtag}'>${hashtag}</a>`; - } - - tokens.set(alias, { - value, - originalText: hashtag - }); - - return prefix + alias; - } - - return output.replace(/(^|\W)(#[a-zA-ZĆ¤Ć¶Ć¼ĆĆĆĆ][a-zA-Z0-9Ć¤Ć¶Ć¼ĆĆĆĆ.\-_]*)\b/g, replaceHashtagWithToken); -} - -const puncStart = /^[.,()&$!\[\]{}':;\\]+/; -const puncEnd = /[.,()&$#!\[\]{}':;\\]+$/; - -function parseSearchTerms(searchTerm) { - let terms = []; - - let termString = searchTerm; - - while (termString) { - let captured; - - // check for a quoted string - captured = (/^"(.*?)"/).exec(termString); - if (captured) { - termString = termString.substring(captured[0].length); - terms.push(captured[1]); - continue; - } - - // check for a search flag (and don't add it to terms) - captured = (/^(?:in|from|channel): ?\S+/).exec(termString); - if (captured) { - termString = termString.substring(captured[0].length); - continue; - } - - // capture any plain text up until the next quote or search flag - captured = (/^.+?(?=\bin|\bfrom|\bchannel|"|$)/).exec(termString); - if (captured) { - termString = termString.substring(captured[0].length); - - // break the text up into words based on how the server splits them in SqlPostStore.SearchPosts and then discard empty terms - terms.push(...captured[0].split(/[ <>+\-\(\)\~\@]/).filter((term) => !!term)); - continue; - } - - // we should never reach this point since at least one of the regexes should match something in the remaining text - throw new Error('Infinite loop in search term parsing: ' + termString); - } - - // remove punctuation from each term - terms = terms.map((term) => term.replace(puncStart, '').replace(puncEnd, '')); - - return terms; -} - -function convertSearchTermToRegex(term) { - let pattern; - if (term.endsWith('*')) { - pattern = '\\b' + escapeRegex(term.substring(0, term.length - 1)); - } else { - pattern = '\\b' + escapeRegex(term) + '\\b'; - } - - return new RegExp(pattern, 'gi'); -} - -function highlightSearchTerm(text, tokens, searchTerm) { - const terms = parseSearchTerms(searchTerm); - - if (terms.length === 0) { - return text; - } - - let output = text; - - function replaceSearchTermWithToken(word) { - const index = tokens.size; - const alias = `MM_SEARCHTERM${index}`; - - tokens.set(alias, { - value: `<span class='search-highlight'>${word}</span>`, - originalText: word - }); - - return alias; - } - - for (const term of terms) { - // highlight existing tokens matching search terms - var newTokens = new Map(); - for (const [alias, token] of tokens) { - if (token.originalText === term.replace(/\*$/, '')) { - const index = tokens.size + newTokens.size; - const newAlias = `MM_SEARCHTERM${index}`; - - newTokens.set(newAlias, { - value: `<span class='search-highlight'>${alias}</span>`, - originalText: token.originalText - }); - - output = output.replace(alias, newAlias); - } - } - - // the new tokens are stashed in a separate map since we can't add objects to a map during iteration - for (const newToken of newTokens) { - tokens.set(newToken[0], newToken[1]); - } - - output = output.replace(convertSearchTermToRegex(term), replaceSearchTermWithToken); - } - - return output; -} - -function replaceTokens(text, tokens) { - let output = text; - - // iterate backwards through the map so that we do replacement in the opposite order that we added tokens - const aliases = [...tokens.keys()]; - for (let i = aliases.length - 1; i >= 0; i--) { - const alias = aliases[i]; - const token = tokens.get(alias); - output = output.replace(alias, token.value); - } - - return output; -} - -function replaceNewlines(text) { - return text.replace(/\n/g, ' '); -} - -// A click handler that can be used with the results of TextFormatting.formatText to add default functionality -// to clicked hashtags and @mentions. -export function handleClick(e) { - const mentionAttribute = e.target.getAttributeNode('data-mention'); - const hashtagAttribute = e.target.getAttributeNode('data-hashtag'); - - if (mentionAttribute) { - Utils.searchForTerm(mentionAttribute.value); - } else if (hashtagAttribute) { - Utils.searchForTerm(hashtagAttribute.value); - } -} diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx deleted file mode 100644 index 360d1f4a5..000000000 --- a/web/react/utils/utils.jsx +++ /dev/null @@ -1,1416 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; -import * as GlobalActions from '../action_creators/global_actions.jsx'; -import ChannelStore from '../stores/channel_store.jsx'; -import UserStore from '../stores/user_store.jsx'; -import LocalizationStore from '../stores/localization_store.jsx'; -import PreferenceStore from '../stores/preference_store.jsx'; -import TeamStore from '../stores/team_store.jsx'; -import Constants from '../utils/constants.jsx'; -var ActionTypes = Constants.ActionTypes; -import * as Client from './client.jsx'; -import * as AsyncClient from './async_client.jsx'; -import * as client from './client.jsx'; -import Autolinker from 'autolinker'; - -import {FormattedTime} from 'mm-intl'; - -export function isEmail(email) { - // writing a regex to match all valid email addresses is really, really hard (see http://stackoverflow.com/a/201378) - // so we just do a simple check and rely on a verification email to tell if it's a real address - return (/^.+@.+$/).test(email); -} - -export function cleanUpUrlable(input) { - var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); - cleaned = cleaned.replace(/-{2,}/, '-'); - cleaned = cleaned.replace(/^\-+/, ''); - cleaned = cleaned.replace(/\-+$/, ''); - return cleaned; -} - -export function isTestDomain() { - if ((/^localhost/).test(window.location.hostname)) { - return true; - } - - if ((/^dockerhost/).test(window.location.hostname)) { - return true; - } - - if ((/^test/).test(window.location.hostname)) { - return true; - } - - if ((/^127.0./).test(window.location.hostname)) { - return true; - } - - if ((/^192.168./).test(window.location.hostname)) { - return true; - } - - if ((/^10./).test(window.location.hostname)) { - return true; - } - - if ((/^176./).test(window.location.hostname)) { - return true; - } - - return false; -} - -export function isChrome() { - if (navigator.userAgent.indexOf('Chrome') > -1) { - return true; - } - return false; -} - -export function isSafari() { - if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) { - return true; - } - return false; -} - -export function isIosChrome() { - // https://developer.chrome.com/multidevice/user-agent - return navigator.userAgent.indexOf('CriOS') !== -1; -} - -export function isMobileApp() { - const userAgent = navigator.userAgent; - - // the mobile app has different user agents for the native api calls and the shim, so handle them both - const isApi = userAgent.indexOf('Mattermost') !== -1; - const isShim = userAgent.indexOf('iPhone') !== -1 && userAgent.indexOf('Safari') === -1 && userAgent.indexOf('Chrome') === -1; - - return isApi || isShim; -} - -export function isInRole(roles, inRole) { - var parts = roles.split(' '); - for (var i = 0; i < parts.length; i++) { - if (parts[i] === inRole) { - return true; - } - } - - return false; -} - -export function isAdmin(roles) { - if (isInRole(roles, 'admin')) { - return true; - } - - if (isInRole(roles, 'system_admin')) { - return true; - } - - return false; -} - -export function isSystemAdmin(roles) { - if (isInRole(roles, 'system_admin')) { - return true; - } - - return false; -} - -export function getDomainWithOutSub() { - var parts = window.location.host.split('.'); - - if (parts.length === 1) { - if (parts[0].indexOf('dockerhost') > -1) { - return 'dockerhost:8065'; - } - - return 'localhost:8065'; - } - - return parts[1] + '.' + parts[2]; -} - -export function getCookie(name) { - var value = '; ' + document.cookie; - var parts = value.split('; ' + name + '='); - if (parts.length === 2) { - return parts.pop().split(';').shift(); - } - return ''; -} - -var requestedNotificationPermission = false; - -export function notifyMe(title, body, channel) { - if (!('Notification' in window)) { - return; - } - - if (Notification.permission === 'granted' || (Notification.permission === 'default' && !requestedNotificationPermission)) { - requestedNotificationPermission = true; - - Notification.requestPermission((permission) => { - if (permission === 'granted') { - try { - var notification = new Notification(title, {body: body, tag: body, icon: '/static/images/icon50x50.png'}); - notification.onclick = () => { - window.focus(); - if (channel) { - switchChannel(channel); - } else { - window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square'; - } - }; - setTimeout(() => { - notification.close(); - }, 5000); - } catch (e) { - console.error(e); //eslint-disable-line no-console - } - } - }); - } -} - -var canDing = true; - -export function ding() { - if (!isBrowserFirefox() && canDing) { - var audio = new Audio('/static/images/bing.mp3'); - audio.play(); - canDing = false; - setTimeout(() => { - canDing = true; - return; - }, 3000); - } -} - -export function getUrlParameter(sParam) { - var sPageURL = window.location.search.substring(1); - var sURLVariables = sPageURL.split('&'); - for (var i = 0; i < sURLVariables.length; i++) { - var sParameterName = sURLVariables[i].split('='); - if (sParameterName[0] === sParam) { - return sParameterName[1]; - } - } - return null; -} - -export function getDateForUnixTicks(ticks) { - return new Date(ticks); -} - -export function displayDate(ticks) { - var d = new Date(ticks); - var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - - return monthNames[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); -} - -export function displayTime(ticks, utc) { - const d = new Date(ticks); - let hours; - let minutes; - let ampm = ''; - let timezone = ''; - - if (utc) { - hours = d.getUTCHours(); - minutes = d.getUTCMinutes(); - timezone = ' UTC'; - } else { - hours = d.getHours(); - minutes = d.getMinutes(); - } - - if (minutes <= 9) { - minutes = '0' + minutes; - } - - const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time'); - if (!useMilitaryTime) { - ampm = ' AM'; - if (hours >= 12) { - ampm = ' PM'; - } - - hours = hours % 12; - if (!hours) { - hours = '12'; - } - } - - return hours + ':' + minutes + ampm + timezone; -} - -export function displayTimeFormatted(ticks) { - const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time'); - - return ( - <FormattedTime - value={ticks} - hour='numeric' - minute='numeric' - hour12={!useMilitaryTime} - /> - ); -} - -export function isMilitaryTime() { - return PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time'); -} - -export function displayDateTime(ticks) { - var seconds = Math.floor((Date.now() - ticks) / 1000); - - var interval = Math.floor(seconds / 3600); - - if (interval > 24) { - return this.displayTime(ticks); - } - - if (interval > 1) { - return interval + ' hours ago'; - } - - if (interval === 1) { - return interval + ' hour ago'; - } - - interval = Math.floor(seconds / 60); - if (interval >= 2) { - return interval + ' minutes ago'; - } - - if (interval >= 1) { - return '1 minute ago'; - } - - return 'just now'; -} - -export function displayCommentDateTime(ticks) { - return displayDate(ticks) + ' ' + displayTime(ticks); -} - -// returns Unix timestamp in milliseconds -export function getTimestamp() { - return Date.now(); -} - -// extracts links not styled by Markdown -export function extractLinks(text) { - const links = []; - let inText = text; - - // strip out code blocks - inText = inText.replace(/`[^`]*`/g, ''); - - // strip out inline markdown images - inText = inText.replace(/!\[[^\]]*\]\([^\)]*\)/g, ''); - - function replaceFn(autolinker, match) { - let link = ''; - const matchText = match.getMatchedText(); - - if (matchText.trim().indexOf('http') === 0) { - link = matchText; - } else { - link = 'http://' + matchText; - } - - links.push(link); - } - - Autolinker.link( - inText, - { - replaceFn, - urls: {schemeMatches: true, wwwMatches: true, tldMatches: false}, - emails: false, - twitter: false, - phone: false, - hashtag: false - } - ); - - return links; -} - -export function escapeRegExp(string) { - return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); -} - -// Taken from http://stackoverflow.com/questions/1068834/object-comparison-in-javascript and modified slightly -export function areObjectsEqual(x, y) { - let p; - const leftChain = []; - const rightChain = []; - - // Remember that NaN === NaN returns false - // and isNaN(undefined) returns true - if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') { - return true; - } - - // Compare primitives and functions. - // Check if both arguments link to the same object. - // Especially useful on step when comparing prototypes - if (x === y) { - return true; - } - - // Works in case when functions are created in constructor. - // Comparing dates is a common scenario. Another built-ins? - // We can even handle functions passed across iframes - if ((typeof x === 'function' && typeof y === 'function') || - (x instanceof Date && y instanceof Date) || - (x instanceof RegExp && y instanceof RegExp) || - (x instanceof String && y instanceof String) || - (x instanceof Number && y instanceof Number)) { - return x.toString() === y.toString(); - } - - if (x instanceof Map && y instanceof Map) { - return areMapsEqual(x, y); - } - - // At last checking prototypes as good a we can - if (!(x instanceof Object && y instanceof Object)) { - return false; - } - - if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) { - return false; - } - - if (x.constructor !== y.constructor) { - return false; - } - - if (x.prototype !== y.prototype) { - return false; - } - - // Check for infinitive linking loops - if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) { - return false; - } - - // Quick checking of one object beeing a subset of another. - for (p in y) { - if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { - return false; - } else if (typeof y[p] !== typeof x[p]) { - return false; - } - } - - for (p in x) { - if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) { - return false; - } else if (typeof y[p] !== typeof x[p]) { - return false; - } - - switch (typeof (x[p])) { - case 'object': - case 'function': - - leftChain.push(x); - rightChain.push(y); - - if (!areObjectsEqual(x[p], y[p])) { - return false; - } - - leftChain.pop(); - rightChain.pop(); - break; - - default: - if (x[p] !== y[p]) { - return false; - } - break; - } - } - - return true; -} - -export function areMapsEqual(a, b) { - if (a.size !== b.size) { - return false; - } - - for (const [key, value] of a) { - if (!b.has(key)) { - return false; - } - - if (!areObjectsEqual(value, b.get(key))) { - return false; - } - } - - return true; -} - -export function replaceHtmlEntities(text) { - var tagsToReplace = { - '&': '&', - '<': '<', - '>': '>' - }; - var newtext = text; - for (var tag in tagsToReplace) { - if ({}.hasOwnProperty.call(tagsToReplace, tag)) { - var regex = new RegExp(tag, 'g'); - newtext = newtext.replace(regex, tagsToReplace[tag]); - } - } - return newtext; -} - -export function insertHtmlEntities(text) { - var tagsToReplace = { - '&': '&', - '<': '<', - '>': '>' - }; - var newtext = text; - for (var tag in tagsToReplace) { - if ({}.hasOwnProperty.call(tagsToReplace, tag)) { - var regex = new RegExp(tag, 'g'); - newtext = newtext.replace(regex, tagsToReplace[tag]); - } - } - return newtext; -} - -export function searchForTerm(term) { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_SEARCH_TERM, - term: term, - do_search: true - }); -} - -export function getFileType(extin) { - var ext = extin.toLowerCase(); - if (Constants.IMAGE_TYPES.indexOf(ext) > -1) { - return 'image'; - } - - if (Constants.AUDIO_TYPES.indexOf(ext) > -1) { - return 'audio'; - } - - if (Constants.VIDEO_TYPES.indexOf(ext) > -1) { - return 'video'; - } - - if (Constants.SPREADSHEET_TYPES.indexOf(ext) > -1) { - return 'spreadsheet'; - } - - if (Constants.CODE_TYPES.indexOf(ext) > -1) { - return 'code'; - } - - if (Constants.WORD_TYPES.indexOf(ext) > -1) { - return 'word'; - } - - if (Constants.PRESENTATION_TYPES.indexOf(ext) > -1) { - return 'presentation'; - } - - if (Constants.PDF_TYPES.indexOf(ext) > -1) { - return 'pdf'; - } - - if (Constants.PATCH_TYPES.indexOf(ext) > -1) { - return 'patch'; - } - - return 'other'; -} - -export function getPreviewImagePathForFileType(fileTypeIn) { - var fileType = fileTypeIn.toLowerCase(); - - var icon; - if (fileType in Constants.ICON_FROM_TYPE) { - icon = Constants.ICON_FROM_TYPE[fileType]; - } else { - icon = Constants.ICON_FROM_TYPE.other; - } - - return '/static/images/icons/' + icon + '.png'; -} - -export function getIconClassName(fileTypeIn) { - var fileType = fileTypeIn.toLowerCase(); - - if (fileType in Constants.ICON_FROM_TYPE) { - return Constants.ICON_FROM_TYPE[fileType]; - } - - return 'glyphicon-file'; -} - -export function splitFileLocation(fileLocation) { - var fileSplit = fileLocation.split('.'); - - var ext = ''; - if (fileSplit.length > 1) { - ext = fileSplit[fileSplit.length - 1]; - fileSplit.splice(fileSplit.length - 1, 1); - } - - var filePath = fileSplit.join('.'); - var filename = filePath.split('/')[filePath.split('/').length - 1]; - - return {ext: ext, name: filename, path: filePath}; -} - -export function getPreviewImagePath(filename) { - // Returns the path to a preview image that can be used to represent a file. - const fileInfo = splitFileLocation(filename); - const fileType = getFileType(fileInfo.ext); - - if (fileType === 'image') { - return getFileUrl(fileInfo.path + '_preview.jpg'); - } - - // only images have proper previews, so just use a placeholder icon for non-images - return getPreviewImagePathForFileType(fileType); -} - -export function toTitleCase(str) { - function doTitleCase(txt) { - return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); - } - return str.replace(/\w\S*/g, doTitleCase); -} - -export function applyTheme(theme) { - if (theme.sidebarBg) { - changeCss('.sidebar--left, .modal .settings-modal .settings-table .settings-links, .sidebar--menu', 'background:' + theme.sidebarBg, 1); - changeCss('body', 'scrollbar-face-color:' + theme.sidebarBg, 3); - } - - if (theme.sidebarText) { - changeCss('.sidebar--left .nav-pills__container li>a, .sidebar--right, .modal .settings-modal .nav-pills>li a, .sidebar--menu', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1); - changeCss('@media(max-width: 768px){.modal .settings-modal .settings-table .nav>li>a', 'color:' + theme.sidebarText, 1); - changeCss('.sidebar--left .nav-pills__container li>h4, .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1); - changeCss('.sidebar--left .add-channel-btn:hover, .sidebar--left .add-channel-btn:focus', 'color:' + theme.sidebarText, 1); - changeCss('.sidebar--left .status .offline--icon, .sidebar--left .status .offline--icon', 'fill:' + theme.sidebarText, 1); - changeCss('@media(max-width: 768px){.modal .settings-modal .settings-table .nav>li>a', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2); - } - - if (theme.sidebarUnreadText) { - changeCss('.sidebar--left .nav-pills__container li>a.unread-title', 'color:' + theme.sidebarUnreadText + '!important;', 2); - } - - if (theme.sidebarTextHoverBg) { - changeCss('.sidebar--left .nav-pills__container li>a:hover, .sidebar--left .nav-pills__container li>a:focus, .modal .settings-modal .nav-pills>li:hover a, .modal .settings-modal .nav-pills>li:focus a', 'background:' + theme.sidebarTextHoverBg, 1); - changeCss('@media(max-width: 768px){.modal .settings-modal .settings-table .nav>li:hover a', 'background:' + theme.sidebarTextHoverBg, 1); - } - - if (theme.sidebarTextActiveBorder) { - changeCss('.sidebar--left .nav li.active a:before, .modal .settings-modal .nav-pills>li.active a:before', 'background:' + theme.sidebarTextActiveBorder, 1); - } - - if (theme.sidebarTextActiveColor) { - changeCss('.sidebar--left .nav-pills__container li.active a, .sidebar--left .nav-pills__container li.active a:hover, .sidebar--left .nav-pills__container li.active a:focus, .modal .settings-modal .nav-pills>li.active a, .modal .settings-modal .nav-pills>li.active a:hover, .modal .settings-modal .nav-pills>li.active a:active', 'color:' + theme.sidebarTextActiveColor, 2); - changeCss('.sidebar--left .nav li.active a, .sidebar--left .nav li.active a:hover, .sidebar--left .nav li.active a:focus', 'background:' + changeOpacity(theme.sidebarTextActiveColor, 0.1), 1); - } - - if (theme.sidebarHeaderBg) { - changeCss('.sidebar--left .team__header, .sidebar--menu .team__header, .post-list__timestamp', 'background:' + theme.sidebarHeaderBg, 1); - changeCss('.modal .modal-header', 'background:' + theme.sidebarHeaderBg, 1); - changeCss('#navbar .navbar-default', 'background:' + theme.sidebarHeaderBg, 1); - changeCss('@media(max-width: 768px){.search-bar__container', 'background:' + theme.sidebarHeaderBg, 1); - changeCss('.attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg, 1); - } - - if (theme.sidebarHeaderTextColor) { - changeCss('.sidebar--left .team__header .header__info, .sidebar--menu .team__header .header__info, .post-list__timestamp', 'color:' + theme.sidebarHeaderTextColor, 1); - changeCss('.sidebar--left .team__header .navbar-right .dropdown__icon, .sidebar--menu .team__header .navbar-right .dropdown__icon', 'fill:' + theme.sidebarHeaderTextColor, 1); - changeCss('.sidebar--left .team__header .user__name, .sidebar--menu .team__header .user__name', 'color:' + changeOpacity(theme.sidebarHeaderTextColor, 0.8), 1); - changeCss('.sidebar--left .team__header:hover .user__name, .sidebar--menu .team__header:hover .user__name', 'color:' + theme.sidebarHeaderTextColor, 1); - changeCss('.modal .modal-header .modal-title, .modal .modal-header .modal-title .name, .modal .modal-header button.close', 'color:' + theme.sidebarHeaderTextColor, 1); - changeCss('#navbar .navbar-default .navbar-brand .heading', 'color:' + theme.sidebarHeaderTextColor, 1); - changeCss('#navbar .navbar-default .navbar-toggle .icon-bar, ', 'background:' + theme.sidebarHeaderTextColor, 1); - changeCss('@media(max-width: 768px){.search-bar__container', 'color:' + theme.sidebarHeaderTextColor, 2); - } - - if (theme.onlineIndicator) { - changeCss('.sidebar--left .status .online--icon', 'fill:' + theme.onlineIndicator, 1); - } - - if (theme.awayIndicator) { - changeCss('.sidebar--left .status .away--icon', 'fill:' + theme.awayIndicator, 1); - } - - if (theme.mentionBj) { - changeCss('.sidebar--left .nav-pills__unread-indicator', 'background:' + theme.mentionBj, 1); - changeCss('.sidebar--left .badge', 'background:' + theme.mentionBj + '!important;', 1); - } - - if (theme.mentionColor) { - changeCss('.sidebar--left .nav-pills__unread-indicator', 'color:' + theme.mentionColor, 2); - changeCss('.sidebar--left .badge', 'color:' + theme.mentionColor + '!important;', 2); - } - - if (theme.centerChannelBg) { - changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .suggestion-list__content, .modal .modal-content', 'background:' + theme.centerChannelBg, 1); - changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1); - changeCss('#post-create', 'background:' + theme.centerChannelBg, 1); - changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1); - changeCss('.post-image__details, .search-help-popover .search-autocomplete__divider span', 'background:' + theme.centerChannelBg, 1); - changeCss('.sidebar--right, .dropdown-menu, .popover, .tip-overlay', 'background:' + theme.centerChannelBg, 1); - changeCss('.popover.bottom>.arrow:after', 'border-bottom-color:' + theme.centerChannelBg, 1); - changeCss('.popover.right>.arrow:after, .tip-overlay.tip-overlay--sidebar .arrow, .tip-overlay.tip-overlay--header .arrow', 'border-right-color:' + theme.centerChannelBg, 1); - changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1); - changeCss('.popover.top>.arrow:after, .tip-overlay.tip-overlay--chat .arrow', 'border-top-color:' + theme.centerChannelBg, 1); - changeCss('@media(min-width: 768px){.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1); - changeCss('.attachment__content', 'background:' + theme.centerChannelBg, 1); - changeCss('body', 'scrollbar-face-color:' + theme.centerChannelBg, 2); - changeCss('body', 'scrollbar-track-color:' + theme.centerChannelBg, 2); - } - - if (theme.centerChannelColor) { - changeCss('.post-list__arrows', 'fill:' + changeOpacity(theme.centerChannelColor, 0.3), 1); - changeCss('.sidebar--left, .sidebar--right .sidebar--right__header, .suggestion-list__content .command', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .suggestion-list__content .command, .modal .modal-content, .dropdown-menu, .popover, .mentions__name, .tip-overlay', 'color:' + theme.centerChannelColor, 1); - changeCss('#archive-link-home', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); - changeCss('#post-create', 'color:' + theme.centerChannelColor, 2); - changeCss('.mentions--top, .suggestion-list', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3); - changeCss('.mentions--top, .suggestion-list', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2); - changeCss('.mentions--top, .suggestion-list', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 1); - changeCss('.dropdown-menu, .popover ', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 3); - changeCss('.dropdown-menu, .popover ', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 2); - changeCss('.dropdown-menu, .popover ', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 1); - changeCss('.post__body hr, .loading-screen .loading__content .round, .tutorial__circles .circle', 'background:' + theme.centerChannelColor, 1); - changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1); - changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); - changeCss('.channel-header #member_popover', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); - changeCss('.custom-textarea, .custom-textarea:focus, .file-preview, .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td, .suggestion-list__content, .modal .modal-content, .modal .settings-modal .settings-table .settings-content .divider-light, .webhooks__container, .dropdown-menu, .modal .modal-header, .popover', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.popover.bottom>.arrow', 'border-bottom-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1); - changeCss('.search-help-popover .search-autocomplete__divider span', 'color:' + changeOpacity(theme.centerChannelColor, 0.7), 1); - changeCss('.popover.right>.arrow', 'border-right-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1); - changeCss('.popover.left>.arrow', 'border-left-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1); - changeCss('.popover.top>.arrow', 'border-top-color:' + changeOpacity(theme.centerChannelColor, 0.25), 1); - changeCss('.suggestion-list__content .command, .popover .popover-title', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.dropdown-menu .divider, .search-help-popover .search-autocomplete__divider:before', 'background:' + theme.centerChannelColor, 1); - changeCss('.custom-textarea', 'color:' + theme.centerChannelColor, 1); - changeCss('.post-image__column', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 2); - changeCss('.post-image__details', 'color:' + theme.centerChannelColor, 2); - changeCss('.post-image__column a, .post-image__column a:hover, .post-image__column a:focus', 'color:' + theme.centerChannelColor, 1); - changeCss('@media(min-width: 768px){.search-bar__container .search__form .search-bar, .form-control', 'color:' + theme.centerChannelColor, 2); - changeCss('.input-group-addon, .search-bar__container .search__form, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.form-control:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); - changeCss('.attachment .attachment__content', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); - changeCss('.channel-intro .channel-intro__content, .webhooks__container', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1); - changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2); - changeCss('.date-separator .separator__hr, .modal-footer, .modal .custom-textarea', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.search-item-container, .post-right__container .post.post--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 1); - changeCss('.modal .custom-textarea:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); - changeCss('.channel-intro, .modal .settings-modal .settings-table .settings-content .divider-dark, hr, .modal .settings-modal .settings-table .settings-links, .modal .settings-modal .settings-table .settings-content .appearance-section .theme-elements__header', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.post.current--user .post__body, .post.post--comment.other--root.current--user .post-comment, pre, .post-right__container .post.post--root', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.post.current--user .post__body, .post.post--comment.other--root.current--user .post-comment, .post.same--root.post--comment .post__body, .more-modal__list .more-modal__row, .member-div:first-child, .member-div, .access-history__table .access__report, .activity-log__table', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.1), 2); - changeCss('@media(max-width: 1800px){.inner-wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); - changeCss('.post:hover, .more-modal__list .more-modal__row:hover, .modal .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.date-separator.hovered--before:after, .date-separator.hovered--after:before, .new-separator.hovered--after:before, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.suggestion-list__content .command:hover, .mentions__name:hover, .suggestion--selected, .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover, .bot-indicator', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); - changeCss('code, .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control', 'background:' + changeOpacity(theme.centerChannelColor, 0.1), 1); - changeCss('@media(min-width: 960px){.post.current--user:hover .post__body ', 'background: none;', 1); - changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2); - changeCss('.search-help-popover .search-autocomplete__item:hover, .modal .settings-modal .settings-table .settings-content .appearance-section .theme-elements__body', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1); - changeCss('.search-help-popover .search-autocomplete__item.selected', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); - changeCss('::-webkit-scrollbar-thumb', 'background:' + changeOpacity(theme.centerChannelColor, 0.4), 1); - changeCss('body', 'scrollbar-arrow-color:' + theme.centerChannelColor, 4); - } - - if (theme.newMessageSeparator) { - changeCss('.new-separator .separator__text', 'color:' + theme.newMessageSeparator, 1); - changeCss('.new-separator .separator__hr', 'border-color:' + changeOpacity(theme.newMessageSeparator, 0.5), 1); - } - - if (theme.linkColor) { - changeCss('a, a:focus, a:hover, .btn, .btn:focus, .btn:hover', 'color:' + theme.linkColor, 1); - changeCss('.post .comment-icon__container, .post .post__reply', 'fill:' + theme.linkColor, 1); - } - - if (theme.buttonBg) { - changeCss('.btn.btn-primary, .tutorial__circles .circle.active', 'background:' + theme.buttonBg, 1); - changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1); - changeCss('.file-playback__controls', 'color:' + changeColor(theme.buttonBg, -0.25), 1); - } - - if (theme.buttonColor) { - changeCss('.btn.btn-primary', 'color:' + theme.buttonColor, 2); - } - - if (theme.mentionHighlightBg) { - changeCss('.mention--highlight, .search-highlight', 'background:' + theme.mentionHighlightBg, 1); - } - - if (theme.mentionHighlightBg) { - changeCss('.post.post--highlight', 'background:' + changeOpacity(theme.mentionHighlightBg, 0.5), 1); - } - - if (theme.mentionHighlightLink) { - changeCss('.mention--highlight .mention-link', 'color:' + theme.mentionHighlightLink, 1); - } - - if (!theme.codeTheme) { - theme.codeTheme = Constants.DEFAULT_CODE_THEME; - } - updateCodeTheme(theme.codeTheme); -} - -export function applyFont(fontName) { - const body = $('body'); - - for (const key of Reflect.ownKeys(Constants.FONTS)) { - const className = Constants.FONTS[key]; - - if (fontName === key) { - if (!body.hasClass(className)) { - body.addClass(className); - } - } else { - body.removeClass(className); - } - } -} - -export function changeCss(className, classValue, classRepeat) { - // we need invisible container to store additional css definitions - var cssMainContainer = $('#css-modifier-container'); - if (cssMainContainer.length === 0) { - cssMainContainer = $('<div id="css-modifier-container"></div>'); - cssMainContainer.hide(); - cssMainContainer.appendTo($('body')); - } - - // and we need one div for each class - var classContainer = cssMainContainer.find('div[data-class="' + className + classRepeat + '"]'); - if (classContainer.length === 0) { - classContainer = $('<div data-class="' + className + classRepeat + '"></div>'); - classContainer.appendTo(cssMainContainer); - } - - // append additional style - classContainer.html('<style>' + className + ' {' + classValue + '}</style>'); -} - -export function rgb2hex(rgbIn) { - if (/^#[0-9A-F]{6}$/i.test(rgbIn)) { - return rgbIn; - } - - var rgb = rgbIn.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); - function hex(x) { - return ('0' + parseInt(x, 10).toString(16)).slice(-2); - } - return '#' + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); -} - -export function updateCodeTheme(theme) { - const path = '/static/css/highlight/' + theme + '.css'; - const $link = $('link.code_theme'); - if (path !== $link.attr('href')) { - changeCss('code.hljs', 'visibility: hidden'); - var xmlHTTP = new XMLHttpRequest(); - xmlHTTP.open('GET', path, true); - xmlHTTP.onload = function onLoad() { - $link.attr('href', path); - if (isBrowserFirefox()) { - $link.one('load', () => { - changeCss('code.hljs', 'visibility: visible'); - }); - } else { - changeCss('code.hljs', 'visibility: visible'); - } - }; - xmlHTTP.send(); - } -} - -export function placeCaretAtEnd(el) { - el.focus(); - el.selectionStart = el.value.length; - el.selectionEnd = el.value.length; - - return; -} - -export function getCaretPosition(el) { - if (el.selectionStart) { - return el.selectionStart; - } else if (document.selection) { - el.focus(); - - var r = document.selection.createRange(); - if (r == null) { - return 0; - } - - var re = el.createTextRange(); - var rc = re.duplicate(); - re.moveToBookmark(r.getBookmark()); - rc.setEndPoint('EndToStart', re); - - return rc.text.length; - } - return 0; -} - -export function setSelectionRange(input, selectionStart, selectionEnd) { - if (input.setSelectionRange) { - input.focus(); - input.setSelectionRange(selectionStart, selectionEnd); - } else if (input.createTextRange) { - var range = input.createTextRange(); - range.collapse(true); - range.moveEnd('character', selectionEnd); - range.moveStart('character', selectionStart); - range.select(); - } -} - -export function setCaretPosition(input, pos) { - setSelectionRange(input, pos, pos); -} - -export function getSelectedText(input) { - var selectedText; - if (typeof document.selection !== 'undefined') { - input.focus(); - var sel = document.selection.createRange(); - selectedText = sel.text; - } else if (typeof input.selectionStart !== 'undefined') { - var startPos = input.selectionStart; - var endPos = input.selectionEnd; - selectedText = input.value.substring(startPos, endPos); - } - - return selectedText; -} - -export function isValidUsername(name) { - var error = ''; - if (!name) { - error = 'This field is required'; - } else if (name.length < Constants.MIN_USERNAME_LENGTH || name.length > Constants.MAX_USERNAME_LENGTH) { - error = 'Must be between ' + Constants.MIN_USERNAME_LENGTH + ' and ' + Constants.MAX_USERNAME_LENGTH + ' characters'; - } else if (!(/^[a-z0-9\.\-\_]+$/).test(name)) { - error = "Must contain only letters, numbers, and the symbols '.', '-', and '_'."; - } else if (!(/[a-z]/).test(name.charAt(0))) { //eslint-disable-line no-negated-condition - error = 'First character must be a letter.'; - } else { - for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) { - if (name === Constants.RESERVED_USERNAMES[i]) { - error = 'Cannot use a reserved word as a username.'; - break; - } - } - } - - return error; -} - -export function updateAddressBar(channelName) { - const teamURL = TeamStore.getCurrentTeamUrl(); - history.replaceState('data', '', teamURL + '/channels/' + channelName); -} - -export function switchChannel(channel) { - GlobalActions.emitChannelClickEvent(channel); - - updateAddressBar(channel.name); - - $('.inner-wrap').removeClass('move--right'); - $('.sidebar--left').removeClass('move--right'); - - client.trackPage(); - - return false; -} - -export function isMobile() { - return screen.width <= 768; -} - -export function isComment(post) { - if ('root_id' in post) { - return post.root_id !== '' && post.root_id != null; - } - return false; -} - -export function getDirectTeammate(channelId) { - var userIds = ChannelStore.get(channelId).name.split('__'); - var curUserId = UserStore.getCurrentId(); - var teammate = {}; - - if (userIds.length !== 2 || userIds.indexOf(curUserId) === -1) { - return teammate; - } - - for (var idx in userIds) { - if (userIds[idx] !== curUserId) { - teammate = UserStore.getProfile(userIds[idx]); - break; - } - } - - return teammate; -} - -Image.prototype.load = function imageLoad(url, progressCallback) { - var self = this; - var xmlHTTP = new XMLHttpRequest(); - xmlHTTP.open('GET', url, true); - xmlHTTP.responseType = 'arraybuffer'; - xmlHTTP.onload = function onLoad() { - var h = xmlHTTP.getAllResponseHeaders(); - var m = h.match(/^Content-Type\:\s*(.*?)$/mi); - var mimeType = m[1] || 'image/png'; - - var blob = new Blob([this.response], {type: mimeType}); - self.src = window.URL.createObjectURL(blob); - }; - xmlHTTP.onprogress = function onprogress(e) { - parseInt(self.completedPercentage = (e.loaded / e.total) * 100, 10); - if (progressCallback) { - progressCallback(); - } - }; - xmlHTTP.onloadstart = function onloadstart() { - self.completedPercentage = 0; - }; - xmlHTTP.send(); -}; - -Image.prototype.completedPercentage = 0; - -export function changeColor(colourIn, amt) { - var hex = colourIn; - var lum = amt; - - // validate hex string - hex = String(hex).replace(/[^0-9a-f]/gi, ''); - if (hex.length < 6) { - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - } - lum = lum || 0; - - // convert to decimal and change luminosity - var rgb = '#'; - var c; - var i; - for (i = 0; i < 3; i++) { - c = parseInt(hex.substr(i * 2, 2), 16); - c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); - rgb += ('00' + c).substr(c.length); - } - - return rgb; -} - -export function changeOpacity(oldColor, opacity) { - var color = oldColor; - if (color[0] === '#') { - color = color.slice(1); - } - - if (color.length === 3) { - const tempColor = color; - color = ''; - - color += tempColor[0] + tempColor[0]; - color += tempColor[1] + tempColor[1]; - color += tempColor[2] + tempColor[2]; - } - - var r = parseInt(color.substring(0, 2), 16); - var g = parseInt(color.substring(2, 4), 16); - var b = parseInt(color.substring(4, 6), 16); - - return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')'; -} - -export function getFullName(user) { - if (user.first_name && user.last_name) { - return user.first_name + ' ' + user.last_name; - } else if (user.first_name) { - return user.first_name; - } else if (user.last_name) { - return user.last_name; - } - - return ''; -} - -export function getDisplayName(user) { - if (user.nickname && user.nickname.trim().length > 0) { - return user.nickname; - } - var fullName = getFullName(user); - - if (fullName) { - return fullName; - } - - return user.username; -} - -export function displayUsername(userId) { - const user = UserStore.getProfile(userId); - const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'); - - let username = ''; - if (user) { - if (nameFormat === Constants.Preferences.DISPLAY_PREFER_NICKNAME) { - username = user.nickname || getFullName(user); - } else if (nameFormat === Constants.Preferences.DISPLAY_PREFER_FULL_NAME) { - username = getFullName(user); - } - if (!username.trim().length) { - username = user.username; - } - } - - return username; -} - -//IE10 does not set window.location.origin automatically so this must be called instead when using it -export function getWindowLocationOrigin() { - var windowLocationOrigin = window.location.origin; - if (!windowLocationOrigin) { - windowLocationOrigin = window.location.protocol + '//' + window.location.hostname; - if (window.location.port) { - windowLocationOrigin += ':' + window.location.port; - } - } - return windowLocationOrigin; -} - -// Converts a file size in bytes into a human-readable string of the form '123MB'. -export function fileSizeToString(bytes) { - // it's unlikely that we'll have files bigger than this - if (bytes > 1024 * 1024 * 1024 * 1024) { - return Math.floor(bytes / (1024 * 1024 * 1024 * 1024)) + 'TB'; - } else if (bytes > 1024 * 1024 * 1024) { - return Math.floor(bytes / (1024 * 1024 * 1024)) + 'GB'; - } else if (bytes > 1024 * 1024) { - return Math.floor(bytes / (1024 * 1024)) + 'MB'; - } else if (bytes > 1024) { - return Math.floor(bytes / 1024) + 'KB'; - } - - return bytes + 'B'; -} - -// Converts a filename (like those attached to Post objects) to a url that can be used to retrieve attachments from the server. -export function getFileUrl(filename, isDownload) { - const downloadParam = isDownload ? '?download=1' : ''; - return getWindowLocationOrigin() + '/api/v1/files/get' + filename + downloadParam; -} - -// Gets the name of a file (including extension) from a given url or file path. -export function getFileName(path) { - var split = path.split('/'); - return split[split.length - 1]; -} - -// Gets the websocket port to use. Configurable on the server. -export function getWebsocketPort(protocol) { - if ((/^wss:/).test(protocol)) { // wss:// - return ':' + global.window.mm_config.WebsocketSecurePort; - } - if ((/^ws:/).test(protocol)) { - return ':' + global.window.mm_config.WebsocketPort; - } - return ''; -} - -// Generates a RFC-4122 version 4 compliant globally unique identifier. -export function generateId() { - // implementation taken from http://stackoverflow.com/a/2117523 - var id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; - - id = id.replace(/[xy]/g, function replaceRandom(c) { - var r = Math.floor(Math.random() * 16); - - var v; - if (c === 'x') { - v = r; - } else { - v = r & 0x3 | 0x8; - } - - return v.toString(16); - }); - - return id; -} - -export function isBrowserFirefox() { - return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox') > -1; -} - -// Checks if browser is IE10 or IE11 -export function isBrowserIE() { - if (window.navigator && window.navigator.userAgent) { - var ua = window.navigator.userAgent; - - return ua.indexOf('Trident/7.0') > 0 || ua.indexOf('Trident/6.0') > 0; - } - - return false; -} - -export function isBrowserEdge() { - return window.navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1; -} - -export function getDirectChannelName(id, otherId) { - let handle; - - if (otherId > id) { - handle = id + '__' + otherId; - } else { - handle = otherId + '__' + id; - } - - return handle; -} - -// Used to get the id of the other user from a DM channel -export function getUserIdFromChannelName(channel) { - var ids = channel.name.split('__'); - var otherUserId = ''; - if (ids[0] === UserStore.getCurrentId()) { - otherUserId = ids[1]; - } else { - otherUserId = ids[0]; - } - - return otherUserId; -} - -// Returns true if the given channel is a direct channel between the current user and the given one -export function isDirectChannelForUser(otherUserId, channel) { - return channel.type === Constants.DM_CHANNEL && getUserIdFromChannelName(channel) === otherUserId; -} - -export function importSlack(file, success, error) { - var formData = new FormData(); - formData.append('file', file, file.name); - formData.append('filesize', file.size); - formData.append('importFrom', 'slack'); - - client.importSlack(formData, success, error); -} - -export function getTeamURLFromAddressBar() { - return window.location.href.split('/channels')[0]; -} - -export function getShortenedTeamURL() { - const teamURL = getTeamURLFromAddressBar(); - if (teamURL.length > 35) { - return teamURL.substring(0, 10) + '...' + teamURL.substring(teamURL.length - 12, teamURL.length) + '/'; - } - return teamURL + '/'; -} - -export function windowWidth() { - return $(window).width(); -} - -export function windowHeight() { - return $(window).height(); -} - -export function openDirectChannelToUser(user, successCb, errorCb) { - const channelName = this.getDirectChannelName(UserStore.getCurrentId(), user.id); - let channel = ChannelStore.getByName(channelName); - - const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, user.id, 'true'); - AsyncClient.savePreferences([preference]); - - if (channel) { - if ($.isFunction(successCb)) { - successCb(channel, true); - } - } else { - channel = { - name: channelName, - last_post_at: 0, - total_msg_count: 0, - type: 'D', - display_name: user.username, - teammate_id: user.id, - status: UserStore.getStatus(user.id) - }; - - Client.createDirectChannel( - channel, - user.id, - (data) => { - AsyncClient.getChannel(data.id); - if ($.isFunction(successCb)) { - successCb(data, false); - } - }, - () => { - window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName; - if ($.isFunction(errorCb)) { - errorCb(); - } - } - ); - } -} - -// Use when sorting multiple channels or teams by their `display_name` field -export function sortByDisplayName(a, b) { - let aDisplayName = ''; - let bDisplayName = ''; - - if (a && a.display_name) { - aDisplayName = a.display_name.toLowerCase(); - } - if (b && b.display_name) { - bDisplayName = b.display_name.toLowerCase(); - } - - if (aDisplayName < bDisplayName) { - return -1; - } - if (aDisplayName > bDisplayName) { - return 1; - } - return 0; -} - -export function getChannelTerm(channelType) { - let channelTerm = 'Channel'; - if (channelType === Constants.PRIVATE_CHANNEL) { - channelTerm = 'Group'; - } - - return channelTerm; -} - -export function getPostTerm(post) { - let postTerm = 'Post'; - if (post.root_id) { - postTerm = 'Comment'; - } - - return postTerm; -} - -export function isFeatureEnabled(feature) { - return PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, Constants.FeatureTogglePrefix + feature.label); -} - -export function isSystemMessage(post) { - return post.type && (post.type.lastIndexOf(Constants.SYSTEM_MESSAGE_PREFIX) === 0); -} - -export function fillArray(value, length) { - const arr = []; - - for (let i = 0; i < length; i++) { - arr.push(value); - } - - return arr; -} - -// Checks if a data transfer contains files not text, folders, etc.. -// Slightly modified from http://stackoverflow.com/questions/6848043/how-do-i-detect-a-file-is-being-dragged-rather-than-a-draggable-element-on-my-pa -export function isFileTransfer(files) { - if (isBrowserIE()) { - return files.types != null && files.types.contains('Files'); - } - - return files.types != null && (files.types.indexOf ? files.types.indexOf('Files') !== -1 : files.types.contains('application/x-moz-file')); -} - -export function clearFileInput(elm) { - // clear file input for all modern browsers - try { - elm.value = ''; - if (elm.value) { - elm.type = 'text'; - elm.type = 'file'; - } - } catch (e) { - // Do nothing - } -} - -export function languages() { - return ( - [ - { - value: 'en', - name: 'English' - }, - { - value: 'es', - name: 'EspaƱol (Beta)' - }, - { - value: 'pt', - name: 'Portugues (Beta)' - } - ] - ); -} - -export function isPostEphemeral(post) { - return post.type === Constants.POST_TYPE_EPHEMERAL || post.state === Constants.POST_DELETED; -} - -export function getRootId(post) { - return post.root_id === '' ? post.id : post.root_id; -} - -export function localizeMessage(id, defaultMessage) { - const translations = LocalizationStore.getTranslations(); - if (translations) { - const value = translations[id]; - if (value) { - return value; - } - } - - if (defaultMessage) { - return defaultMessage; - } - - return id; -} |