mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Additional custom map features (#15806)
* Added options on edges for a label and to show graphs as bps as well as percentages I think that vis.js needs to be updated to allow both bps and percentages at the same time. * Add database migration * Try to avoid putting multiple mid points in the same position * Added a URL parameter for screenshot mode, where node labels are blanked out Also fixed up the node labels in the editor * Added legend to the editor as well as database options for reversing arrows and adjusting the edge separation All features have been implemented in the editor, but need to be implemented in the viewer * Fix missing defaults on the edit map list page Added arrow reverse code to the viewer Added legend code to the viewer Added code to the editor to correclty handle moving the legend * Formatting fixes and DB schema update * Remove view from database schema
This commit is contained in:
@@ -30,6 +30,7 @@ use App\Http\Requests\CustomMapSettingsRequest;
|
|||||||
use App\Models\CustomMap;
|
use App\Models\CustomMap;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
@@ -47,6 +48,16 @@ class CustomMapController extends Controller
|
|||||||
'maps' => CustomMap::orderBy('name')->get(['custom_map_id', 'name']),
|
'maps' => CustomMap::orderBy('name')->get(['custom_map_id', 'name']),
|
||||||
'name' => 'New Map',
|
'name' => 'New Map',
|
||||||
'node_align' => 10,
|
'node_align' => 10,
|
||||||
|
'edge_separation' => 10,
|
||||||
|
'reverse_arrows' => 0,
|
||||||
|
'legend' => [
|
||||||
|
'x' => -1,
|
||||||
|
'y' => -1,
|
||||||
|
'steps' => 7,
|
||||||
|
'hide_invalid' => 0,
|
||||||
|
'hide_overspeed' => 0,
|
||||||
|
'font_size' => 14,
|
||||||
|
],
|
||||||
'background' => null,
|
'background' => null,
|
||||||
'map_conf' => [
|
'map_conf' => [
|
||||||
'height' => '800px',
|
'height' => '800px',
|
||||||
@@ -75,8 +86,14 @@ class CustomMapController extends Controller
|
|||||||
->header('Content-Type', 'text/plain');
|
->header('Content-Type', 'text/plain');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(CustomMap $map): View
|
public function show(Request $request, CustomMap $map): View
|
||||||
{
|
{
|
||||||
|
$request->validate([
|
||||||
|
'screenshot' => 'nullable|in:yes',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$screenshot = $request->input('screenshot') === 'yes' ? 1 : 0;
|
||||||
|
|
||||||
$map_conf = $map->options;
|
$map_conf = $map->options;
|
||||||
$map_conf['width'] = $map->width;
|
$map_conf['width'] = $map->width;
|
||||||
$map_conf['height'] = $map->height;
|
$map_conf['height'] = $map->height;
|
||||||
@@ -84,6 +101,8 @@ class CustomMapController extends Controller
|
|||||||
'edit' => false,
|
'edit' => false,
|
||||||
'map_id' => $map->custom_map_id,
|
'map_id' => $map->custom_map_id,
|
||||||
'name' => $map->name,
|
'name' => $map->name,
|
||||||
|
'reverse_arrows' => $map->reverse_arrows,
|
||||||
|
'legend' => $this->legendConfig($map),
|
||||||
'background' => (bool) $map->background_suffix,
|
'background' => (bool) $map->background_suffix,
|
||||||
'bgversion' => $map->background_version,
|
'bgversion' => $map->background_version,
|
||||||
'page_refresh' => Config::get('page_refresh', 300),
|
'page_refresh' => Config::get('page_refresh', 300),
|
||||||
@@ -93,6 +112,7 @@ class CustomMapController extends Controller
|
|||||||
'newnode_conf' => $map->newnodeconfig,
|
'newnode_conf' => $map->newnodeconfig,
|
||||||
'vmargin' => 20,
|
'vmargin' => 20,
|
||||||
'hmargin' => 20,
|
'hmargin' => 20,
|
||||||
|
'screenshot' => $screenshot,
|
||||||
];
|
];
|
||||||
|
|
||||||
return view('map.custom-view', $data);
|
return view('map.custom-view', $data);
|
||||||
@@ -104,6 +124,9 @@ class CustomMapController extends Controller
|
|||||||
'map_id' => $map->custom_map_id,
|
'map_id' => $map->custom_map_id,
|
||||||
'name' => $map->name,
|
'name' => $map->name,
|
||||||
'node_align' => $map->node_align,
|
'node_align' => $map->node_align,
|
||||||
|
'edge_separation' => $map->edge_separation,
|
||||||
|
'reverse_arrows' => $map->reverse_arrows,
|
||||||
|
'legend' => $this->legendConfig($map),
|
||||||
'newedge_conf' => $map->newedgeconfig,
|
'newedge_conf' => $map->newedgeconfig,
|
||||||
'newnode_conf' => $map->newnodeconfig,
|
'newnode_conf' => $map->newnodeconfig,
|
||||||
'map_conf' => $map->options,
|
'map_conf' => $map->options,
|
||||||
@@ -142,6 +165,8 @@ class CustomMapController extends Controller
|
|||||||
'name' => $map->name,
|
'name' => $map->name,
|
||||||
'width' => $map->width,
|
'width' => $map->width,
|
||||||
'height' => $map->height,
|
'height' => $map->height,
|
||||||
|
'reverse_arrows' => $map->reverse_arrows,
|
||||||
|
'edge_separation' => $map->edge_separation,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,4 +189,21 @@ class CustomMapController extends Controller
|
|||||||
|
|
||||||
return $images;
|
return $images;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the legend config
|
||||||
|
*/
|
||||||
|
private function legendConfig(CustomMap $map): array
|
||||||
|
{
|
||||||
|
$legend = [
|
||||||
|
'x' => $map->legend_x,
|
||||||
|
'y' => $map->legend_y,
|
||||||
|
'steps' => $map->legend_steps,
|
||||||
|
'hide_invalid' => $map->legend_hide_invalid,
|
||||||
|
'hide_overspeed' => $map->legend_hide_overspeed,
|
||||||
|
'font_size' => $map->legend_font_size,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $legend;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -55,6 +55,8 @@ class CustomMapDataController extends Controller
|
|||||||
'reverse' => $edge->reverse,
|
'reverse' => $edge->reverse,
|
||||||
'style' => $edge->style,
|
'style' => $edge->style,
|
||||||
'showpct' => $edge->showpct,
|
'showpct' => $edge->showpct,
|
||||||
|
'showbps' => $edge->showbps,
|
||||||
|
'label' => $edge->label,
|
||||||
'text_face' => $edge->text_face,
|
'text_face' => $edge->text_face,
|
||||||
'text_size' => $edge->text_size,
|
'text_size' => $edge->text_size,
|
||||||
'text_colour' => $edge->text_colour,
|
'text_colour' => $edge->text_colour,
|
||||||
@@ -122,6 +124,8 @@ class CustomMapDataController extends Controller
|
|||||||
$edges[$edgeid]['colour_to'] = $this->speedColour($edges[$edgeid]['port_topct']);
|
$edges[$edgeid]['colour_to'] = $this->speedColour($edges[$edgeid]['port_topct']);
|
||||||
$edges[$edgeid]['colour_from'] = $this->speedColour($edges[$edgeid]['port_frompct']);
|
$edges[$edgeid]['colour_from'] = $this->speedColour($edges[$edgeid]['port_frompct']);
|
||||||
}
|
}
|
||||||
|
$edges[$edgeid]['port_tobps'] = $this->rateString($rateto);
|
||||||
|
$edges[$edgeid]['port_frombps'] = $this->rateString($ratefrom);
|
||||||
$edges[$edgeid]['width_to'] = $this->speedWidth($speedto);
|
$edges[$edgeid]['width_to'] = $this->speedWidth($speedto);
|
||||||
$edges[$edgeid]['width_from'] = $this->speedWidth($speedfrom);
|
$edges[$edgeid]['width_from'] = $this->speedWidth($speedfrom);
|
||||||
}
|
}
|
||||||
@@ -185,11 +189,20 @@ class CustomMapDataController extends Controller
|
|||||||
'newedgeconf' => 'array',
|
'newedgeconf' => 'array',
|
||||||
'nodes' => 'array',
|
'nodes' => 'array',
|
||||||
'edges' => 'array',
|
'edges' => 'array',
|
||||||
|
'legend_x' => 'integer',
|
||||||
|
'legend_y' => 'integer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$map->load(['nodes', 'edges']);
|
$map->load(['nodes', 'edges']);
|
||||||
|
|
||||||
DB::transaction(function () use ($map, $data) {
|
DB::transaction(function () use ($map, $data) {
|
||||||
|
if ($map->legend_x != $data['legend_x'] || $map->legend_y != $data['legend_y']) {
|
||||||
|
$map->legend_x = $data['legend_x'];
|
||||||
|
$map->legend_y = $data['legend_y'];
|
||||||
|
|
||||||
|
$map->save();
|
||||||
|
}
|
||||||
|
|
||||||
$dbnodes = $map->nodes->keyBy('custom_map_node_id')->all();
|
$dbnodes = $map->nodes->keyBy('custom_map_node_id')->all();
|
||||||
$dbedges = $map->edges->keyBy('custom_map_edge_id')->all();
|
$dbedges = $map->edges->keyBy('custom_map_edge_id')->all();
|
||||||
|
|
||||||
@@ -249,6 +262,8 @@ class CustomMapDataController extends Controller
|
|||||||
$dbedge->port_id = $edge['port_id'] ? $edge['port_id'] : null;
|
$dbedge->port_id = $edge['port_id'] ? $edge['port_id'] : null;
|
||||||
$dbedge->reverse = filter_var($edge['reverse'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
$dbedge->reverse = filter_var($edge['reverse'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||||
$dbedge->showpct = filter_var($edge['showpct'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
$dbedge->showpct = filter_var($edge['showpct'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||||
|
$dbedge->showbps = filter_var($edge['showbps'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||||
|
$dbedge->label = $edge['label'] ? $edge['label'] : '';
|
||||||
$dbedge->style = $edge['style'];
|
$dbedge->style = $edge['style'];
|
||||||
$dbedge->text_face = $edge['text_face'];
|
$dbedge->text_face = $edge['text_face'];
|
||||||
$dbedge->text_size = $edge['text_size'];
|
$dbedge->text_size = $edge['text_size'];
|
||||||
@@ -274,6 +289,23 @@ class CustomMapDataController extends Controller
|
|||||||
return response()->json(['id' => $map->custom_map_id]);
|
return response()->json(['id' => $map->custom_map_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function rateString(int $rate): string
|
||||||
|
{
|
||||||
|
if ($rate < 1000) {
|
||||||
|
return $rate . ' bps';
|
||||||
|
} elseif ($rate < 1000000) {
|
||||||
|
return intval($rate / 1000) . ' kbps';
|
||||||
|
} elseif ($rate < 1000000000) {
|
||||||
|
return intval($rate / 1000000) . ' Mbps';
|
||||||
|
} elseif ($rate < 1000000000000) {
|
||||||
|
return intval($rate / 1000000000) . ' Gbps';
|
||||||
|
} elseif ($rate < 1000000000000000) {
|
||||||
|
return intval($rate / 1000000000000) . ' Tbps';
|
||||||
|
} else {
|
||||||
|
return intval($rate / 1000000000000000) . ' Pbps';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function snmpSpeed(string $speeds): int
|
private function snmpSpeed(string $speeds): int
|
||||||
{
|
{
|
||||||
// Only succeed if the string startes with a number optionally followed by a unit
|
// Only succeed if the string startes with a number optionally followed by a unit
|
||||||
|
@@ -25,6 +25,8 @@ class CustomMapSettingsRequest extends FormRequest
|
|||||||
return [
|
return [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'node_align' => 'integer',
|
'node_align' => 'integer',
|
||||||
|
'reverse_arrows' => 'boolean',
|
||||||
|
'edge_separation' => 'integer',
|
||||||
'width_type' => 'in:px,%',
|
'width_type' => 'in:px,%',
|
||||||
'width' => [
|
'width' => [
|
||||||
function (string $attribute, mixed $value, Closure $fail) {
|
function (string $attribute, mixed $value, Closure $fail) {
|
||||||
@@ -49,6 +51,12 @@ class CustomMapSettingsRequest extends FormRequest
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'legend_x' => 'integer',
|
||||||
|
'legend_y' => 'integer',
|
||||||
|
'legend_steps' => 'integer',
|
||||||
|
'legend_font_size' => 'integer',
|
||||||
|
'legend_hide_invalid' => 'boolean',
|
||||||
|
'legend_hide_overspeed' => 'boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,14 @@ class CustomMap extends BaseModel
|
|||||||
'width',
|
'width',
|
||||||
'height',
|
'height',
|
||||||
'node_align',
|
'node_align',
|
||||||
|
'reverse_arrows',
|
||||||
|
'edge_separation',
|
||||||
|
'legend_x',
|
||||||
|
'legend_y',
|
||||||
|
'legend_steps',
|
||||||
|
'legend_font_size',
|
||||||
|
'legend_hide_invalid',
|
||||||
|
'legend_hide_overspeed',
|
||||||
'background_suffix',
|
'background_suffix',
|
||||||
'background_version',
|
'background_version',
|
||||||
];
|
];
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('custom_map_edges', function (Blueprint $table) {
|
||||||
|
$table->boolean('showbps')->default(0)->after('showpct');
|
||||||
|
$table->string('label', 255)->default('')->after('showbps');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('custom_map_edges', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['showbps', 'label']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('custom_maps', function (Blueprint $table) {
|
||||||
|
$table->boolean('reverse_arrows')->default(0)->after('node_align');
|
||||||
|
$table->smallInteger('edge_separation')->default(10)->after('reverse_arrows');
|
||||||
|
$table->integer('legend_x')->default(-1)->after('edge_separation');
|
||||||
|
$table->integer('legend_y')->default(-1)->after('legend_x');
|
||||||
|
$table->smallInteger('legend_steps')->default(7)->after('legend_y');
|
||||||
|
$table->smallInteger('legend_font_size')->default(14)->after('legend_steps');
|
||||||
|
$table->boolean('legend_hide_invalid')->default(0)->after('legend_font_size');
|
||||||
|
$table->boolean('legend_hide_overspeed')->default(0)->after('legend_hide_invalid');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('custom_maps', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['reverse_arrows', 'edge_separation', 'legend_x', 'legend_y', 'legend_steps', 'legend_steps', 'legend_hide_invalid', 'legend_hide_overspeed']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -25,6 +25,19 @@ Some key points about the viewer are:
|
|||||||
- Red at 100% utilisation, with a gradual change to
|
- Red at 100% utilisation, with a gradual change to
|
||||||
- Purple at 150% utilisation and above
|
- Purple at 150% utilisation and above
|
||||||
|
|
||||||
|
### Viewer URL options
|
||||||
|
|
||||||
|
You can manually add the following parameters to a URL to alter the display of a
|
||||||
|
custom map.
|
||||||
|
|
||||||
|
The following URL options are available:
|
||||||
|
|
||||||
|
- bare=yes : Removes the control bar from the top of the page.
|
||||||
|
- screenshot=yes : Removes all labels from the nodes and links
|
||||||
|
|
||||||
|
e.g. If you want bare and screenshot enabled, https://_nmsserver_/maps/custom/2
|
||||||
|
becomes https://_nmsserver_/maps/custom/2?bare=yes&screenshot=yes
|
||||||
|
|
||||||
## Editor
|
## Editor
|
||||||
|
|
||||||
To access the custom map editor, a user must be an admin. The editor
|
To access the custom map editor, a user must be an admin. The editor
|
||||||
|
@@ -36,6 +36,9 @@ return [
|
|||||||
'width' => 'Width',
|
'width' => 'Width',
|
||||||
'height' => 'Height',
|
'height' => 'Height',
|
||||||
'alignment' => 'Node Alignment',
|
'alignment' => 'Node Alignment',
|
||||||
|
'edgeseparation' => 'Link Separation',
|
||||||
|
'reverse' => 'Reverse Arrows',
|
||||||
|
'enable_legend' => 'Enable Legend',
|
||||||
'saving' => 'Saving...',
|
'saving' => 'Saving...',
|
||||||
'save_errors' => 'Save failed due to the following errors:',
|
'save_errors' => 'Save failed due to the following errors:',
|
||||||
'save_error' => 'Save failed. Server returned error response code: :code',
|
'save_error' => 'Save failed. Server returned error response code: :code',
|
||||||
@@ -45,6 +48,12 @@ return [
|
|||||||
'edit' => 'Edit Map Settings',
|
'edit' => 'Edit Map Settings',
|
||||||
'rerender' => 'Re-Render Map',
|
'rerender' => 'Re-Render Map',
|
||||||
'save' => 'Save Map',
|
'save' => 'Save Map',
|
||||||
|
'legend' => [
|
||||||
|
'font_size' => 'Legend Text Size',
|
||||||
|
'steps' => 'Legend Steps',
|
||||||
|
'hideinvalid' => 'Hide Invalid',
|
||||||
|
'hideoverspeed' => 'Hide 100%+',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'node' => [
|
'node' => [
|
||||||
'new' => 'New Node',
|
'new' => 'New Node',
|
||||||
@@ -126,6 +135,8 @@ return [
|
|||||||
'cubicBezier' => 'Cubic Bezier',
|
'cubicBezier' => 'Cubic Bezier',
|
||||||
],
|
],
|
||||||
'show_usage_percent' => 'Show percent usage',
|
'show_usage_percent' => 'Show percent usage',
|
||||||
|
'show_usage_bps' => 'Show bps usage',
|
||||||
|
'label' => 'Label',
|
||||||
'recenter' => 'Recenter Line',
|
'recenter' => 'Recenter Line',
|
||||||
],
|
],
|
||||||
'validate' => [
|
'validate' => [
|
||||||
|
@@ -522,6 +522,14 @@ custom_maps:
|
|||||||
- { Field: width, Type: varchar(10), 'Null': false, Extra: '' }
|
- { Field: width, Type: varchar(10), 'Null': false, Extra: '' }
|
||||||
- { Field: height, Type: varchar(10), 'Null': false, Extra: '' }
|
- { Field: height, Type: varchar(10), 'Null': false, Extra: '' }
|
||||||
- { Field: node_align, Type: smallint, 'Null': false, Extra: '', Default: '0' }
|
- { Field: node_align, Type: smallint, 'Null': false, Extra: '', Default: '0' }
|
||||||
|
- { Field: reverse_arrows, Type: tinyint, 'Null': false, Extra: '', Default: '0' }
|
||||||
|
- { Field: edge_separation, Type: smallint, 'Null': false, Extra: '', Default: '10' }
|
||||||
|
- { Field: legend_x, Type: int, 'Null': false, Extra: '', Default: '-1' }
|
||||||
|
- { Field: legend_y, Type: int, 'Null': false, Extra: '', Default: '-1' }
|
||||||
|
- { Field: legend_steps, Type: smallint, 'Null': false, Extra: '', Default: '7' }
|
||||||
|
- { Field: legend_font_size, Type: smallint, 'Null': false, Extra: '', Default: '14' }
|
||||||
|
- { Field: legend_hide_invalid, Type: tinyint, 'Null': false, Extra: '', Default: '0' }
|
||||||
|
- { Field: legend_hide_overspeed, Type: tinyint, 'Null': false, Extra: '', Default: '0' }
|
||||||
- { Field: background_suffix, Type: varchar(10), 'Null': true, Extra: '' }
|
- { Field: background_suffix, Type: varchar(10), 'Null': true, Extra: '' }
|
||||||
- { Field: background_version, Type: 'int unsigned', 'Null': false, Extra: '' }
|
- { Field: background_version, Type: 'int unsigned', 'Null': false, Extra: '' }
|
||||||
- { Field: options, Type: longtext, 'Null': true, Extra: '' }
|
- { Field: options, Type: longtext, 'Null': true, Extra: '' }
|
||||||
@@ -553,6 +561,8 @@ custom_map_edges:
|
|||||||
- { Field: reverse, Type: tinyint, 'Null': false, Extra: '' }
|
- { Field: reverse, Type: tinyint, 'Null': false, Extra: '' }
|
||||||
- { Field: style, Type: varchar(50), 'Null': false, Extra: '' }
|
- { Field: style, Type: varchar(50), 'Null': false, Extra: '' }
|
||||||
- { Field: showpct, Type: tinyint, 'Null': false, Extra: '' }
|
- { Field: showpct, Type: tinyint, 'Null': false, Extra: '' }
|
||||||
|
- { Field: showbps, Type: tinyint, 'Null': false, Extra: '', Default: '0' }
|
||||||
|
- { Field: label, Type: varchar(255), 'Null': false, Extra: '', Default: '' }
|
||||||
- { Field: text_face, Type: varchar(50), 'Null': false, Extra: '' }
|
- { Field: text_face, Type: varchar(50), 'Null': false, Extra: '' }
|
||||||
- { Field: text_size, Type: int, 'Null': false, Extra: '' }
|
- { Field: text_size, Type: int, 'Null': false, Extra: '' }
|
||||||
- { Field: text_colour, Type: varchar(10), 'Null': false, Extra: '' }
|
- { Field: text_colour, Type: varchar(10), 'Null': false, Extra: '' }
|
||||||
|
@@ -62,12 +62,24 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row single-node">
|
||||||
<label for="edgetextshow" class="col-sm-3 control-label">{{ __('map.custom.edit.edge.show_usage_percent') }}</label>
|
<label for="edgetextshow" class="col-sm-3 control-label">{{ __('map.custom.edit.edge.show_usage_percent') }}</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="edgetextshow">
|
<input class="form-check-input" type="checkbox" role="switch" id="edgetextshow">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="edgebpsshow" class="col-sm-3 control-label">{{ __('map.custom.edit.edge.show_usage_bps') }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="edgebpsshow">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="edgelabel" class="col-sm-3 control-label">{{ __('map.custom.edit.edge.label') }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type=text id="edgelabel" class="form-control input-sm" value="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="edgetextface" class="col-sm-3 control-label">{{ __('map.custom.edit.text_font') }}</label>
|
<label for="edgetextface" class="col-sm-3 control-label">{{ __('map.custom.edit.text_font') }}</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
|
@@ -59,12 +59,136 @@
|
|||||||
var network;
|
var network;
|
||||||
var network_height;
|
var network_height;
|
||||||
var network_width;
|
var network_width;
|
||||||
var node_align = {{$node_align}};
|
|
||||||
var network_nodes = new vis.DataSet({queue: {delay: 100}});
|
var network_nodes = new vis.DataSet({queue: {delay: 100}});
|
||||||
var network_edges = new vis.DataSet({queue: {delay: 100}});
|
var network_edges = new vis.DataSet({queue: {delay: 100}});
|
||||||
|
var edge_nodes_map = [];
|
||||||
var node_device_map = {};
|
var node_device_map = {};
|
||||||
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
|
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
|
||||||
|
|
||||||
|
function edgeNodesRemove(nm_id, edgeid) {
|
||||||
|
// Remove old item from map if it exists
|
||||||
|
if (nm_id in edge_nodes_map) {
|
||||||
|
const edge_idx = edge_nodes_map[nm_id].indexOf(edgeid);
|
||||||
|
if (edge_idx >= 0) {
|
||||||
|
edge_nodes_map[nm_id].splice(edge_idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function edgeNodesUpdate(edgeid, node1_id, node2_id, old_node1_id, old_node2_id) {
|
||||||
|
var nm_id = node1_id < node2_id ? node1_id + '.' + node2_id : node2_id + '.' + node1_id;
|
||||||
|
var old_nm_id = old_node1_id < old_node2_id ? old_node1_id + '.' + old_node2_id : old_node2_id + '.' + old_node1_id;
|
||||||
|
|
||||||
|
// No update is needed if the new and old are the same
|
||||||
|
if (nm_id == old_nm_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_node1_id > 0 && old_node2_id > 0) {
|
||||||
|
edgeNodesRemove(old_nm_id, edgeid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(nm_id in edge_nodes_map)) {
|
||||||
|
edge_nodes_map[nm_id] = [];
|
||||||
|
}
|
||||||
|
edge_nodes_map[nm_id].push(edgeid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMidOffests(pos1, pos2) {
|
||||||
|
// First work out which pos is on the left-hand side
|
||||||
|
var left_pos;
|
||||||
|
var right_pos;
|
||||||
|
if(pos1.x < pos2.x) {
|
||||||
|
left_pos = pos1;
|
||||||
|
right_pos = pos2;
|
||||||
|
} else {
|
||||||
|
left_pos = pos2;
|
||||||
|
right_pos = pos1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The X axis needs to move left/right based on whether the line rises or falls
|
||||||
|
var x_diff = right_pos.y - left_pos.y;
|
||||||
|
// The Y axis needs to move up always based on how far apart the left and right nodes are
|
||||||
|
var y_diff = left_pos.x - right_pos.x;
|
||||||
|
|
||||||
|
// Calculate how far each mid point needs to move
|
||||||
|
var tot_diff = Math.abs(x_diff) + Math.abs(y_diff);
|
||||||
|
return {x: Math.round(edge_sep * (x_diff / tot_diff)), y: Math.round(edge_sep * (y_diff / tot_diff))};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMidPos(edgeid, from_id, to_id) {
|
||||||
|
var nm_id = from_id < to_id ? from_id + '.' + to_id : to_id + '.' + from_id;
|
||||||
|
const node_links = nm_id in edge_nodes_map ? edge_nodes_map[nm_id] : [];
|
||||||
|
|
||||||
|
var node_offsets = [];
|
||||||
|
node_links.forEach((link_edgeid) => {
|
||||||
|
// Ignore the edge we are creating
|
||||||
|
if (link_edgeid == edgeid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the offset in the hash
|
||||||
|
let link_mid = network_nodes.get(link_edgeid + "_mid");
|
||||||
|
let link_mid_offset = link_mid.x + '.' + link_mid.y;
|
||||||
|
node_offsets[link_mid_offset] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var pos = network.getPositions([from_id, to_id]);
|
||||||
|
|
||||||
|
const offsets = getMidOffests(pos[from_id], pos[to_id]);
|
||||||
|
|
||||||
|
// Calculate the center point
|
||||||
|
var mid_center = {x: (pos[from_id].x + pos[to_id].x) >> 1, y: (pos[from_id].y + pos[to_id].y) >> 1};
|
||||||
|
var mids = [mid_center];
|
||||||
|
for (let i = 1; i < node_links.length; i++) {
|
||||||
|
let multiplier = ((i + 1) >> 1);
|
||||||
|
let this_x = mid_center.x;
|
||||||
|
let this_y = mid_center.y;
|
||||||
|
if(i & 1) {
|
||||||
|
// Odd numbers go the normal direction
|
||||||
|
mids.push({x: mid_center.x + (multiplier * offsets.x), y: mid_center.y + (multiplier * offsets.y)});
|
||||||
|
} else {
|
||||||
|
// Even numbers go the opposite direction
|
||||||
|
mids.push({x: mid_center.x - (multiplier * offsets.x), y: mid_center.y - (multiplier * offsets.y)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first unused mid point from the center
|
||||||
|
for (let i = 0; i < mids.length; i++) {
|
||||||
|
let this_offset = mids[i].x + '.' + mids[i].y;
|
||||||
|
if (!(this_offset in node_offsets)) {
|
||||||
|
return {x: mids[i].x, y: mids[i].y};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to mid point
|
||||||
|
return {x: mid_center.x, y: mid_center.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixNodePos(nodeid, node) {
|
||||||
|
var move=false;
|
||||||
|
if ( node_align && !nodeid.endsWith("_mid")) {
|
||||||
|
node.x = Math.round(node.x / node_align) * node_align;
|
||||||
|
node.y = Math.round(node.y / node_align) * node_align;
|
||||||
|
move = true;
|
||||||
|
}
|
||||||
|
if ( node.x < {{ $hmargin }} ) {
|
||||||
|
node.x = {{ $hmargin }};
|
||||||
|
move = true;
|
||||||
|
} else if ( node.x > network_width - {{ $hmargin }} ) {
|
||||||
|
node.x = network_width - {{ $hmargin }};
|
||||||
|
move = true;
|
||||||
|
}
|
||||||
|
if ( node.y < {{ $vmargin }} ) {
|
||||||
|
node.y = {{ $vmargin }};
|
||||||
|
move = true;
|
||||||
|
} else if ( node.y > network_height - {{ $vmargin }} ) {
|
||||||
|
node.y = network_height - {{ $vmargin }};
|
||||||
|
move = true;
|
||||||
|
}
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
|
||||||
function CreateNetwork() {
|
function CreateNetwork() {
|
||||||
// Flush the nodes and edges so they are rendered immediately
|
// Flush the nodes and edges so they are rendered immediately
|
||||||
network_nodes.flush();
|
network_nodes.flush();
|
||||||
@@ -120,12 +244,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pos = network.getPositions([data.from, data.to]);
|
|
||||||
var mid_x = (pos[data.from].x + pos[data.to].x) >> 1;
|
|
||||||
var mid_y = (pos[data.from].y + pos[data.to].y) >> 1;
|
|
||||||
|
|
||||||
var edgeid = "new" + newcount++;
|
var edgeid = "new" + newcount++;
|
||||||
|
|
||||||
|
edgeNodesUpdate(edgeid, data.from, data.to, -1, -1);
|
||||||
|
const mid_pos = getMidPos(edgeid, data.from, data.to);
|
||||||
|
|
||||||
|
// Default to using the center point
|
||||||
|
var mid_x = mid_pos.x;
|
||||||
|
var mid_y = mid_pos.y;
|
||||||
|
|
||||||
var mid = {id: edgeid + "_mid", shape: "dot", size: 3, x: mid_x, y: mid_y};
|
var mid = {id: edgeid + "_mid", shape: "dot", size: 3, x: mid_x, y: mid_y};
|
||||||
|
|
||||||
var edge1 = structuredClone(newedgeconf);
|
var edge1 = structuredClone(newedgeconf);
|
||||||
@@ -185,26 +312,21 @@
|
|||||||
// Make sure a node is not dragged outside the canvas
|
// Make sure a node is not dragged outside the canvas
|
||||||
nodepos = network.getPositions(data.nodes);
|
nodepos = network.getPositions(data.nodes);
|
||||||
$.each( nodepos, function( nodeid, node ) {
|
$.each( nodepos, function( nodeid, node ) {
|
||||||
move = false;
|
if ( nodeid == "legend_header" ) {
|
||||||
if ( node_align && !nodeid.endsWith("_mid")) {
|
// If the legend header was moved, just redraw it
|
||||||
node.x = Math.round(node.x / node_align) * node_align;
|
fixNodePos(nodeid, node);
|
||||||
node.y = Math.round(node.y / node_align) * node_align;
|
legend.x = node.x;
|
||||||
move = true;
|
legend.y = node.y;
|
||||||
}
|
|
||||||
if ( node.x < {{ $hmargin }} ) {
|
redrawLegend();
|
||||||
node.x = {{ $hmargin }};
|
return;
|
||||||
move = true;
|
} else if ( nodeid.startsWith("legend_") ) {
|
||||||
} else if ( node.x > network_width - {{ $hmargin }} ) {
|
// Get the original node and move it back
|
||||||
node.x = network_width - {{ $hmargin }};
|
node = network_nodes.get(nodeid);
|
||||||
move = true;
|
network_nodes.update(node);
|
||||||
}
|
return;
|
||||||
if ( node.y < {{ $vmargin }} ) {
|
|
||||||
node.y = {{ $vmargin }};
|
|
||||||
move = true;
|
|
||||||
} else if ( node.y > network_height - {{ $vmargin }} ) {
|
|
||||||
node.y = network_height - {{ $vmargin }};
|
|
||||||
move = true;
|
|
||||||
}
|
}
|
||||||
|
let move = fixNodePos(nodeid, node);
|
||||||
if ( move ) {
|
if ( move ) {
|
||||||
network.moveNode(nodeid, node.x, node.y);
|
network.moveNode(nodeid, node.x, node.y);
|
||||||
}
|
}
|
||||||
@@ -242,12 +364,87 @@
|
|||||||
window.location.href = "{{ route('maps.custom.index') }}";
|
window.location.href = "{{ route('maps.custom.index') }}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function swapArrows(reverse) {
|
||||||
|
var arrows;
|
||||||
|
if (reverse) {
|
||||||
|
arrows = {from: {enabled: true, scaleFactor: 0.6}, to: {enabled: false}};
|
||||||
|
} else {
|
||||||
|
arrows = {to: {enabled: true, scaleFactor: 0.6}, from: {enabled: false}};
|
||||||
|
}
|
||||||
|
network_edges.forEach((edge) => {
|
||||||
|
edge.arrows = arrows;
|
||||||
|
network_edges.update(edge);
|
||||||
|
});
|
||||||
|
network_edges.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
function legendPctColour(pct) {
|
||||||
|
if (pct < 0) {
|
||||||
|
return "black";
|
||||||
|
} else if (pct < 50) {
|
||||||
|
// 100% green and slowly increase the red until we get to yellow
|
||||||
|
return '#' + parseInt(5.1 * pct).toString(16).padStart(2, 0) + 'ff00';
|
||||||
|
} else if (pct < 100) {
|
||||||
|
// 100% red and slowly remove green to go from yellow to red
|
||||||
|
return '#ff' + parseInt(5.1 * (100.0 - pct)).toString(16).padStart(2, 0) + '00';
|
||||||
|
} else if (pct < 150) {
|
||||||
|
// 100% red and slowly increase blue to go purple
|
||||||
|
return '#ff00' + parseInt(5.1 * (pct - 100.0)).toString(16).padStart(2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to purple for links over 150%
|
||||||
|
return '#ff00ff';
|
||||||
|
}
|
||||||
|
|
||||||
|
function redrawLegend() {
|
||||||
|
// Clear out the old legend
|
||||||
|
old_nodes = network_nodes.get({filter: function(node) { return node.id.startsWith("legend_") }});
|
||||||
|
old_nodes.forEach((node) => {
|
||||||
|
network_nodes.remove(node.id);
|
||||||
|
});
|
||||||
|
if (legend.x >= 0) {
|
||||||
|
let y_pos = legend.y;
|
||||||
|
let y_inc = legend.font_size + 10;
|
||||||
|
|
||||||
|
let legend_header = {id: "legend_header", label: "<b>Legend</b>", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {multi: 'html', size: legend.font_size}, color: {background: "white"}};
|
||||||
|
network_nodes.add(legend_header);
|
||||||
|
y_pos += y_inc;
|
||||||
|
|
||||||
|
if (!(Boolean(legend.hide_invalid))) {
|
||||||
|
let legend_invalid = {id: "legend_invalid", label: "???", title: "Link is down or link speed is not defined", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {face: 'courier new', size: legend.font_size, color: "white"}, color: {background: "black"}};
|
||||||
|
y_pos += y_inc;
|
||||||
|
network_nodes.add(legend_invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pct_step;
|
||||||
|
if (Boolean(legend.hide_overspeed)) {
|
||||||
|
pct_step = 100.0 / (legend.steps - 1);
|
||||||
|
} else {
|
||||||
|
pct_step = 150.0 / (legend.steps - 1);
|
||||||
|
}
|
||||||
|
for (let i=0; i < legend.steps; i++) {
|
||||||
|
let this_pct = Math.round(pct_step * i);
|
||||||
|
let legend_step = {id: "legend_" + i.toString(), label: this_pct.toString().padStart(3, " ") + "%", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {face: 'courier new', size: legend.font_size, color: "black"}, color: {background: legendPctColour(this_pct)}};
|
||||||
|
network_nodes.add(legend_step);
|
||||||
|
y_pos += y_inc;
|
||||||
|
}
|
||||||
|
network_nodes.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function editMapSuccess(data) {
|
function editMapSuccess(data) {
|
||||||
$("#title").text(data.name);
|
$("#title").text(data.name);
|
||||||
$("#savemap-alert").attr("class", "col-sm-12");
|
$("#savemap-alert").attr("class", "col-sm-12");
|
||||||
$("#savemap-alert").text("");
|
$("#savemap-alert").text("");
|
||||||
network.setSize(data.width, data.height);
|
network.setSize(data.width, data.height);
|
||||||
|
|
||||||
|
edge_sep = data.edge_separation;
|
||||||
|
if(reverse_arrows != parseInt(data.reverse_arrows)) {
|
||||||
|
swapArrows(Boolean(parseInt(data.reverse_arrows)));
|
||||||
|
}
|
||||||
|
reverse_arrows = parseInt(data.reverse_arrows);
|
||||||
|
redrawLegend();
|
||||||
|
|
||||||
editMapCancel();
|
editMapCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,11 +458,13 @@
|
|||||||
var edges = {};
|
var edges = {};
|
||||||
|
|
||||||
$.each(network_nodes.get(), function (node_idx, node) {
|
$.each(network_nodes.get(), function (node_idx, node) {
|
||||||
if(node.id.endsWith("_mid")) {
|
if(node.id.startsWith("legend_")) {
|
||||||
|
return;
|
||||||
|
} else if(node.id.endsWith("_mid")) {
|
||||||
edgeid = node.id.split("_")[0];
|
edgeid = node.id.split("_")[0];
|
||||||
edge1 = network_edges.get(edgeid + "_from");
|
edge1 = network_edges.get(edgeid + "_from");
|
||||||
edge2 = network_edges.get(edgeid + "_to");
|
edge2 = network_edges.get(edgeid + "_to");
|
||||||
edges[edgeid] = {id: edgeid, text_colour: edge1.font.color, text_size: edge1.font.size, text_face: edge1.font.face, from: edge1.from, to: edge2.from, showpct: (edge1.label ? true : false), port_id: edge1.title, style: edge1.smooth.type, mid_x: node.x, mid_y: node.y, reverse: (edgeid in edge_port_map ? edge_port_map[edgeid].reverse : false)};
|
edges[edgeid] = {id: edgeid, text_colour: edge1.font.color, text_size: edge1.font.size, text_face: edge1.font.face, from: edge1.from, to: edge2.from, showpct: (edge1.label != null && edge1.label.includes("xx%")), showbps: (edge1.label != null && edge1.label.includes("bps")), label: (node.label || ''), port_id: edge1.title, style: edge1.smooth.type, mid_x: node.x, mid_y: node.y, reverse: (edgeid in edge_port_map ? edge_port_map[edgeid].reverse : false)};
|
||||||
} else {
|
} else {
|
||||||
if(node.icon.code) {
|
if(node.icon.code) {
|
||||||
node.icon = node.icon.code.charCodeAt(0).toString(16);
|
node.icon = node.icon.code.charCodeAt(0).toString(16);
|
||||||
@@ -290,6 +489,8 @@
|
|||||||
newedgeconf: newedgeconf,
|
newedgeconf: newedgeconf,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
edges: edges,
|
edges: edges,
|
||||||
|
legend_x: legend.x,
|
||||||
|
legend_y: legend.y,
|
||||||
},
|
},
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
type: 'POST'
|
type: 'POST'
|
||||||
@@ -646,7 +847,8 @@
|
|||||||
$("#edgetextface").val(newedgeconf.font.face);
|
$("#edgetextface").val(newedgeconf.font.face);
|
||||||
$("#edgetextsize").val(newedgeconf.font.size);
|
$("#edgetextsize").val(newedgeconf.font.size);
|
||||||
$("#edgetextcolour").val(newedgeconf.font.color);
|
$("#edgetextcolour").val(newedgeconf.font.color);
|
||||||
$("#edgetextshow").bootstrapSwitch('state', Boolean(newedgeconf.label));
|
$("#edgetextshow").bootstrapSwitch('state', (newedgeconf.label.includes('xx%') || newedgeconf.label.includes('true')));
|
||||||
|
$("#edgebpsshow").bootstrapSwitch('state', (newedgeconf.label.includes('bps')));
|
||||||
$('#edgecolourtextreset').attr('disabled', 'disabled');
|
$('#edgecolourtextreset').attr('disabled', 'disabled');
|
||||||
|
|
||||||
$("#edge-saveButton").hide();
|
$("#edge-saveButton").hide();
|
||||||
@@ -654,13 +856,30 @@
|
|||||||
$('#edgeModal').modal({backdrop: 'static', keyboard: false}, 'show');
|
$('#edgeModal').modal({backdrop: 'static', keyboard: false}, 'show');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function edgeLabel(show_pct, show_bps, default_val) {
|
||||||
|
var label = '';
|
||||||
|
if(show_pct) {
|
||||||
|
label = 'xx%';
|
||||||
|
}
|
||||||
|
if(show_bps) {
|
||||||
|
if(Boolean(label.length)) {
|
||||||
|
label += "\n";
|
||||||
|
}
|
||||||
|
label += 'xx bps';
|
||||||
|
}
|
||||||
|
if(Boolean(label.length)) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
|
||||||
function editEdgeDefaultsSave() {
|
function editEdgeDefaultsSave() {
|
||||||
editEdgeHide();
|
editEdgeHide();
|
||||||
newedgeconf.smooth.type = $("#edgestyle").val();
|
newedgeconf.smooth.type = $("#edgestyle").val();
|
||||||
newedgeconf.font.face = $("#edgetextface").val();
|
newedgeconf.font.face = $("#edgetextface").val();
|
||||||
newedgeconf.font.size = $("#edgetextsize").val();
|
newedgeconf.font.size = $("#edgetextsize").val();
|
||||||
newedgeconf.font.color = $("#edgetextcolour").val();
|
newedgeconf.font.color = $("#edgetextcolour").val();
|
||||||
newedgeconf.label = $("#edgetextshow").prop('checked');
|
newedgeconf.label = edgeLabel($("#edgetextshow").prop('checked'), $("#edgebpsshow").prop('checked'), '');
|
||||||
$("#map-saveDataButton").show();
|
$("#map-saveDataButton").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,7 +909,8 @@
|
|||||||
$("#edgetextface").val(edgedata.edge1.font.face);
|
$("#edgetextface").val(edgedata.edge1.font.face);
|
||||||
$("#edgetextsize").val(edgedata.edge1.font.size);
|
$("#edgetextsize").val(edgedata.edge1.font.size);
|
||||||
$("#edgetextcolour").val(edgedata.edge1.font.color);
|
$("#edgetextcolour").val(edgedata.edge1.font.color);
|
||||||
$("#edgetextshow").bootstrapSwitch('state', Boolean(edgedata.edge1.label));
|
$("#edgetextshow").bootstrapSwitch('state', (edgedata.edge1.label != null && edgedata.edge1.label.includes('xx%')));
|
||||||
|
$("#edgebpsshow").bootstrapSwitch('state', (edgedata.edge1.label != null && edgedata.edge1.label.includes('bps')));
|
||||||
|
|
||||||
$("#edgeRecenterRow").show();
|
$("#edgeRecenterRow").show();
|
||||||
$("#divEdgeFrom").show();
|
$("#divEdgeFrom").show();
|
||||||
@@ -705,6 +925,8 @@
|
|||||||
function editEdgeSave(event) {
|
function editEdgeSave(event) {
|
||||||
edgedata = event.data.data;
|
edgedata = event.data.data;
|
||||||
|
|
||||||
|
edgeNodesUpdate(edgedata.id, $("#edgefrom").val(), $("#edgeto").val(), edgedata.edge1.from, edgedata.edge2.from);
|
||||||
|
|
||||||
editEdgeHide();
|
editEdgeHide();
|
||||||
edgedata.edge1.smooth.type = $("#edgestyle").val();
|
edgedata.edge1.smooth.type = $("#edgestyle").val();
|
||||||
edgedata.edge2.smooth.type = $("#edgestyle").val();
|
edgedata.edge2.smooth.type = $("#edgestyle").val();
|
||||||
@@ -713,8 +935,9 @@
|
|||||||
edgedata.edge1.font.face = edgedata.edge2.font.face = $("#edgetextface").val();
|
edgedata.edge1.font.face = edgedata.edge2.font.face = $("#edgetextface").val();
|
||||||
edgedata.edge1.font.size = edgedata.edge2.font.size = $("#edgetextsize").val();
|
edgedata.edge1.font.size = edgedata.edge2.font.size = $("#edgetextsize").val();
|
||||||
edgedata.edge1.font.color = edgedata.edge2.font.color = $("#edgetextcolour").val();
|
edgedata.edge1.font.color = edgedata.edge2.font.color = $("#edgetextcolour").val();
|
||||||
edgedata.edge1.label = edgedata.edge2.label = $("#edgetextshow").prop('checked') ? "xx%" : null;
|
edgedata.edge1.label = edgedata.edge2.label = edgeLabel($("#edgetextshow").prop('checked'), $("#edgebpsshow").prop('checked'), null);
|
||||||
edgedata.edge1.title = edgedata.edge2.title = $("#port_id").val();
|
edgedata.edge1.title = edgedata.edge2.title = $("#port_id").val();
|
||||||
|
edgedata.mid.label = ($("#edgelabel").val() || '');
|
||||||
|
|
||||||
if(edgedata.id) {
|
if(edgedata.id) {
|
||||||
if($("#port_id").val()) {
|
if($("#port_id").val()) {
|
||||||
@@ -738,14 +961,14 @@
|
|||||||
network_edges.flush();
|
network_edges.flush();
|
||||||
} else {
|
} else {
|
||||||
network_edges.update([edgedata.edge1, edgedata.edge2]);
|
network_edges.update([edgedata.edge1, edgedata.edge2]);
|
||||||
|
network_nodes.update([edgedata.mid]);
|
||||||
|
|
||||||
if($("#edgerecenter").is(":checked")) {
|
if($("#edgerecenter").is(":checked")) {
|
||||||
var pos = network.getPositions([edgedata.edge1.from, edgedata.edge2.from]);
|
var pos = network.getPositions([edgedata.edge1.from, edgedata.edge2.from]);
|
||||||
var mid_x = (pos[edgedata.edge1.from].x + pos[edgedata.edge2.from].x) >> 1;
|
const mid_pos = getMidPos(edgedata.id, edgedata.edge1.from, edgedata.edge2.from);
|
||||||
var mid_y = (pos[edgedata.edge1.from].y + pos[edgedata.edge2.from].y) >> 1;
|
|
||||||
|
|
||||||
edgedata.mid.x = mid_x;
|
edgedata.mid.x = mid_pos.x;
|
||||||
edgedata.mid.y = mid_y;
|
edgedata.mid.y = mid_pos.y;
|
||||||
network_nodes.update([edgedata.mid]);
|
network_nodes.update([edgedata.mid]);
|
||||||
$("#map-renderButton").show();
|
$("#map-renderButton").show();
|
||||||
}
|
}
|
||||||
@@ -794,6 +1017,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteEdge(edgeid) {
|
function deleteEdge(edgeid) {
|
||||||
|
const edge1 = network_edges.get(edgeid + "_from");
|
||||||
|
const edge2 = network_edges.get(edgeid + "_to");
|
||||||
|
var nm_id = edge1.from < edge2.from ? edge1.from + '.' + edge2.from : edge2.from + '.' + edge1.from;
|
||||||
|
edgeNodesRemove(nm_id, edgeid);
|
||||||
network_edges.remove(edgeid + "_to");
|
network_edges.remove(edgeid + "_to");
|
||||||
network_edges.remove(edgeid + "_from");
|
network_edges.remove(edgeid + "_from");
|
||||||
network_edges.flush();
|
network_edges.flush();
|
||||||
@@ -803,6 +1030,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function refreshMap() {
|
function refreshMap() {
|
||||||
|
edge_nodes_map = [];
|
||||||
$.get( '{{ route('maps.custom.data', ['map' => $map_id]) }}')
|
$.get( '{{ route('maps.custom.data', ['map' => $map_id]) }}')
|
||||||
.done(function( data ) {
|
.done(function( data ) {
|
||||||
// Add/update nodes
|
// Add/update nodes
|
||||||
@@ -853,14 +1081,23 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$.each( data.edges, function( edgeid, edge) {
|
$.each( data.edges, function( edgeid, edge) {
|
||||||
|
edgeNodesUpdate(edgeid, edge.custom_map_node1_id, edge.custom_map_node2_id, -1, -1);
|
||||||
|
|
||||||
var mid_x = edge.mid_x;
|
var mid_x = edge.mid_x;
|
||||||
var mid_y = edge.mid_y;
|
var mid_y = edge.mid_y;
|
||||||
|
|
||||||
var mid = {id: edgeid + "_mid", shape: "dot", size: 0, x: mid_x, y: mid_y};
|
var mid = {id: edgeid + "_mid", shape: "dot", size: 0, x: mid_x, y: mid_y, label: edge.label};
|
||||||
mid.size = 3;
|
mid.size = 3;
|
||||||
|
|
||||||
var edge1 = {id: edgeid + "_from", from: edge.custom_map_node1_id, to: edgeid + "_mid", arrows: {to: {enabled: true, scaleFactor: 0.6}}, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
var arrows;
|
||||||
var edge2 = {id: edgeid + "_to", from: edge.custom_map_node2_id, to: edgeid + "_mid", arrows: {to: {enabled: true, scaleFactor: 0.6}}, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
if (Boolean(reverse_arrows)) {
|
||||||
|
arrows = {from: {enabled: true, scaleFactor: 0.6}, to: {enabled: false}};
|
||||||
|
} else {
|
||||||
|
arrows = {to: {enabled: true, scaleFactor: 0.6}, from: {enabled: false}};
|
||||||
|
}
|
||||||
|
|
||||||
|
var edge1 = {id: edgeid + "_from", from: edge.custom_map_node1_id, to: edgeid + "_mid", arrows: arrows, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
||||||
|
var edge2 = {id: edgeid + "_to", from: edge.custom_map_node2_id, to: edgeid + "_mid", arrows: arrows, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
||||||
|
|
||||||
// Special case for curved lines
|
// Special case for curved lines
|
||||||
if(edge2.smooth.type == "curvedCW") {
|
if(edge2.smooth.type == "curvedCW") {
|
||||||
@@ -874,11 +1111,7 @@
|
|||||||
} else {
|
} else {
|
||||||
edge1.title = edge2.title = '';
|
edge1.title = edge2.title = '';
|
||||||
}
|
}
|
||||||
if(edge.showpct) {
|
edge1.label = edge2.label = edgeLabel(edge.showpct, edge.showbps, '');
|
||||||
edge1.label = edge2.label = 'xx%';
|
|
||||||
} else {
|
|
||||||
edge1.label = edge2.label = '';
|
|
||||||
}
|
|
||||||
if (network_nodes.get(mid.id)) {
|
if (network_nodes.get(mid.id)) {
|
||||||
network_nodes.update(mid);
|
network_nodes.update(mid);
|
||||||
network_edges.update(edge1);
|
network_edges.update(edge1);
|
||||||
@@ -905,6 +1138,9 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add the legend back to the map
|
||||||
|
redrawLegend();
|
||||||
|
|
||||||
// Flush in order to make sure nodes exist for edges to connect to
|
// Flush in order to make sure nodes exist for edges to connect to
|
||||||
network_nodes.flush();
|
network_nodes.flush();
|
||||||
network_edges.flush();
|
network_edges.flush();
|
||||||
|
@@ -33,6 +33,48 @@
|
|||||||
<input type="number" id="mapnodealign" name="mapnodealign" class="form-control input-sm" value="{{ $node_align ?? 10 }}">
|
<input type="number" id="mapnodealign" name="mapnodealign" class="form-control input-sm" value="{{ $node_align ?? 10 }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="mapedgesep" class="col-sm-3 control-label">{{ __('map.custom.edit.map.edgeseparation') }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="number" id="mapedgesep" name="mapedgesep" class="form-control input-sm" value="{{ $edge_separation ?? 10 }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="mapreversearrows" class="col-sm-3 control-label">{{ __('map.custom.edit.map.reverse') }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="mapreversearrows">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="maplegend" class="col-sm-3 control-label">{{ __('map.custom.edit.map.enable_legend') }}</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="maplegend" onChange="toggleMapLegend()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row maplegend">
|
||||||
|
<label for="maplegendfontsize" class="col-sm-4 control-label">{{ __('map.custom.edit.map.legend.font_size') }}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type=number id="maplegendfontsize" class="form-control input-sm" value={{ $legend['font_size'] }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row maplegend">
|
||||||
|
<label for="maplegendsteps" class="col-sm-4 control-label">{{ __('map.custom.edit.map.legend.steps') }}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type=number id="maplegendsteps" class="form-control input-sm" value={{ $legend['steps'] }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row maplegend">
|
||||||
|
<label for="maplegendhideinvalid" class="col-sm-4 control-label">{{ __('map.custom.edit.map.legend.hideinvalid') }}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="maplegendhideinvalid">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group row maplegend">
|
||||||
|
<label for="maplegendhideoverspeed" class="col-sm-4 control-label">{{ __('map.custom.edit.map.legend.hideoverspeed') }}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="maplegendhideoverspeed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12" id="savemap-alert">
|
<div class="col-sm-12" id="savemap-alert">
|
||||||
@@ -53,6 +95,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
var node_align = {{$node_align}};
|
||||||
|
var edge_sep = {{$edge_separation}};
|
||||||
|
var reverse_arrows = {{$reverse_arrows}};
|
||||||
|
var legend = @json($legend);
|
||||||
|
|
||||||
function saveMapSettings() {
|
function saveMapSettings() {
|
||||||
$("#map-saveButton").attr('disabled','disabled');
|
$("#map-saveButton").attr('disabled','disabled');
|
||||||
$("#savemap-alert").text('{{ __('map.custom.edit.map.saving') }}');
|
$("#savemap-alert").text('{{ __('map.custom.edit.map.saving') }}');
|
||||||
@@ -63,6 +110,34 @@
|
|||||||
var height = $("#mapheight").val();
|
var height = $("#mapheight").val();
|
||||||
var node_align = $("#mapnodealign").val();
|
var node_align = $("#mapnodealign").val();
|
||||||
|
|
||||||
|
var mapwdith = 100;
|
||||||
|
if (!isNaN(width)) {
|
||||||
|
mapwidth = width;
|
||||||
|
} else if (width.includes("px")) {
|
||||||
|
mapwidth = width.replace("px", "");
|
||||||
|
} else if (width.includes("%")) {
|
||||||
|
mapwidth = window.innerWidth * width.replace("%", "") / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the x and y coordinates
|
||||||
|
if ($("#maplegend").prop('checked')) {
|
||||||
|
if (legend.x < 0) {
|
||||||
|
legend.x = mapwidth - 50;
|
||||||
|
legend.y = 100;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
legend.x = -1;
|
||||||
|
legend.y = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend.font_size = parseInt($("#maplegendfontsize").val());
|
||||||
|
legend.steps = parseInt($("#maplegendsteps").val());
|
||||||
|
legend.hide_invalid = $("#maplegendhideinvalid").prop('checked') ? 1 : 0;
|
||||||
|
legend.hide_overspeed = $("#maplegendhideoverspeed").prop('checked') ? 1 : 0;
|
||||||
|
|
||||||
|
var map_reverse_arrows = $("#mapreversearrows").prop('checked') ? 1 : 0;
|
||||||
|
var map_edge_sep = $("#mapedgesep").val();
|
||||||
|
|
||||||
if(!isNaN(width)) {
|
if(!isNaN(width)) {
|
||||||
width = width + "px";
|
width = width + "px";
|
||||||
}
|
}
|
||||||
@@ -84,7 +159,15 @@
|
|||||||
name: name,
|
name: name,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
node_align: node_align
|
node_align: node_align,
|
||||||
|
reverse_arrows: map_reverse_arrows,
|
||||||
|
edge_separation: map_edge_sep,
|
||||||
|
legend_x: legend.x,
|
||||||
|
legend_y: legend.y,
|
||||||
|
legend_steps: legend.steps,
|
||||||
|
legend_font_size: legend.font_size,
|
||||||
|
legend_hide_invalid: legend.hide_invalid,
|
||||||
|
legend_hide_overspeed: legend.hide_overspeed,
|
||||||
},
|
},
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
type: method
|
type: method
|
||||||
@@ -105,4 +188,22 @@
|
|||||||
$("#map-saveButton").removeAttr('disabled');
|
$("#map-saveButton").removeAttr('disabled');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleMapLegend() {
|
||||||
|
if($("#maplegend").prop('checked')) {
|
||||||
|
$(".maplegend").show();
|
||||||
|
} else {
|
||||||
|
$(".maplegend").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
if(legend.x < 0 || legend.y < 0) {
|
||||||
|
$(".maplegend").hide();
|
||||||
|
}
|
||||||
|
$("#mapreversearrows").bootstrapSwitch('state', Boolean(reverse_arrows));
|
||||||
|
$("#maplegend").bootstrapSwitch('state', (legend.x >= 0 && legend.y >= 0));
|
||||||
|
$("#maplegendhideinvalid").bootstrapSwitch('state', Boolean(legend.hide_invalid));
|
||||||
|
$("#maplegendhideoverspeed").bootstrapSwitch('state', Boolean(legend.hide_overspeed));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -26,6 +26,9 @@
|
|||||||
@section('scripts')
|
@section('scripts')
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var bgimage = {{ $background ? "true" : "false" }};
|
var bgimage = {{ $background ? "true" : "false" }};
|
||||||
|
var screenshot = {{ $screenshot ? "true" : "false" }};
|
||||||
|
var reverse_arrows = {{$reverse_arrows}};
|
||||||
|
var legend = @json($legend);
|
||||||
var network;
|
var network;
|
||||||
var network_height;
|
var network_height;
|
||||||
var network_width;
|
var network_width;
|
||||||
@@ -36,6 +39,60 @@
|
|||||||
var node_link_map = {};
|
var node_link_map = {};
|
||||||
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
|
var custom_image_base = "{{ $base_url }}images/custommap/icons/";
|
||||||
|
|
||||||
|
function legendPctColour(pct) {
|
||||||
|
if (pct < 0) {
|
||||||
|
return "black";
|
||||||
|
} else if (pct < 50) {
|
||||||
|
// 100% green and slowly increase the red until we get to yellow
|
||||||
|
return '#' + parseInt(5.1 * pct).toString(16).padStart(2, 0) + 'ff00';
|
||||||
|
} else if (pct < 100) {
|
||||||
|
// 100% red and slowly remove green to go from yellow to red
|
||||||
|
return '#ff' + parseInt(5.1 * (100.0 - pct)).toString(16).padStart(2, 0) + '00';
|
||||||
|
} else if (pct < 150) {
|
||||||
|
// 100% red and slowly increase blue to go purple
|
||||||
|
return '#ff00' + parseInt(5.1 * (pct - 100.0)).toString(16).padStart(2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to purple for links over 150%
|
||||||
|
return '#ff00ff';
|
||||||
|
}
|
||||||
|
|
||||||
|
function redrawLegend() {
|
||||||
|
// Clear out the old legend
|
||||||
|
old_nodes = network_nodes.get({filter: function(node) { return node.id.startsWith("legend_") }});
|
||||||
|
old_nodes.forEach((node) => {
|
||||||
|
network_nodes.remove(node.id);
|
||||||
|
});
|
||||||
|
if (legend.x >= 0) {
|
||||||
|
let y_pos = legend.y;
|
||||||
|
let y_inc = legend.font_size + 10;
|
||||||
|
|
||||||
|
let legend_header = {id: "legend_header", label: "<b>Legend</b>", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {multi: 'html', size: legend.font_size}, color: {background: "white"}};
|
||||||
|
network_nodes.add(legend_header);
|
||||||
|
y_pos += y_inc;
|
||||||
|
|
||||||
|
if (!(Boolean(legend.hide_invalid))) {
|
||||||
|
let legend_invalid = {id: "legend_invalid", label: "???", title: "Link is down or link speed is not defined", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {face: 'courier new', size: legend.font_size, color: "white"}, color: {background: "black"}};
|
||||||
|
y_pos += y_inc;
|
||||||
|
network_nodes.add(legend_invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pct_step;
|
||||||
|
if (Boolean(legend.hide_overspeed)) {
|
||||||
|
pct_step = 100.0 / (legend.steps - 1);
|
||||||
|
} else {
|
||||||
|
pct_step = 150.0 / (legend.steps - 1);
|
||||||
|
}
|
||||||
|
for (let i=0; i < legend.steps; i++) {
|
||||||
|
let this_pct = Math.round(pct_step * i);
|
||||||
|
let legend_step = {id: "legend_" + i.toString(), label: this_pct.toString().padStart(3, " ") + "%", shape: "box", borderWidth: 0, x: legend.x, y: y_pos, font: {face: 'courier new', size: legend.font_size, color: "black"}, color: {background: legendPctColour(this_pct)}};
|
||||||
|
network_nodes.add(legend_step);
|
||||||
|
y_pos += y_inc;
|
||||||
|
}
|
||||||
|
network_nodes.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function CreateNetwork() {
|
function CreateNetwork() {
|
||||||
// Flush the nodes and edges so they are rendered immediately
|
// Flush the nodes and edges so they are rendered immediately
|
||||||
network_nodes.flush();
|
network_nodes.flush();
|
||||||
@@ -95,7 +152,7 @@
|
|||||||
} else {
|
} else {
|
||||||
node_cfg.title = null;
|
node_cfg.title = null;
|
||||||
}
|
}
|
||||||
node_cfg.label = node.label;
|
node_cfg.label = screenshot ? node.label.replace(/./g, ' ') : node.label;
|
||||||
node_cfg.shape = node.style;
|
node_cfg.shape = node.style;
|
||||||
node_cfg.borderWidth = node.border_width;
|
node_cfg.borderWidth = node.border_width;
|
||||||
node_cfg.x = node.x_pos;
|
node_cfg.x = node.x_pos;
|
||||||
@@ -134,10 +191,16 @@
|
|||||||
var mid_x = edge.mid_x;
|
var mid_x = edge.mid_x;
|
||||||
var mid_y = edge.mid_y;
|
var mid_y = edge.mid_y;
|
||||||
|
|
||||||
var mid = {id: edgeid + "_mid", shape: "dot", size: 0, x: mid_x, y: mid_y};
|
var mid = {id: edgeid + "_mid", shape: "dot", size: 0, x: mid_x, y: mid_y, label: screenshot ? '' : edge.label};
|
||||||
|
|
||||||
var edge1 = {id: edgeid + "_from", from: edge.custom_map_node1_id, to: edgeid + "_mid", arrows: {to: {enabled: true, scaleFactor: 0.6}}, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
if (Boolean(reverse_arrows)) {
|
||||||
var edge2 = {id: edgeid + "_to", from: edge.custom_map_node2_id, to: edgeid + "_mid", arrows: {to: {enabled: true, scaleFactor: 0.6}}, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
arrows = {from: {enabled: true, scaleFactor: 0.6}, to: {enabled: false}};
|
||||||
|
} else {
|
||||||
|
arrows = {to: {enabled: true, scaleFactor: 0.6}, from: {enabled: false}};
|
||||||
|
}
|
||||||
|
|
||||||
|
var edge1 = {id: edgeid + "_from", from: edge.custom_map_node1_id, to: edgeid + "_mid", arrows: arrows, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
||||||
|
var edge2 = {id: edgeid + "_to", from: edge.custom_map_node2_id, to: edgeid + "_mid", arrows: arrows, font: {face: edge.text_face, size: edge.text_size, color: edge.text_colour}, smooth: {type: edge.style}};
|
||||||
|
|
||||||
// Special case for curved lines
|
// Special case for curved lines
|
||||||
if(edge2.smooth.type == "curvedCW") {
|
if(edge2.smooth.type == "curvedCW") {
|
||||||
@@ -146,15 +209,35 @@
|
|||||||
edge2.smooth.type = "curvedCW";
|
edge2.smooth.type = "curvedCW";
|
||||||
}
|
}
|
||||||
if(edge.port_id) {
|
if(edge.port_id) {
|
||||||
edge1.title = edge2.title = edge.port_info;
|
var edge_port_from;
|
||||||
if(edge.showpct) {
|
var edge_port_to;
|
||||||
edge1.label = edge.port_frompct + "%";
|
if (Boolean(reverse_arrows)) {
|
||||||
edge2.label = edge.port_topct + "%";
|
port_from = edge2;
|
||||||
|
port_to = edge1;
|
||||||
|
} else {
|
||||||
|
port_from = edge1;
|
||||||
|
port_to = edge2;
|
||||||
}
|
}
|
||||||
edge1.color = {color: edge.colour_from};
|
port_from.title = port_to.title = edge.port_info;
|
||||||
edge1.width = edge.width_from;
|
if(edge.showpct) {
|
||||||
edge2.color = {color: edge.colour_to};
|
port_from.label = edge.port_frompct + "%";
|
||||||
edge2.width = edge.width_to;
|
port_to.label = edge.port_topct + "%";
|
||||||
|
}
|
||||||
|
if(edge.showbps) {
|
||||||
|
if(port_from.label == null) {
|
||||||
|
port_from.label = '';
|
||||||
|
port_to.label = '';
|
||||||
|
} else {
|
||||||
|
port_from.label += "\n";
|
||||||
|
port_to.label += "\n";
|
||||||
|
}
|
||||||
|
port_from.label += edge.port_frombps;
|
||||||
|
port_to.label += edge.port_tobps;
|
||||||
|
}
|
||||||
|
port_from.color = {color: edge.colour_from};
|
||||||
|
port_from.width = edge.width_from;
|
||||||
|
port_to.color = {color: edge.colour_to};
|
||||||
|
port_to.width = edge.width_to;
|
||||||
|
|
||||||
edge_port_map[edgeid] = {device_id: edge.device_id, port_id: edge.port_id};
|
edge_port_map[edgeid] = {device_id: edge.device_id, port_id: edge.port_id};
|
||||||
} else {
|
} else {
|
||||||
@@ -186,6 +269,9 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Re-draw the legend
|
||||||
|
redrawLegend();
|
||||||
|
|
||||||
// Flush in order to make sure nodes exist for edges to connect to
|
// Flush in order to make sure nodes exist for edges to connect to
|
||||||
network_nodes.flush();
|
network_nodes.flush();
|
||||||
network_edges.flush();
|
network_edges.flush();
|
||||||
|
Reference in New Issue
Block a user