From 039e3193d92bc7fad818f13cfda41ad9c2019137 Mon Sep 17 00:00:00 2001 From: Ellet <73608287+freshtechtips@users.noreply.github.com> Date: Tue, 5 Dec 2023 07:06:30 +0300 Subject: [PATCH] Improving flutter quill extensions (#1564) * organizations for flutter quill extensions --- .gitignore | 5 +- .../lib/presentation/quill/quill_editor.dart | 4 +- .../quill/samples/quill_images_sample.dart | 18 +- flutter_quill_extensions/CHANGELOG.md | 8 + flutter_quill_extensions/README.md | 25 +- .../embeds/embed_types.dart | 0 .../formula/editor/formula_embed.dart} | 5 +- .../formula}/toolbar/formula_button.dart | 2 +- .../lib/embeds/image/editor/image_embed.dart | 86 +++++ .../image/editor/image_embed_types.dart} | 4 +- .../image/editor}/image_menu.dart | 12 +- .../image/editor/image_web_embed.dart} | 8 +- .../image/toolbar}/image_button.dart | 8 +- .../image/toolbar}/select_image_source.dart | 30 +- .../others}/camera_button/camera_button.dart | 6 +- .../others/camera_button/camera_types.dart} | 4 +- .../camera_button/select_camera_action.dart | 2 +- .../others}/image_video_utils.dart | 0 .../others}/media_button/media_button.dart | 0 .../unknown/editor/unknown_embed.dart} | 0 .../video/editor/video_embed.dart} | 4 +- .../video/editor/video_web_embed.dart} | 6 +- .../video/toolbar}/select_video_source.dart | 22 +- .../video/toolbar}/video_button.dart | 9 +- .../embed_types => embeds/video}/video.dart | 4 +- .../embeds/widgets/image.dart | 2 +- .../embeds/widgets/image_resizer.dart | 0 .../embeds/widgets/video_app.dart | 2 +- .../embeds/widgets/youtube_video_app.dart | 0 .../lib/extensions/attribute.dart | 32 ++ .../{logic => }/extensions/controller.dart | 23 -- .../lib/flutter_quill_extensions.dart | 87 +++-- .../lib/logic/extensions/attribute.dart | 30 -- .../models/config/editor/image/image.dart | 2 +- .../models/config/editor/image/image_web.dart | 0 .../models/config/editor/video/video.dart | 0 .../models/config/editor/video/video_web.dart | 0 .../models/config/editor/webview.dart | 0 .../models/config/shared_configurations.dart | 0 .../models/config/toolbar/buttons/camera.dart | 2 +- .../config/toolbar/buttons/formula.dart | 0 .../models/config/toolbar/buttons/image.dart | 2 +- .../config/toolbar/buttons/media_button.dart | 0 .../models/config/toolbar/buttons/video.dart | 2 +- .../embeds/editor/image/image.dart | 150 --------- .../presentation/embeds/editor/webview.dart | 58 ---- .../lib/presentation/utils/utils.dart | 201 ------------ .../services/image_picker/image_options.dart | 0 .../services/image_picker/image_picker.dart | 0 .../image_picker/packages/image_picker.dart | 0 .../services/image_picker/s_image_picker.dart | 0 .../services/image_saver/exceptions.dart | 0 .../services/image_saver/image_saver.dart | 0 .../services/image_saver/packages/gal.dart | 0 .../services/image_saver/s_image_saver.dart | 0 .../shims => utils/dart_ui}/dart_ui_fake.dart | 0 .../shims => utils/dart_ui}/dart_ui_real.dart | 0 .../element_utils/element_shared_utils.dart | 29 ++ .../utils/element_utils/element_utils.dart | 98 ++++++ .../element_utils/element_web_utils.dart} | 24 +- .../{logic => }/utils/quill_image_utils.dart | 2 +- .../lib/{logic => }/utils/string.dart | 12 +- flutter_quill_extensions/lib/utils/utils.dart | 89 ++++++ flutter_quill_extensions/pubspec.yaml | 2 +- lib/flutter_quill.dart | 1 - .../l10n/generated/quill_localizations.dart | 36 +++ .../generated/quill_localizations_ar.dart | 20 ++ .../generated/quill_localizations_bg.dart | 20 ++ .../generated/quill_localizations_bn.dart | 20 ++ .../generated/quill_localizations_cs.dart | 20 ++ .../generated/quill_localizations_da.dart | 20 ++ .../generated/quill_localizations_de.dart | 20 ++ .../generated/quill_localizations_en.dart | 20 ++ .../generated/quill_localizations_es.dart | 20 ++ .../generated/quill_localizations_fa.dart | 20 ++ .../generated/quill_localizations_fr.dart | 20 ++ .../generated/quill_localizations_he.dart | 20 ++ .../generated/quill_localizations_hi.dart | 20 ++ .../generated/quill_localizations_id.dart | 20 ++ .../generated/quill_localizations_it.dart | 20 ++ .../generated/quill_localizations_ja.dart | 20 ++ .../generated/quill_localizations_ko.dart | 20 ++ .../generated/quill_localizations_ms.dart | 20 ++ .../generated/quill_localizations_nl.dart | 20 ++ .../generated/quill_localizations_no.dart | 20 ++ .../generated/quill_localizations_pl.dart | 20 ++ .../generated/quill_localizations_pt.dart | 20 ++ .../generated/quill_localizations_ru.dart | 20 ++ .../generated/quill_localizations_sr.dart | 20 ++ .../generated/quill_localizations_sw.dart | 20 ++ .../generated/quill_localizations_tk.dart | 20 ++ .../generated/quill_localizations_tr.dart | 20 ++ .../generated/quill_localizations_uk.dart | 20 ++ .../generated/quill_localizations_ur.dart | 20 ++ .../generated/quill_localizations_vi.dart | 20 ++ .../generated/quill_localizations_zh.dart | 20 ++ lib/src/l10n/quill_en.arb | 8 +- lib/src/l10n/untranslated.json | 299 +++++++++++++++++- lib/translations.dart | 1 + 99 files changed, 1441 insertions(+), 625 deletions(-) rename flutter_quill_extensions/lib/{presentation => }/embeds/embed_types.dart (100%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/formula.dart => embeds/formula/editor/formula_embed.dart} (90%) rename flutter_quill_extensions/lib/{presentation/embeds => embeds/formula}/toolbar/formula_button.dart (98%) create mode 100644 flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart rename flutter_quill_extensions/lib/{presentation/embeds/embed_types/image.dart => embeds/image/editor/image_embed_types.dart} (95%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/image => embeds/image/editor}/image_menu.dart (94%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/image/image_web.dart => embeds/image/editor/image_web_embed.dart} (88%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar/image_button => embeds/image/toolbar}/image_button.dart (96%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar/image_button => embeds/image/toolbar}/select_image_source.dart (65%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar => embeds/others}/camera_button/camera_button.dart (96%) rename flutter_quill_extensions/lib/{presentation/embeds/embed_types/camera.dart => embeds/others/camera_button/camera_types.dart} (94%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar => embeds/others}/camera_button/select_camera_action.dart (95%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar/utils => embeds/others}/image_video_utils.dart (100%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar => embeds/others}/media_button/media_button.dart (100%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/unknown.dart => embeds/unknown/editor/unknown_embed.dart} (100%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/video/video.dart => embeds/video/editor/video_embed.dart} (95%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/video/video_web.dart => embeds/video/editor/video_web_embed.dart} (89%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar/video_button => embeds/video/toolbar}/select_video_source.dart (74%) rename flutter_quill_extensions/lib/{presentation/embeds/toolbar/video_button => embeds/video/toolbar}/video_button.dart (96%) rename flutter_quill_extensions/lib/{presentation/embeds/embed_types => embeds/video}/video.dart (94%) rename flutter_quill_extensions/lib/{presentation => }/embeds/widgets/image.dart (99%) rename flutter_quill_extensions/lib/{presentation => }/embeds/widgets/image_resizer.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/embeds/widgets/video_app.dart (98%) rename flutter_quill_extensions/lib/{presentation => }/embeds/widgets/youtube_video_app.dart (100%) create mode 100644 flutter_quill_extensions/lib/extensions/attribute.dart rename flutter_quill_extensions/lib/{logic => }/extensions/controller.dart (70%) delete mode 100644 flutter_quill_extensions/lib/logic/extensions/attribute.dart rename flutter_quill_extensions/lib/{presentation => }/models/config/editor/image/image.dart (98%) rename flutter_quill_extensions/lib/{presentation => }/models/config/editor/image/image_web.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/models/config/editor/video/video.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/models/config/editor/video/video_web.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/models/config/editor/webview.dart (100%) rename flutter_quill_extensions/lib/{logic => }/models/config/shared_configurations.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/models/config/toolbar/buttons/camera.dart (93%) rename flutter_quill_extensions/lib/{presentation => }/models/config/toolbar/buttons/formula.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/models/config/toolbar/buttons/image.dart (95%) rename flutter_quill_extensions/lib/{presentation => }/models/config/toolbar/buttons/media_button.dart (100%) rename flutter_quill_extensions/lib/{presentation => }/models/config/toolbar/buttons/video.dart (95%) delete mode 100644 flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart delete mode 100644 flutter_quill_extensions/lib/presentation/embeds/editor/webview.dart delete mode 100644 flutter_quill_extensions/lib/presentation/utils/utils.dart rename flutter_quill_extensions/lib/{logic => }/services/image_picker/image_options.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_picker/image_picker.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_picker/packages/image_picker.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_picker/s_image_picker.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_saver/exceptions.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_saver/image_saver.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_saver/packages/gal.dart (100%) rename flutter_quill_extensions/lib/{logic => }/services/image_saver/s_image_saver.dart (100%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/shims => utils/dart_ui}/dart_ui_fake.dart (100%) rename flutter_quill_extensions/lib/{presentation/embeds/editor/shims => utils/dart_ui}/dart_ui_real.dart (100%) create mode 100644 flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart create mode 100644 flutter_quill_extensions/lib/utils/element_utils/element_utils.dart rename flutter_quill_extensions/lib/{presentation/utils/web_utils.dart => utils/element_utils/element_web_utils.dart} (77%) rename flutter_quill_extensions/lib/{logic => }/utils/quill_image_utils.dart (99%) rename flutter_quill_extensions/lib/{logic => }/utils/string.dart (66%) create mode 100644 flutter_quill_extensions/lib/utils/utils.dart diff --git a/.gitignore b/.gitignore index cee99ed04..fc03c2767 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,7 @@ pubspec.lock # For local development pubspec_overrides.yaml -old_example \ No newline at end of file +old_example + +# A directory where you put all of your local things that you don't want to push +.flutter-quill \ No newline at end of file diff --git a/example/lib/presentation/quill/quill_editor.dart b/example/lib/presentation/quill/quill_editor.dart index 133f9997f..06f7eb766 100644 --- a/example/lib/presentation/quill/quill_editor.dart +++ b/example/lib/presentation/quill/quill_editor.dart @@ -7,9 +7,9 @@ import 'package:desktop_drop/desktop_drop.dart' show DropTarget; import 'package:flutter/material.dart'; import 'package:flutter_quill/extensions.dart' show isAndroid, isIOS, isWeb; import 'package:flutter_quill/flutter_quill.dart'; -import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; -import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart' +import 'package:flutter_quill_extensions/embeds/widgets/image.dart' show getImageProviderByImageSource, imageFileExtensions; +import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; import 'package:path/path.dart' as path; import '../extensions/scaffold_messenger.dart'; diff --git a/example/lib/presentation/quill/samples/quill_images_sample.dart b/example/lib/presentation/quill/samples/quill_images_sample.dart index 849abc326..9dadcd2ca 100644 --- a/example/lib/presentation/quill/samples/quill_images_sample.dart +++ b/example/lib/presentation/quill/samples/quill_images_sample.dart @@ -6,9 +6,9 @@ final quillImagesSample = [ { 'insert': {'image': Assets.images.screenshot1.path}, 'attributes': { - 'width': '100', - 'height': '100', - 'style': 'width:500px; height:350px; deletable: false;' + 'width': '200', + 'height': '500', + 'style': 'width:500px; height:350px; margin: 20px;' } }, {'insert': '\n'}, @@ -20,8 +20,8 @@ final quillImagesSample = [ 'https://helpx.adobe.com/content/dam/help/en/photoshop/using/convert-color-image-black-white/jcr_content/main-pars/before_and_after/image-before/Landscape-Color.jpg' }, 'attributes': { - 'width': '100', - 'height': '100', + 'width': '250', + 'height': '250', 'style': 'width:250px; height:250px;' } }, @@ -38,8 +38,8 @@ final quillImagesSample = [ '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' }, 'attributes': { - 'width': '100', - 'height': '100', + 'width': '300', + 'height': '500', 'style': 'width:250px; height:250px;' } }, @@ -57,8 +57,8 @@ final quillImagesSample = [ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA4QAAAEwCAIAAABg6CgmAAAMaWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkJDQAhGQEnoTRHqREkKLICBVsBGSQEKJISGI2JFFBdcuoljRVRFF1wKIDbGXRbH3xYKCsi7qoigqb0ICuu4r35t8M/PfM2f+UzJz7wwAmr1ciSQb1QIgR5wnjQ0LYo5PTmGSngME/gCwBEQuTyZhxcREwicw2P+9vL81oAuuOyq4/jn+X4sOXyDjAYBMhDiNL+PlQNwEAL6eJ5HmAUBUyC2m5UkUeC7EulLoIMSrFDhDiXcqcJoSHx3QiY9lQ3wVADUqlyvNAEDjAZQz83kZkEfjM8TOYr5IDIDmCIj9eUIuH2KF7yNycqYqcAXEtlBfAjH0B3ilfceZ8Tf+tCF+LjdjCCvjGihqwSKZJJs7/f9Mzf8uOdnyQRvWsFKF0vBYRfwwh3eypkYoMBXiLnFaVLQi1xD3ivjKvAOAUoTy8ASlPmrEk7Fh/gADYmc+NzgCYiOIQ8XZUZEqeVq6KJQDMVwtaIEojxMPsT7ECwWykDiVzmbp1FiVLbQuXcpmqeTnudIBuwpbj+RZCSwV/1uhgKPixzQKhfFJEFMgtswXJUZBrAGxkywrLkKlM7pQyI4a1JHKYxX+W0IcKxCHBSn5sfx0aWisSr80RzYYL7ZZKOJEqfD+PGF8uDI/2Gked8B/GAt2VSBmJQzyCGTjIwdj4QuCQ5SxYx0CcUKciqdXkhcUq5yLUyTZMSp93FyQHaaQm0PsJsuPU83FE/Pg4lTy4+mSvJh4pZ94YSZ3TIzSH3wZiARsEAyYQA5rGpgKMoGopau+Cz4pR0IBF0hBBhAAR5VkcEbSwIgYtnGgEPwBkQDIhuYFDYwKQD6UfxmSKltHkD4wmj8wIws8hzgHRIBs+CwfmCUespYInkGJ6B/WubDyoL/ZsCrG/718UPpNwoKSSJVEPmiRqTmoSQwhBhPDiaFEO9wQ98d98UjYBsLqgnvh3oNxfNMnPCe0Ep4QbhLaCHeniIqkP3g5FrRB/lBVLtK+zwVuDTnd8SDcD7JDZpyBGwJH3A3aYeEB0LI7lLJVfiuywvyB+28RfPdvqPTIzmSUPIwcSLb9caaGvYb7EIsi19/nR+lr2lC+2UMjP9pnf5d9PuwjftTEFmIHsHPYSewCdhSrB0zsBNaAXcaOKfDQ6no2sLoGrcUO+JMFeUT/sMdV2VRkUuZc49zp/Fk5licoyFNsPPZUyXSpKEOYx2TBr4OAyRHznEYwXZxdXABQfGuUr693jIFvCMK4+E02fyMAfgf7+/uPfJNFNAJwoAxu/9vfZDaz4GviJADnK3lyab5ShisaAnxLaMKdZgBMgAWwhfG4AA/gCwJBCBgDokE8SAaTYZaFcJ1LwTQwE8wDJaAMLAOrwTqwCWwFO8EesB/Ug6PgJDgLLoGr4Ca4D1dPO3gFusF70IcgCAmhIXTEADFFrBAHxAXxQvyRECQSiUWSkVQkAxEjcmQmMh8pQ1Yg65AtSDXyK3IYOYlcQFqRu8hjpBN5i3xCMZSK6qLGqDU6EvVCWWgEGo9OQjPQXLQQLUaXoBVoFbobrUNPopfQm2gb+grtwQCmjjEwM8wR88LYWDSWgqVjUmw2VoqVY1VYLdYI/+frWBvWhX3EiTgdZ+KOcAWH4wk4D8/FZ+OL8XX4TrwOP41fxx/j3fhXAo1gRHAg+BA4hPGEDMI0QgmhnLCdcIhwBu6ldsJ7IpHIINoQPeFeTCZmEmcQFxM3EPcSm4itxKfEHhKJZEByIPmRoklcUh6phLSWtJt0gnSN1E7qVVNXM1VzUQtVS1ETqxWplavtUjuudk3thVofWYtsRfYhR5P55OnkpeRt5EbyFXI7uY+iTbGh+FHiKZmUeZQKSi3lDOUB5Z26urq5urf6OHWR+lz1CvV96ufVH6t/pOpQ7als6kSqnLqEuoPaRL1LfUej0axpgbQUWh5tCa2ador2iNarQddw0uBo8DXmaFRq1Glc03itSda00mRpTtYs1CzXPKB5RbNLi6xlrcXW4mrN1qrUOqx1W6tHm649SjtaO0d7sfYu7QvaHTokHWudEB2+TrHOVp1TOk/pGN2Czqbz6PPp2+hn6O26RF0bXY5upm6Z7h7dFt1uPR09N71EvQK9Sr1jem0MjGHN4DCyGUsZ+xm3GJ+GGQ9jDRMMWzSsdti1YR/0h+sH6gv0S/X36t/U/2TANAgxyDJYblBv8NAQN7Q3HGc4zXCj4RnDruG6w32H84aXDt8//J4RamRvFGs0w2ir0WWjHmMT4zBjifFa41PGXSYMk0CTTJNVJsdNOk3ppv6mItNVpidMXzL1mCxmNrOCeZrZbWZkFm4mN9ti1mLWZ25jnmBeZL7X/KEFxcLLIt1ilUWzRbelqeVYy5mWNZb3rMhWXlZCqzVW56w+WNtYJ1kvsK637rDRt+HYFNrU2DywpdkG2ObaVtnesCPaedll2W2wu2qP2rvbC+0r7a84oA4eDiKHDQ6tIwgjvEeIR1SNuO1IdWQ55jvWOD52YjhFOhU51Tu9Hmk5MmXk8pHnRn51dnfOdt7mfH+Uzqgxo4pGNY5662LvwnOpdLnhSnMNdZ3j2uD6xs3BTeC20e2OO919rPsC92b3Lx6eHlKPWo9OT0vPVM/1nre9dL1ivBZ7nfcmeAd5z/E+6v3Rx8Mnz2e/z5++jr5Zvrt8O0bbjBaM3jb6qZ+5H9dvi1+bP9M/1X+zf1uAWQA3oCrgSaBFID9we+ALlh0rk7Wb9TrIOUgadCjoA9uHPYvdFIwFhwWXBreE6IQkhKwLeRRqHpoRWhPaHeYeNiOsKZwQHhG+PPw2x5jD41Rzusd4jpk15nQENSIuYl3Ek0j7SGlk41h07JixK8c+iLKKEkfVR4NoTvTK6IcxNjG5MUfGEcfFjKsc9zx2VOzM2HNx9Lgpcbvi3scHxS+Nv59gmyBPaE7UTJyYWJ34ISk4aUVS2/iR42eNv5RsmCxKbkghpSSmbE/pmRAyYfWE9onuE0sm3ppkM6lg0oXJhpOzJx+bojmFO+VAKiE1KXVX6mduNLeK25PGSVuf1s1j89bwXvED+av4nQI/wQrBi3S/9BXpHRl+GSszOoUBwnJhl4gtWid6kxmeuSnzQ1Z01o6s/uyk7L05ajmpOYfFOuIs8empJlMLprZKHCQlkrZcn9zVud3SCOl2GSKbJGvI04WH+styW/lP8sf5/vmV+b3TEqcdKNAuEBdcnm4/fdH0F4Whhb/MwGfwZjTPNJs5b+bjWaxZW2Yjs9NmN8+xmFM8p31u2Nyd8yjzsub9VuRctKLor/lJ8xuLjYvnFj/9KeynmhKNEmnJ7QW+CzYtxBeKFrYscl20dtHXUn7pxTLnsvKyz4t5iy/+POrnip/7l6QvaVnqsXTjMuIy8bJbywOW71yhvaJwxdOVY1fWrWKuKl311+opqy+Uu5VvWkNZI1/TVhFZ0bDWcu2ytZ/XCdfdrAyq3LveaP2i9R828Ddc2xi4sXaT8aayTZ82izbf2RK2pa7Kuqp8K3Fr/tbn2xK3nfvF65fq7Ybby7Z/2SHe0bYzdufpas/q6l1Gu5bWoDXyms7dE3df3RO8p6HWsXbLXsbesn1gn3zfy19Tf721P2J/8wGvA7UHrQ6uP0Q/VFqH1E2v664X1rc1JDe0Hh5zuLnRt/HQEacjO46aHa08pnds6XHK8eLj/ScKT/Q0SZq6TmacfNo8pfn+qfGnbpwed7rlTMSZ82dDz546xzp34rzf+aMXfC4cvuh1sf6Sx6W6y+6XD/3m/tuhFo+WuiueVxquel9tbB3devxawLWT14Ovn73BuXHpZtTN1lsJt+7cnni77Q7/Tsfd7Ltv7uXf67s/9wHhQelDrYflj4weVf1u9/veNo+2Y4+DH19+Evfk/lPe01fPZM8+txc/pz0vf2H6orrDpeNoZ2jn1ZcTXra/krzq6yr5Q/uP9a9tXx/8M/DPy93ju9vfSN/0v138zuDdjr/c/mruiel59D7nfd+H0l6D3p0fvT6e+5T06UXftM+kzxVf7L40fo34+qA/p79fwpVyB44CGKxoejoAb3cAQEsGgA7vbZQJyrvgQFHedZUI/CesvC8OFA8AamGnOMazmwDYB6t1IOSGveIIHx8IUFfXoaoqsnRXFyUXFd6ECL39/e+MASDB88wXaX9/34b+/i/boLN3AWjKVd5BFYUI7wybgxXo7spJc8EPRXk//S7GH3ug8MAN/Nj/C+0MkO1nByjeAAAAbGVYSWZNTQAqAAAACAAEARoABQAAAAEAAAA+ARsABQAAAAEAAABGASgAAwAAAAEAAgAAh2kABAAAAAEAAABOAAAAAAAAAJAAAAABAAAAkAAAAAEAAqACAAQAAAABAAADhKADAAQAAAABAAABMAAAAABgscxtAAAACXBIWXMAABYlAAAWJQFJUiTwAABVx0lEQVR42u2dd7cV1Zqvzzfo/gan/7m3x7j/dN8+p293nw7H9nQ4bUY9xmMWcwJRRCWKqCCiggkkGBBFQESSCILknDY557DJObPvb6+XPSlqVtWqldfa+3nGHA5cu1atqlnpqXfO+c5fNQAAAAAAVIhfUQUAAAAAgIwCAAAAADIKAAAAAICMAgAAAAAyCgAAAACAjAIAAAAAMgoAAAAAgIwCAAAAADIKAAAAAICMAgAAAAAyCgAAAACAjAIAAAAAMgoAAAAAgIwCAAAAADIKAAAAAICMAgAAAAAyCgAAAADIKAAAAAAAMgoAAAAAyCgAAAAAADIKAAAAAMgoAAAAAAAyCgAAAADIKAAAAAAAMgoAAAAAyCgAAAAAADIKAAAAAMgoAAAAAAAyCgAAAADIKAAAAAAAMgoAAAAAyCgAAAAAADIKAAAAAMgoAAAAACCjAAAAAADIKAAAAAAgowAAAAAAyCgAAAAAIKMAAAAAAMgoAAAAACCjAAAAAADIKAAAAAAgowAAAAAAyCgAAAAAIKMAAAAAAMgoAAAAACCjAAAAAADIKAAAAAAgowAAAACAjAIAAAAAIKMAAAAAgIwCAAAAALRoGb148eLOnTvnzp377bff9u7d+7nnnnskR5YvX84BBsiDlStXvvrqq3feeWfr1q379et39OjRhIVHjx494Eo2bdpUlIUBAAAZrRgbNmyQfV5TGPPnz+cAA+TKjz/+eO211wYvpT//+c/79++PW96/VGfMmFGUhcvDoUOHkm27GlD9r1ixYs6cOT/99NP06dOXLFmydetWvbFzugIAMlp8jh8//tFHH1133XXXFAwyCpAre/fuvfnmm/2rqXPnzs1VRrVrd999dxXeLuSaixcvVrvQAw88EHmL+9Of/qSNnzBhwunTp2vuTFuzZs3yK1m9ejUXIAAyWnk2b96sp8I1RQIZBcgVmU3k1aRYaZzx1LSMTpo0yW1J3759T506VSUHYvLkyeprlPJed9tttw0aNOjkyZO1cprNmzfP3wvd/LkAAZDRCnPgwIH77rvvmuKBjALkyuDBg+MuqO3btzczGd23b5+Ci8GNeeihh1atWlXZQ7Br164OHTrkccfT/bMmbnrqFBEZdEBGAZDRytO1a9e4m+ztt9/+wgsvdE7klltuqS0ZVa+vyVdSX1+f0xouXLgw2YNzGgphypQpkdfgDTfccO7cuWYmox07dvT3dOzYsRWs/7Vr1956662FvISPHDmyys+xt956K3LLkVEAZLTCKBoReXvq0aOHOrGlWcMTTzxRWzKq0cqhDZ45c2ZOa1CzqV9jjGmAQjh27JiGK/nn1bvvvlsUv6weGdU4LX839UpcwcrXwM1QpDY/lLKgak8wHe64zUZGAZDRCtOrVy//3qSnRfo1IKPIKBSFZcuWqQ9i8KSSQWpkYXOSUbVC+AFIWfiRI0cqVe1KcaUmoDhRu+mmm55++um3337766+/1hDP9u3b33HHHQk++sMPP1ThqaXEBcoXhowCQDXKqPzJv0PlGqJARpFRKBZKJDRw4MCXX35ZTRNjxow5f/58wsK1KKP+1acRWhq6XqkK37JlS5xcPvjgg0q3rD45/rfUi7dLly5xA840Fq3azqvXXnstQaCRUQBktJLolurfmHR3RkaRUah+ak5GIzMGfPrpp5WqQHXG1cCpyGjoV199debMmeSva2S6hDXSR9XuXz3nyc8//5zcuwAZBUBGK4kyzPmjJZKDMcgoMgrIaB6ogd4f7KgW8LjhWWXgu+++izRRZeJMuQZlpHr22Wf9lag1v0pOEiVLCfX9QEYBoLpk1O/S/thjj+W6EmQUGQVkNCuvvPJKaAOU5D8ua1UZ0Igxv6uogpqzZs3KaT3qjhmZG3/27NnVcJIo4Ulow/ytRUYBkNFK4jffKJETMoqMAjJaXCIb6CdOnFjB2hswYIC/SaNGjcpjVdu2bfMH4z/88MMVDPoafuICpURVD11kFACQUWQUoAXJqJLE+Q30r7/+egWrbvfu3eqSFNqkTp065b3CqVOn+vcEdQOo4D76iQsU9126dKk6IdSojJ49e/ZkAdTi3K0AyCgyiowCMlooui6UHCD00/fee69ayStYde+//75/CafvKhq5m/4kohqnn2sX/JJWu1JTNWTmpq9FGVV3iHWFUcE+IQDIKDJaYzKqIRELFy7UbDQaZazokUZ4PPPMM927d+/fv79Sas+ZM6ekE2Hr2al0NkqpqAS0GoTRu3fvb775Rr3fUs6AUB40WY7aH4cMGaL8R1Y/ylzz8ccfawqcXHNBIKOlZty4cX6/TA2drGC96VL1Z8XUbaHot1NRV1dXkX3UDcTvNmChwVqUUd301hUMMgpQeRnVbaiTx5NPPhm6K2ncZacY4vKPppdR3VD81WbNnxKHRC20Ks28nGav/bSCEppOueDrrLXxRfLJJ5+k3CONe9UE5VmHvqrF88MPP8zVutR9LbRhsrfgAjoQytd9//33R/6orLTi14wyPv7yyy+Rg5eDtG3bVqoaUvbVq1eHdj9ufEn5z1LHjh07Qgv37NmzpmV0z549fgP9Z599VtkTaf369f5ps3LlysLPTwlfaLXKGlv+Hdy1a5cGhwU347rrrtMlYH+tRRlVlwNkFKA5yOiJEycKnOlOXawKlFE9hv3VKhCY3x75DznJXNH3unAkAVn3ZefOnZrixe/Elowil+nTGUqnQl+X1QVdLXIuyuqR0fHjx8eJciTanWAqdalnyv585T9LgxHf0MJ33XVX7cqoApAdOnQI/WibNm0q1XLtUA7R0FY99dRTRVmznyvq0UcfLX/cVw1coc3QW65boBZlVB6JUAIgo8hoCWXUnwQyPcqJOG3atAJlVOFGrSf5hyooo4rpvvPOO3lUjpqDNWJagx6Q0YrIqN9SrNpQ0K7iN18JcYkS72/evNk/eTRYqpx7p4QAoQ3QLTo4rh8ZBQBkFBm9AjUo5xoQ9Rk0aFDWHqtxMjpp0iRJW9afqJSMaspyP8yTE5q2UZWDjJZZRtVAH2opFpMnT674nffw4cP+Cb9gwYJiRSX9CZY1rWs5pS30Yqnby8aNG4PLIKMAgIwio5dR57nk78pFXnrpJTXHa9LCG2+8MWFJtfLnIaOReb+rR0Y1UXtkOnHH9ddfr/kYNWpYwqp2+Tirlqwjo+WUUTmZTtrQz7311lvBdwx13FR6TotblxO1A/hnUREHBb7xxhv+61B5dk2dVv2jrMGIocWak4zq/NEFtT1f9F1VGjoCyGj50NP0cY977rkndFdq1arV4zHEdauqZhmN3Gv/i0o083guqB+YvyOavCpy4TfffDNy+zUuPs6x9DzTcGOFcEIPeDUCfvDBB/72p3GLSBnV4Cr/lUPDs/S5Ru4PGzZMP6eJc/S0Lr+MRuamce3vSi+wYsWKUO9D7aMqITKS6o85Q0ZLJ6Pq4OvP+qNXC51UGjQZjJjqUCoNe8eOHbUx5elLqpQLoW17/vnnS7rv2uXyXDJKfOG/Cfuy1WxkVONT1Wm+wFFNWgP5RwEZrTBlTu1Uwcd8kGpI7aSqkGf4K5HmLlmyJPm7iuJIDf3vKkuAUvGll1F5p8bYuv9V5FVpCPft2+d/V6tdtWpVmU9OiUukicpQ1QSc/F1ZnT+uGRktj4wePHjQn45Iv+5/6L8TSqeOHz9e0vNK3UNDv9u3b98irl9XSkVUT2+qoQ4/aq+PbNRuNjKqcZ/rioHWg5EAMoqMtkQZVZOlvwal9kw/f6BczW+VVrrN9DIaRI31hae2KSJbt26NHFOlnFYp42dymsgMXMhoqWVUTQGFdGtRoLSQ5PNZ8V/kiptqSrYUWr9e+Uo9I4buG8pPl/IMbzYy6mxSt4s82uj1LbcGjASQUWS0xcnovHnz/K/rWZJrMsvILqcSmlxlVL0tqy020K5dO387hw4dmtNK1EApO0dGyymjGglUeDdrRfhKN5Fm+g4b+RF5/oS63BSdL774ws/7FndHan4yml+X32AifYwEkFFktMXJaOfOnf0WdmV1zrUGFCP0M8BroqZcZXTRokVVdW0oYuFvpM7MPMJLOuXi8qcio0WXUZ2QGmlXrJF/7777binOLj+COGXKlCKuX2ep32RR0onBNBRMvbpDKbQSurIgo8goADLa0mVUMZLQk0NMnTo1v0rQ1J2hVWlQWuSWxMlov379qu3a0OB3Pz9l1n6icUi1I0fZI6NFl9EJEyYkpz5QoFqDu3W2K7eu8uOqh6hmqEpIbVaK+Kj/clKsvE4Of463rB3B80bDyTVKMvRzEydOTPgKMoqMAiCjLV1GNeWmb1p5zzaplmj/4aph5illVJZWVZPOx+1Rgb36lB4LGS21jKrborp7xk3NoHbkuB3XgCdNiaQ8Hv4X1dty6dKlxT3B/CMSeb0Ugp+PLG7u2VK8uanhJfkryCgyCoCMtnQZ1czpCfkX88CfnSg06XyCjGpkerVdGJGdDgvMdK3E/shoqWXUf8syWrduvWnTpqxfV98MJUfLNUdEUUxRzQvFPYf9vAFF911Ds/iGov4aiZj1NoiMIqMAyGiLllHd/vwvFvgs9Gf/U9bGlDI6bty4arsw/EhP1vlU01S7PzYfGS2ujEbm0lKf5vSioMtKKT/9lSiqWsTt9OtBs6AVcf2KEPu7sGPHjqJfKbqiJfqhH0ozOTAyiowCIKMtWkb9tC+WLf/JAvDbRiOTbEfKaLUNXRLKbxXaSPUsLHy1nTp1QkZLJ6OKfUYGNXMdlqfKkRj568m7H4tP165dQ+v/9ttvi3gCq9eBXxWlSJ7qz1jRo0ePNF9ERpFRAGS0RcuoxjGUYQJSzY6dUkarcKLnF198MY9gT1aU2BwZLZ2M+qmFhOYYy2NVekHyV6WhUcXaVA3SD6184MCBRTyBNXA+tH5NJ1H0y0QztIUa6HWqaJJVZBQZBUBGkdEsTJ48uQwyqqeUnzw/UkZLnf4wD/wufXV1dYWvVkNkkNHSyag/oPuRRx7JO9O7QvuhtbVp06ZYm+pn51Wv6yKewEoU4KfxL+41IosqpOcrMoqMAiCjLVpGldfmmrLgN4/WhIyqDv1EP7t27Sp8zeoXiIyWSEaVACE4r6wxZsyYIh6sW2+9tVjnmD/NrPrJFPEcVqO/33G2uJeJH+bPyaeRUWQUABlt0TIa2ZpZCvwByDUho8qa7ucE3b9/fylOdWS0WDKqA1TcITt6lfJXqONSlK2NTNegjp7FOof920vPnj2LeI343RgUeT1x4gQyiowCIKPIaCoZ9bOCq/1xfgnwN6ZWmun9JKNxE5zmxIgRI5DREsmov9l6o1Ay9rxXGBkgL8ppYBeCn1oh71knQqh7jJ8wVbfZYl0dGgh17733+vNW/JILw4YNC61Buaj8xarq5oCMAiCj1SijkS2D1S+jqpnQt1R75TnWtSKjvjMVJWG4cq+WX0ZzPUtrVEb9acD0RlHgOv0+kblepAn48/EWa+pR9W/28/YXK6bbEDX6qnSUKDcqMgqAjFajjOZ3H4lsGax+Gd28ebM/OU3e4zyapYx27969iL0PHZp2shAZLc9ZWqMyqtTroTXffPPNBa5TydtD69T48WKdY35+frV0682h8DUPGTKk8DtqAv5AMWQUGQVARosgozt37sxjy/1uTzUho8eOHUsz2Kgly6ifPVETmhe4TnVFldUVIqPlOUtrVEbVQbm4p1bk3BD79u0r1jm2Z88ef/2Ft9Sr4+Ztt91W0iSmyCgyCoCMFiqjkXOTLF68OI8tHz58eC3KaENUn8ixY8ciow5/JLW6DxbY0On3jkiQ0QqepbWb2snvKFlIQi6/HoregPDoo4+GfkKfFPgTkbkylHYUGUVGAZDRKpLRyIfW+PHj89hyPxNhITKa61O5EBn1p7vUPIplaKmvFRlVeMkfX1Kgr7/55pvpZbSCZ2ntyqh/EygkeadG5JQ0+5L48ssv/UMzffr0vFcos/G7FhR9s1usjOqWezKD/oGMAiCjhcqoPxRUcpbrL0bOPViIjOaqGpEymlLstm3b5n+3kDE6mtVJ0qPRr8md3mpFRoVS4fgZ1POeEFJpSn27TZbRSp2ltSuj/pwCqvOUEwKF0IBxdTkNrU3uWPR3Hk1UFvoVqV7eSQAis7bNmzevuJuty3xswWjGKT+Nq79YyttpBUFGAZDRPGX05ZdfDi3ctm3bXH+xffv2hcio+iCGvqhJWXLaAPVB9EdJr1+/PuXXfS1QcFTdSfM4gvqWMyetRG3c/txLNSejahP3N1XD4fNYlQRdJ1jk2ZIgo5U6S2tXRrVT119/fVGGqH/wwQf+dJp+3tzCkW/5R0dB9DyaKWbNmuXnx9VZVJ3PnlrMM4qMAiCjxZTRyPiBXvfT/5zfpzBXGfUDA127ds11rx966KHQSjTVZyFPQT265Li5bobfAK1xwZEZGWtIRmUD2gs/dWUeHTcTprxKkNFKnaW1K6OiR48e/s7mOqf8lClT/JX06dMn+VtKvKWUC30yaKR8sCU3+ZXS7zkqNCI+p21WMgE/9K7TVaHx9GvQj6oC9V/9GxlFRgGQ0dLKqD99iPlTykZYNUn7WcRzlVFfFPTkUOt5TnvdpUsXP2NoypiKmgIjexP27t07fSuhIqDqluevRJsRuZIaklEhT/K3Voc+/QTcYuTIkX4AO42MVuosrWkZVRdDf2cV1Eyf8l3bE2l1SoiW8C05X+jNUE0EyV9xRI5syymbmH7Ib+7PKSr86aefBqOq+rfelpFRZBQAGS2hjCoa4XfIE4oKZBUjdazUsy2hx31KGfXTIop27dpFNgXGdcTUI8RfiZr7fR+NjHdqjG1kR0b1Wtu4cWPWXVDrfGQrsOon7jFcWzIq3n77bX+D9ahOkytHOi6zTx6fkSCjlTpLa1pGxYABAyJ3+f3330/2eB2vDz/8MPK7im0nfFGrVX9i/1sKeaZ8c+jYsWPk73bo0GHr1q3JKqObgD9TlE1olPJeNGnSpMhfT9/MgowiowDIaM4yKr755pvI+6/GoqpRLzK4qOCHOnoG4wd6Bvi6kPIBoBESfv82e/ArlrZ06VJNq71s2bLRo0dLDePa2vS0iNyLV1555aefftqwYYN0U/Wgx9Wzzz4buQY/87bbtffee2/lypWR31K2RQ2m8dMZZhWsmpNRHSa/sd4pu8LbkbahQTM6wfxz48EHH0xfV5U6S2tdRhWt93/F1ZuuBT9dq1J+Dh48ODK4aClmk1sb/DuYY9q0aWm2WSOZNCVv3JUovda7a3Dmd72o6NLWOeDnaHODgdLPXKogbuRKWrdujYwiowDIaAllVJJxzz33xD1C9NB6/vnn1fdLATCladQ/NHbEHxygh4HfXTL98M/IToGRxMmonrtPPfVUmjVIg+I2Q31VE76oB5X2UeqpRkONJlaoT0cnUqNd3C7hyV1zMio06U7C/t5xxx2dOnVSRG3UqFFyRxm8olmR8WZFv/SGkJOMVuQsrXUZNblUXDDhrNZfdeHowD399NNx71QuhULQAiOJbKDINQGCjnWcQwc7aeg9U/e6yFBosCdJ+k6fkiH/nHEtAPnNQNuiZFTv/OuKQcpOHQDIaLOSUaHoY9xdOA3WH6sQGZVK+pudk4wKtacnP5myyqg2o2/fvkVJCqhHb3IMqRZlVChEnawsWZHTyzYk9DnJaEXO0mYgo0JpJeJC2umRsMprs/7WsGHD4taQ3L7v++gzzzxT4DYrI1VOuTkVZI3r0aF3qqJMT9q8ZVRz1xVFRsszBx4AMlp1MioUzcrvSa9nvA3QKURG7ZGZRiWTh8RGps5OL6OG4mdptiQOfTdND7MalVGhtl21WuZXOQq/Kc9oQ9Sw+qwyWv6ztHnIqNB5FZfZKmXtpR8rFrcSvcbktM3qh501Ppoc8VUgP9eK0v02cm0vvvhi6S6oZiOj8nXdHAo0Ua2hdN4PgIxWu4wKderKScLUBBYcmVugjDZkOvnFJaFMKaMNmWQ0cT3eUsqoWLVqVVzvsayylTIYU7syaoGrbt265VQzkki14bqsq/7EV2lktMxnabORUQv7qcNlQi+LuOCiegPn9EN+2mDx+uuv57fN6ncR2dMjGW1Dfini9UrsV5E+UQM0MpoSNzlTHqTMAgaAjDZnGbWgl6bbyRp80vSMGpAbGv1QuIw2ZFJajhs3Lm4sgsRCg5myrkQzp6vDoj9tTE750rUlCxcu7Ny5c5pQnJbR8y+n6b9rWkbdy4M6zmZVQy3QvXv3kKP7M0ymlNFynqXNSUYNjbdT7kx/nkyfBx54QMMH9daR60+oX2kox5m68GbtbJrA7t27P/roo4TkXKEBiwsWLCikivT14P1HPZWVWaykB6WZySgA1LyMVgl6fssM5GF6aLlHvjJE6vmkISlqKs1vdqKcUF4nNfnptxTO0dyG6mKoRre42YzibFI7orlY1HavOJzGtaj9Pdf0pQ2Z6Ss1vkqjkWQPLuaqatG4bL0q9OrVa+jQoXpettizZf/+/aoB6aZytTpjUDRLoWVNHDBixIjIFF3SxLxltHrO0hpFL0KKd+rU1Qks2bLac6e0VHLOnDl5zHsUOjrTM/gD9vNDOqveL+owoC4ioeClRs4pzbDOtO3btxfltzRWSXcbVZFeL0s3bgkAABlNi7rvKFy3d+/enESwGaOHk/rXUxsJbxEiq8oULqOcpcVCvWl1Suc9C3z50dmlkK0GVOlMy2OaNAAAZBQAiiyjAAAAyCgAIKMAAADIKAAyCgAAgIwCADIKAACAjAIgowAAAMgoACCjAAAAyCgAMgoAAICMAiCjyCgAAAAyCrXGlClTvio9y5YtQ0YBAACQUYAw7du3v6b0fPbZZ8goAAAAMgqAjAIAACCjAMgoMgoAAICMAiCjAAAAyChAxRg7duyA0rNgwYKS7sWMGTNCv1hXV8fBBQAAZBQAAAAAABkFAAAAAGQUAAAAAAAZBQAAAABkFAAAAAAAGQUAAAAAZBQAAAAAABkFAAAAAGQUAAAAAAAZBQAAAABkFAAAAAAAGQUAAAAAZBQAAAAAABkFAAAAAGQUAAAAAAAZBQAAAABkFAAAAACQUQAAAAAAZBQAAAAAkFEAAAAAAGQUAAAAAJBRAAAAAABkFAAAAACQUQAAAAAAZBQAAAAAkFEAAAAAAGQUAAAAAJBRAAAAAABkFAAAAACQUQAAAAAAZBQAAAAAkFEAAAAAQEYBAAAAAJBRAAAAAEBGAQAAAACQUQAAAABARgEAAAAAkFEAAAAAQEYBAAAAAJBRAAAAAEBGAQAAAACQUQAAAABARgEAAAAAkFEAAAAoEks3Hx4ydUuHL+vu6jPvD12m/337n/9vu8mUuKL6US2prlRjqjfVHqcQMgoAAAD5OGiPkWvkVfhlgUV1qJrESpFRAAAASMWMVfse/Xixc6mbe815Y9SaMfN31W09sv/ombPnL1BFCah+VEuqK9WY6k2152pStaq6pYqQUQAAAIhm674T7T5bbub0+06/vDt2/ZodR6mWAlEdqiZVn1axqmHVM9WCjAIAAMAVjJyzw/qD/u6VqYN+3kIEtLioPlWrqlvrV6rapk6QUQAAALhEz9FrLW7XcdhKtTJTISVCdasatqpWnSOjAAAAAA0a9216RLiuPKiercJV88hoc+b48ePXZHj88cdz+uJTTz1lXzx6NNxR5uLFi5XaqqLTq1cv25Lly5en/Erhu18I/fv3tw2eOXNmRTYg4cRoZpcAFMKxU+fsGXPr23NL/VtTV9Tbb7313Zrg529/fynENXnZ3lJvw4WK3haqijvfmWfVfvjE2Vo0UfVonL/+IMexbKi2rRdpS/bRCsjo4sWL28fw2muvDRw4cMKECVu2bKlOGZWHde3a9eabb/70009boIzu2bPnySefvOuuu6ZNm4aMIqOAjBpz1x74Y/eZrXrOWbvzGIe+RmXUWudlRRr9zUEsM6pz89EW215fARn9+eefr8nGtdde27t37wMHDlSbjK5fv94+vO66606fPt3SZHTkyJG2cNu2bZFRZLT5cejQoZQfIqNBXvz80rDrPmPXcRbVooy6xmJiopVCNd+SO0hUUkb//Oc/d7yS559//k9/+pNT0ltuuWXr1q1VJaNnzpy577779OHLL7/cPPwgJxldt25dq1at9Krw1VdflW6Tzp49a5v08MMPI6MhBg8ebL+u6wgZLXrd/upXv7r//vs3b95sn+gf+l99+N1337VwGT1z7oKt6oY3Z0WqzG9emPIPL/08e81+TqSak1FlF7Kx8/QTrYZXAh2LFpjvqZIyKg2KXEBWpKemLaNGYalJ9ciouHDhwvbt2yulyJWVUXHy5Mn6+vqSbhIyioyWH4U//+qv/upXTdyfwf2v/pRrfLRFyaioP3L6yMlznEi1KKOWT1Qjuzl2FcfG1+uIIKOVl1EzHj0JbLERI0ZUlYwWhZoewFRqkFFktPx07txZ0qn/qoZ///vfm4PqH4qJPvvss/YnZDRBRqFGZVTzAFk+UbI4NTQGmy4qKqnU9CdOn6/IBugoWP7RljY/U5XKqPjll19sMY1qKrr2nTt3btu2bVLeEjmHfnTTpk2HDx/Oaas0Omrnzp379uVwCh48eFCDvbQ7qa+0C9px/XoZZFTVq21Tx4YyyGiuv6UaUAvsiRP5NIWkPDGOHTumTUo+x0KHZteuXQq6Jx/NYsloriebltfCOZ1sBanPmTPqopPHyZMHOhOGDBnyl3/5l3/zN38T/NA11gv9ST6qxZYsWVKIjJ47f3HTnuNFf84VKKPa1PW7jh0/da7UMqod37D7+IFySc+OAycVr01YQIdD23MkF2Xcc+jUlvoTyUngyy+jp89e0I7sPnQqj5wGNtuncrBnXfLoyXMq5y9E/4R+2hbII63Cv3duHLuzaOOlxgfti/73716YXGY5+Wbm9n95dZoduzevvJTKiY6FzReKjFaFjGrgti32wAMPuA8//vjjuzKsWLHC/4r8z/76zjvvxD2Jp06d+txzz9144402TEoffvHFF5G5iiKdQ/piP6Gx/xHX6tGjAwYMuOOOO1y311tvvfWNN97QUz/ZD1avXv3qq6/edtttrrPsu+++G1TGENLczz777Pbbb7flb7jhBvVnGDZsmIQm7it6iL700ktas33lwQcfVEIAaV9OMrpw4ULbfT2Vg5+rDu1zk5VBgwZpv1S9VsmPPPLIggULsq5c49XuasKNEnOfuF0Lyqj91mOPPZbyt7T8+PHjW7du7cbJqR5U1RLHYsmoqvHNN9986KGH3DmgvtGqn1OnTiXUaps2bXQQ3VZpC5VTIng0dV5ZPSiTgy2m3tX2ybfffpuTjOZ6sumy0mjCm266yZ1sTz/9tKo90hT1Dpm8Vapw/VU7GHn+6LpWv2T1Hbfa0IaVMJo4depVV10lB3Vt8QmiqT+5xfQVfVFfz0lGJyzefc9789WrUp+oe+Wtb8/56MeNvjrIdf7QdbpKm8FLI1f76rAVtoAe2AXKqIxqxOwdN70125bRs/+6N2Z1Hb4q2Nq+78hp+zkVW+w3L05xn1xo8pKvZ263T76fvytSZIfP2h6cmPs/u814euASJx8h/vze/ND6g3w769JvjZp7uYOjTNo+bDt4mWr1nR/W/fdrM+y39hxu9NFZq/fbAtYtcvS8nQ/0W2CHQ0Wffz51a4LJfTd3pzb4D10uV4JSB4xduDvyGznJaH5H3Mm0KlZHTcfOflF7pP2aty7t2N+lmw/bCPqscyzJQe0nun27KnKBbftO2gJ5vGxUg4xOWrrHtv8/uk5/eeiKcQt3V0rLdCxsZL2ODjJaeRmV9rmHrvtQlmkfRj423FD3YDDVPYmlKR999FHk4H01wPlBskjnSGjx1K8HR18Fuf766ydOnBjnB9IOJyJB1FEhMq62YcMG+U3kD0mRI1MQjB07VtvgLy+rePHFF9PL6Jw5c2xh1WRktHLWrFnt2rWL3Lbvv/8+eeX79+9PSLDgy+iYMWNkLel/S8cxbts0KE2KVqCM6pVm+PDhEujIn7jnnnu0g/7a3n777bhd1guGc8T33nsvbrHPP/88vYzmerLFnTm2Nj8F2+TJk5O3yl6Hghd18JhqYFzwItJepwoLnT6td9dc09/26dNHZvnrX/9al7/a4rN2CdUCWkwL6yv6or6eUkZle25Km1CR34TikbsOnrI/PfDBwsjVPjtoqS0QTKKUh4zKk2QtkVv1P91nLtty6Sm49/DpyGWsOFkcPHWLfaLYUuiHpLOSy8ivS+kajdwzzmt7zAyt/4pXl2lb7a9fTd/m17Z+S/YW/JW9GRl1VfTxpI09Rq2J3J7nhyyLjOa2/6IurgYe+2Sxv5E5yWh+R9zilH/qPTduwzp/k6oDaI+RjVWh2dKzLulkVCVSdmtdRu/q03jUXh5ad+FCzqHdodO3tf5oUfCELBAdEW2Mjg4yWnkZXbt2rS0m4SiKjBp33323VjI1g4zKPZhff/31QmRUD6p7773X/qSB9kqBtGbNmnnz5mm1Lp60atUqfz3mLnpCd+/eXQ/y2bNnqx3WRaEUMQptlYKsLrqpmKvkTxWlL0pcnI/6wSq3+506dZLDacMUWJWJBmumKDJqIWdVjkKnilD++OOPivm5l4rkdm1F2mZmUBJTd7BmNuFUw/2WVV3K31JM1FXRM888oxpTBE6rdQdIX0kZH42T0ffff9+FQj/44ANtklxNle9G4ykyHRKmr7/+2u1pv379dPTlxKNHj3axYSW1ta/o3LZ60Lltf1IrgX2SNSlv3iebzNWdHgpSal90Vo8bN+6FF16wD/XdUNS/QBl1Z5FOY5mowtjJu6bDqsUsLq5Yr349p8hoHj1BG5p6l6aPjLpwi/xA8VEVKeP/a38pJvfC58vLL6MKvdzW5DF3vzvvhwW7Vm47omVeaMrQ9G8dp1l8VO2/+lxlwuLLcSP7RMWdznEyevLMeTXru/Z9eeSSTYemr9z3/vgN/9jh57hIWyEyGlTqV75aITE1I3RVZNFQhWa7j1g9ta5e+6UwmPtWKLGRYroKbLsI94cTNy7fclh1NWzGtqsysSsV+XRFZPSJ/ovdhvWbsGHhhoPaeEmMC/cq1pv11y3Wqy6SOcnodT1m6cg2JxnVmfxPHRp7aiqCnsfX38i83hSxZV9HpDFg32U6Mlp5GdXj3Bbr27dvsWRUATB1sgx+pa6uziIxep6pM2XeMqqnpn3+4Ycfhm+dX3yRvFVq1ldcJ/iVRYsW2Z/uvPPOUOzNeUAo+KcGd0mw/UnTCgS/opCwfT506NDQ2qSzxZVREZoOQL/i4pcKs6VqpEjXZzSn35Ib2edqQw/1enQHSIKet4zKCE2JFGIMxdjUQK+uJvYVGXDwT3JQfaiW99AJoPC2/UmolTz4p0L6jOZ0su3du9fEUe9RoTkOzp8/L3V2ulxcGdW/03cV/eabb0JNEMGOnmlkVIOTcr2D2XimnGRUCeFDo0P03P3XTO80PXHVi7TMMiqpss9f+rJOTb1X/OnHS3+S34S0LKHPaJyMupCw4kahrplSuv/INP2r08Kq7UeLKKMyG3WBDR/upipqrNt+C7Q7V7xJjt9gf3rxiytug2oEt89V7aGNUf3/9sUp+tM/vzI11L5fBhlVv1X78M4+80It7JqA4DeZDft9x2nJrQXWRq/uE2lOeyej1iWg1/drm5OMnjp73jZ+54FT1SCjwrq1tJyW+mqUUSmFoi8ulhMMKBYio4riqIHb/5ZaBm0BdYzLW0YVN4rbMD1ZLf6qgJm/Hv0psv+rM8hg/4GlS5e64Kv/FY19sdCXhDX4xLWvqKdsZGBJT9YiymiXLl381lIXmtViRZTR9L+lBmiL/yni6EdnVQkmi5LCNEOaIk8MBZvbZIjsIeAioDqx3YfKkGUfBo+XQ2Zsf1V4slgymtPJpl6q9qFeCyMvUnXV9c/5AmVUnVkT+j37+P0uRo0alfK7emew8fJZl1SdB3uv2lj7rM36To8UAoyMPPX/aZMt0OnrleWUUbU7W1RScRd/7I7MRnGvxuHVL08NdiHIQ0a18r9v32hFCgNbx80QPy7ZE7mzhcioBFExwoh3j6Yq0soPHgvbkl4VnNuFjtG9789XUUDXX+fjn1yKTW7ee7zMMqruqvah3iv8r7gOGMnpKodkjppEKicZ1TlmrxAhT/JldM7aA+r58MHE8GNX3Xb1ubrhFi6jqhMFv3XUdNLqcHw2dYti+eE7/PmLOi0V/1Y3XzUIaIKGxYGjqRNbG6MOu7bxTw5Yov8Nqbb8vu2QZTrz1YygTtUrAjNUKa6v5e2SUedd/Vs/1JAZC6V/q09IaGN0curzjsNWZO1VZIKrY4SMllxGFcT6+Up++OEHPQJds6bfdFiIjMbltdFgIJNFtbPnLaMarmGfR47b0DP+eIb0WyXTsgU2brx8NruGYElh5Les6iRVl59GTV0S4wI5OQ1gyiqjkemW3HHp1q1bEWU0/W+5dv84G1Y8OzJymV5Gk4lUZEmwmZkU2Zdg/dXOmVCYsBSpnfyTTa6pTq4WawxFUv2dCopagTKaa7qu4L0iZQ9ah9pJ0kRGLRP+X/zFX7iYq0VGFQJPKaNxqZ2kRNZY/8fXZpRTRl2D+9vfR088+OZ3l7pUKnhZiIy6SX26DY8e8qKAohvVFLTVQmQ0rrbjqsjxz5l8OgpXpz/9eoxcbeuctmJfmWVUs0de6nb86ZLIOF/yyHfDZqIfEzXmLEFGZXLSKf1DbhcMMPsyquFx+t8Hvf2yPrvBA5GfjGr9FqaVGdvhMyMMvmzovLq/7yU1v7rLdDfSa/CUS5ez+hvoQ/e5/a7GF14OJcy8FB3XuWFdIPSK9XXTea5xcqHv/kvmFFIPEOsTEkqdoTdP65matcJ1XFrUbPVVOh2oBTI1YjcUJimFjAobXq1m1mADbk4yqgeVayiUqIWaVvPwA7Um2wIrV64MRbC0nXG9G9966y37lssq5Zr145L4lEFGFbJNCOgWV0Yjf0s9PZIl3nWOnDFjRlFkVDKnDiHqzrs8g4u+h2xYfTfdaaDrImFIe0ll1D/ZXC6LRx99NG612n0/sltmGQ32M8mpWkwo1Tk1+KHuKqF4pzPR0A1HX8wqsmnyjNpIdj1KXVt5GWT09SaF0rcif8JJ5E9L9xYio/bcVdE45bgaeKtJfIOdNSsio9ZnQIHVhGOqw6QDpIig4lsW4oqsyTLIqDz+6qah/c8MXKoRRaHuFmmwITspZ6IPyqgCyeoDEOrLUWYZ3bH/pHWTkFYeOn5WEX0l5rQIZXAgmvWsVS8Ra53QaLa+mS4Z6skQjGe7ZnodjiteuVfu0+WpHsY6PxXL1AmpPAz6XX1dPSVCUcxgM33jiPhMFend7/JRu3DRuhprtVkr3N437royVI+MlklGpVlqy1bT2yeffBI5KrxEMtqhQwdbJhgEynU0vfbLjQWxISmKSk6ZMiXSMPKTUTd0KSvSIPuKjbtXxaqfX4uVUfVrTFlvWdMkJcuoXmZkYxoZFjliXejEDi5/5MgRjdQJdnlUc8GXX37pDl+lZFSt+a7dPGHNylxmkd1KyaheAl2uK8sOkSYNqimmUocG1VOrUs6mv/3bv3UfxploQ6aJ3zKPaplCZPTRjxfZMsqIWTYZfa5pDVlLsIkwDxl9rKkVe/mW2H5vX/5yySy/nb2jamVUTjngp03aKnlJZEUpDFZmGW3IjHFRX2S3DQoNyko1ssqdS1mx0Uspc90HZVT/q0FvVl2uC0qZZdQ24Po3ZgXbu+1D14tXsWGdD7f0mrMx0C1bO2I7PiJwykXKqITSAq6hWVKVBCMzYdWKBBl1b31tA2asFxjry5s1kVZDU9eRljOGqZIyqoHMh64k68CFEslojx49fPPLVUYbMlO36+FtA1mCIV6pW2jX8vADreGa1MyfP9/ic7YxQV1ogTLqRtlnJTQiKicZ1ZuMWn7dqiRJ6ooqPdLgfSXXjJTRhsxgIMVN3XAlxxNPPBF5RMojo3PnzvWb4H1cOlV3epdZRhsyA62k7wp+K01EypFPZplB7zSs4d4+TzBRk1EtVriMariMLbN086Gyyah6QKaU0T5j1xUio3f2ueRkSqUZVwPjmvo+vjdufXXKqMb+2zhrl2fgxjdnq++giotNVkRGxYFjZ7p8szK4eeZwartPqHOHzUefRox8GRXqXmm9bK0zQJll1MROLebBfrFSUDWLZ51UwpJ/BRNaRcroim2NsUk1u4dSB9hJG7wQImVUF3Wopb73mHUJiVrDz8HzF2yeemS05DKaPJq+nDLqolPBSefzkNFLN4gDB7SPGg4VzAaq8UPBNJN5+IHM0iKvepBPzoZrlLdt0BdbsozqDcEFPpPrLe8+ozqgbkCPuvaGhsppU5M7rUpJdaBlpWrydnk99Q8//0B5ZFTbb5+oI0HCmlu1amXjotxIsvLLaH5YM71/XZiP2iT1cSba0DSGqfBmerUe2jKuya8MMmoO0di+OXWLIkkJpcA+o25QiD+23fFZ0xcVeqxCGV2z86gGcplSaB9Dhuf6GJS/mT6IXEfGrGN9S2BaAUXUIgddBbElU14yvoxqwicLHA7M9L8ss4yqW8I9mdcqbYOMXIPhEhxU5qoTTMm81IKvaRH+JxNRDr5rRcqo9RZV11iNQwoWc0p1+HZZFCJlVH+0bgOupV79Wf30YcU6QMhoc5BRSY+1ZUuDCpfRIDI8BcZcWtBC/MBtkrYz/SSTbrixWoRbrIy6r1jAuEAiTwyX/0Ej8PyvZJXRKyTm2DGF+lzy/FDGsfLIqNJRuYbvuNW6PqMSOPdhrchoQ/wAJvs8wURNZIN7nbeMKsZmD103EKQMMuo+nLkqh5SKecioJe72x/dE+py64lWhjL705aV095Gd/KpERoMoTOi2WZM2JQ/ZLjAy2tCU+kqmrv6X5R/ApHNSJ7N91zZDoukaGQx1WrirKUIvbZUdqqO2zfmpabqSZVSOm9x0cKBppFRcaifLoWZ9WPVKZgluU07ZSmS0qmVUib7dTD/FklE9U/3USznJqNRwXYbIccdyC0tlqqTcbjxWfjLqPlR61JQ15kbTK51ki5VRlwVWoccSyah7TYpMHxYpo9pUO22Cr0CXn7hN2U8160/5ZVTYaHrFPuPGaSlnvn+5uQ9DZ0gVymhCaifpZsLUoLmmdorTI5mKjab/r26Xh80p1bx9S5PrlEhGTRFU+v+0qaQyqtw99qGyeMat9vZ3LqWUXxeInrqE/JGzxpdTRk2L1TQfKRBFkdH8jrgysypuvS4q5KwtdRH35Gz2hfQZdb/10IcLLXWrS31aNhk19MayYP1BRSttegU13Ls5aaWYlq5B0x8EW/PNMrNGRk0xJbiz1+yPLO4dMk5GrU6spX5AJpVbXAoLH/qMVrWManIa+66mOPL/6qIykTKqp+COHTv8b7m82cHIZU4y6gQoLozUtm1bW8CNysrPD9Roax9qDs+4KgoJsUZQJYxEUeuw+g9UrYyqq2VRZNRlpL/99tvjIsRxCYxSyqgbAxfZ0O9mcAjKqDvE6qDpf8Xlolejf6SM6siWVEbdNsfFOF1PXOV4ch8qj4SbYjciCLRrl734VYOMli3pveIxwYG3jkFTNtsCmm0y+Pm/ZQbhKnjjxwX18HNNsXnL6Ibdx2wgjua/1jDkyI33B8E4GVVTY0oZVTOupcLRvgTnu3dothv7llpCr7hhNo1SD/YT8ONVpZZRCajlSdUR9F1U+uKmOS1ERvM74n9omi/gaFTF9m3K4a/pvhJ+N+/R9KFYrKWtdRnBnIza24hqqaQyGjxe9nrgQsJKMmBnbOjwpZRRa6ZXt5asP52Q9P6ezEkycckedTLWP1akq+0GRtNXuYyqmdVNNB/K+qTc+G4wezDHZHD6GUXaQmEeJVbUxDPW9h3Kx5ReRqV0FkbSSpT8PLTNElCXTrJAP9APucSKw4YNC6V8V4VIXJR0JjiJoj60Tghi4sSJoV/R/NrFnYGpKDLa0DRMW03Vfi6C/H4rOGOQ38lBU4mqg6ZqLzJImUZG3Vb506m79PWh6cRcy766UvhjwDWUys+T33h//+679GOtCjnZZO0W0dfZu2xZeM5ul1hXZ1dw2JDq1rxf0hnye6Ubc6di9choeaYDVcwm5CXqjGjDX/TEDTW/3tOkOOMX7Q49a10uocag186j+cloQyBBprp1+j3tLHmNOtiFpimyxk0ltfEFKG4GJjfVkzKNh9amp74c1P4azH/eEGjf94d6aBJwVwNDSx8ZdWOwQsdC++JyBZhqpJFR3bD1JuAHWfM44q4t3p9WXtZ4X1NmzVA2/hB55xkN/UnzjrqZmYIyqtihhSpD55jtVIEyqjNKh+yzK3PCK6tocCKlb5o6fYYiqRbNzSqjygJhfUP9uQNCMXuT0cjpA5TfQH+yUYPXR73IxUGe0aqWUT2z3fNM0UGZn1pFZRIa82sPzmQZFRr/O3DgQLXB6fkqn3PJkkKzGjbk2GdUQVl7Blt6VHUYUCueJkxSVMmNpA72JszPDxoyOXfcaP2OHTuqVVSjifVzchSXUlS7H/RUFxy1wdE2nb2Wd9OHVqGMuo62ypyqDdPxcsaT32+ps4Sb8F1pZXW8tm7dqnnVJ02a5JKzavB7XNw064mhw+SOiwLkCrcvXLhQGup2xFDehmAA2I15UrBNx2X37t2KHep8dkdfZ7Umagr+us55+5N6lShSrkBsiZLaBhsiZJbqNqBzT29WeiF0XTu0y36PEddjQb0qdbHrfU/ZW3XU3JVbJTKqNzEbq6Sc/9rTrM3uWkCLqSXBhjfp6+ll1J5D8oa56w6oSVENdi5Ht3Qw9EVLT2NTNym5kkIpmslGwybctDqFy6jilH9oGgmuLnT6ReW+UZ8/tW8qHbebfzwUN7XQjqXs1ogQhZ2cXybMTa+s4PYnPY8lbYq5qnFZiZwstqeiiXNCNaBnv4UkVZQfR7+1bMthJYF64fPlwQTjZZDRwU3Ra32uCJmk+ee6eqn8Na/PDB4LdZ3MKqMyS5vmXqNnQrME5XHEpWv2bmBTmKqK6o+cXr3jqPowuG/peCX3TsxvBiZfRqV3zqeD7f4Kjdvx+nbWdrclrpfFm4XJqCUFU5qkfUdOh/aoMbVTJsY8NxMZDZ7/0mKX/rbzNyuTZdR5s/YuOCmDJFs/EZz7atTc6A4JDZmMB5YPVcWfjCprtJUZmKpURhsySWdCuZMcLoV4pIyqIdU9dEPIa/2OcbkOYJIXJuQBVSt5MNNn3n7Q2LY1a5ZFcyORhPmRv+HDh0dWmtRcXRqqUEbliKFNVQ75An9LiSTdMfXRO4P0NM22xaV2cpntQ+iscFNrPvnkkyFN7NSpU9wmSQH1PhN+JJw/7xIq+dHW4spo49v5mDGKtUdunnJXRU4ioEwOwTfDIHpfsleCapBRvY9Z709HQj9R/Sm4pL4Y6subIKNKJuriWKGiCI3fkitruScm+5LySrqnfiEy2pAZTuGcyS9q2QymZjS+n78rtJjTjjgZFYpRuSlw/KIkRMFJRy+/KoxdF7m8dE1fKZuM6lg83n9x5JYo2q2eiPbv0OyRkTKqIKX7bmjC0jyOuNi057j1koz7Yv2R08mnaH5z0y+OGqSviK91gA51QrUkSvbOowS31hpgCQoKlFG9UEn+rK+CTjDN8uVm83L9N6SktowdL/3bXnLsRUhvR1llVPkTbAi8pl9S7xGF6jU5kzZMErwskD1XVW1f13FUH9PQO4DlJVXRfqW/QTE3fbXLqFi9erUmhgmmylfQRS6osFaCjNrU8yNGjNA8fu67sjpNBRmZKDuP0fTSHbldMNmk9ShQHC7UqaAQP7AgTc+ePV2oz2WmVKjsYsy7sB7zisM5JVU7uNagzXDNvlUlo6ouBZiDAl24jFpkXRE+RUCDa1aIUb+VPkFBQtJ7vSkF43/SuFdeeUXxTv2u9SHR77rJsZpa7i5q5nr1Kg7OmCANVQxbUdLIDVCs0eYMK4OMNmS6gepFLviipU3V5gXzoIVQFjNJdrCSda5K1nVYrZ9JNchoUDT9zKObM7hlXIr7BGGNk1Gbel7ticFwmp7KehjHDWRWxFFDfV100II9mvNaD2DXM69AGW3IJMf58MeNioAGw40a7auUn5FZcvRof3/c+mDi9zQyah7z0Y8bZQPBH9JDXfGkC/HzVaq3wO87/eKWV20o5qfH+U/L9pZNRq2WFMyzbp0u1aj6MKiKpGVuX7LKqBtXpL+GeizkccSdkGlL9Ou/efHyF1VpCr0fO3UuzVlqAfLkcU5pZLTxKs4M0AnJqN40FIB054y2TUdNZ13hMmqHRuOWXO4FU14lAb3iWXn8bHADdA0qKawFMoNTv8bJqAVT1cn1900ngP7x1KdLJN+hxdxotsYsEFc+hdX7ImGAWiQ6Ii1q9FJlZLRYSAXUbqgWZ6WhyeO7aqaXJZRo26TFimmp9Tz99OX5IUXT01EOEWrMTfAS7biapy9evFgTh1itwHr3CAlc4Ug91clYVSGjKnpVSGhk9upAkmY2oGDIU9qnoykHzfpFWZ1SPi1evFjbn9Ov5I1qSaeNhlVpNNjp06fTfEUXpi4BVUXRD18psJ6g6i+h17OrrrrKIqD6x5AhQ+xPefQu9ZGdqJleMxmmjEUpnKamRgUXS3q96nGrAIw2TAOtsuad0S5IGhQWCs4AniyjwR9S8ksl39l7+HS6s66xqVf9AWQneUx3WURULepgoGOhzgyFHIu4EWMFHnG5lI6g6lYhupy+2GPkmsiOp8VF3RLUNyNrpLaQy0r2llC3tgEp8wbEoSipLpCE6lWLvDbDTwFhyXQHTdmc/res27SODjIKcAVnm+ATPinnJ2U7w/UK8etf/9o1xN+Xwf2v/pS1U2lLJqWMQrVhLfUKWKbMNgq5ovcEhWMV5bUxVametprXPtMm0HLa6JFRSMvCJviET8r5SVmNKtNYLwd1DfT6hylpwrSfIF77dnVTLqE91EZt8ejHjZ1iB/28haoohYlaL4uuw1el/5aORaav+eIWVVfIKCCjfIKMXiIy/ElMNBn1d/xj90v99pZvOUyF1BYzVu1rHFT0ytQCW7EhiDpDa0IHG9Sl7siR6WAj0VH4XSbVho4LMgqAjPJJS5RRyAkNH1FqKpdjSErqD82B6qfdZ8t1+DoOW0lVFFFGNWpKyR80pcWKbUfSf1FHQcdCR6Sl1RgyCqmgLyOfVOQTLr1q5oGmvDmWGzxyzD5UP5nEro0xvJFzdlAbxUKjppRnN6evqP5tPno/zT4yCgAAEIEycitdkYrywPvJbqCGMA1Smb/+ILVREVTzdgha5isBMgoAANDS6Tl6rY2sr9t6hNooM6pzG0Gvo9AyawAZBQAAgEuz1cuKiI+WE9W2mWjLmYkeGQUAAIAkH6X/aNlwHSRasokiowAAAHAZa6+38fXkeyodqlsbO9+SW+eRUQAAAIhA4TobX6+cl8rBzvxMxUX1qVq1fKKqZ4LQyCgAAACEUXYhyz9qvUg1W7omXqdaCkR1qJq0HqKWT7QFZnFCRgEAACAtmgfI5gu1cnOvOW+MWjNm/i6N/lYrMxHTZFQ/qiXVlWpM9abaczWpWm1pcywhowAAAJAnSzcf7jFyzR+6THcuRcmvqA5Vk6pPTipkFAAAAPKxUs10oHHfd/WZJ6+yfqWUuKL6US2prlRjqjccFBkFAAAAAGQUAAAAAAAZBQAAAABkFAAAAACQUQAAAAAAZBQAAAAAkFEAAAAAAGQUAAAAAJBRAAAAAABkFAAAAACQUQAAAAAAZBQAAAAAkFEAAAAAAGQUAAAAAJBRAAAAAABkFAAAAACQUQAAAACAisto/amLLaG8891OlRays9VfOJ8pXLlc9QCAjCKjFGSUwpVLQUYBABnlkcZjifOZwpXLVQ8AyCiPNEozl9Gde+qXLatfML9+/jxKrZQv+41XoR5qvixaeG7rhotnz/CAB0BGkVFEsOXJ6Jp19T071t/yz/uu+l8UCqWS5T//z+G295768buL58/zpAdARpFRSguQ0X2H6994uf7qv0YCKJSqKgfv++OZRbN52AMgo8gopVnL6PqN9Xf/J099CqVKy9V/fXL4IJ73AMgoMkpppjK6fXd9q9/xvKdQqrzgowDIKDJKaY4yevxsfetWPOYplJqIj9JeD4CMIqOUZiejI4ZGPvbq//1/19/7x/on76h/6k5KrZQVd92iQj3UfHnk5v3X/Sa6/+j9/8N4JgBkFBmlNCMZPXamvtU/RZjo213qd9VT81y5lEoVGefpaRMPROW1ODVpNA9+AGSURxqlucjotCkRJjp0IHXOlUuphqv+fP1u30cPt72PBz8AMsojjdJcZPS918MmqnZ56pwrl1I1V/3paRP8/KMXz53l2Q+AjPJIozQLGX3+wbCMjh5OhXPlUqrnqr94/tz+a/8udJ2e37aJZz8AMsojjdIsZPSRm8MyumghFc6VS6mqq/7QI+F8F2dXLuHZD4CM8kijNAsZfejGsIzW1VHhXLmU6pLRp+4Iy+jyBTz7AZBRHmkUZJTClUtBRgGg1mR0x9HzCzefnLz8yIb9ZyMX2Hr4/KaD51zR8skrtMU2Hzzn/2nnsQtuPXtPlu+RFtqFUNFfXVXof7cdOZ9cXQmVEFrDlkPnEn7Xld0nLuw6fiHNkm5Tk/coWPneGs41Pxm1ag+VPScuRC6s2rYFkg90qOh0jTv/tZ50x+5c5BFJc4C0L8t3nPpp+ZGZa49vPHA2eb9c0ekXtzvBhZOv6GDdxlVpKa/cxt/V1RH5V23Psky1zF53fNPBc2nuA3M3nJi68mhOh75YJaEOdaQib5h7mg5T3CEOFd1gI+8Pm3O/3yKjAMhomWRU9+XH3l/3d48t+L+PXip/7LDspUGbQs+wP7+52i1g5arnl9zXa81X0/f5jzHdPW2Z3z2z2L/h3ti5zv76VL/15ZRRfxeC5bbXVtpivUbs0P8+8PaahFW9PHizlukwaHPkX0Nr+H3bJQm/68qPSw9/OmlPmiW1I2n26HfPLHL2H/XXxa26rug+bNu6+rPNQ0Z7jdzh7+bfP7Hw+o51z320Yd7GE8GFJy07Ygs8/v669Ps7Zv5B+9adPVaF/vRwn7Vpjt2t3VYGL5C4okMTXLk8rN/YXf/ZfplbQBds6z5rZVShzXD7FSz/8NSiGzvVvfjppsVbT8YtfHv3VQk7/qfXVrolf15xtMwyet2ry/W7n0+t9+8zH0/Y/d8vXVEtuqvM2XA8cj2z1h1/sPflw/Sbxxfe0m3Fe2N2FkXRUpb/eHGpfvrbWftDnysKYDfhJVceI5Wvp+/X5zr69r+6USSfPB+N3x13f9DlcM0ry9sN2Lhwy0lkFAAZrRYZHT3vYFBDg+WmLitW7DydxuT0nFt05a0tTkb1+SPvrnNP5TwiE4XLqO7pd7y+yi96VJdIRh96Z23wh/4r8+z8p6cXhTZgxppjI2cfCH1oR+e6jnXBD/Xrafbo3p5rQjKqI2V/ur37yqtfWOqcdUrd0eYqo0FNeevb7QXKqETHrTB0zpdORhUEvbdn9NUnnRo0eW9WGQ0u/8nEPXELq20kcq/nbzwRXKxKZHT7kfN6i7ZN+sMLS/VvndWSLZPv7+cf9F8k7GpSJdz1xqpXhmzWhWnLt/l4o4smlrq0+XiDfrHLl1tDnw+fud/2RW+koT91+nyLPn++/8agjGpfIq96la9n7I+8P+h945qXl9su6+tjFx5CRgGQ0crLqFxTSmQ3pg6DN3839+CSbaf0bLu/1xq7LerO5cvoB2N3jZpzYOi0fa9/vU1yYx/+y3OL1YCYVUYVh7PPZUIrd50u8yPNduH9MbuyOE2xZTRUFLdoDK29virNNssUtfD4xYcL2SMno9NWXaERio7c/UbjGuTHado3a0VG9V6hU9TKZz/X6ynu3rhGzD6Qt4yu33fWHuRW3hi+PfhXBSndj6poG2wxyUfwc5lE8AKRNgX/6srEJZeP+ONNyqWrRoFA/ZAuVZ17wZi675eK9Nuq9Iaj686FD3/7+MJfVh2LlNEe32yL3HF32VaVjHb6olHR/vHpRSPnHAg2vNjrrip22fbLd6S19Wd0L9Ln2pdNBy6f6gpDXptZuYSvPDI66Ke9je8kTe0wrrww4NIJ85h3Qip8q8+HTNkblFGdivnd8dQSYu9UarHZnldHBWQUABktpozqOW23PwUJQn9yjVlqxA/JqDpmBXuS6bv2ubTGtXZFyqieJe4BrChg+YdBIKMhGQ0+pINP9FqXURlb6E9fNJ176oKSt4wqpmhf+dc2iy0al9B7UttgC787emdkL8C4fiyh8n1Tx4D/7rCsbucV72+uU4deGt2WJOyXa5RwkfWQjF4dtUfqofjv7ZbYK2v1yKga4m17xi065PfrtUDy433Xuw/1EmI9i/wWeXM7Hc3yNNbP33TCorOu57dtsypZfqn/6pIP9o5Vf1nbU9ewXqCMWr9VW2d+92FkFAAZLaaMKhBiT5eBP+0N/Un3fb03qwz4cU+CjNpj1R4VKhr/FPes1Z9cVMnvL4WMVkpGVdSarz/1HrWjGcuo66unYkN/8pDRm7s2Bqi0HieaCQ2dxZJRFxb1e0yqtMpskspPTZdewn6NzAhZsHuoW1hdYOP2aMyCQ/YnNW1Xj4yqWUaf3BFzEU1YfNhiwC4Iqh63+kQhxsixWbJ5vaX4nTVLUeSd/9ZmSSierc6sdlwsZumOpopi5CGNLlxGXUd2hdiRUQBktMIyqg6jLqiZtctUnIwGQ54u4hJ61qq9zA3ieee7HYXcypHRosuotjOhibbZyKjEJRjsz1VG56w/bsurnXfN3jMWWHr2ww0llVG1ov6/JxdaD+PI0e4DmoKjXYduzSqj6khjf7qhU11oYV1T6mkTuUdPf3CpSfftUTuqR0bVizryLdoV2VvwvVeSbXqq41jx1EhP9F1vde4+0SAqfaIaVi8p/SPYuVmL6RMdhVAotxAZXbHrtNVGXO4UZBQAGS2fjKp/lTXAqWjEscZsJnQhSpBRJZppiris9J+1amZyw+fVf67AtjBktLgyqg5k//xso4UMm76vGcuoauBfM7Kl5lGzulxltOuXW235WZnz3wxeQhD3OC+KjGqMlBtNGB0CXHJpYLWa4LPKqHtpdO3XQRl99bMttkfBpFHaO2vQ6PbV1p4jtleJjOoeYlsVd12o6F6kBfr+sMulI7gtkxBAUUkp7PZKJHUKnRsaPhW6kKevOWYvDMEepQ+/09hjqn9g2FkhMqoLQR0bFILV50oxwQAmAGS08jJqQ1j+0DSq2gIwEixfN5NlVE8s1+cs9KzVCi0MYF1FNx8sNL1l4TL6zIcbNPoqVILP15qTUY3A0IMtVDQeIquMql3SBjBpdEt+o4lrQkY1GFxjmELvSznJqFTGQvuyopDYDfDGPucko1KKV4dsDpX3vt/pOreEXDNUFmy6NM7d6Uvkfuma1Tlpbx3BfpZuYQ3GUvdBfzS3i7wqMdZrX22tEhl1N5xF8fmJrL1bDh0MDJuh2n2pzScb1TSUNV9yKYq9ves9xHroqgen3pFcQ7ziAoq7W8I1fWJHLRjQdaPp/ateJTj0zc+2YfcTyyyRX75YZBQAGS2+jNqYet2XQwme1D8slO4nQUbdk1UjW0OfhEpoAHJFZDSyKPFe7cpoZHEjdYIyqmyRSpWgogSxUlg9AvWh+oyu3nOmOaV20uNciZas/M/Ly4PpNt2AvJxkVKO7bGHXs1adES045w+LzklGI4vr2uj6a8YFsVZm2lst+Vdov3RwpVwq2mu3ZjX6D/m53h96b5mGFH8NJtx16UXtk1ebhipWXEbdXq+Iz8hh2QxCQzNV7dJr11DTOBbtucU6pmVWUm2GXdeWD9X68rrUct0y0q92KpdUSz0ogg1KyXlGhwaaOOLuD7rbq1tO3nuNjAIgo8WXUSurdp/+cNxuyyHintxBB0qQUb3E2580tiPyWavgq3mPboKTA33zKyKjEhRlpwqVHxYcql0Z1XNFhy9U1K/Rl1G/KICad4CktvKMyrQmLDmcX57RR9+7NIoomDT+8aaQvxPcPGRU14UOYqi0b/KSn5oiow8HmnT9odlBg0zIMyrxmnXlxesWtjPZbfP8zAQB85rSiw7KdM3UC1u1RUbnbzwR9xU1gGiB177aGheblPO5RiG9mxVrTrKUxRrfrc+rZelyScds9JW1bCidkx8XNxlVj0//qlcJ9kCw+4POQKVEsKKuWdp36wesxHz5TXiBjAIgo6WS0eDjzWX2VhzFxcwSZFRpC0OjdIMyqm6pag52ox/+O9+UlvQZLbzPqPJ+r917xooFYFp1WVFIL97qlFG9U6l3spWOn2/RW5bGr4R2M72MSuvtVUoNqepZ60r7gZcSQ3aLMp6i9BmV+16araBzXcKwcRXpcmi/dGTV4G7F5WYKTU3kFlbWd3urtHCvBmm59KJ6KbUx6WYwVdVnNCGbQajPaNzAdh1HG+qk6azKKaM2YklNUpbnoXHgf9NdUQFL1fnV7RpTTVl8V6kAfBktZACT1mw9aN/Mq6kKGQVARosmo3pLVo8rlWCyenerur7jpZYszQuSVUaV/sn+9OqQ8Gh63WQtbqSfcx227BZc0zLaMTMnSrBxP1jUGyEhmlUlA5g0TsUGUH8RlTOo+Y2mj5s2M6uMfpDJCpRQ1J3Unza9KDJqXmIdYCKnK3OXnulj3H71b8qQ+vCVyuUW1vtnsKulTEg/bQrrrlaXprQaRtPfkJluIzisJzJ1kQs3Zj0TftuY+LN8wVHVoeWOtfCzmyzNSuvMbF56c7Add5MUFDG1k0UH7nlrNTIKgIxWUkbVmuMeh35srF/TA9g95OJkVA8tl8HRzZQd+axVmMd1X3MT1tWojFr+c43+SWgi7Prl1iofTa+4kXWi2HbkPDIaV9w0YwnFz9dYrDyjrpkiMo2R9fJUcT28I/dLJ8B/Nc3ApAUSZPSHpl6qHQZvDk3vVFUyau97SrMaGdcfv/iSrlm4UbWtQfSq6glR15GLswYTf5a66Iio3Umxal28+mkF7/13DN2EtYDeQ0KvOkWRUZ2W+pMWQEYBkNEKN9O7jNnfeGro1NM9AiNlVO137mGpYTHJMzCpDMxMhWdd8lfsrOHpQG2Yc+OUg15cWRFHi8p8OW1flcuoXkjsRSLvKm32Mjq9aYy5qlrVGCouU6+/kmLJ6PimhngdqcVXZmV3M0IpTOguvbj9ctOtqYHCX9jJqFowrg6k11Dozi1cVTKqNALWd2KkF/tU3bq8Ge5Da5XuGDXnp3oi+R2Cy1BsmijbtYWbT4bSXNhhjbwRFaWZ3v6kKVWRUQBktMIyqkepDaKXVPX4epueMbodqxOVGuZc3hl3g3Yy2mf0zq+m79O7u/pNupio+l0FR2QnPGsf7nNpolHdi/PorVglMuru5nLKYD8Hza5pz2x18vObbqswz+iQjKPoV9buPdMyZfTWbit1PvvFZl+07JuNg3gm741M+WSz6aiRN1SBxZJRFXXotIX1W1qbwnt6e3TDiXQJBxNfxMmoLPPappnSXOO1L6Mu6OhPUVFVMto4gVxmEibdo76YVu/uJHoVtLFBCigGX3fdvU7hxmAWM42FeiizvCZhKs90oP7pGkx84YrLAuFXWoEyqgiCndU6aaczHSgAMlpxGbW55tzcSBFZQqZlzxJiXhXKsZLwrJWz2vM76/CCysqoHmaKTPjFPePloOpaZyqvNbfrv1Fjcm2ed8WWNGQ1bv1Fl1G9CURuqooldk2QUTmKTWYTSoLTcmQ0rmgl6oJi3Wr12F6/L3rcsbPVD8fvLpGMqhOFmxQ0VGQkodkKEiK+6htzKQ/Uq8vtTSlSRt2oKdlbXcDnqk1GtQvtmtLHKhmnWmY0ZsvCpY0t8kvCl4x6INjR1GWrV2LZ/IO91+oyt+UTBuaXqExsmrCgc1R4UjHL0ESvfmqnuKve3eL8+4N6a5iUq6LSXCbIKAAyWg4ZtTyjGhWuJnu7j9sT7rH318248qU5JKO6iWuQ07MfbVACbT83UPKz1s2RrR+ate54dcpoXGkVmAtHeVI0R7Zza+t/qYBxcA6bSuUZtWLjoJOnAx2VSaKpo79wy0lkNCijGr1n/04Yaj2l7kjkgPciyqhF4qViroeo+bGSj/qjCRNkVCtp1bSGwZlAb6SMqthUCKHGgWqTUSt6YXbdjeyWoqRIccnwl247pb9aVnmX/V5t9wn5SktXth4+/9vMLdfNQRAslmJWu+NnA03OMxqcG9a/P+h+olwTEnH1cyjKsEVkFAAZLY6MBociafymEkqXubmqbI+00hU19s1ce6xu5+mqrbcSlaqS0ZZQ9Hahi1RSFdcJpGVeuY3Vsv74su1pq0WvkVpenWo4o5BRAGS0umSURxoFGaVw5VKQUQBkFBnlkYaMIqNcuRRkFACQUR5pFGSUwpVLQUYBkFFklEcaMoqMcuVSkFEAQEZ5pFGQUQpXLlc9MgqAjCKjPNKQUQpXLgUZBQBklEcaBRmlcOUio8goADKKjFKQUQpXLgUZBQBklEcaMlrE8/nhm8Iyunw5Fc6VS6kyGb0dGQVARpFRSjOV0SfCD7n6WTOocK5cSlVd9Qfv/WPoOj23po5nPwAyyiON0ixktNOzYRn9+B0qnCuXUj1X/YUD+/b9+/8OXacXDtTz7AdARpv9I22HCs+D5i+jX/QPy+h1v63fvZ8658qlVMlVf+zdrqGL9MCt/8KDHwAZ5ZFGaS4yum596DnX6KOP3oKPcuVSquGqPzliiH+FHuvdkQc/ADLKI43SXGRU62/7QISPXv/39f3frZ87u37lKkoNlUH9f1GhHmq+LF16auKoQ8/e5V+b+67+63Ob1/PgB0BGkVFKM5JRPfm8HmkUCqU6y9GeHXjqAyCjyCilecmoyqB+POMplOovB+7+jwtHD/PUB0BGkVFKs5NRle4v8KSnUKq57G/1j+e2buSRD4CMIqOUZiqjmfhoPY98CqUqy8GHbzi/dxfPewBkFBmlNGsZValbUd/mPh78FEoVBURv+oeTo764eO4sD3sAZBSgpXBu07oTQz8+0unJQ4/fevDB6ygUSrlL6xsPt7332HvdTs+YdPHMGW5KAMgoAAAAAAAyCgAAAADIKAAAAAAAMgoAAAAAyCgAAAAAIKMAAAAAAMgoAAAAACCjAAAAAADIKAAAAAAgowAAAAAAyCgAAAAAIKMAAAAAAMgoAAAAACCjAAAAAADIKAAAAAAgowAAAAAAyCgAAAAAIKMAAAAAAMgoAAAAACCjAAAAAAANDf8fZHwDswJKmGMAAAAASUVORK5CYII=' }, 'attributes': { - 'width': '100', - 'height': '100', + 'width': '150', + 'height': '250', 'style': 'width:250px; height:250px;' } }, diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index cd3ec636f..5cf7f2802 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## 0.8.0-dev +* **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library +from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports +embed, this won't affect how quill js work +* Improvemenets to the image embed +* Add support for `margin` for web +* Add untranslated strings to the `quill_en.arb` + ## 0.7.2 * Fix a bug when opening the link dialog for both video and image buttons * Update `README.md` diff --git a/flutter_quill_extensions/README.md b/flutter_quill_extensions/README.md index 8368999b8..400d1e8af 100644 --- a/flutter_quill_extensions/README.md +++ b/flutter_quill_extensions/README.md @@ -14,8 +14,8 @@ to support embedding widgets like images, formulas, videos, and more. - [Platform Specific Configurations](#platform-specific-configurations) - [Usage](#usage) - [Embed Blocks](#embed-blocks) - - [Custom Size Image for Mobile](#custom-size-image-for-mobile) - - [Custom Size Image for other platforms](#custom-size-image-for-other-platforms) + - [Element properties](#element-properties) + - [Custom Element properties](#custom-element-properties) - [Drag and drop feature](#drag-and-drop-feature) - [Features](#features) - [Contributing](#contributing) @@ -130,37 +130,40 @@ As of version [flutter_quill](https://pub.dev/packages/flutter_quill) 6.0, embed The instructions for using the embed blocks are in the [Usage](#usage) section -### Custom Size Image for Mobile +### Element properties -Define `mobileWidth`, `mobileHeight`, `mobileMargin`, `mobileAlignment` as follows: +Currently the library has limitied support for the image and video properties +and it supports only `width`, `height`, `margin` ```json { "insert": { "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png" }, - "attributes":{ - "style":"mobileWidth: 50; mobileHeight: 50; mobileMargin: 10; mobileAlignment: topLeft" + "attributes": { + "style":"width: 50px; height: 50px; margin: 10px;" } } ``` -### Custom Size Image for other platforms +### Custom Element properties + +Doesn't apply to official Quill JS -Define `width`, `height`, `margin`, `alignment` as follows: +Define flutterAlignment` as follows: ```json { "insert": { "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png" }, - "attributes": { - "style":"width: 50; height: 50; margin: 10; alignment: topLeft" + "attributes":{ + "style":"flutterAlignment: topLeft" } } ``` - +This works for all platforms except Web ### Drag and drop feature Currently, the drag-and-drop feature is not officially supported, but you can achieve this very easily in the following steps: diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types.dart b/flutter_quill_extensions/lib/embeds/embed_types.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/embed_types.dart rename to flutter_quill_extensions/lib/embeds/embed_types.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/formula.dart b/flutter_quill_extensions/lib/embeds/formula/editor/formula_embed.dart similarity index 90% rename from flutter_quill_extensions/lib/presentation/embeds/editor/formula.dart rename to flutter_quill_extensions/lib/embeds/formula/editor/formula_embed.dart index 41b61e098..baee3c50e 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/formula.dart +++ b/flutter_quill_extensions/lib/embeds/formula/editor/formula_embed.dart @@ -1,7 +1,6 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_quill/extensions.dart' as base; import 'package:flutter_quill/flutter_quill.dart' - show BlockEmbed, EmbedBuilder, QuillController; + show BlockEmbed, Embed, EmbedBuilder, QuillController; class QuillEditorFormulaEmbedBuilder extends EmbedBuilder { const QuillEditorFormulaEmbedBuilder(); @@ -15,7 +14,7 @@ class QuillEditorFormulaEmbedBuilder extends EmbedBuilder { Widget build( BuildContext context, QuillController controller, - base.Embed node, + Embed node, bool readOnly, bool inline, TextStyle textStyle, diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/formula_button.dart b/flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart similarity index 98% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/formula_button.dart rename to flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart index e821d689d..935389f6b 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/formula_button.dart +++ b/flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; -import '../../models/config/toolbar/buttons/formula.dart'; +import '../../../models/config/toolbar/buttons/formula.dart'; class QuillToolbarFormulaButton extends StatelessWidget { const QuillToolbarFormulaButton({ diff --git a/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart new file mode 100644 index 000000000..2ddc38dbf --- /dev/null +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart @@ -0,0 +1,86 @@ +import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart' hide OptionalSize; +import 'package:flutter_quill/translations.dart'; + +import '../../../models/config/editor/image/image.dart'; +import '../../../models/config/shared_configurations.dart'; +import '../../../utils/element_utils/element_utils.dart'; +import '../../widgets/image.dart'; +import 'image_menu.dart'; + +class QuillEditorImageEmbedBuilder extends EmbedBuilder { + QuillEditorImageEmbedBuilder({ + required this.configurations, + }); + final QuillEditorImageEmbedConfigurations configurations; + + @override + String get key => BlockEmbed.imageType; + + @override + bool get expanded => false; + + @override + Widget build( + BuildContext context, + QuillController controller, + Embed node, + bool readOnly, + bool inline, + TextStyle textStyle, + ) { + assert(!kIsWeb, 'Please provide image EmbedBuilder for Web'); + + final imageSource = standardizeImageUrl(node.value.data); + final ((imageSize), margin, alignment) = getElementAttributes(node); + + final width = imageSize.width; + final height = imageSize.height; + + final image = getImageWidgetByImageSource( + imageSource, + imageProviderBuilder: configurations.imageProviderBuilder, + imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder, + alignment: alignment, + height: height, + width: width, + assetsPrefix: QuillSharedExtensionsConfigurations.get(context: context) + .assetsPrefix, + ); + + final imageSaverService = + QuillSharedExtensionsConfigurations.get(context: context) + .imageSaverService; + return GestureDetector( + onTap: configurations.onImageClicked ?? + () => showDialog( + context: context, + builder: (_) => QuillProvider.value( + value: context.requireQuillProvider, + child: FlutterQuillLocalizationsWidget( + child: ImageOptionsMenu( + controller: controller, + configurations: configurations, + imageSource: imageSource, + imageSize: imageSize, + isReadOnly: readOnly, + imageSaverService: imageSaverService, + ), + ), + ), + ), + child: Builder( + builder: (context) { + if (margin != null) { + return Padding( + padding: EdgeInsets.all(margin), + child: image, + ); + } + return image; + }, + ), + ); + } +} diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart similarity index 95% rename from flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart rename to flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart index c53796efa..44d2dfaa1 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart @@ -4,8 +4,8 @@ import 'package:flutter/widgets.dart' show BuildContext; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; -import '../../../logic/extensions/controller.dart'; -import '../../../logic/services/image_picker/s_image_picker.dart'; +import '../../../extensions/controller.dart'; +import '../../../services/image_picker/s_image_picker.dart'; /// When request picking an image, for example when the image button toolbar /// clicked, it should be null in case the user didn't choose any image or diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart similarity index 94% rename from flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart rename to flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart index 1ee3201ad..27c83ecef 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart @@ -1,9 +1,7 @@ import 'package:flutter/cupertino.dart' show showCupertinoModalPopup; import 'package:flutter/material.dart'; -import 'package:flutter_quill/extensions.dart' show isMobile; import 'package:flutter_quill/flutter_quill.dart' show - FlutterQuillLocalizationsWidget, ImageUrl, QuillController, QuillProvider, @@ -12,10 +10,11 @@ import 'package:flutter_quill/flutter_quill.dart' getEmbedNode; import 'package:flutter_quill/translations.dart'; -import '../../../../logic/models/config/shared_configurations.dart'; -import '../../../../logic/services/image_saver/s_image_saver.dart'; -import '../../../../logic/utils/string.dart'; import '../../../models/config/editor/image/image.dart'; +import '../../../models/config/shared_configurations.dart'; +import '../../../services/image_saver/s_image_saver.dart'; +import '../../../utils/element_utils/element_utils.dart'; +import '../../../utils/string.dart'; import '../../../utils/utils.dart'; import '../../widgets/image.dart' show ImageTapWrapper, getImageStyleString; import '../../widgets/image_resizer.dart' show ImageResizer; @@ -34,7 +33,7 @@ class ImageOptionsMenu extends StatelessWidget { final QuillController controller; final QuillEditorImageEmbedConfigurations configurations; final String imageSource; - final OptionalSize imageSize; + final ElementSize imageSize; final bool isReadOnly; final ImageSaverService imageSaverService; @@ -70,7 +69,6 @@ class ImageOptionsMenu extends StatelessWidget { getImageStyleString(controller), width: width, height: height, - isMobile: isMobile(supportWeb: false), ); controller ..skipRequestKeyboard = true diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_web.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_web_embed.dart similarity index 88% rename from flutter_quill_extensions/lib/presentation/embeds/editor/image/image_web.dart rename to flutter_quill_extensions/lib/embeds/image/editor/image_web_embed.dart index 2ed5b967e..96c95ee76 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_web.dart +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_web_embed.dart @@ -4,10 +4,10 @@ import 'package:flutter_quill/flutter_quill.dart'; import 'package:universal_html/html.dart' as html; import '../../../models/config/editor/image/image_web.dart'; +import '../../../utils/dart_ui/dart_ui_fake.dart' + if (dart.library.html) '../../../utils/dart_ui/dart_ui_real.dart' as ui; +import '../../../utils/element_utils/element_web_utils.dart'; import '../../../utils/utils.dart'; -import '../../../utils/web_utils.dart'; -import '../shims/dart_ui_fake.dart' - if (dart.library.html) '../shims/dart_ui_real.dart' as ui; class QuillEditorWebImageEmbedBuilder extends EmbedBuilder { const QuillEditorWebImageEmbedBuilder({ @@ -43,7 +43,7 @@ class QuillEditorWebImageEmbedBuilder extends EmbedBuilder { // if not then it will add the data:image/png;base64, at the first if (isImageBase64(imageSource)) { // Sometimes the image base 64 for some reasons - // doesn't displayed with the + // doesn't displayed with the 'data:image/png;base64' if (!(imageSource.startsWith('data:image/') && imageSource.contains('base64'))) { imageSource = 'data:image/png;base64, $imageSource'; diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart b/flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart similarity index 96% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart rename to flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart index 552211b19..fb00a6564 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart +++ b/flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill/translations.dart'; -import '../../../../logic/models/config/shared_configurations.dart'; -import '../../../../logic/services/image_picker/image_picker.dart'; +import '../../../models/config/shared_configurations.dart'; import '../../../models/config/toolbar/buttons/image.dart'; -import '../../embed_types/image.dart'; -import '../utils/image_video_utils.dart'; +import '../../../services/image_picker/image_picker.dart'; +import '../../others/image_video_utils.dart'; +import '../editor/image_embed_types.dart'; import 'select_image_source.dart'; class QuillToolbarImageButton extends StatelessWidget { diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart b/flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart similarity index 65% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart rename to flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart index d40fd8f14..74de2c819 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart +++ b/flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/extensions.dart' show isDesktop; +import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_quill/translations.dart'; -import '../../embed_types/image.dart'; +import '../editor/image_embed_types.dart'; class SelectImageSourceDialog extends StatelessWidget { const SelectImageSourceDialog({super.key}); @@ -14,28 +16,27 @@ class SelectImageSourceDialog extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ - // TODO: Needs to be translated ListTile( - title: const Text('Gallery'), - subtitle: const Text( - 'Pick a photo from your gallery', + title: Text(context.loc.gallery), + subtitle: Text( + context.loc.pickAPhotoFromYourGallery, ), leading: const Icon(Icons.photo_sharp), onTap: () => Navigator.of(context).pop(InsertImageSource.gallery), ), ListTile( - title: const Text('Camera'), - subtitle: const Text( - 'Take a photo using your phone camera', + title: Text(context.loc.camera), + subtitle: Text( + context.loc.takeAPhotoUsingYourCamera, ), leading: const Icon(Icons.camera), enabled: !isDesktop(supportWeb: false), onTap: () => Navigator.of(context).pop(InsertImageSource.camera), ), ListTile( - title: const Text('Link'), - subtitle: const Text( - 'Paste a photo using a link', + title: Text(context.loc.link), + subtitle: Text( + context.loc.pasteAPhotoUsingALink, ), leading: const Icon(Icons.link), onTap: () => Navigator.of(context).pop(InsertImageSource.link), @@ -54,7 +55,12 @@ Future showSelectImageSourceDialog({ showDragHandle: true, context: context, constraints: const BoxConstraints(maxWidth: 640), - builder: (_) => const SelectImageSourceDialog(), + builder: (_) => QuillProvider.value( + value: context.requireQuillProvider, + child: const FlutterQuillLocalizationsWidget( + child: SelectImageSourceDialog(), + ), + ), ); return imageSource; } diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart b/flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart similarity index 96% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart rename to flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart index 937d64435..c62976cb4 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart +++ b/flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart @@ -8,10 +8,10 @@ import 'package:flutter_quill/flutter_quill.dart' QuillToolbarIconButton; import 'package:flutter_quill/translations.dart'; -import '../../../../logic/models/config/shared_configurations.dart'; -import '../../../../logic/services/image_picker/image_options.dart'; +import '../../../models/config/shared_configurations.dart'; import '../../../models/config/toolbar/buttons/camera.dart'; -import '../../embed_types/camera.dart'; +import '../../../services/image_picker/image_options.dart'; +import 'camera_types.dart'; import 'select_camera_action.dart'; class QuillToolbarCameraButton extends StatelessWidget { diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart b/flutter_quill_extensions/lib/embeds/others/camera_button/camera_types.dart similarity index 94% rename from flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart rename to flutter_quill_extensions/lib/embeds/others/camera_button/camera_types.dart index 1e2931230..28f7c5aac 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart +++ b/flutter_quill_extensions/lib/embeds/others/camera_button/camera_types.dart @@ -1,8 +1,8 @@ import 'package:flutter/widgets.dart' show BuildContext; import 'package:meta/meta.dart' show immutable; -import 'image.dart'; -import 'video.dart'; +import '../../image/editor/image_embed_types.dart'; +import '../../video/video.dart'; enum CameraAction { video, diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart b/flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart similarity index 95% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart rename to flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart index f8823cf13..bd6527db7 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart +++ b/flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/translations.dart'; -import '../../embed_types/camera.dart'; +import 'camera_types.dart'; class SelectCameraActionDialog extends StatelessWidget { const SelectCameraActionDialog({super.key}); diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/utils/image_video_utils.dart b/flutter_quill_extensions/lib/embeds/others/image_video_utils.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/utils/image_video_utils.dart rename to flutter_quill_extensions/lib/embeds/others/image_video_utils.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button/media_button.dart b/flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button/media_button.dart rename to flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/unknown.dart b/flutter_quill_extensions/lib/embeds/unknown/editor/unknown_embed.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/editor/unknown.dart rename to flutter_quill_extensions/lib/embeds/unknown/editor/unknown_embed.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/video/video.dart b/flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart similarity index 95% rename from flutter_quill_extensions/lib/presentation/embeds/editor/video/video.dart rename to flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart index a4b2a9b80..831da538b 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/video/video.dart +++ b/flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart @@ -1,9 +1,9 @@ import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; -import 'package:flutter_quill/extensions.dart' as base; import 'package:flutter_quill/flutter_quill.dart'; import '../../../models/config/editor/video/video.dart'; +import '../../../utils/element_utils/element_utils.dart'; import '../../../utils/utils.dart'; import '../../widgets/video_app.dart'; import '../../widgets/youtube_video_app.dart'; @@ -22,7 +22,7 @@ class QuillEditorVideoEmbedBuilder extends EmbedBuilder { Widget build( BuildContext context, QuillController controller, - base.Embed node, + Embed node, bool readOnly, bool inline, TextStyle textStyle, diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/video/video_web.dart b/flutter_quill_extensions/lib/embeds/video/editor/video_web_embed.dart similarity index 89% rename from flutter_quill_extensions/lib/presentation/embeds/editor/video/video_web.dart rename to flutter_quill_extensions/lib/embeds/video/editor/video_web_embed.dart index abc821d08..650cd5431 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/video/video_web.dart +++ b/flutter_quill_extensions/lib/embeds/video/editor/video_web_embed.dart @@ -5,10 +5,10 @@ import 'package:youtube_player_flutter/youtube_player_flutter.dart' show YoutubePlayer; import '../../../models/config/editor/video/video_web.dart'; +import '../../../utils/dart_ui/dart_ui_fake.dart' + if (dart.library.html) '../../../utils/dart_ui/dart_ui_real.dart' as ui; +import '../../../utils/element_utils/element_web_utils.dart'; import '../../../utils/utils.dart'; -import '../../../utils/web_utils.dart'; -import '../shims/dart_ui_fake.dart' - if (dart.library.html) '../shims/dart_ui_real.dart' as ui; class QuillEditorWebVideoEmbedBuilder extends EmbedBuilder { const QuillEditorWebVideoEmbedBuilder({ diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart b/flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart similarity index 74% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart rename to flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart index 78fe6177b..f4c030c9f 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart +++ b/flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/extensions.dart' show isDesktop; +import 'package:flutter_quill/translations.dart'; -import '../../embed_types/video.dart'; +import '../video.dart'; class SelectVideoSourceDialog extends StatelessWidget { const SelectVideoSourceDialog({super.key}); @@ -14,28 +15,25 @@ class SelectVideoSourceDialog extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ - // TODO: Needs to be translated ListTile( - title: const Text('Gallery'), - subtitle: const Text( - 'Pick a video from your gallery', + title: Text(context.loc.gallery), + subtitle: Text( + context.loc.pickAVideoFromYourGallery, ), leading: const Icon(Icons.photo_sharp), onTap: () => Navigator.of(context).pop(InsertVideoSource.gallery), ), ListTile( - title: const Text('Camera'), - subtitle: const Text( - 'Record a video using your phone camera', - ), + title: Text(context.loc.camera), + subtitle: Text(context.loc.recordAVideoUsingYourCamera), leading: const Icon(Icons.camera), enabled: !isDesktop(supportWeb: false), onTap: () => Navigator.of(context).pop(InsertVideoSource.camera), ), ListTile( - title: const Text('Link'), - subtitle: const Text( - 'Paste a video using a link', + title: Text(context.loc.link), + subtitle: Text( + context.loc.pasteAVideoUsingALink, ), leading: const Icon(Icons.link), onTap: () => Navigator.of(context).pop(InsertVideoSource.link), diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart b/flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart similarity index 96% rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart rename to flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart index dd2fb8f58..f8b854b3c 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart +++ b/flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart @@ -2,12 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; +import 'package:flutter_quill/translations.dart'; -import '../../../../logic/models/config/shared_configurations.dart'; -import '../../../../logic/services/image_picker/image_options.dart'; +import '../../../models/config/shared_configurations.dart'; import '../../../models/config/toolbar/buttons/video.dart'; -import '../../embed_types/video.dart'; -import '../utils/image_video_utils.dart'; +import '../../../services/image_picker/image_options.dart'; +import '../../others/image_video_utils.dart'; +import '../video.dart'; import 'select_video_source.dart'; class QuillToolbarVideoButton extends StatelessWidget { diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart b/flutter_quill_extensions/lib/embeds/video/video.dart similarity index 94% rename from flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart rename to flutter_quill_extensions/lib/embeds/video/video.dart index d69ab5e05..b2e1ca24e 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart +++ b/flutter_quill_extensions/lib/embeds/video/video.dart @@ -2,8 +2,8 @@ import 'package:flutter/widgets.dart' show BuildContext; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; -import '../../../logic/extensions/controller.dart'; -import '../../../logic/services/image_picker/s_image_picker.dart'; +import '../../extensions/controller.dart'; +import '../../services/image_picker/s_image_picker.dart'; /// When request picking an video, for example when the video button toolbar /// clicked, it should be null in case the user didn't choose any video or diff --git a/flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart b/flutter_quill_extensions/lib/embeds/widgets/image.dart similarity index 99% rename from flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart rename to flutter_quill_extensions/lib/embeds/widgets/image.dart index 13c77eb9d..d072f202b 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/widgets/image.dart +++ b/flutter_quill_extensions/lib/embeds/widgets/image.dart @@ -7,7 +7,7 @@ import 'package:photo_view/photo_view.dart'; import '../../models/config/editor/image/image.dart'; import '../../utils/utils.dart'; -import '../embed_types/image.dart'; +import '../image/editor/image_embed_types.dart'; const List imageFileExtensions = [ '.jpeg', diff --git a/flutter_quill_extensions/lib/presentation/embeds/widgets/image_resizer.dart b/flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/widgets/image_resizer.dart rename to flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/widgets/video_app.dart b/flutter_quill_extensions/lib/embeds/widgets/video_app.dart similarity index 98% rename from flutter_quill_extensions/lib/presentation/embeds/widgets/video_app.dart rename to flutter_quill_extensions/lib/embeds/widgets/video_app.dart index 5b794394b..334f64ca1 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/widgets/video_app.dart +++ b/flutter_quill_extensions/lib/embeds/widgets/video_app.dart @@ -6,7 +6,7 @@ import 'package:flutter_quill/flutter_quill.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:video_player/video_player.dart'; -import '../../../flutter_quill_extensions.dart'; +import '../../flutter_quill_extensions.dart'; /// Widget for playing back video /// Refer to https://github.com/flutter/plugins/tree/master/packages/video_player/video_player diff --git a/flutter_quill_extensions/lib/presentation/embeds/widgets/youtube_video_app.dart b/flutter_quill_extensions/lib/embeds/widgets/youtube_video_app.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/widgets/youtube_video_app.dart rename to flutter_quill_extensions/lib/embeds/widgets/youtube_video_app.dart diff --git a/flutter_quill_extensions/lib/extensions/attribute.dart b/flutter_quill_extensions/lib/extensions/attribute.dart new file mode 100644 index 000000000..2467be3f2 --- /dev/null +++ b/flutter_quill_extensions/lib/extensions/attribute.dart @@ -0,0 +1,32 @@ +import 'package:flutter_quill/flutter_quill.dart' + show Attribute, AttributeScope; + +// class FlutterWidthAttribute extends Attribute { +// const FlutterWidthAttribute(String? val) +// : super('flutterWidth', AttributeScope.ignore, val); +// } + +// class FlutterHeightAttribute extends Attribute { +// const FlutterHeightAttribute(String? val) +// : super('flutterHeight', AttributeScope.ignore, val); +// } + +// class FlutterMarginAttribute extends Attribute { +// const FlutterMarginAttribute(String? val) +// : super('flutterMargin', AttributeScope.ignore, val); +// } + +class FlutterAlignmentAttribute extends Attribute { + const FlutterAlignmentAttribute(String? val) + : super('flutterAlignment', AttributeScope.ignore, val); +} + +extension AttributeExt on Attribute { + // static const FlutterWidthAttribute flutterWidth = FlutterWidthAttribute(null); + // static const FlutterHeightAttribute flutterHeight = + // FlutterHeightAttribute(null); + // static const FlutterMarginAttribute flutterMargin = + // FlutterMarginAttribute(null); + static const FlutterAlignmentAttribute flutterAlignment = + FlutterAlignmentAttribute(null); +} diff --git a/flutter_quill_extensions/lib/logic/extensions/controller.dart b/flutter_quill_extensions/lib/extensions/controller.dart similarity index 70% rename from flutter_quill_extensions/lib/logic/extensions/controller.dart rename to flutter_quill_extensions/lib/extensions/controller.dart index 474f3cf44..d1687ea7c 100644 --- a/flutter_quill_extensions/lib/logic/extensions/controller.dart +++ b/flutter_quill_extensions/lib/extensions/controller.dart @@ -1,7 +1,5 @@ import 'package:flutter_quill/flutter_quill.dart'; -// ignore: unused_import -import '../../presentation/embeds/editor/webview.dart'; import '../utils/quill_image_utils.dart'; /// Extension functions on [QuillController] @@ -12,27 +10,6 @@ extension QuillControllerExt on QuillController { int get index => selection.baseOffset; int get length => selection.extentOffset - index; - /// Insert webview embed block, it requires [initialUrl] to load - /// the initial page - // void insertWebViewBlock({ - // required String initialUrl, - // }) { - // final block = BlockEmbed.custom( - // QuillEditorWebViewBlockEmbed( - // initialUrl, - // ), - // ); - - // this - // ..skipRequestKeyboard = true - // ..replaceText( - // index, - // length, - // block, - // null, - // ); - // } - /// Insert image embed block, it requires the [imageSource] /// /// it could be local image on the system file diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart index 22a69a4fe..b3f7f3bd0 100644 --- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart +++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart @@ -6,52 +6,49 @@ import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; -import 'presentation/embeds/editor/image/image.dart'; -import 'presentation/embeds/editor/image/image_web.dart'; -import 'presentation/embeds/editor/video/video.dart'; -import 'presentation/embeds/editor/video/video_web.dart'; -import 'presentation/embeds/editor/webview.dart'; -import 'presentation/embeds/toolbar/camera_button/camera_button.dart'; -import 'presentation/embeds/toolbar/image_button/image_button.dart'; -import 'presentation/embeds/toolbar/video_button/video_button.dart'; -import 'presentation/models/config/editor/image/image.dart'; -import 'presentation/models/config/editor/image/image_web.dart'; -import 'presentation/models/config/editor/video/video.dart'; -import 'presentation/models/config/editor/video/video_web.dart'; -import 'presentation/models/config/editor/webview.dart'; -import 'presentation/models/config/toolbar/buttons/camera.dart'; -import 'presentation/models/config/toolbar/buttons/image.dart'; -import 'presentation/models/config/toolbar/buttons/media_button.dart'; -import 'presentation/models/config/toolbar/buttons/video.dart'; +import 'embeds/image/editor/image_embed.dart'; +import 'embeds/image/editor/image_web_embed.dart'; +import 'embeds/image/toolbar/image_button.dart'; +import 'embeds/others/camera_button/camera_button.dart'; +import 'embeds/video/editor/video_embed.dart'; +import 'embeds/video/editor/video_web_embed.dart'; +import 'embeds/video/toolbar/video_button.dart'; +import 'models/config/editor/image/image.dart'; +import 'models/config/editor/image/image_web.dart'; +import 'models/config/editor/video/video.dart'; +import 'models/config/editor/video/video_web.dart'; +import 'models/config/editor/webview.dart'; +import 'models/config/toolbar/buttons/camera.dart'; +import 'models/config/toolbar/buttons/image.dart'; +import 'models/config/toolbar/buttons/media_button.dart'; +import 'models/config/toolbar/buttons/video.dart'; -export '/logic/extensions/controller.dart'; -export '/presentation/models/config/editor/webview.dart'; -export 'logic/models/config/shared_configurations.dart'; -export 'presentation/embeds/editor/image/image.dart'; -export 'presentation/embeds/editor/image/image_web.dart'; -export 'presentation/embeds/editor/unknown.dart'; -export 'presentation/embeds/editor/video/video.dart'; -export 'presentation/embeds/editor/video/video_web.dart'; -export 'presentation/embeds/editor/webview.dart'; -export 'presentation/embeds/embed_types.dart'; -export 'presentation/embeds/embed_types/image.dart'; -export 'presentation/embeds/embed_types/video.dart'; -export 'presentation/embeds/toolbar/camera_button/camera_button.dart'; -export 'presentation/embeds/toolbar/formula_button.dart'; -export 'presentation/embeds/toolbar/image_button/image_button.dart'; -export 'presentation/embeds/toolbar/media_button/media_button.dart'; -export 'presentation/embeds/toolbar/utils/image_video_utils.dart'; -export 'presentation/embeds/toolbar/video_button/video_button.dart'; -export 'presentation/models/config/editor/image/image.dart'; -export 'presentation/models/config/editor/image/image_web.dart'; -export 'presentation/models/config/editor/video/video.dart'; -export 'presentation/models/config/editor/video/video_web.dart'; -export 'presentation/models/config/toolbar/buttons/camera.dart'; -export 'presentation/models/config/toolbar/buttons/formula.dart'; -export 'presentation/models/config/toolbar/buttons/image.dart'; -export 'presentation/models/config/toolbar/buttons/media_button.dart'; -export 'presentation/models/config/toolbar/buttons/video.dart'; -export 'presentation/utils/utils.dart'; +export 'embeds/embed_types.dart'; +export 'embeds/formula/toolbar/formula_button.dart'; +export 'embeds/image/editor/image_embed.dart'; +export 'embeds/image/editor/image_embed_types.dart'; +export 'embeds/image/editor/image_web_embed.dart'; +export 'embeds/image/toolbar/image_button.dart'; +export 'embeds/others/camera_button/camera_button.dart'; +export 'embeds/others/media_button/media_button.dart'; +export 'embeds/unknown/editor/unknown_embed.dart'; +export 'embeds/video/editor/video_embed.dart'; +export 'embeds/video/editor/video_web_embed.dart'; +export 'embeds/video/toolbar/video_button.dart'; +export 'embeds/video/video.dart'; +export 'extensions/controller.dart'; +export 'models/config/editor/image/image.dart'; +export 'models/config/editor/image/image_web.dart'; +export 'models/config/editor/video/video.dart'; +export 'models/config/editor/video/video_web.dart'; +export 'models/config/editor/webview.dart'; +export 'models/config/shared_configurations.dart'; +export 'models/config/toolbar/buttons/camera.dart'; +export 'models/config/toolbar/buttons/formula.dart'; +export 'models/config/toolbar/buttons/image.dart'; +export 'models/config/toolbar/buttons/media_button.dart'; +export 'models/config/toolbar/buttons/video.dart'; +export 'utils/utils.dart'; @immutable class FlutterQuillEmbeds { diff --git a/flutter_quill_extensions/lib/logic/extensions/attribute.dart b/flutter_quill_extensions/lib/logic/extensions/attribute.dart deleted file mode 100644 index fd939e774..000000000 --- a/flutter_quill_extensions/lib/logic/extensions/attribute.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter_quill/flutter_quill.dart' - show Attribute, AttributeScope; - -class MobileWidthAttribute extends Attribute { - const MobileWidthAttribute(String? val) - : super('mobileWidth', AttributeScope.ignore, val); -} - -class MobileHeightAttribute extends Attribute { - const MobileHeightAttribute(String? val) - : super('mobileHeight', AttributeScope.ignore, val); -} - -class MobileMarginAttribute extends Attribute { - const MobileMarginAttribute(String? val) - : super('mobileMargin', AttributeScope.ignore, val); -} - -class MobileAlignmentAttribute extends Attribute { - const MobileAlignmentAttribute(String? val) - : super('mobileAlignment', AttributeScope.ignore, val); -} - -extension AttributeExt on Attribute { - static const MobileWidthAttribute mobileWidth = MobileWidthAttribute(null); - static const MobileHeightAttribute mobileHeight = MobileHeightAttribute(null); - static const MobileMarginAttribute mobileMargin = MobileMarginAttribute(null); - static const MobileAlignmentAttribute mobileAlignment = - MobileAlignmentAttribute(null); -} diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart b/flutter_quill_extensions/lib/models/config/editor/image/image.dart similarity index 98% rename from flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart rename to flutter_quill_extensions/lib/models/config/editor/image/image.dart index 8d6646f0a..208568bd1 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart +++ b/flutter_quill_extensions/lib/models/config/editor/image/image.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart' show VoidCallback; import 'package:flutter_quill/extensions.dart'; import 'package:meta/meta.dart' show immutable; -import '../../../../embeds/embed_types/image.dart'; +import '../../../../embeds/image/editor/image_embed_types.dart'; /// [QuillEditorImageEmbedConfigurations] for desktop, mobile and /// other platforms diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image_web.dart b/flutter_quill_extensions/lib/models/config/editor/image/image_web.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/models/config/editor/image/image_web.dart rename to flutter_quill_extensions/lib/models/config/editor/image/image_web.dart diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/video/video.dart b/flutter_quill_extensions/lib/models/config/editor/video/video.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/models/config/editor/video/video.dart rename to flutter_quill_extensions/lib/models/config/editor/video/video.dart diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/video/video_web.dart b/flutter_quill_extensions/lib/models/config/editor/video/video_web.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/models/config/editor/video/video_web.dart rename to flutter_quill_extensions/lib/models/config/editor/video/video_web.dart diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/webview.dart b/flutter_quill_extensions/lib/models/config/editor/webview.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/models/config/editor/webview.dart rename to flutter_quill_extensions/lib/models/config/editor/webview.dart diff --git a/flutter_quill_extensions/lib/logic/models/config/shared_configurations.dart b/flutter_quill_extensions/lib/models/config/shared_configurations.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/models/config/shared_configurations.dart rename to flutter_quill_extensions/lib/models/config/shared_configurations.dart diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart b/flutter_quill_extensions/lib/models/config/toolbar/buttons/camera.dart similarity index 93% rename from flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart rename to flutter_quill_extensions/lib/models/config/toolbar/buttons/camera.dart index dafdf1abe..32e2f7549 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart +++ b/flutter_quill_extensions/lib/models/config/toolbar/buttons/camera.dart @@ -1,7 +1,7 @@ import 'package:flutter/widgets.dart' show Color; import 'package:flutter_quill/flutter_quill.dart'; -import '../../../../embeds/embed_types/camera.dart'; +import '../../../../embeds/others/camera_button/camera_types.dart'; class QuillToolbarCameraButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/formula.dart b/flutter_quill_extensions/lib/models/config/toolbar/buttons/formula.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/formula.dart rename to flutter_quill_extensions/lib/models/config/toolbar/buttons/formula.dart diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart b/flutter_quill_extensions/lib/models/config/toolbar/buttons/image.dart similarity index 95% rename from flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart rename to flutter_quill_extensions/lib/models/config/toolbar/buttons/image.dart index d1e5efbd1..61e8901a0 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart +++ b/flutter_quill_extensions/lib/models/config/toolbar/buttons/image.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart' show Color; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; -import '../../../../embeds/embed_types/image.dart'; +import '../../../../embeds/image/editor/image_embed_types.dart'; class QuillToolbarImageButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart b/flutter_quill_extensions/lib/models/config/toolbar/buttons/media_button.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart rename to flutter_quill_extensions/lib/models/config/toolbar/buttons/media_button.dart diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart b/flutter_quill_extensions/lib/models/config/toolbar/buttons/video.dart similarity index 95% rename from flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart rename to flutter_quill_extensions/lib/models/config/toolbar/buttons/video.dart index e7efba515..e1f86a761 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart +++ b/flutter_quill_extensions/lib/models/config/toolbar/buttons/video.dart @@ -1,7 +1,7 @@ import 'package:flutter/widgets.dart' show Color; import 'package:flutter_quill/flutter_quill.dart'; -import '../../../../embeds/embed_types/video.dart'; +import '../../../../embeds/video/video.dart'; class QuillToolbarVideoButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart deleted file mode 100644 index e95cf9aa7..000000000 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart +++ /dev/null @@ -1,150 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_quill/extensions.dart' as base; -import 'package:flutter_quill/flutter_quill.dart' hide OptionalSize; - -import '../../../../logic/models/config/shared_configurations.dart'; -import '../../../models/config/editor/image/image.dart'; -import '../../../utils/utils.dart'; -import '../../widgets/image.dart'; -import 'image_menu.dart'; - -class QuillEditorImageEmbedBuilder extends EmbedBuilder { - QuillEditorImageEmbedBuilder({ - required this.configurations, - }); - final QuillEditorImageEmbedConfigurations configurations; - - @override - String get key => BlockEmbed.imageType; - - @override - bool get expanded => false; - - @override - Widget build( - BuildContext context, - QuillController controller, - base.Embed node, - bool readOnly, - bool inline, - TextStyle textStyle, - ) { - assert(!kIsWeb, 'Please provide image EmbedBuilder for Web'); - - final imageSource = standardizeImageUrl(node.value.data); - final ((imageSize), margin, alignment) = getElementAttributes(node); - - final width = imageSize.width; - final height = imageSize.height; - - final image = getImageWidgetByImageSource( - imageSource, - imageProviderBuilder: configurations.imageProviderBuilder, - imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder, - alignment: alignment, - height: height, - width: width, - assetsPrefix: QuillSharedExtensionsConfigurations.get(context: context) - .assetsPrefix, - ); - - // OptionalSize? imageSize; - // final style = node.style.attributes['style']; - - // if (style != null) { - // final attrs = base.isMobile(supportWeb: false) - // ? base.parseKeyValuePairs(style.value.toString(), { - // Attribute.mobileWidth, - // Attribute.mobileHeight, - // Attribute.mobileMargin, - // Attribute.mobileAlignment, - // }) - // : base.parseKeyValuePairs(style.value.toString(), { - // Attribute.width.key, - // Attribute.height.key, - // Attribute.margin, - // Attribute.alignment, - // }); - // if (attrs.isNotEmpty) { - // final width = double.tryParse( - // (base.isMobile(supportWeb: false) - // ? attrs[Attribute.mobileWidth] - // : attrs[Attribute.width.key]) ?? - // '', - // ); - // final height = double.tryParse( - // (base.isMobile(supportWeb: false) - // ? attrs[Attribute.mobileHeight] - // : attrs[Attribute.height.key]) ?? - // '', - // ); - // final alignment = base.getAlignment(base.isMobile(supportWeb: false) - // ? attrs[Attribute.mobileAlignment] - // : attrs[Attribute.alignment]); - // final margin = (base.isMobile(supportWeb: false) - // ? double.tryParse(Attribute.mobileMargin) - // : double.tryParse(Attribute.margin)) ?? - // 0.0; - - // imageSize = OptionalSize(width, height); - // image = Padding( - // padding: EdgeInsets.all(margin), - // child: getImageWidgetByImageSource( - // imageSource, - // width: width, - // height: height, - // alignment: alignment, - // imageProviderBuilder: configurations.imageProviderBuilder, - // imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder, - // ), - // ); - // } - // } - - // if (imageSize == null) { - // image = getImageWidgetByImageSource( - // imageSource, - // imageProviderBuilder: configurations.imageProviderBuilder, - // imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder, - // ); - // imageSize = OptionalSize((image as Image).width, image.height); - // } - - final imageSaverService = - QuillSharedExtensionsConfigurations.get(context: context) - .imageSaverService; - return GestureDetector( - onTap: configurations.onImageClicked ?? - () => showDialog( - context: context, - builder: (_) { - return QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: ImageOptionsMenu( - controller: controller, - configurations: configurations, - imageSource: imageSource, - imageSize: imageSize, - isReadOnly: readOnly, - imageSaverService: imageSaverService, - ), - ), - ); - }, - ), - child: Builder( - builder: (context) { - if (margin != null) { - return Padding( - padding: EdgeInsets.all(margin), - child: image, - ); - } - return image; - }, - ), - ); - } -} diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/webview.dart b/flutter_quill_extensions/lib/presentation/embeds/editor/webview.dart deleted file mode 100644 index 388b39b48..000000000 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/webview.dart +++ /dev/null @@ -1,58 +0,0 @@ -// import 'dart:convert' show jsonDecode, jsonEncode; - -// import 'package:flutter/widgets.dart'; -// import 'package:flutter_inappwebview/flutter_inappwebview.dart'; -// import 'package:flutter_quill/flutter_quill.dart'; -// import 'package:meta/meta.dart' show experimental; - -// import '../../models/config/editor/webview.dart'; - -// @experimental -// class QuillEditorWebViewBlockEmbed extends CustomBlockEmbed { -// const QuillEditorWebViewBlockEmbed( -// String value, -// ) : super(webViewType, value); - -// factory QuillEditorWebViewBlockEmbed.fromDocument(Document document) => -// QuillEditorWebViewBlockEmbed(jsonEncode(document.toDelta().toJson())); - -// static const String webViewType = 'webview'; - -// Document get document => Document.fromJson(jsonDecode(data)); -// } - -// @experimental -// class QuillEditorWebViewEmbedBuilder extends EmbedBuilder { -// const QuillEditorWebViewEmbedBuilder({ -// required this.configurations, -// }); - -// @override -// bool get expanded => false; - -// final QuillEditorWebViewEmbedConfigurations configurations; -// @override -// Widget build( -// BuildContext context, -// QuillController controller, -// Embed node, -// bool readOnly, -// bool inline, -// TextStyle textStyle, -// ) { -// final url = node.value.data as String; - -// return SizedBox( -// width: double.infinity, -// height: 200, -// child: InAppWebView( -// initialUrlRequest: URLRequest( -// url: Uri.parse(url), -// ), -// ), -// ); -// } - -// @override -// String get key => 'webview'; -// } diff --git a/flutter_quill_extensions/lib/presentation/utils/utils.dart b/flutter_quill_extensions/lib/presentation/utils/utils.dart deleted file mode 100644 index 00e986fd2..000000000 --- a/flutter_quill_extensions/lib/presentation/utils/utils.dart +++ /dev/null @@ -1,201 +0,0 @@ -import 'dart:io' show File; - -import 'package:flutter/foundation.dart' show immutable; -import 'package:flutter/widgets.dart' show Alignment; -import 'package:flutter_quill/extensions.dart' as base; -import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node; -import '../../logic/extensions/attribute.dart'; -import '../../logic/services/image_saver/s_image_saver.dart'; -import '../embeds/widgets/image.dart'; - -RegExp _base64 = RegExp( - r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$', -); - -bool isBase64(String str) { - return _base64.hasMatch(str); -} - -bool isHttpBasedUrl(String url) { - try { - final uri = Uri.parse(url.trim()); - return uri.isScheme('HTTP') || uri.isScheme('HTTPS'); - } catch (_) { - return false; - } -} - -bool isImageBase64(String imageUrl) { - return !isHttpBasedUrl(imageUrl) && isBase64(imageUrl); -} - -bool isYouTubeUrl(String videoUrl) { - try { - final uri = Uri.parse(videoUrl); - return uri.host == 'www.youtube.com' || - uri.host == 'youtube.com' || - uri.host == 'youtu.be' || - uri.host == 'www.youtu.be'; - } catch (_) { - return false; - } -} - -enum SaveImageResultMethod { network, localStorage } - -@immutable -class SaveImageResult { - const SaveImageResult({required this.error, required this.method}); - - final String? error; - final SaveImageResultMethod method; -} - -Future saveImage({ - required String imageUrl, - required ImageSaverService imageSaverService, -}) async { - final imageFile = File(imageUrl); - final hasPermission = await imageSaverService.hasAccess(); - if (!hasPermission) { - await imageSaverService.requestAccess(); - } - final imageExistsLocally = await imageFile.exists(); - if (!imageExistsLocally) { - try { - await imageSaverService.saveImageFromNetwork( - Uri.parse(appendFileExtensionToImageUrl(imageUrl)), - ); - return const SaveImageResult( - error: null, - method: SaveImageResultMethod.network, - ); - } catch (e) { - return SaveImageResult( - error: e.toString(), - method: SaveImageResultMethod.network, - ); - } - } - try { - await imageSaverService.saveLocalImage(imageUrl); - return const SaveImageResult( - error: null, - method: SaveImageResultMethod.localStorage, - ); - } catch (e) { - return SaveImageResult( - error: e.toString(), - method: SaveImageResultMethod.localStorage, - ); - } -} - -( - OptionalSize elementSize, - double? margin, - Alignment alignment, -) getElementAttributes( - Node node, -) { - var elementSize = const OptionalSize(null, null); - var elementAlignment = Alignment.center; - double? elementMargin; - - // Usually double value - final heightValue = double.tryParse( - node.style.attributes[Attribute.height.key]?.value.toString() ?? ''); - final widthValue = double.tryParse( - node.style.attributes[Attribute.width.key]?.value.toString() ?? ''); - - if (heightValue != null) { - elementSize = elementSize.copyWith( - height: heightValue, - ); - } - if (widthValue != null) { - elementSize = elementSize.copyWith( - width: widthValue, - ); - } - - final cssStyle = node.style.attributes['style']; - - if (cssStyle != null) { - final attrs = base.isMobile(supportWeb: false) - ? base.parseKeyValuePairs(cssStyle.value.toString(), { - AttributeExt.mobileWidth.key, - AttributeExt.mobileHeight.key, - AttributeExt.mobileMargin.key, - AttributeExt.mobileAlignment.key, - }) - : base.parseKeyValuePairs(cssStyle.value.toString(), { - Attribute.width.key, - Attribute.height.key, - 'margin', - 'alignment', - }); - if (attrs.isEmpty) { - return (elementSize, elementMargin, elementAlignment); - } - - // It css value as string but we will try to support it anyway - - // TODO: This could be improved much better - final cssHeightValue = double.tryParse(((base.isMobile(supportWeb: false) - ? attrs[AttributeExt.mobileHeight.key] - : attrs[Attribute.height.key]) ?? - '') - .replaceFirst('px', '')); - final cssWidthValue = double.tryParse(((!base.isMobile(supportWeb: false) - ? attrs[Attribute.width.key] - : attrs[AttributeExt.mobileWidth.key]) ?? - '') - .replaceFirst('px', '')); - - if (cssHeightValue != null) { - elementSize = elementSize.copyWith(height: cssHeightValue); - } - if (cssWidthValue != null) { - elementSize = elementSize.copyWith(width: cssWidthValue); - } - - elementAlignment = base.getAlignment(base.isMobile(supportWeb: false) - ? attrs[AttributeExt.mobileAlignment.key] - : attrs['alignment']); - final margin = (base.isMobile(supportWeb: false) - ? double.tryParse(AttributeExt.mobileMargin.key) - : double.tryParse('margin')); - if (margin != null) { - elementMargin = margin; - } - } - - return (elementSize, elementMargin, elementAlignment); -} - -@immutable -class OptionalSize { - const OptionalSize( - this.width, - this.height, - ); - - /// If non-null, requires the child to have exactly this width. - /// If null, the child is free to choose its own width. - final double? width; - - /// If non-null, requires the child to have exactly this height. - /// If null, the child is free to choose its own height. - final double? height; - - OptionalSize copyWith({ - double? width, - double? height, - }) { - return OptionalSize( - width ?? this.width, - height ?? this.height, - ); - } -} diff --git a/flutter_quill_extensions/lib/logic/services/image_picker/image_options.dart b/flutter_quill_extensions/lib/services/image_picker/image_options.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_picker/image_options.dart rename to flutter_quill_extensions/lib/services/image_picker/image_options.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_picker/image_picker.dart b/flutter_quill_extensions/lib/services/image_picker/image_picker.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_picker/image_picker.dart rename to flutter_quill_extensions/lib/services/image_picker/image_picker.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_picker/packages/image_picker.dart b/flutter_quill_extensions/lib/services/image_picker/packages/image_picker.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_picker/packages/image_picker.dart rename to flutter_quill_extensions/lib/services/image_picker/packages/image_picker.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_picker/s_image_picker.dart b/flutter_quill_extensions/lib/services/image_picker/s_image_picker.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_picker/s_image_picker.dart rename to flutter_quill_extensions/lib/services/image_picker/s_image_picker.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_saver/exceptions.dart b/flutter_quill_extensions/lib/services/image_saver/exceptions.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_saver/exceptions.dart rename to flutter_quill_extensions/lib/services/image_saver/exceptions.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_saver/image_saver.dart b/flutter_quill_extensions/lib/services/image_saver/image_saver.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_saver/image_saver.dart rename to flutter_quill_extensions/lib/services/image_saver/image_saver.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_saver/packages/gal.dart b/flutter_quill_extensions/lib/services/image_saver/packages/gal.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_saver/packages/gal.dart rename to flutter_quill_extensions/lib/services/image_saver/packages/gal.dart diff --git a/flutter_quill_extensions/lib/logic/services/image_saver/s_image_saver.dart b/flutter_quill_extensions/lib/services/image_saver/s_image_saver.dart similarity index 100% rename from flutter_quill_extensions/lib/logic/services/image_saver/s_image_saver.dart rename to flutter_quill_extensions/lib/services/image_saver/s_image_saver.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/shims/dart_ui_fake.dart b/flutter_quill_extensions/lib/utils/dart_ui/dart_ui_fake.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/editor/shims/dart_ui_fake.dart rename to flutter_quill_extensions/lib/utils/dart_ui/dart_ui_fake.dart diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/shims/dart_ui_real.dart b/flutter_quill_extensions/lib/utils/dart_ui/dart_ui_real.dart similarity index 100% rename from flutter_quill_extensions/lib/presentation/embeds/editor/shims/dart_ui_real.dart rename to flutter_quill_extensions/lib/utils/dart_ui/dart_ui_real.dart diff --git a/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart b/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart new file mode 100644 index 000000000..927a97e18 --- /dev/null +++ b/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart @@ -0,0 +1,29 @@ +Map parseCssString(String cssString) { + final result = {}; + final declarations = cssString.split(';'); + + for (final declaration in declarations) { + final parts = declaration.split(':'); + if (parts.length == 2) { + final property = parts[0].trim(); + final value = parts[1].trim(); + result[property] = value; + } + } + + return result; +} + +double? parseCssPropertyAsDouble(String value) { + if (value.trim().isEmpty) { + return null; + } + final list = [ + 'px', + // '%', 'vw', 'vh', 'em', 'rem' + ]; + for (final element in list) { + value = value.replaceFirst(element, ''); + } + return double.tryParse(value); +} diff --git a/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart b/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart new file mode 100644 index 000000000..24bd166e7 --- /dev/null +++ b/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart @@ -0,0 +1,98 @@ +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter/widgets.dart' show Alignment; +import 'package:flutter_quill/extensions.dart'; +import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node; + +import 'element_shared_utils.dart'; + +/// Theses properties are not officialy supported by quill js +/// but they are only used in all platforms other than web +/// and they will be stored in css style property so quill js ignore them +enum ExtraElementProperties { + deletable, +} + +( + ElementSize elementSize, + double? margin, + Alignment alignment, +) getElementAttributes( + Node node, +) { + var elementSize = const ElementSize(null, null); + var elementAlignment = Alignment.center; + double? elementMargin; + + final heightValue = parseCssPropertyAsDouble( + node.style.attributes[Attribute.height.key]?.value.toString() ?? ''); + final widthValue = parseCssPropertyAsDouble( + node.style.attributes[Attribute.width.key]?.value.toString() ?? ''); + + if (heightValue != null) { + elementSize = elementSize.copyWith( + height: heightValue, + ); + } + if (widthValue != null) { + elementSize = elementSize.copyWith( + width: widthValue, + ); + } + + final cssStyle = node.style.attributes['style']; + + if (cssStyle != null) { + // It css value as string but we will try to support it anyway + + final cssAttrs = parseCssString(cssStyle.value.toString()); + + // todo: This could be improved much better + final cssHeightValue = + parseCssPropertyAsDouble((cssAttrs[Attribute.height.key]) ?? ''); + final cssWidthValue = + parseCssPropertyAsDouble((cssAttrs[Attribute.width.key]) ?? ''); + + // cssHeightValue != null && elementSize.height == null + if (cssHeightValue != null) { + elementSize = elementSize.copyWith(height: cssHeightValue); + } + if (cssWidthValue != null) { + elementSize = elementSize.copyWith(width: cssWidthValue); + } + + elementAlignment = getAlignment(cssAttrs['alignment']); + + final margin = double.tryParse('margin'); + if (margin != null) { + elementMargin = margin; + } + } + + return (elementSize, elementMargin, elementAlignment); +} + +@immutable +class ElementSize { + const ElementSize( + this.width, + this.height, + ); + + /// If non-null, requires the child to have exactly this width. + /// If null, the child is free to choose its own width. + final double? width; + + /// If non-null, requires the child to have exactly this height. + /// If null, the child is free to choose its own height. + final double? height; + + ElementSize copyWith({ + double? width, + double? height, + }) { + return ElementSize( + width ?? this.width, + height ?? this.height, + ); + } +} diff --git a/flutter_quill_extensions/lib/presentation/utils/web_utils.dart b/flutter_quill_extensions/lib/utils/element_utils/element_web_utils.dart similarity index 77% rename from flutter_quill_extensions/lib/presentation/utils/web_utils.dart rename to flutter_quill_extensions/lib/utils/element_utils/element_web_utils.dart index b34d308fb..091f1c319 100644 --- a/flutter_quill_extensions/lib/presentation/utils/web_utils.dart +++ b/flutter_quill_extensions/lib/utils/element_utils/element_web_utils.dart @@ -1,6 +1,7 @@ -import 'package:flutter_quill/extensions.dart' as base; import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node; +import 'element_shared_utils.dart'; + /// Prefer the width, and height from the css style attribute if exits /// it can be `auto` or `100px` so it's specific to HTML && CSS /// if not, we will use the one from attributes which is usually just an double @@ -14,26 +15,20 @@ import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node; ) { var height = 'auto'; var width = 'auto'; - // TODO: Add support for margin and alignment - const margin = 'auto'; + // TODO(): Add support for margin and alignment + var margin = 'auto'; const alignment = 'center'; - // return (height, width, margin, alignment); - final cssStyle = node.style.attributes['style']; - // Usually double value final heightValue = node.style.attributes[Attribute.height.key]?.value; final widthValue = node.style.attributes[Attribute.width.key]?.value; if (cssStyle != null) { - final attrs = base.parseKeyValuePairs(cssStyle.value.toString(), { - Attribute.width.key, - Attribute.height.key, - 'margin', - 'alignment', - }); + final attrs = parseCssString(cssStyle.value.toString()); + final cssHeightValue = attrs[Attribute.height.key]; + if (cssHeightValue != null) { height = cssHeightValue; } else { @@ -46,6 +41,11 @@ import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node; width = '${widthValue}px'; } + final cssMarginValue = attrs['margin']; + if (cssMarginValue != null) { + margin = cssMarginValue; + } + return (height, width, margin, alignment); } diff --git a/flutter_quill_extensions/lib/logic/utils/quill_image_utils.dart b/flutter_quill_extensions/lib/utils/quill_image_utils.dart similarity index 99% rename from flutter_quill_extensions/lib/logic/utils/quill_image_utils.dart rename to flutter_quill_extensions/lib/utils/quill_image_utils.dart index 30eca1286..4a366c092 100644 --- a/flutter_quill_extensions/lib/logic/utils/quill_image_utils.dart +++ b/flutter_quill_extensions/lib/utils/quill_image_utils.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_quill/flutter_quill.dart' as quill; import 'package:path/path.dart' as path; -import '../../presentation/utils/utils.dart'; +import 'utils.dart'; typedef OnGenerateNewFileNameCallback = String Function( String currentFileName, diff --git a/flutter_quill_extensions/lib/logic/utils/string.dart b/flutter_quill_extensions/lib/utils/string.dart similarity index 66% rename from flutter_quill_extensions/lib/logic/utils/string.dart rename to flutter_quill_extensions/lib/utils/string.dart index aca39f68b..a13cd3445 100644 --- a/flutter_quill_extensions/lib/logic/utils/string.dart +++ b/flutter_quill_extensions/lib/utils/string.dart @@ -1,12 +1,9 @@ import 'package:flutter_quill/flutter_quill.dart' show Attribute; -import '../extensions/attribute.dart'; - String replaceStyleStringWithSize( String cssStyle, { required double width, required double height, - required bool isMobile, }) { final result = {}; final pairs = cssStyle.split(';'); @@ -19,13 +16,8 @@ String replaceStyleStringWithSize( result[key] = pair.substring(index + 1).trim(); } - if (isMobile) { - result[AttributeExt.mobileWidth.key] = width.toString(); - result[AttributeExt.mobileHeight.key] = height.toString(); - } else { - result[Attribute.width.key] = width.toString(); - result[Attribute.height.key] = height.toString(); - } + result[Attribute.width.key] = width.toString(); + result[Attribute.height.key] = height.toString(); final sb = StringBuffer(); for (final pair in result.entries) { sb diff --git a/flutter_quill_extensions/lib/utils/utils.dart b/flutter_quill_extensions/lib/utils/utils.dart new file mode 100644 index 000000000..12c802a11 --- /dev/null +++ b/flutter_quill_extensions/lib/utils/utils.dart @@ -0,0 +1,89 @@ +import 'dart:io' show File; + +import 'package:flutter/foundation.dart' show immutable; + +import '../embeds/widgets/image.dart'; +import '../services/image_saver/s_image_saver.dart'; + +RegExp _base64 = RegExp( + r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$', +); + +bool isBase64(String str) { + return _base64.hasMatch(str); +} + +bool isHttpBasedUrl(String url) { + try { + final uri = Uri.parse(url.trim()); + return uri.isScheme('HTTP') || uri.isScheme('HTTPS'); + } catch (_) { + return false; + } +} + +bool isImageBase64(String imageUrl) { + return !isHttpBasedUrl(imageUrl) && isBase64(imageUrl); +} + +bool isYouTubeUrl(String videoUrl) { + try { + final uri = Uri.parse(videoUrl); + return uri.host == 'www.youtube.com' || + uri.host == 'youtube.com' || + uri.host == 'youtu.be' || + uri.host == 'www.youtu.be'; + } catch (_) { + return false; + } +} + +enum SaveImageResultMethod { network, localStorage } + +@immutable +class SaveImageResult { + const SaveImageResult({required this.error, required this.method}); + + final String? error; + final SaveImageResultMethod method; +} + +Future saveImage({ + required String imageUrl, + required ImageSaverService imageSaverService, +}) async { + final imageFile = File(imageUrl); + final hasPermission = await imageSaverService.hasAccess(); + if (!hasPermission) { + await imageSaverService.requestAccess(); + } + final imageExistsLocally = await imageFile.exists(); + if (!imageExistsLocally) { + try { + await imageSaverService.saveImageFromNetwork( + Uri.parse(appendFileExtensionToImageUrl(imageUrl)), + ); + return const SaveImageResult( + error: null, + method: SaveImageResultMethod.network, + ); + } catch (e) { + return SaveImageResult( + error: e.toString(), + method: SaveImageResultMethod.network, + ); + } + } + try { + await imageSaverService.saveLocalImage(imageUrl); + return const SaveImageResult( + error: null, + method: SaveImageResultMethod.localStorage, + ); + } catch (e) { + return SaveImageResult( + error: e.toString(), + method: SaveImageResultMethod.localStorage, + ); + } +} diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index da3c46d87..254d628a2 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill_extensions description: Embed extensions for flutter_quill including image, video, formula and etc. -version: 0.7.2 +version: 0.8.0-dev homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ diff --git a/lib/flutter_quill.dart b/lib/flutter_quill.dart index 640571cc1..2c3170615 100644 --- a/lib/flutter_quill.dart +++ b/lib/flutter_quill.dart @@ -1,7 +1,6 @@ library flutter_quill; export 'src/extensions/quill_provider.dart'; -export 'src/l10n/widgets/localizations.dart'; export 'src/models/config/quill_configurations.dart'; export 'src/models/config/raw_editor/configurations.dart'; export 'src/models/config/toolbar/base_toolbar_configurations.dart'; diff --git a/lib/src/l10n/generated/quill_localizations.dart b/lib/src/l10n/generated/quill_localizations.dart index 756ac2c96..f4425f811 100644 --- a/lib/src/l10n/generated/quill_localizations.dart +++ b/lib/src/l10n/generated/quill_localizations.dart @@ -600,6 +600,42 @@ abstract class FlutterQuillLocalizations { /// In en, this message translates to: /// **'Insert image'** String get insertImage; + + /// No description provided for @pickAPhotoFromYourGallery. + /// + /// In en, this message translates to: + /// **'Pick a photo from your gallery'** + String get pickAPhotoFromYourGallery; + + /// No description provided for @takeAPhotoUsingYourCamera. + /// + /// In en, this message translates to: + /// **'Take a photo using your phone camera'** + String get takeAPhotoUsingYourCamera; + + /// No description provided for @pasteAPhotoUsingALink. + /// + /// In en, this message translates to: + /// **'Paste a photo using a link'** + String get pasteAPhotoUsingALink; + + /// No description provided for @pickAVideoFromYourGallery. + /// + /// In en, this message translates to: + /// **'Pick a video from your gallery'** + String get pickAVideoFromYourGallery; + + /// No description provided for @recordAVideoUsingYourCamera. + /// + /// In en, this message translates to: + /// **'Record a video using your phone camera'** + String get recordAVideoUsingYourCamera; + + /// No description provided for @pasteAVideoUsingALink. + /// + /// In en, this message translates to: + /// **'Paste a video using a link'** + String get pasteAVideoUsingALink; } class _FlutterQuillLocalizationsDelegate diff --git a/lib/src/l10n/generated/quill_localizations_ar.dart b/lib/src/l10n/generated/quill_localizations_ar.dart index d78d815a6..fc7b2dfc4 100644 --- a/lib/src/l10n/generated/quill_localizations_ar.dart +++ b/lib/src/l10n/generated/quill_localizations_ar.dart @@ -226,4 +226,24 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations { @override String get insertImage => 'إدراج صورة'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_bg.dart b/lib/src/l10n/generated/quill_localizations_bg.dart index b0fc467ca..a75b6ffab 100644 --- a/lib/src/l10n/generated/quill_localizations_bg.dart +++ b/lib/src/l10n/generated/quill_localizations_bg.dart @@ -228,4 +228,24 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations { @override String get insertImage => 'Вмъкване на изображение'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_bn.dart b/lib/src/l10n/generated/quill_localizations_bn.dart index eb8b0f564..7ef340ef6 100644 --- a/lib/src/l10n/generated/quill_localizations_bn.dart +++ b/lib/src/l10n/generated/quill_localizations_bn.dart @@ -228,4 +228,24 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations { @override String get insertImage => 'চিত্র সন্নিবেশ'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_cs.dart b/lib/src/l10n/generated/quill_localizations_cs.dart index 2c37d03fe..a74f67564 100644 --- a/lib/src/l10n/generated/quill_localizations_cs.dart +++ b/lib/src/l10n/generated/quill_localizations_cs.dart @@ -228,4 +228,24 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations { @override String get insertImage => 'Vložit obrázek'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_da.dart b/lib/src/l10n/generated/quill_localizations_da.dart index 4a136af45..6e8825fc5 100644 --- a/lib/src/l10n/generated/quill_localizations_da.dart +++ b/lib/src/l10n/generated/quill_localizations_da.dart @@ -226,4 +226,24 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations { @override String get insertImage => 'Indsæt billede'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_de.dart b/lib/src/l10n/generated/quill_localizations_de.dart index 79ae25bea..384b8ea33 100644 --- a/lib/src/l10n/generated/quill_localizations_de.dart +++ b/lib/src/l10n/generated/quill_localizations_de.dart @@ -227,4 +227,24 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations { @override String get insertImage => 'Bild einfügen'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_en.dart b/lib/src/l10n/generated/quill_localizations_en.dart index c69876d96..1233f1cf3 100644 --- a/lib/src/l10n/generated/quill_localizations_en.dart +++ b/lib/src/l10n/generated/quill_localizations_en.dart @@ -228,6 +228,26 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations { @override String get insertImage => 'Insert image'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } /// The translations for English, as used in the United States (`en_US`). diff --git a/lib/src/l10n/generated/quill_localizations_es.dart b/lib/src/l10n/generated/quill_localizations_es.dart index 85c895f32..3c63865d1 100644 --- a/lib/src/l10n/generated/quill_localizations_es.dart +++ b/lib/src/l10n/generated/quill_localizations_es.dart @@ -227,4 +227,24 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations { @override String get insertImage => 'Insertar imagen'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_fa.dart b/lib/src/l10n/generated/quill_localizations_fa.dart index e649354f8..32040119a 100644 --- a/lib/src/l10n/generated/quill_localizations_fa.dart +++ b/lib/src/l10n/generated/quill_localizations_fa.dart @@ -229,4 +229,24 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations { @override String get insertImage => 'وارد کردن تصویر'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_fr.dart b/lib/src/l10n/generated/quill_localizations_fr.dart index 7bd59ee48..b94451ff1 100644 --- a/lib/src/l10n/generated/quill_localizations_fr.dart +++ b/lib/src/l10n/generated/quill_localizations_fr.dart @@ -230,4 +230,24 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations { @override String get insertImage => 'Insérer une image'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_he.dart b/lib/src/l10n/generated/quill_localizations_he.dart index a0d94ce0f..956c10f99 100644 --- a/lib/src/l10n/generated/quill_localizations_he.dart +++ b/lib/src/l10n/generated/quill_localizations_he.dart @@ -228,4 +228,24 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations { @override String get insertImage => 'הכנס תמונה'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_hi.dart b/lib/src/l10n/generated/quill_localizations_hi.dart index 4e2d2d5ed..3e2e738aa 100644 --- a/lib/src/l10n/generated/quill_localizations_hi.dart +++ b/lib/src/l10n/generated/quill_localizations_hi.dart @@ -229,4 +229,24 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations { @override String get insertImage => 'छवि डालें'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_id.dart b/lib/src/l10n/generated/quill_localizations_id.dart index 26eb61cb0..4d4d38c53 100644 --- a/lib/src/l10n/generated/quill_localizations_id.dart +++ b/lib/src/l10n/generated/quill_localizations_id.dart @@ -230,4 +230,24 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations { @override String get insertImage => 'Sisipkan Gambar'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_it.dart b/lib/src/l10n/generated/quill_localizations_it.dart index 9d6c18eea..2b8fe0703 100644 --- a/lib/src/l10n/generated/quill_localizations_it.dart +++ b/lib/src/l10n/generated/quill_localizations_it.dart @@ -230,4 +230,24 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations { @override String get insertImage => 'Inserisci immagine'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_ja.dart b/lib/src/l10n/generated/quill_localizations_ja.dart index 94927b835..7a55d6b53 100644 --- a/lib/src/l10n/generated/quill_localizations_ja.dart +++ b/lib/src/l10n/generated/quill_localizations_ja.dart @@ -225,4 +225,24 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations { @override String get insertImage => '画像を挿入'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_ko.dart b/lib/src/l10n/generated/quill_localizations_ko.dart index bb6d87de0..c8a6b5669 100644 --- a/lib/src/l10n/generated/quill_localizations_ko.dart +++ b/lib/src/l10n/generated/quill_localizations_ko.dart @@ -225,4 +225,24 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations { @override String get insertImage => '이미지 삽입'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_ms.dart b/lib/src/l10n/generated/quill_localizations_ms.dart index b46f2fd90..c443712f5 100644 --- a/lib/src/l10n/generated/quill_localizations_ms.dart +++ b/lib/src/l10n/generated/quill_localizations_ms.dart @@ -228,4 +228,24 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations { @override String get insertImage => 'Masukkan imej'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_nl.dart b/lib/src/l10n/generated/quill_localizations_nl.dart index 4b8b444c6..65bc5b47c 100644 --- a/lib/src/l10n/generated/quill_localizations_nl.dart +++ b/lib/src/l10n/generated/quill_localizations_nl.dart @@ -230,4 +230,24 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations { @override String get insertImage => 'Afbeelding invoegen'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_no.dart b/lib/src/l10n/generated/quill_localizations_no.dart index 37bf4cf0f..087c69971 100644 --- a/lib/src/l10n/generated/quill_localizations_no.dart +++ b/lib/src/l10n/generated/quill_localizations_no.dart @@ -230,4 +230,24 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations { @override String get insertImage => 'Sett inn bilde'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_pl.dart b/lib/src/l10n/generated/quill_localizations_pl.dart index b983356bb..677ff5c82 100644 --- a/lib/src/l10n/generated/quill_localizations_pl.dart +++ b/lib/src/l10n/generated/quill_localizations_pl.dart @@ -227,4 +227,24 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations { @override String get insertImage => 'Wstaw obraz'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_pt.dart b/lib/src/l10n/generated/quill_localizations_pt.dart index a48362b1b..bec9b366c 100644 --- a/lib/src/l10n/generated/quill_localizations_pt.dart +++ b/lib/src/l10n/generated/quill_localizations_pt.dart @@ -228,6 +228,26 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations { @override String get insertImage => 'Inserir imagem'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } /// The translations for Portuguese, as used in Brazil (`pt_BR`). diff --git a/lib/src/l10n/generated/quill_localizations_ru.dart b/lib/src/l10n/generated/quill_localizations_ru.dart index 376e3c12f..96a9314b8 100644 --- a/lib/src/l10n/generated/quill_localizations_ru.dart +++ b/lib/src/l10n/generated/quill_localizations_ru.dart @@ -227,4 +227,24 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations { @override String get insertImage => 'Вставить изображение'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_sr.dart b/lib/src/l10n/generated/quill_localizations_sr.dart index e49dcdb71..1e89073e7 100644 --- a/lib/src/l10n/generated/quill_localizations_sr.dart +++ b/lib/src/l10n/generated/quill_localizations_sr.dart @@ -229,4 +229,24 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations { @override String get insertImage => 'Umetni sliku'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_sw.dart b/lib/src/l10n/generated/quill_localizations_sw.dart index 5928e04ee..640403607 100644 --- a/lib/src/l10n/generated/quill_localizations_sw.dart +++ b/lib/src/l10n/generated/quill_localizations_sw.dart @@ -227,4 +227,24 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations { @override String get insertImage => 'Weka Picha'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_tk.dart b/lib/src/l10n/generated/quill_localizations_tk.dart index 566313280..13e7b1d3c 100644 --- a/lib/src/l10n/generated/quill_localizations_tk.dart +++ b/lib/src/l10n/generated/quill_localizations_tk.dart @@ -226,4 +226,24 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations { @override String get insertImage => 'Surat goş'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_tr.dart b/lib/src/l10n/generated/quill_localizations_tr.dart index dbd5d3ded..e6c52e9a5 100644 --- a/lib/src/l10n/generated/quill_localizations_tr.dart +++ b/lib/src/l10n/generated/quill_localizations_tr.dart @@ -227,4 +227,24 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations { @override String get insertImage => 'Görüntü ekle'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_uk.dart b/lib/src/l10n/generated/quill_localizations_uk.dart index 163c299fc..f7a273a5a 100644 --- a/lib/src/l10n/generated/quill_localizations_uk.dart +++ b/lib/src/l10n/generated/quill_localizations_uk.dart @@ -229,4 +229,24 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations { @override String get insertImage => 'Вставити зображення'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_ur.dart b/lib/src/l10n/generated/quill_localizations_ur.dart index 2f4b14fdb..79c665f56 100644 --- a/lib/src/l10n/generated/quill_localizations_ur.dart +++ b/lib/src/l10n/generated/quill_localizations_ur.dart @@ -231,4 +231,24 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations { @override String get insertImage => 'تصویر داخل کریں'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_vi.dart b/lib/src/l10n/generated/quill_localizations_vi.dart index 439d1de54..6f26695b0 100644 --- a/lib/src/l10n/generated/quill_localizations_vi.dart +++ b/lib/src/l10n/generated/quill_localizations_vi.dart @@ -228,4 +228,24 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations { @override String get insertImage => 'Chèn hình ảnh'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } diff --git a/lib/src/l10n/generated/quill_localizations_zh.dart b/lib/src/l10n/generated/quill_localizations_zh.dart index 5c7a42327..59ba0ce1e 100644 --- a/lib/src/l10n/generated/quill_localizations_zh.dart +++ b/lib/src/l10n/generated/quill_localizations_zh.dart @@ -225,6 +225,26 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations { @override String get insertImage => '插入图像'; + + @override + String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; + + @override + String get takeAPhotoUsingYourCamera => + 'Take a photo using your phone camera'; + + @override + String get pasteAPhotoUsingALink => 'Paste a photo using a link'; + + @override + String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; + + @override + String get recordAVideoUsingYourCamera => + 'Record a video using your phone camera'; + + @override + String get pasteAVideoUsingALink => 'Paste a video using a link'; } /// The translations for Chinese, as used in China (`zh_CN`). diff --git a/lib/src/l10n/quill_en.arb b/lib/src/l10n/quill_en.arb index c0ec79673..0d4d737f0 100644 --- a/lib/src/l10n/quill_en.arb +++ b/lib/src/l10n/quill_en.arb @@ -73,5 +73,11 @@ "photo": "Photo", "image": "Image", "caseSensitivityAndWholeWordSearch": "Case sensitivity and whole word search", - "insertImage": "Insert image" + "insertImage": "Insert image", + "pickAPhotoFromYourGallery": "Pick a photo from your gallery", + "takeAPhotoUsingYourCamera": "Take a photo using your phone camera", + "pasteAPhotoUsingALink": "Paste a photo using a link", + "pickAVideoFromYourGallery": "Pick a video from your gallery", + "recordAVideoUsingYourCamera": "Record a video using your phone camera", + "pasteAVideoUsingALink": "Paste a video using a link" } diff --git a/lib/src/l10n/untranslated.json b/lib/src/l10n/untranslated.json index 9e26dfeeb..d6c2ff3d2 100644 --- a/lib/src/l10n/untranslated.json +++ b/lib/src/l10n/untranslated.json @@ -1 +1,298 @@ -{} \ No newline at end of file +{ + "ar": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "bg": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "bn": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "cs": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "da": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "de": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "en_US": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "es": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "fa": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "fr": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "he": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "hi": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "id": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "it": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "ja": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "ko": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "ms": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "nl": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "no": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "pl": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "pt": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "pt_BR": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "ru": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "sr": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "sw": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "tk": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "tr": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "uk": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "ur": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "vi": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "zh": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "zh_CN": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ], + + "zh_HK": [ + "pickAPhotoFromYourGallery", + "takeAPhotoUsingYourCamera", + "pasteAPhotoUsingALink", + "pickAVideoFromYourGallery", + "recordAVideoUsingYourCamera", + "pasteAVideoUsingALink" + ] +} diff --git a/lib/translations.dart b/lib/translations.dart index d54eb374b..002a538f3 100644 --- a/lib/translations.dart +++ b/lib/translations.dart @@ -1,3 +1,4 @@ library flutter_quill.translations; export 'src/l10n/extensions/localizations.dart'; +export 'src/l10n/widgets/localizations.dart';