Skip to content

Instantly share code, notes, and snippets.

@tsouk
Created February 27, 2012 11:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tsouk/1923098 to your computer and use it in GitHub Desktop.
Save tsouk/1923098 to your computer and use it in GitHub Desktop.
(function () {
var $,
clockLoaded = false,
_environment = null,
_flagpoles = {},
labels = {
searchSuggestion: "Search"
},
features = {
/**
@private
@name updateNoImagesState
@function
@description (ported) Determines whether Blq should be in no-images mode or not
*/
updateNoImagesState: function () {
// TODO refactor after port from blq_core
// consider making descision a public interface.
var x = $('#blq-search-btn')[0];
if (x.currentStyle){
var y = x.currentStyle['backgroundImage'];
} else if (window.getComputedStyle) {
var y = document.defaultView.getComputedStyle(x,null).getPropertyValue('background-image');
}
if (y.indexOf("search_icon.png") == -1){
return false;
} else {
$('#blq-mast-home').removeClass('blq-no-images');
return true;
}
},
/**
@private
@name searchTheBBCSearchHint
@function
@description Adds the 'Search The BBC' text to the search box
*/
searchTheBBCSearchHint: function () {
var $searchInput = $("#blq-search-q"),
$searchForm = $("#blq-search-form");
$searchInput.bind('focus', function () {
if ($searchInput.val() == labels.searchSuggestion) {
$searchInput.val('');
}
});
$searchInput.bind('blur', function () {
if (! $searchInput.val()) {
$searchInput.val(labels.searchSuggestion);
}
});
$searchForm.bind('submit', function(e) {
if ( $searchInput.val() == labels.searchSuggestion) {
$searchInput.val('');
}
});
// Only adds searchSuggestion value if input field is not
// active element (has focus)
if ($searchInput.val() == ''
&& $(document.activeElement) !== $searchInput) {
$searchInput.val(labels.searchSuggestion);
}
},
/**
@private
@name mapPublicApi
@function
@description Creates the public window.blq api
*/
mapPublicApi: function () {
if (! window['blqOnDomReady']) {
window.blqOnDomReady = $(document).ready;
}
},
/**
* Adds the autosuggest.
*
* Will be called when search field is first focused,
* and only loads Autosuggest widget at that point.
*
* @function
* @private
*/
addAutosuggest: function() {
$('#blq-search-q').attr('autocomplete', 'off').bind('focus', function(e) {
$(this).unbind(e);
require(['gelui-1'], function(gelui) {
var autoSuggest,
$input = $('#blq-search-q'),
$searchBtn = $('#blq-search-btn'),
maxListLength = 6,
isRtl = $('#blq-container-inner').hasClass('blq-rtl'),
hasPromo = $('#blq-mast').hasClass('blq-has-promo'),
$searchMask,
usersQuery = '',
$navSearch = $('#blq-nav-search'),
variant = $('#blq-container-outer').attr('class').match(/blq-(\w+)-(domestic|worldwide)/) || [],
lang = $('#blq-container').attr('class').match(/blq-lang-\w+(-\w+)?/) || [],
$scopeElement = $('#blq-nav-search input[name=scope]'),
scope = '';
/* if the input element exists, get the scope and encode it or set a default */
scope = ($scopeElement[0]) ? $scopeElement.serialize() : 'scope=all';
blq.autoSuggest = autoSuggest = new gelui.AutoSuggest($input, 'http://search.bbc.co.uk/suggest?&' + scope + '&format=blq-1&q={input}');
/* Add the old attributes for compatibility*/
autoSuggest.overlay.$wrapper.attr('id', 'blq-autosuggest')
.addClass('blq-rst')
.addClass(variant[0])
.addClass(lang[0]);
autoSuggest.overlay.signals.hide.add(function() {
blq.masthead.on.dropdownClosed.dispatch({self: autoSuggest});
});
/* If the page is in RTL language make adjustments*/
if(isRtl){
autoSuggest.overlay.$wrapper.addClass('blq-rtl');
}
if (hasPromo) {
autoSuggest.overlay.$wrapper.addClass('blq-has-promo');
// Create element to mask out the border under the search area.
$searchMask = $('<span id="blq-search-mask"></span>').appendTo($navSearch);
// Show and hide the mask over the masthead border.
autoSuggest.overlay.signals.show.add(function() {
$searchMask.show();
});
autoSuggest.overlay.signals.hide.add(function() {
$searchMask.hide();
});
}
function positionAutosuggest() {
return autoSuggest.overlay.nextTo($navSearch, 'bottom', 'left');
}
autoSuggest.signals.displayitems.removeAll();
autoSuggest.signals.displayitems.add(function(){
$searchBtn.removeClass('loading');
window.blq.masthead.on.dropdownOpened.dispatch({self: autoSuggest});
// close any open panels
var activePanel = blq.mastheadPanels.getActive();
if (activePanel) {
activePanel.activate();
}
$(window).resize(positionAutosuggest);
positionAutosuggest().show();
});
autoSuggest.signals.inputchanged.add(function(){
$searchBtn.addClass('loading');
usersQuery = $input.val();
});
autoSuggest.signals.dataready.removeAll();
autoSuggest.signals.dataready.add(function(e){
var that = this,
suggestions = e.data;
that.$suggestionList.empty();
for(var i = 0; i < suggestions[1].length; i++) {
that.signals.appenditem.dispatch({
query: suggestions[0],
item : suggestions[1][i].title,
suggid : suggestions[1][i].id
});
}
that.$suggestionList.children().bind({
mouseenter: function() {
that.signals.itemhighlighted.dispatch({
item : $(this)[0]
});
},
click: function() {
that.signals.itemselected.dispatch({
item : $(this)[0],
id : suggestions[1][$(this).index()]['id']
});
}
});
that.signals.displayitems.dispatch();
}, autoSuggest);
autoSuggest.signals.itemselected.removeAll();
autoSuggest.signals.itemselected.add(function(e){
var that = this;
if($(e.item).text() != ''){
this.$input.val($(e.item)[0].suggestion);
var idInput = $('#suggid');
if (!idInput.length){
idInput = $('<input type="hidden" name="suggid" id="suggid" />');
this.$input.closest('form').prepend(idInput);
}
idInput.val($(e.item)[0].suggid);
}
this.$input.closest('form').trigger('submit');
}, autoSuggest);
/* Limit the results to 6 and shorten the long items*/
autoSuggest.signals.appenditem.removeAll();
autoSuggest.signals.appenditem.add(function(e){
if(this.$suggestionList.children().length >= maxListLength){
return false;
}
var suggestion = e.item,
listHtml = suggestion,
suggid = e.suggid,
query = e.query,
maxLength = 28,
trimmedLength = 25,
trimmedString = '',
substrings = [],
listTitle = '';
substrings.push(listHtml.toLowerCase().indexOf(query.toLowerCase()));
substrings.push(substrings[0] + query.length);
if( listHtml.length > maxLength ) {
listTitle = 'title="' + listHtml.replace('"', '&quot;') + '"';
listHtml = listHtml.substring(0, trimmedLength);
trimmedString = '&hellip;';
}
if(-1 !== substrings[0]) {
listHtml = listHtml.substring(0, substrings[0]) + '<em>' + listHtml.substring(substrings[0], substrings[1]) + '</em>' + listHtml.substring(substrings[1]);
}
listHtml = $('<li role="option" ' + listTitle + '>' + listHtml + trimmedString + '</li>');
listHtml[0].suggestion = suggestion;
listHtml[0].suggid = suggid;
this.$suggestionList.append(listHtml);
}, autoSuggest);
autoSuggest.signals.itemhighlighted.add(function(e){
var keyPressed = e.keyPressed,
keyCode = this.keyCode,
$item = $(e.item),
$input = this.$input;
if(keyPressed && (keyPressed === keyCode.DOWNARROW || keyPressed === keyCode.UPARROW)) {
if($item.length > 0) {
$input.val($item[0].suggestion);
} else {
$input.val(usersQuery);
}
}
}, autoSuggest);
});
});
}, // addAutosuggest
setARIAValues: function () {
$('#blq-search-form') .attr('role', 'search');
$('#blq-nav') .attr('role', 'navigation');
$('#blq-foot') .attr('role', 'contentinfo');
},
/**
@private
@name defaultIStatsTracking
@function
@description Adds the default iStats tracking to Barlesque.
*/
defaultIStatsTracking: function() {
if (typeof require !== 'undefined') {
require(['istats-1'], function(istats) {
require.ready(function() {
// Set up tracking for masthead, footer and more panel links.
istats.track('internal', {
region: document.getElementById('blq-blocks'),
linkLocation: 'blq-mast-home-gvl3_5'
});
istats.track('internal', {
region: document.getElementById('blq-nav-main'),
linkLocation: 'blq-nav-main-gvl3_5'
});
istats.track('internal', {
region: document.getElementById('blq-nav-links-inner'),
linkLocation: 'blq-nav-links-gvl3_5'
});
istats.track('internal', {
region: document.getElementById('blq-foot'),
linkLocation: 'blq-foot-gvl3_5'
});
// Don't track opening of the more panel.
istats.addNoTrack(document.getElementById('blq-nav-more').getElementsByTagName('a')[0]);
});
});
}
},
/**
@private
@name mastheadFocusBehaviour
@function
@description Handles adding / removing focus class on masthead.
*/
mastheadFocusBehaviour: function() {
var $masthead = $('#blq-masthead'),
$mastheadBackground = $('#blq-mast-background'),
focusClass = 'blq-masthead-focus',
focusStatus = false,
hoverStatus = false,
timeout,
fadeOut,
fadeIn;
// Transition masthead background to white only if the default background is not white.
// animateMasthead option always sets background to white.
if (!$masthead.hasClass('blq-mast-bg-white')) {
return {
fadeOut : function(e) {
if (e === undefined || e.type === 'mouseleave') {
hoverStatus = false;
}
else if (e.type === 'focusout') {
focusStatus = false;
};
clearTimeout(timeout);
if (!focusStatus && !hoverStatus && (!blq.mastheadPanels || !blq.mastheadPanels.getActive())) {
timeout = setTimeout(function() {
if (!$('#id-status-nav').is(':visible')) {
$masthead.removeClass(focusClass);
};
}, 250);
}
},
fadeIn : function(e) {
if (e === undefined || e.type === 'mouseenter') {
hoverStatus = true;
}
else if (e.type === 'focusin') {
focusStatus = true;
};
clearTimeout(timeout);
timeout = setTimeout(function() {
$masthead.addClass(focusClass);
}, 150);
}
};
$masthead.removeClass(focusClass);
//$masthead.mouseleave(fadeOut);
//$masthead.mouseenter(fadeIn);
//$masthead.delegate('a, input, button', 'blur', fadeOut);
//$masthead.delegate('a, input, button', 'focus', fadeIn);
// Click event is required in order to prevent masthead fading out on click.
//$masthead.bind('click', fadeIn);
if ($('#id-status').length > 0) { // Only when ID Status Bar is enabled.
$(document).bind('click', function(e) {
if ($(e.target).parents('#blq-masthead').length === 0) {
fadeOut();
}
});
};
}
},
/**
@private
@name moveMorePanel
@function
@description Move the More Panel below the Masthead and hide by default.
*/
initMorePanel: function() {
var $morePanel = $('#blq-nav-more-links'),
$morePanelLinksInner = $('#blq-nav-links-inner'),
$masthead = $('#blq-masthead'),
$panel = $('<div id="blq-panel" class="blq-rst"></div>');
$masthead.append($panel);
// Slide panel up initially.
$panel.slideUp(0);
$panel.append($morePanel);
$morePanel.addClass('blq-masthead-container');
initMastheadPanels();
},
/**
@private
@name loadClock
@function
@description
*/
loadClock: function() {
var $moreLink = $('#blq-nav-more');
function handleFocus () {
if(!clockLoaded) {
// Set clockLoaded outside of require statement to prevent race conditions.
clockLoaded = true;
require(['clock-1'], function(Clock) {
Clock.init('#blq-more-panel-clock');
});
}
};
$moreLink.bind({
mouseenter: handleFocus,
focusin: handleFocus
});
}
};
/**
@private
@name idStatusBarClickBehaviour
@function
@description Adds click behaviour to ID status bar.
*/
function idStatusBarClickBehaviour(morePanel) {
var idLink = $('.id-in a');
// "To safely detect a mouse button you have to use the mousedown or mouseup events." (http://www.quirksmode.org/js/events_properties.html#button)
idLink.bind('mouseup keydown', function(e) {
var activePanel = blq.mastheadPanels.getActive();
// Only on left click or enter.
if (e.which === 1 || e.which === 13) {
if (activePanel) {
// Close any active panels.
activePanel.activate();
};
};
});
}
/**
@private
@name initMastheadPanels
@function
@description Handles showing and hiding of the masthead panels
*/
function initMastheadPanels() {
require(['barlesque/panelset-1'], function(PanelSet) {
var $panel = $('#blq-panel'),
panelDefinitions = [
{ name: 'more', content: $('#blq-nav-more-links'), control: $('#blq-nav-more') }
],
isAnimating = false,
mastheadPanels,
morePanel;
// blq.mastheadPanels is used by autosuggest to ensure that no panel is open when the autosuggest opens
mastheadPanels = blq.mastheadPanels = new PanelSet(
panelDefinitions,
{
/** Handle animations of the panels opening and closing
@param {jQuery.Event} [e] - The dom event that initiated the activation.
*/
onActivate: function(e) {
if (e) { // don't follow links
e.preventDefault();
}
if (isAnimating) {
return false; // do nothing while an animation is happening
}
isAnimating = true; // start ignoring clicks
var currentlyActivePanel = this.panelSet.getActive(),
panelToActivate = this;
if (!currentlyActivePanel) { // just show the new panel
blq.masthead.on.dropdownOpened.dispatch(panelToActivate);
}
if (blq.autoSuggest && blq.autoSuggest.overlay.isShown()) { // hide autosuggest menu
blq.autoSuggest.overlay.hide();
}
if (!currentlyActivePanel) { // just show the new panel
panelToActivate.content.removeClass('blq-panel-inactive');
$panel.slideDown(400, function() {
isAnimating = false;
linkTrackMorePanel();
});
}
else if (currentlyActivePanel === panelToActivate) { // just hide the currently active panel
blq.masthead.on.dropdownClosed.dispatch(panelToActivate);
// hide the currently active panel
$panel.slideUp(400, function() {
// HACK!!!
// at the end of slideUp, jQuery sets the display property of the panel to 'none',
// IE8 does not correctly reflow the document, and therefore doesn't recalculate
// the collapse of adjacent margins.
// setting a className forces a reflow in IE8.
var reflowElement = document.getElementById('blq-masthead');
reflowElement.className = reflowElement.className;
panelToActivate.content.addClass('blq-panel-inactive');
isAnimating = false;
});
}
else { // hide the currently active panel and show the new panel
currentlyActivePanel.content.addClass('blq-panel-inactive');
panelToActivate.content.removeClass('blq-panel-inactive');
isAnimating = false;
}
}
}
);
morePanel = mastheadPanels.getPanels('more')[0];
morePanelKeyboardBehaviour(morePanel);
idStatusBarClickBehaviour(morePanel);
});
}
/**
@private
@name morePanelKeyboardBehaviour
@function
@param {Panel} morePanel
@description Handles keyboard behaviours of the More Panel
*/
function morePanelKeyboardBehaviour(morePanel) {
// set up keyboard handlers for the more panel control
morePanel.control.keydown(function(e) {
if (e.keyCode == '13') { // return key pressed
// if the more panel is already open
if (morePanel.panelSet.getActive() === morePanel) {
// throw focus to the first link in the more panel content
$('#blq-nav-more-links a')[0].focus();
e.preventDefault();
}
}
else if (e.keyCode == '27') { // esc key pressed
// if the more panel is already open
if (morePanel.panelSet.getActive() === morePanel) {
// close the more panel
morePanel.activate();
// throw focus to the first link in the more panel control
$(morePanel.control).find('a')[0].focus();
}
}
});
// set up keyboard handlers for the more panel content
morePanel.content.keydown(function(e) {
if (e.keyCode == '27') { // esc key pressed
// if the more panel is already open (which it very likely is if you are here)
if (morePanel.panelSet.getActive() === morePanel) {
morePanel.activate(); // close the more panel
}
// throw focus to the first link in the more panel control
$(morePanel.control).find('a')[0].focus();
}
});
}
/**
@private
@name linkTrackMorePanel
@function
@description Fire an iStats link track event on More Panel
*/
function linkTrackMorePanel () {
require(['istats-1'], function(istats) {
$(function() {
istats.log('click', $('#blq-nav-more'));
});
});
}
/**
@private
@name browserIsCapable
@function
@description (ported) Checks whether document.getElementById & document.getElementsByTagName methods are available
*/
function browserIsCapable () {
return (document.getElementById && document.getElementsByTagName);
}
/**
@public
@name blq.addGoTrack
@function
@description DEPRECATED API. Remains available for legacy code, will be removed in a later release.
*/
function addGoTrack (el, opts) {}
addGoTrack.isStub = true;
/**
* @public
* @name blq.disableFeature
* @function
* @description Allows a page to opt out of a Barlesque feature (e.g. the hint in the Search box)
*/
function disableFeature (featureName) {
if (availableFeatures[featureName]) {
delete availableFeatures[featureName];
}
}
/**
* @public
* @description Returns the environment Barlesque was rendered from.
*/
function environment () {
return _environment;
}
/**
* @public
* @description Sets the environment Barlesque should consider itself rendered from.
* @param {string} env The environment, normally one of 'live', 'stage', 'test', 'int', 'ci' or 'sandbox'.
*/
function setEnvironment (env) {
_environment = env;
}
/**
* @public
* @description Returns the value of the flagpole.
*/
function flagpole (name) {
return _flagpoles[name];
}
/**
* @public
* @description Sets the value of a flagpole.
* @param {string} name The name of the flagpole.
* @param {string} value The value of the flagpole.
*/
function setFlagpole (name, value) {
_flagpoles[name] = value;
}
/**
* @public
* @name setLabel
* @function
* @description Allows setting of default labels (such as 'Search The BBC' search box hint).
* @param {key} Label key
* @param {value} The new value
*/
function setLabel (key, value) {
labels[key] = value;
}
/**
@private
@name setCaretPositionInTextbox
@function
@description Sets the position of a caret in targetElement.
@param {DOMElement} targetElement The text input DOM element.
@param {number} position The new position of the caret.
*/
function setCaretPositionInTextbox (targetElement, position) {
if (targetElement.createTextRange) {
var range = targetElement.createTextRange();
range.moveStart('character', position);
range.moveEnd('character', position - targetElement.value.length);
range.select();
}
else {
if (targetElement.selectionStart) {
targetElement.focus();
targetElement.setSelectionRange(position, position);
}
else
targetElement.focus();
}
}
function initEventManager(cb) {
// start loading the signals library we will need to manage the events
require(['jssignals-1'], function(signals) {
var callbacks = {
dropdownOpened: blq.masthead.on.dropdownOpened._cachedCallbacks,
dropdownClosed: blq.masthead.on.dropdownClosed._cachedCallbacks,
};
blq.masthead.on.dropdownOpened = new signals.Signal();
blq.masthead.on.dropdownClosed = new signals.Signal();
for (var callback in callbacks) {
for (var i = 0, len = callbacks[callback].length; i < len; i++) {
blq.masthead.on[callback].add( callbacks[callback][i] );
}
}
blq.masthead.on.dropdownOpened.add(handleDropdownOpened);
blq.masthead.on.dropdownClosed.add(handleDropdownClosed);
//cb();
});
var mastheadState = {
isOpen: null,
willOpen: null
},
fader = window.blq.availableFeatures.mastheadFocusBehaviour();
console.log(fader);
/**
@param {object} e - an event object.
@param {object} e.self - a reference to the widget being opened.
*/
function handleDropdownOpened(e) {
mastheadState.willOpen = e.self;
if (mastheadState.isOpen === null) {
// FADE IN
fader.fadeIn();
}
else { // something was already open
//if (wasOpen && typeof wasOpen.close === 'function') {
//wasOpen.close();
//}
}
mastheadState.isOpen = mastheadState.willOpen;
setTimeout(function() {mastheadState.willOpen = null;}, 0);
}
function handleDropdownClosed(e) {
if (mastheadState.willOpen === null) {
// FADE OUT
fader.fadeOut();
mastheadState.isOpen = null;
}
}
}
/**
@private
@name initialise
@function
@description Triggers page behaviours.
*/
function initialise(_$) {
if (browserIsCapable()) {
// set up some shortcuts
$ = _$;
initEventManager();
// Disable clock for initial release with homepage 4 beta
disableFeature('loadClock');
// Autosuggest is only supported for English.
var lang = $('#blq-container-inner').attr('lang') || 'en-GB';
if (lang.substr(0, 2) !== 'en') {
disableFeature('addAutosuggest');
}
// TODO allow users to opt out of features
for (var feature in availableFeatures) {
availableFeatures[feature]();
}
}
}
//moved availableFeatures above window.blq definition to expose it in public API
var availableFeatures = (function () {return features})();
/** @constructor */
function DummySignal(signalName) {
this._cachedCallbacks = [];
this.add = function(handler) {
blq.masthead.on[signalName]._cachedCallbacks.push(handler);
};
this.dispatch = function() {
if (window.console && console.warn) {
console.warn('Ignoring dispatch while jsSignals is not yet loaded.');
}
};
}
/**
@namespace
@public
@name blq
@description The public blq api.
*/
window.blq = {
addGoTrack: addGoTrack,
disableFeature: disableFeature,
environment: environment,
setEnvironment: setEnvironment,
setLabel: setLabel,
flagpole: flagpole,
setFlagpole: setFlagpole,
availableFeatures: availableFeatures,
masthead: {
on: {
dropdownOpened: new DummySignal('dropdownOpened'),
dropdownClosed: new DummySignal('dropdownClosed')
}
}
};
if (window['require']) {
// add the blq-js clas to the <html> element.
// intentionally added as soon as possible.
document.documentElement.className += ' blq-js';
require(['jquery-1'], function($) {
$(function() {
initialise($);
});
});
}
})()
/**
* Demi
*
* Stubbed API that loads the full Demi javascript object.
*
* @author Paul Clifford <paul.clifford@bbc.co.uk>
* @author Richard Hodgson <richard.hodgson@bbc.co.uk>
*/
var demi = (function () {
var loading = false,
callbacks = [],
demiUrl = null;
return {
/**
@private
@description Used in the unit tests to restore the initial state.
*/
_reset: function () {
loading = false;
callbacks = [];
demiUrl = null;
},
/**
@private
@description Called by the demi.js library when it has finished loading, and re-registers all the callbacks provided to getDevice with the real implementation.
*/
_loaded: function () {
while (callbacks.length > 0) {
demi.getDevice(callbacks.shift(), blq.environment());
}
},
/**
@private
@description Insert a script tag at the top of the head element. Replaced in the unit tests to avoid side effects.
@param {string} src The value for the script src attribute.
*/
_addScriptTag: function (src) {
var headTag = document.getElementsByTagName('head')[0];
var scriptTag = document.createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.src = src;
headTag.insertBefore(scriptTag, headTag.firstChild);
},
/**
@private
@description Set the location of the demi.js library. Called by inline JavaScript emitted by the DeMI shared module via an inlineHead method.
@param {string} url The URL for the demi.js library
*/
_setSource: function (url) {
demiUrl = url;
},
/**
@public
@description Request a DeMI device object. Behind the scenes this pulls in the demi.js library, and the callback is re-registered with the real getDevice function (see the _loaded function).
@param {function} callback
*/
getDevice: function (callback) {
callbacks.push(callback);
if (! loading) {
loading = true;
demi._addScriptTag(demiUrl);
}
}
};
})();
/**
@module barlesque/panelset-1
*/
define('barlesque/panelset-1', ['jquery-1'], function($) {
/**
A coordinated collection of Panels.
@constructor
@param {array<object>} panelDefinitions
@example
var nav = new PanelSet(
[
{name:'sport', content: $('blq-nav-sport'), control: $('blq-nav-sport-link')},
{name:'more', content: $('blq-nav-more'), control: $('blq-nav-more-link')}
]
);
*/
function PanelSet(panelDefinitions, opts) {
var panelDef = null,
panel;
opts = opts || {
onActivate: function(e) {}
};
this.onActivate = opts.onActivate;
this._panels = [];
for (var i = 0, len = panelDefinitions.length; i < len; i++) {
panelDef = panelDefinitions[i];
if (panelDef.content && panelDef.content.length > 0) {
panelDef.opts = {
name : panelDef.name,
control : panelDef.control,
panelSet : this
};
panel = new Panel(panelDef.content, panelDef.opts);
this._panels.push( panel );
if (panel.control) {
(function(thatPanel) {
panel.control.click(function(e) {
thatPanel.activate(e);
});
})(panel);
}
}
}
}
/**
Add a Panel.
@param {object} panelDefinition
@example
nav.add( {name:'id', content: $('blq-nav-id'), control: $('blq-nav-id-link')} );
*/
PanelSet.prototype.add = function(panelDefinition) {
// stub
}
/**
Get the Panel or Panels wuth the given name.
@param {string} [panelName]
@returns {Array<Panel>} An array of Panels, either empty if no Panel has
the given name, or an array of all the Panels with the given name. If no
name is given, returns all the panels.
*/
PanelSet.prototype.getPanels = function(panelName) {
var panels = [];
if (arguments.length === 0) {
panels = this._panels;
}
else {
for (var i = 0, len = this._panels.length; i < len; i++) {
panel = this._panels[i];
if (panel.name === panelName) {
panels.push( panel );
}
}
}
return panels;
}
/**
Get the currently showing panel.
@returns {Panel|undefined}
*/
PanelSet.prototype.getActive = function() {
for (var i = 0, len = this._panels.length; i < len; i++) {
panel = this._panels[i];
if (panel.active === true) {
return panel;
}
}
return null;
}
/**
@namespace PanelSet.signals
*/
/**
Fired when a Panel's activate method is called, but before the Panel is active.
Returning false from the signal handler will cancel the activate.
@event PanelSet.signals.activated
@param {object} e
@param {Panel} e.panel The panel to be active.
*/
/**
Fired after a Panel's activate method is complete.
@event PanelSet.signals.active
@param {object} e
@param {Panel} e.panel The panel that was active.
*/
/**
Fired when a Panel's hide method is called, but before the Panel is hidden.
Returning false from the signal handler will cancel the hide.
@event PanelSet.signals.deactivated
@param {object} e
@param {Panel} e.panel The panel to be hidden.
*/
/**
Fired after a Panel's hide method is complete.
@event PanelSet.signals.hidden
@param {object} e
@param {Panel} e.panel The panel that was hidden.
*/
/**
Construct a single Panel.
@private
@constructor
@param {jQuery} content
@param {object} [opts]
@param {string} [opts.name]
@param {jQuery} [opts.control]
@param {PanelSet} [opts.panelSet]
*/
function Panel(content, opts) {
if (!opts) {
opts = {};
}
this.content = content;
this.name = opts.name;
this.control = opts.control;
this.panelSet = opts.panelSet;
this.active = false;
// Make elemet focusable by setting tabindex.
this.control.attr('tabindex', '-1');
}
/** Activate this panel.
@param {jQuery.Event} [e] - The dom event that initiated the activation.
*/
Panel.prototype.activate = function(e) {
var activePanel;
// return exactly false from the handler cancels the default action
if (this.panelSet.onActivate.call(this, e) === false) {
return;
}
// only one panel can be active at a time
activePanel = this.panelSet.getActive();
if (activePanel) {
activePanel.active = false;
if (activePanel !== this) {
this.active = true;
}
}
else {
this.active = true;
}
/**
* @todo Once signals are available, move the following out of Panel and into implementation.
*/
// If we have an active panel, then panel is being closed.
if (activePanel !== null && typeof e !== 'undefined') {
// If initiated from keyboard, link will be focused later.
activePanel.control.focus();
}
if ( e && e.isDefaultPrevented() ) {return false;}
else {return true;}
}
/* exports */
return PanelSet;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment