I'm trying to write a little script to make my coworkers and mine lives easier. I am trying to append lines to a spreadsheet based on information entered into a custom form. The code posted below just the doPost block which should be appending the google spreadsheet.
function doPost(form) {
var PN = form.PartNumber;
var REV = form.Revision;
var DATE = form.RevisionDate;
var DESC = form.Description;
var NOTE = form.PartNotes;
var URL = form.myFile.getURL();
var ss = SpreadsheetApp.openById("ID HERE"); // removed ID for sake of safety (let me be paranoid)
var sheet = ss.getSheetName('Uploads');
sheet.appendRow([PN,REV,DATE,DESC,NOTE,URL]);
}
I am unsure why it isn't writing to the spreadsheet but it isn't throwing me any errors. If you can offer any insight as to what is wrong I would greatly appreciate it; there are many guides online but most seem to be based on deprecated functions/code/etc.
Thanks for your time.
Instead of using doPost, set up a "On form submit" trigger.
You need to get the namedValues to be able to pull specific values and take the first output.
Also, it should be "getSheetByName('Uploads')" .
As pointed out in the previous answer, it is unclear what you are trying to achieve by "form.myFile.getURL();" If you want to get the form url you might as well create it as a string, as it always stays the same.
Here is a working example of your code:
function doPost(form) {
var formResponses = form.namedValues;
var PN = formResponses.PartNumber[0];
var REV = formResponses.Revision[0];
var DATE = formResponses.RevisionDate[0];
var DESC = formResponses.Description[0];
var NOTE = formResponses.PartNotes[0];
//var URL = form.myFile.getURL(); //Not sure what you are tyring to get here as form URL will always be the same.
var URL = "Your form's url"; //You can put the form url in here so it will be pushed in to every row.
var ss = SpreadsheetApp.openById("ID HERE"); // removed ID for sake of safety (let me be paranoid)
var sheet = ss.getSheetByName('Uploads');
sheet.appendRow([PN,REV,DATE,DESC,NOTE,URL]);
}
The form fields are nested in a "parameter" property in the doPost parameter.
So, you should access them using:
function doPost(form) {
var actualForm = form.parameter;
var PN = actualForm.PartNumber;
//etc
To double check all parameters your receiving and their names, you could append to your sheet everything stringfied, like this:
sheet.appendRow([JSON.stringify(form)]);
--edit
This form.myFile.getURL() also looks odd. I guess another good debugging trick you could do is to wrap everything in a try-catch and email yourself any errors you get. For example:
function doPost(form) {
try {
//all your code
} catch(err) {
MailApp.sendMail('yourself#etc', 'doPost error', err+'\n\n'+JSON.stringify(form));
}
}
On form submit
onFormSubmit works. "doPost" looks wrong.
Simple example:
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
for(var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("SendGoogleForm")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit()
.create();
}
function SendGoogleForm(e)
{
try
{
Full example - Scroll down to the code http://www.labnol.org/internet/google-docs-email-form/20884/ (Note: example sends email)
Trigger docs: https://developers.google.com/apps-script/guides/triggers/events
Notes: I think the problem is doPost, Does it work with google Forms? Never seen it used with google forms.
First and foremost, thank you everyone who has responded with information thus far. None of the solutions posted here worked for my particular implementation (my implementation is probably to blame, it is very crude), but they definitely set me down the path to a working version of my form which we now lightly use. I have posted some of the code below:
function sheetFill(form, link) {
try {
var formResponses = form.namedValues;
var toForm = [0,0,0,0,0,0,0];
for (i=0;i < form.PartNumber.length;i++){
toForm[0] = toForm[0]+form.PartNumber[i];
}
... (several for loops later)
var d = new Date();
var ss = SpreadsheetApp.openById("IDHERE");
var sheet = ss.getCurrentSheet;
ss.appendRow([toForm[0], toForm[1], toForm[2], toForm[3], toForm[4], toForm[5], toForm[6], link, d]);
} catch(err) {
MailApp.sendEmail('EMAIL', 'doPost error', err+'\n\n'+JSON.stringify(form));
}
}
It is not very versatile or robust and isn't elegant, but it is a starting point.
Related
Script must log postback information about call detail from zvonok.com to google spreadsheets. I has write function which only append row to sreadsheet - no update or modify of any cell in code and during few manual test calls rows has been append correct, but when my client began his usual call campaign, calls and postbacks going very often one after other, values in last row began changing few times and in some cases leave strange values
I seen behavior like this first time made short video record:
https://youtu.be/0_H_mVAbp4g
here is one column with strange value
2103052006092385
2,10305E+15
210305412464544
I have found 9 cases from 248 rows.
Client has show me excel from his user cabinet, totally was maded 5649 calls, so in google spreadsheets must be 5649 rows instead 248.
function getJsonFromUrl(url) {
var query = url;
var result = {};
if (query == undefined){
return result;
}
query.split("&").forEach(function(part) {
var item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
return result;
}
function doGet(e){
const ctCompl = 'ct_completed';
var doc = SpreadsheetApp.openById(SHEET_KEY);
var sheet = doc.getSheetByName(SHEET_NAME);
var row = [];
if(typeof e !== undefined){
mArr = getJsonFromUrl(e.queryString);
for (i in mArr) if( i == ctCompl) {
row.push(convTimeLong(mArr[i]));
} else
row.push(mArr[i]);
sheet.appendRow(row);
} else {
sheet.appendRow(['e undefined!']);
}
SpreadsheetApp.flush();
return handleResponse(e)
}
function convTimeLong(dateTime) {
let d = new Date();
let dt=dateTime.replace('+', 'T');
try {
var res = Utilities.formatDate(d,"GMT+2", "dd.MM.yyyy HH:mm");
return res
} catch(e){
return dateTime; }
}
executions dashboard show status "completed' everywhere, execution time longest - 1.688 s
Client has set delay 5 second between call's, right now I don't now is percent of lost postback's decreased after delay was set or not, but it still very high.
https://youtu.be/0_H_mVAbp4g
In general, using Google Sheets as a database is a bad idea. It's not designed for this so it could fail really bad. Using a proper database will make everything much, much easier. If you are using the spreadsheet to then cook the data, I'd advise to use a function that imports data like IMPORTXML (see reference).
That being said, if you insist on using Sheets, you could try using locks:
function appendRow(sheet, row) {
const lock = LockService.getScriptLock()
while (!lock.tryLock(100000)) /* Spin the lock until it gets aquired */;
try {
sheet.appendRow(row)
SpreadsheetApp.flush()
} finally {
lock.releaseLock()
}
}
To use it, you only need to pass the sheet and the values to add: sheet.appendRow(row) to appendRow(sheet, row).
It will make sure that entries don't get overridden. Note that this will slow down the code a lot and the script can time out if there are a lot of requests.
I'm creating an office js addin that inserts data from the bottom of Table 1 into Table 2 but I am unable to find a method of doing this that works.
I have tried using Excel.Functions.countA() but I can't seem to get a value other than NaN out of it. Here is the code I'm using:
async function run() {
try {
await Excel.run(async context => {
var sheet1Name = "Sheet1";
var sheet1RangeAddress = "B:B";
var sheet2Name = "Sheet2";
var sheet2RangeAddress = "A2:P2";
var sheet2Range = context.workbook.worksheets.getItem(sheet2Name).getRange(sheet2RangeAddress);
sheet2Range.insert("Down");
var sheet1CellAddress = context.workbook.worksheets.getItem(sheet1Name).getRange(sheet1RangeAddress).load("address");
var sheet1RangeLength = Number(context.workbook.functions.countA(sheet1CellAddress));
var sheet1LastCell = context.workbook.worksheets.getItem(sheet1Name).getRangeByIndexes(3,1,sheet1RangeLength,1).getLastCell();
var sheet2Cell = context.workbook.worksheets.getItem(sheet2Name).getRange("A2");
sheet2Cell.values = [[ context.workbook.worksheets.getItem(sheet2Name).getRange("A2").copyFrom(sheet1LastCell) ]]
await context.sync();
});
} catch (error) {
console.error(error);
}
}
I can't find anything useful in Microsoft's documentation or a working example online. Does anyone know what I'm doing wrong?
This line in your code looks problematic:
var sheet1RangeLength = Number(context.workbook.functions.countA(sheet1CellAddress));
The Functions.countA method returns an Excel.FunctionResult object which I don't think can be cast to a Number. The count returned by the function will be in the value property of the returned object. You need to load that value to read it. Try these two lines as a replacement:
var sheet1RangeLength = context.workbook.functions.countA(sheet1CellAddress).load("value");
await context.sync();
BTW, the following line is returning a Range object, not an address. That's OK because countA accepts a Range object parameter, but your variable is misleadingly named. Also, I don't think the load("address") on the end is serving any purpose.
var sheet1CellAddress = context.workbook.worksheets.getItem(sheet1Name).getRange(sheet1RangeAddress).load("address");
If you haven't already, please see this article: Call built-in Excel worksheet functions.
I just joined a company and I'm having issues with a script written by someone who no longer works here. I keep receiving a TypeError. I'm also new to this code.
I've gone through some other threads and didn't see anything related to this particular TypeError.
function csatGmailPull() {
var threads = GmailApp.search("label: name");
var message = threads[0].getMessages()[0];
var attachment = message.getAttachments()[0];
if (attachment.getContentType() === "text/csv") {
var sheet = SpreadsheetApp.openById("SheetID").getSheetByName('Import');
var csvData = Utilities.parseCsv(attachment.getDataAsString(), ",");
sheet.clearContents().clearFormats();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
var labelOld = GmailApp.getUserLabelByName("LabelName");
var labelNew = GmailApp.getUserLabelByName("LabelName2");
threads[0].addLabel(labelNew);
threads[0].removeLabel(labelOld);
}
}
I've removed the sheet ID and label names. But this code is supposed to open up Gmail, get the emails under the label identified in the GmailApp.search, load the data into a preset Google Spreadsheet, and then tag the message with a new label. However, I keep getting "TypeError: Cannot call method "getMessages" of undefined." when I test it. Any help would be appreciated!
sorry if this is an elementary question, but I just can't get this to work the way I need.
I have a script that essentially consists of 3 parts:
1). Removes all protection in a sheet
2). Executes some copying functions (since ranges are protected I need to remove the protection first #1)
3). Sets the protection back up after #2 is finished.
Here's my code:
First clears protection
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('COST REPORT');
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
Second clears data in cells
var costReport = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(
'COST REPORT');
costReport.getRange('F12:F16').clearContent(); //Theoreticals
costReport.getRange('D20:D20').clearContent(); //Week Ending Date
Third sets protection
var ss = SpreadsheetApp.getActive().getSheetByName('COST REPORT');
var costReportCOGS = ss.getRange('G11:G16');
var protection = costReportCOGS.protect().setDescription('costReportCOGS');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
var costReportPurchaseEnding = ss.getRange('D11:E16');
var protection = costReportPurchaseEnding.protect().setDescription(
'costReportPurchaseEnding');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
I've cut some of the script down for ease of debugging, but basically I need the script to Execute & Finish in this order, one by one. If you just try running the script the way it is, the protection doesn't get removed and I get the error "trying to edit protected range...."
If I run each block by itself then it works perfect, but that consists of 3 different scripts the user has to run and I need it all in one.
Thanks in advance!
Sean.
Something like this?
function removeProtection() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('COST REPORT');
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
};
function clearRangeData() {
var costReport = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(
'COST REPORT');
costReport.getRange('F12:F16').clearContent(); //Theoreticals
costReport.getRange('D20:D20').clearContent(); //Week Ending Date
};
function weeklyFileRangeProtection() {
//COST REPORT
var ss = SpreadsheetApp.getActive().getSheetByName('COST REPORT');
var costReportCOGS = ss.getRange('G11:G16');
var protection = costReportCOGS.protect().setDescription('costReportCOGS');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
};
You are having issues because for each function you are calling SpreadsheetApp.getActiveSpreadsheet . Each time you make this call you create a virtual "copy" of the spreadhseet, and the changes you make to this copy are only passed to the version in Google's servers once the whole script is finished. Hence, if you manually run each of the 3 function that the workflow:
Run function 1 -> script finished -> update the spreadsheet in the server -> run function 2 (which now gets the updated spreadsheet) -> script finished -> update the spreadsheet in the server -> run function 3 (which now gets the re-updated spreadsheet) -> script finished -> update the spreadsheet in the server
Now, if you run the three functions, the way the script is here is what happens:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('COST REPORT'); this creates a virtual copy of the spreadsheet -> your code removes the protection from this copy and the server spreadsheet is not modified -> you call again var costReport = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('COST REPORT'); which create a new copy of the server spreadsheet, which hadn't its protections removed yet -> your code tries to clear the data on this copy, which triggers the error.
As #Cameron Roberts suggested in his answer Spreadsheet.flush() between the calls will solve the issue, because if forces the changes to be synced to the spreadsheet in the server. But you will have another "problem", which is the amount of copies you are calling, the .getActiveSpreadsheet() is very time consuming! It is better if you make only one call, store in a variable (you already do that, it is your variable ss) and make all the edits to that.
Your code will end up looking like this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var costReport = ss.getSheetByName('COST REPORT');
//First clear protection
var protections = costReport.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
};
};
//Second clears data in cells
costReport.getRange('F12:F16').clearContent(); //Theoreticals
costReport.getRange('D20:D20').clearContent(); //Week Ending Date
//Third sets protection
var costReportCOGS = costReport.getRange('G11:G16');
var protection = costReportCOGS.protect().setDescription('costReportCOGS');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
};
var costReportPurchaseEnding = costReport.getRange('D11:E16');
var protection = costReportPurchaseEnding.protect().setDescription(
'costReportPurchaseEnding');
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
};
This method also applies to Google Docs, which does NOT have a similar .flush() method for updating the server version.
I believe you have misdiagnosed the issue slightly. The code is already running in the correct order, but the protection is simply not being removed before the write calls are executed, due to the nature of Google's underlying architecture.
The comments steering you towards asynchronous behaviour are not helpful in this case, they do make sense from a Javascript perspective but are not the issue here, this is an Apps Script / Google Sheets issue, none of the functions you are calling are asynchronous.
I have two suggestions, one is to try calling SpreadsheetApp.flush() after the protections are removed. The other is to use Utilities.sleep() to artificially pause the script for a brief period after executing the remove() calls.
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#flush()
https://developers.google.com/apps-script/reference/utilities/utilities#sleep(Integer)
I have a basic HTML text area which will be used to have URLs pasted into it. Once some URLs are passed into the text area, those will be sent to a server-side script for processing via AJAX. I'll be binding those whole process to a keyUp event.
The issue is: How will I know I'm sending valid URLs to the script with a client-side check? I don't want to start sending URLs to the PHP script without having them validated in Javascript/jQuery first.
This would be quite easy to solve of the text area accepted one URL only, but the text area needs to accept multiple URLs separated by line breaks. So for example, I'd need to validate this:
http://someurl.com/something.ex
https://someurl.com/somethingelse.ext
I-M-NOT-AN-URL
So from the above, only the URLs would be sent to the server and I-M-NOT-AN-URL would be ignored.
I've not tried anything in regards to this issue since I'm not very familiar with JS, nor found anything as I couldn't come up with a relevant search term I guess, so I'm asking here for help.
Any kind of help on how to tackle this issue would be appreciated.
Update
Based on the comments and answer below, I've come up with the following Javascript/jQuery. I don't know if it efficient, therefore I'm sharing it with you for feedback and help. I don't seem to know how to prepare logic that well in JS... That's lame from my side.
Anyway here I go:
var char_start = 10;
var index = 0;
var urls = $('textarea.remote-area');
var val_ary = [];
var urls_ary = [];
var single_url = '';
urls.keyup(function(){
if (urls.val().length >= char_start)
{
var has_lbrs = /\r|\n/i.test(urls.val());
if (has_lbrs) {
val_ary = urls.val().split('\n');
for (var i = 0; i < val_ary.length; i++)
{
if (!validate_url(val_ary[i]))
{
continue;
}
urls_ary[i] = val_ary[i];
}
}
else
{
if (validate_url(urls.val()))
{
single_url = urls.val();
}
}
if (urls_ary.length > 0)
{
for (var i = 0; i < urls_ary.length; i++)
{
$.ajax({
// do AJAX here.
});
}
}
else
{
$.ajax({
// do AJAX here.
});
}
}
});
function validate_url(url)
{
if(/^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*#)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|\/|\?)*)?$/i.test(url)){
return true;
}
return false;
}
The jQuery validation plugin makes use of a method such as this:
var anyURL = "http://www.yahoo.com/";
if(/^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*#)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|#)|\/|\?)*)?$/i.test(anyURL)) {
/* the URL is valid */
} else {
/* the URL is invalid)
}
You can use that code directly or use the validation plugin itself.
Please note: it may be that the plugin has evolved and the actual code is different now. Nonetheless, the above should help you.