Archive → February, 2010
Scriptaculous Draggable In a Div with “overflow:auto”
How many times does this happen to you? You have a Scriptaculous Draggable object and it is inside a <div> that has a style of “overflow:auto;”. You start dragging that Draggable around only to find that when you leave the bounds of it’s containing <div> the element disappears! What is a web developer to do?!
I had this very problem and started to tackle it myself by adding some code to the onStart and onEnd callbacks of my Draggable objects to move the Draggable to be a child of document.body temporarily while dragging. This was effective unless the the original div scrolled. There were some other issues as well, so I started searching around for a better approach. I found some discussion of it here:
http://dev.rubyonrails.org/ticket/5771
I then found a solution here:
http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/b614e856a9aa9d05/b89629e07d1a2f8c?#b89629e07d1a2f8c
The code below is the solution that I found as presented by Google Groups user Christophe Boulain:
function getDragElement(element) {
var el = element.cloneNode(true);
el.id = 'sub'+element.id;
el.style.position = 'relative';
document.body.appendChild(el);
return el;
}
var SubsDraggable = Class.create(Draggable, {
initialize:function($super, element) {
var options = arguments[2] || {};
$super(element,options);
if( typeof(this.options.dragelement) == 'undefined' )
this.options.dragelement = false;
},
initDrag:function(event) {
if(!Object.isUndefined(Draggable._dragging[this.element]) && Draggable._dragging[this.element])
return;
if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if((tag_name = src.tagName.toUpperCase()) && (
tag_name=='INPUT' ||
tag_name=='SELECT' ||
tag_name=='OPTION' ||
tag_name=='BUTTON' ||
tag_name=='TEXTAREA')
)
return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
// HERE are my modifications to calculate the new clone position. I'm not sure if there is an easier method, but this one seems to work.
var pos = this.element.cumulativeOffset();
var scroll = this.element.cumulativeScrollOffset();
var vpscroll = document.viewport.getScrollOffsets();
this.offset = [0,1].map( function(i) {
return (pointer[i] - pos[i] + scroll[i] - vpscroll[i])
});
Draggables.activate(this);
Event.stop(event);
}
},
startDrag: function($super, event) {
if( this.options.dragelement ){
this._originalElement = this.element;
this.element = this.options.dragelement(this.element);
Position.absolutize(this.element);
Position.clone(this._originalElement, this.element);
}
$super(event);
},
finishDrag: function($super, event, success) {
$super(event, success);
if(this.options.dragelement){
Element.remove(this.element);
this.element = this._originalElement;
this._originalElement = null;
}
}
});
To use it, initialize your Draggable like this:
new SubsDraggable('elementid', { dragelement: getDragElement, ...});
CodeIgniter’s Var Cache
I wrote a library for CodeIgniter called “Var_cache”. It a simple solution for quickly storing and retrieving a variable that does not need to regularly calculated (a typical example being the result of a COUNT(*) query). It is available here:
http://codeigniter.com/wiki/Var_Cache/
Finding Nearby Locations
Let’s say you have a list of 60 or so locations and you need a fast way to sort them by distance from a user’s current location. You might have a form where a user can enter their current address or zip code, etc. and you want it to output the different locations sorted by distance. Here’s one way to do that:
First, you will need a Google Maps API Key. That is very easy to obtain here: http://code.google.com/apis/maps/signup.html
Now that you have that, you have a couple of options. If you want to work on the client side (and assuming this is a web application), you can use Google’s Javascript API’s Geocoding Object. I am going to talk about doing it on the server side though…
Google also has a HTTP Geocoding Service which can be called as follows:
http://maps.google.com/maps/geo?q=1600+Amphitheatre+Parkway,+Mountain+View,+CA&output=json&sensor=true_or_false&key=your_api_key
In PHP, you can take the query and the result from this web service and get the result:
<?php
$query = $_POST['q']
$result = file_get_contents('http://maps.google.com/maps/geo?q='.urlencode($query).'&output=json&sensor=false&key=your_api_key')
?>
Now, parse the result for the geocode object. Note, the result can be request in json or other formats such as XML. Choose whichever fits your purpose.
At this point, you will also need to know the geocode of all of your locations. Assuming that you have them in a database, you can add a field called “geocode” or “lat” and “lon”. If you you do not have a particular geocode set for a certain location you can set it this one time using the web service and store it so it never has to be checked again. Obviously it would be a large operation to have to check each location for every search.
Assuming that you have all of the locations, run the Haversine formula on each location’s geocode and the entered location’s geocode from the web service. (Note: for much more accuracy you can use Vincenty’s formula, but that is not necessary in this application). Here is an implementation that I found on stackoverflow.com and have tested:
// pass the latitudes and longitudes in as degrees
function getDistance($lat1,$long1,$lat2,$long2)
{
$r = 3963.1; //3963.1 statute miles; 3443.9 nautical miles; 6378 km
$pi = pi();
// convert the degrees to radians
$lat1 = $lat1*($pi/180);
$lat2 = $lat2*($pi/180);
$long1 = $long1*($pi/180);
$long2 = $long2*($pi/180);
$ret = (acos(cos($lat1)*cos($long1)*cos($lat2)*cos($long2) + cos($lat1)*sin($long1)*cos($lat2)*sin($long2) + sin($lat1)*sin($lat2)) * $r) ;
return $ret;
}
Put each of those distances in an array, putting them either at the beginning or end of the array depending on whether they are greater than or less than the first or last item.
Then display to the user the corresponding stores.
Welcome to Developer Pages
I have had this domain name for while and I have finally decided to launch this blog. I think I would like it to be space where I can post various things I learn and/or stumble on and useful techniques in programming and development, to be able to share with others and remind myself in the future. It is my hope that others will find it useful, and make their own accounts on this page and start posting. If it becomes popular enough I can maybe earn an ad revenue, but that’s getting ahead of myself.
If you just found this page, please check back here in a few days for something more than this “Welcome” message. But also feel free to set up your own account on here and start publishing content.