Loop in Jade (currently known as "Pug") template engine - javascript

I want to use a simple loop like for(int i=0; i<10; i++){}.
How do I use it in the Jade engine? I'm working with Node.js and use the expressjs framework.

for example:
- for (var i = 0; i < 10; ++i) {
li= array[i]
- }
you may see https://github.com/visionmedia/jade for detailed document.

Using node I have a collection of stuff #stuff and access it like this:
- each stuff in stuffs
p
= stuff.sentence

An unusual but pretty way of doing it
Without index:
each _ in Array(5)
= 'a'
Will print: aaaaa
With index:
each _, i in Array(5)
= i
Will print: 01234
Notes: In the examples above, I have assigned the val parameter of jade's each iteration syntax to _ because it is required, but will always return undefined.

Here is a very simple jade file that have a loop in it. Jade is very sensitive about white space. After loop definition line (for) you should give an indent(tab) to stuff that want to go inside the loop. You can do this without {}:
- var arr=['one', 'two', 'three'];
- var s = 'string';
doctype html
html
head
body
section= s
- for (var i=0; i<3; i++)
div= arr[i]

Just adding another possibility as it might help someone that's trying to both iterate over an array AND maintain a count. For example, the code below goes through an array named items and only displays the first 3 items. Notice that the each and the if are native jade and don't need a hyphen.
ul
- var count = 0;
each item in items
if count < 3
li= item.name
- count++;

You could also speed things up with a while loop (see here: http://jsperf.com/javascript-while-vs-for-loops). Also much more terse and legible IMHO:
i = 10
while(i--)
//- iterate here
div= i

Pug (renamed from 'Jade') is a templating engine for full stack web app development. It provides a neat and clean syntax for writing HTML and maintains strict whitespace indentation (like Python). It has been implemented with JavaScript APIs. The language mainly supports two iteration constructs: each and while. 'for' can be used instead 'each'. Kindly consult the language reference here:
https://pugjs.org/language/iteration.html
Here is one of my snippets:
each/for iteration in pug_screenshot

Related

node.js change in concatenation?

I'm trying to debug some code that another programmer has left for me to maintain. I've just attempted to upgrade from node.js 5 to node.js 8 and my database queries are for some requests coming back with key not found errors
We're using couchbase for the database and our document keys are "encrypted" for security. So we may have a key that starts like this "User_myemail#gmail.com" but we encrypt it using the following method:
function _GetScrambledKey(dbKey)
{
//select encryption key based on db key content
var eKeyIndex = CalculateEncryptionKeyIndex(dbKey, eKeys.length);
var sha = CalculateSHA512(dbKey + eKeyIndex);
return sha;
}
function CalculateEncryptionKeyIndex(str, max)
{
var hashBuf = CalculateSHA1(str);
var count = 0;
for (var i = 0; i < hashBuf.length; i++)
{
count += hashBuf[i];
count = count % max;
}
return count;
}
We then query couchbase for the document with
cb.get("ECB_"+encryptedKey, opts, callback);
In node5 this worked but in node8 we're getting some documents return fine and others return as missing. I outputted the "ECB_"+encryptedKey as an int array and the results have only confused me more. They are different on node5 to node8 but only by one character right in the middle of the array.
Outputting the encryptedKey as an int array on both versions shows this
188,106,14,227,211,70,94,97,63,130,78,246,155,65,6,148,62,215,47,230,211,109,35,99,21,60,178,74,195,13,233,253,187,142,213,213,104,58,168,60,225,148,25,101,155,91,122,77,2,99,102,235,26,71,157,99,6,47,162,152,58,181,21,175
Then outputting the concatenated string, in the same way, shows slightly different results
This is the node8 output
Node8 key: 69,67,66,95,65533,106,14,65533,65533,70,94,97,63,65533,78,65533,65533,65,6,65533,62,65533,47,65533,65533,109,35,99,21,60,65533,74,65533,13,65533,65533,65533,65533,65533,65533,104,58,65533,60,65533,25,101,65533,91,122,77,2,99,102,65533,26,71,65533,99,6,47,65533,65533,58,65533,21,65533
And this is the node5 output
Node5 key: 69,67,66,95,65533,106,14,65533,65533,70,94,97,63,65533,78,65533,65533,65,6,65533,62,65533,47,65533,65533,109,35,99,21,60,65533,74,65533,13,65533,65533,65533,65533,65533,65533,104,58,65533,60,65533,65533,25,101,65533,91,122,77,2,99,102,65533,26,71,65533,99,6,47,65533,65533,58,65533,21,65533
I had to run it through a diff tool to see the difference
Comparing that to the original pre-append array it looks like the 225 has just been dropped in node8. Is 225 significant? I can't understand how that would be possible otherwise unless it's a bug. Does anyone have any ideas?
Looks like this was a change in v8 5.5 https://github.com/nodejs/node/issues/21278
A lot of the issues you are facing, including the concatenation can be cleaned up using newer features from ES6 that are available in node 8.
In general, you should avoid doing string concatenations with the + operator and should use string literals instead. In your case, you should replace the "ECB_"+encryptedKey with `ECB_${encryptedKey}`.
Additionally, if you want to output the contents of the integers values from this concatenated string, then you are better off using .join, the spread operator (...) and the Buffer class from Node as follows:
let encKey = `ECB_${encryptedKey}`;
let tmpBuff = Buffer.from(encKey);
let buffArrVals = [...tmpBuff];
console.log(buffArrVals.join(','));
Also, if you can help it, you really should avoid using var inside of function blocks like it exists in your sample code. var performs something called variable hoisting and causes the variable to become available outside the scope it was declared, which is seldom intended. From node 6+ onward the recommendation is to use let or const for variable declarations to ensure they stay scoped to the block they are declared.

InDesign Scripting: Deleting elements from the structure panel

I've imported some XML files inside InDesign (you can see the structure in the picture below) and I've also created a script to get some statistics concerning this hierarchy.
For example, to count the "free" elements:
var items = app.activeDocument.xmlElements.everyItem();
var items1 = items.xmlElements.itemByName("cars");
var cars = items1.xmlElements.everyItem();
var c_free = cars.xmlElements.itemByName("free");
var cars_free = c_free.xmlElements.count().length;
I also have apartments in my structure that's why I'm using itemByName.
The code above returns the correct number of free cars in my structure.
What I'm trying to do - without any luck so far - is to target those free items (inside cars) and either delete all of them or a specific number.
My last attempt was using:
var del1 = myInputGroup2.add ("button", undefined, "Delete All");
del1.onClick = function () {
cars.xmlElements.everyItem().remove();
}
inside a dialog I've created.
Any suggestions will be appreciated cause I'm really stuck here.
I would probably use XPath for this. You can use evaluateXPathExpression to create an array of the elements you want to target. Assuming your root element is cars and it contains elements called cars1, and you want to delete all free elements within a cars1 element, you could do something like:
var myDoc = app.activeDocument;
//xmlElements[0] is your root element, in this case "cars". The xPath expression is evaluated from cars.
//evaluateXPathExpression returns an array of all of the free elements that are children of cars.
var myFrees = myDoc.xmlElements[0].evaluateXPathExpression("cars1/free");
for (var i = myFrees.length - 1; i>=0; i--){
myFrees[i].remove();
}
Tweaking this would require some knowledge of xPath, but it's not terribly hard to learn the basics and it does seem like the simplest approach.
I think your main problem was that XMLElements hasn't a itemByName method. You can only reference XMLElements through their indeces or ids.
Secondly you assume that you got xmlElements from XPath expression but it's likely that you got nothing as your xpath seems uncorrect.
var myFrees = myDoc.xmlElements[0].evaluateXPathExpression("./cars1/free");
var n = myFrees.length;
if ( !n ) {
alert("Aucun élément trouvé");
}
else {
while (n--) myFrees[n].remove();
}
You need to start your expression by setting the origin of your xpath. Here a dot "./" is used to tell you want to look for cars1/free xml elements at the "root" of the xmlelement. Using "//" on the contrary would have returned any cars/free items unregardingly of their locations.

Using a for loop iterator to shorten a function

I'm trying to shorten a 2.5k line function into something far more manageable using a few for loops. I've managed to stifle off all syntax errors after a lot of changes. However what was an originally extremely long but working function, has now turned into a much shorter but broken function. Can someone explain what I'm doing wrong.
This is the essence of what I'm trying to do:
function countryChange() {
//Blank auto complete box
for(var i=0; i<30; i++){
for(var q=5; q<7; q++){
for(var u=0; u<3; u++){
$("#_Q6_Q".concat(toString(i),"_Q", toString(q), "_Q0_Q", toString(u))).val('');
};
};
$("#_Q6_Q".concat(toString(i),"_Q4_Q0_Q0")).val('');
};
The function goes on a bit more, but everything is essentially a repetition, using for loops to generate a series of numbers which is then put into a few statements.
Without the for loops there are 30*3*3 variations of the _Q6_Q#_Q#_Q0_Q# which all have to be used and reused several times.
Also it might be worth noting that this is to use the JQuery AutoComplete widget:
http://api.jqueryui.com/autocomplete/#option-source
I wrote the previous 2.5k line script with Python which I'm more comfortable with and could generate all the large repetitive could with.
Fixed for now: had to change toString() to String(). Although some have mentioned that that isn't needed at all.
i suggest you split the problems, and create the numbers you'll later crunch within an array, and then define what will be the crunching function, then crunch the array :
function createArray () {
var res=[];
var i,q,u;
for(i=0; i<30; i++){
for(q=5; q<7; q++){
for(u=0; u<3; u++) {
res.push([i:i, q:q, u:u]);
};
};
};
}
// just build it once and store it
var myIndexes = createArray();
After that the processing can be done with one forEach for all your loops.
myIndexes.forEach(resetAutoCompleteComboBox);
with :
function resetAutoCompleteComboBox(a) {
var i=toString(a.i);
var q=toString(a.q);
var u=toString(a.u);
$("#_Q6_Q" + i + "_Q" + q + "_Q0_Q" + u)).val('');
}
If you take advantage of the similarity of the processing functions, you might also be able to do some other useful code factorisation.
Might be better to use jQuery's built in sizzle selectors:
function countryChange() {
$("[id^='_Q6_Q'],[id^='_Q'], [id^='_Q0_Q'], [id^='_Q4_Q0_Q0']").val('');
};
You can also do wildcards and partial matches if you like.
Here's the selectors reference guide: http://api.jquery.com/category/selectors/

Better way of splitting and assigning many values in Javascript?

I have a for loop that cycles through the number of elements that the user has created. There are a lot of available settings in this plugin, and each element can receive it's specific settings.
User settings are entered in the following format: speed_x: "1000,500 > 1000,200 > 0,0"
This controls the speed_x in/out for 3 separate elements. The > divides by object and the commas separate the in/out.
So I can grab specific object speed_x values, I've split speed_x into speed_x_set (splitting by >) resulting in:
1 1000,500
2 1000,200
3 0,0`
3 Inside the loop, I grab the value by index (since it's the object #) and split it by comma (to get speed_x_in and speed_x_out.)
for(var i=0; i<OS.numberofobjects; ++i){
OS.speed_x_on_set[i]=speed_x_set[i].split(",")[0],
OS.speed_x_off_set[i]=speed_x_set[i].split(",")[1],
...
};
Everything is assigned by object and by setting in/out correctly into the master OS settings object. T*he problem is I have many, many settings which need to be split in this fashion...* for example: delay_x_set, speed_y_set, opacity_set, etc. Their names are all based on the default setting name, with "_set" added as shown above. Hopefully this provides enough information. Thanks!
I would avoid to access to the same item twice and perform the same split twice for each iteration. So, you could have something like:
for (var i = 0, item; item = speed_x_set[i++];) {
var values = item.split(",");
OS.speed_x_on_set.push(values[0]);
OS.speed_x_off_set.push(values[1]);
}
Notice that in JavaScript 1.7 (Firefox) you can simply have:
for (var i = 0, item; item = speed_x_set[i++];) {
var [on, off] = item.split(",");
OS.speed_x_on_set.push(on);
OS.speed_x_off_set.push(off);
}
And hopefully in the next version of ECMAScript as well.
It's called "destructuring assignment".
I would say to cache the split result
for(var objindex=0; objindex<OS.numberofobjects; ++objindex){
var splits = speed_x_set[objindex].split(","); //Cache the split so its does not need to be done twice
OS.speed_x_on_set[objindex] = splits[0];
OS.speed_x_off_set[objindex] = splits[1];
...
};
What you're looking for is called parallel assignment, but unfortunately, JavaScript doesn't have it.
In ruby, however, it is common to see similar patterns:
first, second = "first second".split
As others have noted, the obvious way would be to cache split results and assign them separately. Sorry for not answering your question directly.

Removing items from data bound array

How do I remove an items from a data bound array? My code follows.
for(var i = 0; i < listBox.selectedIndices.length; i++) {
var toRemove = listFiles.selectedIndices[i];
dataArray.splice(toRemove, 1);
}
Thanks in advance!
Edit Here is my swf. The Add Photos works except when you remove items.
http://www.3rdshooter.com/Content/Flash/PhotoUploader.html
Add 3 photos different.
Remove 2nd photo.
Add a different photo.
SWF adds the 2nd photo to the end.
Any ideas on why it would be doing this?
Edit 2 Here is my code
private function OnSelectFileRefList(e:Event):void
{
Alert.show('addstart:' + arrayQueue.length);
for each (var f:FileReference in fileRefList.fileList)
{
var lid:ListItemData = new ListItemData();
lid.fileRef = f;
arrayQueue[arrayQueue.length]=lid;
}
Alert.show('addcomplete:' + arrayQueue.length);
listFiles.executeBindings();
Alert.show(ListItemData(arrayQueue[arrayQueue.length-1]).fileRef.name);
PushStatus('Added ' + fileRefList.fileList.length.toString() + ' photo(s) to queue!');
fileRefList.fileList.length = 0;
buttonUpload.enabled = (arrayQueue.length > 0);
}
private function OnButtonRemoveClicked(e:Event):void
{
for(var i:Number = 0; i < listFiles.selectedIndices.length; i++) {
var toRemove:Number = listFiles.selectedIndices[i];
//Alert.show(toRemove.toString());
arrayQueue.splice(toRemove, 1);
}
listFiles.executeBindings();
Alert.show('removecomplete:' + arrayQueue.length);
PushStatus('Removed photos from queue.');
buttonRemove.enabled = (listFiles.selectedItems.length > 0);
buttonUpload.enabled = (arrayQueue.length > 0);
}
It would definitely be helpful to know two things:
Which version of ActionScript are you targeting?
Judging from the behavior of your application, the error isn't occurring when the user removes an item from the list of files to upload. Looks more like an issue with your logic when a user adds a new item to the list. Any chance you could post that code as well?
UPDATE:
Instead of: arrayQueue[arrayQueue.length]=lid
Try: arrayQueue.push(lid)
That will add a new item to the end of the array and push the item in to that spot.
UPDATE 2:
Ok, did a little more digging. Turns out that the fileList doesn't get cleared every time the dialog is opened (if you're not creating a new instance of the FileReferenceList each time the user selects new files). You need to call splice() on the fileList after you add each file to your Array.
Try something like this in your AddFile() method...
for(var j:int=0; j < fileRefList.fileList.length; j++)
{
arrayQueue.push(fileRefList.fileList[j]);
fileRefList.fileList.splice(j, 1);
}
That will keep the fileList up to date rather than holding on to previous selections.
I see one issue. The selected indices are no longer valid once you have spliced out the first element from the array. But that should only be a problem when removing multiple items at once.
I think we need to see more code about how you are handling the upload before we can figure out what is going on. It looks to me like you are holding a reference to the removed FileReference or something. The described problem is occurring when you upload a new file, not when you remove the selected one.
Do you mean to use listBox and listFiles to refer to the same thing?
I'm stepping out on a limb here, because I don't have a ton of experience with JavaScript, but I'd do this the same way that I'd do it in C, C++, or Java: By copying the remaining array elements down into their new locations.
Assuming that listFiles.selectedIndices is sorted (and its contents are valid indices for dataArray), the code would be something like the following:
(WARNING: untested code follows.)
// Don't bother copying any elements below the first selected element.
var writeIndex = listFiles.selectedIndices[0];
var readIndex = listFiles.selectedIndices[0] + 1;
var selectionIndex = 1;
while(writeIndex < (dataArray.length - listFiles.selectedIndices.length)) {
if (selectionIndex < listFiles.selectedIndices.length) {
// If the read pointer is currently at a selected element,
// then bump it up until it's past selected range.
while(selectionIndex < listFiles.selectedIndices.length &&
readIndex == listFiles.selectedIndices[selectionIndex]) {
selectionIndex++;
readIndex++;
}
}
dataArray[writeIndex++] = dataArray[readIndex++];
}
// Remove the tail of the dataArray
if (writeIndex < dataArray.length) {
dataArray.splice(writeIndex, dataArray.length - writeIndex);
}
EDIT 2009/04/04: Your Remove algorithm still suffers from the flaw that as you remove items in listFiles.selectedIndices, you break the correspondence between the indices in arrayQueue and those in listFiles.selectedIndices.
To see this, try adding 3 files, then doing "Select All" and then hit Remove. It will start by removing the 1st file in the list (index 0). Now what had been the 2nd and 3rd files in the list are at indices 0 and 1. The next value taken from listFiles.selectedIndices is 1 -- but now, what had been the 3rd file is at index 1. So the former File #3 gets spliced out of the array, leaving the former 2nd file un-removed and at index 0. (Using more files, you'll see that this implementation only removes every other file in the array.)
This is why my JavaScript code (above) uses a readIndex and a writeIndex to copy the entries in the array, skipping the readIndex over the indices that are to be deleted. This algorithm avoids the problem of losing correspondence between the array indices. (It does need to be coded carefully to guard against various edge conditions.) I tried some JavaScript code similar to what I wrote above; it worked for me.
I suspect that the problem in your original test case (removing the 2nd file, then adding another) is analogous. Since you've only shown part of your code, I can't tell whether the array indices and the data in listFiles.selectedIndices, arrayQueue, and fileRefList.fileList are always going to match up appropriately. (But I suspect that the problem is that they don't.)
BTW, even if you fix the problem with using splice() by adjusting the array index values appropriately, it's still an O(N2) algorithm in the general case. The array copy algorithm is O(N).
I'd really need to see the whole class to provide a difinitive answer, but I would write a method to handle removing multiple objects from the dataProvider and perhaps assigning a new array as the dataProvider for the list instead of toying with binding and using the same list for the duration. Like I said, this is probably inefficient, and would require a look at the context of the question, but that is what I would do 9unless you have a big need for binding in this circumstance)
/**
* Returns a new Array with the selected objects removed
*/
private function removeSelected(selectedItems:Array):Array
{
var returnArray:Array = []
for each(var object:Object in this.arrayQueue)
{
if( selectedItems.indexOf(object)==-1 )
returnArray.push( object )
}
return returnArray;
}
You might be interested in this blog entry about the fact that robust iterators are missing in the Java language.
The programming language, you mentioned Javascript, is not the issue, it's the concept of robust iterators that I wanted to point out (the paper actually is about C++ as the programming language).
The [research document]() about providing robust iterators for the ET++ C++ framework may still e helpful in solving your problem. I am sure the document can provide you with the necessary ideas how to approach your problem.

Categories