Change-Id: I86d48c11bcb3fdda2193df2d00de61d24123467achanges/98/21398/6
parent
803128ea22
commit
6071b43b49
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
case "$1" in
|
||||
*)
|
||||
cp ./dev-config/ngcp-panel_csc.customtt.tt2 /etc/ngcp-config/templates/etc/nginx/sites-available/
|
||||
cp ./dev-config/ngcp-panel_params.customtt.tt2 /etc/ngcp-config/templates/etc/nginx/
|
||||
|
||||
ngcpcfg set /etc/ngcp-config/config.yml www_admin.http_csc.csc_js_enable=yes
|
||||
ngcpcfg set /etc/ngcp-config/config.yml rtcengine.enable=yes
|
||||
ngcpcfg set /etc/ngcp-config/config.yml pbx.enable=yes
|
||||
ngcpcfg set /etc/ngcp-config/config.yml www_admin.privileges.subscriberadmin.subscribers.0=write
|
||||
|
||||
ngcpcfg apply 'csc, pbx, rtcengine'
|
||||
;;
|
||||
esac
|
@ -0,0 +1,229 @@
|
||||
[%
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_hostname';
|
||||
hostname = out;
|
||||
|
||||
argv.host=hostname; argv.type='sip_ext';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_ips_for_host';
|
||||
sip_ext_ips = out;
|
||||
|
||||
IF !sip_ext_ips.size;
|
||||
argv.type='sip_ext';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_ips_for_host';
|
||||
sip_ext_ips = out;
|
||||
END;
|
||||
ext_ip = sip_ext_ips.0;
|
||||
|
||||
argv.type='web_ext';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_ips_for_host';
|
||||
web_ext_ips = out;
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_v6ips_for_host';
|
||||
web_ext_v6ips = out;
|
||||
|
||||
argv.type='web_ext';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_ips_for_host';
|
||||
web_ext_ips = out.merge(web_ext_ips);
|
||||
|
||||
argv.type='web_ext';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_v6ips_for_host';
|
||||
web_ext_v6ips = out.merge(web_ext_v6ips);
|
||||
|
||||
argv.role='mgmt';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/has_role';
|
||||
is_mgmt = out;
|
||||
|
||||
argv.role='proxy'; argv.type='sip_int';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_shared_ips';
|
||||
sip_int_ips = out;
|
||||
IF !sip_int_ips.size;
|
||||
argv.type='sip_int';
|
||||
PROCESS '/usr/lib/ngcp-ngcpcfg/get_all_ips_for_host';
|
||||
sip_int_ips = out;
|
||||
END;
|
||||
-%]
|
||||
[% IF www_admin.enable == 'yes' && is_mgmt -%]
|
||||
[% IF www_admin.http_admin.port != 80 && www_admin.http_csc.port != 80 -%]
|
||||
server {
|
||||
listen [::]:80 ipv6only=off;
|
||||
|
||||
location /handbook {
|
||||
return 301 http://$host:[% www_admin.http_admin.port %]$request_uri;
|
||||
}
|
||||
|
||||
location /login/subscriber {
|
||||
return 301 https://$host:[% www_admin.http_csc.port %]$request_uri;
|
||||
}
|
||||
|
||||
location /login/admin {
|
||||
return 301 https://$host:[% www_admin.http_admin.port %]$request_uri;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host:[% www_admin.http_csc.port %]$request_uri;
|
||||
}
|
||||
}
|
||||
[% END -%]
|
||||
[% IF rtcengine.enable == 'yes' -%]
|
||||
upstream rtc_ws {
|
||||
[% FOREACH ip IN sip_int_ips -%]
|
||||
[% IF ip -%]
|
||||
server [% ip %]:[% rtcengine.port %];
|
||||
[% END -%]
|
||||
[% END -%]
|
||||
}
|
||||
[% END -%]
|
||||
|
||||
server {
|
||||
[% FOREACH ip IN web_ext_ips -%]
|
||||
[% IF ip -%]
|
||||
listen [% ip %]:[% www_admin.http_csc.port %];
|
||||
[% END -%]
|
||||
[% END -%]
|
||||
[% FOREACH ip IN web_ext_v6ips -%]
|
||||
[% IF ip -%]
|
||||
listen [[% ip %]]:[% www_admin.http_csc.port %];
|
||||
[% END -%]
|
||||
[% END -%]
|
||||
server_name [% www_admin.http_csc.servername.remove('\"') %];
|
||||
|
||||
ssl_certificate [% www_admin.http_csc.sslcertfile %];
|
||||
ssl_certificate_key [% www_admin.http_csc.sslcertkeyfile %];
|
||||
include /etc/nginx/ssl_params;
|
||||
client_max_body_size [% www_admin.filesize_limit ? www_admin.filesize_limit : "128M"%];
|
||||
|
||||
location ~* /login(/)?$ {
|
||||
return 301 /login/subscriber;
|
||||
}
|
||||
|
||||
location /login/admin {
|
||||
return 301 https://$host:[% www_admin.http_admin.port %]$request_uri;
|
||||
}
|
||||
|
||||
location /favicon.ico {
|
||||
alias /usr/share/ngcp-panel/static/favicon.ico;
|
||||
}
|
||||
|
||||
location /static {
|
||||
root /usr/share/ngcp-panel;
|
||||
}
|
||||
|
||||
[% IF rtcengine.enable == 'yes' -%]
|
||||
location ~* /rtc/api(/)?$ {
|
||||
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
|
||||
add_header 'Access-Control-Max-Age' '1728000' always;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
|
||||
add_header 'Content-Length' '0' always;
|
||||
return 204;
|
||||
}
|
||||
|
||||
### Set proxy ####
|
||||
proxy_max_temp_file_size 0;
|
||||
proxy_connect_timeout 43200000;
|
||||
proxy_send_timeout 43200000;
|
||||
proxy_read_timeout 43200000;
|
||||
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 4 32k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
|
||||
### Set headers ####
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Accept-Encoding "";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Server-IP $server_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
add_header Front-End-Https on;
|
||||
|
||||
proxy_pass http://rtc_ws;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
location /rtc/files {
|
||||
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
|
||||
add_header 'Access-Control-Max-Age' '1728000' always;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
|
||||
add_header 'Content-Length' '0' always;
|
||||
return 204;
|
||||
}
|
||||
|
||||
rewrite /rtc/files/(.*) /$1 break;
|
||||
index index.html;
|
||||
root /usr/share/ngcp-rtcengine/public/;
|
||||
}
|
||||
[% END -%]
|
||||
[% IF rtcengine.expose_provisioning_api == 'yes' %]
|
||||
location /rtc/prov/ {
|
||||
rewrite /rtc/prov/(.*) /$1 break;
|
||||
proxy_pass http://rtc_ws;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
[% END %]
|
||||
|
||||
|
||||
location /api {
|
||||
include /etc/nginx/ngcp-panel_params;
|
||||
# fastcgi_param SSL_CLIENT_CERT $ssl_client_raw_cert;
|
||||
# fastcgi_param SSL_CLIENT_M_SERIAL $ssl_client_serial;
|
||||
# fastcgi_param SSL_CLIENT_M_DN $ssl_client_s_dn;
|
||||
fastcgi_param NGCP_API_REALM "subscriber";
|
||||
proxy_buffers 8 1024k;
|
||||
proxy_buffer_size 1024k;
|
||||
proxy_busy_buffers_size 1024k;
|
||||
fastcgi_buffers 8 16k;
|
||||
fastcgi_buffer_size 32k;
|
||||
}
|
||||
|
||||
location / {
|
||||
include /etc/nginx/ngcp-panel_params;
|
||||
}
|
||||
|
||||
location ~ ^/wss/xmpp/(.*)$ {
|
||||
proxy_pass https://127.0.0.1:5281/xmpp-websocket/$1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_buffering off;
|
||||
tcp_nodelay on;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
location ~ ^/wss/sip/(.*)$ {
|
||||
proxy_pass https://[% ext_ip %]:[% kamailio.lb.tls.port %]/ws/$1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_buffering off;
|
||||
tcp_nodelay on;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
[% IF www_admin.http_csc.csc_js_enable == "yes" -%]
|
||||
location /csc {
|
||||
root /usr/share/ngcp-csc;
|
||||
}
|
||||
[% END -%]
|
||||
}
|
||||
[% END -%]
|
@ -0,0 +1,21 @@
|
||||
include /etc/nginx/fastcgi_params;
|
||||
# Catalyst requires setting PATH_INFO (instead of SCRIPT_NAME) to $fastcgi_script_name
|
||||
fastcgi_param SCRIPT_NAME '';
|
||||
fastcgi_param PATH_INFO $fastcgi_script_name;
|
||||
fastcgi_pass unix:/var/run/fastcgi/ngcp-panel.sock;
|
||||
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'POST, GET, PUT, DELETE, PATCH, OPTIONS' always;
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range,Location';
|
||||
add_header 'Access-Control-Max-Age' '1728000' always;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
|
||||
add_header 'Content-Length' '0' always;
|
||||
return 204;
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<div ref="config" class="csc-pbx-device-config justify-center row">
|
||||
<div class="csc-pbx-device-canvas" :style="canvasStyles">
|
||||
<div ref="imageWrapper" class="csc-pbx-device-image" :style="imageWrapperStyles">
|
||||
<img ref="image" :src="imageUrl" @load="imageLoaded" :style="imageStyles" />
|
||||
</div>
|
||||
<div :class="spotClasses(index)" v-for="(key,index) in keys"
|
||||
:key="index" :style="spotPosition(key)">
|
||||
<span>{{ index + 1 }}</span>
|
||||
<q-popover ref="keyPopover">
|
||||
<div class="csc-pbx-key-popover">
|
||||
<q-field v-if="getLineByKey(index) !== null" :icon="getIconByKey(index)">
|
||||
<q-input :value="getLineByKey(index).subscriber.display_name" readonly :float-label="'Key ' + (index + 1)" />
|
||||
</q-field>
|
||||
<q-field v-else >
|
||||
<q-input value="Empty" readonly :float-label="'Key ' + (index + 1)" />
|
||||
</q-field>
|
||||
<q-btn icon="clear" :small="!isMobile" class="absolute-top-right"
|
||||
@click="$refs.keyPopover[index].close()" flat round color="primary" />
|
||||
</div>
|
||||
</q-popover>
|
||||
</div>
|
||||
</div>
|
||||
<q-window-resize-observable @resize="windowResize" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import _ from 'lodash'
|
||||
import { QList, QItem, QItemMain, QItemTile, QTabs, QTab, Platform,
|
||||
QTabPane, QChip, QWindowResizeObservable, QModal, QBtn, QPopover, QIcon, QField, QInput } from 'quasar-framework'
|
||||
import { BoundingBox2D } from '../../../helpers/graphics'
|
||||
|
||||
export default {
|
||||
name: 'csc-pbx-device-config',
|
||||
props: [
|
||||
'device',
|
||||
'loading',
|
||||
],
|
||||
components: {
|
||||
QList, QItem, QItemMain, QItemTile, QTabs, QTab, Platform,
|
||||
QTabPane, QChip, QWindowResizeObservable, QModal, QBtn, QPopover, QIcon, QField, QInput
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
configWidth: 0,
|
||||
imageDeltaX: 0,
|
||||
imageScaleFactor: 1,
|
||||
imageWidth: 0,
|
||||
boundingBox: null,
|
||||
scaledBoundingBox: null,
|
||||
modalOpened: false,
|
||||
selectedKey: null,
|
||||
selectedKeyIndex: null,
|
||||
selectedLine: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isMobile() {
|
||||
return Platform.is.mobile;
|
||||
},
|
||||
imageUrl() {
|
||||
return _.get(this.device, 'profile.modelFrontImageUrl');
|
||||
},
|
||||
keys() {
|
||||
return _.get(this.device, 'profile.model.linerange[0].keys', []);
|
||||
},
|
||||
canvasStyles() {
|
||||
return {
|
||||
width: this.configWidth + "px"
|
||||
}
|
||||
},
|
||||
imageWrapperStyles() {
|
||||
return {
|
||||
width: this.configWidth + "px"
|
||||
}
|
||||
},
|
||||
imageStyles() {
|
||||
return {
|
||||
left: this.imageDeltaX + "px",
|
||||
width: (this.imageWidth * this.imageScaleFactor) + "px"
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.boundingBox = BoundingBox2D.createFromPoints(this.keys);
|
||||
this.boundingBox.addMargin(40);
|
||||
},
|
||||
methods: {
|
||||
getLineByKey(key) {
|
||||
let line = null;
|
||||
this.device.lines.forEach(($line)=>{
|
||||
if($line.key_num === key) {
|
||||
line = $line;
|
||||
}
|
||||
});
|
||||
return line;
|
||||
},
|
||||
openModal(key, index) {
|
||||
this.$refs.modal.open();
|
||||
this.selectedKey = key;
|
||||
this.selectedKeyIndex = index + 1;
|
||||
this.selectedLine = this.getLineByKey(index);
|
||||
},
|
||||
closeModal() {
|
||||
this.$refs.modal.close();
|
||||
this.selectedKey = null;
|
||||
this.selectedKeyIndex = null;
|
||||
},
|
||||
windowResize() {
|
||||
this.resize();
|
||||
this.placeImage();
|
||||
},
|
||||
imageLoaded() {
|
||||
this.resize();
|
||||
this.placeImage();
|
||||
},
|
||||
resize() {
|
||||
this.imageWidth = this.$refs.image.naturalWidth;
|
||||
this.configWidth = this.$refs.config.clientWidth;
|
||||
if(this.boundingBox !== null ) {
|
||||
if (this.boundingBox.getWidth() > this.configWidth) {
|
||||
this.imageScaleFactor = this.configWidth / this.boundingBox.getWidth();
|
||||
}
|
||||
else {
|
||||
this.imageScaleFactor = 1;
|
||||
}
|
||||
this.scaledBoundingBox = this.boundingBox.clone();
|
||||
this.scaledBoundingBox.scale(this.imageScaleFactor);
|
||||
}
|
||||
},
|
||||
placeImage() {
|
||||
let configCenterX = this.configWidth / 2;
|
||||
if (this.scaledBoundingBox !== null) {
|
||||
this.imageDeltaX = -1 * this.scaledBoundingBox.getCenterX() + configCenterX;
|
||||
}
|
||||
},
|
||||
getScaleFactorX() {
|
||||
if (_.isObject(this.$refs.image)) {
|
||||
return this.$refs.image.width / this.$refs.image.naturalWidth;
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
getScaleFactorY() {
|
||||
if (_.isObject(this.$refs.image)) {
|
||||
return this.$refs.image.height / this.$refs.image.naturalHeight;
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
keyPointing(key) {
|
||||
let pointing = {
|
||||
left: 'right',
|
||||
right: 'left',
|
||||
top: 'down',
|
||||
bottom: 'up'
|
||||
};
|
||||
return pointing[key.labelpos];
|
||||
},
|
||||
spotPosition(key) {
|
||||
let deltaX = 0;
|
||||
if(this.scaledBoundingBox !== null) {
|
||||
deltaX = -1 * this.scaledBoundingBox.getCenterX() + this.configWidth / 2;
|
||||
}
|
||||
let width = 25 * this.imageScaleFactor;
|
||||
let height = 25 * this.imageScaleFactor;
|
||||
let x = (key.x * this.imageScaleFactor) + deltaX;
|
||||
let y = (key.y * this.imageScaleFactor);
|
||||
switch(key.labelpos){
|
||||
case 'left':
|
||||
y = y - height / 2;
|
||||
x = x - width;
|
||||
break;
|
||||
case 'right':
|
||||
y = y - height / 2;
|
||||
break;
|
||||
case 'top':
|
||||
x = x - width / 2;
|
||||
y = y - height;
|
||||
break;
|
||||
case 'bottom':
|
||||
x = x - width / 2;
|
||||
break;
|
||||
}
|
||||
return {
|
||||
top: y + "px",
|
||||
left: x + "px",
|
||||
width: width + "px",
|
||||
height: height + "px",
|
||||
position: 'absolute',
|
||||
lineHeight: width + "px",
|
||||
zIndex: 10
|
||||
};
|
||||
},
|
||||
spotClasses(key) {
|
||||
let classes = ['csc-pbx-device-button-spot', 'shadow-3'];
|
||||
if(this.getLineByKey(key) !== null) {
|
||||
classes.push('csc-pbx-device-button-active');
|
||||
}
|
||||
return classes;
|
||||
},
|
||||
getIconByKey(key) {
|
||||
let line = this.getLineByKey(key);
|
||||
if(line !== null && line.subscriber.is_pbx_group === true) {
|
||||
return 'group';
|
||||
}
|
||||
else if(line !== null && line.subscriber.is_pbx_group === false) {
|
||||
return 'person';
|
||||
}
|
||||
else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" rel="stylesheet/stylus">
|
||||
@import '../../../themes/app.common'
|
||||
|
||||
$spotSize = 25px
|
||||
|
||||
.csc-pbx-device-key-details
|
||||
padding 50px
|
||||
position relative
|
||||
|
||||
.csc-pbx-device-config
|
||||
position relative
|
||||
.spot-modal-content
|
||||
position relative
|
||||
|
||||
.csc-pbx-device-image
|
||||
position relative
|
||||
overflow hidden
|
||||
img
|
||||
display block
|
||||
position relative
|
||||
|
||||
.csc-pbx-device-canvas
|
||||
position: relative
|
||||
|
||||
.csc-pbx-key-popover-title
|
||||
font-size 18px
|
||||
font-weight 400
|
||||
letter-spacing normal
|
||||
line-height 1.8rem
|
||||
|
||||
.csc-pbx-device-button-spot
|
||||
border-radius: 50%;
|
||||
width $spotSize
|
||||
height $spotSize
|
||||
background-color white
|
||||
line-height $spotSize
|
||||
color $primary
|
||||
text-align center
|
||||
cursor pointer
|
||||
font-weight bold
|
||||
|
||||
.csc-pbx-device-button
|
||||
background-color $primary
|
||||
|
||||
.csc-pbx-device-button-active
|
||||
background-color $primary
|
||||
color white
|
||||
|
||||
.csc-pbx-key-popover
|
||||
min-height 40px
|
||||
padding 16px
|
||||
padding-right 40px
|
||||
|
||||
</style>
|
@ -0,0 +1,5 @@
|
||||
|
||||
export default {
|
||||
baseHttpUrl: 'https://' + window.location.host,
|
||||
baseWsUrl: 'wss://' + window.location.host
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
export class BoundingBox2D {
|
||||
|
||||
constructor(points) {
|
||||
this.points = points;
|
||||
this.topLeftX = null;
|
||||
this.topLeftY = null;
|
||||
this.bottomRightX = null;
|
||||
this.bottomRightY = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if(_.isArray(this.points)) {
|
||||
for(let i = 0; i < this.points.length; i++) {
|
||||
if(this.points[i].x < this.topLeftX || this.topLeftX === null) {
|
||||
this.topLeftX = this.points[i].x;
|
||||
}
|
||||
if(this.points[i].x > this.bottomRightX || this.bottomRightX === null) {
|
||||
this.bottomRightX = this.points[i].x;
|
||||
}
|
||||
if(this.points[i].y < this.topLeftY || this.topLeftY === null) {
|
||||
this.topLeftY = this.points[i].y;
|
||||
}
|
||||
if(this.points[i].y > this.bottomRightY || this.bottomRightY === null) {
|
||||
this.bottomRightY = this.points[i].y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTopLeftX() {
|
||||
return this.topLeftX;
|
||||
}
|
||||
|
||||
getTopLeftY() {
|
||||
return this.topLeftY;
|
||||
}
|
||||
|
||||
getBottomRightX() {
|
||||
return this.bottomRightX;
|
||||
}
|
||||
|
||||
getBottomRightY() {
|
||||
return this.bottomRightY;
|
||||
}
|
||||
|
||||
getCenterX() {
|
||||
return this.getTopLeftX() + this.getWidth() / 2;
|
||||
}
|
||||
|
||||
getCenterY() {
|
||||
return this.getTopLeftY() + this.getHeight() / 2;
|
||||
}
|
||||
|
||||
getWidth() {
|
||||
return this.getBottomRightX() - this.getTopLeftX();
|
||||
}
|
||||
|
||||
getHeight() {
|
||||
return this.getBottomRightY() - this.getTopLeftY();
|
||||
}
|
||||
|
||||
addMargin(margin) {
|
||||
this.topLeftX = this.topLeftX - margin;
|
||||
this.topLeftY = this.topLeftY - margin;
|
||||
this.bottomRightX = this.bottomRightX + margin;
|
||||
this.bottomRightY = this.bottomRightY + margin;
|
||||
}
|
||||
|
||||
scale(factor) {
|
||||
this.topLeftX = this.topLeftX * factor;
|
||||
this.topLeftY = this.topLeftY * factor;
|
||||
this.bottomRightX = this.bottomRightX * factor;
|
||||
this.bottomRightY = this.bottomRightY * factor;
|
||||
}
|
||||
|
||||
clone() {
|
||||
let boundingBox = new BoundingBox2D();
|
||||
boundingBox.topLeftX = this.getTopLeftX();
|
||||
boundingBox.topLeftY = this.getTopLeftY();
|
||||
boundingBox.bottomRightX = this.getBottomRightX();
|
||||
boundingBox.bottomRightY = this.getBottomRightY();
|
||||
return boundingBox;
|
||||
}
|
||||
|
||||
static createFromPoints(points) {
|
||||
return new BoundingBox2D(points);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue