source: subversion/applications/editors/potlatch/way.as @ 19957

Last change on this file since 19957 was 19957, checked in by richard, 10 years ago

duplicate nodes stuff

File size: 55.5 KB
Line 
1
2        // =====================================================================================
3        // OOP classes - OSMWay
4
5        // ---- Initialise
6       
7        function OSMWay() {
8                this.resetBBox();
9                this.path=new Array();                  // list of nodes
10                this.attr=new Array();                  // hash of tags
11                this.mergedways=new Array();    // list of ways merged into this
12                this.deletednodes=new Object(); // hash of nodes deleted from this
13        };
14
15        OSMWay.prototype=new MovieClip();
16        OSMWay.prototype.clean=true;                            // altered since last upload?
17        OSMWay.prototype.uploading=false;                       // currently uploading?
18        OSMWay.prototype.locked=false;                          // locked against upload?
19        OSMWay.prototype.version=0;                                     // version number?
20        OSMWay.prototype.uid=0;                                         // user ID (used for TIGER detection)
21        OSMWay.prototype.historic=false;                        // is this an undeleted, not-uploaded way?
22        OSMWay.prototype.checkconnections=false;        // check shared nodes on reload
23
24        // ---- Load from remote server
25
26        OSMWay.prototype.load=function() {
27                responder = function() { };
28                responder.onResult = function(result) {
29                        _root.waysreceived+=1;
30                        var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
31                        var w=result[0];
32                        if (length(result[1])==0) { removeMovieClip(_root.map.ways[w]); 
33                                                                                removeMovieClip(_root.map.areas[w]); return; }
34                        var i,id,x,y,prepoint,redrawws;
35                        _root.map.ways[w].clean=true;
36                        _root.map.ways[w].locked=false;
37                        _root.map.ways[w].historic=false;
38                        _root.map.ways[w].version=result[3];
39                        _root.map.ways[w].uid=result[4];
40                        _root.map.ways[w].removeNodeIndex();
41                        _root.map.ways[w].path=[];
42                        _root.map.ways[w].resetBBox();
43                        for (i=0; i<result[1].length; i++) {
44                                x =result[1][i][0];
45                                y =result[1][i][1];
46                                id=result[1][i][2];
47                                _root.map.ways[w].updateBBox(x,y);
48                                x=long2coord(x); y=lat2coord(y);
49                                if (nodes[id]) {
50                                        // already exists: move node in other ways if required
51                                        // ** maybe we should take out 'w'? not sure
52                                        if (_root.map.ways[w].checkconnections) { nodes[id].moveTo(x,y,w); }
53                                } else {
54                                        // doesn't exist, so create new node
55                                        _root.nodes[id]=new Node(id,x,y,result[1][i][3],result[1][i][4]);
56                                        _root.nodes[id].clean=true;
57                                        if (id==prenode) { prepoint=i; }
58                                }
59                                _root.map.ways[w].path.push(_root.nodes[id]);
60                                _root.nodes[id].addWay(w);
61                                if (_root.nodes[id].ways[_root.wayselected] && w!=_root.wayselected) { redrawws=true; }
62                        }
63                        _root.map.ways[w].attr=result[2];
64                        _root.map.ways[w].redraw();
65                        if (redrawws)  { _root.ws.highlightPoints(5000,"anchor"); }     // need to redraw [_]s if new connections loaded
66                        if (w==preway) { _root.map.ways[w].select(); preway=undefined; }
67                        if (prepoint)  { _root.map.ways[w].select(); 
68                                                         _root.map.anchors[prepoint].select();
69                                                         prenode=prepoint=undefined; }
70                        _root.map.ways[w].clearPOIs();
71                        _root.map.ways[w].checkconnections=false;
72                };
73                remote_read.call('getway',responder,Math.floor(this._name));
74        };
75
76        OSMWay.prototype.loadFromDeleted=function(timestamp) {
77                delresponder=function() { };
78                delresponder.onResult=function(result) {
79                        var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
80                        var i,id,n;
81                        var w=_root.map.ways[result[0]];
82                        var oldpath=new Object();               // make sure to delete any nodes not in reverted way
83                        for (i=0; i<w.path.length; i++) { oldpath[w.path[i].id]=w.path[i].version; }
84                        if (result[4]) { w.historic=false; w.clean=true;  }
85                                  else { w.historic=true;  w.clean=false; }
86                        w.version=result[3];
87                        w.removeNodeIndex();
88                        w.path=[];
89                        w.resetBBox();
90                        for (i=0; i<result[1].length; i++) {
91                                n=result[1][i];
92                                x=n[0]; y=n[1]; id=n[2];                                        // 3:current version, 4:tags, 5:reuse?
93                                if (id<0) { id=--_root.newnodeid; }                     // assign negative IDs to anything moved
94                                delete oldpath[id];
95                                w.updateBBox(x,y);
96                                x=long2coord(x); y=lat2coord(y);
97                                if (nodes[id]) {
98                                        if (x!=nodes[id].x || y!=nodes[id].y) {
99                                                // ** also needs to check that tags haven't changed
100                                                nodes[id].moveTo(x,y,w);
101                                                nodes[id].attr=n[4];
102                                                nodes[id].version=n[3];
103                                                w.clean=false;
104                                        }
105                                        if (n[5]) { nodes[id].clean=true; }             // is visible and current version
106                                } else {
107                                        _root.nodes[id]=new Node(id,x,y,n[4],n[3]);
108                                }
109                                w.path.push(_root.nodes[id]);
110                                _root.nodes[id].addWay(result[0]);
111                        }
112                        for (i in oldpath) { if (i>0) { w.deletednodes[i]=oldpath[i]; } }
113                        w.attr=result[2];
114                        if (w==ws) { w.select(); }
115                                  else { w.locked=true; }
116                        w.redraw();
117                        w.clearPOIs();
118                };
119                remote_read.call('getway_old',delresponder,Number(this._name),timestamp);
120        };
121
122        OSMWay.prototype.clearPOIs=function() {
123                // check if any way nodes are POIs, delete the POIs if so
124                var i,z;
125                z=this.path;
126                for (i in z) {
127                        if (_root.map.pois[this.path[i].id]) { removeMovieClip(_root.map.pois[this.path[i].id]); }
128                }
129        };
130
131        // ---- Draw line
132
133        OSMWay.prototype.redraw=function(skip,skiponeway) {
134                this.createEmptyMovieClip("taggednodes",3);
135
136                if (skip && !this.attr["oneway"]) {
137                        // We're at the same scale as previously, so don't redraw
138                        // ** will refactor this when we do proper POI icons
139                        for (var i=0; i<this.path.length; i+=1) {
140                                if (this.path[i].tagged) {
141                                        this.taggednodes.attachMovie("poiinway",i,i);
142                                        this.taggednodes[i]._x=this.path[i].x;
143                                        this.taggednodes[i]._y=this.path[i].y;
144                                        this.taggednodes[i]._xscale=this.taggednodes[i]._yscale=taggedscale;
145                                }
146                        }
147                } else {
148
149                        // Either the line has changed, or we've changed scale
150                        this.createEmptyMovieClip("line",1);                                    // clear line
151                        this.createEmptyMovieClip("arrows",2);                                  //  |
152                        var linealpha=100; // -50*(this.locked==true);
153                        var casingx=(scale<17 || preferences.data.thinlines) ? 1.5 : 1.3; var casingcol=0x222222;
154       
155                        // Set stroke
156       
157                        var f=this.getFill();
158                        if              (this.locked)                                    { this.line.lineStyle(linewidth,0xFF0000,linealpha,false,"none"); }
159                        else if (colours[this.attr["highway"]])  { this.line.lineStyle(linewidth,colours[this.attr["highway" ]],linealpha,false,"none"); }
160                        else if (colours[this.attr["waterway"]]) { this.line.lineStyle(linewidth,colours[this.attr["waterway"]],linealpha,false,"none"); }
161                        else if (colours[this.attr["railway"]])  { this.line.lineStyle(linewidth,colours[this.attr["railway" ]],linealpha,false,"none"); }
162                        else {
163                                var c=0xAAAAAA; var z=this.attr;
164                                for (var i in z) { if (i!='created_by' && this.attr[i]!='' && this.attr[i].substr(0,6)!='(type ') { c=0x707070; } }
165                                this.line.lineStyle((f>-1 || this.attr['boundary']) ? areawidth : linewidth,c,linealpha,false,"none");
166                        }
167                       
168                        // Draw fill/casing
169       
170                        var br=false;
171                        if (preferences.data.tiger && this.uid==7168 && this.version==1 && this.clean && this.attr["tiger:tlid"]) {
172                casingx=2; casingcol=0xFF00FF;
173                        } else if (preferences.data.noname && this.attr["highway"] && (!this.attr["name"] || this.attr["name"].substr(0,6)=='(type ')) {
174                casingx=2; casingcol=0xFF0000;
175                        } else if (this.attr["bridge"] && this.attr["bridge"]!="no") {
176                                casingx=(scale<17) ? 2 : 1.8; br=true;
177                        }
178
179                        if ((f>-1 || br || casing[this.attr['highway']]) && !this.locked) {
180                                if (!_root.map.areas[this._name]) { _root.map.areas.createEmptyMovieClip(this._name,++areadepth); }
181                                with (_root.map.areas[this._name]) {
182                                        clear();
183                                        enabled=false;
184                                        moveTo(this.path[0].x,this.path[0].y); 
185                                        if (f>-1) { beginFill(f,20); }
186                                                 else { lineStyle(linewidth*casingx,casingcol,100,false,"none"); }
187                                        for (var i=1; i<this.path.length; i+=1) {
188                                                lineTo(this.path[i].x,this.path[i].y);
189                                        }
190                                        if (f>-1) { endFill(); }
191                                };
192                        } else if (_root.map.areas[this._name]) {
193                                removeMovieClip(_root.map.areas[this._name]);
194                        }
195       
196                        // Draw line and tagged nodes
197       
198                        this.line.moveTo(this.path[0].x,this.path[0].y);
199                        for (var i=0; i<this.path.length; i+=1) {
200                                if (i>0) { this.line.lineTo(this.path[i].x,this.path[i].y); }
201                                if (this.path[i].tagged) {
202                                        // **** attach correct icon:
203                                        // if (this.path[i].attr['frog']) {
204                                        // this.taggednodes.attachMovie("poi_22",i,i);
205                                        // ...probably don't have to do _root.map.pois[point].__proto__=undefined;
206                                        //    because clicks are handled by the way movieclip
207                                        this.taggednodes.attachMovie("poiinway",i,i);
208                                        this.taggednodes[i]._x=this.path[i].x;
209                                        this.taggednodes[i]._y=this.path[i].y;
210                                        this.taggednodes[i]._xscale=this.taggednodes[i]._yscale=taggedscale;
211                                }
212                        }
213
214                        // Draw relations
215
216                        var z=_root.wayrels[this._name];
217                        for (var rel in z) { _root.map.relations[rel].redraw(); }
218                       
219                        // Draw direction arrows
220                       
221                        if (this.attr["oneway"] && !skiponeway) {
222                                this.drawArrows(this.attr["oneway"]);
223                        }
224                }
225        };
226
227        OSMWay.prototype.getFill=function() {
228                var f=-1; 
229                if (this.path[this.path.length-1]==this.path[0] && this.path.length>2) {
230                        if (this.attr['area']) { f='0x777777'; }
231                        var z=this.attr;
232                        for (var i in z) { if (areas[i] && this.attr[i]!='' && this.attr[i]!='coastline') { f=areas[i]; } }
233                }
234                return f;
235        };
236
237        OSMWay.prototype.drawArrows=function(dir) {
238                if (dir=='no' || dir=='false') { return; }
239                var dashes=[4,1,1,1,1,10];
240                var widths=[2,7,5,3,1,0];
241                if (dir=="-1") { dashes.reverse(); widths.reverse(); }
242                var arrowcol=0x6C70D5; if (this.attr['highway']=='primary' || this.attr['highway']=='primary_link' || 
243                                                                   this.attr['highway']=='trunk' || this.attr['highway']=='trunk_link' || 
244                                                                   this.attr['highway']=='motorway' || this.attr['highway']=='motorway_link') { arrowcol=0; }
245               
246                var draw=false;
247                var dashleft=0;
248                var segleft=0;
249                var dc=[]; var wc=[];
250                var a,xc,yc,curx,cury,dx,dy;
251                var i=0;
252
253                var g=this.arrows;
254                g.moveTo(this.path[0].x,this.path[0].y);
255                while (i<this.path.length-1 || segleft>0) {
256                        if (dashleft<=0) {
257                                if (dc.length==0) { dc=dashes.slice(0); wc=widths.slice(0); }
258                                dashleft=dc.shift()/bscale*2;
259                                dashwidth=wc.shift();
260                        }
261                        if (segleft<=0) {
262                                curx=this.path[i].x; dx=this.path[i+1].x-curx;
263                                cury=this.path[i].y; dy=this.path[i+1].y-cury;
264                                a=Math.atan2(dy,dx); xc=Math.cos(a); yc=Math.sin(a);
265                                segleft=Math.sqrt(dx*dx+dy*dy);
266                                i++;
267                        }
268
269                        if (segleft<=dashleft) {
270                                // the path segment is shorter than the dash
271                                curx+=dx; cury+=dy;
272                                moveLine(g,curx,cury,dashwidth,arrowcol);
273                                dashleft-=segleft; segleft=0;
274                        } else {
275                                // the path segment is longer than the dash
276                                curx+=dashleft*xc; dx-=dashleft*xc;
277                                cury+=dashleft*yc; dy-=dashleft*yc;
278                                moveLine(g,curx,cury,dashwidth,arrowcol);
279                                segleft-=dashleft; dashleft=0;
280                        }
281                }
282        };
283
284        function moveLine(g,x,y,dashwidth,colour) {
285                if (dashwidth==0) {
286                        g.moveTo(x,y);
287                } else {
288                        g.lineStyle(dashwidth,colour,100,false,"none","none");
289                        g.lineTo(x,y);
290                }
291        };
292
293        // ---- Tidy in line/circle
294       
295        OSMWay.prototype.tidy=function() {
296                var a=this.path[0]; var b=this.path[this.path.length-1];
297                var w=new Object(); w[this._name]=true;
298                if (a.id==b.id) {
299                        // Tidy in circle
300                        if (this.path.length<4) { return; }
301                        this.saveChangeUndo();
302                       
303                        // Find centre-point
304                        var patharea=0;
305                        var cx=0; var lx=b.x; //coord2long(b.x);
306                        var cy=0; var ly=b.y; //coord2y(b.y);
307                        for (var i=0; i<this.path.length; i++) {
308                var latp=this.path[i].y; //coord2y(this.path[i].y);
309                var lon =this.path[i].x; // coord2long(this.path[i].x);
310                                var sc = (lx*latp-lon*ly); //*masterscale;
311                                cx += (lx+lon)*sc;
312                                cy += (ly+latp)*sc;
313                                patharea += sc;
314                                lx=lon; ly=latp;
315                        }
316                        patharea/=2;
317                        cx=cx/patharea/6; //long2coord(cx/patharea/6);
318                        cy=cy/patharea/6; //y2coord(cy/patharea/6);
319
320                        // Average distance to centre
321                        var d=0; var angles=[];
322                        for (var i=0; i<this.path.length; i++) {
323                                d+=Math.sqrt(Math.pow(this.path[i].x-cx,2)+Math.pow(this.path[i].y-cy,2));
324                        }
325                        d=d/this.path.length;
326                       
327                        // Move each node
328                        for (var i=0; i<this.path.length-1; i++) {
329                                var c=Math.sqrt(Math.pow(this.path[i].x-cx,2)+Math.pow(this.path[i].y-cy,2));
330                                this.path[i].unsetPosition();
331                                this.path[i].x=cx+(this.path[i].x-cx)/c*d;
332                                this.path[i].y=cy+(this.path[i].y-cy)/c*d;
333                                this.path[i].setPosition();
334                                this.path[i].clean=false;
335                                var l=this.path[i].ways; for (var o in l) { w[o]=true; }
336                        }
337
338                        // Insert extra nodes to make circle
339                        // clockwise: angles decrease, wrapping round from -170 to 170
340                        var newpath=[]; var diff,b;
341                        var clockwise=_root.panel.i_clockwise._visible;
342                        for (var i=0; i<this.path.length-1; i++) {
343                                var j=(i+1) % this.path.length;
344                                newpath.push(this.path[i]);
345                                a1=Math.atan2(this.path[i].x-cx,this.path[i].y-cy)*(180/Math.PI);
346                                a2=Math.atan2(this.path[j].x-cx,this.path[j].y-cy)*(180/Math.PI);
347
348                                if (clockwise) {
349                                        if (a2>a1) { a2=a2-360; }
350                                        diff=a1-a2;
351                                        if (diff>20) {
352                                                for (var ang=a1-20; ang>a2+10; ang-=20) {
353                                                        _root.newnodeid--;
354                                                        _root.nodes[newnodeid]=new Node(newnodeid,cx+Math.sin(ang*Math.PI/180)*d,cy+Math.cos(ang*Math.PI/180)*d,new Object(),0);
355                                                        _root.nodes[newnodeid].addWay(this._name);
356                                                        newpath.push(_root.nodes[newnodeid]);
357                                                }
358                                        }
359                                } else {
360                                        if (a1>a2) { a1=a1-360; }
361                                        diff=a2-a1;
362                                        if (diff>20) {
363                                                for (var ang=a1+20; ang<a2-10; ang+=20) {
364                                                        _root.newnodeid--;
365                                                        _root.nodes[newnodeid]=new Node(newnodeid,cx+Math.sin(ang*Math.PI/180)*d,cy+Math.cos(ang*Math.PI/180)*d,new Object(),0);
366                                                        _root.nodes[newnodeid].addWay(this._name);
367                                                        newpath.push(_root.nodes[newnodeid]);
368                                                }
369                                        }
370                                }
371                        }
372                        newpath.push(this.path[this.path.length-1]);
373                        this.path=newpath;
374
375                } else {
376                        // Tidy in line
377                        if (this.path.length<3) { return; }
378
379                        // First, calculate divergence to make sure we're not straightening a really bendy way
380                        var d=0; var t,x1,y1;
381                        var thislat=coord2lat(_root.map._y); var latfactor=Math.cos(thislat/(180/Math.PI));
382                        for (var i=1; i<this.path.length-1; i++) {
383                                u=((this.path[i].x-a.x)*(b.x-a.x)+
384                                   (this.path[i].y-a.y)*(b.y-a.y))/
385                                   (Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));
386                                x1=a.x+u*(b.x-a.x);
387                                y1=a.y+u*(b.y-a.y);
388                                t=Math.sqrt(Math.pow(x1-this.path[i].x,2)+Math.pow(y1-this.path[i].y,2));
389                                t=Math.floor((111200*latfactor)*t/masterscale);
390                                if (t>d) { d=t; }
391                        }
392                        if (d>50 && !Key.isDown(Key.SHIFT)) { setAdvice(false,iText('advice_bendy')); return; }
393                        this.saveChangeUndo();
394
395                        var el=long2coord(_root.bigedge_l);             // We don't want to delete any off-screen nodes
396                        var er=long2coord(_root.bigedge_r);             // (because they might be used in other ways)
397                        var et= lat2coord(_root.bigedge_t);
398                        var eb= lat2coord(_root.bigedge_b);
399                        var retain=new Array(a);
400                        for (var i=1; i<this.path.length-1; i++) {
401                                if (this.path[i].numberOfWays()>1 || hasTags(this.path[i].attr) ||
402                                        this.path[i].x<el || this.path[i].x>er || 
403                                        this.path[i].y<et || this.path[i].y>eb) {
404                                        u=((this.path[i].x-a.x)*(b.x-a.x)+
405                                           (this.path[i].y-a.y)*(b.y-a.y))/
406                                           (Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));
407                                        this.path[i].unsetPosition();
408                                        this.path[i].x=a.x+u*(b.x-a.x);
409                                        this.path[i].y=a.y+u*(b.y-a.y);
410                                        this.path[i].setPosition();
411                                        this.path[i].clean=false;
412                                        var l=this.path[i].ways; for (var o in l) { w[o]=true; }
413                                        retain.push(this.path[i]);
414                                } else {
415                                        this.markAsDeleted(this.path[i],false);
416                                        memberDeleted('Node', this.path[i].id);
417                                }
418                        }
419                        retain.push(b);
420                        this.path=retain;
421                        this.removeDuplicates();
422                }
423                this.clean=false; markClean(false);
424                for (var i in w) { _root.map.ways[i].redraw(); }
425                this.select();
426        };
427
428
429
430        // ---- Show direction
431
432        OSMWay.prototype.direction=function() {
433                if (this.path.length<2) {
434                        _root.panel.i_clockwise._visible=false;
435                        _root.panel.i_anticlockwise._visible=false;
436                        _root.panel.i_direction._visible=true;
437                        _root.panel.i_direction._alpha=50;
438                } else {
439                        var dx=this.path[this.path.length-1].x-this.path[0].x;
440                        var dy=this.path[this.path.length-1].y-this.path[0].y;
441                        if (dx!=0 || dy!=0) {
442                                // Non-circular
443                                _root.panel.i_direction._rotation=180-Math.atan2(dx,dy)*(180/Math.PI)-45;
444                                _root.panel.i_direction._alpha=100;
445                                _root.panel.i_direction._visible=true;
446                                _root.panel.i_clockwise._visible=false;
447                                _root.panel.i_anticlockwise._visible=false;
448                        } else {
449                                // Circular
450                                _root.panel.i_direction._visible=false;
451                                // Find lowest rightmost point
452                                // cf http://geometryalgorithms.com/Archive/algorithm_0101/
453                                var lowest=0;
454                                var xmax=-999999; var ymin=-999999;
455                                for (var i=0; i<this.path.length; i++) {
456                                        if      (this.path[i].y> ymin) { lowest=i; xmin=this.path[i].x; ymin=this.path[i].y; }
457                                        else if (this.path[i].y==ymin
458                                                  && this.path[i].x> xmax) { lowest=i; xmin=this.path[i].x; ymin=this.path[i].y; }
459                                }
460                                var clockwise=(this.onLeft(lowest)>0);
461                                _root.panel.i_clockwise._visible=clockwise;
462                                _root.panel.i_anticlockwise._visible=!clockwise;
463                        }
464                }
465        };
466
467        OSMWay.prototype.onLeft=function(j) {
468                var left=0;
469                if (this.path.length>=3) {
470                        var i=j-1; if (i==-1) { i=this.path.length-2; }
471                        var k=j+1; if (k==this.path.length) { k=1; }
472                        left=((this.path[j].x-this.path[i].x) * (this.path[k].y-this.path[i].y) -
473                                  (this.path[k].x-this.path[i].x) * (this.path[j].y-this.path[i].y));
474                }
475                return left;
476        };
477       
478
479
480        // ---- Remove from server
481       
482        OSMWay.prototype.remove=function() {
483                clearFloater();
484                memberDeleted('Way', this._name);
485                var z=this.path; for (var i in z) {
486                        if (z[i].numberOfWays()==1) { memberDeleted('Node', z[i].id); }
487                }
488                uploadDirtyRelations();
489
490                this.deleteMergedWays();
491                this.removeNodeIndex();
492
493                if (!this.historic && !this.locked) {
494                        var z=shallowCopy(this.path); this.path=new Array();
495                        for (var i in z) { 
496                                this.markAsDeleted(z[i],false); }
497                        if (_root.sandbox) {
498                                if (this._name>=0) { 
499                                        _root.waystodelete[this._name]=[this.version,deepCopy(this.deletednodes)];
500                                } else {
501                                        var z=this.deletednodes; for (var j in z) { deleteNodeAsPOI(j); }
502                                        var z=this.path;         for (var j in z) { deleteNodeAsPOI(j.id); }
503                                        markClean(false);
504                                }
505                        } else if (this._name>=0) {
506                                if (renewChangeset()) { return; }
507                                deleteresponder = function() { };
508                                deleteresponder.onResult = function(result) { deletewayRespond(result); };
509                                _root.writesrequested++;
510                                remote_write.call('deleteway',deleteresponder,_root.usertoken,_root.changeset,Number(this._name),Number(this.version),this.deletednodes);
511                        }
512                }
513
514                if (this._name==wayselected) { stopDrawing(); deselectAll(); }
515                removeMovieClip(_root.map.areas[this._name]);
516                removeMovieClip(this);
517        };
518
519        function deletewayRespond(result) {
520                _root.writesrequested--;
521                var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
522                var z=result[2]; for (var i in z) { delete _root.nodes[i]; }
523                operationDone(result[0]);
524                freshenChangeset();
525        };
526
527        function deleteNodeAsPOI(nid) {
528                if (nid<0) { return; }
529                if (_root.nodes[nid].numberOfWays()>0) { return; }
530                // if we're not going to send a deleteway for this (because it's negative - probably due to a split),
531                // then delete it as a POI instead
532                var n=_root.nodes[nid];
533                _root.poistodelete[nid]=[n.version,coord2long(n.x),coord2lat(n.y),deepCopy(n.attr)];
534        }
535
536        // ---- Variant with confirmation if any nodes have tags
537       
538        OSMWay.prototype.removeWithConfirm=function() {
539                var c=true;
540                var z=this.path;
541                for (var i in z) {
542                        if (this.path[i].tagged && hashLength(this.path[i].ways)==1) { c=false; }
543                }
544                if (c) {
545                        _root.ws.saveDeleteUndo(iText('deleting'));
546                        this.remove();
547                        markClean(true);
548                } else {
549                        _root.windows.attachMovie("modal","confirm",++windowdepth);
550                        _root.windows.confirm.init(275,80,new Array(iText('cancel'),iText('delete')),
551                                function(choice) {
552                                        if (choice==iText('delete')) { _root.ws.saveDeleteUndo(iText('deleting')); _root.ws.remove(); markClean(true); }
553                                });
554                        _root.windows.confirm.box.createTextField("prompt",2,7,9,250,100);
555                        writeText(_root.windows.confirm.box.prompt,iText('prompt_taggedpoints'));
556                }
557        };
558
559        // ---- Upload to server
560       
561        OSMWay.prototype.upload=function() {
562                putresponder=function() { };
563                putresponder.onResult=function(result) {
564                        _root.writesrequested--;
565                        var code=result.shift(); var msg=result.shift(); if (code) { _root.map.ways[result[0]].notUploading(); handleError(code,msg,result); return; }
566
567                        var i,r,z,nw,ow;
568                        ow=result[0];                   // old way ID
569                        nw=result[1];                   // new way ID
570                        if (ow!=nw) { renumberWay(ow,nw); }
571                        _root.map.ways[nw].clean=true;
572                        _root.map.ways[nw].uploading=false;
573                        _root.map.ways[nw].historic=false;
574                        _root.map.ways[nw].version=result[3];
575                       
576                        // renumber nodes (and any relations/ways they're in)
577                        z=result[2];
578                        for (var oid in z) {
579                                var nid = result[2][oid];
580                                nodes[oid].renumberTo(nid);
581                                nodes[nid].addWay(nw);
582                                renumberMemberOfRelation('Node', oid, nid);
583                        }
584                        for (var oid in z) { delete _root.nodes[oid]; } // delete -ve nodes
585                       
586                        // set versions, clean, not uploading
587                        z=result[4]; for (var nid in z) { nodes[nid].version=result[4][nid]; nodes[nid].uploading=false; nodes[nid].clean=true; }
588
589                        // remove deleted nodes
590                        z=result[5]; for (var nid in z) { c=_root.map.ways[nw]; delete c.deletednodes[nid]; delete _root.nodes[nid]; }
591                       
592                        _root.map.ways[nw].clearPOIs();
593                        uploadDirtyRelations();
594                        _root.map.ways[nw].deleteMergedWays();
595                        uploadDirtyWays();                      // make sure dependencies are uploaded
596                        operationDone(ow);
597                        freshenChangeset();
598                        updateInspector();
599                };
600
601                if (!this.uploading && !this.hasDependentNodes() && !this.locked && (!_root.sandbox || _root.uploading) && this.path.length>1) {
602                        this.deleteMergedWays();
603
604                        // Assemble list of changed nodes, and send
605                        if (renewChangeset()) { return; }
606                        this.uploading=true;
607                        var sendpath =new Array();
608                        var sendnodes=new Array();
609                        for (i=0; i<this.path.length; i++) {
610                                sendpath.push(this.path[i].id);
611                                if (!this.path[i].clean && !this.path[i].uploading) {
612                                        sendnodes.push(new Array(coord2long(this.path[i].x),
613                                                                                         coord2lat (this.path[i].y),
614                                                                                         this.path[i].id,this.path[i].version,
615                                                                                         deepCopy  (this.path[i].attr)));
616                                        this.path[i].uploading=true;
617                                }
618                        }
619                        delete this.attr['created_by'];
620                        _root.writesrequested++;
621                        remote_write.call('putway',putresponder,_root.usertoken,_root.changeset,this.version,Number(this._name),sendpath,this.attr,sendnodes,this.deletednodes);
622                        updateInspector();
623                } else { 
624                        operationDone(this._name);      // next please!
625                }
626        };
627
628        // ---- Delete any ways merged into this one
629
630        OSMWay.prototype.deleteMergedWays=function() {
631                while (this.mergedways.length>0) {
632                        var i=this.mergedways.shift();
633                        _root.map.ways.attachMovie("way",i[0],++waydepth);      // can't remove unless the movieclip exists!
634                        _root.map.ways[i[0]].version=i[1];                                      //  |
635                        _root.map.ways[i[0]].deletednodes=i[2];                         //  |
636                        _root.map.ways[i[0]].remove();
637                }
638        };
639
640        // ---- Revert to copy in database
641       
642        OSMWay.prototype.reload=function() {
643                _root.waysrequested+=1;
644                while (this.mergedways.length>0) {
645                        var i=this.mergedways.shift();
646                        _root.waysrequested+=1;
647                        _root.map.ways.attachMovie("way",i[0],++waydepth);
648                        _root.map.ways[i[0]].load();
649                }
650                this.checkconnections=true;
651                this.load();
652        };
653       
654        // ---- Save for undo
655
656        OSMWay.prototype.saveDeleteUndo=function(str) {
657                _root.undo.append(UndoStack.prototype.undo_deleteway,
658                                                  new Array(this._name,this._x,this._y,
659                                                                        deepCopy(this.attr),deepCopy(this.path),this.version),
660                                                                        iText('a_way',str));
661        };
662       
663        OSMWay.prototype.saveChangeUndo=function(str) {
664                _root.undo.append(UndoStack.prototype.undo_changeway,
665                                                  new Array(this._name,deepCopy(this.path),deepCopy(this.deletednodes),deepCopy(this.attr)),
666                                                  iText('action_changeway'));
667        };
668
669
670        // ---- Click handling 
671
672        OSMWay.prototype.onRollOver=function() {
673                if (this._name!=_root.wayselected && _root.drawpoint>-1 && !_root.map.anchorhints) {
674                        this.highlightPoints(5001,"anchorhint");
675                        setPointer('penplus');
676                } else if (_root.drawpoint>-1) { setPointer('penplus'); }
677                                                                  else { setPointer(''); }
678                if (this._name!=_root.wayselected) { var a=getName(this.attr,waynames); if (a) { setFloater(a); } }
679        };
680       
681        OSMWay.prototype.onRollOut=function() {
682                if (this.hitTest(_root._xmouse,_root._ymouse,true)) { return; } // false rollout
683                if (_root.wayselected) { setPointer(''   ); }
684                                                  else { setPointer('pen'); }
685                _root.map.anchorhints.removeMovieClip();
686                clearFloater();
687        };
688       
689        OSMWay.prototype.onPress=function() {
690                removeWelcome(true);
691                if (Key.isDown(Key.SHIFT) && this._name==_root.wayselected && _root.drawpoint==-1) {
692                        // shift-click current way: insert point
693                        this.insertAnchorPointAtMouse();
694                } else if (Key.isDown(Key.SHIFT) && _root.wayselected && this.name!=_root.wayselected && _root.drawpoint==-1 && _root.ws.hitTest(_root._xmouse,_root._ymouse,true)) {
695                        // shift-click other way (at intersection with current): make intersection
696                        this.insertAnchorPointAtMouse();
697                } else if (Key.isDown(Key.CONTROL) && _root.wayselected && this.name!=_root.wayselected && _root.drawpoint==-1) {
698                        // control/command-click other way: merge two ways
699                        mergeWayKeepingID(this,_root.ws);
700                } else if (_root.drawpoint>-1) {
701                        // click other way while drawing: insert point as junction
702                        if (!this.historic) {
703                                if (this._name==_root.wayselected && _root.drawpoint>0) {
704                                        _root.drawpoint+=1;     // inserting node earlier into the way currently being drawn
705                                }
706                                _root.newnodeid--;
707                                _root.nodes[newnodeid]=new Node(newnodeid,0,0,new Object(),0);
708                                this.insertAnchorPoint(_root.nodes[newnodeid]);
709                                this.highlightPoints(5001,"anchorhint");
710                                addEndPoint(_root.nodes[newnodeid]);
711                        }
712                        _root.junction=true;
713                        restartElastic();
714                } else {
715                        // click way: select
716                        _root.panel.properties.saveAttributes();
717                        this.select();
718                        clearTooltip();
719                        _root.clicktime=new Date();
720                        // was the click on a tagged node? if so, select directly
721                        var n;
722                        for (var i=0; i<this.path.length; i+=1) {
723                                if (this.taggednodes[i].hitTest(_root._xmouse,_root._ymouse,true)) { n=i; }
724                        }
725                        if (n) { _root.map.anchors[n].beginDrag();
726                                         _root.map.anchors[n].select(); }
727                          else { this.beginDrag(); }
728                }
729        };
730
731        OSMWay.prototype.beginDrag=function() {
732                this.onMouseMove=function() { this.trackDrag(); };
733                this.onMouseUp  =function() { this.endDrag();   };
734                this.dragged=false;
735                this.held=true;
736                _root.firstxmouse=_root.map._xmouse;
737                _root.firstymouse=_root.map._ymouse;
738        };
739
740        OSMWay.prototype.trackDrag=function() {
741                var t=new Date();
742                var longclick=(t.getTime()-_root.clicktime)>1000;
743                var xdist=Math.abs(_root.map._xmouse-_root.firstxmouse);
744                var ydist=Math.abs(_root.map._ymouse-_root.firstymouse);
745                // Don't enable drag unless way held for a while after click
746                if ((xdist>=tolerance   || ydist>=tolerance  ) &&
747                        (t.getTime()-_root.clicktime)<300 &&
748                        lastwayselected!=wayselected) { this.held=false; }
749                // Move way if dragged a long way, or dragged a short way after a while
750                if ((xdist>=tolerance*4 || ydist>=tolerance*4) ||
751                   ((xdist>=tolerance/4 || ydist>=tolerance/4) && longclick) &&
752                   this.held) {
753                        this.dragged=true;
754                }
755                if (this.dragged) {
756                        _root.map.anchors._x=_root.map.areas[this._name]._x=_root.map.highlight._x=this._x=_root.map._xmouse-_root.firstxmouse;
757                        _root.map.anchors._y=_root.map.areas[this._name]._y=_root.map.highlight._y=this._y=_root.map._ymouse-_root.firstymouse;
758                }
759        };
760       
761        OSMWay.prototype.endDrag=function() {
762                delete this.onMouseMove;
763                delete this.onMouseUp;
764                _root.map.anchors._x=_root.map.areas[this._name]._x=_root.map.highlight._x=this._x=0;
765                _root.map.anchors._y=_root.map.areas[this._name]._y=_root.map.highlight._y=this._y=0;
766                if (this.dragged) {
767                        this.moveNodes(_root.map._xmouse-_root.firstxmouse,_root.map._ymouse-_root.firstymouse);
768                        setAdvice(false,iText('advice_waydragged'));
769                        this.redraw();
770                        this.select();
771                        _root.undo.append(UndoStack.prototype.undo_movenodes,
772                                                          new Array(this,_root.map._xmouse-_root.firstxmouse,
773                                                                                         _root.map._ymouse-_root.firstymouse),
774                                                          iText('action_moveway'));
775                }
776        };
777       
778        // ---- Select/highlight
779       
780        OSMWay.prototype.select=function() {
781                _root.panel.properties.tidy();
782                if (_root.wayselected!=this._name || _root.poiselected!=0) { uploadSelected(); }
783//              _root.panel.properties.saveAttributes();
784                _root.pointselected=-2;
785                selectWay(this._name);
786                _root.poiselected=0;
787                this.highlightPoints(5000,"anchor");
788                removeMovieClip(_root.map.anchorhints);
789                this.highlight();
790                setTypeText(iText('way'),this._name);
791                removeIconPanel();
792                _root.panel.properties.init('way',getPanelColumns(),4);
793                _root.panel.presets.init(_root.panel.properties);
794                updateButtons();
795                updateScissors(false);
796        };
797       
798        OSMWay.prototype.highlight=function() {
799                _root.map.createEmptyMovieClip("highlight",5);
800                if (_root.pointselected>-2) {
801                        highlightSquare(_root.map.anchors[pointselected]._x,_root.map.anchors[pointselected]._y, _root.anchorsize*0.05);
802                } else {
803                        var linecolour=0xFFFF00; if (this.locked) { var linecolour=0x00FFFF; }
804                        _root.map.highlight.lineStyle(linewidth*1.5+8,linecolour,80,false,"none");
805                        _root.map.highlight.moveTo(this.path[0].x,this.path[0].y);
806                        for (var i=1; i<this.path.length; i+=1) {
807                                _root.map.highlight.lineTo(this.path[i].x,this.path[i].y);
808                        }
809                }
810                this.direction();
811        };
812
813        OSMWay.prototype.highlightPoints=function(d,atype) {
814                var group=atype+"s";
815                _root.map.createEmptyMovieClip(group,d);
816                for (var i=0; i<this.path.length; i+=1) {
817                        var asprite=atype; 
818                        if (this.path[i].isDupe()) { asprite+="_dupe"; }
819                        else if (this.path[i].numberOfWays()>1) { asprite+="_junction"; }
820                        _root.map[group].attachMovie(asprite,i,i);
821                        _root.map[group][i]._x=this.path[i].x;
822                        _root.map[group][i]._y=this.path[i].y;
823                        _root.map[group][i]._xscale=_root.anchorsize;
824                        _root.map[group][i]._yscale=_root.anchorsize;
825                        _root.map[group][i].node=this.path[i].id;
826                        _root.map[group][i].way=this;
827                        if (this.path[i].tagged) {
828                                // anchor point should be black if it has tags
829                                _root.map[group][i].blacken=new Color(_root.map[group][i]);
830                                _root.map[group][i].blacken.setTransform(to_black);
831                        }
832                }
833        };
834
835        // ---- Split, merge, reverse
836
837        OSMWay.prototype.splitWay=function(point,newattr) {
838                var i,z;
839                if (point>0 && point<(this.path.length-1) && !this.historic) {
840                        _root.newwayid--;                                                                                       // create new way
841                        _root.map.ways.attachMovie("way",newwayid,++waydepth);          //  |
842                        _root.map.ways[newwayid].path=shallowCopy(this.path);           // deep copy path array
843                        this.removeNodeIndex();
844
845                        if (newattr) { _root.map.ways[newwayid].attr=newattr; }
846                                        else { _root.map.ways[newwayid].attr=deepCopy(this.attr); }
847
848                        z=_root.wayrels[this._name];                                                            // copy relations
849                        for (i in z) {                                                                                          //  |
850                                _root.map.relations[i].setWayRole(newwayid,z[i]);               //  |
851                        }                                                                                                                       //  |
852 
853                        this.path.splice(Math.floor(point)+1);                                          // current way
854                        this.redraw();                                                                                          //  |
855                        this.createNodeIndex();
856
857                        _root.map.ways[newwayid].path.splice(0,point);                          // new way
858                        _root.map.ways[newwayid].locked=this.locked;                            //  |
859                        _root.map.ways[newwayid].redraw();                                                      //  |
860                        _root.map.ways[newwayid].clean=false;                                           //  | - in case it doesn't upload
861                        _root.map.ways[newwayid].upload();                                                      //  |
862                        _root.map.ways[newwayid].createNodeIndex();                                     //  |
863
864                        this.clean=false;                                                                                       // upload current way
865                        this.upload();                                                                                          //  |
866                        this.select();                                                                                          //  |
867                        uploadDirtyRelations();
868                        _root.undo.append(UndoStack.prototype.undo_splitway,
869                                                          new Array(this,_root.map.ways[newwayid]),
870                                                          iText('action_splitway'));
871                };
872        };
873
874        //              Merge (start/end of this way,other way object,start/end of other way)
875
876        OSMWay.prototype.mergeWay=function(topos,otherway,frompos) {
877                var i,z;
878                var conflict=false;
879                if (this.historic || otherway.historic) { return; }
880                if (otherway==this) { return; }
881
882                var mergepoint=this.path.length;
883                if (topos==0) {
884                        _root.undo.append(UndoStack.prototype.undo_mergeways,
885                                                          new Array(this,deepCopy(otherway.attr),deepCopy(this.attr),frompos),
886                                                          iText('action_mergeways'));
887                } else {
888                        _root.undo.append(UndoStack.prototype.undo_mergeways,
889                                                          new Array(this,deepCopy(this.attr),deepCopy(otherway.attr),topos),
890                                                          iText('action_mergeways'));
891                }
892                if (frompos==0) { for (i=0; i<otherway.path.length;    i+=1) { this.addPointFrom(topos,otherway,i); } }
893                                   else { for (i=otherway.path.length-1; i>=0; i-=1) { this.addPointFrom(topos,otherway,i); } }
894
895                // Merge attributes
896                z=otherway.attr;
897                for (i in z) {
898                        if (otherway.attr[i].substr(0,6)=='(type ') { otherway.attr[i]=null; }
899                        if (this.attr[i].substr(0,6)=='(type ') { this.attr[i]=null; }
900                        if (this.attr[i]) {
901                                if (this.attr[i]!=otherway.attr[i] && otherway.attr[i]) { var s=this.attr[i]+'; '+otherway.attr[i]; this.attr[i]=s.substr(0,255); conflict=true; }
902                        } else {
903                                this.attr[i]=otherway.attr[i];
904                        }
905                        if (!this.attr[i]) { delete this.attr[i]; }
906                }
907
908                // Merge relations
909                z=_root.wayrels[otherway._name];                                                // copy relations
910                for (i in z) {                                                                                  //  |
911                        if (!_root.wayrels[this._name][i]) {                            //  |
912                                _root.map.relations[i].setWayRole(this._name,z[i]);     
913                        }                                                                                                       //  |
914                }                                                                                                               //  |
915                memberDeleted('Way', otherway._name);                                   // then remove old way from them
916                z=otherway.deletednodes;                                                                //  | and its deletednodes
917                for (i in z) { memberDeleted('Node',z[i]); }                    //  |
918
919                // Add to list of merged ways (so they can be deleted on next putway)
920        if (_root.sandbox) {
921            otherway.remove();
922        } else {
923                this.mergedways.push(new Array(otherway._name,otherway.version,otherway.deletednodes));
924                this.mergedways.concat(otherway.mergedways);
925        }
926                this.clean=false;
927                markClean(false);
928                if (otherway.locked) { this.locked=true; }
929                otherway.removeNodeIndex();
930                removeMovieClip(_root.map.areas[otherway._name]);
931                removeMovieClip(otherway);
932                if (this._name==_root.wayselected) { 
933                        _root.panel.properties.reinit();
934                }
935                if (conflict) { setAdvice(false,iText('advice_tagconflict')); }
936        };
937
938        OSMWay.prototype.addPointFrom=function(topos,otherway,srcpt) {
939                if (topos==0) { if (this.path[0                                 ]==otherway.path[srcpt]) { return; } }  // don't add duplicate points
940                                 else { if (this.path[this.path.length-1]==otherway.path[srcpt]) { return; } }  //  |
941                if (topos==0) { this.path.unshift(otherway.path[srcpt]); }
942                             else { this.path.push(otherway.path[srcpt]); }
943                otherway.path[srcpt].addWay(this._name);
944        };
945
946        OSMWay.prototype.mergeAtCommonPoint=function(sel) {
947                var selstart =sel.path[0];
948                var sellen   =sel.path.length-1;
949                var selend   =sel.path[sellen];
950                var thisstart=this.path[0];
951                var thislen  =this.path.length-1;
952                var thisend  =this.path[thislen];
953                if      (selstart==thisstart) { sel.mergeWay(0,this,0);                    return true; }
954                else if (selstart==thisend  ) { sel.mergeWay(0,this,thislen);      return true; }
955                else if (selend  ==thisstart) { sel.mergeWay(sellen,this,0);       return true; }
956                else if (selend  ==thisend  ) { sel.mergeWay(sellen,this,thislen); return true; }
957                else                                              { setAdvice(true,iText('advice_nocommonpoint')); return false; }
958        };
959
960        // ---- Reverse order
961       
962        OSMWay.prototype.reverseWay=function() {
963                if (this.path.length<2) { return; }
964                if (_root.drawpoint>-1) { _root.drawpoint=(this.path.length-1)-_root.drawpoint; }
965                this.path.reverse();
966                this.redraw();
967                this.direction();
968                this.select();
969                this.clean=false;
970                markClean(false);
971                _root.undo.append(UndoStack.prototype.undo_reverse,new Array(this),iText('action_reverseway'));
972        };
973
974        // ---- Remove dupe nodes
975       
976        OSMWay.prototype.joinNodes=function() {
977                for (var i=0; i<this.path.length; i++) {
978                        if (this.path[i].isDupe()) {
979                                this.path[i].removeDupes([]);
980                        }
981                }
982                this.redraw();
983                this.select();
984                this.clean=false;
985                markClean(false);
986        };
987       
988        // ---- Move all nodes within a way
989       
990        OSMWay.prototype.moveNodes=function(xdiff,ydiff) {
991                var i,n;
992                var movedalready=new Array();
993                this.clean=false;
994                markClean(false);
995                for (i=0; i<this.path.length; i+=1) {
996                        n=this.path[i].id;
997                        if (movedalready[n]) {
998                        } else {
999                                this.path[i].moveTo(this.path[i].x+xdiff,
1000                                                                        this.path[i].y+ydiff,
1001                                                                        this._name);
1002                                movedalready[n]=true;
1003                        }
1004                }
1005        };
1006
1007        // ---- Add node to deleted list
1008        //              (should have been removed from way first)
1009       
1010        OSMWay.prototype.markAsDeleted=function(rnode,check2) {
1011                var d=true;
1012                // If we're just removing one point, check it's not used elsewhere in the way before removing from .ways
1013                if (check2) {
1014                        var z=this.path; 
1015                        for (var i in z) { if (this.path[i].id==rnode.id) { d=false; } }
1016                }
1017                if (d) { rnode.removeWay(this._name); }
1018                if (rnode.numberOfWays()==0 && rnode.id>0) { this.deletednodes[rnode.id]=rnode.version; }
1019        };
1020
1021
1022        // ---- Check for duplicates (e.g. when C is removed from ABCB)
1023       
1024        OSMWay.prototype.removeDuplicates=function() {
1025                var z=this.path; var ch=false;
1026                for (var i in z) {
1027                        if (i>0) {
1028                                if (this.path[i]==this.path[i-1]) { this.path.splice(i,1); ch=true; }
1029                        }
1030                }
1031                return ch;
1032        };
1033
1034        // ---- Add point into way with SHIFT-clicking
1035        //              cf http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.vba
1036        //              for algorithm to find nearest point on a line
1037       
1038        OSMWay.prototype.insertAnchorPoint=function(nodeobj) {
1039                var nx,ny,u,closest,closei,i,a,b,direct,via,newpoint;
1040                nx=_root.map._xmouse;   // where we're inserting it
1041                ny=_root.map._ymouse;   //      |
1042                closest=0.1; closei=0;
1043                for (i=0; i<(this.path.length)-1; i+=1) {
1044                        a=this.path[i  ];
1045                        b=this.path[i+1];
1046                        direct=Math.sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));
1047                        via   =Math.sqrt((nx -a.x)*(nx -a.x)+(ny -a.y)*(ny -a.y));
1048                        via  +=Math.sqrt((nx -b.x)*(nx -b.x)+(ny -b.y)*(ny -b.y));
1049                        if (Math.abs(via/direct-1)<closest) {
1050                                closei=i+1;
1051                                closest=Math.abs(via/direct-1);
1052                                u=((nx-a.x)*(b.x-a.x)+
1053                                   (ny-a.y)*(b.y-a.y))/
1054                                   (Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));
1055                                nodeobj.unsetPosition();
1056                                nodeobj.x=a.x+u*(b.x-a.x);
1057                                nodeobj.y=a.y+u*(b.y-a.y);
1058                                nodeobj.setPosition();
1059                        }
1060                }
1061                // Insert
1062                nodeobj.addWay(this._name);
1063                this.path.splice(closei,0,nodeobj);
1064                this.clean=false;
1065                this.redraw();
1066                markClean(false);
1067//              _root.adjustedxmouse=tx;        // in case we're adding extra points...
1068//              _root.adjustedymouse=ty;        // should probably return an array or an object **
1069                return closei;
1070        };
1071
1072        //              Wrapper around the above to:
1073        //              - insert at mouse position
1074        //              - add to undo stack
1075        //              - add intersection at any crossing ways
1076
1077        OSMWay.prototype.insertAnchorPointAtMouse=function() {
1078                _root.newnodeid--;
1079                _root.nodes[newnodeid]=new Node(newnodeid,0,0,new Object(),0);
1080                _root.pointselected=this.insertAnchorPoint(_root.nodes[newnodeid]);
1081                var waylist=new Array(); waylist.push(this);
1082                var poslist=new Array(); poslist.push(_root.pointselected);
1083                for (qway in _root.map.ways) {
1084                        if (_root.map.ways[qway].hitTest(_root._xmouse,_root._ymouse,true) && qway!=this._name) {
1085                                poslist.push(_root.map.ways[qway].insertAnchorPoint(_root.nodes[newnodeid]));
1086                                waylist.push(_root.map.ways[qway]);
1087                        }
1088                }
1089                _root.undo.append(UndoStack.prototype.undo_addpoint,
1090                                                  new Array(waylist,poslist), iText('action_insertnode'));
1091                _root.ws.highlightPoints(5000,"anchor");
1092                _root.map.anchors[pointselected].beginDrag();
1093                updateInspector();
1094        };
1095
1096        // ---- Remove point from this way (only)
1097       
1098        OSMWay.prototype.removeAnchorPoint=function(point) {
1099                // ** if length<2, then mark as way removal
1100                _root.undo.append(UndoStack.prototype.undo_deletepoint,
1101                                                  new Array(deepCopy(this.path[point]),
1102                                                                        new Array(this._name),
1103                                                                        new Array(point)),
1104                                                  iText('action_deletepoint'));
1105                var rnode=this.path[point];
1106                this.path.splice(point,1);
1107                this.removeDuplicates();
1108                this.markAsDeleted(rnode,true);
1109                if (rnode.numberOfWays()==0) { memberDeleted('Node', rnode.id); }
1110                if (this.path.length<2) { this.remove(); }
1111                                                   else { this.redraw(); this.clean=false; }
1112        };
1113
1114        // ---- Bounding box utility functions
1115
1116        OSMWay.prototype.resetBBox=function() {
1117                this.xmin=this.ymin= 999;
1118                this.xmax=this.ymax=-999;
1119        };
1120       
1121        OSMWay.prototype.updateBBox=function(long,lat) {
1122                this.xmin=Math.min(long,this.xmin);
1123                this.xmax=Math.max(long,this.xmax);
1124                this.ymin=Math.min(lat ,this.ymin);
1125                this.ymax=Math.max(lat ,this.ymax);
1126        };
1127
1128        // ---- Reset 'uploading' flag
1129
1130        OSMWay.prototype.notUploading=function() {
1131                this.uploading=false;
1132                var z=this.path; for (i in z) { this.path[i].uploading=false; }
1133                updateInspector();
1134        };
1135
1136        // ---- Node->way associations
1137       
1138        OSMWay.prototype.createNodeIndex  =function() { var z=this.path; for (var i in z) { this.path[i].addWay(this._name);    } };
1139        OSMWay.prototype.removeNodeIndex  =function() { var z=this.path; for (var i in z) { this.path[i].removeWay(this._name); } };
1140        OSMWay.prototype.renumberNodeIndex=function(n) {
1141                var z=this.path; for (i in z) { 
1142                        this.path[i].removeWay(this._name);
1143                        this.path[i].addWay(n);
1144                }
1145        };
1146        OSMWay.prototype.hasDependentNodes=function() {
1147        if (_root.sandbox && _root.uploading) { return false; }    // not an issue in consecutive uploads
1148                var d=false;
1149                var z=this.path; for (var i in z) {
1150                        if (this.path[i].id<0) {
1151                                var ways=this.path[i].ways; for (var w in ways) {
1152                                        if (_root.map.ways[w].uploading) { d=true; }
1153                                }
1154                        }
1155                }
1156                return d;
1157        };
1158
1159        // =====================================================================================
1160        // Offset path
1161        // ** much of the dialogue box could be refactored to share with relations dialogue
1162
1163        function askOffset() {
1164                if (!_root.wayselected) { return; }
1165                _root.windows.attachMovie("modal","offset",++windowdepth);
1166                _root.windows.offset.init(300, 170, [iText('cancel'), iText('ok')], completeOffset);
1167                var z = 5;
1168                var box = _root.windows.offset.box;
1169               
1170                box.createTextField("title",z++,7,7,300-14,20);
1171                box.title.text = iText('prompt_createparallel');
1172                with (box.title) {
1173                        wordWrap=true;
1174                        setTextFormat(boldText);
1175                        selectable=false; type='dynamic';
1176                }
1177                adjustTextField(box.title);
1178               
1179                box.createTextField("instr",z++,7,30,300-14,40);
1180
1181                // Create radio buttons and menu
1182
1183                box.attachMovie("radio","offsetoption",z++);
1184                box.offsetoption.addButton(10,35 ,iText('offset_dual'));
1185                box.offsetoption.addButton(10,55 ,iText('offset_motorway'));
1186                box.offsetoption.addButton(10,75 ,iText('offset_narrowcanal'));
1187                box.offsetoption.addButton(10,95 ,iText('offset_broadcanal'));
1188                box.offsetoption.addButton(10,115,iText('offset_choose'));
1189                box.offsetoption.select(1);
1190
1191                var w=box.offsetoption[5].prompt._width+25;
1192                box.createTextField("useroffset",z++,w,110,290-w,17);
1193                box.useroffset.setNewTextFormat(plainSmall);
1194                box.useroffset.type='input';
1195                box.useroffset.backgroundColor=0xDDDDDD;
1196                box.useroffset.background=true;
1197                box.useroffset.border=true;
1198                box.useroffset.borderColor=0xFFFFFF;
1199                box.useroffset.onSetFocus=function() { this._parent.offsetoption.select(5); };
1200        }
1201
1202        // typical widths:
1203        // central reservation:
1204        //       4.5m on a rural motorway/dual carriageway
1205        //       3.5m on an urban motorway
1206        //       1.8m on an urban dual carriageway
1207        // lane widths are typically always 3.65m
1208        // hard shoulders are typically 3.30m
1209        // hard strips are typically 1m
1210
1211        function completeOffset(button) {
1212                if (button!=iText('ok')) { return false; }
1213                var radio=_root.windows.offset.box.offsetoption.selected;
1214                var m;
1215                if (radio==5) {
1216                        m=_root.windows.offset.box.useroffset.text;
1217                        if (!button) { return false; }
1218                } else {
1219                        m=new Array(0,4.5+3.65*2,4.5+3.65*3,5.5,11);
1220                        m=m[radio];
1221                }
1222                var thislat=coord2lat(_root.map._y);                    // near as dammit
1223                var latfactor=Math.cos(thislat/(180/Math.PI));  // 111200m in a degree at the equator
1224                m=masterscale/(111200*latfactor)*m;                             // 111200m*cos(lat in radians) elsewhere
1225                _root.ws.offset( m);
1226                _root.ws.offset(-m);
1227                _root.undo.append(UndoStack.prototype.undo_makeways,
1228                                                  new Array(_root.newwayid,_root.newwayid+1),
1229                                                  iText('action_createparallel'));
1230        }
1231
1232        // Create (locked) offset way
1233        // offset is + or - depending on which side
1234
1235        OSMWay.prototype.offset=function(tpoffset) {
1236                var a,b,o,df,x,y;
1237                var offsetx=new Array();
1238                var offsety=new Array();
1239                var wm=1;       // was 10 before
1240
1241                _root.newwayid--;                                                                                       // create new way
1242                _root.map.ways.attachMovie("way",newwayid,++waydepth);          //  |
1243                var nw=_root.map.ways[newwayid];
1244                nw.locked=true;
1245                nw.clean=false;
1246
1247                // Normalise, and calculate offset vectors
1248
1249                for (var i=0; i<this.path.length; i++) {
1250                        a=this.path[i  ].y - this.path[i+1].y;
1251                        b=this.path[i+1].x - this.path[i  ].x;
1252                        h=Math.sqrt(a*a+b*b);
1253                        if (h!=0) { a=a/h; b=b/h; }
1254                                 else { a=0; b=0; }
1255                        offsetx[i]=wm*a;
1256                        offsety[i]=wm*b;
1257                }
1258
1259                _root.newnodeid--;
1260                _root.nodes[newnodeid]=new Node(newnodeid,this.path[0].x+tpoffset*offsetx[0],
1261                                                                                                  this.path[0].y+tpoffset*offsety[0],
1262                                                                                                  new Object(),0);
1263                _root.nodes[newnodeid].addWay(newwayid);
1264                nw.path.push(_root.nodes[newnodeid]);
1265               
1266                for (i=1; i<(this.path.length-1); i++) {
1267       
1268                        a=det(offsetx[i]-offsetx[i-1],
1269                                  offsety[i]-offsety[i-1],
1270                                  this.path[i+1].x - this.path[i  ].x,
1271                                  this.path[i+1].y - this.path[i  ].y);
1272                        b=det(this.path[i  ].x - this.path[i-1].x,
1273                                  this.path[i  ].y - this.path[i-1].y,
1274                                  this.path[i+1].x - this.path[i  ].x,
1275                                  this.path[i+1].y - this.path[i  ].y);
1276                        if (b!=0) { df=a/b; } else { df=0; }
1277                       
1278                        x=this.path[i].x + tpoffset*(offsetx[i-1]+df*(this.path[i].x - this.path[i-1].x));
1279                        y=this.path[i].y + tpoffset*(offsety[i-1]+df*(this.path[i].y - this.path[i-1].y));
1280       
1281                        _root.newnodeid--;
1282                        _root.nodes[newnodeid]=new Node(newnodeid,x,y,new Object(),0);
1283                        _root.nodes[newnodeid].addWay(newwayid);
1284                        nw.path.push(_root.nodes[newnodeid]);
1285                }
1286       
1287                _root.newnodeid--;
1288                _root.nodes[newnodeid]=new Node(newnodeid,this.path[i].x+tpoffset*offsetx[i-1],
1289                                                                                                  this.path[i].y+tpoffset*offsety[i-1],new Object(),0);
1290                _root.nodes[newnodeid].addWay(newwayid);
1291                nw.path.push(_root.nodes[newnodeid]);
1292                nw.redraw();
1293        };
1294
1295    function det(a,b,c,d) { return a*d-b*c; }
1296
1297        // =====================================================================================
1298        // Inspector
1299       
1300        OSMWay.prototype.inspect=function() {
1301                var str='';
1302
1303                // Status
1304                if (this.locked) { str+=iText('inspector_way_locked') + "\n"; }
1305                if (!this.clean) { str+=iText('inspector_unsaved'); }
1306                if (this.uploading) { str+=' ' + iText('inspector_uploading'); }
1307                if (!this.clean || this.uploading) { str+="\n"; }
1308
1309                // Number of nodes
1310                if (this.path[this.path.length-1]==this.path[0]) {
1311            str += iText('inspector_way_nodes_closed', this.path.length);
1312        } else {
1313            str += iText('inspector_way_nodes', this.path.length);
1314        }
1315                str+="\n";
1316
1317                // Connections to other ways of same type
1318                var principal='';
1319                if      (this.attr['highway' ]) { principal='highway';  }
1320                else if (this.attr['waterway']) { principal='waterway'; }
1321                else if (this.attr['railway' ]) { principal='railway';  }
1322                var same=0; var different=0;
1323                var z=this.path; for (i in z) {
1324                        var w=z[i].ways; for (id in w) {
1325                                if (id!=this._name) {
1326                                        if (_root.map.ways[id].attr[principal]==this.attr[principal]) { same++; }
1327                                        else if (_root.map.ways[id].attr[principal]) { different++; }
1328                                }
1329                        }
1330                }
1331
1332                if (this.attr[principal]==undefined) { 
1333            str += iText('inspector_way_connects_to', same);
1334                } else {
1335            str += iText('inspector_way_connects_to_principal', same, this.attr[principal], different, principal);
1336                }
1337
1338                return "<p>"+str+"</p>";
1339        };
1340
1341
1342
1343        Object.registerClass("way",OSMWay);
1344
1345
1346        // =====================================================================================
1347        // Drawing support functions
1348
1349        // removeNodeFromWays - now see Node.removeFromAllWays
1350
1351        // startNewWay            - create new way from first node
1352
1353        function startNewWay(node) {
1354                uploadSelected();
1355                _root.newwayid--;
1356                selectWay(newwayid);
1357                _root.poiselected=0;
1358                _root.map.ways.attachMovie("way",newwayid,++waydepth);
1359                _root.map.ways[newwayid].path[0]=_root.nodes[node];
1360                _root.map.ways[newwayid].redraw();
1361                _root.map.ways[newwayid].select();
1362                _root.map.ways[newwayid].clean=false;
1363                _root.nodes[node].addWay(newwayid);
1364                _root.map.anchors[0].startElastic();
1365                _root.drawpoint=0;
1366                markClean(false);
1367                setTooltip(iText('hint_drawmode'),0);
1368        }
1369
1370        // addEndPoint(node object) - add point to start/end of line
1371
1372        function addEndPoint(node) {
1373                var drawnode=_root.ws.path[_root.drawpoint];
1374                if (_root.drawpoint==_root.ws.path.length-1) {
1375                        _root.ws.path.push(node);
1376                        _root.drawpoint=_root.ws.path.length-1;
1377                } else {
1378                        _root.ws.path.unshift(node);    // drawpoint=0, add to start
1379                }
1380                node.addWay(_root.wayselected);
1381       
1382                // Redraw line (if possible, just extend it to save time)
1383                if (_root.ws.getFill()>-1 || 
1384                        _root.ws.path.length<3 ||
1385                        _root.pointselected>-2 ||
1386                        _root.ws.attr['oneway']) {
1387                        _root.ws.redraw();
1388                        _root.ws.select();
1389                } else {
1390                        _root.ws.line.moveTo(drawnode.x,drawnode.y);
1391                        _root.ws.line.lineTo(node.x,node.y);
1392                        if (casing[_root.ws.attr['highway']]) {
1393                                _root.map.areas[wayselected].moveTo(drawnode.x,drawnode.y);
1394                                _root.map.areas[wayselected].lineTo(node.x,node.y);
1395                        }
1396                        _root.map.highlight.moveTo(drawnode.x,drawnode.y);
1397                        _root.map.highlight.lineTo(node.x,node.y);
1398                        _root.ws.direction();
1399                        _root.ws.highlightPoints(5000,"anchor");
1400                        removeMovieClip(_root.map.anchorhints);
1401                        var z=_root.wayrels[_root.wayselected];
1402                        for (var rel in z) { _root.map.relations[rel].redraw(); }
1403                }
1404                // Mark as unclean
1405                _root.ws.clean=false;
1406                markClean(false);
1407                _root.map.elastic.clear();
1408                var poslist=new Array(); poslist.push(_root.drawpoint);
1409                _root.undo.append(UndoStack.prototype.undo_addpoint,
1410                                                  new Array(new Array(_root.ws),poslist),
1411                                                  iText('action_addpoint'));
1412                updateInspector();
1413        }
1414
1415        function stopDrawing() {
1416                _root.map.anchors[_root.drawpoint].endElastic();
1417                _root.drawpoint=-1;
1418                if (_root.ws.path.length<=1) { 
1419                        // way not long enough, so abort
1420                        _root.ws.removeNodeIndex();
1421                        removeMovieClip(_root.map.areas[wayselected]);
1422                        removeMovieClip(_root.ws);
1423                        removeMovieClip(_root.map.anchors);
1424                        updateInspector();
1425                }
1426                _root.map.elastic.clear();
1427                clearTooltip();
1428        };
1429
1430        // cycleStacked - cycle through ways sharing same point
1431
1432        function cycleStacked() {
1433                if (_root.pointselected>-2) {
1434                        stopDrawing();
1435                        var id=_root.ws.path[_root.pointselected].id;
1436                        var firstfound=0; var nextfound=0;
1437                        for (qway in _root.map.ways) {
1438                                if (qway!=_root.wayselected) {
1439                                        for (qs=0; qs<_root.map.ways[qway].path.length; qs+=1) {
1440                                                if (_root.map.ways[qway].path[qs].id==id) {
1441                                                        var qw=Math.floor(qway);
1442                                                        if (firstfound==0 || qw<firstfound) { firstfound=qw; }
1443                                                        if ((nextfound==0 || qw<nextfound) && qw>wayselected) { nextfound=qw; }
1444                                                }
1445                                        }
1446                                }
1447                        }
1448                        if (firstfound) {
1449                                if (nextfound==0) { var nextfound=firstfound; }
1450                                _root.map.ways[nextfound].swapDepths(_root.ws);
1451                                _root.map.ways[nextfound].select();
1452                        }
1453                }
1454        };
1455
1456        // ================================================================
1457        // Way communication
1458       
1459        // whichWays    - get list of ways from remoting server
1460
1461        function whichWays(force) {
1462                _root.lastwhichways=new Date();
1463                var within=(_root.edge_l>=_root.bigedge_l &&
1464                                        _root.edge_r<=_root.bigedge_r &&
1465                                        _root.edge_b>=_root.bigedge_b &&
1466                                        _root.edge_t<=_root.bigedge_t);
1467                if (_root.waycount>500) { purgeWays(); }
1468                if (_root.poicount>500) { purgePOIs(); }
1469                if (within && !force) {
1470                        // we have already loaded this area, so ignore
1471                } else {
1472                        whichresponder=function() {};
1473                        whichresponder.onResult=function(result) {
1474                                _root.whichreceived+=1;
1475                                var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
1476                                var waylist  =result[0];
1477                                var pointlist=result[1];
1478                                var relationlist=result[2];
1479                                var s;
1480
1481                                for (i in pointlist) {                                                                          // POIs
1482                                        point=pointlist[i][0];                                                                  //  |
1483                                        if ((!_root.map.pois[point] || _root.map.pois[point].version!=pointlist[i][4]) && !_root.poistodelete[point]) {
1484                                                var a=getPOIIcon(pointlist[i][3]);                                      //  |
1485                                                if (a=='poi') { s=poiscale; } else { s=iconscale; }     //  |
1486                                                _root.map.pois.attachMovie(a,point,++poidepth); //  |
1487                                                _root.map.pois[point]._x=long2coord(pointlist[i][1]);// |
1488                                                _root.map.pois[point]._y=lat2coord (pointlist[i][2]);// |
1489                                                _root.map.pois[point]._xscale=_root.map.pois[point]._yscale=s;
1490                                                _root.map.pois[point].version=pointlist[i][4];          //  |
1491                                                _root.map.pois[point].attr=pointlist[i][3];                     //  |
1492                                                _root.map.pois[point].icon=a;                                           //  |
1493                                                _root.poicount+=1;                                                                      //  |
1494                                                if (point==prenode) { deselectAll(); prenode=undefined;
1495                                                                                          _root.map.pois[point].select(); }
1496                                        }
1497                                }
1498
1499                                for (i in waylist) {                                                                            // ways
1500                                        way=waylist[i][0];                                                                              //  |
1501                                        if ((!_root.map.ways[way] || _root.map.ways[way].version!=waylist[i][1]) && !_root.waystodelete[way]) {
1502                                                _root.waystoload.push(way);
1503                                        }
1504                                }
1505                               
1506                                for (i in relationlist) {
1507                                        rel = relationlist[i][0];
1508                    if (!_root.map.relations[rel] || _root.map.relations[rel].version!=relationlist[i][1]) {
1509                                                _root.relstoload.push(rel);
1510                                        }
1511                }
1512
1513                                if (preferences.data.limitways && _root.relstoload.length+_root.waystoload.length>1200) {
1514                                        _root.windows.attachMovie("modal","confirm",++windowdepth);
1515                                        _root.windows.confirm.init(275,100,new Array(iText('no'),iText('yes')),
1516                                                function(choice) {
1517                                                        if (choice==iText('yes')) { 
1518                                                                _root.waystoload=[];
1519                                                                _root.relstoload=[];
1520                                                                zoomTo(Math.min(_root.scale+2,_root.maxscale),_root.map._x*2-xradius,_root.map._y*2-yradius,true,true);
1521                                                        } else {
1522                                                                loadItems();
1523                                                        }
1524                                                });
1525                                        _root.windows.confirm.box.createTextField("prompt",2,7,9,250,120);
1526                                        writeText(_root.windows.confirm.box.prompt,iText('prompt_manyways'));
1527                                } else {
1528                                        loadItems();
1529                                }
1530
1531                        };
1532                        remote_read.call('whichways',whichresponder,_root.edge_l,_root.edge_b,_root.edge_r,_root.edge_t);
1533                        _root.whichrequested+=1;
1534                }
1535        }
1536
1537        // loadWay/loadRelation
1538
1539        function loadItems() {
1540                while (_root.waystoload.length) { loadWay(_root.waystoload.pop()); }
1541                while (_root.relstoload.length) { loadRelation(_root.relstoload.pop()); }
1542                if (_root.edge_l<_root.bigedge_l ||
1543                        _root.edge_r>_root.bigedge_r ||
1544                        _root.edge_t>_root.bigedge_t ||
1545                        _root.edge_b<_root.bigedge_b) { setBigEdges(); }
1546        }
1547
1548        function loadWay(way) {
1549                _root.map.ways.attachMovie("way",way,++waydepth);
1550                _root.map.ways[way].load();
1551                _root.waycount+=1;
1552                _root.waysrequested+=1;
1553        }
1554       
1555        function loadRelation(rel) {
1556                _root.map.relations.attachMovie("relation",rel,++reldepth);
1557                _root.map.relations[rel].load();
1558                _root.relcount+=1;
1559                _root.relsrequested+=1;
1560        }
1561       
1562        function setBigEdges() {
1563                _root.bigedge_l=_root.edge_l; _root.bigedge_r=_root.edge_r;
1564                _root.bigedge_b=_root.edge_b; _root.bigedge_t=_root.edge_t;
1565        }
1566
1567        // purgeWays - remove any clean ways outside current view
1568       
1569        function purgeWays() {
1570                for (qway in _root.map.ways) {
1571                        if (qway==_root.wayselected) {
1572                        } else if (!_root.map.ways[qway].clean) {
1573                                _root.map.ways[qway].upload();
1574                                uploadDirtyRelations();
1575                        } else if (((_root.map.ways[qway].xmin<edge_l && _root.map.ways[qway].xmax<edge_l) ||
1576                                                (_root.map.ways[qway].xmin>edge_r && _root.map.ways[qway].xmax>edge_r) ||
1577                                            (_root.map.ways[qway].ymin<edge_b && _root.map.ways[qway].ymax<edge_b) ||
1578                                                (_root.map.ways[qway].ymin>edge_t && _root.map.ways[qway].ymax>edge_t))) {
1579                                removeMovieClip(_root.map.ways[qway]);
1580                                removeMovieClip(_root.map.areas[qway]);
1581                                _root.waycount-=1;
1582                        }
1583                }
1584                setBigEdges();
1585        }
1586
1587        function selectWay(id) {
1588                _root.lastwayselected=_root.wayselected;
1589                _root.wayselected=Math.floor(id);
1590                _root.ws=_root.map.ways[id];
1591               
1592                if (id==0) {
1593                        _root.panel.advanced.disableOption(0);
1594                        _root.panel.advanced.disableOption(1);
1595                } else {
1596                        _root.panel.advanced.enableOption(0);
1597                        _root.panel.advanced.enableOption(1);
1598                }
1599                updateInspector();
1600        }
1601       
1602        function renumberWay(ow,nw) {
1603                _root.map.ways[ow].renumberNodeIndex(nw);
1604                wayrels[nw]=wayrels[ow]; delete wayrels[ow];
1605                _root.map.ways[ow]._name=nw;
1606                renumberMemberOfRelation('Way', ow, nw);
1607                if (_root.map.areas[ow]) { _root.map.areas[ow]._name=nw; }
1608                if (_root.panel.t_details.text==ow) { _root.panel.t_details.text=nw; _root.panel.t_details.setTextFormat(plainText); }
1609                if (wayselected==ow) { selectWay(nw); }
1610        }
1611       
1612        function mergeWayKeepingID(w1,w2) {
1613                var t=(w1==_root.ws || w2==_root.ws);
1614                var w,s;
1615                if (Number(w1._name)<0 && Number(w2._name)>0) {
1616                        s=w1.mergeAtCommonPoint(w2); w=w2;
1617                } else {
1618                        s=w2.mergeAtCommonPoint(w1); w=w1;
1619                }
1620                if (s) { w.redraw(); if (t) { w.select(); } }
1621        }
1622
1623        function uploadDirtyWays(allow_ws) {
1624                if (_root.sandbox) { return; }
1625                var z=_root.map.ways;
1626                for (i in z) {
1627                        if (!_root.map.ways[i].clean && (i!=wayselected || allow_ws) && !_root.map.ways[i].hasDependentNodes()) { 
1628                                _root.map.ways[i].upload();
1629                        }
1630                }
1631        };
1632
Note: See TracBrowser for help on using the repository browser.