From cf7a05f80f68b5b1c8bcc0089679dd497cec2506 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Sun, 14 Jun 2015 23:53:32 -0800 Subject: first commit --- web/react/utils/async_client.jsx | 357 +++++++++++++++++ web/react/utils/client.jsx | 813 +++++++++++++++++++++++++++++++++++++++ web/react/utils/constants.jsx | 78 ++++ web/react/utils/utils.jsx | 732 +++++++++++++++++++++++++++++++++++ 4 files changed, 1980 insertions(+) create mode 100644 web/react/utils/async_client.jsx create mode 100644 web/react/utils/client.jsx create mode 100644 web/react/utils/constants.jsx create mode 100644 web/react/utils/utils.jsx (limited to 'web/react/utils') diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx new file mode 100644 index 000000000..bb7ca458f --- /dev/null +++ b/web/react/utils/async_client.jsx @@ -0,0 +1,357 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var client = require('./client.jsx'); +var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); +var ChannelStore = require('../stores/channel_store.jsx'); +var PostStore = require('../stores/post_store.jsx'); +var UserStore = require('../stores/user_store.jsx'); +var utils = require('./utils.jsx'); + +var Constants = require('./constants.jsx'); +var ActionTypes = Constants.ActionTypes; + +// Used to track in progress async calls +var callTracker = {}; + +var dispatchError = function(err, method) { + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_ERROR, + err: err, + method: method + }); +}; + +var isCallInProgress = function(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; +}; + +module.exports.dispatchError = dispatchError; + +module.exports.getChannels = function(force, updateLastViewed, checkVersion) { + if (isCallInProgress("getChannels")) return; + + if (ChannelStore.getAll().length == 0 || force) { + callTracker["getChannels"] = utils.getTimestamp(); + client.getChannels( + function(data, textStatus, xhr) { + callTracker["getChannels"] = 0; + + if (updateLastViewed && ChannelStore.getCurrentId() != null) { + module.exports.updateLastViewedAt(); + } + + if (checkVersion) { + var serverVersion = xhr.getResponseHeader("X-Version-ID"); + + if (UserStore.getLastVersion() == undefined) { + UserStore.setLastVersion(serverVersion); + } + + if (serverVersion != UserStore.getLastVersion()) { + UserStore.setLastVersion(serverVersion); + window.location.href = window.location.href; + console.log("Detected version update refreshing the page"); + } + } + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_CHANNELS, + channels: data.channels, + members: data.members + }); + + }, + function(err) { + callTracker["getChannels"] = 0; + dispatchError(err, "getChannels"); + } + ); + } +} + +module.exports.updateLastViewedAt = function() { + if (isCallInProgress("updateLastViewed")) return; + + if (ChannelStore.getCurrentId() == null) return; + + callTracker["updateLastViewed"] = utils.getTimestamp(); + client.updateLastViewedAt( + ChannelStore.getCurrentId(), + function(data) { + callTracker["updateLastViewed"] = 0; + }, + function(err) { + callTracker["updateLastViewed"] = 0; + dispatchError(err, "updateLastViewedAt"); + } + ); +} + +module.exports.getMoreChannels = function(force) { + if (isCallInProgress("getMoreChannels")) return; + + if (ChannelStore.getMoreAll().length == 0 || force) { + + callTracker["getMoreChannels"] = utils.getTimestamp(); + client.getMoreChannels( + function(data, textStatus, xhr) { + callTracker["getMoreChannels"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_MORE_CHANNELS, + channels: data.channels, + members: data.members + }); + }, + function(err) { + callTracker["getMoreChannels"] = 0; + dispatchError(err, "getMoreChannels"); + } + ); + } +} + +module.exports.getChannelExtraInfo = function(force) { + var channelId = ChannelStore.getCurrentId(); + + if (channelId != null) { + if (isCallInProgress("getChannelExtraInfo_"+channelId)) return; + var minMembers = ChannelStore.getCurrent() && ChannelStore.getCurrent().type === 'D' ? 1 : 0; + + if (ChannelStore.getCurrentExtraInfo().members.length <= minMembers || force) { + callTracker["getChannelExtraInfo_"+channelId] = utils.getTimestamp(); + client.getChannelExtraInfo( + channelId, + function(data, textStatus, xhr) { + callTracker["getChannelExtraInfo_"+channelId] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_CHANNEL_EXTRA_INFO, + extra_info: data + }); + }, + function(err) { + callTracker["getChannelExtraInfo_"+channelId] = 0; + dispatchError(err, "getChannelExtraInfo"); + } + ); + } + } +} + +module.exports.getProfiles = function() { + if (isCallInProgress("getProfiles")) return; + + callTracker["getProfiles"] = utils.getTimestamp(); + client.getProfiles( + function(data, textStatus, xhr) { + callTracker["getProfiles"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_PROFILES, + profiles: data + }); + }, + function(err) { + callTracker["getProfiles"] = 0; + dispatchError(err, "getProfiles"); + } + ); +} + +module.exports.getSessions = function() { + if (isCallInProgress("getSessions")) return; + + callTracker["getSessions"] = utils.getTimestamp(); + client.getSessions( + UserStore.getCurrentId(), + function(data, textStatus, xhr) { + callTracker["getSessions"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_SESSIONS, + sessions: data + }); + }, + function(err) { + callTracker["getSessions"] = 0; + dispatchError(err, "getSessions"); + } + ); +} + +module.exports.getAudits = function() { + if (isCallInProgress("getAudits")) return; + + callTracker["getAudits"] = utils.getTimestamp(); + client.getAudits( + UserStore.getCurrentId(), + function(data, textStatus, xhr) { + callTracker["getAudits"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_AUDITS, + audits: data + }); + }, + function(err) { + callTracker["getAudits"] = 0; + dispatchError(err, "getAudits"); + } + ); +} + +module.exports.findTeams = function(email) { + if (isCallInProgress("findTeams_"+email)) return; + + var user = UserStore.getCurrentUser(); + if (user) { + callTracker["findTeams_"+email] = utils.getTimestamp(); + client.findTeams( + user.email, + function(data, textStatus, xhr) { + callTracker["findTeams_"+email] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_TEAMS, + teams: data + }); + }, + function(err) { + callTracker["findTeams_"+email] = 0; + dispatchError(err, "findTeams"); + } + ); + } +} + +module.exports.search = function(terms) { + if (isCallInProgress("search_"+String(terms))) return; + + callTracker["search_"+String(terms)] = utils.getTimestamp(); + client.search( + terms, + function(data, textStatus, xhr) { + callTracker["search_"+String(terms)] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_SEARCH, + results: data + }); + }, + function(err) { + callTracker["search_"+String(terms)] = 0; + dispatchError(err, "search"); + } + ); +} + +module.exports.getPosts = function(force, id) { + if (PostStore.getCurrentPosts() == null || force) { + var channelId = id ? id : ChannelStore.getCurrentId(); + + if (isCallInProgress("getPosts_"+channelId)) return; + + var post_list = PostStore.getCurrentPosts(); + // 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 180 + var numPosts = post_list && post_list.order.length > 0 ? Math.min(180, Constants.POST_CHUNK_SIZE * Math.ceil(post_list.order.length / Constants.POST_CHUNK_SIZE)) : Constants.POST_CHUNK_SIZE; + + if (channelId != null) { + callTracker["getPosts_"+channelId] = utils.getTimestamp(); + client.getPosts( + channelId, + 0, + numPosts, + function(data, textStatus, xhr) { + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_POSTS, + id: channelId, + post_list: data + }); + + module.exports.getProfiles(); + }, + function(err) { + dispatchError(err, "getPosts"); + }, + function() { + callTracker["getPosts_"+channelId] = 0; + } + ); + } + } +} + +module.exports.getMe = function() { + if (isCallInProgress("getMe")) return; + + callTracker["getMe"] = utils.getTimestamp(); + client.getMeSynchronous( + function(data, textStatus, xhr) { + callTracker["getMe"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_ME, + me: data + }); + }, + function(err) { + callTracker["getMe"] = 0; + dispatchError(err, "getMe"); + } + ); +} + +module.exports.getStatuses = function() { + if (isCallInProgress("getStatuses")) return; + + callTracker["getStatuses"] = utils.getTimestamp(); + client.getStatuses( + function(data, textStatus, xhr) { + callTracker["getStatuses"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_STATUSES, + statuses: data + }); + }, + function(err) { + callTracker["getStatuses"] = 0; + dispatchError(err, "getStatuses"); + } + ); +} diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx new file mode 100644 index 000000000..b83ee22e7 --- /dev/null +++ b/web/react/utils/client.jsx @@ -0,0 +1,813 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + + +module.exports.track = function(category, action, label, prop, val) { + global.window.snowplow('trackStructEvent', category, action, label, prop, val); + if (global.window.analytics != null) global.window.analytics.track(action, {category: category, label: label, property: prop, value: val}); +}; + +module.exports.trackPage = function() { + global.window.snowplow('trackPageView'); + if (global.window.analytics != null) global.window.analytics.page(); +}; + +function handleError(method_name, xhr, status, err) { + var _LTracker = global.window._LTracker || []; + + var e = null; + try { + e = JSON.parse(xhr.responseText); + } + catch(parse_error) { + } + + var msg = ""; + + if (e) { + msg = "error in " + method_name + " msg=" + e.message + " detail=" + e.detailed_error + " rid=" + e.request_id; + } + else { + msg = "error in " + method_name + " status=" + status + " statusCode=" + xhr.status + " err=" + err; + + if (xhr.status === 0) + e = { message: "There appears to be a problem with your internet connection" }; + else + e = { message: "We received an unexpected status code from the server (" + xhr.status + ")"}; + } + + console.error(msg) + console.error(e); + _LTracker.push(msg); + + module.exports.track('api', 'api_weberror', method_name, 'message', msg); + + if (xhr.status == 401) { + window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search); + } + + return e; +} + +module.exports.createTeamFromSignup = function(team_signup, success, error) { + $.ajax({ + url: "/api/v1/teams/create_from_signup", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(team_signup), + success: success, + error: function(xhr, status, err) { + e = handleError("createTeamFromSignup", xhr, status, err); + error(e); + } + }); +}; + +module.exports.createUser = function(user, data, email_hash, success, error) { + $.ajax({ + url: "/api/v1/users/create?d=" + encodeURIComponent(data) + "&h=" + encodeURIComponent(email_hash), + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(user), + success: success, + error: function(xhr, status, err) { + e = handleError("createUser", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_create', user.team_id, 'email', user.email); +}; + +module.exports.updateUser = function(user, success, error) { + $.ajax({ + url: "/api/v1/users/update", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(user), + success: success, + error: function(xhr, status, err) { + e = handleError("updateUser", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_update'); +}; + +module.exports.updatePassword = function(data, success, error) { + $.ajax({ + url: "/api/v1/users/newpassword", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("newPassword", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_newpassword'); +}; + +module.exports.updateUserNotifyProps = function(data, success, error) { + $.ajax({ + url: "/api/v1/users/update_notify", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("updateUserNotifyProps", xhr, status, err); + error(e); + } + }); +}; + +module.exports.updateRoles = function(data, success, error) { + $.ajax({ + url: "/api/v1/users/update_roles", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("updateRoles", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_update_roles'); +}; + +module.exports.updateActive = function(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: success, + error: function(xhr, status, err) { + e = handleError("updateActive", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_update_roles'); +}; + +module.exports.sendPasswordReset = function(data, success, error) { + $.ajax({ + url: "/api/v1/users/send_password_reset", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("sendPasswordReset", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_send_password_reset'); +}; + +module.exports.resetPassword = function(data, success, error) { + $.ajax({ + url: "/api/v1/users/reset_password", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("resetPassword", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_users_reset_password'); +}; + +module.exports.logout = function() { + module.exports.track('api', 'api_users_logout'); + sessionStorage.clear(); + window.location.href = "/logout"; +}; + +module.exports.loginByEmail = function(domain, email, password, success, error) { + $.ajax({ + url: "/api/v1/users/login", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({domain: domain, email: email, password: password}), + success: function(data, textStatus, xhr) { + module.exports.track('api', 'api_users_login_success', data.team_id, 'email', data.email); + success(data, textStatus, xhr); + }, + error: function(xhr, status, err) { + module.exports.track('api', 'api_users_login_fail', window.getSubDomain(), 'email', email); + + e = handleError("loginByEmail", xhr, status, err); + error(e); + } + }); +}; + +module.exports.revokeSession = function(altId, success, error) { + $.ajax({ + url: "/api/v1/users/revoke_session", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({id: altId}), + success: success, + error: function(xhr, status, err) { + e = handleError("revokeSession", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getSessions = function(userId, success, error) { + $.ajax({ + url: "/api/v1/users/"+userId+"/sessions", + dataType: 'json', + contentType: 'application/json', + type: 'GET', + success: success, + error: function(xhr, status, err) { + e = handleError("getSessions", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getAudits = function(userId, success, error) { + $.ajax({ + url: "/api/v1/users/"+userId+"/audits", + dataType: 'json', + contentType: 'application/json', + type: 'GET', + success: success, + error: function(xhr, status, err) { + e = handleError("getAudits", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getMeSynchronous = function(success, error) { + + var current_user = null; + + $.ajax({ + async: false, + url: "/api/v1/users/me", + dataType: 'json', + contentType: 'application/json', + type: 'GET', + success: function(data, textStatus, xhr) { + current_user = data; + if (success) success(data, textStatus, xhr); + }, + error: function(xhr, status, err) { + if (error) { + e = handleError("getMeSynchronous", xhr, status, err); + error(e); + }; + } + }); + + return current_user; +}; + +module.exports.inviteMembers = function(data, success, error) { + $.ajax({ + url: "/api/v1/teams/invite_members", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("inviteMembers", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_teams_invite_members'); +}; + +module.exports.updateTeamName = function(data, success, error) { + $.ajax({ + url: "/api/v1/teams/update_name", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("updateTeamName", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_teams_update_name'); +}; + +module.exports.signupTeam = function(email, name, success, error) { + $.ajax({ + url: "/api/v1/teams/signup", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({email: email, name: name}), + success: success, + error: function(xhr, status, err) { + e = handleError("singupTeam", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_teams_signup'); +}; + +module.exports.createTeam = function(team, success, error) { + $.ajax({ + url: "/api/v1/teams/create", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(team), + success: success, + error: function(xhr, status, err) { + e = handleError("createTeam", xhr, status, err); + error(e); + } + }); +}; + +module.exports.findTeamByDomain = function(domain, success, error) { + $.ajax({ + url: "/api/v1/teams/find_team_by_domain", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({domain: domain}), + success: success, + error: function(xhr, status, err) { + e = handleError("findTeamByDomain", xhr, status, err); + error(e); + } + }); +}; + +module.exports.findTeamsSendEmail = function(email, success, error) { + $.ajax({ + url: "/api/v1/teams/email_teams", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({email: email}), + success: success, + error: function(xhr, status, err) { + e = handleError("findTeamsSendEmail", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_teams_email_teams'); +}; + +module.exports.findTeams = function(email, success, error) { + $.ajax({ + url: "/api/v1/teams/find_teams", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({email: email}), + success: success, + error: function(xhr, status, err) { + e = handleError("findTeams", xhr, status, err); + error(e); + } + }); +}; + +module.exports.createChannel = function(channel, success, error) { + $.ajax({ + url: "/api/v1/channels/create", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(channel), + success: success, + error: function(xhr, status, err) { + e = handleError("createChannel", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_create', channel.type, 'name', channel.name); +}; + +module.exports.updateChannel = function(channel, success, error) { + $.ajax({ + url: "/api/v1/channels/update", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(channel), + success: success, + error: function(xhr, status, err) { + e = handleError("updateChannel", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_update'); +}; + +module.exports.updateChannelDesc = function(data, success, error) { + $.ajax({ + url: "/api/v1/channels/update_desc", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("updateChannelDesc", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_desc'); +}; + +module.exports.updateNotifyLevel = function(data, success, error) { + $.ajax({ + url: "/api/v1/channels/update_notify_level", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("updateNotifyLevel", xhr, status, err); + error(e); + } + }); +}; + +module.exports.joinChannel = function(id, success, error) { + $.ajax({ + url: "/api/v1/channels/" + id + "/join", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + success: success, + error: function(xhr, status, err) { + e = handleError("joinChannel", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_join'); +}; + +module.exports.leaveChannel = function(id, success, error) { + $.ajax({ + url: "/api/v1/channels/" + id + "/leave", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + success: success, + error: function(xhr, status, err) { + e = handleError("leaveChannel", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_leave'); +}; + +module.exports.deleteChannel = function(id, success, error) { + $.ajax({ + url: "/api/v1/channels/" + id + "/delete", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + success: success, + error: function(xhr, status, err) { + e = handleError("deleteChannel", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_delete'); +}; + +module.exports.updateLastViewedAt = function(channelId, success, error) { + $.ajax({ + url: "/api/v1/channels/" + channelId + "/update_last_viewed_at", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + success: success, + error: function(xhr, status, err) { + e = handleError("updateLastViewedAt", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getChannels = function(success, error) { + $.ajax({ + url: "/api/v1/channels/", + dataType: 'json', + type: 'GET', + success: success, + ifModified: true, + error: function(xhr, status, err) { + e = handleError("getChannels", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getMoreChannels = function(success, error) { + $.ajax({ + url: "/api/v1/channels/more", + dataType: 'json', + type: 'GET', + success: success, + ifModified: true, + error: function(xhr, status, err) { + e = handleError("getMoreChannels", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getChannelExtraInfo = function(id, success, error) { + $.ajax({ + url: "/api/v1/channels/" + id + "/extra_info", + dataType: 'json', + type: 'GET', + success: success, + error: function(xhr, status, err) { + e = handleError("getChannelExtraInfo", xhr, status, err); + error(e); + } + }); +}; + +module.exports.executeCommand = function(channelId, command, suggest, success, error) { + $.ajax({ + url: "/api/v1/command", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify({channelId: channelId, command: command, suggest: "" + suggest}), + success: success, + error: function(xhr, status, err) { + e = handleError("executeCommand", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getPosts = function(channelId, offset, limit, success, error, complete) { + $.ajax({ + url: "/api/v1/channels/" + channelId + "/posts/" + offset + "/" + limit, + dataType: 'json', + type: 'GET', + ifModified: true, + success: success, + error: function(xhr, status, err) { + try { + e = handleError("getPosts", xhr, status, err); + error(e); + } catch(er) { + console.error(er); + } + }, + complete: complete + }); +}; + +module.exports.getPost = function(channelId, postId, success, error) { + $.ajax({ + url: "/api/v1/channels/" + channelId + "/post/" + postId, + dataType: 'json', + type: 'GET', + ifModified: false, + success: success, + error: function(xhr, status, err) { + e = handleError("getPost", xhr, status, err); + error(e); + } + }); +}; + +module.exports.search = function(terms, success, error) { + $.ajax({ + url: "/api/v1/posts/search", + dataType: 'json', + type: 'GET', + data: {"terms": terms}, + success: success, + error: function(xhr, status, err) { + e = handleError("search", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_posts_search'); +}; + +module.exports.deletePost = function(channelId, id, success, error) { + $.ajax({ + url: "/api/v1/channels/" + channelId + "/post/" + id + "/delete", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + success: success, + error: function(xhr, status, err) { + e = handleError("deletePost", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_posts_delete'); +}; + +module.exports.createPost = function(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: success, + error: function(xhr, status, err) { + e = handleError("createPost", xhr, status, err); + error(e); + } + }); + + module.exports.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("//g") || []).length + // }); +}; + +module.exports.updatePost = function(post, success, error) { + $.ajax({ + url: "/api/v1/channels/"+ post.channel_id + "/update", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(post), + success: success, + error: function(xhr, status, err) { + e = handleError("updatePost", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_posts_update'); +}; + +module.exports.addChannelMember = function(id, data, success, error) { + $.ajax({ + url: "/api/v1/channels/" + id + "/add", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("addChannelMember", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_add_member'); +}; + +module.exports.removeChannelMember = function(id, data, success, error) { + $.ajax({ + url: "/api/v1/channels/" + id + "/remove", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("removeChannelMember", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_channels_remove_member'); +}; + +module.exports.getProfiles = function(success, error) { + $.ajax({ + url: "/api/v1/users/profiles", + dataType: 'json', + contentType: 'application/json', + type: 'GET', + success: success, + ifModified: true, + error: function(xhr, status, err) { + e = handleError("getProfiles", xhr, status, err); + error(e); + } + }); +}; + +module.exports.uploadFile = function(formData, success, error) { + $.ajax({ + url: "/api/v1/files/upload", + type: 'POST', + data: formData, + cache: false, + contentType: false, + processData: false, + success: success, + error: function(xhr, status, err) { + e = handleError("uploadFile", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_files_upload'); +}; + +module.exports.getPublicLink = function(data, success, error) { + $.ajax({ + url: "/api/v1/files/get_public_link", + dataType: 'json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("getPublicLink", xhr, status, err); + error(e); + } + }); +}; + +module.exports.uploadProfileImage = function(imageData, success, error) { + $.ajax({ + url: "/api/v1/users/newimage", + type: 'POST', + data: imageData, + cache: false, + contentType: false, + processData: false, + success: success, + error: function(xhr, status, err) { + e = handleError("uploadProfileImage", xhr, status, err); + error(e); + } + }); +}; + +module.exports.getStatuses = function(success, error) { + $.ajax({ + url: "/api/v1/users/status", + dataType: 'json', + contentType: 'application/json', + type: 'GET', + success: success, + error: function(xhr, status, err) { + e = handleError("getStatuses", xhr, status, err); + error(e); + } + }); +}; diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx new file mode 100644 index 000000000..0a3b1db3d --- /dev/null +++ b/web/react/utils/constants.jsx @@ -0,0 +1,78 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var keyMirror = require('keymirror'); + +module.exports = { + ActionTypes: keyMirror({ + RECIEVED_ERROR: null, + + CLICK_CHANNEL: null, + CREATE_CHANNEL: null, + RECIEVED_CHANNELS: null, + RECIEVED_MORE_CHANNELS: null, + RECIEVED_CHANNEL_EXTRA_INFO: null, + + RECIEVED_POSTS: null, + RECIEVED_SEARCH: null, + RECIEVED_POST_SELECTED: null, + RECIEVED_MENTION_DATA: null, + RECIEVED_ADD_MENTION: null, + + RECIEVED_PROFILES: null, + RECIEVED_ME: null, + RECIEVED_SESSIONS: null, + RECIEVED_AUDITS: null, + RECIEVED_TEAMS: null, + RECIEVED_STATUSES: null, + + RECIEVED_MSG: null, + }), + + PayloadSources: keyMirror({ + SERVER_ACTION: null, + VIEW_ACTION: null + }), + CHARACTER_LIMIT: 4000, + IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png'], + AUDIO_TYPES: ['mp3', 'wav', 'wma', 'm4a', 'flac', 'aac'], + VIDEO_TYPES: ['mp4', 'avi', 'webm', 'mkv', 'wmv', 'mpg', 'mov', 'flv'], + SPREADSHEET_TYPES: ['ppt', 'pptx', 'csv'], + EXCEL_TYPES: ['xlsx'], + 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': 'ppt', 'pdf': 'pdf', 'code': 'code' , 'word': 'word' , 'excel': 'excel' , 'patch': 'patch', 'other': 'generic'}, + MAX_DISPLAY_FILES: 5, + MAX_FILE_SIZE: 50000000, // 50 MB + DEFAULT_CHANNEL: 'town-square', + POST_CHUNK_SIZE: 60, + RESERVED_DOMAINS: [ + "www", + "web", + "admin", + "support", + "notify", + "test", + "demo", + "mail", + "team", + "channel", + "internal", + "localhost", + "stag", + "post", + "cluster", + "api", + ], + RESERVED_USERNAMES: [ + "valet", + "all", + "channel", + ], + MONTHS: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + MAX_DMS: 10, + ONLINE_ICON_SVG: "", + OFFLINE_ICON_SVG: "" +}; diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx new file mode 100644 index 000000000..72ed48faf --- /dev/null +++ b/web/react/utils/utils.jsx @@ -0,0 +1,732 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); +var UserStore = require('../stores/user_store.jsx'); +var Constants = require('../utils/constants.jsx'); +var ActionTypes = Constants.ActionTypes; +var AsyncClient = require('./async_client.jsx'); +var client = require('./client.jsx'); +var LinkifyIt = require('linkify-it'); + +module.exports.isEmail = function(email) { + var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/; + return regex.test(email); +}; + +module.exports.cleanUpUrlable = function(input) { + var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); + cleaned = cleaned.replace(/^\-+/, ''); + cleaned = cleaned.replace(/\-+$/, ''); + return cleaned; +}; + + + +module.exports.isTestDomain = function() { + + if ((/^localhost/).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; +}; + +var getSubDomain = function() { + + if (module.exports.isTestDomain()) + return ""; + + if ((/^www/).test(window.location.hostname)) + return ""; + + if ((/^beta/).test(window.location.hostname)) + return ""; + + if ((/^ci/).test(window.location.hostname)) + return ""; + + var parts = window.location.hostname.split("."); + + if (parts.length != 3) + return ""; + + return parts[0]; +} + +global.window.getSubDomain = getSubDomain; +module.exports.getSubDomain = getSubDomain; + +module.exports.getDomainWithOutSub = function() { + + var parts = window.location.host.split("."); + + if (parts.length == 1) + return "localhost:8065"; + + return parts[1] + "." + parts[2]; +} + +module.exports.getCookie = function(name) { + var value = "; " + document.cookie; + var parts = value.split("; " + name + "="); + if (parts.length == 2) return parts.pop().split(";").shift(); +} + +module.exports.notifyMe = function(title, body, channel) { + if ("Notification" in window && Notification.permission !== 'denied') { + Notification.requestPermission(function (permission) { + if (Notification.permission !== permission) { + Notification.permission = permission; + } + + if (permission === "granted") { + var notification = new Notification(title, + { body: body, tag: body, icon: '/static/images/icon50x50.gif' } + ); + notification.onclick = function() { + window.focus(); + if (channel) { + module.exports.switchChannel(channel); + } else { + window.location.href = "/channels/town-square"; + } + }; + setTimeout(function(){ + notification.close(); + }, 5000); + } + }); + } +} + +module.exports.ding = function() { + var audio = new Audio('/static/images/ding.mp3'); + audio.play(); +} + +module.exports.getUrlParameter = function(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; +} + +module.exports.getDateForUnixTicks = function(ticks) { + return new Date(ticks) +} + +module.exports.displayDate = function(ticks) { + var d = new Date(ticks); + var m_names = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); + + return m_names[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear(); +} + +module.exports.displayTime = function(ticks) { + var d = new Date(ticks); + var hours = d.getHours(); + var minutes = d.getMinutes(); + var ampm = hours >= 12 ? "PM" : "AM"; + hours = hours % 12; + hours = hours ? hours : "12" + minutes = minutes > 9 ? minutes : '0'+minutes + return hours + ":" + minutes + " " + ampm +} + +module.exports.displayDateTime = function(ticks) { + var seconds = Math.floor((Date.now() - ticks) / 1000) + + 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 > 1) { + return interval + " minutes ago"; + } + + return "1 minute ago"; + +} + +// returns Unix timestamp in milliseconds +module.exports.getTimestamp = function() { + return Date.now(); +} + +module.exports.extractLinks = function(text) { + var repRegex = new RegExp("
", "g"); + var linkMatcher = new LinkifyIt(); + var matches = linkMatcher.match(text.replace(repRegex, "\n")); + + if (!matches) return { "links": null, "text": text }; + + var links = [] + for (var i = 0; i < matches.length; i++) { + links.push(matches[i].url) + } + + return { "links": links, "text": text }; +} + +module.exports.escapeRegExp = function(string) { + return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); +} + +module.exports.getEmbed = function(link) { + + var ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; + + var match = link.trim().match(ytRegex); + if (match && match[1].length==11){ + return getYoutubeEmbed(link); + } + + // Generl embed feature turned off for now + return; + + var id = parseInt((Math.random() * 1000000) + 1); + + $.ajax({ + type: 'GET', + url: "https://query.yahooapis.com/v1/public/yql", + data: { + q: "select * from html where url=\""+link+"\" and xpath='html/head'", + format: "json" + }, + async: true + }).done(function(data) { + if(!data.query.results) { + return; + } + + var headerData = data.query.results.head; + + var description = "" + for(var i = 0; i < headerData.meta.length; i++) { + if(headerData.meta[i].name && (headerData.meta[i].name === "description" || headerData.meta[i].name === "Description")){ + description = headerData.meta[i].content; + break; + } + } + + $('.embed-title.'+id).html(headerData.title); + $('.embed-description.'+id).html(description); + }) + + return ( +
+
+

+

+

{link}

+
+
+ ); +} + +var getYoutubeEmbed = function(link) { + var regex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; + + var youtubeId = link.trim().match(regex)[1]; + + var onclick = function(e) { + var div = $(e.target).closest('.video-thumbnail__container')[0]; + var iframe = document.createElement("iframe"); + iframe.setAttribute("src", + "https://www.youtube.com/embed/" + div.id + + "?autoplay=1&autohide=1&border=0&wmode=opaque&enablejsapi=1"); + iframe.setAttribute("width", "480px"); + iframe.setAttribute("height", "360px"); + iframe.setAttribute("type", "text/html"); + iframe.setAttribute("frameborder", "0"); + + div.parentNode.replaceChild(iframe, div); + }; + + var success = function(data) { + $('.video-uploader.'+youtubeId).html(data.data.uploader); + $('.video-title.'+youtubeId).find('a').html(data.data.title); + $(".post-list-holder-by-time").scrollTop($(".post-list-holder-by-time")[0].scrollHeight); + $(".post-list-holder-by-time").perfectScrollbar('update'); + }; + + $.ajax({ + async: true, + url: 'https://gdata.youtube.com/feeds/api/videos/'+youtubeId+'?v=2&alt=jsonc', + type: 'GET', + success: success + }); + + return ( +
+

YouTube

+

+

+
+
+
+ +
+ +
+
+
+
+
+ ); + +} + +module.exports.areStatesEqual = function(state1, state2) { + return JSON.stringify(state1) === JSON.stringify(state2); +} + +module.exports.replaceHtmlEntities = function(text) { + var tagsToReplace = { + '&': '&', + '<': '<', + '>': '>' + }; + for (var tag in tagsToReplace) { + var regex = new RegExp(tag, "g"); + text = text.replace(regex, tagsToReplace[tag]); + } + return text; +} + +module.exports.insertHtmlEntities = function(text) { + var tagsToReplace = { + '&': '&', + '<': '<', + '>': '>' + }; + for (var tag in tagsToReplace) { + var regex = new RegExp(tag, "g"); + text = text.replace(regex, tagsToReplace[tag]); + } + return text; +} + +module.exports.searchForTerm = function(term) { + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_SEARCH_TERM, + term: term, + do_search: true + }); +} + +var oldExplicitMentionRegex = /(?:)([\s\S]*?)(?:<\/mention>)/g; +var puncStartRegex = /^((?![@#])\W)+/g; +var puncEndRegex = /(\W)+$/g; + +module.exports.textToJsx = function(text, options) { + + if (options && options['singleline']) { + var repRegex = new RegExp("\n", "g"); + text = text.replace(repRegex, " "); + } else { + var repRegex = new RegExp("\n", "g"); + text = text.replace(repRegex, "
"); + } + + var searchTerm = "" + if (options && options['searchTerm']) { + searchTerm = options['searchTerm'].toLowerCase() + } + + var mentionClass = "mention-highlight"; + if (options && options['noMentionHighlight']) { + mentionClass = ""; + } + + var inner = []; + + // Function specific regexes + var hashRegex = /^href="#[^"]+"|(#[A-Za-z]+[A-Za-z0-9_]*[A-Za-z0-9])$/g; + + var implicitKeywords = {}; + var keywordArray = UserStore.getCurrentMentionKeys(); + for (var i = 0; i < keywordArray.length; i++) { + implicitKeywords[keywordArray[i]] = true; + } + + var lines = text.split("
"); + var urlMatcher = new LinkifyIt(); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var words = line.split(" "); + var highlightSearchClass = ""; + for (var z = 0; z < words.length; z++) { + var word = words[z]; + var trimWord = word.replace(puncStartRegex, '').replace(puncEndRegex, '').trim(); + var mentionRegex = /^(?:@)([a-z0-9_]+)$/gi; // looks loop invariant but a weird JS bug needs it to be redefined here + var explicitMention = mentionRegex.exec(trimWord); + + if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm != "") { + + highlightSearchClass = " search-highlight"; + } + + if (explicitMention && UserStore.getProfileByUsername(explicitMention[1])) { + var name = explicitMention[1]; + // do both a non-case sensitive and case senstive check + var mClass = (name.toLowerCase() in implicitKeywords || name in implicitKeywords) ? mentionClass : ""; + + var suffix = word.match(puncEndRegex); + var prefix = word.match(puncStartRegex); + + if (searchTerm === name) { + highlightSearchClass = " search-highlight"; + } + + inner.push({prefix}@{name}{suffix} ); + } else if (urlMatcher.test(word)) { + var match = urlMatcher.match(word)[0]; + var link = match.url; + + var prefix = word.substring(0,word.indexOf(match.raw)) + var suffix = word.substring(word.indexOf(match.raw)+match.raw.length); + + inner.push({prefix}{match.raw}{suffix} ); + + } else if (trimWord.match(hashRegex)) { + var suffix = word.match(puncEndRegex); + var prefix = word.match(puncStartRegex); + var mClass = trimWord in implicitKeywords ? mentionClass : ""; + + if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) { + highlightSearchClass = " search-highlight"; + } + + inner.push({prefix}{trimWord}{suffix} ); + + } else if (trimWord in implicitKeywords) { + var suffix = word.match(puncEndRegex); + var prefix = word.match(puncStartRegex); + + if (trimWord.charAt(0) === '@') { + if (searchTerm === trimWord.substring(1).toLowerCase()) { + highlightSearchClass = " search-highlight"; + } + inner.push({prefix}{trimWord}{suffix} ); + } else { + inner.push({prefix}{module.exports.replaceHtmlEntities(trimWord)}{suffix} ); + } + + } else if (word === "") { + // if word is empty dont include a span + } else { + inner.push({module.exports.replaceHtmlEntities(word)} ); + } + highlightSearchClass = ""; + } + if (i != lines.length-1) + inner.push(
); + } + + return inner; +} + +module.exports.getFileType = function(ext) { + ext = ext.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.EXCEL_TYPES.indexOf(ext) > -1) { + return "excel"; + } + + if (Constants.PDF_TYPES.indexOf(ext) > -1) { + return "pdf"; + } + + if (Constants.PATCH_TYPES.indexOf(ext) > -1) { + return "patch"; + } + + return "other"; +}; + +module.exports.getIconClassName = function(fileType) { + fileType = fileType.toLowerCase(); + + if (fileType in Constants.ICON_FROM_TYPE) + return Constants.ICON_FROM_TYPE[fileType]; + + return "glyphicon-file"; +} + +module.exports.splitFileLocation = function(fileLocation) { + var fileSplit = fileLocation.split('.'); + if (fileSplit.length < 2) return {}; + + var 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}; +} + +module.exports.toTitleCase = function(str) { + return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); +} + +module.exports.changeCss = function(className, classValue) { + // we need invisible container to store additional css definitions + var cssMainContainer = $('#css-modifier-container'); + if (cssMainContainer.length == 0) { + var cssMainContainer = $('
'); + cssMainContainer.hide(); + cssMainContainer.appendTo($('body')); + } + + // and we need one div for each class + classContainer = cssMainContainer.find('div[data-class="' + className + '"]'); + if (classContainer.length == 0) { + classContainer = $('
'); + classContainer.appendTo(cssMainContainer); + } + + // append additional style + classContainer.html(''); +} + +module.exports.rgb2hex = function(rgb) { + if (/^#[0-9A-F]{6}$/i.test(rgb)) return rgb; + + rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); + function hex(x) { + return ("0" + parseInt(x).toString(16)).slice(-2); + } + return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); +} + +module.exports.placeCaretAtEnd = function(el) { + el.focus(); + if (typeof window.getSelection != "undefined" + && typeof document.createRange != "undefined") { + var range = document.createRange(); + range.selectNodeContents(el); + range.collapse(false); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } else if (typeof document.body.createTextRange != "undefined") { + var textRange = document.body.createTextRange(); + textRange.moveToElementText(el); + textRange.collapse(false); + textRange.select(); + } +} + +module.exports.getCaretPosition = function(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(), + rc = re.duplicate(); + re.moveToBookmark(r.getBookmark()); + rc.setEndPoint('EndToStart', re); + + return rc.text.length; + } + return 0; +} + +module.exports.setSelectionRange = function(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(); + } +} + +module.exports.setCaretPosition = function (input, pos) { + module.exports.setSelectionRange(input, pos, pos); +} + +module.exports.getSelectedText = function(input) { + var selectedText; + if (document.selection != undefined) { + input.focus(); + var sel = document.selection.createRange(); + selectedText = sel.text; + } else if (input.selectionStart != undefined) { + var startPos = input.selectionStart; + var endPos = input.selectionEnd; + selectedText = input.value.substring(startPos, endPos) + } + + return selectedText; +} + +module.exports.isValidUsername = function (name) { + + var error = "" + if (!name) { + error = "This field is required"; + } + + else if (name.length < 3 || name.length > 15) + { + error = "Must be between 3 and 15 characters"; + } + + else if (!/^[a-z0-9\.\-\_]+$/.test(name)) + { + error = "Must contain only lowercase letters, numbers, and the symbols '.', '-', and '_'."; + } + + else if (!/[a-z]/.test(name.charAt(0))) + { + error = "First character must be a letter."; + } + + else + { + var lowerName = name.toLowerCase().trim(); + + for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) + { + if (lowerName === Constants.RESERVED_USERNAMES[i]) + { + error = "Cannot use a reserved word as a username."; + break; + } + } + } + + return error; +} + +module.exports.switchChannel = function(channel, teammate_name) { + AppDispatcher.handleViewAction({ + type: ActionTypes.CLICK_CHANNEL, + name: channel.name, + id: channel.id + }); + + var domain = window.location.href.split('/channels')[0]; + history.replaceState('data', '', domain + '/channels/' + channel.name); + + if (channel.type === 'D' && teammate_name) { + document.title = teammate_name + " " + document.title.substring(document.title.lastIndexOf("-")); + } else { + document.title = channel.display_name + " " + document.title.substring(document.title.lastIndexOf("-")); + } + + AsyncClient.getChannels(true, true, true); + AsyncClient.getChannelExtraInfo(true); + AsyncClient.getPosts(true, channel.id); + + $('.inner__wrap').removeClass('move--right'); + $('.sidebar--left').removeClass('move--right'); + + client.trackPage(); + + return false; +} + +module.exports.isMobile = function() { + return screen.width <= 768; +} + +module.exports.isComment = function(post) { + if ('root_id' in post) { + return post.root_id != ""; + } + return false; +} + +Image.prototype.load = function(url, progressCallback) { + var thisImg = this; + var xmlHTTP = new XMLHttpRequest(); + xmlHTTP.open('GET', url, true); + xmlHTTP.responseType = 'arraybuffer'; + xmlHTTP.onload = function(e) { + var h = xmlHTTP.getAllResponseHeaders(), + m = h.match( /^Content-Type\:\s*(.*?)$/mi ), + mimeType = m[ 1 ] || 'image/png'; + + var blob = new Blob([this.response], { type: mimeType }); + thisImg.src = window.URL.createObjectURL(blob); + }; + xmlHTTP.onprogress = function(e) { + parseInt(thisImg.completedPercentage = (e.loaded / e.total) * 100); + if (progressCallback) progressCallback(); + }; + xmlHTTP.onloadstart = function() { + thisImg.completedPercentage = 0; + }; + xmlHTTP.send(); +}; + +Image.prototype.completedPercentage = 0; -- cgit v1.2.3-1-g7c22