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

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

couple of little fixes

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