mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel
This commit is contained in:
commit
b4acec0fe6
|
@ -124,6 +124,7 @@ ReplicationFetcher::~ReplicationFetcher () {
|
|||
delete _endpoint;
|
||||
}
|
||||
|
||||
TRI_DestroyApplyStateReplicationApplier(&_applyState);
|
||||
TRI_DestroyMasterInfoReplication(&_masterInfo);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ extern "C" {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief static factory method
|
||||
/// @brief static create method
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* TRI_CreateFetcherReplication (TRI_vocbase_t* vocbase,
|
||||
|
@ -58,6 +58,16 @@ void* TRI_CreateFetcherReplication (TRI_vocbase_t* vocbase,
|
|||
return (void*) f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief static free method
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_DeleteFetcherReplication (void* ptr) {
|
||||
ReplicationFetcher* f = static_cast<ReplicationFetcher*>(ptr);
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief static run method
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -50,13 +50,19 @@ struct TRI_vocbase_s;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief static factory method
|
||||
/// @brief static create method
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* TRI_CreateFetcherReplication (struct TRI_vocbase_s*,
|
||||
const char*,
|
||||
double);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief static free method
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_DeleteFetcherReplication (void*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief static run method
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -104,18 +104,64 @@ static int ReadTick (TRI_json_t const* json,
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief get the filename of the replication application file
|
||||
/// @brief get the filename of the replication apply configuration file
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static char* GetApplyConfigurationFilename (TRI_vocbase_t* vocbase) {
|
||||
return TRI_Concatenate2File(vocbase->_path, "REPLICATION-CONFIG");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief get a JSON representation of the replication apply configuration
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static TRI_json_t* JsonApplyConfiguration (TRI_replication_apply_configuration_t const* config) {
|
||||
TRI_json_t* json;
|
||||
|
||||
assert(config->_endpoint != NULL);
|
||||
|
||||
json = TRI_CreateArray2Json(TRI_CORE_MEM_ZONE, 4);
|
||||
|
||||
if (json == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE,
|
||||
json,
|
||||
"endpoint",
|
||||
TRI_CreateStringCopyJson(TRI_CORE_MEM_ZONE, config->_endpoint));
|
||||
|
||||
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE,
|
||||
json,
|
||||
"timeout",
|
||||
TRI_CreateNumberJson(TRI_CORE_MEM_ZONE, config->_timeout));
|
||||
|
||||
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE,
|
||||
json,
|
||||
"ignoreErrors",
|
||||
TRI_CreateNumberJson(TRI_CORE_MEM_ZONE, (double) config->_ignoreErrors));
|
||||
|
||||
TRI_Insert3ArrayJson(TRI_CORE_MEM_ZONE,
|
||||
json,
|
||||
"maxConnectRetries",
|
||||
TRI_CreateNumberJson(TRI_CORE_MEM_ZONE, (double) config->_maxConnectRetries));
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief get the filename of the replication apply state file
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static char* GetApplyStateFilename (TRI_vocbase_t* vocbase) {
|
||||
return TRI_Concatenate2File(vocbase->_path, "REPLICATION");
|
||||
return TRI_Concatenate2File(vocbase->_path, "REPLICATION-STATE");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief get a JSON representation of the replication apply state
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static TRI_json_t* ApplyStateToJson (TRI_replication_apply_state_t const* state) {
|
||||
static TRI_json_t* JsonApplyState (TRI_replication_apply_state_t const* state) {
|
||||
TRI_json_t* json;
|
||||
char* serverId;
|
||||
char* lastProcessedContinuousTick;
|
||||
|
@ -224,6 +270,8 @@ void ApplyLoop (void* data) {
|
|||
if (isActive) {
|
||||
int res;
|
||||
|
||||
assert(applier->_fetcher != NULL);
|
||||
|
||||
res = TRI_RunFetcherReplication(applier->_fetcher, false, 0);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
|
@ -250,7 +298,7 @@ static int StartApplier (TRI_replication_applier_t* applier) {
|
|||
}
|
||||
|
||||
if (state->_endpoint == NULL) {
|
||||
return SetError(applier, TRI_ERROR_REPLICATION_INVALID_APPLY_STATE, NULL);
|
||||
return SetError(applier, TRI_ERROR_REPLICATION_INVALID_APPLY_STATE, "no endpoint configured");
|
||||
}
|
||||
|
||||
applier->_fetcher = (void*) TRI_CreateFetcherReplication(applier->_vocbase, state->_endpoint, 600.0);
|
||||
|
@ -676,7 +724,7 @@ int TRI_SaveStateFileReplicationApplier (TRI_vocbase_t* vocbase,
|
|||
return TRI_ERROR_REPLICATION_INVALID_APPLY_STATE;
|
||||
}
|
||||
|
||||
json = ApplyStateToJson(state);
|
||||
json = JsonApplyState(state);
|
||||
|
||||
if (json == NULL) {
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -685,7 +733,7 @@ int TRI_SaveStateFileReplicationApplier (TRI_vocbase_t* vocbase,
|
|||
filename = GetApplyStateFilename(vocbase);
|
||||
|
||||
if (! TRI_SaveJson(filename, json, sync)) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
res = TRI_errno();
|
||||
}
|
||||
else {
|
||||
res = TRI_ERROR_NO_ERROR;
|
||||
|
@ -772,17 +820,8 @@ int TRI_LoadStateFileReplicationApplier (TRI_vocbase_t* vocbase,
|
|||
/// @brief initialise an apply configuration
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_InitApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t* config,
|
||||
char* endpoint,
|
||||
double timeout,
|
||||
uint64_t ignoreErrors,
|
||||
int maxConnectRetries) {
|
||||
assert(endpoint != NULL);
|
||||
|
||||
config->_endpoint = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, endpoint);
|
||||
config->_timeout = timeout;
|
||||
config->_ignoreErrors = ignoreErrors;
|
||||
config->_maxConnectRetries = maxConnectRetries;
|
||||
void TRI_InitApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t* config) {
|
||||
memset(config, 0, sizeof(TRI_replication_apply_configuration_t));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -796,6 +835,150 @@ void TRI_DestroyApplyConfigurationReplicationApplier (TRI_replication_apply_conf
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set the initial apply configuration
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_SetApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t* config,
|
||||
char* endpoint,
|
||||
double timeout,
|
||||
uint64_t ignoreErrors,
|
||||
int maxConnectRetries) {
|
||||
assert(endpoint != NULL);
|
||||
|
||||
config->_endpoint = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, endpoint);
|
||||
config->_timeout = timeout;
|
||||
config->_ignoreErrors = ignoreErrors;
|
||||
config->_maxConnectRetries = maxConnectRetries;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove the replication application configuration file
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_RemoveConfigurationFileReplicationApplier (TRI_vocbase_t* vocbase) {
|
||||
char* filename;
|
||||
int res;
|
||||
|
||||
filename = GetApplyConfigurationFilename(vocbase);
|
||||
|
||||
if (filename == NULL) {
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (TRI_ExistsFile(filename)) {
|
||||
res = TRI_UnlinkFile(filename);
|
||||
}
|
||||
else {
|
||||
res = TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief save the replication application configuration to a file
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_SaveConfigurationFileReplicationApplier (TRI_vocbase_t* vocbase,
|
||||
TRI_replication_apply_configuration_t const* config,
|
||||
bool sync) {
|
||||
TRI_json_t* json;
|
||||
char* filename;
|
||||
int res;
|
||||
|
||||
json = JsonApplyConfiguration(config);
|
||||
|
||||
if (json == NULL) {
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
filename = GetApplyConfigurationFilename(vocbase);
|
||||
|
||||
if (! TRI_SaveJson(filename, json, sync)) {
|
||||
res = TRI_errno();
|
||||
}
|
||||
else {
|
||||
res = TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
TRI_FreeJson(TRI_CORE_MEM_ZONE, json);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief load the replication application configuration from a file
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_LoadConfigurationFileReplicationApplier (TRI_vocbase_t* vocbase,
|
||||
TRI_replication_apply_configuration_t* config) {
|
||||
TRI_json_t* json;
|
||||
TRI_json_t* value;
|
||||
char* filename;
|
||||
char* error;
|
||||
int res;
|
||||
|
||||
TRI_InitApplyConfigurationReplicationApplier(config);
|
||||
filename = GetApplyConfigurationFilename(vocbase);
|
||||
|
||||
if (! TRI_ExistsFile(filename)) {
|
||||
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
|
||||
|
||||
return TRI_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
error = NULL;
|
||||
json = TRI_JsonFile(TRI_CORE_MEM_ZONE, filename, &error);
|
||||
|
||||
if (json == NULL || json->_type != TRI_JSON_ARRAY) {
|
||||
if (error != NULL) {
|
||||
TRI_Free(TRI_CORE_MEM_ZONE, error);
|
||||
}
|
||||
|
||||
return TRI_ERROR_REPLICATION_INVALID_CONFIGURATION;
|
||||
}
|
||||
|
||||
res = TRI_ERROR_NO_ERROR;
|
||||
|
||||
// read the server id
|
||||
value = TRI_LookupArrayJson(json, "endpoint");
|
||||
|
||||
if (! TRI_IsStringJson(value)) {
|
||||
res = TRI_ERROR_REPLICATION_INVALID_CONFIGURATION;
|
||||
}
|
||||
else {
|
||||
config->_endpoint = TRI_DuplicateString2Z(TRI_CORE_MEM_ZONE,
|
||||
value->_value._string.data,
|
||||
value->_value._string.length - 1);
|
||||
}
|
||||
|
||||
value = TRI_LookupArrayJson(json, "timeout");
|
||||
|
||||
if (! TRI_IsNumberJson(value)) {
|
||||
res = TRI_ERROR_REPLICATION_INVALID_CONFIGURATION;
|
||||
}
|
||||
else {
|
||||
config->_timeout = value->_value._number;
|
||||
}
|
||||
|
||||
value = TRI_LookupArrayJson(json, "maxConnectRetries");
|
||||
|
||||
if (! TRI_IsNumberJson(value)) {
|
||||
res = TRI_ERROR_REPLICATION_INVALID_CONFIGURATION;
|
||||
}
|
||||
else {
|
||||
config->_maxConnectRetries = (int) value->_value._number;
|
||||
}
|
||||
|
||||
TRI_Free(TRI_CORE_MEM_ZONE, json);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -116,8 +116,7 @@ typedef struct TRI_replication_apply_state_s {
|
|||
TRI_voc_tick_t _lastAppliedInitialTick;
|
||||
TRI_server_id_t _serverId;
|
||||
TRI_replication_apply_error_t _lastError;
|
||||
TRI_replication_apply_configuration_t _configuration;
|
||||
char* _endpoint;
|
||||
char* _endpoint;
|
||||
}
|
||||
TRI_replication_apply_state_t;
|
||||
|
||||
|
@ -134,6 +133,7 @@ typedef struct TRI_replication_applier_s {
|
|||
TRI_thread_t _thread;
|
||||
void* _fetcher;
|
||||
char* _databaseName;
|
||||
TRI_replication_apply_configuration_t _configuration;
|
||||
}
|
||||
TRI_replication_applier_t;
|
||||
|
||||
|
@ -275,11 +275,7 @@ int TRI_LoadStateFileReplicationApplier (struct TRI_vocbase_s*,
|
|||
/// @brief initialise an apply configuration
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_InitApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t*,
|
||||
char*,
|
||||
double,
|
||||
uint64_t,
|
||||
int);
|
||||
void TRI_InitApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy an apply configuration
|
||||
|
@ -287,6 +283,16 @@ void TRI_InitApplyConfigurationReplicationApplier (TRI_replication_apply_configu
|
|||
|
||||
void TRI_DestroyApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set the initial apply configuration
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_SetApplyConfigurationReplicationApplier (TRI_replication_apply_configuration_t*,
|
||||
char*,
|
||||
double,
|
||||
uint64_t,
|
||||
int);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -6,3 +6,8 @@
|
|||
margin-left: 1px !important;
|
||||
}
|
||||
|
||||
.svgFigures {
|
||||
margin-top: 0px !important;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
}
|
||||
|
|
|
@ -47,8 +47,22 @@
|
|||
box-shadow: 0 0 0 !important;
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
.span3 .icon-edit, .span3 .icon-info-sign {
|
||||
position: absolute;
|
||||
margin-top: 3px;
|
||||
margin-right: 5px;
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.span3 .icon-edit {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.span3 .icon-info-sign {
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.icon-edit:hover, .icon-info-sign:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#tableView {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#sourceFooter {
|
||||
margin-top: 0;
|
||||
background: none;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#loginWindow {
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
border: 1px solid #868686;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -150px;
|
||||
margin-left: -220px !important;
|
||||
background-color: #F4F4F4;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#loginLogo {
|
||||
}
|
||||
|
||||
#loginWindow {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#loginSpace {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#loginForm {
|
||||
}
|
||||
|
||||
.loginInput {
|
||||
width: 387px;
|
||||
}
|
|
@ -3,6 +3,26 @@
|
|||
text-align:left;
|
||||
}
|
||||
|
||||
.collectinInfoTable {
|
||||
width: 200px !important;
|
||||
max-width: 200px !important;
|
||||
}
|
||||
|
||||
.collectionInfoTh {
|
||||
text-align:left;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.modal-body-right, .modal-body-left {
|
||||
float:left;
|
||||
width:50%;
|
||||
max-width: 50% !important;
|
||||
}
|
||||
|
||||
.tooltipInfoTh {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.modal-body .icon-info-sign {
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -31,6 +31,7 @@
|
|||
<link href="css/swaggerView.css" rel="stylesheet">
|
||||
<link href="css/foxxView.css" rel="stylesheet">
|
||||
<link href="css/graphView.css" rel="stylesheet">
|
||||
<link href="css/loginView.css" rel="stylesheet">
|
||||
|
||||
<link href="css/jquery.snippet.css" rel="stylesheet">
|
||||
<link href="css/jquery.gritter.css" rel="stylesheet">
|
||||
|
@ -162,6 +163,7 @@
|
|||
<script src="js/models/arangoStatistics.js"></script>
|
||||
<script src="js/models/arangoStatisticsDescription.js"></script>
|
||||
<script src="js/models/foxx.js"></script>
|
||||
<script src="js/models/arangoSession.js"></script>
|
||||
|
||||
<!-- collections -->
|
||||
<script src="js/collections/arangoCollections.js"></script>
|
||||
|
@ -171,6 +173,7 @@
|
|||
<script src="js/collections/arangoStatisticsCollection.js"></script>
|
||||
<script src="js/collections/arangoStatisticsDescriptionCollection.js"></script>
|
||||
<script src="js/collections/foxxCollection.js"></script>
|
||||
<script src="js/collections/arangoSession.js"></script>
|
||||
|
||||
<!-- views -->
|
||||
<script src="js/views/navigationView.js"></script>
|
||||
|
@ -182,6 +185,7 @@
|
|||
<script src="js/views/dashboardView.js"></script>
|
||||
<script src="js/views/collectionsView.js"></script>
|
||||
<script src="js/views/collectionView.js"></script>
|
||||
<script src="js/views/collectionInfoView.js"></script>
|
||||
<script src="js/views/newCollectionView.js"></script>
|
||||
<script src="js/views/collectionsItemView.js"></script>
|
||||
<script src="js/views/documentsView.js"></script>
|
||||
|
@ -197,6 +201,7 @@
|
|||
<script src="js/views/foxxMountView.js"></script>
|
||||
<script src="js/views/appDocumentationView.js"></script>
|
||||
<script src="js/views/graphView.js"></script>
|
||||
<script src="js/views/loginView.js"></script>
|
||||
|
||||
<!-- router -->
|
||||
<script src="js/routers/router.js"></script>
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
"ERROR_REPLICATION_INVALID_APPLY_STATE" : { "code" : 1407, "message" : "invalid apply state" },
|
||||
"ERROR_REPLICATION_UNEXPECTED_TRANSACTION" : { "code" : 1408, "message" : "invalid transaction" },
|
||||
"ERROR_REPLICATION_STOPPED" : { "code" : 1409, "message" : "replication stopped" },
|
||||
"ERROR_REPLICATION_INVALID_CONFIGURATION" : { "code" : 1410, "message" : "invalid replication apply configuration" },
|
||||
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
|
||||
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
|
||||
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },
|
||||
|
|
|
@ -169,6 +169,24 @@ window.arangoCollections = Backbone.Collection.extend({
|
|||
});
|
||||
return data2;
|
||||
},
|
||||
getFigures: function (id) {
|
||||
var data2;
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
url: "/_api/collection/" + id + "/figures",
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
async: false,
|
||||
success: function(data) {
|
||||
data2 = data;
|
||||
},
|
||||
error: function(data) {
|
||||
data2 = data;
|
||||
}
|
||||
});
|
||||
return data2;
|
||||
},
|
||||
checkCollectionName: function (name) {
|
||||
},
|
||||
newCollection: function (collName, wfs, isSystem, journalSize, collType) {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global window, Backbone */
|
||||
|
||||
window.ArangoSession = Backbone.Collection.extend({
|
||||
model: window.Session,
|
||||
|
||||
activeUser: "",
|
||||
activeUserSettings: {
|
||||
"query" : {},
|
||||
"shell" : {},
|
||||
"testing": true
|
||||
},
|
||||
|
||||
url: "../api/user",
|
||||
|
||||
initialize: function() {
|
||||
//check cookies / local storage
|
||||
},
|
||||
|
||||
login: function (username, password) {
|
||||
this.activeUser = username;
|
||||
return true;
|
||||
},
|
||||
|
||||
logout: function () {
|
||||
this.activeUser = undefined;
|
||||
this.reset();
|
||||
},
|
||||
|
||||
setUserSettings: function (identifier, content) {
|
||||
this.activeUserSettings.identifier = content;
|
||||
},
|
||||
|
||||
loadUserSettings: function () {
|
||||
var self = this;
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
url: "/_api/user/" + self.activeUser,
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
async: false,
|
||||
success: function(data) {
|
||||
self.activeUserSettings = data.extra;
|
||||
},
|
||||
error: function(data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
saveUserSettings: function () {
|
||||
|
||||
var self = this;
|
||||
console.log(self.activeUserSettings);
|
||||
$.ajax({
|
||||
cache: false,
|
||||
type: "PUT",
|
||||
async: false, // sequential calls!
|
||||
url: "/_api/user/" + self.activeUser,
|
||||
data: JSON.stringify({ extra: self.activeUserSettings }),
|
||||
contentType: "application/json",
|
||||
processData: false,
|
||||
success: function(data) {
|
||||
console.log(data);
|
||||
},
|
||||
error: function(data) {
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
|
@ -85,6 +85,37 @@ function JSONAdapter(jsonPath, nodes, edges, width, height) {
|
|||
self.loadNodeFromTreeById(nodeId, callback);
|
||||
};
|
||||
|
||||
self.loadInitialNode = function(nodeId, callback) {
|
||||
var json = jsonPath + nodeId + ".json";
|
||||
absAdapter.cleanUp();
|
||||
d3.json(json, function(error, node) {
|
||||
if (error !== undefined && error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
var n = absAdapter.insertInitialNode(node);
|
||||
self.requestCentralityChildren(nodeId, function(c) {
|
||||
n._centrality = c;
|
||||
});
|
||||
_.each(node.children, function(c) {
|
||||
var t = absAdapter.insertNode(c),
|
||||
e = {
|
||||
_from: n._id,
|
||||
_to: t._id,
|
||||
_id: n._id + "-" + t._id
|
||||
};
|
||||
absAdapter.insertEdge(e);
|
||||
self.requestCentralityChildren(t._id, function(c) {
|
||||
t._centrality = c;
|
||||
});
|
||||
delete t._data.children;
|
||||
});
|
||||
delete n._data.children;
|
||||
if (callback) {
|
||||
callback(n);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.loadNodeFromTreeById = function(nodeId, callback) {
|
||||
var json = jsonPath + nodeId + ".json";
|
||||
d3.json(json, function(error, node) {
|
||||
|
@ -136,6 +167,10 @@ function JSONAdapter(jsonPath, nodes, edges, width, height) {
|
|||
throw "Sorry this adapter is read-only";
|
||||
};
|
||||
|
||||
self.loadInitialNodeByAttributeValue = function(attribute, value, callback) {
|
||||
throw "Sorry this adapter is read-only";
|
||||
};
|
||||
|
||||
self.createEdge = function(edgeToCreate, callback){
|
||||
throw "Sorry this adapter is read-only";
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
function AbstractAdapter(nodes, edges, descendant) {
|
||||
function AbstractAdapter(nodes, edges, descendant, config) {
|
||||
"use strict";
|
||||
|
||||
if (nodes === undefined) {
|
||||
|
@ -42,6 +42,7 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
if (descendant === undefined) {
|
||||
throw "An inheriting class has to be given.";
|
||||
}
|
||||
config = config || {};
|
||||
|
||||
var self = this,
|
||||
isRunning = false,
|
||||
|
@ -55,6 +56,12 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
childLimit,
|
||||
exports = {},
|
||||
|
||||
changeTo = function (config) {
|
||||
if (config.prioList !== undefined) {
|
||||
reducer.changePrioList(config.prioList || []);
|
||||
}
|
||||
},
|
||||
|
||||
setWidth = function(w) {
|
||||
initialX.range = w / 2;
|
||||
initialX.start = w / 4;
|
||||
|
@ -115,6 +122,21 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
return node;
|
||||
},
|
||||
|
||||
insertInitialNode = function(data) {
|
||||
var n = insertNode(data);
|
||||
n.x = initialX.start * 2;
|
||||
n.y = initialY.start * 2;
|
||||
n.fixed = true;
|
||||
return n;
|
||||
},
|
||||
|
||||
cleanUp = function() {
|
||||
nodes.length = 0;
|
||||
edges.length = 0;
|
||||
joinedInCommunities = {};
|
||||
cachedCommunities = {};
|
||||
},
|
||||
|
||||
insertEdge = function(data) {
|
||||
var source,
|
||||
target,
|
||||
|
@ -293,7 +315,7 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
}
|
||||
},
|
||||
|
||||
collapseCommunity = function (community) {
|
||||
collapseCommunity = function (community, reason) {
|
||||
if (!community || community.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -308,6 +330,9 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
commNode.x = nodesToRemove[0].x;
|
||||
commNode.y = nodesToRemove[0].y;
|
||||
commNode._size = community.length;
|
||||
if (reason) {
|
||||
commNode._reason = reason;
|
||||
}
|
||||
cachedCommunities[commId] = {};
|
||||
cachedCommunities[commId].nodes = nodesToRemove;
|
||||
cachedCommunities[commId].edges = [];
|
||||
|
@ -401,11 +426,11 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
if (_.size(inserted) > childLimit) {
|
||||
var buckets = reducer.bucketNodes(_.values(inserted), childLimit);
|
||||
_.each(buckets, function(b) {
|
||||
if (b.length > 1) {
|
||||
var ids = _.map(b, function(n) {
|
||||
if (b.nodes.length > 1) {
|
||||
var ids = _.map(b.nodes, function(n) {
|
||||
return n._id;
|
||||
});
|
||||
collapseCommunity(ids);
|
||||
collapseCommunity(ids, b.reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -454,15 +479,22 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
|
||||
childLimit = Number.POSITIVE_INFINITY;
|
||||
|
||||
reducer = new NodeReducer(nodes, edges);
|
||||
if (config.prioList) {
|
||||
reducer = new NodeReducer(nodes, edges, config.prioList);
|
||||
} else {
|
||||
reducer = new NodeReducer(nodes, edges);
|
||||
}
|
||||
joiner = new WebWorkerWrapper(ModularityJoiner, joinerCb);
|
||||
|
||||
initialX.getStart = function() {return 0;};
|
||||
initialY.getStart = function() {return 0;};
|
||||
|
||||
exports.cleanUp = cleanUp;
|
||||
|
||||
exports.setWidth = setWidth;
|
||||
exports.setHeight = setHeight;
|
||||
exports.insertNode = insertNode;
|
||||
exports.insertInitialNode = insertInitialNode;
|
||||
exports.insertEdge = insertEdge;
|
||||
|
||||
exports.removeNode = removeNode;
|
||||
|
@ -479,5 +511,7 @@ function AbstractAdapter(nodes, edges, descendant) {
|
|||
|
||||
exports.explore = explore;
|
||||
|
||||
exports.changeTo = changeTo;
|
||||
|
||||
return exports;
|
||||
}
|
|
@ -48,7 +48,8 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
}
|
||||
|
||||
var self = this,
|
||||
absAdapter = new AbstractAdapter(nodes, edges, this),
|
||||
absAdapter,
|
||||
absConfig = {},
|
||||
api = {},
|
||||
queries = {},
|
||||
nodeCollection,
|
||||
|
@ -129,7 +130,8 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
var inserted = {},
|
||||
n = absAdapter.insertNode(result[0].vertex),
|
||||
oldLength = nodes.length,
|
||||
com, buckets;
|
||||
com, buckets;
|
||||
|
||||
_.each(result, function(visited) {
|
||||
var node = absAdapter.insertNode(visited.vertex),
|
||||
path = visited.path;
|
||||
|
@ -198,7 +200,13 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
_.each(res, self.deleteEdge);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
if (config.prioList) {
|
||||
absConfig.prioList = config.prioList;
|
||||
}
|
||||
absAdapter = new AbstractAdapter(nodes, edges, this, absConfig);
|
||||
|
||||
parseConfig(config);
|
||||
|
||||
api.base = arangodb.lastIndexOf("http://", 0) === 0
|
||||
|
@ -270,6 +278,14 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
self.loadNodeFromTreeById(nodeId, callback);
|
||||
};
|
||||
|
||||
self.loadInitialNode = function(nodeId, callback) {
|
||||
absAdapter.cleanUp();
|
||||
var cb = function(n) {
|
||||
callback(absAdapter.insertInitialNode(n));
|
||||
};
|
||||
self.loadNode(nodeId, cb);
|
||||
};
|
||||
|
||||
self.loadNodeFromTreeById = function(nodeId, callback) {
|
||||
sendQuery(queries.traversalById, {
|
||||
id: nodeId
|
||||
|
@ -286,6 +302,14 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
});
|
||||
};
|
||||
|
||||
self.loadInitialNodeByAttributeValue = function(attribute, value, callback) {
|
||||
absAdapter.cleanUp();
|
||||
var cb = function(n) {
|
||||
callback(absAdapter.insertInitialNode(n));
|
||||
};
|
||||
self.loadNodeFromTreeByAttributeValue(attribute, value, cb);
|
||||
};
|
||||
|
||||
self.requestCentralityChildren = function(nodeId, callback) {
|
||||
sendQuery(queries.childrenCentrality,{
|
||||
id: nodeId
|
||||
|
@ -416,7 +440,8 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
});
|
||||
};
|
||||
|
||||
self.changeTo = function (nodesCol, edgesCol, dir) {
|
||||
self.changeToCollections = function (nodesCol, edgesCol, dir) {
|
||||
absAdapter.cleanUp();
|
||||
nodeCollection = nodesCol;
|
||||
edgeCollection = edgesCol;
|
||||
if (dir !== undefined) {
|
||||
|
@ -475,4 +500,6 @@ function ArangoAdapter(nodes, edges, config) {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
self.changeTo = absAdapter.changeTo;
|
||||
}
|
|
@ -84,23 +84,12 @@ function EdgeShaper(parent, flags, idfunc) {
|
|||
},
|
||||
|
||||
getDistance = function(s, t) {
|
||||
if (!s || !s.x || !s.y) {
|
||||
console.log("Source not defined!");
|
||||
console.log(s);
|
||||
}
|
||||
if (!t || !t.x || !t.y) {
|
||||
console.log("Target not defined!");
|
||||
console.log(t);
|
||||
}
|
||||
var res = Math.sqrt(
|
||||
(t.y - s.y)
|
||||
* (t.y - s.y)
|
||||
+ (t.x - s.x)
|
||||
* (t.x - s.x)
|
||||
);
|
||||
if (res === Number.NaN) {
|
||||
console.log(t.x, t.y, s.x, s.y);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
|
@ -130,6 +119,14 @@ function EdgeShaper(parent, flags, idfunc) {
|
|||
+ ")";
|
||||
});
|
||||
line.attr("x2", function(d) {
|
||||
/*
|
||||
if (!d.source.position.x || !d.source.position.y) {
|
||||
console.log(d.source);
|
||||
}
|
||||
if (!d.target.position.x || !d.target.position.y) {
|
||||
console.log(d.target);
|
||||
}
|
||||
*/
|
||||
return getDistance(d.source.position, d.target.position);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -41,8 +41,11 @@ function FoxxAdapter(nodes, edges, route, config) {
|
|||
throw "The route has to be given.";
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
|
||||
var self = this,
|
||||
absAdapter = new AbstractAdapter(nodes, edges, this),
|
||||
absConfig = {},
|
||||
absAdapter,
|
||||
routes = {},
|
||||
baseRoute = route,
|
||||
requestBase = {
|
||||
|
@ -195,8 +198,11 @@ function FoxxAdapter(nodes, edges, route, config) {
|
|||
callback(first);
|
||||
}
|
||||
};
|
||||
|
||||
config = config || {};
|
||||
|
||||
if (config.prioList) {
|
||||
absConfig.prioList = config.prioList;
|
||||
}
|
||||
absAdapter = new AbstractAdapter(nodes, edges, this, absConfig);
|
||||
|
||||
parseConfig(config);
|
||||
fillRoutes();
|
||||
|
@ -209,6 +215,14 @@ function FoxxAdapter(nodes, edges, route, config) {
|
|||
});
|
||||
};
|
||||
|
||||
self.loadInitialNode = function(nodeId, callback) {
|
||||
absAdapter.cleanUp();
|
||||
var cb = function(n) {
|
||||
callback(absAdapter.insertInitialNode(n));
|
||||
};
|
||||
self.loadNode(nodeId, cb);
|
||||
};
|
||||
|
||||
self.requestCentralityChildren = function(nodeId, callback) {
|
||||
/*
|
||||
sendQuery(queries.childrenCentrality,{
|
||||
|
@ -298,4 +312,6 @@ function FoxxAdapter(nodes, edges, route, config) {
|
|||
}
|
||||
};
|
||||
|
||||
self.changeTo = absAdapter.changeTo;
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@
|
|||
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function NodeReducer(nodes, edges) {
|
||||
function NodeReducer(nodes, edges, prioList) {
|
||||
"use strict";
|
||||
|
||||
if (nodes === undefined) {
|
||||
|
@ -38,9 +38,11 @@ function NodeReducer(nodes, edges) {
|
|||
if (edges === undefined) {
|
||||
throw "Edges have to be given.";
|
||||
}
|
||||
|
||||
|
||||
prioList = prioList || [];
|
||||
|
||||
var
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// Private functions //
|
||||
////////////////////////////////////
|
||||
|
@ -53,18 +55,20 @@ function NodeReducer(nodes, edges) {
|
|||
bucket.push(node);
|
||||
},
|
||||
|
||||
getSimilarityValue = function(bucket, node) {
|
||||
if (bucket.length === 0) {
|
||||
getSimilarityValue = function(bucketContainer, node) {
|
||||
if (!bucketContainer.reason.example) {
|
||||
bucketContainer.reason.example = node;
|
||||
return 1;
|
||||
}
|
||||
var comp = bucket[0],
|
||||
props = _.union(_.keys(comp), _.keys(node)),
|
||||
}
|
||||
var data = node._data || {},
|
||||
comp = bucketContainer.reason.example._data || {},
|
||||
props = _.union(_.keys(comp), _.keys(data)),
|
||||
countMatch = 0,
|
||||
propCount = 0;
|
||||
_.each(props, function(key) {
|
||||
if (comp[key] !== undefined && node[key]!== undefined) {
|
||||
if (comp[key] !== undefined && data[key] !== undefined) {
|
||||
countMatch++;
|
||||
if (comp[key] === node[key]) {
|
||||
if (comp[key] === data[key]) {
|
||||
countMatch += 4;
|
||||
}
|
||||
}
|
||||
|
@ -75,32 +79,85 @@ function NodeReducer(nodes, edges) {
|
|||
return countMatch / propCount;
|
||||
},
|
||||
|
||||
changePrioList = function (list) {
|
||||
prioList = list;
|
||||
},
|
||||
|
||||
bucketByPrioList = function (toSort, numBuckets) {
|
||||
var res = {},
|
||||
resArray = [];
|
||||
_.each(toSort, function(n) {
|
||||
var d = n._data,
|
||||
sortTo = {},
|
||||
key,
|
||||
resKey,
|
||||
i = 0;
|
||||
for (i = 0; i < prioList.length; i++) {
|
||||
key = prioList[i];
|
||||
if (d[key] !== undefined) {
|
||||
resKey = d[key];
|
||||
res[key] = res[key] || {};
|
||||
res[key][resKey] = res[key][resKey] || [];
|
||||
res[key][resKey].push(n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
resKey = "default";
|
||||
res[resKey] = res[resKey] || [];
|
||||
res[resKey].push(n);
|
||||
});
|
||||
_.each(res, function(list, key) {
|
||||
_.each(list, function(list, value) {
|
||||
var reason = {
|
||||
key: key,
|
||||
value: value
|
||||
};
|
||||
resArray.push({
|
||||
reason: reason,
|
||||
nodes: list
|
||||
});
|
||||
});
|
||||
});
|
||||
return resArray;
|
||||
},
|
||||
|
||||
bucketNodes = function(toSort, numBuckets) {
|
||||
|
||||
var res = [],
|
||||
threshold = 0.5;
|
||||
if (toSort.length <= numBuckets) {
|
||||
res = _.map(toSort, function(n) {
|
||||
return [n];
|
||||
return {
|
||||
reason: {type: "single"},
|
||||
nodes: [n]
|
||||
};
|
||||
});
|
||||
return res;
|
||||
}
|
||||
if (!_.isEmpty(prioList)) {
|
||||
return bucketByPrioList(toSort, numBuckets);
|
||||
}
|
||||
_.each(toSort, function(n) {
|
||||
var i, shortest, sLength;
|
||||
shortest = 0;
|
||||
sLength = Number.POSITIVE_INFINITY;
|
||||
for (i = 0; i < numBuckets; i++) {
|
||||
res[i] = res[i] || [];
|
||||
res[i] = res[i] || {
|
||||
reason: {
|
||||
type: "similar"
|
||||
},
|
||||
nodes: []
|
||||
};
|
||||
if (getSimilarityValue(res[i], n) > threshold) {
|
||||
addNode(res[i], n);
|
||||
addNode(res[i].nodes, n);
|
||||
return;
|
||||
}
|
||||
if (sLength > res[i].length) {
|
||||
if (sLength > res[i].nodes.length) {
|
||||
shortest = i;
|
||||
sLength = res[i].length;
|
||||
sLength = res[i].nodes.length;
|
||||
}
|
||||
}
|
||||
addNode(res[shortest], n);
|
||||
addNode(res[shortest].nodes, n);
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
@ -111,4 +168,6 @@ function NodeReducer(nodes, edges) {
|
|||
|
||||
this.bucketNodes = bucketNodes;
|
||||
|
||||
this.changePrioList = changePrioList;
|
||||
|
||||
}
|
||||
|
|
|
@ -127,17 +127,49 @@ function NodeShaper(parent, flags, idfunc) {
|
|||
addShape = noop,
|
||||
addLabel = noop,
|
||||
addLabelColor = function() {return "black";},
|
||||
addCommunityShape = function(g) {
|
||||
addCommunityShape = function(g, id) {
|
||||
var move = 9;
|
||||
g.attr("stroke", colourMapper.getForegroundCommunityColour());
|
||||
addShape(g, 9);
|
||||
addShape(g, 6);
|
||||
addShape(g, 3);
|
||||
addShape(g);
|
||||
/* Archive
|
||||
g.append("polygon")
|
||||
.attr("points", "0,-25 -16,20 23,-10 -23,-10 16,20");
|
||||
*/
|
||||
},
|
||||
addCommunityLabel = function(g) {
|
||||
|
||||
var textN = g.append("text") // Append a label for the node
|
||||
.attr("text-anchor", "middle") // Define text-anchor
|
||||
.attr("fill", colourMapper.getForegroundCommunityColour())
|
||||
.attr("stroke", "none"); // Make it readable
|
||||
textN.each(function(d) {
|
||||
var s = d3.select(this);
|
||||
if (d._reason && d._reason.key) {
|
||||
s.append("tspan")
|
||||
.attr("x", "0")
|
||||
.attr("dy", "-4")
|
||||
.text(d._reason.key + ":");
|
||||
s.append("tspan")
|
||||
.attr("x", "0")
|
||||
.attr("dy", "16")
|
||||
.text(d._reason.value);
|
||||
} else {
|
||||
s.text(d._size);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
g.append("text") // Append a label for the node
|
||||
.attr("fill", colourMapper.getForegroundCommunityColour())
|
||||
.attr("text-anchor", "middle") // Define text-anchor
|
||||
.text(function(d) {
|
||||
return d._size;
|
||||
});
|
||||
*/
|
||||
},
|
||||
unbindEvents = function() {
|
||||
// Hard unbind the dragging
|
||||
|
@ -239,10 +271,14 @@ function NodeShaper(parent, flags, idfunc) {
|
|||
break;
|
||||
case NodeShaper.shapes.CIRCLE:
|
||||
radius = shape.radius || 25;
|
||||
addShape = function (node) {
|
||||
addShape = function (node, shift) {
|
||||
node
|
||||
.append("circle") // Display nodes as circles
|
||||
.attr("r", radius); // Set radius
|
||||
if (shift) {
|
||||
node.attr("cx", -shift)
|
||||
.attr("cy", -shift);
|
||||
}
|
||||
};
|
||||
break;
|
||||
case NodeShaper.shapes.RECT:
|
||||
|
@ -253,21 +289,26 @@ function NodeShaper(parent, flags, idfunc) {
|
|||
return -(width(d) / 2);
|
||||
};
|
||||
} else {
|
||||
translateX = -(width / 2);
|
||||
translateX = function(d) {
|
||||
return -(width / 2);
|
||||
};
|
||||
}
|
||||
if (_.isFunction(height)) {
|
||||
translateY = function(d) {
|
||||
return -(height(d) / 2);
|
||||
};
|
||||
} else {
|
||||
translateY = -(height / 2);
|
||||
translateY = function() {
|
||||
return -(height / 2);
|
||||
};
|
||||
}
|
||||
addShape = function(node) {
|
||||
addShape = function(node, shift) {
|
||||
shift = shift || 0;
|
||||
node.append("rect") // Display nodes as rectangles
|
||||
.attr("width", width) // Set width
|
||||
.attr("height", height) // Set height
|
||||
.attr("x", translateX)
|
||||
.attr("y", translateY)
|
||||
.attr("x", function(d) { return translateX(d) - shift;})
|
||||
.attr("y", function(d) { return translateY(d) - shift;})
|
||||
.attr("rx", "8")
|
||||
.attr("ry", "8");
|
||||
};
|
||||
|
|
|
@ -71,6 +71,14 @@ function PreviewAdapter(nodes, edges, config) {
|
|||
|
||||
parseConfig(config);
|
||||
|
||||
self.loadInitialNode = function(nodeId, callback) {
|
||||
absAdapter.cleanUp();
|
||||
var cb = function(n) {
|
||||
callback(absAdapter.insertInitialNode(n));
|
||||
};
|
||||
self.loadNode(nodeId, cb);
|
||||
};
|
||||
|
||||
self.loadNode = function(nodeId, callback) {
|
||||
var ns = [],
|
||||
es = [],
|
||||
|
@ -132,7 +140,7 @@ function PreviewAdapter(nodes, edges, config) {
|
|||
ns.push(n3);
|
||||
ns.push(n4);
|
||||
ns.push(n5);
|
||||
|
||||
|
||||
es.push(e12);
|
||||
es.push(e13);
|
||||
es.push(e14);
|
||||
|
|
|
@ -164,9 +164,9 @@ function GraphViewer(svg, width, height, adapterConfig, config) {
|
|||
};
|
||||
|
||||
self.loadGraph = function(nodeId, callback) {
|
||||
nodes.length = 0;
|
||||
edges.length = 0;
|
||||
self.adapter.loadNode(nodeId, function (node) {
|
||||
// loadNode
|
||||
// loadInitialNode
|
||||
self.adapter.loadInitialNode(nodeId, function (node) {
|
||||
if (node.errorCode) {
|
||||
callback(node);
|
||||
return;
|
||||
|
@ -180,9 +180,8 @@ function GraphViewer(svg, width, height, adapterConfig, config) {
|
|||
};
|
||||
|
||||
self.loadGraphWithAttributeValue = function(attribute, value, callback) {
|
||||
nodes.length = 0;
|
||||
edges.length = 0;
|
||||
self.adapter.loadNodeFromTreeByAttributeValue(attribute, value, function (node) {
|
||||
// loadNodeFromTreeByAttributeValue
|
||||
self.adapter.loadInitialNodeByAttributeValue(attribute, value, function (node) {
|
||||
if (node.errorCode) {
|
||||
callback(node);
|
||||
return;
|
||||
|
|
|
@ -146,6 +146,17 @@
|
|||
expect(window.NodeReducer).wasCalledWith(nodes, edges);
|
||||
});
|
||||
|
||||
it('should send the nodeReducer the configuration if given', function() {
|
||||
spyOn(window, "NodeReducer");
|
||||
var nodes = [],
|
||||
edges = [],
|
||||
config = {
|
||||
prioList: ["foo", "bar", "baz"]
|
||||
},
|
||||
t = new AbstractAdapter(nodes, edges, descendant, config);
|
||||
expect(window.NodeReducer).wasCalledWith(nodes, edges, ["foo", "bar", "baz"]);
|
||||
});
|
||||
|
||||
it('should create a ModularityJoiner worker', function() {
|
||||
spyOn(window, "WebWorkerWrapper");
|
||||
var nodes = [],
|
||||
|
@ -233,6 +244,10 @@
|
|||
it('should offer a function to check the overall amount of nodes', function() {
|
||||
expect(testee).toHaveFunction("checkNodeLimit", 1);
|
||||
});
|
||||
|
||||
it('should offer a function to change to a different configuration', function() {
|
||||
expect(testee).toHaveFunction("changeTo", 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checking nodes', function() {
|
||||
|
@ -1386,9 +1401,27 @@
|
|||
limit = 3;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
[s1, s2],
|
||||
[s3, s4, s5, s6, s7],
|
||||
[s8]
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: s1
|
||||
},
|
||||
nodes: [s1, s2]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: s3
|
||||
},
|
||||
nodes: [s3, s4, s5, s6, s7]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: s8
|
||||
},
|
||||
nodes: [s8]
|
||||
}
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
|
@ -1462,7 +1495,13 @@
|
|||
limit = 1;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
[s1, s2]
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: s1
|
||||
},
|
||||
nodes: [s1, s2]
|
||||
}
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
|
@ -1554,17 +1593,31 @@
|
|||
|
||||
beforeEach(function() {
|
||||
mockReducer = {};
|
||||
mockReducer.changePrioList = function() {};
|
||||
mockReducer.bucketNodes = function() {};
|
||||
spyOn(window, "NodeReducer").andCallFake(function(v, e) {
|
||||
return {
|
||||
bucketNodes: function(toSort, numBuckets) {
|
||||
return mockReducer.bucketNodes(toSort, numBuckets);
|
||||
},
|
||||
changePrioList: function(list) {
|
||||
return mockReducer.changePrioList(list);
|
||||
}
|
||||
};
|
||||
});
|
||||
adapter = new AbstractAdapter(nodes, edges, descendant);
|
||||
});
|
||||
|
||||
it('should be able to change the reducer to a new prioList', function() {
|
||||
spyOn(mockReducer,"changePrioList");
|
||||
var list = ["a", "b", "c"],
|
||||
config = {
|
||||
prioList: list
|
||||
};
|
||||
adapter.changeTo(config);
|
||||
expect(mockReducer.changePrioList).wasCalledWith(list);
|
||||
});
|
||||
|
||||
it('should not take any action if the limit is high enough', function() {
|
||||
adapter.setChildLimit(5);
|
||||
spyOn(mockReducer, "bucketNodes");
|
||||
|
@ -1590,8 +1643,20 @@
|
|||
limit = 2;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
[n1, n2],
|
||||
[n3, n4, n5]
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: n1
|
||||
},
|
||||
nodes: [n1, n2]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: n3
|
||||
},
|
||||
nodes: [n3, n4, n5]
|
||||
}
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
|
@ -1618,9 +1683,27 @@
|
|||
limit = 3;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
[n1],
|
||||
[n3, n4, n5],
|
||||
[n2]
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: n1
|
||||
},
|
||||
nodes: [n1]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: n3
|
||||
},
|
||||
nodes: [n3, n4, n5]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: n2
|
||||
},
|
||||
nodes: [n2]
|
||||
}
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
|
@ -1642,6 +1725,68 @@
|
|||
existNodes(["1", "2"]);
|
||||
});
|
||||
|
||||
it('should display the reason why a community has been joined', function() {
|
||||
var n1, n2, n3, n4, n5,
|
||||
com1, com2,
|
||||
inserted = [],
|
||||
limit = 3;
|
||||
spyOn(mockReducer, "bucketNodes").andCallFake(function() {
|
||||
return [
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: n1
|
||||
},
|
||||
nodes: [n1, n2]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
key: "type",
|
||||
value: "example"
|
||||
},
|
||||
nodes: [n3, n4, n5]
|
||||
}
|
||||
];
|
||||
});
|
||||
adapter.setChildLimit(limit);
|
||||
n1 = adapter.insertNode({_id: "1" });
|
||||
n2 = adapter.insertNode({_id: "2" });
|
||||
n3 = adapter.insertNode({_id: "3" });
|
||||
n4 = adapter.insertNode({_id: "4" });
|
||||
n5 = adapter.insertNode({_id: "5" });
|
||||
_.each(nodes, function(n) {
|
||||
inserted.push(n);
|
||||
});
|
||||
adapter.checkSizeOfInserted(inserted);
|
||||
|
||||
expect(mockReducer.bucketNodes).wasCalledWith(inserted, limit);
|
||||
|
||||
expect(nodes.length).toEqual(2);
|
||||
expect(getCommunityNodes().length).toEqual(2);
|
||||
notExistNodes(["1", "2", "3", "4", "5"]);
|
||||
|
||||
_.each(getCommunityNodes(), function(c) {
|
||||
if (c._size === 2) {
|
||||
com1 = c;
|
||||
return;
|
||||
}
|
||||
if (c._size === 3) {
|
||||
com2 = c;
|
||||
return;
|
||||
}
|
||||
// Should never be reached
|
||||
expect(true).toBeFalsy();
|
||||
});
|
||||
|
||||
expect(com1._reason).toEqual({
|
||||
type: "similar",
|
||||
example: n1
|
||||
});
|
||||
expect(com2._reason).toEqual({
|
||||
key: "type",
|
||||
value: "example"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -94,7 +94,8 @@
|
|||
|
||||
return new ArangoAdapter([], [], {
|
||||
nodeCollection: "",
|
||||
edgeCollection: ""
|
||||
edgeCollection: "",
|
||||
prioList: ["foo", "bar", "baz"]
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -766,7 +767,7 @@
|
|||
c1 = insertNode(altNodesCollection, 1);
|
||||
e1_2 = insertEdge(altEdgesCollection, c0, c1);
|
||||
|
||||
adapter.changeTo(altNodesCollection, altEdgesCollection);
|
||||
adapter.changeToCollections(altNodesCollection, altEdgesCollection);
|
||||
|
||||
callbackCheck = false;
|
||||
adapter.loadNodeFromTreeById(c0, checkCallbackFunction);
|
||||
|
@ -809,7 +810,7 @@
|
|||
|
||||
spyOn($, "ajax");
|
||||
|
||||
adapter.changeTo(altNodesCollection, altEdgesCollection, false);
|
||||
adapter.changeToCollections(altNodesCollection, altEdgesCollection, false);
|
||||
|
||||
adapter.loadNode("42");
|
||||
|
||||
|
@ -827,7 +828,7 @@
|
|||
|
||||
spyOn($, "ajax");
|
||||
|
||||
adapter.changeTo(altNodesCollection, altEdgesCollection, true);
|
||||
adapter.changeToCollections(altNodesCollection, altEdgesCollection, true);
|
||||
|
||||
adapter.loadNode("42");
|
||||
|
||||
|
@ -875,7 +876,13 @@
|
|||
callNodes = ns;
|
||||
for (i = 0; i < 5; i++) {
|
||||
pos = i*4;
|
||||
res.push(ns.slice(pos, pos + 4));
|
||||
res.push({
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[pos]
|
||||
},
|
||||
nodes: ns.slice(pos, pos + 4)
|
||||
});
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
@ -942,7 +949,22 @@
|
|||
});
|
||||
spyOn(this, "fakeReducerBucketRequest").andCallFake(function(ns) {
|
||||
lastCallWith = _.pluck(ns, "_id");
|
||||
return [[ns[0]], [ns[1], ns[2]]];
|
||||
return [
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[0]
|
||||
},
|
||||
nodes: [ns[0]]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[1]
|
||||
},
|
||||
nodes: [ns[1], ns[2]]
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
callbackCheck = false;
|
||||
|
@ -1009,9 +1031,21 @@
|
|||
res = [],
|
||||
pos;
|
||||
for (i = 0; i < 4; i++) {
|
||||
res.push([ns[i]]);
|
||||
res.push({
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[i]
|
||||
},
|
||||
nodes: [ns[i]]
|
||||
});
|
||||
}
|
||||
res.push([ns[4], ns[5]]);
|
||||
res.push({
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[4]
|
||||
},
|
||||
nodes: [ns[4], ns[5]]
|
||||
});
|
||||
return res;
|
||||
});
|
||||
|
||||
|
@ -1423,7 +1457,7 @@
|
|||
});
|
||||
adapter.setNodeLimit(3);
|
||||
|
||||
adapter.changeTo(v, e);
|
||||
adapter.changeToCollections(v, e);
|
||||
adapter.loadNode(v0, counterCallback);
|
||||
adapter.loadNode(v1, counterCallback);
|
||||
|
||||
|
@ -1512,7 +1546,7 @@
|
|||
});
|
||||
adapter.setNodeLimit(3);
|
||||
|
||||
adapter.changeTo(v, e);
|
||||
adapter.changeToCollections(v, e);
|
||||
adapter.loadNode(v0, counterCallback);
|
||||
adapter.loadNode(v1, counterCallback);
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
return new FoxxAdapter([], [], "foxx/route");
|
||||
return new FoxxAdapter([], [], "foxx/route", {prioList: ["foo", "bar", "baz"]});
|
||||
});
|
||||
|
||||
var adapter,
|
||||
|
@ -426,7 +426,22 @@
|
|||
|
||||
spyOn(this, "fakeReducerBucketRequest").andCallFake(function(ns) {
|
||||
lastCallWith = _.pluck(ns, "_id");
|
||||
return [[ns[0]], [ns[1], ns[2]]];
|
||||
return [
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[0]
|
||||
},
|
||||
nodes: [ns[0]]
|
||||
},
|
||||
{
|
||||
reason: {
|
||||
type: "similar",
|
||||
example: ns[1]
|
||||
},
|
||||
nodes: [ns[1], ns[2]]
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
callbackCheck = false;
|
||||
|
|
|
@ -59,6 +59,7 @@ var describeInterface = function (testee) {
|
|||
|
||||
// Add functions to load here:
|
||||
expect(testee).toHaveFunction("loadNode", 2);
|
||||
expect(testee).toHaveFunction("loadInitialNode", 2);
|
||||
expect(testee).toHaveFunction("explore", 2);
|
||||
// expect(testee).toHaveFunction("loadNodeFromTreeById", 2);
|
||||
expect(testee).toHaveFunction("requestCentralityChildren", 2);
|
||||
|
@ -80,6 +81,7 @@ var describeInterface = function (testee) {
|
|||
|
||||
/**
|
||||
* Expectations on constructor:
|
||||
* Created with config: {prioList: ["foo", "bar", "baz"]}
|
||||
* loadNode -> Adds {_id: 1} -{_id:"1-2"}-> {_id: 2}
|
||||
* createEdge({source: {_id: 1}, target: {_id: 1}}) -> {_id: "1-2", _from:1, _to:2}
|
||||
* createNode({}) -> {_id: 1}
|
||||
|
@ -121,17 +123,28 @@ var describeIntegeration = function(constructor) {
|
|||
setChildLimit: function(){},
|
||||
checkSizeOfInserted: function(){},
|
||||
checkNodeLimit: function(){},
|
||||
explore: function(){}
|
||||
explore: function(){},
|
||||
changeTo: function(){}
|
||||
};
|
||||
|
||||
|
||||
spyOn(window, "AbstractAdapter").andCallFake(function(nodes, edges, descendant) {
|
||||
spyOn(window, "AbstractAdapter").andCallFake(function(nodes, edges, descendant, config) {
|
||||
mockedAbstract.nodes = nodes;
|
||||
mockedAbstract.edges = edges;
|
||||
return mockedAbstract;
|
||||
});
|
||||
});
|
||||
|
||||
it('should create the AbstractAdapter with correct values', function() {
|
||||
testee = constructor();
|
||||
expect(window.AbstractAdapter).wasCalledWith(
|
||||
[],
|
||||
[],
|
||||
testee,
|
||||
{prioList: ["foo", "bar", "baz"]}
|
||||
);
|
||||
});
|
||||
|
||||
it('should call setNodeLimit on the abstract', function() {
|
||||
spyOn(mockedAbstract, "setNodeLimit");
|
||||
testee = constructor();
|
||||
|
@ -139,6 +152,14 @@ var describeIntegeration = function(constructor) {
|
|||
expect(mockedAbstract.setNodeLimit).wasCalledWith(5, jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('should propagate changeTo to the abstract', function() {
|
||||
spyOn(mockedAbstract, "changeTo");
|
||||
testee = constructor();
|
||||
testee.changeTo({prioList: ["foo", "bar", "baz"]});
|
||||
expect(mockedAbstract.changeTo).wasCalledWith({prioList: ["foo", "bar", "baz"]});
|
||||
|
||||
});
|
||||
|
||||
it('should call explore on the abstract', function() {
|
||||
spyOn(mockedAbstract, "explore");
|
||||
testee = constructor();
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
|
||||
it('should automatically load the first node', function() {
|
||||
var mockObj = {
|
||||
loadNode: function() {},
|
||||
loadInitialNode: function() {},
|
||||
explore: function() {}
|
||||
},
|
||||
startNode = "1",
|
||||
|
@ -91,9 +91,9 @@
|
|||
spyOn(window, "PreviewAdapter").andCallFake(function() {
|
||||
return mockObj;
|
||||
});
|
||||
spyOn(mockObj, "loadNode");
|
||||
spyOn(mockObj, "loadInitialNode");
|
||||
ui = new GraphViewerPreview(cont, {});
|
||||
expect(mockObj.loadNode).wasCalledWith(startNode, jasmine.any(Function));
|
||||
expect(mockObj.loadInitialNode).wasCalledWith(startNode, jasmine.any(Function));
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
var Tmp = JSONAdapter;
|
||||
JSONAdapter = function (jsonPath, nodes, edges, width, height) {
|
||||
var r = new Tmp(jsonPath, nodes, edges, width, height);
|
||||
r.loadNodeFromTreeByAttributeValue = function(attribute, value, callback) {
|
||||
r.loadInitialNodeByAttributeValue = function(attribute, value, callback) {
|
||||
adapterMockCall = {
|
||||
attribute: attribute,
|
||||
value: value
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
|
||||
it('should try to load a starting node if one is given', function() {
|
||||
var mockObj = {
|
||||
loadNode: function() {},
|
||||
loadInitialNode: function() {},
|
||||
explore: function() {}
|
||||
},
|
||||
startNode = "nodes/123",
|
||||
|
@ -99,9 +99,9 @@
|
|||
spyOn(window, "FoxxAdapter").andCallFake(function() {
|
||||
return mockObj;
|
||||
});
|
||||
spyOn(mockObj, "loadNode");
|
||||
spyOn(mockObj, "loadInitialNode");
|
||||
ui = new GraphViewerWidget({}, startNode);
|
||||
expect(mockObj.loadNode).wasCalledWith(startNode, jasmine.any(Function));
|
||||
expect(mockObj.loadInitialNode).wasCalledWith(startNode, jasmine.any(Function));
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -83,6 +83,12 @@
|
|||
expect(reducer.bucketNodes.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should offer a function to change the prioList', function() {
|
||||
expect(reducer.changePrioList).toBeDefined();
|
||||
expect(reducer.changePrioList).toEqual(jasmine.any(Function));
|
||||
expect(reducer.changePrioList.length).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checking bucket sort of nodes', function() {
|
||||
|
@ -116,28 +122,49 @@
|
|||
|
||||
it('should not bucket anything if #nodes <= #buckets', function() {
|
||||
buckets = 5;
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({
|
||||
_data: {a: 1}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {a: 1}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {a: 1}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {a: 1}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {a: 1}
|
||||
});
|
||||
var res = reducer.bucketNodes(allNodes, buckets);
|
||||
expect(res.length).toEqual(5);
|
||||
expect(res[0].length).toEqual(1);
|
||||
expect(res[1].length).toEqual(1);
|
||||
expect(res[2].length).toEqual(1);
|
||||
expect(res[3].length).toEqual(1);
|
||||
expect(res[4].length).toEqual(1);
|
||||
_.each(res, function(obj) {
|
||||
expect(obj.reason).toEqual({type: "single"});
|
||||
expect(obj.nodes.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should create at most the given amount of buckets', function() {
|
||||
buckets = 3;
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({b: 2});
|
||||
allNodes.push({c: 3});
|
||||
allNodes.push({d: 4});
|
||||
allNodes.push({e: 5});
|
||||
allNodes.push({f: 6});
|
||||
allNodes.push({
|
||||
_data: {a: 1}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {b: 2}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {c: 3}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {d: 4}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {e: 5}
|
||||
});
|
||||
allNodes.push({
|
||||
_data: {f: 6}
|
||||
});
|
||||
|
||||
var res = reducer.bucketNodes(allNodes, buckets);
|
||||
expect(res.length).toEqual(3);
|
||||
|
@ -145,20 +172,42 @@
|
|||
|
||||
it('should uniformly distribute dissimilar nodes', function() {
|
||||
buckets = 3;
|
||||
allNodes.push({a: 1});
|
||||
allNodes.push({b: 2});
|
||||
allNodes.push({c: 3});
|
||||
allNodes.push({d: 4});
|
||||
allNodes.push({e: 5});
|
||||
allNodes.push({f: 6});
|
||||
allNodes.push({g: 7});
|
||||
allNodes.push({h: 8});
|
||||
allNodes.push({i: 9});
|
||||
allNodes.push({
|
||||
_data:{a: 1}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{b: 2}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{c: 3}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{d: 4}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{e: 5}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{f: 6}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{g: 7}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{h: 8}
|
||||
});
|
||||
allNodes.push({
|
||||
_data:{i: 9}
|
||||
});
|
||||
|
||||
var res = reducer.bucketNodes(allNodes, buckets);
|
||||
expect(res[0].length).toEqual(3);
|
||||
expect(res[1].length).toEqual(3);
|
||||
expect(res[2].length).toEqual(3);
|
||||
_.each(res, function(obj) {
|
||||
expect(obj.reason).toEqual({
|
||||
type: "similar",
|
||||
example: jasmine.any(Object)
|
||||
});
|
||||
expect(obj.nodes.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
it('should bucket clearly similar nodes together', function() {
|
||||
|
@ -171,17 +220,35 @@
|
|||
res2,
|
||||
res3;
|
||||
|
||||
a1 = {a: 1};
|
||||
a2 = {a: 1};
|
||||
a3 = {a: 1};
|
||||
a1 = {
|
||||
_data: {a: 1}
|
||||
};
|
||||
a2 = {
|
||||
_data: {a: 1}
|
||||
};
|
||||
a3 = {
|
||||
_data: {a: 1}
|
||||
};
|
||||
|
||||
b1 = {b: 2};
|
||||
b2 = {b: 2};
|
||||
b3 = {b: 2};
|
||||
b1 = {
|
||||
_data: {b: 2}
|
||||
};
|
||||
b2 = {
|
||||
_data: {b: 2}
|
||||
};
|
||||
b3 = {
|
||||
_data: {b: 2}
|
||||
};
|
||||
|
||||
c1 = {c: 3};
|
||||
c2 = {c: 3};
|
||||
c3 = {c: 3};
|
||||
c1 = {
|
||||
_data: {c: 3}
|
||||
};
|
||||
c2 = {
|
||||
_data: {c: 3}
|
||||
};
|
||||
c3 = {
|
||||
_data: {c: 3}
|
||||
};
|
||||
|
||||
allNodes.push(a1);
|
||||
allNodes.push(a2);
|
||||
|
@ -194,40 +261,546 @@
|
|||
allNodes.push(c3);
|
||||
|
||||
resArray = reducer.bucketNodes(allNodes, buckets);
|
||||
res1 = resArray[0];
|
||||
res2 = resArray[1];
|
||||
res3 = resArray[2];
|
||||
|
||||
if (res1[0].a !== undefined) {
|
||||
expect(res1).toContainAll([a1, a2, a3]);
|
||||
} else if (res2[0].a !== undefined) {
|
||||
expect(res2).toContainAll([a1, a2, a3]);
|
||||
} else {
|
||||
expect(res3).toContainAll([a1, a2, a3]);
|
||||
}
|
||||
|
||||
if (res1[0].b !== undefined) {
|
||||
expect(res1).toContainAll([b1, b2, b3]);
|
||||
} else if (res2[0].b !== undefined) {
|
||||
expect(res2).toContainAll([b1, b2, b3]);
|
||||
} else {
|
||||
expect(res3).toContainAll([b1, b2, b3]);
|
||||
}
|
||||
|
||||
if (res1[0].c !== undefined) {
|
||||
expect(res1).toContainAll([c1, c2, c3]);
|
||||
} else if (res2[0].c !== undefined) {
|
||||
expect(res2).toContainAll([c1, c2, c3]);
|
||||
} else {
|
||||
expect(res3).toContainAll([c1, c2, c3]);
|
||||
}
|
||||
_.each(resArray, function(entry) {
|
||||
expect(entry.reason).toEqual({
|
||||
type: "similar",
|
||||
example: jasmine.any(Object)
|
||||
});
|
||||
if (_.isEqual(entry.reason.example, a1)) {
|
||||
res1 = entry;
|
||||
} else if (_.isEqual(entry.reason.example, b1)) {
|
||||
res2 = entry;
|
||||
} else if (_.isEqual(entry.reason.example, c1)) {
|
||||
res3 = entry;
|
||||
} else {
|
||||
// Should never be reached
|
||||
expect(true).toBeFalsy();
|
||||
}
|
||||
});
|
||||
expect(res1.nodes).toContainAll([a1, a2, a3]);
|
||||
expect(res2.nodes).toContainAll([b1, b2, b3]);
|
||||
expect(res3.nodes).toContainAll([c1, c2, c3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('setup with a priority list', function() {
|
||||
|
||||
var reducer,
|
||||
nodes,
|
||||
buckets,
|
||||
prios;
|
||||
|
||||
beforeEach(function () {
|
||||
nodes = [];
|
||||
prios = ["age", "type"];
|
||||
reducer = new NodeReducer([], [], prios);
|
||||
this.addMatchers({
|
||||
toContainAll: function(objs) {
|
||||
var bucket = this.actual,
|
||||
passed = true;
|
||||
_.each(bucket, function(n) {
|
||||
var i;
|
||||
for (i = 0; i < objs.length; i++) {
|
||||
if (objs[i] === n) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
passed = false;
|
||||
});
|
||||
this.message = function() {
|
||||
return JSON.stringify(bucket)
|
||||
+ " should contain all of "
|
||||
+ JSON.stringify(objs);
|
||||
};
|
||||
return passed;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should bucket nodes according to the list', function() {
|
||||
buckets = 3;
|
||||
var a1, a2 ,a3,
|
||||
b1, b2, b3,
|
||||
c1, c2, c3,
|
||||
r1, r2, r3,
|
||||
resArray,
|
||||
res1,
|
||||
res2,
|
||||
res3;
|
||||
|
||||
a1 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
a2 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Bob",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
a3 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Charly",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
|
||||
b1 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
b2 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Bob",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
b3 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Charly",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
|
||||
c1 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
c2 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Bob",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
c3 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Charly",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
r1 = {
|
||||
key: "age",
|
||||
value: "1"
|
||||
};
|
||||
r2 = {
|
||||
key: "age",
|
||||
value: "2"
|
||||
};
|
||||
r3 = {
|
||||
key: "age",
|
||||
value: "3"
|
||||
};
|
||||
|
||||
|
||||
nodes.push(a1);
|
||||
nodes.push(b1);
|
||||
nodes.push(c1);
|
||||
|
||||
nodes.push(a2);
|
||||
nodes.push(b2);
|
||||
nodes.push(c2);
|
||||
|
||||
nodes.push(a3);
|
||||
nodes.push(b3);
|
||||
nodes.push(c3);
|
||||
|
||||
resArray = reducer.bucketNodes(nodes, buckets);
|
||||
_.each(resArray, function(entry) {
|
||||
if (_.isEqual(entry.reason, r1)) {
|
||||
res1 = entry;
|
||||
} else if (_.isEqual(entry.reason, r2)) {
|
||||
res2 = entry;
|
||||
} else if (_.isEqual(entry.reason, r3)) {
|
||||
res3 = entry;
|
||||
} else {
|
||||
expect(true).toBeFalsy();
|
||||
}
|
||||
});
|
||||
expect(res1.nodes).toContainAll([a1, a2, a3]);
|
||||
expect(res2.nodes).toContainAll([b1, b2, b3]);
|
||||
expect(res3.nodes).toContainAll([c1, c2, c3]);
|
||||
});
|
||||
|
||||
it('should bucket following the priorities of the list', function() {
|
||||
buckets = 3;
|
||||
var a1, a2 ,a3,
|
||||
b1, b2, b3,
|
||||
c1, c2, c3,
|
||||
r1, r2, r3,
|
||||
resArray,
|
||||
res1,
|
||||
res2,
|
||||
res3;
|
||||
|
||||
a1 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
a2 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Bob",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
a3 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Charly",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
|
||||
b1 = {
|
||||
_data: {
|
||||
type: "person",
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
b2 = {
|
||||
_data: {
|
||||
type: "person",
|
||||
name: "Bob",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
b3 = {
|
||||
_data: {
|
||||
type: "person",
|
||||
name: "Charly",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
|
||||
c1 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
type: "person",
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
c2 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
type: "person",
|
||||
name: "Bob",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
c3 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Charly",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
r1 = {
|
||||
key: "age",
|
||||
value: "1"
|
||||
};
|
||||
r2 = {
|
||||
key: "type",
|
||||
value: "person"
|
||||
};
|
||||
r3 = {
|
||||
key: "age",
|
||||
value: "3"
|
||||
};
|
||||
|
||||
nodes.push(a1);
|
||||
nodes.push(b1);
|
||||
nodes.push(c1);
|
||||
|
||||
nodes.push(a2);
|
||||
nodes.push(b2);
|
||||
nodes.push(c2);
|
||||
|
||||
nodes.push(a3);
|
||||
nodes.push(b3);
|
||||
nodes.push(c3);
|
||||
|
||||
resArray = reducer.bucketNodes(nodes, buckets);
|
||||
_.each(resArray, function(entry) {
|
||||
if (_.isEqual(entry.reason, r1)) {
|
||||
res1 = entry;
|
||||
} else if (_.isEqual(entry.reason, r2)) {
|
||||
res2 = entry;
|
||||
} else if (_.isEqual(entry.reason, r3)) {
|
||||
res3 = entry;
|
||||
} else {
|
||||
expect(true).toBeFalsy();
|
||||
}
|
||||
});
|
||||
expect(res1.nodes).toContainAll([a1, a2, a3, c1]);
|
||||
expect(res2.nodes).toContainAll([b1, b2, b3]);
|
||||
expect(res3.nodes).toContainAll([c2, c3]);
|
||||
|
||||
});
|
||||
|
||||
it('should be possible to delete the list', function() {
|
||||
buckets = 3;
|
||||
var a1, a2 ,a3,
|
||||
b1, b2, b3,
|
||||
c1, c2, c3,
|
||||
r1, r2, r3,
|
||||
resArray,
|
||||
res1,
|
||||
res2,
|
||||
res3;
|
||||
|
||||
a1 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
a2 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Bob",
|
||||
foo: "baz"
|
||||
}
|
||||
};
|
||||
a3 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Charly",
|
||||
foo: "tango"
|
||||
}
|
||||
};
|
||||
|
||||
b1 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
b2 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Bob",
|
||||
foo: "baz"
|
||||
}
|
||||
};
|
||||
b3 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Charly",
|
||||
foo: "tango"
|
||||
}
|
||||
};
|
||||
|
||||
c1 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
c2 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Bob",
|
||||
foo: "baz"
|
||||
}
|
||||
};
|
||||
c3 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Charly",
|
||||
foo: "tango"
|
||||
}
|
||||
};
|
||||
r1 = {
|
||||
type: "similar",
|
||||
example: a1
|
||||
};
|
||||
r2 = {
|
||||
type: "similar",
|
||||
example: a2
|
||||
};
|
||||
r3 = {
|
||||
type: "similar",
|
||||
example: a3
|
||||
};
|
||||
|
||||
nodes.push(a1);
|
||||
nodes.push(b1);
|
||||
nodes.push(c1);
|
||||
|
||||
nodes.push(a2);
|
||||
nodes.push(b2);
|
||||
nodes.push(c2);
|
||||
|
||||
nodes.push(a3);
|
||||
nodes.push(b3);
|
||||
nodes.push(c3);
|
||||
|
||||
reducer.changePrioList([]);
|
||||
|
||||
resArray = reducer.bucketNodes(nodes, buckets);
|
||||
_.each(resArray, function(entry) {
|
||||
if (_.isEqual(entry.reason, r1)) {
|
||||
res1 = entry;
|
||||
} else if (_.isEqual(entry.reason, r2)) {
|
||||
res2 = entry;
|
||||
} else if (_.isEqual(entry.reason, r3)) {
|
||||
res3 = entry;
|
||||
} else {
|
||||
expect(true).toBeFalsy();
|
||||
}
|
||||
});
|
||||
expect(res1.nodes).toContainAll([a1, b1, c1]);
|
||||
expect(res2.nodes).toContainAll([a2, b2, c2]);
|
||||
expect(res3.nodes).toContainAll([a3, b3, c3]);
|
||||
});
|
||||
|
||||
it('should be possible to change the list', function() {
|
||||
buckets = 3;
|
||||
var a1, a2 ,a3,
|
||||
b1, b2, b3,
|
||||
c1, c2, c3,
|
||||
r1, r2, r3,
|
||||
resArray,
|
||||
res1,
|
||||
res2,
|
||||
res3;
|
||||
|
||||
a1 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
a2 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Bob",
|
||||
foo: "baz"
|
||||
}
|
||||
};
|
||||
a3 = {
|
||||
_data: {
|
||||
age: 1,
|
||||
name: "Charly",
|
||||
foo: "tango"
|
||||
}
|
||||
};
|
||||
|
||||
b1 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
b2 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Bob",
|
||||
foo: "baz"
|
||||
}
|
||||
};
|
||||
b3 = {
|
||||
_data: {
|
||||
age: 2,
|
||||
name: "Charly",
|
||||
foo: "tango"
|
||||
}
|
||||
};
|
||||
|
||||
c1 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Alice",
|
||||
foo: "bar"
|
||||
}
|
||||
};
|
||||
c2 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Bob",
|
||||
foo: "baz"
|
||||
}
|
||||
};
|
||||
c3 = {
|
||||
_data: {
|
||||
age: 3,
|
||||
name: "Charly",
|
||||
foo: "tango"
|
||||
}
|
||||
};
|
||||
r1 = {
|
||||
key: "foo",
|
||||
value: "bar"
|
||||
};
|
||||
r2 = {
|
||||
key: "foo",
|
||||
value: "baz"
|
||||
};
|
||||
r3 = {
|
||||
key: "foo",
|
||||
value: "tango"
|
||||
};
|
||||
|
||||
nodes.push(a1);
|
||||
nodes.push(b1);
|
||||
nodes.push(c1);
|
||||
|
||||
nodes.push(a2);
|
||||
nodes.push(b2);
|
||||
nodes.push(c2);
|
||||
|
||||
nodes.push(a3);
|
||||
nodes.push(b3);
|
||||
nodes.push(c3);
|
||||
|
||||
reducer.changePrioList(["foo"]);
|
||||
|
||||
resArray = reducer.bucketNodes(nodes, buckets);
|
||||
_.each(resArray, function(entry) {
|
||||
if (_.isEqual(entry.reason, r1)) {
|
||||
res1 = entry;
|
||||
} else if (_.isEqual(entry.reason, r2)) {
|
||||
res2 = entry;
|
||||
} else if (_.isEqual(entry.reason, r3)) {
|
||||
res3 = entry;
|
||||
} else {
|
||||
expect(true).toBeFalsy();
|
||||
}
|
||||
});
|
||||
expect(res1.nodes).toContainAll([a1, b1, c1]);
|
||||
expect(res2.nodes).toContainAll([a2, b2, c2]);
|
||||
expect(res3.nodes).toContainAll([a3, b3, c3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -1384,7 +1384,7 @@
|
|||
expect($("svg #\\*community_42")[0]).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render communtiy nodes as stars', function() {
|
||||
it('should render communtiy nodes as stacks', function() {
|
||||
var nodes = helper.createSimpleNodes([0, 1, 2]),
|
||||
commNode = {
|
||||
_id: "*community_42",
|
||||
|
@ -1397,14 +1397,23 @@
|
|||
z: 1
|
||||
}
|
||||
},
|
||||
star;
|
||||
stack,
|
||||
sortFunc = function(a, b) {
|
||||
return a.getAttribute("x") - b.getAttribute("x");
|
||||
};
|
||||
nodes.push(commNode);
|
||||
shaper.drawNodes(nodes);
|
||||
expect($("svg .communitynode").length).toEqual(1);
|
||||
expect($("svg #\\*community_42")[0]).toBeDefined();
|
||||
star = $("svg #\\*community_42 polygon");
|
||||
expect(star.length).toEqual(1);
|
||||
expect(star.attr("points")).toEqual("0,-25 -16,20 23,-10 -23,-10 16,20");
|
||||
stack = $("svg #\\*community_42 rect").sort(sortFunc);
|
||||
|
||||
expect(stack.length).toEqual(4);
|
||||
expect(stack[0].getAttribute("x")).toEqual(String(stack[1].getAttribute("x") - 3));
|
||||
expect(stack[0].getAttribute("y")).toEqual(String(stack[1].getAttribute("y") - 3));
|
||||
expect(stack[1].getAttribute("x")).toEqual(String(stack[2].getAttribute("x") - 3));
|
||||
expect(stack[1].getAttribute("y")).toEqual(String(stack[2].getAttribute("y") - 3));
|
||||
expect(stack[2].getAttribute("x")).toEqual(String(stack[3].getAttribute("x") - 3));
|
||||
expect(stack[2].getAttribute("y")).toEqual(String(stack[3].getAttribute("y") - 3));
|
||||
});
|
||||
|
||||
it('should print the size of the capsulated community', function() {
|
||||
|
@ -1428,6 +1437,32 @@
|
|||
expect($("svg #\\*community_42 text").attr("fill")).toEqual("white");
|
||||
});
|
||||
|
||||
it('should print the reason why it is joined', function() {
|
||||
var nodes = [],
|
||||
commNode = {
|
||||
_id: "*community_42",
|
||||
_size: 4,
|
||||
_inboundCounter: 0,
|
||||
_outboundCounter: 0,
|
||||
_reason: {
|
||||
key: "type",
|
||||
value: "example"
|
||||
},
|
||||
position: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
}
|
||||
},
|
||||
spans;
|
||||
nodes.push(commNode);
|
||||
shaper.drawNodes(nodes);
|
||||
spans = $("svg #\\*community_42 text tspan");
|
||||
expect($(spans.get(0)).text()).toEqual("type:");
|
||||
expect($(spans.get(1)).text()).toEqual("example");
|
||||
expect($("svg #\\*community_42 text").attr("fill")).toEqual("white");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Generated on Thu Jul 04 2013 11:39:34 GMT+0200 (CEST)
|
||||
|
||||
module.exports = function(karma) {
|
||||
karma.configure({
|
||||
karma.set({
|
||||
|
||||
// base path, that will be used to resolve files and exclude
|
||||
basePath: '../jasmine_test/',
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
window.Session = Backbone.Model.extend({
|
||||
defaults: {
|
||||
sessionId: "",
|
||||
userName: "",
|
||||
password: "",
|
||||
userId: "",
|
||||
data: {}
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
},
|
||||
|
||||
isAuthorized: function () {
|
||||
//return Boolean(this.get("sessionId");
|
||||
return true;
|
||||
},
|
||||
|
||||
isNotAuthorized: function () {
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
|
@ -8,7 +8,9 @@ $(document).ready(function() {
|
|||
routes: {
|
||||
"" : "collections",
|
||||
"collection/:colid" : "collection",
|
||||
"collectionInfo/:colid" : "collectionInfo",
|
||||
"new" : "newCollection",
|
||||
"login" : "login",
|
||||
"collection/:colid/documents/:pageid" : "documents",
|
||||
"collection/:colid/:docid" : "document",
|
||||
"collection/:colid/:docid/source" : "source",
|
||||
|
@ -25,22 +27,29 @@ $(document).ready(function() {
|
|||
"applications" : "applications",
|
||||
"application/documentation/:key" : "appDocumentation",
|
||||
"graph" : "graph"
|
||||
|
||||
|
||||
},
|
||||
initialize: function () {
|
||||
|
||||
window.activeSession = new window.ArangoSession();
|
||||
|
||||
window.arangoCollectionsStore = new window.arangoCollections();
|
||||
window.arangoDocumentsStore = new window.arangoDocuments();
|
||||
window.arangoDocumentStore = new window.arangoDocument();
|
||||
|
||||
|
||||
window.collectionsView = new window.collectionsView({
|
||||
collection: window.arangoCollectionsStore
|
||||
});
|
||||
window.arangoCollectionsStore.fetch();
|
||||
|
||||
|
||||
window.collectionView = new window.collectionView({
|
||||
model: arangoCollection
|
||||
});
|
||||
|
||||
window.collectionInfoView = new window.collectionInfoView({
|
||||
model: arangoCollection
|
||||
});
|
||||
|
||||
window.documentsView = new window.documentsView({
|
||||
collection: window.arangoDocuments
|
||||
});
|
||||
|
@ -67,8 +76,32 @@ $(document).ready(function() {
|
|||
collection: window.arangoCollectionsStore
|
||||
});
|
||||
},
|
||||
checkSession: function () {
|
||||
if (window.activeSession.models.length === 0) {
|
||||
window.App.navigate("login", {trigger: true});
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
login: function () {
|
||||
if (!this.aboutView) {
|
||||
this.loginView = new window.loginView({
|
||||
collection: window.activeSession
|
||||
});
|
||||
}
|
||||
this.loginView.render();
|
||||
this.naviView.selectMenuItem('');
|
||||
},
|
||||
collections: function() {
|
||||
var naviView = this.naviView;
|
||||
|
||||
var currentSession = this.checkSession();
|
||||
if (currentSession === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.arangoCollectionsStore.fetch({
|
||||
success: function () {
|
||||
window.collectionsView.render();
|
||||
|
@ -80,6 +113,10 @@ $(document).ready(function() {
|
|||
window.collectionView.options.colId = colid;
|
||||
window.collectionView.render();
|
||||
},
|
||||
collectionInfo: function(colid) {
|
||||
window.collectionInfoView.options.colId = colid;
|
||||
window.collectionInfoView.render();
|
||||
},
|
||||
newCollection: function() {
|
||||
if (!this.newCollectionView) {
|
||||
this.newCollectionView = new window.newCollectionView({});
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<div id="show-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Collection Info</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body-left">
|
||||
<div style="height: 30px"/>
|
||||
<table id="collectionInfoTable">
|
||||
<tr>
|
||||
<th class="collectionInfoTh">Name:</th>
|
||||
<th class="collectionInfoTh"><div id="show-collection-name" class="modal-text"/></th>
|
||||
</tr>
|
||||
<tr id="collectionSizeBox" style="display:none">
|
||||
<th class="collectionInfoTh">Journal size:</th>
|
||||
<th class="collectionInfoTh"><div id="show-collection-size" class="modal-text"/></th>
|
||||
<th class="tooltipInfoTh"><a class="modalInfoTooltips" title="The maximal size of a journal or datafile (in MB). Must be at least 1."><i class="icon-info-sign"></i></a></th>
|
||||
</tr>
|
||||
<tr id="collectionSyncBox" style="display:none">
|
||||
<th class="collectionInfoTh">Sync:</th>
|
||||
<th class="collectionInfoTh">
|
||||
<div id="show-collection-sync" class="modal-text"/>
|
||||
</th>
|
||||
<th class="tooltipInfoTh"><a class="modalInfoTooltips" title="Synchronise to disk before returning from a create or update of a document."><i class="icon-info-sign"></i></a></th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="collectionInfoTh">ID:</th>
|
||||
<th class="collectionInfoTh">
|
||||
<div id="show-collection-id" class="modal-text"/>
|
||||
<th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="collectionInfoTh">Type:</th>
|
||||
<th class="collectionInfoTh">
|
||||
<div id="show-collection-type" class="modal-text"/>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="collectionInfoTh">Status:</th>
|
||||
<th class="collectionInfoTh">
|
||||
<div id="show-collection-status" class="modal-text"/>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-body-right">
|
||||
<svg class="svgFigures"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="colFooter" class="modal-footer">
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
<div class="pull-right">
|
||||
<i class="icon-edit" alt="Change collection properties" title="Change collection properties"></i>
|
||||
<i class="icon-edit" alt="Edit collection properties" title="Edit collection properties"></i>
|
||||
</div>
|
||||
|
||||
<div class="plain">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
<div class="footer-left">
|
||||
<p>Welcome <a id="currentUser" style="color:#FFFFFF"> Guest </a><a id="loginLink"> Login</a></p>
|
||||
<p>Welcome <a id="currentUser" style="color:#FFFFFF"></a></p>
|
||||
</div>
|
||||
|
||||
<div class="copy footer-mid">
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<div id="loginWindow">
|
||||
<div id="loginSpace"/>
|
||||
<img id="loginLogo" src="/_admin/html/img/logo_arangodb_transp.png"/>
|
||||
<div id="loginSpace"/>
|
||||
<form id="loginForm">
|
||||
<input class="loginInput" placeholder="Username" id="loginUsername" type="text" name="username">
|
||||
<input class="loginInput" placeholder="Password" id="loginPassword" type="password" name="password">
|
||||
</form>
|
||||
Testing: Enter custom data!
|
||||
<button id="submitLogin" class="btn btn-success pull-right">Login</button>
|
||||
</div>
|
|
@ -0,0 +1,128 @@
|
|||
/*jslint indent: 2, stupid: true, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require, window, exports, Backbone, EJS, $, arangoHelper */
|
||||
|
||||
var collectionInfoView = Backbone.View.extend({
|
||||
el: '#modalPlaceholder',
|
||||
figures: {
|
||||
"alive" : 0,
|
||||
"dead" : 0,
|
||||
"datafiles" : 0,
|
||||
"journals" : 0,
|
||||
"shapes" : 0,
|
||||
"attributes" : 0
|
||||
},
|
||||
initialize: function () {
|
||||
},
|
||||
|
||||
template: new EJS({url: 'js/templates/collectionInfoView.ejs'}),
|
||||
|
||||
render: function() {
|
||||
$(this.el).html(this.template.text);
|
||||
$('#show-collection').modal('show');
|
||||
$('#show-collection').on('hidden', function () {
|
||||
});
|
||||
$('#show-collection').on('shown', function () {
|
||||
$('#show-collection-name').focus();
|
||||
});
|
||||
this.fillModal();
|
||||
$('.modalInfoTooltips').tooltip({
|
||||
placement: "right"
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
events: {
|
||||
"hidden #show-collection" : "hidden",
|
||||
},
|
||||
listenKey: function(e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.saveModifiedCollection();
|
||||
}
|
||||
},
|
||||
hidden: function () {
|
||||
window.App.navigate("#", {trigger: true});
|
||||
},
|
||||
fillModal: function() {
|
||||
try {
|
||||
this.myCollection = window.arangoCollectionsStore.get(this.options.colId).attributes;
|
||||
}
|
||||
catch (e) {
|
||||
// in case the collection cannot be found or something is not present (e.g. after a reload)
|
||||
window.App.navigate("#");
|
||||
return;
|
||||
}
|
||||
|
||||
$('#show-collection-name').text(this.myCollection.name);
|
||||
$('#show-collection-id').text(this.myCollection.id);
|
||||
$('#show-collection-type').text(this.myCollection.type);
|
||||
$('#show-collection-status').text(this.myCollection.status);
|
||||
|
||||
if (this.myCollection.status === 'unloaded') {
|
||||
$('#colFooter').append(
|
||||
'<div>For more information, collection has to be loaded</div>'
|
||||
);
|
||||
$('#collectionSizeBox').hide();
|
||||
$('#collectionSyncBox').hide();
|
||||
}
|
||||
else if (this.myCollection.status === 'loaded') {
|
||||
this.data = window.arangoCollectionsStore.getFigures(this.options.colId, true);
|
||||
this.fillLoadedModal(this.data);
|
||||
this.convertFigures(this.data);
|
||||
this.renderFigures();
|
||||
}
|
||||
},
|
||||
renderFigures: function () {
|
||||
var self = this;
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.pieChart()
|
||||
.x(function(d) { return d.label; })
|
||||
.y(function(d) { return d.value; })
|
||||
.showLabels(true);
|
||||
|
||||
d3.select(".modal-body-right svg")
|
||||
.datum(self.convertFigures())
|
||||
.transition().duration(1200)
|
||||
.call(chart);
|
||||
|
||||
return chart;
|
||||
});
|
||||
},
|
||||
convertFigures: function () {
|
||||
var self = this;
|
||||
var collValues = [];
|
||||
$.each(self.data.figures, function(k,v) {
|
||||
collValues.push({
|
||||
"label" : k,
|
||||
"value" : v.count
|
||||
});
|
||||
});
|
||||
|
||||
return [{
|
||||
key: "Collections Status",
|
||||
values: collValues
|
||||
}];
|
||||
},
|
||||
fillLoadedModal: function (data) {
|
||||
$('#collectionSizeBox').show();
|
||||
$('#collectionSyncBox').show();
|
||||
if (data.waitForSync === false) {
|
||||
$('#show-collection-sync').text('false');
|
||||
}
|
||||
else {
|
||||
$('#show-collection-sync').text('true');
|
||||
}
|
||||
var calculatedSize = data.journalSize / 1024 / 1024;
|
||||
$('#show-collection-size').text(calculatedSize);
|
||||
$('#show-collection').modal('show');
|
||||
},
|
||||
getCollectionId: function () {
|
||||
return this.myCollection.id;
|
||||
},
|
||||
getCollectionStatus: function () {
|
||||
return this.myCollection.status;
|
||||
},
|
||||
hideModal: function () {
|
||||
$('#show-collection').modal('hide');
|
||||
}
|
||||
|
||||
});
|
|
@ -14,6 +14,7 @@ window.CollectionListItemView = Backbone.View.extend({
|
|||
events: {
|
||||
'click .pull-left' : 'noop',
|
||||
'click .icon-edit' : 'editProperties',
|
||||
'click .icon-info-sign' : 'showProperties',
|
||||
'click': 'selectCollection'
|
||||
},
|
||||
render: function () {
|
||||
|
@ -25,6 +26,13 @@ window.CollectionListItemView = Backbone.View.extend({
|
|||
event.stopPropagation();
|
||||
window.App.navigate("collection/" + encodeURIComponent(this.model.get("id")), {trigger: true});
|
||||
},
|
||||
|
||||
showProperties: function(event) {
|
||||
event.stopPropagation();
|
||||
window.App.navigate(
|
||||
"collectionInfo/" + encodeURIComponent(this.model.get("id")), {trigger: true}
|
||||
);
|
||||
},
|
||||
|
||||
selectCollection: function() {
|
||||
window.App.navigate(
|
||||
|
|
|
@ -28,6 +28,11 @@ var collectionsView = Backbone.View.extend({
|
|||
}).render().el);
|
||||
}, this);
|
||||
|
||||
//append info icon for loaded collections
|
||||
$('.loaded').parent().prev().append(
|
||||
'<i class="icon-info-sign" alt="Show collection properties" title="Show collection properties"></i>'
|
||||
);
|
||||
|
||||
$('#searchInput').val(searchOptions.searchPhrase);
|
||||
$('#searchInput').focus();
|
||||
var val = $('#searchInput').val();
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require, exports, Backbone, EJS, $*/
|
||||
|
||||
var loginView = Backbone.View.extend({
|
||||
el: '#content',
|
||||
el2: '.header',
|
||||
el3: '.footer',
|
||||
|
||||
init: function () {
|
||||
},
|
||||
|
||||
events: {
|
||||
"click #submitLogin" : "login",
|
||||
"keydown #loginUsername" : "checkKey",
|
||||
"keydown #loginPassword" : "checkKey"
|
||||
},
|
||||
|
||||
template: new EJS({url: 'js/templates/loginView.ejs'}),
|
||||
|
||||
render: function() {
|
||||
this.addDummyUser();
|
||||
|
||||
$(this.el).html(this.template.text);
|
||||
$(this.el2).hide();
|
||||
$(this.el3).hide();
|
||||
$.gritter.removeAll();
|
||||
|
||||
$('#loginUsername').focus();
|
||||
|
||||
//DEVELOPMENT
|
||||
$('#loginUsername').val('admin');
|
||||
$('#loginPassword').val('admin');
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
addDummyUser: function () {
|
||||
this.collection.add({
|
||||
"userName" : "admin",
|
||||
"sessionId" : "abc123",
|
||||
"password" :"admin",
|
||||
"userId" : 1
|
||||
});
|
||||
},
|
||||
|
||||
checkKey: function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.login();
|
||||
}
|
||||
},
|
||||
|
||||
login: function () {
|
||||
var username = $('#loginUsername').val();
|
||||
var password = $('#loginPassword').val();
|
||||
|
||||
if (username === '' || password === '') {
|
||||
arangoHelper.arangoNotification("Please fill out required fields");
|
||||
return;
|
||||
}
|
||||
var callback = this.collection.login(username, password);
|
||||
|
||||
if (callback === true) {
|
||||
$(this.el2).show();
|
||||
$(this.el3).show();
|
||||
window.App.navigate("/", {trigger: true});
|
||||
$('#currentUser').text(username);
|
||||
this.collection.loadUserSettings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
|
@ -98,6 +98,7 @@
|
|||
"ERROR_REPLICATION_INVALID_APPLY_STATE" : { "code" : 1407, "message" : "invalid apply state" },
|
||||
"ERROR_REPLICATION_UNEXPECTED_TRANSACTION" : { "code" : 1408, "message" : "invalid transaction" },
|
||||
"ERROR_REPLICATION_STOPPED" : { "code" : 1409, "message" : "replication stopped" },
|
||||
"ERROR_REPLICATION_INVALID_CONFIGURATION" : { "code" : 1410, "message" : "invalid replication apply configuration" },
|
||||
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
|
||||
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
|
||||
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },
|
||||
|
|
|
@ -128,6 +128,7 @@ ERROR_REPLICATION_UNEXPECTED_MARKER,1406,"unexpected marker","Will be raised whe
|
|||
ERROR_REPLICATION_INVALID_APPLY_STATE,1407,"invalid apply state","Will be raised when an invalid apply state file is found."
|
||||
ERROR_REPLICATION_UNEXPECTED_TRANSACTION,1408,"invalid transaction","Will be raised when an unexpected transaction id is found."
|
||||
ERROR_REPLICATION_STOPPED,1409,"replication stopped","Will be raised when the replication application is stopped."
|
||||
ERROR_REPLICATION_INVALID_CONFIGURATION,1410,"invalid replication apply configuration","Will be raised when the configuration for the replication application is invalid."
|
||||
|
||||
################################################################################
|
||||
## ArangoDB query errors
|
||||
|
|
|
@ -685,6 +685,14 @@ bool TRI_IsStringJson (TRI_json_t const* json) {
|
|||
return IsString(json);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief determines whether the JSON passed is of type number
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TRI_IsNumberJson (TRI_json_t const* json) {
|
||||
return json != NULL && json->_type == TRI_JSON_NUMBER;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief adds a new sub-object to a list object, copying it
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -277,6 +277,12 @@ void TRI_FreeJson (TRI_memory_zone_t*, TRI_json_t*);
|
|||
|
||||
bool TRI_IsStringJson (TRI_json_t const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief determines whether the JSON passed is of type number
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TRI_IsNumberJson (TRI_json_t const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief adds a new sub-object to a list object, copying it
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -94,6 +94,7 @@ void TRI_InitialiseErrorMessages (void) {
|
|||
REG_ERROR(ERROR_REPLICATION_INVALID_APPLY_STATE, "invalid apply state");
|
||||
REG_ERROR(ERROR_REPLICATION_UNEXPECTED_TRANSACTION, "invalid transaction");
|
||||
REG_ERROR(ERROR_REPLICATION_STOPPED, "replication stopped");
|
||||
REG_ERROR(ERROR_REPLICATION_INVALID_CONFIGURATION, "invalid replication apply configuration");
|
||||
REG_ERROR(ERROR_QUERY_KILLED, "query killed");
|
||||
REG_ERROR(ERROR_QUERY_PARSE, "%s");
|
||||
REG_ERROR(ERROR_QUERY_EMPTY, "query is empty");
|
||||
|
|
|
@ -197,6 +197,9 @@ extern "C" {
|
|||
/// Will be raised when an unexpected transaction id is found.
|
||||
/// - 1409: @LIT{replication stopped}
|
||||
/// Will be raised when the replication application is stopped.
|
||||
/// - 1410: @LIT{invalid replication apply configuration}
|
||||
/// Will be raised when the configuration for the replication application is
|
||||
/// invalid.
|
||||
/// - 1500: @LIT{query killed}
|
||||
/// Will be raised when a running query is killed by an explicit admin
|
||||
/// command.
|
||||
|
@ -1309,6 +1312,17 @@ void TRI_InitialiseErrorMessages (void);
|
|||
|
||||
#define TRI_ERROR_REPLICATION_STOPPED (1409)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1410: ERROR_REPLICATION_INVALID_CONFIGURATION
|
||||
///
|
||||
/// invalid replication apply configuration
|
||||
///
|
||||
/// Will be raised when the configuration for the replication application is
|
||||
/// invalid.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_REPLICATION_INVALID_CONFIGURATION (1410)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1500: ERROR_QUERY_KILLED
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue