diff --git a/syweb/webclient/app.js b/syweb/webclient/app.js
index 35190a71f4..f9d5c18ec4 100644
--- a/syweb/webclient/app.js
+++ b/syweb/webclient/app.js
@@ -33,6 +33,7 @@ var matrixWebClient = angular.module('matrixWebClient', [
'notificationService',
'recentsService',
'modelService',
+ 'commandsService',
'infinite-scroll',
'ui.bootstrap',
'monospaced.elastic'
diff --git a/syweb/webclient/components/matrix/commands-service.js b/syweb/webclient/components/matrix/commands-service.js
new file mode 100644
index 0000000000..3c516ad1e4
--- /dev/null
+++ b/syweb/webclient/components/matrix/commands-service.js
@@ -0,0 +1,164 @@
+/*
+Copyright 2014 OpenMarket Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+'use strict';
+
+/*
+This service contains logic for parsing and performing IRC style commands.
+*/
+angular.module('commandsService', [])
+.factory('commandsService', ['$q', '$location', 'matrixService', 'modelService', function($q, $location, matrixService, modelService) {
+
+ // create a rejected promise with the given message
+ var reject = function(msg) {
+ var deferred = $q.defer();
+ deferred.reject({
+ data: {
+ error: msg
+ }
+ });
+ return deferred.promise;
+ };
+
+ // Change your nickname
+ var doNick = function(room_id, args) {
+ if (args) {
+ return matrixService.setDisplayName(args);
+ }
+ return reject("Usage: /nick <display_name>");
+ };
+
+ // Join a room
+ var doJoin = function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+)$/);
+ if (matches) {
+ var room_alias = matches[1];
+ $location.url("room/" + room_alias);
+ // NB: We don't need to actually do the join, since that happens
+ // automatically if we are not joined onto a room already when
+ // the page loads.
+ return reject("Joining "+room_alias);
+ }
+ }
+ return reject("Usage: /join <room_alias>");
+ };
+
+ // Kick a user from the room with an optional reason
+ var doKick = function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+?)( +(.*))?$/);
+ if (matches) {
+ return matrixService.kick(room_id, matches[1], matches[3]);
+ }
+ }
+ return reject("Usage: /kick <userId> [<reason>]");
+ };
+
+ // Ban a user from the room with an optional reason
+ var doBan = function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+?)( +(.*))?$/);
+ if (matches) {
+ return matrixService.ban(room_id, matches[1], matches[3]);
+ }
+ }
+ return reject("Usage: /ban <userId> [<reason>]");
+ };
+
+ // Unban a user from the room
+ var doUnban = function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+)$/);
+ if (matches) {
+ // Reset the user membership to "leave" to unban him
+ return matrixService.unban(room_id, matches[1]);
+ }
+ }
+ return reject("Usage: /unban <userId>");
+ };
+
+ // Define the power level of a user
+ var doOp = function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+?)( +(\d+))?$/);
+ var powerLevel = 50; // default power level for op
+ if (matches) {
+ var user_id = matches[1];
+ if (matches.length === 4 && undefined !== matches[3]) {
+ powerLevel = parseInt(matches[3]);
+ }
+ if (powerLevel !== NaN) {
+ var powerLevelEvent = modelService.getRoom(room_id).current_room_state.state("m.room.power_levels");
+ return matrixService.setUserPowerLevel(room_id, user_id, powerLevel, powerLevelEvent);
+ }
+ }
+ }
+ return reject("Usage: /op <userId> [<power level>]");
+ };
+
+ // Reset the power level of a user
+ var doDeop = function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+)$/);
+ if (matches) {
+ var powerLevelEvent = modelService.getRoom(room_id).current_room_state.state("m.room.power_levels");
+ return matrixService.setUserPowerLevel(room_id, args, undefined, powerLevelEvent);
+ }
+ }
+ return reject("Usage: /deop <userId>");
+ };
+
+
+ var commands = {
+ "nick": doNick,
+ "join": doJoin,
+ "kick": doKick,
+ "ban": doBan,
+ "unban": doUnban,
+ "op": doOp,
+ "deop": doDeop
+ };
+
+ return {
+
+ /**
+ * Process the given text for commands and perform them.
+ * @param {String} roomId The room in which the input was performed.
+ * @param {String} input The raw text input by the user.
+ * @return {Promise} A promise of the pending command, or null if the
+ * input is not a command.
+ */
+ processInput: function(roomId, input) {
+ // trim any trailing whitespace, as it can confuse the parser for
+ // IRC-style commands
+ input = input.replace(/\s+$/, "");
+ if (input[0] === "/" && input[1] !== "/") {
+ var bits = input.match(/^(\S+?)( +(.*))?$/);
+ var cmd = bits[1].substring(1);
+ var args = bits[3];
+ if (commands[cmd]) {
+ return commands[cmd](roomId, args);
+ }
+ return reject("Unrecognised IRC-style command: " + cmd);
+ }
+ return null; // not a command
+ }
+
+ };
+
+}]);
+
diff --git a/syweb/webclient/index.html b/syweb/webclient/index.html
index 4bca320e77..fb3c3f528c 100644
--- a/syweb/webclient/index.html
+++ b/syweb/webclient/index.html
@@ -45,6 +45,7 @@
<script src="components/matrix/event-handler-service.js"></script>
<script src="components/matrix/notification-service.js"></script>
<script src="components/matrix/recents-service.js"></script>
+ <script src="components/matrix/commands-service.js"></script>
<script src="components/matrix/model-service.js"></script>
<script src="components/matrix/presence-service.js"></script>
<script src="components/fileInput/file-input-directive.js"></script>
diff --git a/syweb/webclient/room/room-controller.js b/syweb/webclient/room/room-controller.js
index 6670201707..bea0db5759 100644
--- a/syweb/webclient/room/room-controller.js
+++ b/syweb/webclient/room/room-controller.js
@@ -15,8 +15,8 @@ limitations under the License.
*/
angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput', 'angular-peity'])
-.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall', 'notificationService', 'modelService', 'recentsService',
- function($modal, $filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, mPresence, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall, notificationService, modelService, recentsService) {
+.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall', 'notificationService', 'modelService', 'recentsService', 'commandsService',
+ function($modal, $filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, mPresence, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall, notificationService, modelService, recentsService, commandsService) {
'use strict';
var MESSAGES_PER_PAGINATION = 30;
var THUMBNAIL_SIZE = 320;
@@ -435,172 +435,18 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput', 'a
// Store the command in the history
history.push(input);
- var promise;
- var cmd;
- var args;
+ var promise = commandsService.processInput($scope.room_id, input);
var echo = false;
+ var isEmote = input.indexOf("/me ") === 0;
- // Check for IRC style commands first
- // trim any trailing whitespace, as it can confuse the parser for IRC-style commands
- input = input.replace(/\s+$/, "");
-
- if (input[0] === "/" && input[1] !== "/") {
- var bits = input.match(/^(\S+?)( +(.*))?$/);
- cmd = bits[1];
- args = bits[3];
-
- console.log("cmd: " + cmd + ", args: " + args);
-
- switch (cmd) {
- case "/me":
- promise = matrixService.sendEmoteMessage($scope.room_id, args);
- echo = true;
- break;
-
- case "/nick":
- // Change user display name
- if (args) {
- promise = matrixService.setDisplayName(args);
- }
- else {
- $scope.feedback = "Usage: /nick <display_name>";
- }
- break;
-
- case "/join":
- // Join a room
- if (args) {
- var matches = args.match(/^(\S+)$/);
- if (matches) {
- var room_alias = matches[1];
- if (room_alias.indexOf(':') == -1) {
- // FIXME: actually track the :domain style name of our homeserver
- // with or without port as is appropriate and append it at this point
- }
-
- var room_id = modelService.getAliasToRoomIdMapping(room_alias);
- console.log("joining " + room_alias + " id=" + room_id);
- if ($scope.room) { // TODO actually check that you = join
- // don't send a join event for a room you're already in.
- $location.url("room/" + room_alias);
- }
- else {
- promise = matrixService.joinAlias(room_alias).then(
- function(response) {
- // TODO: factor out the common housekeeping whenever we try to join a room or alias
- matrixService.roomState(response.room_id).then(
- function(response) {
- eventHandlerService.handleEvents(response.data, false, true);
- },
- function(error) {
- $scope.feedback = "Failed to get room state for: " + response.room_id;
- }
- );
- $location.url("room/" + room_alias);
- },
- function(error) {
- $scope.feedback = "Can't join room: " + JSON.stringify(error.data);
- }
- );
- }
- }
- }
- else {
- $scope.feedback = "Usage: /join <room_alias>";
- }
- break;
-
- case "/kick":
- // Kick a user from the room with an optional reason
- if (args) {
- var matches = args.match(/^(\S+?)( +(.*))?$/);
- if (matches) {
- promise = matrixService.kick($scope.room_id, matches[1], matches[3]);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /kick <userId> [<reason>]";
- }
- break;
-
- case "/ban":
- // Ban a user from the room with an optional reason
- if (args) {
- var matches = args.match(/^(\S+?)( +(.*))?$/);
- if (matches) {
- promise = matrixService.ban($scope.room_id, matches[1], matches[3]);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /ban <userId> [<reason>]";
- }
- break;
-
- case "/unban":
- // Unban a user from the room
- if (args) {
- var matches = args.match(/^(\S+)$/);
- if (matches) {
- // Reset the user membership to "leave" to unban him
- promise = matrixService.unban($scope.room_id, matches[1]);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /unban <userId>";
- }
- break;
-
- case "/op":
- // Define the power level of a user
- if (args) {
- var matches = args.match(/^(\S+?)( +(\d+))?$/);
- var powerLevel = 50; // default power level for op
- if (matches) {
- var user_id = matches[1];
- if (matches.length === 4 && undefined !== matches[3]) {
- powerLevel = parseInt(matches[3]);
- }
- if (powerLevel !== NaN) {
- var powerLevelEvent = $scope.room.current_room_state.state("m.room.power_levels");
- promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel, powerLevelEvent);
- }
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /op <userId> [<power level>]";
- }
- break;
-
- case "/deop":
- // Reset the power level of a user
- if (args) {
- var matches = args.match(/^(\S+)$/);
- if (matches) {
- var powerLevelEvent = $scope.room.current_room_state.state("m.room.power_levels");
- promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined, powerLevelEvent);
- }
- }
-
- if (!promise) {
- $scope.feedback = "Usage: /deop <userId>";
- }
- break;
-
- default:
- $scope.feedback = ("Unrecognised IRC-style command: " + cmd);
- break;
- }
- }
-
- // By default send this as a message unless it's an IRC-style command
- if (!promise && !cmd) {
- // Make the request
- promise = matrixService.sendTextMessage($scope.room_id, input);
+ if (!promise) { // not a non-echoable command
echo = true;
+ if (isEmote) {
+ promise = matrixService.sendEmoteMessage($scope.room_id, input.substring(4));
+ }
+ else {
+ promise = matrixService.sendTextMessage($scope.room_id, input);
+ }
}
if (echo) {
@@ -608,8 +454,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput', 'a
// To do so, create a minimalist fake text message event and add it to the in-memory list of room messages
var echoMessage = {
content: {
- body: (cmd === "/me" ? args : input),
- msgtype: (cmd === "/me" ? "m.emote" : "m.text"),
+ body: (isEmote ? input.substring(4) : input),
+ msgtype: (isEmote ? "m.emote" : "m.text"),
},
origin_server_ts: new Date().getTime(), // fake a timestamp
room_id: $scope.room_id,
@@ -642,7 +488,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput', 'a
}
},
function(error) {
- $scope.feedback = "Request failed: " + error.data.error;
+ $scope.feedback = error.data.error;
if (echoMessage) {
// Mark the message as unsent for the rest of the page life
|