Mainly layouts and refacts

This commit is contained in:
Paul Schneider 2016-10-14 16:21:03 +02:00
commit 7b3340cbc3
23 changed files with 629 additions and 417 deletions

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Webkit;
using Java.Interop;
namespace BookAStar.Droid.Markdown
{
public class JsBridgeMarkdown : Java.Lang.Object
{
readonly WeakReference<MarkdownViewRenderer> hybridWebViewRenderer;
public JsBridgeMarkdown(MarkdownViewRenderer hybridRenderer)
{
hybridWebViewRenderer = new WeakReference<MarkdownViewRenderer>(hybridRenderer);
}
[JavascriptInterface]
[Export("contentEdited")]
public void ContentEdited(string data)
{
MarkdownViewRenderer hybridRenderer;
if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget(out hybridRenderer))
{
hybridRenderer.Element.Markdown = data;
}
}
[JavascriptInterface]
[Export("jsLoaded")]
public void JSLoaded()
{
}
}
}

View file

@ -0,0 +1,397 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// Ce code a été généré par un outil.
// Version du runtime :4.0.30319.42000
//
// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si
// le code est régénéré.
// </auto-generated>
//------------------------------------------------------------------------------
namespace BookAStar.Droid.Markdown
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorTemplatePreprocessor", "4.2.0.703")]
public partial class MarkdownEditor : MarkdownEditorBase
{
#line hidden
#line 1 "MarkdownEditor.cshtml"
public MarkdownViewModel Model { get; set; }
#line default
#line hidden
public override void Execute()
{
WriteLiteral("<!DOCTYPE html>\r\n<html>\r\n<head>\r\n <meta");
WriteLiteral(" charset=\"utf-8\"");
WriteLiteral(">\r\n <style>\r\n .standalone-container {\r\n margin: 0;\r\n " +
" width: 100%;\r\n height: 100%;\r\n }\r\n </style>\r\n");
#line 13 "MarkdownEditor.cshtml"
#line default
#line hidden
#line 13 "MarkdownEditor.cshtml"
if (Model.Editable)
{
#line default
#line hidden
WriteLiteral(" <link");
WriteLiteral(" rel=\"stylesheet\"");
WriteLiteral(" href=\"quill.snow.css\"");
WriteLiteral(" />\r\n");
WriteLiteral(@" <style>
#bubble-container {
width: 100%;
height: 100%;
}
#bubble-container div.ql-editor {
padding-top:3em;
}
.hidden {
display: none;
}
</style>
");
#line 30 "MarkdownEditor.cshtml"
}
#line default
#line hidden
WriteLiteral("</head>\r\n<body>\r\n <div");
WriteLiteral(" class=\"standalone-container\"");
WriteLiteral(">\r\n <div");
WriteLiteral(" id=\"bubble-container\"");
WriteLiteral(">");
#line 34 "MarkdownEditor.cshtml"
Write(Html.Write(Model.GetHtml()));
#line default
#line hidden
WriteLiteral("</div>\r\n </div>\r\n <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(" src=\"jquery.js\"");
WriteLiteral("></script>\r\n");
#line 37 "MarkdownEditor.cshtml"
#line default
#line hidden
#line 37 "MarkdownEditor.cshtml"
if (Model.Editable)
{
#line default
#line hidden
WriteLiteral(" <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(" src=\"quill.min.js\"");
WriteLiteral("></script>\r\n");
WriteLiteral(" <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(" src=\"showdown.js\"");
WriteLiteral("></script>\r\n");
WriteLiteral(" <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(" src=\"to-markdown.js\"");
WriteLiteral("></script>\r\n");
WriteLiteral(" <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(" src=\"md-helpers.js\"");
WriteLiteral("></script>\r\n");
#line 43 "MarkdownEditor.cshtml"
#line default
#line hidden
WriteLiteral(" <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(">\r\n var toolbarOptions = [\r\n [\'bold\', \'italic\', \'underline\', \'str" +
"ike\'], // toggled buttons\r\n [\'blockquote\', \'code-block\'],\r\n [{ " +
"\'header\': 1 }, { \'header\': 2 }, { \'header\': 3 }], // custom button" +
" values\r\n [{ \'list\': \'ordered\' }, { \'list\': \'bullet\' }],\r\n [{ \'indent\'" +
": \'-1\' }, { \'indent\': \'+1\' }], // outdent/indent\r\n [\'link\', \'image" +
"\', \'video\'],\r\n [\'clean\'] // remove " +
"formatting button\r\n ];\r\n\r\n var showImageUI = function (val" +
"ue) {\r\n if (value) {\r\n var href = prompt(\'Ente" +
"r the URL\');\r\n this.quill.format(\'image\', href);\r\n " +
" } else {\r\n this.quill.format(\'image\', false);\r\n " +
" }\r\n };\r\n\r\n $(document).ready(function () {\r\n " +
" var quill = new Quill(\'#bubble-container\', {\r\n modul" +
"es: {\r\n toolbar: toolbarOptions\r\n },\r\n" +
" placeholder: \'Composez votre texte ...\',\r\n " +
" theme: \'snow\'\r\n });\r\n\r\n function getMD() {\r\n " +
" return markdownize($(\'#bubble-container div.ql-editor\').html())\r" +
"\n }\r\n quill.on(\'text-change\', function (delta, old" +
"Delta, source) {\r\n if (source === \"user\") {\r\n " +
" contentEdited(getMD());\r\n }\r\n });\r\n " +
" var toolbar = quill.getModule(\'toolbar\');\r\n toolbar." +
"addHandler(\'image\', showImageUI);\r\n jsLoaded();\r\n });\r" +
"\n </script>\r\n");
#line 86 "MarkdownEditor.cshtml"
}
else
{
#line default
#line hidden
WriteLiteral(" <script");
WriteLiteral(" type=\"text/javascript\"");
WriteLiteral(">\r\n $(document).ready(function() {\r\n jsLoaded();\r\n });\r\n" +
" </script>\r\n");
#line 94 "MarkdownEditor.cshtml"
}
#line default
#line hidden
WriteLiteral("</body>\r\n</html>\r\n");
}
}
// NOTE: this is the default generated helper class. You may choose to extract it to a separate file
// in order to customize it or share it between multiple templates, and specify the template's base
// class via the @inherits directive.
public abstract class MarkdownEditorBase
{
// This field is OPTIONAL, but used by the default implementation of Generate, Write, WriteAttribute and WriteLiteral
//
System.IO.TextWriter __razor_writer;
// This method is OPTIONAL
//
/// <summary>Executes the template and returns the output as a string.</summary>
/// <returns>The template output.</returns>
public string GenerateString ()
{
using (var sw = new System.IO.StringWriter ()) {
Generate (sw);
return sw.ToString ();
}
}
// This method is OPTIONAL, you may choose to implement Write and WriteLiteral without use of __razor_writer
// and provide another means of invoking Execute.
//
/// <summary>Executes the template, writing to the provided text writer.</summary>
/// <param name="writer">The TextWriter to which to write the template output.</param>
public void Generate (System.IO.TextWriter writer)
{
__razor_writer = writer;
Execute ();
__razor_writer = null;
}
// This method is REQUIRED, but you may choose to implement it differently
//
/// <summary>Writes a literal value to the template output without HTML escaping it.</summary>
/// <param name="value">The literal value.</param>
protected void WriteLiteral (string value)
{
__razor_writer.Write (value);
}
// This method is REQUIRED if the template contains any Razor helpers, but you may choose to implement it differently
//
/// <summary>Writes a literal value to the TextWriter without HTML escaping it.</summary>
/// <param name="writer">The TextWriter to which to write the literal.</param>
/// <param name="value">The literal value.</param>
protected static void WriteLiteralTo (System.IO.TextWriter writer, string value)
{
writer.Write (value);
}
// This method is REQUIRED, but you may choose to implement it differently
//
/// <summary>Writes a value to the template output, HTML escaping it if necessary.</summary>
/// <param name="value">The value.</param>
/// <remarks>The value may be a Action<System.IO.TextWriter>, as returned by Razor helpers.</remarks>
protected void Write (object value)
{
WriteTo (__razor_writer, value);
}
// This method is REQUIRED if the template contains any Razor helpers, but you may choose to implement it differently
//
/// <summary>Writes an object value to the TextWriter, HTML escaping it if necessary.</summary>
/// <param name="writer">The TextWriter to which to write the value.</param>
/// <param name="value">The value.</param>
/// <remarks>The value may be a Action<System.IO.TextWriter>, as returned by Razor helpers.</remarks>
protected static void WriteTo (System.IO.TextWriter writer, object value)
{
if (value == null)
return;
var write = value as Action<System.IO.TextWriter>;
if (write != null) {
write (writer);
return;
}
//NOTE: a more sophisticated implementation would write safe and pre-escaped values directly to the
//instead of double-escaping. See System.Web.IHtmlString in ASP.NET 4.0 for an example of this.
writer.Write(System.Net.WebUtility.HtmlEncode (value.ToString ()));
}
// This method is REQUIRED, but you may choose to implement it differently
//
/// <summary>
/// Conditionally writes an attribute to the template output.
/// </summary>
/// <param name="name">The name of the attribute.</param>
/// <param name="prefix">The prefix of the attribute.</param>
/// <param name="suffix">The suffix of the attribute.</param>
/// <param name="values">Attribute values, each specifying a prefix, value and whether it's a literal.</param>
protected void WriteAttribute (string name, string prefix, string suffix, params Tuple<string,object,bool>[] values)
{
WriteAttributeTo (__razor_writer, name, prefix, suffix, values);
}
// This method is REQUIRED if the template contains any Razor helpers, but you may choose to implement it differently
//
/// <summary>
/// Conditionally writes an attribute to a TextWriter.
/// </summary>
/// <param name="writer">The TextWriter to which to write the attribute.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="prefix">The prefix of the attribute.</param>
/// <param name="suffix">The suffix of the attribute.</param>
/// <param name="values">Attribute values, each specifying a prefix, value and whether it's a literal.</param>
///<remarks>Used by Razor helpers to write attributes.</remarks>
protected static void WriteAttributeTo (System.IO.TextWriter writer, string name, string prefix, string suffix, params Tuple<string,object,bool>[] values)
{
// this is based on System.Web.WebPages.WebPageExecutingBase
// Copyright (c) Microsoft Open Technologies, Inc.
// Licensed under the Apache License, Version 2.0
if (values.Length == 0) {
// Explicitly empty attribute, so write the prefix and suffix
writer.Write (prefix);
writer.Write (suffix);
return;
}
bool first = true;
bool wroteSomething = false;
for (int i = 0; i < values.Length; i++) {
Tuple<string,object,bool> attrVal = values [i];
string attPrefix = attrVal.Item1;
object value = attrVal.Item2;
bool isLiteral = attrVal.Item3;
if (value == null) {
// Nothing to write
continue;
}
// The special cases here are that the value we're writing might already be a string, or that the
// value might be a bool. If the value is the bool 'true' we want to write the attribute name instead
// of the string 'true'. If the value is the bool 'false' we don't want to write anything.
//
// Otherwise the value is another object (perhaps an IHtmlString), and we'll ask it to format itself.
string stringValue;
bool? boolValue = value as bool?;
if (boolValue == true) {
stringValue = name;
} else if (boolValue == false) {
continue;
} else {
stringValue = value as string;
}
if (first) {
writer.Write (prefix);
first = false;
} else {
writer.Write (attPrefix);
}
if (isLiteral) {
writer.Write (stringValue ?? value);
} else {
WriteTo (writer, stringValue ?? value);
}
wroteSomething = true;
}
if (wroteSomething) {
writer.Write (suffix);
}
}
// This method is REQUIRED. The generated Razor subclass will override it with the generated code.
//
///<summary>Executes the template, writing output to the Write and WriteLiteral methods.</summary>.
///<remarks>Not intended to be called directly. Call the Generate method instead.</remarks>
public abstract void Execute ();
}
}
#pragma warning restore 1591

View file

@ -0,0 +1,96 @@
@model MarkdownViewModel
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.standalone-container {
margin: 0;
width: 100%;
height: 100%;
}
</style>
@if (Model.Editable)
{
<link rel="stylesheet" href="quill.snow.css" />
<style>
#bubble-container {
width: 100%;
height: 100%;
}
#bubble-container div.ql-editor {
padding-top:3em;
}
.hidden {
display: none;
}
</style>
}
</head>
<body>
<div class="standalone-container">
<div id="bubble-container">@Html.Write(Model.GetHtml())</div>
</div>
<script type="text/javascript" src="jquery.js"></script>
@if (Model.Editable)
{
<script type="text/javascript" src="quill.min.js"></script>
<script type="text/javascript" src="showdown.js"></script>
<script type="text/javascript" src="to-markdown.js"></script>
<script type="text/javascript" src="md-helpers.js"></script>
<script type="text/javascript">
var toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }, { 'header': 3 }], // custom button values
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'indent': '-1' }, { 'indent': '+1' }], // outdent/indent
['link', 'image', 'video'],
['clean'] // remove formatting button
];
var showImageUI = function (value) {
if (value) {
var href = prompt('Enter the URL');
this.quill.format('image', href);
} else {
this.quill.format('image', false);
}
};
$(document).ready(function () {
var quill = new Quill('#bubble-container', {
modules: {
toolbar: toolbarOptions
},
placeholder: 'Composez votre texte ...',
theme: 'snow'
});
function getMD() {
return markdownize($('#bubble-container div.ql-editor').html())
}
quill.on('text-change', function (delta, oldDelta, source) {
if (source === "user") {
contentEdited(getMD());
}
});
var toolbar = quill.getModule('toolbar');
toolbar.addHandler('image', showImageUI);
jsLoaded();
});
</script>
}
else
{
<script type="text/javascript">
$(document).ready(function() {
jsLoaded();
});
</script>
}
</body>
</html>

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace BookAStar.Droid.Markdown
{
public class MarkdownViewModel
{
protected static MarkdownDeep.Markdown markdown = new MarkdownDeep.Markdown();
public string Content { get; set; }
public bool Editable { get; set; }
public string GetHtml()
{
return markdown.Transform(Content);
}
public override string ToString()
{
return Content;
}
}
}

View file

@ -0,0 +1,100 @@
using BookAStar.Views;
using Android.Webkit;
using Xamarin.Forms.Platform.Android;
using BookAStar.Droid;
using System;
using Java.Interop;
using System.ComponentModel;
using Android.Views;
[assembly: Xamarin.Forms.ExportRenderer(typeof(MarkdownView), typeof(MarkdownViewRenderer))]
namespace BookAStar.Droid
{
using Markdown;
public class MarkdownViewRenderer : ViewRenderer<MarkdownView, WebView>
{
private WebView editorView;
private MarkdownEditor editorTemplate = new MarkdownEditor();
const string jsLoadedJavaScriptFunction = "function jsLoaded(){jsBridge.jsLoaded()}";
const string contentEditedJavaScriptFunction = "function contentEdited(data){jsBridge.contentEdited(data)}";
public WebView EditorView
{
get
{
return editorView;
}
}
/// <summary>
/// To be called once document finished loading
/// </summary>
/// <param name="xview"></param>
/// <param name="view"></param>
public static async void AdjustHeightRequest(MarkdownView xview, WebView view)
{
if (view == null || xview == null) return;
var vch = view.ContentHeight;
xview.HeightRequest = vch > xview.MinimumHeightRequest ? vch : xview.MinimumHeightRequest;
}
protected override void OnElementChanged(ElementChangedEventArgs<MarkdownView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
SetNativeControl(CreateNativeControl());
InjectJS(jsLoadedJavaScriptFunction);
InjectJS(contentEditedJavaScriptFunction);
}
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
// Subscribe
editorTemplate.Model = new Markdown.MarkdownViewModel
{ Content = e.NewElement.Markdown, Editable = e.NewElement.Editable };
var html = editorTemplate.GenerateString();
EditorView.LoadDataWithBaseURL("file:///android_asset/",
html, "text/html", "utf-8", null);
}
}
void InjectJS(string script)
{
if (Control != null)
{
Control.LoadUrl(string.Format("javascript: {0}", script));
}
}
private WebView CreateNativeControl()
{
editorView = new WebView(Context);
EditorView.SetWebChromeClient(
new MarkdownWebChromeClient()
);
EditorView.Settings.BuiltInZoomControls = false;
EditorView.Settings.JavaScriptEnabled = true;
EditorView.Settings.LoadsImagesAutomatically = true;
EditorView.Settings.SetAppCacheEnabled(true);
EditorView.Settings.AllowContentAccess = true;
EditorView.Settings.AllowFileAccess = true;
EditorView.Settings.AllowFileAccessFromFileURLs = true;
EditorView.Settings.AllowUniversalAccessFromFileURLs = true;
EditorView.Settings.BlockNetworkImage = false;
EditorView.Settings.BlockNetworkLoads = false;
EditorView.Settings.DomStorageEnabled = true;
EditorView.AddJavascriptInterface(new JsBridgeMarkdown(this), "jsBridge");
EditorView.ViewTreeObserver.PreDraw += ViewTreeObserver_PreDraw;
return EditorView;
}
private void ViewTreeObserver_PreDraw(object sender, ViewTreeObserver.PreDrawEventArgs e)
{
AdjustHeightRequest(Element, Control);
}
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Webkit;
namespace BookAStar.Droid.Markdown
{
class MarkdownWebChromeClient : WebChromeClient
{
/*public override void OnConsoleMessage(string message, int lineNumber, string sourceID)
{
base.OnConsoleMessage(message, lineNumber, sourceID);
}*/
}
}