<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example: Creating a Node Plugin that chains transitions</title>
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic">
<link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css">
<link rel="stylesheet" href="../assets/css/main.css">
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
<link rel="shortcut icon" type="image/png" href="../assets/favicon.png">
<script src="../../build/yui/yui-min.js"></script>
</head>
<body>
<!--
<a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
-->
<div id="doc">
<div id="hd">
<h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1>
</div>
<a href="#toc" class="jump">Jump to Table of Contents</a>
<h1>Example: Creating a Node Plugin that chains transitions</h1>
<div class="yui3-g">
<div class="yui3-u-3-4">
<div id="main">
<div class="content"><style scoped>
#square {
width: 100px;
height: 100px;
background: gray;
position: relative;
margin: 20px;
}
</style>
<div class="intro">
<p>
In order to run transitions sequentially, you would normally have to use the callback provided by <code>node.transition()</code>. This example shows how to create your own Node plugin based on promises that lets you chain CSS transitions.
</p>
</div>
<div class="example yui3-skin-sam">
<button id="without-plugin">Without Plugin</button>
<button id="with-plugin">With Plugin</button>
<div id="square"></div>
<script>
YUI().use('promise', 'transition', 'node-pluginhost', function (Y) {
// NodePromise will represent a YUI Node
function NodePromise() {
NodePromise.superclass.constructor.apply(this, arguments);
}
Y.extend(NodePromise, Y.Promise);
// This method takes the same "config" parameter as Node's transition method
// but returns a NodePromise instead
NodePromise.prototype.transition = function (config) {
// We call this.then to ensure the promise is fulfilled.
// Since we will be creating a chain of transitions this means we will be
// waiting for the previous transition to end
return this.then(function (node) {
// As noted in the user guide, returning a promise inside the then()
// callback causes the promise returned by then() to be synced with this
// new promise. This is a way to control when the returned promise is
// fulfilled
return new Y.Promise(function (fulfill, reject) {
node.transition(config, function () {
// The transition is done, signal the promise that all is ready
// by fulfilling it with the same node
fulfill(node);
});
});
});
};
function PromisePlugin(config) {
// Create a private NodePromise instance that points to the plugin host
this._promise = new NodePromise(function (fulfill) {
// Since this is a Node plugin, config.host will be an instance of Node
fulfill(config.host);
});
}
// Set up the plugin's namespace
PromisePlugin.NS = 'promise';
PromisePlugin.prototype.transition = function (config) {
// Simply point to the private promise's transition method
return this._promise.transition(config);
};
var square = Y.one('#square');
square.plug(PromisePlugin);
function resetStyles() {
square.setStyles({
width: '100px',
height: '100px',
left: '0'
});
}
Y.one('#without-plugin').on('click', function () {
resetStyles();
square
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
});
Y.one('#with-plugin').on('click', function () {
resetStyles();
square.promise
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
});
});
</script>
</div>
<h2 id="using-promises-to-chain-css-transitions">Using Promises to Chain CSS Transitions</h2>
<h3 id="the-plan">The plan</h3>
<p>Plugins are a way to add functionality to Node without modifying its existing methods. They also are usually subclasses of Plugin.Base that contain various methods to interact with in a different way with a node. In our case we will skip the use of Plugin.Base to focus on returning promises from a plugin method.</p>
<p>The plan is to create a Promise subclass that represents a Node and store one of these promises in the plugin instance. Then the plugin's <code>transition</code> method will return a new promise based on the one already stored.</p>
<h3 id="creating-a-promise-subclass">Creating a Promise Subclass</h3>
<p>Promises represent a value. Since we want to chain transitions on a Node we need to create a Promise sublcass that represents a Node. Promises can be extended the same way as any other YUI class by using <a href="http://yuilibrary.com/yui/docs/api//classes/YUI.html#method_extend"><code>Y.extend</code></a>.</p>
<pre class="code prettyprint">// NodePromise will represent a YUI Node
function NodePromise() {
NodePromise.superclass.constructor.apply(this, arguments);
}
Y.extend(NodePromise, Y.Promise);</pre>
<p>The next step is to add the <code>transition()</code> method to this promise and have it return a promise that is fulfilled when the transition is completed.</p>
<pre class="code prettyprint">// This method takes the same "config" parameter as Node's transition method
// but returns a NodePromise instead
NodePromise.prototype.transition = function (config) {
// We call this.then to ensure the promise is fulfilled.
// Since we will be creating a chain of transitions this means we will be
// waiting for the previous transition to end
return this.then(function (node) {
// As noted in the user guide, returning a promise inside the then()
// callback causes the promise returned by then() to be synced with this
// new promise. This is a way to control when the returned promise is
// fulfilled
return new Y.Promise(function (fulfill, reject) {
node.transition(config, function () {
// The transition is done, signal the promise that all is ready
// by fulfilling it with the same node
fulfill(node);
});
});
});
};</pre>
<h3 id="creating-the-plugin">Creating the Plugin</h3>
<p>Our plugin is a very simple class that contains a NodePromise. In order for it to let us write chains of transitions like <code>node.promise.transition(config1).transition(config2)</code> we will add a <code>transition</code> method to it that simply points to the NodePromise's same method.</p>
<pre class="code prettyprint">function PromisePlugin(config) {
// Create a private NodePromise instance that points to the plugin host
this._promise = new NodePromise(function (fulfill) {
// Since this is a Node plugin, config.host will be an instance of Node
fulfill(config.host);
});
}
// Set up the plugin's namespace
PromisePlugin.NS = 'promise';
PromisePlugin.prototype.transition = function (config) {
// Simply point to the private promise's transition method
return this._promise.transition(config);
};</pre>
<h3 id="using-the-plugin">Using the Plugin</h3>
<p>Now that we have the plugin ready, we can easily chain transitions from the plugin instance:</p>
<pre class="code prettyprint">var square = Y.one('#square');
square.plug(PromisePlugin);
// run a sequence of transitions
square.promise
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});</pre>
<h3 id="fullcode">Full Code Listing</h3>
<h4 id="html">HTML</h4>
<pre class="code prettyprint"><button id="without-plugin">Without Plugin</button>
<button id="with-plugin">With Plugin</button>
<div id="square"></div></pre>
<h4 id="css">CSS</h4>
<pre class="code prettyprint"><style scoped>
#square {
width: 100px;
height: 100px;
background: gray;
position: relative;
margin: 20px;
}
</style></pre>
<h4 id="javascript">JavaScript</h4>
<pre class="code prettyprint"><script>
YUI().use('promise', 'transition', 'node-pluginhost', function (Y) {
// NodePromise will represent a YUI Node
function NodePromise() {
NodePromise.superclass.constructor.apply(this, arguments);
}
Y.extend(NodePromise, Y.Promise);
// This method takes the same "config" parameter as Node's transition method
// but returns a NodePromise instead
NodePromise.prototype.transition = function (config) {
// We call this.then to ensure the promise is fulfilled.
// Since we will be creating a chain of transitions this means we will be
// waiting for the previous transition to end
return this.then(function (node) {
// As noted in the user guide, returning a promise inside the then()
// callback causes the promise returned by then() to be synced with this
// new promise. This is a way to control when the returned promise is
// fulfilled
return new Y.Promise(function (fulfill, reject) {
node.transition(config, function () {
// The transition is done, signal the promise that all is ready
// by fulfilling it with the same node
fulfill(node);
});
});
});
};
function PromisePlugin(config) {
// Create a private NodePromise instance that points to the plugin host
this._promise = new NodePromise(function (fulfill) {
// Since this is a Node plugin, config.host will be an instance of Node
fulfill(config.host);
});
}
// Set up the plugin's namespace
PromisePlugin.NS = 'promise';
PromisePlugin.prototype.transition = function (config) {
// Simply point to the private promise's transition method
return this._promise.transition(config);
};
var square = Y.one('#square');
square.plug(PromisePlugin);
function resetStyles() {
square.setStyles({
width: '100px',
height: '100px',
left: '0'
});
}
Y.one('#without-plugin').on('click', function () {
resetStyles();
square
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
});
Y.one('#with-plugin').on('click', function () {
resetStyles();
square.promise
.transition({width: '300px'})
.transition({height: '300px'})
.transition({left: '200px'});
});
});
</script></pre>
</div>
</div>
</div>
<div class="yui3-u-1-4">
<div class="sidebar">
<div id="toc" class="sidebox">
<div class="hd">
<h2 class="no-toc">Table of Contents</h2>
</div>
<div class="bd">
<ul class="toc">
<li>
<a href="#using-promises-to-chain-css-transitions">Using Promises to Chain CSS Transitions</a>
<ul class="toc">
<li>
<a href="#the-plan">The plan</a>
</li>
<li>
<a href="#creating-a-promise-subclass">Creating a Promise Subclass</a>
</li>
<li>
<a href="#creating-the-plugin">Creating the Plugin</a>
</li>
<li>
<a href="#using-the-plugin">Using the Plugin</a>
</li>
<li>
<a href="#fullcode">Full Code Listing</a>
<ul class="toc">
<li>
<a href="#html">HTML</a>
</li>
<li>
<a href="#css">CSS</a>
</li>
<li>
<a href="#javascript">JavaScript</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="sidebox">
<div class="hd">
<h2 class="no-toc">Examples</h2>
</div>
<div class="bd">
<ul class="examples">
<li data-description="Wrapping async transactions with promises">
<a href="basic-example.html">Wrapping async transactions with promises</a>
</li>
<li data-description="Extend Y.Promise to create classes that encapsulate standard transaction logic in descriptive method names">
<a href="subclass-example.html">Subclassing Y.Promise</a>
</li>
<li data-description="Extend the Promise class to create your own Node plugin that chains transitions">
<a href="plugin-example.html">Creating a Node Plugin that chains transitions</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../assets/vendor/prettify/prettify-min.js"></script>
<script>prettyPrint();</script>
<script>
YUI.Env.Tests = {
examples: [],
project: '../assets',
assets: '../assets/promise',
name: 'plugin-example',
title: 'Creating a Node Plugin that chains transitions',
newWindow: '',
auto: false
};
YUI.Env.Tests.examples.push('basic-example');
YUI.Env.Tests.examples.push('subclass-example');
YUI.Env.Tests.examples.push('plugin-example');
</script>
<script src="../assets/yui/test-runner.js"></script>
</body>
</html>