OJ simplifies creation and manipulation of the DOM by abstracting the web into objects. It literally stands for Object Javascript, and was created under the philosophy that object encapsulation simplifies development.
To this end OJ provides several Collection Types and Form Types to help you create your website server side and manipulate it dynamically client-side. Two-way model binding is built right in if you use Backbone models (or anything with .on
.off
event binding). Collection binding is built into oj.List and oj.Table, for more complex views.
For simple tag creation OJ provides a series of tag functions, one for every tag (<div>
, <span>
, <ul>
, <table>
, <a>
, etc), to build up structure of your site and help create the collection and form types you are using. These tag functions allow you to create html structure, css, and bind jquery events simultaneously.
To truly master OJ it is encouraged to create your own custom types; List and Table are going to get you quite far, but it is much easier to think in terms of ChatWindow
or FriendList
than to manually manipulate several things at once by hand. To this end OJ provides a createType function and some inheritable base types to help you create types of your own. With these base types, model binding is quite easy.
For server side use of oj a command-line tool called oj
can be installed with node's package manager npm
. This tools purpose is to compile oj file types into html
files. It supports --watch
options to help recompile when things change. It also supports minification of html, javascript, and css for production with --minify
. Important Note: This tool is not a web server. It is a static site generator (think for github pages), but for more complex scenarios an express plugin is in the works.
As always contributions are most welcome. Feel free to send pull requests or to report bugs and feature requests as issues.
Copyright (c) 2013 Evan Moran
OJ may be freely distributed under the MIT license
The core function that implements the individual tag functions.
tagName
is the string name of a tag such as "div"
attributes
are any object that maps attribute name to value. These can be in any order after tagName. In addition attributes are smart in a couple ways:
Attributes named after jQuery mouse or keyboard events will be bound with jQuery
Style attributes can be defined with an object instead of a string
Style attribute object keys can be written in camelCase
and will translated to dashed-case
for you
class
may be abbreviated as c
. This is the only abbreviation in OJ, but the class keyword makes it helpful.
Since tags are a core part of OJ an oj.<tagName>
function has been created for every tag in HTML. This includes <div>
, <input>
, <button>
, etc.
These functions support all the fanciness of oj.tag including event binding, and style objects/camelCase.
The function that defines all css within OJ. This function ties data like a tag function but during the compile step converts the data to CSS. Supports nested selectors, camelCase rules, nested @media
queries.
This method can be called multiple times in any context a tag function could be called. Rules will be unioned together. If exactly the same rule is made the last called will be used.
These objects create smart form elements that two-way bind to parts of a model: a model-key. Why only part of a model? The reason is controls usually only affect a single key, for example the email
field, or name
field might be bound to a TextBox. While a CheckBox might bind to boolean properties wantsTheNewsletter
or isRegistered
. These keys can all be on the same UserModel
, but they affect different things.
Create a user
var UserModel = new Backbone.Model.extend();
var user = new UserModel({name:'Evan'});
Bind TextBox to the user's name
var TextBox({model: user, key:'name'});
Two way binding will happen automatically. If you change the model's name the TextBox will update. If you type in the TextBox the model will update.
The model-key binding syntax works on all Form Types the inherit from ModelKeyView.
A built-in OJ type that creates and manipulates a button element. The label
property can set the button text.
label | The text of the button (read-write). |
click(fn) | A pass through method to jQuery click event. Like the jQuery function, calling with a |
An OJ object that creates and manipulates a CheckBox control. The value
property can be set in the first argument, or through via properties
.
value | The value of the control as a boolean (read-write). True means checked. False unchecked. |
model | Bind a model to this control (read-write, inherited). |
key | Bind a specific key of a model to this control (read-write, inherited). |
A built-in OJ type that creates and manipulates a ListBox form element. Binding to models is supported through ModelKeyView, The value
is a string that changes the list box's text. The property options
must be specified so the list box knows which options to drop down.
value | The value of the control as a string (read-write). |
options | Array of options that the list box should be allowed to take on. |
model | Bind a model to this control (read-write, inherited). |
key | Bind a specific key of a model to this control (read-write, inherited). |
A built-in OJ type that creates and manipulates a tag such as a <div>
or <span>
. Generally this is done with tag functions but occasionally you will want to bind text to model data, or change it dynamically.
The value
is a string that changes the text's value.
value | The value of the tag as a string (read-write). |
model | Bind a model to this control (read-write, inherited). |
key | Bind a specific key of a model to this control (read-write, inherited). |
A built-in OJ type that creates and manipulates a TextBox form element. value
is a string that changes the text box's text. This type supports binding to models through ModelKeyView.
value | The value of the control as a string (read-write). |
model | Bind a model to this control (read-write, inherited). |
key | Bind a specific key of a model to this control (read-write, inherited). |
live | Change events should update "live" on keypress instead of waiting for blur event (default=true) |
A built-in OJ type that creates and manipulates a TextArea form element. Binding to models is supported through ModelKeyView, The value
is a string that changes the text box's text. For convenence arguments are joined with newline to allow simple multi-line input in JavaScript.
value | The value of the control as a string (read-write). |
model | Bind a model to this control (read-write, inherited). |
key | Bind a specific key of a model to this control (read-write, inherited). |
live | Boolean value indicating if change events should update "live" on keypress instead of waiting for blur event (default=true) |
All form controls support model binding through ModelKeyView.
These objects are built-in to OJ and designed to be the building blocks for more complex websites. BulletList, NumberList exist to easily create numbered lists or bulleted lists.
All collection types can be created directly through a list of arguments, or by binding to a Backbone.Collection object. If a collection object is used these objects will minimally rebuild themselves as the collection changes.
List exists to create lists of items without symbols. It uses <div>
for its tags instead of <ul>
or <li>
as this is often desired for ChatWindows
or FriendLists
-- basically this is ideal for whenever you don't really want numbers or bullets.
Table is the most complex object built into OJ. It supports everything List does, but adds another dimension. It also supports Backbone.Collection binding and is designed to support hundreds of thousands of rows. In generally OJ is built around the idea of direct DOM manipulation. Server-side it builds strings, but client side it just modifies elements directly for much improved performance.
All collection objects inherit from CollectionView and View and so definitely take a look at what those provide.
This is a simple alias for oj.List to abstract creating bulleted lists. It overrides the properties tagName to ul
, and itemTagName to li
.
This is a simple alias for oj.List to abstract creating numbered lists. It overrides the properties tagName to ul
, and itemTagName to li
.
Lists are at the heart of OJ. They are a fundamental building block that lets you bind collections of models to views. That can be as simple as NumberList and BulletList above. Or they can be more complex and render Chat Messages, Todo Lists, or whatever else you want tied to data.
Lists can be constructed by basic types:
Lists can be constructed from oj objects:
items... | Any number of values to add to the list. Constructing with these arguments has the same effect as setting the items property. |
properties | An object to bulk set properties. Keys that aren't properties are passed as attributes to the root element. |
count | Number of items (Number, read-only)) |
collection | Inherited from CollectionView.collection The collection for the view. This can be an Array or a Backbone.Collection When Backbone is used changes to the collection will cause the view to be automatically redrawn. This is done minimally so adds and removes cause fewest redraws possible. |
each(model) | Inherited from CollectionView.each A mapping function that turns a model into a view. Use this whenever you set models to specify which part of the model to render. |
tagName | Name of tag (String, write-construct) |
itemTagName | Name of tag for each item (String, read-write) |
$items | Helper to get all item jquery elements ([jQueryElement], read-only)) |
item(index, oj) | Get or set the item at When
|
$item(index) | Helper to get an item's containing element wrapped by jquery |
add(index, oj) | Add item at
|
remove(index) | Remove item at index
|
push(oj) | Add a new last item
|
pop() | Remove the last item
|
shift(oj) | Remove the first item
|
unshift(oj) | Add a new first item
|
clear(oj) | Remove all items
|
move(ix, ix2) | Move item from index list = List('a','b','c'); // ['a','b','c'] list.move(2,0); // ['c','b','a'] |
swap(ix, ix2) | Swap items at index list = List('a','b','c'); // ['a','b','c'] list.swap(1,2); // ['a','c','b'] |
Tables are another building block in OJ. Like [List] it can be used to show static data or be bound to collections of models. When the collection changes it will automatically add and remove rows to rerender the table.
Tables can be constructed with arrays of values:
Table(
[1,2,3],
[4,5,6]
);
rows... | Any number of rows to add to the table. Constructing with these arguments has the same effect as setting the items property. |
properties | A object to bulk set properties. Keys that aren't properties are passed as attributes to the root element. |
rowCount | Number of rows (Number, read-only)) |
columnCount | Number of columns (Number, read-only)) |
collection | Inherited from CollectionView.collection The collection for the view. This can be an Array or a Backbone.Collection When Backbone is used changes to the collection will cause the view to be automatically redrawn. This is done minimally so adds and removes cause fewest redraws possible. |
each(model,cell) | Inherited from CollectionView.each A mapping function that turns a model into a view. Use this whenever you set models to specify which part of the model to render. |
$trs | Accessor to all row |
$ths | Accessor to all |
$table | Accessor to |
$thead | Accessor to |
$tbody | Accessor to |
$tfoot | Accessor to |
$caption | Accessor to |
$colgroup | Accessor to |
$theadTR | Accessor to |
$tfootTR | Accessor to |
row(rx, ojArray) | Get or set the row at row index When When How this value is returned is handled by ojValue jQuery plugin. |
cell(rx, cx, oj) | Get or set the cell at row index
When When How this value is returned is handled by ojValue jQuery plugin. |
clear() | Remove everything in the table: rows, headers, footers, and captions. |
clearBody() | Remove all rows in body |
clearHeader() | Remove the header |
clearFooter() | Remove the footer |
addRow(rx, ojArr) | Add row at index
|
removeRow(rx) | Remove item at index |
pushRow(ojArr) | Add a new last row
|
popRow() | Remove the last row |
shiftRow() | Remove the first row and return it. |
unshiftRow(ojArr) | Add a new first row
|
moveRow(rx, rx2) | Move row from index |
swapRow(rx, rx2) | Swap row at index |
$tr(rx) | Accessor to |
$td(rx,cx) | Accessor to |
The point of OJ is creating types that better abstract the web. To do this OJ includes a function called oj.createType.
inherits
up to one Typethis.super.methodName()
new
is optionalThis function creates new type constructors. is a big part of why OJ types are so easy to create and use. Generally you should create new types when the component is well understood and often when it has an analogous model.
name | The string name of the type you want to create. |
options | An object that specifies See more information below: |
base: | The type to inherit. When making OJ views use a base type. |
properties: |
Define the type's properties. |
methods: |
Define the type's methods. To call super use: `TypeName.base.methodName.apply(this, arguments); |
constructor: |
Optionally define a constructor. To call super use: `TypeName.base.constructor.apply(this, arguments); |
// Create a User type
var User = oj.createType('User', {
constructor: function(props){
// Built in method to set all
// properties by name
this.set(props);
},
// Create properties
properties: {
name: null,
birthDate: null,
// The age property is calculated
// automatically from birthDate
age: {
get: function(){
var today = new Date();
var age = today.getFullYear() -
this.birthDate.getFullYear();
var month = today.getMonth() -
this.birthDate.getMonth();
var birthDateIsAfterToday = (
month == 0 && (
today.getDate() <
this.birthDate.getDate()
)
);
if (birthDateIsAfterToday)
age--;
return age;
}
}
},
// Create methods
methods: {
isEighteen: function(){
return this.age >= 18;
}
}
});
// Create the user named Batman
var user = new User({
name:'Batman',
birthDate:new Date(1974,2,1)
});
// Show that the age property works
div(user.name + ' is ' +
user.age + ' years old.');
div(user.name + ' is eighteen: ',
function(){
strong(user.isEighteen())
}
);
All types created by oj.createType have the following methods and properties.
type | The type that made this instance (read-only) |
typeName | The name of type that made this instance (read-only) |
properties | Array of all properties the type supports (read-only) |
methods | Array of all methods the type supports (read-only) |
get() | Get all properties as an object |
get(prop) | Get the value of property |
set(prop,val) | Set property |
set(props) | Set all properties at once. |
has(prop) | Determine if string |
can(method) | Determine if string |
toJSON() | Convert properties to json |
For those of you who want to make your own rich oj views, there are a few inheritable types that will make your life easier.
View - The heart of OJ, this makes objects behave like tag functions.
ModelKeyView - Binds the view to a part of a model (a model + key). Used by all the form elements. Great for creating two-way bindings.
CollectionView - Bind the view to a collection of models. Used by List and Table. Automatically binds to collection add, remove, change events.
This object is the basis of much of the functionality of OJ. It emits creation events so that objects act like oj.tag. It creates the root element of your object and gives convenient access to it through el. It provides an attributes property and related methods to let you set and get attributes.
All types created using oj.createType behave this way.
var textbox = TextBox({value:'my text'});
Is the same as:
var textbox = TextBox();
textbox.value = 'my text';
(Note: because oj.createType removes the need to use new
, so in the above example it wasn't necessary to call new TextBox()
. See oj.createType if you want to learn more about why this was done.)
Any options that don't map to properties will be passed through to the root element as attributes. This lets this work as you would expect:
TextBox({value:'Placeholder text', id:'id123', style:{color:'red'}})
Also options can come in any order. This is especially useful for defining style ()
TextBox({id:'id123'}, 'my text', {style:{
color:'red'
}
})
el | |
$el | |
attributes | Get all attributes for the root element (read-only) |
addAttribute(name,value) | Add an attribute. Supports everything oj.tag supports, including jQuery events and |
addAttribute(name,value) | Add an attribute. Supports everything oj.tag supports, including jQuery events and |
addAttributes(obj) | Add multiple attributes at once. Supports everything oj.tag supports. Including jQuery events and |
removeAttribute(name) | Remove attribute with by |
removeAttributes(list) | Remove all attributes in the list. |
$(selector) | Find elements from root with jQuery. |
toHTML() | Convert view to html |
toDOM() | Convert view to dom |
toString() | Convert view to a string. By default this calls toHTML() |
model | The model part of the model-key that partially binds a model. Both key and model are required. |
key | The key part of the model-key that partially binds a model. Both key and model are required. |
modelChanged(...) | Overridable event method called when the model is changed When overriding be sure to call the base method |
viewChanged(...) | Call this event method to when the view changes. This will cause the model to be updated automatically. |
collection | The inherited collection property for the view. This can be an Array or a Backbone.Collection. When Backbone is used changes the CollectionView automatically binds to changes in the collection and calls the following methods:
When implementing a oj.CollectionView these event methods should be overriden to correctly update state as the collection changes. |
models | Alias for |
collectionModelAdded(...) | Overridable event method called when a model is added to the collection |
collectionModelRemoved(...) | Overridable event method called when a model is removed from the collection |
collectionModelChanged(...) | Overridable event method called when a model changes |
collectionModelDestroyed(...) | Overridable event method called when a model is destroyed |
collectionReset() | Overridable event method called when the entire collection is remade |
OJ has the capability of building static websites on the command-line using the oj command. This is best for sites that don't change content upon request -- think Github Pages. This does not mean the site cannot have ajax requests and dynamic content, just that the tool isn't for building that.
.oj
and .ojc
generate js, html, and css.oj
means javascript, .ojc
means coffee-script.--minify
is setrequire
syntax.require
them, and they will also be brought into the system (similar to browserify)..ojc
files. You don't even need to install coffee-script, it just works.To use OJ on the server you will need to install the oj
command built on top of node.js. For this you need to install node. There are many ways to do this installation, but this will get you started:
Install node
on OSX with homebrew:
brew install node
Install node
on Ubuntu with apt-get:
sudo apt-get install nodejs npm
Finally install oj
with npm
:
npm install -g oj
The oj.js javascript library works perfectly fine inside of .js
, or .coffee
files. The only issue is that it gets tiring writing oj
in front of everything: oj.html
, oj.Table
, etc.
To solve this OJ supports two file types:
.oj
is javascript.ojc
is coffee-scriptThese files have been fully integrated into node. This means you can include other files using require
, and you must use module.exports
to set the what the module should return. Here are some examples:
# hello.oj
module.exports = return [html,
[head],
[body,
[div 'Hello world']
]
]
# hello.ojc
module.exports = ->
html ->
head ->
body ->
div 'Hello world'
The oj
command builds .oj
and .ojc
file types into .html
files.
oj [options] <file> <dir> ...
Options:
-h, --help output usage information
-V, --version output the version number
-m, --minify Turn on minification (default: false)
-o, --output <dir> Directory to output all files to (default: .)
-r, --recurse Recurse into directories (default: off)
-v, --verbose <level> Turn on verbose level 0-3 (default: 1)
-w, --watch Turn on watch mode (default: off)
When options are omitted these are the defaults:
--output defaults to ./
--minify defaults to off<br>
--verbose defaults to 1
_
)oj.
).
)Build hello.oj
to current directory
oj hello.oj
Build hello.oj
without minification to www
directory
oj -d -o www hello.oj
Watch hello.oj
and build to www
directory
oj --watch -o www hello.oj
Build pages
directory to www
oj -o www pages
Recursively build pages
directory to www
oj -r -o www pages
Recursively watch and build pages
directory to www
oj -w -r -o www pages