commit 4dd5d2f6ffbaa9f26eb81d4733320ab01b86c37a Author: brahim Date: Sun Oct 30 15:25:33 2022 +0700 added project' diff --git a/.flutter-plugins b/.flutter-plugins new file mode 100644 index 0000000..608adb7 --- /dev/null +++ b/.flutter-plugins @@ -0,0 +1,9 @@ +# This is a generated file; do not edit or check into version control. +file_picker=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/file_picker-5.2.2/ +flutter_inappwebview=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-6.0.0-beta.11/ +flutter_keyboard_visibility=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility-5.4.0/ +flutter_keyboard_visibility_linux=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_linux-1.0.0/ +flutter_keyboard_visibility_macos=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_macos-1.0.0/ +flutter_keyboard_visibility_web=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_web-2.0.0/ +flutter_keyboard_visibility_windows=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_windows-1.0.0/ +flutter_plugin_android_lifecycle=/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-2.0.7/ diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies new file mode 100644 index 0000000..deab549 --- /dev/null +++ b/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_picker","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/file_picker-5.2.2/","native_build":true,"dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-6.0.0-beta.11/","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility-5.4.0/","native_build":true,"dependencies":[]}],"android":[{"name":"file_picker","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/file_picker-5.2.2/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_inappwebview","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-6.0.0-beta.11/","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility-5.4.0/","native_build":true,"dependencies":[]},{"name":"flutter_plugin_android_lifecycle","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-2.0.7/","native_build":true,"dependencies":[]}],"macos":[{"name":"flutter_inappwebview","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-6.0.0-beta.11/","native_build":true,"dependencies":[]},{"name":"flutter_keyboard_visibility_macos","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_macos-1.0.0/","native_build":false,"dependencies":[]}],"linux":[{"name":"flutter_keyboard_visibility_linux","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_linux-1.0.0/","native_build":false,"dependencies":[]}],"windows":[{"name":"flutter_keyboard_visibility_windows","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_windows-1.0.0/","native_build":false,"dependencies":[]}],"web":[{"name":"file_picker","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/file_picker-5.2.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_inappwebview-6.0.0-beta.11/","dependencies":[]},{"name":"flutter_keyboard_visibility_web","path":"/Users/mac/developer/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_keyboard_visibility_web-2.0.0/","dependencies":[]}]},"dependencyGraph":[{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"flutter_keyboard_visibility","dependencies":["flutter_keyboard_visibility_linux","flutter_keyboard_visibility_macos","flutter_keyboard_visibility_web","flutter_keyboard_visibility_windows"]},{"name":"flutter_keyboard_visibility_linux","dependencies":[]},{"name":"flutter_keyboard_visibility_macos","dependencies":[]},{"name":"flutter_keyboard_visibility_web","dependencies":[]},{"name":"flutter_keyboard_visibility_windows","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]}],"date_created":"2022-10-30 15:22:45.754664","version":"3.3.6"} \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ad9acc5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: tneotia + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Device:** +Add device details here. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..d233fde --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..d54be6c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,10 @@ +--- +name: Question +about: Ask a question about this project +title: "[QUESTION]" +labels: question +assignees: '' + +--- + +Type question here. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9dc58d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..9e8ada0 --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: a29104a69b102a7485cd00d358eaeab219d258ab + channel: beta + +project_type: plugin diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3a711a5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,244 @@ +## [2.5.0] - 2022-06-04 +* Support Flutter 3.0 (remove warnings) (@Cteq3132) +* [BREAKING] Support modifying `foreColorSelected` and `backColorSelected` when using a custom dialog for font coloring + * If you are using a custom `updateStatus` function for the font coloring, that function is now defined as `updateStatus(Color)` +* Added `disabled` parameter to automatically disable editor on initial load +* Fixed white background color appearing sometimes when pressing backspace on text +* Added `useHybridComposition` parameter in case devs want to disable this behavior (improves animations of app) +* [WEB] Fixed editor height being 0 when `initialText` is `null` (@dab246) +* Migrated example to Android embedding V2 +* Removed woff fonts to allow iOS App Store submissions + +## [2.4.0] - 2021-10-30 +* Improved color picker + * Added scrollable support to picker + * Fixed issue where keyboard would disappear and prevent users from selecting a new color +* Added support for getting selected text in Flutter Web (`controller.getSelectedTextWeb()`) +* Added support for spellcheck +* Added support for custom options in summernote initialization +* Added support for a hard stop on character limit (will not allow user to type further) +* Fixed bug where focusing editor would scroll it back to the top and not show the caret position +* Fixed height not updating in Flutter Web when `callbacks` was `null` +* Updated dependencies and fixed flutter_colorpicker dependency error (@eliudio) + +## [2.3.0] - 2021-09-09 +* Potentially fixed bad state error for `stream.first` +* Fixed Summernote-@-Mention not inserting text after selecting the dropdown item +* Fixed whitespace after @ sign when inserting a mention +* Add support for overriding URL loading on mobile with `onNavigationRequestMobile` callback +* Hide keyboard when opening color picker dialog +* Use try/catch on `SystemChannel` call on web to prevent exception +* Added a character counter for text in the editor - access via `controller.characterCount` +* Fixed auto adjust height not working in some cases +* Fixed editor height not updating when focused after a TextField was previously focused +* Updated dependencies (@JEuler) + +NOTE: If you are on Flutter Beta+, you must use `dependency_overrides` in `pubspec.yaml`!! + +```yaml +dependency_overrides: + flutter_colorpicker: ^0.5.0 +``` + +## [2.2.0+1] - 2021-06-15 (BETA) +* Updated `flutter_colorpicker` to the latest version to fix deprecations on Flutter beta+ +* [NOTE] Do not use this version on Flutter stable! + +## [2.2.0] - 2021-06-14 +* Fixed null safety warnings due to latest `file_picker` version +* Potentially fixed editor controller creating a new instance on widget rebuild +* Fixed issue where custom HTML files would have custom JS replaced with built-in JS +* Fixed darkMode not applying when `filePath` is used on Android +* Fixed "null" text showing as the hint when no hint is given +* Added new `onChangeSelection` callback that passes the editor settings whenever the user changes +their current selection (e.g. tap on a different word, create a new node, select text, etc) +* Added support for custom JS injection on Flutter Web +* Fixed minor bug with automatic height adjustment on mobile +* Added new `ToolbarType.nativeExpandable` which allows the user to switch between the +scrollview or gridview toolbar on the fly +* Support setting the `inputmode` for the editor, which changes the virtual keyboard display on mobile devices (e.g. number pad, email keyboard, etc) +* [BREAKING] renamed `onChange` callback to `onChangeContent` +* [BREAKING] disabled a lot of the buttons by default, now only around half of the editor buttons +are enabled to improve the UX. You can still re-enable the rest if you want. +* [BREAKING] min Flutter version requirement bumped to 2.2.0 + +## [2.1.1] - 2021-05-22 +* Fixed bottom overflow error on `AlertDialog`s if the screen size is small +* Fixed `StyleButtons(style: false)` would not remove the style dropdown +* Fixed JS/Dart communication hiccup on Web (make sure `postMessage` data is not null) +* Code cleanup + +## [2.1.0+1] - 2021-05-11 +* Hotfix for `copyWith` not defined for `ScrollBehavior` in v2.1.0 + +## [2.1.0] - 2021-05-10 +* Fixed `setState` and `Stream.first` error on page dispose +* Fixed height adjustment not working +* Fixed `getText` on Web +* Improved dropdown UX when `ToolbarPosition.belowEditor` by opening upwards and making it scrollable after a certain height + +## [2.0.1] - 2021-04-28 +* Added support for setting custom `UserScript`s on the webview (mobile only) +* Added support for customizing the context menu (menu when user selects text) for the webview (mobile only) +* Added `LongPressGestureRecognizer` to the webview to allow users to select text via a long press (mobile only) + * You can set the duration before the long press is recognized via `HtmlEditorOptions > mobileLongPressDuration` +* Added support for placing the toolbar wherever using `HtmlToolbarOptions > toolbarPosition: ToolbarPosition.custom` +* See the README if you'd like to use any of these new features. `UserScript` and the context menu customization have external documentation via flutter_inappwebview - the docs are linked in the README. + +## [2.0.0+1] - 2021-04-22 +* Transitioned to fully native controls! These are extremely customizable and have much better UX than the previous controls. +* [BREAKING] refactored a lot of options into separate constructors +* [BREAKING] refactored toolbar classes, so toolbar customizations will need updating +* Added a bunch of interceptors and callbacks for button presses +* Added the ability to make custom buttons and set their positions +* Added native support for numerous Summernote plugins + * [BREAKING] removed all Summernote plugins except Summernote @ Mention. The package now supports the majority of plugins out of the box. + * Reduced package size by removing the Summernote plugin files + * Reduced size further by using a stripped-down version of Summernote @ Mention libs +* Added `execCommand` to controller to help you create custom toolbar buttons +* Improved automatic height adjustment +* Bumped dependencies +* [BREAKING] Require Flutter 2.0.0+ +* As always, see the README for full documentation on these changes +* See the [Migration Guide](https://github.com/tneotia/html-editor-enhanced/wiki/v2.0.0-Migration-Guide) for help migrating your v1.x.x widget code + +## [1.8.0] - 2021-04-07 +* Add support for `getSuggestionsMobile` (Summernote @ Mentions Plugin) - allows you to programatically return the list of mentions. + * Only supported on mobile. + * [BREAKING] renamed `mentions` to `mentionsWeb` as a result of this change +* Added support for the remainder of Summernote callbacks: + * `onBeforeCommand` + * `onChangeCodeview` + * `onDialogShown` + * `onImageUploadError` + * `onMouseDown` + * `onMouseUp` + * `onScroll` + * See the README for how these work. +* Added a few new functions: + * recalculateHeight(): recalculates the editor height and applies it + * addNotification(): adds a notification bar to the bottom of the editor in a specified style with specified text + * removeNotification(): removes the current notification from the bottom of the editor +* Fixed blank space at the bottom of the editor when `showBottomToolbar: false` +* Fixed 'Android resource linking failed' (bumped flutter_inappwebview to 5.3.1+1) + +## [1.7.1] - 2021-03-26 +* Fixed bug where initial text would not be inserted and default toolbar would be shown regardless of editor options +* Significantly improved keyboard height detection (detect when keyboard comes up and goes down) +* Adjusted HTML processing algorithm to fix issues where `"` and `'` would not be properly escaped on HTML insertion + * Added `processNewLineAsBr` - this will replace any `\n` in the input string to `
` rather than the default `""` + * Applied processing to `setHint()` and `insertHtml()` functions +* Added support for returning the file's base64 data in `onImageUpload` and `onFileUpload` + * Now you can use `MultipartFile.fromBytes()` to upload to server - [example](https://github.com/tneotia/html-editor-enhanced#example-for-onimageupload-and-onimagelinkinsert) +* Added support for `onFileUploadError` and `onFileLinkInsert` (Summernote File plugin) +* Added support for `maximumFileSize` (Summernote File plugin) +* See the README for more details on these changes + +## [1.7.0+1] - 2021-03-22 +* Fixed `type 'double' is not a subtype of type 'int?' in type cast` on iOS + * By extension this fixes the `adjustHeightForKeyboard` not working on iOS +* Fixed `Bad state: Cannot add new events after calling close` exception when disposing the page containing the editor +* Fixed web page not found when inserting a video URL (see [here](https://github.com/summernote/summernote/issues/3252)) + +## [1.7.0] - 2021-03-17 +* [BREAKING]: + * Refactored `height`, `autoAdjustHeight`, `decoration`, `showBottomToolbar`, and `darkMode` into new `HtmlEditorOptions` class - see README for how to migrate + * Removed 'Summernote Classes' plugin + * Sorry for all the breaking changes lately - I think I've finally figured out how I want to do the API design so there should be far less in future releases +* Added `onImageUpload` callback that fires when an image is inserted via `` +* Added `onImageLinkInsert` callback that fires when an image is inserted via URL +* Added `shouldEnsureVisible` that scrolls the editor into view when it is focused or text is typed, kind of like `TextField`s +* Added `adjustHeightForKeyboard` (default true) that adjusts the editor's height when the keyboard is active to ensure no content is cut off by the keyboard +* Added `filePath` which allows you to provide a completely custom HTML file to load +* If you plan on using any of the above, it is highly recommend looking at the README for more details and examples. +* Removed disabled scroll feature since it prevented the editor from scrolling even when the editor content was larger than the height +* Code cleanup + +## [1.6.0] - 2021-03-13 +* [BREAKING] removed `dispose()` method on controller + * The editor no longer uses a `Stream` to get text and therefore nothing needs to be disposed +* Added `onInit` callback that fires once the editor is ready to function and accept JS +* Added a few new parameters: + * `autoAdjustHeight` - for `HtmlEditor`: default true, automatically adjusts the height of the editor to make sure scrolling is not necessary + * `processInputHtml` - for `HtmlEditorController`: processes input HTML (e.g. new lines become `
`) + * `processOutputHtml` - for `HtmlEditorController`: processes output HTML (e.g. `


` becomes `""`) +* Added more plugins: + * Summernote Case Converter + * Summernote List Styles + * Summernote RTL + * Summernote @ Mention + * Summernote Codewrapper + * Summernote File + * See the README for more details. +* Added shim for dart:ui to remove the `ui.PlatformViewRegistry not found` error +* Added the summernote-no-plugins.html file to load a de-bloated HTML file when no plugins are active +* Fixed bug where two editors would be initialized in the same webview in some cases +* Reduced the size of assets to ~650kb - ~300kb summernote libs, ~350kb plugin libs +* Code cleanup + +## [1.5.0+1] - 2021-03-10 +* Fixed getText() returning null on mobile for any device + +## [1.5.0] - 2021-03-01 +* Nullsafety preview +* Added Flutter's Hybrid Composition to the HTML Editor. This significantly improves the keyboard experience on Android. + +## [1.4.0] - 2021-03-01 +* [BREAKING] removed `HtmlParser` for calling methods, instead you now must pass an `HtmlEditorController` to the plugin (like a `TextField`). All methods are accessible from that controller. See the usage section in the README for an example. + * This allows you to have multiple independent editors on a page, whereas earlier the package would not know which editor the method should be called on. +* Add support for certain Summernote plugins from [Summernote Awesome](https://github.com/summernote/awesome-summernote). See the README for details on the API and the currently supported plugins. +* Nullsafety pre-release coming soon. + +## [1.3.0] - 2021-02-23 +* Add official support for Flutter Web +* Add support for modifying the toolbar options. See the README for details on the API. +* Add support for a native dark mode +* Removed image_picker plugin and image button in toolbar because users can insert images via the image button in Summernote + * [BREAKING] Removed the `imageWidth` and `useBottomSheet` params due to the above change + +## [1.2.0+1] - 2021-02-20 +* Add support for accessing `InAppWebViewController` via a getter +* Add support for inserting files via the editor dialog itself +* Add methods: + * toggle code view + * enable/disable editor + * undo/redo + * inserting plaintext/HTML/images/links +* Add callbacks: + * onChange + * onEnter + * onFocus/onBlur/onBlurCodeview + * onKeyUp/onKeyDown + * onPaste +* Downgraded dependencies to non-nullsafety to prevent errors +* Updated docs and example app to showcase new features, refer to those for info on the above changes + +## [1.1.1] - 2021-02-19 +* Minor update to add documentation to code and completely refactor/reorganize code + +## [1.1.0] - 2021-02-19 +* Switch webview dependency to `flutter_inappwebview` +* Remove localserver, instead get Summernote HTML directly from assets (improves performance and loading speeds) +* [BREAKING] Switch to `StatelessWidget` + * You no longer need a `GlobalKey` for the `HtmlEditorState`. All of the methods are static and can be called like so: + ```dart + HtmlEditor.setEmpty(); + ``` +* Fix deprecations and update dependencies + +## Flutter HTML Editor changes by xrb21 +## [1.0.1] - 2020-05-07 +* Update Readme usage for iOS + +## [1.0.0] - 2020-05-07 +* fixing iOS blank screen +* fixing text hint + +## [0.0.2+1] - 2020-05-02 +* fixing path packages + +## [0.0.2] - 2020-05-02 +* Change link repo + +## [0.0.1] - 2020-05-02 +* Initial Release \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9eb9d14 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 tneotia Tanay Neotia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7dce292 --- /dev/null +++ b/README.md @@ -0,0 +1,1135 @@ +# Flutter Html Editor - Enhanced +[![pub package](https://img.shields.io/pub/v/html_editor_enhanced.svg)](https://pub.dev/packages/html_editor_enhanced) + +Flutter HTML Editor Enhanced is a text editor for Android, iOS, and Web to help write WYSIWYG HTML code with the Summernote JavaScript wrapper. + +Note that the API shown in this README.md file shows only a part of the documentation and, also, conforms to the GitHub master branch only! So, here you could have methods, options, and events that aren't published/released yet! If you need a specific version, please change the GitHub branch of this repository to your version or use the online [API Reference](https://pub.dev/documentation/html_editor_enhanced/latest/) (recommended). + + + + + + + + + + + + +
Video ExampleLight Mode and
ToolbarType.nativeGrid
Dark Mode and
ToolbarPosition.belowEditor
GIF exampleLightDark
+ + + + + + + + +
Flutter Web
Flutter Web
+ +
+ +## Table of Contents: + +- ["Enhanced"? In what ways?](#in-what-ways-is-this-package-enhanced) + +- [Setup](#setup) + +- [Usage](#basic-usage) + +- [API Reference](#api-reference) + + - [Parameters Table](#parameters---htmleditor) + + - [Methods Table](#methods) + + - [Callbacks Table](#callbacks) + + - [Getters](#getters) + + - [Toolbar](#toolbar) + + - [Plugins](#plugins) + + - [`HtmlEditorOptions` Parameters](#htmleditoroptions-parameters) + + - [`autoAdjustHeight`](#autoadjustheight) + + - [`adjustHeightForKeyboard`](#adjustheightforkeyboard) + + - [`filePath`](#filepath) + + - [`shouldEnsureVisible`](#shouldensurevisible) + + - [`webInitialScripts`](#webinitialscripts) + + - [`HtmlToolbarOptions` Parameters](#htmltoolbaroptions-parameters) + + - [`customToolbarButtons` and `customToolbarButtonsInsertionIndices`](#customtoolbarbuttons-and-customtoolbarbuttonsinsertionindices) + + - [`linkInsertInterceptor`, `mediaLinkInsertInterceptor`, `otherFileLinkInsert`, `mediaUploadInterceptor`, and `onOtherFileUpload`](#linkinsertinterceptor-medialinkinsertinterceptor-otherfilelinkinsert-mediauploadinterceptor-and-onotherfileupload) + + - [`onButtonPressed` and `onDropdownChanged`](#onbuttonpressed-and-ondropdownchanged) + + - [`toolbarPosition: ToolbarPosition.custom`](#custom-toolbar-position-using-toolbarpositioncustom) + + - [`HtmlEditorController` Parameters](#htmleditorcontroller-parameters) + + - [`processInputHtml`, `processOutputHtml`, and `processNewLineAsBr`](#processinputhtml-processoutputhtml-and-processnewlineasbr) + +- [Examples](#examples) + +- [Notes](#notes) + +- [FAQ](#faq) + +- [License](#license) + +- [Contribution Guide](#contribution-guide) + +## In what ways is this package "enhanced"? + +1. It has official support for Flutter Web, with nearly all mobile features supported. Keyboard shortcuts like Ctrl+B for bold work as well! + +2. It has fully native Flutter-based widget controls + +3. It uses a heavily optimized [WebView](https://github.com/pichillilorenzo/flutter_inappwebview) to deliver the best possible experience when using the editor + +4. It doesn't use a local server to load the HTML code containing the editor. Instead, this package simply loads the HTML file, which improves performance and the editor's startup time. + +5. It uses a controller-based API. You don't have to fiddle around with `GlobalKey`s to access methods, instead you can simply call `.` anywhere you want. + +6. It has support for many of Summernote's methods + +7. It has support for all of Summernote's callbacks + +8. It exposes the `InAppWebViewController` so you can customize the WebView however you like - you can even load your own HTML code and inject your own JavaScript for your use cases. + +9. It has support for dark mode + +10. It has support for extremely granular toolbar customization + +More is on the way! File a feature request or contribute to the project if you'd like to see other features added. + +## Setup + +Add `html_editor_enhanced: ^2.5.0` as dependency to your pubspec.yaml. + +Make sure to declare internet support inside `AndroidManifest.xml`: `` + +Additional setup is required on iOS to allow the user to pick files from storage. See [here](https://github.com/miguelpruivo/flutter_file_picker/wiki/Setup#--ios) for more details. + +For images, the package uses `FileType.image`, for video `FileType.video`, for audio `FileType.audio`, and for any other file `FileType.any`. You can just complete setup for the specific buttons you plan to enable in the editor. + +#### v2.0.0 Migration Guide: + +[Migration Guide](https://github.com/tneotia/html-editor-enhanced/wiki/v2.0.0-Migration-Guide) + +## Basic Usage + +```dart +import 'package:html_editor/html_editor.dart'; + +HtmlEditorController controller = HtmlEditorController(); + +@override Widget build(BuildContext context) { + return HtmlEditor( + controller: controller, //required + htmlEditorOptions: HtmlEditorOptions( + hint: "Your text here...", + //initalText: "text content initial, if any", + ), + otherOptions: OtherOptions( + height: 400, + ), + ); +} +``` + +### Important note for Web: + +At the moment, there is quite a bit of flickering and repainting when having many UI elements draw over `IframeElement`s. See https://github.com/flutter/flutter/issues/71888 for more details. + +The current workaround is to build and/or run your Web app with `flutter run --web-renderer html` and `flutter build web --web-renderer html`. + +Follow https://github.com/flutter/flutter/issues/80524 for updates on a potential fix, in the meantime the above solution should resolve the majority of the flickering issues. + +## API Reference + +For the full API reference, see [here](https://pub.dev/documentation/html_editor_enhanced/latest/). + +For a full example, see [here](https://github.com/tneotia/html-editor-enhanced/tree/master/example). + +Below, you will find brief descriptions of the parameters the `HtmlEditor` widget accepts and some code snippets to help you use this package. + +### Parameters - `HtmlEditor` + +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**controller** | `HtmlEditorController` | empty | Required param. Create a controller instance and pass it to the widget. This ensures that any methods called work only on their `HtmlEditor` instance, allowing you to use multiple HTML widgets on one page. +**callbacks** | `Callbacks` | empty | Customize the callbacks for various events +**options** | `HtmlEditorOptions` | `HtmlEditorOptions()` | Class to set various options. See [below](#parameters---htmleditoroptions) for more details. +**plugins** | `List` | empty | Customize what plugins are activated. See [below](#plugins) for more details. +**toolbar** | `List` | See the widget's constructor | Customize what buttons are shown on the toolbar, and in which order. See [below](#toolbar) for more details. + +### Parameters - `HtmlEditorController` + +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**processInputHtml** | `bool` | `true` | Determines whether processing occurs on any input HTML (e.g. escape quotes, apostrophes, and remove `/n`s) +**processNewLineAsBr** | `bool` | `false` | Determines whether a new line (`\n`) becomes a `
` in any *input* HTML +**processOutputHtml** | `bool` | `true` | Determines whether processing occurs on any output HTML (e.g. `


` becomes `""`) + +### Parameters - `HtmlEditorOptions` + +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**autoAdjustHeight** | `bool` | `true` | Automatically adjust the height of the text editor by analyzing the HTML height once the editor is loaded. Recommended value: `true`. See [below](#autoadjustheight) for more details. +**adjustHeightForKeyboard** | `bool` | `true` | Adjust the height of the editor if the keyboard is active and it overlaps the editor to prevent the overlap. Recommended value: `true`, only works on mobile. See [below](#adjustheightforkeyboard) for more details. +**characterLimit** | `int` | `null` | Sets the character limit for the editor. When reaching the limit the user will not be allowed to type anymore. +**customOptions** | `String` | `null` | Provide custom options for Summernote initialization using the Summernote syntax (see [here](https://summernote.org/deep-dive/#initialization-options) +**darkMode** | `bool` | `null` | Sets the status of dark mode - `false`: always light, `null`: follow system, `true`: always dark +**filePath** | `String` | `null` | Allows you to specify your own HTML to be loaded into the webview. You can create a custom page with Summernote, or theoretically load any other editor/HTML. +**hint** | `String` | empty | Placeholder hint text +**initialText** | `String` | empty | Initial text content for text editor +**inputType** | `HtmlInputType` | `HtmlInputType.text` | Allows you to set how the virtual keyboard displays for the editor on mobile devices +**mobileContextMenu** | `ContextMenu` | `null` | Customize the context menu when a user selects text in the editor. See docs for `ContextMenu` [here](https://inappwebview.dev/docs/context-menu/basic-usage/) +**mobileLongPressDuration** | `Duration` | `Duration(milliseconds: 500)` | Set the duration until a long-press is recognized +**mobileInitialScripts** | `UnmodifiableListView` | `null` | Easily inject scripts to perform actions like changing the background color of the editor. See docs for `UserScript` [here](https://inappwebview.dev/docs/javascript/user-scripts/) +**webInitialScripts** | `UnmodifiableListView` | `null` | Easily inject scripts to perform actions like changing the background color of the editor. See [below](#webinitialscripts) for more details. +**shouldEnsureVisible** | `bool` | `false` | Scroll the parent `Scrollable` to the top of the editor widget when the webview is focused. Do *not* use this parameter if `HtmlEditor` is not inside a `Scrollable`. See [below](#shouldensurevisible) for more details. +**spellCheck** | `bool` | `false` | Specify whether or not to use spellcheck in the editor and underline wrong spellings. + +### Parameters - `HtmlToolbarOptions` + +#### Toolbar Options + +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**audioExtensions** | `List` | `null` | Allowed extensions when inserting audio files +**customToolbarButtons** | `List` | empty | Add custom buttons to the toolbar +**customToolbarInsertionIndices** | `List` | empty | Allows you to set where each custom toolbar button should be inserted into the toolbar widget list +**defaultToolbarButtons** | `List` | (all constructors active) | Allows you to hide/show certain buttons or certain groups of buttons +**otherFileExtensions** | `List` | `null` | Allowed extensions when inserting files other than image/audio/video +**imageExtensions** | `List` | `null` | Allowed extensions when inserting images +**initiallyExpanded** | `bool` | `false` | Sets whether the toolbar is initially expanded or not when using `ToolbarType.nativeExpandable` +**linkInsertInterceptor** | `FutureOr Function(String, String, bool)` | `null` | Intercept any links inserted into the editor. The function passes the display text, the URL, and whether it opens a new tab. +**mediaLinkInsertInterceptor** | `FutureOr Function(String, InsertFileType)` | `null` | Intercept any media links inserted into the editor. The function passes the URL and `InsertFileType` which indicates which file type was inserted +**mediaUploadInterceptor** | `FutureOr Function(PlatformFile, InsertFileType)` | `null` | Intercept any media files inserted into the editor. The function passes `PlatformFile` which holds all relevant file data, and `InsertFileType` which indicates which file type was inserted. +**onButtonPressed** | `FutureOr Function(ButtonType, bool?, void Function()?)` | `null` | Intercept any button presses. The function passes the enum for the pressed button, the current selected status of the button (if applicable) and a function to update the status (if applicable). +**onDropdownChanged** | `FutureOr Function(DropdownType, dynamic, void Function(dynamic)?)` | `null` | Intercept any dropdown changes. The function passes the enum for the changed dropdown, the changed value, and a function to update the changed value (if applicable). +**onOtherFileLinkInsert** | `Function(String)` | `null` | Intercept file link inserts other than image/audio/video. This handler is required when using the other file button, as the package has no built-in handlers +**onOtherFileUpload** | `Function(PlatformFile)` | `null` | Intercept file uploads other than image/audio/video. This handler is required when using the other file button, as the package has no built-in handlers +**otherFileExtensions** | `List` | `null` | Allowed extensions when inserting files other than image/audio/video +**toolbarType** | `ToolbarType` | `ToolbarType.nativeScrollable` | Customize how the toolbar is displayed (gridview, scrollable, or expandable) +**toolbarPosition** | `ToolbarPosition` | `ToolbarPosition.aboveEditor` | Set where the toolbar is displayed (above or below the editor) +**videoExtensions** | `List` | `null` | Allowed extensions when inserting videos + +#### Styling Options + +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**renderBorder** | `bool` | `false` | Render a border around dropdowns and buttons +**textStyle** | `TextStyle` | `null` | The `TextStyle` to use when displaying dropdowns and buttons +**separatorWidget** | `Widget` | `VerticalDivider(indent: 2, endIndent: 2, color: Colors.grey)` | Set the widget that separates each group of buttons/dropdowns +**renderSeparatorWidget** | `bool` | `true` | Whether or not the separator widget should be rendered +**toolbarItemHeight** | `double` | `36` | Set the height of dropdowns and buttons. Buttons will maintain a square aspect ratio. +**gridViewHorizontalSpacing** | `double` | `5` | The horizontal spacing to use between button groups when displaying the toolbar as `ToolbarType.nativeGrid` +**gridViewVerticalSpacing** | `double` | `5` | The vertical spacing to use between button groups when diplaying the toolbar as `ToolbarType.nativeGrid` + +#### Styling Options - applies to dropdowns only + +Parameter | Type | Default +------------ | ------------- | ------------- +**dropdownElevation** | `int` | `8` +**dropdownIcon** | `Widget` | `null` +**dropdownIconColor** | `Color` | `null` +**dropdownIconSize** | `double` | `24` +**dropdownItemHeight** | `double` | `kMinInteractiveDimension` (`48`) +**dropdownFocusColor** | `Color` | `null` +**dropdownBackgroundColor** | `Color` | `null` +**dropdownMenuDirection** | `DropdownMenuDirection` | `null` +**dropdownMenuMaxHeight** | `double` | `null` +**dropdownBoxDecoration** | `BoxDecoration` | `null` + +#### Styling Options - applies to buttons only + +Parameter | Type | Default +------------ | ------------- | ------------- +**buttonColor** | `Color` | `null` +**buttonSelectedColor** | `Color` | `null` +**buttonFillColor** | `Color` | `null` +**buttonFocusColor** | `Color` | `null` +**buttonHighlightColor** | `Color` | `null` +**buttonHoverColor** | `Color` | `null` +**buttonSplashColor** | `Color` | `null` +**buttonBorderColor** | `Color` | `null` +**buttonSelectedBorderColor** | `Color` | `null` +**buttonBorderRadius** | `BorderRadius` | `null` +**buttonBorderWidth** | `double` | `null` + +### Parameters - `Other Options` + +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**decoration** | `BoxDecoration` | `null` | `BoxDecoration` that surrounds the widget +**height** | `double` | `null` | Height of the widget (includes toolbar and editing area) + +### Methods + +Access these methods like this: `.` + +Method | Argument(s) | Returned Value(s) | Description +------------ | ------------- | ------------- | ------------- +**addNotification()** | `String` html, `NotificationType` notificationType | N/A | Adds a notification to the bottom of the editor with the provided HTML content. `NotificationType` determines how it is styled. +**clear()** | N/A | N/A | Resets the HTML editor to its default state +**clearFocus()** | N/A | N/A | Clears focus for the webview and resets the height to the original height on mobile. Do *not* use this method in Flutter Web. +**disable()** | N/A | N/A | Disables the editor (a gray mask is applied and all touches are absorbed) +**enable()** | N/A | N/A | Enables the editor +**execCommand()** | `String` command, `String` argument (optional) | N/A | Allows you to run any `execCommand` command easily. See the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) for usage. +**getText()** | N/A | `Future` | Returns the current HTML in the editor +**getSelectedTextWeb()** | `bool` (optional) | `Future` | Get the currently selected text in the editor, with or without the HTML tags. Do *not* use this method in Flutter Mobile. +**insertHtml()** | `String` | N/A | Inserts the provided HTML string into the editor at the current cursor position. Do *not* use this method for plaintext strings. +**insertLink()** | `String` text, `String` url, `bool` isNewWindow | N/A | Inserts a hyperlink using the provided text and url into the editor at the current cursor position. `isNewWindow` defines whether a new browser window is launched if the link is tapped. +**insertNetworkImage()** | `String` url, `String` filename (optional) | N/A | Inserts an image using the provided url and optional filename into the editor at the current cursor position. The image must be accessible via a URL. +**insertText()** | `String` | N/A | Inserts the provided text into the editor at the current cursor position. Do *not* use this method for HTML strings. +**recalculateHeight()** | N/A | N/A | Recalculates the height of the editor by re-evaluating `document.body.scrollHeight` +**redo()** | N/A | N/A | Redoes the last command in the editor +**reloadWeb()** | N/A | N/A | Reloads the webpage in Flutter Web. This is mainly provided to refresh the text editor theme when the theme is changed. Do *not* use this method in Flutter Mobile. +**removeNotification()** | N/A | N/A | Removes the current notification from the bottom of the editor +**resetHeight()** | N/A | N/A | Resets the height of the webview to the original height. Do *not* use this method in Flutter Web. +**setHint()** | `String` | N/A | Sets the current hint text of the editor +**setFocus()** | N/A | N/A | If the pointer is in the webview, the focus will be set to the editor box +**setFullScreen()** | N/A | N/A | Sets the editor to take up the entire size of the webview +**setText()** | `String` | N/A | Sets the current text in the HTML to the input HTML string +**toggleCodeview()** | N/A | N/A | Toggles between the code view and the rich text view +**undo()** | N/A | N/A | Undoes the last command in the editor + +### Callbacks + +Every callback is defined as a `Function()`. See the [documentation](https://pub.dev/documentation/html_editor_enhanced/latest/) for more specific details on each callback. + +Callback | Parameter(s) | Description +------------ | ------------- | ------------- +**onBeforeCommand** | `String` | Called before certain commands are called (like undo and redo), passes the HTML in the editor before the command is called +**onChangeContent** | `String` | Called when the content of the editor changes, passes the current HTML in the editor +**onChangeCodeview** | `String` | Called when the content of the codeview changes, passes the current code in the codeview +**onChangeSelection** | `EditorSettings` | Called when the current selection of the editor changes, passes all editor settings (e.g. bold/italic/underline, color, text direction, etc). +**onDialogShown** | N/A | Called when either the image, link, video, or help dialogs are shown +**onEnter** | N/A | Called when enter/return is pressed +**onFocus** | N/A | Called when the rich text field gains focus +**onBlur** | N/A | Called when the rich text field or the codeview loses focus +**onBlurCodeview** | N/A | Called when the codeview either gains or loses focus +**onImageLinkInsert** | `String` | Called when an image is inserted via URL, passes the URL of the image +**onImageUpload** | `FileUpload` | Called when an image is inserted via upload, passes `FileUpload` which holds filename, date modified, size, and MIME type +**onImageUploadError** | `FileUpload`, `String`, `UploadError` | Called when an image fails to inserted via upload, passes `FileUpload` which may hold filename, date modified, size, and MIME type (or be null), `String` which is the base64 (or null), and `UploadError` which describes the type of error +**onInit** | N/A | Called when the rich text field is initialized and JavaScript methods can be called +**onKeyDown** | `int` | Called when a key is downed, passes the keycode of the downed key +**onKeyUp** | `int` | Called when a key is released, passes the keycode of the released key +**onMouseDown** | N/A | Called when the mouse/finger is downed +**onMouseUp** | N/A | Called when the mouse/finger is released +**onNavigationRequestMobile** | `String` | Called when the URL of the webview is about to change on mobile only +**onPaste** | N/A | Called when content is pasted into the editor +**onScroll** | N/A | Called when editor box is scrolled + +### Getters + +1) `.editorController`. This returns the `InAppWebViewController`, which manages the webview that displays the editor. + +This is extremely powerful, as it allows you to create your own custom methods and implementations directly in your app. See [`flutter_inappwebview`](https://github.com/pichillilorenzo/flutter_inappwebview) for documentation on the controller. + +This getter *should not* be used in Flutter Web. If you are making a cross platform implementation, please use `kIsWeb` to check the current platform in your code. + +2) `.characterCount`. This returns the number of text characters in the editor. + +### Toolbar + +This API allows you to customize the toolbar in a nice, readable format. + +By default, the toolbar will have all buttons enabled except the "other file" button, because the plugin cannot handle those files out of the box. + +Here's what that a custom implementation could look like: + +```dart +HtmlEditorController controller = HtmlEditorController(); +Widget htmlEditor = HtmlEditor( + controller: controller, //required + //other options + toolbarOptions: HtmlToolbarOptions( + defaultToolbarButtons: [ + StyleButtons(), + ParagraphButtons(lineHeight: false, caseConverter: false) + ] + ) +); +``` + +If you leave the `Toolbar` constructor blank (like `Style()` above), then the package interprets that you want all the buttons for the `Style` group to be visible. + +If you want to remove certain buttons from the group, you can set their button name to `false`, as shown in the example above. + +Order matters! Whatever group you set first will be the first group of buttons to display. + +If you don't want to show an entire group of buttons, simply don't include their constructor in the `Toolbar` list! This means that if you want to disable just one button, you still have to provide all other constructors. + +You can also create your own toolbar buttons! See [below](#customtoolbarbuttons-and-customtoolbarbuttonsinsertionindices) for more details. + +### Plugins + +This API allows you to add certain Summernote plugins from the [Summernote Awesome library](https://github.com/summernote/awesome-summernote). + +Currently the following plugins are supported: + +1. [Summernote Case Converter](https://github.com/piranga8/summernote-case-converter) - +Convert the selected text to all lowercase, all uppercase, sentence case, or title case. Supported via a dropdown in the toolbar in `ParagraphButtons`. + +2. [Summernote List Styles](https://github.com/tylerecouture/summernote-list-styles) - +Customize the ul and ol list style. Supported via a dropdown in the toolbar in `ListButtons`. + +3. [Summernote RTL](https://github.com/virtser/summernote-rtl-plugin) - +Switch the currently selected text between LTR and RTL format. Supported via two buttons in the toolbar in `ParagraphButtons`. + +4. [Summernote At Mention](https://github.com/team-loxo/summernote-at-mention) - +Shows a dropdown of available mentions when the '@' character is typed into the editor. The implementation requires that you pass a list of available mentions, and you can also provide a function to call when a mention is inserted into the editor. + +5. [Summernote File](https://github.com/mathieu-coingt/summernote-file) - +Support picture files (jpg, png, gif, wvg, webp), audio files (mp3, ogg, oga), and video files (mp4, ogv, webm) in base64. Supported via the image/audio/video/other file buttons in the toolbar in `InsertButtons`. + +This list is not final, more can be added. If there's a specific plugin you'd like to see support for, please file a feature request! + +No plugins are activated activated by default. They can be activated by modifying the toolbar items, see [above](#toolbar) for details. + +To activate Summernote At Mention: + +```dart +HtmlEditorController controller = HtmlEditorController(); +Widget htmlEditor = HtmlEditor( + controller: controller, //required + //other options + plugins: [ + SummernoteAtMention( + //returns the dropdown items on mobile + getSuggestionsMobile: (String value) { + List mentions = ['test1', 'test2', 'test3']; + return mentions + .where((element) => element.contains(value)) + .toList(); + }, + //returns the dropdown items on web + mentionsWeb: ['test1', 'test2', 'test3'], + onSelect: (String value) { + print(value); + } + ), + ] +); +``` + +### `HtmlEditorOptions` parameters + +This section contains longer descriptions for select parameters in `HtmlEditorOptions`. For parameters not mentioned here, see the parameters table [above](#parameters---htmleditoroptions) for a short description. If you have further questions, please file an issue. + +#### `autoAdjustHeight` + +Default value: true + +This option parameter sets the height of the editor automatically by getting the value returned by the JS `document.body.scrollHeight` and the toolbar `GlobalKey` (`toolbarKey.currentContext?.size?.height`). + +This is useful because the toolbar could have either 1 - 5 rows depending on the widget's configuration, screen size, orientation, etc. There is no reliable way to tell how large the toolbar is going to be until after `build()` is executed, and thus simply hardcoding the height of the webview can induce either empty space at the bottom or a scrollable webview. By using the JS and a `GlobalKey` on the toolbar widget, the editor can get the exact height and update the widget to reflect that. + +There is a drawback: The webview will visibly shift size after the page is loaded. Depending on how large the change is, it could be jarring. Sometimes, it takes a second for the webview to adjust to the new size and you might see the editor page jump down/up a second or two after the webview container adjusts itself. + +If this does not help your use case feel free to disable it, but the recommended value is `true`. + +#### `adjustHeightForKeyboard` + +Default value: true, only considered on mobile + +This option parameter changes the height of the editor if the keyboard is active and it overlaps with the editor. + +This is useful because webviews do not shift their view when the keyboard is active on Flutter at the moment. This means that if your editor spans the height of the page, if the user types a long text they might not be able to see what they are typing because it is obscured by the keyboard. + +When this parameter is enabled, the webview will shift to the perfect height to ensure all the typed content is visible, and as soon as the keyboard is hidden, the editor shifts back to its original height. + +The webview does take a moment to shift itself back and forth after the keyboard pops up/keyboard disappears, but the delay isn't too bad. It is highly recommended to have the webview in a `Scrollable` and `shouldEnsureVisible` enabled if there are other widgets on the page - if the editor is on the bottom half of the page it will be scrolled to the top and then the height will be set accordingly, rather than the plugin trying to set the height for a webview obscured completely by the keyboard. + +See [below](#example-for-adjustheightforkeyboard) for an example use case. + +If this does not help your use case feel free to disable it, but the recommended value is `true`. + +#### `filePath` + +This option parameter allows you to fully customize what HTML is loaded into the webview, by providing a file path to a custom HTML file from assets. + +There is a particular format that is required/recommended when providing a file path for web, because the web implementation will load the HTML as a `String` and make changes to it directly using `replaceAll().`, rather than using a method like `evaluateJavascript()` - because that does not exist on Web. + +On Web, you should include the following: + +1. `` inside `` - this enables dark mode support + +2. `` inside `` and below your summernote `

` - this allows the JS and CSS files for any enabled plugins to be loaded + +3. `` inside `` and below your summernote `
` - REQUIRED - this allows Dart and JS to communicate with each other. If you don't include this, then methods/callbacks will do nothing. + +Notes: + +1. Do *not* initialize the Summernote editor in your custom HTML file! The package will take care of that. + +2. Make sure to set the `id` for Summernote to `summernote-2`! - `
`. + +3. Make sure to include jquery and the Summernote JS/CSS in your file! The package does not do this for you.

+You can use these files from the package to avoid adding more asset files: + +```html + + + +``` + +See the example HTML file [below](#example-html-for-filepath) for an actual example. + +#### `shouldEnsureVisible` + +Default value: false + +This option parameter will scroll the editor container into view whenever the webview is focused or text is typed into the editor. + +You can only use this parameter if your `HtmlEditor` is inside a `Scrollview`, otherwise it does nothing. + +This is useful in cases where the page is a `SingleChildScrollView` or something similar with multiple widgets (eg a form). When the user is going through the different fields, it will pop the webview into view, just like a `TextField` would scroll into in view if text is being typed inside it. + +See [below](#example-for-shouldensurevisible) for an example with a good way to use this. + +#### `webInitialScripts` + +This parameter allows you to specify custom JavaScript for the editor on Web. These can be called at any point in time using `controller.evaluateJavascriptWeb`. + +You must add these scripts using the `WebScript` class, which takes a `name` and a `script` argument. `name` *must* be a unique identifier, otherwise your desired script may not be executed. Pass your JavaScript code in the `script` argument. + +The package supports returning values from JavaScript as well. You should run `var result = await controller.evaluateJavascriptWeb(, hasReturnValue: true);`. + +To get the return value, you must add the following at the end of your JavaScript: + +```javascript +window.parent.postMessage(JSON.stringify({"type": "toDart: ", }), "*"); +``` + +You can view a complete example [below](#example-for-webinitialscripts) + +### `HtmlToolbarOptions` parameters + +This section contains longer descriptions for select parameters in `HtmlToolbarOptions`. For parameters not mentioned here, see the parameters table [above](#parameters---htmltoolbaroptions) for a short description. If you have further questions, please file an issue. + +#### `customToolbarButtons` and `customToolbarButtonsInsertionIndices` + +These two parameters allow you to insert custom buttons and set where they are inserted into the toolbar widget list. + +This would look something like this: + +```dart +HtmlEditorController controller = HtmlEditorController(); +Widget htmlEditor = HtmlEditor( + controller: controller, //required + //other options + toolbarOptions: HtmlToolbarOptions( + defaultToolbarButtons: [ + StyleButtons(), + FontSettingButtons(), + FontButtons(), + ColorButtons(), + ListButtons(), + ParagraphButtons(), + InsertButtons(), + OtherButtons(), + ], + customToolbarButtons: [ + //your widgets here + Button1(), + Button2(), + ], + customToolbarInsertionIndices: [2, 5] + ) +); +``` + +In the above example, we have defined two buttons to be inserted at indices 2 and 5. These buttons will *not* be inserted before `FontSettingButtons` and before `ListButtons`, respectively! Each default button group may have a few different sub-groups: + +Button Group | Number of Subgroups +------------ | ------------- +`StyleButtons` | 1 +`FontSettingButtons` | 3 +`FontButtons` | 2 +`ColorButtons` | 1 +`ListButtons` | 2 +`ParagraphButtons` | 5 +`InsertButtons` | 1 +`OtherButtons` | 2 + +If some of your buttons are deactivated, the number of subgroups could be reduced. The insertion index depends on these subgroups rather than the overall button group. An easy way to count the insertion index is to build the app and count the number of separator spaces between each button group/dropdown before the location you want to insert your button. + +So with this in mind, `Button1` will be inserted between the first two subgroups in `FontSettingButtons`, and `Button2` will be inserted between the two subgroups in `FontButtons`. + +When creating an `onPressed`/`onTap`/`onChanged` method for your widget, you can use `controller.execCommand` or any of the other methods on the controller to perform actions in the editor. + +Notes: + +1. using `controller.editorController.` will do nothing on Web! + +2. If you don't provide `customToolbarButtonsInsertionIndices`, the plugin will insert your buttons at the end of the default toolbar list + +3. If you provide `customToolbarButtonsInsertionIndices`, it ***must*** be the same length as your `customToolbarButtons` widget list. + +#### `linkInsertInterceptor`, `mediaLinkInsertInterceptor`, `otherFileLinkInsert`, `mediaUploadInterceptor`, and `onOtherFileUpload` + +These callbacks help you intercept any links or files being inserted into the editor. + +Parameter | Type | Description +------------ | ------------- | ------------- +**linkInsertInterceptor** | `FutureOr Function(String, String, bool)` | Intercept any links inserted into the editor. The function passes the display text (`String`), the URL (`String`), and whether it opens a new tab (`bool`). +**mediaLinkInsertInterceptor** | `FutureOr Function(String, InsertFileType)` | Intercept any media links inserted into the editor. The function passes the URL (`String`). +**mediaUploadInterceptor** | `FutureOr Function(PlatformFile, InsertFileType)` | Intercept any media files inserted into the editor. The function passes `PlatformFile` which holds all relevant file data. You can use this to upload into your server, to extract base64 data, perform file validation, etc. It also passes the file type (image/audio/video). +**onOtherFileLinkInsert** | `Function(String)` | Intercept file link inserts other than image/audio/video. This handler is required when using the other file button, as the package has no built-in handlers. The function passes the URL (`String`). It also passes the file type (image/audio/video) +**onOtherFileUpload** | `Function(PlatformFile)` | Intercept file uploads other than image/audio/video. This handler is required when using the other file button, as the package has no built-in handlers. The function passes `PlatformFile` which holds all relevant file data. You can use this to upload into your server, to extract base64 data, perform file validation, etc. + +For `linkInsertInterceptor`, `mediaLinkInsertInterceptor`, and `mediaUploadInterceptor`, you must return a `bool` to tell the plugin what it should do. When you return false, it assumes that you have handled the user request and taken action. When you return true, the plugin will use the default handlers to handle the user request. + +`onOtherFileLinkInsert` and `onOtherFileUpload` are required when using the "other file" button. This button isn't active by default, so if you make it active, you must provide these functions, otherwise nothing will happen when the user inserts a file other than image/audio/video. + +See [below](#example-for-linkinsertinterceptor-medialinkinsertinterceptor-otherfilelinkinsert-mediauploadinterceptor-and-onotherfileupload) for an example. + +#### `onButtonPressed` and `onDropdownChanged` + +These callbacks help you intercept any button presses or dropdown changes. + +Parameter | Type | Description +------------ | ------------- | ------------- +**onButtonPressed** | `FutureOr Function(ButtonType, bool?, void Function()?)` | Intercept any button presses. The function passes the enum for the pressed button, the current selected status of the button (if applicable) and a function to update the status (if applicable). +**onDropdownChanged** | `FutureOr Function(DropdownType, dynamic, void Function(dynamic)?)` | Intercept any dropdown changes. The function passes the enum for the changed dropdown, the changed value, and a function to update the changed value (if applicable). + +You must return a `bool` to tell the plugin what it should do. When you return false, it assumes that you have handled the user request and taken action. When you return true, the plugin will use the default handlers to handle the user request. + +Some buttons and dropdowns, such as copy/paste and the case converter, don't need to update their changed value, so functions to update the value after handling the user request will not be provided for those buttons. + +See [below](#example-for-onbuttonpressed-and-ondropdownchanged) for an example. + +#### Custom toolbar position using `ToolbarPosition.custom` + +You can use `toolbarPosition: ToolbarPosition.custom` and the `ToolbarWidget()` widget to fully customize exactly where you want to place the toolbar. THe possibilities are endless - you could place the toolbar in a sticky header using Slivers, you could decide to show/hide the toolbar whenever you please, or you could make the toolbar into a floating, draggable widget! + +`ToolbarWidget()` requires the `HtmlEditorController` you created for the editor itself, along with the `HtmlToolbarOptions` you supplied to the `Html` constructor. These can be simply copy-pasted, no changed necessary. + +A basic example where the toolbar is placed in a different location than normal: + +```dart +HtmlEditorController controller = HtmlEditorController(); +Widget column = Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HtmlEditor( + controller: controller, + htmlEditorOptions: HtmlEditorOptions( + hint: 'Your text here...', + shouldEnsureVisible: true, + //initialText: "

text content initial, if any

", + ), + htmlToolbarOptions: HtmlToolbarOptions( + toolbarPosition: ToolbarPosition.custom, //required to place toolbar anywhere! + //other options + ), + otherOptions: OtherOptions(height: 550), + ), + //other widgets here + Widget1(), + Widget2(), + ToolbarWidget( + controller: controller, + htmlToolbarOptions: HtmlToolbarOptions( + toolbarPosition: ToolbarPosition.custom, //required to place toolbar anywhere! + //other options + ), + ) + ] +); +``` + +### `HtmlEditorController` Parameters + +#### `processInputHtml`, `processOutputHtml`, and `processNewLineAsBr` + +Default values: true, true, false, respectively + +`processInputHtml` replaces any occurrences of `"` with `\\"`, `'` with `\\'`, and `\r`, `\r\n`, `\n`, and `\n\n` with empty strings. This is necessary to prevent syntax exceptions when inserting HTML into the editor as quotes and other special characters will not be escaped. If you have already sanitized and escaped all relevant characters from your HTML input, it is recommended to set this parameter `false`. You may also want to set this parameter `false` on Web, as in testing it seems these characters are handled correctly by default, but that may not be the case for your HTML. + +`processOutputHtml` replaces the output HTML with `""` if: + +1. It is empty + +2. It is `

` + +3. It is `


` + +4. It is `


` + +These may seem a little random, but they are the three possible default/initial HTML codes the Summernote editor will have. If you'd like to still receive these outputs, set the parameter `false`. + +`processNewLineAsBr` will replace `\n` and `\n\n` with `
`. This is only recommended when inserting plaintext as the initial value. In typical HTML any new-lines are ignored, and therefore this parameter defaults to `false`. + +## Examples + +See the [example app](https://github.com/tneotia/html-editor-enhanced/blob/master/example/lib/main.dart) to see how the majority of methods & callbacks can be used. You can also play around with the parameters to see how they function. + +This section will be updated later with more specialized and specific examples as this library grows and more features are implemented. + +### Example for `linkInsertInterceptor`, `mediaLinkInsertInterceptor`, `otherFileLinkInsert`, `mediaUploadInterceptor`, and `onOtherFileUpload`: + +
Example code + +Note: This example uses the [http](https://pub.dev/packages/http) package. + +```dart +import 'package:file_picker/file_picker.dart'; +import 'package:http/http.dart' as http; + + Widget editor = HtmlEditor( + controller: controller, + toolbarOptions: ToolbarOptions( + mediaLinkInsertInterceptor: (String url, InsertFileType type) { + if (url.contains(website_url)) { + controller.insertNetworkImage(url); + } else { + controller.insertText("This file is invalid!"); + } + return false; + }, + mediaUploadInterceptor: (PlatformFile file, InsertFileType type) async { + print(file.name); //filename + print(file.size); //size in bytes + print(file.extension); //MIME type (e.g. image/jpg) + //either upload to server: + if (file.bytes != null && file.name != null) { + final request = http.MultipartRequest('POST', Uri.parse("your_server_url")); + request.files.add(http.MultipartFile.fromBytes("file", file.bytes, filename: file.name)); //your server may require a different key than "file" + final response = await request.send(); + //try to insert as network image, but if it fails, then try to insert as base64: + if (response.statusCode == 200) { + controller.insertNetworkImage(response.body["url"], filename: file.name!); //where "url" is the url of the uploaded image returned in the body JSON + } else { + if (type == InsertFileType.image) { + String base64Data = base64.encode(file.bytes!); + String base64Image = + """"""; + controller.insertHtml(base64Image); + } else if (type == InsertFileType.video) { + String base64Data = base64.encode(file.bytes!); + String base64Image = + """
+ +### Example for `onButtonPressed` and `onDropdownChanged` + +
Example code + +```dart + Widget editor = HtmlEditor( + controller: controller, + toolbarOptions: ToolbarOptions( + onButtonPressed: (ButtonType type, bool? status, Function()? updateStatus) { + print("button '${describeEnum(type)}' pressed, the current selected status is $status"); + //run a callback and return false and update the status, otherwise + return true; + }, + onDropdownChanged: (DropdownType type, dynamic changed, Function(dynamic)? updateSelectedItem) { + print("dropdown '${describeEnum(type)}' changed to $changed"); + //run a callback and return false and update the changed value, otherwise + return true; + }, + ), + ); +``` + +
+ +### Example for `adjustHeightForKeyboard`: + +
Example code + +```dart +class _HtmlEditorExampleState extends State { + final HtmlEditorController controller = HtmlEditorController(); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (!kIsWeb) { + // this is extremely important to the example, as it allows the user to tap any blank space outside the webview, + // and the webview will lose focus and reset to the original height as expected. + controller.clearFocus(); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text(widget.title), + elevation: 0, + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + //other widgets + HtmlEditor( + controller: controller, + htmlEditorOptions: HtmlEditorOptions( + shouldEnsureVisible: true, + //adjustHeightForKeyboard is true by default + hint: "Your text here...", + //initialText: "

text content initial, if any

", + ), + otherOptions: OtherOptions( + height: 550,c + ), + ), + //other widgets + ], + ), + ), + ), + ); + } +} +``` + +
+ +### Example for `shouldEnsureVisible`: + +
Example code + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; + +class _ExampleState extends State { + final HtmlEditorController controller = HtmlEditorController(); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (!kIsWeb) { + //these lines of code hide the keyboard and clear focus from the webview when any empty + //space is clicked. These are very important for the shouldEnsureVisible to work as intended. + SystemChannels.textInput.invokeMethod('TextInput.hide'); + controller.editorController!.clearFocus(); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text(widget.title), + elevation: 0, + actions: [ + IconButton( + icon: Icon(Icons.check), + tooltip: "Save", + onPressed: () { + //save profile details + } + ), + ] + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(left: 18, right: 18), + child: TextField( + controller: titleController, + textInputAction: TextInputAction.next, + focusNode: titleFocusNode, + decoration: InputDecoration( + hintText: "Name", + border: InputBorder.none + ), + ), + ), + SizedBox(height: 16), + HtmlEditor( + controller: controller, + htmlEditorOptions: HtmlEditorOptions( + shouldEnsureVisible: true, + hint: "Description", + ), + otherOptions: OtherOptions( + height: 450, + ), + ), + SizedBox(height: 16), + Padding( + padding: EdgeInsets.only(left: 18, right: 18), + child: TextField( + controller: bioController, + textInputAction: TextInputAction.next, + focusNode: bioFocusNode, + decoration: InputDecoration( + hintText: "Bio", + border: InputBorder.none + ), + ), + ), + Image.network("path_to_profile_picture"), + IconButton( + icon: Icon(Icons.edit, size: 35), + tooltip: "Edit profile picture", + onPressed: () async { + //open gallery and make api call to update profile picture + } + ), + //etc... just a basic form. + ], + ), + ), + ), + ); + } +} +``` + +
+ +### Example HTML for `filePath`: + +
Example HTML + +```html + + + + + + + Summernote Text Editor HTML + + + + + + +
+ + + + + +``` + +
+ +### Example for `webInitialScripts`: + +
View code + +```dart + String result = ''; + final HtmlEditorController controller = HtmlEditorController(); + final FocusNode node = FocusNode(); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (!kIsWeb) { + controller.clearFocus(); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text(widget.title), + elevation: 0, + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HtmlEditor( + controller: controller, + htmlEditorOptions: HtmlEditorOptions( + darkMode: false, + webInitialScripts: UnmodifiableListView([ + WebScript(name: "editorBG", script: "document.getElementsByClassName('note-editable')[0].style.backgroundColor='blue';"), + WebScript(name: "height", script: """ + var height = document.body.scrollHeight; + window.parent.postMessage(JSON.stringify({"type": "toDart: height", "height": height}), "*"); + """), + ]) + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.evaluateJavascriptWeb("editorBG"); + }, + child: + Text('Change Background', style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () async { + var result = await controller.evaluateJavascriptWeb("height", hasReturnValue: true); + print(result); // prints "{type: toDart: height, height: 561}" + }, + child: + Text('Get Height', style: TextStyle(color: Colors.white)), + ), + ] + ), + ), + ], + ), + ), + ), + ); + } +``` + +
+ +## Notes + +Due to this package depending on a webview for rendering the HTML editor, there will be some general weirdness in how the editor behaves. Unfortunately, these are not things I can fix, they are inherent problems with how webviews function on Flutter. + +If you do find any issues, please report them in the Issues tab and I will see if a fix is possible, but if I close the issue it is likely due to the above fact. + +1. When switching between dark and light mode, a reload is required for the HTML editor to switch to the correct color scheme. You can implement this programmatically in Flutter Mobile: `.editorController.reload()`, or in Flutter Web: `.reloadWeb()`. This will reset the editor! You can save the current text, reload, and then set the text if you'd like to maintain the state. + +2. If you are making a cross platform implementation and are using either the `editorController` getter or the `reloadWeb()` method, use `kIsWeb` in your app to ensure you are calling these in the correct platform. + +## FAQ + +
View answered questions + +* How can I display the final output of the editor? - Examples given for both raw HTML output and rendered HTML output - https://github.com/tneotia/html-editor-enhanced/issues/2 + +* How can I set the editor to "fullscreen" by default? - https://github.com/tneotia/html-editor-enhanced/issues/4 + +* The editor does not accept any key inputs when using the iOS Simulator. How can I fix this? - https://github.com/tneotia/html-editor-enhanced/issues/7 + +* Clicking on the editor makes the cursor appear on the second line relative with the hint. Is there a workaround? - https://github.com/tneotia/html-editor-enhanced/issues/24 + +* How can I give the editor box a custom background color on mobile? - https://github.com/tneotia/html-editor-enhanced/issues/27 + +* I see a file upload button in the top left of my application on Web. How can I remove it? - https://github.com/tneotia/html-editor-enhanced/issues/28 + +* I can't tap drawer items above the text editor on Web. How can I fix this? - https://github.com/tneotia/html-editor-enhanced/issues/30 + +* How can I remove the "dragbar" at the bottom of the editor? - https://github.com/tneotia/html-editor-enhanced/issues/42 + +* How can I detect if an image has been deleted from the editor? - https://github.com/tneotia/html-editor-enhanced/issues/43 + +* How can I handle editor focus? - https://github.com/tneotia/html-editor-enhanced/issues/47 + +* How can I set the default text direction for the editor content? - https://github.com/tneotia/html-editor-enhanced/issues/49 + +* How can I handle relative URLs for images in my initial text? - https://github.com/tneotia/html-editor-enhanced/issues/50 + +* How can I give the editor box a custom background color on web? - https://github.com/tneotia/html-editor-enhanced/issues/57 + +* How can I create a toolbar dropdown for custom fonts? - https://github.com/tneotia/html-editor-enhanced/issues/59 + +* How can I translate things like dialog text? - https://github.com/tneotia/html-editor-enhanced/issues/69 + +* How can I disable copy/paste buttons from the toolbar? - https://github.com/tneotia/html-editor-enhanced/issues/71 + +* How can I extract image tag from the editor HTML? - https://github.com/tneotia/html-editor-enhanced/issues/72 + +* How can I use LaTeX or math formulas in the editor? - https://github.com/tneotia/html-editor-enhanced/issues/74 + +* How can I make a custom button outside of the toolbar to make text bold? - https://github.com/tneotia/html-editor-enhanced/issues/81 + +* How can I style the `
` element differently? - https://github.com/tneotia/html-editor-enhanced/issues/83 + +* How can I set image width to 100% by default? - https://github.com/tneotia/html-editor-enhanced/issues/86 + +* How can I override link opening on mobile? - https://github.com/tneotia/html-editor-enhanced/issues/88 + +* How can I set the initial font family in the editor? - https://github.com/tneotia/html-editor-enhanced/issues/125 + +* How can I change background color of toolbar only? - https://github.com/tneotia/html-editor-enhanced/issues/94 + +* How can I pick images from gallery directly without showing the dialog? - https://github.com/tneotia/html-editor-enhanced/issues/97 + +
+ +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Contribution Guide + +> Coming soon! +> +> Meanwhile, PRs are always welcome + +Original html_editor by [xrb21](https://github.com/xrb21) - [repo link](https://github.com/xrb21/flutter-html-editor). Credits for the original idea and original base code to him. This library is a fork of his repo. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..d4fcc1a --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1 @@ +include: package:pedantic/analysis_options.yaml \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 0000000..4245de9 --- /dev/null +++ b/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: a29104a69b102a7485cd00d358eaeab219d258ab + channel: beta + +project_type: app diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..8176642 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# html_editor_enhanced_example + +Demonstrates how to use the html_editor_enhanced plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..0a741cb --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..1eb3074 --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 33 + + aaptOptions { + noCompress 'html', 'txt' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.tanayneotia.html_editor_enhanced_example" + minSdkVersion 21 + targetSdkVersion 33 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..7443e5d --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..42e226e --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/id/ercode/example/MainActivity.java b/example/android/app/src/main/java/id/ercode/example/MainActivity.java new file mode 100644 index 0000000..2afecb8 --- /dev/null +++ b/example/android/app/src/main/java/id/ercode/example/MainActivity.java @@ -0,0 +1,13 @@ +package id.ercode.example; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + } +} diff --git a/example/android/app/src/main/kotlin/com/tanayneotia/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/tanayneotia/example/MainActivity.kt new file mode 100644 index 0000000..74be3a0 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/tanayneotia/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.tanayneotia.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/kotlin/com/tanayneotia/html_editor_enhanced_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/tanayneotia/html_editor_enhanced_example/MainActivity.kt new file mode 100644 index 0000000..5467591 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/tanayneotia/html_editor_enhanced_example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.tanayneotia.html_editor_enhanced_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..449a9f9 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d74aa35 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..7443e5d --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..714549c --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bc6a58a --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 0000000..e96ef60 --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..e8efba1 --- /dev/null +++ b/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..399e934 --- /dev/null +++ b/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..b03e654 --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,89 @@ +PODS: + - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - Flutter (1.0.0) + - flutter_inappwebview (0.0.1): + - Flutter + - flutter_inappwebview/Core (= 0.0.1) + - OrderedSet (~> 5.0) + - flutter_inappwebview/Core (0.0.1): + - Flutter + - OrderedSet (~> 5.0) + - flutter_keyboard_visibility (0.0.1): + - Flutter + - OrderedSet (5.0.0) + - SDWebImage (5.13.2): + - SDWebImage/Core (= 5.13.2) + - SDWebImage/Core (5.13.2) + - SwiftyGif (5.4.3) + +DEPENDENCIES: + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - Flutter (from `Flutter`) + - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) + - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) + +SPEC REPOS: + trunk: + - DKImagePickerController + - DKPhotoGallery + - OrderedSet + - SDWebImage + - SwiftyGif + +EXTERNAL SOURCES: + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + Flutter: + :path: Flutter + flutter_inappwebview: + :path: ".symlinks/plugins/flutter_inappwebview/ios" + flutter_keyboard_visibility: + :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" + +SPEC CHECKSUMS: + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95 + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 + flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 + OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c + SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 + SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.11.3 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..85c7811 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,539 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + A337FE4295531DD50664781E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E3FF9EC5994F34E0646A50 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0BCF426A5747D60733C82961 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 55E3FF9EC5994F34E0646A50 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 95BEBE361EC0B4EDA3C51470 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A16C753DD94EA25E0B34474C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A337FE4295531DD50664781E /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2F92D2CED148D42F9515C21A /* Pods */ = { + isa = PBXGroup; + children = ( + A16C753DD94EA25E0B34474C /* Pods-Runner.debug.xcconfig */, + 0BCF426A5747D60733C82961 /* Pods-Runner.release.xcconfig */, + 95BEBE361EC0B4EDA3C51470 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 484111EC931D7601963471BA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 55E3FF9EC5994F34E0646A50 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 2F92D2CED148D42F9515C21A /* Pods */, + 484111EC931D7601963471BA /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 3BBEA2A067581CB1B09777D0 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + AC0125F04139E6587D7ECBB1 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 3BBEA2A067581CB1B09777D0 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AC0125F04139E6587D7ECBB1 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tanayneotia.htmlEditorEnhancedExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tanayneotia.htmlEditorEnhancedExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tanayneotia.htmlEditorEnhancedExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..3db53b6 --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..70e8393 --- /dev/null +++ b/example/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#import "AppDelegate.h" +#import "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 0000000..ac77097 --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + html_editor_enhanced_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..8505708 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,455 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:file_picker/file_picker.dart'; + +void main() => runApp(HtmlEditorExampleApp()); + +class HtmlEditorExampleApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData(), + darkTheme: ThemeData.dark(), + home: HtmlEditorExample(title: 'Flutter HTML Editor Example'), + ); + } +} + +class HtmlEditorExample extends StatefulWidget { + HtmlEditorExample({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + _HtmlEditorExampleState createState() => _HtmlEditorExampleState(); +} + +class _HtmlEditorExampleState extends State { + String result = ''; + final HtmlEditorController controller = HtmlEditorController(); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (!kIsWeb) { + controller.clearFocus(); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text(widget.title), + elevation: 0, + actions: [ + IconButton( + icon: Icon(Icons.refresh), + onPressed: () { + if (kIsWeb) { + controller.reloadWeb(); + } else { + controller.editorController!.reload(); + } + }) + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + controller.toggleCodeView(); + }, + child: Text(r'<\>', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + HtmlEditor( + controller: controller, + htmlEditorOptions: HtmlEditorOptions( + hint: 'Your text here...', + shouldEnsureVisible: true, + //initialText: "

text content initial, if any

", + ), + htmlToolbarOptions: HtmlToolbarOptions( + toolbarPosition: ToolbarPosition.aboveEditor, //by default + toolbarType: ToolbarType.nativeScrollable, //by default + onButtonPressed: + (ButtonType type, bool? status, Function? updateStatus) { + print( + "button '${describeEnum(type)}' pressed, the current selected status is $status"); + return true; + }, + onDropdownChanged: (DropdownType type, dynamic changed, + Function(dynamic)? updateSelectedItem) { + print( + "dropdown '${describeEnum(type)}' changed to $changed"); + return true; + }, + mediaLinkInsertInterceptor: + (String url, InsertFileType type) { + print(url); + return true; + }, + mediaUploadInterceptor: + (PlatformFile file, InsertFileType type) async { + print(file.name); //filename + print(file.size); //size in bytes + print(file.extension); //file extension (eg jpeg or mp4) + return true; + }, + ), + otherOptions: OtherOptions(height: 550), + callbacks: Callbacks(onBeforeCommand: (String? currentHtml) { + print('html before change is $currentHtml'); + }, onChangeContent: (String? changed) { + print('content changed to $changed'); + }, onChangeCodeview: (String? changed) { + print('code changed to $changed'); + }, onChangeSelection: (EditorSettings settings) { + print('parent element is ${settings.parentElement}'); + print('font name is ${settings.fontName}'); + }, onDialogShown: () { + print('dialog shown'); + }, onEnter: () { + print('enter/return pressed'); + }, onFocus: () { + print('editor focused'); + }, onBlur: () { + print('editor unfocused'); + }, onBlurCodeview: () { + print('codeview either focused or unfocused'); + }, onInit: () { + print('init'); + }, + //this is commented because it overrides the default Summernote handlers + /*onImageLinkInsert: (String? url) { + print(url ?? "unknown url"); + }, + onImageUpload: (FileUpload file) async { + print(file.name); + print(file.size); + print(file.type); + print(file.base64); + },*/ + onImageUploadError: (FileUpload? file, String? base64Str, + UploadError error) { + print(describeEnum(error)); + print(base64Str ?? ''); + if (file != null) { + print(file.name); + print(file.size); + print(file.type); + } + }, onKeyDown: (int? keyCode) { + print('$keyCode key downed'); + print( + 'current character count: ${controller.characterCount}'); + }, onKeyUp: (int? keyCode) { + print('$keyCode key released'); + }, onMouseDown: () { + print('mouse downed'); + }, onMouseUp: () { + print('mouse released'); + }, onNavigationRequestMobile: (String url) { + print(url); + return NavigationActionPolicy.ALLOW; + }, onPaste: () { + print('pasted into editor'); + }, onScroll: () { + print('editor scrolled'); + }), + plugins: [ + SummernoteAtMention( + getSuggestionsMobile: (String value) { + var mentions = ['test1', 'test2', 'test3']; + return mentions + .where((element) => element.contains(value)) + .toList(); + }, + mentionsWeb: ['test1', 'test2', 'test3'], + onSelect: (String value) { + print(value); + }), + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.undo(); + }, + child: + Text('Undo', style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.clear(); + }, + child: + Text('Reset', style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () async { + var txt = await controller.getText(); + if (txt.contains('src=\"data:')) { + txt = + ''; + } + setState(() { + result = txt; + }); + }, + child: Text( + 'Submit', + style: TextStyle(color: Colors.white), + ), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () { + controller.redo(); + }, + child: Text( + 'Redo', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(result), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.disable(); + }, + child: Text('Disable', + style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () async { + controller.enable(); + }, + child: Text( + 'Enable', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () { + controller.insertText('Google'); + }, + child: Text('Insert Text', + style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () { + controller.insertHtml( + '''

Google in blue

'''); + }, + child: Text('Insert HTML', + style: TextStyle(color: Colors.white)), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () async { + controller.insertLink( + 'Google linked', 'https://google.com', true); + }, + child: Text( + 'Insert Link', + style: TextStyle(color: Colors.white), + ), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () { + controller.insertNetworkImage( + 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', + filename: 'Google network image'); + }, + child: Text( + 'Insert network image', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.addNotification( + 'Info notification', NotificationType.info); + }, + child: + Text('Info', style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.addNotification( + 'Warning notification', NotificationType.warning); + }, + child: Text('Warning', + style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () async { + controller.addNotification( + 'Success notification', NotificationType.success); + }, + child: Text( + 'Success', + style: TextStyle(color: Colors.white), + ), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () { + controller.addNotification( + 'Danger notification', NotificationType.danger); + }, + child: Text( + 'Danger', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ), + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.blueGrey), + onPressed: () { + controller.addNotification('Plaintext notification', + NotificationType.plaintext); + }, + child: Text('Plaintext', + style: TextStyle(color: Colors.white)), + ), + SizedBox( + width: 16, + ), + TextButton( + style: TextButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.secondary), + onPressed: () async { + controller.removeNotification(); + }, + child: Text( + 'Remove', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..4bfc6ea --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,278 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file_picker: + dependency: transitive + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0+1" + flex_color_picker: + dependency: transitive + description: + name: flex_color_picker + url: "https://pub.dartlang.org" + source: hosted + version: "2.6.1" + flex_seed_scheme: + dependency: transitive + description: + name: flex_seed_scheme + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + url: "https://pub.dartlang.org" + source: hosted + version: "5.4.4+3" + flutter_keyboard_visibility: + dependency: transitive + description: + name: flutter_keyboard_visibility + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.0" + flutter_keyboard_visibility_platform_interface: + dependency: transitive + description: + name: flutter_keyboard_visibility_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + flutter_keyboard_visibility_web: + dependency: transitive + description: + name: flutter_keyboard_visibility_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + html_editor_enhanced: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "2.5.0" + infinite_listview: + dependency: transitive + description: + name: infinite_listview + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + numberpicker: + dependency: transitive + description: + name: numberpicker + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + pointer_interceptor: + dependency: transitive + description: + name: pointer_interceptor + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.3+3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" +sdks: + dart: ">=2.18.0 <3.0.0" + flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..5e1c481 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,71 @@ +name: html_editor_enhanced_example +description: Demonstrates how to use the html_editor_enhanced plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + flutter: + sdk: flutter + + html_editor_enhanced: + # When depending on this package from a real application you should use: + # html_editor_enhanced: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.4 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/web/favicon.png b/example/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/example/web/favicon.png differ diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/example/web/icons/Icon-192.png differ diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/example/web/icons/Icon-512.png differ diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 0000000..1460b5e --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 0000000..8c01291 --- /dev/null +++ b/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/lib/assets/font/summernote.eot b/lib/assets/font/summernote.eot new file mode 100644 index 0000000..4f047db Binary files /dev/null and b/lib/assets/font/summernote.eot differ diff --git a/lib/assets/font/summernote.ttf b/lib/assets/font/summernote.ttf new file mode 100644 index 0000000..64ef84c Binary files /dev/null and b/lib/assets/font/summernote.ttf differ diff --git a/lib/assets/jquery.min.js b/lib/assets/jquery.min.js new file mode 100644 index 0000000..b061403 --- /dev/null +++ b/lib/assets/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 { + rangeStartContainer = rangeStartContainer.childNodes[address]; + }); + + return rangeStartContainer; + } + + findRangeStartContainerAddress(selection) { + let rangeStartContainerAddress = []; + + for ( + let currentContainer = selection.getRangeAt(0).startContainer; + currentContainer !== this.rootNode; + currentContainer = currentContainer.parentNode + ) { + const parent = currentContainer.parentElement; + const children = parent.childNodes; + + for (let i = 0; i < children.length; i++) { + if (children[i] === currentContainer) { + rangeStartContainerAddress = [i, ...rangeStartContainerAddress]; + break; + } + } + } + + return rangeStartContainerAddress; + } +} + +const WORD_REGEX = /^[^\s]+$/; + +const UP_KEY_CODE = 38; +const DOWN_KEY_CODE = 40; +const ENTER_KEY_CODE = 13; + +(function(factory) { + if (typeof define === "function" && define.amd) { + define(["jquery"], factory); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(window.jQuery); + } +})(function($) { + $.extend($.summernote.plugins, { + summernoteAtMention: function(context) { + /************************ + * Setup instance vars. * + ************************/ + this.editableEl = context.layoutInfo.editable[0]; + this.editorEl = context.layoutInfo.editor[0]; + + this.autocompleteAnchor = { left: null, top: null }; + this.autocompleteContainer = null; + this.showingAutocomplete = false; + this.selectedIndex = null; + this.suggestions = null; + + this.getSuggestions = _ => { + return []; + }; + + /******************** + * Read-in options. * + ********************/ + if ( + context.options && + context.options.callbacks && + context.options.callbacks.summernoteAtMention + ) { + const summernoteCallbacks = + context.options.callbacks.summernoteAtMention; + + if (summernoteCallbacks.getSuggestions) { + this.getSuggestions = summernoteCallbacks.getSuggestions; + } + + if (summernoteCallbacks.onSelect) { + this.onSelect = summernoteCallbacks.onSelect; + } + } + + /********** + * Events * + **********/ + this.events = { + "summernote.blur": () => { + if (this.showingAutocomplete) this.hideAutocomplete(); + }, + "summernote.keydown": (_, event) => { + if (this.showingAutocomplete) { + switch (event.keyCode) { + case ENTER_KEY_CODE: { + event.preventDefault(); + event.stopPropagation(); + this.handleEnter(); + break; + } + case UP_KEY_CODE: { + event.preventDefault(); + event.stopPropagation(); + const newIndex = + this.selectedIndex === 0 ? 0 : this.selectedIndex - 1; + this.updateAutocomplete(this.suggestions, newIndex); + break; + } + case DOWN_KEY_CODE: { + event.preventDefault(); + event.stopPropagation(); + const newIndex = + this.selectedIndex === this.suggestions.length - 1 + ? this.selectedIndex + : this.selectedIndex + 1; + + this.updateAutocomplete(this.suggestions, newIndex); + break; + } + } + } + }, + "summernote.keyup": async (_, event) => { + const selection = document.getSelection(); + const currentText = selection.anchorNode.nodeValue; + const { word, absoluteIndex } = this.findWordAndIndices( + currentText || "", + selection.anchorOffset + ); + const trimmedWord = word.slice(1); + + if ( + this.showingAutocomplete && + ![DOWN_KEY_CODE, UP_KEY_CODE, ENTER_KEY_CODE].includes( + event.keyCode + ) + ) { + if (word[0] === "@") { + const suggestions = await this.getSuggestions(trimmedWord); + this.updateAutocomplete(suggestions, this.selectedIndex); + } else { + this.hideAutocomplete(); + } + } else if (!this.showingAutocomplete && word[0] === "@") { + this.suggestions = await this.getSuggestions(trimmedWord); + this.selectedIndex = 0; + this.showAutocomplete(absoluteIndex, selection.anchorNode); + } + } + }; + + /*********** + * Helpers * + ***********/ + + this.handleEnter = () => { + this.handleSelection(); + }; + + this.handleClick = suggestion => { + const selectedIndex = this.suggestions.findIndex(s => s === suggestion); + + if (selectedIndex === -1) { + throw new Error("Unable to find suggestion in suggestions."); + } + + this.selectedIndex = selectedIndex; + this.handleSelection(); + }; + + this.handleSelection = () => { + if (this.suggestions === null || this.suggestions.length === 0) { + return; + } + + const newWord = this.suggestions[this.selectedIndex].trimStart(); + + if (this.onSelect !== undefined) { + this.onSelect(newWord); + } + + const selection = document.getSelection(); + const currentText = selection.anchorNode.nodeValue; + const { word, absoluteIndex } = this.findWordAndIndices( + currentText || "", + selection.anchorOffset + ); + + const selectionPreserver = new SelectionPreserver(this.editableEl); + selectionPreserver.preserve(); + + selection.anchorNode.textContent = + currentText.slice(0, absoluteIndex + 1) + + newWord + + " " + + currentText.slice(absoluteIndex + word.length); + + selectionPreserver.restore(absoluteIndex + newWord.length + 1); + + if (context.options.callbacks.onChange !== undefined) { + context.options.callbacks.onChange(this.editableEl.innerHTML); + } + }; + + this.updateAutocomplete = (suggestions, selectedIndex) => { + this.selectedIndex = selectedIndex; + this.suggestions = suggestions; + this.renderAutocomplete(); + }; + + this.showAutocomplete = (atTextIndex, indexAnchor) => { + if (this.showingAutocomplete) { + throw new Error( + "Cannot call showAutocomplete if autocomplete is already showing." + ); + } + this.setAutocompleteAnchor(atTextIndex, indexAnchor); + this.renderAutocompleteContainer(); + this.renderAutocomplete(); + this.showingAutocomplete = true; + }; + + this.renderAutocompleteContainer = () => { + this.autocompleteContainer = document.createElement("div"); + this.autocompleteContainer.style.top = + String(this.autocompleteAnchor.top) + "px"; + this.autocompleteContainer.style.left = + String(this.autocompleteAnchor.left) + "px"; + this.autocompleteContainer.style.position = "absolute"; + this.autocompleteContainer.style.backgroundColor = "#e4e4e4"; + this.autocompleteContainer.style.zIndex = Number.MAX_SAFE_INTEGER; + + document.body.appendChild(this.autocompleteContainer); + }; + + this.renderAutocomplete = () => { + if (this.autocompleteContainer === null) { + throw new Error( + "Cannot call renderAutocomplete without an autocompleteContainer. " + ); + } + const autocompleteContent = document.createElement("div"); + + this.suggestions.forEach((suggestion, idx) => { + const suggestionDiv = document.createElement("div"); + suggestionDiv.textContent = suggestion; + + suggestionDiv.style.padding = "5px 10px"; + + if (this.selectedIndex === idx) { + suggestionDiv.style.backgroundColor = "#2e6da4"; + suggestionDiv.style.color = "white"; + } + + suggestionDiv.addEventListener("mousedown", () => { + this.handleClick(suggestion); + }); + + autocompleteContent.appendChild(suggestionDiv); + }); + + this.autocompleteContainer.innerHTML = ""; + this.autocompleteContainer.appendChild(autocompleteContent); + }; + + this.hideAutocomplete = () => { + if (!this.showingAutocomplete) + throw new Error( + "Cannot call hideAutocomplete if autocomplete is not showing." + ); + + document.body.removeChild(this.autocompleteContainer); + this.autocompleteAnchor = { left: null, top: null }; + this.selectedIndex = null; + this.suggestions = null; + this.showingAutocomplete = false; + }; + + this.findWordAndIndices = (text, offset) => { + if (offset > text.length) { + return { word: "", relativeIndex: 0 }; + } else { + let leftWord = ""; + let rightWord = ""; + let relativeIndex = 0; + let absoluteIndex = offset; + + for (let currentOffset = offset; currentOffset > 0; currentOffset--) { + if (text[currentOffset - 1].match(WORD_REGEX)) { + leftWord = text[currentOffset - 1] + leftWord; + relativeIndex++; + absoluteIndex--; + } else { + break; + } + } + + for ( + let currentOffset = offset - 1; + currentOffset > 0 && currentOffset < text.length - 1; + currentOffset++ + ) { + if (text[currentOffset + 1].match(WORD_REGEX)) { + rightWord = rightWord + text[currentOffset + 1]; + } else { + break; + } + } + + return { + word: leftWord + rightWord, + relativeIndex, + absoluteIndex + }; + } + }; + + this.setAutocompleteAnchor = (atTextIndex, indexAnchor) => { + let html = indexAnchor.parentNode.innerHTML; + const text = indexAnchor.nodeValue; + + let atIndex = -1; + for (let i = 0; i <= atTextIndex; i++) { + if (text[i] === "@") { + atIndex++; + } + } + + let htmlIndex; + for (let i = 0, htmlAtIndex = 0; i < html.length; i++) { + if (html[i] === "@") { + if (htmlAtIndex === atIndex) { + htmlIndex = i; + break; + } else { + htmlAtIndex++; + } + } + } + + const atNodeId = "at-node-" + String(Math.floor(Math.random() * 10000)); + const spanString = `@`; + + const selectionPreserver = new SelectionPreserver(this.editableEl); + selectionPreserver.preserve(); + + indexAnchor.parentNode.innerHTML = + html.slice(0, htmlIndex) + spanString + html.slice(htmlIndex + 1); + const anchorElement = document.querySelector("#" + atNodeId); + const anchorBoundingRect = anchorElement.getBoundingClientRect(); + + this.autocompleteAnchor = { + top: anchorBoundingRect.top + anchorBoundingRect.height + 2, + left: anchorBoundingRect.left + }; + + selectionPreserver.findRangeStartContainer().parentNode.innerHTML = html; + selectionPreserver.restore(); + }; + } + }); +}); diff --git a/lib/assets/summernote-lite-dark.css b/lib/assets/summernote-lite-dark.css new file mode 100644 index 0000000..4b6f4ea --- /dev/null +++ b/lib/assets/summernote-lite-dark.css @@ -0,0 +1,24 @@ +.note-editing-area, .note-status-output, .note-codable, .CodeMirror, .CodeMirror-gutter, .note-modal-content, .note-input, .note-editable { + background: #121212 !important; +} +.panel-heading, .note-toolbar, .note-statusbar { + background: #343434 !important; +} +input, select, textarea, .CodeMirror, .note-editable, [class^="note-icon-"], .caseConverter-toggle, +button > b, button > code, button > var, button > kbd, button > samp, button > small, button > ins, button > del, button > p, button > i { + color: #fff !important; +} +textarea:focus, input:focus, span, label, .note-status-output { + color: #fff !important; +} +.note-icon-font { + color: #000 !important; +} +.note-btn:not(.note-color-btn) { + background-color: #121212 !important; +} +.note-btn:focus, +.note-btn:active, +.note-btn.active { + background-color: #343434 !important; +} \ No newline at end of file diff --git a/lib/assets/summernote-lite.min.css b/lib/assets/summernote-lite.min.css new file mode 100644 index 0000000..715e7ff --- /dev/null +++ b/lib/assets/summernote-lite.min.css @@ -0,0 +1,1470 @@ +.note-toolbar { + display: none !important; +} + +@font-face { + font-family: summernote; + font-style: normal; + font-weight: 400; + font-display: auto; + src: url(font/summernote.eot); + src: url(font/summernote.eot?#iefix) format("embedded-opentype"),url(font/summernote.ttf) format("truetype"); +} + +[class*=" note-icon"]:before,[class^=note-icon]:before { + display: inline-block; + font-family: summernote; + font-style: normal; + font-size: inherit; + text-decoration: inherit; + text-rendering: auto; + text-transform: none; + vertical-align: middle; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + speak: none; +} + +.note-icon-fw { + text-align: center; + width: 1.25em; +} + +.note-icon-border { + border: .08em solid #eee; + border-radius: .1em; + padding: .2em .25em .15em; +} + +.note-icon-pull-left { + float: left; +} + +.note-icon-pull-right { + float: right; +} + +.note-icon.note-icon-pull-left { + margin-right: .3em; +} + +.note-icon.note-icon-pull-right { + margin-left: .3em; +} + +.note-icon-align:before { + content: "\ea01"; +} + +.note-icon-align-center:before { + content: "\ea02"; +} + +.note-icon-align-indent:before { + content: "\ea03"; +} + +.note-icon-align-justify:before { + content: "\ea04"; +} + +.note-icon-align-left:before { + content: "\ea05"; +} + +.note-icon-align-outdent:before { + content: "\ea06"; +} + +.note-icon-align-right:before { + content: "\ea07"; +} + +.note-icon-arrow-circle-down:before { + content: "\ea08"; +} + +.note-icon-arrow-circle-left:before { + content: "\ea09"; +} + +.note-icon-arrow-circle-right:before { + content: "\ea0a"; +} + +.note-icon-arrow-circle-up:before { + content: "\ea0b"; +} + +.note-icon-arrows-alt:before { + content: "\ea0c"; +} + +.note-icon-arrows-h:before { + content: "\ea0d"; +} + +.note-icon-arrows-v:before { + content: "\ea0e"; +} + +.note-icon-bold:before { + content: "\ea0f"; +} + +.note-icon-caret:before { + content: "\ea10"; +} + +.note-icon-chain-broken:before { + content: "\ea11"; +} + +.note-icon-circle:before { + content: "\ea12"; +} + +.note-icon-close:before { + content: "\ea13"; +} + +.note-icon-code:before { + content: "\ea14"; +} + +.note-icon-col-after:before { + content: "\ea15"; +} + +.note-icon-col-before:before { + content: "\ea16"; +} + +.note-icon-col-remove:before { + content: "\ea17"; +} + +.note-icon-eraser:before { + content: "\ea18"; +} + +.note-icon-float-left:before { + content: "\ea19"; +} + +.note-icon-float-none:before { + content: "\ea1a"; +} + +.note-icon-float-right:before { + content: "\ea1b"; +} + +.note-icon-font:before { + content: "\ea1c"; +} + +.note-icon-frame:before { + content: "\ea1d"; +} + +.note-icon-italic:before { + content: "\ea1e"; +} + +.note-icon-link:before { + content: "\ea1f"; +} + +.note-icon-magic:before { + content: "\ea20"; +} + +.note-icon-menu-check:before { + content: "\ea21"; +} + +.note-icon-minus:before { + content: "\ea22"; +} + +.note-icon-orderedlist:before { + content: "\ea23"; +} + +.note-icon-pencil:before { + content: "\ea24"; +} + +.note-icon-picture:before { + content: "\ea25"; +} + +.note-icon-question:before { + content: "\ea26"; +} + +.note-icon-redo:before { + content: "\ea27"; +} + +.note-icon-rollback:before { + content: "\ea28"; +} + +.note-icon-row-above:before { + content: "\ea29"; +} + +.note-icon-row-below:before { + content: "\ea2a"; +} + +.note-icon-row-remove:before { + content: "\ea2b"; +} + +.note-icon-special-character:before { + content: "\ea2c"; +} + +.note-icon-square:before { + content: "\ea2d"; +} + +.note-icon-strikethrough:before { + content: "\ea2e"; +} + +.note-icon-subscript:before { + content: "\ea2f"; +} + +.note-icon-summernote:before { + content: "\ea30"; +} + +.note-icon-superscript:before { + content: "\ea31"; +} + +.note-icon-table:before { + content: "\ea32"; +} + +.note-icon-text-height:before { + content: "\ea33"; +} + +.note-icon-trash:before { + content: "\ea34"; +} + +.note-icon-underline:before { + content: "\ea35"; +} + +.note-icon-undo:before { + content: "\ea36"; +} + +.note-icon-unorderedlist:before { + content: "\ea37"; +} + +.note-icon-video:before { + content: "\ea38"; +} + +.note-frame { + -ms-box-sizing: border-box; + box-sizing: border-box; + color: #000; + font-family: sans-serif; + border-radius: 4px; +} + +.note-toolbar { + padding: 10px 5px; + color: #333; + background-color: #f5f5f5; + border-bottom: 1px solid; + border-color: #ddd; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +.note-btn-group { + position: relative; + display: inline-block; + margin-right: 8px; +} + +.note-btn-group>.note-btn-group { + margin-right: 0; +} + +.note-btn-group>.note-btn:first-child { + margin-left: 0; +} + +.note-btn-group .note-btn+.note-btn,.note-btn-group .note-btn+.note-btn-group,.note-btn-group .note-btn-group+.note-btn,.note-btn-group .note-btn-group+.note-btn-group { + margin-left: -1px; +} + +.note-btn-group>.note-btn-group:not(:first-child)>.note-btn,.note-btn-group>.note-btn:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.note-btn-group>.note-btn-group:not(:last-child)>.note-btn,.note-btn-group>.note-btn:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.note-btn-group.open>.note-dropdown { + display: block; +} + +.note-btn { + display: inline-block; + font-weight: 400; + margin-bottom: 0; + text-align: center; + vertical-align: middle; + touch-action: manipulation; + cursor: pointer; + background-image: none; + white-space: nowrap; + outline: 0; + color: #333; + background-color: #fff; + border: 1px solid #dae0e5; + padding: 5px 10px; + font-size: 14px; + line-height: 1.4; + border-radius: 3px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.note-btn.focus,.note-btn:focus,.note-btn:hover { + color: #333; + background-color: #ebebeb; + border-color: #dae0e5; +} + +.note-btn.disabled.focus,.note-btn.disabled:focus,.note-btn[disabled].focus,.note-btn[disabled]:focus,fieldset[disabled] .note-btn.focus,fieldset[disabled] .note-btn:focus { + background-color: #fff; + border-color: #dae0e5; +} + +.note-btn.active,.note-btn.focus,.note-btn:active,.note-btn:focus,.note-btn:hover { + color: #333; + text-decoration: none; + border: 1px solid #dae0e5; + background-color: #ebebeb; + outline: 0; + border-radius: 1px; +} + +.note-btn.active,.note-btn:active { + background-image: none; + box-shadow: inset 0 3px 5px rgba(0,0,0,.125); +} + +.note-btn.disabled,.note-btn[disabled],fieldset[disabled] .note-btn { + cursor: not-allowed; + -webkit-opacity: .65; + -khtml-opacity: .65; + -moz-opacity: .65; + opacity: .65; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=65); + filter: alpha(opacity=65); + box-shadow: none; +} + +.note-btn>span.note-icon-caret:first-child { + margin-left: -1px; +} + +.note-btn>span.note-icon-caret:nth-child(2) { + padding-left: 3px; + margin-right: -5px; +} + +.note-btn-primary { + background: #fa6362; + color: #fff; +} + +.note-btn-primary.focus,.note-btn-primary:focus,.note-btn-primary:hover { + color: #fff; + text-decoration: none; + border: 1px solid #dae0e5; + background-color: #fa6362; + border-radius: 1px; +} + +.note-btn-block { + display: block; + width: 100%; +} + +.note-btn-block+.note-btn-block { + margin-top: 5px; +} + +input[type=button].note-btn-block,input[type=reset].note-btn-block,input[type=submit].note-btn-block { + width: 100%; +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.close { + float: right; + font-size: 21px; + line-height: 1; + color: #000; + opacity: .2; +} + +.close:hover { + -webkit-opacity: 1; + -khtml-opacity: 1; + -moz-opacity: 1; + -ms-filter: alpha(opacity=100); + filter: alpha(opacity=100); + opacity: 1; +} + +.note-dropdown { + position: relative; +} + +.note-color .dropdown-toggle { + width: 30px; + padding-left: 5px; +} + +.note-dropdown-menu { + display: none; + min-width: 100px; + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + float: left; + text-align: left; + background: #fff; + border: 1px solid #e2e2e2; + padding: 5px; + background-clip: padding-box; + box-shadow: 0 1px 1px rgba(0,0,0,.06); +} + +.note-dropdown-menu>:last-child { + margin-right: 0; +} + +.note-btn-group.open .note-dropdown-menu,.note-dropdown-item { + display: block; +} + +.note-dropdown-item:hover { + background-color: #ebebeb; +} + +a.note-dropdown-item,a.note-dropdown-item:hover { + margin: 5px 0; + color: #000; + text-decoration: none; +} + +.note-modal { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 1050; + -webkit-opacity: 1; + -khtml-opacity: 1; + -moz-opacity: 1; + opacity: 1; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=100); + filter: alpha(opacity=100); + display: none; +} + +.note-modal.open { + display: block; +} + +.note-modal-content { + position: relative; + width: auto; + margin: 30px 20px; + border: 1px solid rgba(0,0,0,.2); + background: #fff; + background-clip: border-box; + outline: 0; + border-radius: 5px; + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} + +.note-modal-header { + padding: 10px 20px; + border: 1px solid #ededef; +} + +.note-modal-body { + position: relative; + padding: 20px 30px; +} + +.note-modal-body kbd { + border-radius: 2px; + background-color: #000; + color: #fff; + padding: 3px 5px; + font-weight: 700; + -ms-box-sizing: border-box; + box-sizing: border-box; +} + +.note-modal-footer { + height: 40px; + padding: 10px; + text-align: center; +} + +.note-modal-footer a { + color: #337ab7; + text-decoration: none; +} + +.note-modal-footer a:focus,.note-modal-footer a:hover { + color: #23527c; + text-decoration: underline; +} + +.note-modal-footer .note-btn { + float: right; +} + +.note-modal-title { + font-size: 20px; + color: #42515f; + margin: 0; + line-height: 1.4; +} + +.note-modal-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 1040; + background: #000; + -webkit-opacity: .5; + -khtml-opacity: .5; + -moz-opacity: .5; + opacity: .5; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=50); + filter: alpha(opacity=50); + display: none; +} + +.note-modal-backdrop.open { + display: block; +} + +@media(min-width:768px) { + .note-modal-content { + width: 600px; + margin: 30px auto; + } +} + +@media(min-width:992px) { + .note-modal-content-large { + width: 900px; + } +} + +.note-form-group { + padding-bottom: 20px; +} + +.note-form-group:last-child { + padding-bottom: 0; +} + +.note-form-label { + display: block; + width: 100%; + font-size: 16px; + color: #42515f; + margin-bottom: 10px; + font-weight: 700; +} + +.note-input { + width: 100%; + display: block; + border: 1px solid #ededef; + background: #fff; + outline: 0; + padding: 6px 4px; + font-size: 14px; + -ms-box-sizing: border-box; + box-sizing: border-box; +} + +.note-input::-webkit-input-placeholder { + color: #eee; +} + +.note-input:-moz-placeholder,.note-input::-moz-placeholder { + color: #eee; +} + +.note-input:-ms-input-placeholder { + color: #eee; +} + +.note-tooltip { + position: absolute; + z-index: 1070; + display: block; + font-size: 13px; + -webkit-transition: opacity .15s; + transition: opacity .15s; + -webkit-opacity: 0; + -khtml-opacity: 0; + -moz-opacity: 0; + opacity: 0; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0); + filter: alpha(opacity=0); +} + +.note-tooltip.in { + -webkit-opacity: .9; + -khtml-opacity: .9; + -moz-opacity: .9; + opacity: .9; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=90); + filter: alpha(opacity=90); +} + +.note-tooltip.top { + margin-top: -3px; + padding: 5px 0; +} + +.note-tooltip.right { + margin-left: 3px; + padding: 0 5px; +} + +.note-tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} + +.note-tooltip.left { + margin-left: -3px; + padding: 0 5px; +} + +.note-tooltip.bottom .note-tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} + +.note-tooltip.top .note-tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} + +.note-tooltip.right .note-tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} + +.note-tooltip.left .note-tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} + +.note-tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.note-tooltip-content { + max-width: 200px; + font-family: sans-serif; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; +} + +.note-popover { + position: absolute; + z-index: 1060; + display: block; + font-size: 13px; + font-family: sans-serif; + display: none; + background: #fff; + border: 1px solid #ccc; +} + +.note-popover.in { + display: block; +} + +.note-popover.top { + margin-top: -10px; + padding: 5px 0; +} + +.note-popover.right { + margin-left: 10px; + padding: 0 5px; +} + +.note-popover.bottom { + margin-top: 10px; + padding: 5px 0; +} + +.note-popover.left { + margin-left: -10px; + padding: 0 5px; +} + +.note-popover.bottom .note-popover-arrow { + top: -11px; + left: 20px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0,0,0,.25); +} + +.note-popover.bottom .note-popover-arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} + +.note-popover.top .note-popover-arrow { + bottom: -11px; + left: 20px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #999; + border-top-color: rgba(0,0,0,.25); +} + +.note-popover.top .note-popover-arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-bottom-width: 0; + border-top-color: #fff; +} + +.note-popover.right .note-popover-arrow { + top: 50%; + left: -11px; + margin-top: -10px; + border-left-width: 0; + border-right-color: #999; + border-right-color: rgba(0,0,0,.25); +} + +.note-popover.right .note-popover-arrow:after { + left: 1px; + margin-top: -10px; + content: " "; + border-left-width: 0; + border-right-color: #fff; +} + +.note-popover.left .note-popover-arrow { + top: 50%; + right: -11px; + margin-top: -10px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0,0,0,.25); +} + +.note-popover.left .note-popover-arrow:after { + right: 1px; + margin-top: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} + +.note-popover-arrow { + position: absolute; + width: 0; + height: 0; + border: 11px solid transparent; +} + +.note-popover-arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + content: " "; + border: 10px solid transparent; +} + +.note-popover-content { + padding: 3px 8px; + color: #000; + text-align: center; + background-color: #fff; + min-width: 100px; + min-height: 30px; +} + +.note-editor { + position: relative; +} + +.note-editor .note-dropzone { + position: absolute; + display: none; + z-index: 100; + color: #87cefa; + background-color: #fff; + opacity: .95; +} + +.note-editor .note-dropzone .note-dropzone-message { + display: table-cell; + vertical-align: middle; + text-align: center; + font-size: 28px; + font-weight: 700; +} + +.note-editor .note-dropzone.hover { + color: #098ddf; +} + +.note-editor.dragover .note-dropzone { + display: table; +} + +.note-editor .note-editing-area { + position: relative; +} + +.note-editor .note-editing-area .note-editable { + outline: none; +} + +.note-editor .note-editing-area .note-editable sup { + vertical-align: super; +} + +.note-editor .note-editing-area .note-editable sub { + vertical-align: sub; +} + +.note-editor .note-editing-area .note-editable img.note-float-left { + margin-right: 10px; +} + +.note-editor .note-editing-area .note-editable img.note-float-right { + margin-left: 10px; +} + +.note-editor.note-airframe,.note-editor.note-frame { + border: 1px solid #a9a9a9; +} + +.note-editor.note-airframe.codeview .note-editing-area .note-editable,.note-editor.note-frame.codeview .note-editing-area .note-editable { + display: none; +} + +.note-editor.note-airframe.codeview .note-editing-area .note-codable,.note-editor.note-frame.codeview .note-editing-area .note-codable { + display: block; +} + +.note-editor.note-airframe .note-editing-area,.note-editor.note-frame .note-editing-area { + overflow: hidden; +} + +.note-editor.note-airframe .note-editing-area .note-editable,.note-editor.note-frame .note-editing-area .note-editable { + padding: 10px; + overflow: auto; + word-wrap: break-word; +} + +.note-editor.note-airframe .note-editing-area .note-editable[contenteditable=false],.note-editor.note-frame .note-editing-area .note-editable[contenteditable=false] { + background-color: #e5e5e5; +} + +.note-editor.note-airframe .note-editing-area .note-codable,.note-editor.note-frame .note-editing-area .note-codable { + display: none; + width: 100%; + padding: 10px; + border: none; + box-shadow: none; + font-family: Menlo,Monaco,monospace,sans-serif; + font-size: 14px; + color: #ccc; + background-color: #222; + resize: none; + outline: none; + -ms-box-sizing: border-box; + box-sizing: border-box; + border-radius: 0; + margin-bottom: 0; +} + +.note-editor.note-airframe.fullscreen,.note-editor.note-frame.fullscreen { + position: fixed; + top: 0; + left: 0; + width: 100%!important; + z-index: 1050; +} + +.note-editor.note-airframe.fullscreen .note-editable,.note-editor.note-frame.fullscreen .note-editable { + background-color: #fff; +} + +.note-editor.note-airframe.fullscreen .note-resizebar,.note-editor.note-frame.fullscreen .note-resizebar { + display: none; +} + +.note-editor.note-airframe .note-status-output,.note-editor.note-frame .note-status-output { + display: block; + width: 100%; + font-size: 14px; + line-height: 1.42857143; + height: 20px; + margin-bottom: 0; + color: #000; + border: 0; + border-top: 1px solid #e2e2e2; +} + +.note-editor.note-airframe .note-status-output:empty,.note-editor.note-frame .note-status-output:empty { + height: 0; + border-top: 0 solid transparent; +} + +.note-editor.note-airframe .note-status-output .pull-right,.note-editor.note-frame .note-status-output .pull-right { + float: right!important; +} + +.note-editor.note-airframe .note-status-output .text-muted,.note-editor.note-frame .note-status-output .text-muted { + color: #777; +} + +.note-editor.note-airframe .note-status-output .text-primary,.note-editor.note-frame .note-status-output .text-primary { + color: #286090; +} + +.note-editor.note-airframe .note-status-output .text-success,.note-editor.note-frame .note-status-output .text-success { + color: #3c763d; +} + +.note-editor.note-airframe .note-status-output .text-info,.note-editor.note-frame .note-status-output .text-info { + color: #31708f; +} + +.note-editor.note-airframe .note-status-output .text-warning,.note-editor.note-frame .note-status-output .text-warning { + color: #8a6d3b; +} + +.note-editor.note-airframe .note-status-output .text-danger,.note-editor.note-frame .note-status-output .text-danger { + color: #a94442; +} + +.note-editor.note-airframe .note-status-output .alert,.note-editor.note-frame .note-status-output .alert { + margin: -7px 0 0; + padding: 7px 10px 2px; + border-radius: 0; + color: #000; + background-color: #f5f5f5; +} + +.note-editor.note-airframe .note-status-output .alert .note-icon,.note-editor.note-frame .note-status-output .alert .note-icon { + margin-right: 5px; +} + +.note-editor.note-airframe .note-status-output .alert-success,.note-editor.note-frame .note-status-output .alert-success { + color: #3c763d!important; + background-color: #dff0d8!important; +} + +.note-editor.note-airframe .note-status-output .alert-info,.note-editor.note-frame .note-status-output .alert-info { + color: #31708f!important; + background-color: #d9edf7!important; +} + +.note-editor.note-airframe .note-status-output .alert-warning,.note-editor.note-frame .note-status-output .alert-warning { + color: #8a6d3b!important; + background-color: #fcf8e3!important; +} + +.note-editor.note-airframe .note-status-output .alert-danger,.note-editor.note-frame .note-status-output .alert-danger { + color: #a94442!important; + background-color: #f2dede!important; +} + +.note-editor.note-airframe .note-statusbar,.note-editor.note-frame .note-statusbar { + background-color: #f5f5f5; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top: 1px solid #ddd; +} + +.note-editor.note-airframe .note-statusbar .note-resizebar,.note-editor.note-frame .note-statusbar .note-resizebar { + padding-top: 1px; + height: 9px; + width: 100%; + cursor: ns-resize; +} + +.note-editor.note-airframe .note-statusbar .note-resizebar .note-icon-bar,.note-editor.note-frame .note-statusbar .note-resizebar .note-icon-bar { + width: 20px; + margin: 1px auto; + border-top: 1px solid #a9a9a9; +} + +.note-editor.note-airframe .note-statusbar.locked .note-resizebar,.note-editor.note-frame .note-statusbar.locked .note-resizebar { + cursor: default; +} + +.note-editor.note-airframe .note-statusbar.locked .note-resizebar .note-icon-bar,.note-editor.note-frame .note-statusbar.locked .note-resizebar .note-icon-bar { + display: none; +} + +.note-editor.note-airframe .note-placeholder,.note-editor.note-frame .note-placeholder { + padding: 10px; +} + +.note-editor.note-airframe { + border: 0; +} + +.note-editor.note-airframe .note-editing-area .note-editable { + padding: 0; +} + +.note-popover.popover { + display: none; + max-width: none; +} + +.note-popover.popover .popover-content a { + display: inline-block; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: middle; +} + +.note-popover.popover .arrow { + left: 20px!important; +} + +.note-toolbar { + position: relative; +} + +.note-popover .popover-content,.note-toolbar { + margin: 0; + padding: 0 0 5px 5px; +} + +.note-popover .popover-content>.note-btn-group,.note-toolbar>.note-btn-group { + margin-top: 5px; + margin-left: 0; + margin-right: 5px; +} + +.note-popover .popover-content .note-btn-group .note-table,.note-toolbar .note-btn-group .note-table { + min-width: 0; + padding: 5px; +} + +.note-popover .popover-content .note-btn-group .note-table .note-dimension-picker,.note-toolbar .note-btn-group .note-table .note-dimension-picker { + font-size: 18px; +} + +.note-popover .popover-content .note-btn-group .note-table .note-dimension-picker .note-dimension-picker-mousecatcher,.note-toolbar .note-btn-group .note-table .note-dimension-picker .note-dimension-picker-mousecatcher { + position: absolute!important; + z-index: 3; + width: 10em; + height: 10em; + cursor: pointer; +} + +.note-popover .popover-content .note-btn-group .note-table .note-dimension-picker .note-dimension-picker-unhighlighted,.note-toolbar .note-btn-group .note-table .note-dimension-picker .note-dimension-picker-unhighlighted { + position: relative!important; + z-index: 1; + width: 5em; + height: 5em; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIj4+Pjp6ekKlAqjAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKhmnaJzPAAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC") repeat; +} + +.note-popover .popover-content .note-btn-group .note-table .note-dimension-picker .note-dimension-picker-highlighted,.note-toolbar .note-btn-group .note-table .note-dimension-picker .note-dimension-picker-highlighted { + position: absolute!important; + z-index: 2; + width: 1em; + height: 1em; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIjd6vvD2f9LKLW+AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKwNDEVT0AAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC") repeat; +} + +.note-popover .popover-content .note-style .dropdown-style blockquote,.note-popover .popover-content .note-style .dropdown-style pre,.note-toolbar .note-style .dropdown-style blockquote,.note-toolbar .note-style .dropdown-style pre { + margin: 0; + padding: 5px 10px; +} + +.note-popover .popover-content .note-style .dropdown-style h1,.note-popover .popover-content .note-style .dropdown-style h2,.note-popover .popover-content .note-style .dropdown-style h3,.note-popover .popover-content .note-style .dropdown-style h4,.note-popover .popover-content .note-style .dropdown-style h5,.note-popover .popover-content .note-style .dropdown-style h6,.note-popover .popover-content .note-style .dropdown-style p,.note-toolbar .note-style .dropdown-style h1,.note-toolbar .note-style .dropdown-style h2,.note-toolbar .note-style .dropdown-style h3,.note-toolbar .note-style .dropdown-style h4,.note-toolbar .note-style .dropdown-style h5,.note-toolbar .note-style .dropdown-style h6,.note-toolbar .note-style .dropdown-style p { + margin: 0; + padding: 0; +} + +.note-popover .popover-content .note-color-all .note-dropdown-menu,.note-toolbar .note-color-all .note-dropdown-menu { + min-width: 337px; +} + +.note-popover .popover-content .note-color .dropdown-toggle,.note-toolbar .note-color .dropdown-toggle { + width: 20px; + padding-left: 5px; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette,.note-toolbar .note-color .note-dropdown-menu .note-palette { + display: inline-block; + margin: 0; + width: 160px; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette:first-child,.note-toolbar .note-color .note-dropdown-menu .note-palette:first-child { + margin: 0 5px; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-palette-title,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-palette-title { + font-size: 12px; + margin: 2px 7px; + text-align: center; + border-bottom: 1px solid #eee; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-color-reset,.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-color-select,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-color-reset,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-color-select { + font-size: 11px; + margin: 3px; + padding: 0 3px; + cursor: pointer; + width: 100%; + border-radius: 5px; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-color-reset:hover,.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-color-select:hover,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-color-reset:hover,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-color-select:hover { + background: #eee; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-color-row,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-color-row { + height: 20px; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-color-select-btn,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-color-select-btn { + display: none; +} + +.note-popover .popover-content .note-color .note-dropdown-menu .note-palette .note-holder-custom .note-color-btn,.note-toolbar .note-color .note-dropdown-menu .note-palette .note-holder-custom .note-color-btn { + border: 1px solid #eee; +} + +.note-popover .popover-content .note-para .note-dropdown-menu,.note-toolbar .note-para .note-dropdown-menu { + min-width: 216px; + padding: 5px; +} + +.note-popover .popover-content .note-para .note-dropdown-menu>div:first-child,.note-toolbar .note-para .note-dropdown-menu>div:first-child { + margin-right: 5px; +} + +.note-popover .popover-content .note-dropdown-menu,.note-toolbar .note-dropdown-menu { + min-width: 160px; +} + +.note-popover .popover-content .note-dropdown-menu.right,.note-toolbar .note-dropdown-menu.right { + right: 0; + left: auto; +} + +.note-popover .popover-content .note-dropdown-menu.right:before,.note-toolbar .note-dropdown-menu.right:before { + right: 9px; + left: auto!important; +} + +.note-popover .popover-content .note-dropdown-menu.right:after,.note-toolbar .note-dropdown-menu.right:after { + right: 10px; + left: auto!important; +} + +.note-popover .popover-content .note-dropdown-menu.note-check a i,.note-toolbar .note-dropdown-menu.note-check a i { + color: #00bfff; + visibility: hidden; +} + +.note-popover .popover-content .note-dropdown-menu.note-check a.checked i,.note-toolbar .note-dropdown-menu.note-check a.checked i { + visibility: visible; +} + +.note-popover .popover-content .note-fontsize-10,.note-toolbar .note-fontsize-10 { + font-size: 10px; +} + +.note-popover .popover-content .note-color-palette,.note-toolbar .note-color-palette { + line-height: 1; +} + +.note-popover .popover-content .note-color-palette div .note-color-btn,.note-toolbar .note-color-palette div .note-color-btn { + width: 20px; + height: 20px; + padding: 0; + margin: 0; + border: 1px solid #fff; +} + +.note-popover .popover-content .note-color-palette div .note-color-btn:hover,.note-toolbar .note-color-palette div .note-color-btn:hover { + border: 1px solid #000; +} + +.note-modal .modal-dialog { + outline: 0; + border-radius: 5px; + box-shadow: 0 3px 9px rgba(0,0,0,.5); +} + +.note-modal .form-group { + margin-left: 0; + margin-right: 0; +} + +.note-modal .note-modal-form { + margin: 0; +} + +.note-modal .note-image-dialog .note-dropzone { + min-height: 100px; + font-size: 30px; + line-height: 4; + color: #d3d3d3; + text-align: center; + border: 4px dashed #d3d3d3; + margin-bottom: 10px; +} + +.note-placeholder { + position: absolute; + display: none; + color: grey; +} + +.note-handle .note-control-selection { + position: absolute; + display: none; + border: 1px solid #000; +} + +.note-handle .note-control-selection>div { + position: absolute; +} + +.note-handle .note-control-selection .note-control-selection-bg { + width: 100%; + height: 100%; + background-color: #000; + -webkit-opacity: .3; + -khtml-opacity: .3; + -moz-opacity: .3; + opacity: .3; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=30); + filter: alpha(opacity=30); +} + +.note-handle .note-control-selection .note-control-handle,.note-handle .note-control-selection .note-control-holder,.note-handle .note-control-selection .note-control-sizing { + width: 7px; + height: 7px; + border: 1px solid #000; +} + +.note-handle .note-control-selection .note-control-sizing { + background-color: #000; +} + +.note-handle .note-control-selection .note-control-nw { + top: -5px; + left: -5px; + border-right: none; + border-bottom: none; +} + +.note-handle .note-control-selection .note-control-ne { + top: -5px; + right: -5px; + border-bottom: none; + border-left: none; +} + +.note-handle .note-control-selection .note-control-sw { + bottom: -5px; + left: -5px; + border-top: none; + border-right: none; +} + +.note-handle .note-control-selection .note-control-se { + right: -5px; + bottom: -5px; + cursor: se-resize; +} + +.note-handle .note-control-selection .note-control-se.note-control-holder { + cursor: default; + border-top: none; + border-left: none; +} + +.note-handle .note-control-selection .note-control-selection-info { + right: 0; + bottom: 0; + padding: 5px; + margin: 5px; + color: #fff; + background-color: #000; + font-size: 12px; + border-radius: 5px; + -webkit-opacity: .7; + -khtml-opacity: .7; + -moz-opacity: .7; + opacity: .7; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(opacity=70); + filter: alpha(opacity=70); +} + +.note-hint-popover { + min-width: 100px; + padding: 2px; +} + +.note-hint-popover .popover-content { + padding: 3px; + max-height: 150px; + overflow: auto; +} + +.note-hint-popover .popover-content .note-hint-group .note-hint-item { + display: block!important; + padding: 3px; +} + +.note-hint-popover .popover-content .note-hint-group .note-hint-item.active,.note-hint-popover .popover-content .note-hint-group .note-hint-item:hover { + display: block; + clear: both; + font-weight: 400; + line-height: 1.4; + color: #fff; + white-space: nowrap; + text-decoration: none; + background-color: #428bca; + outline: 0; + cursor: pointer; +} + +.note-editor .note-editing-area .note-editable table { + width: 100%; + border-collapse: collapse; +} + +.note-editor .note-editing-area .note-editable table td,.note-editor .note-editing-area .note-editable table th { + border: 1px solid #ececec; + padding: 5px 3px; +} + +.note-editor .note-editing-area .note-editable a { + background-color: inherit; + text-decoration: inherit; + font-family: inherit; + font-weight: inherit; + color: #337ab7; +} + +.note-editor .note-editing-area .note-editable a:focus,.note-editor .note-editing-area .note-editable a:hover { + color: #23527c; + text-decoration: underline; + outline: 0; +} + +.note-editor .note-editing-area .note-editable figure { + margin: 0; +} + +.note-modal .note-modal-body label { + margin-bottom: 2px; + padding: 2px 5px; + display: inline-block; +} + +.note-modal .note-modal-body .help-list-item:hover { + background-color: #e0e0e0; +} + +@-moz-document url-prefix() { + .note-modal .note-image-input { + height: auto; + } +} + +.help-list-item label { + margin-bottom: 5px; + display: inline-block; +} \ No newline at end of file diff --git a/lib/assets/summernote-lite.min.js b/lib/assets/summernote-lite.min.js new file mode 100644 index 0000000..83fb838 --- /dev/null +++ b/lib/assets/summernote-lite.min.js @@ -0,0 +1,3 @@ +/*! For license information please see summernote-lite.min.js.LICENSE.txt */ +!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e(require("jquery"));else if("function"==typeof define&&define.amd)define(["jquery"],e);else{var n="object"==typeof exports?e(require("jquery")):e(t.jQuery);for(var o in n)("object"==typeof exports?exports:t)[o]=n[o]}}(window,(function(t){return function(t){var e={};function n(o){if(e[o])return e[o].exports;var i=e[o]={i:o,l:!1,exports:{}};return t[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(o,i,function(e){return t[e]}.bind(null,i));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=51)}({0:function(e,n){e.exports=t},1:function(t,e,n){"use strict";var o=n(0),i=n.n(o);function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(t,e){for(var n=0;n0||navigator.msMaxTouchPoints>0,p=u?"DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted":"input",m={isMac:navigator.appVersion.indexOf("Mac")>-1,isMSIE:u,isEdge:h,isFF:!h&&/firefox/i.test(c),isPhantom:/PhantomJS/i.test(c),isWebkit:!h&&/webkit/i.test(c),isChrome:!h&&/chrome/i.test(c),isSafari:!h&&/safari/i.test(c)&&!/chrome/i.test(c),browserVersion:l,jqueryVersion:parseFloat(i.a.fn.jquery),isSupportAmd:r,isSupportTouch:f,isFontInstalled:function(t){var e="Comic Sans MS"===t?"Courier New":"Comic Sans MS",n=document.createElement("canvas").getContext("2d");n.font="200px '"+e+"'";var o=n.measureText("mmmmmmmmmmwwwww").width;return n.font="200px "+s(t)+', "'+e+'"',o!==n.measureText("mmmmmmmmmmwwwww").width},isW3CRangeSupport:!!document.createRange,inputEventName:p,genericFontFamilies:a,validFontName:s};var v=0;var g={eq:function(t){return function(e){return t===e}},eq2:function(t,e){return t===e},peq2:function(t){return function(e,n){return e[t]===n[t]}},ok:function(){return!0},fail:function(){return!1},self:function(t){return t},not:function(t){return function(){return!t.apply(t,arguments)}},and:function(t,e){return function(n){return t(n)&&e(n)}},invoke:function(t,e){return function(){return t[e].apply(t,arguments)}},resetUniqueId:function(){v=0},uniqueId:function(t){var e=++v+"";return t?t+e:e},rect2bnd:function(t){var e=i()(document);return{top:t.top+e.scrollTop(),left:t.left+e.scrollLeft(),width:t.right-t.left,height:t.bottom-t.top}},invertObject:function(t){var e={};for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[t[n]]=n);return e},namespaceToCamel:function(t,e){return(e=e||"")+t.split(".").map((function(t){return t.substring(0,1).toUpperCase()+t.substring(1)})).join("")},debounce:function(t,e,n){var o;return function(){var i=this,r=arguments,a=function(){o=null,n||t.apply(i,r)},s=n&&!o;clearTimeout(o),o=setTimeout(a,e),s&&t.apply(i,r)}},isValidUrl:function(t){return/[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gi.test(t)}};function b(t){return t[0]}function k(t){return t[t.length-1]}function y(t){return t.slice(1)}function w(t,e){if(t&&t.length&&e){if(t.indexOf)return-1!==t.indexOf(e);if(t.contains)return t.contains(e)}return!1}var C={head:b,last:k,initial:function(t){return t.slice(0,t.length-1)},tail:y,prev:function(t,e){if(t&&t.length&&e){var n=t.indexOf(e);return-1===n?null:t[n-1]}return null},next:function(t,e){if(t&&t.length&&e){var n=t.indexOf(e);return-1===n?null:t[n+1]}return null},find:function(t,e){for(var n=0,o=t.length;n";function U(t){return E(t)?t.nodeValue.length:t?t.childNodes.length:0}function W(t){var e=U(t);return 0===e||(!E(t)&&1===e&&t.innerHTML===j||!(!C.all(t.childNodes,E)||""!==t.innerHTML))}function K(t){$(t)||U(t)||(t.innerHTML=j)}function V(t,e){for(;t;){if(e(t))return t;if(S(t))break;t=t.parentNode}return null}function q(t,e){e=e||g.fail;var n=[];return V(t,(function(t){return S(t)||n.push(t),e(t)})),n}function _(t,e){e=e||g.fail;for(var n=[];t&&!e(t);)n.push(t),t=t.nextSibling;return n}function G(t,e){var n=e.nextSibling,o=e.parentNode;return n?o.insertBefore(t,n):o.appendChild(t),t}function Y(t,e){return i.a.each(e,(function(e,n){t.appendChild(n)})),t}function Z(t){return 0===t.offset}function X(t){return t.offset===U(t.node)}function Q(t){return Z(t)||X(t)}function J(t,e){for(;t&&t!==e;){if(0!==et(t))return!1;t=t.parentNode}return!0}function tt(t,e){if(!e)return!1;for(;t&&t!==e;){if(et(t)!==U(t.parentNode)-1)return!1;t=t.parentNode}return!0}function et(t){for(var e=0;t=t.previousSibling;)e+=1;return e}function nt(t){return!!(t&&t.childNodes&&t.childNodes.length)}function ot(t,e){var n,o;if(0===t.offset){if(S(t.node))return null;n=t.node.parentNode,o=et(t.node)}else nt(t.node)?o=U(n=t.node.childNodes[t.offset-1]):(n=t.node,o=e?0:t.offset-1);return{node:n,offset:o}}function it(t,e){var n,o;if(U(t.node)===t.offset){if(S(t.node))return null;var i=at(t.node);i?(n=i,o=0):(n=t.node.parentNode,o=et(t.node)+1)}else nt(t.node)?(n=t.node.childNodes[t.offset],o=0):(n=t.node,o=e?U(t.node):t.offset+1);return{node:n,offset:o}}function rt(t,e){var n,o;if(W(t.node))return{node:n=t.node.nextSibling,offset:o=0};if(U(t.node)===t.offset){if(S(t.node))return null;var i=at(t.node);i?(n=i,o=0):(n=t.node.parentNode,o=et(t.node)+1),S(n)&&(n=t.node.nextSibling,o=0)}else if(nt(t.node)){if(o=0,W(n=t.node.childNodes[t.offset]))return null}else if(n=t.node,o=e?U(t.node):t.offset+1,W(n))return null;return{node:n,offset:o}}function at(t){if(t.nextSibling&&t.parent===t.nextSibling.parent)return E(t.nextSibling)?t.nextSibling:at(t.nextSibling)}function st(t,e){return t.node===e.node&&t.offset===e.offset}function lt(t,e){var n=e&&e.isSkipPaddingBlankHTML,o=e&&e.isNotSplitEdgePoint,i=e&&e.isDiscardEmptySplits;if(i&&(n=!0),Q(t)&&(E(t.node)||o)){if(Z(t))return t.node;if(X(t))return t.node.nextSibling}if(E(t.node))return t.node.splitText(t.offset);var r=t.node.childNodes[t.offset],a=G(t.node.cloneNode(!1),t.node);return Y(a,_(r)),n||(K(t.node),K(a)),i&&(W(t.node)&&dt(t.node),W(a))?(dt(a),t.node.nextSibling):a}function ct(t,e,n){var o=q(e.node,g.eq(t));return o.length?1===o.length?lt(e,n):o.reduce((function(t,o){return t===e.node&&(t=lt(e,n)),lt({node:o,offset:t?et(t):U(o)},n)})):null}function ut(t){return document.createElement(t)}function dt(t,e){if(t&&t.parentNode){if(t.removeNode)return t.removeNode(e);var n=t.parentNode;if(!e){for(var o=[],i=0,r=t.childNodes.length;i".concat(j,"

"),makePredByNodeName:T,isEditable:S,isControlSizing:function(t){return t&&i()(t).hasClass("note-control-sizing")},isText:E,isElement:function(t){return t&&1===t.nodeType},isVoid:$,isPara:N,isPurePara:function(t){return N(t)&&!P(t)},isHeading:function(t){return t&&/^H[1-7]/.test(t.nodeName.toUpperCase())},isInline:A,isBlock:g.not(A),isBodyInline:function(t){return A(t)&&!V(t,N)},isBody:O,isParaInline:function(t){return A(t)&&!!V(t,N)},isPre:I,isList:F,isTable:R,isData:L,isCell:H,isBlockquote:B,isBodyContainer:z,isAnchor:M,isDiv:T("DIV"),isLi:P,isBR:T("BR"),isSpan:T("SPAN"),isB:T("B"),isU:T("U"),isS:T("S"),isI:T("I"),isImg:T("IMG"),isTextarea:ht,deepestChildIsEmpty:function(t){do{if(null===t.firstElementChild||""===t.firstElementChild.innerHTML)break}while(t=t.firstElementChild);return W(t)},isEmpty:W,isEmptyAnchor:g.and(M,W),isClosestSibling:function(t,e){return t.nextSibling===e||t.previousSibling===e},withClosestSiblings:function(t,e){e=e||g.ok;var n=[];return t.previousSibling&&e(t.previousSibling)&&n.push(t.previousSibling),n.push(t),t.nextSibling&&e(t.nextSibling)&&n.push(t.nextSibling),n},nodeLength:U,isLeftEdgePoint:Z,isRightEdgePoint:X,isEdgePoint:Q,isLeftEdgeOf:J,isRightEdgeOf:tt,isLeftEdgePointOf:function(t,e){return Z(t)&&J(t.node,e)},isRightEdgePointOf:function(t,e){return X(t)&&tt(t.node,e)},prevPoint:ot,nextPoint:it,nextPointWithEmptyNode:rt,isSamePoint:st,isVisiblePoint:function(t){if(E(t.node)||!nt(t.node)||W(t.node))return!0;var e=t.node.childNodes[t.offset-1],n=t.node.childNodes[t.offset];return!(e&&!$(e)||n&&!$(n))},prevPointUntil:function(t,e){for(;t;){if(e(t))return t;t=ot(t)}return null},nextPointUntil:function(t,e){for(;t;){if(e(t))return t;t=it(t)}return null},isCharPoint:function(t){if(!E(t.node))return!1;var e=t.node.nodeValue.charAt(t.offset-1);return e&&" "!==e&&e!==x},isSpacePoint:function(t){if(!E(t.node))return!1;var e=t.node.nodeValue.charAt(t.offset-1);return" "===e||e===x},walkPoint:function(t,e,n,o){for(var i=t;i&&(n(i),!st(i,e));){i=rt(i,o&&t.node!==i.node&&e.node!==i.node)}},ancestor:V,singleChildAncestor:function(t,e){for(t=t.parentNode;t&&1===U(t);){if(e(t))return t;if(S(t))break;t=t.parentNode}return null},listAncestor:q,lastAncestor:function(t,e){var n=q(t);return C.last(n.filter(e))},listNext:_,listPrev:function(t,e){e=e||g.fail;for(var n=[];t&&!e(t);)n.push(t),t=t.previousSibling;return n},listDescendant:function(t,e){var n=[];return e=e||g.ok,function o(i){t!==i&&e(i)&&n.push(i);for(var r=0,a=i.childNodes.length;r-1)return o;return null},wrap:function(t,e){var n=t.parentNode,o=i()("<"+e+">")[0];return n.insertBefore(o,t),o.appendChild(t),o},insertAfter:G,appendChildNodes:Y,position:et,hasChildren:nt,makeOffsetPath:function(t,e){return q(e,g.eq(t)).map(et).reverse()},fromOffsetPath:function(t,e){for(var n=t,o=0,i=e.length;o\s]*)(.*?)(\s*\/?>)/g,(function(t,e,n){n=n.toUpperCase();var o=/^DIV|^TD|^TH|^P|^LI|^H[1-7]/.test(n)&&!!e,i=/^BLOCKQUOTE|^TABLE|^TBODY|^TR|^HR|^UL|^OL/.test(n);return t+(o||i?"\n":"")}))).trim()}return n},value:ft,posFromPlaceholder:function(t){var e=i()(t),n=e.offset(),o=e.outerHeight(!0);return{left:n.left,top:n.top+o}},attachEvents:function(t,e){Object.keys(e).forEach((function(n){t.on(n,e[n])}))},detachEvents:function(t,e){Object.keys(e).forEach((function(n){t.off(n,e[n])}))},isCustomStyleTag:function(t){return t&&!E(t)&&C.contains(t.classList,"note-styletag")}};function mt(t,e){for(var n=0;n1,i=o&&C.head(n),r=o?C.last(n):C.head(n),a=this.modules[i||"editor"];return!i&&this[r]?this[r].apply(this,e):a&&a[r]&&a.shouldInitialize()?a[r].apply(a,e):void 0}}])&&mt(e.prototype,n),o&&mt(e,o),t}();function gt(t,e){for(var n=0;n=0)break;o=a[n]}if(0!==n&&pt.isText(a[n-1])){var s=document.body.createTextRange(),l=null;s.moveToElementText(o||i),s.collapse(!o),l=o?o.nextSibling:i.firstChild;var c=t.duplicate();c.setEndPoint("StartToStart",s);for(var u=c.text.replace(/[\r\n]/g,"").length;u>l.nodeValue.length&&l.nextSibling;)u-=l.nodeValue.length,l=l.nextSibling;l.nodeValue;e&&l.nextSibling&&pt.isText(l.nextSibling)&&u===l.nodeValue.length&&(u-=l.nodeValue.length,l=l.nextSibling),i=l,n=u}return{cont:i,offset:n}}function kt(t){var e=document.body.createTextRange(),n=function t(e,n){var o,i;if(pt.isText(e)){var r=pt.listPrev(e,g.not(pt.isText)),a=C.last(r).previousSibling;o=a||e.parentNode,n+=C.sum(C.tail(r),pt.nodeLength),i=!a}else{if(o=e.childNodes[n]||e,pt.isText(o))return t(o,0);n=0,i=!1}return{node:o,collapseToStart:i,offset:n}}(t.node,t.offset);return e.moveToElementText(n.node),e.collapse(n.collapseToStart),e.moveStart("character",n.offset),e}i.a.fn.extend({summernote:function(){var t=i.a.type(C.head(arguments)),e="string"===t,n="object"===t,o=i.a.extend({},i.a.summernote.options,n?C.head(arguments):{});o.langInfo=i.a.extend(!0,{},i.a.summernote.lang["en-US"],i.a.summernote.lang[o.lang]),o.icons=i.a.extend(!0,{},i.a.summernote.options.icons,o.icons),o.tooltip="auto"===o.tooltip?!m.isSupportTouch:o.tooltip,this.each((function(t,e){var n=i()(e);if(!n.data("summernote")){var r=new vt(n,o);n.data("summernote",r),n.data("summernote").triggerEvent("init",r.layoutInfo)}}));var r=this.first();if(r.length){var a=r.data("summernote");if(e)return a.invoke.apply(a,C.from(arguments));o.focus&&a.invoke("editor.focus")}return this}});var yt=function(){function t(e,n,o,i){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.sc=e,this.so=n,this.ec=o,this.eo=i,this.isOnEditable=this.makeIsOn(pt.isEditable),this.isOnList=this.makeIsOn(pt.isList),this.isOnAnchor=this.makeIsOn(pt.isAnchor),this.isOnCell=this.makeIsOn(pt.isCell),this.isOnData=this.makeIsOn(pt.isData)}var e,n,o;return e=t,(n=[{key:"nativeRange",value:function(){if(m.isW3CRangeSupport){var t=document.createRange();return t.setStart(this.sc,this.so),t.setEnd(this.ec,this.eo),t}var e=kt({node:this.sc,offset:this.so});return e.setEndPoint("EndToEnd",kt({node:this.ec,offset:this.eo})),e}},{key:"getPoints",value:function(){return{sc:this.sc,so:this.so,ec:this.ec,eo:this.eo}}},{key:"getStartPoint",value:function(){return{node:this.sc,offset:this.so}}},{key:"getEndPoint",value:function(){return{node:this.ec,offset:this.eo}}},{key:"select",value:function(){var t=this.nativeRange();if(m.isW3CRangeSupport){var e=document.getSelection();e.rangeCount>0&&e.removeAllRanges(),e.addRange(t)}else t.select();return this}},{key:"scrollIntoView",value:function(t){var e=i()(t).height();return t.scrollTop+e0?n.so-1:0];if(e){var i=pt.listPrev(e,pt.isParaInline).reverse();if((i=i.concat(pt.listNext(e.nextSibling,pt.isParaInline))).length){var r=pt.wrap(C.head(i),"p");pt.appendChildNodes(r,C.tail(i))}}return this.normalize()}},{key:"insertNode",value:function(t){var e=this;(pt.isText(t)||pt.isInline(t))&&(e=this.wrapBodyInlineWithPara().deleteContents());var n=pt.splitPoint(e.getStartPoint(),pt.isInline(t));return n.rightNode?(n.rightNode.parentNode.insertBefore(t,n.rightNode),pt.isEmpty(n.rightNode)&&pt.isPara(t)&&n.rightNode.parentNode.removeChild(n.rightNode)):n.container.appendChild(t),t}},{key:"pasteHTML",value:function(t){t=i.a.trim(t);var e=i()("
").html(t)[0],n=C.from(e.childNodes),o=this,r=!1;return o.so>=0&&(n=n.reverse(),r=!0),n=n.map((function(t){return o.insertNode(t)})),r&&(n=n.reverse()),n}},{key:"toString",value:function(){var t=this.nativeRange();return m.isW3CRangeSupport?t.toString():t.text}},{key:"getWordRange",value:function(e){var n=this.getEndPoint();if(!pt.isCharPoint(n))return this;var o=pt.prevPointUntil(n,(function(t){return!pt.isCharPoint(t)}));return e&&(n=pt.nextPointUntil(n,(function(t){return!pt.isCharPoint(t)}))),new t(o.node,o.offset,n.node,n.offset)}},{key:"getWordsRange",value:function(e){var n=this.getEndPoint(),o=function(t){return!pt.isCharPoint(t)&&!pt.isSpacePoint(t)};if(o(n))return this;var i=pt.prevPointUntil(n,o);return e&&(n=pt.nextPointUntil(n,o)),new t(i.node,i.offset,n.node,n.offset)}},{key:"getWordsMatchRange",value:function(e){var n=this.getEndPoint(),o=pt.prevPointUntil(n,(function(o){if(!pt.isCharPoint(o)&&!pt.isSpacePoint(o))return!0;var i=new t(o.node,o.offset,n.node,n.offset),r=e.exec(i.toString());return r&&0===r.index})),i=new t(o.node,o.offset,n.node,n.offset),r=i.toString(),a=e.exec(r);return a&&a[0].length===r.length?i:null}},{key:"bookmark",value:function(t){return{s:{path:pt.makeOffsetPath(t,this.sc),offset:this.so},e:{path:pt.makeOffsetPath(t,this.ec),offset:this.eo}}}},{key:"paraBookmark",value:function(t){return{s:{path:C.tail(pt.makeOffsetPath(C.head(t),this.sc)),offset:this.so},e:{path:C.tail(pt.makeOffsetPath(C.last(t),this.ec)),offset:this.eo}}}},{key:"getClientRects",value:function(){return this.nativeRange().getClientRects()}}])&>(e.prototype,n),o&>(e,o),t}(),wt={create:function(t,e,n,o){if(4===arguments.length)return new yt(t,e,n,o);if(2===arguments.length)return new yt(t,e,n=t,o=e);var i=this.createFromSelection();if(!i&&1===arguments.length){var r=arguments[0];return pt.isEditable(r)&&(r=r.lastChild),this.createFromBodyElement(r,pt.emptyPara===arguments[0].innerHTML)}return i},createFromBodyElement:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.createFromNode(t);return n.collapse(e)},createFromSelection:function(){var t,e,n,o;if(m.isW3CRangeSupport){var i=document.getSelection();if(!i||0===i.rangeCount)return null;if(pt.isBody(i.anchorNode))return null;var r=i.getRangeAt(0);t=r.startContainer,e=r.startOffset,n=r.endContainer,o=r.endOffset}else{var a=document.selection.createRange(),s=a.duplicate();s.collapse(!1);var l=a;l.collapse(!0);var c=bt(l,!0),u=bt(s,!1);pt.isText(c.node)&&pt.isLeftEdgePoint(c)&&pt.isTextNode(u.node)&&pt.isRightEdgePoint(u)&&u.node.nextSibling===c.node&&(c=u),t=c.cont,e=c.offset,n=u.cont,o=u.offset}return new yt(t,e,n,o)},createFromNode:function(t){var e=t,n=0,o=t,i=pt.nodeLength(o);return pt.isVoid(e)&&(n=pt.listPrev(e).length-1,e=e.parentNode),pt.isBR(o)?(i=pt.listPrev(o).length-1,o=o.parentNode):pt.isVoid(o)&&(i=pt.listPrev(o).length,o=o.parentNode),this.create(e,n,o,i)},createFromNodeBefore:function(t){return this.createFromNode(t).collapse(!0)},createFromNodeAfter:function(t){return this.createFromNode(t).collapse()},createFromBookmark:function(t,e){var n=pt.fromOffsetPath(t,e.s.path),o=e.s.offset,i=pt.fromOffsetPath(t,e.e.path),r=e.e.offset;return new yt(n,o,i,r)},createFromParaBookmark:function(t,e){var n=t.s.offset,o=t.e.offset,i=pt.fromOffsetPath(C.head(e),t.s.path),r=pt.fromOffsetPath(C.last(e),t.e.path);return new yt(i,n,r,o)}},Ct={BACKSPACE:8,TAB:9,ENTER:13,ESCAPE:27,SPACE:32,DELETE:46,LEFT:37,UP:38,RIGHT:39,DOWN:40,NUM0:48,NUM1:49,NUM2:50,NUM3:51,NUM4:52,NUM5:53,NUM6:54,NUM7:55,NUM8:56,B:66,E:69,I:73,J:74,K:75,L:76,R:82,S:83,U:85,V:86,Y:89,Z:90,SLASH:191,LEFTBRACKET:219,BACKSLASH:220,RIGHTBRACKET:221,HOME:36,END:35,PAGEUP:33,PAGEDOWN:34},xt={isEdit:function(t){return C.contains([Ct.BACKSPACE,Ct.TAB,Ct.ENTER,Ct.SPACE,Ct.DELETE],t)},isMove:function(t){return C.contains([Ct.LEFT,Ct.UP,Ct.RIGHT,Ct.DOWN],t)},isNavigation:function(t){return C.contains([Ct.HOME,Ct.END,Ct.PAGEUP,Ct.PAGEDOWN],t)},nameFromCode:g.invertObject(Ct),code:Ct};function St(t,e){for(var n=0;n0&&(this.stackOffset--,this.applySnapshot(this.stack[this.stackOffset]))}},{key:"redo",value:function(){this.stack.length-1>this.stackOffset&&(this.stackOffset++,this.applySnapshot(this.stack[this.stackOffset]))}},{key:"recordUndo",value:function(){this.stackOffset++,this.stack.length>this.stackOffset&&(this.stack=this.stack.slice(0,this.stackOffset)),this.stack.push(this.makeSnapshot()),this.stack.length>this.context.options.historyLimit&&(this.stack.shift(),this.stackOffset-=1)}}])&&St(e.prototype,n),o&&St(e,o),t}();function Et(t,e){for(var n=0;n-1;n["list-style"]=o?"unordered":"ordered"}else n["list-style"]="none";var r=pt.ancestor(t.sc,pt.isPara);if(r&&r.style["line-height"])n["line-height"]=r.style.lineHeight;else{var a=parseInt(n["line-height"],10)/parseInt(n["font-size"],10);n["line-height"]=a.toFixed(1)}return n.anchor=t.isOnAnchor()&&pt.ancestor(t.sc,pt.isAnchor),n.ancestors=pt.listAncestor(t.sc,pt.isEditable),n.range=t,n}}])&&Et(e.prototype,n),o&&Et(e,o),t}();function Nt(t,e){for(var n=0;n25?e-25:""}))}))})),n.select()}},{key:"toggleList",value:function(t,e){var n=this,o=wt.create(e).wrapBodyInlineWithPara(),r=o.nodes(pt.isPara,{includeAncestor:!0}),a=o.paraBookmark(r),s=C.clusterBy(r,g.peq2("parentNode"));if(C.find(r,pt.isPurePara)){var l=[];i.a.each(s,(function(e,o){l=l.concat(n.wrapList(o,t))})),r=l}else{var c=o.nodes(pt.isList,{includeAncestor:!0}).filter((function(e){return!i.a.nodeName(e,t)}));c.length?i.a.each(c,(function(e,n){pt.replace(n,t)})):r=this.releaseList(s,!0)}wt.createFromParaBookmark(a,r).select()}},{key:"wrapList",value:function(t,e){var n=C.head(t),o=C.last(t),i=pt.isList(n.previousSibling)&&n.previousSibling,r=pt.isList(o.nextSibling)&&o.nextSibling,a=i||pt.insertAfter(pt.create(e||"UL"),o);return t=t.map((function(t){return pt.isPurePara(t)?pt.replace(t,"LI"):t})),pt.appendChildNodes(a,t),r&&(pt.appendChildNodes(a,C.from(r.childNodes)),pt.remove(r)),t}},{key:"releaseList",value:function(t,e){var n=this,o=[];return i.a.each(t,(function(t,r){var a=C.head(r),s=C.last(r),l=e?pt.lastAncestor(a,pt.isList):a.parentNode,c=l.parentNode;if("LI"===l.parentNode.nodeName)r.map((function(t){var e=n.findNextSiblings(t);c.nextSibling?c.parentNode.insertBefore(t,c.nextSibling):c.parentNode.appendChild(t),e.length&&(n.wrapList(e,l.nodeName),t.appendChild(e[0].parentNode))})),0===l.children.length&&c.removeChild(l),0===c.childNodes.length&&c.parentNode.removeChild(c);else{var u=l.childNodes.length>1?pt.splitTree(l,{node:s.parentNode,offset:pt.position(s)+1},{isSkipPaddingBlankHTML:!0}):null,d=pt.splitTree(l,{node:a.parentNode,offset:pt.position(a)},{isSkipPaddingBlankHTML:!0});r=e?pt.listDescendant(d,pt.isLi):C.from(d.childNodes).filter(pt.isLi),!e&&pt.isList(l.parentNode)||(r=r.map((function(t){return pt.replace(t,"P")}))),i.a.each(C.from(r).reverse(),(function(t,e){pt.insertAfter(e,l)}));var h=C.compact([l,d,u]);i.a.each(h,(function(t,e){var n=[e].concat(pt.listDescendant(e,pt.isList));i.a.each(n.reverse(),(function(t,e){pt.nodeLength(e)||pt.remove(e,!0)}))}))}o=o.concat(r)})),o}},{key:"appendToPrevious",value:function(t){return t.previousSibling?pt.appendChildNodes(t.previousSibling,[t]):this.wrapList([t],"LI")}},{key:"findList",value:function(t){return t?C.find(t.children,(function(t){return["OL","UL"].indexOf(t.nodeName)>-1})):null}},{key:"findNextSiblings",value:function(t){for(var e=[];t.nextSibling;)e.push(t.nextSibling),t=t.nextSibling;return e}}])&&Nt(e.prototype,n),o&&Nt(e,o),t}();function Pt(t,e){for(var n=0;n1,i=e.rowSpan>1,a=t.rowIndex===r.rowPos&&e.cellIndex===r.colPos;l(t.rowIndex,n,t,e,i,o,!1);var s=e.attributes.rowSpan?parseInt(e.attributes.rowSpan.value,10):0;if(s>1)for(var c=1;c1)for(var p=1;p=n.cellIndex&&n.cellIndex<=e&&!o&&r.colPos++}function f(e){switch(n){case t.where.Column:if(e.isColSpan)return t.resultAction.SubtractSpanCount;break;case t.where.Row:if(!e.isVirtual&&e.isRowSpan)return t.resultAction.AddCell;if(e.isRowSpan)return t.resultAction.SubtractSpanCount}return t.resultAction.RemoveCell}function p(e){switch(n){case t.where.Column:if(e.isColSpan)return t.resultAction.SumSpanCount;if(e.isRowSpan&&e.isVirtual)return t.resultAction.Ignore;break;case t.where.Row:if(e.isRowSpan)return t.resultAction.SumSpanCount;if(e.isColSpan&&e.isVirtual)return t.resultAction.Ignore}return t.resultAction.AddCell}this.getActionList=function(){for(var e=n===t.where.Row?r.rowPos:-1,i=n===t.where.Column?r.colPos:-1,l=0,u=!0;u;){var d=e>=0?e:l,h=i>=0?i:l,m=a[d];if(!m)return u=!1,s;var v=m[h];if(!v)return u=!1,s;var g=t.resultAction.Ignore;switch(o){case t.requestAction.Add:g=p(v);break;case t.requestAction.Delete:g=f(v)}s.push(c(v,g,d,h)),l++}return s},e&&e.tagName&&("td"===e.tagName.toLowerCase()||"th"===e.tagName.toLowerCase())&&(r.colPos=e.cellIndex,e.parentElement&&e.parentElement.tagName&&"tr"===e.parentElement.tagName.toLowerCase()&&(r.rowPos=e.parentElement.rowIndex)),function(){for(var t=i.rows,e=0;e"),s=new At(n,At.where.Row,At.requestAction.Add,i()(o).closest("table")[0]).getActionList(),l=0;l"+pt.blank+"");break;case At.resultAction.SumSpanCount:if("top"===e&&(c.baseCell.parent?c.baseCell.closest("tr").rowIndex:0)<=o[0].rowIndex){var d=i()("
").append(i()(""+pt.blank+"").removeAttr("rowspan")).html();a.append(d);break}var h=parseInt(c.baseCell.rowSpan,10);h++,c.baseCell.setAttribute("rowSpan",h)}}if("top"===e)o.before(a);else{if(n.rowSpan>1){var f=o[0].rowIndex+(n.rowSpan-2);return void i()(i()(o).parent().find("tr")[f]).after(i()(a))}o.after(a)}}},{key:"addCol",value:function(t,e){var n=pt.ancestor(t.commonAncestor(),pt.isCell),o=i()(n).closest("tr");i()(o).siblings().push(o);for(var r=new At(n,At.where.Column,At.requestAction.Add,i()(o).closest("table")[0]).getActionList(),a=0;a"+pt.blank+""):i()(s.baseCell).before(""+pt.blank+"");break;case At.resultAction.SumSpanCount:if("right"===e){var c=parseInt(s.baseCell.colSpan,10);c++,s.baseCell.setAttribute("colSpan",c)}else i()(s.baseCell).before(""+pt.blank+"")}}}},{key:"recoverAttributes",value:function(t){var e="";if(!t)return e;for(var n=t.attributes||[],o=0;o1,d=u?parseInt(l.rowSpan,10):0;switch(a[s].action){case At.resultAction.Ignore:continue;case At.resultAction.AddCell:var h=n.next("tr")[0];if(!h)continue;var f=n[0].cells[o];u&&(d>2?(d--,h.insertBefore(f,h.cells[o]),h.cells[o].setAttribute("rowSpan",d),h.cells[o].innerHTML=""):2===d&&(h.insertBefore(f,h.cells[o]),h.cells[o].removeAttribute("rowSpan"),h.cells[o].innerHTML=""));continue;case At.resultAction.SubtractSpanCount:u&&(d>2?(d--,l.setAttribute("rowSpan",d),c.rowIndex!==r&&l.cellIndex===o&&(l.innerHTML="")):2===d&&(l.removeAttribute("rowSpan"),c.rowIndex!==r&&l.cellIndex===o&&(l.innerHTML="")));continue;case At.resultAction.RemoveCell:continue}}n.remove()}},{key:"deleteCol",value:function(t){for(var e=pt.ancestor(t.commonAncestor(),pt.isCell),n=i()(e).closest("tr"),o=n.children("td, th").index(i()(e)),r=new At(e,At.where.Column,At.requestAction.Delete,i()(n).closest("table")[0]).getActionList(),a=0;a1){var l=s.colSpan?parseInt(s.colSpan,10):0;l>2?(l--,s.setAttribute("colSpan",l),s.cellIndex===o&&(s.innerHTML="")):2===l&&(s.removeAttribute("colSpan"),s.cellIndex===o&&(s.innerHTML=""))}continue;case At.resultAction.RemoveCell:pt.remove(r[a].baseCell,!0);continue}}},{key:"createTable",value:function(t,e,n){for(var o,r=[],a=0;a"+pt.blank+"");o=r.join("");for(var s,l=[],c=0;c"+o+"");s=l.join("");var u=i()(""+s+"
");return n&&n.tableClassName&&u.addClass(n.tableClassName),u[0]}},{key:"deleteTable",value:function(t){var e=pt.ancestor(t.commonAncestor(),pt.isCell);i()(e).closest("table").remove()}}])&&Lt(e.prototype,n),o&&Lt(e,o),t}();function Dt(t,e){for(var n=0;n0&&n.isLimited(l))){var c=s.toString()!==o;"string"==typeof e&&(e=e.trim()),n.options.onCreateLink?e=n.options.onCreateLink(e):a&&(e=/^([A-Za-z][A-Za-z0-9+-.]*\:|#|\/)/.test(e)?e:n.options.defaultProtocol+e);var u=[];if(c){var d=(s=s.deleteContents()).insertNode(i()(""+o+"")[0]);u.push(d)}else u=n.style.styleNodes(s,{nodeName:"A",expandClosestSibling:!0,onlyPartialContains:!0});i.a.each(u,(function(t,n){i()(n).attr("href",e),r?i()(n).attr("target","_blank"):i()(n).removeAttr("target")})),n.setLastRange(n.createRangeFromList(u).select())}})),this.color=this.wrapCommand((function(t){var e=t.foreColor,n=t.backColor;e&&document.execCommand("foreColor",!1,e),n&&document.execCommand("backColor",!1,n)})),this.foreColor=this.wrapCommand((function(t){document.execCommand("foreColor",!1,t)})),this.insertTable=this.wrapCommand((function(t){var e=t.split("x");n.getLastRange().deleteContents().insertNode(n.table.createTable(e[0],e[1],n.options))})),this.removeMedia=this.wrapCommand((function(){var t=i()(n.restoreTarget()).parent();t.closest("figure").length?t.closest("figure").remove():t=i()(n.restoreTarget()).detach(),n.context.triggerEvent("media.delete",t,n.$editable)})),this.floatMe=this.wrapCommand((function(t){var e=i()(n.restoreTarget());e.toggleClass("note-float-left","left"===t),e.toggleClass("note-float-right","right"===t),e.css("float","none"===t?"":t)})),this.resize=this.wrapCommand((function(t){var e=i()(n.restoreTarget());0===(t=parseFloat(t))?e.css("width",""):e.css({width:100*t+"%",height:""})}))}var e,n,o;return e=t,(n=[{key:"initialize",value:function(){var t=this;this.$editable.on("keydown",(function(e){if(e.keyCode===xt.code.ENTER&&t.context.triggerEvent("enter",e),t.context.triggerEvent("keydown",e),t.snapshot=t.history.makeSnapshot(),t.hasKeyShortCut=!1,e.isDefaultPrevented()||(t.options.shortcuts?t.hasKeyShortCut=t.handleKeyMap(e):t.preventDefaultEditableShortCuts(e)),t.isLimited(1,e)){var n=t.getLastRange();if(n.eo-n.so==0)return!1}t.setLastRange(),t.options.recordEveryKeystroke&&!1===t.hasKeyShortCut&&t.history.recordUndo()})).on("keyup",(function(e){t.setLastRange(),t.context.triggerEvent("keyup",e)})).on("focus",(function(e){t.setLastRange(),t.context.triggerEvent("focus",e)})).on("blur",(function(e){t.context.triggerEvent("blur",e)})).on("mousedown",(function(e){t.context.triggerEvent("mousedown",e)})).on("mouseup",(function(e){t.setLastRange(),t.history.recordUndo(),t.context.triggerEvent("mouseup",e)})).on("scroll",(function(e){t.context.triggerEvent("scroll",e)})).on("paste",(function(e){t.setLastRange(),t.context.triggerEvent("paste",e)})).on("input",(function(){t.isLimited(0)&&t.snapshot&&t.history.applySnapshot(t.snapshot)})),this.$editable.attr("spellcheck",this.options.spellCheck),this.$editable.attr("autocorrect",this.options.spellCheck),this.options.disableGrammar&&this.$editable.attr("data-gramm",!1),this.$editable.html(pt.html(this.$note)||pt.emptyPara),this.$editable.on(m.inputEventName,g.debounce((function(){t.context.triggerEvent("change",t.$editable.html(),t.$editable)}),10)),this.$editable.on("focusin",(function(e){t.context.triggerEvent("focusin",e)})).on("focusout",(function(e){t.context.triggerEvent("focusout",e)})),this.options.airMode?this.options.overrideContextMenu&&this.$editor.on("contextmenu",(function(e){return t.context.triggerEvent("contextmenu",e),!1})):(this.options.width&&this.$editor.outerWidth(this.options.width),this.options.height&&this.$editable.outerHeight(this.options.height),this.options.maxHeight&&this.$editable.css("max-height",this.options.maxHeight),this.options.minHeight&&this.$editable.css("min-height",this.options.minHeight)),this.history.recordUndo(),this.setLastRange()}},{key:"destroy",value:function(){this.$editable.off()}},{key:"handleKeyMap",value:function(t){var e=this.options.keyMap[m.isMac?"mac":"pc"],n=[];t.metaKey&&n.push("CMD"),t.ctrlKey&&!t.altKey&&n.push("CTRL"),t.shiftKey&&n.push("SHIFT");var o=xt.nameFromCode[t.keyCode];o&&n.push(o);var i=e[n.join("+")];if("TAB"!==o||this.options.tabDisable)if(i){if(!1!==this.context.invoke(i))return t.preventDefault(),!0}else xt.isEdit(t.keyCode)&&this.afterCommand();else this.afterCommand();return!1}},{key:"preventDefaultEditableShortCuts",value:function(t){(t.ctrlKey||t.metaKey)&&C.contains([66,73,85],t.keyCode)&&t.preventDefault()}},{key:"isLimited",value:function(t,e){return t=t||0,(void 0===e||!(xt.isMove(e.keyCode)||xt.isNavigation(e.keyCode)||e.ctrlKey||e.metaKey||C.contains([xt.code.BACKSPACE,xt.code.DELETE],e.keyCode)))&&this.options.maxTextLength>0&&this.$editable.text().length+t>this.options.maxTextLength}},{key:"createRange",value:function(){return this.focus(),this.setLastRange(),this.getLastRange()}},{key:"createRangeFromList",value:function(t){var e=wt.createFromNodeBefore(C.head(t)).getStartPoint(),n=wt.createFromNodeAfter(C.last(t)).getEndPoint();return wt.create(e.node,e.offset,n.node,n.offset)}},{key:"setLastRange",value:function(t){t?this.lastRange=t:(this.lastRange=wt.create(this.editable),0===i()(this.lastRange.sc).closest(".note-editable").length&&(this.lastRange=wt.createFromBodyElement(this.editable)))}},{key:"getLastRange",value:function(){return this.lastRange||this.setLastRange(),this.lastRange}},{key:"saveRange",value:function(t){t&&this.getLastRange().collapse().select()}},{key:"restoreRange",value:function(){this.lastRange&&(this.lastRange.select(),this.focus())}},{key:"saveTarget",value:function(t){this.$editable.data("target",t)}},{key:"clearTarget",value:function(){this.$editable.removeData("target")}},{key:"restoreTarget",value:function(){return this.$editable.data("target")}},{key:"currentStyle",value:function(){var t=wt.create();return t&&(t=t.normalize()),t?this.style.current(t):this.style.fromNode(this.$editable)}},{key:"styleFromNode",value:function(t){return this.style.fromNode(t)}},{key:"undo",value:function(){this.context.triggerEvent("before.command",this.$editable.html()),this.history.undo(),this.context.triggerEvent("change",this.$editable.html(),this.$editable)}},{key:"commit",value:function(){this.context.triggerEvent("before.command",this.$editable.html()),this.history.commit(),this.context.triggerEvent("change",this.$editable.html(),this.$editable)}},{key:"redo",value:function(){this.context.triggerEvent("before.command",this.$editable.html()),this.history.redo(),this.context.triggerEvent("change",this.$editable.html(),this.$editable)}},{key:"beforeCommand",value:function(){this.context.triggerEvent("before.command",this.$editable.html()),document.execCommand("styleWithCSS",!1,this.options.styleWithCSS),this.focus()}},{key:"afterCommand",value:function(t){this.normalizeContent(),this.history.recordUndo(),t||this.context.triggerEvent("change",this.$editable.html(),this.$editable)}},{key:"tab",value:function(){var t=this.getLastRange();if(t.isCollapsed()&&t.isOnCell())this.table.tab(t);else{if(0===this.options.tabSize)return!1;this.isLimited(this.options.tabSize)||(this.beforeCommand(),this.typing.insertTab(t,this.options.tabSize),this.afterCommand())}}},{key:"untab",value:function(){var t=this.getLastRange();if(t.isCollapsed()&&t.isOnCell())this.table.tab(t,!0);else if(0===this.options.tabSize)return!1}},{key:"wrapCommand",value:function(t){return function(){this.beforeCommand(),t.apply(this,arguments),this.afterCommand()}}},{key:"insertImage",value:function(t,e){var n,o=this;return(n=t,i.a.Deferred((function(t){var e=i()("");e.one("load",(function(){e.off("error abort"),t.resolve(e)})).one("error abort",(function(){e.off("load").detach(),t.reject(e)})).css({display:"none"}).appendTo(document.body).attr("src",n)})).promise()).then((function(t){o.beforeCommand(),"function"==typeof e?e(t):("string"==typeof e&&t.attr("data-filename",e),t.css("width",Math.min(o.$editable.width(),t.width()))),t.show(),o.getLastRange().insertNode(t[0]),o.setLastRange(wt.createFromNodeAfter(t[0]).select()),o.afterCommand()})).fail((function(t){o.context.triggerEvent("image.upload.error",src,t)}))}},{key:"insertImagesAsDataURL",value:function(t){var e=this;i.a.each(t,(function(t,n){var o=n.name;e.options.maximumImageFileSize&&e.options.maximumImageFileSize":t),e&&e.length&&(e[0].tagName.toUpperCase()!==t.toUpperCase()&&(e=e.find(t)),e&&e.length)){var n=e[0].className||"";if(n){var o=this.createRange();i()([o.sc,o.ec]).closest(t).addClass(n)}}}},{key:"formatPara",value:function(){this.formatBlock("P")}},{key:"fontStyling",value:function(t,e){var n=this.getLastRange();if(""!==n){var o=this.style.styleNodes(n);if(this.$editor.find(".note-status-output").html(""),i()(o).css(t,e),n.isCollapsed()){var r=C.head(o);r&&!pt.nodeLength(r)&&(r.innerHTML=pt.ZERO_WIDTH_NBSP_CHAR,wt.createFromNode(r.firstChild).select(),this.setLastRange(),this.$editable.data("bogus",r))}else this.setLastRange(this.createRangeFromList(o).select())}else{var a=i.a.now();this.$editor.find(".note-status-output").html('
'+this.lang.output.noSelection+"
"),setTimeout((function(){i()("#note-status-output-"+a).remove()}),5e3)}}},{key:"unlink",value:function(){var t=this.getLastRange();if(t.isOnAnchor()){var e=pt.ancestor(t.sc,pt.isAnchor);(t=wt.createFromNode(e)).select(),this.setLastRange(),this.beforeCommand(),document.execCommand("unlink"),this.afterCommand()}}},{key:"getLinkInfo",value:function(){var t=this.getLastRange().expand(pt.isAnchor),e=i()(C.head(t.nodes(pt.isAnchor))),n={range:t,text:t.toString(),url:e.length?e.attr("href"):""};return e.length&&(n.isNewWindow="_blank"===e.attr("target")),n}},{key:"addRow",value:function(t){var e=this.getLastRange(this.$editable);e.isCollapsed()&&e.isOnCell()&&(this.beforeCommand(),this.table.addRow(e,t),this.afterCommand())}},{key:"addCol",value:function(t){var e=this.getLastRange(this.$editable);e.isCollapsed()&&e.isOnCell()&&(this.beforeCommand(),this.table.addCol(e,t),this.afterCommand())}},{key:"deleteRow",value:function(){var t=this.getLastRange(this.$editable);t.isCollapsed()&&t.isOnCell()&&(this.beforeCommand(),this.table.deleteRow(t),this.afterCommand())}},{key:"deleteCol",value:function(){var t=this.getLastRange(this.$editable);t.isCollapsed()&&t.isOnCell()&&(this.beforeCommand(),this.table.deleteCol(t),this.afterCommand())}},{key:"deleteTable",value:function(){var t=this.getLastRange(this.$editable);t.isCollapsed()&&t.isOnCell()&&(this.beforeCommand(),this.table.deleteTable(t),this.afterCommand())}},{key:"resizeTo",value:function(t,e,n){var o;if(n){var i=t.y/t.x,r=e.data("ratio");o={width:r>i?t.x:t.y/r,height:r>i?t.x*r:t.y}}else o={width:t.x,height:t.y};e.css(o)}},{key:"hasFocus",value:function(){return this.$editable.is(":focus")}},{key:"focus",value:function(){this.hasFocus()||this.$editable.focus()}},{key:"isEmpty",value:function(){return pt.isEmpty(this.$editable[0])||pt.emptyPara===this.$editable.html()}},{key:"empty",value:function(){this.context.invoke("code",pt.emptyPara)}},{key:"normalizeContent",value:function(){this.$editable[0].normalize()}}])&&Dt(e.prototype,n),o&&Dt(e,o),t}();function Bt(t,e){for(var n=0;n1?n.items[1]:C.head(n.items);"file"===o.kind&&-1!==o.type.indexOf("image/")?(this.context.invoke("editor.insertImagesOrCallback",[o.getAsFile()]),t.preventDefault()):"string"===o.kind&&this.context.invoke("editor.isLimited",n.getData("Text").length)&&t.preventDefault()}else if(window.clipboardData){var i=window.clipboardData.getData("text");this.context.invoke("editor.isLimited",i.length)&&t.preventDefault()}setTimeout((function(){e.context.invoke("editor.afterCommand")}),10)}}])&&Bt(e.prototype,n),o&&Bt(e,o),t}();function Mt(t,e){for(var n=0;n','
',"
"].join("")).prependTo(this.$editor)}var e,n,o;return e=t,(n=[{key:"initialize",value:function(){this.options.disableDragAndDrop?(this.documentEventHandlers.onDrop=function(t){t.preventDefault()},this.$eventListener=this.$dropzone,this.$eventListener.on("drop",this.documentEventHandlers.onDrop)):this.attachDragAndDropEvent()}},{key:"attachDragAndDropEvent",value:function(){var t=this,e=i()(),n=this.$dropzone.find(".note-dropzone-message");this.documentEventHandlers.onDragenter=function(o){var i=t.context.invoke("codeview.isActivated"),r=t.$editor.width()>0&&t.$editor.height()>0;i||e.length||!r||(t.$editor.addClass("dragover"),t.$dropzone.width(t.$editor.width()),t.$dropzone.height(t.$editor.height()),n.text(t.lang.image.dragImageHere)),e=e.add(o.target)},this.documentEventHandlers.onDragleave=function(n){(e=e.not(n.target)).length&&"BODY"!==n.target.nodeName||(e=i()(),t.$editor.removeClass("dragover"))},this.documentEventHandlers.onDrop=function(){e=i()(),t.$editor.removeClass("dragover")},this.$eventListener.on("dragenter",this.documentEventHandlers.onDragenter).on("dragleave",this.documentEventHandlers.onDragleave).on("drop",this.documentEventHandlers.onDrop),this.$dropzone.on("dragenter",(function(){t.$dropzone.addClass("hover"),n.text(t.lang.image.dropImage)})).on("dragleave",(function(){t.$dropzone.removeClass("hover"),n.text(t.lang.image.dragImageHere)})),this.$dropzone.on("drop",(function(e){var n=e.originalEvent.dataTransfer;e.preventDefault(),n&&n.files&&n.files.length?(t.$editable.focus(),t.context.invoke("editor.insertImagesOrCallback",n.files)):i.a.each(n.types,(function(e,o){if(!(o.toLowerCase().indexOf("_moz_")>-1)){var r=n.getData(o);o.toLowerCase().indexOf("text")>-1?t.context.invoke("editor.pasteHTML",r):i()(r).each((function(e,n){t.context.invoke("editor.insertNode",n)}))}}))})).on("dragover",!1)}},{key:"destroy",value:function(){var t=this;Object.keys(this.documentEventHandlers).forEach((function(e){t.$eventListener.off(e.substr(2).toLowerCase(),t.documentEventHandlers[e])})),this.documentEventHandlers={}}}])&&Mt(e.prototype,n),o&&Mt(e,o),t}();function jt(t){if("undefined"==typeof Symbol||null==t[Symbol.iterator]){if(Array.isArray(t)||(t=function(t,e){if(!t)return;if("string"==typeof t)return Ut(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Ut(t,e)}(t))){var e=0,n=function(){};return{s:n,n:function(){return e>=t.length?{done:!0}:{done:!1,value:t[e++]}},e:function(t){throw t},f:n}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i,r=!0,a=!1;return{s:function(){o=t[Symbol.iterator]()},n:function(){var t=o.next();return r=t.done,t},e:function(t){a=!0,i=t},f:function(){try{r||null==o.return||o.return()}finally{if(a)throw i}}}}function Ut(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n.*?(?:<\/iframe>)?)/gi,(function(t){if(/<.+src(?==?('|"|\s)?)[\s\S]+src(?=('|"|\s)?)[^>]*?>/i.test(t))return"";var n,o=jt(e);try{for(o.s();!(n=o.n()).done;){var i=n.value;if(new RegExp('src="(https?:)?//'+i.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")+'/(.+)"').test(t))return t}}catch(t){o.e(t)}finally{o.f()}return""}))}return t}},{key:"activate",value:function(){var t=this,e=this.CodeMirrorConstructor;if(this.$codable.val(pt.html(this.$editable,this.options.prettifyHtml)),this.$codable.height(this.$editable.height()),this.context.invoke("toolbar.updateCodeview",!0),this.context.invoke("airPopover.updateCodeview",!0),this.$editor.addClass("codeview"),this.$codable.focus(),e){var n=e.fromTextArea(this.$codable[0],this.options.codemirror);if(this.options.codemirror.tern){var o=new e.TernServer(this.options.codemirror.tern);n.ternServer=o,n.on("cursorActivity",(function(t){o.updateArgHints(t)}))}n.on("blur",(function(e){t.context.triggerEvent("blur.codeview",n.getValue(),e)})),n.on("change",(function(){t.context.triggerEvent("change.codeview",n.getValue(),n)})),n.setSize(null,this.$editable.outerHeight()),this.$codable.data("cmEditor",n)}else this.$codable.on("blur",(function(e){t.context.triggerEvent("blur.codeview",t.$codable.val(),e)})),this.$codable.on("input",(function(){t.context.triggerEvent("change.codeview",t.$codable.val(),t.$codable)}))}},{key:"deactivate",value:function(){if(this.CodeMirrorConstructor){var t=this.$codable.data("cmEditor");this.$codable.val(t.getValue()),t.toTextArea()}var e=this.purify(pt.value(this.$codable,this.options.prettifyHtml)||pt.emptyPara),n=this.$editable.html()!==e;this.$editable.html(e),this.$editable.height(this.options.height?this.$codable.height():"auto"),this.$editor.removeClass("codeview"),n&&this.context.triggerEvent("change",this.$editable.html(),this.$editable),this.$editable.focus(),this.context.invoke("toolbar.updateCodeview",!1),this.context.invoke("airPopover.updateCodeview",!1)}},{key:"destroy",value:function(){this.isActivated()&&this.deactivate()}}])&&Wt(e.prototype,n),o&&Wt(e,o),t}();function Vt(t,e){for(var n=0;n0?Math.max(o,t.options.minheight):o,o=t.options.maxHeight>0?Math.min(o,t.options.maxHeight):o,t.$editable.height(o)};t.$document.on("mousemove",o).one("mouseup",(function(){t.$document.off("mousemove",o)}))}))}},{key:"destroy",value:function(){this.$statusbar.off(),this.$statusbar.addClass("locked")}}])&&Vt(e.prototype,n),o&&Vt(e,o),t}();function _t(t,e){for(var n=0;n','
','
','
','
','
','
',this.options.disableResizeImage?"":'
',"
","
"].join("")).prependTo(this.$editingArea),this.$handle.on("mousedown",(function(e){if(pt.isControlSizing(e.target)){e.preventDefault(),e.stopPropagation();var n=t.$handle.find(".note-control-selection").data("target"),o=n.offset(),i=t.$document.scrollTop(),r=function(e){t.context.invoke("editor.resizeTo",{x:e.clientX-o.left,y:e.clientY-(o.top-i)},n,!e.shiftKey),t.update(n[0],e)};t.$document.on("mousemove",r).one("mouseup",(function(e){e.preventDefault(),t.$document.off("mousemove",r),t.context.invoke("editor.afterCommand")})),n.data("ratio")||n.data("ratio",n.height()/n.width())}})),this.$handle.on("wheel",(function(e){e.preventDefault(),t.update()}))}},{key:"destroy",value:function(){this.$handle.remove()}},{key:"update",value:function(t,e){if(this.context.isDisabled())return!1;var n=pt.isImg(t),o=this.$handle.find(".note-control-selection");if(this.context.invoke("imagePopover.update",t,e),n){var r=i()(t),a=r.position(),s={left:a.left+parseInt(r.css("marginLeft"),10),top:a.top+parseInt(r.css("marginTop"),10)},l={w:r.outerWidth(!1),h:r.outerHeight(!1)};o.css({display:"block",left:s.left,top:s.top,width:l.w,height:l.h}).data("target",r);var c=new Image;c.src=r.attr("src");var u=l.w+"x"+l.h+" ("+this.lang.image.original+": "+c.width+"x"+c.height+")";o.find(".note-control-selection-info").text(u),this.context.invoke("editor.saveTarget",t)}else this.hide();return n}},{key:"hide",value:function(){this.context.invoke("editor.clearTarget"),this.$handle.children().hide()}}])&&Yt(e.prototype,n),o&&Yt(e,o),t}();function Xt(t,e){for(var n=0;n").html(o).attr("href",n)[0];this.context.options.linkTargetBlank&&i()(r).attr("target","_blank"),this.lastWordRange.insertNode(r),this.lastWordRange=null,this.context.invoke("editor.focus")}}}},{key:"handleKeydown",value:function(t){if(C.contains([xt.code.ENTER,xt.code.SPACE],t.keyCode)){var e=this.context.invoke("editor.createRange").getWordRange();this.lastWordRange=e}}},{key:"handleKeyup",value:function(t){C.contains([xt.code.ENTER,xt.code.SPACE],t.keyCode)&&this.replace()}}])&&Xt(e.prototype,n),o&&Xt(e,o),t}();function te(t,e){for(var n=0;n'),this.$placeholder.on("click",(function(){t.context.invoke("focus")})).html(this.options.placeholder).prependTo(this.$editingArea),this.update()}},{key:"destroy",value:function(){this.$placeholder.remove()}},{key:"update",value:function(){var t=!this.context.invoke("codeview.isActivated")&&this.context.invoke("editor.isEmpty");this.$placeholder.toggle(t)}}])&&ie(e.prototype,n),o&&ie(e,o),t}();function ae(t,e){for(var n=0;n','
'+this.lang.color.background+"
","
",'","
",'
\x3c!-- back colors --\x3e
',"
",'",'',"
",'
',""].join(""):"")+(o?['
','
'+this.lang.color.foreground+"
","
",'","
",'
\x3c!-- fore colors --\x3e
',"
",'",'',"
",'
',"
"].join(""):""),callback:function(t){t.find(".note-holder").each((function(t,e){var n=i()(e);n.append(r.ui.palette({colors:r.options.colors,colorsName:r.options.colorsName,eventName:n.data("event"),container:r.options.container,tooltip:r.options.tooltip}).render())}));var e=[["#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF"]];t.find(".note-holder-custom").each((function(t,n){var o=i()(n);o.append(r.ui.palette({colors:e,colorsName:e,eventName:o.data("event"),container:r.options.container,tooltip:r.options.tooltip}).render())})),t.find("input[type=color]").each((function(e,n){i()(n).change((function(){var e=t.find("#"+i()(this).data("event")).find(".note-color-btn").first(),n=this.value.toUpperCase();e.css("background-color",n).attr("aria-label",n).attr("data-value",n).attr("data-original-title",n),e.click()}))}))},click:function(e){e.stopPropagation();var n=i()("."+t).find(".note-dropdown-menu"),o=i()(e.target),a=o.data("event"),s=o.attr("data-value");if("openPalette"===a){var l=n.find("#"+s),c=i()(n.find("#"+l.data("event")).find(".note-color-row")[0]),u=c.find(".note-color-btn").last().detach(),d=l.val();u.css("background-color",d).attr("aria-label",d).attr("data-value",d).attr("data-original-title",d),c.prepend(u),l.click()}else{if(C.contains(["backColor","foreColor"],a)){var h="backColor"===a?"background-color":"color",f=o.closest(".note-color").find(".note-recent-color"),p=o.closest(".note-color").find(".note-current-color-button");f.css(h,s),p.attr("data-"+a,s)}r.context.invoke("editor."+a,s)}}})]}).render()}},{key:"addToolbarButtons",value:function(){var t=this;this.context.memo("button.style",(function(){return t.ui.buttonGroup([t.button({className:"dropdown-toggle",contents:t.ui.dropdownButtonContents(t.ui.icon(t.options.icons.magic),t.options),tooltip:t.lang.style.style,data:{toggle:"dropdown"}}),t.ui.dropdown({className:"dropdown-style",items:t.options.styleTags,title:t.lang.style.style,template:function(e){"string"==typeof e&&(e={tag:e,title:Object.prototype.hasOwnProperty.call(t.lang.style,e)?t.lang.style[e]:e});var n=e.tag,o=e.title;return"<"+n+(e.style?' style="'+e.style+'" ':"")+(e.className?' class="'+e.className+'"':"")+">"+o+""},click:t.context.createInvokeHandler("editor.formatBlock")})]).render()}));for(var e=function(e,n){var o=t.options.styleTags[e];t.context.memo("button.style."+o,(function(){return t.button({className:"note-btn-style-"+o,contents:'
'+o.toUpperCase()+"
",tooltip:t.lang.style[o],click:t.context.createInvokeHandler("editor.formatBlock")}).render()}))},n=0,o=this.options.styleTags.length;n',t.options),tooltip:t.lang.font.name,data:{toggle:"dropdown"}}),t.ui.dropdownCheck({className:"dropdown-fontname",checkClassName:t.options.icons.menuCheck,items:t.options.fontNames.filter(t.isFontInstalled.bind(t)),title:t.lang.font.name,template:function(t){return''+t+""},click:t.context.createInvokeHandlerAndUpdateState("editor.fontName")})]).render()})),this.context.memo("button.fontsize",(function(){return t.ui.buttonGroup([t.button({className:"dropdown-toggle",contents:t.ui.dropdownButtonContents('',t.options),tooltip:t.lang.font.size,data:{toggle:"dropdown"}}),t.ui.dropdownCheck({className:"dropdown-fontsize",checkClassName:t.options.icons.menuCheck,items:t.options.fontSizes,title:t.lang.font.size,click:t.context.createInvokeHandlerAndUpdateState("editor.fontSize")})]).render()})),this.context.memo("button.fontsizeunit",(function(){return t.ui.buttonGroup([t.button({className:"dropdown-toggle",contents:t.ui.dropdownButtonContents('',t.options),tooltip:t.lang.font.sizeunit,data:{toggle:"dropdown"}}),t.ui.dropdownCheck({className:"dropdown-fontsizeunit",checkClassName:t.options.icons.menuCheck,items:t.options.fontSizeUnits,title:t.lang.font.sizeunit,click:t.context.createInvokeHandlerAndUpdateState("editor.fontSizeUnit")})]).render()})),this.context.memo("button.color",(function(){return t.colorPalette("note-color-all",t.lang.color.recent,!0,!0)})),this.context.memo("button.forecolor",(function(){return t.colorPalette("note-color-fore",t.lang.color.foreground,!1,!0)})),this.context.memo("button.backcolor",(function(){return t.colorPalette("note-color-back",t.lang.color.background,!0,!1)})),this.context.memo("button.ul",(function(){return t.button({contents:t.ui.icon(t.options.icons.unorderedlist),tooltip:t.lang.lists.unordered+t.representShortcut("insertUnorderedList"),click:t.context.createInvokeHandler("editor.insertUnorderedList")}).render()})),this.context.memo("button.ol",(function(){return t.button({contents:t.ui.icon(t.options.icons.orderedlist),tooltip:t.lang.lists.ordered+t.representShortcut("insertOrderedList"),click:t.context.createInvokeHandler("editor.insertOrderedList")}).render()}));var r=this.button({contents:this.ui.icon(this.options.icons.alignLeft),tooltip:this.lang.paragraph.left+this.representShortcut("justifyLeft"),click:this.context.createInvokeHandler("editor.justifyLeft")}),a=this.button({contents:this.ui.icon(this.options.icons.alignCenter),tooltip:this.lang.paragraph.center+this.representShortcut("justifyCenter"),click:this.context.createInvokeHandler("editor.justifyCenter")}),s=this.button({contents:this.ui.icon(this.options.icons.alignRight),tooltip:this.lang.paragraph.right+this.representShortcut("justifyRight"),click:this.context.createInvokeHandler("editor.justifyRight")}),l=this.button({contents:this.ui.icon(this.options.icons.alignJustify),tooltip:this.lang.paragraph.justify+this.representShortcut("justifyFull"),click:this.context.createInvokeHandler("editor.justifyFull")}),c=this.button({contents:this.ui.icon(this.options.icons.outdent),tooltip:this.lang.paragraph.outdent+this.representShortcut("outdent"),click:this.context.createInvokeHandler("editor.outdent")}),u=this.button({contents:this.ui.icon(this.options.icons.indent),tooltip:this.lang.paragraph.indent+this.representShortcut("indent"),click:this.context.createInvokeHandler("editor.indent")});this.context.memo("button.justifyLeft",g.invoke(r,"render")),this.context.memo("button.justifyCenter",g.invoke(a,"render")),this.context.memo("button.justifyRight",g.invoke(s,"render")),this.context.memo("button.justifyFull",g.invoke(l,"render")),this.context.memo("button.outdent",g.invoke(c,"render")),this.context.memo("button.indent",g.invoke(u,"render")),this.context.memo("button.paragraph",(function(){return t.ui.buttonGroup([t.button({className:"dropdown-toggle",contents:t.ui.dropdownButtonContents(t.ui.icon(t.options.icons.alignLeft),t.options),tooltip:t.lang.paragraph.paragraph,data:{toggle:"dropdown"}}),t.ui.dropdown([t.ui.buttonGroup({className:"note-align",children:[r,a,s,l]}),t.ui.buttonGroup({className:"note-list",children:[c,u]})])]).render()})),this.context.memo("button.height",(function(){return t.ui.buttonGroup([t.button({className:"dropdown-toggle",contents:t.ui.dropdownButtonContents(t.ui.icon(t.options.icons.textHeight),t.options),tooltip:t.lang.font.height,data:{toggle:"dropdown"}}),t.ui.dropdownCheck({items:t.options.lineHeights,checkClassName:t.options.icons.menuCheck,className:"dropdown-line-height",title:t.lang.font.height,click:t.context.createInvokeHandler("editor.lineHeight")})]).render()})),this.context.memo("button.table",(function(){return t.ui.buttonGroup([t.button({className:"dropdown-toggle",contents:t.ui.dropdownButtonContents(t.ui.icon(t.options.icons.table),t.options),tooltip:t.lang.table.table,data:{toggle:"dropdown"}}),t.ui.dropdown({title:t.lang.table.table,className:"note-table",items:['
','
','
','
',"
",'
1 x 1
'].join("")})],{callback:function(e){e.find(".note-dimension-picker-mousecatcher").css({width:t.options.insertTableMaxSize.col+"em",height:t.options.insertTableMaxSize.row+"em"}).mousedown(t.context.createInvokeHandler("editor.insertTable")).on("mousemove",t.tableMoveHandler.bind(t))}}).render()})),this.context.memo("button.link",(function(){return t.button({contents:t.ui.icon(t.options.icons.link),tooltip:t.lang.link.link+t.representShortcut("linkDialog.show"),click:t.context.createInvokeHandler("linkDialog.show")}).render()})),this.context.memo("button.picture",(function(){return t.button({contents:t.ui.icon(t.options.icons.picture),tooltip:t.lang.image.image,click:t.context.createInvokeHandler("imageDialog.show")}).render()})),this.context.memo("button.video",(function(){return t.button({contents:t.ui.icon(t.options.icons.video),tooltip:t.lang.video.video,click:t.context.createInvokeHandler("videoDialog.show")}).render()})),this.context.memo("button.hr",(function(){return t.button({contents:t.ui.icon(t.options.icons.minus),tooltip:t.lang.hr.insert+t.representShortcut("insertHorizontalRule"),click:t.context.createInvokeHandler("editor.insertHorizontalRule")}).render()})),this.context.memo("button.fullscreen",(function(){return t.button({className:"btn-fullscreen note-codeview-keep",contents:t.ui.icon(t.options.icons.arrowsAlt),tooltip:t.lang.options.fullscreen,click:t.context.createInvokeHandler("fullscreen.toggle")}).render()})),this.context.memo("button.codeview",(function(){return t.button({className:"btn-codeview note-codeview-keep",contents:t.ui.icon(t.options.icons.code),tooltip:t.lang.options.codeview,click:t.context.createInvokeHandler("codeview.toggle")}).render()})),this.context.memo("button.redo",(function(){return t.button({contents:t.ui.icon(t.options.icons.redo),tooltip:t.lang.history.redo+t.representShortcut("redo"),click:t.context.createInvokeHandler("editor.redo")}).render()})),this.context.memo("button.undo",(function(){return t.button({contents:t.ui.icon(t.options.icons.undo),tooltip:t.lang.history.undo+t.representShortcut("undo"),click:t.context.createInvokeHandler("editor.undo")}).render()})),this.context.memo("button.help",(function(){return t.button({contents:t.ui.icon(t.options.icons.question),tooltip:t.lang.options.help,click:t.context.createInvokeHandler("helpDialog.show")}).render()}))}},{key:"addImagePopoverButtons",value:function(){var t=this;this.context.memo("button.resizeFull",(function(){return t.button({contents:'100%',tooltip:t.lang.image.resizeFull,click:t.context.createInvokeHandler("editor.resize","1")}).render()})),this.context.memo("button.resizeHalf",(function(){return t.button({contents:'50%',tooltip:t.lang.image.resizeHalf,click:t.context.createInvokeHandler("editor.resize","0.5")}).render()})),this.context.memo("button.resizeQuarter",(function(){return t.button({contents:'25%',tooltip:t.lang.image.resizeQuarter,click:t.context.createInvokeHandler("editor.resize","0.25")}).render()})),this.context.memo("button.resizeNone",(function(){return t.button({contents:t.ui.icon(t.options.icons.rollback),tooltip:t.lang.image.resizeNone,click:t.context.createInvokeHandler("editor.resize","0")}).render()})),this.context.memo("button.floatLeft",(function(){return t.button({contents:t.ui.icon(t.options.icons.floatLeft),tooltip:t.lang.image.floatLeft,click:t.context.createInvokeHandler("editor.floatMe","left")}).render()})),this.context.memo("button.floatRight",(function(){return t.button({contents:t.ui.icon(t.options.icons.floatRight),tooltip:t.lang.image.floatRight,click:t.context.createInvokeHandler("editor.floatMe","right")}).render()})),this.context.memo("button.floatNone",(function(){return t.button({contents:t.ui.icon(t.options.icons.rollback),tooltip:t.lang.image.floatNone,click:t.context.createInvokeHandler("editor.floatMe","none")}).render()})),this.context.memo("button.removeMedia",(function(){return t.button({contents:t.ui.icon(t.options.icons.trash),tooltip:t.lang.image.remove,click:t.context.createInvokeHandler("editor.removeMedia")}).render()}))}},{key:"addLinkPopoverButtons",value:function(){var t=this;this.context.memo("button.linkDialogShow",(function(){return t.button({contents:t.ui.icon(t.options.icons.link),tooltip:t.lang.link.edit,click:t.context.createInvokeHandler("linkDialog.show")}).render()})),this.context.memo("button.unlink",(function(){return t.button({contents:t.ui.icon(t.options.icons.unlink),tooltip:t.lang.link.unlink,click:t.context.createInvokeHandler("editor.unlink")}).render()}))}},{key:"addTablePopoverButtons",value:function(){var t=this;this.context.memo("button.addRowUp",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.rowAbove),tooltip:t.lang.table.addRowAbove,click:t.context.createInvokeHandler("editor.addRow","top")}).render()})),this.context.memo("button.addRowDown",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.rowBelow),tooltip:t.lang.table.addRowBelow,click:t.context.createInvokeHandler("editor.addRow","bottom")}).render()})),this.context.memo("button.addColLeft",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.colBefore),tooltip:t.lang.table.addColLeft,click:t.context.createInvokeHandler("editor.addCol","left")}).render()})),this.context.memo("button.addColRight",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.colAfter),tooltip:t.lang.table.addColRight,click:t.context.createInvokeHandler("editor.addCol","right")}).render()})),this.context.memo("button.deleteRow",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.rowRemove),tooltip:t.lang.table.delRow,click:t.context.createInvokeHandler("editor.deleteRow")}).render()})),this.context.memo("button.deleteCol",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.colRemove),tooltip:t.lang.table.delCol,click:t.context.createInvokeHandler("editor.deleteCol")}).render()})),this.context.memo("button.deleteTable",(function(){return t.button({className:"btn-md",contents:t.ui.icon(t.options.icons.trash),tooltip:t.lang.table.delTable,click:t.context.createInvokeHandler("editor.deleteTable")}).render()}))}},{key:"build",value:function(t,e){for(var n=0,o=e.length;n3&&c3&&ul&&ac)&&(this.isFollowing=!1,this.$toolbar.css({position:"relative",top:0,width:"100%",zIndex:"auto"}),this.$editable.css({marginTop:""}))}},{key:"changeContainer",value:function(t){t?this.$toolbar.prependTo(this.$editor):this.options.toolbarContainer&&this.$toolbar.appendTo(this.options.toolbarContainer),this.options.followingToolbar&&this.followScroll()}},{key:"updateFullscreen",value:function(t){this.ui.toggleBtnActive(this.$toolbar.find(".btn-fullscreen"),t),this.changeContainer(t)}},{key:"updateCodeview",value:function(t){this.ui.toggleBtnActive(this.$toolbar.find(".btn-codeview"),t),t?this.deactivate():this.activate()}},{key:"activate",value:function(t){var e=this.$toolbar.find("button");t||(e=e.not(".note-codeview-keep")),this.ui.toggleBtn(e,!0)}},{key:"deactivate",value:function(t){var e=this.$toolbar.find("button");t||(e=e.not(".note-codeview-keep")),this.ui.toggleBtn(e,!1)}}])&&le(e.prototype,n),o&&le(e,o),t}();function ue(t,e){for(var n=0;n','"),''),"",'
','"),''),"
",this.options.disableLinkTarget?"":i()("
").append(this.ui.checkbox({className:"sn-checkbox-open-in-new-window",text:this.lang.link.openInNewWindow,checked:!0}).render()).html(),i()("
").append(this.ui.checkbox({className:"sn-checkbox-use-protocol",text:this.lang.link.useProtocol,checked:!0}).render()).html()].join(""),n='');this.$dialog=this.ui.dialog({className:"link-dialog",title:this.lang.link.insert,fade:this.options.dialogsFade,body:e,footer:n}).render().appendTo(t)}},{key:"destroy",value:function(){this.ui.hideDialog(this.$dialog),this.$dialog.remove()}},{key:"bindEnterKey",value:function(t,e){t.on("keypress",(function(t){t.keyCode===xt.code.ENTER&&(t.preventDefault(),e.trigger("click"))}))}},{key:"toggleLinkBtn",value:function(t,e,n){this.ui.toggleBtn(t,e.val()&&n.val())}},{key:"showLinkDialog",value:function(t){var e=this;return i.a.Deferred((function(n){var o=e.$dialog.find(".note-link-text"),i=e.$dialog.find(".note-link-url"),r=e.$dialog.find(".note-link-btn"),a=e.$dialog.find(".sn-checkbox-open-in-new-window input[type=checkbox]"),s=e.$dialog.find(".sn-checkbox-use-protocol input[type=checkbox]");e.ui.onDialogShown(e.$dialog,(function(){e.context.triggerEvent("dialog.shown"),!t.url&&g.isValidUrl(t.text)&&(t.url=t.text),o.on("input paste propertychange",(function(){t.text=o.val(),e.toggleLinkBtn(r,o,i)})).val(t.text),i.on("input paste propertychange",(function(){t.text||o.val(i.val()),e.toggleLinkBtn(r,o,i)})).val(t.url),m.isSupportTouch||i.trigger("focus"),e.toggleLinkBtn(r,o,i),e.bindEnterKey(i,r),e.bindEnterKey(o,r);var l=void 0!==t.isNewWindow?t.isNewWindow:e.context.options.linkTargetBlank;a.prop("checked",l);var c=!t.url&&e.context.options.useProtocol;s.prop("checked",c),r.one("click",(function(r){r.preventDefault(),n.resolve({range:t.range,url:i.val(),text:o.val(),isNewWindow:a.is(":checked"),checkProtocol:s.is(":checked")}),e.ui.hideDialog(e.$dialog)}))})),e.ui.onDialogHidden(e.$dialog,(function(){o.off(),i.off(),r.off(),"pending"===n.state()&&n.reject()})),e.ui.showDialog(e.$dialog)})).promise()}},{key:"show",value:function(){var t=this,e=this.context.invoke("editor.getLinkInfo");this.context.invoke("editor.saveRange"),this.showLinkDialog(e).then((function(e){t.context.invoke("editor.restoreRange"),t.context.invoke("editor.createLink",e)})).fail((function(){t.context.invoke("editor.restoreRange")}))}}])&&ue(e.prototype,n),o&&ue(e,o),t}();function he(t,e){for(var n=0;n ')}}).render().appendTo(this.options.container);var t=this.$popover.find(".popover-content,.note-popover-content");this.context.invoke("buttons.build",t,this.options.popover.link),this.$popover.on("mousedown",(function(t){t.preventDefault()}))}},{key:"destroy",value:function(){this.$popover.remove()}},{key:"update",value:function(){if(this.context.invoke("editor.hasFocus")){var t=this.context.invoke("editor.getLastRange");if(t.isCollapsed()&&t.isOnAnchor()){var e=pt.ancestor(t.sc,pt.isAnchor),n=i()(e).attr("href");this.$popover.find("a").attr("href",n).text(n);var o=pt.posFromPlaceholder(e),r=i()(this.options.container).offset();o.top-=r.top,o.left-=r.left,this.$popover.css({display:"block",left:o.left,top:o.top})}else this.hide()}else this.hide()}},{key:"hide",value:function(){this.$popover.hide()}}])&&he(e.prototype,n),o&&he(e,o),t}();function pe(t,e){for(var n=0;n")}var o=this.options.dialogsInBody?this.$body:this.options.container,i=['
','",'',t,"
",'
','",'',"
"].join(""),r='');this.$dialog=this.ui.dialog({title:this.lang.image.insert,fade:this.options.dialogsFade,body:i,footer:r}).render().appendTo(o)}},{key:"destroy",value:function(){this.ui.hideDialog(this.$dialog),this.$dialog.remove()}},{key:"bindEnterKey",value:function(t,e){t.on("keypress",(function(t){t.keyCode===xt.code.ENTER&&(t.preventDefault(),e.trigger("click"))}))}},{key:"show",value:function(){var t=this;this.context.invoke("editor.saveRange"),this.showImageDialog().then((function(e){t.ui.hideDialog(t.$dialog),t.context.invoke("editor.restoreRange"),"string"==typeof e?t.options.callbacks.onImageLinkInsert?t.context.triggerEvent("image.link.insert",e):t.context.invoke("editor.insertImage",e):t.context.invoke("editor.insertImagesOrCallback",e)})).fail((function(){t.context.invoke("editor.restoreRange")}))}},{key:"showImageDialog",value:function(){var t=this;return i.a.Deferred((function(e){var n=t.$dialog.find(".note-image-input"),o=t.$dialog.find(".note-image-url"),i=t.$dialog.find(".note-image-btn");t.ui.onDialogShown(t.$dialog,(function(){t.context.triggerEvent("dialog.shown"),n.replaceWith(n.clone().on("change",(function(t){e.resolve(t.target.files||t.target.value)})).val("")),o.on("input paste propertychange",(function(){t.ui.toggleBtn(i,o.val())})).val(""),m.isSupportTouch||o.trigger("focus"),i.click((function(t){t.preventDefault(),e.resolve(o.val())})),t.bindEnterKey(o,i)})),t.ui.onDialogHidden(t.$dialog,(function(){n.off(),o.off(),i.off(),"pending"===e.state()&&e.reject()})),t.ui.showDialog(t.$dialog)}))}}])&&pe(e.prototype,n),o&&pe(e,o),t}();function ve(t,e){for(var n=0;n','"),''),"
"].join(""),n='');this.$dialog=this.ui.dialog({title:this.lang.video.insert,fade:this.options.dialogsFade,body:e,footer:n}).render().appendTo(t)}},{key:"destroy",value:function(){this.ui.hideDialog(this.$dialog),this.$dialog.remove()}},{key:"bindEnterKey",value:function(t,e){t.on("keypress",(function(t){t.keyCode===xt.code.ENTER&&(t.preventDefault(),e.trigger("click"))}))}},{key:"createVideoNode",value:function(t){var e,n=t.match(/\/\/(?:(?:www|m)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w|-]{11})(?:(?:[\?&]t=)(\S+))?$/),o=t.match(/(?:www\.|\/\/)instagram\.com\/p\/(.[a-zA-Z0-9_-]*)/),r=t.match(/\/\/vine\.co\/v\/([a-zA-Z0-9]+)/),a=t.match(/\/\/(player\.)?vimeo\.com\/([a-z]*\/)*(\d+)[?]?.*/),s=t.match(/.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/),l=t.match(/\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/),c=t.match(/\/\/v\.qq\.com.*?vid=(.+)/),u=t.match(/\/\/v\.qq\.com\/x?\/?(page|cover).*?\/([^\/]+)\.html\??.*/),d=t.match(/^.+.(mp4|m4v)$/),h=t.match(/^.+.(ogg|ogv)$/),f=t.match(/^.+.(webm)$/),p=t.match(/(?:www\.|\/\/)facebook\.com\/([^\/]+)\/videos\/([0-9]+)/);if(n&&11===n[1].length){var m=n[1],v=0;if(void 0!==n[2]){var g=n[2].match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);if(g)for(var b=[3600,60,1],k=0,y=b.length;k").attr("frameborder",0).attr("src","https://www.youtube.com/embed/"+m+(v>0?"?start="+v:"")).attr("width","640").attr("height","360")}else if(o&&o[0].length)e=i()("