GithubHelp home page GithubHelp logo

odoo / owl Goto Github PK

View Code? Open in Web Editor NEW
1.1K 67.0 333.0 20.44 MB

OWL: A web framework for structured, dynamic and maintainable applications

Home Page: https://odoo.github.io/owl/

License: Other

JavaScript 11.59% TypeScript 87.75% Python 0.11% HTML 0.05% CSS 0.49%

owl's Introduction

๐Ÿฆ‰ Owl Framework ๐Ÿฆ‰

License: LGPL v3 npm version Downloads

Class based components with hooks, reactive state and concurrent mode

Try it online! you can experiment with the Owl framework in an online playground.

Project Overview

The Odoo Web Library (Owl) is a smallish (~<20kb gzipped) UI framework built by Odoo for its products. Owl is a modern framework, written in Typescript, taking the best ideas from React and Vue in a simple and consistent way. Owl's main features are:

  • a declarative component system,
  • a fine grained reactivity system similar to Vue,
  • hooks
  • fragments
  • asynchronous rendering

Owl components are defined with ES6 classes and xml templates, uses an underlying virtual DOM, integrates beautifully with hooks, and the rendering is asynchronous.

Quick links:

Example

Here is a short example to illustrate interactive components:

const { Component, useState, mount, xml } = owl;

class Counter extends Component {
  static template = xml`
    <button t-on-click="() => state.value = state.value + props.increment">
      Click Me! [<t t-esc="state.value"/>]
    </button>`;

  state = useState({ value: 0 });
}

class Root extends Component {
  static template = xml`
    <span>Hello Owl</span>
    <Counter increment="2"/>`;

  static components = { Counter };
}

mount(Root, document.body);

Note that the counter component is made reactive with the useState hook. Also, all examples here uses the xml helper to define inline templates. But this is not mandatory, many applications will load templates separately.

More interesting examples can be found on the playground application.

Documentation

Learning Owl

Are you new to Owl? This is the place to start!

Reference

Other Topics

Installing Owl

Owl is available on npm and can be installed with the following command:

npm install @odoo/owl

If you want to use a simple <script> tag, the last release can be downloaded here:

Installing Owl devtools

The Owl devtools browser extension is also available in the release: Unzip the owl-devtools.zip file and follow the instructions depending on your browser:

Chrome

Go to your chrome extensions admin panel, activate developer mode and click on Load unpacked. Select the devtools-chrome folder and that's it, your extension is active! There is a convenient refresh button on the extension card (still on the same admin page) to update your code. Do note that if you got some problems, you may need to completly remove and reload the extension to completly refresh the extension.

Firefox

Go to the address about:debugging#/runtime/this-firefox and click on Load temporary Add-on.... Select any file in the devtools-firefox folder and that's it, your extension is active! Here, you can use the reload button to refresh the extension.

Note that you may have to open another window or reload your tab to see the extension working. Also note that the extension will only be active on pages that have a sufficient version of owl.

owl's People

Contributors

aab-odoo avatar alexkuhn avatar atovange avatar bastienfafchamps avatar brboi avatar caburj avatar cammarosano avatar dependabot[bot] avatar devoidfury avatar fdardenne avatar francoisge avatar ged-odoo avatar glovebx avatar jpp-odoo avatar juliusc2066 avatar kebeclibre avatar lucaslefevre avatar mcm-odoo avatar myearn4 avatar polymorphe57 avatar poma-odoo avatar pparidans avatar rfr-odoo avatar sdegueldre avatar seb-odoo avatar simongenin avatar tsm-odoo avatar vincentschippefilt avatar vva-odoo avatar xmo-odoo 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  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

owl's Issues

Wrong compiled template with t-set, t-foreach and t-if

Here's an app that displays todos and group them by date:

JS

const { Component, QWeb } = owl.core;

class App extends Component {
  constructor() {
    super(...arguments);
    this.template = "app";
  }
  
  get todos() {
      return [
          { content: 'todo1', date: '10-01-2019' },
          { content: 'todo2', date: '11-01-2019' },
          { content: 'todo3', date: '12-01-2019' }
      ];
  }
  
  dateDay(todo) {
      if (todo.date.substring(0,2) === '12') {
          return "Today";
      } else if (todo.date.substring(0, 2) === '11') {
          return "Yesterday";
      }
      return todo.date;
  }
  
}

const qweb = new QWeb(TEMPLATES);
const app = new App({ qweb });
app.mount(document.body);

XML

<templates>
  <div t-name="app">
      <t t-set="current_date" t-value="0"/>
      <t t-foreach="todos" t-as="todo">
          <t t-set="todo_date" t-value="dateDay(todo)"/>
          <div t-if="current_date !== todo_date" class="o_date_separator">
              <span><t t-esc="todo_date"/></span>
              <t t-set="current_date" t-value="todo_date"/>
          </div>
      </t>
  </div>
</templates>

It produces the following template and crashes because of it:

(function anonymous(context,extra
) {
    context = Object.create(context);
    var h = this.utils.h;
    var c1 = [], p1 = {key:1};
    var vn1 = h('div', p1, c1);
    var _2 = context['todos'];
    if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
    if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
    var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
    var _4 = _2 instanceof Array ? _2 : Object.values(_2);
    for (let i = 0; i < _3.length; i++) {
        context.todo_first = i === 0;
        context.todo_last = i === _3.length - 1;
        context.todo_parity = i % 2 === 0 ? 'even' : 'odd';
        context.todo_index = i;
        context.todo = _3[i];
        context.todo_value = _4[i];
        if (0 !== dateDay(todo)) {
            var _5 = 'o_date_separator';
            var c6 = [], p6 = {key:6,attrs:{class: _5}};
            var vn6 = h('div', p6, c6);
            c1.push(vn6);
            var c7 = [], p7 = {key:7};
            var vn7 = h('span', p7, c7);
            c6.push(vn7);
            var e8 = context['dateDay'](context['todo']);
            if (e8 || e8 === 0) {
                c7.push({text: e8});
            }
        }
        }
    return vn1;
})

Looks like the compiled template has t-set inside t-if, although the uncompiled template does the opposite.
The template above works correctly with non-owl qweb, and dateDay() may compute momentjs objects, which are heavy thus would be nice to use t-set in the template.

owl performance decrease linearly when adding widgets

With the benchmarks, if we add 10k widgets a few times (without refreshing), we get:

  • vue: 2711ms, 2580ms, 2680ms, 3313ms
  • react: 1891ms, 2190ms, 2162ms, 2530ms
  • owl: 2833ms, 4220ms, 6072ms, 7890ms

it looks like adding 10k widgets is an operation that can be done in a roughly constant time. Maybe snabbdom has an issue.

register t-ref after willPatch with t-widget

JS

const {Component, QWeb} = owl;

class Item extends Component {
    template = "item";
}

class App extends Component {
  template = "app";
  widgets = { Item };

  constructor(...args) {
    super(...args);
    this.state = {
        list: [],
        next: 1,
    };
  }
  
  willPatch() {
      console.log('willPatch:', this.state.list.length);
      console.log('willPatch:', Object.keys(this.refs).length);
  }
  
  patched() {
      console.log('patched:', this.state.list.length);
      console.log('patched:', Object.keys(this.refs).length);
  }
  
  add() {
    this.state.list.push(this.state.next);
    this.state.next++;
  }
}

const qweb = new QWeb(TEMPLATES);
const app = new App({qweb});

app.mount(document.body);

XML

<templates>
  <li t-name="item">
    <t t-esc="props.item"/>
  </li>

  <div t-name="app" class="hello">
    <ul>
      <!--<li t-foreach="state.list" t-as="item" t-key="item" t-ref="item"><t t-esc="item"/></li>-->
      <t t-foreach="state.list" t-as="item" t-widget="Item" t-props="{ item }" t-key="item" t-ref="item"/>
    </ul>
    <button t-on-click="add">Add</button>
  </div>
</templates>

Click on button "Add".

Expected

willPatch: 1
willPatch: 0
patched: 1
patched: 1

Result

willPatch: 1
willPatch: 1
patched: 1
patched: 1

The version without t-widget works fine:

  <div t-name="app">
    <ul>
      <li t-foreach="state.list" t-as="item" t-key="item" t-ref="item"><t t-esc="item"/></li>
      <!--<t t-foreach="state.list" t-as="item" t-widget="Item" t-props="{ item }" t-key="item" t-ref="item"/>-->
    </ul>
    <button t-on-click="add">Add</button>
  </div>

Motivation

In a thread of messages, on re-render, willPatch() can check whether the last message is visible, and auto-scroll to it if that is the case in patched().
We can use this.el.getElementByClassName('o_message') as a workaround, but I feel like using refs should be the recommended way to do it.

At least, there are inconsistencies with and without t-widget.

t-att-ref

Would be nice to define t-ref dynamically, in particular when using t-foreach with t-widget.

Here's an example which would display "Loading..." until a thread is loaded with messages, and when so it would scroll to the last message in the thread:

JS

const { Component, QWeb } = owl.core;
const { connect, Store } = owl.extras;

class Message extends Component {
   template = "Message";

   get message() {
       return this.props.message;
   }

   scrollToVisibleBottom() {
      this.el.scrollIntoView({
         behavior: "smooth",
         block: "end",
         inline: "nearest",
      });
   }
}

class Thread extends Component {
   template = "Thread";
   widgets = { Message };

   get messages() {
      return this.thread.messages;
   }

   get thread() {
      return this.props.thread;
   }

   componentDidUpdate() {
      if (this.thread.isLoaded) {
         // scroll to last message
         const lastMessageID = Math.max(...this.messages.map(msg => msg.id));
         this.refs[`message_${lastMessageID}`].scrollToVisibleBottom();
      }
   }
   
   _onLoad() {
       this.env.store.commit('load');
   }
}

const mutations = {
   load(state) {
      Object.assign(state.thread, {
         isLoaded: true,
         messages: [{ id: 1, content: 'hello' }, { id: 2, content: 'world' }],
      });
   }
};

const state = {
   thread: {
      isLoaded: false,
      messages: [],
   },
};

const store = new Store({ state, mutations });
const ConnectedThread = connect(s => s)(Thread);
const env = {
   qweb: new QWeb(TEMPLATES),
   store,
};
const thread = new ConnectedThread(env);
thread.mount(document.body);
// click on "Load" button to load messages

XML

<templates>
   <div t-name="Thread">
      <t t-if="!thread.isLoaded">
         <span>Loading...</span>
         <button t-on-click="_onLoad">Load</button>
      </t>
      <t t-else="">
         <t t-foreach="messages" t-as="message" t-widget="Message" t-props="{message}" t-att-ref="'message_'+message.id"/>
      </t>
   </div>

   <div t-name="Message">
      <t t-esc="message.content"/>
   </div>
</templates>

remove examples

  • todoapp could be moved to playground
  • benchmarks should be top level

should rationalize exported values by owl

Currently, owl exports this:

export const core = {
  QWeb,
  EventBus,
  Component,
  PureComponent,
  utils
};

export const extras = {
  Store,
  connect,
  Registry
};

Maybe we should simplify by doing this instead:

export default {
  QWeb,
  EventBus,
  Component,
  PureComponent,
  utils,
  Store,
  connect,
  Registry
};

This means that it is slightly easier to use some part of owl:

class SomeComponent extends owl.Component {
    ...
}

widgets should atomically update themselves

With this code

class Slow extends owl.core.Component {
    template = "Slow"
    willStart() {
        const delay = this.props.delay;
        return new Promise(function(resolve, reject) {
          setTimeout(function() {
            resolve();
          }, delay);
        });
    }
    async render() {
        const delay = this.props.delay;
       await new Promise(function(resolve, reject) {
          setTimeout(function() {
            resolve();
          }, delay);});
        return super.render()
    }
}

class B extends owl.core.Component {
    template = "B";
    widgets = { Slow };
}

class C extends owl.core.Component {
    template = "C";
    widgets = { Slow };
}

class A extends owl.core.Component {
    template = "A";
    widgets = {B, C};
    state={b: 1000, c: 2000};
    
    updateB() {
        this.updateState({b: this.state.b + 1200});
    }
    updateC() {
        this.updateState({c: this.state.c + 1200});
    }
    updateBC() {
        this.updateState({b: this.state.b + 1200, c: this.state.c + 1200});
    }
}

const env = {
  qweb: new owl.core.QWeb(TEMPLATES)
};

const app = new A(env);
app.mount(document.body);
<templates>
  <div t-name="Slow">Slow: <t t-esc="props.delay"/></div>
  <div t-name="B">Widget B: <t t-esc="props.value"/><t t-widget="Slow" t-props="{delay: props.value}"/></div>
  <div t-name="C">Widget C: <t t-esc="props.value"/><t t-widget="Slow" t-props="{delay: props.value}"/></div>
  <div t-name="A">
      <t t-widget="B" t-props="{value: state.b}"/>
      <div><button t-on-click="updateB">Update B</button></div>
      <div><button t-on-click="updateC">Update C</button></div>
      <div><button t-on-click="updateBC">Update BC</button></div>
      <t t-widget="C" t-props="{value: state.c}"/>
  </div>
</templates>

we observe a (maybe) problematic situation: updating both B and C widgets will be done in two steps. Need to think if that is ok

add warning when some a11y issue can be detected

Motivation

Some accessibility issues may be discovered by Owl. We are not good at accessibility, and it is difficult to test, but it would be simple to give some warning whenever we have a clear case of a missing accessibility attribute.

Spec

In QWeb, whenever we compile a generic dom node, and only if we are in dev mode, call a _validateAccessibility method, which would do the following:

  • if the node is an image and has no alt attribute, generate a warning (unless it has a aria-labelledby attribute)

t-set with t-esc should evaluate only once an expression

Notice that:

    <t t-set="v" t-value="value + 'str'"/>
    <t t-esc="v"/>
    <t t-esc="v"/>

is compiled into:

    var e4 = context['value'] + 'str';
    if (e4 || e4 === 0) {
        c2.push({text: e4});
    }
    var e5 = context['value'] + 'str';
    if (e5 || e5 === 0) {
        c2.push({text: e5});
    }

Notice that the t-value expression is evaluated twice. This is most likely not what most people would expect when they use t-set. We should make sure it only evaluates the expression once.

qweb: improve adhoc expression parser

Problem

a t t-widget with props such as: t-props="(display: ['js','xml','css']}" will crash. Other simple props will crash as well: t-props="{a: {b: 1}}"

The reason is that we use a adhoc expression parser, which tries to identifies keywords/strings/variables and replace variables x with a lookup in the context: context['x']. This "expression parser" is quite naive.

Spec

Write a very minimalist js tokenizer/parser/evaluator. We can only allow a subset of expressions, and have a very simple grammar. For example, we do not need to be able to handle class, function () {} or lambda functions.

However, we want to allow this:

  • primitive values: 1, true, new Date(), ...
  • arithmetic operations: 1 + 3
  • boolean operations
  • objects, arrays, and expressions such as this: {a: 1}, {a: [b, {c: [d, e]}]}, ...
  • ...

Its job is basically to understand a js expression as well as possible, and:

  • replace variables x by context['x']
  • replace a few keywords: and, or, gt, ... to help with writing expression in xml

owl should output es5 valid code

The code is written in typescript. But the exported file should (?) be compiled to ES5 code. Also, I think that qweb should be adapted to output ES5 compatible code. I think that it will basically only need to change let into var

infinite loop in mapstate to props

TODO app with following mapStateToProps:

function mapStateToProps(state) {
  return { todos: state.todos.sort((t1, t2) => t1.title > t2.title ? -1 : 1) };
}

add an item => infinite loop

maybe prevent mutating state inside mapStateToProps?

t-else with t-widget

const {Component, QWeb} = owl.core;

class Child extends Component {
    constructor() {
        super(...arguments);
        this.template = "child"
    }
}

class App extends Component {
  constructor() {
    super(...arguments);
    this.template = "app";
    this.widgets = { Child };
  }
}

const qweb = new QWeb(TEMPLATES);
const app = new App({qweb});
app.mount(document.body);
<templates>
  <div t-name="app">
    <div t-if="0"/>
    <t t-else="" t-widget="Child" t-props="{ test: 0 }"/>
  </div>
  
  <div t-name="child">
      <t t-esc="props.test"/>
  </div>
</templates>

Crashes with the following error:

Invalid generated code while compiling template 'app': Unexpected token )

improve error message

when a template has a t-on with a non existing method, we get this error message:

Uncaught (in promise) TypeError: Cannot read property 'bind' of undefined

The error should at least mention the template, and the method name

do not call lifecycle hooks if not implemented

We can optimise the code compiled by qweb/t-widget to be smaller and faster in some frequent cases: if a given widget does not implement a specific hook, then it does not need to be set in the output of the t-widget directive.

For example, the willPatch hook is likely to be quite rarely used.

The main problem with this is to determine at compile-time what hooks are implemented. This is currently impossible, given that the actual widget is not given to the template compiler. However, we can do that. I suppose that the compiled template can depend on the widget class (but this raises the question: what if two widgets share the same template? should we authorize that? Or maybe we can have a special keyword to enable/disable widget-dependant template compilation)

t-set should reuse variable if possible

const {Component, QWeb} = owl.core;

class HelloWorld extends Component {

  constructor() {
    super(...arguments);
    this.list = ['a', 'b']
    this.template = "demo.hello";
  }
}

const qweb = new QWeb(TEMPLATES);
const hello = new HelloWorld({qweb}, { name: "World" });
hello.mount(document.body);
<templates>
  <div t-name="demo.hello" t-debug="">
      <t t-set="v" t-value="1"/>
    <div t-foreach="list" t-as="elem">
        <t t-esc="elem"/>
        <span>v<t t-esc="v"/></span>
        <t t-set="v" t-value="elem"/>
    </div>

  </div>
</templates>

Result: av1bv1
Expected: avbva

should t-key be dynamic?

In some way, a key is not really an attribute. It is just a way to give an identity to sub entities.

Should we, for symetry reasons, do as we did for t-ref and make the directive dynamic? always using t-key instead of t-att-key.

need propsupdated hook

Use case: some widget that needs to setup something in its mounted hook depending on props (for example, playground app tabbededitor widget with ace editor).

In that case, we want to update the ace editor value when props are changed. However, if we use the updateProps method as a hook, we need to remember to return a value, otherwise, we will get a cryptic error.

move examples code to gh-pages branch

The examples code should be moved to gh-pages.

Reason:

  • can be hosted by gh-pages (with links from playground to various examples)
  • simpler main master branch

cleanup lifecycle method names

We currently have:

  • willStart
  • mounted
  • beforePatch (not yet actually)
  • componentDidUpdate
  • willUnmount
  • destroyed

What about this instead:

  • beforeInitialRender (???)
  • mounted
  • beforePatch
  • afterUpdate
  • beforeUnmount
  • destroyed

Or maybe this:

  • willStart
  • mounted
  • willPatch
  • updated
  • willUnmount
  • destroyed

store: should deep-observe

Currently, the store only tracks changes to a given object/array. However, it is convenient, in many cases, to be able to connect a component to the store with a simple mapStateToProps which observer for example a list of objects, and be able to rerender the full list if a key inside an object changed.

To enable this in a 'performant' way is possible, but requires changes to the store: each object/array should keep track of more metadata.

It currently only know about the current rev number, but it would need more info: the combined rev number of its children. To be able to do that, it will therefore need a reference to is 'parent' data element.

input type=checkbox incorrectly updated

const { Component, QWeb } = owl.core;

//------------------------------------------------------------------------------
// TodoList root widget
//------------------------------------------------------------------------------
class TodoList extends Component {
    template = "todoapp";
    state = {filter: 'all', nextId: 1, todos: []};

  addTodo(ev) {
    if (ev.keyCode === 13) {
      const title = ev.target.value;
      if (title.trim()) {
        const id = this.state.nextId++;
        const todo = { id, title, completed: false };
        const todos = this.state.todos.slice();
        todos.push(todo);
        this.updateState({todos})
      }
      ev.target.value = "";
    }
  }
  toggleTodo({id}) {
    const todo = this.state.todos.find(t => t.id === id);
    todo.completed = !todo.completed;
    this.updateState({todos: this.state.todos});
  }
  toggleFilter() {
      const newFilter = this.state.filter === 'all' ? 'active' : 'all';
      this.updateState({filter: newFilter});
  }
  get todos() {
      let todos = this.state.todos;
      if (this.state.filter === 'active') {
          todos = todos.filter(t => !t.completed)
      }
      return todos
  } 
}

//------------------------------------------------------------------------------
// App Initialization
//------------------------------------------------------------------------------
const qweb = new QWeb(TEMPLATES);
const env = {
  qweb,
};
const app = new TodoList(env);
app.mount(document.body);
<templates>
  <div t-name="todoapp">
    <input autofocus="true" placeholder="What needs to be done?" t-on-keyup="addTodo"/>
    <div>
        <span>Filter: <t t-esc="state.filter"/></span>
        <button t-on-click="toggleFilter">Toggle</button>
    </div>
    <ul>
        <li class="todo" t-foreach="todos" t-as="todo">
            <input type="checkbox" t-att-checked="todo.completed" t-on-change="toggleTodo(todo)"/>
            <span t-att-class="{completed: todo.completed}"><t t-esc="todo.id"/>. <t t-esc="todo.title"/></span>
        </li>
    </ul>
  </div>
</templates>
  1. add two todo items, A and B
  2. toggle active filter
  3. click on item A checkbox, so it is set to active, so it should disappear

Result: it actually disappear, but the item B checkbox is now checked. However, the item B internal state is correct

add t-mounted directive

Not sure if this is the right place

The motivation is that we want to be able to do something with refs after a state change, but it may not be available yet. So, we wait for a tick.

Problem: this may not exactly be what we need. We actually want to wait for the next patched event of our component

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.