All Projects → xpl → Wyg

xpl / Wyg

Licence: other
A new WYSIWYG editing experience for the modern web

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Wyg

Etherealengine
C++ Game Engine and Editor
Stars: ✭ 653 (+794.52%)
Mutual labels:  editor, wysiwyg
Quill
Quill is a modern WYSIWYG editor built for compatibility and extensibility.
Stars: ✭ 31,554 (+43124.66%)
Mutual labels:  editor, wysiwyg
Uncolored
(Un)colored — Next generation desktop rich content editor that saves documents with themes. HTML & Markdown compatible. For Windows, OS X & Linux. — http://n457.github.io/Uncolored/
Stars: ✭ 733 (+904.11%)
Mutual labels:  editor, wysiwyg
Angular Editor
A simple native WYSIWYG editor component for Angular 6 -10+
Stars: ✭ 428 (+486.3%)
Mutual labels:  editor, wysiwyg
Rich Text Editor
Math editor (http://digabi.github.io/rich-text-editor/)
Stars: ✭ 45 (-38.36%)
Mutual labels:  editor, wysiwyg
Sceditor
A lightweight HTML and BBCode WYSIWYG editor
Stars: ✭ 503 (+589.04%)
Mutual labels:  editor, wysiwyg
Awesome Medium Editor
Medium.com WYSIWYG editor clone, with RTL support.
Stars: ✭ 12 (-83.56%)
Mutual labels:  editor, wysiwyg
Craft.js
🚀 A React Framework for building extensible drag and drop page editors
Stars: ✭ 4,512 (+6080.82%)
Mutual labels:  drag-and-drop, wysiwyg
Megadraft
Megadraft is a Rich Text editor built on top of Facebook's Draft.JS featuring a nice default base of components and extensibility
Stars: ✭ 982 (+1245.21%)
Mutual labels:  editor, wysiwyg
Remirror
ProseMirror toolkit for React 🎉
Stars: ✭ 973 (+1232.88%)
Mutual labels:  editor, wysiwyg
Draftail
📝🍸 A configurable rich text editor built with Draft.js
Stars: ✭ 413 (+465.75%)
Mutual labels:  editor, wysiwyg
Deckdeckgo
The web open source editor for presentations
Stars: ✭ 1,117 (+1430.14%)
Mutual labels:  editor, wysiwyg
Grapesjs Mjml
Newsletter Builder with MJML components in GrapesJS
Stars: ✭ 379 (+419.18%)
Mutual labels:  editor, wysiwyg
Pen
enjoy live editing (+markdown)
Stars: ✭ 4,740 (+6393.15%)
Mutual labels:  editor, wysiwyg
Re Editor
一个开箱即用的React富文本编辑器 🚀re-editor
Stars: ✭ 367 (+402.74%)
Mutual labels:  editor, wysiwyg
Flyapi
基于SSM layui 开发的多人博客系统,目标在于让每个人都能精准阅读和专注写作。
Stars: ✭ 16 (-78.08%)
Mutual labels:  blog, editor
Awesome Web Editor
🔨 Open source WEB editor summary
Stars: ✭ 306 (+319.18%)
Mutual labels:  editor, wysiwyg
Hyper Editor
A backend independent visual composer for web.
Stars: ✭ 332 (+354.79%)
Mutual labels:  editor, wysiwyg
Jodit
Jodit - Best WYSIWYG Editor for You
Stars: ✭ 947 (+1197.26%)
Mutual labels:  editor, wysiwyg
Canner Slate Editor
📝Rich Text / WYSIWYG Editor built for Modularity and Extensibility.
Stars: ✭ 1,071 (+1367.12%)
Mutual labels:  editor, wysiwyg

Wyg Editor

ATTENTION: WORK IN PROGRESS. NOT MEANT TO BE USED BY ANYONE — STILL IN DEVELOPMENT PHASE, MANY BUGS TO FIX

A new WYSIWYG editing experience for the modern web.

  • Built entirely from scratch
  • Works better than anything before
  • Minimalistic UI, blazingly fast (works with DOM nodes directly)
  • Normalizes entered markup on the fly (splits paragraphs with linebreaks)
  • Smart linebreak management (no Shift-Enter required to put a newline)
  • Drag & drop media between paragraphs
  • Insert media by simply pasting links into text
  • Arranges media nicely in columns
  • Fluid layout with animations
  • Floating markup panel with custom tags support
  • Serializes to/from JSON
  • Custom undo/redo manager (works with arbitrary DOM changes)
  • Intercepts native undo/redo commands (not limited to hotkeys)
  • Pluggable/extendable architecture based on traits
  • Test-driven

👉 Watch demo on YouTube

pic

Recent news

TODO

In it's current state the stuff is quite unusable. Things to be done:

  • [ ] Safari compatibility (some tests fail).
  • [ ] MS Edge compatibility.
  • [ ] Implement hyperlink editing in the floating markup panel.
  • [x] Fix nonworking plus button (should trigger file open dialog).
  • [ ] Add build script.

Minor (but important) tasks:

  • [x] Implement ability to insert text paragraphs at arbitrary location around media rows (see demo video).
  • [ ] Get rid of jQuery (moving to the Node+ library)
  • [ ] Make use of $depends mechanism for traits.

Running demo

You will need node and npm.

  1. Clone repo with git clone http://github.com/xpl/wyg
  2. Run npm install to install dependencies.
  3. Run node demo.js
  4. Open localhost:1333 in Chrome (Safari / Firefox / Edge support is coming)

Updating demo

Instead of git pull, use ./update.sh (runs git pull && npm update). This is needed because it's dependencies change rapidly and you need to update all shit together to maintain consistency.

Under the hood

Everything is built upon a JS library called Useless.js (working title). It delivers composable traits support to JavaScript and a powerful unit test system. You may read more about it in the project's wiki. DOM operations are based on the Node+ library (coming with Useless).

Setting value

Editor's state is exposed via the value property:

wyg.value = [
    { type: 'p', html: 'this is <b>text paragraph</b>, containing arbitrary HTML' },
    { type: 'media', // media row
      media: [
          { type: 'img', // media element
            src:  'http://example/some-image.jpg',
            originalSize: { width: 1280, height: 720 } },
          
          { type: 'iframe', // media element
            src:  '...'
            originalSize: ... }
      ]
    },
    ...
  ]

Interpreting value

When reading value, some additional metadata is returned on media elements:

  { type: 'img',
    src:  '...'
    originalSize: ...
    relativeSize: {
        width:  0.5,   // relative to page width
        height: 0.247  // relative to element width
    }
  }

Relative size encodes the calculated size of a media item, relative to page width. It is abstract from absolute metrics and screen sizes, so a responsive layout could be generated from that schema.

Here's how you can describe an element which height is encoded as a percentage of its width, with pure CSS:

<media-row>
  <media-item style="width: 50%;">
      <spacer style="padding-top: 24.7%;"></spacer>
      <content style="background-image:url(some-image.jpg);"></content>
  </media-item>
  ...
</media-row>
media-row          { display: block; white-space: nowrap; overflow: hidden; }
media-item         { display: inline-block; position: relative; overflow: hidden; }
media-item spacer  { display: block; background-size: cover; }
media-item content { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }

It works because padding percentages are relative to element's width, even with padding-top. So a height can be encoded via an additional spacer element put inside.

Implementing the file uploading

See the reference implementation at the file_uploading.js trait. You will need to implement the uploadFile method.

With a Promise, it should return elements instantiated by the renderMedia factory:

uploadFile: function (file, then) {
                return JSONAPI.uploadFile ('/uploads', file).then (this.$ (function (response) {
                    return this.renderMedia ({ type: 'img',
                                                src: '/uploads/' + response.file + '.jpg',
                                       originalSize: { width: response.w,
                                                      height: response.h } }) })).panic },

See instructions below on how to extend the renderMedia method behavior.

Adding support of new media types

All incoming URLs that are pasted from clipboard go through parseMedia facility. This function converts URLs to abstract media definitions in JSON format. Those definitions, when serialized, can be easily stored/interpreted by external applications (e.g. template engines, when rendering to static HTML at server side).

Example:

this.parseMedia ('https://www.youtube.com/watch?v=JQ0qgyCuoCw')
    .then (x => console.log (x))

Rendered output will be:

{ type: 'iframe',
  src:   '...',
  originalUrl: 'https://www.youtube.com/watch?v=JQ0qgyCuoCw',
  originalSize: { width: ..., height: ... } }

This is then feeded to the media rendering facility, which processes those definitions, producing DOM elements:

this.renderMedia (def) // produces DOM element from that definition

Both functions can be extended to introduce new behavior. This is how you do that.

1. Extending parseMedia

Tag a method with $parseMedia to designate it as an URL parser. For asynchronous parsing, you can return Promise:

images: $parseMedia (function (url) {
            return Image.fetch (url)
                        .then (function (img) {
                                return { type: 'img',
                                          src:  url,
                                 originalSize: { width: img.width,
                                                height: img.height } } }) }),
youtube: $parseMedia (function (url) {
            var match = url.match(/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/)
            var id = match && (match[7].length == 11) && match[7]
            if (id) {
                return {
                    type: 'iframe',
                     src: '//www.youtube.com/embed/' + id + '?wmode=transparent',
            originalSize: { width: 16000, // numbers here encode aspect ratio + max size (not actual onscreen size)
                           height: 9000 } } } }),

You can also use tag groups (a feature of every $prototype) to reduce clutter in case of many parser methods:

$parseMedia: {
    images: ...
    youtube: ...
    vimeo: ...
    soundcloud: ... }

You can query what types are supported by checking static supportedMedia property. It will be generated from method names:

MyEditor.supportedMedia // ['images', 'youtube', ...]

For those who are curious, here's the actual implementation of the parseMedia factory:

parseMedia: function (url) {

    /*  Gather values from all methods tagged with $parseMedia   */

        var values = _.map (this.constructor.$membersByTag.parseMedia,
                                function (def, name) {
                                    return this[name] (url) || Promise.reject (null) }, this)
    
        return Promise.firstResolved (values)
                      .then (function (media) {
                                return _.extend (media, { originalUrl: url }) }) },

2. Extending renderMedia

This is done much the same way as with the former one:

img: $renderMedia (function (media) {
        return N.img.attr ({ src: media.src,
                           width: media.originalSize.width,
                          height: media.originalSize.height }) }),
iframe: $renderMedia (function (media) {
            return N.div.add (
                   N.iframe.attr ({ src: media.src, frameborder: 0, allowfullscreen: true })) } }),

Those methods are dispatched by looking into the type property in media definitions.

Changing default icons

Default icons are hard-coded as SVG HTML, and you can change them them by overriding these methods:

makeWaitIcon: function () {
                return N.div.extend ({ className: 'wyg-icon', innerHTML: '<svg>...' }) },

makeAddIcon: function () {
                return N.div.extend ({ className: 'wyg-icon', innerHTML: '<svg>...' }) },

Expected return value is a DOM node. For example, returning an FontAwesome icon (some CSS tweaks may be required):

make: function () {
        return N.div.cls ('wyg-icon fa fa-plus-square') },
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].