1
0
Fork 0

Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel

This commit is contained in:
Oreste Panaia 2013-07-15 22:54:38 +08:00
commit b4acec0fe6
50 changed files with 1974 additions and 187 deletions

View File

@ -124,6 +124,7 @@ ReplicationFetcher::~ReplicationFetcher () {
delete _endpoint;
}
TRI_DestroyApplyStateReplicationApplier(&_applyState);
TRI_DestroyMasterInfoReplication(&_masterInfo);
}

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -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);
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -6,3 +6,8 @@
margin-left: 1px !important;
}
.svgFigures {
margin-top: 0px !important;
width: 250px;
height: 250px;
}

View File

@ -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;
}

View File

@ -1,3 +1,7 @@
#tableView {
cursor: pointer;
}
#sourceFooter {
margin-top: 0;
background: none;

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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" },

View File

@ -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) {

View File

@ -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);
}
});
}
});

View File

@ -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";
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
});
},

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");
};

View File

@ -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);

View File

@ -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;

View File

@ -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"
});
});
});
});

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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));
});

View File

@ -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

View File

@ -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));
});

View File

@ -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]);
});
});
});

View File

@ -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");
});
});
});

View File

@ -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/',

View File

@ -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;
}
});

View File

@ -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({});

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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');
}
});

View File

@ -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(

View File

@ -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();

View File

@ -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();
}
}
});

View File

@ -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" },

View File

@ -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

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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");

View File

@ -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
///