-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathace.js
2543 lines (2193 loc) · 99.6 KB
/
ace.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// ace.js Copyright (c) The New Waters Foundation, all rights reserved. For license see: http://OpenAce.org/license?id=ace.js
//$.getScript("http://crypto-js.googlecode.com/files/CryptoJS%20v3.0.2.zip"); // Fix. Encryption algorithms. Include core in download code to eliminate dependencies and optimize efficiency.
/* Fix. Testing.
var ACE = ace();
var testObj = ace({
"cmd" : "new",
"aceType" : "task"
});
//ace._ACE = "Modified";
ace('testID');
*/
//log("ACE LOADING");
// The ace function. Use ace(aceID) to return the object represented by aceID. aceType can be used to restrict the type(s) of entity that are acceptable to return, and can be a single or space-separated string representing the aceID or an alias of the specified aceType(s).
function ace(aceID, aceType) { // Fix! This is all essentially junked now. Only purpose is to return and ACE instance on login or via omniscience check.
ace._ACE = ace._ACE || new AceAPI(); // Will establish a persistent refernce to the single instance of the aceAPI object.
var ACE = ace._ACE, // The var used globally within the ace() member objects.
caller = ace.caller, // Fix. This should be made more useful.
argType = typeOf(aceID),
result = null;
ace.fixACE = ace.fixACE || ((function holdScope() { // Form a closure to maintain the scope of a single aceAPI object as ace._ACE.
var scope_ACE;
if (ace._ACE instanceof AceAPI) {
ACE = scope_ACE = ace._ACE;
} else {
// Fix. Notify Security Alert.
//ace._ACE = ACE = scope_ACE;
}
return (function() { return scope_ACE; });
})())
if (ace._ACE instanceof AceAPI) { // Fix. Implement some mechanism to prevent ace._ACE from being modified from outside the ace() function. Add listener, etc?
// Fix. Does nothing but ensure lack of modifications to prototype..
} else {
// Fix. Notify Security Alert.
ace._ACE = ACE = ace.fixACE();
}
/*
window.ACE = window.ACE || new AceAPI(getUserLoginObj()); // Fix! Fold this into a closure to protect within ace.
var ACE = window.ACE; // Fix! Remove global reference!
(function() { // Closure to hold a static instance of the AceAPI object.
var typeVal = typeOf(ACE);
log(typeVal, "typeVal");
ACE = ACE || new AceAPI();
return function() { return ACE; };
})(); // Fix! Correct all of this when time is not quite as of the essence.
if (this != window) { // Fix. Use best way to see if this was instantiated using new ace(); and return null if so.
return; // Fix? Should re-assign this to the correct object type and return that.
}
*/
if (argType == "string") {
if (aceID == "rand") {
return randString((_.isNumber(aceType)) || 32); // Fix. Ensure Integer, also may not end up using aceType.
} else if (_.isAceID(aceID)) { // Fix? Perform this here?
return ACE.ace(aceID);
} else {
return; // Fix. Error handling, Notification.
}
} else if (argType == 'object') {
if ((aceID.cmd) || aceID.aceCall) {
result = ACE.aceCall(aceID);
} else if (aceID.login) {
result = ACE.userLogin(aceID); // Fix? Handle login result additionally?
return ((result)?(ACE):(null));
} else {
return; // Fix. Error handling.
}
//ACE.aceCall(aceID); // Fix. Integrity checking and ensure only single AceObj is returned.
} else if (argType == 'undefined') {
result = ACE.aceCall(); // Fix. What should we do here?
} else {
return; // Fix. Error handling and default behavior.
}
if (typeOf(result) == "array") {
if (result.length == 1) {
return result[0];
} else if (result.length > 1) {
return result; // Fix. Figure out best way to return multiple objs asynchronously. Make AceObj act as container for other AceObjs for multiples?
} else {
// Fix. Error handling.
}
} else {
// Fix. Error handling.
}
// Private methods
// Returns the primary aceType of the entity associated with aceID.
function aceType(aceID) {
// Fix.
}
// Converts an aceID into an array of name segments, as separated by '-' and '_'.
function aceIDtoArray(aceID) {
aceID = aceIDchop(aceID);
var segs = aceID.split("_");
var idArray = segs[0].split("-");
if (segs[1]) {
// Fix. Complete behavior for concatenated system IDs.
}
return segs;
}
// Generates random string for salting and hash generation, etc.
function randString(length, chars) {
if (!chars) { chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; }
if (!length) { length = 256; }
var charString = '',
charsL = chars.length;
for (var i=0; i<length; i++) {
charString += chars.charAt(Math.floor(Math.random()*charsL));
}
return charString;
}
// Drops the ace_ prefix from an aceID and returns the modified string for consistent usage.
function aceIDchop(aceID, prefix) {
//console.log("aceIDchop() aceID: "+aceID); // Fix.
if (typeof(aceID) != 'string' || !aceID.length) { return false; } // Fix. Return as string? Error handling.
if (prefix) { aceID = aceIDChop(aceID); } else { prefix = "ace_"; }
if ((aceID.length > prefix.length) && (aceID.substr(0,prefix.length) == prefix)) {
return aceID.substr(prefix.length);
} else {
return aceID;
}
// Fix. The following was an original hack combining this with what became aceIDtoArray().
// var segs = aceID.split("_");
// if (segs[0] == 'ace') { segs.shift(); } // segs.splice(0, 1); }
// if (segs[1]) {
// return segs.join('_');
// } else {
// return segs[0];
// }
}
// Used to return an empty or faulty AceObj when appropriate.
function badCall(aceID) {
var items = {
"cor" : {
"ace" : aceID,
"nam" : "Bad AceObj",
"dsc" : "There was an error loading this aceObj.",
"typ" : "bad"
},
"sys" : {
"topSubAlias" : null
}
};
if (_.isObject(aceID)) {
items.ace = "bad";
items.sys.callObj = aceID;
}
return new AceObj(items);
}
// Boolean check to determine whether aceID represents a 'typ' entity structure by using simple name prefix check. Returns true or false.
function isTyp(aceID) {
if ((!(aceID = aceIDchop(aceID))) || (aceID.length < 5)) { return false; }
return (aceID.substr(0,3) == "typ") ? (true) : (false);
}
/////// Objects available for privilaged use to this object. /////////////////////
// The abstraction used to communicate with the central ACE system. The connection is abstracted and can be a local system call, across a network, or any other mechanism.
function AceAPI() {
var _AceAPI = this,
userID = null,
userName = null,
markedTimes = [],
uiArray = [], // Used to track all AceUI instances currently loaded.
data = null, // The object abstraction used to interact with data locally, which automatically conveys through the ACE network.
keyCheck = {}; // Used by aceCheck to verify mutual access to this specific AceAPI for authentication.
function initialize() {
data = new AceData(_AceAPI);
}
// Authenticates user and returns their userID if successful, else null. Receives an object containing {"login":TRUE|FALSE, "userName":user, ("password":pass || "key":keyHash)}
this.userLogin = function userLogin(loginObj) {
loginObj = { // Fix! Drop this.
"login" : 1,
"userName" : "testUser",
"passHash" : "1234567890",
"keyHash" : "0987654321"
}
userName = loginObj.userName;
//var passHash = loginObj.passHash;
//var keyHash = loginObj.keyHash;
userID = "usr-test"; // Fix!!! Quick hack for testing. Should check local storage for auto-login, and connect through comm if desired. Else should request username and password.
userName = "TestUser"; // Fix! Just for testing.
// Fix! Set userID and loginObj to fixed state here. Perform remaining login tasks!
return userID;
}
// Returns the userID of the current user if logged in, null otherwise. If name is set, the userName will be returned.
this.loggedIn = function loggedIn(name) { // Fix. Identify best way to pass name into this, Remove auto-call?
if (name) {
return userName; // Fix? Error handling, check access?
} else if (userID) {
return userID; // Fix! Ensure this is passed by value.
}
return null;
//return ((userID)?(ace().get('Name')):(null));
}
// Used to securely pass calls and data access scope between objects.
function callKey(callObj) {
return function callPass(localACE) {
// Fix.
}
}
// Verifies a correct match between two key pairs. Used to authenticate mutual simultaneous access to this specific AceAPI by objects not sharing the same scope.
this.aceCheck = function AceAPI_aceCheck(keyString) {
var value = null;
if (typeof(keyString) == "string") {
value = keyCheck[keyString];
if (value) {
delete keyCheck[keyString];
if (value != "verified") {
keyCheck[value] = "verified";
}
return value;
}
} else if (!keyString) {
//keyString = randString(); // Fix. Ensure doesn't already exist. Miniscule odds but theoretically possible.
keyCheck[keyString = randString()] = randString();
return keyString;
}
}
// The universal shortcut method for returning an aceObj represented by aceID. Actually just passes the call to data.ace() or data.aceCall(); loadRadius sets the number of peripheral load steps to take in loading lnk and sub-entities.
this.ace = function AceAPI_ace(aceID, loadDepth) {
if (_.isAceID(aceID)) {
return data.ace(aceID, loadDepth);
} else if (_.isObject(aceID)) { // If using ACE.ace() as shorthand for ACE.aceCall(); Fix? Allow this?
var callObj = aceID;
if (!_.isString(callObj.cmd)) { // Fix. Ensure solid. Allow this? This block is used to handle shorthand calls like { "set":"aceID", "key":"thisKey", "val":"someVal" }
if (_.isString(callObj.get)) {
return data.aceCall({"cmd":"get","key":callObj.get});
} else if (_.isString(callObj.set)) {
if (_.isString(callObj.val)) {
return data.aceCall({"cmd":"set","key":callObj.set,"val":callObj.val});
}
} else if (_.isString(callObj["new"])) {
if (_.isString(callObj.val)) {
return data.aceCall({"cmd":"set","key":callObj.set,"val":callObj.val});
}
} else if (_.isString(callObj.del)) {
return data.aceCall({"cmd":"del","key":callObj.del});
}
return badCall(callObj); // Fix? Error handling, notification.
}
return data.aceCall(callObj);
}
}
// The universal access method for the ACE API. Accepts an object using the ReSTful ACE communications protocols, as single or batch requests. See http://openace
this.aceCall = function AceAPI_aceCall(callObj, caller) { // Fix. This function needs re-optimized since converting the call process.
var aceID, key, value, commandBlock, aceType, result, typ,
callType = typeOf(callObj),
resultsArray = [];
if (callType == "undefined") { return data.ace("_system_", "sys"); } // Creates and returns an AceObj with no aceID, typically used to create an access point for system and retain a closure reference of the AceAPI object.
if (callType == "string") { return data.ace(callObj); } // Allow single string arg for possibility to make "get" aceCall using it as an aceID.
if (callType == "array") { return data.aceCall(callObj, "container"); } // Creates and returns an AceObj with no aceID that serves as a collection of AceObjs to handle batch operations on or route to other processes.
if (callType != 'object') { return; } // Fix. Error handling.
// Call Redirections // Fix. These are old.
if (callObj.aceCall) {
// return ACE.aceCall(value);
} else if (callObj.cmd || callObj.command) { // Fix? Allow shorthand? If so, establish and obtain in data?
callObj.cmd = (callObj.cmd || callObj.command);
return [data.aceCall(callObj)];
}
// Iterate through the acceptable callObj command types:
if (value = callObj["get"]) { // Analogous to ReST: GET, or CRUD: load.
//log("aceCall to ACE 'get': "+value);
callType = typeOf(value);
if (_.isAceID(value)) { // Can be in form { "get" : "{aceID}" (, aceType:"aceType")}
result = data.aceCall({
"cmd" : "get",
"aceID" : value,
"typ" : callObj.aceType
});
resultsArray.push(result);
} else if (callType == 'object') {
if (aceID = value["aceID"]) { // Single nested call { "get" : { "aceID":"{aceID}" } }
result = data.aceCall({
"cmd" : "get",
"aceID" : aceID
});
}
resultsArray.push(result);
} else if (callType == 'array') { // Multiple get calls, can take several forms:
var thisItem;
for (key in value) {
callType = typeOf(thisItem = value[key]);
if (callType == 'string') { // { "get" : [ "{aceID}", "{aceID}", ... ] }
result = data.aceCall({
"cmd" : "get",
"aceID" : thisItem
});
resultsArray.push(result);
} else if (callType == 'object') { // { "get" : [ {"aceID":"{aceID}"}, {"aceID":"{aceID}"}, ... ] }
if (thisItem["aceID"]) {
result = data.aceCall({
"cmd" : "get",
"aceID" : thisItem["aceID"]
});
resultsArray.push(result);
} else {
// Fix? Other possibilities?
}
} else {
// Fix. Error handling.
}
/*result = data.aceCall({ // Fix. Left from previos structure, check.
"cmd" : "get",
"aceID" : value[key]
}); */
}
} else {
// Fix. Error handling.
}
}
if (value = callObj["set"]) { // Analogous to ReST: PUT, or CRUD: update.
//log("aceCall to value 'set': "+value);
callType = typeOf(value);
if (callType == 'object') { // Nested call { "set" : { "{aceID}":{"property":"value"}, "{aceID}":{"property":"value"}, ...} }
for (aceID in value) {
if (typeOf(commandBlock = value[aceID]) == 'object') {
result = data.aceCall({
"cmd" : "set",
"aceID" : aceID,
"items" : value[aceID] // Fix? Parse the individual prop:value pairs here?
});
resultsArray.push(result);
} else {
// Fix. Error handling, Other options?
}
}
} else {
// Fix. Error handling.
}
}
if (value = callObj["new"]) { // Analogous to ReST: POST, or CRUD: create.
var i=0, len=0;
//log("aceCall to value 'new': "+value);
callType = typeOf(value);
if (callType == 'object') { // Nested call { "new" : { "{aceType}":[{"aceID":"value", "property":"value", ...}, {"alias":"value", ...}, ...], "{aceType}":{"aceID":{"property":"value", ...}, "alias":{"property":"value", ...}, ...} } }
for (aceType in value) {
if (typeOf(commandBlock = value[aceType]) == 'array') { // Nested array call { "new" : { "{aceType}":[{"aceID":"value", "property":"value", ...}, {"alias":"value", ...}, ...] }, ... }
if (len = commandBlock.length) {
for (i=0; i<len; i++) {
if (typeOf(key = commandBlock[i]) == 'object') {
aceID = (key.aceID || key.alias || _AceAPI.nextAceID()); // Fix. Remove alias and aceID items from commandBlock, resolve conflicts, duplicates, etc.
result = data.aceCall({
"cmd" : "new",
"typ" : aceType,
"aceID" : aceID,
"items" : key // Fix? Parse the individual prop:value pairs here?
});
resultsArray.push(result);
} else {
// Fix. Error handling.
}
}
} else {
// Fix. Error handling.
}
} else if (typeOf(commandBlock) == 'object') { // Nested objects with proposed alias as keys { "new" : { "{aceType}":{"aceID":{"property":"value", ...}, "alias":{"property":"value", ...}, ...} } }
for (aceID in commandBlock) {
result = data.aceCall({
"cmd" : "new",
"typ" : aceType,
"aceID" : aceID,
"items" : commandBlock[aceID]
});
resultsArray.push(result);
}
} else {
// Fix. Error handling, Other options?
}
}
} else {
// Fix. Error handling.
}
}
if (value = callObj["del"]) { // Analogous to ReST: DELETE, or CRUD: delete.
//log("aceCall to value 'del': "+value);
callType = typeOf(value);
if (_.isAceID(value) || value == "*") { // Fix? Allow clearing cache from top-level call like this?
result = data.aceCall({
"cmd" : "del",
"aceID" : value
});
resultsArray.push(result);
} else if (callType == 'object') {
for (key in value) {
result = data.aceCall({
"cmd" : "del",
"aceID" : value[key]
});
resultsArray.push(result);
}
} else {
// Fix. Error handling.
}
}
if (value = callObj["dat"]) { // If loading from a file, stream, or other data source. Imports the object but does not propogate new or get calls.
key = (callObj.src || callObj.source);
if (key=="file" || key=="stream") {
// Fix. Handling for this. Security and src comparison checks.
} else {
// Fix. Handling for this.
}
if (_.isObject(value)) {
_.each(value, function(items,id) {
resultsArray.push(data.aceCall({
"cmd" : "dat",
"src" : key,
"aceID" : id,
"items" : items
}));
});
} else {
return badCall(value); // Fix. Error handling.
}
return resultsArray;
}
if ((value = resultsArray.length) > 1) {
return data.ace(resultsArray);
} else if (value = 1) {
return resultsArray[0];
} else {
// Fix. Error handling.
}
}
// Returns an incremented aceID. Does nothing to any actual aceObjs, just performs the increment.
this.nextAceID = function AceAPI_nextAceId(aceID) {
if (!aceID || aceID == "") { return "A"; } // Fix? aceIDs start with a letter.
if (typeof(aceID) != 'string') { return false; }
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var lastChar = aceID.charAt(aceID.length - 1);
if (lastChar == '_') { return aceID + "a"; } // Fix? Handle this behavior.
if (lastChar == 'Z') { return (aceID + '0'); }
var newID = aceID[(aceID.length - 1)] = (chars.charAt(chars.indexOf(lastChar) + 1));
//log("aceID: |"+aceID+"|, newID: |"+newID+"|");
return newID;
}
// Used to gauge server load times across operations. eventNote is a string to associate with the event.
this.markTime = function AceAPI_markTime(eventNote) {
var timeNow = Date.now();
var thisEvent = { time:timeNow, note:eventNote }
if (markedTimes.length > 0) {
var slotNum = (markedTimes.length - 1);
var lastEvent = markedTimes[slotNum];
var firstEvent = markedTimes[0];
thisEvent.elapsed = (timeNow - lastEvent.time);
thisEvent.total = (timeNow - firstEvent.time);
} else {
thisEvent.total = thisEvent.elapsed = 0;
}
markedTimes.push(thisEvent);
}
// Used to register and remove AceUI elements, send commands to and otherwise access the UI system associated with this ACE login.
this.ui = function AceAPI_ui(callObj) {
if (!_.isObject(callObj)) { return; }
var cmd = (callObj.cmd || callObj.command),
uiType = callObj.uiType,
domElement = callObj.domElement,
uiID = callObj.uiID,
aceID = callObj.aceID;
}
// Creates (if referTo is set) or resolves (if !referTo) an alias and propogates the call across this node.
this.als = this.alias = function AceAPI_als(alias, referTo) {
if (referTo) {
return data.setAlias(alias, referTo);
} else {
return data.getAlias(alias);
}
}
// Resolves a variable to a string output. Used to handle items that may be strings, aceID's, or even aceObjs.
var toStr = this.toStr = function AceAPI_toStr(objStr) {
if (!objStr) { return null; }
return data.toStr(objStr);
}
// Returns an empty object template structure for the entity represented by aceID.
this.aceTyp = function AceAPI_aceTyp(aceID) {
return data.aceTyp(aceID);
}
// These allow functions defined within ace() to be accessed outside of its scope. Fix.
this.aceIDChop = aceIDchop;
initialize();
}//AceAPI
// The object abstraction used to interact with data locally, which automatically propogates through the ACE network.
function AceData(ACE) {
_AceData = this;
var db = new DatabaseObj(); // An abstraction used for local database access.
var comm = new AceComm(aceDataCallBack); // Used for all communications with servers, other clients, and systems across network interfaces.
var cryptObj = new AceCryptObj();
var memObj = { // An object representation of all data currently in local memory.
"items" : { // Contains all aceIDs stored locally, and links them to the object data associated with their internal items var.
"aceID" : "AceObj.items"
},
"alias" : { // Contains all Alias aceIDs stored locally that refer to a System aceID or another alias.
"aliasID" : "aceID"
},
"aceObj" : { // All aceObjs currently loaded in system memory.
"aceID" : "aceObj"
},
"typ" : { // The alias calls of entity types maintained locally, used to instantiate complex objects using layering via aceObj's "typ" fields.
},
"tempIDs" : { // All aceObjs with temporary aceIDs (tmp-0Aa...) currently loaded in system memory and referenced by that ID.
"tmp-max" : "maxID" // The highest tempID currently issued by this system.
},
"que" : { // Stores requests to be made to server and items returned but not loaded yet.
"out" : { // an object containing calls to be sent to the server in a batch call; divided by API commands.
"get" : {
},
"set" : {
},
"new" : {
},
"del" : {
}
},
"in" : { // a collection of JSON objects representing sequential batches of returned call results.
}
},
"sys" : { // Used to hold system objects in memory for storing to disk between page loads rather than having to re-instantiate everything. // Fix? Security risk?
"db" : db,
"comm" : comm
//"userID" : _AceAPI.loggedIn() // Fix.
}
}
// Used to grab existing aceObj with aceID or instantiate a new one. If aceID represents a 'typ' structure, a new aceObj of that type will be instantiated and returned. If aceID is an array of aceIDs or aceObjs, a new container aceObj will be instantiated to hold them. loadDepth sets the number of peripheral load steps to take in loading lnk and sub-entities.
var _ace = this.ace = function AceData_ace(aceID, loadDepth) {
//if (!securityCheck(aceID, caller, "ace")) { return false; } // Fix! Handle for cases of comm latency. Return AceObj. Error handling and notification.
if (_.isString(aceID)) {
if (!(aceID = aceIDchop(aceID))) { // Fix. Make efficient. Use single point for aceID check and cleanup.
return badCall(); // If this is an invalid aceID, return a bad object.
} else if (memObj.aceObj[aceID]) { // If this AceObj exists in memory, return it.
return memObj.aceObj[aceID];
} else if (result = db.ace(aceID)) { // If we find this aceID in the local db, instantiate it.
return newAceObj(result, aceID);
} else { // Otherwise, send a 'get' call through to comm which returns a waiting AceObj.
return comCall({
"cmd" : "get",
"aceID" : aceID,
"loadDepth" : loadDepth,
"caller" : caller
});
}
} else if (_.isObject(aceID)) { // If using ace() as alias for aceCall();
return aceCall(aceID);
} else if (_.isArray(aceID)) { // Fix. Do we want this? Probably not...
return badCall("Called using array"); // Fix. return new AceObj(aceID, 'container');
} else {
return badCall(aceID);
}
}//AceData_ace()
// Used to call the AceData abstraction for a single AceObj, handles logic and safety for calls. Attempts local action, propogating to the local db, and then sends to aceComm for central server transmission. Ultimately it makes more sense to centralize the security checking, decision-making, and propogation within this function because the object reference is never passed outside of AceAPI or AceObj so we can focus on ensuring integrity of these structures mere dependably than for those objects which we pass back to the user applications.
var aceCall = this.aceCall = function AceData_aceCall(callObj) {
callObj.dataCallTime = Date.now();
//if (_.isAceID(callObj) { return _ace(callObj); } // Fix.
if (!_.isObject(callObj) || (!safetyCheck(callObj))) { return badCall(callObj); } // Fix. Error handling.
var cmd = callObj.cmd = (callObj.cmd || callObj.command); // Fix. Ensure this is only called by AceAPI and remove extra checks.
var aceID = callObj.aceID = (callObj.aceID || callObj.ace || callObj.key || callObj.scope); // Fix. Same as above. Ensure uniformity.
if (!cmd || !aceID) { return badCall(callObj); } // Fix? Other behavior here?
var scopeArray = resolveScope(aceID);
if (scopeArray) {
var objArray = [];
_.each(scopeArray, function(val, key) {
callObj.aceID = key;
objArray.push(AceData_aceCall(callObj));
})
return objArray;
}
if (cmd == "get") { // command == "get"
return getCall(callObj);
} else if (cmd == "set") { // command == "set"
return setCall(callObj);
} else if (cmd == "new") { // command == "new"
return newCall(callObj);
} else if (cmd == "del") { // command == "del"
return delCall(callObj);
} else if (cmd == "dat") { // command == "dat"
return datCall(callObj);
} else {
return badCall(callObj); // Fix? Handling for incorrect commands.
}
}//AceData_aceCall()
// Translates a scope string such as "aceID:parents" or other relational symbols into the resulting aceIDs. Returns an array if successful, even if empty. Returns null if the string does not translate into a scope.
function resolveScope(scopeStr) {
if (!_.isString(scopeStr)) { return; }
var scopeArray = null;
if (scopeStr == "*") {
scopeArray = dbCall({"cmd":"get", "aceID":"*"}); // Fix. Determine most appropriate meaning for this.
} else if (scopeStr.indexOf(":")>0) {
scopeArray = scopeStr.split(":");
// Fix. Complete this.
}
return scopeArray;
}
// The central handler for AceData calls of 'cmd'=='get'.
function getCall(callObj) {
var aceID = (callObj.aceID || callObj.ace);
if (_.isAceID(aceID)) {
return _ace(aceID);
} else {
return badCall(callObj);
}
}
// The central handler for AceObj calls of 'cmd'=='set'.
function setCall(callObj) {
// if (typeOf(callObj) != "object") { return; } // Fix? Shouldn't be necessary.
if (!callObj.aceID) { return; } // Fix?
var resultObj = {},
callItems = callObj.items,
objItems = memObj.items[callObj.aceID];
if (callItems) {
for (var item in callItems) {
resultObj[item] = { // Fix. Use best callObj identifier (time, user.sub-aceID, etc. to stamp call and include sub-portion of entity that was modified. Include also the previous value before the mod?
"userID" : ACE.loggedIn(),
"sect" : "cor",
"new" : callItems.item
}
if (item=="props") { // Fix? Add more... // Check for illegal item names and skip them.
// Fix? Do anything here?
} else if (objItems.cor.item != undefined) { // Fix! Needs converted from old structure.
resultObj[item].old = objItems.cor.item;
objItems.cor.item = callItems.item;
} else if (objItems.cor.props.item != undefined) { // Fix! Needs converted from old structure.
resultObj[item].sect = "cor.props";
resultObj[item].old = objItems.cor.props.item;
objItems.cor.props.item = callItems.item;
}
// Fix. Include other possible sub-modules.
}
} else {
// Fix. Error handling, other options?
}
//memObj.que.out.// Fix. Complete this.
return memObj.aceObj[callObj.aceID];
db.aceCall(callObj);
comm.aceCall(callObj);
}
// The central handler for AceObj calls of 'cmd'=='new'. Returns the newly generated AceObj.
function newCall(callObj) { // Fix. This all needs reviewed/tested.
callObj.status = callObj.cmd = "new";
if (!_.isAceID(callObj.aceID || callObj.ace)) { callObj.aceID = _AceData.nextAceID(); }
if (!callObj.typ) { callObj.typ = "typ-ent"; }
var aceID = (callObj.aceID || callObj.ace),
typ = callObj.typ,
items = aceTyp(typ);
items.als.push(aceID); // Fix? Add this after loading truly assigned aceID only?
items.cor.ace = aceID;
items.cor.typ = typ;
items.sys.topSubID = "a";
items.sys.created = Date.now();
items.sys.creator = ACE.loggedIn();
memObj.tempIDs[aceID] = null;
newAceObj(_.extend(items, (callObj.items || {})), aceID); // Fix? Ensure this works correctly in all cases.
callObj.items = items; // packItems(aceID);
dbCall(callObj);
comCall(callObj);
return memObj.aceObj[aceID];
}
// The central handler for AceObj calls of 'cmd'=='del'. // Fix. Determine best behavior. Currently removes items from local db and updates their internal status as 'deleted'. Probably want to check all references for this user in a particular scope and propose deletion for all shared lnks, Outright removing references where able.
function delCall(callObj) {
var aceID = callObj.aceID;
if (_.isAceID(aceID)) {
// Fix. Complete this. Trace links to remove local instances, update UI. Identify most appropriate scope, update links in accordance with that.
delete memObj.aceObj[aceID]; // Fix. Don't actually want to do this.
delete memObj.items[aceID]; // Fix? Should probably keep in memory and simply update status.
dbCall(callObj); // Fix? Keep in db as well so as to avoid redundant loading? Handle otherwise?
comCall(callObj);
} else {
return badCall(callObj); // Fix. Error handling, alert, appropriate return value.
}
}
// Used when loading existing data into local system as from files, streams, and comm return calls.
function datCall(callObj, queIDs) { // Fix. Error checking.
if (!_.isObject(callObj)) { return; } // Fix? Shouldn't be necessary.
var aceID = callObj.aceID,
items = callObj.items,
typ = (items.cor && items.cor.typ) ? (items.cor.typ) : ("typ-ent"),
lnkTypes = ["als","cor","itm","typ","has","lnk"], // Fix! Obtain programmatically.
obj = null;
if (!_.isObject(items)) { return badCall(callObj); } // Fix. Error handling, notification.
if (aceID.length == 3) { aceID = callObj.aceID = "typ-"+aceID; } // Fix! Temporary hack to facilitate data shorthand quickly.
if (memObj.items[aceID]) {
return memObj.aceObj[aceID]; // Fix! Handle case where object already exists in memory. Alert, merge options, fork, etc.
aceID = objCollision(callObj);
} // else {
// obj = newAceObj(aceTyp(typ), aceID);
// }
_.extend(items, aceTyp(typ), callObj.items);
// Resolves direct references to primitives (and objects?) by creating new AceObj and a corresponding lnk for them. Different cases where loading via inheritence vs legacy instance?
_.each(items, function(itms,itm) {
if (_.contains(lnkTypes,itm)) { // If this is a lnk-typ category
if (itm == "als") {
_.each(itms, function(als) {
if (!setAlias(als, aceID)) {
// Fix! Handle alias collisions.
}
});
} else {
_.each(itms, function(lnk, cat) {
if (_.isAceLnk(lnk)) {
_ace(lnk); // Fix? Does this ever loop perpetually?
} else if (_.isAceID(lnk)) {
// Fix? Do anything here?
} else if (_.isString(lnk)) {
lnk = newCall({
"typ" : "str",
"items" : {
"itm" : { "str" : lnk }
}
}).aceID();
} else if (_.isNumber(lnk)) {
lnk = newCall({
"typ" : "num",
"items" : {
"itm" : { "val" : lnk }
}
}).aceID();
} else if (_.isObject(lnk)) {
// Fix. Handle this?
} else {
// Fix.
}
lnk = lnkTo({
"aceID" : aceID,
"cat" : cat,
"itm" : itm,
"lnk" : lnk
});
});
}
} else {
obj.itm = itms; // Fix! Error, security checking, Merging, etc.
}
});
dbCall({
"cmd" : "set",
"aceID" : aceID,
"val" : obj
});
return obj;
}
// Used to handle calls made to the local AceDatabase object.
function dbCall(callObj) { // Fix! Ensure caller security of this operation
var result = null;
if (typeOf(callObj) != "object") { return; } // Fix?
if (!callObj.aceID) {
return badCall(callObj); // Fix. Error handling. Do anything else here?
}
if (memObj.items[callObj.aceID]) {
// Fix. Collision handling.
}
if (!callObj.cmd || callObj.cmd == "db") { callObj.cmd = "get"; } // Fix? May want to return rather than defaulting to 'get' call?
result = db.aceCall(callObj);
if (callObj.cmd == "get") { // Fix? Place logic elsewhere?
memObj.aceObj[callObj.aceID] = result;
}
return memObj.aceObj[callObj.aceID];
}
// Used to handle calls made to the comm object.
function comCall(callObj) { // Fix! Ensure caller security of this operation
var aceID = callObj.aceID;
if (!aceID) {
// Fix. What to do here?
}
if (!memObj.aceObj[aceID]) { // Fix. Complete this.
memObj.items[aceID] = aceTyp();
memObj.aceObj[aceID] = new AceObj({
"aceID" : aceID,
"status" : "waiting",
"cmd" : "com"
}, memObj.items[aceID]);
}
if (!callObj.cmd || callObj.cmd == "com") { callObj.cmd = "get"; }
comm.aceCall({
"cmd" : "get",
"aceID" : aceID,
"caller" : memObj.aceObj[aceID] // Fix. Pass callBack to handle latency.
});
memObj.tempIDs[aceID] = null; // Fix? Address duplicates only on collision rather than here?
return memObj.aceObj[aceID];
}
// Used to instantiate an AceObj into active memory. Does not propogate the instance, just centralizes the process to a single point.
function newAceObj(items, aceID) {
if (!_.isObject(items)) { items = entityCore(); }
if (!_.isAceID(aceID)) {
aceID = (_.isObject(items.cor) && _.isAceID(items.cor.ace)) ? (items.cor.ace) : (_AceData.nextAceID());
}
if (memObj.items[aceID]) {
return memObj.items[aceID]; // Fix! Handle collisions, notification, etc.
objCollision({
// Fix.
});
} else {
items.cor.ace = aceID; // Fix? Ensure items.cor is always the correct object.
}
memObj.items[aceID] = items;
return memObj.aceObj[aceID] = new AceObj(memObj.items[aceID]);
}
// Cycles through all of the AceObj items for aceID and performs an ace() call on their aceID's to instantiate those not already loaded.
function loadCall(aceID) {
// Fix. This has been transitioned directly into the AceObj.
// if (!aceID) { return; }
// var items = memObj.items[aceID],
// itemName = null,
// itemObj = null,
// subName = null,
// subItem = null;
// itemObj =
// for (itemName in items["ext"]) {
// _ace(item);
// _ace(memObj.items[aceID]["ext"][item]);
// }
// for (item in memObj.items[aceID]["asp"]) {
// _ace(item);
// _ace(memObj.items[aceID]["ext"][item]);
// }
}
// Resolves a variable to a string output. Used to handle items that may be strings, aceID's, or even aceObjs.
var toStr = this.toStr = function toStr(objStr) {
if (!objStr) {
return null;
} else if (_.isAceID(objStr)) {
return toStr(_ace(objStr));
} else if (_.isAceObj(objStr)) {
return objStr.toStr();
} else if (_.isString(objStr)) {
return objStr;
} else {
return objStr.toString(); // Fix? Default to error, null, or other options?
}
}
// Creates a new alias and propogates the call across this node.
var setAlias = this.setAlias = function AceData_setAlias(alias, referTo) {
if (!_.isAceID(referTo)) { return; }
var existing = getAlias(alias); // Fix? Will resolve recursive links, which breaks simple check below...
if (existing) {
if (existing == referTo) {
// Fix! Determine best behavior for recursive aliases.
return true; // Fix?
} else {
// Fix! Handle behavior if alias already exists towards aceID other than referTo.
}
return false; // Fix?
} else {
// Fix! Rules, permissions, error checking, handle latency, etc.
memObj.alias[alias] = referTo;
var obj = memObj.aceObj[referTo];
if (_.isAceObj(obj)) {
obj.als();
}
return true; // Fix?
}
}
// Checks for an alias and propogates the call across this node. On latency, ...
var getAlias = this.getAlias = function AceData_getAlias(alias) {
if (!_.isString(alias)) { return; }
var resolved = memObj.alias[alias];
return getAlias(resolved) || resolved; // For recursive references.
}
// Occurs when multiple instances of same AceObj exist, as when loaded through a aceCall(dat) call or due to changes made to temporary object during latency, etc.
function objCollision(callObj) {
var aceID = callObj.aceID,
items = callObj.items,
newID = 'tmp-'+_AceData.nextAceID(),
action = { // Fix. Identify best protocols here.
"act" : null,
"state" : "sta-conflict",
"var-memID" : aceID,
"var-datID" : newID
};
_ace(aceID).alt(newID);
memObj.tempIDs[newID] = aceID;
ACE.ui({
"act" : "msg",
"msg" : "msg-aceObj-conflict",
"choice" : {
"btn-merge" : act,
"btn-fork" : forkObj()
}
});
}
// Used on AceObj collision to merge multiple instances of same AceObj into single profile.
function mergeObjs(callObj) {
if (!_.isAceID(callObj.aceID)) { return; }
var aceID = callObj.aceID,
realID = getAlias(aceID),
thisObj = _ace(aceID),
tmpObj = null,
alsArray = [];