Merge branch 'develop' into rav/olm

Conflicts:
	webpack.config.js
This commit is contained in:
Richard van der Hoff 2015-12-03 18:28:50 +00:00
commit 97dd4e2e6b
20 changed files with 249 additions and 148 deletions

2
.gitignore vendored
View file

@ -4,4 +4,4 @@ lib
.DS_Store .DS_Store
key.pem key.pem
cert.pem cert.pem
build vector/components.css

View file

@ -9,25 +9,29 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"style": "bundle.css", "style": "bundle.css",
"matrix-react-parent": "matrix-react-sdk",
"scripts": { "scripts": {
"reskindex": "reskindex vector -h src/skins/vector/header", "reskindex": "reskindex -h src/header",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch", "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch",
"build:compile": "babel --source-maps -d lib src", "build:compile": "babel --source-maps -d lib src",
"build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js", "build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js",
"build": "npm run build:css && npm run build:compile && npm run build:bundle", "build": "npm run build:css && npm run build:compile && npm run build:bundle",
"start:js": "webpack -w src/vector/index.js vector/bundle.js", "start:js": "webpack -w src/vector/index.js vector/bundle.js",
"start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css", "start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css",
"//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270", "//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270",
"start": "parallelshell \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"", "start": "parallelshell \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
"clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map", "clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map vector/webpack.css*",
"prepublish": "npm run build:css && npm run build:compile" "prepublish": "npm run build:css && npm run build:compile"
}, },
"dependencies": { "dependencies": {
"classnames": "^2.1.2", "classnames": "^2.1.2",
"extract-text-webpack-plugin": "^0.9.1",
"filesize": "^3.1.2", "filesize": "^3.1.2",
"flux": "~2.0.3", "flux": "~2.0.3",
"gemini-scrollbar": "^1.3.0",
"gfm.css": "^1.1.1", "gfm.css": "^1.1.1",
"highlight.js": "^9.0.0",
"linkifyjs": "^2.0.0-beta.4", "linkifyjs": "^2.0.0-beta.4",
"matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop", "matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop",
"matrix-react-sdk": "https://github.com/matrix-org/matrix-react-sdk.git#develop", "matrix-react-sdk": "https://github.com/matrix-org/matrix-react-sdk.git#develop",
@ -44,12 +48,12 @@
"babel-core": "^5.8.25", "babel-core": "^5.8.25",
"babel-loader": "^5.3.2", "babel-loader": "^5.3.2",
"catw": "^1.0.1", "catw": "^1.0.1",
"css-raw-loader": "^0.1.1",
"http-server": "^0.8.4", "http-server": "^0.8.4",
"json-loader": "^0.5.3", "json-loader": "^0.5.3",
"parallelshell": "^1.2.0", "parallelshell": "^1.2.0",
"rimraf": "^2.4.3", "rimraf": "^2.4.3",
"source-map-loader": "^0.1.5", "source-map-loader": "^0.1.5",
"uglifycss": "0.0.15",
"webpack": "^1.12.6" "webpack": "^1.12.6"
} }
} }

47
src/component-index.js Normal file
View file

@ -0,0 +1,47 @@
/*
Copyright 2015 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.
*/
/*
* THIS FILE IS AUTO-GENERATED
* You can edit it you like, but your changes will be overwritten,
* so you'd just be trying to swim upstream like a salmon.
* You are not a salmon.
*/
module.exports.components = require('matrix-react-sdk/lib/component-index').components;
module.exports.components['structures.BottomLeftMenu'] = require('./components/structures/BottomLeftMenu');
module.exports.components['structures.CompatibilityPage'] = require('./components/structures/CompatibilityPage');
module.exports.components['structures.LeftPanel'] = require('./components/structures/LeftPanel');
module.exports.components['structures.RightPanel'] = require('./components/structures/RightPanel');
module.exports.components['structures.RoomDirectory'] = require('./components/structures/RoomDirectory');
module.exports.components['structures.RoomSubList'] = require('./components/structures/RoomSubList');
module.exports.components['structures.ViewSource'] = require('./components/structures/ViewSource');
module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView');
module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner');
module.exports.components['views.globals.MatrixToolbar'] = require('./components/views/globals/MatrixToolbar');
module.exports.components['views.login.CustomServerDialog'] = require('./components/views/login/VectorCustomServerDialog');
module.exports.components['views.login.LoginFooter'] = require('./components/views/login/VectorLoginFooter');
module.exports.components['views.login.LoginHeader'] = require('./components/views/login/VectorLoginHeader');
module.exports.components['views.messages.DateSeparator'] = require('./components/views/messages/DateSeparator');
module.exports.components['views.messages.MessageTimestamp'] = require('./components/views/messages/MessageTimestamp');
module.exports.components['views.messages.SenderProfile'] = require('./components/views/messages/SenderProfile');
module.exports.components['views.rooms.BottomLeftMenuTile'] = require('./components/views/rooms/BottomLeftMenuTile');
module.exports.components['views.rooms.MessageContextMenu'] = require('./components/views/rooms/MessageContextMenu');
module.exports.components['views.rooms.RoomDNDView'] = require('./components/views/rooms/RoomDNDView');
module.exports.components['views.rooms.RoomDropTarget'] = require('./components/views/rooms/RoomDropTarget');
module.exports.components['views.rooms.RoomTooltip'] = require('./components/views/rooms/RoomTooltip');
module.exports.components['views.rooms.SearchBar'] = require('./components/views/rooms/SearchBar');

View file

@ -0,0 +1,53 @@
/*
Copyright 2015 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.
*/
var React = require("react");
module.exports = React.createClass({
displayName: 'VectorCustomServerDialog',
statics: {
replaces: 'CustomServerDialog',
},
render: function() {
return (
<div className="mx_ErrorDialog">
<div className="mx_ErrorDialogTitle">
Custom Server Options
</div>
<div className="mx_Dialog_content">
<span>
You can use the custom server options to log into other Matrix
servers by specifying a different Home server URL.
<br/>
This allows you to use Vector with an existing Matrix account on
a different Home server.
<br/>
<br/>
You can also set a custom Identity server but this will affect
people&#39;s ability to find you if you use a server in a group other
than the main Matrix.org group.
</span>
</div>
<div className="mx_Dialog_buttons">
<button onClick={this.props.onFinished} autoFocus={true}>
Dismiss
</button>
</div>
</div>
);
}
});

View file

@ -0,0 +1,37 @@
/*
Copyright 2015 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';
var React = require('react');
module.exports = React.createClass({
displayName: 'VectorLoginFooter',
statics: {
replaces: 'LoginFooter',
},
render: function() {
return (
<div className="mx_Login_links">
<a href="https://medium.com/@Vector">blog</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://twitter.com/@VectorCo">twitter</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://github.com/vector-im/vector-web">github</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="https://matrix.org">powered by Matrix</a>
</div>
);
}
});

View file

@ -0,0 +1,34 @@
/*
Copyright 2015 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';
var React = require('react');
module.exports = React.createClass({
displayName: 'VectorLoginHeader',
statics: {
replaces: 'LoginHeader',
},
render: function() {
return (
<div className="mx_Login_logo">
<img src="img/logo.png" width="249" height="78" alt="vector"/>
</div>
);
}
});

View file

@ -202,3 +202,5 @@ DragSource('RoomTile', roomTileSource, function(connect, monitor) {
isDragging: monitor.isDragging() isDragging: monitor.isDragging()
}; };
})(RoomTile)); })(RoomTile));
module.exports.replaces = 'RoomTile';

View file

@ -0,0 +1,42 @@
.mx_UploadBar {
position: relative;
}
.mx_UploadBar_uploadProgressOuter {
height: 4px;
margin-left: 63px;
margin-top: -1px;
}
.mx_UploadBar_uploadProgressInner {
background-color: #76cfa6;
height: 4px;
}
.mx_UploadBar_uploadFilename {
margin-top: 5px;
margin-left: 65px;
opacity: 0.5;
color: #4a4a4a;
}
.mx_UploadBar_uploadIcon {
float: left;
margin-top: 1px;
margin-left: 14px;
}
.mx_UploadBar_uploadCancel {
float: right;
margin-top: 5px;
margin-right: 10px;
position: relative;
z-index: 1;
}
.mx_UploadBar_uploadBytes {
float: right;
margin-top: 5px;
margin-right: 30px;
color: #76cfa6;
}

View file

@ -1 +0,0 @@
../../../../node_modules/react-gemini-scrollbar/node_modules/gemini-scrollbar/gemini-scrollbar.css

View file

@ -1 +0,0 @@
../../../../node_modules/gfm.css/gfm.css

View file

@ -1 +0,0 @@
../../../../node_modules/highlight.js/styles/github.css

View file

@ -75,6 +75,7 @@ limitations under the License.
font-family: inherit ! important; font-family: inherit ! important;
white-space: normal ! important; white-space: normal ! important;
line-height: inherit ! important; line-height: inherit ! important;
color: inherit;
font-size: 15px; font-size: 15px;
} }

View file

@ -112,6 +112,10 @@ limitations under the License.
color: #76cfa6 ! important; color: #76cfa6 ! important;
} }
.mx_RoomTile_highlight .mx_RoomTile_name {
color: #ff0064 ! important;
}
.mx_RoomTile.mx_RoomTile_selected .mx_RoomTile_name { .mx_RoomTile.mx_RoomTile_selected .mx_RoomTile_name {
background: url('img/selected.png'); background: url('img/selected.png');
background-repeat: no-repeat; background-repeat: no-repeat;

View file

@ -166,6 +166,7 @@ limitations under the License.
max-width: 960px; max-width: 960px;
margin: auto; margin: auto;
min-height: 36px; min-height: 36px;
overflow-y: hidden;
} }
.mx_RoomView_statusAreaBox_line { .mx_RoomView_statusAreaBox_line {
@ -241,43 +242,6 @@ limitations under the License.
margin-right: 2px; margin-right: 2px;
} }
.mx_RoomView_uploadProgressOuter {
height: 4px;
margin-left: 63px;
margin-top: -1px;
}
.mx_RoomView_uploadProgressInner {
background-color: #76cfa6;
height: 4px;
}
.mx_RoomView_uploadFilename {
margin-top: 5px;
margin-left: 65px;
opacity: 0.5;
color: #4a4a4a;
}
.mx_RoomView_uploadIcon {
float: left;
margin-top: 1px;
margin-left: 14px;
}
.mx_RoomView_uploadCancel {
float: right;
margin-top: 5px;
margin-right: 10px;
}
.mx_RoomView_uploadBytes {
float: right;
margin-top: 5px;
margin-right: 30px;
color: #76cfa6;
}
.mx_RoomView_ongoingConfCallNotification { .mx_RoomView_ongoingConfCallNotification {
width: 100%; width: 100%;
text-align: center; text-align: center;

View file

@ -1,95 +0,0 @@
/*
Copyright 2015 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.
*/
/*
* THIS FILE IS AUTO-GENERATED
* You can edit it you like, but your changes will be overwritten,
* so you'd just be trying to swim upstream like a salmon.
* You are not a salmon.
*/
var skin = {};
// Vector-specific stuff
skin['elements.Spinner'] = require('../../components/views/elements/Spinner');
skin['elements.ImageView'] = require('../../components/views/elements/ImageView');
skin['messages.MessageTimestamp'] = require('../../components/views/messages/MessageTimestamp');
skin['messages.DateSeparator'] = require('../../components/views/messages/DateSeparator');
skin['messages.SenderProfile'] = require('../../components/views//messages/SenderProfile');
skin['rooms.RoomTile'] = require('../../components/views/rooms/RoomDNDView');
skin['rooms.BottomLeftMenuTile'] = require('../../components/views/rooms/BottomLeftMenuTile');
skin['rooms.MessageContextMenu'] = require('../../components/views/rooms/MessageContextMenu');
skin['rooms.RoomDropTarget'] = require('../../components/views/rooms/RoomDropTarget');
skin['rooms.RoomTooltip'] = require('../../components/views/rooms/RoomTooltip');
skin['rooms.SearchBar'] = require('../../components/views/rooms/SearchBar');
skin['globals.MatrixToolbar'] = require('../../components/views/globals/MatrixToolbar');
skin['structures.BottomLeftMenu'] = require('../../components/structures/BottomLeftMenu');
skin['structures.LeftPanel'] = require('../../components/structures/LeftPanel');
skin['structures.RightPanel'] = require('../../components/structures/RightPanel');
skin['structures.RoomDirectory'] = require('../../components/structures/RoomDirectory');
skin['structures.RoomSubList'] = require('../../components/structures/RoomSubList');
skin['structures.ViewSource'] = require('../../components/structures/ViewSource');
skin['structures.CompatibilityPage'] = require('../../components/structures/CompatibilityPage');
// TODO: Fix this so matrix-react-sdk stuff is in react SDK skindex?
skin['avatars.RoomAvatar'] = require('matrix-react-sdk/lib/components/views/avatars/RoomAvatar');
skin['avatars.MemberAvatar'] = require('matrix-react-sdk/lib/components/views/avatars/MemberAvatar');
skin['settings.EnableNotificationsButton'] = require('matrix-react-sdk/lib/components/views/settings/EnableNotificationsButton');
skin['settings.ChangeAvatar'] = require('matrix-react-sdk/lib/components/views/settings/ChangeAvatar');
skin['settings.ChangeDisplayName'] = require('matrix-react-sdk/lib/components/views/settings/ChangeDisplayName');
skin['settings.ChangePassword'] = require('matrix-react-sdk/lib/components/views/settings/ChangePassword');
skin['elements.EditableText'] = require('matrix-react-sdk/lib/components/views/elements/EditableText');
skin['elements.ProgressBar'] = require('matrix-react-sdk/lib/components/views/elements/ProgressBar');
skin['elements.UserSelector'] = require('matrix-react-sdk/lib/components/views/elements/UserSelector');
skin['messages.TextualEvent'] = require('matrix-react-sdk/lib/components/views/messages/TextualEvent');
skin['messages.MessageEvent'] = require('matrix-react-sdk/lib/components/views/messages/MessageEvent');
skin['messages.MFileBody'] = require('matrix-react-sdk/lib/components/views/messages/MFileBody');
skin['messages.MImageBody'] = require('matrix-react-sdk/lib/components/views/messages/MImageBody');
skin['messages.MVideoBody'] = require('matrix-react-sdk/lib/components/views/messages/MVideoBody');
skin['messages.TextualBody'] = require('matrix-react-sdk/lib/components/views/messages/TextualBody');
skin['messages.UnknownBody'] = require('matrix-react-sdk/lib/components/views/messages/UnknownBody');
skin['rooms.MemberInfo'] = require('matrix-react-sdk/lib/components/views/rooms/MemberInfo');
skin['rooms.RoomHeader'] = require('matrix-react-sdk/lib/components/views/rooms/RoomHeader');
skin['rooms.RoomSettings'] = require('matrix-react-sdk/lib/components/views/rooms/RoomSettings');
skin['rooms.MemberTile'] = require('matrix-react-sdk/lib/components/views/rooms/MemberTile');
skin['rooms.MemberList'] = require('matrix-react-sdk/lib/components/views/rooms/MemberList');
skin['rooms.MessageComposer'] = require('matrix-react-sdk/lib/components/views/rooms/MessageComposer');
skin['rooms.EventTile'] = require('matrix-react-sdk/lib/components/views/rooms/EventTile');
skin['rooms.RoomList'] = require('matrix-react-sdk/lib/components/views/rooms/RoomList');
skin['create_room.CreateRoomButton'] = require('matrix-react-sdk/lib/components/views/create_room/CreateRoomButton');
skin['create_room.Presets'] = require('matrix-react-sdk/lib/components/views/create_room/Presets');
skin['create_room.RoomAlias'] = require('matrix-react-sdk/lib/components/views/create_room/RoomAlias');
skin['voip.CallView'] = require('matrix-react-sdk/lib/components/views/voip/CallView');
skin['voip.IncomingCallBox'] = require('matrix-react-sdk/lib/components/views/voip/IncomingCallBox');
skin['voip.VideoView'] = require('matrix-react-sdk/lib/components/views/voip/VideoView');
skin['voip.VideoFeed'] = require('matrix-react-sdk/lib/components/views/voip/VideoFeed');
skin['dialogs.QuestionDialog'] = require('matrix-react-sdk/lib/components/views/dialogs/QuestionDialog');
skin['dialogs.ErrorDialog'] = require('matrix-react-sdk/lib/components/views/dialogs/ErrorDialog');
skin['dialogs.LogoutPrompt'] = require('matrix-react-sdk/lib/components/views/dialogs/LogoutPrompt');
skin['structures.CreateRoom'] = require('matrix-react-sdk/lib/components/structures/CreateRoom');
skin['structures.UserSettings'] = require('matrix-react-sdk/lib/components/structures/UserSettings');
skin['structures.RoomView'] = require('matrix-react-sdk/lib/components/structures/RoomView');
skin['structures.MatrixChat'] = require('matrix-react-sdk/lib/components/structures/MatrixChat');
module.exports = skin;

View file

@ -1,3 +0,0 @@
{
"baseSkin": ""
}

View file

@ -16,11 +16,18 @@ limitations under the License.
'use strict'; 'use strict';
// CSS requires: just putting them here for now as CSS is going to be
// refactored soon anyway
require('../../vector/components.css');
require('gemini-scrollbar/gemini-scrollbar.css');
require('gfm.css/gfm.css');
require('highlight.js/styles/github.css');
var RunModernizrTests = require("./modernizr"); // this side-effects a global var RunModernizrTests = require("./modernizr"); // this side-effects a global
var React = require("react"); var React = require("react");
var ReactDOM = require("react-dom"); var ReactDOM = require("react-dom");
var sdk = require("matrix-react-sdk"); var sdk = require("matrix-react-sdk");
sdk.loadSkin(require('../skins/vector/skindex')); sdk.loadSkin(require('../component-index'));
var VectorConferenceHandler = require('../VectorConferenceHandler'); var VectorConferenceHandler = require('../VectorConferenceHandler');
var configJson = require("../../config.json"); var configJson = require("../../config.json");

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Vector</title> <title>Vector</title>
<link href='fonts/MyriadPro.css' rel='stylesheet' type='text/css'> <link href='fonts/OpenSans.css' rel='stylesheet' type='text/css'>
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-touch-icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="icons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-touch-icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="icons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-touch-icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="icons/apple-touch-icon-72x72.png">

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var olm_path = path.resolve('./node_modules/olm'); var olm_path = path.resolve('./node_modules/olm');
@ -11,6 +12,8 @@ module.exports = {
loaders: [ loaders: [
{ test: /\.json$/, loader: "json" }, { test: /\.json$/, loader: "json" },
{ test: /\.js$/, loader: "babel", include: path.resolve('./src') }, { test: /\.js$/, loader: "babel", include: path.resolve('./src') },
// css-raw-loader loads CSS but doesn't try to treat url()s as require()s
{ test: /\.css$/, loader: ExtractTextPlugin.extract("css-raw-loader") },
] ]
}, },
output: { output: {
@ -45,6 +48,10 @@ module.exports = {
} }
}), }),
new ExtractTextPlugin("bundle.css", {
allChunks: true
}),
// olm.js includes "require 'fs'", which is never // olm.js includes "require 'fs'", which is never
// executed in the browser. Ignore it. // executed in the browser. Ignore it.
new webpack.IgnorePlugin(/^fs$/, /node_modules\/olm$/) new webpack.IgnorePlugin(/^fs$/, /node_modules\/olm$/)
@ -57,7 +64,7 @@ module.exports = {
var fs = require('fs'); var fs = require('fs');
try { try {
fs.lstatSync(olm_path); fs.lstatSync(olm_path);
console.log("Olm is installed; including it in bundle"); console.log("Olm is installed; including it in webpack bundle");
} catch (e) { } catch (e) {
module.exports.plugins.push( module.exports.plugins.push(
new webpack.IgnorePlugin(/^olm$/) new webpack.IgnorePlugin(/^olm$/)