Git Product home page Git Product logo

htmlflow's Introduction

HtmlFlow

Build Status Maven Central Version Coverage Status javadoc HtmlApiFaster javadoc HtmlFlow

Petclinic Sample

HtmlFlow is a Java DSL to write typesafe HTML in a fluent style. You may use the utility Flowifier.fromHtml(String html) if you need the HtmlFlow definition for an existing HTML source:

output  ↴
HtmlFlow
  .doc(System.out)
    .html() // HtmlPage
      .head()
        .title().text("HtmlFlow").__()
      .__() // head
      .body()
        .div().attrClass("container")
          .h1().text("My first page with HtmlFlow").__()
          .img().attrSrc("http://bit.ly/2MoHwrU").__()
          .p().text("Typesafe is awesome! :-)").__()
        .__() // div
      .__() // body
    .__(); // html
<html>
  <head>
    <title>HtmlFlow</title>
  </head>
  <body>
    <div class="container">
      <h1>My first page with HtmlFlow</h1>
      <img src="http://bit.ly/2MoHwrU">
      <p>Typesafe is awesome! :-)</p>
    </div>
  </body>
</html>
↸  Flowifier.fromHtml("...")

NOTICE for those migrating from legacy API <= 3.x read next about Migrating from HtmlFlow 3.x to 4.x.

HtmlFlow is the most performant engine among state of the art template engines like Velocity, Thymleaf, Mustache, etc and other DSL libraries for HTML such as j2Html and KotlinX Html. Check out the performance results in the most popular benchmarks at spring-comparing-template-engines and our fork of xmlet/template-benchmark.

xmlet/spring-petclinic provides an implementation of the Spring-based petclinic with HtmlFlow views.

Why another templating engine ?

Every general purpose language has its own template engine. Java has several. Most of the time, templates are defined in a new templating language (i.e. external DSL). To allow template engines to produce a view based on a template, they generally use the concept of model.

One of the problems of this technic is that you will end up with a template that won't be type checked. So if you have a typo inside your template, the compiler won't be able to help you before the template is rendered.

HtmlFlow took a different approach. Templates are expressed in an internal DSL. You will write normal Java code to produce your template. So, the full Java toolchain is at your disposal for templating. Put it simply, HtmlFlow templates are essentially plain Java functions.

HtmlFlow is not the only one using this approach. But it's the fastest one. Bonus points it also produces only valid HTML according to HTML 5.2.

Table of Contents

Installation

First, in order to include it to your Gradle project, simply add the following dependency, or use any other form provided in Maven Central Repository:

implementation 'com.github.xmlet:htmlflow:4.4'

You can also download the artifact directly from Maven Central Repository

Core Concepts

HtmlFlow builders:

  • element builders (such as body(), div(), p(), etc) return the created element
  • text() and raw() return the parent element (e.g. .h1().text("...") returns the H1 parent). .text()escapes HTML, while raw() doesn't.
  • attribute builders - attr<attribute name>() - return their parent (e.g. .img().attrSrc("...") returns the Img).
  • __() returns the parent element and emits the end tag of an element.

HtmlFlow provides both an eager and a lazy approach for building HTML. This allows the Appendable to be provided either beforehand or later when the view is rendered. The doc() and view() factory methods follow each of these approaches:

  • eager: HtmlFlow.doc(System.out).html().body().div().table()...
  • lazy: var view = HtmlFlow.view(page -> page.html().body().div().table()...)

An HtmlView is more performant than an HtmlDoc when we need to bind the same template with different data models. In this scenario, static HTML blocks are resolved only once, on HtmlView instantiation.

Given an HtmlView instance, e.g. view, we can render the HTML using one of the following approaches:

  • String html = view.render(tracks)
  • view.setOut(System.out).write(tracks)

The setOut() method accepts any kind of Appendable object.

In the following examples, we showcase using only HtmlDoc. You can find the equivalent HtmlView definition on htmlflow.org

Data Binding

Web templates in HtmlFlow are defined using functions (or methods in Java). The model (or context object) may be passed as arguments to such functions. Next, we have an example of a dynamic web page binding to a Track object.

void trackDoc(Appendable out, Track track) {
  HtmlFlow.doc(out)
  .html()
    .body()
      .ul()
        .li()
          .of((li) -> li
            .text(format("Artist: %s", track.getArtist())))
          .__() // li
        .li()
          .of((li) -> li
            .text(format("Track: %s", track.getName())))
        .__() // li
      .__() // ul
    .__() // body
  .__(); // html
}
...
trackDoc(System.out, new Track("David Bowie", "Space Oddity"));

trackView equivalent definition to trackDoc.

The of() and dynamic() builders in HtmlDoc and HtmlView, respectively, are utilized to chain Java code in the definition of web templates:

  • of(Consumer<E> cons) returns the same element E, where E is the parent HTML element.
  • dynamic(BiConsumer<E, M> cons) - similar to .of() but the consumer receives an additional argument M (model).

If/else

Regarding the previous template of trackDoc or trackView, consider, for example, that you would like to display the year of the artist's death for cases where the artist has already passed away. Considering that Track has a property diedDate of type LocalDate, we can interleave the following HtmlFlow snippet within the ul to achieve this purpose:

void trackDoc(Appendable out, Track track) {
  ...
    .ul()
      ...
      .of(ul -> {
        if(track.getDiedDate() != null)
          ul.li().text(format("Died in %d", track.getDiedDate().getYear())).__();
      })
      ...
}

trackView equivalent definition to trackDoc.

Loops

You can utilize any Java loop statement in your web template definition. Next, we present an example that takes advantage of the forEach loop method of Iterable:

void playlistDoc(Appendable out, List<Track> tracks) {
  HtmlFlow.doc(out)
    .html()
      .body()
        .table()
          .tr()
            .th().text("Artist").__()
            .th().text("Track").__()
          .__() // tr
          .of(table -> tracks.forEach( trk ->
            table
              .tr()
                .td().text(trk.getArtist()).__()
                .td().text(trk.getName()).__()
              .__() // tr
          ))
        .__() // table
      .__() // body
    .__(); // html
}

playlistView equivalent definition to playlistDoc.

Binding to Asynchronous data models

To ensure well-formed HTML, HtmlFlow needs to observe the completion of asynchronous models. Otherwise, text or HTML elements following an asynchronous model binding may be emitted before the HTML resulting from the asynchronous model.

To bind an asynchronous model, one should use the builder .await(parent, model, onCompletion) -> ...) where the onCompletion callback signals to HtmlFlow that it can proceed to the next continuation.

Next we present the asynchronous version of the playlist web template. Instead of a List<Track> we are binding to a Flux, which is a Reactive Streams Publisher with reactive operators that emits 0 to N elements.

HtmlViewAsync<Flux<Track>> playlistView = HtmlFlow.viewAsync(view -> view
  .html()
    .body()
      .table()
        .tr()
          .th().text("Artist").__()
          .th().text("Track").__()
        .__() // tr
        .<Flux<Track>>await((table, tracks, onCompletion) -> tracks
          .doOnComplete(onCompletion::finish)
          .doOnNext( trk ->
            table
              .tr()
                .td().text(trk.getArtist()).__()
                .td().text(trk.getName()).__()
              .__()
        ))
      .__() // table
    .__() // body
  .__() // html
);

HtmlFlow await feature works regardless the type of asynchronous model and can be used with any kind of asynchronous API.

References

License

MIT

About

HtmlFlow was created by Miguel Gamboa (aka fmcarvalho), an assistant professor of Computer Science and Engineering of ISEL, Polytechnic Institute of Lisbon.

htmlflow's People

Contributors

benzen avatar dependabot[bot] avatar fmcarvalho avatar kenji-getpowered avatar lcduarte avatar raidline 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

htmlflow's Issues

Add an Id or Class to elements

Hi

A great possibility would be to have the possibility to set a Css class property and th Css ad property on elements.

A possible solution would be

The HtmlWriterComposite could have a get/setCssId and get/setCssClass

So all elements could add them at doWriteBefore for instance

@OverRide
public void doWriteBefore(PrintStream out, int depth) {
out.println("<div" + this.getCssId + " "+this.getCssClass()" >");
tabs(depth+1);
}

Well I did not think it thouroughly but, in my opinion, it would be really useful

Fix Readme.md

Several errors. Should use try-with-resources. Remove the Task model from the first example.

Use a write method without the int indentation parameter.

Nested Table

Hi,
I think i am missing something but can you please help me understand how can i create nested table.
Something like below? I tried adding table to HtmlTd but that didn't work.

<table cellpadding="0" cellspacing="0">
        <tr class="top">
            <td colspan="2">
                <table>
                    <tr>
                        <td class="title">
                            <img src="logo.png" style="width:100%; max-width:300px;" />
                        </td>

                        <td>
                            Invoice #: 123<br />
                            Created: January 1, 2015<br />
                            Due: February 1, 2015
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
</table>

Issue while maven build

Hi following error i am getting while doing

mvn clean install

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /Users/mohsin/Documents/dev/springdev/workspace/HtmlFlow/src/test/java/htmlflow/test/views/HtmlTables.java:[98,21] cannot find symbol
symbol: method div()
location: interface org.xmlet.htmlapifaster.Element
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.226 s
[INFO] Finished at: 2019-03-16T14:55:58+05:30
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile (default-testCompile) on project htmlflow: Compilation failure
[ERROR] /Users/mohsin/Documents/dev/springdev/workspace/HtmlFlow/src/test/java/htmlflow/test/views/HtmlTables.java:[98,21] cannot find symbol
[ERROR] symbol: method div()
[ERROR] location: interface org.xmlet.htmlapifaster.Element
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

Prepare Release 2.0

If you are an HtmlFlow user then you should be interested about the release 2.0 news. HtmlFlow version 2.0 has full support for all existing HTML5 elements and attributes. Moreover all attributes are strongly typed with enumerated types which restrict accepted values. Now HtmlFlow API is constructed with the support of an automated framework xmlet based on an XSD definition of the HTML5 syntax and rules (soon HtmlFlow will be transferred to xmlet organization).

Thus we remove the package htmlflow.attributes and htmlflow.elements, which have been replaced by the types defined in org.xmlet.htmlapi library. This new approach forces HtmlFlow API to keep consistency along all methods use. You may check the HtmlFlow development branch to verify the main changes to the API (the Readme.md examples and unit tests have been updated according to the new API). Please let me know about your issues if you do not agree with some change.

So version 2.0 introduces the following core changes:

  • All fluent methods have no parameters. For example, formerly when we were specifying the text node of a paragraph or heading (such as, .p("my text") or .h2("my title")), now we have to chain an invocation to the text() method (such as, .p().text("my text") or .h2().text("my title")).

  • All fluent methods now return the created element. Whenever we need to proceed with the parent element we may chain an invocation to .º(). For example, formerly when we wrote .div().br().p()
    now we have to write .div().br().º().p(). Moreover the statement .div().br().p() not even compiles, because HTML does not allow a paragraph inside a break line element, so we will get a
    compilation error.

  • Except for .text() which does not create an element but a text node instead, the rest of fluent methods return the created element. For .text() it returns the element containing the text node (the this).

  • The new method º() returns the parent of an element. This method is strongly typed so it returns exactly an instance of T where T is the concrete class which extends Element. This is an important issue to respect the HTML structure and rules.

  • Indentation. Now every element or text node is printed in a new line. Formerly there were some exceptions, namely for text nodes which were printed in the same line of the beginning tag.
    These exceptions were removed.

  • If you do not like the HtmlFlow print approach you are free to implement your own org.xmlet.htmlapi.ElementVisitor. See the htmlflow.HtmlVisitor implementation as a guideline.

  • Removed default implementation of method write() in interface HtmlWriter<T> .

To all HtmlFlow users @andych008 @dgautier @bignsyd @kenji-getpowered @eorahil @tommywu23
@srekapalli @sndp2510 @vbrknu @alacui @mavarazy @ItisSoham @tenebrius @ohtejera @CapDuan @DNagorny @maksimu @extremely-idle @christhompson121 @luisf11 @AlburIvan @mertserezli @YuriiChukhrai @beatngu13 @prinal10 @xpbtmusic @devaaron @lcduarte

Dynamic html to string wrong

Wrong use of DynamicView! You should provide a model parameter or use a static view instead! method render(T mode), How to define T mode???

Examples

Hey! I'd love to use your library, but I'm not sure how to do some things.
What's the procedure for adding a table?
I noticed that none of your test cases are in the repo. If you've written them, maybe they'd make a good reference for those who want to use it?

Web Site htmlflow

  1. Fix first example to be consistent with Readme.md
  2. Put references consistent with Readme.md
  3. Add link to 2nd part of DZone Article
  4. Fiz installation version of HtmlFlow.
  5. Update Change logs.
  6. Add feed ATOM RSS.

HtmlView-Header.txt can't be loaded

Hi @fmcarvalho,

I really like your library for easily creating html docs. In my case I use it for a mail parsing component. Now the problem with version 1.1 (but not 1.0):

My mail parsing component is a maven dependency like yours. I have an application "main" which only uses my mail parsing component as dependency. I checked the resulting "main.war" and there was my mail parser jar as well as your htmlflow jar.

When I try to create a new Instance of my mail parser, which also invokes to create a instance of HtmlView<?>, an UncheckedIOException is thrown that it couldn't find HtmlView-Header.txt. Just for clarification the structure how everything is deployed within main.war:
--META-INF
--static
--WEB-INF
----classes
----lib
------mailParser1.0.jar
------htmlflow1.1.jar

I am still trying to debug it but for the moment I can tell that ClassLoader.getSystemResource("templates/HtmlView-Header.txt") (related to #16) is not looking at the right point to find the "HtmlView-Header.txt" because of the setup of your project being within another dependency.

Note:
The tests within the mail parser dependency can successfully create a mail parser instance.

Request for help - Several arrayslist to table

My goal is to produce a table with the layout kinda like this:
double table

The code used for producing this output is:

public static void saveWeek(String weekNum, ArrayList<SkemaLektion> man, ArrayList<SkemaLektion> tir) {
        HtmlView<Iterable<SkemaLektion>> taskView = new HtmlView<Iterable<SkemaLektion>>();
        taskView
                .head()
                .title(weekNum)
        HtmlTable<Iterable<SkemaLektion>> table = taskView
                .body().classAttr()
                .heading(1, weekNum)
                .div()
                .table().classAttr(table);


        HtmlTr<Iterable<SkemaLektion>> headerRow = table.tr();
        headerRow.th().addAttr("rowspan", "9999").text("MANDAG");
        headerRow.th().text("Tid");
        headerRow.th().text("Enhed");
        headerRow.th().text("Fag");
        headerRow.th().text("Lek Nr");
        headerRow.th().text("Aktivitet");
        headerRow.th().text("Leder");
        headerRow.th().text("Påklædning");
        headerRow.th().text("Sted");
        table.trFromIterable(
                skemaLektion -> skemaLektion.getLektid().getText(),
                skemaLektion -> skemaLektion.getEnhed().getSelectionModel().getSelectedItem(),
                skemaLektion -> skemaLektion.getFag().getSelectionModel().getSelectedItem().toString(),
                skemaLektion -> skemaLektion.getLekNummer().getSelectionModel().getSelectedItem().toString(),
                skemaLektion -> skemaLektion.getAktivitet().getText(),
                skemaLektion -> skemaLektion.getAnsvar().getText(),
                skemaLektion -> skemaLektion.getPaaklaedning().getText(),
                (SkemaLektion skemaLektion2) -> skemaLektion2.getLokation().getText());
        
        try (PrintStream out = new PrintStream(new FileOutputStream(weekNum + ".html"))) {
            taskView.setPrintStream(out).write(man);
            taskView.setPrintStream(out).write(tir);
            Desktop.getDesktop().browse(URI.create(weekNum + ".html"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

My question is how do so that only the table content is added again and not the whole document and the second "Mandag" is changed to "Tirsdag"? (Monday and tuesday.

Kind regards

Safe output?

It seems that even when I use the Dynamic View:

  .dynamic(el -> el.text(model.title))

The output is not safe for viewing?
My example is that a user has entered some XSS in the title, such as: <script>alert('1')</script>.

Am I using the library wrong?

RFP Flowifier – HTML to HtmlFlow translator

In order to setup an HtmlFlow template it would be useful to have a translator tool – Flowifier – that is able to convert an HTML document into the equivalent template definition with HtmlFlow.

This RFP aims to discuss implementation approaches and requirements for Flowifier.

Some of the topics under discussion (not limited):

  • HTML parsing strategy
  • Nodes visiting approach
  • Execution model
  • Parent repository

Partial feels upside down

I'm trying to figure out the correct way to use this htmlflow as a replacement for thymleaf.

The partial idea is already adressed here, but it seams to me that the partial are upside down.
Maybe that's just my perception that is out of wack at least in the documentation.

The way I intend to use partial is much closer to what was done with SelectField in the petClinic.
But there it's in a folder called fragment.

What is the distinction you are doing between fragment and partial ? Would be welcome a PR on the documentation of partial ?

Nesting dynamic bloc

I'm trying to migrate a use made using thymleaf to a view using html flow.
In a table of user I discovered that we can't nest dynamic bloc.

.tbody()
  .dynamic(body -> {
    users.stream().forEach((user) -> {
      body
        .tr()
          .td()
                // should switch on presence of user.getImage() == null 
                // .img().attrClass("ui image avatar").attrSrc(String.format("data:image/png;base64,%s", user.getImageAsBase64())).__()

          .__()
        .__();
    });
  })

The error message is really clear

You are already in a dynamic block! Do not use dynamic() chained inside another dynamic!

Thanks for that.

I assume that there is a technical reason for this limitation. I don't question them, rather I'd like some guidance on how this kind of stuff should be address by a user of htmlflow.

Regards

Prepare for release 3.0

Release 3.0 will introduce deep changes on end use API and also significantly improvement on performance. Check major major changes on this branch: https://github.com/xmlet/HtmlFlow/tree/readme-for-release-3

  • Improved performance. HtmlFlow is on this date the most performant template engine and Java DSL for HTML.

  • Replaced the method º() by __().

  • New static factory methods view() of the new classes DynamicHtml and StaticHtml

  • Removed the static factory methods html(), head() and div() from HtmlView.

  • Html code is emitted on the fly when the methods of HtmlApi (e.g. html(), div(), h1(), etc)
    are called.

  • Now HtmlView is just a container of a template function and an ElementVisitor, which establishes
    the HTML output format.

  • All emitted HTML is cached.

  • Data binding requires the use of new method dynamic() to avoid caching. Otherwise, the context objects are ignored on further renders.

  • New method of() to enable the use of other methods in the fluent chain.

  • New addPartial() to enable the reuse of same HTML template function with different HTML fragments.

  • Removed the method binder(). The role of this method is replaced by the concept of template function which receives the context object U that is captured and used whenever is needed, such as in dynamic().

@andych008
@dgautier
@bignsyd
@kenji-getpowered
@tommywu23
@srekapalli
@sndp2510
@vbrknu
@alacui
@mavarazy
@tenebrius
@ohtejera
@CapDuan
@DNagorny
@maksimu
@ross-moug
@christhompson121
@luisf11
@AlburIvan
@mertserezli
@YuriiChukhrai
@beatngu13
@prinal10
@Buchx
@thadumi
@davidalexsoares
@jedcua
@navneet-kumar
@lcduarte

Petclinic (Java pet store) sample application with HtmlFlow

Hi @fmcarvalho

thank you for you template engine. Very interesting approach.

Can you provide a standard example Petclinic implemented with "login-registration" (spring security) ? PETCLINIC

The developer need to know how to implement the standard functionality with HtmlFlow.

Read more about petclinic or pet store app:

  1. spring implementation https://projects.spring.io/spring-petclinic/
  2. j2ee implementation https://antoniogoncalves.org/2012/06/25/yet-another-petstore/
    https://www.oracle.com/technetwork/java/petstore1-3-1-02-139690.html

Regards

Internationalization with locale

I am creating a dynamic html with dynamic content to send it by email.
I am using spring boot with Java 8 and I would like to add localize text from message.properties depending the language of the customer.
Is there a way to achieve that when I render dynamically the html?

License

hi

what is the license?

gpl, lgpl, bsd?

thank you

Turn setPrintStream optional

Support other kinds of outputs and make setPrintStream optional, whit a default output.

Today HtmlWriterComposite and HtmlSingleElement are compromised with the setPrintStream.

Also, evaluate support to javax.swing.text.html.HTMLDocument.

Could Taskview return always the same HtmlBody instead of creating?

use case:

    taskView.body()
    .div()
      .classAttr(divClass)
      .idAttr(divId)
      .addAttr("toto", "tutu").form("/action.do");

     taskView.body()
      .div()
      .idAttr("id2")
      .addAttr("toto", "tutu").form("/action2.do");

Proposition

public class HtmlView<T> extends HtmlWriterComposite<T, HtmlView<T>>{

     public HtmlHead<T> head(){return addChild(new HtmlHead<T>());}

    private HtmlBody body = null;
    public HtmlBody<T> body() {
        if (body == null) {
            body = addChild(new HtmlBody<T>());

        }
        return body;
    }

Story for generating inline css or inline js

Does this project plan to produce inline js or inline css.

Using thymleaf it happens from time to time that I need to produce in my inline js base on some variable extracted from the model.

Enhance DynamicHtml view() factory with a pre building process

HtmlTemplate instances can be processed before-hand with a mock model object (through a dynamic proxy) that intercepts all calls.

Thus, all static parts can be processed and stored on call to view(). Any, call to the model out of a dynamic scope it is intercepted by the mock object and throws an exception alerting for the illegal use.

This would avoid control of the first visit in HtmlVisitorCache.

Misunderstanding of how to use printStream to stream html

So I had everything working using render(), when I ran into issues and decided to change to output into a file when I realized just adding the printStream and calling write at the end didn't work.

Debugging the code I realized all the builder code is executing everything eagerly building some StringBuilder as you add html tags, and all the buffer gets discarded as soon as I call setPrintStream.

So the only solution I found was to set the printStream right before I start building the Html:

var view = (HtmlView<?>) StaticHtml.view().setPrintStream(ps); var html = view.html();

But not sure the method returns a less specific type, thats why I had to add the cast, but in the end it works.

Is this how it is supposed to work? because from the documentation I assumed you could decide how to render it at the end, and so I assumed everything would run lazily like Java streams.

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.