JQuery "Constrain" plugin - Weird Javascript Error - javascript

Background: Our web app uses the jquery.constrain.js plugin to handle data entry in some text boxes, to only allow valid characters to be added. This plugin allows various constraints upon the data, using regular expressions, whitelist/blacklist characters, etc. Until today, we were running the 1.0 release of this plugin, unmodified.
I noticed a few days ago that some of the text boxes still allowed invalid data entry. For example, a numeric-only textbox allowed alpha characters, etc. It also displayed a javascript error "Object doesn't support this property or method". I tracked it down to the following function in the jquery.constrain plugin.
function match(item, input, e) {
var arr = item.chars.split("");
for (var i in arr) {
var token = arr[i];
if (token.charCodeAt(0) == e.which) {
return true;
}
}
if (item.regex) {
var re = new RegExp(item.regex);
if (re.test(String.fromCharCode(e.which))) {
return true;
}
}
return false;
};
Debugging through this block of code, I determined the following:
item is an object with two string properties: chars and regex
item.chars is an empty string ("") at the time of failure.
arr, the result of item.chars.split("") is, as expected, an empty array.
Here's where it's weird. Even though arr is an empty array, the for loop assigns a valid value to i. The value is "remove". So we drop into the loop. token is obviously null, because arr["remove"] is null. So token.charCodeAt(0) throws.
I corrected the error by adding an if statement around the for loop, as follows:
if (arr.length > 0) {
for (var i in arr) {
var token = arr[i];
if (token.charCodeAt(0) == e.which) {
return true;
}
}
}
However, I'm completely baffled as to why this was even necessary - is this an IE bug, a bug in the plugin, or am I just holding my breath wrong when I compile the app?

You should never use for(i in arr) to loop over Arrays. If scripts add methods to the Array prototype these too will be iterated using the for(i in arr) loop. This is probably what is causing your errors. You probably have added a script that modifies the Array.prototype chain.
Also read here under "Why you should stop using for…in to iterate (or never take it up)"
http://www.prototypejs.org/api/array

Related

How to return multiple tokens with Jison lexer

I'm new to lexing and parsing so sorry if the title isn't clear enough.
Basically, I'm using Jison to parse some text and I am trying to get the lexer to comprehend indentation. Here's the bit in question:
(\r\n|\r|\n)+\s* %{
parser.indentCount = parser.indentCount || [0];
var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length;
if (indentation > parser.indentCount[0]) {
parser.indentCount.unshift(indentation);
return 'INDENT';
}
var tokens = [];
while (indentation < parser.indentCount[0]) {
tokens.push('DEDENT');
parser.indentCount.shift();
}
if (tokens.length) {
return tokens;
}
if (!indentation.length) {
return 'NEWLINE';
}
%}
So far, almost all of that works as expected. The one problem is the line where I attempt to return an array of DEDENT tokens. It appears that Jison is just converting that array into a string which causes me to get a parse error like Expecting ........, got DEDENT,DEDENT.
What I'm hoping I can do to get around this is manually push some DEDENT tokens onto the stack. Maybe with a function like this.pushToken('DEDENT') or something along those lines. But the Jison documentation is not so great and I could use some help.
Any thoughts?
EDIT:
I seem to have been able to hack my way around this after looking at the generated parser code. Here's what seems to work...
if (tokens.length) {
var args = arguments;
tokens.slice(1).forEach(function () {
lexer.performAction.apply(this, args);
}.bind(this));
return 'DEDENT';
}
This tricks the lexer into performing another action using the exact same input for each DEDENT we have in the stack, thus allowing it to add in the proper dedents. However, it feels gross and I'm worried there could be unforeseen problems.
I would still love it if anyone had any ideas on a better way to do this.
After a couple of days I ended up figuring out a better answer. Here's what it looks like:
(\r\n|\r|\n)+[ \t]* %{
parser.indentCount = parser.indentCount || [0];
parser.forceDedent = parser.forceDedent || 0;
if (parser.forceDedent) {
parser.forceDedent -= 1;
this.unput(yytext);
return 'DEDENT';
}
var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length;
if (indentation > parser.indentCount[0]) {
parser.indentCount.unshift(indentation);
return 'INDENT';
}
var dedents = [];
while (indentation < parser.indentCount[0]) {
dedents.push('DEDENT');
parser.indentCount.shift();
}
if (dedents.length) {
parser.forceDedent = dedents.length - 1;
this.unput(yytext);
return 'DEDENT';
}
return `NEWLINE`;
%}
Firstly, I modified my capture regex to make sure I wasn't inadvertently capturing extra newlines after a series of non-newline spaces.
Next, we make sure there are 2 "global" variables. indentCount will track our current indentation length. forceDedent will force us to return a DEDENT if it has a value above 0.
Next, we have a condition to test for a truthy value on forceDedent. If we have one, we'll decrement it by 1 and use the unput function to make sure we iterate on this same pattern at least one more time, but for this iteration, we'll return a DEDENT.
If we haven't returned, we get the length of our current indentation.
If the current indentation is greater than our most recent indentation, we'll track that on our indentCount variable and return an INDENT.
If we haven't returned, it's time to prepare to possible dedents. We'll make an array to track them.
When we detect a dedent, the user could be attempting to close 1 or more blocks all at once. So we need to include a DEDENT for as many blocks as the user is closing. We set up a loop and say that for as long as the current indentation is less than our most recent indentation, we'll add a DEDENT to our list and shift an item off of our indentCount.
If we tracked any dedents, we need to make sure all of them get returned by the lexer. Because the lexer can only return 1 token at a time, we'll return 1 here, but we'll also set our forceDedent variable to make sure we return the rest of them as well. To make sure we iterate on this pattern again and those dedents can be inserted, we'll use the unput function.
In any other case, we'll just return a NEWLINE.

Removing '<and text within those>' from a string in javascript

I'm receiving an array of strings from a REST api call, and the strings have some html tags in them, for example:
Bard's opponents can also travel through his Magical Journey doorways. You can follow him, if you think it's safe.</li>
You can crush Bard's healing shrines just by walking over them. Don't let his allies take them without a fight.
Bard's ultimate, Tempered Fate, affects allies, enemies, monsters, and turrets alike. Sometimes it can be to your advantage to jump into it!</li>
Each line is a string, and two of them have an </li> tag at the end.
I tried writing a function that receives such an array, and returns a corrected array. Problem is, when I use it, the console in my website shows some weird errors with the strings from the array, and I've realized my function was the cause.
That is the function:
modal.removeBracketsFromArray = function (array) {
if (array == undefined)
return array;
function removeBracketsText(text) {
return text.replace(/<[^>]*>/g, '')
};
var newArray = [];
for (var i = 0; i < array.length; i++) {
newArray.push(removeBracketsText(array[i]));
}
return newArray;
};
It seems to do the work, but it somewhy messes up when using it in an ng-repeat attribute.
This is an use example:
<champion-ally-enemy-tips allytips="modalCtrl.removeBracketsFromArray(modalCtrl.champ.allytips)"
enemytips="modalCtrl.removeBracketsFromArray(modalCtrl.champ.enemytips)">
</champion-ally-enemy-tips>
which then moves to:
<ul>
<li ng-repeat="enemytip in enemytips"><h6>{{enemytip}}</h6></li>
</ul>
When I remove the method call (like so), it doesn't show an error, but the tags remain:
<champion-ally-enemy-tips allytips="modalCtrl.champ.allytips"
enemytips="modalCtrl.champ.enemytips">
</champion-ally-enemy-tips>
Is my function doing something weird without realizing it? thanks for helping
This is a pastebin of the errors I receive: LINK
Instead of messing with the array, how about creating a custom filter to strip out the HTML on display?
.filter('removeHTML', function() {
return function(input) {
return input.replace(/<[^>]*>/g, '');
}
})
Then change your display inside the ng-repeat to:
<h6>{{enemytip | removeHTML}}</h6>

object with array type string causing a typeError

The best way to explain this is just to show you.
var condition = 70;
var formnames = new Array("wheelcheckbox1", "wheelcheckbox2","spokecheckbox","spokecheckbox2","tirecheckbox","tirecheckbox2","tirecheckbox3");
formnames.forEach(function(entry) {
console.log(obj.entry);
if(obj.entry == "") {
condition = condition - 10;
}
});
as you can see I used the console log to show how it needs to work
as that works perfect, however, using the array causes an error as
they're strings and not what the obj wants, it wants text without it being a string.
Any ideas?
for..in should not be used to iterate over an array. Consider using forEach instead.

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

Recursively find variable in page's javascript from firefox extension

So I'm working on a firefox extension that we'll be using internal (thus not terrible worried about security), and I'd like to be able to interact with the functions and values defined in the javascript of a webpage. I'm having trouble reliably finding variable definitions.
Take for example gmail, it has a VIEW_DATA list that has email subjects, etc to display. It can be accessed through window.content.frames.wrappedJSObject.VIEW_DATA, but this doesn't seem to always work for me.
Is there a reasonable way to reliably search (possible recursively) the javascript of a page for a given variable from a firefox extension?
Is this what you are looking for?
var inspected = [];//prevent infinite loop; top===window
function inspector(obj) {
inspected.push(obj);
for(var prop in obj) {
console.log(prop);
if(!is_duplicate(obj) && typeof obj[prop] == 'object')
inspector(obj[prop]);
}
}
function is_duplicate(obj) {
for(var i = 0; i < inspected.length; i++) {
if(inspected[i] === obj)
return true;
}
return false;
}
These functions will run through all of the properties of an object, walking all the way down the tree of objects. I just log the properties, but you may wish to do something more useful. Try inspector(window) and watch your firebug console get filled.

Categories

Resources