gbone.js | |
---|---|
| (function($) {
|
Initial Setup |
var root = this,
|
The top-level namespace. All public Gbone.js classes will be attached to this. Exported for both CommonJS and the browser. | Gbone, |
Mixins | observer, cleanup, transitions, state, |
State machine for Panel Views. | Manager;
if (typeof exports !== 'undefined') {
Gbone = exports;
} else {
Gbone = root.Gbone = {};
} |
Current version of the library. | Gbone.VERSION = '0.1.0';
|
Require Backbone. | if (!root.Backbone && (typeof require !== 'undefined'))
root.Backbone = require('backbone');
|
Require Underscore. | if (!root._ && (typeof require !== 'undefined'))
root._ = require('underscore')._;
|
MixinsEach mixin operates on an object's | |
The observer mixin contains behavior for binding to events in a fashion
that can be cleaned up later.
| observer = function (obj) { |
On top of binding | obj.bindTo = function (source, event, callback) {
source.bind(event, callback, this);
this.bindings = this.bindings || [];
this.bindings.push({ source: source, event: event, callback: callback });
}; |
Unbind all events. | obj.unbindFromAll = function () {
_.each(this.bindings, function (binding) {
binding.source.unbind(binding.event, binding.callback);
});
this.bindings = [];
};
};
|
The cleanup mixin contains set of helpers for adding/managing immediate child Views, cleaning up and housekeeping. Used with the observer mixin. Maintains an internal array of child Views. | cleanup = function (obj) {
|
Cleanup child Views, DOM events, Model/Collection events and events from this View. | obj.cleanup = function () {
this.unbind();
if (this.unbindFromAll) this.unbindFromAll();
this._cleanupChildren();
this.removeFromParent();
this.remove();
};
|
Append a child View into the given | obj.appendChild = function (view) {
this._addChild(view);
$(this.el).append(view.el);
}; |
Append a child View into a specific | obj.appendChildInto = function (view, container) {
this._addChild(view);
this.$(container).append(view.el);
};
obj._addChild = function (view) {
this.children = this.children || [];
this.children.push(view);
view.parent = this;
};
obj._cleanupChildren = function () {
_.each(this.children, function (view) {
if (view.cleanup) view.cleanup();
});
}; |
Remove this View from its parent View. | obj.removeFromParent = function () {
this.parent && this.parent.removeChild(this);
}; |
Remove the given child View | obj.removeChild = function (view) {
var index = _.indexOf(this.children, view);
this.children.splice(index, 1);
};
};
|
The transitions mixin contains functions and objects needed for
doing transition effects when Panel Views are activated/deactivated.
There are default transitions provided, but you can add your own
by using the | transitions = function (obj) {
|
Animation options for the default transitions. | var effectOptions = {
duration: 450,
easing: 'cubic-bezier(.25, .1, .25, 1)'
},
|
Helper function to handle the transitions for the default effects. | handleTransition = function (that, anim, options, callback) {
var l = that.transitionBindings.length, |
Helper function to animate a single element. | animate = function (container, ops, index) {
if (!$.fn[anim]) throw new Error('$.fn.' + anim + ' is not available');
if ($.fn.gfx) { |
Using GFX. | container[anim](ops); |
Only call the callback function if this is the last animation. | if (index === l-1) container.queueNext(callback);
} else { |
Using Zepto-GFX. Only call the callback function if this is the last animation. | (index === l-1) ? container[anim](ops, callback) : container[anim](ops);
}
};
|
Animate each element. | _.each(that.transitionBindings, function(elm, index) {
var container = that.$(elm);
if (container.length === 0)
throw new Error('The container element to animate is not \
availabe in this view.');
animate(container, options, index);
});
};
|
The default element(s) in the Panel to animate for the transitions.
An array of elements/selectors of the form
| obj.transitionBindings = ['.container'];
|
Transition effects for activation. | obj.effects = {
|
Slide in from the left. | left: function (callback) {
var $el = $(this.el),
options = _.extend({}, effectOptions, {direction: 'left'})
handleTransition(this, 'gfxSlideIn', options, callback);
}, |
Slide in from the right. | right: function (callback) {
var $el = $(this.el),
options = _.extend({}, effectOptions, {direction: 'right'});
handleTransition(this, 'gfxSlideIn', options, callback);
}
};
|
Transition effects for deactivation. | obj.reverseEffects = { |
Reverse transition for the slide in from left: slide out to the right. | left: function (callback) {
var $el = $(this.el),
options = _.extend({}, effectOptions, {direction: 'right'});
handleTransition(this, 'gfxSlideOut', options, callback);
},
|
Reverse transition for the slide in from right: slide out to the left. | right: function (callback) {
var $el = $(this.el),
options = _.extend({}, effectOptions, {direction: 'left'});
handleTransition(this, 'gfxSlideOut', options, callback);
}
};
|
Add a new transition. The | obj.addTransition = function (transition) {
if (!transition.effects) throw new Error('transition.effects is not set.');
if (!transition.reverseEffects) throw new Error('transition.reverseEffects \
is not set.');
_.extend(this.effects, transition.effects);
_.extend(this.reverseEffects, transition.reverseEffects);
};
};
|
The state mixin contains methods used by the Manager to handle activating/deactivating the Views it manages. | state = function (obj) {
obj.active = function () { |
Add in | Array.prototype.unshift.call(arguments, 'active');
this.trigger.apply(this, arguments);
};
obj.isActive = function () {
return $(this.el).hasClass('active');
};
obj._activate = function (params) {
var that = this;
$(this.el).addClass('active').show(); |
Once the transition is completed (if any), trigger the activated event. | if (params && params.trans && this.effects &&
this.effects[params.trans]) {
this.effects[params.trans].call(this, function() {
that.trigger('activated');
});
} else {
this.trigger('activated');
}
};
obj._deactivate = function (params) {
if (!this.isActive()) return;
var that = this,
callback = function () {
$(that.el).removeClass('active').hide();
that.trigger('deactivated');
};
if (params && params.trans && this.reverseEffects &&
this.reverseEffects[params.trans]) {
this.reverseEffects[params.trans].call(this, callback);
} else {
callback();
}
};
};
|
Manager | |
The Manager class is a state machine for managing Views. | Manager = function () {
this._setup.apply(this, arguments);
};
_.extend(Manager.prototype, Backbone.Events, {
_setup: function () {
this.views = [];
this.bind('change', this._change, this);
this.add.apply(this, arguments);
}, |
Add one or more Views.
| add: function () {
_.each(Array.prototype.slice.call(arguments), function (view) {
this.addOne(view);
}, this);
}, |
Add a View. | addOne: function (view) {
view.bind('active', function () {
Array.prototype.unshift.call(arguments, view);
Array.prototype.unshift.call(arguments, 'change');
this.trigger.apply(this, arguments);
}, this);
this.views.push(view);
},
|
Deactivate all managed Views. | deactivateAll: function () {
Array.prototype.unshift.call(arguments, false);
Array.prototype.unshift.call(arguments, 'change');
this.trigger.apply(this, arguments);
},
|
For the View passed in - | _change: function (current) {
var args = Array.prototype.slice.call(arguments, 1);
_.each(this.views, function (view) {
if (view === current) {
view._activate.apply(view, args);
} else {
view._deactivate.apply(view, args);
}
}, this);
}
});
|
Gbone.Stage | |
A Stage is a essentially a View that covers the
entire viewport. It has a default | Gbone.Stage = function (options) {
this._setup(options, 'stage');
Backbone.View.call(this, options);
};
_.extend(Gbone.Stage.prototype,
Backbone.View.prototype, {
|
The default html | skeleton: _.template('<header></header><article class="viewport"> \
</article><footer></footer>'),
|
Add Panel(s) to this Stage. | add: function () {
this._manager = this._manager || new Manager();
this._manager.add.apply(this._manager, arguments);
this._append.apply(this, arguments);
},
|
Retrieve a Panel with a name of | getPanel: function(name) { |
This Stage doesn't have any Panels. | if (!this._manager) return null;
var views = this._manager.views;
return _.find(this._manager.views, function (panel) {
return panel.name === name;
});
}, |
Append Panel(s) to this Stage. | _append: function () {
if (this.$('.viewport').length === 0) {
throw new Error('The Stage must have an element with \
class \'viewport\' that will be used to append the Panels to.');
}
_.each(Array.prototype.slice.call(arguments), function (panel) {
if (panel.stage !== this) panel.stage = this;
this.appendChildInto(panel, '.viewport');
}, this);
},
|
Called in the constructor during initialization. | _setup: function (options) {
_.bindAll(this);
options.el ? this.el = options.el : this._ensureElement();
|
If a | this.name = options.name || _.uniqueId('stage-');
$(this.el).addClass('stage').html(this.skeleton());
|
Create a Router if one is not provided. | this.router = options.router || Backbone.Router.extend();
}
});
observer(Gbone.Stage.prototype);
cleanup(Gbone.Stage.prototype);
|
Gbone.Panel | |
Similar to a Stage, a Panel is just a View with transition support whenever it is activated/deactivated. A Panel's parent is a Stage and that Stage is responsible for managing and activating/deactivating the Panel. Usually only one Panel is shown in the application at one time. | Gbone.Panel = function (options) {
this._setup(options, 'panel');
Backbone.View.call(this, options);
};
_.extend(Gbone.Panel.prototype,
Backbone.View.prototype, {
|
The default html | skeleton: _.template('<div class="container"><header></header><article></article></div>'), |
Setup the routing for the Panel.
The route for a Panel is as follows: | routePanel: function (callback) {
if (this.stage) {
this.stage.router.route(this.stage.name + '/' + this.name + '/trans-:trans', this.name, callback);
this.stage.router.route(this.stage.name + '/' + this.name, this.name, callback);
} else {
throw new Error('A Stage for this Panel is not available.');
}
},
|
Called in the constructor during initialization. | _setup: function (options) {
_.bindAll(this);
options.el ? this.el = options.el : this._ensureElement(); |
If a | this.name = options.name || _.uniqueId('panel-');
$(this.el).addClass('panel').html(this.skeleton());
if (options.stage) {
this.stage = options.stage;
this.stage.add(this);
}
}
});
observer(Gbone.Panel.prototype);
state(Gbone.Panel.prototype);
cleanup(Gbone.Panel.prototype);
transitions(Gbone.Panel.prototype);
Gbone.Stage.extend = Gbone.Panel.extend = Backbone.View.extend;
}).call(this, this.Zepto || this.jQuery);
|