mirror of
https://github.com/zhigang1992/deployd.git
synced 2026-06-10 23:39:20 +08:00
604 lines
20 KiB
HTML
604 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Deployd Documentation</title>
|
|
<link href="css/bootstrap.min.css" rel="stylesheet" type="text/css" />
|
|
<link href="css/style.css" rel="stylesheet" type="text/css" />
|
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
|
<link href='http://fonts.googleapis.com/css?family=Bitter:400,700,400italic' rel='stylesheet' type='text/css'>
|
|
<style>
|
|
h3.code {text-transform: lowercase; font-family: Monaco, Bitter, san-serif;}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid">
|
|
<div class="row-fluid">
|
|
<div class="span3" style="height: 1px;">
|
|
</div><!--/span-->
|
|
<div id="side">
|
|
<div class="well sidebar-nav">
|
|
<h4 style="margin-left: 17px; margin-bottom: 10px;">Deployd</h4>
|
|
<ul class="nav nav-list">
|
|
|
|
</ul>
|
|
</div><!--/.well -->
|
|
</div><!-- /#side -->
|
|
<div class="span9" style="max-width: 1000px;"><h1>Deployd</h1>
|
|
|
|
<p><strong><em>Deployd</strong> lets you quickly build secure, dynamic JavaScript and Mobile apps without writing backend code. Instead you boot a simple server that is very good at securely proxying requests to a storage layer such as a database. This enables your un-trusted client code to do things it never could before, such as write directly to a database without a custom application running in-between.</em></p>
|
|
|
|
<p>Instead of reinventing a protocol, deployd embraces HTTP. This means that any HTTP client, such as a JavaScript app, a mobile app, or even another server, can securely interact with your data without having to setup a web server and write a custom backend.</p>
|
|
|
|
<h2>Quick start</h2>
|
|
|
|
<p>Currently deployd requires mongodb to be installed. You can download mongodb <a href="http://www.mongodb.org/downloads">here</a>. It also requires <code>node.js</code> >= v0.6.0. You can download node <a href="http://nodejs.org/#download">here</a>.</p>
|
|
|
|
<p>Install <strong>deployd</strong> from npm. <code>sudo</code> may be required depending on your setup.</p>
|
|
|
|
<pre><code>$ [sudo] npm install deployd -g
|
|
</code></pre>
|
|
|
|
<p>Create an <code>index.html</code> file in a new directory.</p>
|
|
|
|
<pre><code><!doctype html>
|
|
<html>
|
|
<head>
|
|
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
|
|
<script src="/dpd.js"></script>
|
|
<script> console.log(dpd); </script>
|
|
</head>
|
|
<body>
|
|
</body>
|
|
</html>
|
|
</code></pre>
|
|
|
|
<p>Run the following command from the same directory where you created index.html. <em>See <code>dpd -h</code> for more info on how to use the command line interface.</em></p>
|
|
|
|
<pre><code>$ dpd -d
|
|
</code></pre>
|
|
|
|
<p>This will open up the dashboard where you can manage your deployd server. You can create resources and manage their data.</p>
|
|
|
|
<p>Open up <code>http://localhost:2403</code> your browser console. You should see the <code>dpd</code> object logged out. This is an api to all of your resources.</p>
|
|
|
|
<p>If you created a <code>/todos</code> collection you could add a new todo with:</p>
|
|
|
|
<pre><code>dpd.todos.save({title: 'Foo the bar', completed: true}, function(todo) {
|
|
console.log(todo);
|
|
});
|
|
</code></pre>
|
|
|
|
<p>If you created a <code>/users</code> user collection you could log in a user with:</p>
|
|
|
|
<pre><code>dpd.users.login({email: 'foo@bar.com', password: 'mypassword'}, function(user) {
|
|
console.log(user);
|
|
});
|
|
</code></pre>
|
|
|
|
<h2>How does it work?</h2>
|
|
|
|
<h3>Storage</h3>
|
|
|
|
<p>Deployd greatly simplifies web and mobile app development by acting as a proxy between untrusted clients (browser, phones, etc) and your database, allowing you to do more on the client than ever before. This added benefit also comes with added responsibility to secure the newly opened APIs directly to your data. Deployd solves this by validating every request against rules you define in the dashboard or in familiar JavaScript.</p>
|
|
|
|
<p>Once a request is validated for input, authorization, and passes any <a href="#Collection-Event-Handlers">custom validation</a>, it is proxied to the storage layer (database abstraction). This means you do not have to write backend code to expose data to your clients. Data is instantly available.</p>
|
|
|
|
<h3>REST</h3>
|
|
|
|
<p>Deployd uses REST to expose database commands to clients.</p>
|
|
|
|
<p>REST is a web service design pattern that conforms closely to HTTP itself. In Deployd, HTTP methods or verbs have meaning:</p>
|
|
|
|
<ul>
|
|
<li><strong>GET</strong> - Load a resource without modifying it (this is a browser's default method)</li>
|
|
<li><strong>POST</strong> - Create a resource, or send data to a special that doesn't fit within these methods</li>
|
|
<li><strong>PUT</strong> - Update an existing resource</li>
|
|
<li><strong>DELETE</strong> - Destroy an existing resource</li>
|
|
</ul>
|
|
|
|
<p>In Deployd, HTTP response codes are also important:</p>
|
|
|
|
<ul>
|
|
<li><strong>200</strong> OK - The request succeeded</li>
|
|
<li><strong>204</strong> No Content - The request succeeded, but there is no content to return (for example, after a deletion, or requesting an empty list)</li>
|
|
<li><strong>400</strong> Bad Request - The request did not pass validation. Change the parameters and try again.</li>
|
|
<li><strong>401</strong> Unauthorized - The request's session does not have permission to access that resource. </li>
|
|
<li><strong>404</strong> Not Found - That URL does not reference an existing resource</li>
|
|
<li><strong>500</strong> Internal Server Error - Deployd has failed to process the request due to an unexpected error.</li>
|
|
</ul>
|
|
|
|
<p>Deployd exposes everything, even low level functionality, securely over http (with the appropriate auth keys). This lets you use deployd however you want as long as your client can speak http.</p>
|
|
|
|
<h3>Resources</h3>
|
|
|
|
<p>Deployd servers are built using resources. Each resource handles its own unique URL. For example a list of todos could be served as a collection resource.</p>
|
|
|
|
<pre><code>GET /todos?q={"$orderby": "created"}
|
|
</code></pre>
|
|
|
|
<p>Deployd will route this to the <code>/todos</code> collection resource and hand it off to its module. The collection resource will execute a fetch from the storage engine passing the query <code>{"$orderby": "created"}</code> and execute the <code>GET</code> event handler if one has been attached, passing the event handler the current session and data retrieved. Finally, the http response will be handed the final object, potentially modified by an event handler.</p>
|
|
|
|
<hr />
|
|
|
|
<h1>JavaScript Client API</h1>
|
|
|
|
<p>Its very easy to access the deployd api from browser JavaScript. Just include <code><script src="/dpd.js"></script></code>. Deployd automatically builds and generates your data api for you.</p>
|
|
|
|
<h2>Collections</h2>
|
|
|
|
<p>Each collection gets several methods to create/update, read/query, and delete data. The following examples assume you have created a <code>/todos</code> collection.</p>
|
|
|
|
<h3 class="code">save(data, callback) </h3>
|
|
|
|
<p>Save the provided data object to the collection:</p>
|
|
|
|
<pre><code>dpd.todos.save({creator: 'ritch', title: 'foo the bar', order: 7, done: false}, function(todo, err) {
|
|
if(err) return console.log(err);
|
|
console.log(todo); // {title: 'foo the bar', order: 7, done: false, _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<p>If the object already has an <code>_id</code> the existing object in the collection with the matching id will be updated.</p>
|
|
|
|
<pre><code>dpd.todos.save({_id: "4b5783300334000000000aa9", done: true}, function(todo, err) {
|
|
if(err) return console.log(err);
|
|
console.log(todo); // {title: 'foo the bar', order: 7, done: true, _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">post(data, callback) </h3>
|
|
|
|
<p>Creates the provided object in the collection:</p>
|
|
|
|
<pre><code>dpd.todos.post({creator: 'ritch', title: 'foo the bar', order: 7, done: false}, function(todo, err) {
|
|
if(err) return console.log(err);
|
|
console.log(todo); // {title: 'foo the bar', order: 7, done: false, _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">get([query], callback)</h3>
|
|
|
|
<p>Query the collection using the optional <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries">mongo query object</a>.</p>
|
|
|
|
<p>Get all the todos with a creator <code>'ritch'</code>:</p>
|
|
|
|
<pre><code>dpd.todos.get({creator: 'ritch'}, function(todos, err) {
|
|
if(err) return console.log(err);
|
|
console.log(todos); // [{title: 'foo the bar', order: 7, done: true}, ...]
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">first([query | id], callback) or getOne([query | id], callback)</h3>
|
|
|
|
<p>Get the first object from the collection using the optional <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries">mongo query object</a> or an id.</p>
|
|
|
|
<p>Get a todo by id:</p>
|
|
|
|
<pre><code>dpd.todos.first("4b5783300334000000000aa9", function(todo, err) {
|
|
if(err) return console.log(err);
|
|
console.log(todo); // {title: 'foo the bar', order: 7, done: true, _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<p>Get a todo with specified properties:</p>
|
|
|
|
<pre><code>dpd.todos.first({creator: 'ritch', title: 'foo the bar'}, function(todo, err) {
|
|
if(err) return console.log(err);
|
|
console.log(todo); // {title: 'foo the bar', creator: 'ritch', order: 7, done: true, _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">put([id], data, callback)</h3>
|
|
|
|
<p>Updates the object with the specified id with the provided properties. If no id is provided, it will be inferred from the data's <code>_id</code> property.</p>
|
|
|
|
<p>Update a todo:</p>
|
|
|
|
<pre><code>dpd.todos.put("4b5783300334000000000aa9", {order: 3}, function(todo, err)) {
|
|
if(err) return console.log(err);
|
|
console.log(todo); // {title: 'foo the bar', creator: 'ritch', order: 3, done: true, _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">del(id, callback)</h3>
|
|
|
|
<p>Delete the object from the collection with the specified id.</p>
|
|
|
|
<p>Delete a todo by id:</p>
|
|
|
|
<pre><code>dpd.todos.del("4b5783300334000000000aa9", function(success, err) {
|
|
if(err) return console.log(err);
|
|
console.log(success); // true
|
|
});
|
|
</code></pre>
|
|
|
|
<h2>Users Collection</h2>
|
|
|
|
<p>Users collections include methods to login and logout users as well as return the current user. Each users collection gets the same methods to create/update, read/query, and delete data as a regular collection. The following examples assume you have created a <code>/users</code> collection.</p>
|
|
|
|
<h3 class="code">users.post(user, callback)</h3>
|
|
|
|
<p>Register a new user. Passwords will automatically be removed from responses.</p>
|
|
|
|
<pre><code>dpd.users.post({email: 'foo@bar.com', password: 'mypassword'}, function(user, err) {
|
|
if(err) return console.log(err);
|
|
console.log(user); // {email: 'foo@bar.com', _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">users.login(credentials, callback)</h3>
|
|
|
|
<p>Login a user with the given credentials.</p>
|
|
|
|
<pre><code>dpd.users.first({email: 'foo@bar.com', password: 'mypassword'}, function(user, err) {
|
|
if(err) return console.log(err);
|
|
console.log(user); // {email: 'foo@bar.com', _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">users.me(callback)</h3>
|
|
|
|
<p>Get the currently logged in user.</p>
|
|
|
|
<pre><code>dpd.users.me(function(user, err) {
|
|
if(err) return console.log(err);
|
|
console.log(user); // {email: 'foo@bar.com', _id: "4b5783300334000000000aa9"}
|
|
});
|
|
</code></pre>
|
|
|
|
<h3 class="code">users.logout(callback)</h3>
|
|
|
|
<p>Logout the current user.</p>
|
|
|
|
<pre><code>dpd.users.logout(function(success, err) {
|
|
if(err) return console.log(err);
|
|
console.log(success); // true
|
|
});
|
|
</code></pre><h1>Collection Resource</h1>
|
|
|
|
<p>A Collection resource allows client apps to save and query data.</p>
|
|
|
|
<h2>Setting up a collection</h2>
|
|
|
|
<p>After creating a Collection resource in the dashboard, you can set up the schema that will automatically validate data sent to the resource url over HTTP. You can configure this schema in the dashboard.</p>
|
|
|
|
<p>The Data view in the dashboard allows you to edit the Collection manually.</p>
|
|
|
|
<h2>Property types</h2>
|
|
|
|
<p>You can currently use the following property types:</p>
|
|
|
|
<ul>
|
|
<li><strong>String</strong> - Arbritrary text data</li>
|
|
<li><strong>Number</strong> - Numeric value, supports floating points</li>
|
|
<li><strong>Boolean</strong> - True or false</li>
|
|
<li><strong>Date</strong> - A specific point in time</li>
|
|
</ul>
|
|
|
|
<h2>Formats</h2>
|
|
|
|
<p>When POSTing or PUTing data to a resource <code>/path</code>, you must format the request body as a JSON string and pass the header "Content-Type: application/json".</p>
|
|
|
|
<h2>Saving data</h2>
|
|
|
|
<p>To save data, send a POST request to the root of the Collection:</p>
|
|
|
|
<pre><code>POST /people
|
|
Content-Type: application/json
|
|
{
|
|
"age": 23,
|
|
"firstName": "Joe",
|
|
"lastName": "Smith"
|
|
}
|
|
</code></pre>
|
|
|
|
<p>The server will respond with the object, which will have a new <code>_id</code> property. </p>
|
|
|
|
<pre><code>200 OK
|
|
{
|
|
"_id": "4f71fc7c2ba744786f000001",
|
|
"age": 23,
|
|
"firstName": "Joe",
|
|
"lastName": "Smith"
|
|
}
|
|
</code></pre>
|
|
|
|
<p>This <code>_id</code> is used to find the object's URL (i.e. <code>/people/4f71fc7c2ba744786f000001</code>)</p>
|
|
|
|
<h2>Listing data</h2>
|
|
|
|
<p>A GET request to the root of the Collection will return an array of objects in the collection:</p>
|
|
|
|
<pre><code>GET /people
|
|
|
|
200 OK
|
|
[
|
|
{
|
|
"_id": "4f71fc7c2ba744786f000001",
|
|
"age": 23,
|
|
"firstName": "Joe",
|
|
"lastName": "Smith"
|
|
},
|
|
{
|
|
"_id": "4f71fe8b2ba744786f000002",
|
|
"age": 36,
|
|
"firstName": "John",
|
|
"lastName": "Doe"
|
|
}
|
|
]
|
|
</code></pre>
|
|
|
|
<h2>Retrieving a specific object</h2>
|
|
|
|
<p>A GET request at an object's URL will return the properties of that object:</p>
|
|
|
|
<pre><code>GET /people/4f71fc7c2ba744786f000001
|
|
|
|
200 OK
|
|
{
|
|
"_id": "4f71fc7c2ba744786f000001",
|
|
"age": 23,
|
|
"firstName": "Joe",
|
|
"lastName": "Smith"
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Updating an object</h2>
|
|
|
|
<p>A PUT (or POST) request at an object's URL will update the properties specified on the object.</p>
|
|
|
|
<pre><code>PUT /people/4f71fc7c2ba744786f000001
|
|
Content-Type: application/json
|
|
{
|
|
"age": 24
|
|
}
|
|
</code></pre>
|
|
|
|
<p>The server will respond with the entire object:</p>
|
|
|
|
<pre><code>200 OK
|
|
{
|
|
"_id": "4f71fc7c2ba744786f000001",
|
|
"age": 24,
|
|
"firstName": "Fred",
|
|
"lastName": "Smith"
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Deleting an object</h2>
|
|
|
|
<p>A DELETE request at an object's URL will permanently remove that object from the collection:</p>
|
|
|
|
<pre><code>DELETE /people/4f71fc7c2ba744786f000001
|
|
|
|
204 No Content
|
|
</code></pre>
|
|
|
|
<h2>Filtering results</h2>
|
|
|
|
<p>You can add querystring parameters to a GET request at the root to filter the results by properties specified:</p>
|
|
|
|
<pre><code>GET /people?firstName=Joe&lastName=Smith
|
|
</code></pre>
|
|
|
|
<p><strong>NOTE</strong>: This currently only works for String properties.</p>
|
|
|
|
<h2>Advanced querying</h2>
|
|
|
|
<p>If you need to query additional types of properties, pass a JSON object as the <code>q</code> parameter with the properties you wish to filter:</p>
|
|
|
|
<pre><code>GET /people?q={
|
|
"age": 23
|
|
}
|
|
</code></pre>
|
|
|
|
<p>The <code>q</code> parameter supports <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries">MongoDB's query language</a> for particularly advanced queries. Note that Collections do not currently support embedded documents or arrays.</p>
|
|
|
|
<pre><code>GET /people?q={
|
|
"$orderby": { "age": 1 },
|
|
"name": {
|
|
"$regex": "^j"
|
|
"$options": "i",
|
|
}
|
|
}
|
|
</code></pre>
|
|
|
|
<h1>Collection Event Handlers</h1>
|
|
|
|
<p>You can attach micro-scripts to events to add logic and validation to your objects. Collections currently support the following events:</p>
|
|
|
|
<ul>
|
|
<li><strong>On GET</strong> - called when data is read</li>
|
|
<li><strong>On Validate</strong> - called before creating or updating an object.</li>
|
|
<li><strong>On POST</strong> - called when data is created</li>
|
|
<li><strong>On PUT</strong> - called when data is updated</li>
|
|
<li><strong>On DELETE</strong> - called when data is destroyed</li>
|
|
</ul>
|
|
|
|
<h2>Reading and setting properties</h2>
|
|
|
|
<p>In an event micro-script, the <code>this</code> object refers to the current object, and has all of the properties of the object.</p>
|
|
|
|
<p>You can set values on the <code>this</code> object during an On Post or On Put event. These changes will be saved to the database.</p>
|
|
|
|
<pre><code>// On POST:
|
|
this.dateCreated = new Date();
|
|
|
|
// On Validate:
|
|
this.totalScore = this.level1Points + this.level2Points;
|
|
</code></pre>
|
|
|
|
<p><strong>Note</strong>: If you intend for a property to be "automatic" (calculated by the server, and never provided by the client), you will need to mark it as <strong>Optional</strong> in the dashboard.</p>
|
|
|
|
<h2>Accessing the current user</h2>
|
|
|
|
<p>If the request is coming from a logged in User, you can use the "me" object to access their properties.</p>
|
|
|
|
<pre><code>// On POST:
|
|
this.creator = me._id;
|
|
</code></pre>
|
|
|
|
<h2>Cancelling an event</h2>
|
|
|
|
<p>You can stop any event by calling the <code>cancel(message, [code])</code> method.</p>
|
|
|
|
<pre><code>//On DELETE:
|
|
if (this.protected) {
|
|
cancel('This post is protected and cannot be deleted');
|
|
}
|
|
|
|
DELETE /posts/123456
|
|
|
|
400 Bad Request
|
|
{
|
|
"message": "This post is protected and cannot be deleted"
|
|
}
|
|
</code></pre>
|
|
|
|
<p>You can pass an integer to the <code>cancel()</code> method as the second parameter to set the HTTP status code. For example, 401 means "Unauthorized".</p>
|
|
|
|
<pre><code>//On PUT
|
|
if (this.creator !== me._id) {
|
|
cancel("You cannot edit this post because it is not yours!", 401);
|
|
}
|
|
|
|
PUT /posts/13456
|
|
Content-Type: application/json
|
|
{ ... }
|
|
|
|
401 Unauthorized
|
|
{
|
|
"message": "You cannot edit this post because it is not yours!"
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Validation</h2>
|
|
|
|
<p>Use the <code>error(name, message)</code> function to add a validation error.</p>
|
|
|
|
<pre><code>//On Validate
|
|
if (this.age < 18) {
|
|
error('age', 'must be older than 18')
|
|
}
|
|
|
|
POST /people
|
|
{
|
|
"firstName": "Joe",
|
|
"lastName": "Smith",
|
|
"age": 12
|
|
}
|
|
|
|
400 Bad Request
|
|
{
|
|
errors: {
|
|
"age": "must be older than 18"
|
|
}
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Hiding properties</h2>
|
|
|
|
<p>If you wish to hide certain properties from a user, use the <code>hide(propertyName)</code> function.</p>
|
|
|
|
<pre><code>//On Get
|
|
if (this.creator !== me._id) {
|
|
hide('lastName');
|
|
hide('age');
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Protecting properties from modification</h2>
|
|
|
|
<p>Use the <code>protect(propertyName)</code> function to protect specified properties during a PUT.</p>
|
|
|
|
<pre><code>//On Put
|
|
protect('createdDate');
|
|
</code></pre><hr /><h1>User Collection Resource</h1>
|
|
|
|
<p>A User Collection resource behaves much like the standard Collection resource, but adds the ability to authenticate with a username and password.</p>
|
|
|
|
<h2>Special properties</h2>
|
|
|
|
<p>The User Collection contains two special properties:</p>
|
|
|
|
<ul>
|
|
<li><strong>email</strong> - For security, hidden by default on all users except the current user.</li>
|
|
<li><strong>password</strong> - Never readable under any circumstances. Can only be set when the user is logged in, when creating a new user, or from the Dashboard.</li>
|
|
</ul>
|
|
|
|
<h2>Registering a user</h2>
|
|
|
|
<p>First create a user by POSTing it to the root of the collection.
|
|
For this example our collection will be called <code>/users</code>.</p>
|
|
|
|
<pre><code>POST /users
|
|
Content-Type: application/json
|
|
{
|
|
"email": "foo@bar.com",
|
|
"password": "barfoo"
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Authenticating a user</h2>
|
|
|
|
<p>To login a user, send a POST request to <code>/<collection name>/login</code>:</p>
|
|
|
|
<pre><code>POST /users/login
|
|
Content-Type: application/json
|
|
{
|
|
"email": "foo@bar.com",
|
|
"password": "barfoo"
|
|
}
|
|
</code></pre>
|
|
|
|
<p>The server will respond with the user, without the password.</p>
|
|
|
|
<pre><code>200 OK
|
|
{
|
|
"_id": "4f71fc7c2ba744786f000001",
|
|
"email": "foo@bar.com"
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Current user</h2>
|
|
|
|
<p>The currently logged in user is available by sending a GET request to <code>/users/me</code>.</p>
|
|
|
|
<pre><code>200 OK
|
|
{
|
|
"_id": "4f71fc7c2ba744786f000001",
|
|
"email": "foo@bar.com"
|
|
}
|
|
</code></pre>
|
|
|
|
<h2>Logging out</h2>
|
|
|
|
<p>To logout a user send a POST request to <code>/<collection name>/logout</code>:</p>
|
|
|
|
<pre><code>204 No Content
|
|
</code></pre> </div> <!-- /span9 -->
|
|
</div> <!-- /row -->
|
|
</div> <!-- /container -->
|
|
</body>
|
|
|
|
<script>
|
|
var nav = $('.nav').empty();
|
|
|
|
// build out navigation
|
|
$('h1, h2').each(function () {
|
|
var key = $(this).text().replace(/ /g, '-');
|
|
$(this).prepend('<a name="'+ key + '"></a>');
|
|
console.log(this);
|
|
nav.append('<li class="'+ (this.nodeName === 'H1' ? 'nav-header' : '') +'"><a href="#' + key + '">' + $(this).text() + '</a></li>');
|
|
})
|
|
|
|
function fill() {
|
|
$('#side .well').height($(window).height() - 100);
|
|
}
|
|
|
|
$(window).resize(fill)
|
|
|
|
fill();
|
|
</script>
|
|
|
|
</html> |