Click or scroll down Circle me on Google+ Fork me on GitHub Follow me on Twitter Ask me on Stack Overflow Gild me on Reddit Code Ninja, Entrepreneur, Idiot ChalkHQ — consulting, prototyping, mentoring HighF.in — resolve innefficiencies in your startup's workflow DearDrum.org — online open-mic / creative space The Dirac Equation (click to WikiPedia) A maxim Sun Tzu references in his magnum opus The Art of War

If you know the enemy and know yourself, you need not fear the result of a hundred battles.
If you know yourself but not the enemy, for every victory gained you will also suffer a defeat.
If you know neither the enemy nor yourself, you will succumb in every battle.
Fork me on GitHub

Tags

actionscript ad-hoc networks Adobe AIR anonymous Apple array Browsing convert Debugger Error Facebook file permissions Flash Flex fonts function future Google Google Plus grid hackers html javascript logs loop network p2p php privacy regexp Security Server social ssl technology terminal time Twitter upgrade Web 2.0 Web 3.0 Web 4.0 Web 5.0 wordpress

Featured Posts

  • Javascript: Undefined parameters
  • The Web, A Look Forward
  • Let Postfix send mail through your Gmail Account – Snow Leopard
  • Archives

  • April 2013
  • December 2011
  • July 2011
  • June 2011
  • March 2011
  • February 2011
  • January 2011
  • November 2010
  • October 2010
  • September 2010
  • July 2010
  • May 2010
  • Categories

  • Code
  • Design
  • Opinion
  • Security
  • Tools
  • Uncategorized
  • Tag: function

    Flex: Variables, Anonymous Functions, and For Loops

    I just ran into some weird behaviour involving a for loop, some variables, and a bunch ofanonymous functions. This is in Actionscript 3.0 using Flex SDK 3.4 and current Google Maps API(as of the date of this post&mdash I read somewhere they're rolling out a new version although it's not really relevant for this post)

    So below I have a function that loops through the xml result of an http service, for each item in the result it creates a marker on a map and gives that marker a click event. When you click on a given marker I want a window to pop up with the name and description of that location, so the following is the code you'd expect to write. For simplicity sake you can keep an eye on the i:int variable which will help clarify the issue.

     
    //trace(i) will always output total items in the xml result
    private function processResult(event:ResultEvent):void {
    
      var total:int = event.result.data.item.length;
    
      for (var i:int = 0; i<total; i++) {
        var item:Object = event.result.data.item[i];
        //this will create the marker object
        var marker = new Marker(new LatLng(item.lat, item.lng), new MarkerOptions({fillStyle: {color: 0xEE9C21}, radius: 7, tooltip: item.name}));
    
        marker.addEventListener(MapMouseEvent.CLICK, function():void {
          //this will open an info window when the marker is clicked
          map.openInfoWindow(map.getCenter(), new InfoWindowOptions({hasTail: true, tailHeight: 5, hasShadow: true, title:item.name, contentHTML:item.description}));
          trace(i);
        	});
      map.addOverlay(marker);
      }
    }

    Now what you'll find with the above code is that no matter which placemark you click on, they will all show the same name and description. Say that there are 5 items in the xml result, tracing i will output the number 5.

    If you're new to programming, yes i will be 0 during the for loop's first run. Yes having 5 items and starting at 0 means it should be 4 for the last run, but the value of i increments one last time to make the i<totalcondition false before it exits the loop, so essentially it uses the final value of i for all the placemarks which is 5.

    I can't see any reason why this should be happening other than language or framework immaturity.

    The solution; or I should say the easiest, quickest solution, is to create an external function for marker creation that is called by the for loop, which for clarity's sake will only contain the part that's required to explain the concept and make it work ie: adding an event listener to the marker, but in the real world should have all the code necessary for creating a marker - that way you'd have an independent marker creation function you could call from anywhere in the application. Below is the working code:

     
    //trace(i) will output the correct index depending on the placemark clicked
    private function processResult(event:ResultEvent):void {
    
      var total:int = event.result.data.item.length;
    
      for (var i:int = 0; i<total; i++) {
    
        var item:Object = event.result.data.item[i];
        var marker = new Marker(new LatLng(item.lat, item.lng), new MarkerOptions({fillStyle: {color: 0xEE9C21}, radius: 7, tooltip: item.name}));
    
        //call external function and pass variables to it
        placeMarkerAddClickEventListener(marker, item.name, item.description);
        map.addOverlay(marker);
      }
    }
    
    //external function
    private function placeMarkerAddClickEventListener(marker:Marker, name:String, description:String):void {
    
      marker.addEventListener(MapMouseEvent.CLICK, function():void {
    
        map.openInfoWindow(map.getCenter(), new InfoWindowOptions({hasTail: true, tailHeight: 5, hasShadow: true, title:name, contentHTML:description}));
        });
    }

     

    Convert Milliseconds to Time (H:M:S)

    Converting milliseconds to a time string can be a pain, especially when you're measuring something dynamic. In most languages I've come accross the Date object is calculated by the number of milliseconds that have passed since Jan 1, 1970. Because the different parts of a date are based on different bases ie: 60 minutes in an hour, 24 hours in a day etc. It's a lot easier to get the milliseconds passed since Jan1, 1970 and work with that value. This only works if you want to get the difference between two dates. If you were to subtract two dates(expressed as milliseconds) and get 432000000 milliseconds(5 days), then convert that to a Date object, the code would interpret 432000000 as Jan 6, 1970.

    In another scenario I was just writing a podcast player in Flex 3/AIR and wanted to convert the Sound.length and SoundChannel.position values, both of which are measured in milliseconds, and display the length and current position of the episode in formats that would make sense. So I wrote a generic function that accepts millisecondsas an argument and returns the formatted time string.

      /**    Milliseconds to Time String in Flex 3              **/
      /**    Author: Yoav Givati [http://fightskillz.com]       **/
    
    public function fnMillisecondsToTimeCountUp(time:Number):String {
    
    	//calculate playtime from milliseconds
    	var h:Number = new Number(Math.floor(time/1000/60/60));
    	//minutes left shows total minutes left plus hours, 1h5m = 65mins
    	//so we subtract the amount of 60's added by the hours to get just minutes
    	var m:Number = new Number(Math.floor(time/1000/60)-(h*60));
    	//seconds left
    	var s:Number = new Number(Math.floor(time/1000)-(m*60));
    
    	//create string variables
    	var hours:String;
    	var minutes:String;
    	var seconds:String
    
    	//make sure minutes and seconds are always two digits
    	if(m.toString().length == 1) {
    		 minutes = "0"+m;
    	} else {
    		 minutes = m.toString();
    	}
    
    	if(s.toString().length == 1) {
    		seconds = "0"+s;
    	} else {
    		seconds = s.toString();
    	}
    
    	//if hours or minutes are 0 we don't need to see them
    	if(h == 0) {
    		hours = '';
    		if(m == 0) {
    			minutes = '';
    		} else {
    			minutes = minutes+":";
    		}
    	} else {
    		hours = h+":"
    		minutes = minutes+":";
    	}
    
    	// after 1 hour passes the seconds become 4 digits long
    	// the last two of those digits represent the actual seconds
    	seconds = seconds.slice(seconds.length-2, seconds.length);
    	return hours+minutes+seconds;
    
    }

    You'll notice that I'm using Math.floor(), it's crucial that you round down, because the way the hours are being calculated for example, rounding up would show one hour had passed after only a fraction of an hour, just rounding up the minutes or seconds would cause everything to be out of sync and the math would be concussed. For those of you who are confused I should clarify that Math.floor(1.8) would return a value of 1 and Math.ceil(1.3) would return a value of 2, the term 'round' is probably a misleading. If you were using this function to count down instead of up, you would useMath.ceil()(although still not for the hour value), you essentially want to stay on the 'other side' of the minute or second for as long as possible.