Implementing Finite State Machine for Web UI - asp.net

I am intending to develop a Finite State Machine in the following manner.
Extract Control IDs from a --> web-page,
Write control IDs to a XML --> Controls-XML.
Manually declare States and Transition in the --> Controls-XML
Scan Controls-XML and attach pre-declared Jscript to eventhandlers embed them in the --> web-page..
5.
How feasible this would be..
Am I getting into can of worms ?

First, we live in a can of worms!
your questions are a bit vague, please provide more details if i'm missing your point and maybe this should be beak in parfts to deepen the discution in part details
assuming you want to work live on a client side browser over any page
by extrating ID's of controls i supouse it's all controls of a webpage (it could be by clicking or clicking possibly with key combos), but lets be simple.
Extrating ID's
here's a code than might help you:
function scan(e) {
if (e&&e.childNodes)
for(var i=0;i<e.childNodes.length;i++) {
var child=e.childNodes[i];
if (child) {
if(child.id) console.log(child.nodeName,child.id);
scan(child);
}
}
}
note: this was done with chrome, but any browser console will do i think.
just paste the function on the console and then call it like:
scan(document)
and it will list (on console) all elements that have id's showing element type and id... you can filter that eazy by just printing elements of certain tags like INPUT, SELECT TEXTAREA, etc...
About the XML
here browsers get a bit tricky, you can simplify it too your favorite browser, the following functions constructs a XML document given some XML text.
So by this aproach your scan function should compose the xml text (instead of writing to the console) and later we can feed the XML document
function makeNode(text) {
var doc;
if (window.ActiveXObject) {
doc=new ActiveXObject("Microsoft.XMLDOM");
doc.async="false";
doc.loadXML(text);
} else {// code for Mozilla, Firefox, Opera, etc.
var parser=new DOMParser();
doc=parser.parseFromString(text,"text/xml");
}// documentElement always represents the root node
return doc.documentElement;
}
other aproach is to create an empty XML document (as the above function does) and the instead of feed raw xml text your scan function should use XMLDOM commands to insert nodes into the document.
Manually declare States and Transition in the --> Controls-XML
this is an hard one, is this (XML doc) info to be copy/pasted and latter edited? is it edited inplace by inserting a bunch of code to build an interface?
many doubts here, but once we have the information in XML format we would be free to use our imagination and decide a format to handle the wanted state changes.
Scan Controls-XML and attach pre-declared Jscript to eventhandlers embed them in the --> web-page
at this point actions depend on methods followed above, but in any case using XMLDOM functions to traverse the xml doc and apply status change or event handlers to the controls is trivial.
controls need not be scanned anymore because we have stored the id's (use getElementById)
i have a form validation system that does something similar, except the XML is predefined and stored on server side, but once loaded it will do just that. attaching event handlers and do status changes based on evaluated expressions.

Related

How would I pass an array to a template as an attribute and then use the array, then use the template recursively?

I've been working with ColdFusion CFML for a few months after using JSP for several years and there are still some things I'm having trouble figuring out in this language. Google, CFDocs, and Adobe have been no help with this particular question.
I'm building a feature to display comments below an article, where a comment can also have child comments. I have all the data imported and it looks great. I'm using a query that returns an array of structs, it gets each first-level comment for the article and I loop back through that array and attach an array of structs to each comment-struct that has children.
I've written a template to loop through and display the list of first-level comments but I'd like it to call itself recursively on each comment that has child comments until it has displayed all of the child comments of each comment. I'd assume that the best way to accomplish this would be for the template to call itself while passing it the array of child comments.
I've read several articles that explain how to create a CFModule but NONE that say how to actually use the attribute that you pass it. Here's one of the things I've tried:
(in the original template):
<h4>Comments</h4>
<div id="comments" name="comments">
<cfmodule template="comments.cfm" comments="#Variables.page.comments#">
</div>
(in the template that I want to eventually call recursively):
<cfoutput>
<cfloop array = "#comments#" index = "comment">
<div>#comment.commenter_name# says</div>
<div>#comment.created#</div>
<div>#comment.content#</div>
</cfloop>
</cfoutput>
I can't just use Variables.page.comments in the second template because the template's going to call itself recursively; for instance, I'll want the array at Variables.page.comments[2].comments the next time, Variables.page.comments[2].comments[5].comments the time after, etc
You could generate your hierarchical view by wrapping your rendering logic around
<cfscript>
cfsavecontent variable="html" {
do logic stuff here
}
writeOutput(html);
<cfscript>
which will store your output into a variable. Then output the variable.
You could also write a recursive function to do it, and then simply output the returned string, like below. You might want to add some limit logic so you don't output the entire discussion at once, depending on how big the discussion are.
string function outputComments (
required array comments) {
var html = "";
for (var comment in arguments.comments) {
html &= "<li>#comment.message#";
if (comment.children.length) {
html &= outputComments(comment.children);
}
html &= "</li>";
}
if (html != "") {
html = "<ul>#html#</ul>";
}
return html;
}
writeOutput(outputComments(comments));
Having done a bunch of this kind of work, you may want to consider rendering your comments/replies in some sort of JavaScript ajax-driven widget, using a service that returns comments data in JSON, where you get a page of comments or replies at a time. I wrote a Facebook-style comment widget that worked that way, and the advantage is that you don't have to worry about conversation size.

Google Tag Manager - Parse Dynamic Data Layer Variable

I want to parse a 'pushed' data layer string. I intend to use it to track click events and setup the appropiate funnels in Google Analytics, it looks as follows: products.view.19|view recent product|19
The first part (products.view.19) is the unique page identifier.
The second part (view recent product) is the action.
The last part is (19) is the action identifier, this way actions may be grouped and compared more easily.
So I did the following, I first created a trigger (it fires when a link has the tag 'data-trackclick' in it) which pushes the data value to a variable (variable for datalayer). However, now I want to split that variable in to 3 new variables, as described above. I selected 'javascript macro' for this but somehow it returns 'undefined'. The macro looks as follows:
function() {
var data = {{TrackClickData}};
var pieces = data.split('|');
if (pieces[0].length()) {
return pieces[0];
} else {
return data;
}
}
Obviously this didnt work since it would only run on the initial load and not (like I thought) when the macro was requested, so it should somehow be fired on the 'click' and then set the variables accordingly.
Is this possible to do? Or do I really have to add the dataLayer.push() in script tags?
A few things:
.length() is wrong, the property for array length is .length without the ()
if it exists, pieces[0] is not an array, then .length would return the string length, see How do you check for an empty string in JavaScript? for more standard way of checking for empty strings
Is this possible to do? There's virtually nothing you can't do with GTM, since you can write JavaScript code, you can do whathever you code allows you to do, and splitting a string to use parts of it as variables is certainly within the realm of possibilities.
My advise is to make your code work outside GTM first (eg test it in the browser console), then once it's all working, port it to GTM.

What is cursor?

In discover meteor,
posts: function() {
return Posts.find();
}
is used, not this:
posts: function() {
return Posts.find().fetch();
}
I tried function below, it also works, and can realtime update.
What is cursor exactly? And What is the different of above two functions?
The Meteor cursor is like a lazy version of the array of documents.
It's designed to iterate through the results of a query while not actually loading each of the documents until they are actually requested or the cursor is at the position containing the document.
Its better to think of the results of the query as a book. If you use .fetch() all the pages are printed even though you're not reading them.
The cursor prints pages as you're reading them.
Additionally the cursor has several enhancements with regards to Blaze. Content is rendered less often as minute details in a document's change are able to change the DOM section on its own, without recreating an entire object. It's easier for Blaze to interact with the cursor than an array of Javascript objects.
Additionally a cursor can be observed, a plain array of Javascript object's can't
TLDR; Cursor is just like an array of objects, but designed to be more efficient & is slightly more extended with features.

Is there a way to tell meteor a collection is static (will never change)?

On my meteor project users can post events and they have to choose (via an autocomplete) in which city it will take place. I have a full list of french cities and it will never be updated.
I want to use a collection and publish-subscribes based on the input of the autocomplete because I don't want the client to download the full database (5MB). Is there a way, for performance, to tell meteor that this collection is "static"? Or does it make no difference?
Could anyone suggest a different approach?
When you "want to tell the server that a collection is static", I am aware of two potential optimizations:
Don't observe the database using a live query because the data will never change
Don't store the results of this query in the merge box because it doesn't need to be tracked and compared with other data (saving memory and CPU)
(1) is something you can do rather easily by constructing your own publish cursor. However, if any client is observing the same query, I believe Meteor will (at least in the future) optimize for that so it's still just one live query for any number of clients. As for (2), I am not aware of any straightforward way to do this because it could potentially mess up the data merging over multiple publications and subscriptions.
To avoid using a live query, you can manually add data to the publish function instead of returning a cursor, which causes the .observe() function to be called to hook up data to the subscription. Here's a simple example:
Meteor.publish(function() {
var sub = this;
var args = {}; // what you're find()ing
Foo.find(args).forEach(function(document) {
sub.added("client_collection_name", document._id, document);
});
sub.ready();
});
This will cause the data to be added to client_collection_name on the client side, which could have the same name as the collection referenced by Foo, or something different. Be aware that you can do many other things with publications (also, see the link above.)
UPDATE: To resolve issues from (2), which can be potentially very problematic depending on the size of the collection, it's necessary to bypass Meteor altogether. See https://stackoverflow.com/a/21835534/586086 for one way to do it. Another way is to just return the collection fetch()ed as a method call, although this doesn't have the benefits of compression.
From Meteor doc :
"Any change to the collection that changes the documents in a cursor will trigger a recomputation. To disable this behavior, pass {reactive: false} as an option to find."
I think this simple option is the best answer
You don't need to publish your whole collection.
1.Show autocomplete options only after user has inputted first 3 letters - this will narrow your search significantly.
2.Provide no more than 5-10 cities as options - this will keep your recordset really small - thus no need to push 5mb of data to each user.
Your publication should look like this:
Meteor.publish('pub-name', function(userInput){
var firstLetters = new RegExp('^' + userInput);
return Cities.find({name:firstLetters},{limit:10,sort:{name:1}});
});

Meteor is not transforming my documents before publication

For security reasons, I want to add and remove properties of documents before publishing them to the client, depending on some dynamic calculations. I follow the Meteor documentation and this other SO question.
For example simplicity, say I try to add the following static property to every document (SERVER SIDE ONLY):
var Docs = new Meteor.Collection('docs', {
transform: function (f) {
console.log('Tagging doc: ' + f._id);
f.myProp = 1;
return f;
}
});
For some strange reason, this does not work:
Only some documents trigger the transform function, not all (I can see this through the console logging)
On the client side, none of the documents are tagged with myProp
I haven't tried to put the transform on both the client and the server, because in my real life app I cannot do the necessary computation on the client.
Transform functions on collections are intended for convenience, not security -- note that when you call observeChanges on a cursor, the information is not passed through the transform function (it is passed through the transform when you call observe). The default way of publishing a cursor works by calling observeChanges on it.
If you want to strip off fields of a cursor you're publishing, use the fields option to find on your collection. If you want to do something more complicated, you can explicitly do whatever computation you need if your publish function calls added, changed, and removed itself, instead of returning a cursor. Check out the docs for Meteor.publish for details.

Resources