bevacqua/woofmark là dự án Frontend trên GitHub, viết chủ yếu bằng JavaScript, với 1.6k sao. :dog2: Barking up the DOM tree. A modular, progressive, and beautiful Markdown and HTML editor
Tóm tắt dựng từ metadata GitHub của chính dự án — chưa có bài review TopGit. Trang sẽ tự động cập nhật khi bài review đầy đủ được xuất bản.
VÌ SAO CHƯA CÓ REVIEW
TopGit viết bài đầy đủ cho repo có nhiều sao nhất và được yêu cầu nhiều nhất. Trang này là snapshot trong thời gian chờ — xem README gốc ở tab READ ME.
Barking up the DOM tree. A modular, progressive, and beautiful Markdown and HTML editor
Browser support includes every sane browser and IE9+.
Demo
Try out the demo!
Features
Small and focused
Progressive, enhance a raw <textarea>
Markdown, HTML, and WYSIWYG input modes
Text selection persists even across input modes!
Built in Undo and Redo
Entirely customizable styles
Bring your own parsers
Look and feel is meant to blend into your designs
Install
You can get it on npm.
npm install woofmark --save
Or bower, too.
bower install woofmark --save
woofmark.find(textarea)
Returns an editor object associated with a woofmark instance, or null if none exists for the textarea yet. When woofmark(textarea, options?) is called, woofmark.find will be used to look up an existing instance, which gets immediately returned.
woofmark(textarea, options?)
Adds rich editing capabilities to a textarea element. Returns an editor object.
options.parseMarkdown
A method that's called by woofmark whenever it needs to parse Markdown into HTML. This way, editing user input is decoupled from a Markdown parser. We suggest you use megamark to parse Markdown. This parser is used whenever the editor switches from Markdown mode into HTML or WYSIWYG mode.
For optimal consistency, your parseMarkdown method should match whatever Markdown parsing you do on the server-side.
options.parseHTML
A method that's called by woofmark whenever it needs to parse HTML or a DOM tree into Markdown. This way, editing user input is decoupled from a DOM parser. We suggest you use domador to parse HTML and DOM. This parser is used whenever the editor switches to Markdown mode, and also when .value() is called while in the HTML or WYSIWYG modes.
If you're implementing your own parseHTML method, note that woofmark will call parseHTML with either a DOM element or a Markdown string.
While the parseHTML method will never map HTML back to the original Markdown in 100% cases, (because you can't really know if the original source was plain HTML or Markdown), it should strive to detokenize whatever special tokens you may allow in parseMarkdown, so that the user isn't met with inconsistent output when switching between the different editing modes.
A test of sufficiently good-citizen behavior can be found below. This is code for "Once an input Markdown string is parsed into HTML and back into Markdown, any further back-and-forth conversions should return the same output." Ensuring consistent back-and-forth is ensuring humans aren't confused when switching modes in the editor.
var parsed = parseHTML(parseMarkdown(original));
assert.equal(parseHTML(parseMarkdown(parsed)), parsed);
As an example, consider the following piece of Markdown:
Hey @bevacqua I _love_ [woofmark](https://github.com/bevacqua/woofmark)!
Without any custom Markdown hooks, it would translate to HTML similar to the following:
<p>Hey @bevacqua I <em>love</em> <a href="https://github.com/bevacqua/woofmark">woofmark</a>!</p>
However, suppose we were to add a tokenizer in our megamark configuration, like below:
woofmark(textarea, {
parseMarkdown: function (input) {
return require('megamark')(input, {
tokenizers: [{
token: /(^|\s)@([A-z]+)\b/g,
transform: function (all, separator, id) {
return separator + '<a href="/users/' + id + '">@' + id + '</a>';
}
}]
});
},
parseHTML: require('domador')
});
Our HTML output would now look slightly different.
<p>Hey <a href="/users/bevacqua">@bevacqua</a> I <em>love</em> <a href="https://github.com/bevacqua/woofmark">woofmark</a>!</p>
The problem is that parseHTML doesn't know about the tokenizer, so if you were to convert the HTML back into Markdown, you'd get:
Hey [@bevacqua](/users/bevacqua) I _love_ [woofmark](https://github.com/bevacqua/woofmark)!
The solution is to let parseHTML"know" about the tokenizer, so to speak. In the example below, domador is now aware that links that start with @ should be converted back into something like @bevacqua.
woofmark(textarea, {
parseMarkdown: function (input) {
return require('megamark')(input, {
tokenizers: [{
token: /(^|\s)@([A-z]+)\b/g,
transform: function (all, separator, id) {
return separator + '<a href="/users/' + id + '">@' + id + '</a>';
}
}]
});
},
parseHTML: function (input) {
return require('domador')(input, {
transform: function (el) {
if (el.tagName === 'A' && el.innerHTML[0] === '@') {
return el.innerHTML;
}
}
});
}
});
This kind of nudge to the Markdown compiler is particularly useful in simpler use cases where you'd want to preserve HTML elements entirely when they have CSS classes, as well.
Preserving Selection Across Input Modes
Note that both megamark and domador support a special option called markers, needed to preserve selection across input modes. Unless your parseHTML function supports this option, you'll lose that functionality when providing your own custom parsing functions. That's one of the reasons we strongly recommend using megamark and domador.
options.fencing
Prefers to wrap code blocks in "fences" (GitHub style) instead of indenting code blocks using four spaces. Defaults to true.
options.markdown
Enables Markdown user input mode. Defaults to true.
options.html
Enables HTML user input mode. Defaults to true.
options.wysiwyg
Enables WYSIWYG user input mode. Defaults to true.
options.defaultMode
Sets the default mode for the editor.
options.storage
Enables this particular instance woofmark to remember the user's preferred input mode. If enabled, the type of input mode will be persisted across browser refreshes using localStorage. You can pass in true if you'd like all instances to share the same localStorage property name, but you can also pass in the property name you want to use, directly. Useful for grouping preferences as you see fit.
Note that the mode saved by storage is always preferred over the default mode.
options.render.modes
This option can be set to a method that determines how to fill the Markdown, HTML, and WYSIWYG mode buttons. The method will be called once for each of them.
Same as options.render.modes but for command buttons. Called once on each button.
options.images
If you wish to set up file uploads, in addition to letting the user just paste a link to an image (which is always enabled), you can configure options.images like below.
{
// http method to use, defaults to PUT
method: 'PUT',
// endpoint where the images will be uploaded to, required
url: '/uploads',
// image field key, passed to bureaucracy, which defaults to 'uploads'
fieldKey: 'uploads',
// optional additional form submission data, passed to `bureaucracy`
formData: { description: 'A new image' },
// optional options for `xhr` upload request, passed to `bureaucracy`
xhrOptions: { headers: {} },
// optional text describing the kind of files that can be uploaded
restriction: 'GIF, JPG, and PNG images',
// should return whether `e.dataTransfer.files[i]` is valid, defaults to a `true` operation
validate: function isItAnImageFile (file) {
return /^image\/(gif|png|p?jpe?g)$/i.test(file.type);
}
}
woofmark expects a JSON response including a results property that's an array describing the success of each file upload. Each file's entry should include an href and a title:
The editor API allows you to interact with woofmark editor instances. This is what you get back from woofmark(textarea, options) or woofmark.find(textarea).
editor.addCommand(combo, fn)
Binds a keyboard key combination such as cmd+shift+b to a method using kanye. Please note that you should always use cmd rather than ctrl. In non-OSX environments it'll be properly mapped to ctrl. When the combo is entered, fn(e, mode, chunks) will be called.
e is the original event object
mode can be markdown, html, or wysiwyg
chunks is a chunks object, describing the current state of the editor
In addition, fn is given a this context similar to that of Grunt tasks, where you can choose to do nothing and the command is assumed to be synchronous, or you can call this.async() and get back a done callback like in the example below.
editor.addCommand('cmd+j', function jump (e, mode, chunks) {
var done = this.async();
// TODO: async operation
done();
});
When the command finishes, the editor will recover focus, and whatever changes where made to the chunks object will be applied to the editor. All commands performed by woofmark work this way, so please take a look at the source code if you want to implement your own commands.
editor.addCommandButton(id, combo?, fn)
Adds a button to the editor using an id and an event handler. When the button is pressed, fn(e, mode, chunks) will be called with the same arguments as the ones passed if using editor.addCommand(combo, fn).
You can optionally pass in a combo, in which case editor.addCommand(combo, fn) will be called, in addition to creating the command button.
editor.runCommand(fn)
If you just want to run a command without setting up a keyboard shortcut or a button, you can use this method. Note that there won't be any e event argument in this case, you'll only get chunks, mode passed to fn. You can still run the command asynchronously using this.async(). Note that the argument order chunks, mode is the reverse of that passed to addCommand (mode, chunks).
editor.parseMarkdown()
This is the same method passed as an option.
editor.parseHTML()
This is the same method passed as an option.
editor.destroy()
Destroys the editor instance, removing all event handlers. The editor is reverted to markdown mode, and assigned the proper Markdown source code if needed. Then we go back to being a plain old and dull <textarea> element.
editor.value(text)
If optional Markdown string text is provided, it is used to overwrite the current editor content, parsing into HTML if necessary. Regardless of whether text is provided, value() returns the current Markdown value for the editor.
editor.editable
If options.wysiwyg then this will be the contentEditable<div>. Otherwise it'll be set to null.
editor.mode
The current mode for the editor. Can be markdown, html, or wysiwyg.
editor.setMode(mode)
Sets the current mode of the editor.
editor.showLinkDialog()
Shows the insert link dialog as if the button to insert a link had been clicked.
editor.showImageDialog()
Shows the insert image dialog as if the button to insert a image had been clicked.
editor.showAttachmentDialog()
Shows the insert attachment dialog as if the button to insert a attachment had been clicked.
editor.history
Exposes a few methods to gain insight into the operation history for the editor instance.
editor.history.undo()
Undo the last operation.
editor.history.redo()
Re-applies the most recently undone operation.
editor.history.canUndo()
Returns a boolean value indicating whether there are any operations left to undo.
editor.history.canRedo()
Returns a boolean value indicating whether there are any operations left to redo.
chunks
Please ignore undocumented functionality in the chunks object.
Describes the current state of the editor. This is the context you get on command event handlers such as the method passed to editor.runCommand.
Modifying the values in a chunks object during a command will result in changes to the user input in such a way that Undo and Redo can be taken care of automatically on your behalf, basically by going back to the previous (or next) chunks state in the editor's internal history.
chunks.selection
The currently selected piece of text in the editor, regardless of input mode.
chunks.before
The text that comes before chunks.selection in the editor.
chunks.after
The text that comes after chunks.selection in the editor.
chunks.scrollTop
The current scrollTop for the element. Useful to restore later in action history navigation.
chunks.trim(remove?)
Moves whitespace on either end of chunks.selection to chunks.before and chunks.after respectively. If remove has been set to true, the whitespace in the selection is discarded instead.
woofmark.strings
To enable localization, woofmark.strings exposes all user-facing messages used in woofmark. Make sure not to replace woofmark.strings with a new object, as a reference to it is cached during module load.
bevacqua/woofmark có 1.6k sao GitHub — tải lại trang để xem số mới nhất, hoặc xem trực tiếp github.com/bevacqua/woofmark. TopGit phản chiếu số sao của GitHub nhưng không cam kết đến từng phút.
bevacqua/woofmark có phải mã nguồn mở không?
Có — bevacqua/woofmark phát hành theo license MIT, nghĩa là mã nguồn mở để đọc, fork và (tùy license) tái sử dụng. Mã: github.com/bevacqua/woofmark.
bevacqua/woofmark còn đang phát triển không?
Commit gần nhất trên bevacqua/woofmark là 2.6 năm trước (theo timestamp GitHub). Repo có 64 fork — một chỉ báo về mức độ quan tâm của cộng đồng.
bevacqua/woofmark dùng license gì?
bevacqua/woofmark phát hành theo license MIT. Nên mở file LICENSE trên GitHub để xác nhận — license metadata đôi khi lệch với thực tế dự án.
bevacqua/woofmark là gì?
bevacqua/woofmark (bevacqua/woofmark) là dự án JavaScript trên GitHub. Theo mô tả gốc: :dog2: Barking up the DOM tree. A modular, progressive, and beautiful Markdown and HTML editor
bevacqua/woofmark viết bằng ngôn ngữ gì?
bevacqua/woofmark chủ yếu viết bằng JavaScript. Trường "language" của GitHub dựa trên phần lớn byte ở nhánh mặc định.
Cùng nhóm Frontend còn repo nào?
bevacqua/woofmark thuộc nhóm Frontend trên TopGit, cùng 5 topic GitHub. Trang Trending và Topics liệt kê các repo cùng số sao và cùng ngôn ngữ để so sánh.
Đọc thêm về bevacqua/woofmark ở đâu?
Trang TopGit này là một snapshot — tab "Readme" hiển thị nguyên văn README của repo (đã bỏ link, giữ ảnh). Repo GitHub ở github.com/bevacqua/woofmark là nguồn chính thức.
Đọc đầy đủ README ở tab phía trên.
Chưa chắc woofmark có hợp với bạn?
Để ChatGPT, Claude hoặc Perplexity tìm hiểu giúp — bấm bên dưới và xem AI nói gì về woofmark.