Comparar commits
11 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| efb3dd025f | |||
| 457e175a41 | |||
| 757770b9f7 | |||
| 3dee2ef385 | |||
| e70b2481a8 | |||
| 9023a4c9c8 | |||
| c5ad776f8c | |||
| 2bb914cb9a | |||
| 9158727327 | |||
| e904bd8045 | |||
| 4fb2ea3ee3 |
@@ -25,6 +25,3 @@ lib/i18next/
|
||||
lib/jquery-i18next/
|
||||
lib/magnific-popup/
|
||||
.idea
|
||||
/lib/strophejs-plugin-mam
|
||||
/lib/strophejs-plugin-rsm
|
||||
.github.json
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@
|
||||
url = https://github.com/twbs/bootstrap-sass/
|
||||
[submodule "lib/strophe.jinglejs"]
|
||||
path = lib/strophe.jinglejs
|
||||
url = https://github.com/jsxc/strophe.jinglejs
|
||||
url = https://github.com/sualko/strophe.jinglejs
|
||||
+1
-1
@@ -18,7 +18,7 @@ linters:
|
||||
ImportantRule:
|
||||
enabled: false
|
||||
Indentation:
|
||||
width: 4
|
||||
width: 3
|
||||
LeadingZero:
|
||||
style: include_zero
|
||||
NameFormat:
|
||||
|
||||
@@ -9,3 +9,7 @@ before_install:
|
||||
|
||||
script:
|
||||
- ./node_modules/.bin/grunt pre-commit
|
||||
|
||||
branches:
|
||||
except:
|
||||
- refactoring
|
||||
|
||||
@@ -5,26 +5,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- [#150](https://github.com/jsxc/jsxc/issues/150) add Message Archive Management (XEP-0313)
|
||||
- [#464](https://github.com/jsxc/jsxc/issues/464) respond to software version request (XEP-0092)
|
||||
|
||||
### Fixed
|
||||
- [#447](https://github.com/jsxc/jsxc/pull/447) fix muc member presence
|
||||
- fix http upload discovery
|
||||
- fix vcard retrieval for rooms
|
||||
- fix bookmarks
|
||||
- [#470](https://github.com/jsxc/jsxc/issues/470) fix receiving message from unknown sender
|
||||
- [#483](https://github.com/jsxc/jsxc/issues/483) fix unclickable space
|
||||
- catch quota exceeded errors (e.g. Safari in private mode has a quota of 0)
|
||||
|
||||
### Changed
|
||||
- update strophe.jinglejs
|
||||
- make max file size optional for http upload service
|
||||
- [#480](https://github.com/jsxc/jsxc/issues/480) split avatar loading into chunks
|
||||
- [#478](https://github.com/jsxc/jsxc/issues/478) support roster versioning
|
||||
- prefer xmpp password from settings over login form
|
||||
- [#468](https://github.com/jsxc/jsxc/issues/468) move composing message to window header
|
||||
|
||||
## 3.1.1 - 2017-02-14
|
||||
### Fixed
|
||||
|
||||
-431
@@ -1,431 +0,0 @@
|
||||
/* global module:false */
|
||||
module.exports = function(grunt) {
|
||||
|
||||
var dep = grunt.file.readJSON('dep.json');
|
||||
var dep_files = dep.map(function(el) {
|
||||
return el.file;
|
||||
});
|
||||
dep_files.push('<%= target %>/lib/translation.js');
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
github: grunt.file.readJSON('.github.json'),
|
||||
app: grunt.file.readJSON('package.json'),
|
||||
meta: {
|
||||
banner: grunt.file.read('banner.js')
|
||||
},
|
||||
target: 'dev',
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
gruntfile: {
|
||||
src: 'Gruntfile.js'
|
||||
},
|
||||
files: ['src/jsxc.lib.*.js']
|
||||
},
|
||||
copy: {
|
||||
main: {
|
||||
files: [{
|
||||
expand: true,
|
||||
src: ['lib/emojione/assets/svg/*.svg',
|
||||
'lib/otr/build/**', 'lib/otr/lib/*.js',
|
||||
'lib/otr/vendor/*.js', 'lib/*.js', 'LICENSE',
|
||||
'img/**', 'sound/**'
|
||||
],
|
||||
dest: '<%= target %>/'
|
||||
}, {
|
||||
expand: true,
|
||||
cwd: 'lib/',
|
||||
src: ['*.css'],
|
||||
dest: '<%= target %>/css/'
|
||||
}, {
|
||||
expand: true,
|
||||
cwd: 'lib/magnific-popup/dist/',
|
||||
src: ['*.css'],
|
||||
dest: '<%= target %>/css/'
|
||||
}]
|
||||
}
|
||||
},
|
||||
clean: ['<%= target %>/'],
|
||||
usebanner: {
|
||||
dist: {
|
||||
options: {
|
||||
position: 'top',
|
||||
banner: '<%= meta.banner %>'
|
||||
},
|
||||
files: {
|
||||
src: ['<%= target %>/*.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
replace: {
|
||||
version: {
|
||||
src: ['<%= target %>/jsxc.js'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: '< $ app.version $ >',
|
||||
to: "<%= app.version %>"
|
||||
}]
|
||||
},
|
||||
libraries: {
|
||||
src: ['<%= target %>/jsxc.js'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: '<$ dep.libraries $>',
|
||||
to: function() {
|
||||
var i, d, libraries = '';
|
||||
|
||||
for (i = 0; i < dep.length; i++) {
|
||||
d = dep[i];
|
||||
if (typeof d.name === 'string') {
|
||||
libraries += '<a href="' + d.url + '">' + d.name + '</a> (' + d.license + '), ';
|
||||
}
|
||||
}
|
||||
|
||||
return libraries.replace(/, $/, '');
|
||||
}
|
||||
}]
|
||||
},
|
||||
locales: {
|
||||
src: ['<%= target %>/lib/translation.js'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: /^{/g,
|
||||
to: 'var I18next = {'
|
||||
}, {
|
||||
from: /}$/g,
|
||||
to: '};'
|
||||
}]
|
||||
},
|
||||
template: {
|
||||
src: ['tmp/template.js'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: 'var jsxc.gui.template = {};',
|
||||
to: ''
|
||||
}]
|
||||
},
|
||||
imageUrl: {
|
||||
src: ['<%= target %>/css/*.css'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: /image-url\(["'](.+)["']\)/g,
|
||||
to: 'url(\'../img/$1\')'
|
||||
}]
|
||||
},
|
||||
// IE 10 does not like comments starting with @
|
||||
todo: {
|
||||
src: ['build/jsxc.js'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: /\/\/@(.*)/g,
|
||||
to: '//$1'
|
||||
}]
|
||||
}
|
||||
},
|
||||
merge_data: {
|
||||
target: {
|
||||
src: ['locales/*.{json,y{,a}ml}'],
|
||||
dest: '<%= target %>/lib/translation.js'
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
dep: {
|
||||
options: {
|
||||
banner: '/*!\n' +
|
||||
' * <%= app.name %> v<%= app.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
|
||||
' * \n' +
|
||||
' * This file concatenates all dependencies of <%= app.name %>.\n' +
|
||||
' * \n' +
|
||||
' */\n\n',
|
||||
process: function(src, filepath) {
|
||||
filepath = filepath.replace(/^[a-z]+\//i, '');
|
||||
|
||||
if (filepath.match(/crypto\.js$/)) {
|
||||
src += ';';
|
||||
}
|
||||
|
||||
var data = dep[dep_files.indexOf(filepath)];
|
||||
|
||||
if (data) {
|
||||
return '\n/*!\n * Source: ' + filepath + ', license: ' + data.license + ', url: ' + data.url + '\n */\n' + src;
|
||||
} else {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
},
|
||||
src: dep_files,
|
||||
dest: '<%= target %>/lib/jsxc.dep.js',
|
||||
filter: function(filepath) {
|
||||
if (!grunt.file.exists(filepath)) {
|
||||
grunt.fail.warn('Could not find: ' + filepath);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
nonull: true,
|
||||
},
|
||||
jsxc: {
|
||||
options: {
|
||||
banner: '/*! This file is concatenated for the browser. */\n\n'
|
||||
},
|
||||
src: ['src/jsxc.intro.js', 'src/jsxc.lib.js', 'src/jsxc.lib.xmpp.js',
|
||||
'src/jsxc.lib.gui.js', 'src/jsxc.lib.*.js',
|
||||
'tmp/template.js', 'src/jsxc.outro.js'
|
||||
],
|
||||
dest: '<%= target %>/jsxc.js'
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
jsxc: {
|
||||
options: {
|
||||
mangle: false,
|
||||
sourceMap: true,
|
||||
preserveComments: 'some'
|
||||
},
|
||||
files: {
|
||||
'<%= target %>/lib/jsxc.dep.min.js': ['<%= target %>/lib/jsxc.dep.js'],
|
||||
'<%= target %>/jsxc.min.js': ['<%= target %>/jsxc.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
search: {
|
||||
bower: {
|
||||
files: {
|
||||
src: ['bower.json']
|
||||
},
|
||||
options: {
|
||||
searchString: "<%= app.version %>",
|
||||
logFormat: 'console',
|
||||
onComplete: function(m) {
|
||||
if (m.numMatches === 0) {
|
||||
grunt.fail.fatal('No entry in bower.json for current version found.');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
console: {
|
||||
files: {
|
||||
src: ['src/*.js']
|
||||
},
|
||||
options: {
|
||||
searchString: /console\.log\((?!'[<>]|msg)/g,
|
||||
logFormat: 'console',
|
||||
failOnMatch: true
|
||||
}
|
||||
},
|
||||
changelog: {
|
||||
files: {
|
||||
src: ['CHANGELOG.md']
|
||||
},
|
||||
options: {
|
||||
searchString: "<%= app.version %>",
|
||||
logFormat: 'console',
|
||||
onComplete: function(m) {
|
||||
if (m.numMatches === 0) {
|
||||
grunt.fail.fatal("No entry in CHANGELOG.md for current version found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
compress: {
|
||||
main: {
|
||||
options: {
|
||||
archive: "archives/jsxc-<%= app.version %>.zip"
|
||||
},
|
||||
files: [{
|
||||
src: ['**'],
|
||||
expand: true,
|
||||
dest: 'jsxc/',
|
||||
cwd: 'build/'
|
||||
}]
|
||||
}
|
||||
},
|
||||
dataUri: {
|
||||
dist: {
|
||||
src: '<%= target %>/css/*.css',
|
||||
dest: '<%= target %>/css/',
|
||||
options: {
|
||||
target: ['<%= target %>/img/*.*', '<%= target %>/img/**/*.*'],
|
||||
fixDirLevel: false,
|
||||
maxBytes: 2048
|
||||
}
|
||||
}
|
||||
},
|
||||
jsdoc: {
|
||||
dist: {
|
||||
src: ['src/jsxc.lib.*'],
|
||||
dest: 'doc'
|
||||
}
|
||||
},
|
||||
autoprefixer: {
|
||||
no_dest: {
|
||||
src: '<%= target %>/css/*.css'
|
||||
}
|
||||
},
|
||||
csslint: {
|
||||
strict: {
|
||||
options: {
|
||||
import: 2
|
||||
},
|
||||
src: ['<%= target %>/css/*.css']
|
||||
},
|
||||
},
|
||||
sass: {
|
||||
dist: {
|
||||
files: {
|
||||
'<%= target %>/css/jsxc.css': 'scss/jsxc.scss',
|
||||
'<%= target %>/css/jsxc.webrtc.css': 'scss/jsxc.webrtc.scss'
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
locales: {
|
||||
files: ['locales/*'],
|
||||
tasks: ['merge_data', 'replace:locales', 'concat:dep']
|
||||
},
|
||||
css: {
|
||||
files: ['scss/*'],
|
||||
tasks: ['sass', 'autoprefixer', 'replace:imageUrl']
|
||||
},
|
||||
js: {
|
||||
files: ['src/jsxc.lib.*'],
|
||||
tasks: ['concat:jsxc']
|
||||
},
|
||||
template: {
|
||||
files: ['template/*.html'],
|
||||
tasks: ['htmlConvert', 'replace:template', 'concat:jsxc']
|
||||
}
|
||||
},
|
||||
jsbeautifier: {
|
||||
'default': {
|
||||
src: ['Gruntfile.js', 'src/jsxc.lib.*', 'template/*.html',
|
||||
'example/*.html', 'example/js/dev.js', 'example/js/example.js',
|
||||
'example/css/example.css'
|
||||
],
|
||||
options: {
|
||||
config: '.jsbeautifyrc'
|
||||
}
|
||||
},
|
||||
'pre-commit': {
|
||||
src: ['Gruntfile.js', 'src/jsxc.lib.*', 'template/*.html',
|
||||
'example/*.html', 'example/js/dev.js', 'example/js/example.js',
|
||||
'example/css/example.css'
|
||||
],
|
||||
options: {
|
||||
config: '.jsbeautifyrc',
|
||||
mode: 'VERIFY_ONLY'
|
||||
}
|
||||
}
|
||||
},
|
||||
prettysass: {
|
||||
options: {
|
||||
alphabetize: false,
|
||||
indent: 4
|
||||
},
|
||||
jsxc: {
|
||||
src: ['scss/*.scss']
|
||||
}
|
||||
},
|
||||
htmlConvert: {
|
||||
options: {
|
||||
target: 'js',
|
||||
rename: function(name) {
|
||||
return name.match(/([-_0-9a-z]+)\.html$/i)[1];
|
||||
},
|
||||
quoteChar: '\'',
|
||||
indentString: '',
|
||||
indentGlobal: ''
|
||||
},
|
||||
'jsxc.gui.template': {
|
||||
src: 'template/*.html',
|
||||
dest: 'tmp/template.js'
|
||||
}
|
||||
},
|
||||
scsslint: {
|
||||
files: ['scss/*.scss'],
|
||||
options: {
|
||||
config: '.scss-lint.yml'
|
||||
}
|
||||
},
|
||||
github_releaser2: {
|
||||
options: {
|
||||
repository: 'jsxc/jsxc',
|
||||
authentication: {
|
||||
type: 'token',
|
||||
token: '<%= github.token %>'
|
||||
},
|
||||
release: {
|
||||
body: 'see https://github.com/jsxc/jsxc/blob/master/CHANGELOG.md'
|
||||
}
|
||||
},
|
||||
release: {
|
||||
src: ['archives/jsxc-archives/jsxc-<%= app.version %>.zip', 'archives/jsxc-archives/jsxc-<%= app.version %>.zip.sig']
|
||||
},
|
||||
prerelease: {
|
||||
options: {
|
||||
release: {
|
||||
prerelease: true
|
||||
}
|
||||
},
|
||||
src: ['archives/jsxc-archives/jsxc-<%= app.version %>.zip', 'archives/jsxc-archives/jsxc-<%= app.version %>.zip.sig']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// These plugins provide necessary tasks.
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-banner');
|
||||
grunt.loadNpmTasks('grunt-text-replace');
|
||||
grunt.loadNpmTasks('grunt-search');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-jsdoc');
|
||||
grunt.loadNpmTasks('grunt-data-uri');
|
||||
grunt.loadNpmTasks('grunt-merge-data');
|
||||
grunt.loadNpmTasks('grunt-contrib-csslint');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-autoprefixer');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-jsbeautifier');
|
||||
grunt.loadNpmTasks('grunt-prettysass');
|
||||
grunt.loadNpmTasks('grunt-html-convert');
|
||||
grunt.loadNpmTasks('grunt-scss-lint');
|
||||
grunt.loadNpmTasks('grunt-github-releaser2');
|
||||
|
||||
//Default task
|
||||
grunt.registerTask('default', ['build', 'watch']);
|
||||
|
||||
grunt.registerTask('build', ['jshint', 'clean', 'sass', 'replace:imageUrl',
|
||||
'autoprefixer', 'copy', 'merge_data', 'replace:locales', 'htmlConvert',
|
||||
'replace:template', 'concat'
|
||||
]);
|
||||
|
||||
grunt.registerTask('build:prerelease', 'Build a new pre-release', function() {
|
||||
grunt.config.set('target', 'build');
|
||||
|
||||
grunt.task.run(['search:console', 'search:bower', 'build', 'usebanner',
|
||||
'replace:version', 'replace:libraries', 'replace:todo',
|
||||
'uglify', 'compress'
|
||||
]);
|
||||
});
|
||||
|
||||
grunt.registerTask('build:release', 'Build a new release', function() {
|
||||
grunt.config.set('target', 'build');
|
||||
|
||||
grunt.task.run(['search:changelog', 'build:prerelease', 'jsdoc']);
|
||||
});
|
||||
|
||||
grunt.registerTask('publish:release', ['github_releaser2:release']);
|
||||
grunt.registerTask('publish:prerelease', ['github_releaser2:prerelease']);
|
||||
|
||||
// before commit
|
||||
grunt.registerTask('pre-commit', ['search:console', 'jsbeautifier:pre-commit', 'scsslint', 'jshint']);
|
||||
|
||||
grunt.registerTask('beautify', ['jsbeautifier', 'prettysass']);
|
||||
};
|
||||
+6
-1
@@ -1,4 +1,4 @@
|
||||
# JavaScript XMPP Client
|
||||
# JavaScript XMPP Client 4.0
|
||||
|
||||
[](https://travis-ci.org/jsxc/jsxc)
|
||||
[](https://dependencyci.com/github/jsxc/jsxc)
|
||||
@@ -8,3 +8,8 @@ Real-time chat app. This app requires an external XMPP server (openfire, ejabber
|
||||
You find a full list of features, supported protocols and browsers on [our homepage](http://www.jsxc.org).
|
||||
|
||||
If you are looking for install instructions or developer notes, please also checkout our [wiki](https://github.com/jsxc/jsxc/wiki/).
|
||||
|
||||
## Rewrite / Refactoring
|
||||
:warning: This branch is under heavy construction and definitely not ready for production.
|
||||
|
||||
This next big step for JSXC uses [Typescript](http://www.typescriptlang.org/index.html), [Webpack](https://webpack.github.io), [Handlebars](http://handlebarsjs.com), [Karma](http://karma-runner.github.io), [Mocha](https://mochajs.org), [Chai](http://chaijs.com) and [Sinon](http://sinonjs.org) to bring the best open XMPP chat experience to you. Currently we ship no packed version, so install all dependencies with `npm install` and execute `npm run dev` to test the current state. An example application is available at `example/ts.html`. To run all tests, enter `npm test`.
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "jsxc",
|
||||
"version": "3.2.0-beta.1",
|
||||
"homepage": "https://www.jsxc.org",
|
||||
"authors": [
|
||||
"sualko <klaus@jsxc.org>"
|
||||
],
|
||||
"description": "Real-time chat app",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"xmpp",
|
||||
"webrtc",
|
||||
"otr",
|
||||
"chat",
|
||||
"realtime"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"lib/",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"emojione": "~2.2.7",
|
||||
"favico.js": "^0.3.10",
|
||||
"strophe.bookmarks": "strophe/strophejs-plugin-bookmarks#ff9e95a1a823b927f8c4c45453aaa534857a37b4",
|
||||
"strophe.js": "strophejs#a11ebefa3db1b6712d51d0d309b9f68f8e391d1b",
|
||||
"strophe.vcard": "strophe/strophejs-plugin-vcard#de3a0c97a2c520ed900ee15b04a0c16d5c663891",
|
||||
"strophe.x": "strophe/strophejs-plugin-dataforms",
|
||||
"strophe.chatstates": "strophe/strophejs-plugin-chatstates",
|
||||
"jquery-i18next": "^1.2.0",
|
||||
"i18next": "^5.0.0",
|
||||
"magnific-popup": "^1.1.0",
|
||||
"strophejs-plugin-mam": "strophe/strophejs-plugin-mam#27e8d25",
|
||||
"strophejs-plugin-rsm": "strophe/strophejs-plugin-rsm#fa16b3b"
|
||||
}
|
||||
}
|
||||
+25
-70
@@ -2168,8 +2168,7 @@ fieldset[disabled]
|
||||
width: 100%; }
|
||||
|
||||
#jsxc_dialog .mfp-close, #jsxc_webrtc .mfp-close {
|
||||
font-size: 23px;
|
||||
font-weight: normal; }
|
||||
font-size: 23px; }
|
||||
|
||||
.mfp-bg {
|
||||
z-index: 9000; }
|
||||
@@ -2764,14 +2763,15 @@ fieldset[disabled]
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 30px; }
|
||||
width: 30px;
|
||||
height: 30px; }
|
||||
@media (max-width: 768px) {
|
||||
#jsxc_windowListSB {
|
||||
display: none; } }
|
||||
#jsxc_windowListSB > div {
|
||||
box-sizing: border-box;
|
||||
width: 14px;
|
||||
height: 30px;
|
||||
height: 100%;
|
||||
background-color: #cccccc;
|
||||
color: #807f7f;
|
||||
text-align: center;
|
||||
@@ -2881,8 +2881,7 @@ fieldset[disabled]
|
||||
.jsxc_window .jsxc_emoticons:hover:after {
|
||||
opacity: 0.5; }
|
||||
.jsxc_window .jsxc_fade {
|
||||
position: relative;
|
||||
overflow: hidden; }
|
||||
position: relative; }
|
||||
.jsxc_window .jsxc_fade .jsxc_overlay {
|
||||
display: none;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
@@ -2932,31 +2931,6 @@ fieldset[disabled]
|
||||
text-overflow: ellipsis; }
|
||||
.jsxc_window .jsxc_fade .jsxc_overlay > div a:hover {
|
||||
text-decoration: underline; }
|
||||
.jsxc_window .jsxc_fade .jsxc_mam-load-more {
|
||||
display: none;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
position: absolute;
|
||||
top: -42px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 42px;
|
||||
cursor: pointer;
|
||||
z-index: 80;
|
||||
line-height: 42px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.5s, top 0.5s;
|
||||
transition: opacity 0.5s, top 0.5s; }
|
||||
.jsxc_window .jsxc_fade .jsxc_mam-load-more.jsxc_show {
|
||||
top: 0;
|
||||
opacity: 0.7; }
|
||||
.jsxc_window .jsxc_fade .jsxc_mam-load-more.jsxc_show:hover {
|
||||
opacity: 1; }
|
||||
.jsxc_window .jsxc_fade.jsxc_mam-enable .jsxc_textarea {
|
||||
padding-top: 42px; }
|
||||
.jsxc_window .jsxc_fade.jsxc_mam-enable .jsxc_mam-load-more {
|
||||
display: block; }
|
||||
.jsxc_window .jsxc_avatar {
|
||||
margin-top: 1px; }
|
||||
.jsxc_window .jsxc_textarea {
|
||||
@@ -3202,6 +3176,26 @@ fieldset[disabled]
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
vertical-align: middle; }
|
||||
.jsxc_sys.jsxc_composing {
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
-webkit-transition: opacity 0.6s;
|
||||
transition: opacity 0.6s; }
|
||||
.jsxc_sys.jsxc_composing:before {
|
||||
content: " ";
|
||||
width: 1.5em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
background-size: 80%;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 3px 0 0;
|
||||
background-image: url('../img/composing.png'); }
|
||||
.jsxc_sys.jsxc_composing.jsxc_fadein {
|
||||
opacity: 1; }
|
||||
|
||||
div.jsxc_settings {
|
||||
position: relative; }
|
||||
@@ -3238,35 +3232,6 @@ div.jsxc_transfer {
|
||||
div.jsxc_transfer.jsxc_enc.jsxc_trust {
|
||||
background-image: url('../img/padlock_close_green.svg'); }
|
||||
|
||||
.jsxc_status-msg {
|
||||
font-size: 12px;
|
||||
display: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
height: 50%;
|
||||
line-height: 17.5px;
|
||||
opacity: 0.7; }
|
||||
.jsxc_status-msg.jsxc_composing:before {
|
||||
content: " ";
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
background-size: cover;
|
||||
vertical-align: middle;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url('../img/composing.png'); }
|
||||
|
||||
.jsxc_status-msg-show .jsxc_name {
|
||||
height: 50%;
|
||||
line-height: 20px; }
|
||||
|
||||
.jsxc_status-msg-show .jsxc_status-msg {
|
||||
display: block; }
|
||||
|
||||
.jsxc_status-msg-show .jsxc_lastmsg {
|
||||
display: none !important; }
|
||||
|
||||
.jsxc_windowItem.jsxc_groupchat.jsxc_normal .jsxc_bar .jsxc_avatar, li[data-type='groupchat'] .jsxc_avatar {
|
||||
text-indent: 999px;
|
||||
background-image: url('../img/group_white.svg');
|
||||
@@ -3344,8 +3309,6 @@ div.jsxc_transfer {
|
||||
color: inherit; }
|
||||
.jsxc_windowItem .jsxc_memberlist.jsxc_expand ul > li .jsxc_avatar {
|
||||
margin-right: 4px; }
|
||||
.jsxc_windowItem .jsxc_memberlist .jsxc_avatar :before {
|
||||
display: none; }
|
||||
|
||||
li[data-type='groupchat'] .jsxc_video {
|
||||
display: none; }
|
||||
@@ -3431,14 +3394,6 @@ li[data-type='groupchat'] .jsxc_video {
|
||||
background-color: #f2f2f2;
|
||||
padding: 10px;
|
||||
margin: 0 -30px 10px; }
|
||||
#jsxc_dialog form fieldset h3.jsxc_experimental:after {
|
||||
content: "experimental";
|
||||
font-size: 0.7em;
|
||||
border-radius: 2px;
|
||||
padding: 1px 5px;
|
||||
background-color: orange;
|
||||
margin-left: 5px;
|
||||
font-weight: normal; }
|
||||
#jsxc_dialog legend {
|
||||
border: 0;
|
||||
font-size: 20px; }
|
||||
|
||||
+453
-892
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
externo
+9
-9
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+2473
-3277
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+29
-30
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
externo
+7
@@ -0,0 +1,7 @@
|
||||
declare module '*.hbs' {
|
||||
// const content: any;
|
||||
// export default content;
|
||||
export default function template(options:any);
|
||||
}
|
||||
|
||||
declare function require(file:string):(context?:any, options?:any)=>string;
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "strophe.js",
|
||||
"file": "lib/strophe.js/strophe.js",
|
||||
"license": "multiple",
|
||||
"url": "http://strophe.im/strophejs/"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/muc",
|
||||
"file": "lib/strophe.muc.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/disco",
|
||||
"file": "lib/strophe.disco.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/caps",
|
||||
"file": "lib/strophe.caps.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/vcard",
|
||||
"file": "lib/strophe.vcard/strophe.vcard.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/bookmarks",
|
||||
"file": "lib/strophe.bookmarks/strophe.bookmarks.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins/tree/master/bookmarks"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/x",
|
||||
"file": "lib/strophe.x/src/strophe.x.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins/tree/master/dataforms"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/chatstates",
|
||||
"file": "lib/strophe.chatstates/strophe.chatstates.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugins/tree/master/chatstates"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/mam",
|
||||
"file": "lib/strophejs-plugin-mam/strophe.mam.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugin-mam"
|
||||
},
|
||||
{
|
||||
"name": "strophe.js/rsm",
|
||||
"file": "lib/strophejs-plugin-rsm/strophe.rsm.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/strophe/strophejs-plugin-rsm"
|
||||
},
|
||||
{
|
||||
"name": "strophe.jinglejs",
|
||||
"file": "lib/strophe.jinglejs/strophe.jinglejs-bundle.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/sualko/strophe.jinglejs"
|
||||
},
|
||||
{
|
||||
"name": "Salsa20",
|
||||
"file": "lib/otr/build/dep/salsa20.js",
|
||||
"license": "AGPL3",
|
||||
"url": "https://github.com/neoatlantis/node-salsa20"
|
||||
},
|
||||
{
|
||||
"name": "bigint",
|
||||
"file": "lib/otr/build/dep/bigint.js",
|
||||
"license": "public domain",
|
||||
"url": "www.leemon.com"
|
||||
},
|
||||
{
|
||||
"name": "cryptojs",
|
||||
"file": "lib/otr/build/dep/crypto.js",
|
||||
"license": "code.google.com/p/crypto-js/wiki/license",
|
||||
"url": "code.google.com/p/crypto-js"
|
||||
},
|
||||
{
|
||||
"name": "eventemitter",
|
||||
"file": "lib/otr/build/dep/eventemitter.js",
|
||||
"license": "MIT",
|
||||
"url": "http://git.io/ee"
|
||||
},
|
||||
{
|
||||
"name": "otr.js",
|
||||
"file": "lib/otr/build/otr.js",
|
||||
"license": "MPL v2.0",
|
||||
"url": "https://arlolra.github.io/otr/"
|
||||
},
|
||||
{
|
||||
"name": "i18next",
|
||||
"file": "lib/i18next/i18next.min.js",
|
||||
"license": "MIT",
|
||||
"url": "http://i18next.com/"
|
||||
},
|
||||
{
|
||||
"name": "jquery-i18next",
|
||||
"file": "lib/jquery-i18next/jquery-i18next.min.js",
|
||||
"license": "MIT",
|
||||
"url": "http://i18next.com/"
|
||||
},
|
||||
{
|
||||
"name": "Magnific Popup",
|
||||
"file": "lib/magnific-popup/dist/jquery.magnific-popup.min.js",
|
||||
"license": "MIT",
|
||||
"url": "http://dimsemenov.com/plugins/magnific-popup/"
|
||||
},
|
||||
{
|
||||
"name": "favico.js",
|
||||
"file": "lib/favico.js/favico.js",
|
||||
"license": "MIT",
|
||||
"url": "https://github.com/ejci/favico.js"
|
||||
},
|
||||
{
|
||||
"name": "emoji one",
|
||||
"file": "lib/emojione/lib/js/emojione.js",
|
||||
"license": "CC-BY 4.0",
|
||||
"url": "http://emojione.com"
|
||||
}
|
||||
]
|
||||
@@ -117,7 +117,6 @@ $(function() {
|
||||
localStorage.setItem('bosh-url', url);
|
||||
localStorage.setItem('xmpp-domain', domain);
|
||||
|
||||
jsxc.options.xmpp.url = url;
|
||||
settings.xmpp.url = url;
|
||||
settings.xmpp.domain = domain;
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>JSXC example application</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||
|
||||
<link href="css/bootstrap.min.css" media="all" rel="stylesheet" type="text/css" />
|
||||
|
||||
<!-- require:dependencies -->
|
||||
<link href="../build/css/jquery-ui.min.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<link href="../css/bundle.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<!-- endrequire -->
|
||||
|
||||
<link href="../node_modules/simplebar/dist/simplebar.css" media="all" rel="stylesheet" type="text/css" />
|
||||
|
||||
<link href="css/example.css" media="all" rel="stylesheet" type="text/css" />
|
||||
|
||||
<style>
|
||||
#jsxc-role {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: #a1a1a1;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
.jsxc-master #jsxc-role {
|
||||
background-color: green;
|
||||
}
|
||||
.jsxc-slave #jsxc-role {
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="jsxc-role"></div>
|
||||
|
||||
<form id="loginForm" style="margin-top: 100px; padding:15px;" class="form-inline">
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="text" name="url" placeholder="Bosh url" value="/http-bind/" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="text" name="jid" placeholder="Jabber Id" value="" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="text" name="password" placeholder="Password" value="" />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Login</button>
|
||||
</form>
|
||||
|
||||
<p style="padding:15px"><a class="btn btn-danger" href="javascript:jsxc.deleteAllData()">Delete all Data</a>
|
||||
<a class="btn btn-default" href="javascript:jsxc.enableDebugMode()">Enable Debug Mode</a>
|
||||
<a class="btn btn-default" href="javascript:jsxc.disableDebugMode()">Disable Debug Mode</a></p>
|
||||
|
||||
<!-- require:dependencies -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/jquery-ui.min.js"></script>
|
||||
<script src="../build/lib/jquery.slimscroll.js"></script>
|
||||
<script src="../build/lib/jquery.fullscreen.js"></script>
|
||||
<!-- <script src="../build/lib/jsxc.dep.js"></script> -->
|
||||
<!-- endrequire -->
|
||||
|
||||
<script src="../node_modules/simplebar/dist/simplebar.js"></script>
|
||||
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
|
||||
<!-- jsxc library -->
|
||||
<script src="../bundle.js"></script>
|
||||
|
||||
<script>
|
||||
$('#loginForm').submit(function(ev){
|
||||
ev.preventDefault();
|
||||
|
||||
var url = $(this).find('[name="url"]').val();
|
||||
var jid = $(this).find('[name="jid"]').val();
|
||||
var password = $(this).find('[name="password"]').val();
|
||||
|
||||
jsxc.start(url, jid, password);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg8"
|
||||
version="1.1"
|
||||
viewBox="0 0 3.1749999 3.1750001"
|
||||
height="12"
|
||||
width="12"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="presence_online.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1879"
|
||||
inkscape:window-height="1176"
|
||||
id="namedview5307"
|
||||
showgrid="false"
|
||||
inkscape:zoom="55.625733"
|
||||
inkscape:cx="3.7142348"
|
||||
inkscape:cy="6.3732363"
|
||||
inkscape:window-x="1241"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.4 KiB |
+100
@@ -0,0 +1,100 @@
|
||||
// jshint node:true
|
||||
|
||||
// var path = require('path');
|
||||
var webpackConfig = require('./webpack.config.js');
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: [ /*'karma-typescript',*/ 'mocha', 'chai'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'./node_modules/jquery/dist/jquery.min.js',
|
||||
'./node_modules/strophe.js/strophe.js',
|
||||
'./node_modules/es6-promise/dist/es6-promise.js',
|
||||
'test/*.spec.ts',
|
||||
'test/**/*.spec.ts'
|
||||
],
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
// webpack configuration
|
||||
webpack: {
|
||||
devtool: 'eval-source-map',
|
||||
module: webpackConfig.module,
|
||||
resolve: webpackConfig.resolve,
|
||||
//externals: webpackConfig.externals,
|
||||
// target: 'node',
|
||||
node: {
|
||||
fs: 'empty'
|
||||
}
|
||||
},
|
||||
|
||||
webpackMiddleware: {
|
||||
quiet: false,
|
||||
stats: {
|
||||
colors: true
|
||||
}
|
||||
},
|
||||
|
||||
mime: {
|
||||
'text/x-typescript': ['ts', 'tsx']
|
||||
},
|
||||
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
'test/**/*.spec.ts': ['webpack' /*'karma-typescript'*/ ]
|
||||
},
|
||||
|
||||
karmaTypescriptConfig: {
|
||||
include: ['test/**/*.spec.ts']
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: [ /*'progress', 'karma-typescript',*/ 'mocha'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_DEBUG,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['Chrome'],
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: false,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity
|
||||
});
|
||||
};
|
||||
+1
-1
Submodule lib/strophe.jinglejs updated: aac6d4610b...651a2ba8d2
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -56,7 +56,7 @@
|
||||
"Deny": "Ablehnen",
|
||||
"Approve": "Bestätigen",
|
||||
"Remove_buddy": "Kontakt entfernen",
|
||||
"You_are_about_to_remove_": "Du bist gerade dabei __bid_name__ (<b>__bid_jid__</b>) von deiner Kontaktliste zu entfernen. Alle zugehörigen Chats werden geschlossen.",
|
||||
"You_are_about_to_remove_": "Du bist gerade dabei __bid_name__ (<b>__bid_jid__</b>) von deiner Kontaktliste zu entfernen. Alle Chats werden geschlossen.",
|
||||
"Continue_without_chat": "Weiter ohne Chat",
|
||||
"Please_wait": "Bitte warten",
|
||||
"Login_failed": "Chat-Anmeldung fehlgeschlagen",
|
||||
@@ -265,10 +265,10 @@
|
||||
"Use_local_audio_device": "Nutze eigenes Audio Gerät",
|
||||
"Use_local_video_device": "Benutze eigene Webcam",
|
||||
"is_": "ist __status__",
|
||||
"You_received_a_message_from_an_unknown_sender_": "Du hast eine Nachricht von einem unbekannten Absender erhalten (__sender__). Möchtest du sie sehen?",
|
||||
"You_received_a_message_from_an_unknown_sender_": "Du hast eine Nachricht von einem unbekannten Sender erhalten (__sender__) Möchtest du sie sehen?",
|
||||
"Your_roster_is_empty_add_": "Deine Kontaktliste ist leer, füge einen neuen Kontakt <a>hinzu</a>",
|
||||
"onsmp_explanation_question": "Dein Kontakt versucht herauszufinden ob er wirklich mit dir redet. Um dich gegenüber deinem Kontakt zu verifizieren gib die Antwort ein und klick auf Antworten.",
|
||||
"onsmp_explanation_secret": "Dein Kontakt versucht herauszufinden ob er wirklich mit dir redet. Um dich gegenüber deinem Kontakt zu verifizieren gib das Geheimnis ein.",
|
||||
"onsmp_explanation_secret": "Dein Kontakt versucht herauszufinden ob er wirklich mit dir redet. Um dich gegenüber deinem Kontakt zu verifizieren gib das Geheimnis ein.",
|
||||
"from_sender": "von __sender__",
|
||||
"Verified_private_conversation_started": "Verifizierte private Konversation gestartet.",
|
||||
"Unverified_private_conversation_started": "Unverifizierte private Konversation gestartet.",
|
||||
@@ -283,9 +283,9 @@
|
||||
"Change": "Ändern",
|
||||
"Send_file": "Datei senden",
|
||||
"setting-explanation-carbon": "Wenn Kopien aktiviert sind, werden alle eingehenden Nachrichten zu allen angemeldeten Clients gesendet.",
|
||||
"setting-explanation-login": "Wenn diese Option aktiviert ist, wird der Chat beim Anmelden automatisch gestartet.",
|
||||
"setting-explanation-login": "Wenn diese Option aktiviert ist, wird der Chat beim anmelden automatisch gestartet.",
|
||||
"setting-explanation-priority": "Wenn du mit deinem XMPP Konto mehrfach angemeldet bist, werden Nachrichten zu dem Client mit der höchsten Priorität zugestellt.",
|
||||
"setting-explanation-xmpp": "Diese Optionen werden für die Verbindung zum XMPP Server genutzt.",
|
||||
"setting-explanation-xmpp": "Diese Optionen werden für die Verbindung zum XMPP server genutzt.",
|
||||
"_is_composing": " tippt gerade...",
|
||||
"_are_composing": " tippen gerade...",
|
||||
"Chat_state_notifications": "Statusbenachrichtigungen",
|
||||
@@ -300,10 +300,7 @@
|
||||
"Connection_accepted": "Verbindung angenommen",
|
||||
"Stream_terminated": "Stream beendet",
|
||||
"Close_all": "Schließe alle",
|
||||
"Notification": "Benachrichtigung",
|
||||
"Unreadable_OTR_message": "Unlesbare OTR Nachricht verworfen",
|
||||
"Load_older_messages": "Ältere Nachrichten laden",
|
||||
"Message_history": "Nachrichten Verlauf",
|
||||
"setting-mam-enable": null
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -265,13 +265,13 @@
|
||||
"Use_local_audio_device": "Use local audio device.",
|
||||
"Use_local_video_device": "Use local video device.",
|
||||
"is_": "is __status__",
|
||||
"You_received_a_message_from_an_unknown_sender_": "You received a message from an unknown sender (__sender__). Do you want to display them?",
|
||||
"You_received_a_message_from_an_unknown_sender_": "You received a message from an unknown sender (__sender__) Do you want to display them?",
|
||||
"Your_roster_is_empty_add_": "Your roster is empty, add a <a>new contact</a>",
|
||||
"onsmp_explanation_question": "Your contact is attempting to determine if they are really talking to you. To authenticate to your contact, enter the answer and click Answer.",
|
||||
"onsmp_explanation_secret": "Your contact is attempting to determine if they are really talking to you. To authenticate to your contact, enter the secret.",
|
||||
"onsmp_explanation_question": "You contact is attempting to determine if they are really talking to you. To authenticate to your contact, enter the answer and click Answer.",
|
||||
"onsmp_explanation_secret": "You contact is attempting to determine if they are really talking to you. To authenticate to your contact, enter the secret.",
|
||||
"from_sender": "from __sender__",
|
||||
"Verified_private_conversation_started": "Verified private conversation started.",
|
||||
"Unverified_private_conversation_started": "Unverified private conversation started.",
|
||||
"Verified_private_conversation_started": "Verified Private conversation started.",
|
||||
"Unverified_private_conversation_started": "Unverified Private conversation started.",
|
||||
"Bookmark": "Bookmark",
|
||||
"Auto-join": "Auto-join",
|
||||
"Edit_bookmark": "Edit bookmark",
|
||||
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": "Stream terminated",
|
||||
"Close_all": "Close all",
|
||||
"Notification": "Notification",
|
||||
"Unreadable_OTR_message": "Unreadable OTR message omitted",
|
||||
"Load_older_messages": "Load older messages",
|
||||
"Message_history": "Message history",
|
||||
"setting-mam-enable": "If enabled you are able to retrieve stored message from the server."
|
||||
"Unreadable_OTR_message": "Unreadable OTR message omitted"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -267,7 +267,7 @@
|
||||
"is_": "est __status__",
|
||||
"You_received_a_message_from_an_unknown_sender_": "Vous avez reçu un message d'un expéditeur inconnu (__sender__) Voulez-vous les afficher ?",
|
||||
"Your_roster_is_empty_add_": "Votre liste est vide, ajouter <a>Nouveau contact</a>",
|
||||
"onsmp_explanation_question": "Votre contact tente de déterminer si il ou elle vous parle vraiment. Pour vous authentifier auprès de votre contact, saisissez une réponse et cliquez sur Répondre.",
|
||||
"onsmp_explanation_question": "Votre contact tente de déterminer si il ou elle parle vraiment à vous. Pour vous authentifier auprès de votre contact, saisissez une réponse et cliquez sur Répondre.",
|
||||
"onsmp_explanation_secret": "Votre contact tente de déterminer si il ou elle parle vraiment à vous. Pour vous authentifier auprès de votre contact, entrez le mot secret",
|
||||
"from_sender": "de __sender__",
|
||||
"Verified_private_conversation_started": "La conversation privée vérifiée a démarré.",
|
||||
@@ -300,10 +300,7 @@
|
||||
"Connection_accepted": "Connexion acceptée",
|
||||
"Stream_terminated": "Flux terminé",
|
||||
"Close_all": "Tout fermer",
|
||||
"Notification": "Notification",
|
||||
"Unreadable_OTR_message": "Message OTR illisible omis",
|
||||
"Load_older_messages": "Charger des messages plus anciens",
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
{
|
||||
"translation": {
|
||||
"Logging_in": "Inloggen…",
|
||||
"your_connection_is_unencrypted": "Je verbinding is niet versleuteld.",
|
||||
"your_connection_is_encrypted": "Je verbinding is versleuteld.",
|
||||
"your_buddy_closed_the_private_connection": "Je contactpersoon sloot de prive-verbinding.",
|
||||
"start_private": "start privé",
|
||||
"close_private": "Sluit privé",
|
||||
"your_buddy_is_verificated": "Je contactpersoon is geverifieerd.",
|
||||
"you_have_only_a_subscription_in_one_way": "Je hebt een eenrichtingsabonnement.",
|
||||
"authentication_query_sent": "Verificatie vraag gestuurd.",
|
||||
"your_message_wasnt_send_please_end_your_private_conversation": "Je bericht is niet verzonden. Beëindig prive gesprek.",
|
||||
"unencrypted_message_received": "Ongecodeerde bericht ontvangen",
|
||||
"not_available": "Niet beschikbaar",
|
||||
"no_connection": "Geen Niet verbinding!",
|
||||
"relogin": "opnieuw inloggen",
|
||||
"trying_to_start_private_conversation": "Proberen om privé-gesprek te beginnen!",
|
||||
"Verified": "Geverifieerd",
|
||||
"Unverified": "Ongeverifieerd",
|
||||
"private_conversation_aborted": "Privé-gesprek afgebroken!",
|
||||
"your_buddy_closed_the_private_conversation_you_should_do_the_same": "Je contact sloot het Privé-gesprek! Doe hetzelfde.",
|
||||
"conversation_is_now_verified": "Gesprek is geverifieerd.",
|
||||
"authentication_failed": "Verificatie mislukt.",
|
||||
"Creating_your_private_key_": "Een persoonlijke sleutel maken. Dit kan een tijdje duren.",
|
||||
"Authenticating_a_buddy_helps_": "Authenticatie met een contact helpt ervoor te zorgen dat de persoon met wie u praat echt de persoon is die ze beweert te zijn.",
|
||||
"How_do_you_want_to_authenticate_your_buddy": "Hoe wilt u verificeren __bid_name__ (<b>__bid_jid__</b>)?",
|
||||
"Select_method": "Selectie methode...",
|
||||
"Manual": "Handleiding",
|
||||
"Question": "Vraag",
|
||||
"Secret": "Geheim",
|
||||
"To_verify_the_fingerprint_": "Neem via een ander betrouwbaar kanaal, contact op met uw gesprekspartner om de vingerafdruk te controleren. Bijvoorbeeld per telefoon.",
|
||||
"Your_fingerprint": "Jou vingerafdruk",
|
||||
"Buddy_fingerprint": "Contact vingerafdruk",
|
||||
"Close": "Sluiten",
|
||||
"Compared": "Vergeleken",
|
||||
"To_authenticate_using_a_question_": "Gebruik een vraag om te verificeren, neem een antwoord alleen bekend bij u en uw contact.",
|
||||
"Ask": "Vraag",
|
||||
"To_authenticate_pick_a_secret_": "Voor verificatie, kies een geheim alleen bekend is bij u en uw contact.",
|
||||
"Compare": "Vergelijk",
|
||||
"Fingerprints": "Vingerafdrukken",
|
||||
"Authentication": "Verificatie",
|
||||
"Message": "Bericht",
|
||||
"Add_buddy": "Contact toevoegen",
|
||||
"rename_buddy": "contact hernoemen",
|
||||
"delete_buddy": "contact verwijderen",
|
||||
"Login": "Login",
|
||||
"Username": "Gebruikersnaam",
|
||||
"Password": "Paswoord",
|
||||
"Cancel": "Annuleer",
|
||||
"Connect": "Verbind",
|
||||
"Type_in_the_full_username_": "Vul de volledige gebruikersnaam en een optionele alias in.",
|
||||
"Alias": "Alias",
|
||||
"Add": "Voeg toe",
|
||||
"Subscription_request": "Abonnementsverzoek",
|
||||
"You_have_a_request_from": "Je hebt een uitnodiging van",
|
||||
"Deny": "Ontken",
|
||||
"Approve": "Toestaan",
|
||||
"Remove_buddy": "contact verwijderen",
|
||||
"You_are_about_to_remove_": null,
|
||||
"Continue_without_chat": "Doorgaan zonder chat",
|
||||
"Please_wait": "Even geduld",
|
||||
"Login_failed": "Chat login mislukt",
|
||||
"Sorry_we_cant_authentikate_": "Verificatie is mislukt met de chatserver. Is het paswoord fout?",
|
||||
"Retry": "Terug",
|
||||
"clear_history": "Wis geschiedenis",
|
||||
"New_message_from": "Nieuw bericht van__name__",
|
||||
"Should_we_notify_you_": "Zullen wij u notificeren over nieuwe berichten in de toekomst?",
|
||||
"Please_accept_": "Klik op \"Toestaan\" aan de bovenkant.",
|
||||
"Hide_offline": "Offline contacten verbergen",
|
||||
"Show_offline": "Offline contacten weergeven",
|
||||
"About": "Over",
|
||||
"dnd": "Niet storen",
|
||||
"Mute": "Dempen aan",
|
||||
"Unmute": "Dempen uit",
|
||||
"Subscription": "Abonnement",
|
||||
"both": "Beide",
|
||||
"Status": "Status",
|
||||
"online": "online",
|
||||
"chat": "chat",
|
||||
"away": "Afwezig",
|
||||
"xa": "langer afwezig",
|
||||
"offline": "offline",
|
||||
"none": "geen",
|
||||
"Unknown_instance_tag": "Voorbeeld tag onbekend.",
|
||||
"Not_one_of_our_latest_keys": "Niet één van onze laatste sleutels.",
|
||||
"Received_an_unreadable_encrypted_message": "Een niet leesbare versleuteld bericht ontvangen.",
|
||||
"Online": "Online",
|
||||
"Chatty": "Spraakzaam",
|
||||
"Away": "Afwezig",
|
||||
"Extended_away": "Langer afwezig",
|
||||
"Offline": "Offline",
|
||||
"Friendship_request": "Contact verzoek",
|
||||
"Confirm": "Bevestig",
|
||||
"Dismiss": "Afwijzen",
|
||||
"Remove": "Verwijder",
|
||||
"Online_help": "Online hulp",
|
||||
"FN": "Volledige naam",
|
||||
"N": null,
|
||||
"FAMILY": "Familienaam",
|
||||
"GIVEN": "Voornaam",
|
||||
"NICKNAME": "Bijnaam",
|
||||
"URL": "URL",
|
||||
"ADR": "Adres",
|
||||
"STREET": "Adres",
|
||||
"EXTADD": "Uitgebreid adres",
|
||||
"LOCALITY": "Plaats",
|
||||
"REGION": "Regio",
|
||||
"PCODE": "Postcode",
|
||||
"CTRY": "Land",
|
||||
"TEL": "Telefoon",
|
||||
"NUMBER": "Nummer",
|
||||
"EMAIL": "E-mail",
|
||||
"USERID": null,
|
||||
"ORG": "Organisatie",
|
||||
"ORGNAME": "Naam",
|
||||
"ORGUNIT": "Afdeling",
|
||||
"TITLE": "functietitel",
|
||||
"ROLE": "Functie",
|
||||
"BDAY": "Verjaardag",
|
||||
"DESC": "Beschrijving",
|
||||
"PHOTO": null,
|
||||
"send_message": "Zend bericht",
|
||||
"get_info": "Gegevens weergeven",
|
||||
"Settings": "Instellingen",
|
||||
"Priority": "Prioriteit",
|
||||
"Save": "Opslaan",
|
||||
"User_settings": "Gebruikersinstellingen",
|
||||
"A_fingerprint_": "Een vingerafdruk wordt gebruikt om er zeker van te zijn dat uw gesprekspartner ook is wie hij of zij zegt te zijn.",
|
||||
"is": "is",
|
||||
"Login_options": "Login options",
|
||||
"BOSH_url": "BOSH URL",
|
||||
"Domain": "Domein",
|
||||
"Resource": "Bron",
|
||||
"On_login": "Tijdens login",
|
||||
"Received_an_unencrypted_message": "Een niet-versleuteld bericht ontvangen",
|
||||
"Sorry_your_buddy_doesnt_provide_any_information": "Sorry, je contact verschaft geen informatie.",
|
||||
"Info_about": "Gegevens van",
|
||||
"Authentication_aborted": "Verificatie afgebroken.",
|
||||
"Authentication_request_received": "Authenticatie verzoek ontvangen.",
|
||||
"Log_in_without_chat": "Zonder chat inloggen",
|
||||
"has_come_online": "is online gekomen",
|
||||
"Unknown_sender": "Afzender onbekend",
|
||||
"Please_allow_access_to_microphone_and_camera": "Klik op \"Toestaan\" aan de bovenkant voor de microfoon en camera.",
|
||||
"Incoming_call": "Inkomend gesprek",
|
||||
"from": "van",
|
||||
"Do_you_want_to_accept_the_call_from": "Wilt u het gesprek accepteren",
|
||||
"Reject": "Weiger",
|
||||
"Accept": "Aanvaard",
|
||||
"hang_up": "ophangen",
|
||||
"snapshot": "momentopname",
|
||||
"mute_my_audio": "mijn geluid dempen",
|
||||
"pause_my_video": "mijn video pauzeren",
|
||||
"fullscreen": "volledige scherm",
|
||||
"Info": "Info",
|
||||
"Local_IP": "Lokaal IP",
|
||||
"Remote_IP": "Extern IP",
|
||||
"Local_Fingerprint": "Lokale vingerafdruk",
|
||||
"Remote_Fingerprint": "Afstand vingerafdruk",
|
||||
"Video_call_not_possible": null,
|
||||
"Start_video_call": "Video gesprek starten",
|
||||
"Join_chat": "Neem deel aan chat",
|
||||
"Join": "Meedoen",
|
||||
"Room": "Kamer",
|
||||
"Nickname": "Bijnaam",
|
||||
"left_the_building": "__nickname__heeft het gebouw verlaten",
|
||||
"entered_the_room": "__nickname__kwam de kamer binnen",
|
||||
"is_now_known_as": "__newNickname__ is bekend als __oldNickname__",
|
||||
"This_room_is": "Deze kamer is",
|
||||
"muc_hidden": {
|
||||
"keyword": "verborgen",
|
||||
"description": "kan niet worden gevonden via zoeken"
|
||||
},
|
||||
"muc_membersonly": {
|
||||
"keyword": "Alleen leden",
|
||||
"description": null
|
||||
},
|
||||
"muc_moderated": {
|
||||
"keyword": null,
|
||||
"description": null
|
||||
},
|
||||
"muc_nonanonymous": {
|
||||
"keyword": null,
|
||||
"description": null
|
||||
},
|
||||
"muc_open": {
|
||||
"keyword": "open",
|
||||
"description": null
|
||||
},
|
||||
"muc_passwordprotected": {
|
||||
"keyword": "paswoord beschermd",
|
||||
"description": null
|
||||
},
|
||||
"muc_persistent": {
|
||||
"keyword": null,
|
||||
"description": null
|
||||
},
|
||||
"muc_public": {
|
||||
"keyword": "publiek",
|
||||
"description": null
|
||||
},
|
||||
"muc_semianonymous": {
|
||||
"keyword": null,
|
||||
"description": "Je Jabber id is alleen vrijgegeven voor kamer beheerders"
|
||||
},
|
||||
"muc_temporary": {
|
||||
"keyword": "tijdelijk",
|
||||
"description": null
|
||||
},
|
||||
"muc_unmoderated": {
|
||||
"keyword": null,
|
||||
"description": null
|
||||
},
|
||||
"muc_unsecured": {
|
||||
"keyword": "niet beveiligd",
|
||||
"description": null
|
||||
},
|
||||
"Continue": "Doorgaan",
|
||||
"Server": "Server",
|
||||
"Rooms_are_loaded": "Kamers zijn geladen",
|
||||
"Could_load_only": "Alleen aanvullen voor__count__kamers",
|
||||
"muc_explanation": "Vul de kamernaam, optioneel een bijnaam en wachtwoord in om deel te nemen een chat",
|
||||
"You_already_joined_this_room": "Je bent al verbonden met deze kamer",
|
||||
"This_room_will_be_closed": "De kamer wordt gesloten",
|
||||
"Room_not_found_": "Een nieuwe kamer wordt aangemaakt",
|
||||
"Loading_room_information": "Kamer informatie laden",
|
||||
"Destroy": "Vernietigen",
|
||||
"Leave": "Vertrekken",
|
||||
"changed_subject_to": "__nickname__veranderde het onderwerp van de kamer naar \"__subject__\"",
|
||||
"muc_removed_kicked": "Je bent afgemeld van de kamer",
|
||||
"muc_removed_info_kicked": "__nickname__is uit de kamer gegooid",
|
||||
"muc_removed_banned": "Je bent uit de kamer gezet",
|
||||
"muc_removed_info_banned": "__nickname__is uit de kamer gezet",
|
||||
"muc_removed_affiliation": "Je bent verwijderd van de kamer door een verwantschap wijziging",
|
||||
"muc_removed_info_affiliation": "__nickname__is verwijderd van de kamer door een verwantschap wijziging",
|
||||
"muc_removed_membersonly": null,
|
||||
"muc_removed_info_membersonly": "__nickname__is verwijderd van de kamer, door de wijziging naar alleen voor leden. Je hebt geen lidmaatschap.",
|
||||
"muc_removed_shutdown": null,
|
||||
"Reason": "Reden",
|
||||
"message_not_send": null,
|
||||
"message_not_send_item-not-found": null,
|
||||
"message_not_send_forbidden": null,
|
||||
"message_not_send_not-acceptable": null,
|
||||
"This_room_has_been_closed": "Deze kamer is gesloten",
|
||||
"Room_logging_is_enabled": "Kamerlog is ingeschakeld",
|
||||
"A_password_is_required": "Een paswoord is vereist",
|
||||
"You_are_not_on_the_member_list": "Je staat niet op de ledenlijst",
|
||||
"You_are_banned_from_this_room": "Je bent uit deze kamer gezet",
|
||||
"Your_desired_nickname_": null,
|
||||
"The_maximum_number_": null,
|
||||
"This_room_is_locked_": "Deze kamer is afgesloten",
|
||||
"You_are_not_allowed_to_create_": null,
|
||||
"Alert": null,
|
||||
"Call_started": "Gesprek gestart",
|
||||
"Call_terminated": "Gesprek beëindigd",
|
||||
"Carbon_copy": "Carbon kopie",
|
||||
"Enable": "Inschakelen",
|
||||
"jingle_reason_busy": "bezet",
|
||||
"jingle_reason_decline": "afwijzen",
|
||||
"jingle_reason_success": "opgehangen",
|
||||
"Media_failure": null,
|
||||
"No_local_audio_device": "Lokaal audioapparaat niet aanwezig.",
|
||||
"No_local_video_device": "Lokaal videoapparaat niet aanwezig.",
|
||||
"Ok": "Ok",
|
||||
"PermissionDeniedError": null,
|
||||
"Use_local_audio_device": "Lokaal audioapparaat gebruiken.",
|
||||
"Use_local_video_device": "Lokaal videoapparaat gebruiken.",
|
||||
"is_": "is __status__",
|
||||
"You_received_a_message_from_an_unknown_sender_": "U ontvangt een bericht van een onbekende afzender (__sender__). Wilt u om het weergeven?",
|
||||
"Your_roster_is_empty_add_": null,
|
||||
"onsmp_explanation_question": "Je contact probeert te bepalen of ze echt met jou praten. Voer om te verifiëren naar uw contact het antwoord in.",
|
||||
"onsmp_explanation_secret": "U contact probeert te bepalen of ze echt met jou praten. Voer om te verifiëren naar uw contact het geheim in.",
|
||||
"from_sender": "van__sender__",
|
||||
"Verified_private_conversation_started": "Privé-gesprek geverifieerd gestart.",
|
||||
"Unverified_private_conversation_started": "Privé-gesprek ongeverifieerd gestart.",
|
||||
"Bookmark": "Favorieten",
|
||||
"Auto-join": null,
|
||||
"Edit_bookmark": "Favorieten bewerken",
|
||||
"Room_logging_is_disabled": "Kamerlog is uitgeschakeld",
|
||||
"Room_is_now_non-anoymous": null,
|
||||
"Room_is_now_semi-anonymous": null,
|
||||
"Do_you_want_to_change_the_default_room_configuration": null,
|
||||
"Default": "Standaard",
|
||||
"Change": null,
|
||||
"Send_file": "Bestand zenden",
|
||||
"setting-explanation-carbon": "Met ingeschakeld carbon kopie zal de XMPP server een kopie van elk inkomend bericht doorsturen naar deze cliënt, zelfs als het niet aan haar is gericht.",
|
||||
"setting-explanation-login": "Wanneer ingeschakeld zal de chat starten bij het inloggen.",
|
||||
"setting-explanation-priority": "Je XMPP dienst zal een prioriteitsbericht verzenden wanneer je meerdere keren ingelogd bent met hetzelfde account.",
|
||||
"setting-explanation-xmpp": null,
|
||||
"_is_composing": null,
|
||||
"_are_composing": null,
|
||||
"Chat_state_notifications": null,
|
||||
"setting-explanation-chat-state": null,
|
||||
"Share_screen": "Scherm delen",
|
||||
"Incoming_stream": "Stream inkomend",
|
||||
"Stream_started": "Stream gestart",
|
||||
"HTTPS_REQUIRED": null,
|
||||
"EXTENSION_UNAVAILABLE": "Je hebt een browser extensie/addon nodig.",
|
||||
"UNKNOWN_ERROR": null,
|
||||
"Install_extension": "Installeer extensie om scherm delen te gebruiken: ",
|
||||
"Connection_accepted": null,
|
||||
"Stream_terminated": "Stream beëindigd",
|
||||
"Close_all": "Alle sluiten",
|
||||
"Notification": "Notificatie",
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": "Oudere berichten laden",
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
"authentication_failed": "Kimlik doğrulama başarısız.",
|
||||
"Creating_your_private_key_": "Özel anahtarınız oluşturuluyor; bu işlem biraz sürebilir.",
|
||||
"Authenticating_a_buddy_helps_": "Kimlik doğrulaması, konuşmakta olduğunuz kişinin gerçekten o kişi olduğundan emin olmanıza yardımcı olur.",
|
||||
"How_do_you_want_to_authenticate_your_buddy": "Kimlik doğrulamasını nasıl yapmak istersiniz __bid_name__ (<b>__bid_jid__</b>)?",
|
||||
"How_do_you_want_to_authenticate_your_buddy": "Kimlik doğrulasını nasıl yapmak istersiniz __bid_name__ (<b>__bid_jid__</b>)?",
|
||||
"Select_method": "Yöntemi seçin...",
|
||||
"Manual": "Elle",
|
||||
"Question": "Soru",
|
||||
@@ -261,13 +261,13 @@
|
||||
"No_local_audio_device": "Yerel ses cihazı bulunamadı.",
|
||||
"No_local_video_device": "Yerel video cihazı bulunamadı.",
|
||||
"Ok": "Tamam",
|
||||
"PermissionDeniedError": "Siz veya tarayıcınız medya iznini reddetti",
|
||||
"PermissionDeniedError": "Siz veya tarayıcınız sesli/görüntülü izni reddetti",
|
||||
"Use_local_audio_device": "Yerel video cihazını kullan.",
|
||||
"Use_local_video_device": "Yerel video cihazını kullanın.",
|
||||
"is_": "__status__",
|
||||
"You_received_a_message_from_an_unknown_sender_": "Bilinmeyen bir gönderenden bir ileti aldınız (__sender__) İletiyi görüntülemek istiyor musunuz?",
|
||||
"Your_roster_is_empty_add_": "Listeniz boş, yeni bir <a>kişi ekleyin</a>",
|
||||
"onsmp_explanation_question": "Karşınızdaki kişi, konuştuğu kişinin gerçekten siz olup olmadığınızı belirlemeye çalışıyor. Kimliğinizi doğrulamak için yanıtı girin ve Yanıtla'yı tıklayın.",
|
||||
"onsmp_explanation_question": "Karşınızdaki kişi, konuştuğu kişinin gerçekten siz olduğunuzu belirlemeye çalışıyor. Karşınızdaki kişiye kimliğinizi kanıtlamak için, yanıtı girin ve Yanıtla'yı tıklayın.",
|
||||
"onsmp_explanation_secret": "Karşınızdaki kişi, konuştuğu kişinin gerçekten siz olduğunuzu belirlemeye çalışıyor. Karşınızdaki kişiye kimliğinizi kanıtlamak için, parolayı girin.",
|
||||
"from_sender": "__sender__'den",
|
||||
"Verified_private_conversation_started": "Doğrulanmış Özel görüşme başladı.",
|
||||
@@ -300,10 +300,7 @@
|
||||
"Connection_accepted": "Bağlantı kabul edildi",
|
||||
"Stream_terminated": "Akış sonlandırıldı",
|
||||
"Close_all": "Hepsini kapat",
|
||||
"Notification": "Bildirim",
|
||||
"Unreadable_OTR_message": "Okunamayan OTR iletisi atlandı",
|
||||
"Load_older_messages": "Eski iletileri yükle",
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
@@ -300,10 +300,7 @@
|
||||
"Connection_accepted": "連線接受了",
|
||||
"Stream_terminated": "串流結束了",
|
||||
"Close_all": "全部關掉",
|
||||
"Notification": "通知",
|
||||
"Unreadable_OTR_message": "忽略無法解讀的 OTR 訊息",
|
||||
"Load_older_messages": "下載舊訊息",
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -301,9 +301,6 @@
|
||||
"Stream_terminated": null,
|
||||
"Close_all": null,
|
||||
"Notification": null,
|
||||
"Unreadable_OTR_message": null,
|
||||
"Load_older_messages": null,
|
||||
"Message_history": null,
|
||||
"setting-mam-enable": null
|
||||
"Unreadable_OTR_message": null
|
||||
}
|
||||
}
|
||||
+52
-24
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsxc",
|
||||
"version": "3.2.0-beta.1",
|
||||
"version": "3.1.1",
|
||||
"description": "Real-time xmpp chat application with video calls, file transfer and encrypted communication",
|
||||
"homepage": "http://www.jsxc.org/",
|
||||
"bugs": "https://github.com/jsxc/jsxc/issues",
|
||||
@@ -10,31 +10,59 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/jsxc/jsxc"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack --config webpack.config.js",
|
||||
"dev": "webpack --config webpack.config.js --watch",
|
||||
"test": "karma start"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.0.0",
|
||||
"@types/jquery": "^3.2.3",
|
||||
"@types/mocha": "^2.2.41",
|
||||
"@types/simplebar": "^2.4.0",
|
||||
"@types/sinon": "^2.3.1",
|
||||
"autoprefixer-core": "^6.0.1",
|
||||
"bower": "^1.8.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-autoprefixer": "^3.0.4",
|
||||
"grunt-banner": "~0.6.0",
|
||||
"grunt-contrib-clean": "~1.0.0",
|
||||
"grunt-contrib-compress": "^1.4.1",
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-csslint": "^2.0.0",
|
||||
"grunt-contrib-jshint": "~1.1.0",
|
||||
"grunt-contrib-uglify": "^2.0.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-data-uri": "^0.3.0",
|
||||
"grunt-github-releaser2": "^0.1.1",
|
||||
"grunt-html-convert": "0.0.2",
|
||||
"grunt-jsbeautifier": "^0.2.13",
|
||||
"grunt-jsdoc": "^2.1.0",
|
||||
"grunt-merge-data": "^0.4.5",
|
||||
"grunt-prettysass": "^0.2.3",
|
||||
"grunt-sass": "2.0.0",
|
||||
"grunt-scss-lint": "^0.5.0",
|
||||
"grunt-search": "^0.1.8",
|
||||
"grunt-text-replace": "~0.4.0",
|
||||
"node-sass": "4.3.0"
|
||||
"chai": "^4.0.2",
|
||||
"css-loader": "^0.28.4",
|
||||
"es6-promise": "^4.1.0",
|
||||
"extract-text-webpack-plugin": "^2.1.2",
|
||||
"handlebars-loader": "^1.5.0",
|
||||
"handlebars-runtime": "^1.0.12",
|
||||
"i18next": "^8.4.2",
|
||||
"karma": "^1.7.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^2.1.1",
|
||||
"karma-firefox-launcher": "^1.0.1",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-mocha-reporter": "^2.2.3",
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"karma-spec-reporter": "0.0.31",
|
||||
"karma-typescript": "^3.0.4",
|
||||
"karma-webpack": "^2.0.3",
|
||||
"mocha": "^3.4.2",
|
||||
"node-sass": "^4.5.3",
|
||||
"precompile-handlebars": "^1.0.5",
|
||||
"resolve-url-loader": "^2.0.3",
|
||||
"sass-loader": "^6.0.6",
|
||||
"sinon": "^2.3.5",
|
||||
"strophe": "^1.2.4",
|
||||
"ts-loader": "^2.2.0",
|
||||
"ts-node": "^3.1.0",
|
||||
"typescript": "^2.3.4",
|
||||
"webpack": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/emojione": "^2.2.1",
|
||||
"@types/handlebars": "^4.0.33",
|
||||
"@types/strophe": "^1.2.28",
|
||||
"i18next": "^7.1.1",
|
||||
"jquery": "^3.2.1",
|
||||
"magnific-popup": "^1.1.0",
|
||||
"moment": "^2.18.1",
|
||||
"simplebar": "^2.4.3",
|
||||
"strophe.js": "^1.2.14",
|
||||
"tslib": "^1.7.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
@import "modules";
|
||||
|
||||
#jsxc_buddylist {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 204px;
|
||||
z-index: 85;
|
||||
|
||||
.jsxc_unreadMsg {
|
||||
.jsxc_name {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_oneway {
|
||||
.jsxc_avatar, .jsxc_caption {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_right {
|
||||
float: right;
|
||||
margin-right: 6px;
|
||||
|
||||
div {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: $white;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_more {
|
||||
margin-right: 6px;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.jsxc_options {
|
||||
height: 20px;
|
||||
float: left;
|
||||
border-radius: 2px;
|
||||
background-color: $roster_icon_bg;
|
||||
|
||||
> div {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
float: left;
|
||||
margin-right: 0 1px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_hideOffline {
|
||||
.jsxc_rosteritem[data-status='offline'] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
@import "../lib/magnific-popup/src/css/main";
|
||||
@import "../lib/emojione/assets/css/emojione";
|
||||
|
||||
// BEGIN: bootstrap
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/variables";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/mixins";
|
||||
|
||||
// Spec and IE10+
|
||||
@keyframes progress-bar-stripes {
|
||||
from {
|
||||
background-position: 40px 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_dialog {
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/progress-bars";
|
||||
}
|
||||
|
||||
#jsxc_dialog, #jsxc_webrtc {
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/utilities";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/code";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/grid";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/alerts";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/buttons";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/button-groups";
|
||||
@import "../lib/bootstrap/assets/stylesheets/bootstrap/forms";
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-close {
|
||||
font-size: 23px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-bg {
|
||||
z-index: 9000;
|
||||
}
|
||||
|
||||
.mfp-wrap {
|
||||
z-index: 9010;
|
||||
}
|
||||
|
||||
.mfp-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// END: bootstrap
|
||||
@@ -1,540 +0,0 @@
|
||||
.jsxc_right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.jsxc_center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.jsxc_hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.jsxc_uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.jsxc_sep {
|
||||
border-top: 1px solid $separator;
|
||||
}
|
||||
|
||||
.jsxc_name {
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jsxc_maxWidth {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.jsxc_meta {
|
||||
text-align: right;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#jsxc_dialog {
|
||||
padding: 20px;
|
||||
min-width: 320px;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
background: #FFF;
|
||||
width: auto;
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
|
||||
input {
|
||||
margin-bottom: 5px;
|
||||
width: 60%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.jsxc_right {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
form {
|
||||
fieldset {
|
||||
margin-bottom: 30px;
|
||||
padding: 0 30px;
|
||||
border: 1px solid #d9d9d9;
|
||||
|
||||
h3 {
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
background-color: #f2f2f2;
|
||||
padding: 10px;
|
||||
margin: 0 -30px 10px;
|
||||
|
||||
&.jsxc_experimental:after {
|
||||
content: "experimental";
|
||||
font-size: 0.7em;
|
||||
border-radius: 2px;
|
||||
padding: 1px 5px;
|
||||
background-color: orange;
|
||||
margin-left: 5px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
legend {
|
||||
border: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
|
||||
&:invalid {
|
||||
border: 1px solid $dialog_input_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group button {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
background-color: $dialog_input_readonly_bg;
|
||||
}
|
||||
|
||||
.jsxc_inputinfo {
|
||||
padding: 0;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.jsxc_waiting {
|
||||
&:before {
|
||||
content: " ";
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
background-size: 100%;
|
||||
margin: 0 3px 0 0;
|
||||
background-image: image-url("loading.gif");
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_libraries, .jsxc_credits {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.jsxc_warning {
|
||||
display: block;
|
||||
background-color: #fbfe7a;
|
||||
padding: 3px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
margin: 0 5px;
|
||||
background-color: $avatar_bg;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 30px;
|
||||
color: $avatar_color;
|
||||
position: relative;
|
||||
font-family: $font_sans;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -6px;
|
||||
border: 2px solid $roster_bg;
|
||||
}
|
||||
}
|
||||
|
||||
ul.jsxc_vCard {
|
||||
min-width: 400px;
|
||||
|
||||
ul {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Spot which is attached to xmpp: uris
|
||||
.jsxc_spot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
text-indent: -99999em;
|
||||
margin-top: 3px;
|
||||
margin-right: 5px;
|
||||
line-height: 100%;
|
||||
cursor: pointer;
|
||||
border: 1px solid $spot_border;
|
||||
background-color: $white;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&.jsxc_online, &.jsxc_chat, &.jsxc_away, &.jsxc_xa, &.jsxc_dnd, &.jsxc_offline {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_unread {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_unreadMsg {
|
||||
.jsxc_name {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.jsxc_unread {
|
||||
display: block;
|
||||
background-color: $unread_bg;
|
||||
border-radius: 11px;
|
||||
color: $unread_color;
|
||||
font-size: 80%;
|
||||
padding: 2px;
|
||||
line-height: 15px;
|
||||
float: right;
|
||||
margin-right: 3px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check if required
|
||||
.jsxc_list {
|
||||
.jsxc_inner {
|
||||
box-sizing: border-box;
|
||||
max-height: 0;
|
||||
transition: max-height 0.5s;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.jsxc_opened {
|
||||
.jsxc_inner {
|
||||
max-height: 1000px;
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#cboxWrapper {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.jsxc_loading {
|
||||
margin: 0 auto;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 0;
|
||||
background-size: 32px 32px !important;
|
||||
background: image-url("loading.gif");
|
||||
}
|
||||
|
||||
// @TODO: check
|
||||
#jsxc_loginForm input[type='submit'] {
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
color: $white;
|
||||
background-color: $loginForm_bg;
|
||||
border-color: $loginForm_border;
|
||||
}
|
||||
|
||||
.jsxc_oneway .jsxc_avatar {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
img.jsxc_vCard {
|
||||
float: right;
|
||||
max-width: 200px;
|
||||
max-height: 200px;
|
||||
border: 5px solid $white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.jsxc_alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
|
||||
&.jsxc_alert-warning {
|
||||
color: #8a6d3b;
|
||||
background-color: #fcf8e3;
|
||||
border-color: #faebcc;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_btn {
|
||||
width: auto;
|
||||
min-width: 25px;
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
margin: 0 2px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.42857143;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.5s;
|
||||
|
||||
&.jsxc_btn-default {
|
||||
border-color: #ccc;
|
||||
color: #555;
|
||||
background-color: rgba(240, 240, 240, 0.9);
|
||||
|
||||
&:hover {
|
||||
background-color: #d6d6d6;
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_btn-primary {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
|
||||
&:hover {
|
||||
background-color: #296496;
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled], &[disabled]:hover {
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #FFF;
|
||||
color: #333;
|
||||
border-radius: 3px;
|
||||
z-index: 110;
|
||||
margin: 8px 2px 5px 10px;
|
||||
right: 0;
|
||||
filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
|
||||
padding: 4px 12px;
|
||||
padding-left: 5px;
|
||||
|
||||
&:after {
|
||||
bottom: 100%;
|
||||
right: 6px;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-color: rgba(238, 238, 238, 0);
|
||||
border-bottom-color: #fff;
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
&.jsxc_open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
border: 0;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
opacity: 0.5;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.jsxc_disabled {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
vertical-align: sub;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_editicon {
|
||||
background-image: image-url("edit_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_deleteicon {
|
||||
background-image: image-url("delete_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_chaticon {
|
||||
background-image: image-url("speech_balloon_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_videoicon {
|
||||
background-image: image-url("camera_icon_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_infoicon {
|
||||
background-image: image-url("info_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_settingsicon {
|
||||
background-image: image-url("gear_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_helpicon {
|
||||
background-image: image-url("help_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_contacticon {
|
||||
background-image: image-url("contact_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_groupcontacticon {
|
||||
background-image: image-url("groupcontact_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_bookmarkicon {
|
||||
background-image: image-url("bookmark_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_announcementicon {
|
||||
background-image: image-url("megaphone_icon_black.svg");
|
||||
}
|
||||
|
||||
.jsxc_more {
|
||||
float: right;
|
||||
width: 44px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
background-image: image-url("more_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: 25px;
|
||||
}
|
||||
}
|
||||
@@ -1,399 +0,0 @@
|
||||
#jsxc_roster {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 200px;
|
||||
overflow: visible;
|
||||
border-left: 1px solid #e1e1e1;
|
||||
display: none;
|
||||
|
||||
// border-left: 1px solid $roster_border_left;
|
||||
z-index: 80;
|
||||
margin-left: 10px;
|
||||
background-color: $roster_bg;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slimScrollDiv {
|
||||
margin-bottom: 30px;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.jsxc_wait {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 160px;
|
||||
padding: 20px;
|
||||
background-color: $white;
|
||||
z-index: 60;
|
||||
|
||||
img {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.13em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
height: 35px;
|
||||
padding: 7px 6px 5px;
|
||||
font-size: 13px;
|
||||
width: 145px;
|
||||
border: 1px solid #ddd;
|
||||
box-sizing: border-box;
|
||||
background-image: none;
|
||||
background-color: $roster_input_bg;
|
||||
border-radius: 3px;
|
||||
box-shadow: inner 0 0 5px $roster_input_shadow;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $roster_color;
|
||||
padding: 10px;
|
||||
|
||||
a {
|
||||
color: $roster_a;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_expand input {
|
||||
left: 51px;
|
||||
width: 137px;
|
||||
}
|
||||
|
||||
&.jsxc_noConnection {
|
||||
.slimScrollDiv {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .jsxc_bottom {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_state_hidden {
|
||||
display: block;
|
||||
right: -200px;
|
||||
transition: right 0.5s;
|
||||
|
||||
#jsxc_toggleRoster {
|
||||
&:before {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_state_shown {
|
||||
display: block;
|
||||
right: 0;
|
||||
transition: right 0.5s;
|
||||
}
|
||||
|
||||
> .jsxc_bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
line-height: 34px;
|
||||
background-color: $roster_bottom_bg;
|
||||
z-index: 50;
|
||||
padding-right: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: $roster_bottom_bg_hover;
|
||||
}
|
||||
|
||||
.jsxc_inner {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
border-top: 1px solid $roster_bottom_border_top;
|
||||
background-color: $roster_bottom_bg;
|
||||
|
||||
li:last-child {
|
||||
border-bottom: 1px solid $roster_bottom_border_top;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
height: 44px;
|
||||
background-color: $roster_bottom_bg;
|
||||
color: $roster_bottom_color;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding-left: 44px;
|
||||
line-height: 44px;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 15px center;
|
||||
background-size: 16px 16px;
|
||||
opacity: 0.8;
|
||||
|
||||
&.jsxc_disabled {
|
||||
color: $roster_bottom_disabled;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:hover:not(.jsxc_disabled) {
|
||||
color: $roster_bottom_color_hover;
|
||||
background-color: $roster_bottom_bg_hover;
|
||||
}
|
||||
|
||||
&.jsxc_warning {
|
||||
background-color: $warning_bg;
|
||||
|
||||
&:hover {
|
||||
background-color: $warning_bg_hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div > span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_toggleRoster {
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: -14px !important;
|
||||
top: 0;
|
||||
z-index: 110;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $roster_toggle_hover;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
border-style: solid;
|
||||
border-width: 6px 4px 6px 0;
|
||||
border-color: transparent $roster_bg;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_rosteritem {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 44px;
|
||||
border-bottom: 1px solid $roster_bottom_border_top;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
color: $roster_color;
|
||||
font-family: $font_sans;
|
||||
line-height: 44px;
|
||||
padding-left: 6px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
background-color: $roster_bg_hover;
|
||||
}
|
||||
|
||||
&.jsxc_bookmarked {
|
||||
.jsxc_avatar:after {
|
||||
content: " ";
|
||||
width: 20%;
|
||||
height: 30%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-image: image-url("bookmark_red.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_caption {
|
||||
padding-right: 30px;
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
|
||||
// padding-top: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
* {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jsxc_name {
|
||||
height: 100%;
|
||||
line-height: 40px;
|
||||
|
||||
.jsxc_min & {
|
||||
height: 50%;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.jsxc_rosteritem & {
|
||||
height: 50%;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_lastmsg {
|
||||
font-size: 12px;
|
||||
display: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.jsxc_min & {
|
||||
display: block;
|
||||
height: 50%;
|
||||
line-height: 17.5px;
|
||||
}
|
||||
|
||||
.jsxc_rosteritem & {
|
||||
display: block;
|
||||
height: 50%;
|
||||
line-height: 17.5px;
|
||||
}
|
||||
|
||||
.jsxc_text {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.jsxc_unread {
|
||||
line-height: 100%;
|
||||
font-size: 8px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
display: none;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: orange;
|
||||
vertical-align: top;
|
||||
margin: 0;
|
||||
float: none;
|
||||
|
||||
.jsxc_unreadMsg & {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_emoticon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_avatar {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
#jsxc_presence {
|
||||
cursor: pointer;
|
||||
padding-left: 2px;
|
||||
overflow: hidden;
|
||||
|
||||
> span {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
// Presence indicator
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
margin-top: -8px;
|
||||
border: 2px solid whitesmoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_menu {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
text-align: center;
|
||||
|
||||
&:hover > span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
> span {
|
||||
opacity: 0.5;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: image-url("menu_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 10px;
|
||||
background-size: 17px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_notice {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
float: right;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
|
||||
span {
|
||||
background-color: $notice_bg;
|
||||
border-radius: 11px;
|
||||
color: $notice_color;
|
||||
font-size: 80%;
|
||||
padding: 2px;
|
||||
position: relative;
|
||||
animation: bounce 2s 1s infinite;
|
||||
}
|
||||
|
||||
> span:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
.jsxc_online, .jsxc_chat, .jsxc_away, .jsxc_xa, .jsxc_dnd {
|
||||
&:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 100%;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
z-index: 99;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 100%;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_online:before {
|
||||
background-color: $state_online;
|
||||
}
|
||||
|
||||
.jsxc_chat:before {
|
||||
background-image: image-url("presence_chat.svg");
|
||||
background-color: $state_chat;
|
||||
}
|
||||
|
||||
.jsxc_away:before {
|
||||
background-image: image-url("presence_away.svg");
|
||||
background-color: $state_away;
|
||||
}
|
||||
|
||||
.jsxc_xa:before {
|
||||
background-image: image-url("presence_xa.svg");
|
||||
background-color: $state_xa;
|
||||
}
|
||||
|
||||
.jsxc_dnd:before {
|
||||
background-image: image-url("presence_dnd.svg");
|
||||
background-color: $state_dnd;
|
||||
}
|
||||
|
||||
.jsxc_hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_invalid {
|
||||
border: 2px solid $invalid_border !important;
|
||||
}
|
||||
@@ -1,756 +0,0 @@
|
||||
#jsxc_windowList {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 210px;
|
||||
left: 0;
|
||||
z-index: 50;
|
||||
transition: right 0.5s;
|
||||
|
||||
&.jsxc_roster_hidden {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
clip: rect(-10000px, 10000px, 30px, 30px);
|
||||
}
|
||||
|
||||
> ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 44px;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
transition: right 0.5s;
|
||||
|
||||
> li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
height: 44px;
|
||||
width: 46px;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
white-space: normal;
|
||||
|
||||
&.jsxc_normal {
|
||||
transition: width 0.2s;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
&.jsxc_min {
|
||||
transition: width 0.2s;
|
||||
width: 46px !important;
|
||||
|
||||
// overwrite resizeable width
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: 200px !important;
|
||||
}
|
||||
|
||||
.jsxc_emoticons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_tools {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_windowListSB {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 30px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> {
|
||||
div {
|
||||
box-sizing: border-box;
|
||||
width: 14px;
|
||||
height: 30px;
|
||||
background-color: $windowListSB_bg;
|
||||
color: $windowListSB_color;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $windowListSB_bg_hover;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_disabled {
|
||||
background-color: $windowListSB_bg_disabled !important;
|
||||
color: $windowListSB_color_disabled;
|
||||
cursor: default !important;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_bar {
|
||||
background-color: $window_bar_bg;
|
||||
cursor: pointer;
|
||||
height: 44px;
|
||||
line-height: 26px;
|
||||
padding: 2px;
|
||||
color: $window_bar_color;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.jsxc_normal & {
|
||||
color: $window_bar_color_hover;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_tools {
|
||||
&:hover {
|
||||
.jsxc_normal & {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_min & {
|
||||
background-color: $window_min_bar_bg;
|
||||
color: $window_min_bar_color;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_window {
|
||||
position: absolute;
|
||||
bottom: -284px;
|
||||
top: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: auto;
|
||||
background-color: $window_bg;
|
||||
z-index: 80;
|
||||
cursor: default;
|
||||
border: 1px solid $window_border;
|
||||
border-bottom: 0;
|
||||
|
||||
.jsxc_min & {
|
||||
transition: bottom 0.2s;
|
||||
}
|
||||
|
||||
.jsxc_normal & {
|
||||
transition: bottom 0.2s;
|
||||
}
|
||||
|
||||
.jsxc_showOverlay & {
|
||||
.jsxc_overlay {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_emoticons {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
background-image: image-url("smiley.svg");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 30px 30px;
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.jsxc_inner {
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
ul {
|
||||
width: 210px;
|
||||
margin-bottom: 8px;
|
||||
background-color: $emoticon_selection_bg;
|
||||
border-radius: 3px;
|
||||
z-index: 200;
|
||||
list-style-type: none;
|
||||
padding: 3px;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 8px solid $emoticon_selection_bg;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
left: 7px;
|
||||
top: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
div {
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
background-size: 30px 30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: $emoticon_selection_hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_fade {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.jsxc_overlay {
|
||||
display: none;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
overflow-y: scroll;
|
||||
|
||||
> div {
|
||||
background-color: #fff;
|
||||
margin: 30px 10px;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
.jsxc_close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
|
||||
&:after {
|
||||
content: "×";
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
font-size: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
cursor: pointer;
|
||||
color: #000;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_body {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_mam-load-more {
|
||||
display: none;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
position: absolute;
|
||||
top: -42px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 42px;
|
||||
cursor: pointer;
|
||||
z-index: 80;
|
||||
line-height: 42px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s, top 0.5s;
|
||||
|
||||
&.jsxc_show {
|
||||
top: 0;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_mam-enable {
|
||||
.jsxc_textarea {
|
||||
padding-top: 42px;
|
||||
}
|
||||
|
||||
.jsxc_mam-load-more {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.jsxc_textarea {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.slimScrollDiv {
|
||||
margin: 0 0 6px;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
}
|
||||
|
||||
textarea {
|
||||
&.jsxc_textinput {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
margin: 0;
|
||||
padding: 14px 40px 12px;
|
||||
outline: none;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
display: block;
|
||||
resize: none;
|
||||
transition: height 0.5s;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $window_placeholder;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_tools {
|
||||
float: right;
|
||||
|
||||
> .jsxc_disabled {
|
||||
opacity: 0.3;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 25px;
|
||||
height: 40px;
|
||||
display: block;
|
||||
float: left;
|
||||
color: $tools_color;
|
||||
opacity: 0.4;
|
||||
font-family: $font_sans;
|
||||
line-height: 40px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
|
||||
&.jsxc_settings {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_close {
|
||||
font-size: 20px;
|
||||
|
||||
&:hover {
|
||||
color: $window_close_hover;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_more {
|
||||
background-image: image-url("more_white.svg");
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.ui-resizable-w {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ui-resizable-nw {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
z-index: 95 !important;
|
||||
background-image: image-url("resize_gray.svg");
|
||||
}
|
||||
|
||||
.ui-resizable-n {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 15px;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_chatmessage {
|
||||
margin: 3px;
|
||||
padding: 4px;
|
||||
word-wrap: break-word;
|
||||
background-color: $chatmessage_bg;
|
||||
position: relative;
|
||||
outline: none;
|
||||
clear: both;
|
||||
|
||||
&.jsxc_error {
|
||||
opacity: 0.7;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: yellow;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $chatmessage_a;
|
||||
text-decoration: underline;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
|
||||
&[download]:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 5px;
|
||||
left: 0;
|
||||
border-radius: 3px;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
background-image: url("../img/download_icon_black.svg");
|
||||
background-size: contain;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
&[download]:hover {
|
||||
&:before {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_attachment {
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
padding: 3px;
|
||||
padding-left: 30px;
|
||||
min-height: 30px;
|
||||
margin-bottom: 5px;
|
||||
background-position: 3px center;
|
||||
background-size: 25px 25px;
|
||||
background-repeat: no-repeat;
|
||||
background-image: image-url("filetypes/file.svg");
|
||||
|
||||
img {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&.jsxc_image {
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
background-image: url("");
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.jsxc_application {
|
||||
background-image: image-url("filetypes/application.svg");
|
||||
}
|
||||
|
||||
&.jsxc_application-pdf {
|
||||
background-image: image-url("filetypes/application-pdf.svg");
|
||||
}
|
||||
|
||||
&.jsxc_audio {
|
||||
background-image: image-url("filetypes/audio.svg");
|
||||
}
|
||||
|
||||
&.jsxc_video {
|
||||
background-image: image-url("filetypes/video.svg");
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.jsxc_text {
|
||||
background-image: image-url("filetypes/text.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_timestamp {
|
||||
font-size: 8px;
|
||||
color: $chatmessage_timestamp;
|
||||
line-height: 8px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.jsxc_encrypted {
|
||||
&.jsxc_received.jsxc_out .jsxc_timestamp {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.jsxc_timestamp:after {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 8px;
|
||||
margin-left: 2px;
|
||||
background-image: image-url("padlock_close_grey.svg");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_in {
|
||||
float: left;
|
||||
position: relative;
|
||||
max-width: 76%;
|
||||
margin-left: 10px;
|
||||
border-radius: 3px;
|
||||
background-color: $chatmessage_in_bg;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
border-width: 5px 6px 5px 0;
|
||||
border-color: transparent $chatmessage_in_bg;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
left: -6px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.jsxc_timestamp {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_out {
|
||||
float: right;
|
||||
position: relative;
|
||||
max-width: 76%;
|
||||
margin-right: 10px;
|
||||
padding-right: 10px;
|
||||
border-radius: 3px;
|
||||
background-color: $chatmessage_out_bg;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
border-width: 5px 0 5px 6px;
|
||||
border-color: transparent $chatmessage_out_bg;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
&.jsxc_received {
|
||||
&:before {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
color: $chatmessage_received;
|
||||
}
|
||||
|
||||
.jsxc_timestamp {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_timestamp {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_sys {
|
||||
width: auto;
|
||||
max-width: none;
|
||||
padding-right: 4px;
|
||||
box-sizing: border-box;
|
||||
margin-right: 3px;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
|
||||
.jsxc_emoticon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
div.jsxc_settings {
|
||||
position: relative;
|
||||
|
||||
.jsxc_inner {
|
||||
left: auto;
|
||||
top: 100%;
|
||||
right: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
div.jsxc_transfer {
|
||||
background-image: image-url("padlock_open_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 14px 14px;
|
||||
opacity: 0.3;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.jsxc_disabled {
|
||||
background-image: image-url("padlock_open_disabled_black.svg");
|
||||
cursor: default;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_fin {
|
||||
opacity: 1;
|
||||
background-image: image-url("padlock_close_grey.svg");
|
||||
}
|
||||
|
||||
&.jsxc_enc {
|
||||
opacity: 1;
|
||||
background-image: image-url("padlock_close_orange.svg");
|
||||
|
||||
&.jsxc_trust {
|
||||
background-image: image-url("padlock_close_green.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_status-msg {
|
||||
font-size: 12px;
|
||||
display: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
height: 50%;
|
||||
line-height: 17.5px;
|
||||
opacity: 0.7;
|
||||
|
||||
&.jsxc_composing {
|
||||
&:before {
|
||||
content: " ";
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
background-size: cover;
|
||||
vertical-align: middle;
|
||||
background-repeat: no-repeat;
|
||||
background-image: image-url("composing.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_status-msg-show {
|
||||
.jsxc_name {
|
||||
height: 50%;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.jsxc_status-msg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.jsxc_lastmsg {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
@import "colors";
|
||||
@import "dep";
|
||||
|
||||
//fonts
|
||||
$font_sans: Arial, sans-serif;
|
||||
$font_serif: serif;
|
||||
|
||||
@import "modules";
|
||||
@import "buddylist";
|
||||
@import "state";
|
||||
@import "emoticons";
|
||||
@import "roster";
|
||||
@import "window";
|
||||
@import "muc";
|
||||
|
||||
@import "_jsxc";
|
||||
@import "webrtc";
|
||||
@@ -0,0 +1,20 @@
|
||||
@import "modules/all";
|
||||
@import "vendor/all";
|
||||
|
||||
//fonts
|
||||
$font_sans: Arial, sans-serif;
|
||||
$font_serif: serif;
|
||||
|
||||
@import "partials/button";
|
||||
@import "partials/dialog";
|
||||
@import "partials/emoticons";
|
||||
@import "partials/icon";
|
||||
@import "partials/jsxc";
|
||||
@import "partials/menu";
|
||||
@import "partials/roster";
|
||||
@import "partials/webrtc";
|
||||
@import "partials/window-list";
|
||||
@import "partials/window";
|
||||
|
||||
@import "partials/jsxc";
|
||||
@import "partials/webrtc";
|
||||
@@ -0,0 +1,5 @@
|
||||
@import "animation";
|
||||
@import "colors";
|
||||
@import "muc";
|
||||
@import "webrtc";
|
||||
@import "presence";
|
||||
@@ -25,12 +25,23 @@ $dialog_input_invalid: $red;
|
||||
$dialog_input_readonly_bg: $gray90;
|
||||
$spot_bg: $white;
|
||||
$spot_border: $black;
|
||||
|
||||
$state_online: green;
|
||||
$state_chat: green;
|
||||
$state_away: $orange;
|
||||
$state_xa: $orange;
|
||||
$state_dnd: $red;
|
||||
$state_offline: $gray60;
|
||||
|
||||
$presenceColors: (
|
||||
online: green,
|
||||
chat: green,
|
||||
away: $orange,
|
||||
xa: $orange,
|
||||
dnd: $red,
|
||||
offline: $gray60
|
||||
);
|
||||
|
||||
$notice_bg: $orange;
|
||||
$notice_color: $black;
|
||||
$window_unread_cycle: $orange;
|
||||
@@ -0,0 +1,6 @@
|
||||
%muc-avatar-icon {
|
||||
text-indent: 999px;
|
||||
background-image: url("../img/group_white.svg");
|
||||
background-size: 70% 70% !important;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
$presences: online chat away xa dnd;
|
||||
|
||||
@mixin presenceIndicator($target) {
|
||||
|
||||
@each $presence in $presences {
|
||||
[data-presence="#{$presence}"] #{$target} {
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 100%;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
z-index: 99;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 100%;
|
||||
box-sizing: content-box;
|
||||
background-image: url("../img/presence_#{$presence}.svg");
|
||||
background-color: map-get($presenceColors, $presence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
%fullscreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9000;
|
||||
background-color: $fullscreen_bg;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
.jsxc-btn {
|
||||
width: auto;
|
||||
min-width: 25px;
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
margin: 0 2px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.42857143;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.5s;
|
||||
|
||||
&.jsxc-btn-default {
|
||||
border-color: #ccc;
|
||||
color: #555;
|
||||
background-color: rgba(240, 240, 240, 0.9);
|
||||
|
||||
&:hover {
|
||||
background-color: #d6d6d6;
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc-btn-primary {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
|
||||
&:hover {
|
||||
background-color: #296496;
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled], &[disabled]:hover {
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-color: #2e6da4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
.jsxc-dialog {
|
||||
padding: 20px;
|
||||
min-width: 320px;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
background: #FFF;
|
||||
width: auto;
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
|
||||
input {
|
||||
margin-bottom: 5px;
|
||||
width: 60%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type='submit'] {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.jsxc-right {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
form {
|
||||
fieldset {
|
||||
margin-bottom: 30px;
|
||||
padding: 0 30px;
|
||||
border: 1px solid #d9d9d9;
|
||||
|
||||
h3 {
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
background-color: #f2f2f2;
|
||||
padding: 10px;
|
||||
margin: 0 -30px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
legend {
|
||||
border: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
|
||||
&:invalid {
|
||||
border: 1px solid $dialog_input_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group button {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
background-color: $dialog_input_readonly_bg;
|
||||
}
|
||||
|
||||
.jsxc-inputinfo {
|
||||
padding: 0;
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.jsxc-waiting {
|
||||
&:before {
|
||||
content: " ";
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
background-size: 100%;
|
||||
margin: 0 3px 0 0;
|
||||
background-image: url("../img/loading.gif");
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-libraries, .jsxc-credits {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.jsxc-warning {
|
||||
display: block;
|
||||
background-color: #fbfe7a;
|
||||
padding: 3px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
.jsxc_emoticon {
|
||||
.jsxc-emoticon {
|
||||
display: inline-block;
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
@@ -6,14 +6,14 @@
|
||||
border: 0;
|
||||
vertical-align: bottom;
|
||||
|
||||
&.jsxc_large {
|
||||
&.jsxc-large {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc_roster .jsxc_emoticon.jsxc_large {
|
||||
#jsxc-roster .jsxc-emoticon.jsxc-large {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
$emoticons: angel, angry, smile, grin, sad, wink, tonguesmile, surpised, kiss, sunglassess, crysad, doubt, zip, thumbsup, thumbsdown, beer, devil, kissing, rose, music, love, tired, surprised;
|
||||
|
||||
@each $emoticon in $emoticons {
|
||||
.jsxc_#{$emoticon} {
|
||||
background: image-url("emotions/#{$emoticon}.png");
|
||||
.jsxc-#{$emoticon} {
|
||||
background: url("../img/emotions/#{$emoticon}.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
.jsxc-icon-edit {
|
||||
background-image: url("../img/edit_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-delete {
|
||||
background-image: url("../img/delete_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-chat {
|
||||
background-image: url("../img/speech_balloon_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-video {
|
||||
background-image: url("../img/camera_icon_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-info {
|
||||
background-image: url("../img/info_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-setting {
|
||||
background-image: url("../img/gear_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-help {
|
||||
background-image: url("../img/help_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-contact {
|
||||
background-image: url("../img/contact_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-groupcontact {
|
||||
background-image: url("../img/groupcontact_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-bookmark {
|
||||
background-image: url("../img/bookmark_black.svg");
|
||||
}
|
||||
|
||||
.jsxc-icon-announcement {
|
||||
background-image: url("../img/megaphone_icon_black.svg");
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
.jsxc-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.jsxc-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.jsxc-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc-clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.jsxc-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.jsxc-separator {
|
||||
border-top: 1px solid $separator;
|
||||
}
|
||||
|
||||
.jsxc-name {
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jsxc-max-width {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.jsxc-meta {
|
||||
text-align: right;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.jsxc-invalid {
|
||||
border: 2px solid $invalid_border !important;
|
||||
}
|
||||
|
||||
.jsxc-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
margin: 0 5px;
|
||||
background-color: $avatar_bg;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 30px;
|
||||
color: $avatar_color;
|
||||
position: relative;
|
||||
font-family: $font_sans;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -6px;
|
||||
border: 2px solid $roster_bg;
|
||||
}
|
||||
}
|
||||
|
||||
ul.jsxc-vCard {
|
||||
min-width: 400px;
|
||||
|
||||
ul {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Spot which is attached to xmpp: uris
|
||||
.jsxc-spot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
text-indent: -99999em;
|
||||
margin-top: 3px;
|
||||
margin-right: 5px;
|
||||
line-height: 100%;
|
||||
cursor: pointer;
|
||||
border: 1px solid $spot_border;
|
||||
background-color: $white;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&.jsxc-online, &.jsxc-chat, &.jsxc-away, &.jsxc-xa, &.jsxc-dnd, &.jsxc-offline {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-unread {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc-unreadMsg {
|
||||
.jsxc-name {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.jsxc-unread {
|
||||
display: block;
|
||||
background-color: $unread_bg;
|
||||
border-radius: 11px;
|
||||
color: $unread_color;
|
||||
font-size: 80%;
|
||||
padding: 2px;
|
||||
line-height: 15px;
|
||||
float: right;
|
||||
margin-right: 3px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-loading {
|
||||
margin: 0 auto;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 0;
|
||||
background-size: 32px 32px !important;
|
||||
background: url("../img/loading.gif");
|
||||
}
|
||||
|
||||
// @TODO: check
|
||||
#jsxc-login-form input[type='submit'] {
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
color: $white;
|
||||
background-color: $loginForm_bg;
|
||||
border-color: $loginForm_border;
|
||||
}
|
||||
|
||||
.jsxc-oneway .jsxc-avatar {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
img.jsxc-vCard {
|
||||
float: right;
|
||||
max-width: 200px;
|
||||
max-height: 200px;
|
||||
border: 5px solid $white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.jsxc-alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
|
||||
&.jsxc-alert-warning {
|
||||
color: #8a6d3b;
|
||||
background-color: #fcf8e3;
|
||||
border-color: #faebcc;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-more {
|
||||
float: right;
|
||||
width: 44px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
background-image: url("../img/more_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: 25px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
.jsxc-menu {
|
||||
.jsxc-inner {
|
||||
box-sizing: border-box;
|
||||
max-height: 0;
|
||||
transition: max-height 0.5s;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.jsxc-opened {
|
||||
.jsxc-inner {
|
||||
max-height: 1000px;
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
border: 0;
|
||||
cursor: auto;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-menu-dropdown {
|
||||
.jsxc-inner {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #FFF;
|
||||
color: #333;
|
||||
border-radius: 3px;
|
||||
z-index: 110;
|
||||
margin: 8px 2px 5px 10px;
|
||||
right: 0;
|
||||
filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
|
||||
padding: 4px 12px 4px 5px;
|
||||
top: 100%;
|
||||
bottom: auto;
|
||||
overflow: visible;
|
||||
|
||||
&:after {
|
||||
bottom: 100%;
|
||||
right: 6px;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-color: rgba(238, 238, 238, 0);
|
||||
border-bottom-color: #fff;
|
||||
border-width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
opacity: 0.5;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.jsxc-disabled {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
span {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
vertical-align: sub;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,15 @@
|
||||
%muc-avatar-icon {
|
||||
text-indent: 999px;
|
||||
background-image: image-url("group_white.svg");
|
||||
background-size: 70% 70% !important;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.jsxc_windowItem {
|
||||
&.jsxc_groupchat.jsxc_normal {
|
||||
.jsxc_fade {
|
||||
.jsxc-window-item {
|
||||
&.jsxc-groupchat.jsxc-normal {
|
||||
.jsxc-fade {
|
||||
padding-top: 44px;
|
||||
}
|
||||
|
||||
.jsxc_fingerprints, .jsxc_verification, .jsxc_transfer, .jsxc_video, .jsxc_sendFile {
|
||||
.jsxc-fingerprints, .jsxc-verification, .jsxc-transfer, .jsxc-video, .jsxc-sendFile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_members {
|
||||
background-image: image-url("group_white.svg");
|
||||
.jsxc-members {
|
||||
background-image: url("../img/group_white.svg");
|
||||
background-size: 15px 15px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
@@ -26,10 +19,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_chatmessage.jsxc_in {
|
||||
.jsxc-chatmessage.jsxc-in {
|
||||
margin-left: 50px;
|
||||
|
||||
.jsxc_avatar {
|
||||
.jsxc-avatar {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -42,14 +35,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_bar {
|
||||
.jsxc_avatar {
|
||||
.jsxc-bar {
|
||||
.jsxc-avatar {
|
||||
@extend %muc-avatar-icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_memberlist {
|
||||
.jsxc-memberlist {
|
||||
height: 44px;
|
||||
width: 100%;
|
||||
background-color: $window_bar_bg;
|
||||
@@ -79,18 +72,18 @@
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.jsxc_name {
|
||||
.jsxc-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
.jsxc-avatar {
|
||||
margin-left: 2px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_expand {
|
||||
&.jsxc-expand {
|
||||
ul {
|
||||
white-space: normal;
|
||||
|
||||
@@ -100,7 +93,7 @@
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
|
||||
.jsxc_name {
|
||||
.jsxc-name {
|
||||
display: block;
|
||||
cursor: default;
|
||||
|
||||
@@ -109,27 +102,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
.jsxc-avatar {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_avatar {
|
||||
:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li[data-type='groupchat'] {
|
||||
.jsxc_avatar {
|
||||
.jsxc-avatar {
|
||||
@extend %muc-avatar-icon;
|
||||
}
|
||||
|
||||
.jsxc_video {
|
||||
.jsxc-video {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
#jsxc-roster {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 200px;
|
||||
overflow: visible;
|
||||
border-left: 1px solid #e1e1e1;
|
||||
z-index: 80;
|
||||
margin-left: 10px;
|
||||
background-color: $roster_bg;
|
||||
transition: right 0.5s;
|
||||
|
||||
.jsxc-roster-hidden & {
|
||||
right: -200px;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jsxc-avatar {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-wait {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 160px;
|
||||
padding: 20px;
|
||||
background-color: $white;
|
||||
z-index: 60;
|
||||
|
||||
img {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.13em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
height: 35px;
|
||||
padding: 7px 6px 5px;
|
||||
font-size: 13px;
|
||||
width: 145px;
|
||||
border: 1px solid #ddd;
|
||||
box-sizing: border-box;
|
||||
background-image: none;
|
||||
background-color: $roster_input_bg;
|
||||
border-radius: 3px;
|
||||
box-shadow: inner 0 0 5px $roster_input_shadow;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $roster_color;
|
||||
padding: 10px;
|
||||
|
||||
a {
|
||||
color: $roster_a;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-expand input {
|
||||
left: 51px;
|
||||
width: 137px;
|
||||
}
|
||||
|
||||
&.jsxc-state_hidden {
|
||||
display: block;
|
||||
right: -200px;
|
||||
transition: right 0.5s;
|
||||
}
|
||||
|
||||
&.jsxc-state_shown {
|
||||
display: block;
|
||||
right: 0;
|
||||
transition: right 0.5s;
|
||||
}
|
||||
|
||||
> .jsxc-bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
line-height: 34px;
|
||||
background-color: $roster_bottom_bg;
|
||||
z-index: 50;
|
||||
padding-right: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: $roster_bottom_bg_hover;
|
||||
}
|
||||
|
||||
.jsxc-inner {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
border-top: 1px solid $roster_bottom_border_top;
|
||||
background-color: $roster_bottom_bg;
|
||||
|
||||
li:last-child {
|
||||
border-bottom: 1px solid $roster_bottom_border_top;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
height: 44px;
|
||||
background-color: $roster_bottom_bg;
|
||||
color: $roster_bottom_color;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding-left: 44px;
|
||||
line-height: 44px;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 15px center;
|
||||
background-size: 16px 16px;
|
||||
opacity: 0.8;
|
||||
|
||||
&.jsxc-disabled {
|
||||
color: $roster_bottom_disabled;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:hover:not(.jsxc-disabled) {
|
||||
color: $roster_bottom_color_hover;
|
||||
background-color: $roster_bottom_bg_hover;
|
||||
}
|
||||
|
||||
&.jsxc-warning {
|
||||
background-color: $warning_bg;
|
||||
|
||||
&:hover {
|
||||
background-color: $warning_bg_hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div > span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
padding: 15px;
|
||||
|
||||
button,
|
||||
input {
|
||||
width: 100%;
|
||||
margin: 0 0 5px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #dadada;
|
||||
border-color: #c1c1c1;
|
||||
transition: background-color 0.5s;
|
||||
|
||||
&:hover {
|
||||
background-color: #a2a2a2;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-roster-status {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&.jsxc-status-show {
|
||||
.jsxc-roster-status {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.jsxc-bottom, .jsxc-contact-list {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-roster-toggle {
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: -14px;
|
||||
top: 0;
|
||||
z-index: 110;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $roster_toggle_hover;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-roster-item {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 44px;
|
||||
border-bottom: 1px solid $roster_bottom_border_top;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
color: $roster_color;
|
||||
font-family: $font_sans;
|
||||
line-height: 44px;
|
||||
padding-left: 6px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
background-color: $roster_bg_hover;
|
||||
}
|
||||
|
||||
&.jsxc-bookmarked {
|
||||
.jsxc-avatar:after {
|
||||
content: " ";
|
||||
width: 20%;
|
||||
height: 30%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("../img/bookmark_red.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-caption {
|
||||
padding-right: 30px;
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
// padding-top: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
* {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jsxc-name {
|
||||
height: 100%;
|
||||
line-height: 40px;
|
||||
|
||||
.jsxc-min & {
|
||||
height: 50%;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.jsxc-rosteritem & {
|
||||
height: 50%;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-lastmsg {
|
||||
font-size: 12px;
|
||||
display: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.jsxc-min & {
|
||||
display: block;
|
||||
height: 50%;
|
||||
line-height: 17.5px;
|
||||
}
|
||||
|
||||
.jsxc-rosteritem & {
|
||||
display: block;
|
||||
height: 50%;
|
||||
line-height: 17.5px;
|
||||
}
|
||||
|
||||
.jsxc-text {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.jsxc-unread {
|
||||
line-height: 100%;
|
||||
font-size: 8px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
display: none;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: orange;
|
||||
vertical-align: top;
|
||||
margin: 0;
|
||||
float: none;
|
||||
|
||||
.jsxc-unreadMsg & {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-emoticon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-menu-presence {
|
||||
cursor: pointer;
|
||||
padding-left: 2px;
|
||||
overflow: hidden;
|
||||
|
||||
> span {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
// Presence indicator
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
margin-top: -8px;
|
||||
border: 2px solid whitesmoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-menu-main {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
text-align: center;
|
||||
|
||||
&:hover > span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
> span {
|
||||
opacity: 0.5;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../img/menu_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 10px;
|
||||
background-size: 17px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc-notice {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
float: right;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
|
||||
span {
|
||||
background-color: $notice_bg;
|
||||
border-radius: 11px;
|
||||
color: $notice_color;
|
||||
font-size: 80%;
|
||||
padding: 2px;
|
||||
position: relative;
|
||||
animation: bounce 2s 1s infinite;
|
||||
}
|
||||
|
||||
> span:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-contact-list {
|
||||
@include presenceIndicator(".jsxc-avatar");
|
||||
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 204px;
|
||||
z-index: 85;
|
||||
|
||||
&.jsxc-hide-offline {
|
||||
.jsxc-roster-item[data-status='offline'] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-unreadMsg {
|
||||
.jsxc-name {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-oneway {
|
||||
.jsxc-avatar,
|
||||
.jsxc-caption {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-right {
|
||||
float: right;
|
||||
margin-right: 6px;
|
||||
|
||||
div {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: $white;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-menu {
|
||||
float: right;
|
||||
height: 100%;
|
||||
width: 42px;
|
||||
|
||||
> span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-image: url("../img/more_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-inner {
|
||||
left: auto;
|
||||
right: 5px;
|
||||
padding: 4px;
|
||||
overflow: visible;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
line-height: 42px;
|
||||
width: 42px;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,5 @@
|
||||
@import "colors";
|
||||
|
||||
%fullscreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9000;
|
||||
background-color: $fullscreen_bg;
|
||||
}
|
||||
|
||||
#jsxc_webrtc {
|
||||
#jsxc-webrtc {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
@@ -19,7 +8,7 @@
|
||||
z-index: 9999;
|
||||
background-color: black;
|
||||
|
||||
.jsxc_status {
|
||||
.jsxc-status {
|
||||
z-index: 9999;
|
||||
border-radius: 20px;
|
||||
display: none;
|
||||
@@ -37,7 +26,7 @@
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
li .jsxc_name {
|
||||
li .jsxc-name {
|
||||
cursor: auto;
|
||||
|
||||
&:hover {
|
||||
@@ -46,7 +35,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_establishing, .jsxc_ringing {
|
||||
.jsxc-establishing, .jsxc-ringing {
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
@@ -62,14 +51,14 @@
|
||||
$establishingColor1: #a1a1a1;
|
||||
$establishingColor2: #f1f1f1;
|
||||
|
||||
.jsxc_establishing:before {
|
||||
.jsxc-establishing:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 40px;
|
||||
height: 10px;
|
||||
box-sizing: border-box;
|
||||
background-color: $establishingColor1;
|
||||
animation-name: jsxc_establishing;
|
||||
animation-name: jsxc-establishing;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
position: absolute;
|
||||
@@ -80,7 +69,7 @@ $establishingColor2: #f1f1f1;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
@keyframes jsxc_establishing {
|
||||
@keyframes jsxc-establishing {
|
||||
0% {
|
||||
border-width: 0;
|
||||
background-color: $establishingColor1;
|
||||
@@ -105,7 +94,7 @@ $establishingColor2: #f1f1f1;
|
||||
$ringingColor1: #98d48f;
|
||||
$ringingColor2: #76ba6c;
|
||||
|
||||
.jsxc_ringing:before {
|
||||
.jsxc-ringing:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 20px;
|
||||
@@ -113,7 +102,7 @@ $ringingColor2: #76ba6c;
|
||||
box-sizing: border-box;
|
||||
background-color: $ringingColor1;
|
||||
border-radius: 50%;
|
||||
animation-name: jsxc_ringing;
|
||||
animation-name: jsxc-ringing;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
position: absolute;
|
||||
@@ -124,7 +113,7 @@ $ringingColor2: #76ba6c;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
@keyframes jsxc_ringing {
|
||||
@keyframes jsxc-ringing {
|
||||
0% {
|
||||
background-color: $ringingColor1;
|
||||
width: 20px;
|
||||
@@ -150,17 +139,17 @@ $ringingColor2: #76ba6c;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_bell:before {
|
||||
.jsxc-bell:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
box-sizing: border-box;
|
||||
background-image: image-url("bell.svg");
|
||||
background-image: url("../img/bell.svg");
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
animation-name: jsxc_ringing;
|
||||
animation-name: jsxc-ringing;
|
||||
animation-duration: 1.5s;
|
||||
animation-iteration-count: infinite;
|
||||
position: absolute;
|
||||
@@ -171,7 +160,7 @@ $ringingColor2: #76ba6c;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
@keyframes jsxc_bell {
|
||||
@keyframes jsxc-bell {
|
||||
0% {
|
||||
margin-left: -50px;
|
||||
}
|
||||
@@ -205,7 +194,7 @@ $ringingColor2: #76ba6c;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_videoContainer {
|
||||
.jsxc-videoContainer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -213,7 +202,7 @@ $ringingColor2: #76ba6c;
|
||||
bottom: 0;
|
||||
background-color: $video_bg;
|
||||
|
||||
&.jsxc_minimized {
|
||||
&.jsxc-minimized {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
@@ -223,7 +212,7 @@ $ringingColor2: #76ba6c;
|
||||
background-color: transparent;
|
||||
box-shadow: 0 0 10px #a1a1a1;
|
||||
|
||||
.jsxc_localvideo {
|
||||
.jsxc-localvideo {
|
||||
position: static;
|
||||
display: block;
|
||||
}
|
||||
@@ -233,30 +222,30 @@ $ringingColor2: #76ba6c;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_noRemoteVideo {
|
||||
.jsxc-noRemoteVideo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
right: 250px;
|
||||
|
||||
.jsxc_controlbar {
|
||||
.jsxc-controlbar {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.jsxc_controlbar {
|
||||
.jsxc-controlbar {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_remotevideo {
|
||||
.jsxc-remotevideo {
|
||||
@extend %fullscreen;
|
||||
}
|
||||
|
||||
.jsxc_noRemoteVideo {
|
||||
.jsxc-noRemoteVideo {
|
||||
@extend %fullscreen;
|
||||
|
||||
p {
|
||||
@@ -303,7 +292,7 @@ $ringingColor2: #76ba6c;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_localvideo {
|
||||
.jsxc-localvideo {
|
||||
width: 160px;
|
||||
height: 120px;
|
||||
position: absolute;
|
||||
@@ -320,7 +309,7 @@ div {
|
||||
height: 100%;
|
||||
background-color: $black;
|
||||
|
||||
&.jsxc_localvideo {
|
||||
&.jsxc-localvideo {
|
||||
border: 1px solid $white;
|
||||
}
|
||||
}
|
||||
@@ -330,23 +319,23 @@ div {
|
||||
height: 100%;
|
||||
background-color: $black;
|
||||
|
||||
&.jsxc_localvideo {
|
||||
&.jsxc-localvideo {
|
||||
border: 1px solid $white;
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc_video {
|
||||
background-image: image-url("camera_icon_white.svg");
|
||||
&.jsxc-video {
|
||||
background-image: url("../img/camera_icon_white.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 15px 15px;
|
||||
opacity: 0.4;
|
||||
|
||||
&.jsxc_disabled {
|
||||
background-image: image-url("camera_disabled_icon_white.svg");
|
||||
&.jsxc-disabled {
|
||||
background-image: url("../img/camera_disabled_icon_white.svg");
|
||||
}
|
||||
|
||||
&:not(.jsxc_disabled) {
|
||||
&:not(.jsxc-disabled) {
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -354,7 +343,7 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_controlbar {
|
||||
.jsxc-controlbar {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
@@ -369,7 +358,7 @@ div {
|
||||
top: initial;
|
||||
}
|
||||
|
||||
&.jsxc_visible {
|
||||
&.jsxc-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -381,7 +370,7 @@ div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jsxc_videoControl {
|
||||
.jsxc-videoControl {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
margin: 0 5px;
|
||||
@@ -397,24 +386,24 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_hangUp {
|
||||
background-image: image-url("hang_up_red.svg");
|
||||
.jsxc-hangUp {
|
||||
background-image: url("../img/hang_up_red.svg");
|
||||
}
|
||||
|
||||
.jsxc_fullscreen {
|
||||
background-image: image-url("fullscreen_white.svg");
|
||||
.jsxc-fullscreen {
|
||||
background-image: url("../img/fullscreen_white.svg");
|
||||
}
|
||||
|
||||
.jsxc_showchat {
|
||||
.jsxc-showchat {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_multi > div {
|
||||
.jsxc-multi > div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc_snapshotbar {
|
||||
.jsxc-snapshotbar {
|
||||
width: 100%;
|
||||
display: none;
|
||||
|
||||
@@ -423,7 +412,7 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_buttongroup {
|
||||
.jsxc-buttongroup {
|
||||
display: inline;
|
||||
|
||||
button {
|
||||
@@ -441,7 +430,7 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_chatarea {
|
||||
.jsxc-chatarea {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@@ -454,21 +443,21 @@ div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.jsxc_settings {
|
||||
.jsxc-settings {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.jsxc_close {
|
||||
.jsxc-close {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.jsxc_video {
|
||||
.jsxc-video {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.jsxc_bar {}
|
||||
.jsxc-bar {}
|
||||
|
||||
.jsxc_window {
|
||||
.jsxc-window {
|
||||
bottom: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -481,10 +470,10 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc_fullscreen.jsxc_localvideo {
|
||||
.jsxc-fullscreen.jsxc-localvideo {
|
||||
border: 1px solid $white;
|
||||
}
|
||||
|
||||
.jsxc_videoSuitable .jsxc_name {
|
||||
.jsxc-videoSuitable .jsxc-name {
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#jsxc-window-list {
|
||||
@include presenceIndicator(".jsxc-window-bar .jsxc-avatar");
|
||||
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 210px;
|
||||
left: 0;
|
||||
z-index: 50;
|
||||
transition: right 0.5s;
|
||||
|
||||
.jsxc-roster-hidden & {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
clip: rect(-10000px, 10000px, 30px, 30px);
|
||||
}
|
||||
|
||||
> ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 44px;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
transition: right 0.5s;
|
||||
|
||||
> li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
height: 44px;
|
||||
width: 46px;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
white-space: normal;
|
||||
|
||||
&.jsxc-normal {
|
||||
transition: width 0.2s;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
&.jsxc-minimized {
|
||||
transition: width 0.2s;
|
||||
width: 46px !important;
|
||||
// overwrite resizeable width
|
||||
@media (min-width: 768px) {
|
||||
width: 200px !important;
|
||||
}
|
||||
|
||||
.jsxc-emoticons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc-tools {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc-window-list-handler {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> {
|
||||
div {
|
||||
box-sizing: border-box;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
background-color: $windowListSB_bg;
|
||||
color: $windowListSB_color;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $windowListSB_bg_hover;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-disabled {
|
||||
background-color: $windowListSB_bg_disabled !important;
|
||||
color: $windowListSB_color_disabled;
|
||||
cursor: default !important;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,657 @@
|
||||
.jsxc-window {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: auto;
|
||||
background-color: $window_bg;
|
||||
z-index: 80;
|
||||
cursor: default;
|
||||
border: 1px solid $window_border;
|
||||
border-bottom: 0;
|
||||
|
||||
.jsxc-showOverlay & {
|
||||
.jsxc-overlay {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-avatar {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.jsxc-message-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2px;
|
||||
bottom: 44px;
|
||||
left: 0;
|
||||
overflow: auto;
|
||||
padding: 3px;
|
||||
z-index: 10;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #d1d1d1;
|
||||
border: 0;
|
||||
border-right: 3px solid transparent;
|
||||
border-radius: 1px;
|
||||
|
||||
&:hover {
|
||||
background: #c1c1c1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #b1b1b1;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border: 0 none #fff;
|
||||
border-radius: 50px;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
// .slimScrollDiv {
|
||||
// margin: 0 0 6px;
|
||||
// left: auto !important;
|
||||
// top: auto !important;
|
||||
// }
|
||||
textarea {
|
||||
&.jsxc-message-input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
margin: 0;
|
||||
padding: 14px 40px 12px;
|
||||
outline: none;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
display: block;
|
||||
resize: none;
|
||||
transition: height 0.2s;
|
||||
font-size: 13px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $window_placeholder;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-tools {
|
||||
float: right;
|
||||
|
||||
> .jsxc-disabled {
|
||||
opacity: 0.3;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 25px;
|
||||
height: 40px;
|
||||
display: block;
|
||||
float: left;
|
||||
color: $tools_color;
|
||||
opacity: 0.4;
|
||||
font-family: $font_sans;
|
||||
line-height: 40px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
|
||||
&.jsxc-menu {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-close {
|
||||
font-size: 20px;
|
||||
|
||||
&:hover {
|
||||
color: $window_close_hover;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-more {
|
||||
background-image: url("../img/more_white.svg");
|
||||
opacity: 0.4;
|
||||
}
|
||||
// .ui-resizable-w {
|
||||
// left: 0;
|
||||
// }
|
||||
//
|
||||
// .ui-resizable-nw {
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// width: 15px;
|
||||
// height: 15px;
|
||||
// z-index: 95 !important;
|
||||
// background-image: url("../img/resize_gray.svg");
|
||||
// }
|
||||
//
|
||||
// .ui-resizable-n {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// height: 15px;
|
||||
// z-index: 100;
|
||||
// }
|
||||
}
|
||||
|
||||
.jsxc-window-bar {
|
||||
background-color: $window_bar_bg;
|
||||
cursor: pointer;
|
||||
height: 44px;
|
||||
line-height: 26px;
|
||||
padding: 2px;
|
||||
color: $window_bar_color;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&:hover {
|
||||
.jsxc-normal & {
|
||||
color: $window_bar_color_hover;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-tools {
|
||||
&:hover {
|
||||
.jsxc-normal & {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-minimized & {
|
||||
background-color: $window_min_bar_bg;
|
||||
color: $window_min_bar_color;
|
||||
}
|
||||
|
||||
.jsxc-highlight & {
|
||||
background-color: orange;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-window-fade {
|
||||
position: relative;
|
||||
height: 320px;
|
||||
transition: height 0.2s;
|
||||
overflow: hidden;
|
||||
|
||||
.jsxc-normal & {}
|
||||
|
||||
.jsxc-minimized & {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.jsxc-overlay {
|
||||
display: none;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
overflow-y: scroll;
|
||||
|
||||
> div {
|
||||
background-color: #fff;
|
||||
margin: 30px 10px;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
.jsxc-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
|
||||
&:after {
|
||||
content: "×";
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
font-size: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
cursor: pointer;
|
||||
color: #000;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-body {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-menu-emoticons {
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
z-index: 30;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
background-image: url("../img/smiley.svg");
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 30px 30px;
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.jsxc-inner {
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
ul {
|
||||
width: 217px;
|
||||
margin-bottom: 8px;
|
||||
background-color: $emoticon_selection_bg;
|
||||
border-radius: 3px;
|
||||
z-index: 200;
|
||||
list-style-type: none;
|
||||
padding: 3px;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 8px solid $emoticon_selection_bg;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
left: 7px;
|
||||
top: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: $emoticon_selection_hover;
|
||||
}
|
||||
|
||||
.jsxc-emoticon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-chatmessage {
|
||||
margin: 3px;
|
||||
padding: 4px;
|
||||
word-wrap: break-word;
|
||||
background-color: $chatmessage_bg;
|
||||
position: relative;
|
||||
outline: none;
|
||||
clear: both;
|
||||
|
||||
&.jsxc-error {
|
||||
opacity: 0.7;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: yellow;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $chatmessage_a;
|
||||
text-decoration: underline;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
|
||||
&[download]:before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 5px;
|
||||
left: 0;
|
||||
border-radius: 3px;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
background-image: url("../img/download_icon_black.svg");
|
||||
background-size: contain;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
&[download]:hover {
|
||||
&:before {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.jsxc-avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jsxc-attachment {
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
padding: 3px 3px 3px 30px;
|
||||
min-height: 30px;
|
||||
margin-bottom: 5px;
|
||||
background-position: 3px center;
|
||||
background-size: 25px 25px;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("../img/filetypes/file.svg");
|
||||
|
||||
img {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&.jsxc-image {
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
background-image: url("");
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&.jsxc-application {
|
||||
background-image: url("../img/filetypes/application.svg");
|
||||
}
|
||||
|
||||
&.jsxc-application-pdf {
|
||||
background-image: url("../img/filetypes/application-pdf.svg");
|
||||
}
|
||||
|
||||
&.jsxc-audio {
|
||||
background-image: url("../img/filetypes/audio.svg");
|
||||
}
|
||||
|
||||
&.jsxc-video {
|
||||
background-image: url("../img/filetypes/video.svg");
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.jsxc-text {
|
||||
background-image: url("../img/filetypes/text.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-timestamp {
|
||||
font-size: 8px;
|
||||
color: $chatmessage_timestamp;
|
||||
line-height: 8px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.jsxc-encrypted {
|
||||
&.jsxc-received.jsxc-out .jsxc-timestamp {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.jsxc-timestamp:after {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 8px;
|
||||
margin-left: 2px;
|
||||
background-image: url("../img/padlock_close_grey.svg");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-in {
|
||||
float: left;
|
||||
position: relative;
|
||||
max-width: 76%;
|
||||
margin-left: 10px;
|
||||
border-radius: 3px;
|
||||
background-color: $chatmessage_in_bg;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
border-width: 5px 6px 5px 0;
|
||||
border-color: transparent $chatmessage_in_bg;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
left: -6px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.jsxc-timestamp {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-out {
|
||||
float: right;
|
||||
position: relative;
|
||||
max-width: 76%;
|
||||
margin-right: 10px;
|
||||
padding-right: 10px;
|
||||
border-radius: 3px;
|
||||
background-color: $chatmessage_out_bg;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
border-width: 5px 0 5px 6px;
|
||||
border-color: transparent $chatmessage_out_bg;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
&.jsxc-received {
|
||||
&:before {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
color: $chatmessage_received;
|
||||
}
|
||||
|
||||
.jsxc-timestamp {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-timestamp {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-sys {
|
||||
width: auto;
|
||||
max-width: none;
|
||||
padding-right: 4px;
|
||||
box-sizing: border-box;
|
||||
margin-right: 3px;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
|
||||
.jsxc-emoticon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.jsxc-composing {
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.6s;
|
||||
|
||||
&:before {
|
||||
content: " ";
|
||||
width: 1.5em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
background-size: 80%;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 3px 0 0;
|
||||
background-image: url("../img/composing.png");
|
||||
}
|
||||
|
||||
&.jsxc-fadein {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jsxc-menu-settings {
|
||||
position: relative;
|
||||
|
||||
.jsxc-inner {
|
||||
left: auto;
|
||||
top: 100%;
|
||||
right: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
div.jsxc-transfer {
|
||||
background-image: url("../img/padlock_open_black.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 14px 14px;
|
||||
opacity: 0.3;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
z-index: 20;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.jsxc-disabled {
|
||||
background-image: url("../img/padlock_open_disabled_black.svg");
|
||||
cursor: default;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
&.jsxc-fin {
|
||||
opacity: 1;
|
||||
background-image: url("../img/padlock_close_grey.svg");
|
||||
}
|
||||
|
||||
&.jsxc-enc {
|
||||
opacity: 1;
|
||||
background-image: url("../img/padlock_close_orange.svg");
|
||||
|
||||
&.jsxc-trust {
|
||||
background-image: url("../img/padlock_close_green.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
externo
+61
@@ -0,0 +1,61 @@
|
||||
@import "../../lib/magnific-popup/src/css/main";
|
||||
@import "../../lib/emojione/assets/css/emojione";
|
||||
|
||||
// BEGIN: bootstrap
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/variables";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/mixins";
|
||||
|
||||
// Spec and IE10+
|
||||
@keyframes progress-bar-stripes {
|
||||
from {
|
||||
background-position: 40px 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
#jsxc-dialog {
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/progress-bars";
|
||||
}
|
||||
|
||||
#jsxc-dialog, #jsxc-webrtc {
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/utilities";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/code";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/grid";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/alerts";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/buttons";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/button-groups";
|
||||
@import "../../lib/bootstrap/assets/stylesheets/bootstrap/forms";
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-close {
|
||||
font-size: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-bg {
|
||||
z-index: 9000;
|
||||
}
|
||||
|
||||
.mfp-wrap {
|
||||
z-index: 9010;
|
||||
}
|
||||
|
||||
.mfp-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// END: bootstrap
|
||||
|
||||
#cboxWrapper {
|
||||
outline: none;
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
import Storage from './Storage'
|
||||
import {IConnection} from './connection/ConnectionInterface'
|
||||
import * as Connector from './connection/xmpp/connector'
|
||||
import XMPPConnection from './connection/xmpp/Connection'
|
||||
import StorageConnection from './connection/storage/Connection'
|
||||
import JID from './JID'
|
||||
import Contact from './Contact'
|
||||
import ContactData from './ContactData'
|
||||
import Roster from './ui/Roster'
|
||||
import ChatWindow from './ui/ChatWindow'
|
||||
import ChatWindowList from './ui/ChatWindowList'
|
||||
import SortedPersistentMap from './SortedPersistentMap'
|
||||
import PersistentMap from './PersistentMap'
|
||||
import Log from './util/Log'
|
||||
import {Presence} from './connection/AbstractConnection'
|
||||
import Client from './Client'
|
||||
import {Notice, NoticeData, TYPE as NOTICETYPE} from './Notice'
|
||||
|
||||
interface IConnectionParameters {
|
||||
url:string,
|
||||
jid: string,
|
||||
sid?:string,
|
||||
rid?:string,
|
||||
timestamp?:number,
|
||||
inactivity?:number
|
||||
};
|
||||
|
||||
export default class Account {
|
||||
private storage:Storage;
|
||||
|
||||
private uid:string;
|
||||
|
||||
private connection:IConnection;
|
||||
|
||||
private connectionArguments;
|
||||
|
||||
private connectionParameters:IConnectionParameters;
|
||||
|
||||
private contacts = {};
|
||||
|
||||
private windows:SortedPersistentMap;
|
||||
|
||||
private notices:SortedPersistentMap;
|
||||
|
||||
private contact:Contact;
|
||||
|
||||
constructor(boshUrl: string, jid: string, sid: string, rid:string);
|
||||
constructor(boshUrl: string, jid: string, password: string);
|
||||
constructor(uid:string);
|
||||
constructor() {
|
||||
if (arguments.length === 1) {
|
||||
this.uid = arguments[0];
|
||||
} else if (arguments.length === 3 || arguments.length === 4) {
|
||||
this.uid = (new JID(arguments[1])).bare;
|
||||
this.connectionArguments = arguments;
|
||||
}
|
||||
|
||||
this.connection = new StorageConnection(this);
|
||||
|
||||
this.contact = new Contact(this, new ContactData({
|
||||
jid: new JID(this.uid),
|
||||
name: this.uid
|
||||
}));
|
||||
Roster.get().setRosterAvatar(this.contact);
|
||||
|
||||
this.restoreContacts();
|
||||
this.initNotices();
|
||||
this.initWindows();
|
||||
|
||||
this.getStorage().registerHook('contact:', (contactData) => {
|
||||
let contact = new Contact(this, contactData.jid);
|
||||
|
||||
if (typeof this.contacts[contact.getId()] === 'undefined') { console.log('add', contactData.jid)
|
||||
this.contacts[contact.getId()] = contact;
|
||||
|
||||
Roster.get().add(contact);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public connect() {
|
||||
let self = this;
|
||||
|
||||
if (!this.connectionArguments) {
|
||||
this.reloadConnectionData();
|
||||
}
|
||||
|
||||
if (self.connectionParameters && self.connectionParameters.inactivity && (new Date()).getTime() - self.connectionParameters.timestamp > self.connectionParameters.inactivity) {
|
||||
Log.warn('Credentials expired')
|
||||
|
||||
this.closeAllChatWindows();
|
||||
|
||||
return Promise.reject('Credentials expired');
|
||||
}
|
||||
|
||||
return Connector.login.apply(this, this.connectionArguments).then(this.successfulConnected);
|
||||
}
|
||||
|
||||
public getContact(jid:JID):Contact {
|
||||
return this.contacts[jid.bare];
|
||||
}
|
||||
|
||||
public addContact(data:ContactData):Contact {
|
||||
let contact = new Contact(this, data);
|
||||
contact.save();
|
||||
|
||||
this.contacts[contact.getId()] = contact;
|
||||
|
||||
this.save();
|
||||
|
||||
return contact;
|
||||
}
|
||||
|
||||
public removeContact(contact:Contact) {
|
||||
let id = contact.getId();
|
||||
|
||||
if (this.contacts[id]) {
|
||||
delete this.contacts[id];
|
||||
|
||||
Roster.get().remove(contact);
|
||||
|
||||
//@REVIEW contact.getChatWindow would be nice
|
||||
let chatWindow = this.windows.get(id);
|
||||
|
||||
if (chatWindow) {
|
||||
this.closeChatWindow(chatWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removeAllContacts() {
|
||||
for(let id in this.contacts) {
|
||||
let contact = this.contacts[id];
|
||||
|
||||
delete this.contacts[id];
|
||||
|
||||
Roster.get().remove(contact);
|
||||
}
|
||||
}
|
||||
|
||||
public openChatWindow(contact:Contact) {
|
||||
let chatWindow = new ChatWindow(this, contact);
|
||||
|
||||
chatWindow = ChatWindowList.get().add(chatWindow);
|
||||
|
||||
this.windows.push(chatWindow);
|
||||
|
||||
this.save();
|
||||
|
||||
return chatWindow;
|
||||
}
|
||||
|
||||
public closeChatWindow(chatWindow:ChatWindow) {
|
||||
// let id = chatWindow.getContact().getId();
|
||||
console.log('chatWindow', chatWindow)
|
||||
ChatWindowList.get().remove(chatWindow);
|
||||
|
||||
this.windows.remove(chatWindow);
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public closeAllChatWindows() {
|
||||
this.windows.empty((id, chatWindow) => {
|
||||
ChatWindowList.get().remove(chatWindow);
|
||||
});
|
||||
}
|
||||
|
||||
public addNotice(noticeData:NoticeData) {
|
||||
let notice = new Notice(this.getStorage(), noticeData);
|
||||
|
||||
if (this.notices.get(notice.getId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Roster.get().addNotice(this, notice);
|
||||
|
||||
this.notices.push(notice);
|
||||
}
|
||||
|
||||
public removeNotice(notice:Notice) { console.log('removeNotice', notice);
|
||||
//Roster.get().removeNotice(this, notice);
|
||||
|
||||
this.notices.remove(notice);
|
||||
}
|
||||
|
||||
public getStorage() {
|
||||
if(!this.storage) {
|
||||
this.storage = new Storage(this.uid);
|
||||
}
|
||||
|
||||
return this.storage;
|
||||
}
|
||||
|
||||
public getConnection():IConnection {
|
||||
return this.connection;
|
||||
}
|
||||
|
||||
public getUid() {
|
||||
return this.uid;
|
||||
}
|
||||
|
||||
public getJID():JID {
|
||||
let storedAccountData = this.getStorage().getItem('account') || {};
|
||||
let jidString = (storedAccountData.connectionParameters) ? storedAccountData.connectionParameters.jid : this.getUid();
|
||||
|
||||
//@REVIEW maybe promise?
|
||||
return new JID(jidString);
|
||||
}
|
||||
|
||||
public remove() {
|
||||
this.removeAllContacts();
|
||||
this.closeAllChatWindows();
|
||||
|
||||
Client.removeAccount(this);
|
||||
}
|
||||
|
||||
private successfulConnected = (data) => {
|
||||
let connection = data.connection;
|
||||
let status = data.status;
|
||||
|
||||
this.connectionParameters = $.extend(this.connectionParameters, {
|
||||
url: connection.service,
|
||||
jid: connection.jid,
|
||||
sid: connection._proto.sid,
|
||||
rid: connection._proto.rid,
|
||||
timestamp: (new Date()).getTime()
|
||||
});
|
||||
|
||||
if (connection._proto.inactivity) {
|
||||
this.connectionParameters.inactivity = connection._proto.inactivity * 1000;
|
||||
}
|
||||
|
||||
this.save();
|
||||
|
||||
connection.connect_callback = (status) => {
|
||||
if (status === Strophe.Status.DISCONNECTED) {
|
||||
this.connectionDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
connection.nextValidRid = (rid) => {
|
||||
this.connectionParameters.timestamp = (new Date()).getTime();
|
||||
this.connectionParameters.rid = rid;
|
||||
this.save();
|
||||
};
|
||||
|
||||
this.connection = new XMPPConnection(this, connection);
|
||||
|
||||
if (status === Strophe.Status.CONNECTED) {
|
||||
Roster.get().setPresence(Presence.online);
|
||||
Roster.get().refreshOwnPresenceIndicator();
|
||||
|
||||
this.connection.getRoster().then(() => {
|
||||
this.connection.sendPresence();
|
||||
});
|
||||
} else {
|
||||
this.connection.sendPresence();
|
||||
}
|
||||
}
|
||||
|
||||
private connectionDisconnected() {
|
||||
console.log('disconnected');
|
||||
|
||||
this.remove();
|
||||
}
|
||||
|
||||
private save() {
|
||||
this.getStorage().setItem('account', {
|
||||
connectionParameters: this.connectionParameters,
|
||||
contacts: Object.keys(this.contacts),
|
||||
// windows: Object.keys(this.windows)
|
||||
});
|
||||
}
|
||||
|
||||
private reloadConnectionData() {
|
||||
let storedAccountData = this.getStorage().getItem('account') || {};
|
||||
console.log('storedAccountData', storedAccountData)
|
||||
this.connectionParameters = storedAccountData.connectionParameters;
|
||||
|
||||
let p = this.connectionParameters;
|
||||
this.connectionArguments = [p.url, (new JID(p.jid)).full, p.sid, p.rid];
|
||||
}
|
||||
|
||||
private restoreContacts() {
|
||||
let storedAccountData = this.getStorage().getItem('account');
|
||||
|
||||
storedAccountData.contacts.forEach((id) => {
|
||||
this.contacts[id] = new Contact(this, id);
|
||||
|
||||
Roster.get().add(this.contacts[id]);
|
||||
});
|
||||
}
|
||||
|
||||
private initWindows() {
|
||||
this.windows = new SortedPersistentMap(this.getStorage(), 'windows');
|
||||
|
||||
this.windows.setRemoveHook((id, chatWindow) => {
|
||||
console.log('remove hook', id, chatWindow);
|
||||
if (chatWindow) {
|
||||
ChatWindowList.get().remove(chatWindow);
|
||||
}
|
||||
});
|
||||
|
||||
this.windows.setPushHook((id) => {
|
||||
let chatWindow = new ChatWindow(this, this.contacts[id]);
|
||||
this.windows[id] = chatWindow;
|
||||
|
||||
ChatWindowList.get().add(chatWindow);
|
||||
|
||||
return chatWindow;
|
||||
});
|
||||
|
||||
this.windows.init();
|
||||
}
|
||||
|
||||
private initNotices() {
|
||||
this.notices = new SortedPersistentMap(this.getStorage(), 'notices');
|
||||
|
||||
this.notices.setRemoveHook((id) => {
|
||||
Roster.get().removeNotice(this, id);
|
||||
});
|
||||
|
||||
this.notices.setPushHook((id) => {
|
||||
let notice = new Notice(this.getStorage(), id);
|
||||
|
||||
Roster.get().addNotice(this, notice);
|
||||
|
||||
return notice;
|
||||
});
|
||||
|
||||
this.notices.init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import Options from './Options';
|
||||
import Log from './util/Log';
|
||||
|
||||
export default class Attachment {
|
||||
private mimeType:string;
|
||||
|
||||
private data:any;
|
||||
|
||||
private thumbnailData:any;
|
||||
|
||||
private size:number;
|
||||
|
||||
private persistent:boolean;
|
||||
|
||||
private name:string;
|
||||
|
||||
constructor(name:string, mimeType:string, data:any);
|
||||
constructor(id:string);
|
||||
constructor() {
|
||||
// @TODO
|
||||
}
|
||||
|
||||
public save():boolean {
|
||||
if (this.isImage() && this.data && !this.thumbnailData) {
|
||||
this.generateThumbnail();
|
||||
}
|
||||
|
||||
if (this.size > Options.get('maxStorableSize')) {
|
||||
Log.debug('Attachment to large to store');
|
||||
|
||||
this.persistent = false;
|
||||
|
||||
return false;
|
||||
|
||||
//@TODO delete data and store thumbnailData
|
||||
}
|
||||
|
||||
//@TODO save to storage
|
||||
return true;
|
||||
}
|
||||
|
||||
public getSize():number {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public getMimeType():string {
|
||||
return this.mimeType;
|
||||
}
|
||||
|
||||
public getThumbnailData() {
|
||||
return this.thumbnailData;
|
||||
}
|
||||
|
||||
public getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public isPersistent():boolean {
|
||||
return this.persistent;
|
||||
}
|
||||
|
||||
public isImage():boolean {
|
||||
return /^image\//i.test(this.mimeType);
|
||||
}
|
||||
|
||||
public hasThumbnailData():boolean {
|
||||
return !!this.thumbnailData;
|
||||
}
|
||||
|
||||
public hasData():boolean {
|
||||
return !!this.data;
|
||||
}
|
||||
|
||||
public clearData() {
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
private generateThumbnail():void {
|
||||
if(typeof Image === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var sHeight, sWidth, sx, sy;
|
||||
var dHeight = 100,
|
||||
dWidth = 100;
|
||||
var canvas = <HTMLCanvasElement> $("<canvas>").get(0);
|
||||
|
||||
canvas.width = dWidth;
|
||||
canvas.height = dHeight;
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
var img = new Image();
|
||||
|
||||
img.src = this.data;
|
||||
|
||||
if (img.height > img.width) {
|
||||
sHeight = img.width;
|
||||
sWidth = img.width;
|
||||
sx = 0;
|
||||
sy = (img.height - img.width) / 2;
|
||||
} else {
|
||||
sHeight = img.height;
|
||||
sWidth = img.height;
|
||||
sx = (img.width - img.height) / 2;
|
||||
sy = 0;
|
||||
}
|
||||
|
||||
ctx.drawImage(img, sx, sy, sWidth, sHeight, 0, 0, dWidth, dHeight);
|
||||
|
||||
this.thumbnailData = canvas.toDataURL();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
export let NOTIFICATION_DEFAULT = 'default';
|
||||
export let NOTIFICATION_GRANTED = 'granted';
|
||||
export let NOTIFICATION_DENIED = 'denied';
|
||||
export let STATUS = ['offline', 'dnd', 'xa', 'away', 'chat', 'online'];
|
||||
export let SOUNDS = {
|
||||
MSG: 'incomingMessage.wav',
|
||||
CALL: 'Rotary-Phone6.mp3',
|
||||
NOTICE: 'Ping1.mp3'
|
||||
};
|
||||
export let REGEX = {
|
||||
JID: new RegExp('\\b[^"&\'\\/:<>@\\s]+@[\\w-_.]+\\b', 'ig'),
|
||||
URL: new RegExp(/(https?:\/\/|www\.)[^\s<>'"]+/gi)
|
||||
};
|
||||
export let NS = {
|
||||
CARBONS: 'urn:xmpp:carbons:2',
|
||||
FORWARD: 'urn:xmpp:forward:0'
|
||||
};
|
||||
export let HIDDEN = 'hidden';
|
||||
export let SHOWN = 'shown';
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
import Account from './Account'
|
||||
import Message from './Message'
|
||||
import {PluginInterface} from './PluginInterface'
|
||||
import Storage from './Storage';
|
||||
import * as UI from './ui/web'
|
||||
import JID from './JID'
|
||||
import Roster from './ui/Roster'
|
||||
import ChatWindowList from './ui/ChatWindowList'
|
||||
import RoleAllocator from './RoleAllocator'
|
||||
|
||||
export default class Client implements ClientInterface {
|
||||
private static storage;
|
||||
|
||||
private static accounts = {};
|
||||
|
||||
public static init() {
|
||||
let roleAllocator = RoleAllocator.get();
|
||||
let accountIds = Client.getStorage().getItem('accounts') || [];
|
||||
|
||||
accountIds.forEach(function(id) {
|
||||
Client.accounts[id] = new Account(id);
|
||||
|
||||
roleAllocator.waitUntilMaster().then(function(){
|
||||
return Client.accounts[id].connect();
|
||||
}).then(function(){
|
||||
|
||||
}).catch(function(msg){
|
||||
Client.accounts[id].remove();
|
||||
|
||||
console.warn(msg)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static addConnectionPlugin(plugin:PluginInterface) {
|
||||
|
||||
}
|
||||
|
||||
public static addPreSendMessageHook(hook:(Message, Builder)=>void, position?:number) {
|
||||
|
||||
}
|
||||
|
||||
public static hasFocus() {
|
||||
|
||||
}
|
||||
|
||||
public static isExtraSmallDevice():boolean {
|
||||
return $(window).width() < 500;
|
||||
}
|
||||
|
||||
public static isDebugMode():boolean {
|
||||
return Client.getStorage().getItem('debug') === true;
|
||||
}
|
||||
|
||||
public static getStorage() {
|
||||
if (!Client.storage) {
|
||||
Client.storage = new Storage();
|
||||
}
|
||||
|
||||
return Client.storage;
|
||||
}
|
||||
|
||||
public static getAccout(jid:JID):Account;
|
||||
public static getAccout(uid?:string):Account;
|
||||
public static getAccout() {
|
||||
let uid;
|
||||
|
||||
if (arguments[0] instanceof JID) {
|
||||
uid = arguments[0].bare;
|
||||
} else if (arguments[0]) {
|
||||
uid = arguments[0];
|
||||
} else {
|
||||
uid = Object.keys(Client.accounts)[0];
|
||||
}
|
||||
|
||||
return Client.accounts[uid];
|
||||
}
|
||||
|
||||
public static createAccount(boshUrl: string, jid: string, sid: string, rid:string);
|
||||
public static createAccount(boshUrl: string, jid: string, password: string);
|
||||
public static createAccount() {
|
||||
let account;
|
||||
|
||||
if (arguments.length === 4) {
|
||||
account = new Account(arguments[0], arguments[1], arguments[2], arguments[3]);
|
||||
} else if (arguments.length === 3) {
|
||||
account = new Account(arguments[0], arguments[1], arguments[2]);
|
||||
} else {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
return account.connect().then(function(){
|
||||
Client.addAccount(account);
|
||||
});
|
||||
}
|
||||
|
||||
public static removeAccount(account:Account) {
|
||||
delete Client.accounts[account.getUid()];
|
||||
|
||||
Client.save();
|
||||
|
||||
if (Object.keys(Client.accounts).length === 0) {
|
||||
Roster.get().setNoConnection();
|
||||
}
|
||||
}
|
||||
|
||||
private static addAccount(account:Account) {
|
||||
Client.accounts[account.getUid()] = account;
|
||||
|
||||
Client.save()
|
||||
}
|
||||
|
||||
private static save() {
|
||||
Client.getStorage().setItem('accounts', Object.keys(this.accounts));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {JIDInterface} from './JIDInterface'
|
||||
import {PluginInterface} from './PluginInterface'
|
||||
|
||||
export interface ClientInterface {
|
||||
init();
|
||||
|
||||
addConnectionPlugin(plugin:PluginInterface);
|
||||
|
||||
addPreSendMessageHook(hook:(Message, Builder)=>void, position?:number);
|
||||
|
||||
hasFocus();
|
||||
|
||||
isExtraSmallDevice():boolean;
|
||||
|
||||
isDebugMode():boolean;
|
||||
|
||||
getStorage();
|
||||
|
||||
getAccout(jid:JIDInterface):Account;
|
||||
getAccout(uid?:string):Account;
|
||||
|
||||
createAccount(boshUrl: string, jid: string, sid: string, rid:string);
|
||||
createAccount(boshUrl: string, jid: string, password: string);
|
||||
|
||||
removeAccount(account:Account);
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
import Storage from './Storage'
|
||||
import JID from './JID'
|
||||
import Message from './Message'
|
||||
import Notification from './Notification'
|
||||
import Translation from './util/Translation'
|
||||
import Account from './Account'
|
||||
import ContactData from './ContactData'
|
||||
import PersistentMap from './PersistentMap'
|
||||
import IdentifiableInterface from './IdentifiableInterface'
|
||||
import Log from './util/Log'
|
||||
import {Presence} from './connection/AbstractConnection'
|
||||
|
||||
export default class Contact implements IdentifiableInterface {
|
||||
private storage: Storage;
|
||||
|
||||
private readonly account:Account;
|
||||
|
||||
// @REVIEW Data to own object/type?
|
||||
private data:PersistentMap;
|
||||
|
||||
private jid:JID;
|
||||
|
||||
constructor(account:Account, data: ContactData);
|
||||
constructor(account:Account, id:string);
|
||||
constructor() {
|
||||
this.account = arguments[0];
|
||||
this.storage = this.account.getStorage();
|
||||
|
||||
if (typeof arguments[1] === 'string') {
|
||||
let id = arguments[1]; console.log('id', id)
|
||||
this.data = new PersistentMap(this.storage, 'contact', id);
|
||||
this.jid = new JID(this.data.get('jid'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let data = arguments[1] || {};
|
||||
|
||||
if (!data.jid) {
|
||||
throw 'Jid missing';
|
||||
} else if (typeof data.jid === 'string') {
|
||||
this.jid = new JID(data.jid);
|
||||
} else {
|
||||
this.jid = data.jid;
|
||||
data.jid = this.jid.full;
|
||||
}
|
||||
|
||||
this.data = new PersistentMap(this.storage, 'contact', this.jid.bare);
|
||||
|
||||
data.rnd = Math.random() // force storage event
|
||||
this.data.set(data);
|
||||
}
|
||||
|
||||
public save() {
|
||||
// if (this.storage.getItem('contact', this.getId())) {
|
||||
// this.storage.updateItem('contact', this.getId(), this.data);
|
||||
//
|
||||
// return 'updated';
|
||||
// }
|
||||
//
|
||||
// this.storage.setItem('contact', this.getId(), this.data);
|
||||
//
|
||||
// return 'created';
|
||||
}
|
||||
|
||||
public openWindow = () => {
|
||||
return this.account.openChatWindow(this);
|
||||
}
|
||||
|
||||
public addResource(resource:string) {
|
||||
let resources = this.data.get('resources') || [];
|
||||
|
||||
if (resource && resources.indexOf(resource) < 0) {
|
||||
resources.push(resource);
|
||||
|
||||
this.data.set('resources', resources);
|
||||
}
|
||||
}
|
||||
|
||||
public removeResource(resource:string) {
|
||||
let resources = this.data.get('resources') || [];
|
||||
|
||||
resources = $.grep(resources, function(r) {
|
||||
return resource !== r;
|
||||
});
|
||||
|
||||
this.data.set('resources', resources);
|
||||
}
|
||||
|
||||
public setResource = (resource:string) => {
|
||||
//this.addResource(resource);
|
||||
console.log('setResource', this.jid.bare + '/' + resource)
|
||||
this.jid = new JID(this.jid.bare + '/' + resource);
|
||||
|
||||
this.data.set('jid', this.jid.full);
|
||||
}
|
||||
|
||||
public setPresence(resource:string, presence:Presence) {
|
||||
Log.debug('set presence for ' + this.jid.bare + ' / ' + resource, presence);
|
||||
|
||||
let resources = this.data.get('resources') || {};
|
||||
|
||||
if (presence === Presence.offline) {
|
||||
delete resources[resource];
|
||||
} else if (resource) {
|
||||
resources[resource] = presence;
|
||||
}
|
||||
|
||||
if (this.getType() === 'groupchat') {
|
||||
// group chat doesn't have a presence
|
||||
return;
|
||||
}
|
||||
|
||||
presence = this.getHighestPresence();
|
||||
console.log('highest presence', presence);
|
||||
if (this.data.get('presence') === Presence.offline && presence !== Presence.offline) {
|
||||
// buddy has come online
|
||||
// @TODO
|
||||
// Notification.notify({
|
||||
// title: this.getName(),
|
||||
// message: Translation.t('has_come_online'),
|
||||
// source: this.getId()
|
||||
// });
|
||||
}
|
||||
|
||||
this.data.set('presence', presence);
|
||||
}
|
||||
|
||||
public sendMessage(message:Message) {
|
||||
// message.bid = this.getId();
|
||||
}
|
||||
|
||||
public getId():string {
|
||||
return this.jid.bare;
|
||||
}
|
||||
|
||||
public getJid():JID {
|
||||
return this.jid;
|
||||
}
|
||||
|
||||
public getFingerprint() {
|
||||
return this.data.get('fingerprint');
|
||||
}
|
||||
|
||||
public getMsgState() {
|
||||
return this.data.get('msgstate');
|
||||
}
|
||||
|
||||
public getPresence() {
|
||||
return this.data.get('presence');
|
||||
}
|
||||
|
||||
public getType() {
|
||||
return this.data.get('type');
|
||||
}
|
||||
|
||||
public getNumberOfUnreadMessages():number {
|
||||
|
||||
}
|
||||
|
||||
public getName():string {
|
||||
return this.data.get('name') || this.jid.bare;
|
||||
}
|
||||
|
||||
public getAvatar():Promise<{}> {
|
||||
|
||||
}
|
||||
|
||||
public getSubscription() {
|
||||
return this.data.get('subscription');
|
||||
}
|
||||
|
||||
public getCapabilitiesByRessource():Promise<{}> {
|
||||
// @TODO
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
public getVcard():Promise<{}> {
|
||||
return this.account.getConnection().loadVcard(this.getJid());
|
||||
}
|
||||
|
||||
public isEncrypted() {
|
||||
|
||||
}
|
||||
|
||||
public getStatus():string {
|
||||
return this.data.get('status');
|
||||
}
|
||||
|
||||
public setStatus(status:string) { console.trace(this.getId() + ', setStatus: ' + status)
|
||||
return this.data.set('status', status);
|
||||
}
|
||||
|
||||
public setTrust(trust:boolean) {
|
||||
this.data.set('trust', trust);
|
||||
}
|
||||
|
||||
public setName(name:string) {
|
||||
let oldName = this.getName();
|
||||
|
||||
this.data.set('name', name);
|
||||
|
||||
if (oldName !== name) {
|
||||
this.account.getConnection().setDisplayName(this.jid, name);
|
||||
}
|
||||
}
|
||||
|
||||
public setSubscription(subscription:string) {
|
||||
this.data.set('subscription', subscription);
|
||||
}
|
||||
|
||||
public registerHook(property:string, func:(newValue:any, oldValue:any)=>void) {
|
||||
this.data.registerHook(property, func);
|
||||
}
|
||||
|
||||
private getHighestPresence() {
|
||||
let maxPresence = Presence.offline;
|
||||
let resources = this.data.get('resources');
|
||||
|
||||
for (let resource in resources) {
|
||||
if(resources[resource] < maxPresence) {
|
||||
maxPresence = resources[resource];
|
||||
}
|
||||
}
|
||||
|
||||
return maxPresence;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//@TODO duplicate of AbstractConnection
|
||||
enum Presence {
|
||||
online,
|
||||
chat,
|
||||
away,
|
||||
xa,
|
||||
dnd,
|
||||
offline
|
||||
}
|
||||
|
||||
export default class ContactData {
|
||||
public jid;
|
||||
public name;
|
||||
public presence:Presence = Presence.offline;
|
||||
public status:string = '';
|
||||
public subscription = 'none';
|
||||
public msgstate = 0;
|
||||
public trust:boolean = false;
|
||||
public fingerprint:string = null;
|
||||
public resources = {};
|
||||
public type = 'chat'
|
||||
|
||||
constructor(data:any) {
|
||||
$.extend(this, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import {MessageInterface} from './MessageInterface'
|
||||
import {JIDInterface} from './JIDInterface'
|
||||
import {Presence} from './connection/AbstractConnection'
|
||||
|
||||
export interface ContactInterface {
|
||||
openWindow();
|
||||
|
||||
addResource(resource:string);
|
||||
|
||||
removeResource(resource:string);
|
||||
|
||||
setResource(resource:string);
|
||||
|
||||
setPresence(resource:string, presence:Presence);
|
||||
|
||||
sendMessage(message:MessageInterface);
|
||||
|
||||
getId():string;
|
||||
|
||||
getJid():JIDInterface;
|
||||
|
||||
getFingerprint();
|
||||
|
||||
getMsgState();
|
||||
|
||||
getPresence();
|
||||
|
||||
getType();
|
||||
|
||||
getNumberOfUnreadMessages():number;
|
||||
|
||||
getName():string;
|
||||
|
||||
getAvatar():Promise<{}>;
|
||||
|
||||
getSubscription();
|
||||
|
||||
getCapabilitiesByRessource():Promise<{}>;
|
||||
|
||||
getVcard():Promise<{}>;
|
||||
|
||||
isEncrypted();
|
||||
|
||||
getStatus():string;
|
||||
|
||||
setStatus(status:string);
|
||||
|
||||
setTrust(trust:boolean);
|
||||
|
||||
setName(name:string);
|
||||
|
||||
setSubscription(subscription:string);
|
||||
|
||||
registerHook(property:string, func:(newValue:any, oldValue:any)=>void);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import Options from './Options'
|
||||
|
||||
const EMOTICONS = [
|
||||
['O:-) O:)', 'innocent'],
|
||||
['>:-( >:( >:-( >:(', 'angry'],
|
||||
[':-) :)', 'slight_smile'],
|
||||
[':-D :D', 'grin'],
|
||||
[':-( :(', 'disappointed'],
|
||||
[';-) ;)', 'wink'],
|
||||
[':-P :P', 'stuck_out_tongue'],
|
||||
['=-O', 'astonished'],
|
||||
[':kiss: :-*', 'kissing_heart'],
|
||||
['8-) :cool:', 'sunglasses'],
|
||||
[':-X :X', 'zipper_mouth'],
|
||||
[':yes:', 'thumbsup'],
|
||||
[':no:', 'thumbsdown'],
|
||||
[':beer:', 'beer'],
|
||||
[':coffee:', 'coffee'],
|
||||
[':devil:', 'smiling_imp'],
|
||||
[':kiss: :kissing:', 'kissing'],
|
||||
['@->-- @->--', 'rose'],
|
||||
[':music:', 'musical_note'],
|
||||
[':love:', 'heart_eyes'],
|
||||
[':heart:', 'heart'],
|
||||
[':brokenheart:', 'broken_heart'],
|
||||
[':zzz:', 'zzz'],
|
||||
[':wait:', 'hand_splayed']
|
||||
]
|
||||
import * as emojione from '../lib/emojione/lib/js/emojione.js';
|
||||
|
||||
const EMOTICON_LIST = {
|
||||
'core': {
|
||||
':klaus:': ['klaus'],
|
||||
':jabber:': ['jabber'],
|
||||
':xmpp:': ['xmpp'],
|
||||
':jsxc:': ['jsxc'],
|
||||
':owncloud:': ['owncloud'],
|
||||
':nextcloud:': ['nextcloud']
|
||||
},
|
||||
'emojione': emojione.emojioneList
|
||||
}
|
||||
|
||||
export default class Emoticons {
|
||||
|
||||
private static initialised = false;
|
||||
|
||||
private static shortRegex = new RegExp(emojione.regShortNames.source + '|(' + Object.keys(EMOTICON_LIST.core).join('|') + ')', 'gi');
|
||||
|
||||
public static getDefaultEmoticonList() {
|
||||
let list = [];
|
||||
|
||||
EMOTICONS.forEach(emoticon => {
|
||||
list.push(emoticon[0].split(' ')[0]);
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static toImage(text:string):string {
|
||||
Emoticons.init();
|
||||
|
||||
text = Emoticons.standardToImage(text);
|
||||
text = Emoticons.shortnameToImage(text);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static init() {
|
||||
if (Emoticons.initialised) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(EMOTICONS, function(i, val) {
|
||||
// escape characters
|
||||
var reg = val[0].replace(/(\/|\||\*|\.|\+|\?|\^|\$|\(|\)|\[|\]|\{|\})/g, '\\$1');
|
||||
reg = '(' + reg.split(' ').join('|') + ')';
|
||||
EMOTICONS[i][2] = new RegExp(reg, 'g');
|
||||
});
|
||||
|
||||
Emoticons.initialised = true;
|
||||
}
|
||||
|
||||
private static standardToImage(text:string):string {
|
||||
// replace emoticons from XEP-0038 and pidgin with shortnames
|
||||
$.each(EMOTICONS, function(i, val) {
|
||||
text = text.replace(val[2], ':' + val[1] + ':');
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static shortnameToImage(text:string):string {
|
||||
text = text.replace(this.shortRegex, Emoticons.replaceShortnameWithImage);
|
||||
|
||||
var wrapper = $('<div>' + text + '</div>');
|
||||
if (wrapper.find('.jsxc_emoticon').length === 1 && wrapper.text().replace(/ /, '').length === 0 && wrapper.find('*').length === 1) {
|
||||
wrapper.find('.jsxc_emoticon').addClass('jsxc_large');
|
||||
text = wrapper.html();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static replaceShortnameWithImage = (shortname) => {
|
||||
if (typeof shortname === 'undefined' || shortname === '' || (!(shortname in EMOTICON_LIST.emojione) && !(shortname in EMOTICON_LIST.core))) {
|
||||
return shortname;
|
||||
}
|
||||
|
||||
var src, filename;
|
||||
|
||||
if (EMOTICON_LIST.core[shortname]) {
|
||||
filename = EMOTICON_LIST.core[shortname][EMOTICON_LIST.core[shortname].length - 1].replace(/^:([^:]+):$/, '$1');
|
||||
src = Options.get('root') + '/img/emotions/' + filename + '.svg';
|
||||
} else if (EMOTICON_LIST.emojione[shortname]) {
|
||||
filename = EMOTICON_LIST.emojione[shortname].fname;
|
||||
src = Options.get('root') + '/lib/emojione/assets/svg/' + filename + '.svg';
|
||||
}
|
||||
|
||||
var div = $('<div>');
|
||||
|
||||
div.addClass('jsxc-emoticon');
|
||||
div.css('background-image', 'url(' + src + ')');
|
||||
div.attr('title', shortname);
|
||||
|
||||
return div.prop('outerHTML');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
interface Identifiable {
|
||||
getId():string
|
||||
}
|
||||
|
||||
export default Identifiable;
|
||||
@@ -0,0 +1,65 @@
|
||||
import {JIDInterface} from './JIDInterface'
|
||||
|
||||
export default class JID implements JIDInterface {
|
||||
public readonly full:string;
|
||||
|
||||
public readonly bare:string;
|
||||
|
||||
public readonly node:string;
|
||||
|
||||
public readonly domain:string;
|
||||
|
||||
public readonly resource:string;
|
||||
|
||||
constructor(full:string) {
|
||||
let matches = /([^@]+)@([^/]+)(?:\/(.+))?/.exec(full);
|
||||
|
||||
this.node = this.unescapeNode(matches[1].toLowerCase());
|
||||
this.domain = matches[2].toLowerCase();
|
||||
this.resource = matches[3];
|
||||
|
||||
this.bare = this.node + '@' + this.domain;
|
||||
this.full = this.bare + ((this.resource) ? '/' + this.resource : '');
|
||||
}
|
||||
|
||||
public toString():string {
|
||||
return this.full;
|
||||
}
|
||||
|
||||
public toEscapedString():string {
|
||||
let bare = this.escapeNode(this.node) + '@' + this.domain;
|
||||
|
||||
return bare + ((this.resource) ? '/' + this.resource : '');
|
||||
}
|
||||
|
||||
public isBare():boolean {
|
||||
return this.full === this.bare;
|
||||
}
|
||||
|
||||
private escapeNode(node:string) {
|
||||
return node.replace(/^\s+|\s+$/g, '')
|
||||
.replace(/\\/g, "\\5c")
|
||||
.replace(/ /g, "\\20")
|
||||
.replace(/\"/g, "\\22")
|
||||
.replace(/\&/g, "\\26")
|
||||
.replace(/\'/g, "\\27")
|
||||
.replace(/\//g, "\\2f")
|
||||
.replace(/:/g, "\\3a")
|
||||
.replace(/</g, "\\3c")
|
||||
.replace(/>/g, "\\3e")
|
||||
.replace(/@/g, "\\40");
|
||||
}
|
||||
|
||||
private unescapeNode(node:string) {
|
||||
return node.replace(/\\20/g, " ")
|
||||
.replace(/\\22/g, '"')
|
||||
.replace(/\\26/g, "&")
|
||||
.replace(/\\27/g, "'")
|
||||
.replace(/\\2f/g, "/")
|
||||
.replace(/\\3a/g, ":")
|
||||
.replace(/\\3c/g, "<")
|
||||
.replace(/\\3e/g, ">")
|
||||
.replace(/\\40/g, "@")
|
||||
.replace(/\\5c/g, "\\");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
export interface JIDInterface {
|
||||
readonly full:string;
|
||||
|
||||
readonly bare:string;
|
||||
|
||||
readonly node:string;
|
||||
|
||||
readonly domain:string;
|
||||
|
||||
readonly resource:string;
|
||||
|
||||
toString():string;
|
||||
|
||||
isBare():boolean;
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
import Storage from './Storage';
|
||||
import Log from './util/Log';
|
||||
import Options from './Options';
|
||||
import Attachment from './Attachment';
|
||||
import StorageSingleton from './StorageSingleton';
|
||||
import JID from './JID'
|
||||
import * as CONST from './CONST'
|
||||
import Emoticons from './Emoticons'
|
||||
import Translation from './util/Translation'
|
||||
import Identifiable from './IdentifiableInterface'
|
||||
import Client from './Client'
|
||||
import Utils from './util/Utils'
|
||||
import {MessageInterface, DIRECTION, MSGTYPE} from './MessageInterface'
|
||||
|
||||
const MSGPOSTFIX = ':msg';
|
||||
|
||||
const ATREGEX = new RegExp('(xmpp:)?(' + CONST.REGEX.JID.source + ')(\\?[^\\s]+\\b)?', 'i');
|
||||
|
||||
interface MessagePayload {
|
||||
peer:JID,
|
||||
direction:DIRECTION,
|
||||
plaintextMessage?:string,
|
||||
htmlMessage?:string,
|
||||
errorMessage?:string,
|
||||
attachment?:Attachment,
|
||||
received?:boolean,
|
||||
encrypted?:boolean,
|
||||
forwarded?:boolean,
|
||||
stamp?:number,
|
||||
type?:MSGTYPE,
|
||||
}
|
||||
|
||||
export default class Message implements Identifiable, MessageInterface {
|
||||
|
||||
private uid:string;
|
||||
|
||||
private payload:MessagePayload = {
|
||||
received: false,
|
||||
encrypted: null,
|
||||
forwarded: false,
|
||||
stamp: new Date().getTime(),
|
||||
type: MSGTYPE.CHAT
|
||||
} as any;
|
||||
|
||||
static readonly DIRECTION = DIRECTION;
|
||||
|
||||
static readonly MSGTYPE = MSGTYPE;
|
||||
|
||||
private storage:Storage;
|
||||
|
||||
constructor(uid:string);
|
||||
constructor(data:MessagePayload);
|
||||
constructor() {
|
||||
this.storage = Client.getStorage();
|
||||
|
||||
if (typeof arguments[0] === 'string' && arguments[0].length > 0 && arguments.length === 1) {
|
||||
this.uid = arguments[0];
|
||||
|
||||
this.load(this.uid);
|
||||
} else if (typeof arguments[0] === 'object' && arguments[0] !== null) { console.log('arg', arguments[0])
|
||||
$.extend(this.payload, arguments[0]);
|
||||
}
|
||||
|
||||
if (!this.uid) {
|
||||
this.uid = new Date().getTime() + MSGPOSTFIX;
|
||||
}
|
||||
}
|
||||
|
||||
public getId() {
|
||||
return this.uid;
|
||||
}
|
||||
|
||||
public save() {
|
||||
let attachment = this.getAttachment();
|
||||
|
||||
if (attachment) {
|
||||
if (this.getDirection() === DIRECTION.OUT) {
|
||||
// save storage
|
||||
attachment.clearData();
|
||||
}
|
||||
|
||||
let saved = attachment.save();
|
||||
|
||||
if (!saved && this.getDirection() === DIRECTION.IN) {
|
||||
//@TODO inform user
|
||||
}
|
||||
}
|
||||
|
||||
let payloadCopy = $.extend({}, this.payload); //Object.assign
|
||||
if (payloadCopy.attachment) {
|
||||
payloadCopy.attachment = payloadCopy.attachment.getId();
|
||||
}
|
||||
|
||||
this.storage.setItem('msg', this.uid, {
|
||||
payload: payloadCopy
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public delete() {
|
||||
var data = this.storage.getItem('msg', this.uid);
|
||||
|
||||
if (data) {
|
||||
this.storage.removeItem('msg', this.uid);
|
||||
}
|
||||
}
|
||||
|
||||
public getCssId() {
|
||||
return this.uid.replace(/:/g, '-');
|
||||
}
|
||||
|
||||
public getDOM() {
|
||||
return $('#' + this.getCssId());
|
||||
}
|
||||
|
||||
public getStamp() {
|
||||
return this.payload.stamp;
|
||||
}
|
||||
|
||||
public getDirection():DIRECTION {
|
||||
return this.payload.direction;
|
||||
}
|
||||
|
||||
public getDirectionString():string {
|
||||
return DIRECTION[this.payload.direction].toLowerCase();
|
||||
}
|
||||
|
||||
public getAttachment():Attachment {
|
||||
return this.payload.attachment;
|
||||
}
|
||||
|
||||
public getPeer():JID {
|
||||
return this.payload.peer;
|
||||
}
|
||||
|
||||
public getType():MSGTYPE {
|
||||
return this.payload.type;
|
||||
}
|
||||
|
||||
public getTypeString():string {
|
||||
return MSGTYPE[this.payload.type].toLowerCase();
|
||||
}
|
||||
|
||||
public getHtmlMessage():string {
|
||||
return this.payload.htmlMessage;
|
||||
}
|
||||
|
||||
public getPlaintextMessage():string {
|
||||
return this.payload.plaintextMessage;
|
||||
}
|
||||
|
||||
public received() {
|
||||
this.payload.received = true;
|
||||
this.save();
|
||||
|
||||
this.getDOM().addClass('jsxc_received');
|
||||
}
|
||||
|
||||
public isReceived():boolean {
|
||||
return this.payload.received;
|
||||
}
|
||||
|
||||
public isForwarded():boolean {
|
||||
return this.payload.forwarded;
|
||||
}
|
||||
|
||||
public isEncrypted():boolean {
|
||||
return this.payload.encrypted;
|
||||
}
|
||||
|
||||
public hasAttachment():boolean {
|
||||
return !!this.payload.attachment;
|
||||
}
|
||||
|
||||
public setUnread() {
|
||||
|
||||
}
|
||||
|
||||
public getProcessedBody():string {
|
||||
let body = this.payload.plaintextMessage;
|
||||
|
||||
body = this.convertUrlToLink(body);
|
||||
|
||||
body = this.convertEmailToLink(body);
|
||||
|
||||
body = Emoticons.toImage(body);
|
||||
|
||||
body = this.replaceLineBreaks(body);
|
||||
|
||||
// hide unprocessed otr messages
|
||||
if (body.match(/^\?OTR([:,|?]|[?v0-9x]+)/)) {
|
||||
body = '<i title="' + body + '">' + Translation.t('Unreadable_OTR_message') + '</i>';
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
public getErrorMessage():string {
|
||||
return this.payload.errorMessage;
|
||||
}
|
||||
|
||||
private load(uid:string):void {
|
||||
var data = this.storage.getItem('msg', uid);
|
||||
window._storage = this.storage;
|
||||
if (!data) {
|
||||
Log.debug('Could not load message with uid ' + uid);
|
||||
|
||||
throw new Error('Could not load message with uid ' + uid)
|
||||
}
|
||||
|
||||
$.extend(this.payload, data.payload);
|
||||
|
||||
if (data.attachment) {
|
||||
this.payload.attachment = new Attachment(data.attachment);
|
||||
}
|
||||
}
|
||||
|
||||
private replaceLineBreaks(text) {
|
||||
return text.replace(/(\r\n|\r|\n)/g, '<br />');
|
||||
}
|
||||
|
||||
private convertUrlToLink(text) {
|
||||
return text.replace(CONST.REGEX.URL, function(url) {
|
||||
let href = (url.match(/^https?:\/\//i)) ? url : 'http://' + url;
|
||||
|
||||
return '<a href="' + href + '" target="_blank">' + url + '</a>';
|
||||
});
|
||||
}
|
||||
|
||||
private convertEmailToLink(text) {
|
||||
return text.replace(ATREGEX, function(undefined, protocol, jid, action) {
|
||||
if (protocol === 'xmpp:') {
|
||||
if (typeof action === 'string') {
|
||||
jid += action;
|
||||
}
|
||||
|
||||
return '<a href="xmpp:' + jid + '">xmpp:' + jid + '</a>';
|
||||
}
|
||||
|
||||
return '<a href="mailto:' + jid + '" target="_blank">mailto:' + jid + '</a>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import {JIDInterface} from './JIDInterface'
|
||||
|
||||
export enum DIRECTION {
|
||||
IN, OUT, SYS
|
||||
};
|
||||
|
||||
export enum MSGTYPE {
|
||||
CHAT, GROUPCHAT
|
||||
};
|
||||
|
||||
export interface MessageInterface {
|
||||
getId();
|
||||
|
||||
save();
|
||||
|
||||
delete();
|
||||
|
||||
getCssId();
|
||||
|
||||
getDOM();
|
||||
|
||||
getStamp();
|
||||
|
||||
getDirection():DIRECTION;
|
||||
|
||||
getDirectionString():string;
|
||||
|
||||
getAttachment():Attachment;
|
||||
|
||||
getPeer():JIDInterface;
|
||||
|
||||
getType():MSGTYPE;
|
||||
|
||||
getTypeString():string;
|
||||
|
||||
getHtmlMessage():string;
|
||||
|
||||
getPlaintextMessage():string;
|
||||
|
||||
received();
|
||||
|
||||
isReceived():boolean;
|
||||
|
||||
isForwarded():boolean;
|
||||
|
||||
isEncrypted():boolean;
|
||||
|
||||
hasAttachment():boolean;
|
||||
|
||||
setUnread();
|
||||
|
||||
getProcessedBody():string;
|
||||
|
||||
getErrorMessage():string;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import Storable from './StorableAbstract';
|
||||
import Identifiable from './IdentifiableInterface'
|
||||
import Storage from './Storage'
|
||||
|
||||
const SEP = ':';
|
||||
|
||||
export default class ModelManager <Element extends Storable> {
|
||||
|
||||
private id:string;
|
||||
|
||||
private elementIds;
|
||||
|
||||
constructor(private owner:Identifiable, private ElementClass:any, private storage:Storage) { //@REVIEW any -> Element?
|
||||
this.id = owner.getId() + SEP + ElementClass.constructor.name;
|
||||
|
||||
this.elementIds = this.storage.getItem(this.id);
|
||||
}
|
||||
|
||||
public get(id:string):Element {
|
||||
let data = this.storage.getItem(this.id + SEP + id);
|
||||
|
||||
return new this.ElementClass(data);
|
||||
}
|
||||
|
||||
public getAll():Element[] {
|
||||
let elements:Element[] = [];
|
||||
|
||||
for(let elementId in this.elementIds) {
|
||||
elements.push(this.get(elementId));
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
public add(element:Element) {
|
||||
this.elementIds.push(element.getId());
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public remove(element:Element) {
|
||||
this.elementIds = $.grep(this.elementIds, function(e) {
|
||||
return e !== element.getId();
|
||||
})
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
private save() {
|
||||
this.storage.setItem(this.id, this.elementIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import IdentifiableInterface from './IdentifiableInterface'
|
||||
import Storage from './Storage'
|
||||
import ContactDialog from './ui/dialogs/contact'
|
||||
|
||||
export const enum TYPE {
|
||||
normal, announcement, contact
|
||||
}
|
||||
|
||||
export const enum FUNCTION {
|
||||
contactRequest
|
||||
}
|
||||
let functions = {};
|
||||
functions[FUNCTION.contactRequest] = ContactDialog;
|
||||
|
||||
export interface NoticeData {
|
||||
title:string;
|
||||
description:string;
|
||||
fnName: FUNCTION;
|
||||
fnParams?:Array<string>;
|
||||
type?:TYPE;
|
||||
}
|
||||
|
||||
export class Notice implements IdentifiableInterface {
|
||||
|
||||
private storage:Storage;
|
||||
|
||||
private data:NoticeData;
|
||||
|
||||
constructor(storage:Storage, data:NoticeData);
|
||||
constructor(storage:Storage, id:string);
|
||||
constructor() {
|
||||
this.storage = arguments[0];
|
||||
|
||||
if (arguments.length === 2 && typeof arguments[1] === 'string') {
|
||||
this.data = this.storage.getItem(arguments[1]);
|
||||
} else {
|
||||
this.data = arguments[1];
|
||||
this.data.fnParams = this.data.fnParams || [];
|
||||
this.data.type = this.data.type || TYPE.normal;
|
||||
|
||||
this.storage.setItem(this.getId(), this.data);
|
||||
}
|
||||
}
|
||||
|
||||
public getId():string {
|
||||
return this.data.fnName + '|' + this.data.fnParams.toString();
|
||||
}
|
||||
|
||||
public getTitle():string {
|
||||
return this.data.title;
|
||||
}
|
||||
|
||||
public getDescription():string {
|
||||
return this.data.description;
|
||||
}
|
||||
|
||||
public getFnParams():Array<string> {
|
||||
return this.data.fnParams;
|
||||
}
|
||||
|
||||
public getType():TYPE {
|
||||
return this.data.type;
|
||||
}
|
||||
|
||||
public callFunction() {
|
||||
return functions[this.data.fnName].apply(this, this.data.fnParams);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
import Options from './Options'
|
||||
import Contact from './Contact'
|
||||
import Translation from './util/Translation'
|
||||
import Client from './Client'
|
||||
import * as CONST from './CONST'
|
||||
|
||||
interface NotificationSettings {
|
||||
title:string,
|
||||
message:string,
|
||||
duration?:number,
|
||||
force?:boolean,
|
||||
soundFile?:string,
|
||||
loop?:boolean,
|
||||
source?:string,
|
||||
icon?:string
|
||||
};
|
||||
|
||||
export default class Notification {
|
||||
private static inited = false;
|
||||
|
||||
private static popupTimeout;
|
||||
|
||||
private static popupDelay = 1000;
|
||||
|
||||
private static audioObject;
|
||||
|
||||
public static init() {
|
||||
if(Notification.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(document).on('postmessagein.jsxc', function(event, bid, msg) {
|
||||
msg = (msg && msg.match(/^\?OTR/)) ? $.t('Encrypted_message') : msg;
|
||||
var data = jsxc.storage.getUserItem('buddy', bid);
|
||||
|
||||
Notification.notify({
|
||||
title: Translation.t('New_message_from'),
|
||||
message: msg,
|
||||
soundFile: CONST.SOUNDS.MSG,
|
||||
source: bid
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('callincoming.jingle', function() {
|
||||
Notification.playSound(CONST.SOUNDS.CALL, true, true);
|
||||
});
|
||||
|
||||
$(document).on('accept.call.jsxc reject.call.jsxc', function() {
|
||||
Notification.stopSound();
|
||||
});
|
||||
|
||||
if (!Notice.has('gui.showRequestNotification')) {
|
||||
Notice.add({
|
||||
msg: Translation.t('Notifications') + '?',
|
||||
description: Translation.t('Should_we_notify_you_')
|
||||
}, 'gui.showRequestNotification');
|
||||
}
|
||||
}
|
||||
|
||||
public static muteSound(external?) {
|
||||
$('#jsxc-menu .jsxc-muteNotification').text(Translation.t('Unmute'));
|
||||
|
||||
if (external !== true) {
|
||||
Options.set('muteNotification', true);
|
||||
}
|
||||
}
|
||||
|
||||
public static unmuteSound(external?) {
|
||||
$('#jsxc-menu .jsxc-muteNotification').text(Translation.t('Mute'));
|
||||
|
||||
if (external !== true) {
|
||||
Options.set('muteNotification', false);
|
||||
}
|
||||
}
|
||||
|
||||
public static notify(settings:NotificationSettings) {
|
||||
if (!Options.get('notification') || !Notification.hasPermission()) {
|
||||
return; // notifications disabled
|
||||
}
|
||||
|
||||
if (Client.hasFocus() && !settings.force) {
|
||||
return; // Tab is visible
|
||||
}
|
||||
|
||||
settings.icon = settings.icon || Options.get('root') + '/img/XMPP_logo.png';
|
||||
|
||||
if (typeof settings.source === 'string') {
|
||||
let contact = new Contact(settings.source);
|
||||
let avatar = contact.getAvatar();
|
||||
|
||||
if (typeof avatar === 'string' && avatar !== '0') {
|
||||
settings.icon = avatar;
|
||||
}
|
||||
}
|
||||
|
||||
settings.duration = settings.duration || Options.get('notification').duration;
|
||||
settings.title = Translation.t(settings.title);
|
||||
settings.message = Translation.t(settings.message);
|
||||
|
||||
Notification.popupTimeout = setTimeout(function() {
|
||||
Notification.showPopup(settings);
|
||||
}, Notification.popupDelay);
|
||||
}
|
||||
|
||||
private static showPopup(settings:NotificationSettings) {
|
||||
if (typeof settings.soundFile === 'string') {
|
||||
Notification.playSound(settings.soundFile, settings.loop, settings.force);
|
||||
}
|
||||
|
||||
var popup = new window.Notification(settings.title, {
|
||||
body: settings.message,
|
||||
icon: settings.icon
|
||||
});
|
||||
|
||||
if (settings.duration > 0) {
|
||||
setTimeout(function() {
|
||||
popup.close();
|
||||
}, settings.duration);
|
||||
}
|
||||
}
|
||||
|
||||
private static hasSupport() {
|
||||
return !!window.Notification;
|
||||
}
|
||||
|
||||
private static requestPermission() {
|
||||
window.Notification.requestPermission(function(status) {
|
||||
if (window.Notification.permission !== status) {
|
||||
window.Notification.permission = status;
|
||||
}
|
||||
|
||||
if (Notification.hasPermission()) {
|
||||
$(document).trigger('notificationready.jsxc');
|
||||
} else {
|
||||
$(document).trigger('notificationfailure.jsxc');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static hasPermission() {
|
||||
return window.Notification.permission === CONST.NOTIFICATION_GRANTED;
|
||||
}
|
||||
|
||||
private static playSound(soundFile:string, loop?:boolean, force?:boolean) {
|
||||
if (!jsxc.master) {
|
||||
// only master plays sound
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.get('muteNotification') || jsxc.storage.getUserItem('presence') === 'dnd') {
|
||||
// sound mute or own presence is dnd
|
||||
return;
|
||||
}
|
||||
|
||||
if (Client.hasFocus() && !force) {
|
||||
// tab is visible
|
||||
return;
|
||||
}
|
||||
|
||||
// stop current audio file
|
||||
Notification.stopSound();
|
||||
|
||||
var audio = new Audio(Options.get('root') + '/sound/' + soundFile);
|
||||
audio.loop = loop || false;
|
||||
audio.play();
|
||||
|
||||
Notification.audioObject = audio;
|
||||
}
|
||||
|
||||
private static stopSound() {
|
||||
var audio = Notification.audioObject;
|
||||
|
||||
if (typeof audio !== 'undefined' && audio !== null) {
|
||||
audio.pause();
|
||||
Notification.audioObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,39 @@
|
||||
/**
|
||||
* Set some options for the chat.
|
||||
*
|
||||
* @namespace jsxc.options
|
||||
*/
|
||||
jsxc.options = {
|
||||
export default class Options {
|
||||
|
||||
public static get(key) {
|
||||
if (jsxc && jsxc.bid) {
|
||||
var local = jsxc.storage.getUserItem('options') || {};
|
||||
|
||||
return (typeof local[key] !== 'undefined') ? local[key] : Options[key];
|
||||
}
|
||||
|
||||
return (typeof Options[key] !== 'undefined') ? Options[key] : {};
|
||||
};
|
||||
|
||||
public static set(key, value) {
|
||||
jsxc.storage.updateItem('options', key, value, true);
|
||||
};
|
||||
|
||||
/** name of container application (e.g. owncloud or SOGo) */
|
||||
app_name: 'web applications',
|
||||
private static app_name = 'web applications';
|
||||
|
||||
/** Timeout for the keepalive signal */
|
||||
timeout: 3000,
|
||||
private static timeout = 3000;
|
||||
|
||||
/** Timeout for the keepalive signal if the master is busy */
|
||||
busyTimeout: 15000,
|
||||
private static busyTimeout = 15000;
|
||||
|
||||
/** OTR options */
|
||||
otr: {
|
||||
private static otr = {
|
||||
enable: true,
|
||||
ERROR_START_AKE: false,
|
||||
debug: false,
|
||||
SEND_WHITESPACE_TAG: false,
|
||||
WHITESPACE_START_AKE: true
|
||||
},
|
||||
};
|
||||
|
||||
/** xmpp options */
|
||||
xmpp: {
|
||||
private static xmpp = {
|
||||
/** BOSH url */
|
||||
url: null,
|
||||
|
||||
@@ -48,16 +57,16 @@ jsxc.options = {
|
||||
|
||||
/** @deprecated since v2.1.0. Use now loginForm.enable. */
|
||||
onlogin: null
|
||||
},
|
||||
};
|
||||
|
||||
/** default xmpp priorities */
|
||||
priority: {
|
||||
private static priority = {
|
||||
online: 0,
|
||||
chat: 0,
|
||||
away: 0,
|
||||
xa: 0,
|
||||
dnd: 0
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is called if a login form was found, but before any
|
||||
@@ -66,10 +75,10 @@ jsxc.options = {
|
||||
* @memberOf jsxc.options
|
||||
* @function
|
||||
*/
|
||||
formFound: null,
|
||||
private static formFound = null;
|
||||
|
||||
/** If all 3 properties are set and enable is true, the login form is used */
|
||||
loginForm: {
|
||||
private static loginForm = {
|
||||
/** False, disables login through login form */
|
||||
enable: true,
|
||||
|
||||
@@ -128,45 +137,45 @@ jsxc.options = {
|
||||
* roster state will be used.
|
||||
*/
|
||||
startMinimized: false
|
||||
},
|
||||
};
|
||||
|
||||
/** jquery object from logout element */
|
||||
logoutElement: null,
|
||||
private static logoutElement = null;
|
||||
|
||||
/** How many messages should be logged? */
|
||||
numberOfMsg: 10,
|
||||
private static numberOfMsg = 10;
|
||||
|
||||
/** Default language */
|
||||
defaultLang: 'en',
|
||||
private static defaultLang = 'en';
|
||||
|
||||
/** auto language detection */
|
||||
autoLang: true,
|
||||
private static autoLang = true;
|
||||
|
||||
/** Place for roster */
|
||||
rosterAppend: 'body',
|
||||
private static rosterAppend = 'body';
|
||||
|
||||
/** Should we use the HTML5 notification API? */
|
||||
notification: true,
|
||||
private static notification = true;
|
||||
|
||||
/** duration for notification */
|
||||
popupDuration: 6000,
|
||||
private static popupDuration = 6000;
|
||||
|
||||
/** Absolute path root of JSXC installation */
|
||||
root: '',
|
||||
private static root = '/jsxc4.0/';
|
||||
|
||||
/**
|
||||
* This function decides wether the roster will be displayed or not if no
|
||||
* connection is found.
|
||||
*/
|
||||
displayRosterMinimized: function() {
|
||||
private static displayRosterMinimized = function() {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
/** Set to true if you want to hide offline buddies. */
|
||||
hideOffline: false,
|
||||
private static hideOffline = false;
|
||||
|
||||
/** Mute notification sound? */
|
||||
muteNotification: false,
|
||||
private static muteNotification = false;
|
||||
|
||||
/**
|
||||
* If no avatar is found, this function is called.
|
||||
@@ -174,7 +183,9 @@ jsxc.options = {
|
||||
* @param jid Jid of that user.
|
||||
* @this {jQuery} Elements to update with probable .jsxc_avatar elements
|
||||
*/
|
||||
defaultAvatar: null,
|
||||
private static defaultAvatar = function(jid) {
|
||||
jsxc.gui.avatarPlaceholder($(this).find('.jsxc_avatar'), jid);
|
||||
};
|
||||
|
||||
/**
|
||||
* This callback processes all settings.
|
||||
@@ -191,7 +202,7 @@ jsxc.options = {
|
||||
* @param password {string} password
|
||||
* @param cb {loadSettingsCallback} Callback that handles the result
|
||||
*/
|
||||
loadSettings: null,
|
||||
private static loadSettings = null;
|
||||
|
||||
/**
|
||||
* Call this function to save user settings permanent.
|
||||
@@ -200,14 +211,14 @@ jsxc.options = {
|
||||
* @param data Holds all data as key/value
|
||||
* @param cb Called with true on success, false otherwise
|
||||
*/
|
||||
saveSettinsPermanent: function(data, cb) {
|
||||
private static saveSettinsPermanent = function(data, cb) {
|
||||
cb(true);
|
||||
},
|
||||
};
|
||||
|
||||
carbons: {
|
||||
private static carbons = {
|
||||
/** Enable carbon copies? */
|
||||
enable: true
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes user list.
|
||||
@@ -224,10 +235,10 @@ jsxc.options = {
|
||||
* @param {string} search Search token (start with)
|
||||
* @param {getUsers-cb} cb Called with list of users
|
||||
*/
|
||||
getUsers: null,
|
||||
private static getUsers = null;
|
||||
|
||||
/** Options for info in favicon */
|
||||
favicon: {
|
||||
private static favicon = {
|
||||
enable: true,
|
||||
|
||||
/** Favicon info background color */
|
||||
@@ -235,13 +246,13 @@ jsxc.options = {
|
||||
|
||||
/** Favicon info text color */
|
||||
textColor: '#fff'
|
||||
},
|
||||
};
|
||||
|
||||
/** @deprecated since v2.1.0. Use now RTCPeerConfig.url. */
|
||||
turnCredentialsPath: null,
|
||||
private static turnCredentialsPath = null;
|
||||
|
||||
/** RTCPeerConfiguration used for audio/video calls. */
|
||||
RTCPeerConfig: {
|
||||
private static RTCPeerConfig = {
|
||||
/** Time-to-live for config from url */
|
||||
ttl: 3600,
|
||||
|
||||
@@ -255,12 +266,12 @@ jsxc.options = {
|
||||
iceServers: [{
|
||||
urls: 'stun:stun.stunprotocol.org'
|
||||
}]
|
||||
},
|
||||
};
|
||||
|
||||
/** Link to an online user manual */
|
||||
onlineHelp: 'http://www.jsxc.org/manual.html',
|
||||
private static onlineHelp = 'http://www.jsxc.org/manual.html';
|
||||
|
||||
viewport: {
|
||||
private static viewport = {
|
||||
getSize: function() {
|
||||
var w = $(window).width() - $('#jsxc_windowListSB').width();
|
||||
var h = $(window).height();
|
||||
@@ -274,23 +285,23 @@ jsxc.options = {
|
||||
height: h
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/** Maximal storage size for attachments received via data channels (webrtc). */
|
||||
maxStorableSize: 1000000,
|
||||
private static maxStorableSize = 1000000;
|
||||
|
||||
/** Options for file transfer. */
|
||||
fileTransfer: {
|
||||
private static fileTransfer = {
|
||||
httpUpload: {
|
||||
enable: true
|
||||
},
|
||||
// @TODO add option to enable/disable data channels
|
||||
},
|
||||
};
|
||||
|
||||
/** Default option for chat state notifications */
|
||||
chatState: {
|
||||
private static chatState = {
|
||||
enable: true
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Download urls to screen media extensions.
|
||||
@@ -298,16 +309,10 @@ jsxc.options = {
|
||||
* @type {Object}
|
||||
* @see example extensions {@link https://github.com/otalk/getScreenMedia}
|
||||
*/
|
||||
screenMediaExtension: {
|
||||
private static screenMediaExtension = {
|
||||
firefox: '',
|
||||
chrome: ''
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for Message Archive Management (XEP-0313)
|
||||
*/
|
||||
mam: {
|
||||
enable: false,
|
||||
max: null
|
||||
}
|
||||
private static storage;
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import Identifiable from './IdentifiableInterface'
|
||||
import Storage from './Storage'
|
||||
|
||||
export default class PersistentMap {
|
||||
|
||||
private map = {};
|
||||
|
||||
private key:string;
|
||||
|
||||
private initialized = false;
|
||||
|
||||
constructor(private storage:Storage, ...identifier:string[]) {
|
||||
this.key = storage.generateKey.apply(storage, identifier);
|
||||
|
||||
this.map = this.storage.getItem(this.key) || {};
|
||||
|
||||
this.storage.registerHook(this.key, (newValue) => {
|
||||
this.map = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
public get(id:string) {
|
||||
return this.map[id];
|
||||
}
|
||||
|
||||
public set(id:string, value:any);
|
||||
public set(value:any);
|
||||
public set() {
|
||||
if (typeof arguments[0] === 'string'){
|
||||
let id = arguments[0];
|
||||
let value = arguments[1];
|
||||
|
||||
this.map[id] = value;
|
||||
} else if(typeof arguments[0] === 'object' && arguments[0] !== null) {
|
||||
$.extend(this.map, arguments[0]);
|
||||
}
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public empty() {
|
||||
this.map = {};
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public remove(id:Identifiable);
|
||||
public remove(id:string);
|
||||
public remove() {
|
||||
let id;
|
||||
|
||||
if (typeof arguments[0] === 'string') {
|
||||
id = arguments[0];
|
||||
} else if(typeof arguments[0].getId === 'function') {
|
||||
id = arguments[0].getId();
|
||||
} else {
|
||||
//@TODO error
|
||||
return;
|
||||
}
|
||||
|
||||
delete this.map[id];
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public registerHook(id:string, func: (newValue: any, oldValue: any, key: string) => void);
|
||||
public registerHook(func: (newValue: any, oldValue: any, key: string) => void);
|
||||
public registerHook() {
|
||||
if (typeof arguments[0] === 'string' && typeof arguments[1] === 'function') {
|
||||
let id = arguments[0];
|
||||
let func = arguments[1];
|
||||
|
||||
this.storage.registerHook(this.key, function(newData, oldData) {
|
||||
if (newData && !oldData) {
|
||||
func(newData[id]);
|
||||
} else if (newData[id] !== oldData[id]) {
|
||||
func(newData[id], oldData[id]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let func = arguments[0];
|
||||
|
||||
this.storage.registerHook(this.key, func);
|
||||
}
|
||||
}
|
||||
|
||||
private save() {
|
||||
this.initialized = true;
|
||||
|
||||
this.storage.setItem(this.key, this.map);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import Connection from './connection/Connection'
|
||||
|
||||
export interface PluginInterface {
|
||||
constructor:(Connection);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,145 @@
|
||||
import Storage from './Storage'
|
||||
import Client from './Client'
|
||||
import Log from './util/Log'
|
||||
|
||||
export default class RoleAllocator {
|
||||
private storage:Storage;
|
||||
|
||||
private master:boolean = null;
|
||||
|
||||
private keepAliveInterval;
|
||||
|
||||
private resolveTimeout;
|
||||
|
||||
private observationTimeout;
|
||||
|
||||
private resolves = [];
|
||||
|
||||
// private reject;
|
||||
|
||||
private slaveValue;
|
||||
|
||||
private masterValue;
|
||||
|
||||
private static instance;
|
||||
|
||||
private constructor() {
|
||||
this.storage = Client.getStorage();
|
||||
|
||||
this.storage.registerHook('master', (value) => {
|
||||
if (this.masterValue === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.master === null) {
|
||||
Log.debug('i am slave');
|
||||
|
||||
this.master = false;
|
||||
clearTimeout(this.resolveTimeout);
|
||||
|
||||
$('body').addClass('jsxc-slave').removeClass('jsxc-master');
|
||||
// if (typeof this.reject === 'function') {
|
||||
// //this.reject();
|
||||
// this.reject = null;
|
||||
// }
|
||||
}
|
||||
|
||||
if (this.master !== true) {
|
||||
this.masterIsStillAlive();
|
||||
} else {
|
||||
Log.error('Something went wrong. We have another master.');
|
||||
}
|
||||
});
|
||||
|
||||
this.storage.registerHook('slave', (value) => {
|
||||
if (this.slaveValue === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.master === true) {
|
||||
this.stillAlive();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static get() {
|
||||
if (!RoleAllocator.instance) {
|
||||
RoleAllocator.instance = new RoleAllocator();
|
||||
}
|
||||
|
||||
return RoleAllocator.instance;
|
||||
}
|
||||
|
||||
public isMaster() {
|
||||
return this.master;
|
||||
}
|
||||
|
||||
public waitUntilMaster() {
|
||||
return new Promise((resolve) => {
|
||||
if (this.master === true || typeof this.storage.getItem('master') === 'undefined') {
|
||||
resolve();
|
||||
} else {
|
||||
this.resolves.push(resolve);
|
||||
|
||||
if (this.master !== false){
|
||||
this.queryMaster();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private queryMaster() {
|
||||
Log.debug('query master');
|
||||
|
||||
let self = this;
|
||||
|
||||
this.resolveTimeout = setTimeout(() => {
|
||||
this.master = true;
|
||||
|
||||
Log.debug('no one responded, i am master')
|
||||
|
||||
$('body').addClass('jsxc-master').removeClass('jsxc-slave');
|
||||
|
||||
this.startKeepAliveSignal();
|
||||
|
||||
this.resolveAll();
|
||||
}, 1000);
|
||||
|
||||
this.storage.setItem('slave', this.slaveValue = Math.random());
|
||||
}
|
||||
|
||||
private masterIsStillAlive() {
|
||||
clearTimeout(this.observationTimeout);
|
||||
|
||||
let randomTime = (Math.random() * 1000) % 500;
|
||||
|
||||
this.observationTimeout = setTimeout(this.masterProbablyDied, 2000 + randomTime)
|
||||
}
|
||||
|
||||
private masterProbablyDied = () => {
|
||||
this.master = null;
|
||||
this.queryMaster();
|
||||
}
|
||||
|
||||
private startKeepAliveSignal() {
|
||||
this.stillAlive();
|
||||
|
||||
this.keepAliveInterval = window.setInterval(this.stillAlive, 1000);
|
||||
}
|
||||
|
||||
private stopKeepAliveSignal() {
|
||||
window.clearInterval(this.keepAliveInterval);
|
||||
}
|
||||
|
||||
private resolveAll() {
|
||||
for(let resolveIndex in this.resolves) {
|
||||
this.resolves[resolveIndex]();
|
||||
|
||||
delete this.resolves[resolveIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private stillAlive = () => {
|
||||
this.storage.setItem('master', this.masterValue = Math.random());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import Identifiable from './IdentifiableInterface'
|
||||
import Storage from './Storage'
|
||||
import Log from './util/Log'
|
||||
|
||||
export default class SortedPersistentMap {
|
||||
|
||||
private map = {};
|
||||
|
||||
private list;
|
||||
|
||||
private key: string;
|
||||
|
||||
private initialized = false;
|
||||
|
||||
private pushHook;
|
||||
|
||||
private removeHook;
|
||||
|
||||
constructor(private storage: Storage, ...identifier: string[]) {
|
||||
this.key = storage.generateKey.apply(storage, identifier);
|
||||
|
||||
this.list = this.storage.getItem(this.key) || [];
|
||||
|
||||
this.storage.registerHook(this.key, this.onStorage);
|
||||
}
|
||||
|
||||
public init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.pushHook !== 'function') {
|
||||
Log.error('push hook required');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.list.forEach(id => {
|
||||
try {
|
||||
this.map[id] = this.pushHook(id);
|
||||
} catch(err) {
|
||||
Log.error('Push hook threw the following error', err);
|
||||
}
|
||||
});
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
public get(id: string) {
|
||||
return this.map[id];
|
||||
}
|
||||
|
||||
public push(element: Identifiable) {
|
||||
let id = element.getId();
|
||||
|
||||
if (typeof this.map[id] !== 'undefined') {
|
||||
//@TODO error reporting
|
||||
return;
|
||||
}
|
||||
|
||||
this.map[id] = element;
|
||||
this.list.push(id);
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public empty(callback) {
|
||||
this.list.forEach(id => {
|
||||
callback(id, this.map[id]);
|
||||
});
|
||||
|
||||
this.map = {};
|
||||
this.list = [];
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public remove(id: Identifiable);
|
||||
public remove(id: string);
|
||||
public remove() {
|
||||
let id;
|
||||
|
||||
if (typeof arguments[0] === 'string') {
|
||||
id = arguments[0];
|
||||
} else if (typeof arguments[0].getId === 'function') {
|
||||
id = arguments[0].getId();
|
||||
} else {
|
||||
//@TODO error
|
||||
return;
|
||||
}
|
||||
|
||||
this.list = $.grep(this.list, function(i) {
|
||||
return id !== i;
|
||||
});
|
||||
|
||||
delete this.map[id];
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public registerHook(func: (newValue: any, oldValue: any, key: string) => void) {
|
||||
this.storage.registerHook(this.key, func);
|
||||
}
|
||||
|
||||
public setPushHook(func: (newValue: any, oldValue: any, key: string) => void) {
|
||||
this.pushHook = func;
|
||||
}
|
||||
|
||||
public setRemoveHook(func: (newValue: any, oldValue: any, key: string) => void) {
|
||||
this.removeHook = func;
|
||||
}
|
||||
|
||||
private onStorage = (newValue: any, oldValue: any, key: string) => {
|
||||
oldValue = oldValue || [];
|
||||
|
||||
if (newValue.length === oldValue.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let pushDiff: string[] = newValue.filter(id => oldValue.indexOf(id) < 0);
|
||||
|
||||
for (let value of pushDiff) {
|
||||
// call push hook
|
||||
if (typeof this.pushHook === 'function') {
|
||||
let result = this.pushHook(value);
|
||||
|
||||
if (typeof this.get(value) === 'undefined') {
|
||||
this.map[value] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let removeDiff: string[] = oldValue.filter(id => newValue.indexOf(id) < 0);
|
||||
|
||||
for (let value of removeDiff) {
|
||||
// call remove hook
|
||||
if (typeof this.removeHook === 'function') {
|
||||
this.removeHook(value, this.map[value]);
|
||||
}
|
||||
|
||||
delete this.map[value];
|
||||
}
|
||||
|
||||
this.list = newValue;
|
||||
|
||||
}
|
||||
|
||||
private save() {
|
||||
this.initialized = true;
|
||||
|
||||
this.storage.setItem(this.key, this.list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import Log from './util/Log'
|
||||
|
||||
export default class StateMachine{
|
||||
public static STATE = {
|
||||
INITIATING: 0,
|
||||
PREVCONFOUND: 1,
|
||||
SUSPEND: 2,
|
||||
TRYTOINTERCEPT: 3,
|
||||
INTERCEPTED: 4,
|
||||
ESTABLISHING: 5,
|
||||
READY: 6
|
||||
};
|
||||
|
||||
public static UISTATE = {
|
||||
INITIATING: 0,
|
||||
READY: 1
|
||||
}
|
||||
|
||||
private static currentState;
|
||||
private static currentUIState;
|
||||
|
||||
public static changeState(state:number) {
|
||||
StateMachine.currentState = state;
|
||||
|
||||
Log.debug('State changed to ' + Object.keys(StateMachine.STATE)[state]);
|
||||
|
||||
$(document).trigger('stateChange.jsxc', state);
|
||||
}
|
||||
|
||||
public static changeUIState(state:number) {
|
||||
StateMachine.currentUIState = state;
|
||||
|
||||
Log.debug('UI State changed to ' + Object.keys(StateMachine.UISTATE)[state]);
|
||||
|
||||
$(document).trigger('stateUIChange.jsxc', state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Identifiable from './IdentifiableInterface'
|
||||
|
||||
abstract class Storable implements Identifiable {
|
||||
constructor(data:any) {
|
||||
$.extend(this, data);
|
||||
}
|
||||
|
||||
public abstract getId():string;
|
||||
|
||||
public abstract save();
|
||||
|
||||
public abstract remove();
|
||||
}
|
||||
|
||||
export default Storable;
|
||||
@@ -0,0 +1,273 @@
|
||||
import Log from './util/Log'
|
||||
|
||||
const PREFIX = 'jsxc2';
|
||||
|
||||
const SEP = ':';
|
||||
|
||||
const IGNORE_KEY = ['rid'];
|
||||
|
||||
const BACKEND = localStorage;
|
||||
|
||||
export default class Storage {
|
||||
static storageNotConform: boolean = false;
|
||||
static tested: boolean = false;
|
||||
|
||||
private hooks: any = {};
|
||||
|
||||
static toSNC: number;
|
||||
|
||||
constructor(private name: string = null) {
|
||||
if (!Storage.tested) {
|
||||
Storage.tested = true;
|
||||
|
||||
this.testStorage();
|
||||
}
|
||||
|
||||
window.addEventListener('storage', this.onStorageEvent, false);
|
||||
}
|
||||
|
||||
public generateKey(...args:string[]):string {
|
||||
let key = '';
|
||||
|
||||
args.forEach(function(arg) {
|
||||
if (key !== '') {
|
||||
key += SEP;
|
||||
}
|
||||
|
||||
key += arg;
|
||||
})
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
private testStorage() {
|
||||
let randomNumber = Math.round(Math.random() * 1000000000) + '';
|
||||
let key = this.getPrefix() + randomNumber;
|
||||
let timeout;
|
||||
|
||||
let listenerFunction = function(ev) {
|
||||
if (ev.newValue === randomNumber) {
|
||||
clearTimeout(timeout);
|
||||
cleanup();
|
||||
Storage.storageNotConform = true;
|
||||
}
|
||||
};
|
||||
|
||||
let cleanup = function() {
|
||||
window.removeEventListener('storage', listenerFunction, false);
|
||||
BACKEND.removeItem(key)
|
||||
}
|
||||
|
||||
window.addEventListener('storage', listenerFunction, false);
|
||||
|
||||
timeout = setTimeout(function() {
|
||||
cleanup();
|
||||
}, 20);
|
||||
|
||||
BACKEND.setItem(key, randomNumber);
|
||||
}
|
||||
|
||||
public getPrefix(): string {
|
||||
let prefix = PREFIX + SEP;
|
||||
|
||||
if (this.name) {
|
||||
prefix += this.name + SEP;
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public getBackend() {
|
||||
return BACKEND;
|
||||
}
|
||||
|
||||
public setItem(type: string, key: string, value: any): void;
|
||||
public setItem(key: string, value: any): void
|
||||
public setItem(): void {
|
||||
let key, value;
|
||||
|
||||
if (arguments.length === 2) {
|
||||
key = arguments[0];
|
||||
value = arguments[1];
|
||||
} else if (arguments.length === 3) {
|
||||
key = arguments[0] + SEP + arguments[1];
|
||||
value = arguments[2];
|
||||
}
|
||||
|
||||
//@REVIEW why do we just stringify objects?
|
||||
if (typeof (value) === 'object') {
|
||||
// exclude jquery objects, because otherwise safari will fail
|
||||
try {
|
||||
value = JSON.stringify(value, function(key, val) {
|
||||
if (!(val instanceof jQuery)) {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('Could not stringify value', err);
|
||||
}
|
||||
}
|
||||
|
||||
let oldValue = BACKEND.getItem(this.getPrefix() + key);
|
||||
|
||||
BACKEND.setItem(this.getPrefix() + key, value);
|
||||
|
||||
if (!Storage.storageNotConform && oldValue !== value) {
|
||||
this.onStorageEvent({
|
||||
key: this.getPrefix() + key,
|
||||
oldValue: oldValue,
|
||||
newValue: value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getItem(type: string, key: string): any;
|
||||
public getItem(key: string): any;
|
||||
public getItem(): any {
|
||||
let key;
|
||||
|
||||
if (arguments.length === 1) {
|
||||
key = arguments[0];
|
||||
} else if (arguments.length === 2) {
|
||||
key = arguments[0] + SEP + arguments[1];
|
||||
}
|
||||
|
||||
key = this.getPrefix() + key;
|
||||
|
||||
var value = BACKEND.getItem(key);
|
||||
|
||||
return this.parseValue(value);
|
||||
}
|
||||
|
||||
public removeItem(type, key): void;
|
||||
public removeItem(key): void;
|
||||
public removeItem(): void {
|
||||
let key;
|
||||
|
||||
if (arguments.length === 1) {
|
||||
key = arguments[0];
|
||||
} else if (arguments.length === 2) {
|
||||
key = arguments[0] + SEP + arguments[1];
|
||||
}
|
||||
|
||||
BACKEND.removeItem(this.getPrefix() + key);
|
||||
}
|
||||
|
||||
public updateItem(type, key, variable, value): void;
|
||||
public updateItem(key, variable, value): void;
|
||||
public updateItem(): void {
|
||||
let key, variable, value;
|
||||
|
||||
if (arguments.length === 4 || (arguments.length === 3 && typeof variable === 'object')) {
|
||||
key = arguments[0] + SEP + arguments[1];
|
||||
variable = arguments[2];
|
||||
value = arguments[3];
|
||||
} else {
|
||||
key = arguments[0];
|
||||
variable = arguments[1];
|
||||
value = arguments[2];
|
||||
}
|
||||
|
||||
var data = this.getItem(key) || {};
|
||||
|
||||
if (typeof (variable) === 'object') {
|
||||
|
||||
$.each(variable, function(key, val) {
|
||||
if (typeof (data[key]) === 'undefined') {
|
||||
Log.debug('Variable ' + key + ' doesn\'t exist in ' + variable + '. It was created.');
|
||||
}
|
||||
|
||||
data[key] = val;
|
||||
});
|
||||
} else {
|
||||
if (typeof data[variable] === 'undefined') {
|
||||
Log.debug('Variable ' + variable + ' doesn\'t exist. It was created.');
|
||||
}
|
||||
|
||||
data[variable] = value;
|
||||
}
|
||||
|
||||
this.setItem(key, data);
|
||||
}
|
||||
|
||||
public increment(key: string): void {
|
||||
let value = Number(this.getItem(key));
|
||||
|
||||
this.setItem(key, value + 1);
|
||||
}
|
||||
|
||||
public removeElement(type, key, name): void;
|
||||
public removeElement(key, name): void;
|
||||
public removeElement(): void {
|
||||
let key, name;
|
||||
|
||||
if (arguments.length === 2) {
|
||||
key = arguments[0];
|
||||
name = arguments[1];
|
||||
} else if (arguments.length === 3) {
|
||||
key = arguments[0] + SEP + arguments[1];
|
||||
name = arguments[2];
|
||||
}
|
||||
|
||||
var item = this.getItem(key);
|
||||
|
||||
if ($.isArray(item)) {
|
||||
item = $.grep(item, function(e) {
|
||||
return e !== name;
|
||||
});
|
||||
} else if (typeof (item) === 'object' && item !== null) {
|
||||
delete item[name];
|
||||
}
|
||||
|
||||
this.setItem(key, item);
|
||||
}
|
||||
|
||||
public registerHook(eventName: string, func: (newValue: any, oldValue: any, key: string) => void) {
|
||||
if (!this.hooks[eventName]) {
|
||||
this.hooks[eventName] = [];
|
||||
}
|
||||
|
||||
this.hooks[eventName].push(func);
|
||||
}
|
||||
|
||||
public removeHook(eventName:string, func: (newValue: any, oldValue: any, key: string) => void) {
|
||||
let eventNameList = this.hooks[eventName] || [];
|
||||
|
||||
if (eventNameList.indexOf(func) > -1) {
|
||||
eventNameList = $.grep(eventNameList, function(i) {
|
||||
return func !== i;
|
||||
});
|
||||
}
|
||||
|
||||
this.hooks[eventName] = eventNameList;
|
||||
}
|
||||
|
||||
private onStorageEvent = (ev: any) => {
|
||||
let hooks = this.hooks;
|
||||
let key = ev.key.replace(new RegExp('^' + this.getPrefix()), '');
|
||||
let oldValue = this.parseValue(ev.oldValue);
|
||||
let newValue = this.parseValue(ev.newValue);
|
||||
|
||||
if (IGNORE_KEY.indexOf(key) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let eventNames = Object.keys(hooks);
|
||||
eventNames.forEach(function(eventName) {
|
||||
if (key.match(new RegExp('^' + eventName + '(:.+)?$'))) {
|
||||
let eventNameHooks = hooks[eventName] || [];
|
||||
eventNameHooks.forEach(function(hook) {
|
||||
hook(newValue, oldValue, key);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private parseValue(value:string) {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (e) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import Storage from './Storage';
|
||||
import User from './User';
|
||||
|
||||
export default class StorageSingleton {
|
||||
private static globalStorage:Storage;
|
||||
|
||||
private static userStorage:Storage;
|
||||
|
||||
public static getGlobalStorage():Storage {
|
||||
|
||||
}
|
||||
|
||||
public static getUserStorage():Storage {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
import Message from '../Message'
|
||||
import JID from '../JID'
|
||||
import * as NS from './xmpp/namespace'
|
||||
import onRoster from './xmpp/handlers/roster'
|
||||
import Log from '../util/Log'
|
||||
|
||||
enum Presence {
|
||||
online,
|
||||
chat,
|
||||
away,
|
||||
xa,
|
||||
dnd,
|
||||
offline
|
||||
}
|
||||
|
||||
abstract class AbstractConnection {
|
||||
protected abstract connection;
|
||||
|
||||
protected abstract send(stanzaElement:Element);
|
||||
protected abstract send(stanzaElement:Strophe.Builder);
|
||||
|
||||
protected abstract sendIQ(stanzaElement:Element):Promise<{}>;
|
||||
protected abstract sendIQ(stanzaElement:Strophe.Builder):Promise<{}>;
|
||||
|
||||
public getRoster() {
|
||||
let iq = $iq({
|
||||
type: 'get'
|
||||
}).c('query', {
|
||||
xmlns: 'jabber:iq:roster'
|
||||
});
|
||||
|
||||
//@TODO use account.getStorage().getItem('roster', 'version'), maybe better as parameter
|
||||
|
||||
return this.sendIQ(iq).then(function() {
|
||||
return onRoster.apply(this, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
public sendMessage(message:Message) {
|
||||
// @TODO pipes
|
||||
|
||||
if (message.getDirection() !== Message.DIRECTION.OUT) {
|
||||
return;
|
||||
}
|
||||
|
||||
let xmlMsg = $msg({
|
||||
to: message.getPeer().full,
|
||||
type: message.getTypeString(),
|
||||
id: message.getId()
|
||||
});
|
||||
|
||||
if (message.getHtmlMessage()) {
|
||||
xmlMsg.c('html', {
|
||||
xmlns: Strophe.NS.XHTML_IM
|
||||
}).c('body', {
|
||||
xmlns: Strophe.NS.XHTML
|
||||
}).h(message.getHtmlMessage()).up();
|
||||
}
|
||||
|
||||
if (message.getPlaintextMessage()) {
|
||||
xmlMsg.c('body').t(message.getPlaintextMessage()).up();
|
||||
}
|
||||
|
||||
// @TODO call pre send hook
|
||||
|
||||
this.send(xmlMsg);
|
||||
}
|
||||
|
||||
public sendPresence(presence:Presence = Presence.online) {
|
||||
if (this.connection.disco) {
|
||||
this.connection.disco.addIdentity('client', 'web', 'JSXC');
|
||||
this.connection.disco.addFeature(NS.get('DISCO_INFO'));
|
||||
this.connection.disco.addFeature(NS.get('RECEIPTS'));
|
||||
}
|
||||
|
||||
var presenceStanza = $pres();
|
||||
|
||||
if (this.connection.caps) {
|
||||
presenceStanza.c('c', this.connection.caps.generateCapsAttrs()).up();
|
||||
}
|
||||
|
||||
if (presence !== Presence.online) {
|
||||
presenceStanza.c('show').t(Presence[presence]).up();
|
||||
}
|
||||
|
||||
// var priority = Options.get('priority');
|
||||
// if (priority && typeof priority[status] !== 'undefined' && parseInt(priority[status]) !== 0) {
|
||||
// presenceStanza.c('priority').t(priority[status]).up();
|
||||
// }
|
||||
|
||||
Log.debug('Send presence', presenceStanza.toString());
|
||||
|
||||
this.send(presenceStanza);
|
||||
}
|
||||
|
||||
public removeContact(jid:JID) {
|
||||
let self = this;
|
||||
|
||||
// Shortcut to remove buddy from roster and cancle all subscriptions
|
||||
let iq = $iq({
|
||||
type: 'set'
|
||||
}).c('query', {
|
||||
xmlns: NS.get('roster')
|
||||
}).c('item', {
|
||||
jid: jid.bare,
|
||||
subscription: 'remove'
|
||||
});
|
||||
|
||||
// @TODO
|
||||
// jsxc.gui.roster.purge(bid);
|
||||
|
||||
return this.sendIQ(iq);
|
||||
}
|
||||
|
||||
public addContact(jid:JID, alias:string) {
|
||||
let waitForRoster = this.addContactToRoster(jid, alias);
|
||||
|
||||
this.sendSubscriptionRequest(jid);
|
||||
|
||||
return waitForRoster;
|
||||
};
|
||||
|
||||
public loadVcard(jid:JID) {
|
||||
let iq = $iq({
|
||||
type: 'get',
|
||||
to: jid.full
|
||||
}).c('vCard', {
|
||||
xmlns: NS.get('VCARD')
|
||||
});
|
||||
|
||||
//@TODO register Namespace 'VCARD', 'vcard-temp'
|
||||
|
||||
return this.sendIQ(iq).then(this.parseVcard);
|
||||
}
|
||||
|
||||
public getAvatar(jid:JID) {
|
||||
return this.loadVcard(jid).then(function(vcard){
|
||||
return new Promise(function(resolve, reject){
|
||||
if (vcard.PHOTO && vcard.PHOTO.src) {
|
||||
resolve(vcard.PHOTO);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public setDisplayName(jid:JID, displayName:string) {
|
||||
var iq = $iq({
|
||||
type: 'set'
|
||||
}).c('query', {
|
||||
xmlns: 'jabber:iq:roster'
|
||||
}).c('item', {
|
||||
jid: jid.bare,
|
||||
name: displayName
|
||||
});
|
||||
|
||||
this.sendIQ(iq);
|
||||
}
|
||||
|
||||
public sendSubscriptionAnswer(to:JID, accept:boolean) {
|
||||
let presenceStanza = $pres({
|
||||
to: to.bare,
|
||||
type: (accept) ? 'subscribed' : 'unsubscribed'
|
||||
});
|
||||
|
||||
this.send(presenceStanza);
|
||||
}
|
||||
|
||||
private addContactToRoster(jid:JID, alias:string) {
|
||||
var iq = $iq({
|
||||
type: 'set'
|
||||
}).c('query', {
|
||||
xmlns: 'jabber:iq:roster'
|
||||
}).c('item', {
|
||||
jid: jid.full,
|
||||
name: alias || ''
|
||||
});
|
||||
|
||||
return this.sendIQ(iq);
|
||||
}
|
||||
|
||||
private sendSubscriptionRequest(jid:JID) {
|
||||
// send subscription request to buddy (trigger onRosterChanged)
|
||||
this.send($pres({
|
||||
to: jid.full,
|
||||
type: 'subscribe'
|
||||
}));
|
||||
}
|
||||
|
||||
private parseVcard = (stanza) => {
|
||||
let data:any = {};
|
||||
let vcard = $(stanza).find('vCard');
|
||||
|
||||
if (!vcard.length) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return this.parseVcardChildren(vcard);
|
||||
}
|
||||
|
||||
private parseVcardChildren(stanza) {
|
||||
let data:any = {};
|
||||
let children = stanza.children();
|
||||
|
||||
children.each(function(){
|
||||
let item = $(this);
|
||||
let children = item.children();
|
||||
let itemName = item[0].tagName;
|
||||
let value = null;
|
||||
|
||||
if (itemName === 'PHOTO') {
|
||||
let img = item.find('BINVAL').text();
|
||||
let type = item.find('TYPE').text();
|
||||
let src = 'data:' + type + ';base64,' + img;
|
||||
|
||||
if (item.find('EXTVAL').length > 0) {
|
||||
src = item.find('EXTVAL').text();
|
||||
}
|
||||
|
||||
// concat chunks
|
||||
src = src.replace(/[\t\r\n\f]/gi, '');
|
||||
|
||||
value = {
|
||||
type: type,
|
||||
src: src
|
||||
};
|
||||
} else if (children.length > 0) {
|
||||
value = this.parseVcardChildren(children);
|
||||
} else {
|
||||
value = item.text();
|
||||
}
|
||||
|
||||
data[itemName] = value;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export {AbstractConnection, Presence};
|
||||
@@ -0,0 +1,27 @@
|
||||
import JID from './../JID';
|
||||
import Message from './../Message';
|
||||
|
||||
export interface IConnection {
|
||||
loadVcard(jid:JID):any;
|
||||
|
||||
getCapabilitiesByJid(jid:JID):any;
|
||||
|
||||
addContact(jid:JID, alias:string);
|
||||
|
||||
removeContact(jid:JID);
|
||||
|
||||
sendMessage(message:Message);
|
||||
|
||||
sendPresence();
|
||||
|
||||
getAvatar(jid:JID);
|
||||
|
||||
setDisplayName(jid:JID, displayName:string);
|
||||
|
||||
hasFeatureByJid(jid:JID, feature:string);
|
||||
hasFeatureByJid(jid:JID, feature:string[]);
|
||||
|
||||
getRoster();
|
||||
|
||||
logout();
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import JID from '../../JID';
|
||||
import Message from '../../Message';
|
||||
import {IConnection} from '../ConnectionInterface';
|
||||
import Log from '../../util/Log'
|
||||
import Account from '../../Account'
|
||||
import {AbstractConnection, Presence} from '../AbstractConnection'
|
||||
import * as StropheLib from 'strophe.js'
|
||||
|
||||
let Strophe = StropheLib.Strophe;
|
||||
|
||||
export default class StorageConnection extends AbstractConnection implements IConnection {
|
||||
|
||||
protected connection:any = {};
|
||||
|
||||
constructor(private account: Account) {
|
||||
super();
|
||||
window.storageConnection = this;
|
||||
this.connection = {
|
||||
jid: account.getJID().full,
|
||||
send: this.send,
|
||||
sendIQ: (elem, success, error) => {
|
||||
this.sendIQ(elem).then(success).catch(error);
|
||||
},
|
||||
addHandler: () => {}
|
||||
}
|
||||
|
||||
for (var k in (<any>Strophe)._connectionPlugins) {
|
||||
if ((<any>Strophe)._connectionPlugins.hasOwnProperty(k)) {
|
||||
var ptype = (<any>Strophe)._connectionPlugins[k];
|
||||
// jslint complaints about the below line, but this is fine
|
||||
var F = function() { }; // jshint ignore:line
|
||||
F.prototype = ptype;
|
||||
this.connection[k] = new F();
|
||||
this.connection[k].init(this.connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getCapabilitiesByJid(jid: JID): any {
|
||||
Log.info('[SC] getCapabilitiesByJid');
|
||||
};
|
||||
|
||||
public hasFeatureByJid(jid: JID, feature: string);
|
||||
public hasFeatureByJid(jid: JID, feature: string[]);
|
||||
public hasFeatureByJid() {
|
||||
Log.info('[SC] has feature by jid');
|
||||
}
|
||||
|
||||
public logout() {
|
||||
Log.info('[SC] logout');
|
||||
}
|
||||
|
||||
protected send(stanzaElement: Element);
|
||||
protected send(stanzaElement: Strophe.Builder);
|
||||
protected send() {
|
||||
let storage = this.account.getStorage();
|
||||
let stanzaString = this.stanzaElementToString(arguments[0]);
|
||||
let key = storage.generateKey(
|
||||
'stanza',
|
||||
stanzaString.length + '',
|
||||
new Date().getTime() + ''
|
||||
);
|
||||
|
||||
storage.setItem(key, stanzaString);
|
||||
}
|
||||
|
||||
protected sendIQ(stanzaElement:Element):Promise<{}>;
|
||||
protected sendIQ(stanzaElement:Strophe.Builder):Promise<{}>;
|
||||
protected sendIQ():Promise<{}> {
|
||||
let storage = this.account.getStorage();
|
||||
let stanzaString = this.stanzaElementToString(arguments[0]);
|
||||
let key = storage.generateKey(
|
||||
'stanzaIQ',
|
||||
stanzaString.length + '',
|
||||
new Date().getTime() + ''
|
||||
);
|
||||
|
||||
storage.setItem(key, stanzaString);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
storage.registerHook(key, function(newValue) { console.log('got an answer', newValue)
|
||||
storage.removeItem(key);
|
||||
|
||||
if (newValue.type === 'success') {
|
||||
resolve(newValue.stanza);
|
||||
} else if (newValue.type === 'error') {
|
||||
reject(newValue.stanza);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private stanzaElementToString(stanzaElement: Element):string;
|
||||
private stanzaElementToString(stanzaElement: Strophe.Builder):string;
|
||||
private stanzaElementToString() {
|
||||
let stanzaString: string;
|
||||
let stanzaElement = arguments[0] || {};
|
||||
|
||||
if (typeof stanzaElement.innerHTML === 'string') {
|
||||
stanzaString = stanzaElement.innerHTML;
|
||||
} else {
|
||||
stanzaString = stanzaElement.toString();
|
||||
}
|
||||
|
||||
return stanzaString;
|
||||
}
|
||||
}
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário