bing maps upgrading from v7 to v8 error with userMapView - rest

I'm upgrading a project from using the bing maps v7 ajax api to using the v8 api. I've run into a problem where I'm getting back a message saying that "The waypoint you entered cannot be found." So I backed up and modified a sample screen to do what I'm doing and it works fine. So I've narrowed it down (with fiddler) to the REST calls that are made for each waypoint getting an error saying the userMapView is out of range in the first case but works fine in the second. I'm not really sure why this might be different in the sample code versus my actual app. Here are the two URLs. The first is the one that fails:
https://dev.virtualearth.net/REST/v1/Locations/?q=Cincinnati%2C%20OH&o=json&culture=en-US&jsonp=Microsoft.Maps.NetworkCallbacks.f5de44&key=MY_BING_MAPS_KEY&maxResults=5&userMapView=39.18820190429691,-180,39.18820190429691,-180&inclnb=0&incl=
https://dev.virtualearth.net/REST/v1/Locations/?q=cincinnati%2C%20oh&o=json&culture=en-US&jsonp=Microsoft.Maps.NetworkCallbacks.f66617&key=MY_BING_MAPS_KEY&maxResults=5&userMapView=39.02836016935031,-85.12275695800781,39.34768093312603,-85.12275695800781&inclnb=0&incl=
If I replace the userMapView parameter of the first URL with those of the second, the first URL works too. It seems obvious that the "-180" degree part is incorrect, but I don't know how it is getting there.
BTW, both of these URLs were generated using my dev key.
Thanks for any help!
EDIT:
Here's the bulk of the code that runs into trouble. Prior to this I've new'd the Map. This code is the callback from the loadModule of the Directions module. The code is a little convoluted beyond that though as I'm plugging in origins and destinations from the form. Also note, not that it makes much difference, that userAddress is passed into this function as true.
function createDrivingRoute(useAddress) {
directionsManager = new Microsoft.Maps.Directions.DirectionsManager(bmap);
locs = [];
var fromWayPoint;
fromWayPoint = new Microsoft.Maps.Directions.Waypoint({ address: fromAddressSearch });
locs.push(fromWayPoint);
directionsManager.addWaypoint(fromWayPoint);
if (toLocationArray != null) {
if (toLocationArray.length == 1) {
if (toLocationArray[0] == false) {
toLocationArray = [];
}
}
}
if (useAddress) {
if (toLocationArray != null) {
for (i = 0; i < toLocationArray.length; i++) {
//var toWayPointLoc = new Microsoft.Maps.Directions.Waypoint({ location: toLocationArray[i] });
var toWayPointLoc = new Microsoft.Maps.Directions.Waypoint({ address: toLocationArray[i] });
locs.push(toWayPointLoc);
directionsManager.addWaypoint(toWayPointLoc);
}
for (i = 0; i < toAddressArray.length; i++) {
var toWayPoint = new Microsoft.Maps.Directions.Waypoint({ address: toAddressArray[i] });
locs.push(toWayPoint);
directionsManager.addWaypoint(toWayPoint);
}
} else {
for (i = 0; i < toAddressArray.length; i++) {
var toWayPoint = new Microsoft.Maps.Directions.Waypoint({ address: toAddressArray[i] });
locs.push(toWayPoint);
directionsManager.addWaypoint(toWayPoint);
}
}
} else {
if (toLocationArray != null) {
for (i = 0; i < toLocationArray.length; i++) {
//var toWayPointLoc = new Microsoft.Maps.Directions.Waypoint({ location: toLocationArray[i] });
var toWayPointLoc = new Microsoft.Maps.Directions.Waypoint({ address: toLocationArray[i] });
locs.push(toWayPointLoc);
directionsManager.addWaypoint(toWayPointLoc);
}
}
for (i = 0; i < toAddressArray.length; i++) {
var toWayPoint = new Microsoft.Maps.Directions.Waypoint({ address: toAddressArray[i] });
locs.push(toWayPoint);
directionsManager.addWaypoint(toWayPoint);
}
}
if ($get("<%= chkReturnOrigin.ClientID %>").checked) {
var returnWayPoint = new Microsoft.Maps.Directions.Waypoint({ address: fromAddressSearch });
directionsManager.addWaypoint(fromWayPoint);
}
// Set the element in which the itinerary will be rendered
directionsManager.setRenderOptions({ itineraryContainer: '#directions' });
// Specify a handler for when the directions are calculated
if (directionsUpdatedEventObj) {
Microsoft.Maps.Events.removeHandler(directionsUpdatedEventObj);
directionsUpdatedEventObj = null;
}
directionsUpdatedEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', onDirectionsDisplayedEvent);
if (directionsErrorEventObj) {
Microsoft.Maps.Events.removeHandler(directionsErrorEventObj);
directionsErrorEventObj = null;
}
directionsErrorEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', onDirectionsErrorEvent);
loadedRoute = null;
loadedSteps = [];
directionsManager.calculateDirections();
var destAddress = $get("<%= DestAddressList.ClientID %>").value;
if (destAddress == null || destAddress == "") {
document.getElementById("DistTotal").innerHTML = '';
}
}

Haven't seen this issue before, very odd. Take a look at your code, if you are using the Search module to do the geocoding, don't set the bounds option and see if it that corrects the issue. The bounds property is used to set the userMapView property of the REST request. If that works, then there is likely an issue with the LocationRect object being passed in, or how the search module uses it. I'll take a look into the source code to see if there is a bug that might be causing this.
Update 1: Now that I see your edit and see that you are using the directions manager I'm able to see that the userMapView is automatically added from the map. Not sure why it would be adding such incorrect values though. It looks like they are adding the same coordinate twice rather than adding the northwest and southeast coordinates. Will see if I can verify this by digging through the code.
Update 2: I've found the bug in the code. I have logged this with the development team so that they can resolve it the next time they touch the code for the search manager. The issue is that the west value is being added to the userMapView request twice, but not adding east value.
Update 3: Good news, this bug is now resolved in the experimental branch of Bing Maps. You can try this out by adding "&branch=experimental" to the map script URL. This will be added to the release branch in the next regular update planned for February.

Related

Entity Core and SaveChanges works only once

I have problem with Entity Core 3.1.2.
I have code like this:
SQL.Database.EnsureCreated();
var ThisCollector = SQL.CollectorServers
.Where(esa => esa.ServerName == ServerCollectorName)
.FirstOrDefault();
while (foo)
{
await SQL.Entry(ThisCollector)
.ReloadAsync();
DateTime dtTimeOut = DateTime.UtcNow.AddMinutes(-1);
//check status of current Worker
var CurrentLB = SQL.CollectorServers
.Where(esa => esa.isWorker == true
&& esa.LastSeenLB < dtTimeOut)
.FirstOrDefault();
if (CurrentLB!=null) //Current Worker is dead!
{
CurrentLB.isWorker = false;
ThisCollector.isWorker = true;
SQL.SaveChanges(); //This works allways
}
var Collectors = SQL.CollectorServers
.Where(Esa => Esa.isWorker == true);
if (Collectors.Count() == 0)
{
ThisCollector.isWorker = true;
SQL.SaveChanges();
}
if (Collectors.Count() >= 2)
{
foreach(CollectorServer cs in Collectors)
{
cs.isWorker = false;
//why this is requied?
SQL.Entry(cs).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
ThisCollector.isWorker = true;
//<--This works only once, without manually setting State to
// modified!!! Why? Values has been changed from external program.
//(Management studio in this case)
SQL.SaveChanges();
}
await Task.Delay(10000, stoppingToken);
}
Problem is that last SaveChanges works only first time it has been called without I set Entry state to Modified. After that it does not make SQL query (I can see that in SQL Profiler).
In this case this can be fixed by this way, but I'm trying to undestand why this happends. My software saves lot data to SQL, and I need to know can I trust to this code without opening all queries and adding this modified state.
I didin't have this kind of problems in full version (6) of Entity, this is something quite new for me.
The described behavior would make sense if the entities returned by CollectorServers are not tracked.
ThisCollector is attached on every loop by the call to SQL.Entry(ThisCollector) :
await SQL.Entry(ThisCollector) //Attaching
.ReloadAsync(); //Reloading
Any changes made to it would be tracked and saved by the first call to DbContext.SaveChanges().
On the other hand, the entities returned by the Collectors query :
var Collectors = SQL.CollectorServers
.Where(Esa => Esa.isWorker == true);
Would remain untracked, until they get reattached by the call to SQL.Entry(cs) :
foreach(CollectorServer cs in Collectors)
{
cs.isWorker = false;
SQL.Entry(cs).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
That's equivalent to calling DbContext.Update to attach and set the state to Modified. Update is easier to read though :
foreach(CollectorServer cs in Collectors)
{
cs.isWorker = false;
SQL.Update(cs);
}

How do I resolve code.org's App Lab readRecord() asynchronous timing?

I'm a fairly new student in AP CSP, and I wanted to create a username/password system in code.org's App Lab, however, it requires me to get the readRecords() command to function first.
I have the code as such :
function read (array) {
var truth;
readRecords("loginarray", {}, function(records) {
if (records.length > 0) {
for (var i = 0; i < records.length; i++){
if (array.user == records[i].user){
if (array.pass == records[i].pass) {
truth = false;
hideElement("unverifiedtext");
hideElement("retrybutton");
setScreen("verifyscreen");
}
}
}
}
}
);
return truth;
}
But nothing I do seems to get the readRecords() command working. I'm rather confused, can this asynchronous timing be meddled with at all? If not, how should I go about fixing this issue?
Thanks in advance!

Google form that turns on and off each day automatically

I love Google Forms I can play with them for hours. I have spent days trying to solve this one, searching for an answer. It is very much over my head. I have seen similar questions but none that seemed to have helped me get to an answer. We have a café where I work and I created a pre-order form on Google Forms. That was the easy part. The Café can only accept pre-orders up to 10:30am. I want the form to open at 7am and close at 10:30am everyday to stop people pre ordering when the café isn't able to deal with their order. I used the very helpful tutorial from http://labnol.org/?p=20707 to start me off I have added and messed it up and managed to get back to the below which is currently how it looks. It doesn't work and I can't get my head around it. At one point I managed to turn it off but I couldn't turn it back on!! I'm finding it very frustrating and any help in solving this would be amazing. To me it seems very simple as it just needs to turn on and off at a certain time every day. I don't know! Please help me someone?
FORM_OPEN_DATE = "7:00";
FORM_CLOSE_DATE = "10:30";
RESPONSE_COUNT = "";
/* Initialize the form, setup time based triggers */
function Initialize() {
deleteTriggers_();
if ((FORM_OPEN_DATE !== "7:00") &&
((new Date()).getTime("7:00") < parseDate_(FORM_OPEN_DATE).getTime ("7:00"))) {
closeForm("10:30");
ScriptApp.newTrigger("openForm")
.timeBased("7:00")
.at(parseDate_(FORM_OPEN_DATE))
.create(); }
if (FORM_CLOSE_DATE !== "10:30") {
ScriptApp.newTrigger("closeForm")
.timeBased("10:30")
.at(parseDate_(FORM_CLOSE_DATE))
.create(); }
if (RESPONSE_COUNT !== "") {
ScriptApp.newTrigger("checkLimit")
.forForm(FormApp.getActiveForm())
.onFormSubmit()
.create(); } }
/* Delete all existing Script Triggers */
function deleteTriggers_() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
/* Allow Google Form to Accept Responses */
function openForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(true);
informUser_("Your Google Form is now accepting responses");
}
/* Close the Google Form, Stop Accepting Reponses */
function closeForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(false);
deleteTriggers_();
informUser_("Your Google Form is no longer accepting responses");
}
/* If Total # of Form Responses >= Limit, Close Form */
function checkLimit() {
if (FormApp.getActiveForm().getResponses().length >= RESPONSE_COUNT ) {
closeForm();
}
}
/* Parse the Date for creating Time-Based Triggers */
function parseDate_(d) {
return new Date(d.substr(0,4), d.substr(5,2)-1,
d.substr(8,2), d.substr(11,2), d.substr(14,2));
}
I don't think you can use .timebased('7:00'); And it is good to check that you don't have a trigger before you try creating a new one so I like to do this. You can only specify that you want a trigger at a certain hour like say 7. The trigger will be randomly selected somewhere between 7 and 8. So you really can't pick 10:30 either. It has to be either 10 or 11. If you want more precision you may have to trigger your daily triggers early and then count some 5 minute triggers to get you closer to the mark. You'll have to wait to see where the daily triggers are placed in the hour first. Once they're set they don't change.
I've actually played around with the daily timers in a log by creating new ones until I get one that close enough to my desired time and then I turn the others off and keep that one. You have to be patient. As long as you id the trigger by the function name in the log you can change the function and keep the timer going.
Oh and I generally created the log file with drive notepad and then open it up whenever I want to view the log.
function formsOnOff()
{
if(!isTrigger('openForm'))
{
ScriptApp.newTrigger('openForm').timeBased().atHour(7).create()
}
if(!isTrigger('closeForm'))
{
ScriptApp.newTrigger('closeForm').timeBased().atHour(11)
}
}
function isTrigger(funcName)
{
var r=false;
if(funcName)
{
var allTriggers=ScriptApp.getProjectTriggers();
var allHandlers=[];
for(var i=0;i<allTriggers.length;i++)
{
allHandlers.push(allTriggers[i].getHandlerFunction());
}
if(allHandlers.indexOf(funcName)>-1)
{
r=true;
}
}
return r;
}
I sometimes run a log entry on my timers so that I can figure out exactly when they're happening.
function logEntry(entry,file)
{
var file = (typeof(file) != 'undefined')?file:'eventlog.txt';
var entry = (typeof(entry) != 'undefined')?entry:'No entry string provided.';
if(entry)
{
var ts = Utilities.formatDate(new Date(), "GMT-6", "yyyy-MM-dd' 'hh:mm:ss a");
var s = ts + ' - ' + entry + '\n';
myUtilities.saveFile(s, file, true);//this is part of a library that I created. But any save file function will do as long as your appending.
}
}
This is my utilities save file function. You have to provide defaultfilename and datafolderid.
function saveFile(datstr,filename,append)
{
var append = (typeof(append) !== 'undefined')? append : false;
var filename = (typeof(filename) !== 'undefined')? filename : DefaultFileName;
var datstr = (typeof(datstr) !== 'undefined')? datstr : '';
var folderID = (typeof(folderID) !== 'undefined')? folderID : DataFolderID;
var fldr = DriveApp.getFolderById(folderID);
var file = fldr.getFilesByName(filename);
var targetFound = false;
while(file.hasNext())
{
var fi = file.next();
var target = fi.getName();
if(target == filename)
{
if(append)
{
datstr = fi.getBlob().getDataAsString() + datstr;
}
targetFound = true;
fi.setContent(datstr);
}
}
if(!targetFound)
{
var create = fldr.createFile(filename, datstr);
if(create)
{
targetFound = true;
}
}
return targetFound;
}

JSTree creating duplicate nodes when loading data with create_node

I'm having an issue when I'm trying to load my initial data for JSTree; I have 2 top level nodes attached to the root node but when I load them it looks like the last node added is being duplicated within JSTree. At first it looked as if it was my fault for not specifically declaring a new object each time but I've fixed that. I'm using .net MVC so the initial data is coming from the model that is passed to my view (that is the data passed into the data parameter of the method).
this.loadInitialData = function (data) {
var tree = self.getTree();
for (var i = 0; i < data.length; i++) {
var node = new Object();
node.id = data[i].Id;
node.parent = data[i].Parent;
node.text = data[i].Text;
node.state = {
opened: data[i].State.Opened,
disabled: data[i].State.Disabled,
selected: data[i].State.Selected
};
node.li_attr = { "node-type": data[i].NodeType };
node.children = [];
for (var j = 0; j < data[i].Children.length; j++) {
var childNode = new Object();
childNode.id = data[i].Children[j].Id;
childNode.parent = data[i].Children[j].Parent;
childNode.text = data[i].Children[j].Text;
childNode.li_attr = { "node-type": data[i].Children[j].NodeType };
childNode.children = data[i].Children[j].HasChildren;
node.children.push(childNode);
}
tree.create_node("#", node, "last");
}
}
My initial code was declaring node like the following:
var node = {
id: data[i].Id
}
I figured that was the cause of what I'm seeing but fixing it has not changed anything. Here is what is happening when I run the application; on the first pass of the method everything looks like it is working just fine.
But after the loop is run for the second (and last) time here is the final result.
It looks like the node objects are just a copy of each other, but when I run the code through the debugger I see the object being initialized each time. Does anyone have an idea what would cause this behavior in JSTree? Should I be using a different method to create my initial nodes besides create_node?
Thanks in advance.
I found the issue; I didn't realize but I was setting my id property to the same id for both node groups. After I fixed it everything started working as expected.

How can i force website to stay in frame?

I'm using Firefox + searchbastard addon to do a multi-search on shopping search engines.The pages are part of a frame. This works just fine for all sites I tried so far except for shopmania.com.
If I use noscript to forbid scripts from the shopmania domain name then everything stays in place but the part of the website elements become nonresponsive. I know there is an option in Firefox to force links that open in a new window to open in a new tab. Is there something similar to prevent websites from popping out of frame? Maybe a Firefox addon that blocks these requests?
Or at least can someone please tell me what is causing only this website to act like this?
EDIT: What tool can i use to pause firefox OR javascript and stepthrough code like in c++ ? I tried a javascript debugger and firebug. They don't help but i'm probably not using them right..
EDIT2: I tried this greasemonkey script : https://userscripts.org/scripts/show/92424. It does not work so i guess it isn't because of 'target' attribute
This is wrong. I'm guessing you're using a plugin to capture and override the output some site gives you. I'm pretty sure this violates their ToS and it's not a very nice thing to do in general.
JavaScript is not designed to allow this kind of meddling. It's patchy at best.
If you want to use the data from a website, to aggregate or display in some manner, use their public API. If they don't have a public API they probably don't want you to use their service in such a manner.
The solution : I took the script from Stop execution of Javascript function (client side) or tweak it and modified it to search for the tag that has in it top.location = location and then appended a new script with the if (top != self) {top.location = location;} line commented . Being a js total newbie i don't know if it's the most elegant choice but it soves the prob. Special thanks to Tim Fountain.
I will leave this open just in case someone else will suggest a better solution, for my and others's education. Again thanks for the help.
Below is the code:
// ==UserScript==
// #name _Replace evil Javascript
// #run-at document-start
// ==/UserScript==
/****** New "init" function that we will use
instead of the old, bad "init" function.
*/
function init () {
/* //changing stuff around here
var newParagraph = document.createElement ('p');
newParagraph.textContent = "I was added by the new, good init() function!";
document.body.appendChild (newParagraph); */
<!--//--><![CDATA[//><!--
document.getElementsByTagName("html")[0].className+=" js "+(navigator.userAgent.toLowerCase().indexOf("webkit")>=0?"webkit":navigator.userAgent.toLowerCase().indexOf("opera")>=0?"opera":"");
for(i in css3_tags="abbr|header|footer".split("|")){document.createElement(css3_tags[i]);}
var PATH = "http://www.shopmania.com";
var PATH_STATIC = "http://im4.shopmania.org";
var PATH_SELF = "http://www.shopmania.com/";
var RETURN = "http%3A%2F%2Fwww.shopmania.com%2F";
var DOMAIN_BASE = "shopmania.com";
var SUBDOMAINS_FORCE_FILES_JS = "aff.remote,biz.remote,my.remote,cp.remote,cp.register_quick,cp.account_details,partner.remote,site.recommend,site.remote,site.feedback,site.report_problem,site.report,site.cropper";
var URL_REWRITE_MAPPING_JS = "cmd,section,do,option|feed,mode,option|forgot,section|info,page|login,section|logout,section|new_password,section,code|settings,section|shopping,param_main,param_sec|site,store_str_key|register,section|unsubscribe,section|agentie,store_str_key,id|brand,manuf_str_key|brands,letter|build,type,param_main,param_sec|compare,online|confirm,section|edit,section|deal,deal|dictionary,online|home,section|link_accounts,section|profile,user|reactivate,section|searches,letter|signup,section|rs_agent,store_str_key|rs_list,param_main,param_sec|rs_view,ad|agents,state|complex_list,param_main|complex_view,complex|list,cat|ad,a|map,option|my_ads,section|my_alerts,section";
var SVR_SITE_ID = "us";
var CONTEXT = "c5b27de70340c97a94092a43bd34b2b8";
var link_close = "Close";
var txt_loading = "Loading...";
var form_is_submitted = 0;
var search_is_focused = 0;
// Overlay object
var OL;
var DB;
var iframe_cnt = "";
// Facebook post to user's Wall action
var FACEBOOK_WALL_FEED_SIGNUP = "";
var SITENAME = "ShopMania";
//if (top != self) {top.location = location;} // SIT!
var comps = new Array(); comps['all'] = 0;var comps_cat_titles = new Array(); var views = new Array(); views['auto'] = 0; views['prod'] = 0; views['realestate'] = 0; views['classifieds'] = 0; views['all'] = 0; var search = new Array(); search['all'] = 0; search['prod'] = 0;
var favs = new Array(); favs['all'] = 0; favs['prod'] = 0; favs['store'] = 0; favs['manuf'] = 0; favs['other'] = 0; favs['realestate'] = 0; favs['auto'] = 0;
function addCss(c){var b=document.getElementsByTagName("head")[0];var a=document.createElement("style");a.setAttribute("type","text/css");if(a.styleSheet){a.styleSheet.cssText=c}else{a.appendChild(document.createTextNode(c))}b.appendChild(a)};
addCss(".lzl {visibility: hidden;}");
var RecaptchaOptions = { theme : 'clean' };
//--><!]]>
}
/*--- Check for bad scripts to intercept and specify any actions to take.
*/
checkForBadJavascripts ( [
[false, /top.location = location/, function () {addJS_Node (init);} ]
] );
function checkForBadJavascripts (controlArray) {
/*--- Note that this is a self-initializing function. The controlArray
parameter is only active for the FIRST call. After that, it is an
event listener.
The control array row is defines like so:
[bSearchSrcAttr, identifyingRegex, callbackFunction]
Where:
bSearchSrcAttr True to search the SRC attribute of a script tag
false to search the TEXT content of a script tag.
identifyingRegex A valid regular expression that should be unique
to that particular script tag.
callbackFunction An optional function to execute when the script is
found. Use null if not needed.
*/
if ( ! controlArray.length) return null;
checkForBadJavascripts = function (zEvent) {
for (var J = controlArray.length - 1; J >= 0; --J) {
var bSearchSrcAttr = controlArray[J][0];
var identifyingRegex = controlArray[J][1];
if (bSearchSrcAttr) {
if (identifyingRegex.test (zEvent.target.src) ) {
stopBadJavascript (J);
return false;
}
}
else {
if (identifyingRegex.test (zEvent.target.textContent) ) {
stopBadJavascript (J);
return false;
}
}
}
function stopBadJavascript (controlIndex) {
zEvent.stopPropagation ();
zEvent.preventDefault ();
var callbackFunction = controlArray[J][2];
if (typeof callbackFunction == "function")
callbackFunction ();
//--- Remove the node just to clear clutter from Firebug inspection.
zEvent.target.parentNode.removeChild (zEvent.target);
//--- Script is intercepted, remove it from the list.
controlArray.splice (J, 1);
if ( ! controlArray.length) {
//--- All done, remove the listener.
window.removeEventListener (
'beforescriptexecute', checkForBadJavascripts, true
);
}
}
}
/*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
Note that it does not work on acripts that are dynamically created.
*/
window.addEventListener ('beforescriptexecute', checkForBadJavascripts, true);
return checkForBadJavascripts;
}
function addJS_Node (text, s_URL, funcToRun) {
var D = document;
var scriptNode = D.createElement ('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
//--- Don't error check here. if DOM not available, should throw error.
targ.appendChild (scriptNode);
}
there are some escaping issues with the cdata part in the code.So SO does not allow me to post the code.
EDIT: fixed