xmpp.chapril.org-conversejs/src/converse-dragresize.js
JC Brand 910e9bddcd Refactoring in MUC
What started as an attempt to fix a bug in parseXUserElement, turned into
another large refactoring of MUC code, and it's not clear how to break this up
into multiple atomic commits. So I'm just pushing it all.

At least there are two new tests added to the suite.
2016-12-02 18:41:05 +01:00

310 lines
13 KiB
JavaScript

// Converse.js (A browser based XMPP chat client)
// http://conversejs.org
//
// Copyright (c) 2012-2016, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
//
/*global define, window */
(function (root, factory) {
define("converse-dragresize", [
"converse-core",
"converse-api",
"tpl!dragresize",
"converse-chatview",
"converse-muc", // XXX: would like to remove this
"converse-controlbox"
], factory);
}(this, function (converse, converse_api, tpl_dragresize) {
"use strict";
var $ = converse_api.env.jQuery,
_ = converse_api.env._;
converse.templates.dragresize = tpl_dragresize;
converse_api.plugins.add('converse-dragresize', {
overrides: {
// Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
registerGlobalEventHandlers: function () {
$(document).on('mousemove', function (ev) {
if (!this.resizing || !this.allow_dragresize) { return true; }
ev.preventDefault();
this.resizing.chatbox.resizeChatBox(ev);
}.bind(this));
$(document).on('mouseup', function (ev) {
if (!this.resizing || !this.allow_dragresize) { return true; }
ev.preventDefault();
var height = this.applyDragResistance(
this.resizing.chatbox.height,
this.resizing.chatbox.model.get('default_height')
);
var width = this.applyDragResistance(
this.resizing.chatbox.width,
this.resizing.chatbox.model.get('default_width')
);
if (this.connection.connected) {
this.resizing.chatbox.model.save({'height': height});
this.resizing.chatbox.model.save({'width': width});
} else {
this.resizing.chatbox.model.set({'height': height});
this.resizing.chatbox.model.set({'width': width});
}
this.resizing = null;
}.bind(this));
return this.__super__.registerGlobalEventHandlers.apply(this, arguments);
},
ChatBox: {
initialize: function () {
var result = this.__super__.initialize.apply(this, arguments),
height = this.get('height'), width = this.get('width'),
save = this.get('id') === 'controlbox' ? this.set.bind(this) : this.save.bind(this);
save({
'height': converse.applyDragResistance(height, this.get('default_height')),
'width': converse.applyDragResistance(width, this.get('default_width')),
});
return result;
}
},
ChatBoxView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize: function () {
$(window).on('resize', _.debounce(this.setDimensions.bind(this), 100));
this.__super__.initialize.apply(this, arguments);
},
render: function () {
var result = this.__super__.render.apply(this, arguments);
this.setWidth();
return result;
},
setWidth: function () {
// If a custom width is applied (due to drag-resizing),
// then we need to set the width of the .chatbox element as well.
if (this.model.get('width')) {
this.$el.css('width', this.model.get('width'));
}
},
_show: function () {
this.initDragResize().setDimensions();
this.__super__._show.apply(this, arguments);
},
initDragResize: function () {
/* Determine and store the default box size.
* We need this information for the drag-resizing feature.
*/
var $flyout = this.$el.find('.box-flyout');
if (typeof this.model.get('height') === 'undefined') {
var height = $flyout.height();
var width = $flyout.width();
this.model.set('height', height);
this.model.set('default_height', height);
this.model.set('width', width);
this.model.set('default_width', width);
}
var min_width = $flyout.css('min-width');
var min_height = $flyout.css('min-height');
this.model.set('min_width', min_width.endsWith('px') ? Number(min_width.replace(/px$/, '')) :0);
this.model.set('min_height', min_height.endsWith('px') ? Number(min_height.replace(/px$/, '')) :0);
// Initialize last known mouse position
this.prev_pageY = 0;
this.prev_pageX = 0;
if (converse.connection.connected) {
this.height = this.model.get('height');
this.width = this.model.get('width');
}
return this;
},
setDimensions: function () {
// Make sure the chat box has the right height and width.
this.adjustToViewport();
this.setChatBoxHeight(this.model.get('height'));
this.setChatBoxWidth(this.model.get('width'));
},
setChatBoxHeight: function (height) {
if (height) {
height = converse.applyDragResistance(height, this.model.get('default_height'))+'px';
} else {
height = "";
}
this.$el.children('.box-flyout')[0].style.height = height;
},
setChatBoxWidth: function (width) {
if (width) {
width = converse.applyDragResistance(width, this.model.get('default_width'))+'px';
} else {
width = "";
}
this.$el[0].style.width = width;
this.$el.children('.box-flyout')[0].style.width = width;
},
adjustToViewport: function () {
/* Event handler called when viewport gets resized. We remove
* custom width/height from chat boxes.
*/
var viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
var viewport_height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
if (viewport_width <= 480) {
this.model.set('height', undefined);
this.model.set('width', undefined);
} else if (viewport_width <= this.model.get('width')) {
this.model.set('width', undefined);
} else if (viewport_height <= this.model.get('height')) {
this.model.set('height', undefined);
}
},
onStartVerticalResize: function (ev) {
if (!converse.allow_dragresize) { return true; }
// Record element attributes for mouseMove().
this.height = this.$el.children('.box-flyout').height();
converse.resizing = {
'chatbox': this,
'direction': 'top'
};
this.prev_pageY = ev.pageY;
},
onStartHorizontalResize: function (ev) {
if (!converse.allow_dragresize) { return true; }
this.width = this.$el.children('.box-flyout').width();
converse.resizing = {
'chatbox': this,
'direction': 'left'
};
this.prev_pageX = ev.pageX;
},
onStartDiagonalResize: function (ev) {
this.onStartHorizontalResize(ev);
this.onStartVerticalResize(ev);
converse.resizing.direction = 'topleft';
},
resizeChatBox: function (ev) {
var diff;
if (converse.resizing.direction.indexOf('top') === 0) {
diff = ev.pageY - this.prev_pageY;
if (diff) {
this.height = ((this.height-diff) > (this.model.get('min_height') || 0)) ? (this.height-diff) : this.model.get('min_height');
this.prev_pageY = ev.pageY;
this.setChatBoxHeight(this.height);
}
}
if (converse.resizing.direction.indexOf('left') !== -1) {
diff = this.prev_pageX - ev.pageX;
if (diff) {
this.width = ((this.width+diff) > (this.model.get('min_width') || 0)) ? (this.width+diff) : this.model.get('min_width');
this.prev_pageX = ev.pageX;
this.setChatBoxWidth(this.width);
}
}
}
},
ControlBoxView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize: function () {
$(window).on('resize', _.debounce(this.setDimensions.bind(this), 100));
this.__super__.initialize.apply(this, arguments);
},
renderLoginPanel: function () {
var result = this.__super__.renderLoginPanel.apply(this, arguments);
this.initDragResize().setDimensions();
return result;
},
renderContactsPanel: function () {
var result = this.__super__.renderContactsPanel.apply(this, arguments);
this.initDragResize().setDimensions();
return result;
}
},
ChatRoomView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize: function () {
$(window).on('resize', _.debounce(this.setDimensions.bind(this), 100));
this.__super__.initialize.apply(this, arguments);
},
render: function () {
var result = this.__super__.render.apply(this, arguments);
this.renderDragResizeHandles();
this.setWidth();
return result;
},
renderDragResizeHandles: function () {
var flyout = this.el.querySelector('.box-flyout');
var div = document.createElement('div');
div.innerHTML = converse.templates.dragresize();
flyout.insertBefore(
div.firstChild,
flyout.firstChild
);
}
}
},
initialize: function () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
var converse = this.converse;
this.updateSettings({
allow_dragresize: true,
});
converse.applyDragResistance = function (value, default_value) {
/* This method applies some resistance around the
* default_value. If value is close enough to
* default_value, then default_value is returned instead.
*/
if (typeof value === 'undefined') {
return undefined;
} else if (typeof default_value === 'undefined') {
return value;
}
var resistance = 10;
if ((value !== default_value) &&
(Math.abs(value- default_value) < resistance)) {
return default_value;
}
return value;
};
}
});
}));