QUEUE and 'IF' 'ELSE' 'WHILE' statements from scratch and running functions - javascript

I'm making a game engine in javascript, and needed some way to code some actions.
The code is here:
<!DOCTYPE html> <html> <body> <script>
var engine={}, actions={}; engine.atomStack=new Array();
events = {
1: ["action1|5","action2|2;2","action1|2"],
2: ["action2|5;2","action2|2;2"],
3: ["action2|5;2","action1|2"] };
engine.runatomStack = function(){
while(engine.atomStack.length > 0){
var actionToRun = engine.atomStack.shift();
actionToRun[0](actionToRun[1]); } };
eventActivate = function(event) {
for (var i = 0; i < events[event].length ; i++) {
var actionAndParam = events[event][i].split('|');
translateActions(actionAndParam[0],actionAndParam[1]); } };
engine.action1 = function( param ) {
console.log("executed action 1, param "+param[0]); }
engine.action2 = function( param ) {
console.log("executed action 2, params "+param[0]+" "+param[1]); }
actions.action1 = function( param ) {
var params = param.split(';');
engine.atomStack.push([engine.action1,params]); }
actions.action2 = function( param ) {
var params = param.split(';');
params[1]=parseInt(params[1],10)+2
engine.atomStack.push([engine.action2,params]); }
translateActions = function(action, param) { actions[action](param); };
</script> </body> </html>
Something happens, and I need to run the actions inside an event. I call eventActivate passing the event that should happen. The function translateAction read this information and calls the function that set up the actions. My logic is based that a level contain events, an event can contain actions, and each different action contain atoms.
Example: at some point you call eventActivate(1) and that will push the relative events on the stack. Then from time to time the engine is used and calls engine.runatomStack() to execute whatever is there.
//engine.atomStack is Array [ ]
eventActivate(2)
//engine.atomStack is Array [ Array[2], Array[2] ]
engine.runatomStack()
//prints:
// "executed action 2, params 5 4" example.html:18
// "executed action 2, params 2 4" example.html:18
//engine.atomStack is Array [ ]
Ok, so my engine start to grow and all and now I think I need to add IF/ELSE statements and WHILE/BREAK loops. I have some ideas on implementation but wanted help to what's works best using this queue. Sorry if it's duplicate, but couldn't find help using Google.
I thought something like, if I had events:
4: ["action2|5;2","IF|condition","action2|2;2","END|"]
5: ["action2|5;2","IF|condition","action2|2;2","ELSE|","action1|2","END|"]
I'm not sure how exactly to go, what's works best...
Link to jsFiddle version: http://jsfiddle.net/e3b0kocc/

Ok,
I have a implementation that can solve some stuff, but I think I can't put an if inside another if, which is a problem.
<!DOCTYPE html> <html> <body> <script>
var engine={}, actions={}; engine.atomStack=new Array();
events = {
1: ["action1|5","action2|2;2","action1|2"],
2: ["action2|5;2","if|true","action1|5","else|","action2|2;2","end|"],
3: ["action2|5;2","action1|2"] };
engine.runatomStack = function(){
while(engine.atomStack.length > 0){
var actionToRun = engine.atomStack.shift();
actionToRun[0](actionToRun[1]); } };
eventActivate = function(event) {
for (var i = 0; i < events[event].length ; i++) {
var actionAndParam = events[event][i].split('|');
translateActions(actionAndParam[0],actionAndParam[1]); } };
evalCondition = function( param ){
return false
}
engine.if = function( param ) {
if ( evalCondition(param)) {
var removeActions = false
for (var i = 0; i < engine.atomStack.length ; i++) {
if(engine.atomStack[i][0] == engine.else) {
removeActions = true
}
if(engine.atomStack[i][0] == engine.end) {
return
}
if(removeActions == true){
engine.atomStack.splice(i)
}
}
} else {
var actionToRun =[0,0]
while(engine.atomStack.length > 0 &&
actionToRun[0] != engine.end &&
actionToRun[0] != engine.else ){
var actionToRun = engine.atomStack.shift();
}
}
}
engine.end = function () {}
engine.else = function () {}
engine.action1 = function( param ) {
console.log("executed action 1, param "+param[0]); }
engine.action2 = function( param ) {
console.log("executed action 2, params "+param[0]+" "+param[1]); }
actions.action1 = function( param ) {
var params = param.split(';');
engine.atomStack.push([engine.action1,params]); }
actions.if = function( param ) {
var params = param.split(';');
engine.atomStack.push([engine.if,params]); }
actions.else = function( param ) {
engine.atomStack.push([engine.else,'']); }
actions.end = function( param ) {
engine.atomStack.push([engine.end,'']); }
actions.action2 = function( param ) {
var params = param.split(';');
params[1]=parseInt(params[1],10)+2
engine.atomStack.push([engine.action2,params]); }
translateActions = function(action, param) { actions[action](param); };
</script> </body> </html>
It can be tested it by running
eventActivate(2)
engine.runatomStack()
Modifying evalCondition return value for true will reach different result. Can someone modify the engine.if code to allow if inside if?

Related

Javascript Callback in for Loop

My problem is that I'm having a Function A which calls at one point another function, let's call it Function B (getChildContent) and needs the return value of Function B in order to proceed. I know that it's because of Javascripts Asynchronous Nature, and i tried to solve it with a callback. But i can't get it work properly.
FunctionA(){
//some Code.....
else {
for(i in clustertitles) {
if(S(text).contains(clustertitles[i])) {
var parent = {};
parent.ClusterName = clustertitles[i];
parent.Functions = [];
var str = '== ' + clustertitles[i] + ' ==\n* ';
str = S(text).between(str,'.').s;
var caps = parseFunctions(str);
for(y in caps) {
//var content = getChildContent(caps[y]);
getChildContent(caps[y], function(content) { //Function call
var child = {};
child.FunctionName = caps[y];
child.Content = [];
child.Content.push(content);
parent.Functions.push(child);
console.log(content);
});
}}}
}
function getChildContent (capname, callback) {
t = capname.replace(' ', '_');
bot.page(t).complete(function (title, text, date) {
var str = S(text).between('== Kurzbeschreibung ==\n* ', '.').s;
if(str === undefined || str === null || str === '') {
throw new Error('Undefined, Null or Empty!');
}
else {
var content = {};
str = parseTitles(str);
content.Owner = str[0];
content.Aim = str[1];
content.What = str[2];
content.Who = str[3];
content.Steps = str[4];
content.Page = 'some URL';
callback(content);
}
});
}
So in Function A I'm trying to call getChildContent from a for-Loop and pass the current string from caps-array. For each String in caps-array getChildContent() makes a http request over a node.js module and retrieves a string. With this string i'm building an object (content) which is needed in Function A to continue. However the 'console.log(content)' in Function A just prints out the object which is created with the last string in caps-array, but for many times. E.G. if caps-array has 5 entries, i get 5 times the object which is created with the last entry of caps-array.
How can i manage the loop/callback to get every time the right object on my console?
Your loop should call another function that preserves the value of y, something like this:
FunctionA(){
//some Code.....
else {
for(i in clustertitles) {
if(S(text).contains(clustertitles[i])) {
var parent = {};
parent.ClusterName = clustertitles[i];
parent.Functions = [];
var str = '== ' + clustertitles[i] + ' ==\n* ';
str = S(text).between(str,'.').s;
var caps = parseFunctions(str);
for(y in caps) {
yourNewFunction (y, caps, parent);
}}}
}
function yourNewFunction (y, caps, parent) {
getChildContent(caps[y], function(content) { //Function call
var child = {};
child.FunctionName = caps[y];
child.Content = [];
child.Content.push(content);
parent.Functions.push(child);
console.log(content);
});
}
function getChildContent (capname, callback) {
t = capname.replace(' ', '_');
bot.page(t).complete(function (title, text, date) {
var str = S(text).between('== Kurzbeschreibung ==\n* ', '.').s;
if(str === undefined || str === null || str === '') {
throw new Error('Undefined, Null or Empty!');
}
else {
var content = {};
str = parseTitles(str);
content.Owner = str[0];
content.Aim = str[1];
content.What = str[2];
content.Who = str[3];
content.Steps = str[4];
content.Page = 'some URL';
callback(content);
}
});
}
There are 2 ways to do so.
Put the loop inside a function, execute your callback after the loop is done. (Problematic if you are doing async call inside the loop.
function doLoopdiloopStuff() {
for() {
}
callback();
}
The other way, the way i prefer looks like this:
for(var i = 0; i < stuff || function(){ /* here's the callback */ }(), false; i++) {
/* do your loop-di-loop */
}
In another example:
for (var index = 0; index < caps.length || function(){ callbackFunction(); /* This is the callback you are calling */ return false;}(); index++) {
var element = caps[index];
// here comes the code of what you want to do with a single element
}

Rxjs observing object updates and changes

I am currently trying to observe any changes to a given object including all of it's elements.
The following code only fires when an object[x] is updates, but not if individually updating object[x]'s elements such as object[x][y]
<script>
var elem = document.getElementById("test1");
var log = function(x) {
elem.innerHTML += x + "<br/><br/><br/>";
};
var a = [{a:1,b:2},
{a:2,b:5}
];
var source = Rx.Observable
.ofObjectChanges(a)
.map(function(x) {
return JSON.stringify(x);
});
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1];
</script>
This code runs and fires correctly.
however. if I instead to this
a[0]['a'] = 3;
Then nothing happens.
EDIT
A better way to phrase this, how can I observe changes from an array of objects?
If you want only the nested object changes:
var source = rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
});
If you also want changes like a[0] = a[1]:
var source = rx.Observable.merge(
rx.Observable.ofArrayChanges(a),
rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
})
);
The flatMap or selectMany (they are the same function) will allow you to iterate over a value and execute a function that returns an Observable. The values from all these Observables are "flattened" onto a new stream that is returned.
http://reactivex.io/documentation/operators/flatmap.html
Perhaps something like this by merging two Observables (one for the array and the other observing the elements of the array):
var a = [
{a:1,b:2},
{a:2,b:5}
];
var source1 = Rx.Observable.ofArrayChanges(a).map(function(x) {
return JSON.stringify(x);
});
var source2 = Rx.Observable
.fromArray(a.map(function(o, i) { return [o, i]; }))
.flatMap(function(oi) {
return Rx.Observable.ofObjectChanges(oi[0])
.map(function(x) {
var y = {
type: x.type,
object: x.object,
name: x.name,
oldValue: x.oldValue,
arrayIndex: oi[1] // pass the index of the member that changed
};
return JSON.stringify(y);
});
})
source = source1.merge(source2)
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1]
a[1]['b'] = 7
Thanks to #electrichead here we're not using concatMap because the sources that we made by ofObjectChanges and ofArrayChanges never complete.
Here's a working example of Rx.Observable.ofNestedObjectChanges simple implementation, you can get the gist of it and implement you own.
http://jsbin.com/wekote/edit?js,console
Rx.Observable.ofNestedObjectChanges = function(obj) {
if (obj == null) { throw new TypeError('object must not be null or undefined.'); }
if (typeof Object.observe !== 'function' && typeof Object.unobserve !== 'function') { throw new TypeError('Object.observe is not supported on your platform') }
return new Rx.AnonymousObservable(function(observer) {
function observerFn(changes) {
for(var i = 0, len = changes.length; i < len; i++) {
observer.onNext(changes[i]);
}
}
Object.observe(obj, observerFn);
//Recursive observers hooks - same observerFn
traverseObjectTree(obj, observerFn);
function traverseObjectTree(element, observerFn){
for(var i=0;i<Object.keys(element).length;i++){
var myObj = element[Object.keys(element)[i]];
if(typeof myObj === "object"){
Object.observe(myObj, observerFn);
traverseObjectTree(myObj,observerFn);
}
}
}
return function () {
Object.unobserve(obj, observerFn);
};
});
};
//Test
var json = {
element : {
name : "Yocto",
job : {
title: "Designer"
}
},
element1: {
name : "Mokto"
}
};
setTimeout(function(){
json.element.job.title = "A Great Designer";
},3000);
var source = Rx.Observable.ofNestedObjectChanges(json);
var subscription = source.subscribe(
function (x) {
console.log(x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
json.element.name = "Candy Joe";

ng-show/ng-hide limit multiple calls

I am trying to limit the call of ng-hide/ng-show. Currently, what it does is it calls the getLicense function multiple times that will overloads the browser.
$scope.getLicense = function( value ) {
if( $sessionStorage.license === '' ) {
DashboardService.getLicense( ).then( function( data ) {
$scope.licenses = data;
var arr = [ ],
id = '';
for( var i in $scope.licenses ) {
arr.push( [ i ] );
}
$sessionStorage.license = arr;
} );
for( var cnt = 0; cnt < $sessionStorage.license.length; cnt++ ) {
if( $sessionStorage.license[ cnt ] == value ) {
console.log( 'true' );
return true;
break;
} else {
return false;
break;
}
}
} else {
for( var cnt = 0; cnt < $sessionStorage.license.length; cnt++ ) {
if( $sessionStorage.license[ cnt ] == value ) {
console.log('true');
return true;
break;
} else {
console.log('false');
return false;
break;
}
}
}
};
My HTML code looks like this:
<md-list-item class="md-caption" ng-class="{'active': $state.includes('security.webcontrol')}" translate="SIDEBAR.NAV.WEBCONTROL.TITLE" ng-hide="getLicense('web_control_da')">
Giving a function to ng-show / hide / if / etc is a very bad practice.
Each time $digest is called (very often) it check each watcher to see if it has changed. So it will have to execute your function to know if the result is different (or not).
Add a console.log('function executed') in your function getLicense and you will see how often it is called.
To avoid that (like Icycool explained) you have to replace that by a boolean in your scope. And only change the boolean when getLicense should be tested.
For example : If getLicense need to be calculated each time $sessionStorage.license change (for example) :
$scope.licence = getLicense();
$scope.watch("$sessionStorage.license", function (newValue, oldValue){
$scope.licence = getLicense();
});
And in your view/template : ng-hide="licence"
So it will execute your getLicense only when it does really matter.
You can assign it to a scope variable and have ng-hide point to that instead. Call check license on other occasions.

Load gif image > execute statement > hide gif

Is there something wrong with this sequence?
//loading_image is hidden at first
$('#run_only').click(function(e) {
$('#loading_image').show();
var def_main = false,
def_header = false,
def_iostream = false,
def_conio = false,
def_stdio = false,
def_math = false,
def_string = false;
//REMOVE PREVIOUSLY ADDED CLASSESS TO LN
$('#board_ln').contents().removeClass('line-error');
if(er_syntax_pre) {
var error_text = '<p>Syntax Error. Detected brace matching error.</p>';
var debugging_text = '<p>DEBUGGING: Check the proper pairing of ( ), { }, [ ], \' \', " " </p>';
mb_error(error_text, debugging_text);
} else {
mb_noerror('');//transfer this at the end part later
var inlineDiv_num = $('#board_code > div ').size(),
kword_search_raw="",
kword_search="",
kwordSyntaxSearch_tilde="",
kwordSyntaxSearch="",
noKeyword = true
er_text = new Array(),
er_ln = new Array(),
er_debug_text = new Array();
er_text = [];
er_ln = [];
er_debug_text = [];
for( var x=0; x<inlineDiv_num; x++ ) {
var inlineDiv_num_textL = $('#board_code > div ').eq(x).text().length,
inlineDiv_num_text = $('#board_code > div ').eq(x).text();
for( var y=0; y<inlineDiv_num_textL; y++) {
//kword_search = kword_search + inlineDiv_num_text.charAt(y);
kword_search_raw = kword_search_raw + inlineDiv_num_text.charAt(y);
kword_search = kword_search + inlineDiv_num_text.charAt(y);
//SEARCH FOR STRINGS AND REPLACE THEM
if(/(?:'[^']+'|"[^"]+")$/.test(kword_search)) {
//alert('found 2nd occurence of quote');
kword_search = kword_search.replace(/(?:'[^']+'|"[^"]+")$/, "!string!");
}
//alert(kword_search);
//include***********
if(/\s*\#include$/.test(kword_search) ) {
noKeyword = false;
//alert('at the tail of #include');
var kwordSyntaxSearch = sliceToEndString(x,y);
//alert('kwordSyntaxSearch: '+kwordSyntaxSearch);
//SYNTAX CHECKER
if( /^\s*<\s*iostream\.h\s*>*/.test(kwordSyntaxSearch) ) {
def_iostream = true;
var charLast = kwordSyntaxSearch.indexOf(">");
kwordSyntaxSearch_tilde = kwordSyntaxSearch.splice(charLast+1, 0, "~~");
} else if( /^\s*<\s*conio\.h\s*>*/.test(kwordSyntaxSearch) ) {
def_conio = true;
var charLast = kwordSyntaxSearch.indexOf(">");
kwordSyntaxSearch_tilde = kwordSyntaxSearch.splice(charLast+1, 0, "~~");
} else if( /^\s*<\s*stdio\.h\s*>*/.test(kwordSyntaxSearch) ) {
def_stdio = true;
var charLast = kwordSyntaxSearch.indexOf(">");
kwordSyntaxSearch_tilde = kwordSyntaxSearch.splice(charLast+1, 0, "~~");
} else if( /^\s*<\s*string\.h\s*>*/.test(kwordSyntaxSearch) ) {
def_string = true;
var charLast = kwordSyntaxSearch.indexOf(">");
kwordSyntaxSearch_tilde = kwordSyntaxSearch.splice(charLast+1, 0, "~~");
} else if( /^\s*<\s*math\.h\s*>*/.test(kwordSyntaxSearch) ) {
def_math = true;
var charLast = kwordSyntaxSearch.indexOf(">");
kwordSyntaxSearch_tilde = kwordSyntaxSearch.splice(charLast+1, 0, "~~");
} else {
//alert('#include syntax error');
er_text.push('Error: Syntax | Runtime. #include<headerfile.h>');
er_debug_text.push('Debugging: (1)Check text after "#include" (2)Check "support" for supported libraries');
er_ln.push(x);
y=inlineDiv_num_textL;
x=inlineDiv_num;
}//else
}//IF FOUND #INCLUDE|
}//for y
}//for x
//NO KEYWORDS AT ALL
if(noKeyword){
er_text[0] = "Error: No keywords detected";
er_debug_text[0] = "Debugging: Make sure that you are using the supported C++ keywords of this system";
er_ln[0] = inlineDiv_num-1;
mb_error(er_text[0], er_debug_text[0], er_ln[0]);
}
if(er_ln.length > 0) {
mb_error(er_text[0], er_debug_text[0], er_ln[0]);
}
}//else no brace error
$('#loading_image').hide();
});
The gif image is not showing with this code. I tried removing the $('#loading_image').hide(); and i discover that the loading of image happens after "long code processing"
Although I haven't test it, my guess is that the show() function uses the setTimeout function. Even with the timeout set to 0 miliseconds, this will change the order of the statements. Use the callback-option of show to make sure the long code processing is done after showing the image:
$('#run_only').click(function(e) {
$('#loading_image').show(0, function(){
//long code processing
$('#loading_image').hide();
});
});
This can be one aspect of asynchronous programming that is hard to reason about. Consider this example:
$('#run_only').click(function(e) {
$('#loading_image').show();
$.getJSON('/something.json?wait=3seconds', function() {
//This code will take about 3 seconds to execute
});
//This code will execute as soon as the $.get is fired (before it has returned)
$('#loading_image').hide();
});
To fix this try something like this:
$('#run_only').click(function(e) {
$('#loading_image').show();
$.getJSON('/something.json?wait=3seconds', function() {
//This code will take about 3 seconds to execute
//But this will only execute after the $.getJSON() is sucessful
$('#loading_image').hide();
});
});

Delete row from Object

I have an object containing an array:
<script type="text/javascript">
var statusData = {
Status: []
};
var uniqueCounter = 1
function createJsonFmtData() {
// Used as unique id at client side
var uniqueCounter =uniqueCounter + 1;
statusData.Status.push({
"Name": Name,
"Time": Time,
"Email": Mail,
"Name": Type,
"Value": Value,
"uniqueId": uniqueCounter
});
}
function DelNewlyCreStatusRow(rowId) {
// First pop elment from json data
var val;
for (val = 0; val < statusData.Status.length; ++val) {
if (statusData.Status[val].uniqueId == rowId) {
delete statusData.Status[val];
break;
}
}
</script>
When try to call DelNewlyCreStatusRow it gives the error:
TypeError: statusData.Status[val] is undefined
I am not able to figure it out here where as I have declared it as global.
This is because you are trying to delete from the array incorrectly. delete operator is quite funny on arrays. It replaces element with undefined. Check this out:
>>> var A = [1, 2, 3];
>>> delete a[1];
>>> A;
[1, undefined, 3];
Thus calling DelNewlyCreStatusRow multiple times will throw an exception, because statusData.Status[val].uniqueId cannot be evaluated ( statusData.Status[val] is undefined ).
To fix this use this code instead of delete:
var val;
for (val = 0; val < statusData.Status.length; ++val) {
if (statusData.Status[val].uniqueId == rowId) {
statusData.Status.splice( val, 1 );
break;
}
}
Note that splice modifies an array, so if you want to do multiple deletes in one go, then you will have to replace for loop with while ( and refactor the code a bit ). This is not needed here because of break statement.
You should replace
delete statusData.Status[val];
with
statusData.Status.splice(val,1);
val -= 1;
to remove an object in an array.
The function DelNewlyCreStatusRow missing a closing '}'
function DelNewlyCreStatusRow(rowId) {
// First pop elment from json data
var val;
for (val = 0; val < statusData.Status.length; ++val) {
if (statusData.Status[val].uniqueId == rowId) {
delete statusData.Status[val];
break;
}
}
}
You made an Error in your Code at the second var declaration inside the Function.
var uniqueCounter = uniqueCounter + 1 => NAN + 1.
You dont need the var a Second time so it's just
uniqueCounter = uniqueCounter + 1 => 2.
Then delete works fine in my Case.
<script type="text/javascript">
var statusData = {
Status : []
};
var uniqueCounter = 1
function createJsonFmtData() {
var statusName = $('#<%= ddlStatus.ClientID %> option:selected').text();
var dateTime = $("#body_ctl04_ctl02").val();
var forceMail = ($("#chkForceMail").is(':checked')) ? 1 : 0;
var noteType = $('#divTrackId').text();
var dataValue = $("#<%=txtTrackId.ClientID %>").val();
var curLogedUserName = $("#<%=curLoginUserName.ClientID %>").val();
// Used as unique id at client side
uniqueCounter = uniqueCounter + 1;
statusData.Status.push({
"statusName" : statusName,
"dateTime" : dateTime,
"forceEmail" : forceMail,
"typeName" : noteType,
"dataValue" : dataValue,
"uniqueId" : uniqueCounter
});
}
function DelNewlyCreStatusRow(rowId) {
// First pop elment from json data
var val;
for ( val = 0; val < statusData.Status.length; ++val) {
if (statusData.Status[val].uniqueId == rowId) {
console.log(typeof(statusData.Status[val]));
delete statusData.Status[val];
break;
}
}
}
createJsonFmtData();
console.log(statusData);
DelNewlyCreStatusRow(2);
console.log(statusData);
</script>

Categories

Resources