File: app/widgets/overlay-indicator.js
'use strict';
YUI.add('browser-overlay-indicator', function(Y) {
var sub = Y.Lang.sub,
ns = Y.namespace('juju.widgets.browser');
ns.OverlayIndicator = Y.Base.create('overlay-indicator', Y.Widget, [], {
/**
* Initializer; hides the indicator on creation.
*
* @method initializer
*/
initializer: function(cfg) {
this.hide();
},
/**
* Wire up our events listeners.
*
* @method _addListeners
* @private
*/
_addListeners: function() {
this.on('visibleChange', function(e) {
if (e.newVal === true) {
this.resizeAndReposition();
}
}, this);
},
/**
* To prevent having to force call sites to pass in
* parentNode, we must override YUI's built-in _renderUI
* method.
*
* This is a copy of the YUI method, except for using our
* own parentNode. This is needed so the spinner overlays
* correctly.
*
* @method _renderUI
*/
_renderUI: function() {
var local_parent = this.get('target').get('parentNode');
this._renderBoxClassNames();
this._renderBox(local_parent);
},
/**
* Build the indicator overlay itself.
*
* @method renderUI
*/
renderUI: function() {
var node_html = '<img src={src}>';
var img = Y.Node.create(
sub(node_html, {src: this.get('loading_image')}));
img.set('src', '/juju-ui/assets/images/non-sprites/loading-spinner.gif');
this.get('contentBox').append(img);
},
/**
* Sets up event listeners.
*
* @method bindUI
*/
bindUI: function() {
this._addListeners();
},
/**
* Resize and reposition before we show the overlay,
* to ensure the overlay always matches its target's size/pos.
*
* @method resizeAndReposition
*/
resizeAndReposition: function() {
var boundingBox = this.get('boundingBox');
var target = this.get('target');
var width = target.get('offsetWidth');
var height = target.get('offsetHeight');
boundingBox.set('offsetWidth', width);
boundingBox.set('offsetHeight', height);
// Now do position too.
boundingBox.setXY(target.getXY());
},
/**
* Mark the loading or busy action as in progress,
* and show the overlay.
*
* @method setBusy
*/
setBusy: function() {
this.show();
},
/**
* Method called to clear overlay on success.
*
* @method success
*/
success: function() {
this.hide();
var callback = this.get('success_action');
if (typeof callback === 'function') {
callback.call(this);
}
},
/**
* Method called to clear overlay on error.
*
* @method error
*/
error: function() {
this.hide();
var callback = this.get('error_action');
if (typeof callback === 'function') {
callback.call(this);
}
}
}, {
ATTRS: {
/**
* A reference to the node that we're going to overlay.
*
* @attribute target
* @type {Y.Node}
* @default undefined
*/
target: {},
/**
* Callback to fire upon calling success.
*
* @attribute success_action
* @type {function}
* @default undefined
*/
success_action: {},
/**
* Callback to fire upon calling error.
*
* @attribute error_action
* @type {function}
* @default undefined
*/
error_action: {},
/**
* @attribute loading_image
* @default '/juju-ui/assets/images/loading-spinner.gif'
* @type {string}
*/
loading_image: {
value: '/juju-ui/assets/images/loading-spinner.gif'
}
}
});
/**
* Manage indicator instances and make sure they're destroyed.
*
* @class IndicatorManager
*
*/
ns.IndicatorManager = function() {
this._initIndicatorManager();
};
ns.IndicatorManager.prototype = {
/**
* Init during class initialization. Add _indicators and catch destroy
* event to clean up indicator instances.
*
* @method _initIndicatorManager
* @private
*
*/
_initIndicatorManager: function() {
this._indicators = {};
this.on('destroy', this._destroyIndicators, this);
},
/**
* On destroy, run destroy on any indicator instances we have. This is a
* method so we can hook up and test that it's called vs a closure in the
* init.
*
* @method _destroyIndicators
* @private
*
*/
_destroyIndicators: function() {
Y.Object.each(this._indicators, function(ind, key) {
ind.destroy();
});
},
/**
* Show/setBusy an indicator for a given node. If an indicator is already
* attached then just show it, else create a new indicator instance on the
* node.
*
* @method showIndicator
* @param {Node} node the node to cover with the indicator.
*
*/
showIndicator: function(node) {
var id = node._yuid;
if (this._indicators[id]) {
this._indicators[id].setBusy();
} else {
this._indicators[id] = new ns.OverlayIndicator({
target: node
});
this._indicators[id].render();
this._indicators[id].setBusy();
}
},
/**
* Helper to make sure we can hide an indicator correctly.
*
* @method hideIndicator
* @param {Node} node the container the indicator is currently over.
*
*/
hideIndicator: function(node) {
var id = node._yuid;
if (this._indicators[id]) {
this._indicators[id].success();
}
}
};
}, '0.1.0', { requires: [
'base',
'node-screen',
'widget'
]});