Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mario-deluna/tattoo

Tattoo, A simple hyper text programming language.
https://github.com/mario-deluna/tattoo

Last synced: 3 months ago
JSON representation

Tattoo, A simple hyper text programming language.

Awesome Lists containing this project

README

        

Tattoo Language
===============

Tattoo is a simple hyper text programming / template language that renders into HTML.

[![Build Status](https://travis-ci.org/mario-deluna/Tattoo.svg)](https://travis-ci.org/mario-deluna/Tattoo)
[![Packagist](https://img.shields.io/packagist/dt/mario-deluna/tattoo.svg)]()
[![Packagist](https://img.shields.io/packagist/l/mario-deluna/tattoo.svg)]()
[![GitHub release](https://img.shields.io/github/release/mario-deluna/tattoo.svg)]()

```tattoo
h1 => 'Welcome to Tattoo :)'
```

_There are still PHP5.3 users and I don't want to discriminate these outlaws. Means Tattoo works from 5.3 to 7._

**Tattoo is not finished but in heavy development.**

## Tell me more!

A programming language written in PHP that compiles into PHP? When all you have is a hammer everything starts to look like a nail hu?

There is no real use case where you would build a stand alone Tattoo application. So making Tattoo work as a library seemed to be best way to go. Why PHP? Huge community, pretty fast and Im used to the syntax.

## Installation

Composer, whoop whoop.

```
$ composer require mario-deluna/tattoo
```

## Usage

```php
$tattoo = new Tattoo\Tattoo(array('cache' => 'path/to/my/cache/dir/'));

echo $tattoo->render('/path/to/a/tattoofile.tto', array(
'name' => $_GET['name'],
));
```

```tattoo
h1.page-title => 'Hello ' % @name
```

## The great goal

After years of developing web applications I've got kind of sick that the only thing that always stayed a mess was the HTML markup. So there was this idea stuck in my head of a templating engine that actually was a programming language.

Let me show you an example:

```html


```

This something i've seen a lot. We have got two inline statements here and honestly I dont know how to format that piece of code decently. Well this is the same piece of code in tattoo:

```tattoo
[ul #main-navigation .nav]
{
[li][a .navigation-item ~ home] => 'Home'
{
if @currentUrl == '/' {
@this.class.add('navigation-item-active')
@this.parent.class.add('active')
}
}
}
```

Tattoo considers html tags as scoped objects this allows you to write your markup in a real new dynamic way.

---

## Syntax

Let's just drive directly into the syntax.

### Nodes

Tattoo _nodes_ are basically HTML tags. So let's write the alltime classic hello world:

```tattoo
h1 => "Hello World"
```

```html

Hello World


```

You can use the first argument to add classes and set the nodes id.

```tattoo
h1 #page-title .underlined => "Hello World"
```

```html

Hello World


```

All other arguments will be used as node attributes.

```tattoo
a.btn.btn-sm, href: '/login' => 'Sign in'
```

```html
Sign In
```

#### Value less nodes

Sometimes you want to create a node without any contents.

```tattoo
[img src: 'logo.png']
```

```html

```

#### Scoped nodes

Obviously you are going to build a tree structure with tattoo. Node definitions inside `[]` allow a scope.

```tattoo
[div.image-container]
{
[img src: 'wallpaper.jpg']
}
```

```html




```

You are still able to directly assign a text value.

```tattoo
[p] => 'Hello '
{
span => 'World'
}
```

```html


Hello World


```

#### Node tree

Often you have a tree with many levels that just contain one child. Instead of creating a scope for every level you can just forward them.

```tattoo
[header][nav.navbar][ul][li.active][a href: '/'] => 'Home'
{
i.glyphicon ~ home => ''
}
```

```html



```

#### Appending classes

```tattoo
[div.row][div .col ~ md-4 ~ sm-6 ~ xs-12]
{
a .btn ~ lg ~ primary ~ block => 'Click Me'
}
```

```html


```

---

## OLD STUFF KEEP UNTIL I REWROTE EVERYTHING

```tattoo
[form #login-form, action: '/login', method: 'post']
{
p .info => "Please provide your login information."

[input ]
}
```

```tattoo
button.btn title: 'Amazing Tooltip', data: {toggle: 'tooltip', placement: 'left'} => 'Hello!'
```

```html
Hello!
```

### tag with scope

HTML:

```html

Please provide you login information.

```

### modifying scope data

```
[a .ajax-trigger, href: "/notes/save/"]
{
@this.data: {
noteId: 123,
userId: 42,
revision: 1
};

@this.text = "Save your Note"
}
```

HTML:

```html
Save your Note
```

### The are vars

```
@applicationName = "Tattoo Application"

[head] {
title => 'Welcome | ' + @applicationName
}
[body][footer] {
span .small => 'Powerd by ' + @applicationName
}
```

HTML:

```html

Welcome | Tattoo Application


Powerd by Tattoo Application

```

### Loops and tree modifications

```
@pages = {
{ title: 'Home', link: '/home/', isActive: false },
{ title: 'About', link: '/about/', isActive: true },
{ title: 'Terms', link: '/terms/', isActive: false }
}

[ul .navigation]
{
each @page in @pages
{
[li][a .navigation-item, href: @page.link]
{
span.navigation-item-title => @page.title

if @page.isActive
{
@this.parent.class.add('active')
}
}
}
}
```

HTML:

```html


```

### Extending tags

```
// the '*' tells tattoo to not automatically print the tag
extend input*: @input
{
[div .form-group]
{
@input.id = 'input-' + @input.name

label .form-label, for: @input.id => @input.placeholder

@input.class.add( 'form-control' )

if [email protected] {
@this.type = 'text'
}

render @input
}
}

[form action: '/login', method: 'post']
{
[input name: 'username', placeholder: 'your username']
[input name: 'password', placeholder: 'your password', type: 'password']
}
```

HTML:

```html


your username



your password

```

### Views

```
view page-header
{
default @title = 'Unknown'
default @subtitle = ''
default @underline = true

[div .page-title]
{
[h1]
{
print @title + ' '

if @subtitle
{
small .subtitle => @subtitle
}

if @underline
{
[hr .page-title-underline]{}
}
}
}
}

[page-header title: 'Welcome', subtitle: 'to tattoo']
```

HTML:

```html


Welcome to tattoo





```

## Notes

_Notes to myself and everyone who's bored._

A node ( `[div]`, `span => 'foo'` ) always directly represents an element. All given data of such a node object is interpreted as element attribute.
Extending (`extend`) a node allows to a callback like thingy after a the given node has been created also from the context of the node.
Preparing (`prepare`) does the same thing as extend but the callback acts before any custom data is passed.
A view does not stand in any context of a node so all given data / arguments have to be handeled by the view itself.

You should be able to store nodes in variables and modify them before printing. ( see `concept/objectvars.tto`, `concept/bootstrapmodal.tto` )

Only nodes can be printed. When you print a string or a number to parser basically creates a text node.

A view can be loaded using the dobule point `:` initiator.