Code Monkey home page Code Monkey logo

tattoo's Introduction

Tattoo Language

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

Build Status Packagist Packagist GitHub release

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

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

echo $tattoo->render('/path/to/a/tattoofile.tto', array(
	'name' => $_GET['name'],
));
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:

<ul id="main-navigation" class="nav">
	<li <?php if ($currentUrl === '/') : ?>class="active"<?php endif; ?>>
		<a 
			class="navigation-item 
				   navigation-item-home 
				   <?php if ($currentUrl === '/') : ?>
				   navigation-item-active
				   <?php endif; ?>" 
			href="/"
		>
			Home
		</a>
	</li>
</ul> 

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:

[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:

h1 => "Hello World"
<h1>Hello World</h1>

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

h1 #page-title .underlined => "Hello World"
<h1 id="page-title" class="underlined">Hello World</h1>

All other arguments will be used as node attributes.

a.btn.btn-sm, href: '/login' => 'Sign in'
<a class="btn btn-sm" href="/login">Sign In</a>

Value less nodes

Sometimes you want to create a node without any contents.

[img src: 'logo.png']
<img src="logo.png" />

Scoped nodes

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

[div.image-container]
{
	[img src: 'wallpaper.jpg']
}
<div class="image-container">
	<img src="wallpaper.jpg">
</div>

You are still able to directly assign a text value.

[p] => 'Hello '
{
	span => 'World'
}
<p>
	Hello <span>World</span>
</p>

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.

[header][nav.navbar][ul][li.active][a href: '/'] => 'Home'
{
	i.glyphicon ~ home => ''
}
<header>
	<nav class="navbar">
		<ul>
			<li class="active">
				<a href="/">Home <i class="glyphicon glyphicon-home"></i></a>
			</li>
		</ul>
	</nav>
</header>

#### Appending classes

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

## OLD STUFF KEEP UNTIL I REWROTE EVERYTHING

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

	[input ]
}
button.btn title: 'Amazing Tooltip', data: {toggle: 'tooltip', placement: 'left'} => 'Hello!'
<button class="btn" title="Amazing Tooltip" data-toggle="tooltip" data-placement="left" >Hello!</button>

tag with scope

HTML:

<form id="login-form" action="/login/">
	<span class="info">Please provide you login information.</span>
</form>

modifying scope data

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

HTML:

<a class="ajax-trigger" href="/notes/save/" data-noteId="123" data-userId="42" data-revision="1">Save your Note</a>

The are vars

@applicationName = "Tattoo Application"

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

HTML:

<head>
	<title>Welcome | Tattoo Application</title>
</head>
<body>
	<footer>
		<span class="small">Powerd by Tattoo Application</span>
	</footer>
</body>

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:

<ul class="navigation">
	<li>
		<a class="navigation-item" href="/home/">
			<span class="navigation-item-title">Home</span>
		</a>
	</li>
	<li class="active">
		<a class="navigation-item" href="/about/">
			<span class="navigation-item-title">About</span>
		</a>
	</li>
	<li>
		<a class="navigation-item" href="/terms/">
			<span class="navigation-item-title">Terms</span>
		</a>
	</li>
</ul>

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:

<form action="/login" method="post">
	<div class="form-group">
		<label for="input-username">your username</label>
		<input id="input-username" class="form-control" type="text" name="username" placeholder="your username" />
	</div>
	<div class="form-group">
		<label for="input-password">your password</label>
		<input id="input-password"class="form-control" type="password" name="password" placeholder="your password" />
	</div>
</form>

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:

<div class="page-title">
	<h1>Welcome <small>to tattoo</small></h1>
	<hr class="page-title-underline" />
</div>

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.

tattoo's People

Contributors

mario-deluna avatar

Stargazers

 avatar Rahul Thakare avatar Leonard Schütz avatar Dante avatar  avatar Stefano Azzolini avatar

Watchers

James Cloos avatar  avatar  avatar

tattoo's Issues

A suggestion for in-scope parameters

Instead of the this.data form :

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

    print "Save your Note"
}

You could write something like that :

[a .ajax-trigger, href="/notes/save/"]
{
   @noteId = 123
   @userId = 42
   @revision = 1 

   print "Save your Note"
}

more consistent with the already defined variable syntax.

What do you think?

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.