/** * RightJS-UI Billboard v2.2.0 * http://rightjs.org/ui/billboard * * Copyright (C) 2010-2011 Nikolay Nemshilov */ var Billboard = RightJS.Billboard = (function(RightJS) { /** * This module defines the basic widgets constructor * it creates an abstract proxy with the common functionality * which then we reuse and override in the actual widgets * * Copyright (C) 2010-2011 Nikolay Nemshilov */ /** * The widget units constructor * * @param String tag-name or Object methods * @param Object methods * @return Widget wrapper */ function Widget(tag_name, methods) { if (!methods) { methods = tag_name; tag_name = 'DIV'; } /** * An Abstract Widget Unit * * Copyright (C) 2010 Nikolay Nemshilov */ var AbstractWidget = new RightJS.Class(RightJS.Element.Wrappers[tag_name] || RightJS.Element, { /** * The common constructor * * @param Object options * @param String optional tag name * @return void */ initialize: function(key, options) { this.key = key; var args = [{'class': 'rui-' + key}]; // those two have different constructors if (!(this instanceof RightJS.Input || this instanceof RightJS.Form)) { args.unshift(tag_name); } this.$super.apply(this, args); if (RightJS.isString(options)) { options = RightJS.$(options); } // if the options is another element then // try to dynamically rewrap it with our widget if (options instanceof RightJS.Element) { this._ = options._; if ('$listeners' in options) { options.$listeners = options.$listeners; } options = {}; } this.setOptions(options, this); return (RightJS.Wrapper.Cache[RightJS.$uid(this._)] = this); }, // protected /** * Catches the options * * @param Object user-options * @param Element element with contextual options * @return void */ setOptions: function(options, element) { if (element) { options = RightJS.Object.merge(options, new Function("return "+( element.get('data-'+ this.key) || '{}' ))()); } if (options) { RightJS.Options.setOptions.call(this, RightJS.Object.merge(this.options, options)); } return this; } }); /** * Creating the actual widget class * */ var Klass = new RightJS.Class(AbstractWidget, methods); // creating the widget related shortcuts RightJS.Observer.createShortcuts(Klass.prototype, Klass.EVENTS || []); return Klass; } /** * Billboard initialization script * * Copyright (C) 2010 Nikolay Nemshilov */ var R = RightJS, $ = RightJS.$, $$ = RightJS.$$, $w = RightJS.$w, $E = RightJS.$E, Fx = RightJS.Fx, Class = RightJS.Class, Object = RightJS.Object; /** * Billboards basic class * * Copyright (C) 2010-2011 Nikolay Nemshilov */ var Billboard = new Widget('UL', { extend: { version: '2.2.0', EVENTS: $w('change first last'), Options: { fxName: 'stripe', // visual effect name fxDuration: 'long', // visual effect duration autostart: true, // if it should automatically start rotate things delay: 4000, // delay between item shows loop: true, // loop after reaching the last one showButtons: true, // should it show the next/prev buttons or not prevButton: 'native', // prev item button, 'native' or an ID of your own nextButton: 'native', // next item button, 'native' or an ID of your own stripes: 10, // the number of stripes cssRule: '*.rui-billboard' } }, /** * Basic constructor * * @param mixed an element reference * @return void */ initialize: function(element) { this.$super('billboard', element); // initializing the buttons if (this.options.showButtons) { this.prevButton = this.options.prevButton !== 'native' ? $(this.options.prevButton) : $E('div', {'class': 'rui-billboard-button-prev', 'html': '‹'}).insertTo(this); this.nextButton = this.options.nextButton !== 'native' ? $(this.options.nextButton) : $E('div', {'class': 'rui-billboard-button-next', 'html': '›'}).insertTo(this); this.prevButton.onClick(R(function(event) { event.stop(); this.showPrev(); }).bind(this)); this.nextButton.onClick(R(function(event) { event.stop(); this.showNext(); }).bind(this)); } // catching the 'first'/'last' events this.onChange(function(event) { if (event.item === this.items().first()) { this.fire('first'); } else if (event.item === this.items().last()) { this.fire('last'); } }); // stopping/starting the slideshow with mouse over/out events this.on({ mouseover: function() { this.stop(); }, mouseout: function(event) { if (this.options.autostart && !event.find('.rui-billboard')) { this.start(); } } }); // autostart if (this.options.autostart) { this.start(); } }, /** * Returns the list of items to swap * * @return Array swappable items */ items: function() { return this.children().without(this.prevButton, this.nextButton); }, /** * Show next item on the list * * @return Billboard this */ showNext: function() { var items = this.items(), index = items.indexOf(this.current()) + 1; if (index == items.length && this.options.loop) { index = 0; } return this.current(index); }, /** * Show prev item on the list * * @return Billboard this */ showPrev: function() { var items = this.items(), index = items.indexOf(this.current()) - 1; if (index < 0 && this.options.loop) { index = items.length - 1; } return this.current(index); }, /** * Gets/sets the current item * * @param mixed integer index or a LI element reference * @return Billboard this or current LI element */ current: function(index) { var items = this.items(); if (arguments.length) { if (index instanceof Element) { index = items.indexOf(index); } this.runFx(items[index]); } else { return items.length ? ( items.first('hasClass', 'rui-billboard-current') || items.first().addClass('rui-billboard-current') ) : null; } return this; }, /** * Starts the slide show * * @return Billboard this */ start: function() { this.timer = R(this.showNext).bind(this).periodical(this.options.delay); }, /** * stops the slideshow * * @return Billboard this */ stop: function() { if (this.timer) { this.timer.stop(); } }, /** * Wrapping the event trigger so it always sent the * current element references * * @param String event name * @param Object options * @return Billboard this */ fire: function(name, options) { return this.$super(name, Object.merge({ index: this.items().indexOf(this.current()), item: this.current() }, options)); }, // protected /** * Runs the fx transition * * @param Element new LI element * @return void */ runFx: function(item) { if (item && !this._running) { var Fx = Billboard.Fx[R(this.options.fxName || '').capitalize()]; if (Fx) { new Fx(this).start(this.current(), item); } else { this.current().removeClass('rui-billboard-current'); item.addClass('rui-billboard-current'); } } } }); /** * Basic billboard visual effect * * Copyright (C) 2010 Nikolay Nemshilov */ Billboard.Fx = new Class(Fx, { /** * basic constructor * * @param Billboard billboard * @return void */ initialize: function(billboard) { this.container = $E('div', {'class': 'rui-billboard-fx-container'}); this.$super(billboard, { duration: billboard.options.fxDuration, onStart: function() { billboard._running = true; billboard.insert(this.container); }, onFinish: function() { this.container.remove(); billboard._running = false; billboard.fire('change'); } }); }, /** * Starts an fx on the given item * * @param {Element} old LI element * @param {Element} new LI element * @return void */ prepare: function(old_item, new_item) { old_item.removeClass('rui-billboard-current'); new_item.addClass('rui-billboard-current'); this.clone = old_item.clone(); this.container.update(this.clone); } }); /** * Fade visual effects class * * Copyright (C) 2010 Nikolay Nemshilov */ Billboard.Fx.Fade = new Class(Billboard.Fx, { /** * Starts an fx on the given item * * @param {Element} old LI element * @param {Element} new LI element * @return void */ prepare: function(old_item, new_item) { this.$super(old_item, new_item); }, /** * Rendering the effect * * @param Float delta value * @return void */ render: function(delta) { this.container.setStyle({opacity: 1 - delta}); } }); /** * The slide visual effects class * * Copyright (C) 2010 Nikolay Nemshilov */ Billboard.Fx.Slide = new Class(Billboard.Fx, { /** * overloading the 'prepare' method to add some stuff * to the container depending on which direction do we slide * * @param {Element} old LI element * @param {Element} new LI element * @return void */ prepare: function(old_item, new_item) { this._width = this.element.current().size().x; this._direction = old_item.nextSiblings().include(new_item) ? -1 : 1; this.$super(old_item, new_item); this.clone.setStyle({width: this._width + 'px'}); }, /** * Rendering the Fx * * @param Float delta * @return void */ render: function(delta) { this.clone._.style.left = this._direction * this._width * delta + 'px'; } }); /** * Stripe visual effects class * * Copyright (C) 2010-2011 Nikolay Nemshilov */ Billboard.Fx.Stripe = new Class(Billboard.Fx, { directions: ['down', 'up', 'left', 'right'], /** * Breaking the original element onto sripes in here * * @param {Element} old LI element * @param {Element} new LI element * @return void */ prepare: function(old_item, new_item) { this.$super(old_item, new_item); var length = this.element.options.stripes, width = this.element.items()[0].size().x / length, delay = 100, direction = this.directions.shift(); this.directions.push(direction); this.container.clean(); for (var i=0; i < length; i++) { var stripe = $E('div', { 'class': 'rui-billboard-stripe', 'style': { width: width + 1 + 'px', left: i * width + 'px' } }).insert(old_item.clone().setStyle({ width: width * length + 'px', left: - i * width + 'px' })); this.container.append(stripe); var options = { duration: this.options.duration }; if (direction !== 'right' && i === (length - 1) || (direction === 'right' && i === 0)) { options.onFinish = R(this.finish).bind(this, true); } switch (direction) { case 'up': R(function(stripe, options) { stripe.setHeight(stripe.size().y); stripe.morph({height: '0px'}, options); }).bind(this, stripe, options).delay(i * delay); break; case 'down': stripe.setStyle('top: auto; bottom: 0px'); R(function(stripe, options) { stripe.setHeight(stripe.size().y); stripe.morph({height: '0px'}, options); }).bind(this, stripe, options).delay(i * delay); break; case 'left': R(function(stripe, options) { stripe.morph({width: '0px'}, options); }).bind(this, stripe, options).delay(i * delay); break; case 'right': R(function(stripe, options) { stripe.morph({width: '0px'}, options); }).bind(this, stripe, options).delay((length - i -1) * delay); break; default: this.finish(true); } } }, /** * Stubbing the finish method so it didn't finish prematurely * * @return Fx this */ finish: function(for_sure) { if (for_sure) { this.$super(); } return this; } }); /** * Document level hooks for billboards * * Copyright (C) 2010 Nikolay Nemshilov */ $(document).onReady(function() { $$(Billboard.Options.cssRule).each(function(element) { if (!(element instanceof Billboard)) { element = new Billboard(element); } }); }); var embed_style = document.createElement('style'), embed_rules = document.createTextNode("*.rui-billboard, *.rui-billboard> *{margin:0;padding:0;list-style:none} *.rui-billboard{display:inline-block; *display:inline; *zoom:1;position:relative} *.rui-billboard> *{display:none;width:100%;height:100%} *.rui-billboard> *:first-child, *.rui-billboard> *.rui-billboard-current:first-child{display:block;position:relative} *.rui-billboard> *>img{margin:0;padding:0} *.rui-billboard-current{position:absolute;left:0;top:0;display:block;z-index:999} *.rui-billboard-button-prev, *.rui-billboard-button-next{position:absolute;z-index:99999;left:.25em;top:auto;bottom:.25em;display:block;width:.5em;height:auto;text-align:center;font-size:200%;font-family:Arial;font-weight:bold;padding:0em .5em .2em .5em;background:white;opacity:0;filter:alpha(opacity:0);cursor:pointer;border:.12em solid #888;border-radius:.2em;-moz-border-radius:.2em;-webkit-border-radius:.2em;user-select:none;-moz-user-select:none;-webkit-user-select:none;transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out} *.rui-billboard-button-next{left:auto;right:.25em;text-align:right} *.rui-billboard-button-prev:active{text-indent:-.1em} *.rui-billboard-button-next:active{text-indent:.2em} *.rui-billboard:hover *.rui-billboard-button-prev, *.rui-billboard:hover *.rui-billboard-button-next{opacity:0.4;filter:alpha(opacity:40)} *.rui-billboard:hover *.rui-billboard-button-prev:hover, *.rui-billboard:hover *.rui-billboard-button-next:hover{opacity:0.7;filter:alpha(opacity:70)}.rui-billboard-fx-container{position:absolute;left:0;top:0;display:block;z-index:9999;overflow:hidden}.rui-billboard-fx-container> *{position:absolute;left:0;top:0}.rui-billboard-stripe{overflow:hidden}.rui-billboard-stripe> *{position:relative}"); embed_style.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(embed_style); if(embed_style.styleSheet) { embed_style.styleSheet.cssText = embed_rules.nodeValue; } else { embed_style.appendChild(embed_rules); } return Billboard; })(RightJS);