commit
						24151dff7d
					
				| @ -1,5 +1,9 @@ | ||||
| # https://docs.travis-ci.com/user/languages/android/ | ||||
| 
 | ||||
| language: android | ||||
| 
 | ||||
| # so, out of blue travis requires this now (without it build would not even execute, immediate failure when downloading jdk) | ||||
| dist: trusty | ||||
| jdk: openjdk8 | ||||
| sudo: false | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										219
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,219 @@ | ||||
| # Changelog | ||||
| 
 | ||||
| # 4.0.0 | ||||
| * maven group-id change to `io.noties.markwon` (was `ru.noties.markwon`) | ||||
| * package name change to `io.notier.markwon.*` (was `ru.noties.markwon.*`) | ||||
| * androidx artifacts ([#76]) | ||||
| * `Markwon#builder` does not require explicit `CorePlugin` (added automatically),  | ||||
| use `Markwon#builderNoCore()` to obtain a builder without `CorePlugin` | ||||
| * Removed `Priority` abstraction and `MarkwonPlugin#priority` (use `MarkwonPlugin.Registry`) | ||||
| * Removed `MarkwonPlugin#configureHtmlRenderer` (for configuration use `HtmlPlugin` directly) | ||||
| * Removed `MarkwonPlugin#configureImages` (for configuration use `ImagesPlugin` directly) | ||||
| * Added `MarkwonPlugin.Registry` and `MarkwonPlugin#configure(Registry)` method | ||||
| * `CorePlugin#addOnTextAddedListener` (process raw text added) | ||||
| * `ImageSizeResolver` signature change (accept `AsyncDrawable`) | ||||
| * `LinkResolver` is now an independent entity (previously part of `LinkSpan`) | ||||
| * `AsyncDrawableScheduler` can now be called multiple times without performance penalty | ||||
| * `AsyncDrawable` now exposes its destination, image-size, last known dimensions (canvas, text-size) | ||||
| * `AsyncDrawableLoader` signature change (accept `AsyncDrawable`) | ||||
| * Add `LastLineSpacingSpan` | ||||
| * Add `MarkwonConfiguration.Builder#asyncDrawableLoader` method | ||||
| * `ImagesPlugin` removed from `core` artifact  | ||||
| (also removed `images-gif`, `images-okhttp` and `images-svg` artifacts and their plugins) | ||||
| * `ImagesPlugin` exposes configuration (adding scheme-handler, media-decoder, etc) | ||||
| * `ImagesPlugin` allows multiple images with the same source (URL) | ||||
| * Add `PlaceholderProvider` and `ErrorHandler` to `ImagesPlugin` | ||||
| * `GIF` and `SVG` media-decoders are automatically added to `ImagesPlugin` if required libraries are found in the classpath | ||||
| * `ImageItem` is now abstract, has 2 implementations: `withResult`, `withDecodingNeeded` | ||||
| * Add `images-glide`, `images-picasso`, `linkify`, `simple-ext` modules | ||||
| * `JLatexMathPlugin` is now independent of `ImagesPlugin` | ||||
| * Fix wrong `JLatexMathPlugin` formulas sizes ([#138]) | ||||
| * `JLatexMathPlugin` has `backgroundProvider`, `executorService` configuration | ||||
| * `HtmlPlugin` is self-contained (all configuration is moved in the plugin itself) | ||||
| 
 | ||||
| [#76]: https://github.com/noties/Markwon/issues/76 | ||||
| [#138]: https://github.com/noties/Markwon/issues/138 | ||||
| 
 | ||||
| 
 | ||||
| ## 3.0.2 | ||||
| * Fix `latex` plugin ([#136]) | ||||
| * Add `#create(Call.Factory)` factory method to `OkHttpImagesPlugin` ([#129]) | ||||
| <br>Thanks to [@ZacSweers]  | ||||
| 
 | ||||
| [#136]: https://github.com/noties/Markwon/issues/136 | ||||
| [#129]: https://github.com/noties/Markwon/issues/129 | ||||
| [@ZacSweers]: https://github.com/ZacSweers | ||||
| 
 | ||||
| 
 | ||||
| ## 3.0.1 | ||||
| * Add `AsyncDrawableLoader.Builder#implementation` method ([#109]) | ||||
| * AsyncDrawable allow placeholder to have independent size ([#115]) | ||||
| * `addFactory` method for MarkwonSpansFactory | ||||
| * Add optional spans for list blocks (bullet and ordered) | ||||
| * AsyncDrawable placeholder bounds fix | ||||
| * SpannableBuilder setSpans allow array of arrays | ||||
| * Add `requireFactory` method to MarkwonSpansFactory | ||||
| * Add DrawableUtils | ||||
| 
 | ||||
| [#109]: https://github.com/noties/Markwon/issues/109 | ||||
| [#115]: https://github.com/noties/Markwon/issues/115 | ||||
| 
 | ||||
| 
 | ||||
| ## 3.0.0 | ||||
| * Plugins, plugins, plugins | ||||
| * Split basic functionality blocks into standalone modules | ||||
| * Maven artifacts group changed to `ru.noties.markwon` (previously had been `ru.noties`) | ||||
| * removed `markwon`, `markwon-image-loader`, `markwon-html-pareser-api`, `markwon-html-parser-impl`, `markwon-view` modules | ||||
| * new module system: `core`, `ext-latex`, `ext-strikethrough`, `ext-tables`, `ext-tasklist`, `html`, `image-gif`, `image-okhttp`, `image-svg`, `recycler`, `recycler-table`, `syntax-highlight` | ||||
| * Add BufferType option for Markwon configuration | ||||
| * Fix typo in AsyncDrawable waitingForDimensions | ||||
| * New tests format | ||||
| * `Markwon.render` returns `Spanned` instance of generic `CharSequence` | ||||
| * LinkMovementMethod is applied implicitly if not set on a TextView explicitly | ||||
| * Split code and codeBlock spans and factories | ||||
| * Add CustomTypefaceSpan  | ||||
| * Add NoCopySpansFactory | ||||
| * Add placeholder to image loading | ||||
| 
 | ||||
| Generally speaking there are a lot of changes. Most of them are not backwards-compatible. | ||||
| The main point of this release is the `Plugin` system that allows more fluent configuration | ||||
| and opens the possibility of extending `Markwon` with 3rd party functionality in a simple | ||||
| and intuitive fashion. Please refer to the [documentation web-site](https://noties.github.io/Markwon) | ||||
| that has information on how to start migration. | ||||
| 
 | ||||
| The shortest excerpt of this release can be expressed like this: | ||||
| 
 | ||||
| ```java | ||||
| // previous v2.x.x way | ||||
| Markwon.setMarkdown(textView, "**Hello there!**"); | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| // 3.x.x | ||||
| Markwon.create(context) | ||||
|         .setMarkdown(textView, "**Hello there!**"); | ||||
| ``` | ||||
| 
 | ||||
| But there is much more to it, please visit documentation web-site | ||||
| to get the full picture of latest changes. | ||||
| 
 | ||||
| ## 2.0.1 | ||||
| * `SpannableMarkdownVisitor` Rename blockQuoteIndent to blockIndent | ||||
| * Fixed block new lines logic for block quote and paragraph ([#82]) | ||||
| * AsyncDrawable fix no dimensions bug ([#81]) | ||||
| * Update SpannableTheme to use Px instead of Dimension annotation | ||||
| * Allow TaskListSpan isDone mutation | ||||
| * Updated commonmark-java to 0.12.1 | ||||
| * Add OrderedListItemSpan measure utility method ([#78]) | ||||
| * Add SpannableBuilder#getSpans method | ||||
| * Fix DataUri scheme handler in image-loader ([#74]) | ||||
| * Introduced a "copy" builder for SpannableThem | ||||
| <br>Thanks [@c-b-h] | ||||
| 
 | ||||
| [#82]: https://github.com/noties/Markwon/issues/82 | ||||
| [#81]: https://github.com/noties/Markwon/issues/81 | ||||
| [#78]: https://github.com/noties/Markwon/issues/78 | ||||
| [#74]: https://github.com/noties/Markwon/issues/74 | ||||
| [@c-b-h]: https://github.com/c-b-h | ||||
| 
 | ||||
| 
 | ||||
| ## 2.0.0 | ||||
| * Add `html-parser-api` and `html-parser-impl` modules | ||||
| * Add `HtmlEmptyTagReplacement` | ||||
| * Implement Appendable and CharSequence in SpannableBuilder | ||||
| * Renamed library modules to reflect maven artifact names | ||||
| * Rename `markwon-syntax` to `markwon-syntax-highlight` | ||||
| * Add HtmlRenderer asbtraction | ||||
| * Add CssInlineStyleParser | ||||
| * Fix Theme#listItemColor and OL | ||||
| * Fix task list block parser to revert parsing state when line is not matching | ||||
| * Defined test format files | ||||
| * image-loader add datauri parser | ||||
| * image-loader add support for inline data uri image references | ||||
| * Add travis configuration | ||||
| * Fix image with width greater than canvas scaled | ||||
| * Fix blockquote span | ||||
| * Dealing with white spaces at the end of a document | ||||
| * image-loader add SchemeHandler abstraction | ||||
| * Add sample-latex-math module | ||||
| 
 | ||||
| ## v1.1.1 | ||||
| * Fix OrderedListItemSpan text position (baseline) ([#55]) | ||||
| * Add softBreakAddsNewLine option for SpannableConfiguration ([#54]) | ||||
| * Paragraph text can now explicitly be spanned ([#58]) | ||||
| <br>Thanks to [@c-b-h] | ||||
| * Fix table border color if odd background is specified ([#56]) | ||||
| * Add table customizations (even and header rows) | ||||
| 
 | ||||
| [#55]: https://github.com/noties/Markwon/issues/55 | ||||
| [#54]: https://github.com/noties/Markwon/issues/54 | ||||
| [#58]: https://github.com/noties/Markwon/issues/58 | ||||
| [#56]: https://github.com/noties/Markwon/issues/56 | ||||
| [@c-b-h]: https://github.com/c-b-h | ||||
| 
 | ||||
| 
 | ||||
| ## v1.1.0 | ||||
| * Update commonmark to 0.11.0 and android-gif to 1.2.14 | ||||
| * Add syntax highlight functionality (`library-syntax` module and `markwon-syntax` artifact) | ||||
| * Add headingTypeface, headingTextSizes to SpannableTheme | ||||
| <br>Thanks to [@edenman] | ||||
| * Introduce `MediaDecoder` abstraction to `image-loader` module | ||||
| * Introduce `SpannableFactory` | ||||
| <br>Thanks for idea to [@c-b-h] | ||||
| * Update sample application to use syntax-highlight | ||||
| * Update sample application to use clickable placeholder for GIF media | ||||
| 
 | ||||
| [@edenman]: https://github.com/edenman | ||||
| [@c-b-h]: https://github.com/c-b-h | ||||
| 
 | ||||
| 
 | ||||
| ## v1.0.6 | ||||
| * Fix bullet list item size (depend on text size and not top-bottom arguments) | ||||
| * Add ability to specify MovementMethod when applying markdown to a TextView | ||||
| * Markdown images size is also resolved via ImageSizeResolver | ||||
| * Moved `ImageSize`, `ImageSizeResolver` and `ImageSizeResolverDef`  | ||||
| to `ru.noties.markwon.renderer` package (one level up, previously `ru.noties.markwon.renderer.html`) | ||||
| 
 | ||||
| ## v1.0.5 | ||||
| * Change LinkSpan to extend URLSpan. Allow default linkColor (if not set explicitly) | ||||
| * Fit an image without dimensions to canvas width (and keep ratio) | ||||
| * Add support for separate color for code blocks ([#37]) | ||||
| <br>Thanks to [@Arcnor] | ||||
| 
 | ||||
| [#37]: https://github.com/noties/Markwon/issues/37 | ||||
| [@Arcnor]: https://github.com/Arcnor | ||||
| 
 | ||||
| 
 | ||||
| ## v1.0.4 | ||||
| * Fixes [#28] (tables are not rendered when at the end of the markdown) | ||||
| * Adds support for `indented code blocks` | ||||
| <br>Thanks to [@dlew] | ||||
| 
 | ||||
| [#28]: https://github.com/noties/Markwon/issues/ | ||||
| [@dlew]: https://github.com/dlew | ||||
| 
 | ||||
| 
 | ||||
| ## v1.0.3 | ||||
| * Fixed ordered lists (when number width is greater than block margin) | ||||
| 
 | ||||
| ## v1.0.2 | ||||
| * Fixed additional white spaces at the end of parsed markdown | ||||
| * Fixed headings with no underline (levels 1 & 2) | ||||
| * Tables can have no borders | ||||
| 
 | ||||
| ## v1.0.1 | ||||
| * Support for task-lists ([#2]) | ||||
| * Spans now are applied in reverse order ([#5] [#10]) | ||||
| * Added `SpannableBuilder` to follow the reverse order of spans | ||||
| * Updated `commonmark-java` to `0.10.0` | ||||
| * Fixes [#1] | ||||
| 
 | ||||
| [#1]: https://github.com/noties/Markwon/issues/1 | ||||
| [#2]: https://github.com/noties/Markwon/issues/2 | ||||
| [#5]: https://github.com/noties/Markwon/issues/5 | ||||
| [#10]: https://github.com/noties/Markwon/issues/10 | ||||
| 
 | ||||
| 
 | ||||
| ## v1.0.0 | ||||
| 
 | ||||
| Initial release | ||||
							
								
								
									
										25
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README.md
									
									
									
									
									
								
							| @ -28,20 +28,21 @@ features listed in [commonmark-spec] are supported | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 
 | ||||
| ```groovy | ||||
| implementation "ru.noties.markwon:core:${markwonVersion}" | ||||
| ```kotlin | ||||
| implementation "io.noties.markwon:core:${markwonVersion}" | ||||
| ``` | ||||
| 
 | ||||
| Full list of available artifacts is present in the [install section](https://noties.github.io/Markwon/docs/v3/install.html) | ||||
| Full list of available artifacts is present in the [install section](https://noties.github.io/Markwon/docs/v4/install.html) | ||||
| of the [documentation] web-site. | ||||
| 
 | ||||
| Please visit [documentation] web-site for further reference. | ||||
| 
 | ||||
| 
 | ||||
| > You can find previous version of Markwon in [2.x.x](https://github.com/noties/Markwon/tree/2.x.x) branch | ||||
| > You can find previous version of Markwon in [2.x.x](https://github.com/noties/Markwon/tree/2.x.x) | ||||
| and [3.x.x](https://github.com/noties/Markwon/tree/3.x.x) branches | ||||
| 
 | ||||
| 
 | ||||
| ## Supported markdown features: | ||||
| @ -97,15 +98,6 @@ Please visit [documentation] web-site for reference | ||||
| 
 | ||||
| [documentation]: https://noties.github.io/Markwon | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ## Applications using Markwon | ||||
| 
 | ||||
| * [Partiko](https://partiko.app) | ||||
| * [FairNote Notepad](https://play.google.com/store/apps/details?id=com.rgiskard.fairnote) | ||||
| * [Boxcryptor](https://www.boxcryptor.com) | ||||
| 
 | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| # Demo | ||||
| @ -217,7 +209,6 @@ public static Parser createParser() { | ||||
|     android:layout_margin="16dip" | ||||
|     android:lineSpacingExtra="2dip" | ||||
|     android:textSize="16sp" | ||||
|     tools:context="ru.noties.markwon.MainActivity" | ||||
|     tools:text="yo\nman" /> | ||||
| 
 | ||||
| </ScrollView> | ||||
| @ -296,7 +287,7 @@ Underscores (`_`) | ||||
| ## License | ||||
| 
 | ||||
| ``` | ||||
|   Copyright 2017 Dimitry Ivanov (mail@dimitryivanov.ru) | ||||
|   Copyright 2019 Dimitry Ivanov (legal@noties.io) | ||||
| 
 | ||||
|   Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   you may not use this file except in compliance with the License. | ||||
|  | ||||
| @ -6,7 +6,7 @@ android { | ||||
|     buildToolsVersion config['build-tools'] | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         applicationId "ru.noties.markwon" | ||||
|         applicationId "io.noties.markwon" | ||||
|         minSdkVersion config['min-sdk'] | ||||
|         targetSdkVersion config['target-sdk'] | ||||
|         versionCode 1 | ||||
| @ -33,8 +33,7 @@ dependencies { | ||||
|     implementation project(':markwon-ext-tables') | ||||
|     implementation project(':markwon-ext-tasklist') | ||||
|     implementation project(':markwon-html') | ||||
|     implementation project(':markwon-image-gif') | ||||
|     implementation project(':markwon-image-svg') | ||||
|     implementation project(':markwon-image') | ||||
|     implementation project(':markwon-syntax-highlight') | ||||
| 
 | ||||
|     deps.with { | ||||
| @ -42,6 +41,8 @@ dependencies { | ||||
|         implementation it['prism4j'] | ||||
|         implementation it['debug'] | ||||
|         implementation it['dagger'] | ||||
|         implementation it['android-svg'] | ||||
|         implementation it['android-gif'] | ||||
|     } | ||||
| 
 | ||||
|     deps['annotationProcessor'].with { | ||||
|  | ||||
| @ -1,15 +1,15 @@ | ||||
| package ru.noties.markwon.debug; | ||||
| package io.noties.markwon.debug; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.Nullable; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import ru.noties.markwon.R; | ||||
| import ru.noties.markwon.ext.tasklist.TaskListDrawable; | ||||
| import io.noties.markwon.app.R; | ||||
| import io.noties.markwon.ext.tasklist.TaskListDrawable; | ||||
| 
 | ||||
| public class DebugCheckboxDrawableView extends View { | ||||
| 
 | ||||
| @ -4,7 +4,7 @@ | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
| 
 | ||||
|     <ru.noties.markwon.debug.DebugCheckboxDrawableView | ||||
|     <io.noties.markwon.debug.DebugCheckboxDrawableView | ||||
|         android:layout_width="128dip" | ||||
|         android:layout_height="128dip" | ||||
|         android:layout_gravity="center" | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     package="ru.noties.markwon"> | ||||
|     package="io.noties.markwon.app"> | ||||
| 
 | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|     <uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS" /> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import javax.inject.Scope; | ||||
| 
 | ||||
| @ -1,11 +1,12 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.app.Application; | ||||
| import android.content.Context; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import ru.noties.debug.AndroidLogDebugOutput; | ||||
| import ru.noties.debug.Debug; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import io.noties.debug.AndroidLogDebugOutput; | ||||
| import io.noties.debug.Debug; | ||||
| 
 | ||||
| public class App extends Application { | ||||
| 
 | ||||
| @ -1,10 +1,11 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.text.TextUtils; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| abstract class AppBarItem { | ||||
| 
 | ||||
|     static class State { | ||||
| @ -1,4 +1,4 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import javax.inject.Singleton; | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.Resources; | ||||
| @ -12,12 +12,12 @@ import javax.inject.Singleton; | ||||
| 
 | ||||
| import dagger.Module; | ||||
| import dagger.Provides; | ||||
| import io.noties.markwon.syntax.Prism4jThemeDarkula; | ||||
| import io.noties.markwon.syntax.Prism4jThemeDefault; | ||||
| import okhttp3.Cache; | ||||
| import okhttp3.OkHttpClient; | ||||
| import ru.noties.markwon.syntax.Prism4jThemeDarkula; | ||||
| import ru.noties.markwon.syntax.Prism4jThemeDefault; | ||||
| import ru.noties.prism4j.Prism4j; | ||||
| import ru.noties.prism4j.annotations.PrismBundle; | ||||
| import io.noties.prism4j.Prism4j; | ||||
| import io.noties.prism4j.annotations.PrismBundle; | ||||
| 
 | ||||
| @Module | ||||
| @PrismBundle(includeAll = true) | ||||
| @ -1,18 +1,20 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spanned; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import ru.noties.debug.Debug; | ||||
| import io.noties.debug.Debug; | ||||
| import io.noties.markwon.Markwon; | ||||
| 
 | ||||
| public class MainActivity extends Activity { | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import dagger.Subcomponent; | ||||
| 
 | ||||
| @ -1,13 +1,14 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.content.ContentResolver; | ||||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| import android.os.Handler; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| @ -20,11 +21,11 @@ import java.util.concurrent.Future; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import io.noties.debug.Debug; | ||||
| import okhttp3.OkHttpClient; | ||||
| import okhttp3.Request; | ||||
| import okhttp3.Response; | ||||
| import okhttp3.ResponseBody; | ||||
| import ru.noties.debug.Debug; | ||||
| 
 | ||||
| @ActivityScope | ||||
| public class MarkdownLoader { | ||||
| @ -1,35 +1,38 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| import android.os.Handler; | ||||
| import android.os.SystemClock; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spanned; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Future; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import ru.noties.debug.Debug; | ||||
| import ru.noties.markwon.core.CorePlugin; | ||||
| import ru.noties.markwon.ext.strikethrough.StrikethroughPlugin; | ||||
| import ru.noties.markwon.ext.tables.TablePlugin; | ||||
| import ru.noties.markwon.ext.tasklist.TaskListPlugin; | ||||
| import ru.noties.markwon.gif.GifAwarePlugin; | ||||
| import ru.noties.markwon.html.HtmlPlugin; | ||||
| import ru.noties.markwon.image.ImagesPlugin; | ||||
| import ru.noties.markwon.image.gif.GifPlugin; | ||||
| import ru.noties.markwon.image.svg.SvgPlugin; | ||||
| import ru.noties.markwon.syntax.Prism4jTheme; | ||||
| import ru.noties.markwon.syntax.Prism4jThemeDarkula; | ||||
| import ru.noties.markwon.syntax.Prism4jThemeDefault; | ||||
| import ru.noties.markwon.syntax.SyntaxHighlightPlugin; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute; | ||||
| import ru.noties.prism4j.Prism4j; | ||||
| import io.noties.debug.Debug; | ||||
| import io.noties.markwon.AbstractMarkwonPlugin; | ||||
| import io.noties.markwon.Markwon; | ||||
| import io.noties.markwon.MarkwonConfiguration; | ||||
| import io.noties.markwon.app.gif.GifAwarePlugin; | ||||
| import io.noties.markwon.ext.strikethrough.StrikethroughPlugin; | ||||
| import io.noties.markwon.ext.tables.TablePlugin; | ||||
| import io.noties.markwon.ext.tasklist.TaskListPlugin; | ||||
| import io.noties.markwon.html.HtmlPlugin; | ||||
| import io.noties.markwon.image.ImagesPlugin; | ||||
| import io.noties.markwon.image.file.FileSchemeHandler; | ||||
| import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler; | ||||
| import io.noties.markwon.syntax.Prism4jTheme; | ||||
| import io.noties.markwon.syntax.Prism4jThemeDarkula; | ||||
| import io.noties.markwon.syntax.Prism4jThemeDefault; | ||||
| import io.noties.markwon.syntax.SyntaxHighlightPlugin; | ||||
| import io.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import io.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute; | ||||
| import io.noties.prism4j.Prism4j; | ||||
| 
 | ||||
| @ActivityScope | ||||
| public class MarkdownRenderer { | ||||
| @ -94,10 +97,17 @@ public class MarkdownRenderer { | ||||
|                         : prism4JThemeDarkula; | ||||
| 
 | ||||
|                 final Markwon markwon = Markwon.builder(context) | ||||
|                         .usePlugin(CorePlugin.create()) | ||||
|                         .usePlugin(ImagesPlugin.createWithAssets(context)) | ||||
|                         .usePlugin(SvgPlugin.create(context.getResources())) | ||||
|                         .usePlugin(GifPlugin.create(false)) | ||||
|                         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|                             @Override | ||||
|                             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                                 // data uri scheme handler is added automatically | ||||
|                                 // SVG & GIF will be added if required dependencies are present in the classpath | ||||
|                                 // default-media-decoder is also added automatically | ||||
|                                 plugin | ||||
|                                         .addSchemeHandler(OkHttpNetworkSchemeHandler.create()) | ||||
|                                         .addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets())); | ||||
|                             } | ||||
|                         })) | ||||
|                         .usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme)) | ||||
|                         .usePlugin(GifAwarePlugin.create(context)) | ||||
|                         .usePlugin(TablePlugin.create(context)) | ||||
| @ -1,8 +1,9 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Singleton; | ||||
| @ -1,7 +1,8 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.net.Uri; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public interface UriProcessor { | ||||
| @ -1,7 +1,8 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.net.Uri; | ||||
| import android.support.annotation.NonNull; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @ -32,7 +33,7 @@ class UriProcessorImpl implements UriProcessor { | ||||
|                         .fragment(uri.getFragment()) | ||||
|                         .query(uri.getQuery()); | ||||
| 
 | ||||
|                 for (String segment: segments) { | ||||
|                 for (String segment : segments) { | ||||
|                     final String part; | ||||
|                     if ("blob".equals(segment)) { | ||||
|                         part = "raw"; | ||||
| @ -1,11 +1,12 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.net.Uri; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import io.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import io.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute; | ||||
| 
 | ||||
| class UrlProcessorInitialReadme implements UrlProcessor { | ||||
| 
 | ||||
| @ -1,9 +1,10 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon.app; | ||||
| 
 | ||||
| import android.support.annotation.IntDef; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import androidx.annotation.IntDef; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public abstract class Views { | ||||
| 
 | ||||
| @ -1,15 +1,16 @@ | ||||
| package ru.noties.markwon.gif; | ||||
| package io.noties.markwon.app.gif; | ||||
| 
 | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import io.noties.markwon.image.AsyncDrawable; | ||||
| import io.noties.markwon.image.AsyncDrawableLoader; | ||||
| import io.noties.markwon.image.ImageSize; | ||||
| import io.noties.markwon.image.ImageSizeResolver; | ||||
| import pl.droidsonroids.gif.GifDrawable; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.image.ImageSize; | ||||
| import ru.noties.markwon.image.ImageSizeResolver; | ||||
| import ru.noties.markwon.image.AsyncDrawable; | ||||
| 
 | ||||
| public class GifAwareAsyncDrawable extends AsyncDrawable { | ||||
| 
 | ||||
| @ -1,21 +1,20 @@ | ||||
| package ru.noties.markwon.gif; | ||||
| package io.noties.markwon.app.gif; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import org.commonmark.node.Image; | ||||
| 
 | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonSpansFactory; | ||||
| import ru.noties.markwon.R; | ||||
| import ru.noties.markwon.RenderProps; | ||||
| import ru.noties.markwon.SpanFactory; | ||||
| import ru.noties.markwon.image.AsyncDrawableSpan; | ||||
| import ru.noties.markwon.image.ImageProps; | ||||
| import ru.noties.markwon.image.ImagesPlugin; | ||||
| import ru.noties.markwon.priority.Priority; | ||||
| import io.noties.markwon.AbstractMarkwonPlugin; | ||||
| import io.noties.markwon.MarkwonConfiguration; | ||||
| import io.noties.markwon.MarkwonSpansFactory; | ||||
| import io.noties.markwon.RenderProps; | ||||
| import io.noties.markwon.SpanFactory; | ||||
| import io.noties.markwon.app.R; | ||||
| import io.noties.markwon.image.AsyncDrawableSpan; | ||||
| import io.noties.markwon.image.ImageProps; | ||||
| 
 | ||||
| public class GifAwarePlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
| @ -59,12 +58,6 @@ public class GifAwarePlugin extends AbstractMarkwonPlugin { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Priority priority() { | ||||
|         return Priority.after(ImagesPlugin.class); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void afterSetText(@NonNull TextView textView) { | ||||
|         processor.process(textView); | ||||
| @ -1,4 +1,4 @@ | ||||
| package ru.noties.markwon.gif; | ||||
| package io.noties.markwon.app.gif; | ||||
| 
 | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.ColorFilter; | ||||
| @ -6,9 +6,10 @@ import android.graphics.Paint; | ||||
| import android.graphics.PixelFormat; | ||||
| import android.graphics.Rect; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.support.annotation.ColorInt; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| 
 | ||||
| import androidx.annotation.ColorInt; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| public class GifPlaceholder extends Drawable { | ||||
| 
 | ||||
| @ -1,15 +1,16 @@ | ||||
| package ru.noties.markwon.gif; | ||||
| package io.noties.markwon.app.gif; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spannable; | ||||
| import android.text.Spanned; | ||||
| import android.text.style.ClickableSpan; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import io.noties.markwon.image.AsyncDrawableSpan; | ||||
| import pl.droidsonroids.gif.GifDrawable; | ||||
| import ru.noties.markwon.image.AsyncDrawableSpan; | ||||
| 
 | ||||
| public abstract class GifProcessor { | ||||
| 
 | ||||
| @ -8,11 +8,11 @@ | ||||
|         android:id="@+id/scroll_view" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:padding="16dip" | ||||
|         android:clipToPadding="false" | ||||
|         android:layout_marginTop="?android:attr/actionBarSize" | ||||
|         android:clipChildren="false" | ||||
|         android:scrollbarStyle="outsideOverlay" | ||||
|         android:layout_marginTop="?android:attr/actionBarSize"> | ||||
|         android:clipToPadding="false" | ||||
|         android:padding="16dip" | ||||
|         android:scrollbarStyle="outsideOverlay"> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/text" | ||||
| @ -20,7 +20,6 @@ | ||||
|             android:layout_height="wrap_content" | ||||
|             android:lineSpacingExtra="2dip" | ||||
|             android:textSize="16sp" | ||||
|             tools:context="ru.noties.markwon.MainActivity" | ||||
|             tools:text="yo\nman" /> | ||||
| 
 | ||||
|     </ScrollView> | ||||
|  | ||||
							
								
								
									
										28
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								build.gradle
									
									
									
									
									
								
							| @ -4,8 +4,8 @@ buildscript { | ||||
|         jcenter() | ||||
|     } | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:3.3.2' | ||||
|         classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0' | ||||
|         classpath 'com.android.tools.build:gradle:3.4.1' | ||||
|         classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -29,8 +29,8 @@ task clean(type: Delete) { | ||||
|     delete rootProject.buildDir | ||||
| } | ||||
| 
 | ||||
| task wrapper(type: Wrapper) { | ||||
|     gradleVersion '4.10.2' | ||||
| wrapper { | ||||
|     gradleVersion '5.1.1' | ||||
|     distributionType 'all' | ||||
| } | ||||
| 
 | ||||
| @ -53,29 +53,29 @@ ext { | ||||
|             'push-aar-gradle': 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' | ||||
|     ] | ||||
| 
 | ||||
|     final def supportVersion = '28.0.0' | ||||
|     final def commonMarkVersion = '0.12.1' | ||||
|     final def daggerVersion = '2.10' | ||||
| 
 | ||||
|     deps = [ | ||||
|             'support-annotations'     : "com.android.support:support-annotations:$supportVersion", | ||||
|             'support-app-compat'      : "com.android.support:appcompat-v7:$supportVersion", | ||||
|             'support-recycler-view'   : "com.android.support:recyclerview-v7:$supportVersion", | ||||
|             'x-annotations'           : 'androidx.annotation:annotation:1.1.0', | ||||
|             'x-recycler-view'         : 'androidx.recyclerview:recyclerview:1.0.0', | ||||
|             'commonmark'              : "com.atlassian.commonmark:commonmark:$commonMarkVersion", | ||||
|             'commonmark-strikethrough': "com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:$commonMarkVersion", | ||||
|             'commonmark-table'        : "com.atlassian.commonmark:commonmark-ext-gfm-tables:$commonMarkVersion", | ||||
|             'android-svg'             : 'com.caverock:androidsvg:1.2.1', | ||||
|             'android-svg'             : 'com.caverock:androidsvg:1.4', | ||||
|             'android-gif'             : 'pl.droidsonroids.gif:android-gif-drawable:1.2.14', | ||||
|             'jlatexmath-android'      : 'ru.noties:jlatexmath-android:0.1.0', | ||||
|             'okhttp'                  : 'com.squareup.okhttp3:okhttp:3.9.0', | ||||
|             'prism4j'                 : 'ru.noties:prism4j:1.1.0', | ||||
|             'debug'                   : 'ru.noties:debug:3.0.0@jar', | ||||
|             'adapt'                   : 'ru.noties:adapt:1.1.0', | ||||
|             'dagger'                  : "com.google.dagger:dagger:$daggerVersion" | ||||
|             'prism4j'                 : 'io.noties:prism4j:2.0.0', | ||||
|             'debug'                   : 'io.noties:debug:5.0.0@jar', | ||||
|             'adapt'                   : 'io.noties:adapt:2.0.0', | ||||
|             'dagger'                  : "com.google.dagger:dagger:$daggerVersion", | ||||
|             'picasso'                 : 'com.squareup.picasso:picasso:2.71828', | ||||
|             'glide'                   : 'com.github.bumptech.glide:glide:4.9.0' | ||||
|     ] | ||||
| 
 | ||||
|     deps['annotationProcessor'] = [ | ||||
|             'prism4j-bundler': 'ru.noties:prism4j-bundler:1.1.0', | ||||
|             'prism4j-bundler': 'io.noties:prism4j-bundler:2.0.0', | ||||
|             'dagger-compiler': "com.google.dagger:dagger-compiler:$daggerVersion" | ||||
|     ] | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| 
 | ||||
| // this is a generated file, do not modify. To update it run 'collectArtifacts.js' script
 | ||||
| const artifacts = [{"id":"core","name":"Core","group":"ru.noties.markwon","description":"Core Markwon artifact that includes basic markdown parsing and rendering"},{"id":"ext-latex","name":"LaTeX","group":"ru.noties.markwon","description":"Extension to add LaTeX formulas to Markwon markdown"},{"id":"ext-strikethrough","name":"Strikethrough","group":"ru.noties.markwon","description":"Extension to add strikethrough markup to Markwon markdown"},{"id":"ext-tables","name":"Tables","group":"ru.noties.markwon","description":"Extension to add tables markup (GFM) to Markwon markdown"},{"id":"ext-tasklist","name":"Task List","group":"ru.noties.markwon","description":"Extension to add task lists (GFM) to Markwon markdown"},{"id":"html","name":"HTML","group":"ru.noties.markwon","description":"Provides HTML parsing functionality"},{"id":"image-gif","name":"Image GIF","group":"ru.noties.markwon","description":"Adds GIF media support to Markwon markdown"},{"id":"image-okhttp","name":"Image OkHttp","group":"ru.noties.markwon","description":"Adds OkHttp client to retrieve images data from network"},{"id":"image-svg","name":"Image SVG","group":"ru.noties.markwon","description":"Adds SVG media support to Markwon markdown"},{"id":"recycler","name":"Recycler","group":"ru.noties.markwon","description":"Provides RecyclerView.Adapter to display Markwon markdown"},{"id":"recycler-table","name":"Recycler Table","group":"ru.noties.markwon","description":"Provides MarkwonAdapter.Entry to render TableBlocks inside Android-native TableLayout widget"},{"id":"syntax-highlight","name":"Syntax Highlight","group":"ru.noties.markwon","description":"Add syntax highlight to Markwon markdown via Prism4j library"}]; | ||||
| const artifacts = [{"id":"core","name":"Core","group":"io.noties.markwon","description":"Core Markwon artifact that includes basic markdown parsing and rendering"},{"id":"ext-latex","name":"LaTeX","group":"io.noties.markwon","description":"Extension to add LaTeX formulas to Markwon markdown"},{"id":"ext-strikethrough","name":"Strikethrough","group":"io.noties.markwon","description":"Extension to add strikethrough markup to Markwon markdown"},{"id":"ext-tables","name":"Tables","group":"io.noties.markwon","description":"Extension to add tables markup (GFM) to Markwon markdown"},{"id":"ext-tasklist","name":"Task List","group":"io.noties.markwon","description":"Extension to add task lists (GFM) to Markwon markdown"},{"id":"html","name":"HTML","group":"io.noties.markwon","description":"Provides HTML parsing functionality"},{"id":"image","name":"Image","group":"io.noties.markwon","description":"Markwon image loading module (with optional GIF and SVG support)"},{"id":"image-glide","name":"Image Glide","group":"io.noties.markwon","description":"Markwon image loading module (based on Glide library)"},{"id":"image-picasso","name":"Image Picasso","group":"io.noties.markwon","description":"Markwon image loading module (based on Picasso library)"},{"id":"linkify","name":"Linkify","group":"io.noties.markwon","description":"Markwon plugin to linkify text (based on Android Linkify)"},{"id":"recycler","name":"Recycler","group":"io.noties.markwon","description":"Provides RecyclerView.Adapter to display Markwon markdown"},{"id":"recycler-table","name":"Recycler Table","group":"io.noties.markwon","description":"Provides MarkwonAdapter.Entry to render TableBlocks inside Android-native TableLayout widget"},{"id":"simple-ext","name":"Simple Extension","group":"io.noties.markwon","description":"Custom extension based on simple delimiter usage"},{"id":"syntax-highlight","name":"Syntax Highlight","group":"io.noties.markwon","description":"Add syntax highlight to Markwon markdown via Prism4j library"}]; | ||||
| export { artifacts }; | ||||
|  | ||||
							
								
								
									
										4
									
								
								docs/.vuepress/.artifacts.v3.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/.vuepress/.artifacts.v3.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| 
 | ||||
| // this is a generated file, do not modify. To update it run 'collectArtifacts.js' script
 | ||||
| const artifacts = [{"id":"core","name":"Core","group":"ru.noties.markwon","description":"Core Markwon artifact that includes basic markdown parsing and rendering"},{"id":"ext-latex","name":"LaTeX","group":"ru.noties.markwon","description":"Extension to add LaTeX formulas to Markwon markdown"},{"id":"ext-strikethrough","name":"Strikethrough","group":"ru.noties.markwon","description":"Extension to add strikethrough markup to Markwon markdown"},{"id":"ext-tables","name":"Tables","group":"ru.noties.markwon","description":"Extension to add tables markup (GFM) to Markwon markdown"},{"id":"ext-tasklist","name":"Task List","group":"ru.noties.markwon","description":"Extension to add task lists (GFM) to Markwon markdown"},{"id":"html","name":"HTML","group":"ru.noties.markwon","description":"Provides HTML parsing functionality"},{"id":"image-gif","name":"Image GIF","group":"ru.noties.markwon","description":"Adds GIF media support to Markwon markdown"},{"id":"image-okhttp","name":"Image OkHttp","group":"ru.noties.markwon","description":"Adds OkHttp client to retrieve images data from network"},{"id":"image-svg","name":"Image SVG","group":"ru.noties.markwon","description":"Adds SVG media support to Markwon markdown"},{"id":"recycler","name":"Recycler","group":"ru.noties.markwon","description":"Provides RecyclerView.Adapter to display Markwon markdown"},{"id":"recycler-table","name":"Recycler Table","group":"ru.noties.markwon","description":"Provides MarkwonAdapter.Entry to render TableBlocks inside Android-native TableLayout widget"},{"id":"syntax-highlight","name":"Syntax Highlight","group":"ru.noties.markwon","description":"Add syntax highlight to Markwon markdown via Prism4j library"}]; | ||||
| export { artifacts }; | ||||
| @ -29,7 +29,7 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import { artifacts } from "../.artifacts.js"; | ||||
| import { artifacts } from "../.artifacts.v3.js"; | ||||
| 
 | ||||
| if (!artifacts) { | ||||
|   throw "Artifacts not found. Use `collectArtifacts.js` script to obtain artifacts metadata."; | ||||
|  | ||||
							
								
								
									
										105
									
								
								docs/.vuepress/components/ArtifactPicker4.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								docs/.vuepress/components/ArtifactPicker4.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <div class="artifact-container"> | ||||
|       <div v-for="artifact in artifacts" class="artifact" @click="toggleSelection(artifact)"> | ||||
|         <div class="artifact-header"> | ||||
|           <input type="checkbox" v-model="selected" :value="artifact.id" :id="artifact.id"> | ||||
|           <strong> | ||||
|             <label :for="artifact.id">{{artifact.name}}</label> | ||||
|           </strong> | ||||
|         </div> | ||||
|         <div class="artifact-description" v-if="artifact.description">{{artifact.description}}</div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="extra-class language-gradle selected-artifacts" v-if="selected.length > 0"> | ||||
|       <div class="selected-artifact-script"> | ||||
|         <span class="token keyword">final def</span> | ||||
|         <span> markwon_version = </span> | ||||
|         <span class="token string">'{{latestVersion}}'</span> | ||||
|       </div> | ||||
|       <br> | ||||
|       <div class="selected-artifact-script" v-for="artifact in selectedArtifacts"> | ||||
|         <span>implementation </span> | ||||
|         <span class="token string">"{{artifact.group}}:{{artifact.id}}:</span> | ||||
|         <span>$markwon_version</span> | ||||
|         <span class="token string">"</span> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import { artifacts } from "../.artifacts.js"; | ||||
| 
 | ||||
| if (!artifacts) { | ||||
|   throw "Artifacts not found. Use `collectArtifacts.js` script to obtain artifacts metadata."; | ||||
| } | ||||
| 
 | ||||
| export default { | ||||
|   name: "ArtifactPicker", | ||||
|   data() { | ||||
|     return { | ||||
|       artifacts, | ||||
|       selected: ["core"], | ||||
|       latestVersion: "latest_version" | ||||
|     }; | ||||
|   }, | ||||
|   methods: { | ||||
|     toggleSelection(artifact) { | ||||
|       const index = this.selected.indexOf(artifact.id); | ||||
|       if (index < 0) { | ||||
|         this.selected.push(artifact.id); | ||||
|       } else { | ||||
|         this.selected.splice(index, 1); | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     selectedArtifacts() { | ||||
|       return this.artifacts.filter(a => this.selected.indexOf(a.id) >= 0); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .artifact-container { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   flex-direction: row; | ||||
|   margin-top: 0.5em; | ||||
| } | ||||
| .artifact { | ||||
|   flex: 1; | ||||
|   border: 1px #ccc solid; | ||||
|   background-color: #fafafa; | ||||
|   padding: 0.5em; | ||||
|   margin: 0.2em; | ||||
|   border-radius: 0.25em; | ||||
|   min-width: 10em; | ||||
|   max-width: 10em; | ||||
| } | ||||
| .artifact-description { | ||||
|   font-size: 0.85em; | ||||
|   margin-top: 0.5em; | ||||
| } | ||||
| .selected-artifacts { | ||||
|   color: white; | ||||
|   font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", | ||||
|     monospace; | ||||
|   padding: 16px; | ||||
|   text-align: left; | ||||
|   word-spacing: normal; | ||||
|   word-break: normal; | ||||
|   word-wrap: normal; | ||||
|   line-height: 1.5; | ||||
|   -moz-tab-size: 4; | ||||
|   hyphens: none; | ||||
|   font-size: 0.85em; | ||||
|   margin-top: 0.5em; | ||||
| } | ||||
| .selected-artifact-script { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										57
									
								
								docs/.vuepress/components/AwesomeGroup.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								docs/.vuepress/components/AwesomeGroup.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| <template> | ||||
|   <div class="awesome-group"> | ||||
|     <div v-for="app in apps" class="awesome-app"> | ||||
|       <a :href="app.link" class="awesome-app-name" target="_blank" rel="noopener noreferrer">{{app.name}} <OutboundLink/></a> | ||||
|       <img class="awesome-app-image" :src="app.image"> | ||||
|       <span v-if="app.description" class="awesome-app-description">{{ app.description }}</span> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| 
 | ||||
| import Link from './Link.vue' | ||||
| 
 | ||||
| export default { | ||||
|   name: "AwesomeGroup", | ||||
|   props: ["apps"], | ||||
|   components: { | ||||
|       Link | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .awesome-group { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
| } | ||||
| .awesome-app { | ||||
|   padding: 1em; | ||||
|   background-color: #fff; | ||||
|   margin: 0.25em; | ||||
|   border-radius: 0.25em; | ||||
|   box-shadow: 0 0 0.1em 0.1em #eee; | ||||
|   max-width: 30%; | ||||
|   min-width: 100px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   flex-direction: column; | ||||
| } | ||||
| .awesome-app-name { | ||||
|   font-size: 1em; | ||||
|   font-weight: 500; | ||||
|   margin-bottom: 0.5em; | ||||
| } | ||||
| .awesome-app-image { | ||||
|   width: 96px; | ||||
|   height: 96px; | ||||
|   display: block; | ||||
| } | ||||
| .awesome-app-description { | ||||
|   margin-top: 1em; | ||||
|   font-size: 0.85em; | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										24
									
								
								docs/.vuepress/components/MavenBadge4.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								docs/.vuepress/components/MavenBadge4.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| <template> | ||||
|     <a :href="mavenSearchUrl()"><img :src="shieldImgageUrl()" :alt="displayLabel"></a> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|     name: 'MavenBadge', | ||||
|     props: ['artifact', 'label'], | ||||
|     methods: { | ||||
|         mavenSearchUrl: function() { | ||||
|             return `http://search.maven.org/#search|ga|1|g%3A%22io.noties.markwon%22%20AND%20a%3A%22${this.artifact}%22`; | ||||
|         }, | ||||
|         shieldImgageUrl: function() { | ||||
|             return `https://img.shields.io/maven-central/v/io.noties.markwon/${this.artifact}.svg?label=${this.displayLabel}`; | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
|         displayLabel() { | ||||
|             return this.label || this.artifact; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| @ -12,16 +12,16 @@ module.exports = { | ||||
|     ], | ||||
|     themeConfig: { | ||||
|         nav: [ | ||||
|             { text: 'Install', link: '/docs/v3/install.md' }, | ||||
|             { text: 'Changelog', link: '/CHANGELOG.md' }, | ||||
|             { text: 'Install', link: '/docs/v4/install.md' }, | ||||
|             { | ||||
|                 text: 'API Version', | ||||
|                 items: [ | ||||
|                     { text: 'Current (3.x.x)', link: '/' }, | ||||
|                     { text: 'Current (4.x.x)', link: '/' }, | ||||
|                     { text: 'Legacy (3.x.x)', link: '/docs/v3/install.md' }, | ||||
|                     { text: 'Legacy (2.x.x)', link: '/docs/v2/' } | ||||
|                 ] | ||||
|             }, | ||||
|             { text: 'Sandbox', link: '/sandbox.md' }, | ||||
|             { text: 'Changelog', link: 'https://github.com/noties/Markwon/blob/master/CHANGELOG.md' }, | ||||
|             { text: 'Github', link: 'https://github.com/noties/Markwon' } | ||||
|         ], | ||||
|         sidebar: { | ||||
| @ -36,8 +36,8 @@ module.exports = { | ||||
|                 '/docs/v2/html.md', | ||||
|                 '/docs/v2/view.md' | ||||
|             ], | ||||
|             '/': [ | ||||
|                 '', | ||||
|             '/docs/v3': [ | ||||
|                 '/docs/v3/install.md', | ||||
|                 { | ||||
|                     title: 'Core', | ||||
|                     collapsable: false, | ||||
| @ -67,7 +67,40 @@ module.exports = { | ||||
|                 '/docs/v3/recycler-table/', | ||||
|                 '/docs/v3/syntax-highlight/', | ||||
|                 '/docs/v3/migration-2-3.md' | ||||
|             ] | ||||
|             ], | ||||
|             '/': [ | ||||
|                 '', | ||||
|                 { | ||||
|                     title: 'Core', | ||||
|                     collapsable: false, | ||||
|                     children: [ | ||||
|                         '/docs/v4/core/getting-started.md', | ||||
|                         '/docs/v4/core/plugins.md', | ||||
|                         '/docs/v4/core/registry.md', | ||||
|                         '/docs/v4/core/theme.md', | ||||
|                         '/docs/v4/core/configuration.md', | ||||
|                         '/docs/v4/core/visitor.md', | ||||
|                         '/docs/v4/core/spans-factory.md', | ||||
|                         '/docs/v4/core/core-plugin.md', | ||||
|                         '/docs/v4/core/movement-method-plugin.md', | ||||
|                         '/docs/v4/core/render-props.md' | ||||
|                     ] | ||||
|                 }, | ||||
|                 '/docs/v4/ext-latex/', | ||||
|                 '/docs/v4/ext-strikethrough/', | ||||
|                 '/docs/v4/ext-tables/', | ||||
|                 '/docs/v4/ext-tasklist/', | ||||
|                 '/docs/v4/html/', | ||||
|                 '/docs/v4/image/', | ||||
|                 '/docs/v4/image-glide/', | ||||
|                 '/docs/v4/image-picasso/', | ||||
|                 '/docs/v4/linkify/', | ||||
|                 '/docs/v4/recycler/', | ||||
|                 '/docs/v4/recycler-table/', | ||||
|                 '/docs/v4/simple-ext/', | ||||
|                 '/docs/v4/syntax-highlight/', | ||||
|                 '/docs/v4/recipes.md' | ||||
|             ], | ||||
|         }, | ||||
|         sidebarDepth: 2, | ||||
|         lastUpdated: true | ||||
|  | ||||
| @ -1,132 +0,0 @@ | ||||
| # Changelog | ||||
| 
 | ||||
| # 3.0.1 | ||||
| * Add `AsyncDrawableLoader.Builder#implementation` method (<GithubIssue id="109" />) | ||||
| * AsyncDrawable allow placeholder to have independent size (<GithubIssue id="115" />) | ||||
| * `addFactory` method for MarkwonSpansFactory | ||||
| * Add optional spans for list blocks (bullet and ordered) | ||||
| * AsyncDrawable placeholder bounds fix | ||||
| * SpannableBuilder setSpans allow array of arrays | ||||
| * Add `requireFactory` method to MarkwonSpansFactory | ||||
| * Add DrawableUtils | ||||
| 
 | ||||
| ## 3.0.0 | ||||
| * Plugins, plugins, plugins | ||||
| * Split basic functionality blocks into standalone modules | ||||
| * Maven artifacts group changed to `ru.noties.markwon` (previously had been `ru.noties`) | ||||
| * removed `markwon`, `markwon-image-loader`, `markwon-html-pareser-api`, `markwon-html-parser-impl`, `markwon-view` modules | ||||
| * new module system: `core`, `ext-latex`, `ext-strikethrough`, `ext-tables`, `ext-tasklist`, `html`, `image-gif`, `image-okhttp`, `image-svg`, `recycler`, `recycler-table`, `syntax-highlight` | ||||
| * Add BufferType option for Markwon configuration | ||||
| * Fix typo in AsyncDrawable waitingForDimensions | ||||
| * New tests format | ||||
| * `Markwon.render` returns `Spanned` instance of generic `CharSequence` | ||||
| * LinkMovementMethod is applied implicitly if not set on a TextView explicitly | ||||
| * Split code and codeBlock spans and factories | ||||
| * Add CustomTypefaceSpan  | ||||
| * Add NoCopySpansFactory | ||||
| * Add placeholder to image loading | ||||
| 
 | ||||
| Generally speaking there are a lot of changes. Most of them are not backwards-compatible. | ||||
| The main point of this release is the `Plugin` system that allows more fluent configuration | ||||
| and opens the possibility of extending `Markwon` with 3rd party functionality in a simple | ||||
| and intuitive fashion. Please refer to the [documentation web-site](https://noties.github.io/Markwon) | ||||
| that has information on how to start migration. | ||||
| 
 | ||||
| The shortest excerpt of this release can be expressed like this: | ||||
| 
 | ||||
| ```java | ||||
| // previous v2.x.x way | ||||
| Markwon.setMarkdown(textView, "**Hello there!**"); | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| // 3.x.x | ||||
| Markwon.create(context) | ||||
|         .setMarkdown(textView, "**Hello there!**"); | ||||
| ``` | ||||
| 
 | ||||
| But there is much more to it, please visit documentation web-site | ||||
| to get the full picture of latest changes. | ||||
| 
 | ||||
| ## 2.0.1 | ||||
| * `SpannableMarkdownVisitor` Rename blockQuoteIndent to blockIndent | ||||
| * Fixed block new lines logic for block quote and paragraph (<GithubIssue id="82" />) | ||||
| * AsyncDrawable fix no dimensions bug (<GithubIssue id="81" />) | ||||
| * Update SpannableTheme to use Px instead of Dimension annotation | ||||
| * Allow TaskListSpan isDone mutation | ||||
| * Updated commonmark-java to 0.12.1 | ||||
| * Add OrderedListItemSpan measure utility method (<GithubIssue id="78" />) | ||||
| * Add SpannableBuilder#getSpans method | ||||
| * Fix DataUri scheme handler in image-loader (<GithubIssue id="74" />) | ||||
| * Introduced a "copy" builder for SpannableThem <br>Thanks <GithubUser name="c-b-h" /> | ||||
| 
 | ||||
| ## 2.0.0 | ||||
| * Add `html-parser-api` and `html-parser-impl` modules | ||||
| * Add `HtmlEmptyTagReplacement` | ||||
| * Implement Appendable and CharSequence in SpannableBuilder | ||||
| * Renamed library modules to reflect maven artifact names | ||||
| * Rename `markwon-syntax` to `markwon-syntax-highlight` | ||||
| * Add HtmlRenderer asbtraction | ||||
| * Add CssInlineStyleParser | ||||
| * Fix Theme#listItemColor and OL | ||||
| * Fix task list block parser to revert parsing state when line is not matching | ||||
| * Defined test format files | ||||
| * image-loader add datauri parser | ||||
| * image-loader add support for inline data uri image references | ||||
| * Add travis configuration | ||||
| * Fix image with width greater than canvas scaled | ||||
| * Fix blockquote span | ||||
| * Dealing with white spaces at the end of a document | ||||
| * image-loader add SchemeHandler abstraction | ||||
| * Add sample-latex-math module | ||||
| 
 | ||||
| ## v1.1.1 | ||||
| * Fix OrderedListItemSpan text position (baseline) (<GithubIssue id="55" />) | ||||
| * Add softBreakAddsNewLine option for SpannableConfiguration (<GithubIssue id="54" />) | ||||
| * Paragraph text can now explicitly be spanned (<GithubPull id="58" />)<br>Thanks to <GithubUser name="c-b-h" /> | ||||
| * Fix table border color if odd background is specified (<GithubIssue id="56" />) | ||||
| * Add table customizations (even and header rows) | ||||
| 
 | ||||
| ## v1.1.0 | ||||
| * Update commonmark to 0.11.0 and android-gif to 1.2.14 | ||||
| * Add syntax highlight functionality (`library-syntax` module and `markwon-syntax` artifact) | ||||
| * Add headingTypeface, headingTextSizes to SpannableTheme<br>Thanks to <GithubUser name="edenman" /> | ||||
| * Introduce `MediaDecoder` abstraction to `image-loader` module | ||||
| * Introduce `SpannableFactory`<br>Thanks for idea to <GithubUser name="c-b-h" /> | ||||
| * Update sample application to use syntax-highlight | ||||
| * Update sample application to use clickable placeholder for GIF media | ||||
| 
 | ||||
| ## v1.0.6 | ||||
| * Fix bullet list item size (depend on text size and not top-bottom arguments) | ||||
| * Add ability to specify MovementMethod when applying markdown to a TextView | ||||
| * Markdown images size is also resolved via ImageSizeResolver | ||||
| * Moved `ImageSize`, `ImageSizeResolver` and `ImageSizeResolverDef`  | ||||
| to `ru.noties.markwon.renderer` package (one level up, previously `ru.noties.markwon.renderer.html`) | ||||
| 
 | ||||
| ## v1.0.5 | ||||
| * Change LinkSpan to extend URLSpan. Allow default linkColor (if not set explicitly) | ||||
| * Fit an image without dimensions to canvas width (and keep ratio) | ||||
| * Add support for separate color for code blocks (<GithubPull id="37" />)<br>Thanks to <GithubUser name="Arcnor" /> | ||||
| 
 | ||||
| ## v1.0.4 | ||||
| * Fixes <GithubIssue id="28"/> (tables are not rendered when at the end of the markdown) | ||||
| * Adds support for `indented code blocks`<br>Thanks to <GithubUser name="dlew"/> | ||||
| 
 | ||||
| ## v1.0.3 | ||||
| * Fixed ordered lists (when number width is greater than block margin) | ||||
| 
 | ||||
| ## v1.0.2 | ||||
| * Fixed additional white spaces at the end of parsed markdown | ||||
| * Fixed headings with no underline (levels 1 & 2) | ||||
| * Tables can have no borders | ||||
| 
 | ||||
| ## v1.0.1 | ||||
| * Support for task-lists (<GithubIssue id="2" />) | ||||
| * Spans now are applied in reverse order (<GithubIssue id="5" /> <GithubIssue id="10" />) | ||||
| * Added `SpannableBuilder` to follow the reverse order of spans | ||||
| * Updated `commonmark-java` to `0.10.0` | ||||
| * Fixes <GithubIssue id="1" /> | ||||
| 
 | ||||
| ## v1.0.0 | ||||
| 
 | ||||
| Initial release | ||||
| @ -5,7 +5,7 @@ title: 'Introduction' | ||||
| <img :src="$withBase('/art/markwon_logo.png')" alt="Markwon Logo" width="50%"> | ||||
| 
 | ||||
| <br><br> | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties.markwon%22%20) | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22io.noties.markwon%22%20) | ||||
| [](https://travis-ci.org/noties/Markwon) | ||||
| 
 | ||||
| **Markwon** is a markdown library for Android. It parses markdown following  | ||||
| @ -27,17 +27,17 @@ listed in <Link name="commonmark-spec" /> are supported (including support for * | ||||
| * Strong emphasis (`**`, `__`) | ||||
| * Headers (`#{1,6}`) | ||||
| * Links (`[]()` && `[][]`) | ||||
| * [Images](/docs/v3/core/images.md) | ||||
| * [Images](/docs/v4/image/) | ||||
| * Thematic break (`---`, `***`, `___`) | ||||
| * Quotes & nested quotes (`>{1,}`) | ||||
| * Ordered & non-ordered lists & nested ones | ||||
| * Inline code | ||||
| * Code blocks | ||||
| * [Strike-through](/docs/v3/ext-strikethrough/) (`~~`) | ||||
| * [Tables](/docs/v3/ext-tables/) (*with limitations*) | ||||
| * [Syntax highlight](/docs/v3/syntax-highlight/) | ||||
| * [LaTeX](/docs/v3/ext-latex/) formulas | ||||
| * [HTML](/docs/v3/html/) | ||||
| * [Strike-through](/docs/v4/ext-strikethrough/) (`~~`) | ||||
| * [Tables](/docs/v4/ext-tables/) (*with limitations*) | ||||
| * [Syntax highlight](/docs/v4/syntax-highlight/) | ||||
| * [LaTeX](/docs/v4/ext-latex/) formulas | ||||
| * [HTML](/docs/v4/html/) | ||||
|   * Emphasis (`<i>`, `<em>`, `<cite>`, `<dfn>`) | ||||
|   * Strong emphasis (`<b>`, `<strong>`) | ||||
|   * SuperScript (`<sup>`) | ||||
| @ -49,9 +49,9 @@ listed in <Link name="commonmark-spec" /> are supported (including support for * | ||||
|   * Images (`img` will require configured image loader) | ||||
|   * Blockquote (`blockquote`) | ||||
|   * Heading (`h1`, `h2`, `h3`, `h4`, `h5`, `h6`) | ||||
|   * there is support to render any HTML tag, but it will require to create a special `TagHandler`, | ||||
|     more information can be found in [HTML section](/docs/v3/core/html-renderer.md) | ||||
| * [Task lists](/docs/v3/ext-tasklist/): | ||||
|   * there is support to render any HTML/XML tag, but it will require to create a special `TagHandler`, | ||||
|     more information can be found in [HTML section](/docs/v4/html/#taghandler) | ||||
| * [Task lists](/docs/v4/ext-tasklist/): | ||||
| <ul style="list-style-type: none; margin: 0; padding: 0;"> | ||||
| <li><input type="checkbox" disabled>Not <i>done</i></li> | ||||
| <li><input type="checkbox" disabled checked><strong>Done</strong> with <code>X</code></li> | ||||
| @ -82,6 +82,9 @@ and 2 themes included: Light & Dark. It can be downloaded from [releases](ht | ||||
| * [FairNote](https://play.google.com/store/apps/details?id=com.rgiskard.fairnote) - simple and intuitive notepad app. It gives you speed and efficiency when you write notes, to-do lists, e-mails, or jot down quick ideas. | ||||
| * [Boxcryptor](https://www.boxcryptor.com) - A software that adds AES-256 and RSA encryption to Dropbox, Google Drive, OneDrive and many other clouds. | ||||
| 
 | ||||
| <AwesomeGroup :apps="[ | ||||
|     {name: 'Cinopsys: Movies and Shows', image: 'http://drive.google.com/uc?export=view&id=1rD0HLd8tDUCe8QcVEG_iGvsJbFyozRhC', link: 'https://play.google.com/store/apps/details?id=com.cinopsys.movieshows'} | ||||
| ]" /> | ||||
| 
 | ||||
| <u>Extension/plugins</u>: | ||||
| 
 | ||||
|  | ||||
| @ -7,6 +7,8 @@ title: 'Overview' | ||||
| <br><br> | ||||
| <MavenBadges2xx/> | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| **Markwon** is a markdown library for Android. It parses markdown following  | ||||
| <Link name="commonmark-spec" /> with the help of amazing <Link name="commonmark-java" /> library | ||||
| and renders result as _Android-native_ Spannables. **No HTML** is involved | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Configuration | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| `SpannableConfiguration` is the core component that controls how markdown is parsed and rendered. | ||||
| It can be obtained via factory methods: | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Factory <Badge text="1.1.0" /> | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| `SpannableFactory` is used to create Span implementations. | ||||
| 
 | ||||
| ```java | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Getting started | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| ## Quick one | ||||
| 
 | ||||
| This is the most simple way to set markdown to a `TextView` or any of its siblings: | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # HTML <Badge text="2.0.0" /> | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| Starting with version `2.0.0` `Markwon` brings the whole HTML parsing/rendering | ||||
| stack _on-site_. The main reason for this are _special_ definitions of HTML nodes | ||||
| by <Link name="commonmark-spec" />. More specifically: <Link name="commonmark-spec#inline" displayName="inline" />  | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Images | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| By default `Markwon` doesn't handle images. Although `AsyncDrawable.Loader` is | ||||
| defined in main artifact, it does not provide implementation. | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Installation | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| <MavenBadges2xx /> | ||||
| 
 | ||||
| In order to start using `Markwon` add this to your dependencies block | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Syntax highlight | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| <MavenBadge2xx artifact="markwon-syntax-highlight" /> | ||||
| 
 | ||||
| This is a simple module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance. | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # Theme | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| Here is the list of properties that can be configured via `SpannableTheme`. If you wish to control what  | ||||
| is out of this list, you can use [SpannableFactory](/docs/v2/factory.md) | ||||
| abstraction which lets you to gather full control of Spans that are used to display markdown. | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| # MarkwonView | ||||
| 
 | ||||
| <LegacyWarning /> | ||||
| 
 | ||||
| <MavenBadge2xx artifact="markwon-view" /> | ||||
| 
 | ||||
| This is simple library containing 2 views that are able to display markdown: | ||||
|  | ||||
| @ -56,7 +56,7 @@ myTableWidget.setTable(table); | ||||
| 
 | ||||
| :::tip | ||||
| To take advantage of this functionality and render tables without limitations (including | ||||
| horizontally scrollable layout when its contents exceed screen width), refer to [recycler-table](/docs/v3/recycler-table/) | ||||
| horizontally scrollable layout when its contents exceed screen width), refer to [recycler-table](/docs/v4/recycler-table/) | ||||
| module documentation that adds support for rendering `TableBlock` markdown node inside Android-native `TableLayout` widget. | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										168
									
								
								docs/docs/v4/core/configuration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								docs/docs/v4/core/configuration.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,168 @@ | ||||
| # Configuration | ||||
| 
 | ||||
| `MarkwonConfiguration` class holds common Markwon functionality. | ||||
| These are _configurable_ properties: | ||||
| * `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />) | ||||
| * `SyntaxHighlight` | ||||
| * `LinkResolver` (since <Badge text="4.0.0" />, before — `LinkSpan.Resolver`) | ||||
| * `UrlProcessor` | ||||
| * `ImageSizeResolver` | ||||
| 
 | ||||
| :::tip | ||||
| Additionally `MarkwonConfiguration` holds: | ||||
| * `MarkwonTheme` | ||||
| * `MarkwonSpansFactory` | ||||
| 
 | ||||
| Please note that these values can be retrieved from `MarkwonConfiguration` | ||||
| instance, but their _configuration_ must be done by a `Plugin` by overriding | ||||
| one of the methods: | ||||
| * `Plugin#configureTheme` | ||||
| * `Plugin#configureSpansFactory` | ||||
| ::: | ||||
| 
 | ||||
| ## AsyncDrawableLoader | ||||
| 
 | ||||
| Allows loading and displaying of images in markdown. Please note that if one is not specified | ||||
| directly (or via plugin) no images will be displayed. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.asyncDrawableLoader(AsyncDrawableLoader.noOp()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| Currently `Markwon` provides 3 implementations for loading images: | ||||
| * [markwon implementation](/docs/v4/image/) with SVG, GIF, data uri and android_assets support | ||||
| * [based on Picasso](/docs/v4/image-picasso/) | ||||
| * [based on Glide](/docs/v4/image-glide/) | ||||
| 
 | ||||
| ## SyntaxHighlight | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.syntaxHighlight(new SyntaxHighlightNoOp()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| Use [syntax-highlight](/docs/v4/syntax-highlight/) to add syntax highlighting | ||||
| to your application | ||||
| ::: | ||||
| 
 | ||||
| ## LinkResolver | ||||
| 
 | ||||
| React to a link click event. By default `LinkResolverDef` is used, | ||||
| which tries to start an Activity given the `link` argument. If no | ||||
| Activity can handle `link` `LinkResolverDef` silently ignores click event | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.linkResolver(new LinkResolver() { | ||||
|                     @Override | ||||
|                     public void resolve(@NonNull View view, @NonNull String link) { | ||||
|                         // react to link click here | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| Please note that `Markwon` will apply `LinkMovementMethod` to a resulting TextView | ||||
| if there is none registered. if you wish to register own instance of a `MovementMethod` | ||||
| apply it directly to a TextView or use [MovementMethodPlugin](/docs/v4/core/movement-method-plugin.md) | ||||
| ::: | ||||
| 
 | ||||
| ## UrlProcessor | ||||
| 
 | ||||
| Process URLs in your markdown (for links and images). If not provided explicitly,  | ||||
| default **no-op** implementation will be used, which does not modify URLs (keeping them as-is). | ||||
| 
 | ||||
| `Markwon` provides 2 implementations of `UrlProcessor`: | ||||
| * `UrlProcessorRelativeToAbsolute` | ||||
| * `UrlProcessorAndroidAssets` | ||||
| 
 | ||||
| ### UrlProcessorRelativeToAbsolute | ||||
| 
 | ||||
| `UrlProcessorRelativeToAbsolute` can be used to make relative URL absolute. For example if an image is | ||||
| defined like this: `` and `UrlProcessorRelativeToAbsolute` | ||||
| is created with `https://github.com/noties/Markwon/raw/master/` as the base:  | ||||
| `new UrlProcessorRelativeToAbsolute("https://github.com/noties/Markwon/raw/master/")`, | ||||
| then final image will have `https://github.com/noties/Markwon/raw/master/art/image.JPG` | ||||
| as the destination. | ||||
| 
 | ||||
| ### UrlProcessorAndroidAssets | ||||
| 
 | ||||
| `UrlProcessorAndroidAssets` can be used to make processed links to point to Android assets folder. | ||||
| So an image: `` will have `file:///android_asset/art/image.JPG` as the | ||||
| destination. | ||||
| 
 | ||||
| :::tip | ||||
| Please note that `UrlProcessorAndroidAssets` will process only URLs that have no `scheme` information, | ||||
| so a `./art/image.png` will become `file:///android_asset/art/image.JPG` whilst `https://so.me/where.png` | ||||
| will be kept as-is. | ||||
| ::: | ||||
| 
 | ||||
| ## ImageSizeResolver | ||||
| 
 | ||||
| `ImageSizeResolver` controls the size of an image to be displayed.  | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.imageSizeResolver(new ImageSizeResolver() { | ||||
|                     @NonNull | ||||
|                     @Override | ||||
|                     public Rect resolveImageSize(@NonNull AsyncDrawable drawable) { | ||||
|                         final ImageSize imageSize = drawable.getImageSize(); | ||||
|                         return drawable.getResult().getBounds(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| If not provided explicitly, default `ImageSizeResolverDef` implementation will be used. | ||||
| It handles 3 dimension units: | ||||
| * `%` (percent, relative to Canvas width) | ||||
| * `em` (relative to text size) | ||||
| * `px` (absolute size, every dimension that is not `%` or `em` is considered to be _absolute_) | ||||
| 
 | ||||
| ```html | ||||
| <img width="100%"> | ||||
| <img width="2em" height="10px"> | ||||
| <img style="{width: 100%; height: 8em;}"> | ||||
| ``` | ||||
| 
 | ||||
| `ImageSizeResolverDef` keeps the ratio of original image if one of the dimensions is missing. | ||||
| 
 | ||||
| :::warning Height% | ||||
| There is no support for `%` units for `height` dimension. This is due to the fact that | ||||
| height of an TextView in which markdown is displayed is non-stable and changes with time | ||||
| (for example when image is loaded and applied to a TextView it will _increase_ TextView's height), | ||||
| so we will have no point-of-reference from which to _calculate_ image height. | ||||
| ::: | ||||
| 
 | ||||
| :::tip | ||||
| `ImageSizeResolverDef` also takes care for an image to **not** exceed | ||||
| canvas width. If an image has greater width than a TextView Canvas, then | ||||
| image will be _scaled-down_ to fit the canvas. Please note that this rule | ||||
| applies only if image has no sizes specified (`ImageSize == null`). | ||||
| ::: | ||||
							
								
								
									
										141
									
								
								docs/docs/v4/core/core-plugin.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								docs/docs/v4/core/core-plugin.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,141 @@ | ||||
| # Core plugin <Badge text="3.0.0" /> | ||||
| 
 | ||||
| Since <Badge text="3.0.0" /> with introduction of _plugins_, Markwon | ||||
| **core** functionality was moved to a dedicated plugin. | ||||
| 
 | ||||
| ```java | ||||
| CorePlugin.create(); | ||||
| ``` | ||||
| 
 | ||||
| ## Node visitors | ||||
| 
 | ||||
| `CorePlugin` registers these `commonmark-java` node visitors: | ||||
| * `Text` | ||||
| * `StrongEmphasis` | ||||
| * `Emphasis` | ||||
| * `BlockQuote` | ||||
| * `Code` | ||||
| * `Image` | ||||
| * `FencedCodeBlock` | ||||
| * `IndentedCodeBlock` | ||||
| * `BulletList` | ||||
| * `OrderedList` | ||||
| * `ListItem` | ||||
| * `ThematicBreak` | ||||
| * `Heading` | ||||
| * `SoftLineBreak` | ||||
| * `HardLineBreak` | ||||
| * `Paragraph` | ||||
| * `Link` | ||||
| 
 | ||||
| ## Span factories | ||||
| 
 | ||||
| `CorePlugin` adds these `SpanFactory`s: | ||||
| * `StrongEmphasis` | ||||
| * `Emphasis` | ||||
| * `BlockQuote` | ||||
| * `Code` | ||||
| * `FencedCodeBlock` | ||||
| * `IndentedCodeBlock` | ||||
| * `ListItem` | ||||
| * `Heading` | ||||
| * `Link` | ||||
| * `ThematicBreak` | ||||
| 
 | ||||
| 
 | ||||
| :::tip | ||||
| By default `CorePlugin` does not register a `Paragraph` `SpanFactory` but | ||||
| this can be done in your custom plugin: | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 builder.setFactory(Paragraph.class, (configuration, props) ->  | ||||
|                         new ForegroundColorSpan(Color.RED)); | ||||
|             } | ||||
|         }) | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ## Props | ||||
| These props are exported by `CorePlugin` and can be found in `CoreProps`: | ||||
| * `Prop<ListItemType> LIST_ITEM_TYPE` (BULLET | ORDERED) | ||||
| * `Prop<Integer> BULLET_LIST_ITEM_LEVEL` | ||||
| * `Prop<Integer> ORDERED_LIST_ITEM_NUMBER` | ||||
| * `Prop<Integer> HEADING_LEVEL` | ||||
| * `Prop<String> LINK_DESTINATION` | ||||
| * `Prop<Boolean> PARAGRAPH_IS_IN_TIGHT_LIST` | ||||
| 
 | ||||
| :::warning List item type | ||||
| Before <Badge text="3.0.0" /> `Markwon` had 2 distinct lists (bullet and ordered).  | ||||
| Since <Badge text="3.0.0" /> a single `SpanFactory` is used, which internally checks  | ||||
| for `Prop<ListItemType> LIST_ITEM_TYPE`. | ||||
| Beware of this if you would like to override only one of the list types. This is | ||||
| done to correspond to `commonmark-java` implementation. | ||||
| ::: | ||||
| 
 | ||||
| More information about props can be found [here](/docs/v4/core/render-props.md) | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| :::tip Soft line break | ||||
| Since <Badge text="3.0.0" /> Markwon core does not give an option to | ||||
| insert a new line when there is a soft line break in markdown. Instead a | ||||
| custom plugin can be used: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                 builder.on(SoftLineBreak.class, (visitor, softLineBreak) -> | ||||
|                         visitor.forceNewLine()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| :::warning | ||||
| Please note that `CorePlugin` will implicitly set a `LinkMovementMethod` on a TextView | ||||
| if one is not present. If you wish to customize a MovementMethod that is used, apply | ||||
| one manually to a TextView (before applying markdown) or use the [MovementMethodPlugin](/docs/v4/core/movement-method-plugin.md) | ||||
| which accepts a MovementMethod as an argument. | ||||
| ::: | ||||
| 
 | ||||
| ## OnTextAddedListener <Badge text="4.0.0"/> | ||||
| 
 | ||||
| Since `4.0.0` `CorePlugin` provides ability to receive text-added event. This can | ||||
| be useful in order to process raw text (for example to [linkify](/docs/v4/linkify/) it): | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configure(@NonNull Registry registry) { | ||||
|                 registry.require(CorePlugin.class, new Action<CorePlugin>() { | ||||
|                     @Override | ||||
|                     public void apply(@NonNull CorePlugin corePlugin) { | ||||
|                         corePlugin.addOnTextAddedListener(new CorePlugin.OnTextAddedListener() { | ||||
|                             @Override | ||||
|                             public void onTextAdded(@NonNull MarkwonVisitor visitor, @NonNull String text, int start) { | ||||
|                                  | ||||
|                                 // NB text is already added and you are __strongly__ adviced not to | ||||
|                                 // modify visitor here, but only add spans | ||||
|                                 // | ||||
|                                 // this will make all text BLUE | ||||
|                                 visitor.builder().setSpan( | ||||
|                                         new ForegroundColorSpan(Color.BLUE), | ||||
|                                         start, | ||||
|                                         visitor.length() | ||||
|                                 ); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										52
									
								
								docs/docs/v4/core/getting-started.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								docs/docs/v4/core/getting-started.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| # Getting started | ||||
| 
 | ||||
| :::tip Installation | ||||
| Please follow [installation](/docs/v4/install.md) instructions | ||||
| to learn how to add `Markwon` to your project | ||||
| ::: | ||||
| 
 | ||||
| ## Quick one | ||||
| 
 | ||||
| This is the most simple way to set markdown to a `TextView` or any of its siblings: | ||||
| 
 | ||||
| ```java | ||||
| // obtain an instance of Markwon | ||||
| final Markwon markwon = Markwon.create(context); | ||||
| 
 | ||||
| // set markdown | ||||
| markwon.setMarkdown(textView, "**Hello there!**"); | ||||
| ``` | ||||
| 
 | ||||
| The most simple way to obtain markdown to be applied _somewhere_ else: | ||||
| 
 | ||||
| ```java | ||||
| // obtain an instance of Markwon | ||||
| final Markwon markwon = Markwon.create(context); | ||||
| 
 | ||||
| // parse markdown and create styled text | ||||
| final Spanned markdown = markwon.toMarkdown("**Hello there!**"); | ||||
| 
 | ||||
| // use it | ||||
| Toast.makeText(context, markdown, Toast.LENGTH_LONG).show(); | ||||
| ``` | ||||
| 
 | ||||
| ## Longer one | ||||
| 
 | ||||
| With explicit `parse` and `render` methods: | ||||
| 
 | ||||
| ```java | ||||
| // obtain an instance of Markwon | ||||
| final Markwon markwon = Markwon.create(context); | ||||
| 
 | ||||
| // parse markdown to commonmark-java Node | ||||
| final Node node = markwon.parse("Are **you** still there?"); | ||||
| 
 | ||||
| // create styled text from parsed Node | ||||
| final Spanned markdown = markwon.render(node); | ||||
| 
 | ||||
| // use it on a TextView | ||||
| markwon.setParsedMarkdown(textView, markdown); | ||||
| 
 | ||||
| // or a Toast | ||||
| Toast.makeText(context, markdown, Toast.LENGTH_LONG).show(); | ||||
| ``` | ||||
							
								
								
									
										17
									
								
								docs/docs/v4/core/movement-method-plugin.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								docs/docs/v4/core/movement-method-plugin.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| # Movement method plugin | ||||
| 
 | ||||
| `MovementMethodPlugin` can be used to apply a `MovementMethod` to a TextView | ||||
| (important if you have links inside your markdown). By default `CorePlugin` | ||||
| will set a `LinkMovementMethod` on a TextView if one is missing. If you have | ||||
| specific needs for a `MovementMethod` and `LinkMovementMethod` doesn't answer | ||||
| your needs use `MovementMethodPlugin`: | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|         .usePlugin(MovementMethodPlugin.create(ScrollingMovementMethod.getInstance())) | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| If you are having trouble with system `LinkMovementMethod` as an alternative | ||||
| [BetterLinkMovementMethod](https://github.com/saket/Better-Link-Movement-Method) library can be used. | ||||
| ::: | ||||
							
								
								
									
										361
									
								
								docs/docs/v4/core/plugins.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								docs/docs/v4/core/plugins.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,361 @@ | ||||
| # Plugins <Badge text="3.0.0" /> | ||||
| 
 | ||||
| Since <Badge text="3.0.0" /> `MarkwonPlugin` takes the key role in | ||||
| processing and rendering markdown. Even **core** functionaly is abstracted | ||||
| into a `CorePlugin`. So it's still possible to use `Markwon` with a completely | ||||
| own set of plugins. | ||||
| 
 | ||||
| To register a plugin `Markwon.Builder` must be used: | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|     // @since 4.0.0 there is no need to register CorePlugin, as it's registered automatically | ||||
| //    .usePlugin(CorePlugin.create()) | ||||
|     .usePlugin(MyPlugin.create()) | ||||
|     .build(); | ||||
| ``` | ||||
| 
 | ||||
| All the process of transforming _raw_ markdown into a styled text (Spanned) | ||||
| will go through plugins. A plugin can: | ||||
| 
 | ||||
| * [configure plugin registry](#registry) | ||||
| * [configure commonmark-java `Parser`](#parser) | ||||
| * [configure `MarkwonTheme`](#markwontheme) | ||||
| * [configure `AsyncDrawableLoader` (used to display images in markdown)](#images) | ||||
| * [configure `MarkwonConfiguration`](#configuration) | ||||
| * [configure `MarkwonVisitor` (extensible commonmark-java Node visitor)](#visitor) | ||||
| * [configure `MarkwonSpansFactory` (factory to hold spans information for each Node)](#spans-factory) | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| * [process raw input markdown before parsing it](#process-markdown) | ||||
| * [inspect/modify commonmark-java Node after it's been parsed, but before rendering](#inspect-modify-node) | ||||
| * [inspect commonmark-java Node after it's been rendered](#inspect-node-after-render) | ||||
| * [prepare TextView to display markdown _before_ markdown is applied to a TextView](#prepare-textview) | ||||
| * [post-process TextView _after_ markdown was applied](#textview-after-markdown-applied) | ||||
| 
 | ||||
| :::tip | ||||
| if you need to override only few methods of `MarkwonPlugin` (since it is an interface), | ||||
| `AbstractMarkwonPlugin` can be used. | ||||
| ::: | ||||
| 
 | ||||
| ## Registry <Badge text="4.0.0" /> | ||||
| 
 | ||||
| Registry is a special step to pre-configure all registered plugins. It is also | ||||
| used to determine the order of plugins inside `Markwon` instance. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configure(@NonNull Registry registry) { | ||||
| 
 | ||||
|                 final CorePlugin corePlugin = registry.require(CorePlugin.class); | ||||
| 
 | ||||
|                 // or | ||||
|                 registry.require(CorePlugin.class, new Action<CorePlugin>() { | ||||
|                     @Override | ||||
|                     public void apply(@NonNull CorePlugin corePlugin) { | ||||
| 
 | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| More information about registry can be found [here](/docs/v4/core/registry.md) | ||||
| 
 | ||||
| ## Parser | ||||
| 
 | ||||
| For example, let's register a new commonmark-java Parser extension: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureParser(@NonNull Parser.Builder builder) { | ||||
|                 // no need to call `super.configureParser(builder)` | ||||
|                 builder.extensions(Collections.singleton(StrikethroughExtension.create())); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| There are no limitations on what to do with commonmark-java Parser. For more info | ||||
| _what_ can be done please refer to <Link name="commonmark-java" displayName="commonmark-java documentation" />. | ||||
| 
 | ||||
| ## MarkwonTheme | ||||
| 
 | ||||
| Starting <Badge text="3.0.0" /> `MarkwonTheme` represents _core_ theme. Aka theme for | ||||
| things core module knows of. For example it doesn't know anything about `strikethrough` | ||||
| or `tables` (as they belong to different modules). | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureTheme(@NonNull MarkwonTheme.Builder builder) { | ||||
|                 builder | ||||
|                         .codeTextColor(Color.BLACK) | ||||
|                         .codeBackgroundColor(Color.GREEN); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| `CorePlugin` has special handling - it will be added automatically  | ||||
| when `Markwon.builder(Context)` method is used. If you wish to create  | ||||
| Markwon instance _without_ CorePlugin registered -  | ||||
| use `Markwon.builderNoCore(Context)` method instead | ||||
| ::: | ||||
| 
 | ||||
| More information about `MarkwonTheme` can be found [here](/docs/v4/core/theme.md). | ||||
| 
 | ||||
| 
 | ||||
| ## Configuration | ||||
| 
 | ||||
| `MarkwonConfiguration` is a set of common tools that are used by different parts | ||||
| of `Markwon`. It allows configurations of these: | ||||
| 
 | ||||
| * `AsyncDrawableLoader` (image loading) | ||||
| * `SyntaxHighlight` (highlighting code blocks) | ||||
| * `LinkResolver` (opens links in markdown) | ||||
| * `UrlProcessor` (process URLs in markdown for both links and images) | ||||
| * `ImageSizeResolver` (resolve image sizes, like `fit-to-canvas`, etc) | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                 builder.linkResolver(new LinkResolverDef()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| More information about `MarkwonConfiguration` can be found [here](/docs/v4/core/configuration.md) | ||||
| 
 | ||||
| 
 | ||||
| ## Visitor | ||||
| 
 | ||||
| `MarkwonVisitor` <Badge text="3.0.0" /> is commonmark-java Visitor that allows | ||||
| configuration of how each Node is visited. There is no longer need to create | ||||
| own subclass of Visitor and override required methods (like in `2.x.x` versions). | ||||
| `MarkwonVisitor` also allows registration of Nodes, that `core` module knows | ||||
| nothing about (instead of relying on `visit(CustomNode)` method)). | ||||
| 
 | ||||
| For example, let's add `strikethrough` Node visitor: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                 // please note that strike-through parser extension must be registered | ||||
|                 // in order to receive such callback | ||||
|                 builder | ||||
|                         .on(Strikethrough.class, new MarkwonVisitor.NodeVisitor<Strikethrough>() { | ||||
|                             @Override | ||||
|                             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Strikethrough strikethrough) { | ||||
|                                 final int length = visitor.length(); | ||||
|                                 visitor.visitChildren(strikethrough); | ||||
|                                 visitor.setSpansForNodeOptional(strikethrough, length); | ||||
|                             } | ||||
|                         }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| `MarkwonVisitor` also allows _overriding_ already registered nodes. For example, | ||||
| you can disable `Heading` Node rendering: | ||||
| 
 | ||||
| ```java | ||||
| builder.on(Heading.class, null); | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| More information about `MarkwonVisitor` can be found [here](/docs/v4/core/visitor.md) | ||||
| 
 | ||||
| 
 | ||||
| ## Spans Factory | ||||
| 
 | ||||
| `MarkwonSpansFactory` <Badge text="3.0.0" /> is an abstract factory (factory that produces other factories) | ||||
| for spans that `Markwon` uses. It controls what spans to use for certain Nodes. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 // override emphasis factory to make all emphasis nodes underlined | ||||
|                 builder.setFactory(Emphasis.class, new SpanFactory() { | ||||
|                     @Override | ||||
|                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                         return new UnderlineSpan(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| `SpanFactory` allows to return an _array_ of spans to apply multiple spans | ||||
| for a Node: | ||||
| 
 | ||||
| ```java | ||||
| @Override | ||||
| public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|     // make underlined and set text color to red | ||||
|     return new Object[]{ | ||||
|             new UnderlineSpan(), | ||||
|             new ForegroundColorSpan(Color.RED) | ||||
|     }; | ||||
| } | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| More information about spans factory can be found [here](/docs/v4/core/spans-factory.md) | ||||
| 
 | ||||
| 
 | ||||
| ## Process markdown | ||||
| 
 | ||||
| A plugin can be used to _pre-process_ input markdown (this will be called before _parsing_): | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @NonNull | ||||
|             @Override | ||||
|             public String processMarkdown(@NonNull String markdown) { | ||||
|                 return markdown.replaceAll("foo", "bar"); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## Inspect/modify Node | ||||
| 
 | ||||
| A plugin can inspect/modify commonmark-java Node _before_ it's being rendered. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void beforeRender(@NonNull Node node) { | ||||
| 
 | ||||
|                 // for example inspect it with custom visitor | ||||
|                 node.accept(new MyVisitor()); | ||||
| 
 | ||||
|                 // or modify (you know what you are doing, right?) | ||||
|                 node.appendChild(new Text("Appended")); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## Inspect Node after render | ||||
| 
 | ||||
| A plugin can inspect commonmark-java Node after it's been rendered. | ||||
| Modifying Node at this point makes not much sense (it's already been | ||||
| rendered and all modifications won't change anything). But this method can be used, | ||||
| for example, to clean-up some internal state (after rendering). Generally | ||||
| speaking, a plugin must be stateless, but if it cannot, then this method is | ||||
| the best place to clean-up. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) { | ||||
|                 cleanUp(); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## Prepare TextView | ||||
| 
 | ||||
| A plugin can _prepare_ a TextView before markdown is applied. For example `images` | ||||
| unschedules all previously scheduled `AsyncDrawableSpans` (if any) here. This way | ||||
| when new markdown (and set of Spannables) arrives, previous set won't be kept in | ||||
| memory and could be garbage-collected. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { | ||||
|                 // clean-up previous | ||||
|                 AsyncDrawableScheduler.unschedule(textView); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## TextView after markdown applied | ||||
| 
 | ||||
| A plugin will receive a callback _after_ markdown is applied to a TextView. | ||||
| For example `images` uses this callback to schedule new set of Spannables. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void afterSetText(@NonNull TextView textView) { | ||||
|                 AsyncDrawableScheduler.schedule(textView); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| Please note that unlike `#beforeSetText`, `#afterSetText` won't receive | ||||
| `Spanned` markdown. This happens because at this point spans must be | ||||
| queried directly from a TextView. | ||||
| ::: | ||||
| 
 | ||||
| ## What happens underneath | ||||
| 
 | ||||
| Here is what happens inside `Markwon` when `setMarkdown` method is called: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.create(context); | ||||
| 
 | ||||
| // warning: pseudo-code | ||||
| 
 | ||||
| // 0. each plugin will be called to _pre-process_ raw input markdown | ||||
| rawInput = plugins.reduce(rawInput, (input, plugin) -> plugin.processMarkdown(input)); | ||||
| 
 | ||||
| // 1. after input is processed it's being parsed to a Node | ||||
| node = parser.parse(rawInput); | ||||
| 
 | ||||
| // 2. each plugin will be able to inspect or manipulate resulting Node | ||||
| //  before rendering | ||||
| plugins.forEach(plugin -> plugin.beforeRender(node)); | ||||
| 
 | ||||
| // 3. node is being visited by a visitor | ||||
| node.accept(visitor); | ||||
| 
 | ||||
| // 4. each plugin will be called after node is being visited (aka rendered) | ||||
| plugins.forEach(plugin -> plugin.afterRender(node, visitor)); | ||||
| 
 | ||||
| // 5. styled markdown ready at this point | ||||
| final Spanned markdown = visitor.markdown(); | ||||
| 
 | ||||
| // NB, points 6-8 are applied **only** if markdown is set to a TextView | ||||
| 
 | ||||
| // 6. each plugin will be called before styled markdown is applied to a TextView | ||||
| plugins.forEach(plugin -> plugin.beforeSetText(textView, markdown)); | ||||
| 
 | ||||
| // 7. markdown is applied to a TextView | ||||
| textView.setText(markdown); | ||||
| 
 | ||||
| // 8. each plugin will be called after markdown is applied to a TextView | ||||
| plugins.forEach(plugin -> plugin.afterSetText(textView)); | ||||
| ``` | ||||
							
								
								
									
										97
									
								
								docs/docs/v4/core/registry.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								docs/docs/v4/core/registry.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| # Registry <Badge text="4.0.0" /> | ||||
| 
 | ||||
| `Registry` allows to pre-configure other plugins and/or declare a dependency on a plugin, | ||||
| which also will modify internal order of plugins inside a `Markwon` instance. | ||||
| 
 | ||||
| For example, you have a configurable plugin: | ||||
| 
 | ||||
| ```java | ||||
| public class MyPlugin extends AbstractMarkwonPlugin { | ||||
|      | ||||
|     private boolean enabled; | ||||
| 
 | ||||
|     public boolean enabled() { | ||||
|         return enabled; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public MyPlugin enabled(boolean enabled) { | ||||
|         this.enabled = enabled; | ||||
|         return this; | ||||
|     } | ||||
|      | ||||
|     {...} | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| and other plugin that needs to access `MyPlugin` or modify/configure it: | ||||
| 
 | ||||
| ```java | ||||
| public class MyOtherPlugin extends AbstractMarkwonPlugin { | ||||
|     @Override | ||||
|     public void configure(@NonNull Registry registry) { | ||||
|         registry.require(MyPlugin.class, new Action<MyPlugin>() { | ||||
|             @Override | ||||
|             public void apply(@NonNull MyPlugin myPlugin) { | ||||
|                 myPlugin.enabled(false); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new MyOtherPlugin()) | ||||
|         .usePlugin(new MyPlugin()) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| _Internal_ plugins order (in this case) will be: | ||||
| * `CorePlugin` (added automatically and always the first one) | ||||
| * `MyPlugin` (was required by `MyOtherPlugin`) | ||||
| * `MyOtherPlugin` | ||||
| 
 | ||||
| :::tip | ||||
| There is no need to _require_ `CorePlugin` as it will be the first one inside | ||||
| `Markwon` instance. | ||||
| ::: | ||||
| 
 | ||||
| The order matters if you want to _override_ some plugin. For example, `CoolPlugin` | ||||
| adds a `SpanFactory` for a `Cool` markdown node. Other `NotCoolPlugin` wants to | ||||
| use a different `SpanFactory`, then: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(CoolPlugin.create()) | ||||
|         .usePlugin(new NotCoolPlugin() { | ||||
|              | ||||
|             @Override | ||||
|             public void configure(@NonNull MarkwonPlugin.Registry registry) { | ||||
|                 registry.require(CoolPlugin.class); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 builder.setFactory(Cool.class, new NotCoolSpanFactory()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| All `require` calls to the `Registry` will also validate at runtime that | ||||
| _required_ plugins are registered. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configure(@NonNull Registry registry) { | ||||
|                 // will throw an exception if `NotPresentPlugin` is not present | ||||
|                 registry.require(NotPresentPlugin.class); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										75
									
								
								docs/docs/v4/core/render-props.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								docs/docs/v4/core/render-props.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| # RenderProps <Badge text="3.0.0" /> | ||||
| 
 | ||||
| `RenderProps` encapsulates passing arguments from a node visitor to a node renderer. | ||||
| Without hardcoding arguments into an API method calls. | ||||
| 
 | ||||
| `RenderProps` is the state collection for `Props` that are set by a node visitor and | ||||
| retrieved by a node renderer. | ||||
| 
 | ||||
| ```java | ||||
| public class Prop<T> { | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static <T> Prop<T> of(@NonNull String name) { | ||||
|         return new Prop<>(name); | ||||
|     } | ||||
| 
 | ||||
|     /* ... */ | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| For example `CorePlugin` defines a _Heading level_ prop (inside `CoreProps` class): | ||||
| 
 | ||||
| ```java | ||||
| public static final Prop<Integer> HEADING_LEVEL = Prop.of("heading-level"); | ||||
| ``` | ||||
| 
 | ||||
| Then CorePlugin registers a `Heading` node visitor and applies heading value: | ||||
| 
 | ||||
| ```java | ||||
| @Override | ||||
| public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|     builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() { | ||||
|         @Override | ||||
|         public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||
|              | ||||
|             /* Heading node handling logic */ | ||||
| 
 | ||||
|             // set heading level | ||||
|             CoreProps.HEADING_LEVEL.set(visitor.renderProps(), heading.getLevel()); | ||||
|              | ||||
|             // a helper method to apply span(s) for a node  | ||||
|             // (internally obtains a SpanFactory for Heading or silently ignores | ||||
|             // this call if no factory for a Heading is registered) | ||||
|             visitor.setSpansForNodeOptional(heading, start); | ||||
| 
 | ||||
|             /* Heading node handling logic */ | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| And finally `HeadingSpanFactory` (which is also registered by `CorePlugin`): | ||||
| 
 | ||||
| ```java | ||||
| public class HeadingSpanFactory implements SpanFactory { | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|         return new HeadingSpan( | ||||
|                 configuration.theme(), | ||||
|                 CoreProps.HEADING_LEVEL.require(props) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| `Prop<T>` has these methods: | ||||
| 
 | ||||
| * `@Nullable T get(RenderProps)` - returns value stored in RenderProps or `null` if none is present | ||||
| * `@NonNull T get(RenderProps, @NonNull T defValue)` - returns value stored in RenderProps or default value (this method always return non-null value) | ||||
| * `@NonNull T require(RenderProps)` - returns value stored in RenderProps or _throws an exception_ if none is present | ||||
| * `void set(RenderProps, @Nullable T value)` - updates value stored in RenderProps, passing `null` as value is the same as calling `clear` | ||||
| * `void clear(RenderProps)` - clears value stored in RenderProps | ||||
							
								
								
									
										103
									
								
								docs/docs/v4/core/spans-factory.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								docs/docs/v4/core/spans-factory.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| # Spans Factory | ||||
| 
 | ||||
| Starting with <Badge text="3.0.0" /> `MarkwonSpansFactory` controls what spans are displayed | ||||
| for markdown nodes. | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 // passing null as second argument will remove previously added  | ||||
|                 // factory for the Link node | ||||
|                 builder.setFactory(Link.class, null); | ||||
|             } | ||||
|         }); | ||||
| ``` | ||||
| 
 | ||||
| ## SpanFactory | ||||
| 
 | ||||
| In order to create a _generic_ interface for all possible Nodes, a `SpanFactory` | ||||
| was added: | ||||
| 
 | ||||
| ```java | ||||
| builder.setFactory(Link.class, new SpanFactory() { | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|         return null; | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| All possible arguments are passed via [RenderProps](/docs/v4/core/render-props.md): | ||||
| 
 | ||||
| ```java | ||||
| builder.setFactory(Link.class, new SpanFactory() { | ||||
|     @Override | ||||
|     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|         final String href = CoreProps.LINK_DESTINATION.require(props); | ||||
|         return new LinkSpan(configuration.theme(), href, configuration.linkResolver()); | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| `SpanFactory` allows returning `null` for a certain span (no span will be applied). | ||||
| Or an array of spans (you _can_ go deeper): | ||||
| 
 | ||||
| ```java | ||||
| builder.setFactory(Link.class, new SpanFactory() { | ||||
|     @Override | ||||
|     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|         return new Object[]{ | ||||
|                 new LinkSpan( | ||||
|                         configuration.theme(), | ||||
|                         CoreProps.LINK_DESTINATION.require(props), | ||||
|                         configuration.linkResolver()), | ||||
|                 new ForegroundColorSpan(Color.RED) | ||||
|         }; | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Since <Badge text="3.0.1" /> you can _add_ multiple `SpanFactory` for a single node: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 // this factory will be used _along_ with all other factories for specified node | ||||
|                 builder.addFactory(Code.class, new SpanFactory() { | ||||
|                     @Override | ||||
|                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                         return new ForegroundColorSpan(Color.GREEN); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| If you wish to inspect existing factory you can use: | ||||
| * `builder#getFactory()` -> returns registered factory or `null` | ||||
| * `builder#requireFactory()` -> returns registered factory or throws <Badge text="3.0.1" /> | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 final SpanFactory codeFactory = builder.requireFactory(Code.class); | ||||
|                 final SpanFactory linkFactory = builder.getFactory(Link.class); | ||||
|                 if (linkFactory != null) { | ||||
|                     {...} | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										187
									
								
								docs/docs/v4/core/theme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								docs/docs/v4/core/theme.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | ||||
| # Theme | ||||
| 
 | ||||
| Here is the list of properties that can be configured via `MarkwonTheme.Builder` class.  | ||||
| 
 | ||||
| :::tip | ||||
| Starting with <Badge text="3.0.0" /> there is no need to manually construct a `MarkwonTheme`. | ||||
| Instead a `Plugin` should be used: | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureTheme(@NonNull MarkwonTheme.Builder builder) { | ||||
|                 builder | ||||
|                         .codeTextColor(Color.BLACK) | ||||
|                         .codeBackgroundColor(Color.GREEN); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ## Link color | ||||
| 
 | ||||
| Controls the color of a [link](#) | ||||
| 
 | ||||
| <ThemeProperty name="linkColor" type="@ColorInt int" defaults="Default link color of a context where markdown is displayed <sup>*</sup>" /> | ||||
| 
 | ||||
| <sup>*</sup> `TextPaint#linkColor` will be used to determine linkColor of a context | ||||
| 
 | ||||
| ## Block margin | ||||
| 
 | ||||
| Starting margin before text content for the: | ||||
| * lists | ||||
| * blockquotes | ||||
| * task lists | ||||
| 
 | ||||
| <ThemeProperty name="blockMargin" type="@Px int" defaults="24dp" /> | ||||
| 
 | ||||
| ## Block quote | ||||
| 
 | ||||
| Customizations for the `blockquote` stripe | ||||
| 
 | ||||
| > Quote | ||||
| 
 | ||||
| ### Stripe width | ||||
| 
 | ||||
| Width of a blockquote stripe | ||||
| 
 | ||||
| <ThemeProperty name="blockQuoteWidth" type="@Px int" defaults="1/4 of the <a href='#block-margin'>block margin</a>" /> | ||||
| 
 | ||||
| ### Stripe color | ||||
| 
 | ||||
| Color of a blockquote stripe | ||||
| 
 | ||||
| <ThemeProperty name="blockQuoteColor" type="@ColorInt int" defaults="textColor with <code>25</code> (0-255) alpha value" /> | ||||
| 
 | ||||
| ## List | ||||
| 
 | ||||
| ### List item color | ||||
| 
 | ||||
| Controls the color of a list item. For ordered list: leading number, | ||||
| for unordered list: bullet. | ||||
| 
 | ||||
| * UL | ||||
| 1. OL | ||||
| 
 | ||||
| <ThemeProperty name="listItemColor" type="@ColorInt int" defaults="Text color" /> | ||||
| 
 | ||||
| ### Bullet item stroke width | ||||
| 
 | ||||
| Border width of a bullet list item (level 2) | ||||
| 
 | ||||
| * First | ||||
| * * Second | ||||
| * * * Third | ||||
| 
 | ||||
| <ThemeProperty name="bulletListItemStrokeWidth" type="@Px int" defaults="Stroke width of TextPaint" /> | ||||
| 
 | ||||
| ### Bullet width | ||||
| 
 | ||||
| The width of the bullet item | ||||
| 
 | ||||
| * First | ||||
|   * Second | ||||
|     * Third | ||||
| 
 | ||||
| <ThemeProperty name="bulletWidth" type="@Px int" defaults="min(<a href='#block-margin'>blockMargin</a>, lineHeight) / 2" /> | ||||
| 
 | ||||
| ## Code | ||||
| 
 | ||||
| ### Inline code text color | ||||
| 
 | ||||
| The color of the `code` content | ||||
| 
 | ||||
| <ThemeProperty name="codeTextColor" type="@ColorInt int" defaults="Content text color" /> | ||||
| 
 | ||||
| ### Inline code background color | ||||
| 
 | ||||
| The color of `background` of a code content | ||||
| 
 | ||||
| <ThemeProperty name="codeBackgroundColor" type="@ColorInt int" defaults="<a href='#inline-code-text-color'>inline code text color</a> with 25 (0-255) alpha" /> | ||||
| 
 | ||||
| ### Block code text color | ||||
| 
 | ||||
| ``` | ||||
| The color of code block text | ||||
| ``` | ||||
| 
 | ||||
| <ThemeProperty name="codeBlockTextColor" type="@ColorInt int" defaults="<a href='#inline-code-text-color'>inline code text color</a>" /> | ||||
| 
 | ||||
| ### Block code background color | ||||
| 
 | ||||
| ``` | ||||
| The color of background of code block text | ||||
| ``` | ||||
| 
 | ||||
| <ThemeProperty name="codeBlockBackgroundColor" type="@ColorInt int" defaults="<a href='#inline-code-background-color'>inline code background color</a>" /> | ||||
| 
 | ||||
| ### Block code leading margin | ||||
| 
 | ||||
| Leading margin for the block code content | ||||
| 
 | ||||
| <ThemeProperty name="codeMultilineMargin" type="@Px int" defaults="8dip" /> | ||||
| 
 | ||||
| ### Code typeface | ||||
| 
 | ||||
| Typeface of code content | ||||
| 
 | ||||
| <ThemeProperty name="codeTypeface" type="android.graphics.Typeface" defaults="Typeface.MONOSPACE" /> | ||||
| 
 | ||||
| ### Block code typeface <Badge text="3.0.0" /> | ||||
| 
 | ||||
| Typeface of block code content | ||||
| 
 | ||||
| <ThemeProperty name="codeBlockTypeface" type="android.graphics.Typeface" defaults="<code>codeTypeface</code> if set or Typeface.MONOSPACE" /> | ||||
| 
 | ||||
| ### Code text size | ||||
| 
 | ||||
| Text size of code content | ||||
| 
 | ||||
| <ThemeProperty name="codeTextSize" type="@Px int" defaults="(Content text size) * 0.87 if no custom <a href='#code-typeface'>Typeface</a> was set, otherwise (content text size)" /> | ||||
| 
 | ||||
| ### Block code text size <Badge text="3.0.0" /> | ||||
| 
 | ||||
| Text size of block code content | ||||
| 
 | ||||
| <ThemeProperty name="codeBlockTextSize" type="@Px int" defaults="<code>codeTextSize</code> if set or (content text size) * 0.87 if no custom <a href='#code-typeface'>Typeface</a> was set, otherwise (content text size)" /> | ||||
| 
 | ||||
| ## Heading | ||||
| 
 | ||||
| ### Break height | ||||
| 
 | ||||
| The height of a brake under H1 & H2 | ||||
| 
 | ||||
| <ThemeProperty name="headingBreakHeight" type="@Px int" defaults="Stroke width of context TextPaint" /> | ||||
| 
 | ||||
| ### Break color | ||||
| 
 | ||||
| The color of a brake under H1 & H2 | ||||
| 
 | ||||
| <ThemeProperty name="headingBreakColor" type="@ColorInt int" defaults="(text color) with 75 (0-255) alpha" /> | ||||
| 
 | ||||
| ### Typeface <Badge text="1.1.0" /> | ||||
| 
 | ||||
| The typeface of heading elements | ||||
| 
 | ||||
| <ThemeProperty name="headingTypeface" type="android.graphics.Typeface" defaults="default text Typeface" /> | ||||
| 
 | ||||
| ### Text size <Badge text="1.1.0" /> | ||||
| 
 | ||||
| Array of heading text sizes _ratio_ that is applied to text size | ||||
| 
 | ||||
| <ThemeProperty name="headingTextSizeMultipliers" type="float[]" defaults="<code>{2.F, 1.5F, 1.17F, 1.F, .83F, .67F}</code> (HTML spec)" /> | ||||
| 
 | ||||
| ## Thematic break | ||||
| 
 | ||||
| ### Color | ||||
| 
 | ||||
| Color of a thematic break | ||||
| 
 | ||||
| <ThemeProperty name="thematicBreakColor" type="@ColorInt int" defaults="(text color) with 25 (0-255) alpha" /> | ||||
| 
 | ||||
| ### Height | ||||
| 
 | ||||
| Height of a thematic break | ||||
| 
 | ||||
| <ThemeProperty name="thematicBreakHeight" type="@Px int" defaults="Stroke width of context TextPaint" /> | ||||
							
								
								
									
										73
									
								
								docs/docs/v4/core/visitor.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								docs/docs/v4/core/visitor.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| # Visitor | ||||
| 
 | ||||
| Starting with <Badge text="3.0.0" /> _visiting_ of parsed markdown | ||||
| nodes does not require creating own instance of commonmark-java `Visitor`, | ||||
| instead a composable/configurable `MarkwonVisitor` is used. | ||||
| 
 | ||||
| ## Visitor.Builder | ||||
| There is no need to create own instance of `MarkwonVisitor.Builder` as | ||||
| it is done by `Markwon` itself. One still can configure it as one wishes: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(contex) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                 builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { | ||||
|                     @Override | ||||
|                     public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { | ||||
|                         visitor.forceNewLine(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| `MarkwonVisitor` encapsulates most of the functionality of rendering parsed markdown. | ||||
| 
 | ||||
| It holds rendering configuration: | ||||
| * `MarkwonVisitor#configuration` - getter for current [MarkwonConfiguration](/docs/v4/core/configuration.md) | ||||
| * `MarkwonVisitor#renderProps` - getter for current [RenderProps](/docs/v4/core/render-props.md) | ||||
| * `MarkwonVisitor#builder` - getter for current `SpannableBuilder` | ||||
| 
 | ||||
| It contains also a number of utility functions: | ||||
| * `visitChildren(Node)` - will visit all children of supplied Node | ||||
| * `hasNext(Node)` - utility function to check if supplied Node has a Node after it (useful for white-space management, so there should be no blank new line after last BlockNode) | ||||
| * `ensureNewLine` - will insert a new line at current `SpannableBuilder` position only if current (last) character is not a new-line | ||||
| * `forceNewLine` - will insert a new line character without any condition checking | ||||
| * `length` - helper function to call `visitor.builder().length()`, returns current length of `SpannableBuilder` | ||||
| * `clear` - will clear state for `RenderProps` and `SpannableBuilder`, this is done by `Markwon` automatically after each render call | ||||
| 
 | ||||
| And some utility functions to control the spans: | ||||
| * `setSpans(int start, Object spans)` - will apply supplied `spans` on `SpannableBuilder` starting at `start` position and ending at `SpannableBuilder#length`. `spans` can be `null` (no spans will be applied) or an array of spans (each span of this array will be applied) | ||||
| * `setSpansForNodeOptional(N node, int start)` - helper method to set spans for specified `node` (internally obtains `SpanFactory` for that node and uses it to apply spans) | ||||
| * `setSpansForNode(N node, int start)` - almost the same as `setSpansForNodeOptional` but instead of silently ignoring call if none `SpanFactory` is registered, this method will throw an exception. | ||||
| 
 | ||||
| ```java | ||||
| @Override | ||||
| public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|     builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() { | ||||
|         @Override | ||||
|         public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||
| 
 | ||||
|             // or just `visitor.length()` | ||||
|             final int start = visitor.builder().length(); | ||||
| 
 | ||||
|             visitor.visitChildren(heading); | ||||
| 
 | ||||
|             // or just `visitor.setSpansForNodeOptional(heading, start)` | ||||
|             final SpanFactory factory = visitor.configuration().spansFactory().get(heading.getClass()); | ||||
|             if (factory != null) { | ||||
|                 visitor.setSpans(start, factory.getSpans(visitor.configuration(), visitor.renderProps())); | ||||
|             } | ||||
|              | ||||
|             if (visitor.hasNext(heading)) { | ||||
|                 visitor.ensureNewLine(); | ||||
|                 visitor.forceNewLine(); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										52
									
								
								docs/docs/v4/ext-latex/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								docs/docs/v4/ext-latex/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| # LaTeX extension | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'ext-latex'" /> | ||||
| 
 | ||||
| This is an extension that will help you display LaTeX formulas in your markdown. | ||||
| Syntax is pretty simple: pre-fix and post-fix your latex with `$$` (double dollar sign). | ||||
| `$$` should be the first characters in a line. | ||||
| 
 | ||||
| ```markdown | ||||
| $$ | ||||
| \\text{A long division \\longdiv{12345}{13} | ||||
| $$ | ||||
| ``` | ||||
| 
 | ||||
| ```markdown | ||||
| $$\\text{A long division \\longdiv{12345}{13}$$ | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|     .use(JLatexMathPlugin.create(textSize)) | ||||
|     .build(); | ||||
| ``` | ||||
| 
 | ||||
| This extension uses [jlatexmath-android](https://github.com/noties/jlatexmath-android) artifact to create LaTeX drawable.  | ||||
| 
 | ||||
| ## Config | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(JLatexMathPlugin.create(textSize, new BuilderConfigure() { | ||||
|             @Override | ||||
|             public void configureBuilder(@NonNull Builder builder) { | ||||
|                 builder | ||||
|                         .align(JLatexMathDrawable.ALIGN_CENTER) | ||||
|                         .fitCanvas(true) | ||||
|                         .padding(paddingPx) | ||||
|                         // @since 4.0.0 - horizontal and vertical padding | ||||
|                         .padding(paddingHorizontalPx, paddingVerticalPx) | ||||
|                         // @since 4.0.0 - change to provider | ||||
|                         .backgroundProvider(() -> new MyDrawable())) | ||||
|                         // @since 4.0.0 - optional, by default cached-thread-pool will be used | ||||
|                         .executorService(Executors.newCachedThreadPool()); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| :::tip | ||||
| Since <Badge text="4.0.0" /> `JLatexMathPlugin` operates independently of `ImagesPlugin` | ||||
| ::: | ||||
							
								
								
									
										29
									
								
								docs/docs/v4/ext-strikethrough/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								docs/docs/v4/ext-strikethrough/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| # Strikethrough extension | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'ext-strikethrough'" /> | ||||
| 
 | ||||
| This module adds `strikethrough` functionality to `Markwon` via `StrikethroughPlugin`: | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|     .usePlugin(StrikethroughPlugin.create()) | ||||
| ``` | ||||
| 
 | ||||
| This plugin registers `SpanFactory` for `Strikethrough` node, so it's possible to customize Strikethrough Span that is used in rendering: | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|         .usePlugin(StrikethroughPlugin.create()) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 builder.setFactory(Strikethrough.class, new SpanFactory() { | ||||
|                     @Override | ||||
|                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                         // will use Underline span instead of Strikethrough | ||||
|                         return new UnderlineSpan(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
| ``` | ||||
							
								
								
									
										99
									
								
								docs/docs/v4/ext-tables/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								docs/docs/v4/ext-tables/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| # Tables extension | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'ext-tables'" /> | ||||
| 
 | ||||
| This extension adds support for GFM tables. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         // create default instance of TablePlugin | ||||
|         .usePlugin(TablePlugin.create(context)) | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| final TableTheme tableTheme = TableTheme.builder() | ||||
|         .tableBorderColor(Color.RED) | ||||
|         .tableBorderWidth(0) | ||||
|         .tableCellPadding(0) | ||||
|         .tableHeaderRowBackgroundColor(Color.BLACK) | ||||
|         .tableEvenRowBackgroundColor(Color.GREEN) | ||||
|         .tableOddRowBackgroundColor(Color.YELLOW) | ||||
|         .build(); | ||||
| 
 | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(TablePlugin.create(tableTheme)) | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|         .usePlugin(TablePlugin.create(builder -> | ||||
|                 builder | ||||
|                         .tableBorderColor(Color.RED) | ||||
|                         .tableBorderWidth(0) | ||||
|                         .tableCellPadding(0) | ||||
|                         .tableHeaderRowBackgroundColor(Color.BLACK) | ||||
|                         .tableEvenRowBackgroundColor(Color.GREEN) | ||||
|                         .tableOddRowBackgroundColor(Color.YELLOW) | ||||
| )) | ||||
| ``` | ||||
| 
 | ||||
| Please note, that _by default_ tables have limitations. For example, there is no support | ||||
| for images inside table cells. And table contents won't be copied to clipboard if a TextView | ||||
| has such functionality. Table will always take full width of a TextView in which it is displayed. | ||||
| All columns will always be of the same width. So, _default_ implementation provides basic | ||||
| functionality which can answer some needs. These all come from the limited nature of the TextView | ||||
| to display such content. | ||||
| 
 | ||||
| In order to provide full-fledged experience, tables must be displayed in a special widget. | ||||
| Since version `3.0.0` Markwon provides a special artifact `markwon-recycler` that allows | ||||
| to render markdown in a set of widgets in a RecyclerView. It also gives ability to change | ||||
| display widget form TextView to any other. | ||||
| 
 | ||||
| ```java | ||||
| final Table table = Table.parse(Markwon, TableBlock); | ||||
| myTableWidget.setTable(table); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| To take advantage of this functionality and render tables without limitations (including | ||||
| horizontally scrollable layout when its contents exceed screen width), refer to [recycler-table](/docs/v4/recycler-table/) | ||||
| module documentation that adds support for rendering `TableBlock` markdown node inside Android-native `TableLayout` widget. | ||||
| ::: | ||||
| 
 | ||||
| ## Theme | ||||
| 
 | ||||
| ### Cell padding | ||||
| 
 | ||||
| Padding inside a table cell | ||||
| 
 | ||||
| <ThemeProperty name="tableCellPadding" type="@Px int" defaults="0" /> | ||||
| 
 | ||||
| ### Border color | ||||
| 
 | ||||
| The color of table borders | ||||
| 
 | ||||
| <ThemeProperty name="tableBorderColor" type="@ColorInt int" defaults="(text color) with 75 (0-255) alpha" /> | ||||
| 
 | ||||
| ### Border width | ||||
| 
 | ||||
| The width of table borders | ||||
| 
 | ||||
| <ThemeProperty name="tableBorderWidth" type="@Px int" defaults="Stroke with of context TextPaint" /> | ||||
| 
 | ||||
| ### Odd row background | ||||
| 
 | ||||
| Background of an odd table row | ||||
| 
 | ||||
| <ThemeProperty name="tableOddRowBackgroundColor" type="@ColorInt int" defaults="(text color) with 22 (0-255) alpha" /> | ||||
| 
 | ||||
| ### Even row background <Badge text="1.1.1" /> | ||||
| 
 | ||||
| Background of an even table row | ||||
| 
 | ||||
| <ThemeProperty name="tableEventRowBackgroundColor" type="@ColorInt int" defaults="0" /> | ||||
| 
 | ||||
| ### Header row background <Badge text="1.1.1" /> | ||||
| 
 | ||||
| Background of header table row | ||||
| 
 | ||||
| <ThemeProperty name="tableHeaderRowBackgroundColor" type="@ColorInt int" defaults="0" /> | ||||
							
								
								
									
										146
									
								
								docs/docs/v4/ext-tasklist/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								docs/docs/v4/ext-tasklist/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | ||||
| # Task list extension | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'ext-tasklist'" /> | ||||
| 
 | ||||
| Adds support for GFM (Github-flavored markdown) task-lists: | ||||
| 
 | ||||
| ```java | ||||
| Markwon.builder(context) | ||||
|         .usePlugin(TaskListPlugin.create(context)); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Create a default instance of `TaskListPlugin` with `TaskListDrawable` initialized to use | ||||
| `android.R.attr.textColorLink` as primary color and `android.R.attr.colorBackground` as background | ||||
| ```java | ||||
| TaskListPlugin.create(context); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Create an instance of `TaskListPlugin` with exact color values to use: | ||||
| ```java | ||||
| // obtain color values | ||||
| final int checkedFillColor = /* */; | ||||
| final int normalOutlineColor = /* */; | ||||
| final int checkMarkColor = /* */; | ||||
| 
 | ||||
| TaskListPlugin.create(checkedFillColor, normalOutlineColor, checkMarkColor); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Specify own drawable for a task list item: | ||||
| 
 | ||||
| ```java | ||||
| // obtain drawable | ||||
| final Drawable drawable = /* */; | ||||
| 
 | ||||
| TaskListPlugin.create(drawable); | ||||
| ``` | ||||
| 
 | ||||
| :::warning | ||||
| Please note that custom drawable for a task list item must correctly handle state | ||||
| in order to display done/not-done: | ||||
| 
 | ||||
| ```java | ||||
| public class MyTaskListDrawable extends Drawable { | ||||
| 
 | ||||
|     private boolean isChecked; | ||||
| 
 | ||||
|     @Override | ||||
|     public void draw(@NonNull Canvas canvas) { | ||||
|         // draw accordingly to the isChecked value | ||||
|     } | ||||
|      | ||||
|     /* implementation omitted */ | ||||
| 
 | ||||
|     @Override | ||||
|     protected boolean onStateChange(int[] state) { | ||||
|         final boolean isChecked = contains(state, android.R.attr.state_checked); | ||||
|         final boolean result = this.isChecked != isChecked; | ||||
|         if (result) { | ||||
|             this.isChecked = isChecked; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     private static boolean contains(@Nullable int[] states, int value) { | ||||
|         if (states != null) { | ||||
|             for (int state : states) { | ||||
|                 if (state == value) { | ||||
|                     // NB return here | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ## Task list mutation | ||||
| 
 | ||||
| It is possible to mutate task list item state (toggle done/not-done). But note | ||||
| that `Markwon` won't handle state change internally by any means and this change | ||||
| is merely a visual one. If you need to persist state of a task list | ||||
| item change you have to implement it yourself. This should get your started: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(TaskListPlugin.create(context)) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
| 
 | ||||
|                 // obtain original SpanFactory set by TaskListPlugin | ||||
|                 final SpanFactory origin = builder.getFactory(TaskListItem.class); | ||||
|                 if (origin == null) { | ||||
|                     // or throw, as it's a bit weird state and we expect | ||||
|                     // this factory to be present | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 builder.setFactory(TaskListItem.class, new SpanFactory() { | ||||
|                     @Override | ||||
|                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                         // it's a bit non-secure behavior and we should validate | ||||
|                         // the type of returned span first, but for the sake of brevity | ||||
|                         // we skip this step | ||||
|                         final TaskListSpan span = (TaskListSpan) origin.getSpans(configuration, props); | ||||
| 
 | ||||
|                         if (span == null) { | ||||
|                             // or throw | ||||
|                             return null; | ||||
|                         } | ||||
| 
 | ||||
|                         // return an array of spans | ||||
|                         return new Object[]{ | ||||
|                                 span, | ||||
|                                 new ClickableSpan() { | ||||
|                                     @Override | ||||
|                                     public void onClick(@NonNull View widget) { | ||||
|                                         // toggle VISUAL state | ||||
|                                         span.setDone(!span.isDone()); | ||||
| 
 | ||||
|                                         // do not forget to invalidate widget | ||||
|                                         widget.invalidate(); | ||||
| 
 | ||||
|                                         // execute your persistence logic | ||||
|                                     } | ||||
| 
 | ||||
|                                     @Override | ||||
|                                     public void updateDrawState(@NonNull TextPaint ds) { | ||||
|                                         // no-op, so appearance is not changed (otherwise | ||||
|                                         // task list item will look like a link) | ||||
|                                     } | ||||
|                                 } | ||||
|                         }; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										235
									
								
								docs/docs/v4/html/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								docs/docs/v4/html/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,235 @@ | ||||
| # HTML | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'html'" /> | ||||
| 
 | ||||
| This artifact encapsulates HTML parsing from the core artifact and provides | ||||
| few predefined `TagHandlers` | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(HtmlPlugin.create()) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| As this artifact brings modified [jsoup](https://github.com/jhy/jsoup) library  | ||||
| it was moved to a standalone module in order to minimize dependencies and unused code | ||||
| in applications that does not require HTML render capabilities. | ||||
| 
 | ||||
| Before <Badge text="2.0.0" /> `Markwon` used android `Html` class for parsing and | ||||
| rendering. Unfortunately, according to markdown specification, markdown can contain | ||||
| HTML in _unpredictable_ way if rendered _outside_ of browser. For example: | ||||
| 
 | ||||
| ```markdown{4} | ||||
| <i> | ||||
| Hello from italics tag | ||||
| 
 | ||||
| </i><b>bold></b> | ||||
| ``` | ||||
| 
 | ||||
| This snippet could be represented as: | ||||
| * HtmlBlock (`<i>\nHello from italics tag`) | ||||
| * HtmlInline (`<i>`) | ||||
| * HtmlInline (`<b>`) | ||||
| * Text (`bold`) | ||||
| * HtmlInline (`</b>`) | ||||
| 
 | ||||
| :::tip A bit of background | ||||
| <br> | ||||
| <GithubIssue id="52" displayName="This issue" /> had brought attention to differences between HTML & commonmark implementations. <br><br> | ||||
| ::: | ||||
| 
 | ||||
| Unfortunately Android `HTML` class cannot parse a _fragment_ of HTML to later | ||||
| be included in a bigger set of content. This is why the decision was made to bring | ||||
| HTML parsing _in-markwon-house_ | ||||
| 
 | ||||
| ## Predefined TagHandlers | ||||
| * `<img>` | ||||
| * `<a>` | ||||
| * `<blockquote>` | ||||
| * `<sub>` | ||||
| * `<sup>` | ||||
| * `<b>, <strong>` | ||||
| * `<s>, <del>` | ||||
| * `<u>, <ins>` | ||||
| * `<ul>, <ol>` | ||||
| * `<i>, <cite>, <em>, <dfn>` | ||||
| * `<h1>, <h2>, <h3>, <h4>, <h5>, <h6>` | ||||
| 
 | ||||
| :::tip | ||||
| All predefined tag handlers will use styling spans for native markdown content. | ||||
| So, if your `Markwon` instance was configured to, for example, render Emphasis | ||||
| nodes as a <span style="color: #FF0000">red text</span> then HTML tag handler will | ||||
| use the same span. This includes images, links, UrlResolver, LinkProcessor, etc | ||||
| ::: | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Staring with <Badge text="4.0.0" /> you can exclude all default tag handlers: | ||||
| 
 | ||||
| ```java | ||||
| .usePlugin(HtmlPlugin.create(new HtmlPlugin.HtmlConfigure() { | ||||
|     @Override | ||||
|     public void configureHtml(@NonNull HtmlPlugin plugin) { | ||||
|         plugin.excludeDefaults(true); | ||||
|     } | ||||
| })) | ||||
| ``` | ||||
| 
 | ||||
| or via plugin: | ||||
| 
 | ||||
| ```java | ||||
| .usePlugin(new AbstractMarkwonPlugin() { | ||||
|     @Override | ||||
|     public void configure(@NonNull Registry registry) { | ||||
|         registry.require(HtmlPlugin.class, new Action<HtmlPlugin>() { | ||||
|             @Override | ||||
|             public void apply(@NonNull HtmlPlugin htmlPlugin) { | ||||
|                 htmlPlugin.excludeDefaults(true); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| }) | ||||
| ``` | ||||
| 
 | ||||
| If you wish to exclude some of them `TagHandlerNoOp` can be used: | ||||
| 
 | ||||
| ```java | ||||
| .usePlugin(HtmlPlugin.create(new HtmlPlugin.HtmlConfigure() { | ||||
|     @Override | ||||
|     public void configureHtml(@NonNull HtmlPlugin plugin) { | ||||
|         plugin.addHandler(TagHandlerNoOp.create("h4", "h5", "h6", "img")); | ||||
|     } | ||||
| })) | ||||
| ``` | ||||
| 
 | ||||
| ## TagHandler | ||||
| 
 | ||||
| To define a tag-handler that applies style for the whole tag content (from start to end), | ||||
| a `SimpleTagHandler` can be used. For example, let's define `<align>` tag, which can be used | ||||
| like this: | ||||
| 
 | ||||
| * `<align center>centered text</align>` | ||||
| * `<align end>this should be aligned at the end (right for LTR locales)</align>` | ||||
| * `<align>regular alignment</align>` | ||||
| 
 | ||||
| ```java | ||||
| public class AlignTagHandler extends SimpleTagHandler { | ||||
| 
 | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public Object getSpans( | ||||
|             @NonNull MarkwonConfiguration configuration, | ||||
|             @NonNull RenderProps renderProps, | ||||
|             @NonNull HtmlTag tag) { | ||||
| 
 | ||||
|         final Layout.Alignment alignment; | ||||
| 
 | ||||
|         // html attribute without value, <align center></align> | ||||
|         if (tag.attributes().containsKey("center")) { | ||||
|             alignment = Layout.Alignment.ALIGN_CENTER; | ||||
|         } else if (tag.attributes().containsKey("end")) { | ||||
|             alignment = Layout.Alignment.ALIGN_OPPOSITE; | ||||
|         } else { | ||||
|             // empty value or any other will make regular alignment | ||||
|             alignment = Layout.Alignment.ALIGN_NORMAL; | ||||
|         } | ||||
| 
 | ||||
|         return new AlignmentSpan.Standard(alignment); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Collection<String> supportedTags() { | ||||
|         return Collections.singleton("align"); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| `SimpleTagHandler` can return an array of spans from `getSpans` method | ||||
| ::: | ||||
| 
 | ||||
| Then register `AlignTagHandler`: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(HtmlPlugin.create()) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configure(@NonNull Registry registry) { | ||||
|                 registry.require(HtmlPlugin.class, htmlPlugin -> htmlPlugin | ||||
|                         .addHandler(new AlignTagHandler()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| or directly on `HtmlPlugin`: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(HtmlPlugin.create(plugin -> plugin.addHandler(new AlignTagHandler()))) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| If a tag requires special handling `TagHandler` can be used directly. For example | ||||
| let's define an `<enhance>` tag with `start` and `end` arguments, that will mark | ||||
| start and end positions of the text that needs to be enlarged: | ||||
| 
 | ||||
| ```html | ||||
| <enhance start="5" end="12">This is text that must be enhanced, at least a part of it</enhance> | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ```java | ||||
| public class EnhanceTagHandler extends TagHandler { | ||||
| 
 | ||||
|     private final int enhanceTextSize; | ||||
| 
 | ||||
|     EnhanceTagHandler(@Px int enhanceTextSize) { | ||||
|         this.enhanceTextSize = enhanceTextSize; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void handle( | ||||
|             @NonNull MarkwonVisitor visitor, | ||||
|             @NonNull MarkwonHtmlRenderer renderer, | ||||
|             @NonNull HtmlTag tag) { | ||||
| 
 | ||||
|         // we require start and end to be present | ||||
|         final int start = parsePosition(tag.attributes().get("start")); | ||||
|         final int end = parsePosition(tag.attributes().get("end")); | ||||
| 
 | ||||
|         if (start > -1 && end > -1) { | ||||
|             visitor.builder().setSpan( | ||||
|                     new AbsoluteSizeSpan(enhanceTextSize), | ||||
|                     tag.start() + start, | ||||
|                     tag.start() + end | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Collection<String> supportedTags() { | ||||
|         return Collections.singleton("enhance"); | ||||
|     } | ||||
| 
 | ||||
|     private static int parsePosition(@Nullable String value) { | ||||
|         int position; | ||||
|         if (!TextUtils.isEmpty(value)) { | ||||
|             try { | ||||
|                 position = Integer.parseInt(value); | ||||
|             } catch (NumberFormatException e) { | ||||
|                 e.printStackTrace(); | ||||
|                 position = -1; | ||||
|             } | ||||
|         } else { | ||||
|             position = -1; | ||||
|         } | ||||
|         return position; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										27
									
								
								docs/docs/v4/image-glide/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								docs/docs/v4/image-glide/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| # Image Glide | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'image-glide'" /> | ||||
| 
 | ||||
| Image loading based on `Glide` library | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         // automatically create Glide instance | ||||
|         .usePlugin(GlideImagesPlugin.create(context)) | ||||
|         // use supplied Glide instance | ||||
|         .usePlugin(GlideImagesPlugin.create(Glide.with(context))) | ||||
|         // if you need more control | ||||
|         .usePlugin(GlideImagesPlugin.create(new GlideImagesPlugin.GlideStore() { | ||||
|             @NonNull | ||||
|             @Override | ||||
|             public RequestBuilder<Drawable> load(@NonNull AsyncDrawable drawable) { | ||||
|                 return Glide.with(context).load(drawable.getDestination()); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void cancel(@NonNull Target<?> target) { | ||||
|                 Glide.with(context).clear(target); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										32
									
								
								docs/docs/v4/image-picasso/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								docs/docs/v4/image-picasso/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| # Image Picasso | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'image-picasso'" /> | ||||
| 
 | ||||
| Image loading based on `Picasso` library | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         // automatically create Picasso instance | ||||
|         .usePlugin(PicassoImagesPlugin.create(context)) | ||||
|         // use provided picasso instance | ||||
|         .usePlugin(PicassoImagesPlugin.create(Picasso.get())) | ||||
|         // if you need more control | ||||
|         .usePlugin(PicassoImagesPlugin.create(new PicassoImagesPlugin.PicassoStore() { | ||||
|             @NonNull | ||||
|             @Override | ||||
|             public RequestCreator load(@NonNull AsyncDrawable drawable) { | ||||
|                 return Picasso.get() | ||||
|                         .load(drawable.getDestination()) | ||||
|                         // please note that drawable should be used as tag (not a destination) | ||||
|                         // otherwise there won't be support for multiple images with the same URL  | ||||
|                         .tag(drawable); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void cancel(@NonNull AsyncDrawable drawable) { | ||||
|                 Picasso.get() | ||||
|                         .cancelTag(drawable); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										354
									
								
								docs/docs/v4/image/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								docs/docs/v4/image/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,354 @@ | ||||
| # Image | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'image'" /> | ||||
| 
 | ||||
| In order to display images in your markdown `ImagesPlugin` can be used. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create()) | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| There are also modules that add image loading capabilities to markdown | ||||
| based on image-loading libraries: [image-glide](/docs/v4/image-glide/) and | ||||
| [image-picasso](/docs/v4/image-picasso/) | ||||
| ::: | ||||
| 
 | ||||
| `ImagesPlugin` splits the image-loading into 2 parts: scheme-handling and media-decoding. | ||||
| 
 | ||||
| ## SchemeHandler | ||||
| 
 | ||||
| To add a scheme-handler to `ImagesPlugin`: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create()) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configure(@NonNull Registry registry) { | ||||
|                 registry.require(ImagesPlugin.class, new Action<ImagesPlugin>() { | ||||
|                     @Override | ||||
|                     public void apply(@NonNull ImagesPlugin imagesPlugin) { | ||||
|                         imagesPlugin.addSchemeHandler(DataUriSchemeHandler.create()); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                 plugin.addSchemeHandler(DataUriSchemeHandler.create()); | ||||
|             } | ||||
|         })) | ||||
| ``` | ||||
| 
 | ||||
| `ImagesPlugin` comes with a set of predefined scheme-handlers: | ||||
| * `FileSchemeHandler` - `file://` | ||||
| * `DataUriSchemeHandler` - `data:` | ||||
| * `NetworkSchemeHandler` - `http`, `https` | ||||
| * `OkHttpNetworkSchemeHandler` - `http`, `https` | ||||
| 
 | ||||
| ### FileSchemeHandler | ||||
| 
 | ||||
| Loads images via `file://` scheme. Allows loading images from `assets` folder. | ||||
| 
 | ||||
| ```java | ||||
| // default implementation, no assets handling | ||||
| FileSchemeHandler.create(); | ||||
| 
 | ||||
| // assets loading | ||||
| FileSchemeHandler.createWithAssets(context); | ||||
| ``` | ||||
| 
 | ||||
| :::warning | ||||
| Assets loading will work when your URL will include `android_asset` in the path, | ||||
| for example: `file:///android_asset/image.png` (mind the 3 slashes `///`). If you wish | ||||
| to _assume_ all images without proper scheme to point to assets folder, then you can use | ||||
| [UrlProcessorAndroidAssets](/docs/v4/core/configuration.html#urlprocessorandroidassets) | ||||
| ::: | ||||
| 
 | ||||
| By default `ImagesPlugin` includes _plain_ `FileSchemeHandler` (without assets support), | ||||
| so if you wish to change that you can explicitly specify it: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                 plugin.addSchemeHandler(FileSchemeHandler.createWithAssets(context)); | ||||
|             } | ||||
|         })) | ||||
| ``` | ||||
| 
 | ||||
| ### DataUriSchemeHandler | ||||
| `DataUriSchemeHandler` allows _inlining_ images with `data:` scheme (`data:image/svg+xml;base64,MTIz`). | ||||
| This scheme-handler is registered by default, so you do not need to add it explicitly. | ||||
| 
 | ||||
| ### NetworkSchemeHandler | ||||
| `NetworkSchemeHandler` allows obtaining images from `http://` and `https://` uris  | ||||
| (internally it uses `HttpURLConnection`). This scheme-handler is registered by default | ||||
| 
 | ||||
| ### OkHttpNetworkSchemeHandler | ||||
| `OkHttpNetworkSchemeHandler` allows obtaining images from `http://` and `https://` uris | ||||
| via [okhttp library](https://github.com/square/okhttp). Please note that in order to use | ||||
| this scheme-handler you must explicitly add `okhttp` library to your project. | ||||
| 
 | ||||
| ```java | ||||
| // default instance | ||||
| OkHttpNetworkSchemeHandler.create(); | ||||
| 
 | ||||
| // specify OkHttpClient to use | ||||
| OkHttpNetworkSchemeHandler.create(new OkHttpClient()); | ||||
| 
 | ||||
| // @since 4.0.0 | ||||
| OkHttpNetworkSchemeHandler.create(Call.Factory); | ||||
| ``` | ||||
| 
 | ||||
| ### Custom SchemeHandler | ||||
| 
 | ||||
| ```java | ||||
| public abstract class SchemeHandler { | ||||
| 
 | ||||
|     @NonNull | ||||
|     public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri); | ||||
| 
 | ||||
|     @NonNull | ||||
|     public abstract Collection<String> supportedSchemes(); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Starting with <Badge text="4.0.0" /> `SchemeHandler` can return a result (when no | ||||
| further decoding is required): | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                 // for example to return a drawable resource | ||||
|                 plugin.addSchemeHandler(new SchemeHandler() { | ||||
|                     @NonNull | ||||
|                     @Override | ||||
|                     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||
| 
 | ||||
|                         // will handle URLs like `drawable://ic_account_24dp_white` | ||||
|                         final int resourceId = context.getResources().getIdentifier( | ||||
|                                 raw.substring("drawable://".length()), | ||||
|                                 "drawable", | ||||
|                                 context.getPackageName()); | ||||
| 
 | ||||
|                         // it's fine if it throws, async-image-loader will catch exception | ||||
|                         final Drawable drawable = context.getDrawable(resourceId); | ||||
| 
 | ||||
|                         return ImageItem.withResult(drawable); | ||||
|                     } | ||||
| 
 | ||||
|                     @NonNull | ||||
|                     @Override | ||||
|                     public Collection<String> supportedSchemes() { | ||||
|                         return Collections.singleton("drawable"); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         })) | ||||
| ``` | ||||
| 
 | ||||
| Otherwise `SchemeHandler` must return an `InputStream` with proper `content-type` information | ||||
| for further processing by a `MediaDecoder`: | ||||
| 
 | ||||
| ```java | ||||
| imagesPlugin.addSchemeHandler(new SchemeHandler() { | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||
|         return ImageItem.withDecodingNeeded("image/png", load(raw)); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private InputStream load(@NonNull String raw) {...} | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| ## MediaDecoder | ||||
| 
 | ||||
| `ImagesPlugin` comes with predefined media-decoders: | ||||
| * `GifMediaDecoder` adds support for GIF | ||||
| * `SvgMediaDecoder` adds support for SVG | ||||
| * `DefaultMediaDecoder` | ||||
| 
 | ||||
| :::warning | ||||
| If you wish to add support for **SVG** or **GIF** you must explicitly add these dependencies | ||||
| to your project: | ||||
| * for `SVG`: `com.caverock:androidsvg:1.4` | ||||
| * for `GIF`: `pl.droidsonroids.gif:android-gif-drawable:1.2.14` | ||||
| 
 | ||||
| You can try more recent versions of these libraries, but make sure that they doesn't  | ||||
| introduce any unexpected behavior. | ||||
| ::: | ||||
| 
 | ||||
| 
 | ||||
| ### GifMediaDecoder | ||||
| 
 | ||||
| Adds support for GIF media in markdown. If `pl.droidsonroids.gif:android-gif-drawable:*` dependency | ||||
| is found in the classpath, then registration will happen automatically. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                 // autoplayGif controls if GIF should be automatically started | ||||
|                 plugin.addMediaDecoder(GifMediaDecoder.create(/*autoplayGif*/false)); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ### SvgMediaDecoder | ||||
| 
 | ||||
| Adds support for SVG media in markdown. If `com.caverock:androidsvg:*` dependency is found | ||||
| in the classpath, then registration will happen automatically. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
| 
 | ||||
|                 // uses supplied Resources | ||||
|                 plugin.addMediaDecoder(SvgMediaDecoder.create(context.getResources())); | ||||
| 
 | ||||
|                 // uses Resources.getSystem() | ||||
|                 plugin.addMediaDecoder(SvgMediaDecoder.create()); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ### DefaultMediaDecoder | ||||
| 
 | ||||
| `DefaultMediaDecoder` _tries_ to decode supplied InputStream  | ||||
| as Bitmap (via `BitmapFactory.decodeStream(inputStream)`). This decoder is registered automatically. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                  | ||||
|                 // uses supplied Resources | ||||
|                 plugin.defaultMediaDecoder(DefaultMediaDecoder.create(context.getResources())); | ||||
|                  | ||||
|                 // uses Resources.getSystem() | ||||
|                 plugin.defaultMediaDecoder(DefaultMediaDecoder.create()); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## AsyncDrawableScheduler | ||||
| 
 | ||||
| `AsyncDrawableScheduler` is used in order to give `AsyncDrawable` a way to invalidate `TextView` | ||||
| that is holding it. A plugin that is dealing with `AsyncDrawable` should always call these methods: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { | ||||
|                 AsyncDrawableScheduler.unschedule(textView); | ||||
|             } | ||||
| 
 | ||||
|             @Override | ||||
|             public void afterSetText(@NonNull TextView textView) { | ||||
|                 AsyncDrawableScheduler.schedule(textView); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| Starting with <Badge text="4.0.0" /> multiple plugins can call `AsyncDrawableScheduler#schedule` | ||||
| method without the penalty to process `AsyncDrawable` callbacks multiple times (internally caches | ||||
| state which ensures that a `TextView` is processed only once the text has changed). | ||||
| ::: | ||||
| 
 | ||||
| ## ErrorHandler | ||||
| 
 | ||||
| An `ErrorHandler` can be used to receive an error that has happened during image loading | ||||
| and (optionally) return an error drawable to be displayed instead | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                 plugin.errorHandler(new ImagesPlugin.ErrorHandler() { | ||||
|                     @Nullable | ||||
|                     @Override | ||||
|                     public Drawable handleError(@NonNull String url, @NonNull Throwable throwable) { | ||||
|                         return null; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## PlaceholderProvider | ||||
| 
 | ||||
| To display a placeholder during image loading `PlaceholderProvider` can be used: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull ImagesPlugin plugin) { | ||||
|                 plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() { | ||||
|                     @Nullable | ||||
|                     @Override | ||||
|                     public Drawable providePlaceholder(@NonNull AsyncDrawable drawable) { | ||||
|                         return null; | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         })) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| If your placeholder drawable has _specific_ size which is not the same an image that is being loaded, | ||||
| you can manually assign bounds to the placeholder: | ||||
| 
 | ||||
| ```java | ||||
| plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() { | ||||
|     @Override | ||||
|     public Drawable providePlaceholder(@NonNull AsyncDrawable drawable) { | ||||
|         final ColorDrawable placeholder = new ColorDrawable(Color.BLUE); | ||||
|         // these bounds will be used to display a placeholder, | ||||
|         // so even if loading image has size `width=100%`, placeholder | ||||
|         // bounds won't be affected by it | ||||
|         placeholder.setBounds(0, 0, 48, 48); | ||||
|         return placeholder; | ||||
|     } | ||||
| }); | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| :::tip | ||||
| If you are using [html](/docs/v4/html/) you do not have to additionally setup | ||||
| images displayed via `<img>` tag, as `HtmlPlugin` automatically uses configured | ||||
| image loader. But images referenced in HTML come with additional support for | ||||
| sizes, which is not supported natively by markdown, allowing absolute or relative sizes: | ||||
| 
 | ||||
| ```html | ||||
| <img src="./assets/my-image" width="100%"> | ||||
| ``` | ||||
| ::: | ||||
							
								
								
									
										34
									
								
								docs/docs/v4/install.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/docs/v4/install.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| --- | ||||
| prev: false | ||||
| next: /docs/v4/core/getting-started.md | ||||
| --- | ||||
| 
 | ||||
| # Installation | ||||
| 
 | ||||
|  | ||||
|  | ||||
| 
 | ||||
| <ArtifactPicker4 /> | ||||
| 
 | ||||
| ## Snapshot | ||||
| 
 | ||||
| In order to use latest `SNAPSHOT` version add snapshot repository  | ||||
| to your root project's `build.gradle` file: | ||||
| 
 | ||||
| ```groovy | ||||
| allprojects { | ||||
|     repositories { | ||||
|         jcenter() | ||||
|         google() | ||||
|         // this one 👇 | ||||
|         maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } // 👈 this one | ||||
|         // this one 👆 | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::tip Info | ||||
| All official artifacts share the same version number and all  | ||||
| are uploaded to **release** and **snapshot** repositories | ||||
| ::: | ||||
| 
 | ||||
							
								
								
									
										31
									
								
								docs/docs/v4/linkify/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								docs/docs/v4/linkify/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| # Linkify | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'linkify'" /> | ||||
| 
 | ||||
| A plugin to automatically add links to your markdown. Currently autolinking works for: | ||||
| * email (`me@web.com`) | ||||
| * phone numbers (`+10000000`) | ||||
| * web URLS | ||||
| 
 | ||||
| :::warning | ||||
| `Linkify` plugin is based on `android.text.util.Linkify` which can lead to significant performance  | ||||
| drop due to its implementation based on regex. | ||||
| ::: | ||||
| 
 | ||||
| :::danger | ||||
| Do not use `autolink` XML attribute on your `TextView` as it will remove  | ||||
| all links except autolinked ones ¯\\\_(ツ)_/¯ | ||||
| ::: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         // will autolink all supported types | ||||
|         .usePlugin(LinkifyPlugin.create()) | ||||
|         // the same as above | ||||
|         .usePlugin(LinkifyPlugin.create( | ||||
|                 Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS | ||||
|         )) | ||||
|         // only emails | ||||
|         .usePlugin(LinkifyPlugin.create(Linkify.EMAIL_ADDRESSES)) | ||||
|         .build(); | ||||
| ``` | ||||
							
								
								
									
										96
									
								
								docs/docs/v4/recipes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								docs/docs/v4/recipes.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | ||||
| # Recipes | ||||
| 
 | ||||
| 
 | ||||
| ## SpannableFactory | ||||
| 
 | ||||
| Consider using `NoCopySpannableFactory` when a `TextView` will be used to display markdown | ||||
| multiple times (for example in a `RecyclerView`): | ||||
| 
 | ||||
| ```java | ||||
| // call after inflation and before setting markdown | ||||
| textView.setSpannableFactory(NoCopySpannableFactory.getInstance()); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Autolink | ||||
| 
 | ||||
| Do not use `autolink` XML attribute on your `TextView` as it will remove all links except autolinked ones. | ||||
| Consider using [linkify plugin](/docs/v4/linkify/) or commonmark-java [autolink extension](https://github.com/atlassian/commonmark-java) | ||||
| 
 | ||||
| 
 | ||||
| ## List item spacing | ||||
| 
 | ||||
| If your list items, task list items or paragraphs need special space between them  | ||||
| (increasing spacing between them, but keeping the original line height),  | ||||
| `LastLineSpacingSpan` <Badge text="4.0.0" /> can be used: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 // or Paragraph, or TaskListItem | ||||
|                 builder.addFactory(ListItem.class, new SpanFactory() { | ||||
|                     @Override | ||||
|                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                         return new LastLineSpacingSpan(spacingPx); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## Softbreak new-line | ||||
| 
 | ||||
| If you want to add a new line when a `softbreak` is used: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                 builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { | ||||
|                     @Override | ||||
|                     public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { | ||||
|                         visitor.forceNewLine(); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Custom typeface | ||||
| 
 | ||||
| When using a custom typeface on a `TextView` you might find that **bold** and *italic* nodes | ||||
| are displayed incorrectly. Consider registering own `SpanFactories` for `StrongEmphasis` and `Emphasis` nodes: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                 builder | ||||
|                         .setFactory(StrongEmphasis.class, new SpanFactory() { | ||||
|                             @Override | ||||
|                             public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                                 return new StyleSpan(Typeface.BOLD); | ||||
|                             } | ||||
|                         }) | ||||
|                         .setFactory(Emphasis.class, new SpanFactory() { | ||||
|                             @Override | ||||
|                             public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                                 return new StyleSpan(Typeface.ITALIC); | ||||
|                             } | ||||
|                         }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| Please check that `StyleSpan` works for you. If it doesn't consider  | ||||
| using `CustomTypefaceSpan` with your typeface directly. | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										92
									
								
								docs/docs/v4/recycler-table/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								docs/docs/v4/recycler-table/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| # Recycler Table <Badge text="3.0.0" /> | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'recycler-table'" /> | ||||
| 
 | ||||
| Artifact that provides [MarkwonAdapter.Entry](/docs/v4/recycler/) to render `TableBlock` inside  | ||||
| Android-native `TableLayout` widget. | ||||
| 
 | ||||
| <img :src="$withBase('/assets/recycler-table-screenshot.png')" alt="screenshot" width="45%"> | ||||
| <br> | ||||
| <small><em><sup>*</sup> It's possible to wrap `TableLayout` inside a `HorizontalScrollView` to include all table content</em></small> | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Register instance of `TableEntry` with `MarkwonAdapter` to render TableBlocks: | ||||
| ```java | ||||
| final MarkwonAdapter adapter = MarkwonAdapter.builder(R.layout.adapter_default_entry, R.id.text) | ||||
|         .include(TableBlock.class, TableEntry.create(builder -> builder | ||||
|                 .tableLayout(R.layout.adapter_table_block, R.id.table_layout) | ||||
|                 .textLayoutIsRoot(R.layout.view_table_entry_cell))) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| `TableEntry` requires at least 2 arguments: | ||||
| * `tableLayout` - layout with `TableLayout` inside | ||||
| * `textLayout` - layout with `TextView` inside (represents independent table cell) | ||||
| 
 | ||||
| In case when required view is the root of layout specific builder methods can be used: | ||||
| * `tableLayoutIsRoot(int)` | ||||
| * `textLayoutIsRoot(int)` | ||||
| 
 | ||||
| If your layouts have different structure (for example wrap a `TableView` inside a `HorizontalScrollView`) | ||||
| then you should use methods that accept ID of required view inside layout: | ||||
| * `tableLayout(int, int)` | ||||
| * `textLayout(int, int)` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| To display `TableBlock` as a `TableLayout` specific `MarkwonPlugin` must be used: `TableEntryPlugin`. | ||||
| 
 | ||||
| :::warning | ||||
| Do not use `TablePlugin` if you wish to display markdown tables via `TableEntry`. Use **TableEntryPlugin** instead | ||||
| ::: | ||||
| 
 | ||||
| `TableEntryPlugin` can reuse existing `TablePlugin` to make appearance of tables the same in both contexts: | ||||
| when rendering _natively_ in a TextView and when rendering in RecyclerView with TableEntry. | ||||
| 
 | ||||
| * `TableEntryPlugin.create(Context)` - creates plugin with default `TableTheme` | ||||
| * `TableEntryPlugin.create(TableTheme)` - creates plugin with provided `TableTheme` | ||||
| * `TableEntryPlugin.create(TablePlugin.ThemeConfigure)` - creates plugin with theme configured by `ThemeConfigure` | ||||
| * `TableEntryPlugin.create(TablePlugin)` - creates plugin with `TableTheme` used in provided `TablePlugin` | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(TableEntryPlugin.create(context)) | ||||
|         // other plugins | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(TableEntryPlugin.create(builder -> builder | ||||
|                 .tableBorderWidth(0) | ||||
|                 .tableHeaderRowBackgroundColor(Color.RED))) | ||||
|         // other plugins | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## Table with scrollable content | ||||
| 
 | ||||
| To stretch table columns to fit the width of screen or to make table scrollable when content exceeds screen width | ||||
| this layout can be used: | ||||
| 
 | ||||
| ```xml | ||||
| <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingTop="8dip" | ||||
|     android:paddingRight="16dip" | ||||
|     android:paddingBottom="8dip" | ||||
|     android:scrollbarStyle="outsideInset"> | ||||
| 
 | ||||
|     <TableLayout | ||||
|         android:id="@+id/table_layout" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:stretchColumns="*" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
| ``` | ||||
							
								
								
									
										153
									
								
								docs/docs/v4/recycler/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								docs/docs/v4/recycler/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | ||||
| # Recycler <Badge text="3.0.0" /> | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'recycler'" /> | ||||
| 
 | ||||
| This artifact allows displaying markdown in a set of Android widgets | ||||
| inside a RecyclerView. Can be useful when displaying lengthy markdown | ||||
| content or **displaying certain markdown blocks inside specific widgets**. | ||||
| 
 | ||||
| ```java | ||||
| // create an adapter that will use a TextView for each block of markdown | ||||
| // `createTextViewIsRoot` accepts a layout in which TextView is the root view | ||||
| final MarkwonAdapter adapter =  | ||||
|         MarkwonAdapter.createTextViewIsRoot(R.layout.adapter_default_entry); | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| // `create` method accepts a layout with TextView and ID of a TextView | ||||
| // which allows wrapping a TextView inside another widget or combine with other widgets | ||||
| final MarkwonAdapter adapter =  | ||||
|         MarkwonAdapter.create(R.layout.adapter_default_entry, R.id.text_view); | ||||
| 
 | ||||
| // initialize RecyclerView (LayoutManager, Decorations, etc) | ||||
| final RecyclerView recyclerView = obtainRecyclerView(); | ||||
| 
 | ||||
| // set adapter | ||||
| recyclerView.setAdapter(adapter); | ||||
| 
 | ||||
| // obtain an instance of Markwon (register all required plugins) | ||||
| final Markwon markwon = obtainMarkwon(); | ||||
| 
 | ||||
| // set markdown to be displayed | ||||
| adapter.setMarkdown(markwon, "# This is markdown!"); | ||||
| 
 | ||||
| // NB, adapter does not handle updates on its own, please use | ||||
| // whatever method appropriate for you. | ||||
| adapter.notifyDataSetChanged(); | ||||
| ``` | ||||
| 
 | ||||
| Initialized adapter above will use a TextView for each markdown block. | ||||
| In order to tell adapter to render certain blocks differently a `builder` can be used. | ||||
| For example, let's render `FencedCodeBlock` inside a `HorizontalScrollView`: | ||||
| 
 | ||||
| ```java | ||||
| // we still need to have a _default_ entry | ||||
| final MarkwonAdapter adapter = | ||||
|         MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_default_entry) | ||||
|                 .include(FencedCodeBlock.class, new FencedCodeBlockEntry()) | ||||
|                 .build(); | ||||
| ``` | ||||
| 
 | ||||
| where `FencedCodeBlockEntry` is: | ||||
| 
 | ||||
| ```java | ||||
| public class FencedCodeBlockEntry extends MarkwonAdapter.Entry<FencedCodeBlock, FencedCodeBlockEntry.Holder> { | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Holder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) { | ||||
|         return new Holder(inflater.inflate(R.layout.adapter_fenced_code_block, parent, false)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void bindHolder(@NonNull Markwon markwon, @NonNull Holder holder, @NonNull FencedCodeBlock node) { | ||||
|         markwon.setParsedMarkdown(holder.textView, markwon.render(node)); | ||||
|     } | ||||
| 
 | ||||
|     public static class Holder extends MarkwonAdapter.Holder { | ||||
| 
 | ||||
|         final TextView textView; | ||||
| 
 | ||||
|         public Holder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
| 
 | ||||
|             this.textView = requireView(R.id.text_view); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| and its layout (`R.layout.adapter_fenced_code_block`): | ||||
| 
 | ||||
| ```xml | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:fillViewport="true" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingRight="16dip" | ||||
|     android:scrollbarStyle="outsideInset"> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:id="@+id/text" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:background="#0f000000" | ||||
|         android:fontFamily="monospace" | ||||
|         android:lineSpacingExtra="2dip" | ||||
|         android:paddingLeft="16dip" | ||||
|         android:paddingTop="8dip" | ||||
|         android:paddingRight="16dip" | ||||
|         android:paddingBottom="8dip" | ||||
|         android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|         android:textSize="14sp" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
| ``` | ||||
| 
 | ||||
| As we apply styling to `FencedCodeBlock` _manually_, we no longer need | ||||
| `Markwon` to apply styling spans for us, so `Markwon` initialization could be: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         // your other plugins | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                 builder.on(FencedCodeBlock.class, (visitor, fencedCodeBlock) -> { | ||||
|                     // we actually won't be applying code spans here, as our custom view will | ||||
|                     // draw background and apply mono typeface | ||||
|                     // | ||||
|                     // NB the `trim` operation on literal (as code will have a new line at the end) | ||||
|                     final CharSequence code = visitor.configuration() | ||||
|                             .syntaxHighlight() | ||||
|                             .highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim()); | ||||
|                     visitor.builder().append(code); | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| Previously we have created a `FencedCodeBlockEntry` but all it does is apply markdown to a TextView. | ||||
| For such a case there is a `SimpleEntry` that could be used instead: | ||||
| 
 | ||||
| ```java | ||||
| final MarkwonAdapter adapter = | ||||
|         MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_default_entry) | ||||
|                 .include(FencedCodeBlock.class, SimpleEntry.create(R.layout.adapter_fenced_code_block, R.id.text_view)) | ||||
|                 .build(); | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| `SimpleEntry` also takes care of _caching_ parsed markdown. So each node will be | ||||
| parsed only once and each subsequent adapter binding call will reuse previously cached markdown. | ||||
| ::: | ||||
| 
 | ||||
| :::tip Tables | ||||
| There is a standalone artifact that adds support for displaying markdown tables | ||||
| natively via `TableLayout`. Please refer to its [documentation](/docs/v4/recycler-table/) | ||||
| ::: | ||||
							
								
								
									
										70
									
								
								docs/docs/v4/simple-ext/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								docs/docs/v4/simple-ext/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| # Simple Extension <Badge text="4.0.0" /> | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'simple-ext'" /> | ||||
| 
 | ||||
| `SimpleExtPlugin` allows creating simple _delimited_ extensions, for example: | ||||
| 
 | ||||
| ```md | ||||
| +this is text surrounded by `+`+ | ||||
| ``` | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(SimpleExtPlugin.create(plugin -> plugin | ||||
|                 // +sometext+ | ||||
|                 .addExtension(1, '+', new SpanFactory() { | ||||
|                     @Override | ||||
|                     public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                         return new EmphasisSpan(); | ||||
|                     } | ||||
|                 }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| or | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(this) | ||||
|         .usePlugin(SimpleExtPlugin.create()) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configure(@NonNull Registry registry) { | ||||
|                 registry.require(SimpleExtPlugin.class, new Action<SimpleExtPlugin>() { | ||||
|                     @Override | ||||
|                     public void apply(@NonNull SimpleExtPlugin plugin) { | ||||
|                         plugin.addExtension(1, '+', new SpanFactory() { | ||||
|                             @Override | ||||
|                             public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { | ||||
|                                 return new EmphasisSpan(); | ||||
|                             } | ||||
|                         }) | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| If opening and closing characters are different another method can be used: | ||||
| 
 | ||||
| ```java | ||||
| plugin.addExtension( | ||||
|     /*length*/2,  | ||||
|     /*openingCharacter*/'@',  | ||||
|     /*closingCharacter*/'$',  | ||||
|     /*spanFactory*/(configuration, props) -> new ForegroundColorSpan(Color.RED)))) | ||||
| ``` | ||||
| 
 | ||||
| This extension will be applied to a text like this: | ||||
| 
 | ||||
| ```md | ||||
| @@we are inside different delimiter characters$$ | ||||
| ``` | ||||
| 
 | ||||
| :::warning | ||||
| Space character cannot be used as a delimiter (from either side). So, | ||||
| ```java | ||||
| plugin.addExtension(1, '@', ' ', /*spanFactory*/); | ||||
| ``` | ||||
| won't work for `@some-text ` text | ||||
| ::: | ||||
							
								
								
									
										74
									
								
								docs/docs/v4/syntax-highlight/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								docs/docs/v4/syntax-highlight/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| # Syntax highlight | ||||
| 
 | ||||
| <MavenBadge4 :artifact="'syntax-highlight'" /> | ||||
| 
 | ||||
| This is a module to add **syntax highlight** functionality to your markdown rendered with `Markwon` library. It is based on [Prism4j](https://github.com/noties/Prism4j) so lead there to understand how to configure `Prism4j` instance. | ||||
| 
 | ||||
| <img :src="$withBase('/art/markwon-syntax-default.png')" alt="theme-default" width="80%"> | ||||
| 
 | ||||
| 
 | ||||
| <img :src="$withBase('/art/markwon-syntax-darkula.png')" alt="theme-darkula" width="80%"> | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| First, we need to obtain an instance of `Prism4jSyntaxHighlight` which implements Markwon's `SyntaxHighlight`: | ||||
| 
 | ||||
| ```java | ||||
| final SyntaxHighlight highlight =  | ||||
|     Prism4jSyntaxHighlight.create(Prism4j, Prism4jTheme); | ||||
| ``` | ||||
| 
 | ||||
| we also can obtain an instance of `Prism4jSyntaxHighlight` that has a _fallback_ option (if a language is not defined in `Prism4j` instance, fallback language can be used): | ||||
| 
 | ||||
| ```java | ||||
| final SyntaxHighlight highlight =  | ||||
|     Prism4jSyntaxHighlight.create(Prism4j, Prism4jTheme, String); | ||||
| ``` | ||||
| 
 | ||||
| Generally obtaining a `Prism4j` instance is pretty easy: | ||||
| 
 | ||||
| ```java | ||||
| final Prism4j prism4j = new Prism4j(new GrammarLocatorDef()); | ||||
| ``` | ||||
| 
 | ||||
| Where `GrammarLocatorDef` is a generated grammar locator (if you use `prism4j-bundler` annotation processor) | ||||
| 
 | ||||
| `Prism4jTheme` is a specific type that is defined in this module (`prism4j` doesn't know anything about rendering). It has 2 implementations: | ||||
| 
 | ||||
| * `Prism4jThemeDefault` | ||||
| * `Prism4jThemeDarkula` | ||||
| 
 | ||||
| Both of them can be obtained via factory method `create`: | ||||
| 
 | ||||
| * `Prism4jThemeDefault.create()` | ||||
| * `Prism4jThemeDarkula.create()` | ||||
| 
 | ||||
| But of cause nothing is stopping you from defining your own theme: | ||||
| 
 | ||||
| ```java | ||||
| public interface Prism4jTheme { | ||||
| 
 | ||||
|     @ColorInt | ||||
|     int background(); | ||||
| 
 | ||||
|     @ColorInt | ||||
|     int textColor(); | ||||
| 
 | ||||
|     void apply( | ||||
|             @NonNull String language, | ||||
|             @NonNull Prism4j.Syntax syntax, | ||||
|             @NonNull SpannableStringBuilder builder, | ||||
|             int start, | ||||
|             int end | ||||
|     ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::tip | ||||
| You can extend `Prism4jThemeBase` which has some helper methods | ||||
| ::: | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme)) | ||||
| ``` | ||||
| @ -3,12 +3,14 @@ org.gradle.jvmargs=-Xmx5g -Dfile.encoding=UTF-8 | ||||
| #org.gradle.parallel=true | ||||
| org.gradle.configureondemand=true | ||||
| 
 | ||||
| android.useAndroidX=true | ||||
| android.enableJetifier=true | ||||
| android.enableBuildCache=true | ||||
| android.buildCacheDir=build/pre-dex-cache | ||||
| 
 | ||||
| VERSION_NAME=3.0.1 | ||||
| VERSION_NAME=4.0.0 | ||||
| 
 | ||||
| GROUP=ru.noties.markwon | ||||
| GROUP=io.noties.markwon | ||||
| POM_DESCRIPTION=Markwon markdown for Android | ||||
| POM_URL=https://github.com/noties/Markwon | ||||
| POM_SCM_URL=https://github.com/noties/Markwon | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,5 @@ | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
|  | ||||
							
								
								
									
										2
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @ -28,7 +28,7 @@ APP_NAME="Gradle" | ||||
| APP_BASE_NAME=`basename "$0"` | ||||
| 
 | ||||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| DEFAULT_JVM_OPTS="" | ||||
| DEFAULT_JVM_OPTS='"-Xmx64m"' | ||||
| 
 | ||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||
| MAX_FD="maximum" | ||||
|  | ||||
							
								
								
									
										2
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							| @ -14,7 +14,7 @@ set APP_BASE_NAME=%~n0 | ||||
| set APP_HOME=%DIRNAME% | ||||
| 
 | ||||
| @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| set DEFAULT_JVM_OPTS= | ||||
| set DEFAULT_JVM_OPTS="-Xmx64m" | ||||
| 
 | ||||
| @rem Find java.exe | ||||
| if defined JAVA_HOME goto findJavaFromJavaHome | ||||
|  | ||||
							
								
								
									
										3
									
								
								markwon-core/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								markwon-core/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| # Markwon Core | ||||
| 
 | ||||
| https://noties.io/Markwon/docs/v4/core/getting-started.html | ||||
| @ -16,7 +16,7 @@ android { | ||||
| dependencies { | ||||
| 
 | ||||
|     deps.with { | ||||
|         api it['support-annotations'] | ||||
|         api it['x-annotations'] | ||||
|         api it['commonmark'] | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| <manifest package="ru.noties.markwon.renderer" /> | ||||
| <manifest package="io.noties.markwon" /> | ||||
|  | ||||
| @ -1,17 +1,14 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.text.Spanned; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| import ru.noties.markwon.core.CorePlugin; | ||||
| import ru.noties.markwon.core.MarkwonTheme; | ||||
| import ru.noties.markwon.html.MarkwonHtmlRenderer; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.priority.Priority; | ||||
| import io.noties.markwon.core.MarkwonTheme; | ||||
| 
 | ||||
| /** | ||||
|  * Class that extends {@link MarkwonPlugin} with all methods implemented (empty body) | ||||
| @ -22,6 +19,11 @@ import ru.noties.markwon.priority.Priority; | ||||
|  */ | ||||
| public abstract class AbstractMarkwonPlugin implements MarkwonPlugin { | ||||
| 
 | ||||
|     @Override | ||||
|     public void configure(@NonNull Registry registry) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureParser(@NonNull Parser.Builder builder) { | ||||
| 
 | ||||
| @ -32,11 +34,6 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
| 
 | ||||
| @ -52,18 +49,6 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Priority priority() { | ||||
|         // by default all come after CorePlugin | ||||
|         return Priority.after(CorePlugin.class); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public String processMarkdown(@NonNull String markdown) { | ||||
| @ -0,0 +1,14 @@ | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| /** | ||||
|  * @see LinkResolverDef | ||||
|  * @see MarkwonConfiguration.Builder#linkResolver(LinkResolver) | ||||
|  * @since 4.0.0 | ||||
|  */ | ||||
| public interface LinkResolver { | ||||
|     void resolve(@NonNull View view, @NonNull String link); | ||||
| } | ||||
| @ -1,19 +1,18 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.content.ActivityNotFoundException; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.provider.Browser; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.util.Log; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import ru.noties.markwon.core.spans.LinkSpan; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| public class LinkResolverDef implements LinkSpan.Resolver { | ||||
| public class LinkResolverDef implements LinkResolver { | ||||
|     @Override | ||||
|     public void resolve(View view, @NonNull String link) { | ||||
|     public void resolve(@NonNull View view, @NonNull String link) { | ||||
|         final Uri uri = Uri.parse(link); | ||||
|         final Context context = view.getContext(); | ||||
|         final Intent intent = new Intent(Intent.ACTION_VIEW, uri); | ||||
| @ -1,14 +1,15 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spanned; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| 
 | ||||
| import ru.noties.markwon.core.CorePlugin; | ||||
| import io.noties.markwon.core.CorePlugin; | ||||
| 
 | ||||
| /** | ||||
|  * Class to parse and render markdown. Since version 3.0.0 instance specific (previously consisted | ||||
| @ -37,13 +38,26 @@ public abstract class Markwon { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Factory method to obtain an instance of {@link Builder}. | ||||
|      * Factory method to obtain an instance of {@link Builder} with {@link CorePlugin} added. | ||||
|      * | ||||
|      * @see Builder | ||||
|      * @see #builderNoCore(Context) | ||||
|      * @since 3.0.0 | ||||
|      */ | ||||
|     @NonNull | ||||
|     public static Builder builder(@NonNull Context context) { | ||||
|         return new MarkwonBuilderImpl(context) | ||||
|                 // @since 4.0.0 add CorePlugin | ||||
|                 .usePlugin(CorePlugin.create()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Factory method to obtain an instance of {@link Builder} without {@link CorePlugin}. | ||||
|      * | ||||
|      * @since 4.0.0 | ||||
|      */ | ||||
|     @NonNull | ||||
|     public static Builder builderNoCore(@NonNull Context context) { | ||||
|         return new MarkwonBuilderImpl(context); | ||||
|     } | ||||
| 
 | ||||
| @ -0,0 +1,110 @@ | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import io.noties.markwon.core.MarkwonTheme; | ||||
| 
 | ||||
| /** | ||||
|  * @since 3.0.0 | ||||
|  */ | ||||
| class MarkwonBuilderImpl implements Markwon.Builder { | ||||
| 
 | ||||
|     private final Context context; | ||||
| 
 | ||||
|     private final List<MarkwonPlugin> plugins = new ArrayList<>(3); | ||||
| 
 | ||||
|     private TextView.BufferType bufferType = TextView.BufferType.SPANNABLE; | ||||
| 
 | ||||
|     MarkwonBuilderImpl(@NonNull Context context) { | ||||
|         this.context = context; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Markwon.Builder bufferType(@NonNull TextView.BufferType bufferType) { | ||||
|         this.bufferType = bufferType; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Markwon.Builder usePlugin(@NonNull MarkwonPlugin plugin) { | ||||
|         plugins.add(plugin); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Markwon.Builder usePlugins(@NonNull Iterable<? extends MarkwonPlugin> plugins) { | ||||
| 
 | ||||
|         final Iterator<? extends MarkwonPlugin> iterator = plugins.iterator(); | ||||
| 
 | ||||
|         MarkwonPlugin plugin; | ||||
| 
 | ||||
|         while (iterator.hasNext()) { | ||||
|             plugin = iterator.next(); | ||||
|             if (plugin == null) { | ||||
|                 throw new NullPointerException(); | ||||
|             } | ||||
|             this.plugins.add(plugin); | ||||
|         } | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Markwon build() { | ||||
| 
 | ||||
|         if (plugins.isEmpty()) { | ||||
|             throw new IllegalStateException("No plugins were added to this builder. Use #usePlugin " + | ||||
|                     "method to add them"); | ||||
|         } | ||||
| 
 | ||||
|         // please note that this method must not modify supplied collection | ||||
|         // if nothing should be done -> the same collection can be returned | ||||
|         final List<MarkwonPlugin> plugins = preparePlugins(this.plugins); | ||||
| 
 | ||||
|         final Parser.Builder parserBuilder = new Parser.Builder(); | ||||
|         final MarkwonTheme.Builder themeBuilder = MarkwonTheme.builderWithDefaults(context); | ||||
|         final MarkwonConfiguration.Builder configurationBuilder = new MarkwonConfiguration.Builder(); | ||||
|         final MarkwonVisitor.Builder visitorBuilder = new MarkwonVisitorImpl.BuilderImpl(); | ||||
|         final MarkwonSpansFactory.Builder spanFactoryBuilder = new MarkwonSpansFactoryImpl.BuilderImpl(); | ||||
| 
 | ||||
|         for (MarkwonPlugin plugin : plugins) { | ||||
|             plugin.configureParser(parserBuilder); | ||||
|             plugin.configureTheme(themeBuilder); | ||||
|             plugin.configureConfiguration(configurationBuilder); | ||||
|             plugin.configureVisitor(visitorBuilder); | ||||
|             plugin.configureSpansFactory(spanFactoryBuilder); | ||||
|         } | ||||
| 
 | ||||
|         final MarkwonConfiguration configuration = configurationBuilder.build( | ||||
|                 themeBuilder.build(), | ||||
|                 spanFactoryBuilder.build()); | ||||
| 
 | ||||
|         final RenderProps renderProps = new RenderPropsImpl(); | ||||
| 
 | ||||
|         return new MarkwonImpl( | ||||
|                 bufferType, | ||||
|                 parserBuilder.build(), | ||||
|                 visitorBuilder.build(configuration, renderProps), | ||||
|                 Collections.unmodifiableList(plugins) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static List<MarkwonPlugin> preparePlugins(@NonNull List<MarkwonPlugin> plugins) { | ||||
|         return new RegistryImpl(plugins).process(); | ||||
|     } | ||||
| } | ||||
| @ -1,18 +1,15 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import ru.noties.markwon.core.MarkwonTheme; | ||||
| import ru.noties.markwon.core.spans.LinkSpan; | ||||
| import ru.noties.markwon.html.MarkwonHtmlParser; | ||||
| import ru.noties.markwon.html.MarkwonHtmlRenderer; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.image.ImageSizeResolver; | ||||
| import ru.noties.markwon.image.ImageSizeResolverDef; | ||||
| import ru.noties.markwon.syntax.SyntaxHighlight; | ||||
| import ru.noties.markwon.syntax.SyntaxHighlightNoOp; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessorNoOp; | ||||
| import io.noties.markwon.core.MarkwonTheme; | ||||
| import io.noties.markwon.image.AsyncDrawableLoader; | ||||
| import io.noties.markwon.image.ImageSizeResolver; | ||||
| import io.noties.markwon.image.ImageSizeResolverDef; | ||||
| import io.noties.markwon.syntax.SyntaxHighlight; | ||||
| import io.noties.markwon.syntax.SyntaxHighlightNoOp; | ||||
| import io.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import io.noties.markwon.urlprocessor.UrlProcessorNoOp; | ||||
| 
 | ||||
| /** | ||||
|  * since 3.0.0 renamed `SpannableConfiguration` -> `MarkwonConfiguration` | ||||
| @ -28,11 +25,9 @@ public class MarkwonConfiguration { | ||||
|     private final MarkwonTheme theme; | ||||
|     private final AsyncDrawableLoader asyncDrawableLoader; | ||||
|     private final SyntaxHighlight syntaxHighlight; | ||||
|     private final LinkSpan.Resolver linkResolver; | ||||
|     private final LinkResolver linkResolver; | ||||
|     private final UrlProcessor urlProcessor; | ||||
|     private final ImageSizeResolver imageSizeResolver; | ||||
|     private final MarkwonHtmlParser htmlParser; | ||||
|     private final MarkwonHtmlRenderer htmlRenderer; | ||||
| 
 | ||||
|     // @since 3.0.0 | ||||
|     private final MarkwonSpansFactory spansFactory; | ||||
| @ -45,8 +40,6 @@ public class MarkwonConfiguration { | ||||
|         this.urlProcessor = builder.urlProcessor; | ||||
|         this.imageSizeResolver = builder.imageSizeResolver; | ||||
|         this.spansFactory = builder.spansFactory; | ||||
|         this.htmlParser = builder.htmlParser; | ||||
|         this.htmlRenderer = builder.htmlRenderer; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
| @ -65,7 +58,7 @@ public class MarkwonConfiguration { | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public LinkSpan.Resolver linkResolver() { | ||||
|     public LinkResolver linkResolver() { | ||||
|         return linkResolver; | ||||
|     } | ||||
| 
 | ||||
| @ -79,16 +72,6 @@ public class MarkwonConfiguration { | ||||
|         return imageSizeResolver; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public MarkwonHtmlParser htmlParser() { | ||||
|         return htmlParser; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public MarkwonHtmlRenderer htmlRenderer() { | ||||
|         return htmlRenderer; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @since 3.0.0 | ||||
|      */ | ||||
| @ -103,16 +86,23 @@ public class MarkwonConfiguration { | ||||
|         private MarkwonTheme theme; | ||||
|         private AsyncDrawableLoader asyncDrawableLoader; | ||||
|         private SyntaxHighlight syntaxHighlight; | ||||
|         private LinkSpan.Resolver linkResolver; | ||||
|         private LinkResolver linkResolver; | ||||
|         private UrlProcessor urlProcessor; | ||||
|         private ImageSizeResolver imageSizeResolver; | ||||
|         private MarkwonHtmlParser htmlParser; | ||||
|         private MarkwonHtmlRenderer htmlRenderer; | ||||
|         private MarkwonSpansFactory spansFactory; | ||||
| 
 | ||||
|         Builder() { | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @since 4.0.0 | ||||
|          */ | ||||
|         @NonNull | ||||
|         public Builder asyncDrawableLoader(@NonNull AsyncDrawableLoader asyncDrawableLoader) { | ||||
|             this.asyncDrawableLoader = asyncDrawableLoader; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         public Builder syntaxHighlight(@NonNull SyntaxHighlight syntaxHighlight) { | ||||
|             this.syntaxHighlight = syntaxHighlight; | ||||
| @ -120,7 +110,7 @@ public class MarkwonConfiguration { | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         public Builder linkResolver(@NonNull LinkSpan.Resolver linkResolver) { | ||||
|         public Builder linkResolver(@NonNull LinkResolver linkResolver) { | ||||
|             this.linkResolver = linkResolver; | ||||
|             return this; | ||||
|         } | ||||
| @ -131,12 +121,6 @@ public class MarkwonConfiguration { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         public Builder htmlParser(@NonNull MarkwonHtmlParser htmlParser) { | ||||
|             this.htmlParser = htmlParser; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @since 1.0.1 | ||||
|          */ | ||||
| @ -149,15 +133,16 @@ public class MarkwonConfiguration { | ||||
|         @NonNull | ||||
|         public MarkwonConfiguration build( | ||||
|                 @NonNull MarkwonTheme theme, | ||||
|                 @NonNull AsyncDrawableLoader asyncDrawableLoader, | ||||
|                 @NonNull MarkwonHtmlRenderer htmlRenderer, | ||||
|                 @NonNull MarkwonSpansFactory spansFactory) { | ||||
| 
 | ||||
|             this.theme = theme; | ||||
|             this.asyncDrawableLoader = asyncDrawableLoader; | ||||
|             this.htmlRenderer = htmlRenderer; | ||||
|             this.spansFactory = spansFactory; | ||||
| 
 | ||||
|             // @since 4.0.0 | ||||
|             if (asyncDrawableLoader == null) { | ||||
|                 asyncDrawableLoader = AsyncDrawableLoader.noOp(); | ||||
|             } | ||||
| 
 | ||||
|             if (syntaxHighlight == null) { | ||||
|                 syntaxHighlight = new SyntaxHighlightNoOp(); | ||||
|             } | ||||
| @ -174,10 +159,6 @@ public class MarkwonConfiguration { | ||||
|                 imageSizeResolver = new ImageSizeResolverDef(); | ||||
|             } | ||||
| 
 | ||||
|             if (htmlParser == null) { | ||||
|                 htmlParser = MarkwonHtmlParser.noOp(); | ||||
|             } | ||||
| 
 | ||||
|             return new MarkwonConfiguration(this); | ||||
|         } | ||||
|     } | ||||
| @ -1,10 +1,11 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spanned; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| @ -1,30 +1,58 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.text.Spanned; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| import ru.noties.markwon.core.MarkwonTheme; | ||||
| import ru.noties.markwon.html.MarkwonHtmlRenderer; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.image.MediaDecoder; | ||||
| import ru.noties.markwon.image.SchemeHandler; | ||||
| import ru.noties.markwon.priority.Priority; | ||||
| import io.noties.markwon.core.CorePlugin; | ||||
| import io.noties.markwon.core.MarkwonTheme; | ||||
| import io.noties.markwon.image.AsyncDrawableSpan; | ||||
| import io.noties.markwon.movement.MovementMethodPlugin; | ||||
| 
 | ||||
| /** | ||||
|  * Class represents a plugin (extension) to Markwon to configure how parsing and rendering | ||||
|  * of markdown is carried on. | ||||
|  * | ||||
|  * @see AbstractMarkwonPlugin | ||||
|  * @see ru.noties.markwon.core.CorePlugin | ||||
|  * @see ru.noties.markwon.image.ImagesPlugin | ||||
|  * @see CorePlugin | ||||
|  * @see MovementMethodPlugin | ||||
|  * @since 3.0.0 | ||||
|  */ | ||||
| public interface MarkwonPlugin { | ||||
| 
 | ||||
|     /** | ||||
|      * @see Registry#require(Class, Action) | ||||
|      * @since 4.0.0 | ||||
|      */ | ||||
|     interface Action<P extends MarkwonPlugin> { | ||||
|         void apply(@NonNull P p); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see #configure(Registry) | ||||
|      * @since 4.0.0 | ||||
|      */ | ||||
|     interface Registry { | ||||
| 
 | ||||
|         @NonNull | ||||
|         <P extends MarkwonPlugin> P require(@NonNull Class<P> plugin); | ||||
| 
 | ||||
|         <P extends MarkwonPlugin> void require( | ||||
|                 @NonNull Class<P> plugin, | ||||
|                 @NonNull Action<? super P> action); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This method will be called before any other during {@link Markwon} instance construction. | ||||
|      * | ||||
|      * @since 4.0.0 | ||||
|      */ | ||||
|     void configure(@NonNull Registry registry); | ||||
| 
 | ||||
|     /** | ||||
|      * Method to configure <code>org.commonmark.parser.Parser</code> (for example register custom | ||||
|      * extension, etc). | ||||
| @ -39,17 +67,6 @@ public interface MarkwonPlugin { | ||||
|      */ | ||||
|     void configureTheme(@NonNull MarkwonTheme.Builder builder); | ||||
| 
 | ||||
|     /** | ||||
|      * Configure image loading functionality. For example add new content-types | ||||
|      * {@link AsyncDrawableLoader.Builder#addMediaDecoder(String, MediaDecoder)}, a transport | ||||
|      * layer (network, file, etc) {@link AsyncDrawableLoader.Builder#addSchemeHandler(String, SchemeHandler)} | ||||
|      * or modify existing properties. | ||||
|      * | ||||
|      * @see AsyncDrawableLoader | ||||
|      * @see AsyncDrawableLoader.Builder | ||||
|      */ | ||||
|     void configureImages(@NonNull AsyncDrawableLoader.Builder builder); | ||||
| 
 | ||||
|     /** | ||||
|      * Configure {@link MarkwonConfiguration} | ||||
|      * | ||||
| @ -74,17 +91,6 @@ public interface MarkwonPlugin { | ||||
|      */ | ||||
|     void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder); | ||||
| 
 | ||||
|     /** | ||||
|      * Configure {@link MarkwonHtmlRenderer} to add or remove HTML {@link ru.noties.markwon.html.TagHandler}s | ||||
|      * | ||||
|      * @see MarkwonHtmlRenderer | ||||
|      * @see MarkwonHtmlRenderer.Builder | ||||
|      */ | ||||
|     void configureHtmlRenderer(@NonNull MarkwonHtmlRenderer.Builder builder); | ||||
| 
 | ||||
|     @NonNull | ||||
|     Priority priority(); | ||||
| 
 | ||||
|     /** | ||||
|      * Process input markdown and return new string to be used in parsing stage further. | ||||
|      * Can be described as <code>pre-processing</code> of markdown String. | ||||
| @ -117,9 +123,9 @@ public interface MarkwonPlugin { | ||||
|     /** | ||||
|      * This method will be called <strong>before</strong> calling <code>TextView#setText</code>. | ||||
|      * <p> | ||||
|      * It can be useful to prepare a TextView for markdown. For example {@link ru.noties.markwon.image.ImagesPlugin} | ||||
|      * uses this method to unregister previously registered {@link ru.noties.markwon.image.AsyncDrawableSpan} | ||||
|      * (if there are such spans in this TextView at this point). Or {@link ru.noties.markwon.core.CorePlugin} | ||||
|      * It can be useful to prepare a TextView for markdown. For example {@code ru.noties.markwon.image.ImagesPlugin} | ||||
|      * uses this method to unregister previously registered {@link AsyncDrawableSpan} | ||||
|      * (if there are such spans in this TextView at this point). Or {@link CorePlugin} | ||||
|      * which measures ordered list numbers | ||||
|      * | ||||
|      * @param textView TextView to which <code>markdown</code> will be applied | ||||
| @ -130,8 +136,8 @@ public interface MarkwonPlugin { | ||||
|     /** | ||||
|      * This method will be called <strong>after</strong> markdown was applied. | ||||
|      * <p> | ||||
|      * It can be useful to trigger certain action on spans/textView. For example {@link ru.noties.markwon.image.ImagesPlugin} | ||||
|      * uses this method to register {@link ru.noties.markwon.image.AsyncDrawableSpan} and start | ||||
|      * It can be useful to trigger certain action on spans/textView. For example {@code ru.noties.markwon.image.ImagesPlugin} | ||||
|      * uses this method to register {@link AsyncDrawableSpan} and start | ||||
|      * asynchronously loading images. | ||||
|      * <p> | ||||
|      * Unlike {@link #beforeSetText(TextView, Spanned)} this method does not receive parsed markdown | ||||
| @ -1,6 +1,6 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| 
 | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| 
 | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| 
 | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.node.Visitor; | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import org.commonmark.node.BlockQuote; | ||||
| import org.commonmark.node.BulletList; | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * Class to hold data in {@link RenderProps}. Represents a certain <em>property</em>. | ||||
							
								
								
									
										116
									
								
								markwon-core/src/main/java/io/noties/markwon/RegistryImpl.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								markwon-core/src/main/java/io/noties/markwon/RegistryImpl.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| import io.noties.markwon.core.CorePlugin; | ||||
| 
 | ||||
| // @since 4.0.0 | ||||
| class RegistryImpl implements MarkwonPlugin.Registry { | ||||
| 
 | ||||
|     private final List<MarkwonPlugin> origin; | ||||
|     private final List<MarkwonPlugin> plugins; | ||||
|     private final Set<MarkwonPlugin> pending; | ||||
| 
 | ||||
|     RegistryImpl(@NonNull List<MarkwonPlugin> origin) { | ||||
|         this.origin = origin; | ||||
|         this.plugins = new ArrayList<>(origin.size()); | ||||
|         this.pending = new HashSet<>(3); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public <P extends MarkwonPlugin> P require(@NonNull Class<P> plugin) { | ||||
|         return get(plugin); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public <P extends MarkwonPlugin> void require( | ||||
|             @NonNull Class<P> plugin, | ||||
|             @NonNull MarkwonPlugin.Action<? super P> action) { | ||||
|         action.apply(get(plugin)); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     List<MarkwonPlugin> process() { | ||||
|         for (MarkwonPlugin plugin : origin) { | ||||
|             configure(plugin); | ||||
|         } | ||||
|         return plugins; | ||||
|     } | ||||
| 
 | ||||
|     private void configure(@NonNull MarkwonPlugin plugin) { | ||||
| 
 | ||||
|         // important -> check if it's in plugins | ||||
|         //  if it is -> no need to configure (already configured) | ||||
| 
 | ||||
|         if (!plugins.contains(plugin)) { | ||||
| 
 | ||||
|             if (pending.contains(plugin)) { | ||||
|                 throw new IllegalStateException("Cyclic dependency chain found: " + pending); | ||||
|             } | ||||
| 
 | ||||
|             // start tracking plugins that are pending for configuration | ||||
|             pending.add(plugin); | ||||
| 
 | ||||
|             plugin.configure(this); | ||||
| 
 | ||||
|             // stop pending tracking | ||||
|             pending.remove(plugin); | ||||
| 
 | ||||
|             // check again if it's included (a child might've configured it already) | ||||
|             // add to out-collection if not already present | ||||
|             // this is a bit different from `find` method as it does check for exact instance | ||||
|             // and not a sub-type | ||||
|             if (!plugins.contains(plugin)) { | ||||
|                 // core-plugin must always be the first one (if it's present) | ||||
|                 if (CorePlugin.class.isAssignableFrom(plugin.getClass())) { | ||||
|                     plugins.add(0, plugin); | ||||
|                 } else { | ||||
|                     plugins.add(plugin); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private <P extends MarkwonPlugin> P get(@NonNull Class<P> type) { | ||||
| 
 | ||||
|         // check if present already in plugins | ||||
|         // find in origin, if not found -> throw, else add to out-plugins | ||||
| 
 | ||||
|         P plugin = find(plugins, type); | ||||
| 
 | ||||
|         if (plugin == null) { | ||||
| 
 | ||||
|             plugin = find(origin, type); | ||||
| 
 | ||||
|             if (plugin == null) { | ||||
|                 throw new IllegalStateException("Requested plugin is not added: " + | ||||
|                         "" + type.getName() + ", plugins: " + origin); | ||||
|             } | ||||
| 
 | ||||
|             configure(plugin); | ||||
|         } | ||||
| 
 | ||||
|         return plugin; | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     private static <P extends MarkwonPlugin> P find( | ||||
|             @NonNull List<MarkwonPlugin> plugins, | ||||
|             @NonNull Class<P> type) { | ||||
|         for (MarkwonPlugin plugin : plugins) { | ||||
|             if (type.isAssignableFrom(plugin.getClass())) { | ||||
|                 //noinspection unchecked | ||||
|                 return (P) plugin; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * @since 3.0.0 | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| @ -1,7 +1,7 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| /** | ||||
|  * @since 3.0.0 | ||||
| @ -1,11 +1,12 @@ | ||||
| package ru.noties.markwon; | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.annotation.VisibleForTesting; | ||||
| import android.text.SpannableStringBuilder; | ||||
| import android.text.Spanned; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.VisibleForTesting; | ||||
| 
 | ||||
| import java.util.ArrayDeque; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| @ -1,12 +1,13 @@ | ||||
| package ru.noties.markwon.core; | ||||
| package io.noties.markwon.core; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.annotation.VisibleForTesting; | ||||
| import android.text.Spanned; | ||||
| import android.text.method.LinkMovementMethod; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.VisibleForTesting; | ||||
| 
 | ||||
| import org.commonmark.node.BlockQuote; | ||||
| import org.commonmark.node.BulletList; | ||||
| import org.commonmark.node.Code; | ||||
| @ -14,6 +15,7 @@ import org.commonmark.node.Emphasis; | ||||
| import org.commonmark.node.FencedCodeBlock; | ||||
| import org.commonmark.node.HardLineBreak; | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.Image; | ||||
| import org.commonmark.node.IndentedCodeBlock; | ||||
| import org.commonmark.node.Link; | ||||
| import org.commonmark.node.ListBlock; | ||||
| @ -26,21 +28,27 @@ import org.commonmark.node.StrongEmphasis; | ||||
| import org.commonmark.node.Text; | ||||
| import org.commonmark.node.ThematicBreak; | ||||
| 
 | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonSpansFactory; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.core.factory.BlockQuoteSpanFactory; | ||||
| import ru.noties.markwon.core.factory.CodeBlockSpanFactory; | ||||
| import ru.noties.markwon.core.factory.CodeSpanFactory; | ||||
| import ru.noties.markwon.core.factory.EmphasisSpanFactory; | ||||
| import ru.noties.markwon.core.factory.HeadingSpanFactory; | ||||
| import ru.noties.markwon.core.factory.LinkSpanFactory; | ||||
| import ru.noties.markwon.core.factory.ListItemSpanFactory; | ||||
| import ru.noties.markwon.core.factory.StrongEmphasisSpanFactory; | ||||
| import ru.noties.markwon.core.factory.ThematicBreakSpanFactory; | ||||
| import ru.noties.markwon.core.spans.OrderedListItemSpan; | ||||
| import ru.noties.markwon.priority.Priority; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import io.noties.markwon.AbstractMarkwonPlugin; | ||||
| import io.noties.markwon.MarkwonConfiguration; | ||||
| import io.noties.markwon.MarkwonSpansFactory; | ||||
| import io.noties.markwon.MarkwonVisitor; | ||||
| import io.noties.markwon.RenderProps; | ||||
| import io.noties.markwon.SpanFactory; | ||||
| import io.noties.markwon.SpannableBuilder; | ||||
| import io.noties.markwon.core.factory.BlockQuoteSpanFactory; | ||||
| import io.noties.markwon.core.factory.CodeBlockSpanFactory; | ||||
| import io.noties.markwon.core.factory.CodeSpanFactory; | ||||
| import io.noties.markwon.core.factory.EmphasisSpanFactory; | ||||
| import io.noties.markwon.core.factory.HeadingSpanFactory; | ||||
| import io.noties.markwon.core.factory.LinkSpanFactory; | ||||
| import io.noties.markwon.core.factory.ListItemSpanFactory; | ||||
| import io.noties.markwon.core.factory.StrongEmphasisSpanFactory; | ||||
| import io.noties.markwon.core.factory.ThematicBreakSpanFactory; | ||||
| import io.noties.markwon.core.spans.OrderedListItemSpan; | ||||
| import io.noties.markwon.image.ImageProps; | ||||
| 
 | ||||
| /** | ||||
|  * @see CoreProps | ||||
| @ -48,14 +56,57 @@ import ru.noties.markwon.priority.Priority; | ||||
|  */ | ||||
| public class CorePlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|     /** | ||||
|      * @see #addOnTextAddedListener(OnTextAddedListener) | ||||
|      * @since 4.0.0 | ||||
|      */ | ||||
|     public interface OnTextAddedListener { | ||||
| 
 | ||||
|         /** | ||||
|          * Will be called when new text is added to resulting {@link SpannableBuilder}. | ||||
|          * Please note that only text represented by {@link Text} node will trigger this callback | ||||
|          * (text inside code and code-blocks won\'t trigger it). | ||||
|          * <p> | ||||
|          * Please note that if you wish to add spans you must use {@code start} parameter | ||||
|          * in order to place spans correctly ({@code start} represents the index at which {@code text} | ||||
|          * was added). So, to set a span for the whole length of the text added one should use: | ||||
|          * <p> | ||||
|          * {@code | ||||
|          * visitor.builder().setSpan(new MySpan(), start, start + text.length(), 0); | ||||
|          * } | ||||
|          * | ||||
|          * @param visitor {@link MarkwonVisitor} | ||||
|          * @param text    literal that had been added | ||||
|          * @param start   index in {@code visitor} as which text had been added | ||||
|          * @see #addOnTextAddedListener(OnTextAddedListener) | ||||
|          */ | ||||
|         void onTextAdded(@NonNull MarkwonVisitor visitor, @NonNull String text, int start); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static CorePlugin create() { | ||||
|         return new CorePlugin(); | ||||
|     } | ||||
| 
 | ||||
|     // @since 4.0.0 | ||||
|     private final List<OnTextAddedListener> onTextAddedListeners = new ArrayList<>(0); | ||||
| 
 | ||||
|     protected CorePlugin() { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Can be useful to post-process text added. For example for auto-linking capabilities. | ||||
|      * | ||||
|      * @see OnTextAddedListener | ||||
|      * @since 4.0.0 | ||||
|      */ | ||||
|     @SuppressWarnings("UnusedReturnValue") | ||||
|     @NonNull | ||||
|     public CorePlugin addOnTextAddedListener(@NonNull OnTextAddedListener onTextAddedListener) { | ||||
|         onTextAddedListeners.add(onTextAddedListener); | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         text(builder); | ||||
| @ -65,6 +116,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|         code(builder); | ||||
|         fencedCodeBlock(builder); | ||||
|         indentedCodeBlock(builder); | ||||
|         image(builder); | ||||
|         bulletList(builder); | ||||
|         orderedList(builder); | ||||
|         listItem(builder); | ||||
| @ -95,12 +147,6 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|                 .setFactory(ThematicBreak.class, new ThematicBreakSpanFactory()); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Priority priority() { | ||||
|         return Priority.none(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { | ||||
|         OrderedListItemSpan.measure(textView, markdown); | ||||
| @ -116,11 +162,23 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void text(@NonNull MarkwonVisitor.Builder builder) { | ||||
|     private void text(@NonNull MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Text.class, new MarkwonVisitor.NodeVisitor<Text>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) { | ||||
|                 visitor.builder().append(text.getLiteral()); | ||||
| 
 | ||||
|                 final String literal = text.getLiteral(); | ||||
| 
 | ||||
|                 visitor.builder().append(literal); | ||||
| 
 | ||||
|                 // @since 4.0.0 | ||||
|                 if (!onTextAddedListeners.isEmpty()) { | ||||
|                     // calculate the start position | ||||
|                     final int length = visitor.length() - literal.length(); | ||||
|                     for (OnTextAddedListener onTextAddedListener : onTextAddedListeners) { | ||||
|                         onTextAddedListener.onTextAdded(visitor, literal, length); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| @ -204,6 +262,53 @@ public class CorePlugin extends AbstractMarkwonPlugin { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     // @since 4.0.0 | ||||
|     // his method is moved from ImagesPlugin. Alternative implementations must set SpanFactory | ||||
|     // for Image node in order for this visitor to function | ||||
|     private static void image(MarkwonVisitor.Builder builder) { | ||||
|         builder.on(Image.class, new MarkwonVisitor.NodeVisitor<Image>() { | ||||
|             @Override | ||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) { | ||||
| 
 | ||||
|                 // if there is no image spanFactory, ignore | ||||
|                 final SpanFactory spanFactory = visitor.configuration().spansFactory().get(Image.class); | ||||
|                 if (spanFactory == null) { | ||||
|                     visitor.visitChildren(image); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 final int length = visitor.length(); | ||||
| 
 | ||||
|                 visitor.visitChildren(image); | ||||
| 
 | ||||
|                 // we must check if anything _was_ added, as we need at least one char to render | ||||
|                 if (length == visitor.length()) { | ||||
|                     visitor.builder().append('\uFFFC'); | ||||
|                 } | ||||
| 
 | ||||
|                 final MarkwonConfiguration configuration = visitor.configuration(); | ||||
| 
 | ||||
|                 final Node parent = image.getParent(); | ||||
|                 final boolean link = parent instanceof Link; | ||||
| 
 | ||||
|                 final String destination = configuration | ||||
|                         .urlProcessor() | ||||
|                         .process(image.getDestination()); | ||||
| 
 | ||||
|                 final RenderProps props = visitor.renderProps(); | ||||
| 
 | ||||
|                 // apply image properties | ||||
|                 // Please note that we explicitly set IMAGE_SIZE to null as we do not clear | ||||
|                 // properties after we applied span (we could though) | ||||
|                 ImageProps.DESTINATION.set(props, destination); | ||||
|                 ImageProps.REPLACEMENT_TEXT_IS_LINK.set(props, link); | ||||
|                 ImageProps.IMAGE_SIZE.set(props, null); | ||||
| 
 | ||||
|                 visitor.setSpans(length, spanFactory.getSpans(configuration, props)); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @VisibleForTesting | ||||
|     static void visitCodeBlock( | ||||
|             @NonNull MarkwonVisitor visitor, | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry
						Dimitry