leaflet.draw-src.js 161 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769
  1. /*
  2. Leaflet.draw 0.4.14, a plugin that adds drawing and editing tools to Leaflet powered maps.
  3. (c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet
  4. https://github.com/Leaflet/Leaflet.draw
  5. http://leafletjs.com
  6. */
  7. (function (window, document, undefined) {
  8. /**
  9. * Leaflet.draw assumes that you have already included the Leaflet library.
  10. */
  11. L.drawVersion = "0.4.14";
  12. /**
  13. * @class L.Draw
  14. * @aka Draw
  15. *
  16. *
  17. * To add the draw toolbar set the option drawControl: true in the map options.
  18. *
  19. * @example
  20. * ```js
  21. * var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13);
  22. *
  23. * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  24. * attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
  25. * }).addTo(map);
  26. * ```
  27. *
  28. * ### Adding the edit toolbar
  29. * To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map.
  30. *
  31. * ```js
  32. * var map = L.map('map').setView([51.505, -0.09], 13);
  33. *
  34. * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  35. * attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
  36. * }).addTo(map);
  37. *
  38. * // FeatureGroup is to store editable layers
  39. * var drawnItems = new L.FeatureGroup();
  40. * map.addLayer(drawnItems);
  41. *
  42. * var drawControl = new L.Control.Draw({
  43. * edit: {
  44. * featureGroup: drawnItems
  45. * }
  46. * });
  47. * map.addControl(drawControl);
  48. * ```
  49. *
  50. * The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that
  51. * should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon.
  52. * Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon,
  53. * or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a
  54. * FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons).
  55. */
  56. L.Draw = {};
  57. /**
  58. * @class L.drawLocal
  59. * @aka L.drawLocal
  60. *
  61. * The core toolbar class of the API — it is used to create the toolbar ui
  62. *
  63. * @example
  64. * ```js
  65. * var modifiedDraw = L.drawLocal.extend({
  66. * draw: {
  67. * toolbar: {
  68. * buttons: {
  69. * polygon: 'Draw an awesome polygon'
  70. * }
  71. * }
  72. * }
  73. * });
  74. * ```
  75. *
  76. * The default state for the control is the draw toolbar just below the zoom control.
  77. * This will allow map users to draw vectors and markers.
  78. * **Please note the edit toolbar is not enabled by default.**
  79. */
  80. L.drawLocal = {
  81. // format: {
  82. // numeric: {
  83. // delimiters: {
  84. // thousands: ',',
  85. // decimal: '.'
  86. // }
  87. // }
  88. // },
  89. draw: {
  90. toolbar: {
  91. // #TODO: this should be reorganized where actions are nested in actions
  92. // ex: actions.undo or actions.cancel
  93. actions: {
  94. title: 'Cancel drawing',
  95. text: 'Cancel'
  96. },
  97. finish: {
  98. title: 'Finish drawing',
  99. text: 'Finish'
  100. },
  101. undo: {
  102. title: 'Delete last point drawn',
  103. text: 'Delete last point'
  104. },
  105. buttons: {
  106. polyline: 'Draw a polyline',
  107. polygon: 'Draw a polygon',
  108. rectangle: 'Draw a rectangle',
  109. circle: 'Draw a circle',
  110. marker: 'Draw a marker',
  111. circlemarker: 'Draw a circlemarker'
  112. }
  113. },
  114. handlers: {
  115. circle: {
  116. tooltip: {
  117. start: 'Click and drag to draw circle.'
  118. },
  119. radius: 'Radius'
  120. },
  121. circlemarker: {
  122. tooltip: {
  123. start: 'Click map to place circle marker.'
  124. }
  125. },
  126. marker: {
  127. tooltip: {
  128. start: 'Click map to place marker.'
  129. }
  130. },
  131. polygon: {
  132. tooltip: {
  133. start: 'Click to start drawing shape.',
  134. cont: 'Click to continue drawing shape.',
  135. end: 'Click first point to close this shape.'
  136. }
  137. },
  138. polyline: {
  139. error: '<strong>Error:</strong> shape edges cannot cross!',
  140. tooltip: {
  141. start: 'Click to start drawing line.',
  142. cont: 'Click to continue drawing line.',
  143. end: 'Click last point to finish line.'
  144. }
  145. },
  146. rectangle: {
  147. tooltip: {
  148. start: 'Click and drag to draw rectangle.'
  149. }
  150. },
  151. simpleshape: {
  152. tooltip: {
  153. end: 'Release mouse to finish drawing.'
  154. }
  155. }
  156. }
  157. },
  158. edit: {
  159. toolbar: {
  160. actions: {
  161. save: {
  162. title: 'Save changes',
  163. text: 'Save'
  164. },
  165. cancel: {
  166. title: 'Cancel editing, discards all changes',
  167. text: 'Cancel'
  168. },
  169. clearAll: {
  170. title: 'Clear all layers',
  171. text: 'Clear All'
  172. }
  173. },
  174. buttons: {
  175. edit: 'Edit layers',
  176. editDisabled: 'No layers to edit',
  177. remove: 'Delete layers',
  178. removeDisabled: 'No layers to delete'
  179. }
  180. },
  181. handlers: {
  182. edit: {
  183. tooltip: {
  184. text: 'Drag handles or markers to edit features.',
  185. subtext: 'Click cancel to undo changes.'
  186. }
  187. },
  188. remove: {
  189. tooltip: {
  190. text: 'Click on a feature to remove.'
  191. }
  192. }
  193. }
  194. }
  195. };
  196. /**
  197. * ### Events
  198. * Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different
  199. * actions users can initiate. The following events will be triggered on the map:
  200. *
  201. * @class L.Draw.Event
  202. * @aka Draw.Event
  203. *
  204. * Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct.
  205. *
  206. * @example
  207. * ```js
  208. * map.on(L.Draw.Event.CREATED; function (e) {
  209. * var type = e.layerType,
  210. * layer = e.layer;
  211. *
  212. * if (type === 'marker') {
  213. * // Do marker specific actions
  214. * }
  215. *
  216. * // Do whatever else you need to. (save to db; add to map etc)
  217. * map.addLayer(layer);
  218. *});
  219. * ```
  220. */
  221. L.Draw.Event = {};
  222. /**
  223. * @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String
  224. *
  225. * Layer that was just created.
  226. * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`
  227. * Triggered when a new vector or marker has been created.
  228. *
  229. */
  230. L.Draw.Event.CREATED = 'draw:created';
  231. /**
  232. * @event draw:edited: LayerGroup
  233. *
  234. * List of all layers just edited on the map.
  235. *
  236. *
  237. * Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved.
  238. *
  239. * @example
  240. * ```js
  241. * map.on('draw:edited', function (e) {
  242. * var layers = e.layers;
  243. * layers.eachLayer(function (layer) {
  244. * //do whatever you want; most likely save back to db
  245. * });
  246. * });
  247. * ```
  248. */
  249. L.Draw.Event.EDITED = 'draw:edited';
  250. /**
  251. * @event draw:deleted: LayerGroup
  252. *
  253. * List of all layers just removed from the map.
  254. *
  255. * Triggered when layers have been removed (and saved) from the FeatureGroup.
  256. */
  257. L.Draw.Event.DELETED = 'draw:deleted';
  258. /**
  259. * @event draw:drawstart: String
  260. *
  261. * The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker`
  262. *
  263. * Triggered when the user has chosen to draw a particular vector or marker.
  264. */
  265. L.Draw.Event.DRAWSTART = 'draw:drawstart';
  266. /**
  267. * @event draw:drawstop: String
  268. *
  269. * The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`
  270. *
  271. * Triggered when the user has finished a particular vector or marker.
  272. */
  273. L.Draw.Event.DRAWSTOP = 'draw:drawstop';
  274. /**
  275. * @event draw:drawvertex: LayerGroup
  276. *
  277. * List of all layers just being added from the map.
  278. *
  279. * Triggered when a vertex is created on a polyline or polygon.
  280. */
  281. L.Draw.Event.DRAWVERTEX = 'draw:drawvertex';
  282. /**
  283. * @event draw:editstart: String
  284. *
  285. * The type of edit this is. One of: `edit`
  286. *
  287. * Triggered when the user starts edit mode by clicking the edit tool button.
  288. */
  289. L.Draw.Event.EDITSTART = 'draw:editstart';
  290. /**
  291. * @event draw:editmove: ILayer
  292. *
  293. * Layer that was just moved.
  294. *
  295. * Triggered as the user moves a rectangle; circle or marker.
  296. */
  297. L.Draw.Event.EDITMOVE = 'draw:editmove';
  298. /**
  299. * @event draw:editresize: ILayer
  300. *
  301. * Layer that was just moved.
  302. *
  303. * Triggered as the user resizes a rectangle or circle.
  304. */
  305. L.Draw.Event.EDITRESIZE = 'draw:editresize';
  306. /**
  307. * @event draw:editvertex: LayerGroup
  308. *
  309. * List of all layers just being edited from the map.
  310. *
  311. * Triggered when a vertex is edited on a polyline or polygon.
  312. */
  313. L.Draw.Event.EDITVERTEX = 'draw:editvertex';
  314. /**
  315. * @event draw:editstop: String
  316. *
  317. * The type of edit this is. One of: `edit`
  318. *
  319. * Triggered when the user has finshed editing (edit mode) and saves edits.
  320. */
  321. L.Draw.Event.EDITSTOP = 'draw:editstop';
  322. /**
  323. * @event draw:deletestart: String
  324. *
  325. * The type of edit this is. One of: `remove`
  326. *
  327. * Triggered when the user starts remove mode by clicking the remove tool button.
  328. */
  329. L.Draw.Event.DELETESTART = 'draw:deletestart';
  330. /**
  331. * @event draw:deletestop: String
  332. *
  333. * The type of edit this is. One of: `remove`
  334. *
  335. * Triggered when the user has finished removing shapes (remove mode) and saves.
  336. */
  337. L.Draw.Event.DELETESTOP = 'draw:deletestop';
  338. /**
  339. * @event draw:toolbaropened: String
  340. *
  341. * Triggered when a toolbar is opened.
  342. */
  343. L.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened';
  344. /**
  345. * @event draw:toolbarclosed: String
  346. *
  347. * Triggered when a toolbar is closed.
  348. */
  349. L.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed';
  350. /**
  351. * @event draw:markercontext: String
  352. *
  353. * Triggered when a marker is right clicked.
  354. */
  355. L.Draw.Event.MARKERCONTEXT = 'draw:markercontext';
  356. L.Draw = L.Draw || {};
  357. /**
  358. * @class L.Draw.Feature
  359. * @aka Draw.Feature
  360. */
  361. L.Draw.Feature = L.Handler.extend({
  362. // @method initialize(): void
  363. initialize: function (map, options) {
  364. this._map = map;
  365. this._container = map._container;
  366. this._overlayPane = map._panes.overlayPane;
  367. this._popupPane = map._panes.popupPane;
  368. // Merge default shapeOptions options with custom shapeOptions
  369. if (options && options.shapeOptions) {
  370. options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
  371. }
  372. L.setOptions(this, options);
  373. var version = L.version.split('.');
  374. //If Version is >= 1.2.0
  375. if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
  376. L.Draw.Feature.include(L.Evented.prototype);
  377. } else {
  378. L.Draw.Feature.include(L.Mixin.Events);
  379. }
  380. },
  381. // @method enable(): void
  382. // Enables this handler
  383. enable: function () {
  384. if (this._enabled) {
  385. return;
  386. }
  387. L.Handler.prototype.enable.call(this);
  388. this.fire('enabled', {handler: this.type});
  389. this._map.fire(L.Draw.Event.DRAWSTART, {layerType: this.type});
  390. },
  391. // @method disable(): void
  392. disable: function () {
  393. if (!this._enabled) {
  394. return;
  395. }
  396. L.Handler.prototype.disable.call(this);
  397. this._map.fire(L.Draw.Event.DRAWSTOP, {layerType: this.type});
  398. this.fire('disabled', {handler: this.type});
  399. },
  400. // @method addHooks(): void
  401. // Add's event listeners to this handler
  402. addHooks: function () {
  403. var map = this._map;
  404. if (map) {
  405. L.DomUtil.disableTextSelection();
  406. map.getContainer().focus();
  407. this._tooltip = new L.Draw.Tooltip(this._map);
  408. L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);
  409. }
  410. },
  411. // @method removeHooks(): void
  412. // Removes event listeners from this handler
  413. removeHooks: function () {
  414. if (this._map) {
  415. L.DomUtil.enableTextSelection();
  416. this._tooltip.dispose();
  417. this._tooltip = null;
  418. L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);
  419. }
  420. },
  421. // @method setOptions(object): void
  422. // Sets new options to this handler
  423. setOptions: function (options) {
  424. L.setOptions(this, options);
  425. },
  426. _fireCreatedEvent: function (layer) {
  427. this._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type});
  428. },
  429. // Cancel drawing when the escape key is pressed
  430. _cancelDrawing: function (e) {
  431. if (e.keyCode === 27) {
  432. this._map.fire('draw:canceled', {layerType: this.type});
  433. this.disable();
  434. }
  435. }
  436. });
  437. /**
  438. * @class L.Draw.Polyline
  439. * @aka Draw.Polyline
  440. * @inherits L.Draw.Feature
  441. */
  442. L.Draw.Polyline = L.Draw.Feature.extend({
  443. statics: {
  444. TYPE: 'polyline'
  445. },
  446. Poly: L.Polyline,
  447. options: {
  448. allowIntersection: true,
  449. repeatMode: false,
  450. drawError: {
  451. color: '#b00b00',
  452. timeout: 2500
  453. },
  454. icon: new L.DivIcon({
  455. iconSize: new L.Point(8, 8),
  456. className: 'leaflet-div-icon leaflet-editing-icon'
  457. }),
  458. touchIcon: new L.DivIcon({
  459. iconSize: new L.Point(20, 20),
  460. className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
  461. }),
  462. guidelineDistance: 20,
  463. maxGuideLineLength: 4000,
  464. shapeOptions: {
  465. stroke: true,
  466. color: '#3388ff',
  467. weight: 4,
  468. opacity: 0.5,
  469. fill: false,
  470. clickable: true
  471. },
  472. metric: true, // Whether to use the metric measurement system or imperial
  473. feet: true, // When not metric, to use feet instead of yards for display.
  474. nautic: false, // When not metric, not feet use nautic mile for display
  475. showLength: true, // Whether to display distance in the tooltip
  476. zIndexOffset: 2000, // This should be > than the highest z-index any map layers
  477. factor: 1, // To change distance calculation
  478. maxPoints: 0 // Once this number of points are placed, finish shape
  479. },
  480. // @method initialize(): void
  481. initialize: function (map, options) {
  482. // if touch, switch to touch icon
  483. if (L.Browser.touch) {
  484. this.options.icon = this.options.touchIcon;
  485. }
  486. // Need to set this here to ensure the correct message is used.
  487. this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;
  488. // Merge default drawError options with custom options
  489. if (options && options.drawError) {
  490. options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
  491. }
  492. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  493. this.type = L.Draw.Polyline.TYPE;
  494. L.Draw.Feature.prototype.initialize.call(this, map, options);
  495. },
  496. // @method addHooks(): void
  497. // Add listener hooks to this handler
  498. addHooks: function () {
  499. L.Draw.Feature.prototype.addHooks.call(this);
  500. if (this._map) {
  501. this._markers = [];
  502. this._markerGroup = new L.LayerGroup();
  503. this._map.addLayer(this._markerGroup);
  504. this._poly = new L.Polyline([], this.options.shapeOptions);
  505. this._tooltip.updateContent(this._getTooltipText());
  506. // Make a transparent marker that will used to catch click events. These click
  507. // events will create the vertices. We need to do this so we can ensure that
  508. // we can create vertices over other map layers (markers, vector layers). We
  509. // also do not want to trigger any click handlers of objects we are clicking on
  510. // while drawing.
  511. if (!this._mouseMarker) {
  512. this._mouseMarker = L.marker(this._map.getCenter(), {
  513. icon: L.divIcon({
  514. className: 'leaflet-mouse-marker',
  515. iconAnchor: [20, 20],
  516. iconSize: [40, 40]
  517. }),
  518. opacity: 0,
  519. zIndexOffset: this.options.zIndexOffset
  520. });
  521. }
  522. this._mouseMarker
  523. .on('mouseout', this._onMouseOut, this)
  524. .on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter
  525. .on('mousedown', this._onMouseDown, this)
  526. .on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility
  527. .addTo(this._map);
  528. this._map
  529. .on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility
  530. .on('mousemove', this._onMouseMove, this)
  531. .on('zoomlevelschange', this._onZoomEnd, this)
  532. .on('touchstart', this._onTouch, this)
  533. .on('zoomend', this._onZoomEnd, this);
  534. }
  535. },
  536. // @method removeHooks(): void
  537. // Remove listener hooks from this handler.
  538. removeHooks: function () {
  539. L.Draw.Feature.prototype.removeHooks.call(this);
  540. this._clearHideErrorTimeout();
  541. this._cleanUpShape();
  542. // remove markers from map
  543. this._map.removeLayer(this._markerGroup);
  544. delete this._markerGroup;
  545. delete this._markers;
  546. this._map.removeLayer(this._poly);
  547. delete this._poly;
  548. this._mouseMarker
  549. .off('mousedown', this._onMouseDown, this)
  550. .off('mouseout', this._onMouseOut, this)
  551. .off('mouseup', this._onMouseUp, this)
  552. .off('mousemove', this._onMouseMove, this);
  553. this._map.removeLayer(this._mouseMarker);
  554. delete this._mouseMarker;
  555. // clean up DOM
  556. this._clearGuides();
  557. this._map
  558. .off('mouseup', this._onMouseUp, this)
  559. .off('mousemove', this._onMouseMove, this)
  560. .off('zoomlevelschange', this._onZoomEnd, this)
  561. .off('zoomend', this._onZoomEnd, this)
  562. .off('touchstart', this._onTouch, this)
  563. .off('click', this._onTouch, this);
  564. },
  565. // @method deleteLastVertex(): void
  566. // Remove the last vertex from the polyline, removes polyline from map if only one point exists.
  567. deleteLastVertex: function () {
  568. if (this._markers.length <= 1) {
  569. return;
  570. }
  571. var lastMarker = this._markers.pop(),
  572. poly = this._poly,
  573. // Replaces .spliceLatLngs()
  574. latlngs = poly.getLatLngs(),
  575. latlng = latlngs.splice(-1, 1)[0];
  576. this._poly.setLatLngs(latlngs);
  577. this._markerGroup.removeLayer(lastMarker);
  578. if (poly.getLatLngs().length < 2) {
  579. this._map.removeLayer(poly);
  580. }
  581. this._vertexChanged(latlng, false);
  582. },
  583. // @method addVertex(): void
  584. // Add a vertex to the end of the polyline
  585. addVertex: function (latlng) {
  586. var markersLength = this._markers.length;
  587. // markersLength must be greater than or equal to 2 before intersections can occur
  588. if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
  589. this._showErrorTooltip();
  590. return;
  591. } else if (this._errorShown) {
  592. this._hideErrorTooltip();
  593. }
  594. this._markers.push(this._createMarker(latlng));
  595. this._poly.addLatLng(latlng);
  596. if (this._poly.getLatLngs().length === 2) {
  597. this._map.addLayer(this._poly);
  598. }
  599. this._vertexChanged(latlng, true);
  600. },
  601. // @method completeShape(): void
  602. // Closes the polyline between the first and last points
  603. completeShape: function () {
  604. if (this._markers.length <= 1) {
  605. return;
  606. }
  607. this._fireCreatedEvent();
  608. this.disable();
  609. if (this.options.repeatMode) {
  610. this.enable();
  611. }
  612. },
  613. _finishShape: function () {
  614. var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs();
  615. var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]);
  616. if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
  617. this._showErrorTooltip();
  618. return;
  619. }
  620. this._fireCreatedEvent();
  621. this.disable();
  622. if (this.options.repeatMode) {
  623. this.enable();
  624. }
  625. },
  626. // Called to verify the shape is valid when the user tries to finish it
  627. // Return false if the shape is not valid
  628. _shapeIsValid: function () {
  629. return true;
  630. },
  631. _onZoomEnd: function () {
  632. if (this._markers !== null) {
  633. this._updateGuide();
  634. }
  635. },
  636. _onMouseMove: function (e) {
  637. var newPos = this._map.mouseEventToLayerPoint(e.originalEvent);
  638. var latlng = this._map.layerPointToLatLng(newPos);
  639. // Save latlng
  640. // should this be moved to _updateGuide() ?
  641. this._currentLatLng = latlng;
  642. this._updateTooltip(latlng);
  643. // Update the guide line
  644. this._updateGuide(newPos);
  645. // Update the mouse marker position
  646. this._mouseMarker.setLatLng(latlng);
  647. L.DomEvent.preventDefault(e.originalEvent);
  648. },
  649. _vertexChanged: function (latlng, added) {
  650. this._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup});
  651. this._updateFinishHandler();
  652. this._updateRunningMeasure(latlng, added);
  653. this._clearGuides();
  654. this._updateTooltip();
  655. },
  656. _onMouseDown: function (e) {
  657. if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) {
  658. this._onMouseMove(e);
  659. this._clickHandled = true;
  660. this._disableNewMarkers();
  661. var originalEvent = e.originalEvent;
  662. var clientX = originalEvent.clientX;
  663. var clientY = originalEvent.clientY;
  664. this._startPoint.call(this, clientX, clientY);
  665. }
  666. },
  667. _startPoint: function (clientX, clientY) {
  668. this._mouseDownOrigin = L.point(clientX, clientY);
  669. },
  670. _onMouseUp: function (e) {
  671. var originalEvent = e.originalEvent;
  672. var clientX = originalEvent.clientX;
  673. var clientY = originalEvent.clientY;
  674. this._endPoint.call(this, clientX, clientY, e);
  675. this._clickHandled = null;
  676. },
  677. _endPoint: function (clientX, clientY, e) {
  678. if (this._mouseDownOrigin) {
  679. var dragCheckDistance = L.point(clientX, clientY)
  680. .distanceTo(this._mouseDownOrigin);
  681. var lastPtDistance = this._calculateFinishDistance(e.latlng);
  682. if (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) {
  683. this.addVertex(e.latlng);
  684. this._finishShape();
  685. } else if (lastPtDistance < 10 && L.Browser.touch) {
  686. this._finishShape();
  687. } else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) {
  688. this.addVertex(e.latlng);
  689. }
  690. this._enableNewMarkers(); // after a short pause, enable new markers
  691. }
  692. this._mouseDownOrigin = null;
  693. },
  694. // ontouch prevented by clickHandled flag because some browsers fire both click/touch events,
  695. // causing unwanted behavior
  696. _onTouch: function (e) {
  697. var originalEvent = e.originalEvent;
  698. var clientX;
  699. var clientY;
  700. if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) {
  701. clientX = originalEvent.touches[0].clientX;
  702. clientY = originalEvent.touches[0].clientY;
  703. this._disableNewMarkers();
  704. this._touchHandled = true;
  705. this._startPoint.call(this, clientX, clientY);
  706. this._endPoint.call(this, clientX, clientY, e);
  707. this._touchHandled = null;
  708. }
  709. this._clickHandled = null;
  710. },
  711. _onMouseOut: function () {
  712. if (this._tooltip) {
  713. this._tooltip._onMouseOut.call(this._tooltip);
  714. }
  715. },
  716. // calculate if we are currently within close enough distance
  717. // of the closing point (first point for shapes, last point for lines)
  718. // this is semi-ugly code but the only reliable way i found to get the job done
  719. // note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work
  720. _calculateFinishDistance: function (potentialLatLng) {
  721. var lastPtDistance;
  722. if (this._markers.length > 0) {
  723. var finishMarker;
  724. if (this.type === L.Draw.Polyline.TYPE) {
  725. finishMarker = this._markers[this._markers.length - 1];
  726. } else if (this.type === L.Draw.Polygon.TYPE) {
  727. finishMarker = this._markers[0];
  728. } else {
  729. return Infinity;
  730. }
  731. var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()),
  732. potentialMarker = new L.Marker(potentialLatLng, {
  733. icon: this.options.icon,
  734. zIndexOffset: this.options.zIndexOffset * 2
  735. });
  736. var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng());
  737. lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint);
  738. } else {
  739. lastPtDistance = Infinity;
  740. }
  741. return lastPtDistance;
  742. },
  743. _updateFinishHandler: function () {
  744. var markerCount = this._markers.length;
  745. // The last marker should have a click handler to close the polyline
  746. if (markerCount > 1) {
  747. this._markers[markerCount - 1].on('click', this._finishShape, this);
  748. }
  749. // Remove the old marker click handler (as only the last point should close the polyline)
  750. if (markerCount > 2) {
  751. this._markers[markerCount - 2].off('click', this._finishShape, this);
  752. }
  753. },
  754. _createMarker: function (latlng) {
  755. var marker = new L.Marker(latlng, {
  756. icon: this.options.icon,
  757. zIndexOffset: this.options.zIndexOffset * 2
  758. });
  759. this._markerGroup.addLayer(marker);
  760. return marker;
  761. },
  762. _updateGuide: function (newPos) {
  763. var markerCount = this._markers ? this._markers.length : 0;
  764. if (markerCount > 0) {
  765. newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);
  766. // draw the guide line
  767. this._clearGuides();
  768. this._drawGuide(
  769. this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
  770. newPos
  771. );
  772. }
  773. },
  774. _updateTooltip: function (latLng) {
  775. var text = this._getTooltipText();
  776. if (latLng) {
  777. this._tooltip.updatePosition(latLng);
  778. }
  779. if (!this._errorShown) {
  780. this._tooltip.updateContent(text);
  781. }
  782. },
  783. _drawGuide: function (pointA, pointB) {
  784. var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
  785. guidelineDistance = this.options.guidelineDistance,
  786. maxGuideLineLength = this.options.maxGuideLineLength,
  787. // Only draw a guideline with a max length
  788. i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,
  789. fraction,
  790. dashPoint,
  791. dash;
  792. //create the guides container if we haven't yet
  793. if (!this._guidesContainer) {
  794. this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
  795. }
  796. //draw a dash every GuildeLineDistance
  797. for (; i < length; i += this.options.guidelineDistance) {
  798. //work out fraction along line we are
  799. fraction = i / length;
  800. //calculate new x,y point
  801. dashPoint = {
  802. x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
  803. y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
  804. };
  805. //add guide dash to guide container
  806. dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
  807. dash.style.backgroundColor =
  808. !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
  809. L.DomUtil.setPosition(dash, dashPoint);
  810. }
  811. },
  812. _updateGuideColor: function (color) {
  813. if (this._guidesContainer) {
  814. for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
  815. this._guidesContainer.childNodes[i].style.backgroundColor = color;
  816. }
  817. }
  818. },
  819. // removes all child elements (guide dashes) from the guides container
  820. _clearGuides: function () {
  821. if (this._guidesContainer) {
  822. while (this._guidesContainer.firstChild) {
  823. this._guidesContainer.removeChild(this._guidesContainer.firstChild);
  824. }
  825. }
  826. },
  827. _getTooltipText: function () {
  828. var showLength = this.options.showLength,
  829. labelText, distanceStr;
  830. if (this._markers.length === 0) {
  831. labelText = {
  832. text: L.drawLocal.draw.handlers.polyline.tooltip.start
  833. };
  834. } else {
  835. distanceStr = showLength ? this._getMeasurementString() : '';
  836. if (this._markers.length === 1) {
  837. labelText = {
  838. text: L.drawLocal.draw.handlers.polyline.tooltip.cont,
  839. subtext: distanceStr
  840. };
  841. } else {
  842. labelText = {
  843. text: L.drawLocal.draw.handlers.polyline.tooltip.end,
  844. subtext: distanceStr
  845. };
  846. }
  847. }
  848. return labelText;
  849. },
  850. _updateRunningMeasure: function (latlng, added) {
  851. var markersLength = this._markers.length,
  852. previousMarkerIndex, distance;
  853. if (this._markers.length === 1) {
  854. this._measurementRunningTotal = 0;
  855. } else {
  856. previousMarkerIndex = markersLength - (added ? 2 : 1);
  857. // Calculate the distance based on the version
  858. if (L.GeometryUtil.isVersion07x()) {
  859. // distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
  860. distance = 1;
  861. } else {
  862. // distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
  863. distance = 1;
  864. }
  865. this._measurementRunningTotal += distance * (added ? 1 : -1);
  866. }
  867. },
  868. _getMeasurementString: function () {
  869. var currentLatLng = this._currentLatLng,
  870. previousLatLng = this._markers[this._markers.length - 1].getLatLng(),
  871. distance;
  872. // Calculate the distance from the last fixed point to the mouse position based on the version
  873. if (L.GeometryUtil.isVersion07x()) {
  874. // distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;
  875. distance = 0;
  876. } else {
  877. // distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;
  878. distance = 0;
  879. }
  880. return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision);
  881. },
  882. _showErrorTooltip: function () {
  883. this._errorShown = true;
  884. // Update tooltip
  885. this._tooltip
  886. .showAsError()
  887. .updateContent({text: this.options.drawError.message});
  888. // Update shape
  889. this._updateGuideColor(this.options.drawError.color);
  890. this._poly.setStyle({color: this.options.drawError.color});
  891. // Hide the error after 2 seconds
  892. this._clearHideErrorTimeout();
  893. this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
  894. },
  895. _hideErrorTooltip: function () {
  896. this._errorShown = false;
  897. this._clearHideErrorTimeout();
  898. // Revert tooltip
  899. this._tooltip
  900. .removeError()
  901. .updateContent(this._getTooltipText());
  902. // Revert shape
  903. this._updateGuideColor(this.options.shapeOptions.color);
  904. this._poly.setStyle({color: this.options.shapeOptions.color});
  905. },
  906. _clearHideErrorTimeout: function () {
  907. if (this._hideErrorTimeout) {
  908. clearTimeout(this._hideErrorTimeout);
  909. this._hideErrorTimeout = null;
  910. }
  911. },
  912. // disable new markers temporarily;
  913. // this is to prevent duplicated touch/click events in some browsers
  914. _disableNewMarkers: function () {
  915. this._disableMarkers = true;
  916. },
  917. // see _disableNewMarkers
  918. _enableNewMarkers: function () {
  919. setTimeout(function () {
  920. this._disableMarkers = false;
  921. }.bind(this), 50);
  922. },
  923. _cleanUpShape: function () {
  924. if (this._markers.length > 1) {
  925. this._markers[this._markers.length - 1].off('click', this._finishShape, this);
  926. }
  927. },
  928. _fireCreatedEvent: function () {
  929. var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
  930. L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
  931. }
  932. });
  933. /**
  934. * @class L.Draw.Polygon
  935. * @aka Draw.Polygon
  936. * @inherits L.Draw.Polyline
  937. */
  938. L.Draw.Polygon = L.Draw.Polyline.extend({
  939. statics: {
  940. TYPE: 'polygon'
  941. },
  942. Poly: L.Polygon,
  943. options: {
  944. showArea: false,
  945. showLength: false,
  946. shapeOptions: {
  947. stroke: true,
  948. color: '#3388ff',
  949. weight: 4,
  950. opacity: 0.5,
  951. fill: true,
  952. fillColor: null, //same as color by default
  953. fillOpacity: 0.2,
  954. clickable: true
  955. },
  956. // Whether to use the metric measurement system (truthy) or not (falsy).
  957. // Also defines the units to use for the metric system as an array of
  958. // strings (e.g. `['ha', 'm']`).
  959. metric: true,
  960. feet: true, // When not metric, to use feet instead of yards for display.
  961. nautic: false, // When not metric, not feet use nautic mile for display
  962. // Defines the precision for each type of unit (e.g. {km: 2, ft: 0}
  963. precision: {}
  964. },
  965. // @method initialize(): void
  966. initialize: function (map, options) {
  967. L.Draw.Polyline.prototype.initialize.call(this, map, options);
  968. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  969. this.type = L.Draw.Polygon.TYPE;
  970. },
  971. _updateFinishHandler: function () {
  972. var markerCount = this._markers.length;
  973. // The first marker should have a click handler to close the polygon
  974. if (markerCount === 1) {
  975. this._markers[0].on('click', this._finishShape, this);
  976. }
  977. // Add and update the double click handler
  978. if (markerCount > 2) {
  979. this._markers[markerCount - 1].on('dblclick', this._finishShape, this);
  980. // Only need to remove handler if has been added before
  981. if (markerCount > 3) {
  982. this._markers[markerCount - 2].off('dblclick', this._finishShape, this);
  983. }
  984. }
  985. },
  986. _getTooltipText: function () {
  987. var text, subtext;
  988. if (this._markers.length === 0) {
  989. text = L.drawLocal.draw.handlers.polygon.tooltip.start;
  990. } else if (this._markers.length < 3) {
  991. text = L.drawLocal.draw.handlers.polygon.tooltip.cont;
  992. subtext = this._getMeasurementString();
  993. } else {
  994. text = L.drawLocal.draw.handlers.polygon.tooltip.end;
  995. subtext = this._getMeasurementString();
  996. }
  997. return {
  998. text: text,
  999. subtext: subtext
  1000. };
  1001. },
  1002. _getMeasurementString: function () {
  1003. var area = this._area,
  1004. measurementString = '';
  1005. if (!area && !this.options.showLength) {
  1006. return null;
  1007. }
  1008. if (this.options.showLength) {
  1009. measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this);
  1010. }
  1011. if (area) {
  1012. measurementString += '<br>' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision);
  1013. }
  1014. return measurementString;
  1015. },
  1016. _shapeIsValid: function () {
  1017. return this._markers.length >= 3;
  1018. },
  1019. _vertexChanged: function (latlng, added) {
  1020. var latLngs;
  1021. // Check to see if we should show the area
  1022. if (!this.options.allowIntersection && this.options.showArea) {
  1023. latLngs = this._poly.getLatLngs();
  1024. this._area = L.GeometryUtil.geodesicArea(latLngs);
  1025. }
  1026. L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);
  1027. },
  1028. _cleanUpShape: function () {
  1029. var markerCount = this._markers.length;
  1030. if (markerCount > 0) {
  1031. this._markers[0].off('click', this._finishShape, this);
  1032. if (markerCount > 2) {
  1033. this._markers[markerCount - 1].off('dblclick', this._finishShape, this);
  1034. }
  1035. }
  1036. }
  1037. });
  1038. L.SimpleShape = {};
  1039. /**
  1040. * @class L.Draw.SimpleShape
  1041. * @aka Draw.SimpleShape
  1042. * @inherits L.Draw.Feature
  1043. */
  1044. L.Draw.SimpleShape = L.Draw.Feature.extend({
  1045. options: {
  1046. repeatMode: false
  1047. },
  1048. // @method initialize(): void
  1049. initialize: function (map, options) {
  1050. this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;
  1051. L.Draw.Feature.prototype.initialize.call(this, map, options);
  1052. },
  1053. // @method addHooks(): void
  1054. // Add listener hooks to this handler.
  1055. addHooks: function () {
  1056. L.Draw.Feature.prototype.addHooks.call(this);
  1057. if (this._map) {
  1058. this._mapDraggable = this._map.dragging.enabled();
  1059. if (this._mapDraggable) {
  1060. this._map.dragging.disable();
  1061. }
  1062. //TODO refactor: move cursor to styles
  1063. this._container.style.cursor = 'crosshair';
  1064. this._tooltip.updateContent({text: this._initialLabelText});
  1065. this._map
  1066. .on('mousedown', this._onMouseDown, this)
  1067. .on('mousemove', this._onMouseMove, this)
  1068. .on('touchstart', this._onMouseDown, this)
  1069. .on('touchmove', this._onMouseMove, this);
  1070. // we should prevent default, otherwise default behavior (scrolling) will fire,
  1071. // and that will cause document.touchend to fire and will stop the drawing
  1072. // (circle, rectangle) in touch mode.
  1073. // (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means,
  1074. // handler can't event.preventDefault
  1075. // check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners
  1076. document.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false});
  1077. }
  1078. },
  1079. // @method removeHooks(): void
  1080. // Remove listener hooks from this handler.
  1081. removeHooks: function () {
  1082. L.Draw.Feature.prototype.removeHooks.call(this);
  1083. if (this._map) {
  1084. if (this._mapDraggable) {
  1085. this._map.dragging.enable();
  1086. }
  1087. //TODO refactor: move cursor to styles
  1088. this._container.style.cursor = '';
  1089. this._map
  1090. .off('mousedown', this._onMouseDown, this)
  1091. .off('mousemove', this._onMouseMove, this)
  1092. .off('touchstart', this._onMouseDown, this)
  1093. .off('touchmove', this._onMouseMove, this);
  1094. L.DomEvent.off(document, 'mouseup', this._onMouseUp, this);
  1095. L.DomEvent.off(document, 'touchend', this._onMouseUp, this);
  1096. document.removeEventListener('touchstart', L.DomEvent.preventDefault);
  1097. // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
  1098. if (this._shape) {
  1099. this._map.removeLayer(this._shape);
  1100. delete this._shape;
  1101. }
  1102. }
  1103. this._isDrawing = false;
  1104. },
  1105. _getTooltipText: function () {
  1106. return {
  1107. text: this._endLabelText
  1108. };
  1109. },
  1110. _onMouseDown: function (e) {
  1111. this._isDrawing = true;
  1112. this._startLatLng = e.latlng;
  1113. L.DomEvent
  1114. .on(document, 'mouseup', this._onMouseUp, this)
  1115. .on(document, 'touchend', this._onMouseUp, this)
  1116. .preventDefault(e.originalEvent);
  1117. },
  1118. _onMouseMove: function (e) {
  1119. var latlng = e.latlng;
  1120. this._tooltip.updatePosition(latlng);
  1121. if (this._isDrawing) {
  1122. this._tooltip.updateContent(this._getTooltipText());
  1123. this._drawShape(latlng);
  1124. }
  1125. },
  1126. _onMouseUp: function () {
  1127. if (this._shape) {
  1128. this._fireCreatedEvent();
  1129. }
  1130. this.disable();
  1131. if (this.options.repeatMode) {
  1132. this.enable();
  1133. }
  1134. }
  1135. });
  1136. /**
  1137. * @class L.Draw.Rectangle
  1138. * @aka Draw.Rectangle
  1139. * @inherits L.Draw.SimpleShape
  1140. */
  1141. L.Draw.Rectangle = L.Draw.SimpleShape.extend({
  1142. statics: {
  1143. TYPE: 'rectangle'
  1144. },
  1145. options: {
  1146. shapeOptions: {
  1147. stroke: true,
  1148. color: '#3388ff',
  1149. weight: 4,
  1150. opacity: 0.5,
  1151. fill: true,
  1152. fillColor: null, //same as color by default
  1153. fillOpacity: 0.2,
  1154. showArea: true,
  1155. clickable: true
  1156. },
  1157. metric: true // Whether to use the metric measurement system or imperial
  1158. },
  1159. // @method initialize(): void
  1160. initialize: function (map, options) {
  1161. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  1162. this.type = L.Draw.Rectangle.TYPE;
  1163. this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;
  1164. L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
  1165. },
  1166. // @method disable(): void
  1167. disable: function () {
  1168. if (!this._enabled) {
  1169. return;
  1170. }
  1171. this._isCurrentlyTwoClickDrawing = false;
  1172. L.Draw.SimpleShape.prototype.disable.call(this);
  1173. },
  1174. _onMouseUp: function (e) {
  1175. if (!this._shape && !this._isCurrentlyTwoClickDrawing) {
  1176. this._isCurrentlyTwoClickDrawing = true;
  1177. return;
  1178. }
  1179. // Make sure closing click is on map
  1180. if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) {
  1181. return;
  1182. }
  1183. L.Draw.SimpleShape.prototype._onMouseUp.call(this);
  1184. },
  1185. _drawShape: function (latlng) {
  1186. if (!this._shape) {
  1187. this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
  1188. this._map.addLayer(this._shape);
  1189. } else {
  1190. this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
  1191. }
  1192. },
  1193. _fireCreatedEvent: function () {
  1194. var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
  1195. L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
  1196. },
  1197. _getTooltipText: function () {
  1198. var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),
  1199. shape = this._shape,
  1200. showArea = this.options.showArea,
  1201. latLngs, area, subtext;
  1202. if (shape) {
  1203. latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();
  1204. area = L.GeometryUtil.geodesicArea(latLngs);
  1205. subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : '';
  1206. }
  1207. return {
  1208. text: tooltipText.text,
  1209. subtext: subtext
  1210. };
  1211. }
  1212. });
  1213. function _hasAncestor(el, cls) {
  1214. while ((el = el.parentElement) && !el.classList.contains(cls)) {
  1215. ;
  1216. }
  1217. return el;
  1218. }
  1219. /**
  1220. * @class L.Draw.Marker
  1221. * @aka Draw.Marker
  1222. * @inherits L.Draw.Feature
  1223. */
  1224. L.Draw.Marker = L.Draw.Feature.extend({
  1225. statics: {
  1226. TYPE: 'marker'
  1227. },
  1228. options: {
  1229. icon: new L.Icon.Default(),
  1230. repeatMode: false,
  1231. zIndexOffset: 2000 // This should be > than the highest z-index any markers
  1232. },
  1233. // @method initialize(): void
  1234. initialize: function (map, options) {
  1235. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  1236. this.type = L.Draw.Marker.TYPE;
  1237. this._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start;
  1238. L.Draw.Feature.prototype.initialize.call(this, map, options);
  1239. },
  1240. // @method addHooks(): void
  1241. // Add listener hooks to this handler.
  1242. addHooks: function () {
  1243. L.Draw.Feature.prototype.addHooks.call(this);
  1244. if (this._map) {
  1245. this._tooltip.updateContent({text: this._initialLabelText});
  1246. // Same mouseMarker as in Draw.Polyline
  1247. if (!this._mouseMarker) {
  1248. this._mouseMarker = L.marker(this._map.getCenter(), {
  1249. icon: L.divIcon({
  1250. className: 'leaflet-mouse-marker',
  1251. iconAnchor: [20, 20],
  1252. iconSize: [40, 40]
  1253. }),
  1254. opacity: 0,
  1255. zIndexOffset: this.options.zIndexOffset
  1256. });
  1257. }
  1258. this._mouseMarker
  1259. .on('click', this._onClick, this)
  1260. .addTo(this._map);
  1261. this._map.on('mousemove', this._onMouseMove, this);
  1262. this._map.on('click', this._onTouch, this);
  1263. }
  1264. },
  1265. // @method removeHooks(): void
  1266. // Remove listener hooks from this handler.
  1267. removeHooks: function () {
  1268. L.Draw.Feature.prototype.removeHooks.call(this);
  1269. if (this._map) {
  1270. this._map
  1271. .off('click', this._onClick, this)
  1272. .off('click', this._onTouch, this);
  1273. if (this._marker) {
  1274. this._marker.off('click', this._onClick, this);
  1275. this._map
  1276. .removeLayer(this._marker);
  1277. delete this._marker;
  1278. }
  1279. this._mouseMarker.off('click', this._onClick, this);
  1280. this._map.removeLayer(this._mouseMarker);
  1281. delete this._mouseMarker;
  1282. this._map.off('mousemove', this._onMouseMove, this);
  1283. }
  1284. },
  1285. _onMouseMove: function (e) {
  1286. var latlng = e.latlng;
  1287. this._tooltip.updatePosition(latlng);
  1288. this._mouseMarker.setLatLng(latlng);
  1289. if (!this._marker) {
  1290. this._marker = this._createMarker(latlng);
  1291. // Bind to both marker and map to make sure we get the click event.
  1292. this._marker.on('click', this._onClick, this);
  1293. this._map
  1294. .on('click', this._onClick, this)
  1295. .addLayer(this._marker);
  1296. } else {
  1297. latlng = this._mouseMarker.getLatLng();
  1298. this._marker.setLatLng(latlng);
  1299. }
  1300. },
  1301. _createMarker: function (latlng) {
  1302. return new L.Marker(latlng, {
  1303. icon: this.options.icon,
  1304. zIndexOffset: this.options.zIndexOffset
  1305. });
  1306. },
  1307. _onClick: function () {
  1308. this._fireCreatedEvent();
  1309. this.disable();
  1310. if (this.options.repeatMode) {
  1311. this.enable();
  1312. }
  1313. },
  1314. _onTouch: function (e) {
  1315. // called on click & tap, only really does any thing on tap
  1316. this._onMouseMove(e); // creates & places marker
  1317. this._onClick(); // permanently places marker & ends interaction
  1318. },
  1319. _fireCreatedEvent: function () {
  1320. var marker = new L.Marker.Touch(this._marker.getLatLng(), {icon: this.options.icon});
  1321. L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);
  1322. }
  1323. });
  1324. /**
  1325. * @class L.Draw.CircleMarker
  1326. * @aka Draw.CircleMarker
  1327. * @inherits L.Draw.Marker
  1328. */
  1329. L.Draw.CircleMarker = L.Draw.Marker.extend({
  1330. statics: {
  1331. TYPE: 'circlemarker'
  1332. },
  1333. options: {
  1334. stroke: true,
  1335. color: '#3388ff',
  1336. weight: 4,
  1337. opacity: 0.5,
  1338. fill: true,
  1339. fillColor: null, //same as color by default
  1340. fillOpacity: 0.2,
  1341. clickable: true,
  1342. zIndexOffset: 2000 // This should be > than the highest z-index any markers
  1343. },
  1344. // @method initialize(): void
  1345. initialize: function (map, options) {
  1346. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  1347. this.type = L.Draw.CircleMarker.TYPE;
  1348. this._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start;
  1349. L.Draw.Feature.prototype.initialize.call(this, map, options);
  1350. },
  1351. _fireCreatedEvent: function () {
  1352. var circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options);
  1353. L.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker);
  1354. },
  1355. _createMarker: function (latlng) {
  1356. return new L.CircleMarker(latlng, this.options);
  1357. }
  1358. });
  1359. /**
  1360. * @class L.Draw.Circle
  1361. * @aka Draw.Circle
  1362. * @inherits L.Draw.SimpleShape
  1363. */
  1364. L.Draw.Circle = L.Draw.SimpleShape.extend({
  1365. statics: {
  1366. TYPE: 'circle'
  1367. },
  1368. options: {
  1369. shapeOptions: {
  1370. stroke: true,
  1371. color: '#3388ff',
  1372. weight: 4,
  1373. opacity: 0.5,
  1374. fill: true,
  1375. fillColor: null, //same as color by default
  1376. fillOpacity: 0.2,
  1377. clickable: true
  1378. },
  1379. showRadius: true,
  1380. metric: true, // Whether to use the metric measurement system or imperial
  1381. feet: true, // When not metric, use feet instead of yards for display
  1382. nautic: false // When not metric, not feet use nautic mile for display
  1383. },
  1384. // @method initialize(): void
  1385. initialize: function (map, options) {
  1386. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  1387. this.type = L.Draw.Circle.TYPE;
  1388. this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;
  1389. L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
  1390. },
  1391. _drawShape: function (latlng) {
  1392. // Calculate the distance based on the version
  1393. if (L.GeometryUtil.isVersion07x()) {
  1394. var distance = this._startLatLng.distanceTo(latlng);
  1395. } else {
  1396. var distance = this._map.distance(this._startLatLng, latlng);
  1397. }
  1398. if (!this._shape) {
  1399. this._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions);
  1400. this._map.addLayer(this._shape);
  1401. } else {
  1402. this._shape.setRadius(distance);
  1403. }
  1404. },
  1405. _fireCreatedEvent: function () {
  1406. var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);
  1407. L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);
  1408. },
  1409. _onMouseMove: function (e) {
  1410. var latlng = e.latlng,
  1411. showRadius = this.options.showRadius,
  1412. useMetric = this.options.metric,
  1413. radius;
  1414. this._tooltip.updatePosition(latlng);
  1415. if (this._isDrawing) {
  1416. this._drawShape(latlng);
  1417. // Get the new radius (rounded to 1 dp)
  1418. radius = this._shape.getRadius().toFixed(1);
  1419. var subtext = '';
  1420. if (showRadius) {
  1421. subtext = L.drawLocal.draw.handlers.circle.radius + ': ' +
  1422. L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic);
  1423. }
  1424. this._tooltip.updateContent({
  1425. text: this._endLabelText,
  1426. subtext: subtext
  1427. });
  1428. }
  1429. }
  1430. });
  1431. L.Edit = L.Edit || {};
  1432. /**
  1433. * @class L.Edit.Marker
  1434. * @aka Edit.Marker
  1435. */
  1436. L.Edit.Marker = L.Handler.extend({
  1437. // @method initialize(): void
  1438. initialize: function (marker, options) {
  1439. this._marker = marker;
  1440. L.setOptions(this, options);
  1441. },
  1442. // @method addHooks(): void
  1443. // Add listener hooks to this handler
  1444. addHooks: function () {
  1445. var marker = this._marker;
  1446. marker.dragging.enable();
  1447. marker.on('dragend', this._onDragEnd, marker);
  1448. this._toggleMarkerHighlight();
  1449. },
  1450. // @method removeHooks(): void
  1451. // Remove listener hooks from this handler
  1452. removeHooks: function () {
  1453. var marker = this._marker;
  1454. marker.dragging.disable();
  1455. marker.off('dragend', this._onDragEnd, marker);
  1456. this._toggleMarkerHighlight();
  1457. },
  1458. _onDragEnd: function (e) {
  1459. var layer = e.target;
  1460. layer.edited = true;
  1461. this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});
  1462. },
  1463. _toggleMarkerHighlight: function () {
  1464. var icon = this._marker._icon;
  1465. // Don't do anything if this layer is a marker but doesn't have an icon. Markers
  1466. // should usually have icons. If using Leaflet.draw with Leaflet.markercluster there
  1467. // is a chance that a marker doesn't.
  1468. if (!icon) {
  1469. return;
  1470. }
  1471. // This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)
  1472. icon.style.display = 'none';
  1473. if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {
  1474. L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');
  1475. // Offset as the border will make the icon move.
  1476. this._offsetMarker(icon, -4);
  1477. } else {
  1478. L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');
  1479. // Offset as the border will make the icon move.
  1480. this._offsetMarker(icon, 4);
  1481. }
  1482. icon.style.display = '';
  1483. },
  1484. _offsetMarker: function (icon, offset) {
  1485. var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,
  1486. iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;
  1487. icon.style.marginTop = iconMarginTop + 'px';
  1488. icon.style.marginLeft = iconMarginLeft + 'px';
  1489. }
  1490. });
  1491. L.Marker.addInitHook(function () {
  1492. if (L.Edit.Marker) {
  1493. this.editing = new L.Edit.Marker(this);
  1494. if (this.options.editable) {
  1495. this.editing.enable();
  1496. }
  1497. }
  1498. });
  1499. L.Edit = L.Edit || {};
  1500. /**
  1501. * @class L.Edit.Polyline
  1502. * @aka L.Edit.Poly
  1503. * @aka Edit.Poly
  1504. */
  1505. L.Edit.Poly = L.Handler.extend({
  1506. // @method initialize(): void
  1507. initialize: function (poly) {
  1508. this.latlngs = [poly._latlngs];
  1509. if (poly._holes) {
  1510. this.latlngs = this.latlngs.concat(poly._holes);
  1511. }
  1512. this._poly = poly;
  1513. this._poly.on('revert-edited', this._updateLatLngs, this);
  1514. },
  1515. // Compatibility method to normalize Poly* objects
  1516. // between 0.7.x and 1.0+
  1517. _defaultShape: function () {
  1518. if (!L.Polyline._flat) {
  1519. return this._poly._latlngs;
  1520. }
  1521. return L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0];
  1522. },
  1523. _eachVertexHandler: function (callback) {
  1524. for (var i = 0; i < this._verticesHandlers.length; i++) {
  1525. callback(this._verticesHandlers[i]);
  1526. }
  1527. },
  1528. // @method addHooks(): void
  1529. // Add listener hooks to this handler
  1530. addHooks: function () {
  1531. this._initHandlers();
  1532. this._eachVertexHandler(function (handler) {
  1533. handler.addHooks();
  1534. });
  1535. },
  1536. // @method removeHooks(): void
  1537. // Remove listener hooks from this handler
  1538. removeHooks: function () {
  1539. this._eachVertexHandler(function (handler) {
  1540. handler.removeHooks();
  1541. });
  1542. },
  1543. // @method updateMarkers(): void
  1544. // Fire an update for each vertex handler
  1545. updateMarkers: function () {
  1546. this._eachVertexHandler(function (handler) {
  1547. handler.updateMarkers();
  1548. });
  1549. },
  1550. _initHandlers: function () {
  1551. this._verticesHandlers = [];
  1552. for (var i = 0; i < this.latlngs.length; i++) {
  1553. this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly));
  1554. }
  1555. },
  1556. _updateLatLngs: function (e) {
  1557. this.latlngs = [e.layer._latlngs];
  1558. if (e.layer._holes) {
  1559. this.latlngs = this.latlngs.concat(e.layer._holes);
  1560. }
  1561. }
  1562. });
  1563. /**
  1564. * @class L.Edit.PolyVerticesEdit
  1565. * @aka Edit.PolyVerticesEdit
  1566. */
  1567. L.Edit.PolyVerticesEdit = L.Handler.extend({
  1568. options: {
  1569. icon: new L.DivIcon({
  1570. iconSize: new L.Point(8, 8),
  1571. className: 'leaflet-div-icon leaflet-editing-icon'
  1572. }),
  1573. touchIcon: new L.DivIcon({
  1574. iconSize: new L.Point(20, 20),
  1575. className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
  1576. }),
  1577. drawError: {
  1578. color: '#b00b00',
  1579. timeout: 1000
  1580. }
  1581. },
  1582. // @method intialize(): void
  1583. initialize: function (poly, latlngs, options) {
  1584. // if touch, switch to touch icon
  1585. if (L.Browser.touch) {
  1586. this.options.icon = this.options.touchIcon;
  1587. }
  1588. this._poly = poly;
  1589. if (options && options.drawError) {
  1590. options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
  1591. }
  1592. this._latlngs = latlngs;
  1593. L.setOptions(this, options);
  1594. },
  1595. // Compatibility method to normalize Poly* objects
  1596. // between 0.7.x and 1.0+
  1597. _defaultShape: function () {
  1598. if (!L.Polyline._flat) {
  1599. return this._latlngs;
  1600. }
  1601. return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
  1602. },
  1603. // @method addHooks(): void
  1604. // Add listener hooks to this handler.
  1605. addHooks: function () {
  1606. var poly = this._poly;
  1607. var path = poly._path;
  1608. if (!(poly instanceof L.Polygon)) {
  1609. poly.options.fill = false;
  1610. if (poly.options.editing) {
  1611. poly.options.editing.fill = false;
  1612. }
  1613. }
  1614. if (path) {
  1615. if (poly.options.editing.className) {
  1616. if (poly.options.original.className) {
  1617. poly.options.original.className.split(' ').forEach(function (className) {
  1618. L.DomUtil.removeClass(path, className);
  1619. });
  1620. }
  1621. poly.options.editing.className.split(' ').forEach(function (className) {
  1622. L.DomUtil.addClass(path, className);
  1623. });
  1624. }
  1625. }
  1626. poly.setStyle(poly.options.editing);
  1627. if (this._poly._map) {
  1628. this._map = this._poly._map; // Set map
  1629. if (!this._markerGroup) {
  1630. this._initMarkers();
  1631. }
  1632. this._poly._map.addLayer(this._markerGroup);
  1633. }
  1634. },
  1635. // @method removeHooks(): void
  1636. // Remove listener hooks from this handler.
  1637. removeHooks: function () {
  1638. var poly = this._poly;
  1639. var path = poly._path;
  1640. if (path) {
  1641. if (poly.options.editing.className) {
  1642. poly.options.editing.className.split(' ').forEach(function (className) {
  1643. L.DomUtil.removeClass(path, className);
  1644. });
  1645. if (poly.options.original.className) {
  1646. poly.options.original.className.split(' ').forEach(function (className) {
  1647. L.DomUtil.addClass(path, className);
  1648. });
  1649. }
  1650. }
  1651. }
  1652. poly.setStyle(poly.options.original);
  1653. if (poly._map) {
  1654. poly._map.removeLayer(this._markerGroup);
  1655. delete this._markerGroup;
  1656. delete this._markers;
  1657. }
  1658. },
  1659. // @method updateMarkers(): void
  1660. // Clear markers and update their location
  1661. updateMarkers: function () {
  1662. this._markerGroup.clearLayers();
  1663. this._initMarkers();
  1664. },
  1665. _initMarkers: function () {
  1666. if (!this._markerGroup) {
  1667. this._markerGroup = new L.LayerGroup();
  1668. }
  1669. this._markers = [];
  1670. var latlngs = this._defaultShape(),
  1671. i, j, len, marker;
  1672. for (i = 0, len = latlngs.length; i < len; i++) {
  1673. marker = this._createMarker(latlngs[i], i);
  1674. marker.on('click', this._onMarkerClick, this);
  1675. marker.on('contextmenu', this._onContextMenu, this);
  1676. this._markers.push(marker);
  1677. }
  1678. var markerLeft, markerRight;
  1679. for (i = 0, j = len - 1; i < len; j = i++) {
  1680. if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
  1681. continue;
  1682. }
  1683. markerLeft = this._markers[j];
  1684. markerRight = this._markers[i];
  1685. this._createMiddleMarker(markerLeft, markerRight);
  1686. this._updatePrevNext(markerLeft, markerRight);
  1687. }
  1688. },
  1689. _createMarker: function (latlng, index) {
  1690. // Extending L.Marker in TouchEvents.js to include touch.
  1691. var marker = new L.Marker.Touch(latlng, {
  1692. draggable: true,
  1693. icon: this.options.icon,
  1694. });
  1695. marker._origLatLng = latlng;
  1696. marker._index = index;
  1697. marker
  1698. .on('dragstart', this._onMarkerDragStart, this)
  1699. .on('drag', this._onMarkerDrag, this)
  1700. .on('dragend', this._fireEdit, this)
  1701. .on('touchmove', this._onTouchMove, this)
  1702. .on('touchend', this._fireEdit, this)
  1703. .on('MSPointerMove', this._onTouchMove, this)
  1704. .on('MSPointerUp', this._fireEdit, this);
  1705. this._markerGroup.addLayer(marker);
  1706. return marker;
  1707. },
  1708. _onMarkerDragStart: function () {
  1709. this._poly.fire('editstart');
  1710. },
  1711. _spliceLatLngs: function () {
  1712. var latlngs = this._defaultShape();
  1713. var removed = [].splice.apply(latlngs, arguments);
  1714. this._poly._convertLatLngs(latlngs, true);
  1715. this._poly.redraw();
  1716. return removed;
  1717. },
  1718. _removeMarker: function (marker) {
  1719. var i = marker._index;
  1720. this._markerGroup.removeLayer(marker);
  1721. this._markers.splice(i, 1);
  1722. this._spliceLatLngs(i, 1);
  1723. this._updateIndexes(i, -1);
  1724. marker
  1725. .off('dragstart', this._onMarkerDragStart, this)
  1726. .off('drag', this._onMarkerDrag, this)
  1727. .off('dragend', this._fireEdit, this)
  1728. .off('touchmove', this._onMarkerDrag, this)
  1729. .off('touchend', this._fireEdit, this)
  1730. .off('click', this._onMarkerClick, this)
  1731. .off('MSPointerMove', this._onTouchMove, this)
  1732. .off('MSPointerUp', this._fireEdit, this);
  1733. },
  1734. _fireEdit: function () {
  1735. this._poly.edited = true;
  1736. this._poly.fire('edit');
  1737. this._poly._map.fire(L.Draw.Event.EDITVERTEX, {layers: this._markerGroup, poly: this._poly});
  1738. },
  1739. _onMarkerDrag: function (e) {
  1740. var marker = e.target;
  1741. var poly = this._poly;
  1742. L.extend(marker._origLatLng, marker._latlng);
  1743. if (marker._middleLeft) {
  1744. marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
  1745. }
  1746. if (marker._middleRight) {
  1747. marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
  1748. }
  1749. if (poly.options.poly) {
  1750. var tooltip = poly._map._editTooltip; // Access the tooltip
  1751. // If we don't allow intersections and the polygon intersects
  1752. if (!poly.options.poly.allowIntersection && poly.intersects()) {
  1753. var originalColor = poly.options.color;
  1754. poly.setStyle({color: this.options.drawError.color});
  1755. // Manually trigger 'dragend' behavior on marker we are about to remove
  1756. // WORKAROUND: introduced in 1.0.0-rc2, may be related to #4484
  1757. if (L.version.indexOf('0.7') !== 0) {
  1758. marker.dragging._draggable._onUp(e);
  1759. }
  1760. this._onMarkerClick(e); // Remove violating marker
  1761. // FIXME: Reset the marker to it's original position (instead of remove)
  1762. if (tooltip) {
  1763. tooltip.updateContent({
  1764. text: L.drawLocal.draw.handlers.polyline.error
  1765. });
  1766. }
  1767. // Reset everything back to normal after a second
  1768. setTimeout(function () {
  1769. poly.setStyle({color: originalColor});
  1770. if (tooltip) {
  1771. tooltip.updateContent({
  1772. text: L.drawLocal.edit.handlers.edit.tooltip.text,
  1773. subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
  1774. });
  1775. }
  1776. }, 1000);
  1777. }
  1778. }
  1779. //refresh the bounds when draging
  1780. this._poly._bounds._southWest = L.latLng(Infinity, Infinity);
  1781. this._poly._bounds._northEast = L.latLng(-Infinity, -Infinity);
  1782. var latlngs = this._poly.getLatLngs();
  1783. this._poly._convertLatLngs(latlngs, true);
  1784. this._poly.redraw();
  1785. this._poly.fire('editdrag');
  1786. },
  1787. _onMarkerClick: function (e) {
  1788. var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,
  1789. marker = e.target;
  1790. // If removing this point would create an invalid polyline/polygon don't remove
  1791. if (this._defaultShape().length < minPoints) {
  1792. return;
  1793. }
  1794. // remove the marker
  1795. this._removeMarker(marker);
  1796. // update prev/next links of adjacent markers
  1797. this._updatePrevNext(marker._prev, marker._next);
  1798. // remove ghost markers near the removed marker
  1799. if (marker._middleLeft) {
  1800. this._markerGroup.removeLayer(marker._middleLeft);
  1801. }
  1802. if (marker._middleRight) {
  1803. this._markerGroup.removeLayer(marker._middleRight);
  1804. }
  1805. // create a ghost marker in place of the removed one
  1806. if (marker._prev && marker._next) {
  1807. this._createMiddleMarker(marker._prev, marker._next);
  1808. } else if (!marker._prev) {
  1809. marker._next._middleLeft = null;
  1810. } else if (!marker._next) {
  1811. marker._prev._middleRight = null;
  1812. }
  1813. this._fireEdit();
  1814. },
  1815. _onContextMenu: function (e) {
  1816. var marker = e.target;
  1817. var poly = this._poly;
  1818. this._poly._map.fire(L.Draw.Event.MARKERCONTEXT, {
  1819. marker: marker,
  1820. layers: this._markerGroup,
  1821. poly: this._poly
  1822. });
  1823. L.DomEvent.stopPropagation;
  1824. },
  1825. _onTouchMove: function (e) {
  1826. var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
  1827. latlng = this._map.layerPointToLatLng(layerPoint),
  1828. marker = e.target;
  1829. L.extend(marker._origLatLng, latlng);
  1830. if (marker._middleLeft) {
  1831. marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
  1832. }
  1833. if (marker._middleRight) {
  1834. marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
  1835. }
  1836. this._poly.redraw();
  1837. this.updateMarkers();
  1838. },
  1839. _updateIndexes: function (index, delta) {
  1840. this._markerGroup.eachLayer(function (marker) {
  1841. if (marker._index > index) {
  1842. marker._index += delta;
  1843. }
  1844. });
  1845. },
  1846. _createMiddleMarker: function (marker1, marker2) {
  1847. var latlng = this._getMiddleLatLng(marker1, marker2),
  1848. marker = this._createMarker(latlng),
  1849. onClick,
  1850. onDragStart,
  1851. onDragEnd;
  1852. marker.setOpacity(0.6);
  1853. marker1._middleRight = marker2._middleLeft = marker;
  1854. onDragStart = function () {
  1855. marker.off('touchmove', onDragStart, this);
  1856. var i = marker2._index;
  1857. marker._index = i;
  1858. marker
  1859. .off('click', onClick, this)
  1860. .on('click', this._onMarkerClick, this);
  1861. latlng.lat = marker.getLatLng().lat;
  1862. latlng.lng = marker.getLatLng().lng;
  1863. this._spliceLatLngs(i, 0, latlng);
  1864. this._markers.splice(i, 0, marker);
  1865. marker.setOpacity(1);
  1866. this._updateIndexes(i, 1);
  1867. marker2._index++;
  1868. this._updatePrevNext(marker1, marker);
  1869. this._updatePrevNext(marker, marker2);
  1870. this._poly.fire('editstart');
  1871. };
  1872. onDragEnd = function () {
  1873. marker.off('dragstart', onDragStart, this);
  1874. marker.off('dragend', onDragEnd, this);
  1875. marker.off('touchmove', onDragStart, this);
  1876. this._createMiddleMarker(marker1, marker);
  1877. this._createMiddleMarker(marker, marker2);
  1878. };
  1879. onClick = function () {
  1880. onDragStart.call(this);
  1881. onDragEnd.call(this);
  1882. this._fireEdit();
  1883. };
  1884. marker
  1885. .on('click', onClick, this)
  1886. .on('dragstart', onDragStart, this)
  1887. .on('dragend', onDragEnd, this)
  1888. .on('touchmove', onDragStart, this);
  1889. this._markerGroup.addLayer(marker);
  1890. },
  1891. _updatePrevNext: function (marker1, marker2) {
  1892. if (marker1) {
  1893. marker1._next = marker2;
  1894. }
  1895. if (marker2) {
  1896. marker2._prev = marker1;
  1897. }
  1898. },
  1899. _getMiddleLatLng: function (marker1, marker2) {
  1900. var map = this._poly._map,
  1901. p1 = map.project(marker1.getLatLng()),
  1902. p2 = map.project(marker2.getLatLng());
  1903. return map.unproject(p1._add(p2)._divideBy(2));
  1904. }
  1905. });
  1906. L.Polyline.addInitHook(function () {
  1907. // Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit
  1908. if (this.editing) {
  1909. return;
  1910. }
  1911. if (L.Edit.Poly) {
  1912. this.editing = new L.Edit.Poly(this);
  1913. if (this.options.editable) {
  1914. this.editing.enable();
  1915. }
  1916. }
  1917. this.on('add', function () {
  1918. if (this.editing && this.editing.enabled()) {
  1919. this.editing.addHooks();
  1920. }
  1921. });
  1922. this.on('remove', function () {
  1923. if (this.editing && this.editing.enabled()) {
  1924. this.editing.removeHooks();
  1925. }
  1926. });
  1927. });
  1928. L.Edit = L.Edit || {};
  1929. /**
  1930. * @class L.Edit.SimpleShape
  1931. * @aka Edit.SimpleShape
  1932. */
  1933. L.Edit.SimpleShape = L.Handler.extend({
  1934. options: {
  1935. moveIcon: new L.DivIcon({
  1936. iconSize: new L.Point(8, 8),
  1937. className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'
  1938. }),
  1939. resizeIcon: new L.DivIcon({
  1940. iconSize: new L.Point(8, 8),
  1941. className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'
  1942. }),
  1943. touchMoveIcon: new L.DivIcon({
  1944. iconSize: new L.Point(20, 20),
  1945. className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon'
  1946. }),
  1947. touchResizeIcon: new L.DivIcon({
  1948. iconSize: new L.Point(20, 20),
  1949. className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon'
  1950. }),
  1951. },
  1952. // @method intialize(): void
  1953. initialize: function (shape, options) {
  1954. // if touch, switch to touch icon
  1955. if (L.Browser.touch) {
  1956. this.options.moveIcon = this.options.touchMoveIcon;
  1957. this.options.resizeIcon = this.options.touchResizeIcon;
  1958. }
  1959. this._shape = shape;
  1960. L.Util.setOptions(this, options);
  1961. },
  1962. // @method addHooks(): void
  1963. // Add listener hooks to this handler
  1964. addHooks: function () {
  1965. var shape = this._shape;
  1966. if (this._shape._map) {
  1967. this._map = this._shape._map;
  1968. shape.setStyle(shape.options.editing);
  1969. if (shape._map) {
  1970. this._map = shape._map;
  1971. if (!this._markerGroup) {
  1972. this._initMarkers();
  1973. }
  1974. this._map.addLayer(this._markerGroup);
  1975. }
  1976. }
  1977. },
  1978. // @method removeHooks(): void
  1979. // Remove listener hooks from this handler
  1980. removeHooks: function () {
  1981. var shape = this._shape;
  1982. shape.setStyle(shape.options.original);
  1983. if (shape._map) {
  1984. this._unbindMarker(this._moveMarker);
  1985. for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
  1986. this._unbindMarker(this._resizeMarkers[i]);
  1987. }
  1988. this._resizeMarkers = null;
  1989. this._map.removeLayer(this._markerGroup);
  1990. delete this._markerGroup;
  1991. }
  1992. this._map = null;
  1993. },
  1994. // @method updateMarkers(): void
  1995. // Remove the edit markers from this layer
  1996. updateMarkers: function () {
  1997. this._markerGroup.clearLayers();
  1998. this._initMarkers();
  1999. },
  2000. _initMarkers: function () {
  2001. if (!this._markerGroup) {
  2002. this._markerGroup = new L.LayerGroup();
  2003. }
  2004. // Create center marker
  2005. this._createMoveMarker();
  2006. // Create edge marker
  2007. this._createResizeMarker();
  2008. },
  2009. _createMoveMarker: function () {
  2010. // Children override
  2011. },
  2012. _createResizeMarker: function () {
  2013. // Children override
  2014. },
  2015. _createMarker: function (latlng, icon) {
  2016. // Extending L.Marker in TouchEvents.js to include touch.
  2017. var marker = new L.Marker.Touch(latlng, {
  2018. draggable: true,
  2019. icon: icon,
  2020. zIndexOffset: 10
  2021. });
  2022. this._bindMarker(marker);
  2023. this._markerGroup.addLayer(marker);
  2024. return marker;
  2025. },
  2026. _bindMarker: function (marker) {
  2027. marker
  2028. .on('dragstart', this._onMarkerDragStart, this)
  2029. .on('drag', this._onMarkerDrag, this)
  2030. .on('dragend', this._onMarkerDragEnd, this)
  2031. .on('touchstart', this._onTouchStart, this)
  2032. .on('touchmove', this._onTouchMove, this)
  2033. .on('MSPointerMove', this._onTouchMove, this)
  2034. .on('touchend', this._onTouchEnd, this)
  2035. .on('MSPointerUp', this._onTouchEnd, this);
  2036. },
  2037. _unbindMarker: function (marker) {
  2038. marker
  2039. .off('dragstart', this._onMarkerDragStart, this)
  2040. .off('drag', this._onMarkerDrag, this)
  2041. .off('dragend', this._onMarkerDragEnd, this)
  2042. .off('touchstart', this._onTouchStart, this)
  2043. .off('touchmove', this._onTouchMove, this)
  2044. .off('MSPointerMove', this._onTouchMove, this)
  2045. .off('touchend', this._onTouchEnd, this)
  2046. .off('MSPointerUp', this._onTouchEnd, this);
  2047. },
  2048. _onMarkerDragStart: function (e) {
  2049. var marker = e.target;
  2050. marker.setOpacity(0);
  2051. this._shape.fire('editstart');
  2052. },
  2053. _fireEdit: function () {
  2054. this._shape.edited = true;
  2055. this._shape.fire('edit');
  2056. },
  2057. _onMarkerDrag: function (e) {
  2058. var marker = e.target,
  2059. latlng = marker.getLatLng();
  2060. if (marker === this._moveMarker) {
  2061. this._move(latlng);
  2062. } else {
  2063. this._resize(latlng);
  2064. }
  2065. this._shape.redraw();
  2066. this._shape.fire('editdrag');
  2067. },
  2068. _onMarkerDragEnd: function (e) {
  2069. var marker = e.target;
  2070. marker.setOpacity(1);
  2071. this._fireEdit();
  2072. },
  2073. _onTouchStart: function (e) {
  2074. L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
  2075. if (typeof (this._getCorners) === 'function') {
  2076. // Save a reference to the opposite point
  2077. var corners = this._getCorners(),
  2078. marker = e.target,
  2079. currentCornerIndex = marker._cornerIndex;
  2080. marker.setOpacity(0);
  2081. // Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart()
  2082. // Latlng is null otherwise.
  2083. this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
  2084. this._toggleCornerMarkers(0, currentCornerIndex);
  2085. }
  2086. this._shape.fire('editstart');
  2087. },
  2088. _onTouchMove: function (e) {
  2089. var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
  2090. latlng = this._map.layerPointToLatLng(layerPoint),
  2091. marker = e.target;
  2092. if (marker === this._moveMarker) {
  2093. this._move(latlng);
  2094. } else {
  2095. this._resize(latlng);
  2096. }
  2097. this._shape.redraw();
  2098. // prevent touchcancel in IOS
  2099. // e.preventDefault();
  2100. return false;
  2101. },
  2102. _onTouchEnd: function (e) {
  2103. var marker = e.target;
  2104. marker.setOpacity(1);
  2105. this.updateMarkers();
  2106. this._fireEdit();
  2107. },
  2108. _move: function () {
  2109. // Children override
  2110. },
  2111. _resize: function () {
  2112. // Children override
  2113. }
  2114. });
  2115. L.Edit = L.Edit || {};
  2116. /**
  2117. * @class L.Edit.Rectangle
  2118. * @aka Edit.Rectangle
  2119. * @inherits L.Edit.SimpleShape
  2120. */
  2121. L.Edit.Rectangle = L.Edit.SimpleShape.extend({
  2122. _createMoveMarker: function () {
  2123. var bounds = this._shape.getBounds(),
  2124. center = bounds.getCenter();
  2125. this._moveMarker = this._createMarker(center, this.options.moveIcon);
  2126. },
  2127. _createResizeMarker: function () {
  2128. var corners = this._getCorners();
  2129. this._resizeMarkers = [];
  2130. for (var i = 0, l = corners.length; i < l; i++) {
  2131. this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));
  2132. // Monkey in the corner index as we will need to know this for dragging
  2133. this._resizeMarkers[i]._cornerIndex = i;
  2134. }
  2135. },
  2136. _onMarkerDragStart: function (e) {
  2137. L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
  2138. // Save a reference to the opposite point
  2139. var corners = this._getCorners(),
  2140. marker = e.target,
  2141. currentCornerIndex = marker._cornerIndex;
  2142. this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
  2143. this._toggleCornerMarkers(0, currentCornerIndex);
  2144. },
  2145. _onMarkerDragEnd: function (e) {
  2146. var marker = e.target,
  2147. bounds, center;
  2148. // Reset move marker position to the center
  2149. if (marker === this._moveMarker) {
  2150. bounds = this._shape.getBounds();
  2151. center = bounds.getCenter();
  2152. marker.setLatLng(center);
  2153. }
  2154. this._toggleCornerMarkers(1);
  2155. this._repositionCornerMarkers();
  2156. L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);
  2157. },
  2158. _move: function (newCenter) {
  2159. var latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(),
  2160. bounds = this._shape.getBounds(),
  2161. center = bounds.getCenter(),
  2162. offset, newLatLngs = [];
  2163. // Offset the latlngs to the new center
  2164. for (var i = 0, l = latlngs.length; i < l; i++) {
  2165. offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];
  2166. newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);
  2167. }
  2168. this._shape.setLatLngs(newLatLngs);
  2169. // Reposition the resize markers
  2170. this._repositionCornerMarkers();
  2171. this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});
  2172. },
  2173. _resize: function (latlng) {
  2174. var bounds;
  2175. // Update the shape based on the current position of this corner and the opposite point
  2176. this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));
  2177. // Reposition the move marker
  2178. bounds = this._shape.getBounds();
  2179. this._moveMarker.setLatLng(bounds.getCenter());
  2180. this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});
  2181. },
  2182. _getCorners: function () {
  2183. var bounds = this._shape.getBounds(),
  2184. nw = bounds.getNorthWest(),
  2185. ne = bounds.getNorthEast(),
  2186. se = bounds.getSouthEast(),
  2187. sw = bounds.getSouthWest();
  2188. return [nw, ne, se, sw];
  2189. },
  2190. _toggleCornerMarkers: function (opacity) {
  2191. for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
  2192. this._resizeMarkers[i].setOpacity(opacity);
  2193. }
  2194. },
  2195. _repositionCornerMarkers: function () {
  2196. var corners = this._getCorners();
  2197. for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
  2198. this._resizeMarkers[i].setLatLng(corners[i]);
  2199. }
  2200. }
  2201. });
  2202. L.Rectangle.addInitHook(function () {
  2203. if (L.Edit.Rectangle) {
  2204. this.editing = new L.Edit.Rectangle(this);
  2205. if (this.options.editable) {
  2206. this.editing.enable();
  2207. }
  2208. }
  2209. });
  2210. L.Edit = L.Edit || {};
  2211. /**
  2212. * @class L.Edit.CircleMarker
  2213. * @aka Edit.Circle
  2214. * @inherits L.Edit.SimpleShape
  2215. */
  2216. L.Edit.CircleMarker = L.Edit.SimpleShape.extend({
  2217. _createMoveMarker: function () {
  2218. var center = this._shape.getLatLng();
  2219. this._moveMarker = this._createMarker(center, this.options.moveIcon);
  2220. },
  2221. _createResizeMarker: function () {
  2222. // To avoid an undefined check in L.Edit.SimpleShape.removeHooks
  2223. this._resizeMarkers = [];
  2224. },
  2225. _move: function (latlng) {
  2226. if (this._resizeMarkers.length) {
  2227. var resizemarkerPoint = this._getResizeMarkerPoint(latlng);
  2228. // Move the resize marker
  2229. this._resizeMarkers[0].setLatLng(resizemarkerPoint);
  2230. }
  2231. // Move the circle
  2232. this._shape.setLatLng(latlng);
  2233. this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});
  2234. },
  2235. });
  2236. L.CircleMarker.addInitHook(function () {
  2237. if (L.Edit.CircleMarker) {
  2238. this.editing = new L.Edit.CircleMarker(this);
  2239. if (this.options.editable) {
  2240. this.editing.enable();
  2241. }
  2242. }
  2243. this.on('add', function () {
  2244. if (this.editing && this.editing.enabled()) {
  2245. this.editing.addHooks();
  2246. }
  2247. });
  2248. this.on('remove', function () {
  2249. if (this.editing && this.editing.enabled()) {
  2250. this.editing.removeHooks();
  2251. }
  2252. });
  2253. });
  2254. L.Edit = L.Edit || {};
  2255. /**
  2256. * @class L.Edit.Circle
  2257. * @aka Edit.Circle
  2258. * @inherits L.Edit.CircleMarker
  2259. */
  2260. L.Edit.Circle = L.Edit.CircleMarker.extend({
  2261. _createResizeMarker: function () {
  2262. var center = this._shape.getLatLng(),
  2263. resizemarkerPoint = this._getResizeMarkerPoint(center);
  2264. this._resizeMarkers = [];
  2265. this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));
  2266. },
  2267. _getResizeMarkerPoint: function (latlng) {
  2268. // From L.shape.getBounds()
  2269. var delta = this._shape._radius * Math.cos(Math.PI / 4),
  2270. point = this._map.project(latlng);
  2271. return this._map.unproject([point.x + delta, point.y - delta]);
  2272. },
  2273. _resize: function (latlng) {
  2274. var moveLatLng = this._moveMarker.getLatLng();
  2275. // Calculate the radius based on the version
  2276. if (L.GeometryUtil.isVersion07x()) {
  2277. radius = moveLatLng.distanceTo(latlng);
  2278. } else {
  2279. radius = this._map.distance(moveLatLng, latlng);
  2280. }
  2281. this._shape.setRadius(radius);
  2282. this._map._editTooltip.updateContent({
  2283. text: L.drawLocal.edit.handlers.edit.tooltip.subtext + '<br />' + L.drawLocal.edit.handlers.edit.tooltip.text,
  2284. subtext: L.drawLocal.draw.handlers.circle.radius + ': ' +
  2285. L.GeometryUtil.readableDistance(radius, true, this.options.feet, this.options.nautic)
  2286. });
  2287. this._shape.setRadius(radius);
  2288. this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});
  2289. }
  2290. });
  2291. L.Circle.addInitHook(function () {
  2292. if (L.Edit.Circle) {
  2293. this.editing = new L.Edit.Circle(this);
  2294. if (this.options.editable) {
  2295. this.editing.enable();
  2296. }
  2297. }
  2298. this.on('add', function () {
  2299. if (this.editing && this.editing.enabled()) {
  2300. this.editing.addHooks();
  2301. }
  2302. });
  2303. this.on('remove', function () {
  2304. if (this.editing && this.editing.enabled()) {
  2305. this.editing.removeHooks();
  2306. }
  2307. });
  2308. });
  2309. L.Map.mergeOptions({
  2310. touchExtend: true
  2311. });
  2312. /**
  2313. * @class L.Map.TouchExtend
  2314. * @aka TouchExtend
  2315. */
  2316. L.Map.TouchExtend = L.Handler.extend({
  2317. // @method initialize(): void
  2318. // Sets TouchExtend private accessor variables
  2319. initialize: function (map) {
  2320. this._map = map;
  2321. this._container = map._container;
  2322. this._pane = map._panes.overlayPane;
  2323. },
  2324. // @method addHooks(): void
  2325. // Adds dom listener events to the map container
  2326. addHooks: function () {
  2327. L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this);
  2328. L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this);
  2329. L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this);
  2330. if (this._detectIE()) {
  2331. L.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this);
  2332. L.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this);
  2333. L.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this);
  2334. L.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this);
  2335. } else {
  2336. L.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this);
  2337. L.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this);
  2338. }
  2339. },
  2340. // @method removeHooks(): void
  2341. // Removes dom listener events from the map container
  2342. removeHooks: function () {
  2343. L.DomEvent.off(this._container, 'touchstart', this._onTouchStart);
  2344. L.DomEvent.off(this._container, 'touchend', this._onTouchEnd);
  2345. L.DomEvent.off(this._container, 'touchmove', this._onTouchMove);
  2346. if (this._detectIE()) {
  2347. L.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart);
  2348. L.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd);
  2349. L.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove);
  2350. L.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel);
  2351. } else {
  2352. L.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel);
  2353. L.DomEvent.off(this._container, 'touchleave', this._onTouchLeave);
  2354. }
  2355. },
  2356. _touchEvent: function (e, type) {
  2357. // #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events
  2358. // _filterClick is what leaflet uses as a workaround.
  2359. // This is a problem with more things than just android. Another problem is touchEnd has no touches in
  2360. // its touch list.
  2361. var touchEvent = {};
  2362. if (typeof e.touches !== 'undefined') {
  2363. if (!e.touches.length) {
  2364. return;
  2365. }
  2366. touchEvent = e.touches[0];
  2367. } else if (e.pointerType === 'touch') {
  2368. touchEvent = e;
  2369. if (!this._filterClick(e)) {
  2370. return;
  2371. }
  2372. } else {
  2373. return;
  2374. }
  2375. var containerPoint = this._map.mouseEventToContainerPoint(touchEvent),
  2376. layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
  2377. latlng = this._map.layerPointToLatLng(layerPoint);
  2378. this._map.fire(type, {
  2379. latlng: latlng,
  2380. layerPoint: layerPoint,
  2381. containerPoint: containerPoint,
  2382. pageX: touchEvent.pageX,
  2383. pageY: touchEvent.pageY,
  2384. originalEvent: e
  2385. });
  2386. },
  2387. /** Borrowed from Leaflet and modified for bool ops **/
  2388. _filterClick: function (e) {
  2389. var timeStamp = (e.timeStamp || e.originalEvent.timeStamp),
  2390. elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
  2391. // are they closer together than 500ms yet more than 100ms?
  2392. // Android typically triggers them ~300ms apart while multiple listeners
  2393. // on the same event should be triggered far faster;
  2394. // or check if click is simulated on the element, and if it is, reject any non-simulated events
  2395. if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
  2396. L.DomEvent.stop(e);
  2397. return false;
  2398. }
  2399. L.DomEvent._lastClick = timeStamp;
  2400. return true;
  2401. },
  2402. _onTouchStart: function (e) {
  2403. if (!this._map._loaded) {
  2404. return;
  2405. }
  2406. var type = 'touchstart';
  2407. this._touchEvent(e, type);
  2408. },
  2409. _onTouchEnd: function (e) {
  2410. if (!this._map._loaded) {
  2411. return;
  2412. }
  2413. var type = 'touchend';
  2414. this._touchEvent(e, type);
  2415. },
  2416. _onTouchCancel: function (e) {
  2417. if (!this._map._loaded) {
  2418. return;
  2419. }
  2420. var type = 'touchcancel';
  2421. if (this._detectIE()) {
  2422. type = 'pointercancel';
  2423. }
  2424. this._touchEvent(e, type);
  2425. },
  2426. _onTouchLeave: function (e) {
  2427. if (!this._map._loaded) {
  2428. return;
  2429. }
  2430. var type = 'touchleave';
  2431. this._touchEvent(e, type);
  2432. },
  2433. _onTouchMove: function (e) {
  2434. if (!this._map._loaded) {
  2435. return;
  2436. }
  2437. var type = 'touchmove';
  2438. this._touchEvent(e, type);
  2439. },
  2440. _detectIE: function () {
  2441. var ua = window.navigator.userAgent;
  2442. var msie = ua.indexOf('MSIE ');
  2443. if (msie > 0) {
  2444. // IE 10 or older => return version number
  2445. return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
  2446. }
  2447. var trident = ua.indexOf('Trident/');
  2448. if (trident > 0) {
  2449. // IE 11 => return version number
  2450. var rv = ua.indexOf('rv:');
  2451. return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
  2452. }
  2453. var edge = ua.indexOf('Edge/');
  2454. if (edge > 0) {
  2455. // IE 12 => return version number
  2456. return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
  2457. }
  2458. // other browser
  2459. return false;
  2460. }
  2461. });
  2462. L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend);
  2463. /**
  2464. * @class L.Marker.Touch
  2465. * @aka Marker.Touch
  2466. *
  2467. * This isn't full Touch support. This is just to get markers to also support dom touch events after creation
  2468. * #TODO: find a better way of getting markers to support touch.
  2469. */
  2470. L.Marker.Touch = L.Marker.extend({
  2471. _initInteraction: function () {
  2472. if (!this.addInteractiveTarget) {
  2473. // 0.7.x support
  2474. return this._initInteractionLegacy();
  2475. }
  2476. // TODO this may need be updated to re-add touch events for 1.0+
  2477. return L.Marker.prototype._initInteraction.apply(this);
  2478. },
  2479. // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js
  2480. // with the addition of the touch events
  2481. _initInteractionLegacy: function () {
  2482. if (!this.options.clickable) {
  2483. return;
  2484. }
  2485. // TODO refactor into something shared with Map/Path/etc. to DRY it up
  2486. var icon = this._icon,
  2487. events = ['dblclick',
  2488. 'mousedown',
  2489. 'mouseover',
  2490. 'mouseout',
  2491. 'contextmenu',
  2492. 'touchstart',
  2493. 'touchend',
  2494. 'touchmove'];
  2495. if (this._detectIE) {
  2496. events.concat(['MSPointerDown',
  2497. 'MSPointerUp',
  2498. 'MSPointerMove',
  2499. 'MSPointerCancel']);
  2500. } else {
  2501. events.concat(['touchcancel']);
  2502. }
  2503. L.DomUtil.addClass(icon, 'leaflet-clickable');
  2504. L.DomEvent.on(icon, 'click', this._onMouseClick, this);
  2505. L.DomEvent.on(icon, 'keypress', this._onKeyPress, this);
  2506. for (var i = 0; i < events.length; i++) {
  2507. L.DomEvent.on(icon, events[i], this._fireMouseEvent, this);
  2508. }
  2509. if (L.Handler.MarkerDrag) {
  2510. this.dragging = new L.Handler.MarkerDrag(this);
  2511. if (this.options.draggable) {
  2512. this.dragging.enable();
  2513. }
  2514. }
  2515. },
  2516. _detectIE: function () {
  2517. var ua = window.navigator.userAgent;
  2518. var msie = ua.indexOf('MSIE ');
  2519. if (msie > 0) {
  2520. // IE 10 or older => return version number
  2521. return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
  2522. }
  2523. var trident = ua.indexOf('Trident/');
  2524. if (trident > 0) {
  2525. // IE 11 => return version number
  2526. var rv = ua.indexOf('rv:');
  2527. return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
  2528. }
  2529. var edge = ua.indexOf('Edge/');
  2530. if (edge > 0) {
  2531. // IE 12 => return version number
  2532. return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
  2533. }
  2534. // other browser
  2535. return false;
  2536. }
  2537. });
  2538. /**
  2539. * @class L.LatLngUtil
  2540. * @aka LatLngUtil
  2541. */
  2542. L.LatLngUtil = {
  2543. // Clones a LatLngs[], returns [][]
  2544. // @method cloneLatLngs(LatLngs[]): L.LatLngs[]
  2545. // Clone the latLng point or points or nested points and return an array with those points
  2546. cloneLatLngs: function (latlngs) {
  2547. var clone = [];
  2548. for (var i = 0, l = latlngs.length; i < l; i++) {
  2549. // Check for nested array (Polyline/Polygon)
  2550. if (Array.isArray(latlngs[i])) {
  2551. clone.push(L.LatLngUtil.cloneLatLngs(latlngs[i]));
  2552. } else {
  2553. clone.push(this.cloneLatLng(latlngs[i]));
  2554. }
  2555. }
  2556. return clone;
  2557. },
  2558. // @method cloneLatLng(LatLng): L.LatLng
  2559. // Clone the latLng and return a new LatLng object.
  2560. cloneLatLng: function (latlng) {
  2561. return L.latLng(latlng.lat, latlng.lng);
  2562. }
  2563. };
  2564. (function () {
  2565. var defaultPrecision = {
  2566. km: 2,
  2567. ha: 2,
  2568. m: 0,
  2569. mi: 2,
  2570. ac: 2,
  2571. yd: 0,
  2572. ft: 0,
  2573. nm: 2
  2574. };
  2575. /**
  2576. * @class L.GeometryUtil
  2577. * @aka GeometryUtil
  2578. */
  2579. L.GeometryUtil = L.extend(L.GeometryUtil || {}, {
  2580. // Ported from the OpenLayers implementation. See https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270
  2581. // @method geodesicArea(): number
  2582. geodesicArea: function (latLngs) {
  2583. var pointsCount = latLngs.length,
  2584. area = 0.0,
  2585. d2r = Math.PI / 180,
  2586. p1, p2;
  2587. if (pointsCount > 2) {
  2588. for (var i = 0; i < pointsCount; i++) {
  2589. p1 = latLngs[i];
  2590. p2 = latLngs[(i + 1) % pointsCount];
  2591. area += ((p2.lng - p1.lng) * d2r) *
  2592. (2 + Math.sin(p1.lat * d2r) + Math.sin(p2.lat * d2r));
  2593. }
  2594. area = area * 6378137.0 * 6378137.0 / 2.0;
  2595. }
  2596. return Math.abs(area);
  2597. },
  2598. // @method formattedNumber(n, precision): string
  2599. // Returns n in specified number format (if defined) and precision
  2600. formattedNumber: function (n, precision) {
  2601. var formatted = parseFloat(n).toFixed(precision),
  2602. format = L.drawLocal.format && L.drawLocal.format.numeric,
  2603. delimiters = format && format.delimiters,
  2604. thousands = delimiters && delimiters.thousands,
  2605. decimal = delimiters && delimiters.decimal;
  2606. if (thousands || decimal) {
  2607. var splitValue = formatted.split('.');
  2608. formatted = thousands ? splitValue[0].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + thousands) : splitValue[0];
  2609. decimal = decimal || '.';
  2610. if (splitValue.length > 1) {
  2611. formatted = formatted + decimal + splitValue[1];
  2612. }
  2613. }
  2614. return formatted;
  2615. },
  2616. // @method readableArea(area, isMetric, precision): string
  2617. // Returns a readable area string in yards or metric.
  2618. // The value will be rounded as defined by the precision option object.
  2619. readableArea: function (area, isMetric, precision) {
  2620. var areaStr,
  2621. units,
  2622. precision = L.Util.extend({}, defaultPrecision, precision);
  2623. if (isMetric) {
  2624. units = ['ha', 'm'];
  2625. type = typeof isMetric;
  2626. if (type === 'string') {
  2627. units = [isMetric];
  2628. } else if (type !== 'boolean') {
  2629. units = isMetric;
  2630. }
  2631. if (area >= 1000000 && units.indexOf('km') !== -1) {
  2632. areaStr = L.GeometryUtil.formattedNumber(area * 0.000001, precision['km']) + ' km²';
  2633. } else if (area >= 10000 && units.indexOf('ha') !== -1) {
  2634. areaStr = L.GeometryUtil.formattedNumber(area * 0.0001, precision['ha']) + ' ha';
  2635. } else {
  2636. areaStr = L.GeometryUtil.formattedNumber(area, precision['m']) + ' m²';
  2637. }
  2638. } else {
  2639. area /= 0.836127; // Square yards in 1 meter
  2640. if (area >= 3097600) { //3097600 square yards in 1 square mile
  2641. areaStr = L.GeometryUtil.formattedNumber(area / 3097600, precision['mi']) + ' mi²';
  2642. } else if (area >= 4840) { //4840 square yards in 1 acre
  2643. areaStr = L.GeometryUtil.formattedNumber(area / 4840, precision['ac']) + ' acres';
  2644. } else {
  2645. areaStr = L.GeometryUtil.formattedNumber(area, precision['yd']) + ' yd²';
  2646. }
  2647. }
  2648. return areaStr;
  2649. },
  2650. // @method readableDistance(distance, units): string
  2651. // Converts a metric distance to one of [ feet, nauticalMile, metric or yards ] string
  2652. //
  2653. // @alternative
  2654. // @method readableDistance(distance, isMetric, useFeet, isNauticalMile, precision): string
  2655. // Converts metric distance to distance string.
  2656. // The value will be rounded as defined by the precision option object.
  2657. readableDistance: function (distance, isMetric, isFeet, isNauticalMile, precision) {
  2658. var distanceStr,
  2659. units,
  2660. precision = L.Util.extend({}, defaultPrecision, precision);
  2661. if (isMetric) {
  2662. units = typeof isMetric == 'string' ? isMetric : 'metric';
  2663. } else if (isFeet) {
  2664. units = 'feet';
  2665. } else if (isNauticalMile) {
  2666. units = 'nauticalMile';
  2667. } else {
  2668. units = 'yards';
  2669. }
  2670. switch (units) {
  2671. case 'metric':
  2672. // show metres when distance is < 1km, then show km
  2673. if (distance > 1000) {
  2674. distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['km']) + ' km';
  2675. } else {
  2676. distanceStr = L.GeometryUtil.formattedNumber(distance, precision['m']) + ' m';
  2677. }
  2678. break;
  2679. case 'feet':
  2680. distance *= 1.09361 * 3;
  2681. distanceStr = L.GeometryUtil.formattedNumber(distance, precision['ft']) + ' ft';
  2682. break;
  2683. case 'nauticalMile':
  2684. distance *= 0.53996;
  2685. distanceStr = L.GeometryUtil.formattedNumber(distance / 1000, precision['nm']) + ' nm';
  2686. break;
  2687. case 'yards':
  2688. default:
  2689. distance *= 1.09361;
  2690. if (distance > 1760) {
  2691. distanceStr = L.GeometryUtil.formattedNumber(distance / 1760, precision['mi']) + ' miles';
  2692. } else {
  2693. distanceStr = L.GeometryUtil.formattedNumber(distance, precision['yd']) + ' yd';
  2694. }
  2695. break;
  2696. }
  2697. return distanceStr;
  2698. },
  2699. // @method isVersion07x(): boolean
  2700. // Returns true if the Leaflet version is 0.7.x, false otherwise.
  2701. isVersion07x: function () {
  2702. var version = L.version.split('.');
  2703. //If Version is == 0.7.*
  2704. return parseInt(version[0], 10) === 0 && parseInt(version[1], 10) === 7;
  2705. },
  2706. });
  2707. })();
  2708. /**
  2709. * @class L.LineUtil
  2710. * @aka Util
  2711. * @aka L.Utils
  2712. */
  2713. L.Util.extend(L.LineUtil, {
  2714. // @method segmentsIntersect(): boolean
  2715. // Checks to see if two line segments intersect. Does not handle degenerate cases.
  2716. // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
  2717. segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {
  2718. return this._checkCounterclockwise(p, p2, p3) !==
  2719. this._checkCounterclockwise(p1, p2, p3) &&
  2720. this._checkCounterclockwise(p, p1, p2) !==
  2721. this._checkCounterclockwise(p, p1, p3);
  2722. },
  2723. // check to see if points are in counterclockwise order
  2724. _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
  2725. return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);
  2726. }
  2727. });
  2728. /**
  2729. * @class L.Polyline
  2730. * @aka Polyline
  2731. */
  2732. L.Polyline.include({
  2733. // @method intersects(): boolean
  2734. // Check to see if this polyline has any linesegments that intersect.
  2735. // NOTE: does not support detecting intersection for degenerate cases.
  2736. intersects: function () {
  2737. var points = this._getProjectedPoints(),
  2738. len = points ? points.length : 0,
  2739. i, p, p1;
  2740. if (this._tooFewPointsForIntersection()) {
  2741. return false;
  2742. }
  2743. for (i = len - 1; i >= 3; i--) {
  2744. p = points[i - 1];
  2745. p1 = points[i];
  2746. if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
  2747. return true;
  2748. }
  2749. }
  2750. return false;
  2751. },
  2752. // @method newLatLngIntersects(): boolean
  2753. // Check for intersection if new latlng was added to this polyline.
  2754. // NOTE: does not support detecting intersection for degenerate cases.
  2755. newLatLngIntersects: function (latlng, skipFirst) {
  2756. // Cannot check a polyline for intersecting lats/lngs when not added to the map
  2757. if (!this._map) {
  2758. return false;
  2759. }
  2760. return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
  2761. },
  2762. // @method newPointIntersects(): boolean
  2763. // Check for intersection if new point was added to this polyline.
  2764. // newPoint must be a layer point.
  2765. // NOTE: does not support detecting intersection for degenerate cases.
  2766. newPointIntersects: function (newPoint, skipFirst) {
  2767. var points = this._getProjectedPoints(),
  2768. len = points ? points.length : 0,
  2769. lastPoint = points ? points[len - 1] : null,
  2770. // The previous previous line segment. Previous line segment doesn't need testing.
  2771. maxIndex = len - 2;
  2772. if (this._tooFewPointsForIntersection(1)) {
  2773. return false;
  2774. }
  2775. return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
  2776. },
  2777. // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
  2778. // Cannot have intersection when < 3 line segments (< 4 points)
  2779. _tooFewPointsForIntersection: function (extraPoints) {
  2780. var points = this._getProjectedPoints(),
  2781. len = points ? points.length : 0;
  2782. // Increment length by extraPoints if present
  2783. len += extraPoints || 0;
  2784. return !points || len <= 3;
  2785. },
  2786. // Checks a line segment intersections with any line segments before its predecessor.
  2787. // Don't need to check the predecessor as will never intersect.
  2788. _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
  2789. var points = this._getProjectedPoints(),
  2790. p2, p3;
  2791. minIndex = minIndex || 0;
  2792. // Check all previous line segments (beside the immediately previous) for intersections
  2793. for (var j = maxIndex; j > minIndex; j--) {
  2794. p2 = points[j - 1];
  2795. p3 = points[j];
  2796. if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
  2797. return true;
  2798. }
  2799. }
  2800. return false;
  2801. },
  2802. _getProjectedPoints: function () {
  2803. if (!this._defaultShape) {
  2804. return this._originalPoints;
  2805. }
  2806. var points = [],
  2807. _shape = this._defaultShape();
  2808. for (var i = 0; i < _shape.length; i++) {
  2809. points.push(this._map.latLngToLayerPoint(_shape[i]));
  2810. }
  2811. return points;
  2812. }
  2813. });
  2814. /**
  2815. * @class L.Polygon
  2816. * @aka Polygon
  2817. */
  2818. L.Polygon.include({
  2819. // @method intersects(): boolean
  2820. // Checks a polygon for any intersecting line segments. Ignores holes.
  2821. intersects: function () {
  2822. var polylineIntersects,
  2823. points = this._getProjectedPoints(),
  2824. len, firstPoint, lastPoint, maxIndex;
  2825. if (this._tooFewPointsForIntersection()) {
  2826. return false;
  2827. }
  2828. polylineIntersects = L.Polyline.prototype.intersects.call(this);
  2829. // If already found an intersection don't need to check for any more.
  2830. if (polylineIntersects) {
  2831. return true;
  2832. }
  2833. len = points.length;
  2834. firstPoint = points[0];
  2835. lastPoint = points[len - 1];
  2836. maxIndex = len - 2;
  2837. // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)
  2838. return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);
  2839. }
  2840. });
  2841. /**
  2842. * @class L.Control.Draw
  2843. * @aka L.Draw
  2844. */
  2845. L.Control.Draw = L.Control.extend({
  2846. // Options
  2847. options: {
  2848. position: 'topleft',
  2849. draw: {},
  2850. edit: false
  2851. },
  2852. // @method initialize(): void
  2853. // Initializes draw control, toolbars from the options
  2854. initialize: function (options) {
  2855. if (L.version < '0.7') {
  2856. throw new Error('Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/');
  2857. }
  2858. L.Control.prototype.initialize.call(this, options);
  2859. var toolbar;
  2860. this._toolbars = {};
  2861. // Initialize toolbars
  2862. if (L.DrawToolbar && this.options.draw) {
  2863. toolbar = new L.DrawToolbar(this.options.draw);
  2864. this._toolbars[L.DrawToolbar.TYPE] = toolbar;
  2865. // Listen for when toolbar is enabled
  2866. this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);
  2867. }
  2868. if (L.EditToolbar && this.options.edit) {
  2869. toolbar = new L.EditToolbar(this.options.edit);
  2870. this._toolbars[L.EditToolbar.TYPE] = toolbar;
  2871. // Listen for when toolbar is enabled
  2872. this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);
  2873. }
  2874. L.toolbar = this; //set global var for editing the toolbar
  2875. },
  2876. // @method onAdd(): container
  2877. // Adds the toolbar container to the map
  2878. onAdd: function (map) {
  2879. var container = L.DomUtil.create('div', 'leaflet-draw'),
  2880. addedTopClass = false,
  2881. topClassName = 'leaflet-draw-toolbar-top',
  2882. toolbarContainer;
  2883. for (var toolbarId in this._toolbars) {
  2884. if (this._toolbars.hasOwnProperty(toolbarId)) {
  2885. toolbarContainer = this._toolbars[toolbarId].addToolbar(map);
  2886. if (toolbarContainer) {
  2887. // Add class to the first toolbar to remove the margin
  2888. if (!addedTopClass) {
  2889. if (!L.DomUtil.hasClass(toolbarContainer, topClassName)) {
  2890. L.DomUtil.addClass(toolbarContainer.childNodes[0], topClassName);
  2891. }
  2892. addedTopClass = true;
  2893. }
  2894. container.appendChild(toolbarContainer);
  2895. }
  2896. }
  2897. }
  2898. return container;
  2899. },
  2900. // @method onRemove(): void
  2901. // Removes the toolbars from the map toolbar container
  2902. onRemove: function () {
  2903. for (var toolbarId in this._toolbars) {
  2904. if (this._toolbars.hasOwnProperty(toolbarId)) {
  2905. this._toolbars[toolbarId].removeToolbar();
  2906. }
  2907. }
  2908. },
  2909. // @method setDrawingOptions(options): void
  2910. // Sets options to all toolbar instances
  2911. setDrawingOptions: function (options) {
  2912. for (var toolbarId in this._toolbars) {
  2913. if (this._toolbars[toolbarId] instanceof L.DrawToolbar) {
  2914. this._toolbars[toolbarId].setOptions(options);
  2915. }
  2916. }
  2917. },
  2918. _toolbarEnabled: function (e) {
  2919. var enabledToolbar = e.target;
  2920. for (var toolbarId in this._toolbars) {
  2921. if (this._toolbars[toolbarId] !== enabledToolbar) {
  2922. this._toolbars[toolbarId].disable();
  2923. }
  2924. }
  2925. }
  2926. });
  2927. L.Map.mergeOptions({
  2928. drawControlTooltips: true,
  2929. drawControl: false
  2930. });
  2931. L.Map.addInitHook(function () {
  2932. if (this.options.drawControl) {
  2933. this.drawControl = new L.Control.Draw();
  2934. this.addControl(this.drawControl);
  2935. }
  2936. });
  2937. /**
  2938. * @class L.Draw.Toolbar
  2939. * @aka Toolbar
  2940. *
  2941. * The toolbar class of the API — it is used to create the ui
  2942. * This will be depreciated
  2943. *
  2944. * @example
  2945. *
  2946. * ```js
  2947. * var toolbar = L.Toolbar();
  2948. * toolbar.addToolbar(map);
  2949. * ```
  2950. *
  2951. * ### Disabling a toolbar
  2952. *
  2953. * If you do not want a particular toolbar in your app you can turn it off by setting the toolbar to false.
  2954. *
  2955. * ```js
  2956. * var drawControl = new L.Control.Draw({
  2957. * draw: false,
  2958. * edit: {
  2959. * featureGroup: editableLayers
  2960. * }
  2961. * });
  2962. * ```
  2963. *
  2964. * ### Disabling a toolbar item
  2965. *
  2966. * If you want to turn off a particular toolbar item, set it to false. The following disables drawing polygons and
  2967. * markers. It also turns off the ability to edit layers.
  2968. *
  2969. * ```js
  2970. * var drawControl = new L.Control.Draw({
  2971. * draw: {
  2972. * polygon: false,
  2973. * marker: false
  2974. * },
  2975. * edit: {
  2976. * featureGroup: editableLayers,
  2977. * edit: false
  2978. * }
  2979. * });
  2980. * ```
  2981. */
  2982. L.Toolbar = L.Class.extend({
  2983. // @section Methods for modifying the toolbar
  2984. // @method initialize(options): void
  2985. // Toolbar constructor
  2986. initialize: function (options) {
  2987. L.setOptions(this, options);
  2988. this._modes = {};
  2989. this._actionButtons = [];
  2990. this._activeMode = null;
  2991. var version = L.version.split('.');
  2992. //If Version is >= 1.2.0
  2993. if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
  2994. L.Toolbar.include(L.Evented.prototype);
  2995. } else {
  2996. L.Toolbar.include(L.Mixin.Events);
  2997. }
  2998. },
  2999. // @method enabled(): boolean
  3000. // Gets a true/false of whether the toolbar is enabled
  3001. enabled: function () {
  3002. return this._activeMode !== null;
  3003. },
  3004. // @method disable(): void
  3005. // Disables the toolbar
  3006. disable: function () {
  3007. if (!this.enabled()) {
  3008. return;
  3009. }
  3010. this._activeMode.handler.disable();
  3011. },
  3012. // @method addToolbar(map): L.DomUtil
  3013. // Adds the toolbar to the map and returns the toolbar dom element
  3014. addToolbar: function (map) {
  3015. var container = L.DomUtil.create('div', 'leaflet-draw-section'),
  3016. buttonIndex = 0,
  3017. buttonClassPrefix = this._toolbarClass || '',
  3018. modeHandlers = this.getModeHandlers(map),
  3019. i;
  3020. this._toolbarContainer = L.DomUtil.create('div', 'leaflet-draw-toolbar leaflet-bar');
  3021. this._map = map;
  3022. for (i = 0; i < modeHandlers.length; i++) {
  3023. if (modeHandlers[i].enabled) {
  3024. this._initModeHandler(
  3025. modeHandlers[i].handler,
  3026. this._toolbarContainer,
  3027. buttonIndex++,
  3028. buttonClassPrefix,
  3029. modeHandlers[i].title
  3030. );
  3031. }
  3032. }
  3033. // if no buttons were added, do not add the toolbar
  3034. if (!buttonIndex) {
  3035. return;
  3036. }
  3037. // Save button index of the last button, -1 as we would have ++ after the last button
  3038. this._lastButtonIndex = --buttonIndex;
  3039. // Create empty actions part of the toolbar
  3040. this._actionsContainer = L.DomUtil.create('ul', 'leaflet-draw-actions');
  3041. // Add draw and cancel containers to the control container
  3042. container.appendChild(this._toolbarContainer);
  3043. container.appendChild(this._actionsContainer);
  3044. return container;
  3045. },
  3046. // @method removeToolbar(): void
  3047. // Removes the toolbar and drops the handler event listeners
  3048. removeToolbar: function () {
  3049. // Dispose each handler
  3050. for (var handlerId in this._modes) {
  3051. if (this._modes.hasOwnProperty(handlerId)) {
  3052. // Unbind handler button
  3053. this._disposeButton(
  3054. this._modes[handlerId].button,
  3055. this._modes[handlerId].handler.enable,
  3056. this._modes[handlerId].handler
  3057. );
  3058. // Make sure is disabled
  3059. this._modes[handlerId].handler.disable();
  3060. // Unbind handler
  3061. this._modes[handlerId].handler
  3062. .off('enabled', this._handlerActivated, this)
  3063. .off('disabled', this._handlerDeactivated, this);
  3064. }
  3065. }
  3066. this._modes = {};
  3067. // Dispose the actions toolbar
  3068. for (var i = 0, l = this._actionButtons.length; i < l; i++) {
  3069. this._disposeButton(
  3070. this._actionButtons[i].button,
  3071. this._actionButtons[i].callback,
  3072. this
  3073. );
  3074. }
  3075. this._actionButtons = [];
  3076. this._actionsContainer = null;
  3077. },
  3078. _initModeHandler: function (handler, container, buttonIndex, classNamePredix, buttonTitle) {
  3079. var type = handler.type;
  3080. this._modes[type] = {};
  3081. this._modes[type].handler = handler;
  3082. this._modes[type].button = this._createButton({
  3083. type: type,
  3084. title: buttonTitle,
  3085. className: classNamePredix + '-' + type,
  3086. container: container,
  3087. callback: this._modes[type].handler.enable,
  3088. context: this._modes[type].handler
  3089. });
  3090. this._modes[type].buttonIndex = buttonIndex;
  3091. this._modes[type].handler
  3092. .on('enabled', this._handlerActivated, this)
  3093. .on('disabled', this._handlerDeactivated, this);
  3094. },
  3095. /* Detect iOS based on browser User Agent, based on:
  3096. * http://stackoverflow.com/a/9039885 */
  3097. _detectIOS: function () {
  3098. var iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream);
  3099. return iOS;
  3100. },
  3101. _createButton: function (options) {
  3102. var link = L.DomUtil.create('a', options.className || '', options.container);
  3103. // Screen reader tag
  3104. var sr = L.DomUtil.create('span', 'sr-only', options.container);
  3105. link.href = '#';
  3106. link.appendChild(sr);
  3107. if (options.title) {
  3108. link.title = options.title;
  3109. sr.innerHTML = options.title;
  3110. }
  3111. if (options.text) {
  3112. link.innerHTML = options.text;
  3113. sr.innerHTML = options.text;
  3114. }
  3115. /* iOS does not use click events */
  3116. var buttonEvent = this._detectIOS() ? 'touchstart' : 'click';
  3117. L.DomEvent
  3118. .on(link, 'click', L.DomEvent.stopPropagation)
  3119. .on(link, 'mousedown', L.DomEvent.stopPropagation)
  3120. .on(link, 'dblclick', L.DomEvent.stopPropagation)
  3121. .on(link, 'touchstart', L.DomEvent.stopPropagation)
  3122. .on(link, 'click', L.DomEvent.preventDefault)
  3123. .on(link, buttonEvent, options.callback, options.context);
  3124. return link;
  3125. },
  3126. _disposeButton: function (button, callback) {
  3127. /* iOS does not use click events */
  3128. var buttonEvent = this._detectIOS() ? 'touchstart' : 'click';
  3129. L.DomEvent
  3130. .off(button, 'click', L.DomEvent.stopPropagation)
  3131. .off(button, 'mousedown', L.DomEvent.stopPropagation)
  3132. .off(button, 'dblclick', L.DomEvent.stopPropagation)
  3133. .off(button, 'touchstart', L.DomEvent.stopPropagation)
  3134. .off(button, 'click', L.DomEvent.preventDefault)
  3135. .off(button, buttonEvent, callback);
  3136. },
  3137. _handlerActivated: function (e) {
  3138. // Disable active mode (if present)
  3139. this.disable();
  3140. // Cache new active feature
  3141. this._activeMode = this._modes[e.handler];
  3142. L.DomUtil.addClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');
  3143. this._showActionsToolbar();
  3144. this.fire('enable');
  3145. },
  3146. _handlerDeactivated: function () {
  3147. this._hideActionsToolbar();
  3148. L.DomUtil.removeClass(this._activeMode.button, 'leaflet-draw-toolbar-button-enabled');
  3149. this._activeMode = null;
  3150. this.fire('disable');
  3151. },
  3152. _createActions: function (handler) {
  3153. var container = this._actionsContainer,
  3154. buttons = this.getActions(handler),
  3155. l = buttons.length,
  3156. li, di, dl, button;
  3157. // Dispose the actions toolbar (todo: dispose only not used buttons)
  3158. for (di = 0, dl = this._actionButtons.length; di < dl; di++) {
  3159. this._disposeButton(this._actionButtons[di].button, this._actionButtons[di].callback);
  3160. }
  3161. this._actionButtons = [];
  3162. // Remove all old buttons
  3163. while (container.firstChild) {
  3164. container.removeChild(container.firstChild);
  3165. }
  3166. for (var i = 0; i < l; i++) {
  3167. if ('enabled' in buttons[i] && !buttons[i].enabled) {
  3168. continue;
  3169. }
  3170. li = L.DomUtil.create('li', '', container);
  3171. button = this._createButton({
  3172. title: buttons[i].title,
  3173. text: buttons[i].text,
  3174. container: li,
  3175. callback: buttons[i].callback,
  3176. context: buttons[i].context
  3177. });
  3178. this._actionButtons.push({
  3179. button: button,
  3180. callback: buttons[i].callback
  3181. });
  3182. }
  3183. },
  3184. _showActionsToolbar: function () {
  3185. var buttonIndex = this._activeMode.buttonIndex,
  3186. lastButtonIndex = this._lastButtonIndex,
  3187. toolbarPosition = this._activeMode.button.offsetTop - 1;
  3188. // Recreate action buttons on every click
  3189. this._createActions(this._activeMode.handler);
  3190. // Correctly position the cancel button
  3191. this._actionsContainer.style.top = toolbarPosition + 'px';
  3192. if (buttonIndex === 0) {
  3193. L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
  3194. L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-top');
  3195. }
  3196. if (buttonIndex === lastButtonIndex) {
  3197. L.DomUtil.addClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
  3198. L.DomUtil.addClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
  3199. }
  3200. this._actionsContainer.style.display = 'block';
  3201. this._map.fire(L.Draw.Event.TOOLBAROPENED);
  3202. },
  3203. _hideActionsToolbar: function () {
  3204. this._actionsContainer.style.display = 'none';
  3205. L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-notop');
  3206. L.DomUtil.removeClass(this._toolbarContainer, 'leaflet-draw-toolbar-nobottom');
  3207. L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-top');
  3208. L.DomUtil.removeClass(this._actionsContainer, 'leaflet-draw-actions-bottom');
  3209. this._map.fire(L.Draw.Event.TOOLBARCLOSED);
  3210. }
  3211. });
  3212. L.Draw = L.Draw || {};
  3213. /**
  3214. * @class L.Draw.Tooltip
  3215. * @aka Tooltip
  3216. *
  3217. * The tooltip class — it is used to display the tooltip while drawing
  3218. * This will be depreciated
  3219. *
  3220. * @example
  3221. *
  3222. * ```js
  3223. * var tooltip = L.Draw.Tooltip();
  3224. * ```
  3225. *
  3226. */
  3227. L.Draw.Tooltip = L.Class.extend({
  3228. // @section Methods for modifying draw state
  3229. // @method initialize(map): void
  3230. // Tooltip constructor
  3231. initialize: function (map) {
  3232. this._map = map;
  3233. this._popupPane = map._panes.popupPane;
  3234. this._visible = false;
  3235. this._container = map.options.drawControlTooltips ?
  3236. L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;
  3237. this._singleLineLabel = false;
  3238. this._map.on('mouseout', this._onMouseOut, this);
  3239. },
  3240. // @method dispose(): void
  3241. // Remove Tooltip DOM and unbind events
  3242. dispose: function () {
  3243. this._map.off('mouseout', this._onMouseOut, this);
  3244. if (this._container) {
  3245. this._popupPane.removeChild(this._container);
  3246. this._container = null;
  3247. }
  3248. },
  3249. // @method updateContent(labelText): this
  3250. // Changes the tooltip text to string in function call
  3251. updateContent: function (labelText) {
  3252. if (!this._container) {
  3253. return this;
  3254. }
  3255. labelText.subtext = labelText.subtext || '';
  3256. // update the vertical position (only if changed)
  3257. if (labelText.subtext.length === 0 && !this._singleLineLabel) {
  3258. L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');
  3259. this._singleLineLabel = true;
  3260. } else if (labelText.subtext.length > 0 && this._singleLineLabel) {
  3261. L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');
  3262. this._singleLineLabel = false;
  3263. }
  3264. this._container.innerHTML =
  3265. (labelText.subtext.length > 0 ?
  3266. '<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
  3267. '<span>' + labelText.text + '</span>';
  3268. if (!labelText.text && !labelText.subtext) {
  3269. this._visible = false;
  3270. this._container.style.visibility = 'hidden';
  3271. } else {
  3272. this._visible = true;
  3273. this._container.style.visibility = 'inherit';
  3274. }
  3275. return this;
  3276. },
  3277. // @method updatePosition(latlng): this
  3278. // Changes the location of the tooltip
  3279. updatePosition: function (latlng) {
  3280. var pos = this._map.latLngToLayerPoint(latlng),
  3281. tooltipContainer = this._container;
  3282. if (this._container) {
  3283. if (this._visible) {
  3284. tooltipContainer.style.visibility = 'inherit';
  3285. }
  3286. L.DomUtil.setPosition(tooltipContainer, pos);
  3287. }
  3288. return this;
  3289. },
  3290. // @method showAsError(): this
  3291. // Applies error class to tooltip
  3292. showAsError: function () {
  3293. if (this._container) {
  3294. L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');
  3295. }
  3296. return this;
  3297. },
  3298. // @method removeError(): this
  3299. // Removes the error class from the tooltip
  3300. removeError: function () {
  3301. if (this._container) {
  3302. L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');
  3303. }
  3304. return this;
  3305. },
  3306. _onMouseOut: function () {
  3307. if (this._container) {
  3308. this._container.style.visibility = 'hidden';
  3309. }
  3310. }
  3311. });
  3312. /**
  3313. * @class L.DrawToolbar
  3314. * @aka Toolbar
  3315. */
  3316. L.DrawToolbar = L.Toolbar.extend({
  3317. statics: {
  3318. TYPE: 'draw'
  3319. },
  3320. options: {
  3321. polyline: {},
  3322. polygon: {},
  3323. rectangle: {},
  3324. circle: {},
  3325. marker: {},
  3326. circlemarker: {}
  3327. },
  3328. // @method initialize(): void
  3329. initialize: function (options) {
  3330. // Ensure that the options are merged correctly since L.extend is only shallow
  3331. for (var type in this.options) {
  3332. if (this.options.hasOwnProperty(type)) {
  3333. if (options[type]) {
  3334. options[type] = L.extend({}, this.options[type], options[type]);
  3335. }
  3336. }
  3337. }
  3338. this._toolbarClass = 'leaflet-draw-draw';
  3339. L.Toolbar.prototype.initialize.call(this, options);
  3340. },
  3341. // @method getModeHandlers(): object
  3342. // Get mode handlers information
  3343. getModeHandlers: function (map) {
  3344. return [
  3345. {
  3346. enabled: this.options.polyline,
  3347. handler: new L.Draw.Polyline(map, this.options.polyline),
  3348. title: L.drawLocal.draw.toolbar.buttons.polyline
  3349. },
  3350. {
  3351. enabled: this.options.polygon,
  3352. handler: new L.Draw.Polygon(map, this.options.polygon),
  3353. title: L.drawLocal.draw.toolbar.buttons.polygon
  3354. },
  3355. {
  3356. enabled: this.options.rectangle,
  3357. handler: new L.Draw.Rectangle(map, this.options.rectangle),
  3358. title: L.drawLocal.draw.toolbar.buttons.rectangle
  3359. },
  3360. {
  3361. enabled: this.options.circle,
  3362. handler: new L.Draw.Circle(map, this.options.circle),
  3363. title: L.drawLocal.draw.toolbar.buttons.circle
  3364. },
  3365. {
  3366. enabled: this.options.marker,
  3367. handler: new L.Draw.Marker(map, this.options.marker),
  3368. title: L.drawLocal.draw.toolbar.buttons.marker
  3369. },
  3370. {
  3371. enabled: this.options.circlemarker,
  3372. handler: new L.Draw.CircleMarker(map, this.options.circlemarker),
  3373. title: L.drawLocal.draw.toolbar.buttons.circlemarker
  3374. }
  3375. ];
  3376. },
  3377. // @method getActions(): object
  3378. // Get action information
  3379. getActions: function (handler) {
  3380. return [
  3381. {
  3382. enabled: handler.completeShape,
  3383. title: L.drawLocal.draw.toolbar.finish.title,
  3384. text: L.drawLocal.draw.toolbar.finish.text,
  3385. callback: handler.completeShape,
  3386. context: handler
  3387. },
  3388. {
  3389. enabled: handler.deleteLastVertex,
  3390. title: L.drawLocal.draw.toolbar.undo.title,
  3391. text: L.drawLocal.draw.toolbar.undo.text,
  3392. callback: handler.deleteLastVertex,
  3393. context: handler
  3394. },
  3395. {
  3396. title: L.drawLocal.draw.toolbar.actions.title,
  3397. text: L.drawLocal.draw.toolbar.actions.text,
  3398. callback: this.disable,
  3399. context: this
  3400. }
  3401. ];
  3402. },
  3403. // @method setOptions(): void
  3404. // Sets the options to the toolbar
  3405. setOptions: function (options) {
  3406. L.setOptions(this, options);
  3407. for (var type in this._modes) {
  3408. if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {
  3409. this._modes[type].handler.setOptions(options[type]);
  3410. }
  3411. }
  3412. }
  3413. });
  3414. /*L.Map.mergeOptions({
  3415. editControl: true
  3416. });*/
  3417. /**
  3418. * @class L.EditToolbar
  3419. * @aka EditToolbar
  3420. */
  3421. L.EditToolbar = L.Toolbar.extend({
  3422. statics: {
  3423. TYPE: 'edit'
  3424. },
  3425. options: {
  3426. edit: {
  3427. selectedPathOptions: {
  3428. dashArray: '10, 10',
  3429. fill: true,
  3430. fillColor: '#fe57a1',
  3431. fillOpacity: 0.1,
  3432. // Whether to user the existing layers color
  3433. maintainColor: false
  3434. }
  3435. },
  3436. remove: {},
  3437. poly: null,
  3438. featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */
  3439. },
  3440. // @method intialize(): void
  3441. initialize: function (options) {
  3442. // Need to set this manually since null is an acceptable value here
  3443. if (options.edit) {
  3444. if (typeof options.edit.selectedPathOptions === 'undefined') {
  3445. options.edit.selectedPathOptions = this.options.edit.selectedPathOptions;
  3446. }
  3447. options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);
  3448. }
  3449. if (options.remove) {
  3450. options.remove = L.extend({}, this.options.remove, options.remove);
  3451. }
  3452. if (options.poly) {
  3453. options.poly = L.extend({}, this.options.poly, options.poly);
  3454. }
  3455. this._toolbarClass = 'leaflet-draw-edit';
  3456. L.Toolbar.prototype.initialize.call(this, options);
  3457. this._selectedFeatureCount = 0;
  3458. },
  3459. // @method getModeHandlers(): object
  3460. // Get mode handlers information
  3461. getModeHandlers: function (map) {
  3462. var featureGroup = this.options.featureGroup;
  3463. return [
  3464. {
  3465. enabled: this.options.edit,
  3466. handler: new L.EditToolbar.Edit(map, {
  3467. featureGroup: featureGroup,
  3468. selectedPathOptions: this.options.edit.selectedPathOptions,
  3469. poly: this.options.poly
  3470. }),
  3471. title: L.drawLocal.edit.toolbar.buttons.edit
  3472. },
  3473. {
  3474. enabled: this.options.remove,
  3475. handler: new L.EditToolbar.Delete(map, {
  3476. featureGroup: featureGroup
  3477. }),
  3478. title: L.drawLocal.edit.toolbar.buttons.remove
  3479. }
  3480. ];
  3481. },
  3482. // @method getActions(): object
  3483. // Get actions information
  3484. getActions: function (handler) {
  3485. var actions = [
  3486. {
  3487. title: L.drawLocal.edit.toolbar.actions.save.title,
  3488. text: L.drawLocal.edit.toolbar.actions.save.text,
  3489. callback: this._save,
  3490. context: this
  3491. },
  3492. {
  3493. title: L.drawLocal.edit.toolbar.actions.cancel.title,
  3494. text: L.drawLocal.edit.toolbar.actions.cancel.text,
  3495. callback: this.disable,
  3496. context: this
  3497. }
  3498. ];
  3499. if (handler.removeAllLayers) {
  3500. actions.push({
  3501. title: L.drawLocal.edit.toolbar.actions.clearAll.title,
  3502. text: L.drawLocal.edit.toolbar.actions.clearAll.text,
  3503. callback: this._clearAllLayers,
  3504. context: this
  3505. });
  3506. }
  3507. return actions;
  3508. },
  3509. // @method addToolbar(map): L.DomUtil
  3510. // Adds the toolbar to the map
  3511. addToolbar: function (map) {
  3512. var container = L.Toolbar.prototype.addToolbar.call(this, map);
  3513. this._checkDisabled();
  3514. this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);
  3515. return container;
  3516. },
  3517. // @method removeToolbar(): void
  3518. // Removes the toolbar from the map
  3519. removeToolbar: function () {
  3520. this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);
  3521. L.Toolbar.prototype.removeToolbar.call(this);
  3522. },
  3523. // @method disable(): void
  3524. // Disables the toolbar
  3525. disable: function () {
  3526. if (!this.enabled()) {
  3527. return;
  3528. }
  3529. this._activeMode.handler.revertLayers();
  3530. L.Toolbar.prototype.disable.call(this);
  3531. },
  3532. _save: function () {
  3533. this._activeMode.handler.save();
  3534. if (this._activeMode) {
  3535. this._activeMode.handler.disable();
  3536. }
  3537. },
  3538. _clearAllLayers: function () {
  3539. this._activeMode.handler.removeAllLayers();
  3540. if (this._activeMode) {
  3541. this._activeMode.handler.disable();
  3542. }
  3543. },
  3544. _checkDisabled: function () {
  3545. var featureGroup = this.options.featureGroup,
  3546. hasLayers = featureGroup.getLayers().length !== 0,
  3547. button;
  3548. if (this.options.edit) {
  3549. button = this._modes[L.EditToolbar.Edit.TYPE].button;
  3550. if (hasLayers) {
  3551. L.DomUtil.removeClass(button, 'leaflet-disabled');
  3552. } else {
  3553. L.DomUtil.addClass(button, 'leaflet-disabled');
  3554. }
  3555. button.setAttribute(
  3556. 'title',
  3557. hasLayers ?
  3558. L.drawLocal.edit.toolbar.buttons.edit
  3559. : L.drawLocal.edit.toolbar.buttons.editDisabled
  3560. );
  3561. }
  3562. if (this.options.remove) {
  3563. button = this._modes[L.EditToolbar.Delete.TYPE].button;
  3564. if (hasLayers) {
  3565. L.DomUtil.removeClass(button, 'leaflet-disabled');
  3566. } else {
  3567. L.DomUtil.addClass(button, 'leaflet-disabled');
  3568. }
  3569. button.setAttribute(
  3570. 'title',
  3571. hasLayers ?
  3572. L.drawLocal.edit.toolbar.buttons.remove
  3573. : L.drawLocal.edit.toolbar.buttons.removeDisabled
  3574. );
  3575. }
  3576. }
  3577. });
  3578. /**
  3579. * @class L.EditToolbar.Edit
  3580. * @aka EditToolbar.Edit
  3581. */
  3582. L.EditToolbar.Edit = L.Handler.extend({
  3583. statics: {
  3584. TYPE: 'edit'
  3585. },
  3586. // @method intialize(): void
  3587. initialize: function (map, options) {
  3588. L.Handler.prototype.initialize.call(this, map);
  3589. L.setOptions(this, options);
  3590. // Store the selectable layer group for ease of access
  3591. this._featureGroup = options.featureGroup;
  3592. if (!(this._featureGroup instanceof L.FeatureGroup)) {
  3593. throw new Error('options.featureGroup must be a L.FeatureGroup');
  3594. }
  3595. this._uneditedLayerProps = {};
  3596. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  3597. this.type = L.EditToolbar.Edit.TYPE;
  3598. var version = L.version.split('.');
  3599. //If Version is >= 1.2.0
  3600. if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
  3601. L.EditToolbar.Edit.include(L.Evented.prototype);
  3602. } else {
  3603. L.EditToolbar.Edit.include(L.Mixin.Events);
  3604. }
  3605. },
  3606. // @method enable(): void
  3607. // Enable the edit toolbar
  3608. enable: function () {
  3609. if (this._enabled || !this._hasAvailableLayers()) {
  3610. return;
  3611. }
  3612. this.fire('enabled', {handler: this.type});
  3613. //this disable other handlers
  3614. this._map.fire(L.Draw.Event.EDITSTART, {handler: this.type});
  3615. //allow drawLayer to be updated before beginning edition.
  3616. L.Handler.prototype.enable.call(this);
  3617. this._featureGroup
  3618. .on('layeradd', this._enableLayerEdit, this)
  3619. .on('layerremove', this._disableLayerEdit, this);
  3620. },
  3621. // @method disable(): void
  3622. // Disable the edit toolbar
  3623. disable: function () {
  3624. if (!this._enabled) {
  3625. return;
  3626. }
  3627. this._featureGroup
  3628. .off('layeradd', this._enableLayerEdit, this)
  3629. .off('layerremove', this._disableLayerEdit, this);
  3630. L.Handler.prototype.disable.call(this);
  3631. this._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type});
  3632. this.fire('disabled', {handler: this.type});
  3633. },
  3634. // @method addHooks(): void
  3635. // Add listener hooks for this handler
  3636. addHooks: function () {
  3637. var map = this._map;
  3638. if (map) {
  3639. map.getContainer().focus();
  3640. this._featureGroup.eachLayer(this._enableLayerEdit, this);
  3641. this._tooltip = new L.Draw.Tooltip(this._map);
  3642. this._tooltip.updateContent({
  3643. text: L.drawLocal.edit.handlers.edit.tooltip.text,
  3644. subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
  3645. });
  3646. // Quickly access the tooltip to update for intersection checking
  3647. map._editTooltip = this._tooltip;
  3648. this._updateTooltip();
  3649. this._map
  3650. .on('mousemove', this._onMouseMove, this)
  3651. .on('touchmove', this._onMouseMove, this)
  3652. .on('MSPointerMove', this._onMouseMove, this)
  3653. .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
  3654. }
  3655. },
  3656. // @method removeHooks(): void
  3657. // Remove listener hooks for this handler
  3658. removeHooks: function () {
  3659. if (this._map) {
  3660. // Clean up selected layers.
  3661. this._featureGroup.eachLayer(this._disableLayerEdit, this);
  3662. // Clear the backups of the original layers
  3663. this._uneditedLayerProps = {};
  3664. this._tooltip.dispose();
  3665. this._tooltip = null;
  3666. this._map
  3667. .off('mousemove', this._onMouseMove, this)
  3668. .off('touchmove', this._onMouseMove, this)
  3669. .off('MSPointerMove', this._onMouseMove, this)
  3670. .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
  3671. }
  3672. },
  3673. // @method revertLayers(): void
  3674. // Revert each layer's geometry changes
  3675. revertLayers: function () {
  3676. this._featureGroup.eachLayer(function (layer) {
  3677. this._revertLayer(layer);
  3678. }, this);
  3679. },
  3680. // @method save(): void
  3681. // Save the layer geometries
  3682. save: function () {
  3683. var editedLayers = new L.LayerGroup();
  3684. this._featureGroup.eachLayer(function (layer) {
  3685. if (layer.edited) {
  3686. editedLayers.addLayer(layer);
  3687. layer.edited = false;
  3688. }
  3689. });
  3690. this._map.fire(L.Draw.Event.EDITED, {layers: editedLayers});
  3691. },
  3692. _backupLayer: function (layer) {
  3693. var id = L.Util.stamp(layer);
  3694. if (!this._uneditedLayerProps[id]) {
  3695. // Polyline, Polygon or Rectangle
  3696. if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
  3697. this._uneditedLayerProps[id] = {
  3698. latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())
  3699. };
  3700. } else if (layer instanceof L.Circle) {
  3701. this._uneditedLayerProps[id] = {
  3702. latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),
  3703. radius: layer.getRadius()
  3704. };
  3705. } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker
  3706. this._uneditedLayerProps[id] = {
  3707. latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())
  3708. };
  3709. }
  3710. }
  3711. },
  3712. _getTooltipText: function () {
  3713. return ({
  3714. text: L.drawLocal.edit.handlers.edit.tooltip.text,
  3715. subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
  3716. });
  3717. },
  3718. _updateTooltip: function () {
  3719. this._tooltip.updateContent(this._getTooltipText());
  3720. },
  3721. _revertLayer: function (layer) {
  3722. var id = L.Util.stamp(layer);
  3723. layer.edited = false;
  3724. if (this._uneditedLayerProps.hasOwnProperty(id)) {
  3725. // Polyline, Polygon or Rectangle
  3726. if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
  3727. layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
  3728. } else if (layer instanceof L.Circle) {
  3729. layer.setLatLng(this._uneditedLayerProps[id].latlng);
  3730. layer.setRadius(this._uneditedLayerProps[id].radius);
  3731. } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker
  3732. layer.setLatLng(this._uneditedLayerProps[id].latlng);
  3733. }
  3734. layer.fire('revert-edited', {layer: layer});
  3735. }
  3736. },
  3737. _enableLayerEdit: function (e) {
  3738. var layer = e.layer || e.target || e,
  3739. pathOptions, poly;
  3740. // Back up this layer (if haven't before)
  3741. this._backupLayer(layer);
  3742. if (this.options.poly) {
  3743. poly = L.Util.extend({}, this.options.poly);
  3744. layer.options.poly = poly;
  3745. }
  3746. // Set different style for editing mode
  3747. if (this.options.selectedPathOptions) {
  3748. pathOptions = L.Util.extend({}, this.options.selectedPathOptions);
  3749. // Use the existing color of the layer
  3750. if (pathOptions.maintainColor) {
  3751. pathOptions.color = layer.options.color;
  3752. pathOptions.fillColor = layer.options.fillColor;
  3753. }
  3754. layer.options.original = L.extend({}, layer.options);
  3755. layer.options.editing = pathOptions;
  3756. }
  3757. if (layer instanceof L.Marker) {
  3758. if (layer.editing) {
  3759. layer.editing.enable();
  3760. }
  3761. layer.dragging.enable();
  3762. layer
  3763. .on('dragend', this._onMarkerDragEnd)
  3764. // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.
  3765. .on('touchmove', this._onTouchMove, this)
  3766. .on('MSPointerMove', this._onTouchMove, this)
  3767. .on('touchend', this._onMarkerDragEnd, this)
  3768. .on('MSPointerUp', this._onMarkerDragEnd, this);
  3769. } else {
  3770. layer.editing.enable();
  3771. }
  3772. },
  3773. _disableLayerEdit: function (e) {
  3774. var layer = e.layer || e.target || e;
  3775. layer.edited = false;
  3776. if (layer.editing) {
  3777. layer.editing.disable();
  3778. }
  3779. delete layer.options.editing;
  3780. delete layer.options.original;
  3781. // Reset layer styles to that of before select
  3782. if (this._selectedPathOptions) {
  3783. if (layer instanceof L.Marker) {
  3784. this._toggleMarkerHighlight(layer);
  3785. } else {
  3786. // reset the layer style to what is was before being selected
  3787. layer.setStyle(layer.options.previousOptions);
  3788. // remove the cached options for the layer object
  3789. delete layer.options.previousOptions;
  3790. }
  3791. }
  3792. if (layer instanceof L.Marker) {
  3793. layer.dragging.disable();
  3794. layer
  3795. .off('dragend', this._onMarkerDragEnd, this)
  3796. .off('touchmove', this._onTouchMove, this)
  3797. .off('MSPointerMove', this._onTouchMove, this)
  3798. .off('touchend', this._onMarkerDragEnd, this)
  3799. .off('MSPointerUp', this._onMarkerDragEnd, this);
  3800. } else {
  3801. layer.editing.disable();
  3802. }
  3803. },
  3804. _onMouseMove: function (e) {
  3805. this._tooltip.updatePosition(e.latlng);
  3806. },
  3807. _onMarkerDragEnd: function (e) {
  3808. var layer = e.target;
  3809. layer.edited = true;
  3810. this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});
  3811. },
  3812. _onTouchMove: function (e) {
  3813. var touchEvent = e.originalEvent.changedTouches[0],
  3814. layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
  3815. latlng = this._map.layerPointToLatLng(layerPoint);
  3816. e.target.setLatLng(latlng);
  3817. },
  3818. _hasAvailableLayers: function () {
  3819. return this._featureGroup.getLayers().length !== 0;
  3820. }
  3821. });
  3822. /**
  3823. * @class L.EditToolbar.Delete
  3824. * @aka EditToolbar.Delete
  3825. */
  3826. L.EditToolbar.Delete = L.Handler.extend({
  3827. statics: {
  3828. TYPE: 'remove' // not delete as delete is reserved in js
  3829. },
  3830. // @method intialize(): void
  3831. initialize: function (map, options) {
  3832. L.Handler.prototype.initialize.call(this, map);
  3833. L.Util.setOptions(this, options);
  3834. // Store the selectable layer group for ease of access
  3835. this._deletableLayers = this.options.featureGroup;
  3836. if (!(this._deletableLayers instanceof L.FeatureGroup)) {
  3837. throw new Error('options.featureGroup must be a L.FeatureGroup');
  3838. }
  3839. // Save the type so super can fire, need to do this as cannot do this.TYPE :(
  3840. this.type = L.EditToolbar.Delete.TYPE;
  3841. var version = L.version.split('.');
  3842. //If Version is >= 1.2.0
  3843. if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
  3844. L.EditToolbar.Delete.include(L.Evented.prototype);
  3845. } else {
  3846. L.EditToolbar.Delete.include(L.Mixin.Events);
  3847. }
  3848. },
  3849. // @method enable(): void
  3850. // Enable the delete toolbar
  3851. enable: function () {
  3852. if (this._enabled || !this._hasAvailableLayers()) {
  3853. return;
  3854. }
  3855. this.fire('enabled', {handler: this.type});
  3856. this._map.fire(L.Draw.Event.DELETESTART, {handler: this.type});
  3857. L.Handler.prototype.enable.call(this);
  3858. this._deletableLayers
  3859. .on('layeradd', this._enableLayerDelete, this)
  3860. .on('layerremove', this._disableLayerDelete, this);
  3861. },
  3862. // @method disable(): void
  3863. // Disable the delete toolbar
  3864. disable: function () {
  3865. if (!this._enabled) {
  3866. return;
  3867. }
  3868. this._deletableLayers
  3869. .off('layeradd', this._enableLayerDelete, this)
  3870. .off('layerremove', this._disableLayerDelete, this);
  3871. L.Handler.prototype.disable.call(this);
  3872. this._map.fire(L.Draw.Event.DELETESTOP, {handler: this.type});
  3873. this.fire('disabled', {handler: this.type});
  3874. },
  3875. // @method addHooks(): void
  3876. // Add listener hooks to this handler
  3877. addHooks: function () {
  3878. var map = this._map;
  3879. if (map) {
  3880. map.getContainer().focus();
  3881. this._deletableLayers.eachLayer(this._enableLayerDelete, this);
  3882. this._deletedLayers = new L.LayerGroup();
  3883. this._tooltip = new L.Draw.Tooltip(this._map);
  3884. this._tooltip.updateContent({text: L.drawLocal.edit.handlers.remove.tooltip.text});
  3885. this._map.on('mousemove', this._onMouseMove, this);
  3886. }
  3887. },
  3888. // @method removeHooks(): void
  3889. // Remove listener hooks from this handler
  3890. removeHooks: function () {
  3891. if (this._map) {
  3892. this._deletableLayers.eachLayer(this._disableLayerDelete, this);
  3893. this._deletedLayers = null;
  3894. this._tooltip.dispose();
  3895. this._tooltip = null;
  3896. this._map.off('mousemove', this._onMouseMove, this);
  3897. }
  3898. },
  3899. // @method revertLayers(): void
  3900. // Revert the deleted layers back to their prior state.
  3901. revertLayers: function () {
  3902. // Iterate of the deleted layers and add them back into the featureGroup
  3903. this._deletedLayers.eachLayer(function (layer) {
  3904. this._deletableLayers.addLayer(layer);
  3905. layer.fire('revert-deleted', {layer: layer});
  3906. }, this);
  3907. },
  3908. // @method save(): void
  3909. // Save deleted layers
  3910. save: function () {
  3911. this._map.fire(L.Draw.Event.DELETED, {layers: this._deletedLayers});
  3912. },
  3913. // @method removeAllLayers(): void
  3914. // Remove all delateable layers
  3915. removeAllLayers: function () {
  3916. // Iterate of the delateable layers and add remove them
  3917. this._deletableLayers.eachLayer(function (layer) {
  3918. this._removeLayer({layer: layer});
  3919. }, this);
  3920. this.save();
  3921. },
  3922. _enableLayerDelete: function (e) {
  3923. var layer = e.layer || e.target || e;
  3924. layer.on('click', this._removeLayer, this);
  3925. },
  3926. _disableLayerDelete: function (e) {
  3927. var layer = e.layer || e.target || e;
  3928. layer.off('click', this._removeLayer, this);
  3929. // Remove from the deleted layers so we can't accidentally revert if the user presses cancel
  3930. this._deletedLayers.removeLayer(layer);
  3931. },
  3932. _removeLayer: function (e) {
  3933. var layer = e.layer || e.target || e;
  3934. this._deletableLayers.removeLayer(layer);
  3935. this._deletedLayers.addLayer(layer);
  3936. layer.fire('deleted');
  3937. },
  3938. _onMouseMove: function (e) {
  3939. this._tooltip.updatePosition(e.latlng);
  3940. },
  3941. _hasAvailableLayers: function () {
  3942. return this._deletableLayers.getLayers().length !== 0;
  3943. }
  3944. });
  3945. }(window, document));
  3946. //# sourceMappingURL=leaflet.draw-src.map