Advertisement

使用Node.js构建实时Markdown编辑器

阅读量:

介绍 (Intro)

Markdown是一种易于阅读的文字格式,并且可以转换为HTML。它是一种广受欢迎的标记语言,在GitHub和Stack Overflow等网站上得到了推广。今天我们将开发一个应用程序,在左侧展示原始Markdown文字,在右侧展示转换后的Markdown(以HTML形式)。此外,我们将允许多人在同一共享链接上协作编辑同一个Markdown文档,并且所有修改都会被保存下来。

流行的文字格式Markdown易于阅读,并能方便地转换为HTML。 该标记语言广泛应用于开发者社区。 在左侧窗口预览原始Markdown文件,在右侧则展示其HTML版本。 支持多人协作使用同一个Markdown文档,并确保所有修改都被记录下来。

设置节点 (Setup Node)

让我们开始开发我们的实时Markdown查看器应用。接下来,我们将使用Node语言开发该应用的后端部分。请创建一个项目目录,并从命令行执行以下操作:

让我们决定采用实时降价查看器应用程序进行价格监控功能的开发。
我们将在Node中为该应用设计并构建后端系统。
首先建立一个新的项目文件夹用于存储相关代码,并通过git初始化该仓库后将工作目录切换至其中。
接下来的操作是从终端输入相应的初始化命令以完成项目的搭建。

复制代码
    npm init

该方法将向我们提出几个问题。请根据实际情况填写这些问题。该方法将生成一个名为package.json的文件。以下是一个示例的package.json文件:

这将成为我们面临的一些关键问题。 请根据实际情况补充相应的信息。 该操作将生成一个位于此目录下的标准JSON配置文件。 例如,在这个项目根目录中提供了这样一个基准配置方案。

复制代码
 {

    
       "name": "RealtimeMarkdownViewer",
    
       "description": "Realtime Markdown Viewer",
    
       "main": "server.js",
    
       "version": "1.0.0",
    
       "repository": {
    
     "type": "git",
    
     "url": "git@github.com:sifxtreme/realtime-markdown.git"
    
       },
    
       "keywords": [
    
     "markdown",
    
     "realtime",
    
     "sharejs"
    
       ],
    
       "author": "Asif Ahmed",
    
       "dependencies": {
    
     "express": "^4.12.4",
    
     "ejs": "^2.3.1",
    
     "redis": "^0.10.3",
    
     "share": "0.6.3"
    
       },
    
       "engines": {
    
     "node": "0.10.x",
    
     "npm": "1.3.x"
    
       }
    
     }

Now, let's establish a server.js file within our root directory. This will function as the primary server file. By employing Express, constructing a web application becomes more straightforward. Utilizing Express, we will implement EJS for our view templates when needed. To install both Express and EJS, execute the following commands:

现在,在项目根目录中新建一个server.js文件。
这个文件将是我们的主服务器应用。
我们选择将Express作为我们的Web应用程序框架。
通过采用这一技术可以显著简化服务器架构设计。
同时,在构建视图模板时我们会选择EJS.
为了完成这一目标,请按照以下步骤进行操作:

  1. 首先确保开发环境配置正确
  2. 执行必要的准备工作
  3. 然后开始安装所需的依赖项
  4. 最后验证整个系统的配置设置
复制代码
 node install --save express

    
 node install --save ejs

Set up two folders: a views folder and a public folder in the root directory. The views folder serves as the repository for our EJS templates, while the public folder is designated to host our assets, including stylesheets, JavaScript files, and images. Currently, we are prepared to implement some code into our server configuration file named server.js!

建议设置两个关键路径:一个是用于管理个人作品的个人作品库;另一个则是专门负责展示公共作品的内容存储空间。这两个路径下分别放置相应的项目资源包及元数据信息库;这样既能实现作品分类管理的目的;又能保证系统功能的一致性和完整性

复制代码
 // server.js

    
  
    
 var express = require('express');
    
 var app = express();
    
  
    
 // set the view engine to ejs
    
 app.set('view engine', 'ejs');
    
  
    
 // public folder to store assets
    
 app.use(express.static(__dirname + '/public'));
    
  
    
 // routes for app
    
 app.get('/', function(req, res) {
    
   res.render('pad');
    
 });
    
  
    
 // listen on port 8000 (for localhost) or the port defined for heroku
    
 var port = process.env.PORT || 8000;
    
 app.listen(port);

Here we require using the Express module, assign EJS as the rendering engine, and include a route for our homepage. Additionally, we configure the public directory as a static one Furthermore, we configure it to listen on port 8000 From our homepage setup, we will generate an EJS file located in pad.ejs within views

在本项目中,默认情况下我们会使用Express框架,并将渲染引擎配置为EJS。同时,默认情况下主页路径已经被配置好了。此外,默认情况下会将公共目录配置为静态资源。最后,请注意服务器监听地址默认设置在localhost:8000。系统会根据当前请求路径生成对应的EJS组件文件,请确保生成的组件文件命名规范且位置正确例如,在views目录下我们可以找到这个组件文件pad.ejs

复制代码
 <!-- views/pad.ejs -->

    
  
    
 <!DOCTYPE html>
    
 <html>
    
 <head> 
    
     <title>Realtime Markdown Viewer</title>
    
     <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
    
 </head>
    
  
    
 <body class="container-fluid">
    
 Hello World!
    
 </body>
    
 </html>

When it comes to appearance, we incorporated Bootstrap into our project. When launching our node server (via node server.js) and navigating to http://localhost:8000 within our web browser, you should see something similar to this setup. When you open the application link: http://localhost:8000, you'll notice the familiar Bootstrap interface.

为样式配置了Bootstrap 。运行Node脚本(`node server.js``),然后在Web浏览器访问http://localhost:8000$``。预计您会看到以下界面:

[

image1

](https://cask.scotch.io/2015/06/image1.png)

设置视图,CSS和JS文件 (Setup View, CSS, and JS Files)

We are not satisfied with our view file merely displaying "Hello World!", so modifications are necessary. On the left side, we plan to include a text area where users can input Markdown content, while on the right side, there will be an equivalent HTML output. Whenever users modify the Markdown input, real-time updates should be implemented for corresponding HTML content. Styling-wise, both areas should achieve a full-height display.

我们不希望视图组件仅局限于简单的" Hello World!"界面设计。
因此,请对其进行编辑以提升功能。
我们希望用户提供Markdown文本输入的支持。
同时确保右侧区域显示转换后的HTML-Markdown格式。
当用户修改左侧区域时,请确保右侧区域同步更新以反映最新内容。
从设计角度看,在风格上我们要求文本区域和转换后的HTML-Markdown区域均实现100%的高度一致。

To transform text into HTML, we will utilize the Showdown library, employing the Showdown library from https://github.com/showdownjs/showdown. It would be beneficial for us to examine the updated version of our view file.

要将文本转换为HTML,则会采用名为Showdown的库包。 请检查更新后的视图文件以获取最新信息。

复制代码
 <!-- views/pad.ejs -->

    
  
    
 <!DOCTYPE html>
    
 <html>
    
 <head>
    
     <title>Realtime Markdown Viewer</title>
    
     <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
    
     <link href="style.css" rel="stylesheet">
    
 </head>
    
  
    
 <body class="container-fluid">
    
  
    
     <section class="row">
    
     <textarea class="col-md-6 full-height" id="pad">Write your text here..</textarea>
    
     <div class="col-md-6 full-height" id="markdown"></div>
    
     </section>
    
  
    
     <script src="https://cdn.rawgit.com/showdownjs/showdown/1.0.2/dist/showdown.min.js"></script>
    
     <script src="script.js"></script>
    
  
    
 </body>
    
 </html>

In the viewing file, we integrated bindings for both CSS and JavaScript files. Additionally, we incorporated a code block for inputting Markdown text as well as an independent section dedicated solely to displaying Markdown content. Notably, these elements have unique IDs, which will prove beneficial for our JavaScript implementation. Now, let's update the styling for public/style.css.

在视图文件中插入CSS与JavaScript链接,在其文字处理区域内新增text area元素用于编写Markdown格式文本,并设置一个独立的Markdown查看区用于显示输入的内容。这些元素均带有指定编号标识,在JavaScript脚本处理时非常有用。下一步,在public/style.css路径下新增样式规则。

复制代码
 /* public/style.css */

    
  
    
 html, body, section, .full-height {
    
     height: 100%;
    
 } 
    
  
    
 #pad{
    
     font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
    
  
    
     border: none;
    
     overflow: auto;
    
     outline: none;
    
     resize: none;
    
  
    
     -webkit-box-shadow: none;
    
     -moz-box-shadow: none;
    
     box-shadow: none;
    
 }
    
  
    
 #markdown {
    
     overflow: auto;
    
     border-left: 1px solid black;
    
 }

Within our JavaScript file (public/script.js), we aim to develop a function that is capable of converting textarea text inputs into corresponding HTML content and then insert that HTML into the markdown region. Additionally, we desire an event listener mechanism that will trigger automatically for any input event (such as keydown, copy/paste operations), ensuring seamless conversion whenever user interacts with the textarea. Furthermore, at initialization phase upon page load, this conversion process should execute automatically. This is the code for our JavaScript file: public/script.js.

我们希望在我们的javascript文件( public/script.js )中实现一个转换功能。该功能能够将textarea中的输入内容进行处理,并将生成的HTML内容复制到此处。为了确保数据的一致性我们将对整个流程进行监控以防止数据丢失或损坏。为此我们需要为文本区域的所有修改操作(包括但不限于键击、剪切和粘贴)触发该转换功能最后我们希望该转换操作从页面加载时开始自动执行。

复制代码
 /* public/script.js */

    
  
    
 window.onload = function() {
    
     var converter = new showdown.Converter();
    
     var pad = document.getElementById('pad');
    
     var markdownArea = document.getElementById('markdown');   
    
  
    
     var convertTextAreaToMarkdown = function(){
    
     var markdownText = pad.value;
    
     html = converter.makeHtml(markdownText);
    
     markdownArea.innerHTML = html;
    
     };
    
  
    
     pad.addEventListener('input', convertTextAreaToMarkdown);
    
  
    
     convertTextAreaToMarkdown();
    
 };

Currently, we anticipate having a fully functional application that allows users to edit and view Markdown immediately. Upon visiting the homepage and adding sample Markdown, users should expect to see a preview similar to what is displayed here.

在此处, 我们应当期待拥有一个功能强大的应用程序. 该应用程序将会提供一种便捷的方式, 即时地编辑和预览Markdown文档. 当访问主页并在其导航栏中加入一些价格下降的例子时, 我们应该能够看到类似以下内容:

[

image2

](https://cask.scotch.io/2015/06/image2.png)

将ShareJS添加到后端 (Add ShareJS to Backend)

Despite having an existing prototype where users are currently able to edit a Markdown document, we must implement additional features that would enable multiple individuals to collaborate on the same Markdown document. At this stage, if several users visit the homepage, each of them would have access only to their own separate Markdown file; any modifications they make would remain confined to their individual files. However, if they refresh the page accidentally, all their changes would be permanently lost. Therefore, we must implement a system that allows multiple users to simultaneously edit the same Markdown document and ensure that all changes are properly saved.

基于当前的工作架构, 我们已经有一个功能允许用户提供降价相关内容. 然而, 为了实现多用户协作, 我们需要完成一个关键功能:让多个用户共享同一份Markdown文件. 每个用户都能拥有独立的降价文档副本, 并且每次修改仅限于该用户的本地副本. 如果页面刷新会导致所有修改归零, 那么我们就必须设计一种能够同时供多用户编辑同一Markdown文件并支持版本控制的技术方案.

As soon a user types on a page, we want this change to be reflected for all users. We want this markdown app to be a real time updating app. Basically we are trying to add a "Google Document" type functionality where changes are seen automatically. This is not an easy problem to solve, however, there is a library that does the heavy lifting for us. ShareJS is a library that implements real time communication. ShareJS has one dependency though — it requires Redis. Redis is a fast data store and that is where we will be storing our markdown files. To download and install Redis, we can follow the Redis documentation. Once we install Redis, we need to add the node modules for sharejs and redis, and then we should restart Node. ShareJS allows us to save the markdown document as soon as any user make a change to it.

当用户在页面上输入内容时,我们希望更改能够立即同步到所有用户。 为了实现这一目标,请确保我们的降价应用具备实时更新功能。 为了方便操作与管理,请考虑引入类似Google文档的功能模块。 由于这是一个具有挑战性的任务,请查阅相关技术资料获取解决方案建议。 这个问题虽然复杂但并非不可解决为此目的我们可以依赖现成的库来处理这些功能需求。 需要注意的是该库依赖Redis作为后端支持 为此我们需要先下载并安装Redis软件工具包 Redis是一个高效的数据存储解决方案 它将用于存储我们的Markdown文件数据集 我们可以通过官方文档获取Redis安装指南请访问Redis官网获取详细指导 Redis官网提供的安装指南非常详细您可以按照指引一步步完成Redis服务器的配置过程 确保数据存储位置正确无误之后 我们需要添加两个额外的Node.js节点模块:一个是与ShareJS兼容的支持模块 另一个是用于与Redis交互的数据传输模块 在完成上述配置后请重新启动Node.js服务即可开始正常运行我们的系统架构 ShareJS组件将负责在每次用户的更改发生时自动同步数据到各个端点 这将显著提升系统的可用性和稳定性。

复制代码
 npm install --save share@0.6.3

    
 npm install --save redis

First let's add the ShareJS code to our server file.

首先,让我们将ShareJS代码添加到我们的服务器文件中。

复制代码
 /* server.js */

    
  
    
 var express = require('express');
    
 var app = express();
    
  
    
 // set the view engine to ejs
    
 app.set('view engine', 'ejs');
    
  
    
 // public folder to store assets
    
 app.use(express.static(__dirname + '/public'));
    
  
    
 // routes for app
    
 app.get('/', function(req, res) {
    
   res.render('pad');
    
 });
    
 app.get('/(:id)', function(req, res) {
    
   res.render('pad');
    
 });
    
  
    
 // get sharejs dependencies
    
 var sharejs = require('share');
    
 require('redis');
    
  
    
 // options for sharejs 
    
 var options = {
    
   db: {type: 'redis'},
    
 };
    
  
    
 // attach the express server to sharejs
    
 sharejs.server.attach(app, options);
    
  
    
 // listen on port 8000 (for localhost) or the port defined for heroku
    
 var port = process.env.PORT || 8000;
    
 app.listen(port);

We recommend installing the ShareJS library along with its required dependencies, such as Redis. We configure ShareJS to utilize Redis as its data storage system. Subsequently, we integrate our Express server with the ShareJS application framework. Finally, we must incorporate references to certain 'ShareJS' frontend JavaScript files within the structure of our view file.

为实现目标需求,我们引入了ShareJS和Redis,并为两者配置必要的参数设置。通过强制配置,我们确保了ShareJS以Redis作为数据存储的基础运行起来。随后,在配置完成之后,请您将Express服务器成功地连接到了当前运行的ShareJS实例上。现在,在构建完整后端服务的过程中,请您将Express应用的前端JavaScript脚本成功地导入到对应的视图文件中。

复制代码
 <!-- views/pad.ejs -->

    
  
    
 <!DOCTYPE html>
    
 <html>
    
 <head>
    
     <title>Realtime Markdown Viewer</title>
    
     <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
    
     <link href="style.css" rel="stylesheet">
    
 </head>
    
  
    
 <body class="container-fluid">
    
  
    
     <section class="row">
    
     <textarea class="col-md-6 full-height" id="pad">Write markdown text here..</textarea>
    
     <div class="col-md-6 full-height" id="markdown"></div>
    
     </section>
    
  
    
     <script src="https://cdn.rawgit.com/showdownjs/showdown/1.0.2/dist/showdown.min.js"></script>
    
     <script src="/channel/bcsocket.js"></script>
    
     <script src="/share/share.uncompressed.js"></script>
    
     <script src="/share/textarea.js"></script>
    
     <script src="script.js"></script>
    
  
    
 </body>
    
 </html>

A set of files is necessary for establishing a socket connection to our backend (bcsocket.js) as well as transmitting and receiving textarea events through share.uncompressed.js and textarea.js. Finally, it is necessary to incorporate the implementation of ShareJS into our frontend javascript file located at public/script.js. This will ensure that all required functionalities are correctly integrated.

我们所需的文件主要包括构建与后端服务器之间通信的套接字连接( bcsocket.js ),以及处理textarea事件时使用的两个辅助文件( `share.uncompressed.js11111111111111$1textarea.js``)。最终,在放置于前端JavaScript代码实际运行环境中的位置上进行具体实现工作。

复制代码
 /* public/script.js */

    
  
    
 window.onload = function() {
    
     var converter = new showdown.Converter();
    
     var pad = document.getElementById('pad');
    
     var markdownArea = document.getElementById('markdown');   
    
  
    
     var convertTextAreaToMarkdown = function(){
    
     var markdownText = pad.value;
    
     html = converter.makeHtml(markdownText);
    
     markdownArea.innerHTML = html;
    
     };
    
  
    
     pad.addEventListener('input', convertTextAreaToMarkdown);
    
  
    
     sharejs.open('home', 'text', function(error, doc) {
    
     doc.attach_textarea(pad);
    
     });
    
 };

At the very bottom of this file, we open up a sharejs connection to "home" (because we are on the home page). We then attach the textarea to the object returned by this connection. This code keeps our textarea in sync with everyone else's textarea. So if Person A makes a change in their textarea, Person B will see that change automatically in their textarea. However, Person B's markdown area will not be updated right away. In fact, Person B's markdown area won't be updated until they make a change to their textarea themselves. This is a problem. We will solve this by making sure a change is reflected every second if the textarea has changed.

在这个文件的底部部分(位于主页),我们启动了一个指向'home'位置的sharejs链接(因为我们希望在此处链接)。随后我们将一个特定的文本字段绑定到上述链接返回的对象上。这一操作实现了我们的所有相关文本字段之间的自动同步机制:每当人员A修改其本地存储时(即其特定的编辑区),人员B都可以实时查看这些修改;然而这种实时性仅限于编辑区所在的领域,并非适用于其他领域如定价区等其他地方;实际上,在人员B对其定价区进行修改之前(即在他们个人主动发起修改之前),定价区并不会自动更新;这是一个潜在的问题所在:当多个编辑区发生改动时可能会出现信息滞后的情况;为了避免这种状况的发生,在每次数据修改事件发生后我们应确保系统能够在每秒钟内完成一次数据同步操作以保证系统的稳定运行

复制代码
 /* public/script.js */

    
  
    
 window.onload = function() {
    
     var converter = new showdown.Converter();
    
     var pad = document.getElementById('pad');
    
     var markdownArea = document.getElementById('markdown');   
    
  
    
     var previousMarkdownValue;          
    
  
    
     var convertTextAreaToMarkdown = function(){
    
     var markdownText = pad.value;
    
     previousMarkdownValue = markdownText;
    
     html = converter.makeHtml(markdownText);
    
     markdownArea.innerHTML = html;
    
     };
    
  
    
     var didChangeOccur = function(){
    
     if(previousMarkdownValue != pad.value){
    
         return true;
    
     }
    
     return false;
    
     };
    
  
    
     setInterval(function(){
    
     if(didChangeOccur()){
    
         convertTextAreaToMarkdown();
    
     }
    
     }, 1000);
    
  
    
     pad.addEventListener('input', convertTextAreaToMarkdown);
    
  
    
     sharejs.open('home', 'text', function(error, doc) {
    
     doc.attach_textarea(pad);
    
     convertTextAreaToMarkdown();
    
     });
    
 };

多个Markdown文件 (Multiple Markdown Files)

Now, we possess an application that enables multiple users to edit the home page's Markdown file. However, suppose we desired editing several Markdown files instead. Suppose additionally, could one navigate URLs such as http://localhost:3000/important_doc1 alongside Bob and similarly access http://localhost:3000/important_doc2 with Alice? How might one proceed to realize this functionality? Initially, one should aim to incorporate matching wildcard routes into the server configuration.

目前我们拥有一个应用程序系统,在线的多个用户可以在其主页面上编辑markdown格式的文档文件。但是当需要处理多个markdown文件时该怎么办呢?如果我们想要将某个文档(如important_doc1)转接到类似http://localhost:3000/important_doc1的URL上并与Bob团队协作同时又希望将另一个文档(如important_doc2)转接到类似http://localhost:3000/important_doc2并与其团队Alice进行协作那我们应该如何实现这一目标呢?为此我们需要设置相应的路由配置以便能够匹配服务器中所有可能的文件路径并支持跨团队协作功能。

复制代码
 /* server.js */

    
  
    
 var express = require('express');
    
 var app = express();
    
  
    
 // set the view engine to ejs
    
 app.set('view engine', 'ejs');
    
  
    
 // public folder to store assets
    
 app.use(express.static(__dirname + '/public'));
    
  
    
 // routes for app
    
 app.get('/', function(req, res) {
    
   res.render('pad');
    
 });
    
 app.get('/(:id)', function(req, res) {
    
   res.render('pad');
    
 });
    
  
    
 // get sharejs dependencies
    
 var sharejs = require('share');
    
 require('redis');
    
  
    
 // options for sharejs 
    
 var options = {
    
   db: {type: 'redis'},
    
 };
    
  
    
 // attach the express server to sharejs
    
 sharejs.server.attach(app, options);
    
  
    
 // listen on port 8000 (for localhost) or the port defined for heroku
    
 var port = process.env.PORT || 8000;
    
 app.listen(port);

Instead of merely connecting on the frontend, we aim to utilize the correct sharejs room. Change home in the code to document.location.pathname.

在前端开发中,并不仅局限于采用"home"这一术语。
前端开发中,并不仅局限于采用正确的sharejs组件。
为了实现功能的一致性与可维护性,在具体的实现过程中,
我们将具体的实现路径替换为 document\.location\.pathname

复制代码
 /* public/script.js */

    
  
    
 sharejs.open(document.location.pathname, 'text', function(error, doc) {
    
     doc.attach_textarea(pad);
    
     convertTextAreaToMarkdown();
    
 });

清理 (Clean Up)

We have several key concerns to address. First and foremost, if we could ensure that the homepage doesn’t merely display arbitrary text from the last user’s input, that would be ideal. We plan to disable realtime markdown functionality on our homepage.

我们面临几个待解决的问题。一项设想是:如果主页不仅仅展示最后一个用户的无意义字符串(即随机文本),这将是一个非常有益的想法。为了减少不必要的操作负担,请考虑取消主页中的实时降价功能。

复制代码
 /* public/script.js */

    
  
    
 // ignore if on home page
    
 if(document.location.pathname.length > 1){
    
     // implement share js
    
     var documentName = document.location.pathname.substring(1);
    
     sharejs.open(documentName, 'text', function(error, doc) {
    
     doc.attach_textarea(pad);
    
     convertTextAreaToMarkdown();
    
     });        
    
 }
    
  
    
 convertTextAreaToMarkdown();

The last problem we must address is ensuring that our tab button functions as expected within the textarea. When pressing the tab key in the textarea, users often lose focus, which is unacceptable. To resolve this, let's implement a solution by adding an event listener to the textarea element that redirects navigation when the Tab key is pressed below is a copy of our final front-end JavaScript file

最后要解决的问题是确保弹出按钮发挥作用(就像我们期望弹出按钮在文本区域中发挥作用一样)。目前,在文本区域按下弹出按钮会让我们失去焦点(这确实是个问题)。让我们编写一个函数来处理这个问题(以下是我们的前端JavaScript文件的副本)。

复制代码
 /* public/script.js */

    
  
    
 window.onload = function() {
    
     var converter = new showdown.Converter();
    
     var pad = document.getElementById('pad');
    
     var markdownArea = document.getElementById('markdown'); 
    
  
    
     // make the tab act like a tab
    
     pad.addEventListener('keydown',function(e) {
    
     if(e.keyCode === 9) { // tab was pressed
    
         // get caret position/selection
    
         var start = this.selectionStart;
    
         var end = this.selectionEnd;
    
  
    
         var target = e.target;
    
         var value = target.value;
    
  
    
         // set textarea value to: text before caret + tab + text after caret
    
         target.value = value.substring(0, start)
    
                         + "\t"
    
                         + value.substring(end);
    
  
    
         // put caret at right position again (add one for the tab)
    
         this.selectionStart = this.selectionEnd = start + 1;
    
  
    
         // prevent the focus lose
    
         e.preventDefault();
    
     }
    
     });
    
  
    
     var previousMarkdownValue;          
    
  
    
     // convert text area to markdown html
    
     var convertTextAreaToMarkdown = function(){
    
     var markdownText = pad.value;
    
     previousMarkdownValue = markdownText;
    
     html = converter.makeHtml(markdownText);
    
     markdownArea.innerHTML = html;
    
     };
    
  
    
     var didChangeOccur = function(){
    
     if(previousMarkdownValue != pad.value){
    
         return true;
    
     }
    
     return false;
    
     };
    
  
    
     // check every second if the text area has changed
    
     setInterval(function(){
    
     if(didChangeOccur()){
    
         convertTextAreaToMarkdown();
    
     }
    
     }, 1000);
    
  
    
     // convert textarea on input change
    
     pad.addEventListener('input', convertTextAreaToMarkdown);
    
  
    
     // ignore if on home page
    
     if(document.location.pathname.length > 1){
    
     // implement share js
    
     var documentName = document.location.pathname.substring(1);
    
     sharejs.open(documentName, 'text', function(error, doc) {
    
         doc.attach_textarea(pad);
    
         convertTextAreaToMarkdown();
    
     });        
    
     }
    
  
    
     // convert on page load
    
     convertTextAreaToMarkdown();
    
  
    
 };

推送到Heroku (Push to Heroku)

在此刻时刻,我们已开发出一个功能完善的实时Markdown编辑器。
现在,请指导我们将其部署到Heroku上?
首先,请确保您已拥有一个有效的Heroku账户。
随后,请安装Herosu toolbelt
请登录到您的Heroku账户,请执行命令heroku login
为了整合Redis功能,请参考使用Go语言与Node.js集成Redis在Heroku上的文章
同时,请更新我们的server.js以适应新的配置设置。

至此,我们已经开发出了一个功能全面的实时降价编辑器系统。为了在Heroku上启动并运行该系统,请按照以下步骤操作:首先,请确保您拥有有效的Heroku账户;然后,请安装必要的Heroku工具带软件包;接着,在命令行界面输入heroku login命令以完成登录操作;由于Heroku采用Git进行版本管理,请确认已建立个人存储库,并将所有项目文件提交至本地存储库;如果您是新用户,请运行heroku create命令创建新的HEROKU application;在集成Redis之前,请确保修改后的Redis去技术已成功部署至您的应用环境中;此外,在编写新的配置文件时,请确保修改后的server.js能够正确集成到现有环境中。

复制代码
 /* server.js */

    
  
    
 var express = require('express');
    
 var app = express();
    
  
    
 // set the view engine to ejs
    
 app.set('view engine', 'ejs');
    
  
    
 // public folder to store assets
    
 app.use(express.static(__dirname + '/public'));
    
  
    
 // routes for app
    
 app.get('/', function(req, res) {
    
   res.render('pad');
    
 });
    
 app.get('/(:id)', function(req, res) {
    
   res.render('pad');
    
 });
    
  
    
 // get sharejs dependencies
    
 var sharejs = require('share');
    
  
    
 // set up redis server
    
 var redisClient;
    
 console.log(process.env.REDISTOGO_URL);
    
 if (process.env.REDISTOGO_URL) {
    
   var rtg   = require("url").parse(process.env.REDISTOGO_URL);
    
   redisClient = require("redis").createClient(rtg.port, rtg.hostname);
    
   redisClient.auth(rtg.auth.split(":")[1]);
    
 } else {
    
   redisClient = require("redis").createClient();
    
 }
    
  
    
 // options for sharejs 
    
 var options = {
    
   db: {type: 'redis', client: redisClient}
    
 };
    
  
    
 // attach the express server to sharejs
    
 sharejs.server.attach(app, options);
    
  
    
 // listen on port 8000 (for localhost) or the port defined for heroku
    
 var port = process.env.PORT || 8000;
    
 app.listen(port);

Finally, we must inform our Heroku app that we employ Node and specify which file Node utilizes for startup. We should add a Procfile named Procfile located in the application's root directory.

最终目标是为了告知Heroku应用程序我们正采用Node作为开发环境,并告知该程序使用的启动脚本文件路径,在根目录中配置该程序启动脚本路径为Procfile

复制代码
    web: node server.js

Once we have pushed our code into Git, it's time to deploy the application onto Heroku. Execute the command $ git push heroku master for deploying the repository. The deployment process will show various statuses indicating progress. Running $ heroku open will launch the application immediately. (Please note that you might need to rename your application later on; in some cases, Heroku might initially assign a rather nonsensical name). The initial deployment might take a moment, but everything should function properly afterward. The official documentation for creating new Markdown files can be accessed at $ heroku文档链接.

当我们提交修改至Git仓库后

Our gratitude knows no bounds. An application that allows real-time Markdown editing and collaboration among users has been developed.

Our gratitude knows no bounds. An application that allows real-time Markdown editing and collaboration among users has been developed.

祝贺!我们现在拥有一个实时的Markdown编辑器,支持编写并共享Markdown文档与我们的合作伙伴轻松协作。

You are invited to visit the live demo at this link. Additionally, you may examine the complete code repository here.

你能够访问实时演示页面此处并查阅代码存储库此处

翻译自: https://scotch.io/tutorials/building-a-real-time-markdown-viewer

全部评论 (0)

还没有任何评论哟~