umap-project 3.1.2__py3-none-any.whl → 3.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (199) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/en/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/en/LC_MESSAGES/django.po +22 -18
  4. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/fr/LC_MESSAGES/django.po +21 -17
  6. umap/management/commands/export_pictogram.py +29 -0
  7. umap/management/commands/migrate_to_S3.py +5 -1
  8. umap/management/commands/purge_old_versions.py +8 -6
  9. umap/settings/__init__.py +21 -0
  10. umap/settings/base.py +3 -0
  11. umap/static/umap/content.css +7 -2
  12. umap/static/umap/css/contextmenu.css +58 -2
  13. umap/static/umap/css/form.css +175 -45
  14. umap/static/umap/css/icon.css +97 -3
  15. umap/static/umap/css/panel.css +31 -1
  16. umap/static/umap/img/16-white.svg +21 -40
  17. umap/static/umap/img/16.svg +1 -1
  18. umap/static/umap/img/24-white.svg +9 -9
  19. umap/static/umap/img/24.svg +23 -10
  20. umap/static/umap/img/source/16-white.svg +23 -41
  21. umap/static/umap/img/source/16.svg +1 -1
  22. umap/static/umap/img/source/24-white.svg +11 -11
  23. umap/static/umap/img/source/24.svg +25 -12
  24. umap/static/umap/js/modules/browser.js +1 -1
  25. umap/static/umap/js/modules/caption.js +8 -0
  26. umap/static/umap/js/modules/data/features.js +331 -202
  27. umap/static/umap/js/modules/data/layer.js +263 -152
  28. umap/static/umap/js/modules/facets.js +2 -2
  29. umap/static/umap/js/modules/form/builder.js +11 -7
  30. umap/static/umap/js/modules/form/fields.js +66 -26
  31. umap/static/umap/js/modules/formatter.js +78 -28
  32. umap/static/umap/js/modules/importer.js +6 -1
  33. umap/static/umap/js/modules/importers/opendata.js +138 -33
  34. umap/static/umap/js/modules/importers/openrouteservice.js +140 -0
  35. umap/static/umap/js/modules/managers.js +67 -0
  36. umap/static/umap/js/modules/printer.js +107 -0
  37. umap/static/umap/js/modules/rendering/controls.js +78 -2
  38. umap/static/umap/js/modules/rendering/icon.js +116 -87
  39. umap/static/umap/js/modules/rendering/layers/classified.js +8 -7
  40. umap/static/umap/js/modules/rendering/layers/cluster.js +199 -63
  41. umap/static/umap/js/modules/rendering/map.js +6 -2
  42. umap/static/umap/js/modules/rendering/template.js +71 -1
  43. umap/static/umap/js/modules/rendering/ui.js +111 -34
  44. umap/static/umap/js/modules/rules.js +76 -23
  45. umap/static/umap/js/modules/schema.js +27 -0
  46. umap/static/umap/js/modules/share.js +19 -12
  47. umap/static/umap/js/modules/slideshow.js +1 -1
  48. umap/static/umap/js/modules/sync/updaters.js +1 -6
  49. umap/static/umap/js/modules/tableeditor.js +13 -37
  50. umap/static/umap/js/modules/templates.js +7 -6
  51. umap/static/umap/js/modules/ui/bar.js +6 -1
  52. umap/static/umap/js/modules/ui/base.js +24 -9
  53. umap/static/umap/js/modules/ui/contextmenu.js +17 -7
  54. umap/static/umap/js/modules/ui/dialog.js +7 -4
  55. umap/static/umap/js/modules/ui/panel.js +7 -0
  56. umap/static/umap/js/modules/umap.js +84 -67
  57. umap/static/umap/js/modules/utils.js +8 -7
  58. umap/static/umap/js/umap.controls.js +22 -57
  59. umap/static/umap/locale/am_ET.js +81 -9
  60. umap/static/umap/locale/am_ET.json +81 -9
  61. umap/static/umap/locale/ar.js +81 -9
  62. umap/static/umap/locale/ar.json +81 -9
  63. umap/static/umap/locale/ast.js +81 -9
  64. umap/static/umap/locale/ast.json +81 -9
  65. umap/static/umap/locale/bg.js +81 -9
  66. umap/static/umap/locale/bg.json +81 -9
  67. umap/static/umap/locale/br.js +68 -29
  68. umap/static/umap/locale/br.json +68 -29
  69. umap/static/umap/locale/ca.js +88 -16
  70. umap/static/umap/locale/ca.json +88 -16
  71. umap/static/umap/locale/cs_CZ.js +81 -9
  72. umap/static/umap/locale/cs_CZ.json +81 -9
  73. umap/static/umap/locale/da.js +48 -9
  74. umap/static/umap/locale/da.json +48 -9
  75. umap/static/umap/locale/de.js +48 -9
  76. umap/static/umap/locale/de.json +48 -9
  77. umap/static/umap/locale/el.js +58 -13
  78. umap/static/umap/locale/el.json +58 -13
  79. umap/static/umap/locale/en.js +48 -9
  80. umap/static/umap/locale/en.json +48 -9
  81. umap/static/umap/locale/en_US.json +81 -9
  82. umap/static/umap/locale/es.js +48 -9
  83. umap/static/umap/locale/es.json +48 -9
  84. umap/static/umap/locale/et.js +81 -9
  85. umap/static/umap/locale/et.json +81 -9
  86. umap/static/umap/locale/eu.js +97 -25
  87. umap/static/umap/locale/eu.json +97 -25
  88. umap/static/umap/locale/fa_IR.js +81 -9
  89. umap/static/umap/locale/fa_IR.json +81 -9
  90. umap/static/umap/locale/fi.js +81 -9
  91. umap/static/umap/locale/fi.json +81 -9
  92. umap/static/umap/locale/fr.js +48 -9
  93. umap/static/umap/locale/fr.json +48 -9
  94. umap/static/umap/locale/gl.js +81 -9
  95. umap/static/umap/locale/gl.json +81 -9
  96. umap/static/umap/locale/he.js +81 -9
  97. umap/static/umap/locale/he.json +81 -9
  98. umap/static/umap/locale/hr.js +81 -9
  99. umap/static/umap/locale/hr.json +81 -9
  100. umap/static/umap/locale/hu.js +72 -27
  101. umap/static/umap/locale/hu.json +72 -27
  102. umap/static/umap/locale/id.js +81 -9
  103. umap/static/umap/locale/id.json +81 -9
  104. umap/static/umap/locale/is.js +81 -9
  105. umap/static/umap/locale/is.json +81 -9
  106. umap/static/umap/locale/it.js +48 -9
  107. umap/static/umap/locale/it.json +48 -9
  108. umap/static/umap/locale/ja.js +81 -9
  109. umap/static/umap/locale/ja.json +81 -9
  110. umap/static/umap/locale/ko.js +81 -9
  111. umap/static/umap/locale/ko.json +81 -9
  112. umap/static/umap/locale/lt.js +81 -9
  113. umap/static/umap/locale/lt.json +81 -9
  114. umap/static/umap/locale/ms.js +81 -9
  115. umap/static/umap/locale/ms.json +81 -9
  116. umap/static/umap/locale/nl.js +48 -9
  117. umap/static/umap/locale/nl.json +48 -9
  118. umap/static/umap/locale/no.js +81 -9
  119. umap/static/umap/locale/no.json +81 -9
  120. umap/static/umap/locale/pl.js +81 -9
  121. umap/static/umap/locale/pl.json +81 -9
  122. umap/static/umap/locale/pl_PL.json +81 -9
  123. umap/static/umap/locale/pt.js +81 -9
  124. umap/static/umap/locale/pt.json +81 -9
  125. umap/static/umap/locale/pt_BR.js +91 -19
  126. umap/static/umap/locale/pt_BR.json +91 -19
  127. umap/static/umap/locale/pt_PT.js +81 -9
  128. umap/static/umap/locale/pt_PT.json +81 -9
  129. umap/static/umap/locale/ro.js +81 -9
  130. umap/static/umap/locale/ro.json +81 -9
  131. umap/static/umap/locale/ru.js +81 -9
  132. umap/static/umap/locale/ru.json +81 -9
  133. umap/static/umap/locale/sk_SK.js +81 -9
  134. umap/static/umap/locale/sk_SK.json +81 -9
  135. umap/static/umap/locale/sl.js +81 -9
  136. umap/static/umap/locale/sl.json +81 -9
  137. umap/static/umap/locale/sr.js +81 -9
  138. umap/static/umap/locale/sr.json +81 -9
  139. umap/static/umap/locale/sv.js +81 -9
  140. umap/static/umap/locale/sv.json +81 -9
  141. umap/static/umap/locale/th_TH.js +81 -9
  142. umap/static/umap/locale/th_TH.json +81 -9
  143. umap/static/umap/locale/tr.js +81 -9
  144. umap/static/umap/locale/tr.json +81 -9
  145. umap/static/umap/locale/uk_UA.js +81 -9
  146. umap/static/umap/locale/uk_UA.json +81 -9
  147. umap/static/umap/locale/vi.js +81 -9
  148. umap/static/umap/locale/vi.json +81 -9
  149. umap/static/umap/locale/vi_VN.json +81 -9
  150. umap/static/umap/locale/zh.js +81 -9
  151. umap/static/umap/locale/zh.json +81 -9
  152. umap/static/umap/locale/zh_CN.json +81 -9
  153. umap/static/umap/locale/zh_TW.Big5.json +81 -9
  154. umap/static/umap/locale/zh_TW.js +98 -26
  155. umap/static/umap/locale/zh_TW.json +98 -26
  156. umap/static/umap/map.css +325 -102
  157. umap/static/umap/vars.css +1 -0
  158. umap/static/umap/vendors/betterknown/betterknown.mjs +287 -0
  159. umap/static/umap/vendors/editable/Leaflet.Editable.js +3 -1
  160. umap/static/umap/vendors/openrouteservice/ors-js-client.js +521 -0
  161. umap/static/umap/vendors/openrouteservice/ors-js-client.js.map +1 -0
  162. umap/static/umap/vendors/simple-elevation-chart/elevation.js +63 -0
  163. umap/static/umap/vendors/simple-elevation-chart/elevation.svg +8 -0
  164. umap/static/umap/vendors/snapdom/snapdom.min.mjs +3 -0
  165. umap/storage/fs.py +3 -2
  166. umap/storage/staticfiles.py +12 -0
  167. umap/templates/base.html +4 -1
  168. umap/templates/umap/css.html +0 -4
  169. umap/templates/umap/js.html +1 -3
  170. umap/tests/base.py +9 -1
  171. umap/tests/integration/test_basics.py +3 -1
  172. umap/tests/integration/test_conditional_rules.py +79 -37
  173. umap/tests/integration/test_datalayer.py +1 -1
  174. umap/tests/integration/test_draw_polygon.py +3 -5
  175. umap/tests/integration/test_draw_polyline.py +4 -6
  176. umap/tests/integration/test_draw_route.py +178 -0
  177. umap/tests/integration/test_edit_datalayer.py +1 -1
  178. umap/tests/integration/test_edit_map.py +1 -1
  179. umap/tests/integration/test_edit_marker.py +8 -8
  180. umap/tests/integration/test_edit_polygon.py +2 -2
  181. umap/tests/integration/test_export_map.py +84 -10
  182. umap/tests/integration/test_import.py +140 -0
  183. umap/tests/integration/test_map_preview.py +1 -1
  184. umap/tests/integration/test_optimistic_merge.py +72 -12
  185. umap/tests/integration/test_share.py +1 -1
  186. umap/tests/integration/test_tableeditor.py +10 -7
  187. umap/tests/integration/test_websocket_sync.py +4 -4
  188. umap/utils.py +37 -0
  189. umap/views.py +18 -2
  190. umap_project-3.3.0.dist-info/METADATA +76 -0
  191. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/RECORD +194 -188
  192. umap/static/umap/vendors/markercluster/MarkerCluster.Default.css +0 -60
  193. umap/static/umap/vendors/markercluster/MarkerCluster.css +0 -14
  194. umap/static/umap/vendors/markercluster/leaflet.markercluster.js +0 -2
  195. umap/static/umap/vendors/markercluster/leaflet.markercluster.js.map +0 -1
  196. umap_project-3.1.2.dist-info/METADATA +0 -68
  197. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/WHEEL +0 -0
  198. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/entry_points.txt +0 -0
  199. {umap_project-3.1.2.dist-info → umap_project-3.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,3 @@
1
+ var l={image:new Map,background:new Map,resource:new Map,defaultStyle:new Map,baseStyle:new Map,computedStyle:new WeakMap,font:new Set,snapshot:new WeakMap,snapshotKey:new Map,preStyleMap:new Map,preStyle:new WeakMap,preNodeMap:new Map,reset:Re};function Re(){l.computedStyle=new WeakMap,l.snapshot=new WeakMap,l.snapshotKey.clear(),l.preStyleMap.clear(),l.preStyle=new WeakMap,l.preNodeMap.clear()}var Ne=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function ie(){for(let e of Ne)ce(e)}function ce(e){if(l.defaultStyle.has(e))return l.defaultStyle.get(e);if(new Set(["script","style","meta","link","noscript","template","defs","symbol","title","metadata","desc"]).has(e)){let n={};return l.defaultStyle.set(e,n),n}let r=document.getElementById("snapdom-sandbox");r||(r=document.createElement("div"),r.id="snapdom-sandbox",r.style.position="absolute",r.style.left="-9999px",r.style.top="-9999px",r.style.width="0",r.style.height="0",r.style.overflow="hidden",document.body.appendChild(r));let c=document.createElement(e);c.style.all="initial",r.appendChild(c);let s=getComputedStyle(c),o={};for(let n of s)o[n]=s.getPropertyValue(n);return r.removeChild(c),l.defaultStyle.set(e,o),o}var Ae=new Set(["-webkit-locale"]);function D(e,t,r=!1){let c=[],s=ce(t);for(let[o,n]of Object.entries(e))if(!Ae.has(o))if(!r)n&&c.push(`${o}:${n}`);else{let a=s[o];n&&n!==a&&c.push(`${o}:${n}`)}return c.sort().join(";")}function le(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(r=>t.add(r.tagName.toLowerCase())),Array.from(t))}function de(e){let t=new Map;for(let c of e){let s=l.defaultStyle.get(c);if(!s)continue;let o=Object.entries(s).map(([n,a])=>`${n}:${a};`).sort().join("");t.has(o)||t.set(o,[]),t.get(o).push(c)}let r="";for(let[c,s]of t.entries())r+=`${s.join(",")} { ${c} }
2
+ `;return r}function fe(){let e=new Set(l.preStyleMap.values()),t=new Map,r=1;for(let c of e)c.trim()&&t.set(c,`c${r++}`);return t}async function P(e,t={}){let r=O(e),c=/^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(e);if(r){let s=H(r);if(l.background.has(s))return t.skipInline?void 0:`url(${l.background.get(s)})`;{let o=await M(s,{useProxy:t.useProxy});return l.background.set(s,o),t.skipInline?void 0:`url("${o}")`}}return e}function F(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function I(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let r=l.computedStyle.get(e);if(r||(r=new Map,l.computedStyle.set(e,r)),!r.has(t)){let c=window.getComputedStyle(e,t);r.set(t,c)}return r.get(t)}function ue(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function O(e){let t=e.match(/url\((['"]?)(.*?)(\1)\)/);if(!t)return null;let r=t[2].trim();return r.startsWith("#")?null:r}function M(e,{timeout:t=3e3,useProxy:r=""}={}){function c(i){try{return new URL(i,window.location.href).origin===window.location.origin?"use-credentials":"anonymous"}catch{return"anonymous"}}async function s(i){let d=u=>fetch(u,{mode:"cors",credentials:c(u)==="use-credentials"?"include":"omit"}).then(f=>f.blob()).then(f=>new Promise((m,p)=>{let h=new FileReader;h.onloadend=()=>{let y=h.result;if(typeof y!="string"||!y.startsWith("data:image/")){p(new Error("Invalid image data URL"));return}m(y)},h.onerror=()=>p(new Error("FileReader error")),h.readAsDataURL(f)}));try{return await d(i)}catch{if(r&&typeof r=="string"){let f=r.replace(/\/$/,"")+H(i);try{return await d(f)}catch{throw new Error("[SnapDOM - fetchImage] CORS restrictions prevented image capture (even via proxy)")}}else throw new Error("[SnapDOM - fetchImage] Fetch fallback failed and no proxy provided")}}let o=c(e);return l.image.has(e)?Promise.resolve(l.image.get(e)):e.startsWith("data:image/")?(l.image.set(e,e),Promise.resolve(e)):/\.svg(\?.*)?$/i.test(e)?(async()=>{try{let d=await(await fetch(e,{mode:"cors",credentials:o==="use-credentials"?"include":"omit"})).text(),u=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(d)}`;return l.image.set(e,u),u}catch{return s(e)}})():new Promise((i,d)=>{let u=setTimeout(()=>{d(new Error("[SnapDOM - fetchImage] Image load timed out"))},t),f=new Image;f.crossOrigin=o,f.onload=async()=>{clearTimeout(u);try{await f.decode();let m=document.createElement("canvas");m.width=f.width,m.height=f.height,m.getContext("2d").drawImage(f,0,0,m.width,m.height);let h=m.toDataURL("image/png");l.image.set(e,h),i(h)}catch{try{let m=await s(e);l.image.set(e,m),i(m)}catch(m){d(m)}}},f.onerror=async()=>{clearTimeout(u),console.error(`[SnapDOM - fetchImage] Image failed to load: ${e}`);try{let m=await s(e);l.image.set(e,m),i(m)}catch(m){d(m)}},f.src=e})}function Q(e){let t={};for(let r of e)t[r]=e.getPropertyValue(r);return t}function me(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function pe(e){if(!e||e==="none")return"";let t=e.replace(/translate[XY]?\([^)]*\)/g,"");return t=t.replace(/matrix\(([^)]+)\)/g,(r,c)=>{let s=c.split(",").map(o=>o.trim());return s.length!==6?`matrix(${c})`:(s[4]="0",s[5]="0",`matrix(${s.join(", ")})`)}),t=t.replace(/matrix3d\(([^)]+)\)/g,(r,c)=>{let s=c.split(",").map(o=>o.trim());return s.length!==16?`matrix3d(${c})`:(s[12]="0",s[13]="0",`matrix3d(${s.join(", ")})`)}),t.trim().replace(/\s{2,}/g," ")}function H(e){if(/%[0-9A-Fa-f]{2}/.test(e))return e;try{return encodeURI(e)}catch{return e}}function U(e){let t=[],r=0,c=0;for(let s=0;s<e.length;s++){let o=e[s];o==="("&&r++,o===")"&&r--,o===","&&r===0&&(t.push(e.slice(c,s).trim()),c=s+1)}return t.push(e.slice(c).trim()),t}function Te(e){let t={},r=e.getPropertyValue("visibility");for(let c=0;c<e.length;c++){let s=e[c],o=e.getPropertyValue(s);(s==="background-image"||s==="content")&&o.includes("url(")&&!o.includes("data:")&&(o="none"),t[s]=o}return r==="hidden"&&(t.opacity="0"),t}function z(e,t,r){if(e.tagName==="STYLE")return;l.preStyle.has(e)||l.preStyle.set(e,I(e));let c=l.preStyle.get(e);if(!l.snapshot.has(e)){let i=Te(c);l.snapshot.set(e,i)}let s=l.snapshot.get(e),o=Object.entries(s).sort(([i],[d])=>i.localeCompare(d)).map(([i,d])=>`${i}:${d}`).join(";");if(l.snapshotKey.has(o)){l.preStyleMap.set(t,l.snapshotKey.get(o));return}let n=e.tagName?.toLowerCase()||"div",a=D(s,n,r);l.snapshotKey.set(o,a),l.preStyleMap.set(t,a)}function j(e,t,r={},c){if(!e)throw new Error("Invalid node");let s=new Set,o=null;if(e.nodeType===Node.TEXT_NODE||e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);if(e.getAttribute("data-capture")==="exclude"){let a=document.createElement("div"),i=e.getBoundingClientRect();return a.style.cssText=`display:inline-block;width:${i.width}px;height:${i.height}px;visibility:hidden;`,a}if(r.exclude&&Array.isArray(r.exclude))for(let a of r.exclude)try{if(e.matches?.(a)){let i=document.createElement("div"),d=e.getBoundingClientRect();return i.style.cssText=`display:inline-block;width:${d.width}px;height:${d.height}px;visibility:hidden;`,i}}catch(i){console.warn(`Invalid selector in exclude option: ${a}`,i)}if(typeof r.filter=="function")try{if(!r.filter(e,c||e)){let a=document.createElement("div"),i=e.getBoundingClientRect();return a.style.cssText=`display:inline-block;width:${i.width}px;height:${i.height}px;visibility:hidden;`,a}}catch(a){console.warn("Error in filter function:",a)}if(e.tagName==="IFRAME"){let a=document.createElement("div");return a.style.cssText=`width:${e.offsetWidth}px;height:${e.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`,a}if(e.getAttribute("data-capture")==="placeholder"){let a=e.cloneNode(!1);l.preNodeMap.set(a,e),z(e,a,t);let i=document.createElement("div");return i.textContent=e.getAttribute("data-placeholder-text")||"",i.style.cssText="color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;",a.appendChild(i),a}if(e.tagName==="CANVAS"){let a=e.toDataURL(),i=document.createElement("img");return i.src=a,i.width=e.width,i.height=e.height,l.preNodeMap.set(i,e),z(e,i,t),i}let n;try{n=e.cloneNode(!1),l.preNodeMap.set(n,e)}catch(a){throw console.error("[Snapdom] Failed to clone node:",e,a),a}if(e instanceof HTMLTextAreaElement){n.textContent=e.value,n.value=e.value;let a=e.getBoundingClientRect();return n.style.width=`${a.width}px`,n.style.height=`${a.height}px`,n}if(e instanceof HTMLInputElement&&(n.value=e.value,n.setAttribute("value",e.value),e.checked!==void 0&&(n.checked=e.checked,e.checked&&n.setAttribute("checked",""),e.indeterminate&&(n.indeterminate=e.indeterminate))),e instanceof HTMLSelectElement&&(o=e.value),z(e,n,t),e.shadowRoot)if(Array.from(e.shadowRoot.querySelectorAll("slot")).length>0){for(let i of e.shadowRoot.childNodes)if(i.nodeType===Node.ELEMENT_NODE&&i.tagName==="STYLE"){let d=i.textContent||"";d.trim()&&t&&(l.preStyle||(l.preStyle=new WeakMap),l.preStyle.set(i,d))}}else{let i=document.createDocumentFragment();for(let d of e.shadowRoot.childNodes){if(d.nodeType===Node.ELEMENT_NODE&&d.tagName==="STYLE"){let f=d.textContent||"";f.trim()&&t&&(l.preStyle||(l.preStyle=new WeakMap),l.preStyle.set(d,f));continue}let u=j(d,t,r,c||e);u&&i.appendChild(u)}n.appendChild(i)}if(e.tagName==="SLOT"){let a=e.assignedNodes?.({flatten:!0})||[],i=a.length>0?a:Array.from(e.childNodes),d=document.createDocumentFragment();for(let u of i){let f=j(u,t,r,c||e);f&&d.appendChild(f)}return d}for(let a of e.childNodes){if(s.has(a))continue;let i=j(a,t,r,c||e);i&&n.appendChild(i)}if(o!==null&&n instanceof HTMLSelectElement){n.value=o;for(let a of n.options)a.value===o?a.setAttribute("selected",""):a.removeAttribute("selected")}return n}var Me=[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i,/layui/i,/lucide/i],Z=[];function he(e){let t=Array.isArray(e)?e:[e];for(let r of t)r instanceof RegExp?Z.push(r):typeof r=="string"?Z.push(new RegExp(r,"i")):console.warn("[snapdom] Ignored invalid iconFont value:",r)}function k(e){let t=typeof e=="string"?e:"",r=[...Me,...Z];for(let c of r)if(c instanceof RegExp&&c.test(t))return!0;return!!(/icon/i.test(t)||/glyph/i.test(t)||/symbols/i.test(t)||/feather/i.test(t)||/fontawesome/i.test(t))}async function ge(e,t,r,c=32,s="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let o=window.devicePixelRatio||1;await document.fonts.ready;let n=document.createElement("span");n.textContent=e,n.style.position="absolute",n.style.visibility="hidden",n.style.fontFamily=`"${t}"`,n.style.fontWeight=r||"normal",n.style.fontSize=`${c}px`,n.style.lineHeight="1",n.style.whiteSpace="nowrap",n.style.padding="0",n.style.margin="0",document.body.appendChild(n);let a=n.getBoundingClientRect(),i=Math.ceil(a.width),d=Math.ceil(a.height);document.body.removeChild(n);let u=document.createElement("canvas");u.width=i*o,u.height=d*o;let f=u.getContext("2d");return f.scale(o,o),f.font=r?`${r} ${c}px "${t}"`:`${c}px "${t}"`,f.textAlign="left",f.textBaseline="top",f.fillStyle=s,f.fillText(e,0,0),{dataUrl:u.toDataURL(),width:i,height:d}}function ye(e){return Array.from(document.styleSheets).some(t=>t.href===e)}function Le(e){return new Promise(t=>{if(ye(e))return t(null);let r=document.createElement("link");r.rel="stylesheet",r.href=e,r.setAttribute("data-snapdom","injected-import"),r.onload=()=>t(r),r.onerror=()=>t(null),document.head.appendChild(r)})}async function K({preCached:e=!1}={}){if(l.resource.has("fonts-embed-css")){if(e){let o=document.createElement("style");o.setAttribute("data-snapdom","embedFonts"),o.textContent=l.resource.get("fonts-embed-css"),document.head.appendChild(o)}return l.resource.get("fonts-embed-css")}let t=/@import\s+url\(["']?([^"')]+)["']?\)/g,r=[];for(let o of document.querySelectorAll("style")){let n=o.textContent||"",a=Array.from(n.matchAll(t));for(let i of a){let d=i[1];k(d)||ye(d)||r.push(d)}}await Promise.all(r.map(Le));let c=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(o=>o.href),s="";for(let o of c)try{let a=await(await fetch(o.href)).text();if(k(o.href)||k(a))continue;let i=/url\((["']?)([^"')]+)\1\)/g,d=await Promise.all(Array.from(a.matchAll(i)).map(async f=>{let m=O(f[0]);if(!m)return null;let p=m;if(!p.startsWith("http")&&!p.startsWith("data:")&&(p=new URL(p,o.href).href),k(p))return null;if(l.resource.has(p))return l.font.add(p),{original:f[0],inlined:`url(${l.resource.get(p)})`};if(l.font.has(p))return null;try{let y=await(await fetch(p)).blob(),w=await new Promise(g=>{let E=new FileReader;E.onload=()=>g(E.result),E.readAsDataURL(y)});return l.resource.set(p,w),l.font.add(p),{original:f[0],inlined:`url(${w})`}}catch{return console.warn("[snapdom] Failed to fetch font resource:",p),null}})),u=a;for(let f of d)f&&(u=u.replace(f.original,f.inlined));s+=u+`
3
+ `}catch{console.warn("[snapdom] Failed to fetch CSS:",o.href)}for(let o of document.styleSheets)try{if(!o.href||c.every(n=>n.href!==o.href)){for(let n of o.cssRules)if(n.type===CSSRule.FONT_FACE_RULE){let a=n.style.getPropertyValue("src"),i=n.style.getPropertyValue("font-family");if(!a||k(i))continue;let d=/url\((["']?)([^"')]+)\1\)/g,u=/local\((["']?)[^)]+?\1\)/g,f=!!a.match(d),m=!!a.match(u);if(!f&&m){s+=`@font-face{font-family:${i};src:${a};font-style:${n.style.getPropertyValue("font-style")||"normal"};font-weight:${n.style.getPropertyValue("font-weight")||"normal"};}`;continue}let p=a,h=Array.from(a.matchAll(d));for(let y of h){let w=y[2].trim();if(!w)continue;let g=w;if(!g.startsWith("http")&&!g.startsWith("data:")&&(g=new URL(g,o.href||location.href).href),!k(g)){if(l.resource.has(g)){l.font.add(g),p=p.replace(y[0],`url(${l.resource.get(g)})`);continue}if(!l.font.has(g))try{let $=await(await fetch(g)).blob(),R=await new Promise(N=>{let x=new FileReader;x.onload=()=>N(x.result),x.readAsDataURL($)});l.resource.set(g,R),l.font.add(g),p=p.replace(y[0],`url(${R})`)}catch{console.warn("[snapdom] Failed to fetch font URL:",g)}}}s+=`@font-face{font-family:${i};src:${p};font-style:${n.style.getPropertyValue("font-style")||"normal"};font-weight:${n.style.getPropertyValue("font-weight")||"normal"};}`}}}catch(n){console.warn("[snapdom] Cannot access stylesheet",o.href,n)}for(let o of document.fonts)if(o.family&&o.status==="loaded"&&o._snapdomSrc){if(k(o.family))continue;let n=o._snapdomSrc;if(!n.startsWith("data:")){if(l.resource.has(o._snapdomSrc))n=l.resource.get(o._snapdomSrc),l.font.add(o._snapdomSrc);else if(!l.font.has(o._snapdomSrc))try{let i=await(await fetch(o._snapdomSrc)).blob();n=await new Promise(d=>{let u=new FileReader;u.onload=()=>d(u.result),u.readAsDataURL(i)}),l.resource.set(o._snapdomSrc,n),l.font.add(o._snapdomSrc)}catch{console.warn("[snapdom] Failed to fetch dynamic font src:",o._snapdomSrc);continue}}s+=`@font-face{font-family:'${o.family}';src:url(${n});font-style:${o.style||"normal"};font-weight:${o.weight||"normal"};}`}if(s&&(l.resource.set("fonts-embed-css",s),e)){let o=document.createElement("style");o.setAttribute("data-snapdom","embedFonts"),o.textContent=s,document.head.appendChild(o)}return s}async function ee(e,t,r){if(!(e instanceof Element)||!(t instanceof Element))return;for(let o of["::before","::after","::first-letter"])try{let n=I(e,o);if(!n||typeof n[Symbol.iterator]!="function"||n.content==="none"&&n.backgroundImage==="none"&&n.backgroundColor==="transparent"&&(n.borderStyle==="none"||parseFloat(n.borderWidth)===0)&&(!n.transform||n.transform==="none")&&n.display==="inline")continue;if(o==="::first-letter"){let C=getComputedStyle(e);if(!(n.color!==C.color||n.fontSize!==C.fontSize||n.fontWeight!==C.fontWeight))continue;let T=Array.from(t.childNodes).find(se=>se.nodeType===Node.TEXT_NODE&&se.textContent?.trim().length>0);if(!T)continue;let L=T.textContent,_=L.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u)?.[0],ke=L.slice(_?.length||0);if(!_||/[\uD800-\uDFFF]/.test(_))continue;let q=document.createElement("span");q.textContent=_,q.dataset.snapdomPseudo="::first-letter";let $e=Q(n),Ie=D($e,"span",r);l.preStyleMap.set(q,Ie);let ae=document.createTextNode(ke);t.replaceChild(ae,T),t.insertBefore(q,ae);continue}let i=n.content,d=/counter\s*\(|counters\s*\(/.test(i)?"- ":ue(i),u=n.backgroundImage,f=n.backgroundColor,m=n.fontFamily,p=parseInt(n.fontSize)||32,h=parseInt(n.fontWeight)||!1,y=n.color||"#000",w=n.display,g=parseFloat(n.width),E=parseFloat(n.height),$=n.borderStyle,R=parseFloat(n.borderWidth),N=n.transform,x=k(m),B=i!=="none"&&d!=="",V=u&&u!=="none",W=f&&f!=="transparent"&&f!=="rgba(0, 0, 0, 0)",Y=w!=="inline"&&(g>0||E>0),G=$&&$!=="none"&&R>0,A=N&&N!=="none";if(!(B||V||W||Y||G||A))continue;let S=document.createElement("span");S.dataset.snapdomPseudo=o,S.style.verticalAlign="middle";let X=Q(n),J=D(X,"span",r);if(l.preStyleMap.set(S,J),x&&d.length===1){let{dataUrl:C,width:v,height:T}=await ge(d,m,h,p,y),L=document.createElement("img");L.src=C,L.style=`height:${p}px;width:${v/T*p}px;object-fit:contain;`,S.appendChild(L),t.dataset.snapdomHasIcon="true"}else if(d.startsWith("url(")){let C=O(d);if(C?.trim())try{let v=document.createElement("img"),T=await M(H(C),r);v.src=T,v.style=`width:${p}px;height:auto;object-fit:contain;`,S.appendChild(v)}catch(v){console.error(`[snapdom] Error in pseudo ${o} for`,e,v)}}else!x&&B&&(S.textContent=d);if(V)try{let C=U(u),v=await Promise.all(C.map(P));S.style.backgroundImage=v.join(", ")}catch(C){console.warn(`[snapdom] Failed to inline background-image for ${o}`,C)}if(W&&(S.style.backgroundColor=f),!(S.childNodes.length>0||S.textContent?.trim()!==""||V||W||Y||G||A))continue;o==="::before"?t.insertBefore(S,t.firstChild):t.appendChild(S)}catch(n){console.warn(`[snapdom] Failed to capture ${o} for`,e,n)}let c=Array.from(e.children),s=Array.from(t.children).filter(o=>!o.dataset.snapdomPseudo);for(let o=0;o<Math.min(c.length,s.length);o++)await ee(c[o],s[o],r)}function we(e){if(!e)return;let t=new Set;if(e.querySelectorAll("use").forEach(a=>{let i=a.getAttribute("xlink:href")||a.getAttribute("href");i&&i.startsWith("#")&&t.add(i.slice(1))}),!t.size)return;let r=Array.from(document.querySelectorAll("svg > symbol, svg > defs")),c=r.filter(a=>a.tagName.toLowerCase()==="symbol"),s=r.filter(a=>a.tagName.toLowerCase()==="defs"),o=e.querySelector("svg.inline-defs-container");o||(o=document.createElementNS("http://www.w3.org/2000/svg","svg"),o.setAttribute("aria-hidden","true"),o.setAttribute("style","position: absolute; width: 0; height: 0; overflow: hidden;"),o.classList.add("inline-defs-container"),e.insertBefore(o,e.firstChild));let n=new Set;e.querySelectorAll("symbol[id], defs > *[id]").forEach(a=>{n.add(a.id)}),t.forEach(a=>{if(n.has(a))return;let i=c.find(d=>d.id===a);if(i){o.appendChild(i.cloneNode(!0)),n.add(a);return}for(let d of s){let u=d.querySelector(`#${CSS.escape(a)}`);if(u){let f=o.querySelector("defs");f||(f=document.createElementNS("http://www.w3.org/2000/svg","defs"),o.appendChild(f)),f.appendChild(u.cloneNode(!0)),n.add(a);break}}})}async function be(e,t=!1,r=!1,c={}){let s,o="";Pe(e);try{we(e)}catch(n){console.warn("inlineExternal defs or symbol failed:",n)}try{s=j(e,t,c,e)}catch(n){throw console.warn("deepClone failed:",n),n}try{await ee(e,s,t,r,c.useProxy)}catch(n){console.warn("inlinePseudoElements failed:",n)}if(t){let n=fe();o=Array.from(n.entries()).map(([a,i])=>`.${i}{${a}}`).join("");for(let[a,i]of l.preStyleMap.entries()){if(a.tagName==="STYLE")continue;if(a.getRootNode&&a.getRootNode()instanceof ShadowRoot){a.setAttribute("style",i.replace(/;/g,"; "));continue}let d=n.get(i);d&&a.classList.add(d);let u=a.style?.backgroundImage,f=a.dataset?.snapdomHasIcon;u&&u!=="none"&&(a.style.backgroundImage=u),f&&(a.style.verticalAlign="middle",a.style.display="inline")}}else for(let[n,a]of l.preStyleMap.entries())n.tagName!=="STYLE"&&n.setAttribute("style",a.replace(/;/g,"; "));for(let[n,a]of l.preNodeMap.entries()){let i=a.scrollLeft,d=a.scrollTop;if((i||d)&&n instanceof HTMLElement){n.style.overflow="hidden",n.style.scrollbarWidth="none",n.style.msOverflowStyle="none";let f=document.createElement("div");for(f.style.transform=`translate(${-i}px, ${-d}px)`,f.style.willChange="transform",f.style.display="inline-block",f.style.width="100%";n.firstChild;)f.appendChild(n.firstChild);n.appendChild(f)}}if(e===l.preNodeMap.get(s)){let n=l.preStyle.get(e)||window.getComputedStyle(e);l.preStyle.set(e,n);let a=pe(n.transform);s.style.margin="0",s.style.position="static",s.style.top="auto",s.style.left="auto",s.style.right="auto",s.style.bottom="auto",s.style.zIndex="auto",s.style.float="none",s.style.clear="none",s.style.transform=a||""}for(let[n,a]of l.preNodeMap.entries())a.tagName==="PRE"&&(n.style.marginTop="0",n.style.marginBlockStart="0");return{clone:s,classCSS:o}}function Pe(e){let t=getComputedStyle(e),r=t.outlineStyle,c=t.outlineWidth,s=t.borderStyle,o=t.borderWidth,n=r!=="none"&&parseFloat(c)>0,a=s==="none"||parseFloat(o)===0;n&&a&&(e.style.border=`${c} solid transparent`)}async function Se(e,t={}){let r=Array.from(e.querySelectorAll("img")),c=async s=>{let o=s.src;try{let n=await M(o,{useProxy:t.useProxy});s.src=n,s.width||(s.width=s.naturalWidth||100),s.height||(s.height=s.naturalHeight||100)}catch{let n=document.createElement("div");n.style=`width: ${s.width||100}px; height: ${s.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${s.height||100}px; color: #666; font-size: 12px;`,n.innerText="img",s.replaceWith(n)}};for(let s=0;s<r.length;s+=4){let o=r.slice(s,s+4).map(c);await Promise.allSettled(o)}}async function xe(e,t,r={}){let c=[[e,t]],s=["background-image","mask","mask-image","-webkit-mask-image","mask-source","mask-box-image-source","mask-border-source","-webkit-mask-box-image-source","border-image","border-image-source","border-image-slice","border-image-width","border-image-outset","border-image-repeat"];for(;c.length;){let[o,n]=c.shift(),a=l.preStyle.get(o)||I(o);l.preStyle.has(o)||l.preStyle.set(o,a);let i=(()=>{let m=a.getPropertyValue("border-image"),p=a.getPropertyValue("border-image-source");return m&&m!=="none"||p&&p!=="none"})();for(let m of s){if(["border-image-slice","border-image-width","border-image-outset","border-image-repeat"].includes(m)&&!i)continue;let p=a.getPropertyValue(m);if(!p||p==="none")continue;let h=U(p),y=await Promise.all(h.map(w=>P(w,r)));y.some(w=>w&&w!=="none"&&!/^url\(undefined/.test(w))&&n.style.setProperty(m,y.join(", "))}let d=a.getPropertyValue("background-color");d&&d!=="transparent"&&d!=="rgba(0, 0, 0, 0)"&&(n.style.backgroundColor=d);let u=Array.from(o.children),f=Array.from(n.children);for(let m=0;m<Math.min(u.length,f.length);m++)c.push([u[m],f[m]])}}async function Ce(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");l.reset();let{compress:r=!0,embedFonts:c=!1,fast:s=!0,scale:o=1,useProxy:n=""}=t,a,i,d="",u="",f,m;if({clone:a,classCSS:i}=await be(e,r,c,t),await new Promise(h=>{F(async()=>{await Se(a,t),h()},{fast:s})}),await new Promise(h=>{F(async()=>{await xe(e,a,t),h()},{fast:s})}),c&&await new Promise(h=>{F(async()=>{d=await K(),h()},{fast:s})}),r){let h=le(a).sort(),y=h.join(",");l.baseStyle.has(y)?u=l.baseStyle.get(y):await new Promise(w=>{F(()=>{u=de(h),l.baseStyle.set(y,u),w()},{fast:s})})}await new Promise(h=>{F(()=>{let y=e.getBoundingClientRect(),w=y.width,g=y.height,E=Number.isFinite(t.width),$=Number.isFinite(t.height),R=typeof o=="number"&&o!==1;if(!R){let A=y.width/y.height;E&&$?(w=t.width,g=t.height):E?(w=t.width,g=w/A):$&&(g=t.height,w=g*A)}if(w=Math.ceil(w),g=Math.ceil(g),a.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),a.style.transformOrigin="top left",!R&&(E||$)){let A=y.width,oe=y.height,S=w/A,X=g/oe,J=a.style.transform||"",re=`scale(${S}, ${X})`;a.style.transform=`${re} ${J}`.trim()}let N="http://www.w3.org/2000/svg",x=document.createElementNS(N,"foreignObject");x.setAttribute("width","100%"),x.setAttribute("height","100%");let B=document.createElement("style");B.textContent=u+d+"svg{overflow:visible;}"+i,x.appendChild(B),x.appendChild(a);let W=new XMLSerializer().serializeToString(x);m=`<svg xmlns="${N}" width="${w}" height="${g}" viewBox="0 0 ${w} ${g}">`+W+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(m)}`,h()},{fast:s})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function Fe(e,{scale:t=1}={}){let r=new Image;return r.src=e,await r.decode(),t!==1&&(r.style.width=`${r.naturalWidth*t}px`,r.style.height=`${r.naturalHeight*t}px`),r}async function Ee(e,{dpr:t=1,scale:r=1}={}){let c=new Image;c.src=e,c.crossOrigin="anonymous",c.loading="eager",c.decoding="sync";let s=me(),o=!1;if(s&&(document.body.appendChild(c),o=!0),await c.decode(),s&&await new Promise(u=>setTimeout(u,100)),c.width===0||c.height===0)throw o&&c.remove(),new Error("Image failed to load or has no dimensions");let n=c.naturalWidth*r,a=c.naturalHeight*r,i=document.createElement("canvas");i.width=Math.ceil(n*t),i.height=Math.ceil(a*t),i.style.width=`${n}px`,i.style.height=`${a}px`;let d=i.getContext("2d");return d.scale(t,t),d.drawImage(c,0,0,n,a),o&&c.remove(),i}async function ve(e,{type:t="svg",scale:r=1,backgroundColor:c="#fff",quality:s}={}){let o={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[t]||"image/png";if(t==="svg"){let a=decodeURIComponent(e.split(",")[1]);return new Blob([a],{type:"image/svg+xml"})}let n=await ne(e,{dpr:1,scale:r},c);return new Promise(a=>{n.toBlob(i=>a(i),`${o}`,s)})}async function ne(e,{dpr:t=1,scale:r=1},c){let s=await Ee(e,{dpr:t,scale:r});if(!c)return s;let o=document.createElement("canvas");o.width=s.width,o.height=s.height;let n=o.getContext("2d");return n.fillStyle=c,n.fillRect(0,0,o.width,o.height),n.drawImage(s,0,0),o}async function te(e,{dpr:t=1,scale:r=1,backgroundColor:c,quality:s},o="png"){let n=["jpg","jpeg","webp"].includes(o)?"#fff":void 0,i=await ne(e,{dpr:t,scale:r},c??n),d=new Image;return d.src=i.toDataURL(`image/${o}`,s),await d.decode(),d.style.width=`${i.width/t}px`,d.style.height=`${i.height/t}px`,d}async function Ue(e,{dpr:t=1,scale:r=1,backgroundColor:c,format:s="png",filename:o="snapDOM"}={}){if(s==="svg"){let m=await ve(e),p=URL.createObjectURL(m),h=document.createElement("a");h.href=p,h.download=`${o}.svg`,h.click(),URL.revokeObjectURL(p);return}let n=["jpg","jpeg","webp"].includes(s)?"#fff":void 0,i=await ne(e,{dpr:t,scale:r},c??n),d={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[s]||"image/png",u=i.toDataURL(d),f=document.createElement("a");f.href=u,f.download=`${o}.${s}`,f.click()}async function b(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return t.iconFonts&&he(t.iconFonts),await b.capture(e,t)}b.capture=async(e,t={})=>{let r=await Ce(e,t),c=t.dpr??(window.devicePixelRatio||1),s=t.scale||1;return{url:r,options:t,toRaw:()=>r,toImg:(o={})=>Fe(r,{dpr:c,scale:s,...o}),toCanvas:(o={})=>Ee(r,{dpr:c,scale:s,...o}),toBlob:(o={})=>ve(r,{dpr:c,scale:s,...o}),toPng:(o={})=>te(r,{dpr:c,scale:s,...o},"png"),toJpg:(o={})=>te(r,{dpr:c,scale:s,...o},"jpeg"),toWebp:(o={})=>te(r,{dpr:c,scale:s,...o},"webp"),download:({format:o="png",filename:n="snapDOM",backgroundColor:a,...i}={})=>Ue(r,{dpr:c,scale:s,format:o,filename:n,backgroundColor:a,...i})}};b.toRaw=async(e,t)=>(await b.capture(e,t)).toRaw();b.toImg=async(e,t)=>(await b.capture(e,t)).toImg();b.toCanvas=async(e,t)=>(await b.capture(e,t)).toCanvas();b.toBlob=async(e,t)=>(await b.capture(e,t)).toBlob(t);b.toPng=async(e,t)=>(await b.capture(e,t)).toPng(t);b.toJpg=async(e,t)=>(await b.capture(e,t)).toJpg(t);b.toWebp=async(e,t)=>(await b.capture(e,t)).toWebp(t);b.download=async(e,t={})=>{let{format:r="png",filename:c="capture",backgroundColor:s,...o}=t;return await(await b.capture(e,o)).download({format:r,filename:c,backgroundColor:s})};async function Be(e=document,t={}){let{embedFonts:r=!0,reset:c=!1}=t;if(c){l.reset();return}await document.fonts.ready,ie();let s=[],o=[];e?.querySelectorAll&&(s=Array.from(e.querySelectorAll("img[src]")),o=Array.from(e.querySelectorAll("*")));let n=[];for(let a of s){let i=a.src;l.image.has(i)||n.push(M(i,{useProxy:t.useProxy}).then(d=>l.image.set(i,d)).catch(()=>{}))}for(let a of o){let i=I(a).backgroundImage;if(i&&i!=="none"){let d=U(i);for(let u of d)u.startsWith("url(")&&n.push(P(u,t).catch(()=>{}))}}r&&await K({preCached:!0}),await Promise.all(n)}export{Be as preCache,b as snapdom};
umap/storage/fs.py CHANGED
@@ -79,7 +79,7 @@ class FSDataStorage(FileSystemStorage):
79
79
  "size": self.size(self._base_path(instance) / name),
80
80
  }
81
81
 
82
- def purge_old_versions(self, instance, keep=None):
82
+ def purge_old_versions(self, instance, keep=None, dry_run=False):
83
83
  root = self._base_path(instance)
84
84
  versions = self.list_versions(instance)
85
85
  if keep is not None:
@@ -92,7 +92,8 @@ class FSDataStorage(FileSystemStorage):
92
92
  if keep is not None and instance.geojson.name.endswith(name):
93
93
  continue
94
94
  try:
95
- self.delete(root / name)
95
+ if not dry_run:
96
+ self.delete(root / name)
96
97
  except FileNotFoundError:
97
98
  pass
98
99
  else:
@@ -43,6 +43,18 @@ class UmapManifestStaticFilesStorage(ManifestStaticFilesStorage):
43
43
  ),
44
44
  )
45
45
 
46
+ patterns = ManifestStaticFilesStorage.patterns + (
47
+ (
48
+ "*.svg",
49
+ (
50
+ (
51
+ r'(?P<matched>xlink:href="(?P<url>.*\.js)")',
52
+ 'xlink:href="%(url)s"',
53
+ ),
54
+ ),
55
+ ),
56
+ )
57
+
46
58
  def post_process(self, paths, **options):
47
59
  collected = super().post_process(paths, **options)
48
60
  for original_path, processed_path, processed in collected:
umap/templates/base.html CHANGED
@@ -1,7 +1,10 @@
1
1
  {% load umap_tags i18n static %}
2
2
 
3
+ {% get_current_language as LANGUAGE_CODE %}
4
+
3
5
  <!DOCTYPE html>
4
- <html {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
6
+ <html {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}
7
+ lang="{{ LANGUAGE_CODE }}">
5
8
  <head>
6
9
  <title>
7
10
  {% block head_title %}
@@ -4,10 +4,6 @@
4
4
 
5
5
  <link rel="stylesheet"
6
6
  href="{% static 'umap/vendors/leaflet/leaflet.css' %}" />
7
- <link rel="stylesheet"
8
- href="{% static 'umap/vendors/markercluster/MarkerCluster.css' %}" />
9
- <link rel="stylesheet"
10
- href="{% static 'umap/vendors/markercluster/MarkerCluster.Default.css' %}" />
11
7
  <link rel="stylesheet"
12
8
  href="{% static 'umap/vendors/editinosm/Leaflet.EditInOSM.css' %}" />
13
9
  <link rel="stylesheet"
@@ -7,17 +7,15 @@
7
7
  <script type="module"
8
8
  src="{% static 'umap/js/modules/leaflet-configure.js' %}"
9
9
  defer></script>
10
- <script src="{% static 'umap/vendors/markercluster/leaflet.markercluster.js' %}"
11
- defer></script>
12
10
  <script src="{% static 'umap/vendors/heat/leaflet-heat.js' %}" defer></script>
13
11
  {% if locale %}
14
12
  {% with "umap/locale/"|add:locale|add:".js" as path %}
15
13
  <script src="{% static path %}" defer></script>
16
14
  {% endwith %}
17
15
  {% endif %}
18
- <script type="module" src="{% static 'umap/js/modules/global.js' %}" defer></script>
19
16
  <script src="{% static 'umap/vendors/editable/Path.Drag.js' %}" defer></script>
20
17
  <script src="{% static 'umap/vendors/editable/Leaflet.Editable.js' %}" defer></script>
18
+ <script type="module" src="{% static 'umap/js/modules/global.js' %}" defer></script>
21
19
  <script src="{% static 'umap/vendors/hash/leaflet-hash.js' %}" defer></script>
22
20
  <script src="{% static 'umap/vendors/editinosm/Leaflet.EditInOSM.js' %}"
23
21
  defer></script>
umap/tests/base.py CHANGED
@@ -128,7 +128,15 @@ class DataLayerFactory(factory.django.DjangoModelFactory):
128
128
  def _adjust_kwargs(cls, **kwargs):
129
129
  if "data" in kwargs:
130
130
  data = copy.deepcopy(kwargs.pop("data"))
131
- data.setdefault("_umap_options", {})
131
+ data.setdefault(
132
+ "_umap_options",
133
+ {
134
+ "fields": [
135
+ {"key": "name", "type": "String"},
136
+ {"key": "description", "type": "Text"},
137
+ ]
138
+ },
139
+ )
132
140
  if "name" in data["_umap_options"] and kwargs["name"] == cls.name:
133
141
  kwargs["name"] = data["_umap_options"]["name"]
134
142
  kwargs.setdefault("settings", {})
@@ -42,8 +42,10 @@ def test_create_map_with_cursor(page, live_server, tilelayer):
42
42
  (
43
43
  "margin-left: -16px; "
44
44
  "margin-top: -40px; "
45
+ "width: 12px; "
46
+ "height: 12px; "
45
47
  "transform: translate3d(200px, 200px, 0px); "
46
- "z-index: 200;"
48
+ "z-index: 0;"
47
49
  ),
48
50
  )
49
51
 
@@ -27,6 +27,7 @@ DATALAYER_DATA1 = {
27
27
  "myboolean": True,
28
28
  "mydate": "2024/04/14 12:19:17",
29
29
  "maybeempty": "not empty",
30
+ "onlyinone": "blah",
30
31
  },
31
32
  "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
32
33
  },
@@ -39,6 +40,7 @@ DATALAYER_DATA1 = {
39
40
  "myboolean": False,
40
41
  "mydate": "2024/03/13 12:20:20",
41
42
  "maybeempty": "",
43
+ "onlyinone": "ffff",
42
44
  },
43
45
  "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
44
46
  },
@@ -95,13 +97,13 @@ DATALAYER_DATA2 = {
95
97
 
96
98
  def test_simple_equal_rule_at_load(live_server, page, map):
97
99
  map.settings["properties"]["rules"] = [
98
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}}
100
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}}
99
101
  ]
100
102
  map.save()
101
103
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
102
104
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
103
105
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
104
- markers = page.locator(".leaflet-marker-icon .icon_container")
106
+ markers = page.locator(".leaflet-marker-icon .icon-container")
105
107
  expect(markers).to_have_count(5)
106
108
  colors = getColors(markers)
107
109
  assert colors.count("rgb(240, 248, 255)") == 3
@@ -109,13 +111,13 @@ def test_simple_equal_rule_at_load(live_server, page, map):
109
111
 
110
112
  def test_simple_not_equal_rule_at_load(live_server, page, map):
111
113
  map.settings["properties"]["rules"] = [
112
- {"condition": "mytype!=even", "options": {"color": "aliceblue"}}
114
+ {"condition": "mytype!=even", "properties": {"color": "aliceblue"}}
113
115
  ]
114
116
  map.save()
115
117
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
116
118
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
117
119
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
118
- markers = page.locator(".leaflet-marker-icon .icon_container")
120
+ markers = page.locator(".leaflet-marker-icon .icon-container")
119
121
  expect(markers).to_have_count(5)
120
122
  colors = getColors(markers)
121
123
  assert colors.count("rgb(240, 248, 255)") == 3
@@ -123,13 +125,13 @@ def test_simple_not_equal_rule_at_load(live_server, page, map):
123
125
 
124
126
  def test_gt_rule_with_number_at_load(live_server, page, map):
125
127
  map.settings["properties"]["rules"] = [
126
- {"condition": "mynumber>10", "options": {"color": "aliceblue"}}
128
+ {"condition": "mynumber>10", "properties": {"color": "aliceblue"}}
127
129
  ]
128
130
  map.save()
129
131
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
130
132
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
131
133
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
132
- markers = page.locator(".leaflet-marker-icon .icon_container")
134
+ markers = page.locator(".leaflet-marker-icon .icon-container")
133
135
  expect(markers).to_have_count(5)
134
136
  colors = getColors(markers)
135
137
  assert colors.count("rgb(240, 248, 255)") == 2
@@ -137,13 +139,13 @@ def test_gt_rule_with_number_at_load(live_server, page, map):
137
139
 
138
140
  def test_lt_rule_with_number_at_load(live_server, page, map):
139
141
  map.settings["properties"]["rules"] = [
140
- {"condition": "mynumber<14", "options": {"color": "aliceblue"}}
142
+ {"condition": "mynumber<14", "properties": {"color": "aliceblue"}}
141
143
  ]
142
144
  map.save()
143
145
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
144
146
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
145
147
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
146
- markers = page.locator(".leaflet-marker-icon .icon_container")
148
+ markers = page.locator(".leaflet-marker-icon .icon-container")
147
149
  expect(markers).to_have_count(5)
148
150
  colors = getColors(markers)
149
151
  assert colors.count("rgb(240, 248, 255)") == 4
@@ -151,13 +153,13 @@ def test_lt_rule_with_number_at_load(live_server, page, map):
151
153
 
152
154
  def test_lt_rule_with_float_at_load(live_server, page, map):
153
155
  map.settings["properties"]["rules"] = [
154
- {"condition": "mynumber<12.3", "options": {"color": "aliceblue"}}
156
+ {"condition": "mynumber<12.3", "properties": {"color": "aliceblue"}}
155
157
  ]
156
158
  map.save()
157
159
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
158
160
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
159
161
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
160
- markers = page.locator(".leaflet-marker-icon .icon_container")
162
+ markers = page.locator(".leaflet-marker-icon .icon-container")
161
163
  expect(markers).to_have_count(5)
162
164
  colors = getColors(markers)
163
165
  assert colors.count("rgb(240, 248, 255)") == 4
@@ -165,13 +167,13 @@ def test_lt_rule_with_float_at_load(live_server, page, map):
165
167
 
166
168
  def test_equal_rule_with_boolean_at_load(live_server, page, map):
167
169
  map.settings["properties"]["rules"] = [
168
- {"condition": "myboolean=true", "options": {"color": "aliceblue"}}
170
+ {"condition": "myboolean=true", "properties": {"color": "aliceblue"}}
169
171
  ]
170
172
  map.save()
171
173
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
172
174
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
173
175
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
174
- markers = page.locator(".leaflet-marker-icon .icon_container")
176
+ markers = page.locator(".leaflet-marker-icon .icon-container")
175
177
  expect(markers).to_have_count(5)
176
178
  colors = getColors(markers)
177
179
  assert colors.count("rgb(240, 248, 255)") == 2
@@ -179,13 +181,13 @@ def test_equal_rule_with_boolean_at_load(live_server, page, map):
179
181
 
180
182
  def test_equal_rule_with_boolean_not_true_at_load(live_server, page, map):
181
183
  map.settings["properties"]["rules"] = [
182
- {"condition": "myboolean!=true", "options": {"color": "aliceblue"}}
184
+ {"condition": "myboolean!=true", "properties": {"color": "aliceblue"}}
183
185
  ]
184
186
  map.save()
185
187
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
186
188
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
187
189
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
188
- markers = page.locator(".leaflet-marker-icon .icon_container")
190
+ markers = page.locator(".leaflet-marker-icon .icon-container")
189
191
  expect(markers).to_have_count(5)
190
192
  colors = getColors(markers)
191
193
  assert colors.count("rgb(240, 248, 255)") == 3
@@ -193,13 +195,13 @@ def test_equal_rule_with_boolean_not_true_at_load(live_server, page, map):
193
195
 
194
196
  def test_equal_rule_with_boolean_false_at_load(live_server, page, map):
195
197
  map.settings["properties"]["rules"] = [
196
- {"condition": "myboolean=false", "options": {"color": "aliceblue"}}
198
+ {"condition": "myboolean=false", "properties": {"color": "aliceblue"}}
197
199
  ]
198
200
  map.save()
199
201
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
200
202
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
201
203
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
202
- markers = page.locator(".leaflet-marker-icon .icon_container")
204
+ markers = page.locator(".leaflet-marker-icon .icon-container")
203
205
  expect(markers).to_have_count(5)
204
206
  colors = getColors(markers)
205
207
  assert colors.count("rgb(240, 248, 255)") == 1
@@ -207,13 +209,13 @@ def test_equal_rule_with_boolean_false_at_load(live_server, page, map):
207
209
 
208
210
  def test_equal_rule_with_boolean_not_false_at_load(live_server, page, map):
209
211
  map.settings["properties"]["rules"] = [
210
- {"condition": "myboolean!=false", "options": {"color": "aliceblue"}}
212
+ {"condition": "myboolean!=false", "properties": {"color": "aliceblue"}}
211
213
  ]
212
214
  map.save()
213
215
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
214
216
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
215
217
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
216
- markers = page.locator(".leaflet-marker-icon .icon_container")
218
+ markers = page.locator(".leaflet-marker-icon .icon-container")
217
219
  expect(markers).to_have_count(5)
218
220
  colors = getColors(markers)
219
221
  assert colors.count("rgb(240, 248, 255)") == 4
@@ -221,13 +223,13 @@ def test_equal_rule_with_boolean_not_false_at_load(live_server, page, map):
221
223
 
222
224
  def test_empty_rule_at_load(live_server, page, map):
223
225
  map.settings["properties"]["rules"] = [
224
- {"condition": "maybeempty=", "options": {"color": "aliceblue"}}
226
+ {"condition": "maybeempty=", "properties": {"color": "aliceblue"}}
225
227
  ]
226
228
  map.save()
227
229
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
228
230
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
229
231
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
230
- markers = page.locator(".leaflet-marker-icon .icon_container")
232
+ markers = page.locator(".leaflet-marker-icon .icon-container")
231
233
  expect(markers).to_have_count(5)
232
234
  colors = getColors(markers)
233
235
  assert colors.count("rgb(240, 248, 255)") == 3
@@ -235,13 +237,13 @@ def test_empty_rule_at_load(live_server, page, map):
235
237
 
236
238
  def test_not_empty_rule_at_load(live_server, page, map):
237
239
  map.settings["properties"]["rules"] = [
238
- {"condition": "maybeempty!=", "options": {"color": "aliceblue"}}
240
+ {"condition": "maybeempty!=", "properties": {"color": "aliceblue"}}
239
241
  ]
240
242
  map.save()
241
243
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
242
244
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
243
245
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
244
- markers = page.locator(".leaflet-marker-icon .icon_container")
246
+ markers = page.locator(".leaflet-marker-icon .icon-container")
245
247
  expect(markers).to_have_count(5)
246
248
  colors = getColors(markers)
247
249
  assert colors.count("rgb(240, 248, 255)") == 2
@@ -251,7 +253,7 @@ def test_can_create_new_rule(live_server, page, openmap):
251
253
  DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
252
254
  DataLayerFactory(map=openmap, data=DATALAYER_DATA2)
253
255
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}#6/48.948/1.670")
254
- markers = page.locator(".leaflet-marker-icon .icon_container")
256
+ markers = page.locator(".leaflet-marker-icon .icon-container")
255
257
  expect(markers).to_have_count(5)
256
258
  page.get_by_role("button", name="Edit").click()
257
259
  page.get_by_role("button", name="Map advanced properties").click()
@@ -270,13 +272,13 @@ def test_can_create_new_rule(live_server, page, openmap):
270
272
 
271
273
  def test_can_deactive_rule_from_list(live_server, page, openmap):
272
274
  openmap.settings["properties"]["rules"] = [
273
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}}
275
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}}
274
276
  ]
275
277
  openmap.save()
276
278
  DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
277
279
  DataLayerFactory(map=openmap, data=DATALAYER_DATA2)
278
280
  page.goto(f"{live_server.url}{openmap.get_absolute_url()}#6/48.948/1.670")
279
- markers = page.locator(".leaflet-marker-icon .icon_container")
281
+ markers = page.locator(".leaflet-marker-icon .icon-container")
280
282
  expect(markers).to_have_count(5)
281
283
  colors = getColors(markers)
282
284
  assert colors.count("rgb(240, 248, 255)") == 3
@@ -299,9 +301,17 @@ def test_autocomplete_datalist(live_server, page, openmap):
299
301
  page.get_by_role("button", name="Add rule").click()
300
302
  panel = page.locator(".panel.right.on")
301
303
  datalist = panel.locator(".umap-field-condition datalist option")
302
- expect(datalist).to_have_count(6)
304
+ expect(datalist).to_have_count(7)
303
305
  values = {option.inner_text() for option in datalist.all()}
304
- assert values == {"myboolean", "mytype", "mynumber", "mydate", "name", "maybeempty"}
306
+ assert values == {
307
+ "myboolean",
308
+ "mytype",
309
+ "mynumber",
310
+ "mydate",
311
+ "name",
312
+ "maybeempty",
313
+ "onlyinone",
314
+ }
305
315
  page.get_by_placeholder("key=value or key!=value").fill("mytype")
306
316
  expect(datalist).to_have_count(4)
307
317
  values = {option.inner_text() for option in datalist.all()}
@@ -314,15 +324,15 @@ def test_autocomplete_datalist(live_server, page, openmap):
314
324
 
315
325
  def test_can_combine_rules(live_server, page, map):
316
326
  map.settings["properties"]["rules"] = [
317
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}},
318
- {"condition": "mynumber>10", "options": {"iconClass": "Drop"}},
327
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}},
328
+ {"condition": "mynumber>10", "properties": {"iconClass": "Drop"}},
319
329
  ]
320
330
  map.save()
321
331
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
322
332
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
323
333
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
324
- markers = page.locator(".leaflet-marker-icon .icon_container")
325
- drops = page.locator(".umap-drop-icon .icon_container")
334
+ markers = page.locator(".leaflet-marker-icon .icon-container")
335
+ drops = page.locator(".umap-drop-icon .icon-container")
326
336
  expect(markers).to_have_count(5)
327
337
  expect(drops).to_have_count(2)
328
338
  colors = getColors(markers)
@@ -333,14 +343,14 @@ def test_can_combine_rules(live_server, page, map):
333
343
 
334
344
  def test_first_matching_rule_wins_on_given_property(live_server, page, map):
335
345
  map.settings["properties"]["rules"] = [
336
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}},
337
- {"condition": "mytype!=even", "options": {"color": "darkred"}},
346
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}},
347
+ {"condition": "mytype!=even", "properties": {"color": "darkred"}},
338
348
  ]
339
349
  map.save()
340
350
  DataLayerFactory(map=map, data=DATALAYER_DATA1)
341
351
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
342
352
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
343
- markers = page.locator(".leaflet-marker-icon .icon_container")
353
+ markers = page.locator(".leaflet-marker-icon .icon-container")
344
354
  expect(markers).to_have_count(5)
345
355
  colors = getColors(markers)
346
356
  assert colors.count("rgb(240, 248, 255)") == 3
@@ -348,20 +358,52 @@ def test_first_matching_rule_wins_on_given_property(live_server, page, map):
348
358
 
349
359
  def test_rules_from_datalayer(live_server, page, map):
350
360
  map.settings["properties"]["rules"] = [
351
- {"condition": "mytype=odd", "options": {"color": "darkred"}}
361
+ {"condition": "mytype=odd", "properties": {"color": "darkred"}}
352
362
  ]
353
363
  map.save()
354
364
  data = deepcopy(DATALAYER_DATA1)
355
365
  data["_umap_options"]["rules"] = [
356
- {"condition": "mytype=odd", "options": {"color": "aliceblue"}}
366
+ {"condition": "mytype=odd", "properties": {"color": "aliceblue"}}
357
367
  ]
358
368
  DataLayerFactory(map=map, data=data)
359
369
  DataLayerFactory(map=map, data=DATALAYER_DATA2)
360
370
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
361
- markers = page.locator(".leaflet-marker-icon .icon_container")
371
+ markers = page.locator(".leaflet-marker-icon .icon-container")
362
372
  expect(markers).to_have_count(5)
363
373
  colors = getColors(markers)
364
374
  # Alice Blue should only affect layer 1
365
375
  assert colors.count("rgb(240, 248, 255)") == 1
366
376
  # Dark Red as for map global rules
367
377
  assert colors.count("rgb(139, 0, 0)") == 2
378
+
379
+
380
+ def test_rules_in_caption(live_server, page, map):
381
+ map.settings["properties"]["rules"] = [
382
+ {
383
+ "condition": "mytype=odd",
384
+ "name": "Rule shown twice",
385
+ "properties": {"color": "darkred"},
386
+ },
387
+ {
388
+ "condition": "onlyinone=fff",
389
+ "name": "Rule shown once",
390
+ "properties": {"color": "darkred"},
391
+ },
392
+ ]
393
+ map.settings["properties"]["onLoadPanel"] = "caption"
394
+ map.save()
395
+ data = deepcopy(DATALAYER_DATA1)
396
+ data["_umap_options"]["rules"] = [
397
+ {
398
+ "condition": "myboolean=true",
399
+ "name": "Rule shown also once",
400
+ "properties": {"color": "aliceblue"},
401
+ }
402
+ ]
403
+ DataLayerFactory(map=map, data=data)
404
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
405
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
406
+ panel = page.locator(".panel.left.on")
407
+ expect(panel.get_by_text("Rule shown twice")).to_have_count(2)
408
+ expect(panel.get_by_text("Rule shown once")).to_have_count(1)
409
+ expect(panel.get_by_text("Rule shown also once")).to_have_count(1)
@@ -115,7 +115,7 @@ def test_should_honour_color_variable(live_server, map, page):
115
115
  DataLayerFactory(map=map, data=data)
116
116
  page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/2.5")
117
117
  expect(page.locator(".leaflet-overlay-pane path[fill='tomato']"))
118
- markers = page.locator(".leaflet-marker-icon .icon_container")
118
+ markers = page.locator(".leaflet-marker-icon .icon-container")
119
119
  expect(markers).to_have_css("background-color", "rgb(240, 248, 255)")
120
120
 
121
121
 
@@ -154,9 +154,7 @@ def test_can_draw_multi(live_server, page, tilelayer):
154
154
  expect(multi_button).to_be_hidden()
155
155
  polygons.first.click(button="right", position={"x": 10, "y": 10})
156
156
  expect(page.get_by_role("button", name="Transform to lines")).to_be_hidden()
157
- expect(
158
- page.get_by_role("button", name="Remove shape from the multi")
159
- ).to_be_visible()
157
+ expect(page.get_by_role("button", name="Delete this shape")).to_be_visible()
160
158
 
161
159
 
162
160
  def test_can_draw_hole(page, live_server, tilelayer):
@@ -179,7 +177,7 @@ def test_can_draw_hole(page, live_server, tilelayer):
179
177
  expect(vertices).to_have_count(4)
180
178
 
181
179
  # First vertex of the hole will be created here
182
- map.click(position={"x": 180, "y": 120})
180
+ map.click(position={"x": 180, "y": 120}, button="right")
183
181
  page.get_by_role("button", name="Start a hole here").click()
184
182
  map.click(position={"x": 180, "y": 180})
185
183
  map.click(position={"x": 120, "y": 180})
@@ -474,7 +472,7 @@ def test_vertexmarker_not_shown_if_too_many(live_server, map, page, settings):
474
472
  page.locator(".umap-import textarea").fill(geojson)
475
473
  page.locator('select[name="format"]').select_option("geojson")
476
474
  page.get_by_role("button", name="Import data", exact=True).click()
477
- page.locator("path").click()
475
+ page.locator("path").click(button="right")
478
476
  page.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
479
477
  expect(page.locator(".umap-tooltip-container")).to_contain_text(
480
478
  "Please zoom in to edit the geometry"
@@ -150,9 +150,7 @@ def test_can_draw_multi(live_server, page, tilelayer):
150
150
  expect(add_shape).to_be_hidden()
151
151
  lines.first.click(button="right", position={"x": 10, "y": 1})
152
152
  expect(page.get_by_role("button", name="Transform to polygon")).to_be_hidden()
153
- expect(
154
- page.get_by_role("button", name="Remove shape from the multi")
155
- ).to_be_visible()
153
+ expect(page.get_by_role("button", name="Delete this shape")).to_be_visible()
156
154
 
157
155
 
158
156
  def test_can_transfer_shape_from_simple_polyline(live_server, page, tilelayer):
@@ -328,11 +326,11 @@ def test_can_delete_shape_using_toolbar(live_server, page, tilelayer, settings):
328
326
  map.click(position={"x": 100, "y": 200})
329
327
 
330
328
  # Now split the line
331
- map.click(position={"x": 100, "y": 100})
329
+ map.click(position={"x": 100, "y": 100}, button="right")
332
330
  page.get_by_role("button", name="Split line").click()
333
331
 
334
332
  # Delete part of it
335
- map.click(position={"x": 125, "y": 100})
333
+ map.click(position={"x": 125, "y": 100}, button="right")
336
334
  page.get_by_role("button", name="Delete this shape").click()
337
335
  data = save_and_get_json(page)
338
336
  assert len(data["features"]) == 1
@@ -369,7 +367,7 @@ def test_can_merge_lines(live_server, page, tilelayer, settings):
369
367
  )
370
368
 
371
369
  # Right click and merge nodes
372
- map.click(button="right", position={"x": 100, "y": 200})
370
+ map.click(button="right", position={"x": 100, "y": 120})
373
371
  page.get_by_role("button", name="Merge lines").click()
374
372
  data = save_and_get_json(page)
375
373
  assert len(data["features"]) == 1