@@ -20,6 +20,9 @@ fiftyoneDegreesManager = function() {
2020 // by 1.
2121 var callbackCounter = 0;
2222
23+ // Array of JavaScript properties that have started evaluation.
24+ var jsPropertiesStarted = [];
25+
2326 // startsWith polyfill.
2427 var startsWith = function(source, searchValue){
2528 return source.lastIndexOf(searchValue, 0) === 0;
@@ -63,8 +66,11 @@ fiftyoneDegreesManager = function() {
6366 // Fetch a value safely from the json object. If a key somewhere down the
6467 // '.' separated hierarchy of keys is not present then 'undefined' is
6568 // returned rather than letting an exception occur.
66- var getFromJson = function(key) {
69+ var getFromJson = function(key, allowObjects, allowBooleans ) {
6770 var result = undefined;
71+ if (typeof allowObjects === ' undefined' ) { allowObjects = false ; }
72+ if(typeof allowBooleans === 'undefined') { allowBooleans = false ; }
73+
6874 if (typeof(key) === 'string') {
6975 var functions = json;
7076 var segments = key.split(' .' );
@@ -74,6 +80,10 @@ fiftyoneDegreesManager = function() {
7480 }
7581 if (typeof(functions) === "string") {
7682 result = functions;
83+ } else if (allowBooleans && typeof(functions) === "boolean") {
84+ result = functions;
85+ } else if (allowObjects && typeof functions === 'object' && functions !== null) {
86+ result = functions;
7787 }
7888 }
7989 return result;
@@ -95,56 +105,66 @@ fiftyoneDegreesManager = function() {
95105
96106 // Executes any Javascript contained in the json data. Sets the processedJs
97107 // flag to true when there is no further Javascript to be processed.
98- var processJSproperties = function(resolve, reject) {
108+ var processJsProperties = function(resolve, reject, jsProperties, ignoreDelayFlag ) {
99109 var executeCallback = true ;
110+ var started = 0;
100111
101- if (json.javascriptProperties !== undefined &&
102- json.javascriptProperties .length > 0) {
112+ if (jsProperties !== undefined &&
113+ jsProperties .length > 0) {
103114
104115 // Execute each of the Javascript property code snippets using the
105116 // index of the value to access the value to avoid problems with
106117 // JavaScript returning erroneous values.
107118 for(var index = 0;
108- index < json.javascriptProperties .length;
119+ index < jsProperties .length;
109120 index++) {
110121
111- var name = json.javascriptProperties[index];
112-
113- // Create new function bound to this instance and execute it.
114- // This is needed to ensure the scope of the function is
115- // associated with this instance if any members are altered or
116- // added. Avoids global scoped variables.
117- var body = getFromJson(name);
118-
119- if (body !== undefined) {
120- var func = undefined;
121- var searchString = ' // 51D replace this comment with callback function.' ;
122-
123- if (body.indexOf(searchString) !== -1){
124- callbackCounter++;
125- body = body.replace(/\/\/ 51D replace this comment with callback function./g, ' callbackFunc(resolveFunc, rejectFunc);' );
126- func = new Function(' callbackFunc' , ' resolveFunc' , ' rejectFunc' ,
127- " try {\n " +
128- body + " \n " +
129- " } catch (err) {\n " +
130- " console.log(err);" +
131- " }"
132- );
133- func(completedCallback, resolve, reject);
134- executeCallback = false ;
135- } else {
136- func = new Function(
137- " try {\n " +
138- body + " \n " +
139- " } catch (err) {\n " +
140- " console.log(err);" +
141- " }"
142- );
143- func();
122+ var name = jsProperties[index];
123+
124+ if (jsPropertiesStarted.includes(name) === false ) {
125+ // Create new function bound to this instance and execute it.
126+ // This is needed to ensure the scope of the function is
127+ // associated with this instance if any members are altered or
128+ // added. Avoids global scoped variables.
129+ var body = getFromJson(name);
130+ var delay = getFromJson(name + ' delayexecution' , false , true );
131+
132+ if ((ignoreDelayFlag || (delay === undefined || delay === false )) &&
133+ body !== undefined) {
134+ var func = undefined;
135+ var searchString = ' // 51D replace this comment with callback function.' ;
136+ completed = false ;
137+ jsPropertiesStarted.push(name);
138+ started++;
139+
140+ if (body.indexOf(searchString) !== -1){
141+ callbackCounter++;
142+ body = body.replace(/\/\/ 51D replace this comment with callback function./g, ' callbackFunc(resolveFunc, rejectFunc);' );
143+ func = new Function(' callbackFunc' , ' resolveFunc' , ' rejectFunc' ,
144+ " try {\n " +
145+ body + " \n " +
146+ " } catch (err) {\n " +
147+ " console.log(err);" +
148+ " }"
149+ );
150+ func(completedCallback, resolve, reject);
151+ executeCallback = false ;
152+ } else {
153+ func = new Function(
154+ " try {\n " +
155+ body + " \n " +
156+ " } catch (err) {\n " +
157+ " console.log(err);" +
158+ " }"
159+ );
160+ func();
161+ }
144162 }
145163 }
146164 }
147- } else {
165+ }
166+
167+ if(started === 0) {
148168 executeCallback = false ;
149169 completed = true ;
150170 }
@@ -198,7 +218,7 @@ fiftyoneDegreesManager = function() {
198218
199219 // Process the JavaScript properties.
200220 var process = function(resolve, reject){
201- processJSproperties (resolve, reject);
221+ processJsProperties (resolve, reject, json.javascriptProperties, false );
202222 }
203223
204224 var fireChangeFuncs = function(json) {
@@ -308,6 +328,59 @@ fiftyoneDegreesManager = function() {
308328 });
309329 }
310330
331+ { {#_hasDelayedProperties} }
332+ // Get the JS property(s) that, when evaluated, will populate
333+ // evidence that can be used to determine the value of the
334+ // supplied property.
335+ // The supplied name can either be a complete property name or a top level
336+ // aspect name.
337+ // Where the aspect name is given, ALL evidence properties under that
338+ // key will be returned.
339+ // Example property names are 'location.country' or 'devices.profiles.hardwarename'
340+ // Example aspect names are 'location' or 'devices'
341+ var getEvidenceProperties = function (name) {
342+ var evidenceProperties = getFromJson(name + ' evidenceproperties' );
343+ if (typeof evidenceProperties === " undefined" ) {
344+ var item = getFromJson(name, true );
345+ evidenceProperties = getEvidencePropertiesFromObject(item);
346+ }
347+ return evidenceProperties;
348+ }
349+
350+ // Get all values in any 'evidenceproperty' fields on this object
351+ // or sub-objects.
352+ var getEvidencePropertiesFromObject = function (dataObject) {
353+ evidenceProperties = [];
354+
355+ for (var prop in dataObject) {
356+ if (dataObject.hasOwnProperty(prop)) {
357+ var value = dataObject[prop];
358+ // Property name ends with ' evidenceproperties' so is
359+ // what we' re looking for.
360+ // Add the values to the array if we don' t already have it.
361+ if (value !== null && Array.isArray(value) && prop.endsWith(' evidenceproperties' )) {
362+ value.forEach(function(item, index) {
363+ if (evidenceProperties.includes(item) === false ) {
364+ evidenceProperties.push(item);
365+ }
366+ });
367+ }
368+ // Item is an object so recursively call this method
369+ // and add any resulting evidence properties to the list.
370+ else if(typeof value === 'object' && value !== null) {
371+ getEvidencePropertiesFromObject(value).forEach(function(item, index) {
372+ if (evidenceProperties.includes(item) === false ) {
373+ evidenceProperties.push(item);
374+ }
375+ });
376+ }
377+ }
378+ }
379+
380+ return evidenceProperties;
381+ }
382+ { {/_hasDelayedProperties} }
383+
311384{ {#_supportsPromises} }
312385 this.promise = new Promise(function(resolve, reject) {
313386 process(resolve,reject);
@@ -318,7 +391,28 @@ fiftyoneDegreesManager = function() {
318391 changeFuncs.push(resolve);
319392 }
320393
321- this.complete = function(resolve) {
394+ this.complete = function(resolve, properties) {
395+ {{#_hasDelayedProperties} }
396+ // If properties is set then check if we need to kick off
397+ // processing of anything.
398+ if(typeof properties !== "undefined") {
399+ // If properties is a string then split on comma to produce
400+ // an array of one or more key names.
401+ if (typeof properties === " string" ) {
402+ properties = properties.split(' ,' );
403+ }
404+ if(Array.isArray(properties)) {
405+ properties.forEach(function(key, i) {
406+ // We pass an empty function rather than ' resolve' because we
407+ // don' t want to call resolve when a single evidence function
408+ // evaluates but after all of them have completed.
409+ // This is handled by the ' if (complete)' code below.
410+ processJsProperties(function(json) {}, catchError, getEvidenceProperties(key), true);
411+ });
412+ }
413+ }
414+
415+ {{/_hasDelayedProperties}}
322416 if(completed){
323417 resolve(json);
324418 }else{
@@ -337,7 +431,6 @@ fiftyoneDegreesManager = function() {
337431 this.promise.then(function(value) {
338432 // JSON has been updated so replace the current instance.
339433 update.call(parent, value);
340- resolve(parent);
341434 completed = true;
342435 }).catch(catchError);
343436{{/_supportsPromises}}
0 commit comments