/*(c) Copyright 2008 Licensed under LGPL3. See details below*/
var o = Ext.Container.prototype.lookupComponent;
Ext.override(Ext.Container, {
// Override of our container's method to allow raw Elements to be added.
// This is called in the context of the container.
lookupComponent: function(comp) {
if (comp instanceof Ext.Element) {
return comp;
} else if (comp.nodeType && (comp.nodeType == 1)) {
return Ext.get(comp);
} else {
return o.call(this, comp);
}
}
});
Ext.namespace('Ext.ux.layout');
/**
* Carousel Layout.
*
*
See the following for a demo
*
*
Example Usage
*
* margins: '5 5 5 0',
* title: 'Photos',
* layoutConfig: {
* pagedScroll: true
* },
* layout: 'carousel',
* items: [{
* title: "First",
* style: {
* margin: '5px 5px 5px 5px'
* },
* width: 120,
* html: "<img src='../view/images/thumbs/dance_fever.jpg'>"
* }]
*
*
Forum thread: http://extjs.com/forum/showthread.php?t=47672
* @class Ext.ux.layout.Carousel
* @author Nigel White aka Animal; updated by Jerry Brown, published by SamuraiJack
* @license Ext.ux.layout.Carousel is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
* @version 0.2
*/
Ext.ux.layout.Carousel = Ext.extend(Ext.layout.ContainerLayout, {
constructor: function(config) {
config = config || {};
// Non-chunked, then animation makes no sense.
if (!(config.chunkedScroll || config.pagedScroll)) {
Ext.applyIf(config, {
scrollIncrement: 10,
scrollRepeatInterval: 10,
marginScrollButtons: 12,
animScroll: false
});
}
Ext.ux.layout.Carousel.superclass.constructor.call(this, config);
// Set up animation config depending upon animation requirements.
if (this.chunkedScroll || this.pagedScroll) {
this.scrollRepeatInterval = this.scrollDuration * 1000;
this.scrollAnimationConfig = {
duration: this.chunkedScroll ? this.scrollDuration : this.scrollDuration * 2,
callback: this.updateScrollButtons,
scope: this
};
} else {
this.scrollAnimationConfig = this.animScroll ? {
duration: this.scrollDuration,
callback: this.updateScrollButtons,
scope: this
} : false;
}
// if (Ext.isIE){
// if (!this.defaults) { this.defaults={} }
// if (!this.defaults.bodyStyle) { this.defaults.bodyStyle={} }
// this.defaults.bodyStyle.position='static';
// }
},
/**
* @cfg {Number} marginScrollButtons This is to set aside space for the left and right navigation buttons.
*/
marginScrollButtons : 10,
/**
* @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed (defaults
* to 10, or if {@link #Ext.ux.layout.Carousel-resizeTabs} = true, the calculated tab width).
*/
scrollIncrement : 10,
/**
* @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a scroll button is
* continuously pressed (defaults to 10).
*/
scrollRepeatInterval : 10,
/**
* @cfg {Float} scrollDuration The number of seconds that each scroll animation should last (defaults to .35).
* Only applies when {@link #Ext.ux.layout.Carousel-animScroll} = true.
*/
scrollDuration : .35,
/**
* @cfg {Boolean} animScroll True to animate tab scrolling so that hidden items slide smoothly into view (defaults
* to true). Only applies when {@link #Ext.ux.layout.Carousel-enableTabScroll} = true.
*/
animScroll : true,
/**
* @cfg {Boolean} chunkedScroll To control scrolling more than one image at a time
*/
chunkedScroll: false,
/**
* @cfg {Boolean} pageScroll Scroll full pages
*/
pagedScroll: false,
/**
* @cfg {Boolean} loopCount This does an intro page loop this many times after render
*/
loopCount: 0,
/**
* @cfg {Boolean} loopPictureDelay This controls the delay on each picture during the intro page loop
*/
loopPictureDelay: 5,
// private
monitorResize: true,
/**
* @cfg scrollButtonPosition (split or right) This determines where the scroll bar buttons will be. Matching style elements need to be in place.
*/
scrollButtonPosition: 'right',
loopImages: function(){
if (this.loopCount <= 0){ return; }
var s = this.getScrollTo(1);
if (s) {
s = Math.min(this.getMaxScrollPos(), s);
if(s != this.getScrollPos()) {
this.scrollTo(s);
}else{
return; /*it should not get here*/
}
}else{
this.scrollTo(0);
this.loopCount--;
if (this.loopCount <= 0){ return; }
}
this.loopImages.defer(this.loopPictureDelay * 1000, this);
},
// private
onLayout : function(ct, target){
var cs = ct.items.items, len = cs.length, c, i;
if(!this.scrollWrap){
this.scrollWrap = target.createChild({
cls: 'x-carousel-layout',
cn: [
{
tag: this.scrollElementTag || 'div',
cls: 'x-carousel-scroller',
cn: {
cls: 'x-carousel-body'
}
}, {
cls: 'x-carousel-left-scrollbutton',
style: {
height: '100%'
}
}, {
cls: 'x-carousel-right-scrollbutton',
style: {
height: '100%'
}
}]
});
// Add the class that defines element positions
this.scrollWrap.addClass('x-scroll-button-position-' + this.scrollButtonPosition);
this.scrollLeft = this.scrollWrap.child('.x-carousel-left-scrollbutton');
this.scrollRight = this.scrollWrap.child('.x-carousel-right-scrollbutton');
this.scroller = this.scrollWrap.child('.x-carousel-scroller');
this.strip = this.scroller.child('.x-carousel-body');
if (this.pagedScroll) {
this.scrollLeft.on('click',this.onScrollLeftClick, this);
this.scrollRight.on('click',this.onScrollRightClick, this);
} else {
this.leftRepeater = new Ext.util.ClickRepeater(this.scrollLeft, {
interval : this.pagedScroll ? 10000 : this.scrollRepeatInterval,
delay: 0,
handler: this.onScrollLeftClick,
scope: this
});
this.rightRepeater = new Ext.util.ClickRepeater(this.scrollRight, {
interval : this.pagedScroll ? 10000 : this.scrollRepeatInterval,
delay: 0,
handler: this.onScrollRightClick,
scope: this
});
}
this.renderAll(ct, this.strip);
}
this.scroller.setWidth(this.container.getLayoutTarget().getWidth() - (this.scrollLeft.getWidth() + this.scrollRight.getWidth() + this.marginScrollButtons));
this.updateScrollButtons.defer(10, this);
if (this.loopCount > 0 && (this.chunkedScroll || this.pagedScroll) && !this.startedLoop){
this.startedLoop=true; /*this var is important since onLayout can be called more than once*/
this.loopImages.defer(this.loopPictureDelay * 1000, this);
}
},
// private
renderItem : function(c, position, target){
if(c) {
if (c.initialConfig) {
if (c.rendered){
if(typeof position == 'number'){
position = target.dom.childNodes[position];
}
target.dom.insertBefore(c.getEl().dom, position || null);
} else {
c.render(target, position);
}
} else if (c instanceof Ext.Element) {
c.el = c;
if(typeof position == 'number'){
position = target.dom.childNodes[position];
}
target.dom.insertBefore(c.dom, position || null);
}
}
c.el.addClass('x-carousel-item');
},
// private
onResize : function(c, position, target){
Ext.ux.layout.Carousel.superclass.onResize.apply(this, arguments);
if (Ext.isIE) {
this.scrollLeft.setHeight(this.scroller.getHeight());
this.scrollRight.setHeight(this.scroller.getHeight());
}
if(!this.strip) return;
this.setItemsEdges();
// If width increase has introduced spare space to the right, close it up.
var r = this.getMaxScrollPos();
if (this.getScrollPos() > r) {
this.scrollTo(r);
}
},
setItemsEdges:function(){
// Register strip-relative left/right edges for easy chunked scrolling
var stripLeft = this.strip.getLeft();
var t = this.container.items.items;
var lt = t.length;
for (var i = 0; i < lt; i++) {
var c = t[i];
var e = c.el;
var b = e.getBox();
var l = b.x - stripLeft;
var r = b.right - stripLeft;
// "left" is the leftmost visible pixel.
// "leftAnchor" is the postition to scroll to to bring the
// item correctly into view with half the inter-item gap visible.
// Same principle applies to "right"
c.edges = {
left: l,
leftAnchor: l,
right: r,
rightAnchor: r
};
// Adjust anchors to be halfway between items.
if (i == 0) {
e.setStyle({'margin-left': '0px'});
} else {
if (i == t.length - 1) {
e.setStyle({'margin-right': '0px'});
}
var prev = t[i - 1];
var halfGap = ((l - prev.edges.right) / 2);
prev.edges.rightAnchor += halfGap;
c.edges.leftAnchor -= halfGap;
}
}
// Work out average item width
this.itemWidth = t[lt - 1].edges.rightAnchor / lt;
},
getNextOnLeft: function() {
var t = this.container.items.items;
if (t.length) {
for (var i = t.length - 1; i > -1; i--) {
if (t[i].edges.left < this.getScrollPos()) {
return t[i];
}
}
}
return null;
},
getNextOnRight: function() {
var t = this.container.items.items;
if (t.length) {
var scrollRight = this.scroller.dom.scrollLeft + this.getClientWidth();
for (var i = 0, l = t.length; i < l; i++) {
if (t[i].edges.right > scrollRight) {
return t[i];
}
}
}
return null;
},
/**
* Called when a click event is fired by the right scroll button.
* May also be used to programatically trigger a right scroll event.
*/
onScrollRightClick : function(){
this.loopCount=0;
var s = this.getScrollTo(1);
if (s) {
s = Math.min(this.getMaxScrollPos(), s);
if(s != this.getScrollPos()) {
this.scrollTo(s);
}
}
},
/**
* Called when a click event is fired by the right scroll button.
* May also be used to programatically trigger a left scroll event.
*/
onScrollLeftClick : function(){
this.loopCount=0;
var s = Math.max(0, this.getScrollTo(-1));
if(s != this.getScrollPos()) {
this.scrollTo(s);
}
},
// private
scrollTo : function(pos){
// Calculate scroll duration based on how far we have to scroll.
if (this.scrollAnimationConfig) {
var distance = Math.abs(this.getScrollPos() - pos);
this.scrollAnimationConfig.duration = this.scrollDuration * (distance / this.itemWidth);
}
this.scroller.scrollTo('left', pos, this.scrollAnimationConfig);
// Scroll animation will have called this in its callback.
if(!this.scrollAnimationConfig){
this.updateScrollButtons();
}
},
// private
updateScrollButtons : function(){
var pos = this.getScrollPos();
this.scrollLeft[(pos == 0) ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
this.scrollRight[(pos >= this.getMaxScrollPos()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
},
getScrollWidth : function(){
var t = this.container.items.items;
if (t.length && ! t[0].edges){
this.setItemsEdges();
}
return t.length ? t[t.length - 1].edges.rightAnchor : 0;
},
// private
getScrollPos : function(){
return this.scroller.dom.scrollLeft || 0;
},
getMaxScrollPos: function() {
return this.getScrollWidth() - this.getClientWidth();
},
// private
getClientWidth : function(){
return this.scroller.dom.clientWidth || 0;
},
// private
getScrollTo : function(dir){
var pos = this.getScrollPos();
if (this.chunkedScroll || this.pagedScroll) {
// -1 for left, 1 for right
if (dir == -1) {
var nextLeft = this.getNextOnLeft();
if (nextLeft) {
if (this.pagedScroll) {
return nextLeft.edges.rightAnchor - this.getClientWidth();
} else {
return nextLeft.edges.leftAnchor;
}
}
} else {
var nextRight = this.getNextOnRight();
if (nextRight) {
if (this.pagedScroll) {
return nextRight.edges.leftAnchor;
} else {
return (nextRight.edges.rightAnchor - this.getClientWidth());
}
}
}
} else {
return (dir == -1) ? pos - this.scrollIncrement : pos + this.scrollIncrement;
}
return 0;
},
// private
isValidParent : function(c, target){
return true;
}
});
Ext.Container.LAYOUTS['carousel'] = Ext.ux.layout.Carousel;