diff options
author | Robin Appelman <robin@icewind.nl> | 2017-08-29 12:49:34 +0300 |
---|---|---|
committer | Robin Appelman <robin@icewind.nl> | 2017-08-29 12:49:34 +0300 |
commit | ee7fd7148c9b0fb40e91640057dfe42238faf094 (patch) | |
tree | 05baa8875225ce0fb254cfe4220f198e411b9ecc /js | |
parent | 8322441915a9baa85d99b663714ba65ba414f782 (diff) |
Make checkboxes clickable
Signed-off-by: Robin Appelman <robin@icewind.nl>
Diffstat (limited to 'js')
-rw-r--r-- | js/CheckboxPlugin.ts | 9 | ||||
-rw-r--r-- | js/PreviewPlugin.ts | 23 | ||||
-rw-r--r-- | js/Renderer.ts | 29 | ||||
-rw-r--r-- | js/SidebarPreview.ts | 23 | ||||
-rw-r--r-- | js/VideoPlugin.ts | 2 | ||||
-rw-r--r-- | js/editor.ts | 2 |
6 files changed, 67 insertions, 21 deletions
diff --git a/js/CheckboxPlugin.ts b/js/CheckboxPlugin.ts index 055ceeb..0bb6800 100644 --- a/js/CheckboxPlugin.ts +++ b/js/CheckboxPlugin.ts @@ -31,7 +31,7 @@ export function CheckBoxReplacer(md: MarkdownIt.MarkdownIt, userOptions: Partial }; const options = $.extend(defaults, userOptions); const pattern = /\[(X|\s|\_|\-)\]\s(.*)/i; - const createTokens = function (checked: boolean, label: string, Token: TokenConstructor): Token[] { + const createTokens = function (checked: boolean, label: string, Token: TokenConstructor, line: number): Token[] { const nodes: Token[] = []; let token: Token; @@ -60,6 +60,7 @@ export function CheckBoxReplacer(md: MarkdownIt.MarkdownIt, userOptions: Partial if (options.checkboxClass) { token.attrs.push(["class", options.checkboxClass]); } + token.attrs.push(["data-line", String(line)]); nodes.push(token); /** @@ -86,7 +87,7 @@ export function CheckBoxReplacer(md: MarkdownIt.MarkdownIt, userOptions: Partial return nodes; }; - const splitTextToken = function (original: Token, Token: TokenConstructor): Token[] { + const splitTextToken = function (original: Token, Token: TokenConstructor, line: number): Token[] { const text = original.content; const matches = text.match(pattern); if (matches === null) { @@ -95,14 +96,14 @@ export function CheckBoxReplacer(md: MarkdownIt.MarkdownIt, userOptions: Partial const value = matches[1]; const label = matches[2]; const checked = (value === "X" || value === "x"); - return createTokens(checked, label, Token); + return createTokens(checked, label, Token, line); }; return function (state: CheckboxReplacerState) { for (const token of state.tokens) { if (token.type === "inline") { token.children = ([] as Token[]).concat.apply([], - token.children.map(childToken => splitTextToken(childToken, state.Token)) + token.children.map(childToken => splitTextToken(childToken, state.Token, token.map ? token.map[0] : 0)) ); } } diff --git a/js/PreviewPlugin.ts b/js/PreviewPlugin.ts index 5323499..2a07095 100644 --- a/js/PreviewPlugin.ts +++ b/js/PreviewPlugin.ts @@ -3,10 +3,13 @@ import {UnderscoreStatic} from "underscore"; declare const _: UnderscoreStatic; +declare const aceEditor: AceAjax.Editor; + type onPopstate = (this: Window, ev: PopStateEvent) => any; export class PreviewPlugin { private renderer: Renderer; + private Range: new (startRow: number, startColumn: number, endRow: number, endColumn: number) => AceAjax.Range; private initPromise: JQueryPromise<void> | null = null; private textEditorOnHashChange: onPopstate | null; @@ -20,11 +23,15 @@ export class PreviewPlugin { deferred.resolve(); }); this.initPromise = deferred.promise(); - const onHashChange = window.onpopstate; if (!this.textEditorOnHashChange) { this.textEditorOnHashChange = window.onpopstate; } + + this.Range = window['ace'].require("ace/range").Range; + + aceEditor.$blockScrolling = Infinity; } + return this.initPromise; } @@ -37,6 +44,18 @@ export class PreviewPlugin { preview = _.throttle((text: string, element) => { window.onpopstate = this.onHashChange; - this.renderer.renderText(text, element); + const Range = this.Range; + this.renderer.renderText(text, element).then(() => { + element.find('input[type=checkbox]').change(function () { + const checked = this.checked; + const row = this.dataset.line; + const session = aceEditor.getSession(); + const oldText = session.getLine(row); + const newText = checked ? + oldText.replace('[ ]', '[x]') : + oldText.replace(/\[(x|X)\]/, '[ ]'); + session.replace(new Range(row, 0, row, Number.MAX_VALUE), newText); + }); + }); }, 500); }
\ No newline at end of file diff --git a/js/Renderer.ts b/js/Renderer.ts index 1b60d57..1f77c98 100644 --- a/js/Renderer.ts +++ b/js/Renderer.ts @@ -9,6 +9,7 @@ import VideoPlugin from './VideoPlugin'; import 'katex/dist/katex.min.css'; import 'highlight.js/styles/github.css'; import 'mermaid/dist/mermaid.forest.min.css'; +import Thenable = JQuery.Thenable; const slugifyHeading = name => 'editor/' + slugify(name).toLowerCase(); @@ -74,7 +75,8 @@ export class Renderer { constructor() { this.md = new MarkdownIt(); this.md.use(CheckboxPlugin, { - checkboxClass: 'checkbox' + checkboxClass: 'checkbox', + readonly: false }); this.md.use(AnchorPlugin, { slugify: slugifyHeading @@ -94,6 +96,20 @@ export class Renderer { this.md.use(iterator, 'internal_image_link', 'image', (tokens: MarkdownIt.Token[], idx: number) => { tokens[idx].attrSet('src', this.getImageUrl(tokens[idx].attrGet('src') as string)); }); + + function injectLineNumbers(tokens, idx, options, env, slf) { + if (tokens[idx].map && tokens[idx].level === 0) { + const line = tokens[idx].map[0]; + tokens[idx].attrJoin('class', 'line'); + tokens[idx].attrSet('data-line', String(line)); + } + return slf.renderToken(tokens, idx, options, env, slf); + } + + this.md.renderer.rules.paragraph_open = + this.md.renderer.rules.heading_open = + this.md.renderer.rules.heading_open = + injectLineNumbers; } prepareText(text: string): string { @@ -130,11 +146,12 @@ export class Renderer { } } - renderText(text: string, element): void { - this.loadPlugins(text).then(() => { - const html = this.md.render(this.prepareText(text)); - element.html(html); - }); + renderText(text: string, element): Thenable<void> { + return this.loadPlugins(text).then(() => { + const html = this.md.render(this.prepareText(text)); + element.html(html); + } + ); } loadPlugins(text: string) { diff --git a/js/SidebarPreview.ts b/js/SidebarPreview.ts index 64fef07..6c7bf62 100644 --- a/js/SidebarPreview.ts +++ b/js/SidebarPreview.ts @@ -1,10 +1,21 @@ -import {PreviewPlugin} from "./PreviewPlugin"; +import {Renderer} from "./Renderer"; export class SidebarPreview implements SidebarPreviewPlugin { - private previewPlugin: PreviewPlugin; + private renderer: Renderer; - constructor(previewPlugin: PreviewPlugin) { - this.previewPlugin = previewPlugin; + private initPromise: JQueryPromise<void> | null = null; + + init() { + if (!this.initPromise) { + const deferred = $.Deferred(); + require.ensure(['./Renderer'], () => { + const {Renderer} = require('./Renderer'); + this.renderer = new Renderer(); + deferred.resolve(); + }); + this.initPromise = deferred.promise(); + } + return this.initPromise; } attach(manager) { @@ -17,13 +28,13 @@ export class SidebarPreview implements SidebarPreviewPlugin { $.when( this.getFileContent(model.getFullPath()), - this.previewPlugin.init() + this.init() ).then(([content]) => { $thumbnailDiv.removeClass('icon-loading icon-32'); $thumbnailContainer.addClass('large'); $thumbnailContainer.addClass('text'); const $textPreview = $('<div id="preview" class="text-markdown"/>'); - this.previewPlugin.preview(content, $textPreview); + this.renderer.renderText(content, $textPreview); $thumbnailDiv.children('.stretcher').remove(); $thumbnailDiv.append($textPreview); $thumbnailContainer.css("max-height", previewHeight); diff --git a/js/VideoPlugin.ts b/js/VideoPlugin.ts index 3a72e4f..2382f8b 100644 --- a/js/VideoPlugin.ts +++ b/js/VideoPlugin.ts @@ -85,7 +85,6 @@ function renderVideo(md: MarkdownIt.MarkdownIt, options: VideoOptions) { if (alt && isVideoService(alt)) { return renderVideoService(md, options, alt, url); } - console.log(url, isEmbeddedVideo(url)); if (isEmbeddedVideo(url)) { return renderEmbededVideo(md, options, url); } @@ -141,7 +140,6 @@ export default function VideoPlugin(md: MarkdownIt.MarkdownIt, options: VideoOpt const originalRenderer = md.renderer.rules.image; md.renderer.rules.image = (tokens: Token[], idx: number, options: VideoOptions, env, slf) => { options = $.extend(defaults, options); - console.log(JSON.stringify(tokens[idx])); const videoResult = renderVideo(md, options)(tokens, idx, env); return videoResult || originalRenderer(tokens, idx, options, env, slf) }; diff --git a/js/editor.ts b/js/editor.ts index ce6022a..18b27e7 100644 --- a/js/editor.ts +++ b/js/editor.ts @@ -13,4 +13,4 @@ $(document).ready(function () { __webpack_require__.p = OC.filePath('files_markdown', 'js', '../build/'); __webpack_require__.nc = $('script[nonce]')[0].getAttribute('nonce'); -OC.Plugins.register('OCA.Files.SidebarPreviewManager', new SidebarPreview(previewPlugin)); +OC.Plugins.register('OCA.Files.SidebarPreviewManager', new SidebarPreview()); |