diff --git a/coverage/coverage-summary.json b/coverage/coverage-summary.json index 4a45cfe53..cf04a894a 100644 --- a/coverage/coverage-summary.json +++ b/coverage/coverage-summary.json @@ -1,69 +1,71 @@ -{"total": {"lines":{"total":6260,"covered":3796,"skipped":0,"pct":60.64},"statements":{"total":6574,"covered":3913,"skipped":0,"pct":59.52},"functions":{"total":949,"covered":495,"skipped":0,"pct":52.16},"branches":{"total":3223,"covered":1398,"skipped":0,"pct":43.38}} -,"/Users/jfhenon/Work/svgedit/packages/svgcanvas/common/browser.js": {"lines":{"total":25,"covered":24,"skipped":0,"pct":96},"functions":{"total":6,"covered":2,"skipped":0,"pct":33.33},"statements":{"total":30,"covered":25,"skipped":0,"pct":83.33},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/packages/svgcanvas/common/util.js": {"lines":{"total":90,"covered":8,"skipped":0,"pct":8.89},"functions":{"total":7,"covered":3,"skipped":0,"pct":42.86},"statements":{"total":92,"covered":10,"skipped":0,"pct":10.87},"branches":{"total":98,"covered":10,"skipped":0,"pct":10.2}} -,"/Users/jfhenon/Work/svgedit/src/editor/ConfigObj.js": {"lines":{"total":101,"covered":46,"skipped":0,"pct":45.54},"functions":{"total":14,"covered":11,"skipped":0,"pct":78.57},"statements":{"total":102,"covered":46,"skipped":0,"pct":45.1},"branches":{"total":96,"covered":30,"skipped":0,"pct":31.25}} -,"/Users/jfhenon/Work/svgedit/src/editor/Editor.js": {"lines":{"total":410,"covered":200,"skipped":0,"pct":48.78},"functions":{"total":102,"covered":34,"skipped":0,"pct":33.33},"statements":{"total":417,"covered":201,"skipped":0,"pct":48.2},"branches":{"total":213,"covered":78,"skipped":0,"pct":36.62}} -,"/Users/jfhenon/Work/svgedit/src/editor/EditorStartup.js": {"lines":{"total":346,"covered":216,"skipped":0,"pct":62.43},"functions":{"total":51,"covered":27,"skipped":0,"pct":52.94},"statements":{"total":357,"covered":224,"skipped":0,"pct":62.75},"branches":{"total":122,"covered":41,"skipped":0,"pct":33.61}} -,"/Users/jfhenon/Work/svgedit/src/editor/MainMenu.js": {"lines":{"total":111,"covered":44,"skipped":0,"pct":39.64},"functions":{"total":14,"covered":7,"skipped":0,"pct":50},"statements":{"total":111,"covered":44,"skipped":0,"pct":39.64},"branches":{"total":48,"covered":7,"skipped":0,"pct":14.58}} -,"/Users/jfhenon/Work/svgedit/src/editor/Rulers.js": {"lines":{"total":119,"covered":93,"skipped":0,"pct":78.15},"functions":{"total":6,"covered":5,"skipped":0,"pct":83.33},"statements":{"total":124,"covered":98,"skipped":0,"pct":79.03},"branches":{"total":43,"covered":32,"skipped":0,"pct":74.42}} -,"/Users/jfhenon/Work/svgedit/src/editor/browser-not-supported.js": {"lines":{"total":4,"covered":3,"skipped":0,"pct":75},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":4,"covered":3,"skipped":0,"pct":75},"branches":{"total":4,"covered":3,"skipped":0,"pct":75}} -,"/Users/jfhenon/Work/svgedit/src/editor/contextmenu.js": {"lines":{"total":22,"covered":9,"skipped":0,"pct":40.91},"functions":{"total":8,"covered":1,"skipped":0,"pct":12.5},"statements":{"total":23,"covered":9,"skipped":0,"pct":39.13},"branches":{"total":13,"covered":0,"skipped":0,"pct":0}} -,"/Users/jfhenon/Work/svgedit/src/editor/locale.js": {"lines":{"total":14,"covered":9,"skipped":0,"pct":64.29},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":14,"covered":9,"skipped":0,"pct":64.29},"branches":{"total":10,"covered":3,"skipped":0,"pct":30}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/PaintBox.js": {"lines":{"total":64,"covered":51,"skipped":0,"pct":79.69},"functions":{"total":4,"covered":4,"skipped":0,"pct":100},"statements":{"total":67,"covered":53,"skipped":0,"pct":79.1},"branches":{"total":33,"covered":21,"skipped":0,"pct":63.64}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/index.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seButton.js": {"lines":{"total":57,"covered":40,"skipped":0,"pct":70.18},"functions":{"total":15,"covered":7,"skipped":0,"pct":46.67},"statements":{"total":60,"covered":42,"skipped":0,"pct":70},"branches":{"total":33,"covered":23,"skipped":0,"pct":69.7}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seColorPicker.js": {"lines":{"total":50,"covered":41,"skipped":0,"pct":82},"functions":{"total":16,"covered":11,"skipped":0,"pct":68.75},"statements":{"total":51,"covered":41,"skipped":0,"pct":80.39},"branches":{"total":11,"covered":8,"skipped":0,"pct":72.73}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seExplorerButton.js": {"lines":{"total":95,"covered":72,"skipped":0,"pct":75.79},"functions":{"total":17,"covered":10,"skipped":0,"pct":58.82},"statements":{"total":98,"covered":72,"skipped":0,"pct":73.47},"branches":{"total":30,"covered":17,"skipped":0,"pct":56.67}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seFlyingButton.js": {"lines":{"total":74,"covered":47,"skipped":0,"pct":63.51},"functions":{"total":14,"covered":9,"skipped":0,"pct":64.29},"statements":{"total":75,"covered":48,"skipped":0,"pct":64},"branches":{"total":29,"covered":12,"skipped":0,"pct":41.38}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seInput.js": {"lines":{"total":46,"covered":31,"skipped":0,"pct":67.39},"functions":{"total":16,"covered":8,"skipped":0,"pct":50},"statements":{"total":47,"covered":31,"skipped":0,"pct":65.96},"branches":{"total":8,"covered":4,"skipped":0,"pct":50}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seList.js": {"lines":{"total":87,"covered":59,"skipped":0,"pct":67.82},"functions":{"total":21,"covered":10,"skipped":0,"pct":47.62},"statements":{"total":89,"covered":61,"skipped":0,"pct":68.54},"branches":{"total":22,"covered":12,"skipped":0,"pct":54.55}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seListItem.js": {"lines":{"total":39,"covered":26,"skipped":0,"pct":66.67},"functions":{"total":12,"covered":4,"skipped":0,"pct":33.33},"statements":{"total":40,"covered":27,"skipped":0,"pct":67.5},"branches":{"total":10,"covered":8,"skipped":0,"pct":80}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seMenu.js": {"lines":{"total":26,"covered":20,"skipped":0,"pct":76.92},"functions":{"total":7,"covered":3,"skipped":0,"pct":42.86},"statements":{"total":27,"covered":20,"skipped":0,"pct":74.07},"branches":{"total":5,"covered":3,"skipped":0,"pct":60}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seMenuItem.js": {"lines":{"total":37,"covered":26,"skipped":0,"pct":70.27},"functions":{"total":9,"covered":5,"skipped":0,"pct":55.56},"statements":{"total":40,"covered":27,"skipped":0,"pct":67.5},"branches":{"total":19,"covered":8,"skipped":0,"pct":42.11}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/sePalette.js": {"lines":{"total":33,"covered":32,"skipped":0,"pct":96.97},"functions":{"total":7,"covered":7,"skipped":0,"pct":100},"statements":{"total":33,"covered":32,"skipped":0,"pct":96.97},"branches":{"total":13,"covered":10,"skipped":0,"pct":76.92}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/sePlainBorderButton.js": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/sePlainMenuButton.js": {"lines":{"total":2,"covered":2,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":2,"covered":2,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seSelect.js": {"lines":{"total":56,"covered":45,"skipped":0,"pct":80.36},"functions":{"total":17,"covered":9,"skipped":0,"pct":52.94},"statements":{"total":59,"covered":47,"skipped":0,"pct":79.66},"branches":{"total":16,"covered":14,"skipped":0,"pct":87.5}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seSpinInput.js": {"lines":{"total":65,"covered":55,"skipped":0,"pct":84.62},"functions":{"total":18,"covered":10,"skipped":0,"pct":55.56},"statements":{"total":66,"covered":55,"skipped":0,"pct":83.33},"branches":{"total":17,"covered":13,"skipped":0,"pct":76.47}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seText.js": {"lines":{"total":28,"covered":18,"skipped":0,"pct":64.29},"functions":{"total":10,"covered":4,"skipped":0,"pct":40},"statements":{"total":29,"covered":18,"skipped":0,"pct":62.07},"branches":{"total":8,"covered":5,"skipped":0,"pct":62.5}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/seZoom.js": {"lines":{"total":99,"covered":93,"skipped":0,"pct":93.94},"functions":{"total":28,"covered":24,"skipped":0,"pct":85.71},"statements":{"total":107,"covered":100,"skipped":0,"pct":93.46},"branches":{"total":36,"covered":21,"skipped":0,"pct":58.33}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/jgraduate/ColorValuePicker.js": {"lines":{"total":231,"covered":54,"skipped":0,"pct":23.38},"functions":{"total":9,"covered":3,"skipped":0,"pct":33.33},"statements":{"total":249,"covered":63,"skipped":0,"pct":25.3},"branches":{"total":157,"covered":34,"skipped":0,"pct":21.66}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/jgraduate/Slider.js": {"lines":{"total":156,"covered":91,"skipped":0,"pct":58.33},"functions":{"total":17,"covered":9,"skipped":0,"pct":52.94},"statements":{"total":176,"covered":96,"skipped":0,"pct":54.55},"branches":{"total":186,"covered":97,"skipped":0,"pct":52.15}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/jgraduate/jQuery.jGraduate.js": {"lines":{"total":580,"covered":275,"skipped":0,"pct":47.41},"functions":{"total":44,"covered":14,"skipped":0,"pct":31.82},"statements":{"total":602,"covered":282,"skipped":0,"pct":46.84},"branches":{"total":278,"covered":100,"skipped":0,"pct":35.97}} -,"/Users/jfhenon/Work/svgedit/src/editor/components/jgraduate/jQuery.jPicker.js": {"lines":{"total":840,"covered":449,"skipped":0,"pct":53.45},"functions":{"total":60,"covered":39,"skipped":0,"pct":65},"statements":{"total":926,"covered":475,"skipped":0,"pct":51.3},"branches":{"total":777,"covered":327,"skipped":0,"pct":42.08}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/SePlainAlertDialog.js": {"lines":{"total":7,"covered":1,"skipped":0,"pct":14.29},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":7,"covered":1,"skipped":0,"pct":14.29},"branches":{"total":2,"covered":0,"skipped":0,"pct":0}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/cmenuDialog.js": {"lines":{"total":120,"covered":112,"skipped":0,"pct":93.33},"functions":{"total":28,"covered":16,"skipped":0,"pct":57.14},"statements":{"total":131,"covered":117,"skipped":0,"pct":89.31},"branches":{"total":23,"covered":19,"skipped":0,"pct":82.61}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/cmenuLayersDialog.js": {"lines":{"total":61,"covered":49,"skipped":0,"pct":80.33},"functions":{"total":16,"covered":6,"skipped":0,"pct":37.5},"statements":{"total":66,"covered":49,"skipped":0,"pct":74.24},"branches":{"total":18,"covered":13,"skipped":0,"pct":72.22}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/editorPreferencesDialog.js": {"lines":{"total":157,"covered":125,"skipped":0,"pct":79.62},"functions":{"total":30,"covered":9,"skipped":0,"pct":30},"statements":{"total":159,"covered":126,"skipped":0,"pct":79.25},"branches":{"total":46,"covered":35,"skipped":0,"pct":76.09}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/exportDialog.js": {"lines":{"total":55,"covered":39,"skipped":0,"pct":70.91},"functions":{"total":14,"covered":5,"skipped":0,"pct":35.71},"statements":{"total":58,"covered":39,"skipped":0,"pct":67.24},"branches":{"total":12,"covered":6,"skipped":0,"pct":50}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/imagePropertiesDialog.js": {"lines":{"total":161,"covered":90,"skipped":0,"pct":55.9},"functions":{"total":20,"covered":5,"skipped":0,"pct":25},"statements":{"total":162,"covered":90,"skipped":0,"pct":55.56},"branches":{"total":53,"covered":23,"skipped":0,"pct":43.4}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/index.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/seAlertDialog.js": {"lines":{"total":6,"covered":2,"skipped":0,"pct":33.33},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":6,"covered":2,"skipped":0,"pct":33.33},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/seConfirmDialog.js": {"lines":{"total":8,"covered":2,"skipped":0,"pct":25},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":8,"covered":2,"skipped":0,"pct":25},"branches":{"total":2,"covered":0,"skipped":0,"pct":0}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/sePromptDialog.js": {"lines":{"total":24,"covered":5,"skipped":0,"pct":20.83},"functions":{"total":7,"covered":2,"skipped":0,"pct":28.57},"statements":{"total":24,"covered":5,"skipped":0,"pct":20.83},"branches":{"total":9,"covered":0,"skipped":0,"pct":0}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/seSelectDialog.js": {"lines":{"total":8,"covered":2,"skipped":0,"pct":25},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":8,"covered":2,"skipped":0,"pct":25},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/svgSourceDialog.js": {"lines":{"total":74,"covered":56,"skipped":0,"pct":75.68},"functions":{"total":17,"covered":6,"skipped":0,"pct":35.29},"statements":{"total":75,"covered":57,"skipped":0,"pct":76},"branches":{"total":17,"covered":14,"skipped":0,"pct":82.35}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/se-elix/define/NumberSpinBox.js": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/se-elix/src/base/NumberSpinBox.js": {"lines":{"total":54,"covered":52,"skipped":0,"pct":96.3},"functions":{"total":13,"covered":11,"skipped":0,"pct":84.62},"statements":{"total":54,"covered":52,"skipped":0,"pct":96.3},"branches":{"total":53,"covered":47,"skipped":0,"pct":88.68}} -,"/Users/jfhenon/Work/svgedit/src/editor/dialogs/se-elix/src/plain/PlainNumberSpinBox.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-eyedropper/ext-eyedropper.js": {"lines":{"total":60,"covered":39,"skipped":0,"pct":65},"functions":{"total":8,"covered":5,"skipped":0,"pct":62.5},"statements":{"total":71,"covered":39,"skipped":0,"pct":54.93},"branches":{"total":39,"covered":15,"skipped":0,"pct":38.46}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-eyedropper/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-grid/ext-grid.js": {"lines":{"total":88,"covered":43,"skipped":0,"pct":48.86},"functions":{"total":8,"covered":4,"skipped":0,"pct":50},"statements":{"total":90,"covered":43,"skipped":0,"pct":47.78},"branches":{"total":10,"covered":4,"skipped":0,"pct":40}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-grid/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-markers/ext-markers.js": {"lines":{"total":149,"covered":46,"skipped":0,"pct":30.87},"functions":{"total":21,"covered":12,"skipped":0,"pct":57.14},"statements":{"total":164,"covered":48,"skipped":0,"pct":29.27},"branches":{"total":80,"covered":22,"skipped":0,"pct":27.5}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-opensave/ext-opensave.js": {"lines":{"total":136,"covered":36,"skipped":0,"pct":26.47},"functions":{"total":13,"covered":3,"skipped":0,"pct":23.08},"statements":{"total":141,"covered":36,"skipped":0,"pct":25.53},"branches":{"total":32,"covered":0,"skipped":0,"pct":0}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-opensave/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-panning/ext-panning.js": {"lines":{"total":30,"covered":22,"skipped":0,"pct":73.33},"functions":{"total":7,"covered":6,"skipped":0,"pct":85.71},"statements":{"total":30,"covered":22,"skipped":0,"pct":73.33},"branches":{"total":6,"covered":2,"skipped":0,"pct":33.33}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-panning/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-polystar/ext-polystar.js": {"lines":{"total":247,"covered":232,"skipped":0,"pct":93.93},"functions":{"total":18,"covered":16,"skipped":0,"pct":88.89},"statements":{"total":256,"covered":241,"skipped":0,"pct":94.14},"branches":{"total":62,"covered":39,"skipped":0,"pct":62.9}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-polystar/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-shapes/ext-shapes.js": {"lines":{"total":76,"covered":74,"skipped":0,"pct":97.37},"functions":{"total":7,"covered":7,"skipped":0,"pct":100},"statements":{"total":80,"covered":78,"skipped":0,"pct":97.5},"branches":{"total":26,"covered":17,"skipped":0,"pct":65.38}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-shapes/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-storage/ext-storage.js": {"lines":{"total":91,"covered":59,"skipped":0,"pct":64.84},"functions":{"total":12,"covered":7,"skipped":0,"pct":58.33},"statements":{"total":91,"covered":59,"skipped":0,"pct":64.84},"branches":{"total":68,"covered":27,"skipped":0,"pct":39.71}} -,"/Users/jfhenon/Work/svgedit/src/editor/extensions/ext-storage/storageDialog.js": {"lines":{"total":60,"covered":56,"skipped":0,"pct":93.33},"functions":{"total":10,"covered":7,"skipped":0,"pct":70},"statements":{"total":62,"covered":57,"skipped":0,"pct":91.94},"branches":{"total":15,"covered":13,"skipped":0,"pct":86.67}} -,"/Users/jfhenon/Work/svgedit/src/editor/locale/lang.en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} -,"/Users/jfhenon/Work/svgedit/src/editor/panels/BottomPanel.js": {"lines":{"total":71,"covered":58,"skipped":0,"pct":81.69},"functions":{"total":22,"covered":14,"skipped":0,"pct":63.64},"statements":{"total":77,"covered":61,"skipped":0,"pct":79.22},"branches":{"total":30,"covered":16,"skipped":0,"pct":53.33}} -,"/Users/jfhenon/Work/svgedit/src/editor/panels/LayersPanel.js": {"lines":{"total":157,"covered":70,"skipped":0,"pct":44.59},"functions":{"total":27,"covered":6,"skipped":0,"pct":22.22},"statements":{"total":163,"covered":71,"skipped":0,"pct":43.56},"branches":{"total":46,"covered":6,"skipped":0,"pct":13.04}} -,"/Users/jfhenon/Work/svgedit/src/editor/panels/LeftPanel.js": {"lines":{"total":58,"covered":47,"skipped":0,"pct":81.03},"functions":{"total":20,"covered":15,"skipped":0,"pct":75},"statements":{"total":59,"covered":47,"skipped":0,"pct":79.66},"branches":{"total":32,"covered":12,"skipped":0,"pct":37.5}} -,"/Users/jfhenon/Work/svgedit/src/editor/panels/TopPanel.js": {"lines":{"total":391,"covered":296,"skipped":0,"pct":75.7},"functions":{"total":73,"covered":48,"skipped":0,"pct":65.75},"statements":{"total":411,"covered":304,"skipped":0,"pct":73.97},"branches":{"total":207,"covered":127,"skipped":0,"pct":61.35}} +{"total": {"lines":{"total":6564,"covered":3798,"skipped":0,"pct":57.86},"statements":{"total":6874,"covered":3901,"skipped":0,"pct":56.75},"functions":{"total":976,"covered":490,"skipped":0,"pct":50.2},"branches":{"total":3361,"covered":1358,"skipped":0,"pct":40.4}} +,"/Users/jean-francoishenon/Work/svgedit/packages/svgcanvas/common/browser.js": {"lines":{"total":25,"covered":24,"skipped":0,"pct":96},"functions":{"total":6,"covered":2,"skipped":0,"pct":33.33},"statements":{"total":30,"covered":25,"skipped":0,"pct":83.33},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/packages/svgcanvas/common/util.js": {"lines":{"total":90,"covered":8,"skipped":0,"pct":8.89},"functions":{"total":7,"covered":3,"skipped":0,"pct":42.86},"statements":{"total":92,"covered":10,"skipped":0,"pct":10.87},"branches":{"total":98,"covered":10,"skipped":0,"pct":10.2}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/ConfigObj.js": {"lines":{"total":101,"covered":49,"skipped":0,"pct":48.51},"functions":{"total":14,"covered":11,"skipped":0,"pct":78.57},"statements":{"total":102,"covered":49,"skipped":0,"pct":48.04},"branches":{"total":96,"covered":33,"skipped":0,"pct":34.38}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/Editor.js": {"lines":{"total":410,"covered":184,"skipped":0,"pct":44.88},"functions":{"total":102,"covered":32,"skipped":0,"pct":31.37},"statements":{"total":417,"covered":185,"skipped":0,"pct":44.36},"branches":{"total":213,"covered":68,"skipped":0,"pct":31.92}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/EditorStartup.js": {"lines":{"total":346,"covered":211,"skipped":0,"pct":60.98},"functions":{"total":51,"covered":26,"skipped":0,"pct":50.98},"statements":{"total":357,"covered":219,"skipped":0,"pct":61.34},"branches":{"total":122,"covered":38,"skipped":0,"pct":31.15}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/MainMenu.js": {"lines":{"total":111,"covered":44,"skipped":0,"pct":39.64},"functions":{"total":14,"covered":7,"skipped":0,"pct":50},"statements":{"total":111,"covered":44,"skipped":0,"pct":39.64},"branches":{"total":48,"covered":7,"skipped":0,"pct":14.58}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/Rulers.js": {"lines":{"total":119,"covered":91,"skipped":0,"pct":76.47},"functions":{"total":6,"covered":4,"skipped":0,"pct":66.67},"statements":{"total":124,"covered":94,"skipped":0,"pct":75.81},"branches":{"total":43,"covered":30,"skipped":0,"pct":69.77}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/browser-not-supported.js": {"lines":{"total":4,"covered":3,"skipped":0,"pct":75},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":4,"covered":3,"skipped":0,"pct":75},"branches":{"total":4,"covered":3,"skipped":0,"pct":75}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/contextmenu.js": {"lines":{"total":22,"covered":9,"skipped":0,"pct":40.91},"functions":{"total":8,"covered":1,"skipped":0,"pct":12.5},"statements":{"total":23,"covered":9,"skipped":0,"pct":39.13},"branches":{"total":13,"covered":0,"skipped":0,"pct":0}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/locale.js": {"lines":{"total":14,"covered":9,"skipped":0,"pct":64.29},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":14,"covered":9,"skipped":0,"pct":64.29},"branches":{"total":10,"covered":3,"skipped":0,"pct":30}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/PaintBox.js": {"lines":{"total":64,"covered":35,"skipped":0,"pct":54.69},"functions":{"total":4,"covered":4,"skipped":0,"pct":100},"statements":{"total":67,"covered":35,"skipped":0,"pct":52.24},"branches":{"total":33,"covered":12,"skipped":0,"pct":36.36}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/index.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seButton.js": {"lines":{"total":57,"covered":40,"skipped":0,"pct":70.18},"functions":{"total":15,"covered":7,"skipped":0,"pct":46.67},"statements":{"total":60,"covered":42,"skipped":0,"pct":70},"branches":{"total":33,"covered":23,"skipped":0,"pct":69.7}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seColorPicker.js": {"lines":{"total":50,"covered":41,"skipped":0,"pct":82},"functions":{"total":16,"covered":11,"skipped":0,"pct":68.75},"statements":{"total":51,"covered":41,"skipped":0,"pct":80.39},"branches":{"total":11,"covered":8,"skipped":0,"pct":72.73}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seExplorerButton.js": {"lines":{"total":95,"covered":72,"skipped":0,"pct":75.79},"functions":{"total":17,"covered":10,"skipped":0,"pct":58.82},"statements":{"total":98,"covered":72,"skipped":0,"pct":73.47},"branches":{"total":30,"covered":17,"skipped":0,"pct":56.67}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seFlyingButton.js": {"lines":{"total":74,"covered":47,"skipped":0,"pct":63.51},"functions":{"total":14,"covered":9,"skipped":0,"pct":64.29},"statements":{"total":75,"covered":48,"skipped":0,"pct":64},"branches":{"total":29,"covered":12,"skipped":0,"pct":41.38}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seInput.js": {"lines":{"total":46,"covered":31,"skipped":0,"pct":67.39},"functions":{"total":16,"covered":8,"skipped":0,"pct":50},"statements":{"total":47,"covered":31,"skipped":0,"pct":65.96},"branches":{"total":8,"covered":4,"skipped":0,"pct":50}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seList.js": {"lines":{"total":87,"covered":59,"skipped":0,"pct":67.82},"functions":{"total":21,"covered":10,"skipped":0,"pct":47.62},"statements":{"total":89,"covered":61,"skipped":0,"pct":68.54},"branches":{"total":22,"covered":12,"skipped":0,"pct":54.55}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seListItem.js": {"lines":{"total":39,"covered":26,"skipped":0,"pct":66.67},"functions":{"total":12,"covered":4,"skipped":0,"pct":33.33},"statements":{"total":40,"covered":27,"skipped":0,"pct":67.5},"branches":{"total":10,"covered":8,"skipped":0,"pct":80}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seMenu.js": {"lines":{"total":26,"covered":20,"skipped":0,"pct":76.92},"functions":{"total":7,"covered":3,"skipped":0,"pct":42.86},"statements":{"total":27,"covered":20,"skipped":0,"pct":74.07},"branches":{"total":5,"covered":3,"skipped":0,"pct":60}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seMenuItem.js": {"lines":{"total":37,"covered":26,"skipped":0,"pct":70.27},"functions":{"total":9,"covered":5,"skipped":0,"pct":55.56},"statements":{"total":40,"covered":27,"skipped":0,"pct":67.5},"branches":{"total":19,"covered":8,"skipped":0,"pct":42.11}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/sePalette.js": {"lines":{"total":33,"covered":32,"skipped":0,"pct":96.97},"functions":{"total":7,"covered":7,"skipped":0,"pct":100},"statements":{"total":33,"covered":32,"skipped":0,"pct":96.97},"branches":{"total":13,"covered":10,"skipped":0,"pct":76.92}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/sePlainBorderButton.js": {"lines":{"total":3,"covered":3,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":3,"covered":3,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/sePlainMenuButton.js": {"lines":{"total":2,"covered":2,"skipped":0,"pct":100},"functions":{"total":1,"covered":1,"skipped":0,"pct":100},"statements":{"total":2,"covered":2,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seSelect.js": {"lines":{"total":56,"covered":45,"skipped":0,"pct":80.36},"functions":{"total":17,"covered":9,"skipped":0,"pct":52.94},"statements":{"total":59,"covered":47,"skipped":0,"pct":79.66},"branches":{"total":16,"covered":14,"skipped":0,"pct":87.5}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seSpinInput.js": {"lines":{"total":65,"covered":55,"skipped":0,"pct":84.62},"functions":{"total":18,"covered":10,"skipped":0,"pct":55.56},"statements":{"total":66,"covered":55,"skipped":0,"pct":83.33},"branches":{"total":17,"covered":13,"skipped":0,"pct":76.47}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seText.js": {"lines":{"total":28,"covered":18,"skipped":0,"pct":64.29},"functions":{"total":10,"covered":4,"skipped":0,"pct":40},"statements":{"total":29,"covered":18,"skipped":0,"pct":62.07},"branches":{"total":8,"covered":5,"skipped":0,"pct":62.5}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/seZoom.js": {"lines":{"total":102,"covered":65,"skipped":0,"pct":63.73},"functions":{"total":28,"covered":9,"skipped":0,"pct":32.14},"statements":{"total":107,"covered":66,"skipped":0,"pct":61.68},"branches":{"total":36,"covered":7,"skipped":0,"pct":19.44}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/jgraduate/ColorValuePicker.js": {"lines":{"total":231,"covered":54,"skipped":0,"pct":23.38},"functions":{"total":9,"covered":3,"skipped":0,"pct":33.33},"statements":{"total":249,"covered":63,"skipped":0,"pct":25.3},"branches":{"total":157,"covered":34,"skipped":0,"pct":21.66}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/jgraduate/Slider.js": {"lines":{"total":156,"covered":91,"skipped":0,"pct":58.33},"functions":{"total":17,"covered":9,"skipped":0,"pct":52.94},"statements":{"total":176,"covered":96,"skipped":0,"pct":54.55},"branches":{"total":186,"covered":97,"skipped":0,"pct":52.15}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/jgraduate/jQuery.jGraduate.js": {"lines":{"total":580,"covered":275,"skipped":0,"pct":47.41},"functions":{"total":44,"covered":14,"skipped":0,"pct":31.82},"statements":{"total":602,"covered":282,"skipped":0,"pct":46.84},"branches":{"total":278,"covered":100,"skipped":0,"pct":35.97}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/components/jgraduate/jQuery.jPicker.js": {"lines":{"total":840,"covered":449,"skipped":0,"pct":53.45},"functions":{"total":60,"covered":39,"skipped":0,"pct":65},"statements":{"total":926,"covered":475,"skipped":0,"pct":51.3},"branches":{"total":777,"covered":327,"skipped":0,"pct":42.08}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/SePlainAlertDialog.js": {"lines":{"total":7,"covered":1,"skipped":0,"pct":14.29},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":7,"covered":1,"skipped":0,"pct":14.29},"branches":{"total":2,"covered":0,"skipped":0,"pct":0}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/cmenuDialog.js": {"lines":{"total":120,"covered":112,"skipped":0,"pct":93.33},"functions":{"total":28,"covered":15,"skipped":0,"pct":53.57},"statements":{"total":131,"covered":116,"skipped":0,"pct":88.55},"branches":{"total":23,"covered":19,"skipped":0,"pct":82.61}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/cmenuLayersDialog.js": {"lines":{"total":61,"covered":49,"skipped":0,"pct":80.33},"functions":{"total":16,"covered":6,"skipped":0,"pct":37.5},"statements":{"total":66,"covered":49,"skipped":0,"pct":74.24},"branches":{"total":18,"covered":13,"skipped":0,"pct":72.22}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/editorPreferencesDialog.js": {"lines":{"total":157,"covered":124,"skipped":0,"pct":78.98},"functions":{"total":30,"covered":9,"skipped":0,"pct":30},"statements":{"total":159,"covered":125,"skipped":0,"pct":78.62},"branches":{"total":46,"covered":34,"skipped":0,"pct":73.91}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/exportDialog.js": {"lines":{"total":55,"covered":39,"skipped":0,"pct":70.91},"functions":{"total":14,"covered":5,"skipped":0,"pct":35.71},"statements":{"total":58,"covered":39,"skipped":0,"pct":67.24},"branches":{"total":12,"covered":6,"skipped":0,"pct":50}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/imagePropertiesDialog.js": {"lines":{"total":161,"covered":90,"skipped":0,"pct":55.9},"functions":{"total":20,"covered":5,"skipped":0,"pct":25},"statements":{"total":162,"covered":90,"skipped":0,"pct":55.56},"branches":{"total":53,"covered":23,"skipped":0,"pct":43.4}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/index.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/seAlertDialog.js": {"lines":{"total":6,"covered":2,"skipped":0,"pct":33.33},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":6,"covered":2,"skipped":0,"pct":33.33},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/seConfirmDialog.js": {"lines":{"total":8,"covered":2,"skipped":0,"pct":25},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":8,"covered":2,"skipped":0,"pct":25},"branches":{"total":2,"covered":0,"skipped":0,"pct":0}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/sePromptDialog.js": {"lines":{"total":24,"covered":5,"skipped":0,"pct":20.83},"functions":{"total":7,"covered":2,"skipped":0,"pct":28.57},"statements":{"total":24,"covered":5,"skipped":0,"pct":20.83},"branches":{"total":9,"covered":0,"skipped":0,"pct":0}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/seSelectDialog.js": {"lines":{"total":8,"covered":2,"skipped":0,"pct":25},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":8,"covered":2,"skipped":0,"pct":25},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/svgSourceDialog.js": {"lines":{"total":74,"covered":56,"skipped":0,"pct":75.68},"functions":{"total":17,"covered":6,"skipped":0,"pct":35.29},"statements":{"total":75,"covered":56,"skipped":0,"pct":74.67},"branches":{"total":17,"covered":13,"skipped":0,"pct":76.47}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/se-elix/define/NumberSpinBox.js": {"lines":{"total":1,"covered":1,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":1,"covered":1,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/se-elix/src/base/NumberSpinBox.js": {"lines":{"total":54,"covered":52,"skipped":0,"pct":96.3},"functions":{"total":13,"covered":11,"skipped":0,"pct":84.62},"statements":{"total":54,"covered":52,"skipped":0,"pct":96.3},"branches":{"total":53,"covered":47,"skipped":0,"pct":88.68}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/dialogs/se-elix/src/plain/PlainNumberSpinBox.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-connector/ext-connector.js": {"lines":{"total":287,"covered":83,"skipped":0,"pct":28.92},"functions":{"total":26,"covered":16,"skipped":0,"pct":61.54},"statements":{"total":295,"covered":86,"skipped":0,"pct":29.15},"branches":{"total":140,"covered":19,"skipped":0,"pct":13.57}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-connector/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-eyedropper/ext-eyedropper.js": {"lines":{"total":60,"covered":39,"skipped":0,"pct":65},"functions":{"total":8,"covered":5,"skipped":0,"pct":62.5},"statements":{"total":71,"covered":39,"skipped":0,"pct":54.93},"branches":{"total":39,"covered":14,"skipped":0,"pct":35.9}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-eyedropper/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-grid/ext-grid.js": {"lines":{"total":88,"covered":43,"skipped":0,"pct":48.86},"functions":{"total":8,"covered":4,"skipped":0,"pct":50},"statements":{"total":90,"covered":43,"skipped":0,"pct":47.78},"branches":{"total":10,"covered":4,"skipped":0,"pct":40}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-grid/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-markers/ext-markers.js": {"lines":{"total":149,"covered":46,"skipped":0,"pct":30.87},"functions":{"total":21,"covered":12,"skipped":0,"pct":57.14},"statements":{"total":164,"covered":48,"skipped":0,"pct":29.27},"branches":{"total":80,"covered":22,"skipped":0,"pct":27.5}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-opensave/ext-opensave.js": {"lines":{"total":136,"covered":36,"skipped":0,"pct":26.47},"functions":{"total":13,"covered":3,"skipped":0,"pct":23.08},"statements":{"total":141,"covered":36,"skipped":0,"pct":25.53},"branches":{"total":32,"covered":0,"skipped":0,"pct":0}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-opensave/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-panning/ext-panning.js": {"lines":{"total":30,"covered":22,"skipped":0,"pct":73.33},"functions":{"total":7,"covered":6,"skipped":0,"pct":85.71},"statements":{"total":30,"covered":22,"skipped":0,"pct":73.33},"branches":{"total":6,"covered":2,"skipped":0,"pct":33.33}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-panning/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-polystar/ext-polystar.js": {"lines":{"total":247,"covered":232,"skipped":0,"pct":93.93},"functions":{"total":18,"covered":16,"skipped":0,"pct":88.89},"statements":{"total":256,"covered":241,"skipped":0,"pct":94.14},"branches":{"total":62,"covered":39,"skipped":0,"pct":62.9}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-polystar/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-shapes/ext-shapes.js": {"lines":{"total":76,"covered":74,"skipped":0,"pct":97.37},"functions":{"total":7,"covered":7,"skipped":0,"pct":100},"statements":{"total":80,"covered":78,"skipped":0,"pct":97.5},"branches":{"total":26,"covered":17,"skipped":0,"pct":65.38}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-shapes/locale/en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-storage/ext-storage.js": {"lines":{"total":91,"covered":59,"skipped":0,"pct":64.84},"functions":{"total":12,"covered":7,"skipped":0,"pct":58.33},"statements":{"total":91,"covered":59,"skipped":0,"pct":64.84},"branches":{"total":68,"covered":26,"skipped":0,"pct":38.24}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/extensions/ext-storage/storageDialog.js": {"lines":{"total":60,"covered":56,"skipped":0,"pct":93.33},"functions":{"total":10,"covered":7,"skipped":0,"pct":70},"statements":{"total":62,"covered":57,"skipped":0,"pct":91.94},"branches":{"total":15,"covered":13,"skipped":0,"pct":86.67}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/locale/lang.en.js": {"lines":{"total":0,"covered":0,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":0,"covered":0,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/panels/BottomPanel.js": {"lines":{"total":71,"covered":56,"skipped":0,"pct":78.87},"functions":{"total":22,"covered":14,"skipped":0,"pct":63.64},"statements":{"total":77,"covered":59,"skipped":0,"pct":76.62},"branches":{"total":30,"covered":12,"skipped":0,"pct":40}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/panels/LayersPanel.js": {"lines":{"total":157,"covered":70,"skipped":0,"pct":44.59},"functions":{"total":27,"covered":6,"skipped":0,"pct":22.22},"statements":{"total":163,"covered":71,"skipped":0,"pct":43.56},"branches":{"total":46,"covered":6,"skipped":0,"pct":13.04}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/panels/LeftPanel.js": {"lines":{"total":58,"covered":47,"skipped":0,"pct":81.03},"functions":{"total":20,"covered":15,"skipped":0,"pct":75},"statements":{"total":59,"covered":47,"skipped":0,"pct":79.66},"branches":{"total":32,"covered":12,"skipped":0,"pct":37.5}} +,"/Users/jean-francoishenon/Work/svgedit/src/editor/panels/TopPanel.js": {"lines":{"total":405,"covered":282,"skipped":0,"pct":69.63},"functions":{"total":74,"covered":47,"skipped":0,"pct":63.51},"statements":{"total":416,"covered":285,"skipped":0,"pct":68.51},"branches":{"total":205,"covered":111,"skipped":0,"pct":54.15}} } diff --git a/cypress/__svgSnapshots__/scenario6-check tool_polygon_align_to_page.svg b/cypress/__svgSnapshots__/scenario6-check tool_polygon_align_to_page.svg index 363dc28dc..9ad2cee9b 100644 --- a/cypress/__svgSnapshots__/scenario6-check tool_polygon_align_to_page.svg +++ b/cypress/__svgSnapshots__/scenario6-check tool_polygon_align_to_page.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_sides.svg b/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_sides.svg index a6d115807..d2f2a6c82 100644 --- a/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_sides.svg +++ b/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_sides.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stoke_fill_color.svg b/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stoke_fill_color.svg index 26d2d66e7..2301417e7 100644 --- a/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stoke_fill_color.svg +++ b/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stoke_fill_color.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stroke_width.svg b/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stroke_width.svg index d630c8a61..ecd4dd638 100644 --- a/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stroke_width.svg +++ b/cypress/__svgSnapshots__/scenario6-check tool_polygon_change_stroke_width.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario7-check tool_star_align_to_page.svg b/cypress/__svgSnapshots__/scenario7-check tool_star_align_to_page.svg index 489e3f5ff..e43174625 100644 --- a/cypress/__svgSnapshots__/scenario7-check tool_star_align_to_page.svg +++ b/cypress/__svgSnapshots__/scenario7-check tool_star_align_to_page.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario7-check tool_star_change_sides.svg b/cypress/__svgSnapshots__/scenario7-check tool_star_change_sides.svg index 7dd9fc9fd..cb997fc8e 100644 --- a/cypress/__svgSnapshots__/scenario7-check tool_star_change_sides.svg +++ b/cypress/__svgSnapshots__/scenario7-check tool_star_change_sides.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario7-check tool_star_change_stoke_fill_color.svg b/cypress/__svgSnapshots__/scenario7-check tool_star_change_stoke_fill_color.svg index 6da95a839..cc488d112 100644 --- a/cypress/__svgSnapshots__/scenario7-check tool_star_change_stoke_fill_color.svg +++ b/cypress/__svgSnapshots__/scenario7-check tool_star_change_stoke_fill_color.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/__svgSnapshots__/scenario7-check tool_star_change_stroke_width.svg b/cypress/__svgSnapshots__/scenario7-check tool_star_change_stroke_width.svg index 6a4d21ec7..774fc2cc7 100644 --- a/cypress/__svgSnapshots__/scenario7-check tool_star_change_stroke_width.svg +++ b/cypress/__svgSnapshots__/scenario7-check tool_star_change_stroke_width.svg @@ -1,7 +1,7 @@ Layer 1 - + \ No newline at end of file diff --git a/cypress/e2e/unit/utilities-bbox.cy.js b/cypress/e2e/unit/utilities-bbox.cy.js index 9e5913d87..0e181c857 100644 --- a/cypress/e2e/unit/utilities-bbox.cy.js +++ b/cypress/e2e/unit/utilities-bbox.cy.js @@ -5,6 +5,7 @@ import * as utilities from '../../../packages/svgcanvas/core/utilities.js' import * as math from '../../../packages/svgcanvas/core/math.js' import * as path from '../../../packages/svgcanvas/core/path.js' import setAssertionMethods from '../../support/assert-close.js' +import * as units from '../../../packages/svgcanvas/core/units.js' // eslint-disable-next-line chai.use(setAssertionMethods) @@ -94,6 +95,7 @@ describe('utilities bbox', function () { } path.init(mockSvgCanvas) + units.init({ getRoundDigits: () => 2 }) // mock getRoundDigits mockaddSVGElementsFromJsonCallCount = 0 }) diff --git a/cypress/e2e/unit/utilities-performance.cy.js b/cypress/e2e/unit/utilities-performance.cy.js index 72a3c10f4..66e721810 100644 --- a/cypress/e2e/unit/utilities-performance.cy.js +++ b/cypress/e2e/unit/utilities-performance.cy.js @@ -4,9 +4,12 @@ import 'pathseg' import { NS } from '../../../packages/svgcanvas/core/namespaces.js' import * as utilities from '../../../packages/svgcanvas/core/utilities.js' import * as math from '../../../packages/svgcanvas/core/math.js' +import * as units from '../../../packages/svgcanvas/core/units.js' + describe('utilities performance', function () { let currentLayer; let groupWithMatrixTransform; let textWithMatrixTransform + units.init({ getRoundDigits: () => 2 }) // mock getRoundDigits beforeEach(() => { document.body.textContent = '' const style = document.createElement('style') diff --git a/packages/svgcanvas/core/undo.js b/packages/svgcanvas/core/undo.js index f822b27c2..fe8206bc6 100644 --- a/packages/svgcanvas/core/undo.js +++ b/packages/svgcanvas/core/undo.js @@ -169,7 +169,6 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => { const bbox = getStrokedBBoxDefaultVisible([elem]) const diffX = attr === 'x' ? parseFloat(newValue) - bbox.x : 0 const diffY = attr === 'y' ? parseFloat(newValue) - bbox.y : 0 - console.log('diffX, diffY', diffX, diffY) svgCanvas.moveSelectedElements(diffX * zoom, diffY * zoom, true) continue } @@ -190,7 +189,7 @@ export const changeSelectedAttributeNoUndoMethod = (attr, newValue, elems) => { } else if (attr === '#href') { setHref(elem, newValue) } else if (newValue) { - elem.setAttribute(attr, parseFloat(newValue)) + elem.setAttribute(attr, isNaN(parseFloat(newValue)) ? newValue : parseFloat(newValue)) } else if (typeof newValue === 'number') { elem.setAttribute(attr, newValue) } else { diff --git a/packages/svgcanvas/core/utilities.js b/packages/svgcanvas/core/utilities.js index fa953f8da..9be88f32c 100644 --- a/packages/svgcanvas/core/utilities.js +++ b/packages/svgcanvas/core/utilities.js @@ -7,14 +7,18 @@ */ import { NS } from './namespaces.js' -import { setUnitAttr, getTypeMap } from './units.js' +import { setUnitAttr, getTypeMap, shortFloat } from './units.js' import { - hasMatrixTransform, transformListToTransform, transformBox, getTransformList + hasMatrixTransform, + transformListToTransform, + transformBox, + getTransformList } from './math.js' import { getClosest, mergeDeep } from '../common/util.js' // Much faster than running getBBox() every time -const visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use,clipPath' +const visElems = + 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use,clipPath' const visElemsArr = visElems.split(',') // const hidElems = 'defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath'; @@ -22,15 +26,15 @@ let svgCanvas = null let svgroot_ = null /** -* Object with the following keys/values. -* @typedef {PlainObject} module:utilities.SVGElementJSON -* @property {string} element - Tag name of the SVG element to create -* @property {PlainObject} attr - Has key-value attributes to assign to the new element. -* An `id` should be set so that {@link module:utilities.EditorContext#addSVGElementsFromJson} can later re-identify the element for modification or replacement. -* @property {boolean} [curStyles=false] - Indicates whether current style attributes should be applied first -* @property {module:utilities.SVGElementJSON[]} [children] - Data objects to be added recursively as children -* @property {string} [namespace="http://www.w3.org/2000/svg"] - Indicate a (non-SVG) namespace -*/ + * Object with the following keys/values. + * @typedef {PlainObject} module:utilities.SVGElementJSON + * @property {string} element - Tag name of the SVG element to create + * @property {PlainObject} attr - Has key-value attributes to assign to the new element. + * An `id` should be set so that {@link module:utilities.EditorContext#addSVGElementsFromJson} can later re-identify the element for modification or replacement. + * @property {boolean} [curStyles=false] - Indicates whether current style attributes should be applied first + * @property {module:utilities.SVGElementJSON[]} [children] - Data objects to be added recursively as children + * @property {string} [namespace="http://www.w3.org/2000/svg"] - Indicate a (non-SVG) namespace + */ /** * An object that creates SVG elements for the canvas. @@ -49,38 +53,38 @@ let svgroot_ = null * @function module:utilities.EditorContext#addSVGElementsFromJson * @param {module:utilities.SVGElementJSON} data * @returns {Element} The new element -*/ + */ /** * @function module:utilities.EditorContext#getSelectedElements * @returns {Element[]} the array with selected DOM elements -*/ + */ /** * @function module:utilities.EditorContext#getDOMDocument * @returns {HTMLDocument} -*/ + */ /** * @function module:utilities.EditorContext#getDOMContainer * @returns {HTMLElement} -*/ + */ /** * @function module:utilities.EditorContext#getSvgRoot * @returns {SVGSVGElement} -*/ + */ /** * @function module:utilities.EditorContext#getBaseUnit * @returns {string} -*/ + */ /** * @function module:utilities.EditorContext#getSnappingStep * @returns {Float|string} -*/ + */ /** -* @function module:utilities.init -* @param {module:utilities.EditorContext} canvas -* @returns {void} -*/ -export const init = (canvas) => { + * @function module:utilities.init + * @param {module:utilities.EditorContext} canvas + * @returns {void} + */ +export const init = canvas => { svgCanvas = canvas svgroot_ = canvas.getSvgRoot() } @@ -92,19 +96,19 @@ export const init = (canvas) => { * @returns {string} The string with entity declarations in the internal subset removed * @todo This might be needed in other places `parseFromString` is used even without LGTM flagging */ -export const dropXMLInternalSubset = (str) => { +export const dropXMLInternalSubset = str => { return str.replace(/()/, '$1$2') // return str.replace(/(?\?\]>)/, '$$'); } /** -* Converts characters in a string to XML-friendly entities. -* @function module:utilities.toXml -* @example `&` becomes `&` -* @param {string} str - The string to be converted -* @returns {string} The converted string -*/ -export const toXml = (str) => { + * Converts characters in a string to XML-friendly entities. + * @function module:utilities.toXml + * @example `&` becomes `&` + * @param {string} str - The string to be converted + * @returns {string} The converted string + */ +export const toXml = str => { // ' is ok in XML, but not HTML // > does not normally need escaping, though it can if within a CDATA expression (and preceded by "]]") return str @@ -123,11 +127,11 @@ export const toXml = (str) => { // also precalculate the size of the array needed. /** -* Converts a string to base64. -* @function module:utilities.encode64 -* @param {string} input -* @returns {string} Base64 output -*/ + * Converts a string to base64. + * @function module:utilities.encode64 + * @param {string} input + * @returns {string} Base64 output + */ export function encode64 (input) { // base64 strings are 4/3 larger than the original string input = encodeUTF8(input) // convert non-ASCII characters @@ -135,11 +139,11 @@ export function encode64 (input) { } /** -* Converts a string from base64. -* @function module:utilities.decode64 -* @param {string} input Base64-encoded input -* @returns {string} Decoded output -*/ + * Converts a string from base64. + * @function module:utilities.decode64 + * @param {string} input Base64-encoded input + * @returns {string} Decoded output + */ export function decode64 (input) { return decodeUTF8(window.atob(input)) } @@ -155,28 +159,28 @@ export function hashCode (word) { if (word.length === 0) return hash for (let i = 0; i < word.length; i++) { chr = word.charCodeAt(i) - hash = ((hash << 5) - hash) + chr + hash = (hash << 5) - hash + chr hash |= 0 // Convert to 32bit integer } return hash } /** -* @function module:utilities.decodeUTF8 -* @param {string} argString -* @returns {string} -*/ + * @function module:utilities.decodeUTF8 + * @param {string} argString + * @returns {string} + */ export function decodeUTF8 (argString) { return decodeURIComponent(escape(argString)) } // codedread:does not seem to work with webkit-based browsers on OSX // Brettz9: please test again as function upgraded /** -* @function module:utilities.encodeUTF8 -* @param {string} argString -* @returns {string} -*/ -export const encodeUTF8 = (argString) => { + * @function module:utilities.encodeUTF8 + * @param {string} argString + * @returns {string} + */ +export const encodeUTF8 = argString => { return unescape(encodeURIComponent(argString)) } @@ -186,8 +190,13 @@ export const encodeUTF8 = (argString) => { * @param {string} dataurl * @returns {string} object URL or empty string */ -export const dataURLToObjectURL = (dataurl) => { - if (typeof Uint8Array === 'undefined' || typeof Blob === 'undefined' || typeof URL === 'undefined' || !URL.createObjectURL) { +export const dataURLToObjectURL = dataurl => { + if ( + typeof Uint8Array === 'undefined' || + typeof Blob === 'undefined' || + typeof URL === 'undefined' || + !URL.createObjectURL + ) { return '' } const arr = dataurl.split(',') @@ -213,7 +222,7 @@ export const dataURLToObjectURL = (dataurl) => { * @param {Blob} blob A Blob object or File object * @returns {string} object URL or empty string */ -export const createObjectURL = (blob) => { +export const createObjectURL = blob => { if (!blob || typeof URL === 'undefined' || !URL.createObjectURL) { return '' } @@ -227,39 +236,43 @@ export const blankPageObjectURL = (() => { if (typeof Blob === 'undefined') { return '' } - const blob = new Blob(['SVG-edit '], { type: 'text/html' }) + const blob = new Blob( + ['SVG-edit '], + { type: 'text/html' } + ) return createObjectURL(blob) })() /** -* Converts a string to use XML references (for non-ASCII). -* @function module:utilities.convertToXMLReferences -* @param {string} input -* @returns {string} Decimal numeric character references -*/ -export const convertToXMLReferences = (input) => { - let output = ''; - [...input].forEach((ch) => { + * Converts a string to use XML references (for non-ASCII). + * @function module:utilities.convertToXMLReferences + * @param {string} input + * @returns {string} Decimal numeric character references + */ +export const convertToXMLReferences = input => { + let output = '' + ;[...input].forEach(ch => { const c = ch.charCodeAt() - output += (c <= 127) ? ch : `&#${c};` + output += c <= 127 ? ch : `&#${c};` }) return output } /** -* Cross-browser compatible method of converting a string to an XML tree. -* Found this function [here]{@link http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f}. -* @function module:utilities.text2xml -* @param {string} sXML -* @throws {Error} -* @returns {XMLDocument} -*/ -export const text2xml = (sXML) => { + * Cross-browser compatible method of converting a string to an XML tree. + * Found this function [here]{@link http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f}. + * @function module:utilities.text2xml + * @param {string} sXML + * @throws {Error} + * @returns {XMLDocument} + */ +export const text2xml = sXML => { if (sXML.includes(' { } try { out = dXML.parseFromString(sXML, 'text/xml') - } catch (e2) { throw new Error('Error parsing XML string') } + } catch (e2) { + throw new Error('Error parsing XML string') + } return out } /** -* @typedef {PlainObject} module:utilities.BBoxObject (like `DOMRect`) -* @property {Float} x -* @property {Float} y -* @property {Float} width -* @property {Float} height -*/ + * @typedef {PlainObject} module:utilities.BBoxObject (like `DOMRect`) + * @property {Float} x + * @property {Float} y + * @property {Float} width + * @property {Float} height + */ /** -* Converts a `SVGRect` into an object. -* @function module:utilities.bboxToObj -* @param {SVGRect} bbox - a SVGRect -* @returns {module:utilities.BBoxObject} An object with properties names x, y, width, height. -*/ + * Converts a `SVGRect` into an object. + * @function module:utilities.bboxToObj + * @param {SVGRect} bbox - a SVGRect + * @returns {module:utilities.BBoxObject} An object with properties names x, y, width, height. + */ export const bboxToObj = ({ x, y, width, height }) => { return { x, y, width, height } } /** -* @callback module:utilities.TreeWalker -* @param {Element} elem - DOM element being traversed -* @returns {void} -*/ + * @callback module:utilities.TreeWalker + * @param {Element} elem - DOM element being traversed + * @returns {void} + */ /** -* Walks the tree and executes the callback on each element in a top-down fashion. -* @function module:utilities.walkTree -* @param {Element} elem - DOM element to traverse -* @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element -* @returns {void} -*/ + * Walks the tree and executes the callback on each element in a top-down fashion. + * @function module:utilities.walkTree + * @param {Element} elem - DOM element to traverse + * @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element + * @returns {void} + */ export const walkTree = (elem, cbFn) => { if (elem?.nodeType === 1) { cbFn(elem) @@ -314,13 +329,13 @@ export const walkTree = (elem, cbFn) => { } /** -* Walks the tree and executes the callback on each element in a depth-first fashion. -* @function module:utilities.walkTreePost -* @todo Shouldn't this be calling walkTreePost? -* @param {Element} elem - DOM element to traverse -* @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element -* @returns {void} -*/ + * Walks the tree and executes the callback on each element in a depth-first fashion. + * @function module:utilities.walkTreePost + * @todo Shouldn't this be calling walkTreePost? + * @param {Element} elem - DOM element to traverse + * @param {module:utilities.TreeWalker} cbFn - Callback function to run on each element + * @returns {void} + */ export const walkTreePost = (elem, cbFn) => { if (elem?.nodeType === 1) { let i = elem.childNodes.length @@ -332,15 +347,15 @@ export const walkTreePost = (elem, cbFn) => { } /** -* Extracts the URL from the `url(...)` syntax of some attributes. -* Three variants: -* - `` -* - `` -* - `` -* @function module:utilities.getUrlFromAttr -* @param {string} attrVal The attribute value as a string -* @returns {string} String with just the URL, like "someFile.svg#foo" -*/ + * Extracts the URL from the `url(...)` syntax of some attributes. + * Three variants: + * - `` + * - `` + * - `` + * @function module:utilities.getUrlFromAttr + * @param {string} attrVal The attribute value as a string + * @returns {string} String with just the URL, like "someFile.svg#foo" + */ export const getUrlFromAttr = function (attrVal) { if (attrVal) { // url('#somegrad') @@ -359,29 +374,29 @@ export const getUrlFromAttr = function (attrVal) { } /** -* @function module:utilities.getHref -* @param {Element} elem -* @returns {string} The given element's `xlink:href` value -*/ + * @function module:utilities.getHref + * @param {Element} elem + * @returns {string} The given element's `xlink:href` value + */ export let getHref = function (elem) { return elem.getAttributeNS(NS.XLINK, 'href') } /** -* Sets the given element's `xlink:href` value. -* @function module:utilities.setHref -* @param {Element} elem -* @param {string} val -* @returns {void} -*/ + * Sets the given element's `xlink:href` value. + * @function module:utilities.setHref + * @param {Element} elem + * @param {string} val + * @returns {void} + */ export let setHref = function (elem, val) { elem.setAttributeNS(NS.XLINK, 'xlink:href', val) } /** -* @function module:utilities.findDefs -* @returns {SVGDefsElement} The document's `` element, creating it first if necessary -*/ + * @function module:utilities.findDefs + * @returns {SVGDefsElement} The document's `` element, creating it first if necessary + */ export const findDefs = function () { const svgElement = svgCanvas.getSvgContent() let defs = svgElement.getElementsByTagNameNS(NS.SVG, 'defs') @@ -403,12 +418,12 @@ export const findDefs = function () { // TODO(codedread): Consider moving the next to functions to bbox.js /** -* Get correct BBox for a path in Webkit. -* Converted from code found [here]{@link http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html}. -* @function module:utilities.getPathBBox -* @param {SVGPathElement} path - The path DOM element to get the BBox for -* @returns {module:utilities.BBoxObject} A BBox-like object -*/ + * Get correct BBox for a path in Webkit. + * Converted from code found [here]{@link http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html}. + * @function module:utilities.getPathBBox + * @param {SVGPathElement} path - The path DOM element to get the BBox for + * @returns {module:utilities.BBoxObject} A BBox-like object + */ export const getPathBBox = function (path) { const seglist = path.pathSegList const tot = seglist.numberOfItems @@ -419,17 +434,23 @@ export const getPathBBox = function (path) { const getCalc = function (j, P1, P2, P3) { return function (t) { - return 1 - t ** 3 * P0[j] + - 3 * 1 - t ** 2 * t * P1[j] + + return ( + 1 - + t ** 3 * P0[j] + + 3 * 1 - + t ** 2 * t * P1[j] + 3 * (1 - t) * t ** 2 * P2[j] + t ** 3 * P3[j] + ) } } for (let i = 0; i < tot; i++) { const seg = seglist.getItem(i) - if (seg.x === undefined) { continue } + if (seg.x === undefined) { + continue + } // Add actual points to limits bounds[0].push(P0[0]) @@ -448,7 +469,9 @@ export const getPathBBox = function (path) { const c = 3 * P1[j] - 3 * P0[j] if (a === 0) { - if (b === 0) { continue } + if (b === 0) { + continue + } const t = -c / b if (t > 0 && t < 1) { bounds[j].push(calc(t)) @@ -456,11 +479,17 @@ export const getPathBBox = function (path) { continue } const b2ac = b ** 2 - 4 * c * a - if (b2ac < 0) { continue } + if (b2ac < 0) { + continue + } const t1 = (-b + Math.sqrt(b2ac)) / (2 * a) - if (t1 > 0 && t1 < 1) { bounds[j].push(calc(t1)) } + if (t1 > 0 && t1 < 1) { + bounds[j].push(calc(t1)) + } const t2 = (-b - Math.sqrt(b2ac)) / (2 * a) - if (t2 > 0 && t2 < 1) { bounds[j].push(calc(t2)) } + if (t2 > 0 && t2 < 1) { + bounds[j].push(calc(t2)) + } } P0 = P3 } else { @@ -482,15 +511,17 @@ export const getPathBBox = function (path) { } /** -* Get the given/selected element's bounding box object, convert it to be more -* usable when necessary. -* @function module:utilities.getBBox -* @param {Element} elem - Optional DOM element to get the BBox for -* @returns {module:utilities.BBoxObject} Bounding box object -*/ + * Get the given/selected element's bounding box object, convert it to be more + * usable when necessary. + * @function module:utilities.getBBox + * @param {Element} elem - Optional DOM element to get the BBox for + * @returns {module:utilities.BBoxObject} Bounding box object + */ export const getBBox = function (elem) { const selected = elem || svgCanvas.getSelectedElements()[0] - if (elem.nodeType !== 1) { return null } + if (elem.nodeType !== 1) { + return null + } const elname = selected.nodeName let ret = null @@ -512,7 +543,6 @@ export const getBBox = function (elem) { } break default: - if (elname === 'use') { ret = selected.getBBox() // , true); } else if (visElemsArr.includes(elname)) { @@ -520,8 +550,8 @@ export const getBBox = function (elem) { try { ret = selected.getBBox() } catch (err) { - // tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268 - // Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835 + // tspan (and textPath apparently) have no `getBBox` in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=937268 + // Re: Chrome returning bbox for containing text element, see: https://bugs.chromium.org/p/chromium/issues/detail?id=349835 const extent = selected.getExtentOfChar(0) // pos+dimensions of the first glyph const width = selected.getComputedTextLength() // width of the tspan ret = { @@ -532,7 +562,7 @@ export const getBBox = function (elem) { } } } else { - // Check if element is child of a foreignObject + // Check if element is child of a foreignObject const fo = getClosest(selected.parentNode, 'foreignObject') if (fo.length && fo[0].getBBox) { ret = fo[0].getBBox() @@ -549,26 +579,26 @@ export const getBBox = function (elem) { } /** -* @typedef {GenericArray} module:utilities.PathSegmentArray -* @property {Integer} length 2 -* @property {"M"|"L"|"C"|"Z"} 0 -* @property {Float[]} 1 -*/ + * @typedef {GenericArray} module:utilities.PathSegmentArray + * @property {Integer} length 2 + * @property {"M"|"L"|"C"|"Z"} 0 + * @property {Float[]} 1 + */ /** -* Create a path 'd' attribute from path segments. -* Each segment is an array of the form: `[singleChar, [x,y, x,y, ...]]` -* @function module:utilities.getPathDFromSegments -* @param {module:utilities.PathSegmentArray[]} pathSegments - An array of path segments to be converted -* @returns {string} The converted path d attribute. -*/ + * Create a path 'd' attribute from path segments. + * Each segment is an array of the form: `[singleChar, [x,y, x,y, ...]]` + * @function module:utilities.getPathDFromSegments + * @param {module:utilities.PathSegmentArray[]} pathSegments - An array of path segments to be converted + * @returns {string} The converted path d attribute. + */ export const getPathDFromSegments = function (pathSegments) { let d = '' pathSegments.forEach(function ([singleChar, pts], _j) { d += singleChar for (let i = 0; i < pts.length; i += 2) { - d += (pts[i] + ',' + pts[i + 1]) + ' ' + d += pts[i] + ',' + pts[i + 1] + ' ' } }) @@ -576,15 +606,17 @@ export const getPathDFromSegments = function (pathSegments) { } /** -* Make a path 'd' attribute from a simple SVG element shape. -* @function module:utilities.getPathDFromElement -* @param {Element} elem - The element to be converted -* @returns {string} The path d attribute or `undefined` if the element type is unknown. -*/ + * Make a path 'd' attribute from a simple SVG element shape. + * @function module:utilities.getPathDFromElement + * @param {Element} elem - The element to be converted + * @returns {string} The path d attribute or `undefined` if the element type is unknown. + */ export const getPathDFromElement = function (elem) { // Possibly the cubed root of 6, but 1.81 works best let num = 1.81 - let d; let rx; let ry + let d + let rx + let ry switch (elem.tagName) { case 'ellipse': case 'circle': { @@ -597,24 +629,26 @@ export const getPathDFromElement = function (elem) { rx = ry } d = getPathDFromSegments([ - ['M', [(cx - rx), (cy)]], - ['C', [(cx - rx), (cy - ry / num), (cx - rx / num), (cy - ry), (cx), (cy - ry)]], - ['C', [(cx + rx / num), (cy - ry), (cx + rx), (cy - ry / num), (cx + rx), (cy)]], - ['C', [(cx + rx), (cy + ry / num), (cx + rx / num), (cy + ry), (cx), (cy + ry)]], - ['C', [(cx - rx / num), (cy + ry), (cx - rx), (cy + ry / num), (cx - rx), (cy)]], + ['M', [cx - rx, cy]], + ['C', [cx - rx, cy - ry / num, cx - rx / num, cy - ry, cx, cy - ry]], + ['C', [cx + rx / num, cy - ry, cx + rx, cy - ry / num, cx + rx, cy]], + ['C', [cx + rx, cy + ry / num, cx + rx / num, cy + ry, cx, cy + ry]], + ['C', [cx - rx / num, cy + ry, cx - rx, cy + ry / num, cx - rx, cy]], ['Z', []] ]) break - } case 'path': + } + case 'path': d = elem.getAttribute('d') break - case 'line': { - const x1 = elem.getAttribute('x1') - const y1 = elem.getAttribute('y1') - const x2 = elem.getAttribute('x2') - const y2 = elem.getAttribute('y2') - d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 - } + case 'line': + { + const x1 = elem.getAttribute('x1') + const y1 = elem.getAttribute('y1') + const x2 = elem.getAttribute('x2') + const y2 = elem.getAttribute('y2') + d = 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + } break case 'polyline': d = 'M' + elem.getAttribute('points') @@ -647,14 +681,25 @@ export const getPathDFromElement = function (elem) { ['L', [x + w - rx, y]], ['C', [x + w - rx / num, y, x + w, y + ry / num, x + w, y + ry]], ['L', [x + w, y + h - ry]], - ['C', [x + w, y + h - ry / num, x + w - rx / num, y + h, x + w - rx, y + h]], + [ + 'C', + [ + x + w, + y + h - ry / num, + x + w - rx / num, + y + h, + x + w - rx, + y + h + ] + ], ['L', [x + rx, y + h]], ['C', [x + rx / num, y + h, x, y + h - ry / num, x, y + h - ry]], ['L', [x, y + ry]], ['Z', []] ]) break - } default: + } + default: break } @@ -662,33 +707,39 @@ export const getPathDFromElement = function (elem) { } /** -* Get a set of attributes from an element that is useful for convertToPath. -* @function module:utilities.getExtraAttributesForConvertToPath -* @param {Element} elem - The element to be probed -* @returns {PlainObject<"marker-start"|"marker-end"|"marker-mid"|"filter"|"clip-path", string>} An object with attributes. -*/ + * Get a set of attributes from an element that is useful for convertToPath. + * @function module:utilities.getExtraAttributesForConvertToPath + * @param {Element} elem - The element to be probed + * @returns {PlainObject<"marker-start"|"marker-end"|"marker-mid"|"filter"|"clip-path", string>} An object with attributes. + */ export const getExtraAttributesForConvertToPath = function (elem) { - const attrs = {}; + const attrs = {} // TODO: make this list global so that we can properly maintain it // TODO: what about @transform, @clip-rule, @fill-rule, etc? - ['marker-start', 'marker-end', 'marker-mid', 'filter', 'clip-path'].forEach(function (item) { - const a = elem.getAttribute(item) - if (a) { - attrs[item] = a + ;['marker-start', 'marker-end', 'marker-mid', 'filter', 'clip-path'].forEach( + function (item) { + const a = elem.getAttribute(item) + if (a) { + attrs[item] = a + } } - }) + ) return attrs } /** -* Get the BBox of an element-as-path. -* @function module:utilities.getBBoxOfElementAsPath -* @param {Element} elem - The DOM element to be probed -* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson -* @param {module:path.pathActions} pathActions - If a transform exists, `pathActions.resetOrientation()` is used. See: canvas.pathActions. -* @returns {DOMRect|false} The resulting path's bounding box object. -*/ -export const getBBoxOfElementAsPath = function (elem, addSVGElementsFromJson, pathActions) { + * Get the BBox of an element-as-path. + * @function module:utilities.getBBoxOfElementAsPath + * @param {Element} elem - The DOM element to be probed + * @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson + * @param {module:path.pathActions} pathActions - If a transform exists, `pathActions.resetOrientation()` is used. See: canvas.pathActions. + * @returns {DOMRect|false} The resulting path's bounding box object. + */ +export const getBBoxOfElementAsPath = function ( + elem, + addSVGElementsFromJson, + pathActions +) { const path = addSVGElementsFromJson({ element: 'path', attr: getExtraAttributesForConvertToPath(elem) @@ -726,18 +777,18 @@ export const getBBoxOfElementAsPath = function (elem, addSVGElementsFromJson, pa } /** -* Convert selected element to a path. -* @function module:utilities.convertToPath -* @param {Element} elem - The DOM element to be converted -* @param {module:utilities.SVGElementJSON} attrs - Apply attributes to new path. see canvas.convertToPath -* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson -* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. -* @param {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection} clearSelection - see [canvas.clearSelection]{@link module:svgcanvas.SvgCanvas#clearSelection} -* @param {module:path.EditorContext#addToSelection} addToSelection - see [canvas.addToSelection]{@link module:svgcanvas.SvgCanvas#addToSelection} -* @param {module:history} hstry - see history module -* @param {module:path.EditorContext#addCommandToHistory|module:draw.DrawCanvasInit#addCommandToHistory} addCommandToHistory - see [canvas.addCommandToHistory]{@link module:svgcanvas~addCommandToHistory} -* @returns {SVGPathElement|null} The converted path element or null if the DOM element was not recognized. -*/ + * Convert selected element to a path. + * @function module:utilities.convertToPath + * @param {Element} elem - The DOM element to be converted + * @param {module:utilities.SVGElementJSON} attrs - Apply attributes to new path. see canvas.convertToPath + * @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson + * @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. + * @param {module:draw.DrawCanvasInit#clearSelection|module:path.EditorContext#clearSelection} clearSelection - see [canvas.clearSelection]{@link module:svgcanvas.SvgCanvas#clearSelection} + * @param {module:path.EditorContext#addToSelection} addToSelection - see [canvas.addToSelection]{@link module:svgcanvas.SvgCanvas#addToSelection} + * @param {module:history} hstry - see history module + * @param {module:path.EditorContext#addCommandToHistory|module:draw.DrawCanvasInit#addCommandToHistory} addCommandToHistory - see [canvas.addCommandToHistory]{@link module:svgcanvas~addCommandToHistory} + * @returns {SVGPathElement|null} The converted path element or null if the DOM element was not recognized. + */ export const convertToPath = (elem, attrs, svgCanvas) => { const batchCmd = new svgCanvas.history.BatchCommand('Convert element to Path') @@ -777,7 +828,13 @@ export const convertToPath = (elem, attrs, svgCanvas) => { } const { nextSibling } = elem - batchCmd.addSubCommand(new svgCanvas.history.RemoveElementCommand(elem, nextSibling, elem.parentNode)) + batchCmd.addSubCommand( + new svgCanvas.history.RemoveElementCommand( + elem, + nextSibling, + elem.parentNode + ) + ) svgCanvas.clearSelection() elem.remove() // We need to remove this element otherwise the nextSibling of 'path' won't be null and an exception will be thrown after subsequent undo and redos. @@ -796,25 +853,25 @@ export const convertToPath = (elem, attrs, svgCanvas) => { } /** -* Can the bbox be optimized over the native getBBox? The optimized bbox is the same as the native getBBox when -* the rotation angle is a multiple of 90 degrees and there are no complex transforms. -* Getting an optimized bbox can be dramatically slower, so we want to make sure it's worth it. -* -* The best example for this is a circle rotate 45 degrees. The circle doesn't get wider or taller when rotated -* about it's center. -* -* The standard, unoptimized technique gets the native bbox of the circle, rotates the box 45 degrees, uses -* that width and height, and applies any transforms to get the final bbox. This means the calculated bbox -* is much wider than the original circle. If the angle had been 0, 90, 180, etc. both techniques render the -* same bbox. -* -* The optimization is not needed if the rotation is a multiple 90 degrees. The default technique is to call -* getBBox then apply the angle and any transforms. -* -* @param {Float} angle - The rotation angle in degrees -* @param {boolean} hasAMatrixTransform - True if there is a matrix transform -* @returns {boolean} True if the bbox can be optimized. -*/ + * Can the bbox be optimized over the native getBBox? The optimized bbox is the same as the native getBBox when + * the rotation angle is a multiple of 90 degrees and there are no complex transforms. + * Getting an optimized bbox can be dramatically slower, so we want to make sure it's worth it. + * + * The best example for this is a circle rotate 45 degrees. The circle doesn't get wider or taller when rotated + * about it's center. + * + * The standard, unoptimized technique gets the native bbox of the circle, rotates the box 45 degrees, uses + * that width and height, and applies any transforms to get the final bbox. This means the calculated bbox + * is much wider than the original circle. If the angle had been 0, 90, 180, etc. both techniques render the + * same bbox. + * + * The optimization is not needed if the rotation is a multiple 90 degrees. The default technique is to call + * getBBox then apply the angle and any transforms. + * + * @param {Float} angle - The rotation angle in degrees + * @param {boolean} hasAMatrixTransform - True if there is a matrix transform + * @returns {boolean} True if the bbox can be optimized. + */ function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasAMatrixTransform) { const angleModulo90 = angle % 90 const closeTo90 = angleModulo90 < -89.99 || angleModulo90 > 89.99 @@ -823,14 +880,18 @@ function bBoxCanBeOptimizedOverNativeGetBBox (angle, hasAMatrixTransform) { } /** -* Get bounding box that includes any transforms. -* @function module:utilities.getBBoxWithTransform -* @param {Element} elem - The DOM element to be converted -* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson -* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. -* @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object -*/ -export const getBBoxWithTransform = function (elem, addSVGElementsFromJson, pathActions) { + * Get bounding box that includes any transforms. + * @function module:utilities.getBBoxWithTransform + * @param {Element} elem - The DOM element to be converted + * @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson + * @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. + * @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object + */ +export const getBBoxWithTransform = function ( + elem, + addSVGElementsFromJson, + pathActions +) { // TODO: Fix issue with rotated groups. Currently they work // fine in FF, but not in other browsers (same problem mentioned // in Issue 339 comment #2). @@ -852,14 +913,22 @@ export const getBBoxWithTransform = function (elem, addSVGElementsFromJson, path // TODO: why ellipse and not circle const elemNames = ['ellipse', 'path', 'line', 'polyline', 'polygon'] if (elemNames.includes(elem.tagName)) { - goodBb = getBBoxOfElementAsPath(elem, addSVGElementsFromJson, pathActions) + goodBb = getBBoxOfElementAsPath( + elem, + addSVGElementsFromJson, + pathActions + ) bb = goodBb } else if (elem.tagName === 'rect') { // Look for radius const rx = Number(elem.getAttribute('rx')) const ry = Number(elem.getAttribute('ry')) if (rx || ry) { - goodBb = getBBoxOfElementAsPath(elem, addSVGElementsFromJson, pathActions) + goodBb = getBBoxOfElementAsPath( + elem, + addSVGElementsFromJson, + pathActions + ) bb = goodBb } } @@ -879,9 +948,9 @@ export const getBBoxWithTransform = function (elem, addSVGElementsFromJson, path * @todo This is problematic with large stroke-width and, for example, a single * horizontal line. The calculated BBox extends way beyond left and right sides. */ -const getStrokeOffsetForBBox = (elem) => { +const getStrokeOffsetForBBox = elem => { const sw = elem.getAttribute('stroke-width') - return (!isNaN(sw) && elem.getAttribute('stroke') !== 'none') ? sw / 2 : 0 + return !isNaN(sw) && elem.getAttribute('stroke') !== 'none' ? sw / 2 : 0 } /** @@ -893,25 +962,33 @@ const getStrokeOffsetForBBox = (elem) => { */ /** -* Get the bounding box for one or more stroked and/or transformed elements. -* @function module:utilities.getStrokedBBox -* @param {Element[]} elems - Array with DOM elements to check -* @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson -* @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. -* @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object -*/ + * Get the bounding box for one or more stroked and/or transformed elements. + * @function module:utilities.getStrokedBBox + * @param {Element[]} elems - Array with DOM elements to check + * @param {module:utilities.EditorContext#addSVGElementsFromJson} addSVGElementsFromJson - Function to add the path element to the current layer. See canvas.addSVGElementsFromJson + * @param {module:path.pathActions} pathActions - If a transform exists, pathActions.resetOrientation() is used. See: canvas.pathActions. + * @returns {module:utilities.BBoxObject|module:math.TransformedBox|DOMRect} A single bounding box object + */ export const getStrokedBBox = (elems, addSVGElementsFromJson, pathActions) => { - if (!elems || !elems.length) { return false } + if (!elems || !elems.length) { + return false + } let fullBb - elems.forEach((elem) => { - if (fullBb) { return } - if (!elem.parentNode) { return } + elems.forEach(elem => { + if (fullBb) { + return + } + if (!elem.parentNode) { + return + } fullBb = getBBoxWithTransform(elem, addSVGElementsFromJson, pathActions) }) // This shouldn't ever happen... - if (!fullBb) { return null } + if (!fullBb) { + return null + } // fullBb doesn't include the stoke, so this does no good! // if (elems.length == 1) return fullBb; @@ -929,8 +1006,12 @@ export const getStrokedBBox = (elems, addSVGElementsFromJson, pathActions) => { maxX += offset maxY += offset } else { - elems.forEach((elem) => { - const curBb = getBBoxWithTransform(elem, addSVGElementsFromJson, pathActions) + elems.forEach(elem => { + const curBb = getBBoxWithTransform( + elem, + addSVGElementsFromJson, + pathActions + ) if (curBb) { const offset = getStrokeOffsetForBBox(elem) minX = Math.min(minX, curBb.x - offset) @@ -944,28 +1025,33 @@ export const getStrokedBBox = (elems, addSVGElementsFromJson, pathActions) => { }) } - fullBb.x = minX.toFixed(2) - fullBb.y = minY.toFixed(2) - fullBb.width = (maxX - minX).toFixed(2) - fullBb.height = (maxY - minY).toFixed(2) + fullBb.x = shortFloat(minX) + fullBb.y = shortFloat(minY) + fullBb.width = shortFloat(maxX - minX) + fullBb.height = shortFloat(maxY - minY) return fullBb } /** -* Get all elements that have a BBox (excludes ``, ``, etc). -* Note that 0-opacity, off-screen etc elements are still considered "visible" -* for this function. -* @function module:utilities.getVisibleElements -* @param {Element} parentElement - The parent DOM element to search within -* @returns {Element[]} All "visible" elements. -*/ -export const getVisibleElements = (parentElement) => { + * Get all elements that have a BBox (excludes `<defs>`, `<title>`, etc). + * Note that 0-opacity, off-screen etc elements are still considered "visible" + * for this function. + * @function module:utilities.getVisibleElements + * @param {Element} parentElement - The parent DOM element to search within + * @returns {Element[]} All "visible" elements. + */ +export const getVisibleElements = parentElement => { if (!parentElement) { const svgContent = svgCanvas.getSvgContent() for (let i = 0; i < svgContent.children.length; i++) { if (svgContent.children[i].getBBox) { const bbox = svgContent.children[i].getBBox() - if (bbox.width !== 0 && bbox.height !== 0 && bbox.width !== 0 && bbox.height !== 0) { + if ( + bbox.width !== 0 && + bbox.height !== 0 && + bbox.width !== 0 && + bbox.height !== 0 + ) { parentElement = svgContent.children[i] break } @@ -977,7 +1063,7 @@ export const getVisibleElements = (parentElement) => { if (parentElement) { const children = parentElement.children // eslint-disable-next-line array-callback-return - Array.from(children, (elem) => { + Array.from(children, elem => { if (elem.getBBox) { contentElems.push(elem) } @@ -987,13 +1073,15 @@ export const getVisibleElements = (parentElement) => { } /** -* Get the bounding box for one or more stroked and/or transformed elements. -* @function module:utilities.getStrokedBBoxDefaultVisible -* @param {Element[]} elems - Array with DOM elements to check -* @returns {module:utilities.BBoxObject} A single bounding box object -*/ -export const getStrokedBBoxDefaultVisible = (elems) => { - if (!elems) { elems = getVisibleElements() } + * Get the bounding box for one or more stroked and/or transformed elements. + * @function module:utilities.getStrokedBBoxDefaultVisible + * @param {Element[]} elems - Array with DOM elements to check + * @returns {module:utilities.BBoxObject} A single bounding box object + */ +export const getStrokedBBoxDefaultVisible = elems => { + if (!elems) { + elems = getVisibleElements() + } return getStrokedBBox( elems, svgCanvas.addSVGElementsFromJson, @@ -1002,30 +1090,32 @@ export const getStrokedBBoxDefaultVisible = (elems) => { } /** -* Get the rotation angle of the given transform list. -* @function module:utilities.getRotationAngleFromTransformList -* @param {SVGTransformList} tlist - List of transforms -* @param {boolean} toRad - When true returns the value in radians rather than degrees -* @returns {Float} The angle in degrees or radians -*/ + * Get the rotation angle of the given transform list. + * @function module:utilities.getRotationAngleFromTransformList + * @param {SVGTransformList} tlist - List of transforms + * @param {boolean} toRad - When true returns the value in radians rather than degrees + * @returns {Float} The angle in degrees or radians + */ export const getRotationAngleFromTransformList = (tlist, toRad) => { - if (!tlist) { return 0 } // <svg> element have no tlist + if (!tlist) { + return 0 + } // <svg> element have no tlist for (let i = 0; i < tlist.numberOfItems; ++i) { const xform = tlist.getItem(i) if (xform.type === 4) { - return toRad ? xform.angle * Math.PI / 180.0 : xform.angle + return toRad ? (xform.angle * Math.PI) / 180.0 : xform.angle } } return 0.0 } /** -* Get the rotation angle of the given/selected DOM element. -* @function module:utilities.getRotationAngle -* @param {Element} [elem] - DOM element to get the angle for. Default to first of selected elements. -* @param {boolean} [toRad=false] - When true returns the value in radians rather than degrees -* @returns {Float} The angle in degrees or radians -*/ + * Get the rotation angle of the given/selected DOM element. + * @function module:utilities.getRotationAngle + * @param {Element} [elem] - DOM element to get the angle for. Default to first of selected elements. + * @param {boolean} [toRad=false] - When true returns the value in radians rather than degrees + * @returns {Float} The angle in degrees or radians + */ export let getRotationAngle = (elem, toRad) => { const selected = elem || svgCanvas.getSelectedElements()[0] // find the rotation transform (if any) and set it @@ -1034,21 +1124,21 @@ export let getRotationAngle = (elem, toRad) => { } /** -* Get the reference element associated with the given attribute value. -* @function module:utilities.getRefElem -* @param {string} attrVal - The attribute value as a string -* @returns {Element} Reference element -*/ -export const getRefElem = (attrVal) => { + * Get the reference element associated with the given attribute value. + * @function module:utilities.getRefElem + * @param {string} attrVal - The attribute value as a string + * @returns {Element} Reference element + */ +export const getRefElem = attrVal => { return getElement(getUrlFromAttr(attrVal).substr(1)) } /** -* Get the reference element associated with the given attribute value. -* @function module:utilities.getFeGaussianBlur -* @param {any} Element -* @returns {any} Reference element -*/ -export const getFeGaussianBlur = (ele) => { + * Get the reference element associated with the given attribute value. + * @function module:utilities.getFeGaussianBlur + * @param {any} Element + * @returns {any} Reference element + */ +export const getFeGaussianBlur = ele => { if (ele?.firstChild?.tagName === 'feGaussianBlur') { return ele.firstChild } else { @@ -1064,30 +1154,33 @@ export const getFeGaussianBlur = (ele) => { } /** -* Get a DOM element by ID within the SVG root element. -* @function module:utilities.getElement -* @param {string} id - String with the element's new ID -* @returns {?Element} -*/ -export const getElement = (id) => { + * Get a DOM element by ID within the SVG root element. + * @function module:utilities.getElement + * @param {string} id - String with the element's new ID + * @returns {?Element} + */ +export const getElement = id => { // querySelector lookup return svgroot_.querySelector('#' + id) } /** -* Assigns multiple attributes to an element. -* @function module:utilities.assignAttributes -* @param {Element} elem - DOM element to apply new attribute values to -* @param {PlainObject<string, string>} attrs - Object with attribute keys/values -* @param {Integer} [suspendLength] - Milliseconds to suspend redraw -* @param {boolean} [unitCheck=false] - Boolean to indicate the need to use units.setUnitAttr -* @returns {void} -*/ + * Assigns multiple attributes to an element. + * @function module:utilities.assignAttributes + * @param {Element} elem - DOM element to apply new attribute values to + * @param {PlainObject<string, string>} attrs - Object with attribute keys/values + * @param {Integer} [suspendLength] - Milliseconds to suspend redraw + * @param {boolean} [unitCheck=false] - Boolean to indicate the need to use units.setUnitAttr + * @returns {void} + */ export const assignAttributes = (elem, attrs, suspendLength, unitCheck) => { for (const [key, value] of Object.entries(attrs)) { - const ns = (key.substr(0, 4) === 'xml:' - ? NS.XML - : key.substr(0, 6) === 'xlink:' ? NS.XLINK : null) + const ns = + key.substr(0, 4) === 'xml:' + ? NS.XML + : key.substr(0, 6) === 'xlink:' + ? NS.XLINK + : null if (value === undefined) { if (ns) { elem.removeAttributeNS(ns, key) @@ -1107,12 +1200,12 @@ export const assignAttributes = (elem, attrs, suspendLength, unitCheck) => { } /** -* Remove unneeded (default) attributes, making resulting SVG smaller. -* @function module:utilities.cleanupElement -* @param {Element} element - DOM element to clean up -* @returns {void} -*/ -export const cleanupElement = (element) => { + * Remove unneeded (default) attributes, making resulting SVG smaller. + * @function module:utilities.cleanupElement + * @param {Element} element - DOM element to clean up + * @returns {void} + */ +export const cleanupElement = element => { const defaults = { 'fill-opacity': 1, 'stop-opacity': 1, @@ -1141,12 +1234,12 @@ export const cleanupElement = (element) => { } /** -* Round value to for snapping. -* @function module:utilities.snapToGrid -* @param {Float} value -* @returns {Integer} -*/ -export const snapToGrid = (value) => { + * Round value to for snapping. + * @function module:utilities.snapToGrid + * @param {Float} value + * @returns {Integer} + */ +export const snapToGrid = value => { const unit = svgCanvas.getBaseUnit() let stepSize = svgCanvas.getSnappingStep() if (unit !== 'px') { @@ -1162,8 +1255,8 @@ export const snapToGrid = (value) => { * @param {Element} img - The DOM element to prevent the click on * @returns {void} */ -export const preventClickDefault = (img) => { - $click(img, (e) => { +export const preventClickDefault = img => { + $click(img, e => { e.preventDefault() }) } @@ -1178,28 +1271,30 @@ export const preventClickDefault = (img) => { * @param {any} val * @returns {boolean} */ -export const isNullish = (val) => { +export const isNullish = val => { return val === null || val === undefined } /** -* Overwrite methods for unit testing. -* @function module:utilities.mock -* @param {PlainObject} mockMethods -* @param {module:utilities.getHref} mockMethods.getHref -* @param {module:utilities.setHref} mockMethods.setHref -* @param {module:utilities.getRotationAngle} mockMethods.getRotationAngle -* @returns {void} -*/ + * Overwrite methods for unit testing. + * @function module:utilities.mock + * @param {PlainObject} mockMethods + * @param {module:utilities.getHref} mockMethods.getHref + * @param {module:utilities.setHref} mockMethods.setHref + * @param {module:utilities.getRotationAngle} mockMethods.getRotationAngle + * @returns {void} + */ export const mock = ({ - getHref: getHrefUser, setHref: setHrefUser, getRotationAngle: getRotationAngleUser + getHref: getHrefUser, + setHref: setHrefUser, + getRotationAngle: getRotationAngleUser }) => { getHref = getHrefUser setHref = setHrefUser getRotationAngle = getRotationAngleUser } -export const stringToHTML = (str) => { +export const stringToHTML = str => { const parser = new DOMParser() const doc = parser.parseFromString(str, 'text/html') return doc.body.firstChild @@ -1215,9 +1310,9 @@ export const insertChildAtIndex = (parent, child, index = 0) => { } // shortcuts to common DOM functions -export const $id = (id) => document.getElementById(id) -export const $qq = (sel) => document.querySelector(sel) -export const $qa = (sel) => [...document.querySelectorAll(sel)] +export const $id = id => document.getElementById(id) +export const $qq = sel => document.querySelector(sel) +export const $qa = sel => [...document.querySelectorAll(sel)] export const $click = (element, handler) => { element.addEventListener('click', handler) element.addEventListener('touchend', handler) diff --git a/src/editor/panels/TopPanel.js b/src/editor/panels/TopPanel.js index b34c84183..9f80d73df 100644 --- a/src/editor/panels/TopPanel.js +++ b/src/editor/panels/TopPanel.js @@ -232,7 +232,7 @@ class TopPanel { if (!isNode && currentMode !== 'pathedit') { this.displayTool('selected_panel') // Elements in this array already have coord fields - if (['line', 'circle', 'ellipse'].includes(elname)) { + if (['line', 'circle', 'ellipse', 'polygon'].includes(elname)) { this.hideTool('xy_panel') } else { let x