-== Welcome to Rails
-Rails is a web-application and persistence framework that includes everything
-needed to create database-backed web-applications according to the
-Model-View-Control pattern of separation. This pattern splits the view (also
-called the presentation) into "dumb" templates that are primarily responsible
-for inserting pre-built data in between HTML tags. The model contains the
-"smart" domain objects (such as Account, Product, Person, Post) that holds all
-the business logic and knows how to persist themselves to a database. The
-controller handles the incoming requests (such as Save New Account, Update
-Product, Show Post) by manipulating the model and directing data to the view.
+Task one
+========
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
+Get OSM on rails with a 0.4 API, without changing the db schema
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
+see db/README for how to create the db
+Two
+===
-== Getting started
+change the schema
-1. Start the web server: <tt>ruby script/server</tt> (run with --help for options)
-2. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!"
-3. Follow the guidelines to start developing your application
-
-
-== Web servers
-
-Rails uses the built-in web server in Ruby called WEBrick by default, so you don't
-have to install or configure anything to play around.
-
-If you have lighttpd installed, though, it'll be used instead when running script/server.
-It's considerably faster than WEBrick and suited for production use, but requires additional
-installation and currently only works well on OS X/Unix (Windows users are encouraged
-to start with WEBrick). We recommend version 1.4.11 and higher. You can download it from
-http://www.lighttpd.net.
-
-If you want something that's halfway between WEBrick and lighttpd, we heartily recommend
-Mongrel. It's a Ruby-based web server with a C-component (so it requires compilation) that
-also works very well with Windows. See more at http://mongrel.rubyforge.org/.
-
-But of course its also possible to run Rails with the premiere open source web server Apache.
-To get decent performance, though, you'll need to install FastCGI. For Apache 1.3, you want
-to use mod_fastcgi. For Apache 2.0+, you want to use mod_fcgid.
-
-See http://wiki.rubyonrails.com/rails/pages/FastCGI for more information on FastCGI.
-
-== Example for Apache conf
-
- <VirtualHost *:80>
- ServerName rails
- DocumentRoot /path/application/public/
- ErrorLog /path/application/log/server.log
-
- <Directory /path/application/public/>
- Options ExecCGI FollowSymLinks
- AllowOverride all
- Allow from all
- Order allow,deny
- </Directory>
- </VirtualHost>
-
-NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI
-should be on and ".cgi" should respond. All requests from 127.0.0.1 go
-through CGI, so no Apache restart is necessary for changes. All other requests
-go through FCGI (or mod_ruby), which requires a restart to show changes.
-
-
-== Debugging Rails
-
-Have "tail -f" commands running on both the server.log, production.log, and
-test.log files. Rails will automatically display debugging and runtime
-information to these files. Debugging info will also be shown in the browser
-on requests from 127.0.0.1.
-
-
-== Breakpoints
-
-Breakpoint support is available through the script/breakpointer client. This
-means that you can break out of execution at any point in the code, investigate
-and change the model, AND then resume execution! Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.find_all
- breakpoint "Breaking out from the list"
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the breakpointer window. Here you can do things like:
-
-Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
-
- >> @posts.inspect
- => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
- #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
- >> @posts.first.title = "hello from a breakpoint"
- => "hello from a breakpoint"
-
-...and even better is that you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you press CTRL-D
-
-
-== Console
-
-You can interact with the domain model by starting the console through script/console.
-Here you'll have all parts of the application configured, just like it is when the
-application is running. You can inspect domain models, change values, and save to the
-database. Starting the script without arguments will launch it in the development environment.
-Passing an argument will specify a different environment, like <tt>script/console production</tt>.
-
-
-== Description of contents
-
-app
- Holds all the code that's specific to this particular application.
-
-app/controllers
- Holds controllers that should be named like weblog_controller.rb for
- automated URL mapping. All controllers should descend from
- ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb.
- Most models will descend from ActiveRecord::Base.
-
-app/views
- Holds the template files for the view that should be named like
- weblog/index.rhtml for the WeblogController#index action. All views use eRuby
- syntax. This directory can also be used to keep stylesheets, images, and so on
- that can be symlinked to public.
-
-app/helpers
- Holds view helpers that should be named like weblog_helper.rb.
-
-app/apis
- Holds API classes for web services.
-
-config
- Configuration files for the Rails environment, the routing map, the database, and other dependencies.
-
-components
- Self-contained mini-applications that can bundle together controllers, models, and views.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all
- the sequence of Migrations for your schema.
-
-lib
- Application specific libraries. Basically, any kind of custom code that doesn't
- belong under controllers, models, or helpers. This directory is in the load path.
-
-public
- The directory available for the web server. Contains subdirectories for images, stylesheets,
- and javascripts. Also contains the dispatchers and the default HTML files.
-
-script
- Helper scripts for automation and generation.
-
-test
- Unit and functional tests along with fixtures.
-
-vendor
- External libraries that the application depends on. Also includes the plugins subdirectory.
- This directory is in the load path.
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
- <head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
- <title>Ruby on Rails: Welcome aboard</title>
- <style type="text/css" media="screen">
- body {
- margin: 0;
- margin-bottom: 25px;
- padding: 0;
- background-color: #f0f0f0;
- font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
- font-size: 13px;
- color: #333;
- }
-
- h1 {
- font-size: 28px;
- color: #000;
- }
-
- a {color: #03c}
- a:hover {
- background-color: #03c;
- color: white;
- text-decoration: none;
- }
-
-
- #page {
- background-color: #f0f0f0;
- width: 750px;
- margin: 0;
- margin-left: auto;
- margin-right: auto;
- }
-
- #content {
- float: left;
- background-color: white;
- border: 3px solid #aaa;
- border-top: none;
- padding: 25px;
- width: 500px;
- }
-
- #sidebar {
- float: right;
- width: 175px;
- }
- #footer {
- clear: both;
- }
-
+nuffin here
- #header, #about, #getting-started {
- padding-left: 75px;
- padding-right: 30px;
- }
- #header {
- background-image: url("images/rails.png");
- background-repeat: no-repeat;
- background-position: top left;
- height: 64px;
- }
- #header h1, #header h2 {margin: 0}
- #header h2 {
- color: #888;
- font-weight: normal;
- font-size: 16px;
- }
-
-
- #about h3 {
- margin: 0;
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- #about-content {
- background-color: #ffd;
- border: 1px solid #fc0;
- margin-left: -11px;
- }
- #about-content table {
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 11px;
- border-collapse: collapse;
- }
- #about-content td {
- padding: 10px;
- padding-top: 3px;
- padding-bottom: 3px;
- }
- #about-content td.name {color: #555}
- #about-content td.value {color: #000}
-
- #about-content.failure {
- background-color: #fcc;
- border: 1px solid #f00;
- }
- #about-content.failure p {
- margin: 0;
- padding: 10px;
- }
-
-
- #getting-started {
- border-top: 1px solid #ccc;
- margin-top: 25px;
- padding-top: 15px;
- }
- #getting-started h1 {
- margin: 0;
- font-size: 20px;
- }
- #getting-started h2 {
- margin: 0;
- font-size: 14px;
- font-weight: normal;
- color: #333;
- margin-bottom: 25px;
- }
- #getting-started ol {
- margin-left: 0;
- padding-left: 0;
- }
- #getting-started li {
- font-size: 18px;
- color: #888;
- margin-bottom: 25px;
- }
- #getting-started li h2 {
- margin: 0;
- font-weight: normal;
- font-size: 18px;
- color: #333;
- }
- #getting-started li p {
- color: #555;
- font-size: 13px;
- }
-
-
- #search {
- margin: 0;
- padding-top: 10px;
- padding-bottom: 10px;
- font-size: 11px;
- }
- #search input {
- font-size: 11px;
- margin: 2px;
- }
- #search-text {width: 170px}
-
-
- #sidebar ul {
- margin-left: 0;
- padding-left: 0;
- }
- #sidebar ul h3 {
- margin-top: 25px;
- font-size: 16px;
- padding-bottom: 10px;
- border-bottom: 1px solid #ccc;
- }
- #sidebar li {
- list-style-type: none;
- }
- #sidebar ul.links li {
- margin-bottom: 5px;
- }
-
- </style>
- <script type="text/javascript" src="javascripts/prototype.js"></script>
- <script type="text/javascript" src="javascripts/effects.js"></script>
- <script type="text/javascript">
- function about() {
- if (Element.empty('about-content')) {
- new Ajax.Updater('about-content', 'rails/info/properties', {
- method: 'get',
- onFailure: function() {Element.classNames('about-content').add('failure')},
- onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
- });
- } else {
- new Effect[Element.visible('about-content') ?
- 'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
- }
- }
-
- window.onload = function() {
- $('search-text').value = '';
- $('search').onsubmit = function() {
- $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
- }
- }
- </script>
- </head>
- <body>
- <div id="page">
- <div id="sidebar">
- <ul id="sidebar-items">
- <li>
- <form id="search" action="http://www.google.com/search" method="get">
- <input type="hidden" name="hl" value="en" />
- <input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
- <input type="submit" value="Search" /> the Rails site
- </form>
- </li>
-
- <li>
- <h3>Join the community</h3>
- <ul class="links">
- <li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
- <li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
- <li><a href="http://lists.rubyonrails.org/">Mailing lists</a></li>
- <li><a href="http://wiki.rubyonrails.org/rails/pages/IRC">IRC channel</a></li>
- <li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
- <li><a href="http://dev.rubyonrails.org/">Bug tracker</a></li>
- </ul>
- </li>
-
- <li>
- <h3>Browse the documentation</h3>
- <ul class="links">
- <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
- <li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
- <li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
- </ul>
- </li>
- </ul>
- </div>
-
- <div id="content">
- <div id="header">
- <h1>Welcome aboard</h1>
- <h2>You’re riding the Rails!</h2>
- </div>
-
- <div id="about">
- <h3><a href="rails/info/properties" onclick="about(); return false">About your application’s environment</a></h3>
- <div id="about-content" style="display: none"></div>
- </div>
-
- <div id="getting-started">
- <h1>Getting started</h1>
- <h2>Here’s how to get rolling:</h2>
-
- <ol>
- <li>
- <h2>Create your databases and edit <tt>config/database.yml</tt></h2>
- <p>Rails needs to know your login and password.</p>
- </li>
-
- <li>
- <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
- <p>To see all available options, run it without parameters.</p>
- </li>
-
- <li>
- <h2>Set up a default route and remove or rename this file</h2>
- <p>Routes are setup in config/routes.rb.</p>
- </li>
- </ol>
- </div>
- </div>
-
- <div id="footer"> </div>
- </div>
- </body>
-</html>
\ No newline at end of file
--- /dev/null
+// June 20th 2005 http://civicactions.net anselm@hook.org public domain version 0.5
+
+//
+// miscellaneous
+//
+
+var netscape = ( document.getElementById && !document.all ) || document.layers;
+var defaultEngine = null; // xxx for firefox keyboard events.
+
+var PI = 3.14159265358979323846;
+
+var lat_range = PI, lon_range = PI;
+
+//
+// Utility - get div position - may not be accurate
+//
+
+function getCSSPositionX(parent)
+{
+ var offset = parent.x ? parseInt(parent.x) : 0;
+ offset += parent.style.left ? parseInt(parent.style.left) : 0;
+ for(var node = parent; node ; node = node.offsetParent )
+ {
+ offset += node.offsetLeft;
+ }
+ return offset;
+}
+
+function getCSSPositionY(parent)
+{
+ var offset = parent.y ? parseInt(parent.y) : 0;
+ offset += parent.style.top ? parseInt(parent.style.top) : 0;
+ for(var node = parent; node ; node = node.offsetParent )
+ {
+ offset += node.offsetTop;
+ }
+ return offset;
+}
+
+///
+/// initialize a new tile engine object
+/// usage: var engine = new tile_engine_new(parentdiv,stylehints,wmssource,lon,lat,zoom,optional width, optional height)
+///
+function tile_engine_new(parentname,hints,feedurl,url,lon,lat,zoom,w,h)
+{
+ // NW geocoder removed for now
+
+ this.timestamp = new Date().getTime();
+ this.urlAttr = new Array();
+
+ // NW Removed navigation buttons entirely for flexibility
+
+ this.lonPerPixel = function()
+ { return (this.lon_quant/this.scale)/this.tilewidth; }
+
+ this.latPerPixel = function()
+ { return (this.lat_quant/this.scale)/this.tileheight; }
+
+ this.xToLon = function(x)
+ { return this.lon + (x-this.thewidth/2)*this.lonPerPixel(); }
+
+ this.yToLat = function(y)
+ { return normallat(this.lat - (y-this.theheight/2)
+ *this.latPerPixel()); }
+
+ this.lonToX = function (lon)
+ { return ((lon-this.lon)/this.lonPerPixel()) + this.thewidth/2;}
+
+ this.latToY = function(lat)
+ { return ((this.lat-mercatorlat(lat))/this.latPerPixel()) +
+ this.theheight/2; }
+
+
+ //
+ // it is possible that this collection is already in use - clean it
+ //
+ this.clean = function()
+ {
+ /*
+ while( this.parent.hasChildNodes() )
+ this.parent.removeChild( this.parent.firstChild );
+ */
+
+ for(var ct=0; ct<this.parent.childNodes.length; ct++)
+ {
+ if(this.parent.childNodes[ct].id != "controls")
+ this.parent.removeChild(this.parent.childNodes[ct]);
+ }
+
+ //
+ // build inner tile container for theoretical speed improvement?
+ // center in parent for simplicity of math
+ // size of inner container is irrelevant since overflow is enabled
+ //
+
+ if( this.dragcontainer )
+ {
+ this.tiles = document.createElement('div');
+ this.tiles.style.position = 'absolute';
+ this.tiles.style.left = this.displaywidth/2 + 'px';
+ this.tiles.style.top = this.displayheight/2 + 'px';
+ this.tiles.style.width = '16px';
+ this.tiles.style.height = '16px';
+ if( this.debug )
+ {
+ this.tiles.style.border = 'dashed green 1px';
+ }
+ this.tiles.tile_engine = this;
+ this.parent.appendChild(this.tiles);
+
+ }
+ else
+ {
+ this.tiles = this.parent;
+ }
+ }
+
+ /// focus over specified lon/lat and zoom
+ /// user should call this.drag(0,0) after this to force an initial refresh
+ ///
+
+ this.performzoom = function(lon,lat,z)
+ {
+ // setup for zoom
+ // this engine operates at * scale to try avoid tile errors thrashing
+ // server cache
+
+ this.scale = 1000000;
+ this.lon_min_clamp = -180 * this.scale;
+ this.lon_max_clamp = 180 * this.scale;
+ this.lat_min_clamp = -180 * this.scale; //t
+ this.lat_max_clamp = 180 * this.scale; //t
+ this.lon_start_tile = 180 * this.scale;
+ this.lat_start_tile = 90 * this.scale; //t
+ this.zoom_power = 2;
+ this.lon_quant = this.lon_start_tile;
+ this.lat_quant = this.lat_start_tile;
+ this.lon = lon;
+ this.lat = lat;
+
+ // operational lat - = lat due to quirks in our engine and quirks in o
+ // lon/lat design
+ lat = -lat;
+
+ // divide tile size until reach requested zoom
+ // trying to guarantee consistency so as to not thrash the server side tile cache
+ while(z > 0)
+ {
+ this.lon_quant = this.lon_quant / this.zoom_power;
+ this.lat_quant = this.lat_quant / this.zoom_power;
+ z--;
+ }
+ this.lon_quant = Math.round( this.lon_quant );
+ this.lat_quant = Math.round( this.lat_quant );
+
+ // get user requested exact lon/lat
+ this.lon_scaled = Math.round( lon * this.scale );
+ this.lat_scaled = Math.round( lat * this.scale );
+
+
+
+ // convert requested exact lon/lat to quantized lon lat (rounding down
+ // or up as best suits)
+ this.lon_round = Math.round( this.lon_scaled / this.lon_quant ) *
+ this.lon_quant;
+ this.lat_round = Math.round( this.lat_scaled / this.lat_quant ) *
+ this.lat_quant;
+
+ //alert('lon_round=' + this.lon_round+ ' lat_round='+this.lat_round);
+
+ // calculate world extents [ this is the span of all tiles in lon/lat ]
+ this.lon_min = this.lon_round - this.lon_quant;
+ this.lon_max = this.lon_round + this.lon_quant;
+ this.lat_min = this.lat_round - this.lat_quant;
+ this.lat_max = this.lat_round + this.lat_quant;
+
+ // set tiled region details [ this is the span of all tiles in pixels ]
+ this.centerx = 0;
+ this.centery = 0;
+ this.tilewidth = 256;
+ this.tileheight = 128;
+ this.left = -this.tilewidth;
+ this.right = this.tilewidth;
+ this.top = -this.tileheight;
+ this.bot = this.tileheight;
+
+
+ // adjust the current center position slightly to reflect exact lat/lon
+ // not rounded
+ this.centerx -= (this.lon_scaled-this.lon_round)/
+ (this.lon_max-this.lon_min)*(this.right-this.left);
+ this.centery -= (this.lat_scaled-this.lat_round)/
+ (this.lat_max-this.lat_min)*(this.bot-this.top);
+ }
+
+ this.update_perma_link = function() {
+ // because we're using mercator
+ updatelinks(this.lon,normallat(this.lat),this.zoom);
+ }
+
+ ///
+ /// draw the spanning lon/lat range
+ /// drag is simply the mouse delta in pixels
+ ///
+
+ this.drag = function(dragx,dragy)
+ {
+ var fred=true;
+
+ // move the drag offset
+ this.centerx += dragx;
+ this.centery += dragy;
+
+ // update where we think the user is actually focused
+ this.lon = ( this.lon_round - ( this.lon_max - this.lon_min ) /
+ ( this.right - this.left ) * this.centerx ) / this.scale;
+ this.lat = - ( this.lat_round - ( this.lat_max - this.lat_min ) /
+ ( this.bot - this.top ) * this.centery ) / this.scale;
+
+ this.update_perma_link();
+
+ // show it
+ var helper = this.navhelp;
+
+ // extend exposed sections
+ var dirty = false;
+ while( this.left + this.centerx > -this.displaywidth/2 &&
+ this.lon_min > this.lon_min_clamp )
+ {
+ this.left -= this.tilewidth;
+ this.lon_min -= this.lon_quant;
+ dirty = true;
+ }
+ while( this.right + this.centerx < this.displaywidth/2 &&
+ this.lon_max < this.lon_max_clamp )
+ {
+ this.right += this.tilewidth;
+ this.lon_max += this.lon_quant;
+ dirty = true;
+ }
+ while( this.top + this.centery > -this.displayheight/2 &&
+ this.lat_min > this.lat_min_clamp )
+ {
+ this.top -= this.tileheight;
+ this.lat_min -= this.lat_quant;
+ dirty = true;
+ }
+
+ while( this.bot + this.centery < this.displayheight/2 &&
+ this.lat_max < this.lat_max_clamp )
+ {
+ this.bot += this.tileheight;
+ this.lat_max += this.lat_quant;
+ dirty = true;
+ }
+
+
+ // prepare to walk the container and assure that all nodes are correct
+ var containerx;
+ var containery;
+
+ // in drag container mode we do not have to move the children all the
+ // time
+ if( this.dragcontainer )
+ {
+ this.tiles.style.left = this.displaywidth / 2 + this.centerx + 'px';
+ this.tiles.style.top = this.displayheight / 2 + this.centery + 'px';
+ if( !dirty && this.tiles.hasChildNodes() )
+ {
+ return;
+ }
+ containerx = this.left;
+ containery = this.top;
+ }
+ else
+ {
+ containerx = this.left + this.centerx;
+ containery = this.top + this.centery;
+ }
+
+ // walk all tiles and repair as needed
+ // xxx one bug is that it walks the _entire_ width and height...
+ // not just visible.
+ // xxx this makes cleanup harder and perhaps a bitmap is better
+
+ var removehidden = 1;
+ var removecolumn = 0;
+ var removerow = 0;
+ var containeryreset = containery;
+
+ for( var x = this.lon_min; x < this.lon_max ; x+= this.lon_quant )
+ {
+ // will this row be visible in the next round?
+ if( removehidden )
+ {
+ var rx = containerx + this.centerx;
+ if( rx > this.displaywidth / 2 )
+ {
+ removerow = 1;
+ // ideally i would truncate max width here
+ }
+ else if( rx + this.tilewidth < - this.displaywidth / 2 )
+ {
+ removerow = 1;
+ }
+ else
+ {
+ removerow = 0;
+ }
+ }
+
+ for( var y = this.lat_min; y < this.lat_max ; y+= this.lat_quant )
+ {
+ // is this column visible?
+ if( removehidden )
+ {
+ var ry = containery + this.centery;
+ if( ry > this.displayheight / 2 )
+ {
+ removecolumn = 1;
+ }
+ else if( ry + this.tileheight < - this.displayheight/2)
+ {
+ removecolumn = 1;
+ }
+ else
+ {
+ removecolumn = 0;
+ }
+ }
+
+ // convert to WMS compliant coordinate system
+ var lt = x / this.scale;
+ var rt = lt + this.lon_quant / this.scale;
+ var tp = y / this.scale;
+ var bt = tp + this.lat_quant / this.scale;
+ var temp = bt;
+ var bt = -tp;
+ var tp = -temp;
+
+ // modify for mercator-projected tiles:
+ tp = 180/PI * (2 * Math.atan(Math.exp(tp * PI / 180)) - PI / 2);
+ bt = 180/PI * (2 * Math.atan(Math.exp(bt * PI / 180)) - PI / 2);
+
+ // make a key
+ var key = this.url + "?WIDTH="+(this.tilewidth)+"&HEIGHT="+
+ (this.tileheight)+"&BBOX="+lt+","+tp+","+rt+","+bt;
+
+ // see if our tile is already present
+ var node = document.getElementById(key);
+
+ // create if not present
+ if(!node)
+ {
+ if( this.debug > 0)
+ {
+ node = document.createElement('div');
+ }
+ else
+ {
+ node = document.createElement('img');
+ }
+ node.id = key;
+ node.className = 'tile';
+ node.style.position = 'absolute';
+ node.style.width = this.tilewidth + 'px';
+ node.style.height = this.tileheight + 'px';
+ node.style.left = containerx + 'px';
+ node.style.top = containery + 'px';
+ node.style.zIndex = 10; // to appear under the rss elements
+ node.tile_engine = this;
+ if( this.debug > 0)
+ {
+ node.style.border = "1px solid yellow";
+ node.innerHTML = key;
+ if( this.debug > 1 )
+ {
+ var img = document.createElement('img');
+ img.src = key;
+ node.appendChild(img);
+ }
+ }
+
+ var goURL = key + "&zoom=" + this.zoom;
+
+ for(var k in this.urlAttr)
+ {
+ goURL += "&"+k+"="+this.urlAttr[k];
+ }
+
+ node.src = goURL;
+
+ node.alt = "loading tile..";
+ node.style.color = "#ffffff";
+ this.tiles.appendChild(node);
+ }
+ // adjust if using active style
+ else if( !this.dragcontainer ) {
+ node.style.left = containerx + 'px';
+ node.style.top = containery + 'px';
+ }
+
+ containery += this.tileheight;
+ }
+ containery = containeryreset;
+ containerx += this.tilewidth;
+ }
+ }
+
+ this.zoomTo = function(zoom)
+ {
+
+ this.zoom = zoom;
+
+ if (this.zoom < this.minzoom) { this.zoom = this.minzoom; }
+ if (this.zoom > this.maxzoom) { this.zoom = this.maxzoom; }
+
+
+
+
+ ///
+ /// immediately draw and or fit to feed
+ ///
+ this.performzoom(this.lon,this.lat,zoom);
+ this.drag(0,0);
+
+ ///
+ } // CLOSE ZOOM FUNCTION
+
+ this.setLatLon = function(lat,lon)
+ {
+ this.lon=lon;
+ this.lat=mercatorlat(lat);
+ this.clean();
+ this.performzoom(lon,mercatorlat(lat),this.zoom);
+ this.drag(0,0);
+ }
+
+ this.forceRefresh = function()
+ {
+ this.clean();
+ this.performzoom(this.lon,this.lat,this.zoom);
+ this.drag(0,0);
+ }
+
+ ///
+ /// zoom a tile group
+ ///
+ this.tile_engine_zoomout = function()
+ {
+ this.clean();
+ this.zoomTo(this.zoom-1);
+
+ return false; // or safari falls over
+ }
+
+ ///
+ /// zoom a tile group
+ ///
+ this.tile_engine_zoomin = function()
+ {
+
+ this.clean();
+ this.zoomTo(this.zoom+1);
+
+ return false; // or safari falls over
+ }
+
+ this.setURL=function(url) { this.url=url; }
+
+
+
+ ///
+ /// intercept context events to minimize out-of-browser interruptions
+ ///
+
+ this.event_context = function(e)
+ {
+ return false;
+ }
+
+ ///
+ /// keys
+ ///
+
+ this.event_key = function(e)
+ {
+ var key = 0;
+
+ var hostengine = defaultEngine;
+
+ if( window && window.event && window.event.srcElement ) {
+ hostengine = window.event.srcElement.tile_engine;
+ } else if( e.target ) {
+ hostengine = e.target.tile_engine;
+ } else if( e.srcElement ) {
+ hostengine = e.srcElement.tile_engine;
+ }
+
+ if( hostengine == null ) {
+ hostengine = defaultEngine;
+ if( hostengine == null ) {
+ return;
+ }
+ }
+
+
+ if( e == null && document.all ) {
+ e = window.event;
+ }
+
+ if( e ) {
+ if( e.keyCode ) {
+ key = e.keyCode;
+ }
+ else if( e.which ) {
+ key = e.which;
+ }
+
+ switch(key) {
+ case 97: // a = left
+ hostengine.drag(16,0);
+ break;
+ case 100: // d = right
+ hostengine.drag(-16,0);
+ break;
+ case 119: // w = up
+ hostengine.drag(0,16);
+ break;
+ case 120: // x = dn
+ hostengine.drag(0,-16);
+ break;
+ case 115: // s = center
+ new tile_engine_new(hostengine.parentname,
+ "FULL",
+ hostengine.feedurl, // xxx hrm, cache this?
+ hostengine.url,
+ hostengine.lon,
+ hostengine.lat,
+ hostengine.zoom,
+ 0,0
+ );
+ break;
+ case 122: // z = zoom
+ new tile_engine_new(hostengine.parentname,
+ "FULL",
+ hostengine.feedurl, // xxx hrm, cache this?
+ hostengine.url,
+ hostengine.lon,
+ hostengine.lat,
+ hostengine.zoom + 1,
+ 0,0
+ );
+ break;
+ case 99: // c = unzoom
+ new tile_engine_new(hostengine.parentname,
+ "FULL",
+ hostengine.feedurl, // xxx hrm, cache this?
+ hostengine.url,
+ hostengine.lon,
+ hostengine.lat,
+ hostengine.zoom - 1,
+ 0,0
+ );
+ break;
+ }
+ }
+ }
+
+ ///
+ /// catch mouse move events
+ /// this routine _must_ return false or else the operating system outside-of-browser-scope drag and drop handler will interfere
+ ///
+
+ this.event_mouse_move = function(e) {
+
+ var hostengine = null;
+ if( window && window.event && window.event.srcElement ) {
+ hostengine = window.event.srcElement.tile_engine;
+ } else if( e.target ) {
+ hostengine = e.target.tile_engine;
+ } else if( e.srcElement ) {
+ hostengine = e.srcElement.tile_engine;
+ }
+
+
+ if( hostengine && hostengine.drag ) {
+ if( hostengine.mousedown ) {
+ if( netscape ) {
+ hostengine.mousex = parseInt(e.pageX) + 0.0;
+ hostengine.mousey = parseInt(e.pageY) + 0.0;
+ } else {
+ hostengine.mousex = parseInt(window.event.clientX) + 0.0;
+ hostengine.mousey = parseInt(window.event.clientY) + 0.0;
+ }
+ hostengine.drag(hostengine.mousex-hostengine.lastmousex,hostengine.mousey-hostengine.lastmousey);
+ }
+ hostengine.lastmousex = hostengine.mousex;
+ hostengine.lastmousey = hostengine.mousey;
+ }
+
+ // must return false to prevent operating system drag and drop from handling events
+ return false;
+ }
+
+ ///
+ /// catch mouse down
+ ///
+
+ this.event_mouse_down = function(e) {
+
+ var hostengine = null;
+ if( window && window.event && window.event.srcElement ) {
+ hostengine = window.event.srcElement.tile_engine;
+ } else if( e.target ) {
+ hostengine = e.target.tile_engine;
+ } else if( e.srcElement ) {
+ hostengine = e.srcElement.tile_engine;
+ }
+
+
+ if( hostengine ) {
+ if( netscape ) {
+ hostengine.mousex = parseInt(e.pageX) + 0.0;
+ hostengine.mousey = parseInt(e.pageY) + 0.0;
+ } else {
+ hostengine.mousex = parseInt(window.event.clientX) + 0.0;
+ hostengine.mousey = parseInt(window.event.clientY) + 0.0;
+ }
+ hostengine.lastmousex = hostengine.mousex;
+ hostengine.lastmousey = hostengine.mousey;
+ hostengine.mousedown = 1;
+ }
+
+ // must return false to prevent operating system drag and drop from handling events
+ return false;
+ }
+
+ ///
+ /// catch double click (use to center map)
+ ///
+
+ this.event_double_click = function(e) {
+
+ var hostengine = null;
+ if( window && window.event && window.event.srcElement ) {
+ hostengine = window.event.srcElement.tile_engine;
+ } else if( e.target ) {
+ hostengine = e.target.tile_engine;
+ } else if( e.srcElement ) {
+ hostengine = e.srcElement.tile_engine;
+ }
+
+
+ if( hostengine ) {
+ if( netscape ) {
+ hostengine.mousex = parseInt(e.pageX) + 0.0;
+ hostengine.mousey = parseInt(e.pageY) + 0.0;
+ } else {
+ hostengine.mousex = parseInt(window.event.clientX) + 0.0;
+ hostengine.mousey = parseInt(window.event.clientY) + 0.0;
+ }
+ var dx = hostengine.mousex-(hostengine.displaywidth/2)-hostengine.parent_x;
+ var dy = hostengine.mousey-(hostengine.displayheight/2)-hostengine.parent_y;
+ hostengine.drag(-dx,-dy); // TODO smooth
+ }
+
+ // must return false to prevent operating system drag and drop from handling events
+ return false;
+
+ }
+
+ ///
+ /// catch mouse up
+ ///
+
+ this.event_mouse_up = function(e) {
+
+ var hostengine = null;
+ if( window && window.event && window.event.srcElement ) {
+ hostengine = window.event.srcElement.tile_engine;
+ } else if( e.target ) {
+ hostengine = e.target.tile_engine;
+ } else if( e.srcElement ) {
+ hostengine = e.srcElement.tile_engine;
+ }
+
+
+ if( hostengine ) {
+ if( netscape ) {
+ hostengine.mousex = parseInt(e.pageX) + 0.0;
+ hostengine.mousey = parseInt(e.pageY) + 0.0;
+ } else {
+ hostengine.mousex = parseInt(window.event.clientX) + 0.0;
+ hostengine.mousey = parseInt(window.event.clientY) + 0.0;
+ }
+ hostengine.mousedown = 0;
+ }
+
+ // must return false to prevent operating system drag and drop from handling events
+ return false;
+ }
+
+ ///
+ /// catch mouse out
+ ///
+
+ this.event_mouse_out = function(e) {
+
+ var hostengine = null;
+ if( window && window.event && window.event.srcElement ) {
+ hostengine = window.event.srcElement.tile_engine;
+ } else if( e.target ) {
+ hostengine = e.target.tile_engine;
+ } else if( e.srcElement ) {
+ hostengine = e.srcElement.tile_engine;
+ }
+
+
+ if( hostengine ) {
+ if( netscape ) {
+ hostengine.mousex = parseInt(e.pageX) + 0.0;
+ hostengine.mousey = parseInt(e.pageY) + 0.0;
+ } else {
+ hostengine.mousex = parseInt(window.event.clientX) + 0.0;
+ hostengine.mousey = parseInt(window.event.clientY) + 0.0;
+ }
+ hostengine.mousedown = 0;
+ }
+
+ // must return false to prevent operating system drag and drop from handling events
+ return false;
+ }
+
+
+ ///
+ /// register new handlers to catch desired events
+ ///
+
+ // NW removed parameter - always use parent
+ this.event_catch = function() {
+
+ this.parent.style.cursor = 'move';
+
+ if( netscape ) {
+ window.captureEvents(Event.MOUSEMOVE);
+ window.captureEvents(Event.KEYPRESS);
+ }
+
+ this.parent.onmousemove = this.event_mouse_move;
+ this.parent.onmousedown = this.event_mouse_down;
+ this.parent.onmouseup = this.event_mouse_up;
+ this.parent.onkeypress = this.event_key;
+ window.ondblclick = this.event_double_click;
+
+ if( window ) {
+ window.onmousemove = this.event_mouse_move;
+ window.onmouseup = this.event_mouse_up;
+ window.ondblclick = this.event_double_click;
+ }
+
+ }
+
+ this.setURLAttribute = function(k,v)
+ {
+ this.urlAttr[k] = v;
+ }
+
+ this.getURLAttribute = function(k)
+ {
+ return this.urlAttr[k];
+ }
+
+ this.getDownloadedTileBounds = function()
+ {
+ var bounds = new Array();
+ bounds.w=this.lon_min;
+ bounds.s=normallat(this.lat_min);
+ bounds.e=this.lon_max;
+ bounds.n=normallat(this.lat_max);
+ return bounds;
+ }
+
+ this.getVisibleBounds = function()
+ {
+ var bounds = new Array();
+ bounds.w = this.xToLon(0);
+ bounds.s = this.yToLat(this.theheight);
+ bounds.e = this.xToLon(this.thewidth);
+ bounds.n = this.yToLat(0);
+ return bounds;
+ }
+
+ // navout and navin stuff - START
+ // draw navigation buttons into the parent div
+
+ // ENTRY CODE BEGINS HERE....
+
+
+ // get parent div or fail
+ this.parent = document.getElementById(parentname);
+ if( this.parent == null ) {
+ alert('The tile map engine cannot find a parent container named ['
+ +parentname+']');
+ return;
+ }
+
+ //
+ // store for later
+ //
+
+ this.parentname = parentname;
+ this.hints = hints;
+ this.feedurl = feedurl;
+ this.url = url;
+ this.lon = lon;
+ this.lat = mercatorlat(lat);
+ this.thewidth = w;
+ this.theheight = h;
+ this.dragcontainer = 1;
+ this.debug = 0;
+
+ // for firefox keyboard
+ defaultEngine = this;
+ document.engine = this;
+
+
+ //
+ // decide on display width and height
+ //
+ if( !w || !h )
+ {
+ w = parseInt(this.parent.style.width);
+ h = parseInt(this.parent.style.height);
+ if(!w || !h)
+ {
+ w = 512;
+ h = 256;
+ this.parent.style.width = w + 'px';
+ this.parent.style.height = h + 'px';
+ }
+ }
+ else
+ {
+ this.parent.style.width = parseInt(w) + 'px';
+ this.parent.style.height = parseInt(h) + 'px';
+ }
+ this.displaywidth = w;
+ this.displayheight = h;
+
+ this.minzoom = 0;
+ this.maxzoom = 20;
+
+ //
+ // enforce parent div style?
+ // position absolute is really only required for firefox
+ // http://www.quirksmode.org/js/findpos.html
+ //
+
+ this.parent_x = getCSSPositionX(this.parent);
+ this.parent_y = getCSSPositionY(this.parent);
+
+ this.parent.style.position = 'relative';
+ this.parent.style.overflow = 'hidden';
+ this.parent.style.backgroundColor = '#000036';
+
+
+ // attach event capture parent div
+ this.event_catch();
+
+ this.clean();
+ //this.makeZoom();
+
+ this.zoomTo(zoom);
+}
+
+
+function normallat(mercatorlat)
+{
+ var tp = 180/PI*(2 * Math.atan(Math.exp(mercatorlat * PI / 180)) - PI / 2);
+ return tp;
+}
+
+function mercatorlat(normallat)
+{
+ var lpi = 3.14159265358979323846;
+ return Math.log( Math.tan( (lpi / 4.0) + (normallat / 180.0 * lpi / 2.0))) *
+ 180.0 / lpi ;
+}