feat: migrate to jsxc v4.0.0-alpha
- use webpack to build packages - use typescript
This commit is contained in:
Родитель
e5214aa68c
Коммит
8d04c91ee2
|
@ -0,0 +1,15 @@
|
|||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[ts/**.ts]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 3
|
||||
quote_type = single
|
|
@ -18,3 +18,6 @@ coverage
|
|||
.phpstorm_helpers
|
||||
.phpcomplete_extended
|
||||
.php_cs.cache
|
||||
/js/
|
||||
yarn-error.log
|
||||
/dist/
|
||||
|
|
4
LICENSE
4
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Klaus Herberth <klaus@jsxc.org>
|
||||
Copyright (c) 2018 Klaus Herberth <klaus@jsxc.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -18,4 +18,4 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
|
|
|
@ -5,10 +5,6 @@ use OCA\OJSXC\Hooks;
|
|||
|
||||
\OCP\App::registerPersonal('ojsxc', 'settings/personal');
|
||||
|
||||
$isDevEnv = \OC::$server->getConfig()->getSystemValue('jsxc.environment') === 'dev';
|
||||
$jsxc_root = ($isDevEnv)? 'jsxc/dev/' : 'jsxc/';
|
||||
$jsProdSuffix = (!$isDevEnv)? '.min' : '';
|
||||
|
||||
$linkToGeneralConfig = \OC::$server->getURLGenerator()->linkToRoute('ojsxc.javascript.generalConfig');
|
||||
|
||||
\OCP\Util::addHeader(
|
||||
|
@ -19,14 +15,11 @@ $linkToGeneralConfig = \OC::$server->getURLGenerator()->linkToRoute('ojsxc.javas
|
|||
], ''
|
||||
);
|
||||
|
||||
OCP\Util::addScript ( 'ojsxc', $jsxc_root.'lib/jquery.slimscroll' );
|
||||
OCP\Util::addScript ( 'ojsxc', $jsxc_root.'lib/jquery.fullscreen' );
|
||||
OCP\Util::addScript ( 'ojsxc', $jsxc_root.'lib/jsxc.dep'.$jsProdSuffix );
|
||||
OCP\Util::addScript ( 'ojsxc', $jsxc_root.'jsxc'.$jsProdSuffix );
|
||||
OCP\Util::addScript ( 'ojsxc', 'ojsxc');
|
||||
OCP\Util::addScript ( 'ojsxc', 'jsxc/jsxc.bundle' );
|
||||
OCP\Util::addScript ( 'ojsxc', 'bundle');
|
||||
|
||||
// ############# CSS #############
|
||||
OCP\Util::addStyle ( 'ojsxc', 'jsxc.oc' );
|
||||
OCP\Util::addStyle ( 'ojsxc', '../js/jsxc/styles/jsxc.bundle' );
|
||||
OCP\Util::addStyle ( 'ojsxc', 'bundle' );
|
||||
|
||||
if(class_exists('\\OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy')) {
|
||||
$manager = \OC::$server->getContentSecurityPolicyManager();
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<lib>xmlreader</lib>
|
||||
<lib>xmlwriter</lib>
|
||||
<lib>dom</lib>
|
||||
<nextcloud min-version="12" max-version="14"/>
|
||||
<nextcloud min-version="12" max-version="15"/>
|
||||
</dependencies>
|
||||
|
||||
<repair-steps>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
declare var OC: {
|
||||
generateUrl(string, any?): string,
|
||||
};
|
||||
declare var oc_requesttoken: string;
|
||||
declare var oc_current_user: string;
|
||||
declare var jsxc: any;
|
||||
declare var OJSXC_CONFIG: {
|
||||
defaultLoginFormEnable: boolean,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* This is a helper file for the concatenation.
|
||||
*/
|
||||
;
|
1
js/jsxc
1
js/jsxc
|
@ -1 +0,0 @@
|
|||
Subproject commit 5c7c7a4cff6ea306833713bfd8c6b1a71fe1a9f3
|
523
js/ojsxc.js
523
js/ojsxc.js
|
@ -1,523 +0,0 @@
|
|||
/* global jsxc, oc_appswebroots, OC, oc_requesttoken, dijit, oc_config, OJSXC_CONFIG */
|
||||
/* jshint latedef: nofunc */
|
||||
|
||||
|
||||
(function($) {
|
||||
"use strict";
|
||||
|
||||
var serverTypes = {
|
||||
INTERNAL: 0,
|
||||
EXTERNAL: 1,
|
||||
MANAGED: 2
|
||||
};
|
||||
|
||||
var forceLoginFormEnable;
|
||||
|
||||
function observeContactsMenu() {
|
||||
var target = document.getElementById('contactsmenu');
|
||||
|
||||
var observer = new MutationObserver(function(mutations) {
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.target.id !== 'contactsmenu-contacts') {
|
||||
return;
|
||||
}
|
||||
|
||||
$(mutation.target).find('[href^="xmpp:"]').addClass('jsxc_statusIndicator');
|
||||
|
||||
$(mutation.target).find('.contact').each(function() {
|
||||
updateContactItem($(this));
|
||||
});
|
||||
|
||||
jsxc.gui.detectUriScheme(mutation.target);
|
||||
});
|
||||
});
|
||||
|
||||
var config = {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
observer.observe(target, config);
|
||||
}
|
||||
|
||||
function updateContactItem(contactElement) {
|
||||
var xmppAddresses = contactElement.find('[href^="xmpp:"]').map(function() {
|
||||
return $(this).attr('href').replace(/^xmpp:/, '');
|
||||
});
|
||||
|
||||
if (xmppAddresses.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lastMessages = [];
|
||||
var highestPresent = jsxc.CONST.STATUS.indexOf('offline');
|
||||
var highestPresentBid = xmppAddresses.get(0);
|
||||
|
||||
xmppAddresses.each(function(index, bid) {
|
||||
var lastMsg = jsxc.getLastMsg(bid);
|
||||
|
||||
if (lastMsg) {
|
||||
lastMessages.push(lastMsg);
|
||||
}
|
||||
|
||||
var data = jsxc.storage.getUserItem('buddy', bid) || {};
|
||||
|
||||
if (data.status > highestPresent) {
|
||||
highestPresent = data.status;
|
||||
highestPresentBid = bid;
|
||||
}
|
||||
});
|
||||
|
||||
var latestMsg = {
|
||||
date: 0
|
||||
};
|
||||
$(lastMessages).each(function(index, msg) {
|
||||
if (msg.date > latestMsg.date) {
|
||||
latestMsg = msg;
|
||||
}
|
||||
});
|
||||
|
||||
if (latestMsg.date > 0) {
|
||||
// replace emoticons from XEP-0038 and pidgin with shortnames
|
||||
$.each(jsxc.gui.emotions, function(i, val) {
|
||||
latestMsg.text = latestMsg.text.replace(val[2], ':' + val[1] + ':');
|
||||
});
|
||||
|
||||
// translate shortnames to images
|
||||
latestMsg.text = jsxc.gui.shortnameToImage(latestMsg.text);
|
||||
|
||||
contactElement.find('.last-message').html(latestMsg.text);
|
||||
}
|
||||
|
||||
if (highestPresent > 0) {
|
||||
var status = jsxc.CONST.STATUS[highestPresent];
|
||||
|
||||
contactElement.removeClass('jsxc_' + jsxc.CONST.STATUS.join(' jsxc_')).addClass('jsxc_' + status);
|
||||
}
|
||||
|
||||
if (highestPresentBid) {
|
||||
contactElement.find('.avatar').click(function() {
|
||||
jsxc.gui.queryActions.message(highestPresentBid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function injectChatIcon() {
|
||||
var div = $('<div/>');
|
||||
|
||||
div.addClass('jsxc_chatIcon');
|
||||
div.click(function() {
|
||||
jsxc.gui.roster.toggle();
|
||||
});
|
||||
|
||||
$('#header form.searchbox').after(div);
|
||||
|
||||
}
|
||||
|
||||
function onRosterToggle(ev, state, duration) {
|
||||
$('body').removeClass('jsxc-roster-hidden jsxc-roster-shown').addClass('jsxc-roster-' + state);
|
||||
|
||||
// trigger nextcloud/owncloud triggers
|
||||
setTimeout(function() {
|
||||
$(window).resize();
|
||||
}, duration + 50);
|
||||
}
|
||||
|
||||
function onRosterReady(ev, rosterState) {
|
||||
injectChatIcon();
|
||||
|
||||
$('body').removeClass('jsxc-roster-hidden jsxc-roster-shown').addClass('jsxc-roster-' + rosterState);
|
||||
|
||||
// update webodf
|
||||
$(window).on('hashchange', function() {
|
||||
if (window.location.pathname.match(/\/documents\/$/)) {
|
||||
var docNo = window.location.hash.replace(/^#/, '');
|
||||
|
||||
if (docNo.match(/[0-9]+/) && typeof dijit !== 'undefined') {
|
||||
dijit.byId("mainContainer").resize();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function defaultAvatar(element, jid) {
|
||||
var adminSettings = jsxc.options.get('adminSettings') || {};
|
||||
var cache = jsxc.storage.getUserItem('defaultAvatars') || {};
|
||||
var data = jsxc.storage.getUserItem('buddy', jsxc.jidToBid(jid)) || {};
|
||||
|
||||
var node = Strophe.getNodeFromJid(jid);
|
||||
var domain = Strophe.getDomainFromJid(jid);
|
||||
var user = Strophe.unescapeNode(node);
|
||||
|
||||
$(element).each(function() {
|
||||
|
||||
var $div = $(this).find('.jsxc_avatar');
|
||||
var size = $div.width();
|
||||
var key = user + '@' + size;
|
||||
|
||||
var handleResponse = function(result) {
|
||||
if (typeof(result) === 'object') {
|
||||
if (result.data && result.data.displayname) {
|
||||
$div.imageplaceholder(user, result.data.displayname);
|
||||
} else {
|
||||
$div.imageplaceholder(user);
|
||||
}
|
||||
} else {
|
||||
$div.css('backgroundImage', 'url(' + result + ')');
|
||||
$div.text('');
|
||||
}
|
||||
};
|
||||
|
||||
if (domain !== adminSettings.xmppDomain) {
|
||||
// probably external user, don't request avatar
|
||||
$div.imageplaceholder(user);
|
||||
} else if (typeof cache[key] === 'undefined' || cache[key] === null) {
|
||||
if (data.status === 0) {
|
||||
// don't query avatar for offline users
|
||||
$div.imageplaceholder(user, data.name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var url;
|
||||
|
||||
url = OC.generateUrl('/avatar/' + encodeURIComponent(user) + '/' + size + '?requesttoken={requesttoken}', {
|
||||
user: user,
|
||||
size: size,
|
||||
requesttoken: oc_requesttoken
|
||||
});
|
||||
|
||||
$.get(url, function(result) {
|
||||
|
||||
var val = (typeof result === 'object') ? result : url;
|
||||
handleResponse(val);
|
||||
|
||||
jsxc.storage.updateItem('defaultAvatars', key, val, true);
|
||||
});
|
||||
|
||||
} else {
|
||||
handleResponse(cache[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadSettings(username, password, cb) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/settings'),
|
||||
data: {
|
||||
username: username,
|
||||
password: password
|
||||
},
|
||||
success: function(d) {
|
||||
if (d.result === 'success' && d.data && d.data.serverType !== 'internal' && d.data.xmpp.url !== '' && d.data.xmpp.url !== null) {
|
||||
jsxc.storage.setItem('serverType', serverTypes[d.data.serverType.toUpperCase()]);
|
||||
|
||||
if (forceLoginFormEnable) {
|
||||
d.data.loginForm.enable = true;
|
||||
}
|
||||
|
||||
cb(d.data);
|
||||
} else if (d.data && d.data.serverType === 'internal') {
|
||||
jsxc.storage.setItem('serverType', serverTypes.INTERNAL);
|
||||
|
||||
var node = username || OC.currentUser;
|
||||
jsxc.bid = node.toLowerCase() + '@' + window.location.host;
|
||||
|
||||
jsxc.options.set('adminSettings', d.data.adminSettings);
|
||||
|
||||
if (d.data.loginForm) {
|
||||
jsxc.options.set('loginForm', {
|
||||
startMinimized: d.data.loginForm.startMinimized
|
||||
});
|
||||
}
|
||||
|
||||
cb(false);
|
||||
} else {
|
||||
cb(false);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
jsxc.error('XHR error on getSettings.php');
|
||||
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
jsxc.debug('Error message: ' + xhr.responseJSON.message);
|
||||
}
|
||||
|
||||
if (xhr.status === 412) {
|
||||
jsxc.debug('Refresh page to get a new CSRF token');
|
||||
|
||||
window.location.href = window.location.href;
|
||||
return;
|
||||
}
|
||||
|
||||
cb(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveSettinsPermanent(data, cb) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/settings/user'),
|
||||
data: data,
|
||||
success: function(data) {
|
||||
cb(data && data.status === 'success');
|
||||
},
|
||||
error: function() {
|
||||
cb(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getUsers(search, cb) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: OC.generateUrl('apps/ojsxc/settings/users'),
|
||||
data: {
|
||||
search: search
|
||||
},
|
||||
success: cb,
|
||||
error: function() {
|
||||
jsxc.error('XHR error on getUsers.php');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getViewportSize() {
|
||||
var w = $(window).width() - $('#jsxc_windowListSB').width();
|
||||
var h = $(window).height() - $('#header').height() - 10;
|
||||
|
||||
if (jsxc.storage.getUserItem('roster') === 'shown') {
|
||||
w -= $('#jsxc_roster').outerWidth(true);
|
||||
}
|
||||
|
||||
return {
|
||||
width: w,
|
||||
height: h
|
||||
};
|
||||
}
|
||||
|
||||
function addChatSubmitButton() {
|
||||
var defaultEnable = OJSXC_CONFIG.defaultLoginFormEnable;
|
||||
var jsxcSubmitWrapperElement = $('<div>');
|
||||
jsxcSubmitWrapperElement.attr('id', 'jsxc_submit_wrapper');
|
||||
|
||||
var submitElement = $('<input>');
|
||||
submitElement.attr({
|
||||
type: 'button',
|
||||
id: 'jsxc_submit',
|
||||
});
|
||||
submitElement.addClass('login primary');
|
||||
submitElement.val(defaultEnable ? $.t('Log_in_without_chat') : $.t('Log_in_with_chat'));
|
||||
submitElement.click(function() {
|
||||
jsxc.storage.setItem('login_without_chat', defaultEnable);
|
||||
|
||||
if (defaultEnable) { // log in without chat
|
||||
jsxc.submitLoginForm();
|
||||
} else { // log in with chat
|
||||
forceLoginFormEnable = true;
|
||||
$(jsxc.options.loginForm.form).submit();
|
||||
}
|
||||
});
|
||||
|
||||
jsxcSubmitWrapperElement.append(submitElement);
|
||||
$('.login-additional').prepend(jsxcSubmitWrapperElement);
|
||||
|
||||
$('#lost-password').mouseup(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
jsxcSubmitWrapperElement.slideUp().fadeOut();
|
||||
});
|
||||
$('#lost-password-back').mouseup(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
jsxcSubmitWrapperElement.slideDown().fadeIn();
|
||||
});
|
||||
}
|
||||
|
||||
function addServerTypetoBodyTag() {
|
||||
let type = parseInt(jsxc.storage.getItem('serverType'));
|
||||
|
||||
if (parseInt(type) === serverTypes.INTERNAL) {
|
||||
$('body').addClass('jsxc-internal-server');
|
||||
}
|
||||
}
|
||||
|
||||
// initialization
|
||||
$(function() {
|
||||
if (location.pathname.substring(location.pathname.lastIndexOf("/") + 1) === 'public.php') {
|
||||
// abort on shares
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.parent && window !== window.parent) {
|
||||
// abort if inside a frame
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof jsxc === 'undefined' || typeof emojione === 'undefined') {
|
||||
// abort if core or dependencies threw an error
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof oc_config === 'undefined' || typeof oc_appswebroots === 'undefined' || typeof OC === 'undefined') {
|
||||
// abort if a dependency is missing
|
||||
return;
|
||||
}
|
||||
|
||||
if (OC.generateUrl('login/flow') === window.location.pathname) {
|
||||
// abort on login flow
|
||||
return;
|
||||
}
|
||||
|
||||
addServerTypetoBodyTag();
|
||||
|
||||
$(document).one('ready-roster-jsxc', onRosterReady);
|
||||
$(document).on('toggle.roster.jsxc', onRosterToggle);
|
||||
|
||||
$(document).on('connected.jsxc', function() {
|
||||
// reset default avatar cache
|
||||
jsxc.storage.removeUserItem('defaultAvatars');
|
||||
// when we are connected it doesn't matter anymore whether we logged in without chat since the user
|
||||
// must have manually logged in
|
||||
jsxc.storage.setItem('login_without_chat', false);
|
||||
});
|
||||
|
||||
$(document).on('connfail.jsxc', function(ev, condition) {
|
||||
if (condition === 'x-nc-not_allowed_to_chat') {
|
||||
jsxc.gui.roster.toggle(jsxc.CONST.HIDDEN);
|
||||
$('.jsxc_chatIcon').remove();
|
||||
jsxc.storage.removeItem('jid');
|
||||
jsxc.storage.removeItem('sid');
|
||||
jsxc.storage.removeItem('rid');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('status.contacts.count status.contact.updated', function() {
|
||||
if (jsxc.restoreCompleted) {
|
||||
setTimeout(function() {
|
||||
jsxc.gui.detectEmail($('table#contactlist'));
|
||||
}, 500);
|
||||
} else {
|
||||
$(document).on('restoreCompleted.jsxc', function() {
|
||||
jsxc.gui.detectEmail($('table#contactlist'));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
jsxc.init({
|
||||
app_name: 'Nextcloud',
|
||||
loginForm: {
|
||||
form: '#body-login form',
|
||||
jid: '#user',
|
||||
pass: '#password',
|
||||
ifFound: 'force',
|
||||
onConnecting: (oc_config.version.match(/^([8-9]|[0-9]{2,})+\./)) ? 'quiet' : 'dialog'
|
||||
},
|
||||
logoutElement: $('#logout'),
|
||||
rosterAppend: 'body',
|
||||
root: oc_appswebroots.ojsxc + '/js/jsxc',
|
||||
RTCPeerConfig: {
|
||||
url: OC.generateUrl('apps/ojsxc/settings/iceServers')
|
||||
},
|
||||
displayRosterMinimized: function() {
|
||||
return OC.currentUser != null;
|
||||
},
|
||||
defaultAvatar: function(jid) {
|
||||
defaultAvatar(this, jid);
|
||||
},
|
||||
loadSettings: loadSettings,
|
||||
saveSettinsPermanent: saveSettinsPermanent,
|
||||
getUsers: getUsers,
|
||||
viewport: {
|
||||
getSize: getViewportSize
|
||||
}
|
||||
});
|
||||
|
||||
// Add submit link without chat functionality
|
||||
if (jsxc.el_exists(jsxc.options.loginForm.form) && jsxc.el_exists(jsxc.options.loginForm.jid) && jsxc.el_exists(jsxc.options.loginForm.pass)) {
|
||||
|
||||
addChatSubmitButton();
|
||||
|
||||
Strophe.log = function(level, msg) {
|
||||
if (level === 3 && /^request id/.test(msg)) {
|
||||
console.warn('Something went wrong during BOSH connection establishment. Continue without chat.');
|
||||
|
||||
jsxc.submitLoginForm();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if ($('#contactsmenu').length > 0) {
|
||||
observeContactsMenu();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$(document).on('click', '#jsxc_roster p', function() {
|
||||
if (jsxc.storage.getItem('serverType') === serverTypes.INTERNAL) {
|
||||
startInternalBackend();
|
||||
}
|
||||
});
|
||||
|
||||
function startInternalBackend() {
|
||||
var currentUser = OC.currentUser;
|
||||
|
||||
if (!currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsxc.bid = currentUser.toLowerCase() + '@' + window.location.host;
|
||||
|
||||
jsxc.options.set('xmpp', {
|
||||
url: OC.generateUrl('apps/ojsxc/http-bind')
|
||||
});
|
||||
|
||||
$(document).one('attached.jsxc', function() {
|
||||
if (jsxc.options.get('loginForm').startMinimized !== true) {
|
||||
jsxc.gui.roster.toggle(jsxc.CONST.SHOWN);
|
||||
}
|
||||
});
|
||||
|
||||
jsxc.start(jsxc.bid + '/internal', 'internal', '123456');
|
||||
}
|
||||
|
||||
if (jsxc.storage.getItem('serverType') === serverTypes.INTERNAL) {
|
||||
jsxc.gui.showLoginBox = function() {};
|
||||
}
|
||||
|
||||
$(document).on('stateChange.jsxc', function _handler(event, state) {
|
||||
if (state === jsxc.CONST.STATE.SUSPEND) {
|
||||
/**
|
||||
* The first time we go into suspend mode we check if we are using the internal backend.
|
||||
* If this is the case and the user explicitly press the "login_without_chat" button when logging
|
||||
* into Nextcloud we know we are using another authentication mechanism (like SAML/SSO) and thus have
|
||||
* to manually start the connection.
|
||||
*/
|
||||
var chatDisabledByUser = jsxc.storage.getUserItem('forcedLogout') || jsxc.storage.getItem('login_without_chat');
|
||||
$(document).off('stateChange.jsxc', _handler);
|
||||
if (jsxc.storage.getItem('serverType') === null) {
|
||||
$.ajax({
|
||||
url: OC.generateUrl('apps/ojsxc/settings/servertype'),
|
||||
success: function(data) {
|
||||
jsxc.storage.setItem('serverType', serverTypes[data.serverType.toUpperCase()]);
|
||||
|
||||
if (data.serverType === 'internal' && !chatDisabledByUser) {
|
||||
jsxc.gui.showLoginBox = function() {};
|
||||
startInternalBackend();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (jsxc.storage.getItem('serverType') === serverTypes.INTERNAL && !chatDisabledByUser) {
|
||||
jsxc.gui.showLoginBox = function() {};
|
||||
startInternalBackend();
|
||||
}
|
||||
} else if (state === jsxc.CONST.STATE.READY) {
|
||||
// if JSXC is ready this means we successfully connected and thus don't have to listen to the suspend state
|
||||
$(document).off('stateChange.jsxc', _handler);
|
||||
}
|
||||
});
|
||||
}(jQuery));
|
|
@ -1,290 +0,0 @@
|
|||
/* global $, OC */
|
||||
|
||||
$(document).ready(function() {
|
||||
/**
|
||||
* Test if bosh server is up and running.
|
||||
*
|
||||
* @param {string} url BOSH url
|
||||
* @param {string} domain host domain for BOSH server
|
||||
* @param {Function} cb called if test is done
|
||||
*/
|
||||
function testBoshServer(url, domain, cb) {
|
||||
var rid = jsxc.storage.getItem('rid') || '123456';
|
||||
|
||||
function fail(m) {
|
||||
var msg = 'BOSH server NOT reachable or misconfigured.';
|
||||
|
||||
if (typeof m === 'string') {
|
||||
msg += '<br /><br />' + m;
|
||||
}
|
||||
|
||||
cb({
|
||||
status: 'fail',
|
||||
msg: msg
|
||||
});
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: url,
|
||||
data: "<body rid='" + rid + "' xmlns='http://jabber.org/protocol/httpbind' to='" + domain + "' xml:lang='en' wait='60' hold='1' content='text/xml; charset=utf-8' ver='1.6' xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh'/>",
|
||||
global: false,
|
||||
dataType: 'xml'
|
||||
}).done(function(stanza) {
|
||||
if (typeof stanza === 'string') {
|
||||
// shouldn't be needed anymore, because of dataType
|
||||
stanza = $.parseXML(stanza);
|
||||
}
|
||||
|
||||
var body = $(stanza).find('body[xmlns="http://jabber.org/protocol/httpbind"]');
|
||||
var condition = (body) ? body.attr('condition') : null;
|
||||
var type = (body) ? body.attr('type') : null;
|
||||
|
||||
// we got a valid xml response, but we have test for errors
|
||||
|
||||
if (body.length > 0 && type !== 'terminate') {
|
||||
cb({
|
||||
status: 'success',
|
||||
msg: 'BOSH Server reachable.'
|
||||
});
|
||||
} else {
|
||||
if (condition === 'internal-server-error') {
|
||||
fail('Internal server error: ' + body.text());
|
||||
} else if (condition === 'host-unknown') {
|
||||
if (url) {
|
||||
fail('Host unknown: ' + domain + ' is unknown to your XMPP server.');
|
||||
} else {
|
||||
fail('Host unknown: Please provide a XMPP domain.');
|
||||
}
|
||||
} else {
|
||||
fail(condition);
|
||||
}
|
||||
}
|
||||
}).fail(function(xhr, textStatus) {
|
||||
// no valid xml, not found or csp issue
|
||||
|
||||
var fullurl;
|
||||
if (url.match(/^https?:\/\//)) {
|
||||
fullurl = url;
|
||||
} else {
|
||||
fullurl = window.location.protocol + '//' + window.location.host;
|
||||
if (url.match(/^\//)) {
|
||||
fullurl += url;
|
||||
} else {
|
||||
fullurl += window.location.pathname.replace(/[^/]+$/, "") + url;
|
||||
}
|
||||
}
|
||||
|
||||
if (xhr.status === 0) {
|
||||
// cross-side
|
||||
fail('Cross domain request was not possible. Either your BOSH server does not send any ' +
|
||||
'Access-Control-Allow-Origin header or the content-security-policy (CSP) blocks your request. ' +
|
||||
'Starting from Owncloud 9.0 your CSP will be updated in any app which uses the appframework (e.g. files) ' +
|
||||
'after you save these settings and reload.' +
|
||||
'The savest way is still to use Apache ProxyRequest or Nginx proxy_pass.');
|
||||
} else if (xhr.status === 404) {
|
||||
// not found
|
||||
fail('Your server responded with "404 Not Found". Please check if your BOSH server is running and reachable via ' + fullurl + '.');
|
||||
} else if (textStatus === 'parsererror') {
|
||||
fail('Invalid XML received. Maybe ' + fullurl + ' was redirected. You should use an absolute url.');
|
||||
} else {
|
||||
fail(xhr.status + ' ' + xhr.statusText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#ojsxc [name=serverType]').change(function() {
|
||||
$('#ojsxc .ojsxc-external, #ojsxc .ojsxc-internal, #ojsxc .ojsxc-managed').hide();
|
||||
$('#ojsxc .ojsxc-external, #ojsxc .ojsxc-internal, #ojsxc .ojsxc-managed').find('.required').removeAttr('required');
|
||||
$('#ojsxc .ojsxc-' + $(this).val()).show();
|
||||
$('#ojsxc .ojsxc-' + $(this).val()).find('.required').attr('required', 'true');
|
||||
});
|
||||
$('#ojsxc [name=serverType]:checked').change();
|
||||
|
||||
$('#boshUrl, #xmppDomain').on('input', function() {
|
||||
var self = $(this);
|
||||
var timeout = self.data('timeout');
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
var url = $('#boshUrl').val();
|
||||
var domain = $('#xmppDomain').val();
|
||||
|
||||
if (!url || !domain) {
|
||||
// we need url and domain to test BOSH server
|
||||
return;
|
||||
}
|
||||
|
||||
$('#ojsxc .boshUrl-msg').html('<div></div>');
|
||||
var status = $('#ojsxc .boshUrl-msg div');
|
||||
status.html('<img src="' + jsxc.options.root + '/img/loading.gif" alt="wait" width="16px" height="16px" /> Testing BOSH Server...');
|
||||
|
||||
// test only every 2 seconds
|
||||
timeout = setTimeout(function() {
|
||||
testBoshServer(url, domain, function(res) {
|
||||
status.addClass('jsxc_' + res.status);
|
||||
status.html(res.msg);
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
self.data('timeout', timeout);
|
||||
});
|
||||
|
||||
function saveAdminSettings() {
|
||||
if (OC.PasswordConfirmation && OC.PasswordConfirmation.requiresPasswordConfirmation()) {
|
||||
OC.PasswordConfirmation.requirePasswordConfirmation(saveAdminSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
var post = $('#ojsxc').serialize();
|
||||
|
||||
$('#ojsxc .msg').html('<div>');
|
||||
var status = $('#ojsxc .msg div');
|
||||
status.html('<img src="' + jsxc.options.root + '/img/loading.gif" alt="wait" width="16px" height="16px" /> Saving...');
|
||||
|
||||
$.post(OC.generateUrl('apps/ojsxc/settings/admin'), post, function(data) {
|
||||
if (data && data.status === 'success') {
|
||||
status.addClass('jsxc_success').text('Settings saved. Please log out and in again.');
|
||||
} else {
|
||||
status.addClass('jsxc_fail').text('Error!');
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
status.hide('slow');
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
$('#ojsxc').submit(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
saveAdminSettings();
|
||||
});
|
||||
|
||||
$('#ojsxc .add-input').click(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var clone = $(this).prev().clone();
|
||||
clone.val('');
|
||||
|
||||
$(this).before(clone);
|
||||
});
|
||||
|
||||
$('#insert-upload-service').click(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!jsxc.xmpp.conn || !jsxc.xmpp.conn.connected) {
|
||||
console.warn('Not connected to any XMPP server.');
|
||||
return;
|
||||
}
|
||||
|
||||
var options = jsxc.options.get('httpUpload') || {};
|
||||
|
||||
var services = $('[name="externalServices[]"]').map(function() {
|
||||
var inputField = $(this);
|
||||
|
||||
return inputField.val() || null;
|
||||
});
|
||||
|
||||
if (options.server && services.toArray().indexOf(options.server) < 0) {
|
||||
// insert service
|
||||
var emptyInputFields = $('[name="externalServices[]"]').filter(function() {
|
||||
return $(this).val() === '';
|
||||
});
|
||||
|
||||
var targetInputField;
|
||||
|
||||
if (emptyInputFields.length === 0) {
|
||||
$(this).parents('.form-group').find('.add-input').click();
|
||||
targetInputField = $('[name="externalServices[]"]').last();
|
||||
} else {
|
||||
targetInputField = $(emptyInputFields[0]);
|
||||
}
|
||||
|
||||
targetInputField.val(options.server);
|
||||
}
|
||||
});
|
||||
|
||||
$('#ojsxc input[readonly]').focus(function() {
|
||||
if (typeof this.select === 'function') {
|
||||
this.select();
|
||||
}
|
||||
});
|
||||
|
||||
$('#ojsxc-register').click(function() {
|
||||
var el = $(this);
|
||||
var msgEl = el.parents('.ojsxc-managed').find('.msg');
|
||||
var promotionCode = $('#ojsxc-managed-promotion-code').val();
|
||||
|
||||
if (promotionCode.length > 0 && !/^[0-9a-z]+$/i.test(promotionCode)) {
|
||||
msgEl.addClass('jsxc_fail');
|
||||
msgEl.text('Your promotion code is invalid.');
|
||||
|
||||
$('#ojsxc-managed-promotion-code').one('input', function() {
|
||||
msgEl.removeClass('jsxc_fail');
|
||||
msgEl.text('');
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
el.prop('disabled', 'disabled');
|
||||
el.val(el.attr('data-toggle-value'));
|
||||
el.addClass('jsxc-loading');
|
||||
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/managedServer/register'),
|
||||
data: {
|
||||
promotionCode: promotionCode
|
||||
}
|
||||
}).always(function(responseJSON) {
|
||||
el.removeClass('jsxc-loading');
|
||||
|
||||
if (responseJSON && responseJSON.result === 'success') {
|
||||
$('.ojsxc-managed-registration').hide();
|
||||
|
||||
msgEl.addClass('jsxc_success');
|
||||
msgEl.text('Congratulations! You got your own XMPP server. Please log out and in again.');
|
||||
|
||||
var submitEl = $('#ojsxc input[type="submit"]');
|
||||
submitEl.prop('disabled', 'disabled');
|
||||
submitEl.val('Please reload this page to continue');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseJSON.responseJSON) {
|
||||
responseJSON = responseJSON.responseJSON;
|
||||
}
|
||||
|
||||
var errorMsg = (responseJSON && responseJSON.data) ? responseJSON.data.msg : 'unknown error';
|
||||
|
||||
msgEl.addClass('jsxc_fail');
|
||||
msgEl.append($('<span>').text('Sorry we couldn\'t complete your registration.'));
|
||||
msgEl.append($('<br><br>'));
|
||||
msgEl.append($('<span>').text(errorMsg));
|
||||
msgEl.append($('<br><br>'));
|
||||
msgEl.append($('<span>').html('Please report this to our <a href="https://jsxc.ch/managed-issue-tracker" target="_blank">issue tracker</a> and mention the request id ' + responseJSON.data.requestId + '.'));
|
||||
|
||||
el.val('Registration failed');
|
||||
});
|
||||
});
|
||||
|
||||
$('.ojsxc-refresh-registration').click(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var msgEl = $(this).parents('.msg');
|
||||
|
||||
msgEl.removeClass('jsxc_success');
|
||||
msgEl.empty();
|
||||
|
||||
$('.ojsxc-managed-registration').show();
|
||||
});
|
||||
|
||||
$('#ojsxc-legal, #ojsxc-dp').on('change', function() {
|
||||
$('#ojsxc-register').prop('disabled', !$('#ojsxc-legal').prop('checked') || !$('#ojsxc-dp').prop('checked'));
|
||||
})
|
||||
});
|
|
@ -1,71 +0,0 @@
|
|||
/* global OC */
|
||||
|
||||
(function($) {
|
||||
"use strict";
|
||||
|
||||
$(function() {
|
||||
$('#ojsxc-settings [name="loginFormEnable"]').change(function() {
|
||||
var loginFormData = {
|
||||
enable: $(this).val().match(/^true|false$/) ? JSON.parse($(this).val()) : null
|
||||
};
|
||||
|
||||
if (jsxc.bid) {
|
||||
var options = jsxc.storage.getUserItem('options');
|
||||
|
||||
if (loginFormData.enable === null && options.loginForm) {
|
||||
delete options.loginForm.enable;
|
||||
|
||||
jsxc.storage.setUserItem('options', options);
|
||||
} else {
|
||||
loginFormData = $.extend(jsxc.options.get('loginForm'), loginFormData);
|
||||
|
||||
jsxc.options.set('loginForm', loginFormData);
|
||||
}
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/settings/user'),
|
||||
data: {
|
||||
loginForm: loginFormData
|
||||
},
|
||||
success: function(data) {
|
||||
if (data && data.status === 'success') {
|
||||
jsxc.debug('loginFormEnable saved.');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function savePersonalSettings() {
|
||||
if (OC.PasswordConfirmation && OC.PasswordConfirmation.requiresPasswordConfirmation()) {
|
||||
OC.PasswordConfirmation.requirePasswordConfirmation(savePersonalSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
var post = $('#ojsxc').serialize();
|
||||
|
||||
$('#ojsxc .msg').html('<div>');
|
||||
var status = $('#ojsxc .msg div');
|
||||
status.html('<img src="' + jsxc.options.root + '/img/loading.gif" alt="wait" width="16px" height="16px" /> Saving...');
|
||||
|
||||
$.post(OC.generateUrl('apps/ojsxc/settings/user'), post, function(data) {
|
||||
if (data && data.status === 'success') {
|
||||
status.addClass('jsxc_success').text('Settings saved. Please log out and in again.');
|
||||
} else {
|
||||
status.addClass('jsxc_fail').text('Error!');
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
status.hide('slow');
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
$('#ojsxc').submit(function(ev) {
|
||||
ev.preventDefault()
|
||||
|
||||
savePersonalSettings();
|
||||
})
|
||||
});
|
||||
}(jQuery));
|
39
package.json
39
package.json
|
@ -10,23 +10,28 @@
|
|||
"type": "git",
|
||||
"url": "https://github.com/jsxc/jsxc.nextcloud"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack --mode production",
|
||||
"dev": "mkdir node_modules/jsxc/dist/; webpack --watch --mode development --config webpack.config.js",
|
||||
"jsxc": "cd node_modules/jsxc/ && webpack --watch --mode development",
|
||||
"fix-typescript-format": "node_modules/.bin/tsfmt -r",
|
||||
"checking-typescript-format": "node_modules/.bin/tsfmt --verify"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~1.0.1",
|
||||
"grunt-autoprefixer": "^3.0.4",
|
||||
"grunt-banner": "~0.6.0",
|
||||
"grunt-cli": "~1.2.0",
|
||||
"grunt-contrib-clean": "~1.1.0",
|
||||
"grunt-contrib-compress": "^1.4.3",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-jshint": "~1.1.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-data-uri": "^0.3.0",
|
||||
"grunt-exec": "^2.0.0",
|
||||
"grunt-jsbeautifier": "^0.2.13",
|
||||
"grunt-prettysass": "^0.2.3",
|
||||
"grunt-sass": "2.0.0",
|
||||
"grunt-search": "^0.1.6",
|
||||
"grunt-text-replace": "~0.4.0",
|
||||
"node-sass": "4.5.3"
|
||||
"@types/jquery": "^3.3.6",
|
||||
"clean-webpack-plugin": "^0.1.19",
|
||||
"copy-webpack-plugin": "^4.5.3",
|
||||
"css-loader": "^1.0.0",
|
||||
"mini-css-extract-plugin": "^0.4.4",
|
||||
"node-sass": "4.9.4",
|
||||
"sass-loader": "^7.1.0",
|
||||
"ts-loader": "^5.1.0",
|
||||
"ts-node": "^7.0.1",
|
||||
"typescript": "^3.1.3",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack-cli": "^3.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript-formatter": "^7.2.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
#jsxc_roster {
|
||||
#jsxc-roster {
|
||||
padding-top: 5px; // NC >= 14
|
||||
top: 45px;
|
||||
z-index: 1500;
|
||||
|
@ -22,10 +22,14 @@
|
|||
width: 8px;
|
||||
}
|
||||
|
||||
#jsxc_windowList, #jsxc_windowListSB {
|
||||
#jsxc-window-list, #jsxc_windowListSB {
|
||||
z-index: 1500;
|
||||
}
|
||||
|
||||
.jsxc-dialog-wrapper {
|
||||
z-index: 1600;
|
||||
}
|
||||
|
||||
#jsxc {
|
||||
padding: 20px;
|
||||
|
||||
|
@ -298,4 +302,4 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
@import "js/jsxc/scss/colors";
|
||||
@import "js/jsxc/scss/dep";
|
||||
|
||||
//fonts
|
||||
$font_sans: Arial, sans-serif;
|
||||
$font_serif: serif;
|
||||
|
@ -9,15 +6,13 @@ $window_bar_bg: #0082c9;
|
|||
$window_bar_color: #c0dff1;
|
||||
$window_bar_color_hover: #fff;
|
||||
|
||||
@import "js/jsxc/scss/modules";
|
||||
@import "js/jsxc/scss/buddylist";
|
||||
@import "js/jsxc/scss/state";
|
||||
@import "js/jsxc/scss/emoticons";
|
||||
@import "js/jsxc/scss/roster";
|
||||
@import "js/jsxc/scss/window";
|
||||
@import "js/jsxc/scss/muc";
|
||||
@import "oc";
|
||||
|
||||
@import "js/jsxc/scss/_jsxc.scss";
|
||||
@import "js/jsxc/scss/webrtc";
|
||||
.jsxc-bar--window, .jsxc-window-item .jsxc-memberlist {
|
||||
background-color: $window_bar_bg;
|
||||
color: $window_bar_color;
|
||||
|
||||
@import "_oc";
|
||||
:hover {
|
||||
// color: $window_bar_color_hover;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { DEPENDENCIES } from './CONST'
|
||||
|
||||
export default class Bootstrap {
|
||||
public static start() {
|
||||
Bootstrap.checkDependencies();
|
||||
Bootstrap.checkFrame();
|
||||
Bootstrap.checkSpecialPage();
|
||||
}
|
||||
|
||||
private static checkDependencies() {
|
||||
for (let dependency of DEPENDENCIES) {
|
||||
if (typeof (<any>window)[dependency] === 'undefined') {
|
||||
throw `Dependency "${dependency}" is missing.`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static checkFrame() {
|
||||
if (window.parent && window !== window.parent) {
|
||||
throw `Abort, because we are running inside a frame.`;
|
||||
}
|
||||
}
|
||||
|
||||
private static checkSpecialPage() {
|
||||
if (/^(\/index.php)?\/s\//.test(location.pathname)) {
|
||||
throw `Abort, because we dont want to start chat on public shares.`;
|
||||
}
|
||||
|
||||
if (OC.generateUrl('login/flow') === window.location.pathname) {
|
||||
throw `Abort, because chat is not needed on flow login.`;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export const SERVER_TYPE = {
|
||||
INTERNAL: 0,
|
||||
EXTERNAL: 1,
|
||||
MANAGED: 2
|
||||
};
|
||||
|
||||
export const DEPENDENCIES = ['jsxc', 'oc_config', 'oc_appswebroots', 'OC'];
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
export default function injectChatIcon() {
|
||||
var div = $('<div/>');
|
||||
|
||||
div.addClass('jsxc_chatIcon');
|
||||
div.click(function() {
|
||||
jsxc.toggleRoster();
|
||||
});
|
||||
|
||||
$('#header form.searchbox').after(div);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import Storage from './Storage'
|
||||
|
||||
function addChatSubmitButton(formElement: JQuery<any>) {
|
||||
let storage = Storage.get();
|
||||
let defaultEnable = OJSXC_CONFIG.defaultLoginFormEnable;
|
||||
let submitWrapperElement = $('<div>');
|
||||
submitWrapperElement.attr('id', 'jsxc-submit-wrapper');
|
||||
|
||||
let submitElement = $('<input>');
|
||||
submitElement.attr({
|
||||
type: 'button',
|
||||
id: 'jsxc-submit',
|
||||
});
|
||||
submitElement.addClass('login primary');
|
||||
if (defaultEnable) {
|
||||
submitElement.val('Log_in_without_chat'); //@TODO translate
|
||||
submitElement.click(function() {
|
||||
// submit form without login
|
||||
});
|
||||
} else {
|
||||
submitElement.val('Log_in_with_chat');
|
||||
submitElement.click(function() {
|
||||
let forceLoginFormEnable = true;
|
||||
formElement.submit();
|
||||
});
|
||||
}
|
||||
|
||||
submitWrapperElement.append(submitElement);
|
||||
$('.login-additional').prepend(submitWrapperElement);
|
||||
|
||||
$('#lost-password').mouseup(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
submitWrapperElement.slideUp().fadeOut();
|
||||
});
|
||||
$('#lost-password-back').mouseup(function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
submitWrapperElement.slideDown().fadeIn();
|
||||
});
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
function observeContactsMenu() {
|
||||
var target = document.getElementById('contactsmenu');
|
||||
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.target.id !== 'contactsmenu-contacts') {
|
||||
return;
|
||||
}
|
||||
|
||||
$(mutation.target).find('[href^="xmpp:"]').addClass('jsxc_statusIndicator');
|
||||
|
||||
$(mutation.target).find('.contact').each(function () {
|
||||
updateContactItem($(this));
|
||||
});
|
||||
|
||||
jsxc.gui.detectUriScheme(mutation.target);
|
||||
});
|
||||
});
|
||||
|
||||
var config = {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
observer.observe(target, config);
|
||||
}
|
||||
|
||||
function updateContactItem(contactElement) {
|
||||
var xmppAddresses = contactElement.find('[href^="xmpp:"]').map(function () {
|
||||
return $(this).attr('href').replace(/^xmpp:/, '');
|
||||
});
|
||||
|
||||
if (xmppAddresses.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lastMessages = [];
|
||||
var highestPresent = jsxc.CONST.STATUS.indexOf('offline');
|
||||
var highestPresentBid = xmppAddresses.get(0);
|
||||
|
||||
xmppAddresses.each(function (index, bid) {
|
||||
var lastMsg = jsxc.getLastMsg(bid);
|
||||
|
||||
if (lastMsg) {
|
||||
lastMessages.push(lastMsg);
|
||||
}
|
||||
|
||||
var data = jsxc.storage.getUserItem('buddy', bid) || {};
|
||||
|
||||
if (data.status > highestPresent) {
|
||||
highestPresent = data.status;
|
||||
highestPresentBid = bid;
|
||||
}
|
||||
});
|
||||
|
||||
var latestMsg = {
|
||||
date: 0
|
||||
};
|
||||
$(lastMessages).each(function (index, msg) {
|
||||
if (msg.date > latestMsg.date) {
|
||||
latestMsg = msg;
|
||||
}
|
||||
});
|
||||
|
||||
if (latestMsg.date > 0) {
|
||||
// replace emoticons from XEP-0038 and pidgin with shortnames
|
||||
$.each(jsxc.gui.emotions, function (i, val) {
|
||||
latestMsg.text = latestMsg.text.replace(val[2], ':' + val[1] + ':');
|
||||
});
|
||||
|
||||
// translate shortnames to images
|
||||
latestMsg.text = jsxc.gui.shortnameToImage(latestMsg.text);
|
||||
|
||||
contactElement.find('.last-message').html(latestMsg.text);
|
||||
}
|
||||
|
||||
if (highestPresent > 0) {
|
||||
var status = jsxc.CONST.STATUS[highestPresent];
|
||||
|
||||
contactElement.removeClass('jsxc_' + jsxc.CONST.STATUS.join(' jsxc_')).addClass('jsxc_' + status);
|
||||
}
|
||||
|
||||
if (highestPresentBid) {
|
||||
contactElement.find('.avatar').click(function () {
|
||||
jsxc.gui.queryActions.message(highestPresentBid);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import Storage from "./Storage";
|
||||
import { IJID } from "jsxc/src/JID.interface";
|
||||
|
||||
enum Presence {
|
||||
online,
|
||||
chat,
|
||||
away,
|
||||
xa,
|
||||
dnd,
|
||||
offline
|
||||
}
|
||||
|
||||
interface Avatar {
|
||||
username: string,
|
||||
type: 'url' | 'placeholder',
|
||||
displayname?: string,
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export default function defaultAvatar(elements, jid: IJID, name: string, presence) {
|
||||
let storage = Storage.get();
|
||||
|
||||
let adminSettings = storage.getItem('adminSettings') || {};
|
||||
|
||||
$(elements).each(function() {
|
||||
let element = $(this);
|
||||
|
||||
if (element.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let size = <number>element.width();
|
||||
let username = jid.node;
|
||||
let key = username + '@' + size;
|
||||
let cache = storage.getItem('avatar:' + key);
|
||||
let isExternalUser = jid.domain !== adminSettings.xmppDomain;
|
||||
|
||||
if (cache) {
|
||||
displayAvatar(element, cache);
|
||||
} else if (isExternalUser || presence === Presence.offline) {
|
||||
setPlaceholder(element, username);
|
||||
} else {
|
||||
requestAvatar(element, username, size);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function requestAvatar(element: JQuery<any>, username: string, size: number) {
|
||||
let url = getAvatarUrl(username, size);
|
||||
|
||||
$.get(url, function(result) {
|
||||
let avatar: Avatar = {
|
||||
username: username,
|
||||
type: typeof result === 'string' ? 'url' : 'placeholder',
|
||||
displayname: result.data && result.data.displayname ? result.data.displayname : undefined,
|
||||
url: typeof result === 'string' ? result : undefined,
|
||||
};
|
||||
|
||||
displayAvatar(element, avatar);
|
||||
|
||||
Storage.get().setItem('avatar:' + username + '@' + size, avatar);
|
||||
});
|
||||
}
|
||||
|
||||
function displayAvatar(element, avatar: Avatar) {
|
||||
if (avatar.type === 'url') {
|
||||
element.css('backgroundImage', 'url(' + avatar.url + ')');
|
||||
element.text('');
|
||||
} else {
|
||||
setPlaceholder(element, avatar.username, avatar.displayname);
|
||||
}
|
||||
}
|
||||
|
||||
function getAvatarUrl(username: string, size: number) {
|
||||
return OC.generateUrl('/avatar/' + encodeURIComponent(username) + '/' + size + '?requesttoken={requesttoken}', {
|
||||
user: username,
|
||||
size: size,
|
||||
requesttoken: oc_requesttoken
|
||||
})
|
||||
}
|
||||
|
||||
function setPlaceholder(element, username: string, displayname?: string) {
|
||||
(<any>element).imageplaceholder(username, displayname);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
export default class FormWatcher {
|
||||
public static callback(username: string, password: string) {
|
||||
return new Promise(resolve => {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/settings'),
|
||||
data: {
|
||||
username: username,
|
||||
password: password
|
||||
},
|
||||
success: response => FormWatcher.success(response, resolve),
|
||||
error: xhr => FormWatcher.error(xhr, resolve),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static success(response, resolve) {
|
||||
if (response.result !== 'success') {
|
||||
resolve(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let xmpp = response.data.xmpp || {};
|
||||
|
||||
if (!xmpp.url) {
|
||||
resolve(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
resolve({
|
||||
xmpp: {
|
||||
url: xmpp.url,
|
||||
domain: xmpp.domain,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static error(xhr, resolve) {
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
throw 'Error message: ' + xhr.responseJSON.message;
|
||||
}
|
||||
|
||||
if (xhr.status === 412) {
|
||||
console.log('Refresh page to get a new CSRF token');
|
||||
|
||||
window.location.href = window.location.href;
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
$(document).on('click', '#jsxc_roster p', function () {
|
||||
if (jsxc.storage.getItem('serverType') === serverTypes.INTERNAL) {
|
||||
startInternalBackend();
|
||||
}
|
||||
});
|
||||
|
||||
function startInternalBackend() {
|
||||
var currentUser = OC.currentUser;
|
||||
|
||||
if (!currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsxc.bid = currentUser.toLowerCase() + '@' + window.location.host;
|
||||
|
||||
jsxc.options.set('xmpp', {
|
||||
url: OC.generateUrl('apps/ojsxc/http-bind')
|
||||
});
|
||||
|
||||
$(document).one('attached.jsxc', function () {
|
||||
if (jsxc.options.get('loginForm').startMinimized !== true) {
|
||||
jsxc.gui.roster.toggle(jsxc.CONST.SHOWN);
|
||||
}
|
||||
});
|
||||
|
||||
jsxc.start(jsxc.bid + '/internal', 'internal', '123456');
|
||||
}
|
||||
|
||||
|
||||
if (jsxc.storage.getItem('serverType') === serverTypes.INTERNAL) {
|
||||
jsxc.gui.showLoginBox = function() {};
|
||||
}
|
||||
|
||||
$(document).on('stateChange.jsxc', function _handler(event, state) {
|
||||
if (state === jsxc.CONST.STATE.SUSPEND) {
|
||||
/**
|
||||
* The first time we go into suspend mode we check if we are using the internal backend.
|
||||
* If this is the case and the user explicitly press the "login_without_chat" button when logging
|
||||
* into Nextcloud we know we are using another authentication mechanism (like SAML/SSO) and thus have
|
||||
* to manually start the connection.
|
||||
*/
|
||||
var chatDisabledByUser = jsxc.storage.getUserItem('forcedLogout') || jsxc.storage.getItem('login_without_chat');
|
||||
$(document).off('stateChange.jsxc', _handler);
|
||||
if (jsxc.storage.getItem('serverType') === null) {
|
||||
$.ajax({
|
||||
url: OC.generateUrl('apps/ojsxc/settings/servertype'),
|
||||
success: function(data) {
|
||||
jsxc.storage.setItem('serverType', serverTypes[data.serverType.toUpperCase()]);
|
||||
|
||||
if (data.serverType === 'internal' && !chatDisabledByUser) {
|
||||
jsxc.gui.showLoginBox = function() {};
|
||||
startInternalBackend();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (jsxc.storage.getItem('serverType') === serverTypes.INTERNAL && !chatDisabledByUser) {
|
||||
jsxc.gui.showLoginBox = function() {};
|
||||
startInternalBackend();
|
||||
}
|
||||
} else if (state === jsxc.CONST.STATE.READY) {
|
||||
// if JSXC is ready this means we successfully connected and thus don't have to listen to the suspend state
|
||||
$(document).off('stateChange.jsxc', _handler);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
import { SERVER_TYPE } from "./CONST";
|
||||
import Storage from "./Storage";
|
||||
|
||||
export function loadSettings(username, password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/settings'),
|
||||
data: {
|
||||
username: username,
|
||||
password: password
|
||||
},
|
||||
success: (d) => resolve(d),
|
||||
error: xhr => reject(xhr),
|
||||
});
|
||||
})
|
||||
.then(handleResponse)
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function handleResponse(response) {
|
||||
if (response.result !== 'success' || !response.data) {
|
||||
throw 'Received unsuccessful response.';
|
||||
}
|
||||
|
||||
let data = response.data;
|
||||
let serverType = SERVER_TYPE[data.serverType.toUpperCase()];
|
||||
let xmpp = data.xmpp || {};
|
||||
|
||||
Storage.get().setItem('serverType', serverType);
|
||||
|
||||
if (serverType !== SERVER_TYPE.INTERNAL && xmpp.url) {
|
||||
|
||||
// if (forceLoginFormEnable) {
|
||||
// response.data.loginForm.enable = true;
|
||||
// }
|
||||
|
||||
return {
|
||||
xmpp: {
|
||||
url: xmpp.url,
|
||||
domain: xmpp.domain,
|
||||
}
|
||||
};
|
||||
} else if (serverType === SERVER_TYPE.INTERNAL) {
|
||||
// var node = username || OC.currentUser;
|
||||
// jsxc.bid = node.toLowerCase() + '@' + window.location.host;
|
||||
|
||||
// jsxc.options.set('adminSettings', response.data.adminSettings);
|
||||
|
||||
// if (response.data.loginForm) {
|
||||
// jsxc.options.set('loginForm', {
|
||||
// startMinimized: response.data.loginForm.startMinimized
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
Storage.get().removeItem('serverType');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleError(xhr) {
|
||||
console.warn('XHR error on getSettings.php');
|
||||
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
console.log('Error message: ' + xhr.responseJSON.message);
|
||||
}
|
||||
|
||||
if (xhr.status === 412) {
|
||||
console.log('Refresh page to get a new CSRF token');
|
||||
|
||||
window.location.href = window.location.href;
|
||||
|
||||
return new Promise(() => { });
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
function saveSettinsPermanent(data, cb) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: OC.generateUrl('apps/ojsxc/settings/user'),
|
||||
data: data,
|
||||
success: function(data) {
|
||||
cb(data && data.status === 'success');
|
||||
},
|
||||
error: function() {
|
||||
cb(false);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
const PREFIX = 'ojsxc:';
|
||||
|
||||
const BACKEND = localStorage;
|
||||
|
||||
export default class Storage {
|
||||
|
||||
private static instance;
|
||||
|
||||
public static get(): Storage {
|
||||
if (!Storage.instance) {
|
||||
Storage.instance = new Storage();
|
||||
}
|
||||
|
||||
return Storage.instance;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
|
||||
}
|
||||
|
||||
public setItem(key: string, value: any): void {
|
||||
try {
|
||||
value = JSON.stringify(value);
|
||||
} catch (err) {
|
||||
console.warn('Error while stringifing', err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BACKEND.setItem(PREFIX + key, value);
|
||||
}
|
||||
|
||||
public getItem(key: string): any {
|
||||
let value = BACKEND.getItem(PREFIX + key);
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public removeItem(key: string): void {
|
||||
BACKEND.removeItem(PREFIX + key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
function getUsers(search, cb) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: OC.generateUrl('apps/ojsxc/settings/users'),
|
||||
data: {
|
||||
search: search
|
||||
},
|
||||
success: cb,
|
||||
error: function() {
|
||||
console.warn('XHR error on getUsers.php');
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// import jsxc from 'jsxc/src';
|
||||
import FormWatcher from './FormWatcher';
|
||||
import Bootstrap from './Bootstrap';
|
||||
import { loadSettings } from './SettingsLoader';
|
||||
import injectChatIcon from './ChatIconInjector';
|
||||
|
||||
(function() {
|
||||
try {
|
||||
Bootstrap.start();
|
||||
} catch (err) {
|
||||
console.warn('Abort JSXC', err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let numberOfCachedAccounts = jsxc.init({
|
||||
loadSettings: loadSettings,
|
||||
});
|
||||
|
||||
if (numberOfCachedAccounts === 0 && oc_current_user) {
|
||||
jsxc.start();
|
||||
}
|
||||
|
||||
injectChatIcon();
|
||||
|
||||
let formElement = $('#body-login form');
|
||||
let usernameElement = $('#user');
|
||||
let passwordElement = $('#password');
|
||||
|
||||
if (formElement.length > 0) {
|
||||
jsxc.watchForm(formElement, usernameElement, passwordElement, FormWatcher.callback);
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,18 @@
|
|||
import Storage from '../Storage'
|
||||
|
||||
export default class Viewport {
|
||||
public static getSize(): { width: number, height: number } {
|
||||
var w = <number>$(window).width() - <number>$('#jsxc_windowListSB').width();
|
||||
var h = <number>$(window).height() - <number>$('#header').height() - 10;
|
||||
|
||||
//@TODO
|
||||
if (Storage.get().getItem('roster') === 'shown') {
|
||||
w -= <number>$('#jsxc-roster').outerWidth(true);
|
||||
}
|
||||
|
||||
return {
|
||||
width: w,
|
||||
height: h
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"include": [
|
||||
"./ts/**/*.ts",
|
||||
"./*.d.ts"
|
||||
],
|
||||
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist",
|
||||
"target": "es5",
|
||||
"lib": ["dom", "es2015", "es2015.promise", "es5"]
|
||||
}
|
||||
|
||||
// "references": [
|
||||
// { "path": "./node_modules/jsxc" }
|
||||
// ]
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/* jshint node:true */
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
|
||||
const DESTINATION_DIR = 'dist/';
|
||||
|
||||
const fileLoader = {
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[path][name]-[sha1:hash:hex:8].[ext]',
|
||||
outputPath: 'assets/'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
entry: ['./scss/jsxc.oc.scss', './ts/index.ts'],
|
||||
output: {
|
||||
filename: 'js/bundle.js',
|
||||
path: path.resolve(__dirname, DESTINATION_DIR),
|
||||
publicPath: DESTINATION_DIR,
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
minSize: 10,
|
||||
cacheGroups: {
|
||||
styles: {
|
||||
name: 'styles',
|
||||
test: /\.css$/,
|
||||
chunks: 'all',
|
||||
enforce: true
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
node: {
|
||||
fs: 'empty'
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.hbs$/,
|
||||
loader: 'handlebars-loader',
|
||||
exclude: /node_modules/,
|
||||
options: {
|
||||
helperDirs: [
|
||||
path.resolve(__dirname, 'template', 'helpers')
|
||||
],
|
||||
partialDirs: [
|
||||
path.resolve(__dirname, 'template', 'partials')
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
'css-loader?importLoaders=1',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(sass|scss)$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader, {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
url: false
|
||||
}
|
||||
},
|
||||
'sass-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /.*\.(svg|png|jpg|gif|mp3|wav)$/,
|
||||
use: [fileLoader]
|
||||
},
|
||||
{
|
||||
test: /.*\.(js)$/,
|
||||
resourceQuery: /path/,
|
||||
use: [fileLoader]
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".hbs"],
|
||||
alias: {}
|
||||
},
|
||||
externals: {
|
||||
'jquery': 'jQuery',
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'css/bundle.css',
|
||||
}),
|
||||
new CleanWebpackPlugin([DESTINATION_DIR]),
|
||||
new CopyWebpackPlugin([{
|
||||
from: 'node_modules/jsxc/dist/',
|
||||
to: 'js/jsxc/'
|
||||
}, {
|
||||
from: 'appinfo/',
|
||||
to: 'appinfo/'
|
||||
}, {
|
||||
from: 'img/',
|
||||
to: 'img/'
|
||||
}, {
|
||||
from: 'templates/',
|
||||
to: 'templates/'
|
||||
}, {
|
||||
from: 'lib/',
|
||||
to: 'lib/'
|
||||
}, {
|
||||
from: 'settings/',
|
||||
to: 'settings/'
|
||||
}, 'LICENSE']),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
options: {
|
||||
handlebarsLoader: {}
|
||||
}
|
||||
}),
|
||||
// new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, new RegExp(MOMENTJS_LOCALES.join('|'))),
|
||||
// new BundleAnalyzerPlugin(),
|
||||
]
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче