Juniper is two things:
- a php-driven website rendering engine
- a Mac OS X GUI front-end for the rendering engine
Sites built with Juniper:
This page provides a quick overview of Juniper. There is also some in-progress documentation on Juniper, which goes into more detail:
- FAQ
- the PHP rendering engine
- Juniper.app, the Mac OS X application
Templating overview
Juniper is primarily a templating engine that allows authors to encapsulate content in elements, and print those elements to the output page with a PI tag. You can pass arguments to the element as attributes on the tag. Here is an element definition:
<element name="greeting"> <renderable> Hello, <?attribute name="user-name" ?> </renderable> </element>
And here is the element in use:
<div id="user-bar"> <?element name="greeting" user-name="World" ?> </div>
Elements can contain HTML markup, PHP, and other elements. So a more useful version of the element definition might be:
<element name="greeting"> <renderable> Hello, <?php echoCurrentUser(); ?>! </renderable> </element>
Or you might define a user-bar element like this:
<element name="user-bar"> <renderable> <div id="user-bar"> <?element name="greeting" user-name="<?php echoCurrentUser(); ?>" ?> | <?element name="user-preferences" ?> | <a href="logout.php">Logout</a> </div> </renderable> </element>
A page is thus generated by a context-free grammar, the terminals of which you define over varying scopes. Scope is hierarchical, with scopes enclosing other scopes in the following order:
- current theme
- site root directory
- each directory in the path to your current directory
- per-page in the current directory
Elements can be defined for each of those scopes, and overridden in any enclosing scope.
This lets you structure your page as a series of high-level descriptions of content with internal dependencies on scope, rather than as a string of unrelated variables or snippets.
As a simple example, every page on your site might be generated from the following:
<div id="header"> <?element name="header"?> </div> <div id="sidebar"> <?element name="sidebar"?> </div> <div id="content"> <?element name="content"?> </div> <div id="footer"> <?element name="footer"?> </div>
The header, sidebar, and footer elements rarely change, so you define those in a top-level scope — probably in the theme folder. You can define "content" with some placeholder if you wish.
But content will change frequently, so you override that placeholder definition wherever you need to, based primarly on directory (eg, "mysite.com/news" versus "mysite.com/about", and so on), but also for multiple pages in a directory if desired.
You define all of these elements precisely where they belong: you define top-level scope elements in an XML file in the theme folder (or top-level directory, if you wish). You define overriding elements in XML files in the directory for which they're valid.
Localization, themes, and client-sensitivity
Elements are high-level descriptions of content, not just chunks of code. Any element can render differently depending on the the current theme, language, and browser of the viewer. You define these different renderables in the element itself, and Juniper will choose the right renderable as the situation demands.
Here is another definition for the greeting element:
<element name="greeting">
<renderable locale='en'>
<![CDATA[
Hello, <?attribute name="user-name" ?>!
]]>
</renderable>
<renderable locale='zh'>
<![CDATA[
你好, <?attribute name="user-name" ?>!
]]>
</renderable>
</element>
When a viewer's default language is set to Chinese, Juniper will render the Chinese version of the greeting element.
If you forget to define the Chinese version of your header, Juniper will attempt to pick the best renderable it can, based on other context.
Packages
Juniper has a plug-in facility, called packages. If you have applications that require certain behaviors or scripts on some pages but not on others, it's good practice to bundle them in a package. Juniper then allows you to include those packages only where needed, helping you make your site as light-weight as possible.
For example, ViennaTeng.com has a database for shows and a database for fans. A php interface simplifies the several MySQL tables and messy logic required to generate pages that use them: the tour page and the mailinglist page. That php interface is bundled as a package. Whenever I need access to the tour package on a page, I can either mark a package dependency on the page layout:
<package-dependency>vt-fans</package-dependency>
or load the package explicitly in the header of the index.php file for the page:
<?php juniper_loadPackageNamed( "vt-fans" ); ?>
How to NOT use Juniper
Juniper is built entirely with PHP, and while it has facilities to make many tasks of life as a web author easier, you can always just not use it, or not use parts of it. There is, for example, a way to define page titles through Juniper, but often I find it easier to just do those the old-fashioned way.
The GUI front-end
The GUI front-end is being developed for Mac OS X. It is not yet available in any form.
