From 4924697996b96b11b4575720b47b7366d7df85fd Mon Sep 17 00:00:00 2001 From: Piotr Labudda Date: Fri, 25 Sep 2015 13:07:27 +0200 Subject: [PATCH] Added Github Task Lists --- main.js | 171 ++++++++++++++++++++++++++++------------ templates/settings.html | 5 +- 2 files changed, 125 insertions(+), 51 deletions(-) diff --git a/main.js b/main.js index 3634340..a39c831 100644 --- a/main.js +++ b/main.js @@ -1,16 +1,16 @@ /* * Copyright (c) 2012 Glenn Ruehle - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -45,17 +45,17 @@ define(function (require, exports, module) { var panelHTML = require("text!templates/panel.html"), previewHTML = require("text!templates/preview.html"), settingsHTML = require("text!templates/settings.html"); - + // Local modules var marked = require("lib/marked"); - + // jQuery objects var $icon, $iframe, $panel, $settingsToggle, $settings; - + // Other vars var currentDoc, currentEditor, @@ -66,9 +66,10 @@ define(function (require, exports, module) { // Prefs var _prefs = PreferencesManager.getExtensionPrefs("markdown-preview"); _prefs.definePreference("useGFM", "boolean", false); + _prefs.definePreference("useGFMCheckboxes", "boolean", false); _prefs.definePreference("theme", "string", "clean"); _prefs.definePreference("syncScroll", "boolean", true); - + // (based on code in brackets.js) function _handleLinkClick(e) { // Check parents too, in case link has inline formatting tags @@ -84,19 +85,59 @@ define(function (require, exports, module) { } node = node.parentElement; } - + // Close settings dropdown, if open _hideSettings(); } - + + function _handleGFMCheckboxClick(e) { + var node = e.target, + chbxIndex = -1; + if (currentDoc && _prefs.get('useGFMCheckboxes')) { + if (node.tagName === "INPUT" && $(node).hasClass('gfm-checkbox')) { + var gfmCheckboxes = $($iframe[0].contentDocument).find('input.gfm-checkbox').each(function(idx, n) { + if (node === n) { + chbxIndex = idx; + } + }); + if (chbxIndex > -1) { + var docText = currentDoc.getText(), + docLines = docText.split("\n"), + gfmChbxLineNrs = [], + chbxChecked = node.checked, + chbxLine = '', + chbxPos = '', + chbxCurrVal = (chbxChecked)? ' ' : 'x', + chbxNewVal = (chbxChecked)? 'x' : ' '; + docLines.map(function(line, lineNr) { + if (/^\s*\-\s*\[[ x]\]\s*/.test(line)) { + gfmChbxLineNrs.push(lineNr); + } + }); + if (chbxIndex >= 0 && chbxIndex < gfmChbxLineNrs.length) { + chbxLine = docLines[gfmChbxLineNrs[chbxIndex]]; + chbxPos = chbxLine.indexOf('[' + chbxCurrVal + ']'); + if (chbxPos != -1) { + chbxLine = chbxLine.substr(0, chbxPos + 1) + chbxNewVal + chbxLine.substr(chbxPos + 2); + docLines[gfmChbxLineNrs[chbxIndex]] = chbxLine; + } + } + if (chbxPos != -1) { + currentDoc.setText(docLines.join("\n")); + } + } + } + } + } + function _calcScrollPos() { var scrollInfo = currentEditor._codeMirror.getScrollInfo(); var scrollPercentage = scrollInfo.top / (scrollInfo.height - scrollInfo.clientHeight); var scrollTop = ($iframe[0].contentDocument.body.scrollHeight - $iframe[0].clientHeight) * scrollPercentage; - + return Math.round(scrollTop); } - + function _editorScroll() { if (_prefs.get("syncScroll") && $iframe) { var scrollTop = _calcScrollPos(); @@ -104,7 +145,7 @@ define(function (require, exports, module) { $iframe[0].contentDocument.body.scrollTop = scrollTop; } } - + function _loadDoc(doc, isReload) { if (doc && visible && $iframe) { var docText = doc.getText(), @@ -117,22 +158,22 @@ define(function (require, exports, module) { if (yamlMatch) { docText = docText.substr(yamlMatch[0].length); } - + if (isReload) { scrollPos = $iframe.contents()[0].body.scrollTop; } else if (_prefs.get("syncScroll")) { scrollPos = _calcScrollPos(); } - + // Parse markdown into HTML bodyText = marked(docText); - + // Show URL in link tooltip bodyText = bodyText.replace(/(href=\"([^\"]*)\")/g, "$1 title=\"$2\""); - + // Convert protocol-relative URLS bodyText = bodyText.replace(/src="\/\//g, "src=\"http://"); - + if (isReload) { $iframe[0].contentDocument.body.innerHTML = bodyText; } else { @@ -154,6 +195,7 @@ define(function (require, exports, module) { // Open external browser when links are clicked // (similar to what brackets.js does - but attached to the iframe's document) $iframe[0].contentDocument.body.addEventListener("click", _handleLinkClick, true); + $iframe[0].contentDocument.body.addEventListener("click", _handleGFMCheckboxClick, true); // Sync scroll position (if needed) if (!isReload) { @@ -166,30 +208,48 @@ define(function (require, exports, module) { } } } - + function _documentChange(e) { _loadDoc(e.target, true); } - + function _resizeIframe() { if (visible && $iframe) { var iframeWidth = panel.$panel.innerWidth(); $iframe.attr("width", iframeWidth + "px"); } } - + function _updateSettings() { // Format var useGFM = _prefs.get("useGFM"); + + var renderer = new marked.Renderer(); + if (_prefs.get('useGFMCheckboxes')) { + renderer.listitem = function(text) { + if (/^\[ \] \s*/.test(text)) { + text = ' ' + text.substr(4); + } else if (/^\[x\] \s*/.test(text)) { + text = ' ' + text.substr(4); + } + return '
  • ' + text + '
  • \n'; + }; + } else { + renderer.listitem = function(text) { + return '
  • ' + text + '
  • \n'; + } + } + marked.setOptions({ + renderer: renderer, breaks: useGFM, gfm: useGFM }); - + // Re-render _loadDoc(currentDoc); } - + function _documentClicked(e) { if (!$settings.is(e.target) && !$settingsToggle.is(e.target) && @@ -197,7 +257,7 @@ define(function (require, exports, module) { _hideSettings(); } } - + function _hideSettings() { if ($settings) { $settings.remove(); @@ -205,57 +265,69 @@ define(function (require, exports, module) { $(window.document).off("mousedown", _documentClicked); } } - + function _showSettings(e) { _hideSettings(); - + $settings = $(settingsHTML) .css({ right: 12, top: $settingsToggle.position().top + $settingsToggle.outerHeight() + 12 }) .appendTo($panel); - + var selIndex = 0; + if (_prefs.get("useGFM")) selIndex = 1; + if (_prefs.get("useGFMCheckboxes")) selIndex = 2; $settings.find("#markdown-preview-format") - .prop("selectedIndex", _prefs.get("useGFM") ? 1 : 0) + .prop("selectedIndex", selIndex) .change(function (e) { - _prefs.set("useGFM", e.target.selectedIndex === 1); + var selVal = e.target.options[e.target.selectedIndex].value; + var useGFM = false; + var useGFMCheckboxes = false; + if ('gfm' == selVal) { + useGFM = true; + } else if ('gfm_and_checkboxes' == selVal) { + useGFM = true; + useGFMCheckboxes = true; + } + _prefs.set("useGFM", useGFM); + _prefs.set("useGFMCheckboxes", useGFMCheckboxes); _updateSettings(); }); - + $settings.find("#markdown-preview-theme") .val(_prefs.get("theme")) .change(function (e) { _prefs.set("theme", e.target.value); _updateSettings(); }); - + var $syncScroll = $settings.find("#markdown-preview-sync-scroll"); - + $syncScroll.change(function (e) { _prefs.set("syncScroll", e.target.checked); _editorScroll(); }); - + if (_prefs.get("syncScroll")) { $syncScroll.attr("checked", true); } - + PopUpManager.addPopUp($settings, _hideSettings, true); $(window.document).on("mousedown", _documentClicked); } - + function _setPanelVisibility(isVisible) { if (isVisible === realVisibility) { return; } - + realVisibility = isVisible; if (isVisible) { if (!panel) { $panel = $(panelHTML); $iframe = $panel.find("#panel-markdown-preview-frame"); - + panel = WorkspaceManager.createBottomPanel("markdown-preview-panel", $panel); $panel.on("panelResizeUpdate", function (e, newSize) { $iframe.attr("height", newSize); @@ -263,7 +335,7 @@ define(function (require, exports, module) { $iframe.attr("height", $panel.height()); window.setTimeout(_resizeIframe); - + $settingsToggle = $("#markdown-settings-toggle") .click(function (e) { if ($settings) { @@ -272,7 +344,7 @@ define(function (require, exports, module) { _showSettings(e); } }); - + $iframe.hide(); } _loadDoc(DocumentManager.getCurrentDocument()); @@ -288,17 +360,17 @@ define(function (require, exports, module) { function _currentDocChangedHandler() { var doc = DocumentManager.getCurrentDocument(), ext = doc ? FileUtils.getFileExtension(doc.file.fullPath).toLowerCase() : ""; - + if (currentDoc) { currentDoc.off("change", _documentChange); currentDoc = null; } - + if (currentEditor) { currentEditor.off("scroll", _editorScroll); currentEditor = null; } - + if (doc && /md|markdown|litcoffee|txt/.test(ext)) { currentDoc = doc; currentDoc.on("change", _documentChange); @@ -306,29 +378,30 @@ define(function (require, exports, module) { currentEditor.on("scroll", _editorScroll); $icon.css({display: "block"}); _setPanelVisibility(visible); + _updateSettings(); _loadDoc(doc); } else { $icon.css({display: "none"}); _setPanelVisibility(false); } } - + function _toggleVisibility() { visible = !visible; _setPanelVisibility(visible); } - + // Debounce event callback to avoid excess overhead // Update preview 300 ms ofter document change // Sync scroll 1ms after document scroll (just enough to ensure // the document scroll isn't blocked). _documentChange = _.debounce(_documentChange, 300); _editorScroll = _.debounce(_editorScroll, 1); - + // Insert CSS for this extension ExtensionUtils.loadStyleSheet(module, "styles/MarkdownPreview.css"); - - // Add toolbar icon + + // Add toolbar icon $icon = $("") .attr({ id: "markdown-preview-icon", @@ -339,16 +412,16 @@ define(function (require, exports, module) { }) .click(_toggleVisibility) .appendTo($("#main-toolbar .buttons")); - + // Add a document change handler MainViewManager.on("currentFileChange", _currentDocChangedHandler); - + // currentDocumentChange is *not* called for the initial document. Use // appReady() to set initial state. AppInit.appReady(function () { _currentDocChangedHandler(); }); - + // Listen for resize events WorkspaceManager.on("workspaceUpdateLayout", _resizeIframe); $("#sidebar").on("panelCollapsed panelExpanded panelResizeUpdate", _resizeIframe); diff --git a/templates/settings.html b/templates/settings.html index a2ecc5a..dd8c72b 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -2,8 +2,9 @@
    Format