Code Monkey home page Code Monkey logo

carbon's Introduction

Carbon

Carbon is an extendable rich text editor in the browser that is consistent and beautiful. Carbon is inspired by Medium Editor. It is built on top of an internal model for an article that clients can translate to and from JSON and HTML.

The browser is built with extendability in mind and supports operations and history to allow proper undo-redo management with support for embeddable objects and uploading attachments.

Carbon is an open source project by your friends at Manshar - an open source publishing platform for Arabic content. Carbon supports both LTR and RTL languages.

Carbon is still in early alpha and you might run into bugs. If you do, please file a bug request or send a pull request with the proper fix. :mineral:

Compatibility And Tests

Carbon is still in alpha. Carbon is not yet compatible with all major browsers and platforms. Currently it only supports Desktop Browsers like Chrome, Firefox and Safari. We’re still working on adding more compatibility, especially on mobile browsers, feel free to send us pull requests 😄. We also don’t have a good unit tests coverage yet and we’ll continue working on adding those.

Usage

You can load Carbon from our CDN (Recommended)

<link href="https://cdn.carbon.tools/0.2.62/carbon.min.css"></script>
<script src="https://cdn.carbon.tools/0.2.62/carbon.min.js"></script>

To use the latest version use latest (Be careful this will most likey keep breaking until Carbon APIs are stable).

<link href="https://cdn.carbon.tools/latest/carbon.min.css"></script>
<script src="https://cdn.carbon.tools/latest/carbon.min.js"></script>

Or you can grab carbon source code (including minified files) using bower:

bower install carbon-tools/carbon

And then include the main Carbon scripts and css:

<link href="bower_components/carbon/dist/carbon.min.css"/>
<script src="bower_components/carbon/dist/carbon.min.js"></script>

After that you can create a new Carbon object and pass it the element you want to transfer into your editor.

<div id="my-editor"></div>
<script>
  var editor = new carbon.Editor(document.getElementById('my-editor'));
  editor.render();
</script>

That’s it. You now have Carbon enabled. See demo/index.html source code and demo/rtl-demo.html for more usage examples.

carbon.Editor accepts a second optional parameters you can use to configure the editor with. Here’s a table of the parameters you can pass with their explanation:

Parameter Explanation Values
modules A list of custom components and extensions you’d like to enable in the editor.

The editor has built in extensions and components enabled for every editor, these include: [Section, Paragraph, List, Figure, FormattingExtension, ToolbeltExtension, UploadExtension]

A list of classes for each module you want to enable.

Default: []

Example: [YouTubeComponent, VimeoComponent]

rtl Whether this editor is an RTL editor or not. Boolean

Default: false

article A carbon.Article object to initiate the editor with. carbon.Article object.

Default: A carbon.Article instance with one Paragraph with placeholder ‘Editor’

The editor also exposes methods to allow the clients to interact with it.

Method Args Explanation
registerRegex regexStr: A regex string to listen to.

callback: A callback function to call when the regex is matched.

Registers a regex with the editor.
getHTML Returns HTML string represents the whole article
getJSONModel Returns JSON model for the article
loadJSON classmethod json: JSON model to create article model from Creates and sets a new article model from JSON format
install module: The module to install in the editor.
getModule name: Name of the module to return Returns the module with the given name
registerToolbar name: Toolbar name toolbar: The toolbar object to register Registers a toolbar with the editor
getToolbar name: Name of the toolbar to return Returns toolbar with the given name
addEventListener name: Event name handler: Callback handler for the event
registerShortcut shortcutId: A String representing the shortcut (e.g. "cmd+alt+1") handler: A function to call when the shortcut is triggered optForce: Optional argument whether to override an already registered shortcut Registers a shortcut listener with the editor
isEmpty Returns true if the editor is empty
getTitle Returns the first header paragraph text in the editor
getSnippet Returns the first non-header paragraph text in the editor

Installing More Modules

The editor by default installs:

  • Section, Paragraph, List and Figure Components

  • Formatting, Toolbelt and Upload Extensions

Carbon also package one other components that are not installed by default, you can install them by providing them to the modules attribute of the editor params, or by calling editor.install(module) method.

editor.install(carbon.YouTubeComponent);

Article Model

The editor relies on carbon.Article to render and control the model. Article is made of Sections. A section is made of Components. A component can be a Paragraph, Figure, List or others that inherit from carbon.Component.

Component

Component is an abstract construct that all components in the editor extend and inherits from. All components have these attributes and are setup by passing them as an object parameter to the constructor.

Parameter Explanation Values
name A unique name of the component instance. String

Default: Randomly generated unique ID using Utils.getUID()

editor A reference to the editor that contains this component instance. Editor instance
section A reference to the section that contains this component instance Section instance
inline Whether this component is a single line component If set to True in a paragraph, the editor will not allow the component to create more siblings.

Default: False

parentComponent If the component is nested and initialized in another component this points to the parent component

Each component will also inherit Component methods. Components are responsible of overriding and implementing some of these methods.

Method Explanation Should Override?
getNextComponent Returns the next component No
getPreviousComponent Returns the previous component No
getIndexInSection Returns the index of the component in its section No
getJSONModel Returns a JSON representation of the component. This representation must have at least a ‘component’ and ‘name’ attributes with component being the CLASS_NAME of the component and the unique name of the component instance. Must Override
getDeleteOps Returns the operations needed to delete this component Must Override
getInsertOps Returns the operations needed to be executed to insert this component in the editor Must Override
getLength Returns the length of the component content. For text components this is the text length and for other components this should be 1. Optional Override
getInsertCharsOps Returns operations needed to insert characters in a component (only needed for text-based components) Optional Override
getRemoveCharsOps Returns operations needed to remove characters in a component (only needed for text-based components) Optional Override
getUpdateOps Returns operations needed to update component’s attributes. Optional Override

Every component must also:

  • Expose a dom property that is the container element of the component. The dom element must set a name attribute equals to the component.name property

  • Define a class variable CLASS_NAME with a string name for its class, usually matching its constructor name.

  • Register its constructor with the carbon.Loader module. This needs to happen at the load time

carbon.Loader.register(Compnent.CLASS_NAME, Component);
  • Implement an onInstall(editor) class method that is called when a component is installed in an editor.

  • Implement a fromJSON class method that allows the component to be recreated from its JSON representation.

  • Optionally worry about its selection and how users interact with it

Article

Article consists of sections. Article is also responsible of transaction operations on the article and keeping history of the changes to allow undo and redo operations.

Section

Section consists of components and is responsible of inserting and removing components in the section.

Paragraph

A paragraph takes few arguments when initializing it programmatically. Paragraph is any text-element that allow users to input text.

Parameter Explanation Values
text Text in this paragraph. String
placeholderText A placeholder text to show in the paragraph when it’s empty.
paragraphType The type of paragraph. Paragraph.Types = { Paragraph: 'p', MainHeader: 'h1', SecondaryHeader: 'h2', ThirdHeader: 'h3', Quote: 'blockquote', Code: 'pre', Caption: 'figcaption', ListItem: 'li' };

Default: Paragraph.Types.Paragraph

formats A list of objects representing formatting of the paragraph.

This attribute is mostly managed by the FormattingExtension.

Example: [{from: 3, to: 10, type: ‘em’}]

Figure

Figure component registers a regex to match any image links pasted in a new paragraph and replaces the link with an image. Figure component also handles its own selection by adding a click listener on the image.

Parameter Explanation Values
src The source attribute for the image inside the figure. Data URI, Remote absolute or relative URL
caption The caption text for the figure.

Internally the caption is implemented by nesting a Paragraph component with inline:true and placeholder:params.captionPlaceholder

captionPlaceholder Placeholder for the caption.
width Width of the figure in the editor. String

Default: ‘100%’

List

Parameter Explanation Values
tagName The tag name to use for the container of the component. Values: ‘ol’ (List.ORDERED_LIST_TAG) or ‘ul’ (List.UNORDERED_LIST_TAG)

Default: List.UNORDERED_LIST_TAG

components List of paragraphs components of type ListItem representing list items. Default: A list with a single empty Paragraph of type listItem.

Attachment

Attachment is a class to track the progress of an upload in progress and update the inserted component attributes after it is done.

UploadExtension creates attachments when the user upload images and insert a pending figures into the editor by reading the image as data URL. The client is notified through an event and should handle persisting the file remotely. After the client is done persisting the file, they need to update the attachment attributes - e.g. Update the source to a remote URL.

Attachment has the following attributes.

Parameter Explanation
file The file the user choose in the file picker
figure Figure object tied to this file upload to update its attributes
insertedOps Operations used to insert the figure object. This allows attachment to update the history of insertedOps to match the new attributes to avoid any discrepancies.

And the following method can be used to update the attributes of the attachment.

Method Args Explanation
setAttributes attrs: An object with the attributes and their value to update to. Update attachment attributes

Editor Shortcuts

The editor supports formatting shortcuts:

  • Ctrl/Cmd+B to bold

  • Ctrl/Cmd+i to Italic

  • Ctrl/Cmd+u to Underscore

  • Ctrl/Cmd+s to Strikethrough

  • Ctrl/Cmd+k to Activate link field to type a URL and hit enter to apply it

  • Ctrl/Cmd+Alt+1/2/3 to apply header format (h1, h2, h3)

  • Ctrl/Cmd+Alt+4 to apply Quote

  • Ctrl/Cmd+Alt+5 to apply Code

Styling Carbon Editor

Carbon includes a default styling for the editor components and the toolbars. You can override these in your own stylesheet or not include the main stylesheet for carbon and build your own.

Extending Carbon Editor

There are currently two ways to extend the editor: Custom Components and Extensions. The difference is in the definition.

Custom Components are built to create multiple instances of and insert into the editor - e.g. YouTubeComponent allows people to insert multiple YouTube videos by creating multiple YouTubeComponent’s.

Whereas Extensions are meant to be initialized once and add functionality to the editor. For example, FormattingExtension enables formatting toolbars in the editor to allow formatting paragraphs and text.

Custom Components

Developers can write custom components to extend the editor with, currently there are two ways your component can interface with the editor and allow insertion: RegEx Triggers and Toolbelt.

RegEx Triggers

A custom component can register a regex to listen to with the editor. When that RegEx is matched when the user hits enter on the current paragraph the editor will notify the component that matched that regex. For example, YouTubeComponent registers a RegEx to match a YouTube link typed in a new paragraph and then replaces that paragraph with a YouTubeComponent which embeds the YouTube video in the editor!

See YouTubeComponent and GiphySearch to learn more about extending the editor.

Editor Toolbelt

A component (or an extension) can also add a button on the editor toolbelt to allow insertion into the editor. For example, YouTubeComponent could add a "Embed YouTube" Button to the toolbelt that prompts the user to paste a link URL.

See UploadExtension to see how you can add your own actions to the Toolbelt.

Extensions

An extension is used to enable new global functionality to the editor. For example, UploadExtension adds an "Upload Image" button to the editor’s toolbelt which allows users to upload images and insert the image in the editor.

See ToolbeltExtension to see how you can extend the editor with extensions.

Serializing and Deserializing Article

You can store and load the article model you can serialize and deserialize the article to and from JSON format using editor.getJSONModel() and editor.loadJSON(json). For example, the following example saves the article in localStorage and loads it back.

var saveBtn = document.getElementById('saveBtn');
saveBtn.addEventListener('click', function() {
  var json = editor.getJSONModel();
  localStorage.setItem('article', JSON.stringify(json));
});
var loadBtn = document.getElementById('loadBtn');
loadBtn.addEventListener('click', function() {
  var jsonStr = localStorage.getItem('article');
  editor.loadJSON(JSON.parse(jsonStr));
});

See demo/index.html for an example of storing and loading from the localStorage.

Editor Events

change

Fired when the editor content changes.

attachment-added

Fired after an attachment has been added to the editor to allow clients to upload the attachment and update the source. The client can access the attachment object from event.detail.attachment. See Attachment for more details. The following example listens to the event and update the attachment source and caption after uploading to a remote resource.

editor.addEventListener('attachment-added', function(event) {
  var attachment = event.detail.attachment;
  ajax.uploadFile(attachment.file, function uploadComplete(data) {
    attachment.updateAttributes({
      src: data.src,
      caption: data.caption
    });
  });
});

selection-changed

Fires when the selection in the editor changes.

Contributing

We'll check out your contribution if you:

  • Provide a comprehensive suite of tests for your fork.
  • Have a clear and documented rationale for your changes.
  • Package these up in a pull request (all squashed and neat if possible).

We'll do our best to help you out with any contribution issues you may have.

Feel free to send pull requests implementing new components.

Development

# clone
git clone --recursive git://github.com/manshar/carbon.git

cd carbon
npm install
bower install

# start coding with live reloading
grunt serve

Run grunt test to run the tests and run grunt serve to build the project and open the demo page. Any changes will be built and you'd need to refresh the demo to see them. You can also run grunt to just build and watch files, you can use this when you want to write some tests and see them running as you change files.

License

Simplified BSD. See LICENSE in this directory.

carbon's People

Contributors

mkhatib avatar rashahussein avatar saada avatar saadshahd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

carbon's Issues

Create Photo Collages When User Upload Multiple Photos

When uploading multiple photos we'd like to insert a collage layout(s) that split the images in a nice way. Similar to what Medium.com does.

The main idea is that when the user selects multiple photos, we need to create multiple layouts. One way to go about this is by creating multiple row layouts to insert three (or more) images per row with the same height. We don't have a row layout but we can create one. flexbox might be useful here but I don't think it's necessary just limiting the height of the image would do nicely I believe.

screen_shot_2016-01-20_at_8 22 52_pm

Formatting Toolbar

The formatting toolbar need to trigger on selecting text with mouse or keyboard selections. This needs to support basic formatting, bold, italic, link, quote, h1, h2 and code.

Keyboard Shortcuts for Formatting

Ctrl/Cmd+B ▶️ bold
Ctrl/Cmd+i ▶️ italic
Ctrl/Cmd+k ▶️ link
Ctrl/Cmd+Alt+1/2 ▶️ h1/h2
Ctrl/Cmd+Alt+0 ▶️ un-header (p)

Any Links Embed Card

We need a way to paste a mnshar's specific article url to get it embedded.
like https://www.manshar.com/articles/0000 then got a frame for it like youtube's or other's component's ones.

Implement Article.trim()

This would remove blank paragraphs components at the start and the end of the article. For example calling it on an article that has something like the following:

<p></p>
<p>   </p>
<p>Hello</p>
<p>&nbsp;</sp>
<p></p>

Would result in the following:

<p>Hello</p>

Adding links

The inline-formatting toolbar should allow users to add links.

Preserve Formats of List Items when Pasting

Pasting seems to strips out the formatting of list items. For example, if you copy the following list and paste it into a Carbon editor, it will remove the bold and italic.

  • Hello World
  • Goodbye Now

This can be handled inside Editor.processPastedContent method.

Firefox compatibility

There are many issues many other browsers than chrome.
I had many issues recently while I am using Firefox but this is the worst.
I wrote an article in a simple text editor then I copied it to manshar :
this picture

هناك عدة مشاكل مع المتصفحات ما عدا كروم.
منذ فترة بدأت استعمال فيرفوكس وواجهتني عدة مشاكل ولكن كانت هذه هي الأسوأ:
this picture

Writing English should switch the paragraph to be LTR in RTL mode

In RTL mode, if user starts typing LTR-language, should we switch that paragraph to LTR.

Currently, for example, all code blocks are LTR regardless of the mode.

This also applies to the other situation where we have the editor in LTR mode and the user wrote an RTL paragraph - should that switch the paragraph to RTL.

Fix Broken Tests

After npm install, I ran into this issue

$ grunt test
Running "browserify:tests" (browserify) task
>> Bundle build/editor.spec.js created.

Running "jshint:dist" (jshint) task
>> 9 files lint free.

Running "jshint:test" (jshint) task
>> 7 files lint free.

Running "karma:continous" (karma) task
INFO [karma]: Karma v0.12.37 server started at http://localhost:8080/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Mac OS X 0.0.0)]: Connected on socket 4nCswkj99XcDPqfMO15e with id 4897509
PhantomJS 1.9.8 (Mac OS X 0.0.0) Article should return the json model FAILED
    Expected Object({ sections: [ Object({ paragraphs: [ Object({ name: '12', text: '', paragraphType: 'p', formats: [  ] }) ] }) ] }) to equal Object({ sections: [ Object({ paragraphs: [ Object({ text: '', name: '12', paragraphType: 'p' }) ] }) ] }).
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3669
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor should initialize listeners on initilaization FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3805
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should handle Enter key FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3828
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should handle backspace key FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3884
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should handle delete key FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3925
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should remove selected text when backspace FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3966
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should remove selected text when typing character FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4000
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should not remove selected text when non-changing key FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4033
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handleKeyDownEvent should undo/redo FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4066
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.handlePaste should call processPastedContent FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4093
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.processPastedContent should generate single operation for inline content FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4107
PhantomJS 1.9.8 (Mac OS X 0.0.0) Editor Editor.processPastedContent should generate proper operations when pasting multi-line FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.handleKeyDownEvent.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:2108
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:367
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4121
PhantomJS 1.9.8 (Mac OS X 0.0.0) Paragraph should return the json model FAILED
    Expected Object({ name: '1234', text: 'hello', paragraphType: 'p', formats: [  ] }) to equal Object({ name: '1234', text: 'hello', paragraphType: 'p' }).
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4205
PhantomJS 1.9.8 (Mac OS X 0.0.0) Section should return a json model FAILED
    Expected Object({ paragraphs: [ Object({ name: '12', text: '', paragraphType: 'p', formats: [  ] }), Object({ name: '23', text: 'Hello p1', paragraphType: 'p', formats: [  ] }) ] }) to equal Object({ paragraphs: [ Object({ text: '', name: '12', paragraphType: 'p' }), Object({ text: 'Hello p1', name: '23', paragraphType: 'p' }) ] }).
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4283
PhantomJS 1.9.8 (Mac OS X 0.0.0) Selection should initialize event listeners FAILED
    TypeError: 'undefined' is not a function (evaluating 'this.updateSelectionFromWindow.bind(this)')
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:3348
        at /Users/saada/HackHackHack/Projects/manshar/editor/build/editor.spec.js:4349
PhantomJS 1.9.8 (Mac OS X 0.0.0): Executed 42 of 42 (15 FAILED) (0.026 secs / 0.014 secs)
Warning: Task "karma:continous" failed. Use --force to continue.

Aborted due to warnings.

Is there a special way to setup PhantomJS?

EmbeddedComponent Loading Placeholder

While the embedded components are being loaded, we need to present a nice placeholder in place of the component that takes the same size. We need a way to listen to render finished events and hide that placeholder and show the rendered embed.

Refactor Text Input Handling

Input handler is being called on every input event which causes the typing experience to lag and have some delays.

The idea is to debounce the handler and write better logic to understand what was deleted or inserted to run specific operations insertChars and deleteChars instead of updateComponent.

Lists Deletion is Buggy

When having multiple lists with multiple items in a long document, try to select all and delete. This needs further investigation.

Embedding YouTube Videos

The editor should allow the insertion of youtube videos and playlists through side-toolbar or through detecting pasted YouTube link.

Weird behavior

I faced a weird behavior when hit the Shift+Enter to get down by one line, and I lost the whole paragraph. Also the one line down is not supported so far ?

Instant Photos Filters

Allow the users to select pre-defined filters (instagram-like) to apply on photos.

There are multiple ways one can accomplish this, one by just sticking with CSS properties that allow us to do that, the other is by more advanced methods with some canvas manipulations. I think we should start with experimenting with pre-defined filters with classnames.

See:

Load content from Html

Hi,

Congrats for this nice tool.
I'm trying it to see if it can replace an old Wysiwyg editor in a CMS.

The thing is at this time, contents are store in the database as HTML.

In the editor description you say "It is built on top of an internal model for an article that clients can translate to and from JSON and HTML".

But I was not able to find a way to load HTML content.
Is this option already available ?
Or can you advice a way to do such a thing ?

Thanks for your answer.

Regards.

Missing instructions on installing json-tree?

So I tried running git submodule init and then grunt serve. The demo ran fine but I can't see the json-tree generated on the right-hand side at all.
Not sure what I'm doing wrong.
Thanks

Inserting Images using URL or Upload

Allow inserting Images using URL (by detecting a pasted image link and by formatting bar) and upload by formatting bar or by a separate side-toolbar.

Support for Code Blocks & same-paragraph multi-line text

Currently we have no way for users to write a large block of code. The current support is limited to single-line code and paragraphs. There isn't a way to insert a \n or <br> characters to allow smaller line height.

As mentioned in #42. One suggestion from @RashaHussein to implement this is to add a class for special paragraphs to treat them as "lines", for example one can have the following to signify that these shouldn't be padded as the normal paragraphs:

<p class="nomargin">Line 1</p>
<p class="nomargin">Line 2</p>
<p class="nomargin">Line 3</p>

Of course one could add some logic to handle \n or <br> instead, this requires some more thoughts and discussion on which way to go.

As for code blocks, we could aim to achieve something similar to Lists, where the UL element would have a special class like .code-block and the LIs representing lines of codes. Using a similar logic (or the same - logic) as Lists also could give us the tab-to-indent logic for both code and normal lists.

<ul class="code-block">
  <li>def sayHello(name):
  <li>    print 'Hello, ', name
</ul>

Required Components

This allows clients to require the user to enter specific input, for example requiring the article to have a title (h1 Paragraph) and a subtitle (h2 Paragraph) and an image. This would prevent the user from deleting these required components and show a simple error message when the user tries to delete them.

Selecting a non-text component and copying doesn't work

When selecting a single non-text component (figure, embed) copying and pasting doesn't work.

Not sure what would copying a selected figure means, does it copy the source of the figure or does it copy to dom tree of it.

Handle Cut Events

Currently, we don't handle cut events at all, falling back to the browser handling which breaks the model representation of the article.

Handle Enter inside figcaption of a staged component

Repro:

  • Have a figure as the last component in the document
  • Type something in its caption
  • Hit Enter
  • Observe that instead of creating a new single-column layout it and inserting the paragraph in it
  • It inserts the paragraph inside the staged layout which should never happen.

Implement an API to check in-progress attachments

This API would be something like editor.getAttachments(optIncludeFinished) and returns the list of attachments objects that are still being processed (uploaded or otherwise). If the optIncludeFinished parameter is passed true, it should return all attachments that has been added to the editor.

More thinking needs to go into this and how we handle attachments in general.

Delete key is broken

For some weird reason it actually inserts a character now instead of deleting.

Video Component

Similar to Figure component, we need a video component for HTML5 videos.

After introducing this, this would allow us also to allow uploading videos and inserting the VideoComponent for them - And will also enable us to build +velfie/+vlog for quick video selfie recording 🐼

Process Pasting Embedded Videos/Embeds

When users copy a large block of text with a YouTube embed for example the embed is stripped out and not processed.

Typically we need to get the embed source and create an EmbeddedComponent for them. One challenge might be is that I am not sure oembed endpoints would return the correct results when using the embed src. Usually the normal link and the embed source are different, for example:

Embedly oembed endpoints seem to catch both type of URLs for YouTube.

Strange Input when writing/editing

Sometimes the cursor gets a mind of it's own and jump back or forth , tested on the demo site using ios 9.2 .

Also if i put cursor on word and select a replacement from the virtual keyboard , sometimes the words get jumbled

Pasting a big chunk of Code

Automatically detect the pasted content is code and format it accordingly (regardless if the copied content is already formatted or not).

This is blocked by #59

Inserting Lists

Ordered and unordered lists, whether through syntactic analysis and replacement (typing "1." followed by space at the beginning of the line would trigger an ordered list) or through formatting bar.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.