Show:

File: platform/plugins/Q/web/js/tools/ticker.js

(function (Q, $) {

/**
 * Q Tools
 * @module Q-tools
 */

/**
 * This renders a ticker that scrolls its items
 * @class Q ticker
 * @constructor
 * @param {Object} [options] This is an object of parameters for this function
 *  @param {Boolean} [options.vertical]
 *  @default true
 *  @param {Number} [options.speed]
 *  @default 1
 *  @param {Number} [options.pause_ms]
 *  @default 2000
 *  @param {Boolean} [options.scrollbars]
 *  @default true
 *  @param {Number} [options.scrollbars_pause_ms]
 *  @default 500
 *  @param {Number} [options.anim_ms]
 *  @default 100
 */

Q.Tool.define("Q/ticker", function(options) {
	// private variables:
	var threshold = 5; // how many pixels until manual scrolling kicks in

	if (!('vertical' in options))
		options['vertical'] = true;

	if (!('pause_ms_min' in options))
		options['pause_ms_min'] = options['pause_ms'];

	// constructor
	var me = this;
	var $ticker = $('#'+this.prefix+'tool .Q_ticker');
	var $container = $ticker.children();
	var $children = $container.children();
	if ($children.length && $children.get(0).tagName.toUpperCase() == 'TABLE') {
		$children = $children.children();
	}
	if ($children.length && $children.get(0).tagName.toUpperCase() == 'TBODY') {
		$children = $children.children();
	}
	if ($children.length && !options.vertical) {
		$children = $children.children();
	}
	
	Q.addStylesheet('{{Q}}/css/Ticker.css', { slotName: 'Q' });

	this.onHitStart = function() {}; // override this to handle this event
	this.onHitEnd = function() {}; // override this to handle this event

	this.ready = function() {
		if ($ticker.data('isTicker'))
			return;

		$ticker.data('isTicker', true);

		if (!$ticker.hasClass('ticker'))
			$ticker.addClass('ticker');

		if (!$ticker.hasClass('vertical') && !$ticker.hasClass('horizontal'))
			$ticker.addClass(options['vertical'] ? 'vertical' : 'horizontal');

		me.setScrollbars(options['scrollbars']);

		$container.css('visibility','hidden');

		setTimeout(function(){
			me.calculateDimensions();

			if (options['speed'] < 0) {
				if (options['vertical'])
					$ticker.scrollTop($container.height());
				else
					$ticker.scrollLeft($container.width());
			}

			$container.css('visibility','visible');

			me.anim_ms = options['anim_ms'] ? options['anim_ms'] : 100;

			setInterval(me.autoScroll, me.anim_ms);
		}, 1000);
	};

	this.setScrollbars = function(shouldShow) {
		$ticker.toggleClass('scrollbars', shouldShow);
	};

	this.pause = function() {
		me.scrollMode = 'paused';
		me.msSincePaused = 0;
		me.pause_ms = options['pause_ms_min'] + Math.random() * (options['pause_ms'] - options['pause_ms_min']);
	};

	this.resume = function(curScrollPos) {
		me.scrollMode = 'auto';
		me.frameIndex = 0;
		me.startScrollPos = curScrollPos;
		me.newScrollPos = me.getNextScrollPos(curScrollPos);
	};

	this.autoScroll = function () {
		var curScrollPos = (options['vertical']) ? $ticker.scrollTop() : $ticker.scrollLeft();

		// Raise scrolling events, if any
		if (! ('lastScrollPos' in me) || me.lastScrollPos != curScrollPos)
		{
			me.raisedOnHitStart = false;
			me.raisedOnHitEnd = false;
		}

		if (curScrollPos === 0)
		{
			if (!me.raisedOnHitStart)
				me.onHitStart();

			me.raisedOnHitStart = true;
		}
		else
		{
			if (options['vertical'])
			{
				if (curScrollPos == $ticker[0].scrollHeight - $ticker[0].clientHeight) //FIXME: check scrollHeight && clientHeight
				{
					if (!me.raisedOnHitEnd)
						me.onHitEnd();
					me.raisedOnHitEnd = true;
				}
			}
			else
			{
				if (curScrollPos == $ticker[0].scrollWidth - $ticker[0].clientWidth) //FIXME: check scrollHeight && clientHeight
				{
					if (!me.raisedOnHitEnd)
						me.onHitEnd();

					me.raisedOnHitEnd = true;
				}
			}
		}

		// Handle the auto scrolling

		if (! ('scrollMode' in me)) {
			if ('initial_scroll_mode' in options) {
				me.scrollMode = options.initial_scroll_mode;
			} else {
				me.scrollMode = 'auto';
			}
			if (me.scrollMode == 'auto') {
				me.resume(curScrollPos);
				me.newScrollPos = me.getFirstScrollPos(curScrollPos);
			} else {
				me.msSincePaused = 0;
				me.pause_ms = options['pause_ms_min'] + Math.random() *
						(options['pause_ms'] - options['pause_ms_min']);
			}
		}

		if (speed === 0) {
			return -1;
		}

		if (! ('frameIndex' in me)) {
			me.frameIndex = 0;
		}

		if (! ('startScrollPos' in me)) {
			me.startScrollPos = 0;
		}

		if (me.scrollMode != 'manual'
				&& 'lastAutoScrollPos' in me
				&& !isNaN(me.lastAutoScrollPos)
				&& Math.abs(me.lastAutoScrollPos - curScrollPos) > threshold) {

			// the scrollbar has started moving by some other means
			// stop this function from executing, start waiting
			// until it stops and options['scrollbars_pause_ms']
			// milliseconds have elapsed since then.
			me.scrollMode = 'manual';

			// reset # of msec passed since manual scrolling
			me.msSinceManual = 0;

		} else if (me.scrollMode == 'manual') {

			if ('lastScrollPos' in me
					&& me.lastScrollPos != curScrollPos) {
				// the scrollbar continues to move,
				// reset # of msec to wait since manual scrolling
				me.msSinceManual = 0;

				// keep the scroll mode as 'manual'
			} else {
				// increment the # of msec passed since manual scrolling
				me.msSinceManual += me.anim_ms;

				if (options['scrollbars_pause_ms'] >= 0
						&& me.msSinceManual > options['scrollbars_pause_ms']) {
					me.resume(curScrollPos);
				}
			}

		} else if (me.scrollMode == 'paused') {

			// increment the # of msec passed since manual scrolling
			me.msSincePaused += me.anim_ms;

			if (me.pause_ms >= 0 && me.msSincePaused > me.pause_ms) {
				me.resume(curScrollPos);
			}
		}

		if (me.scrollMode == 'auto') {
			var speed = options['speed'];
			var fraction = me.frameIndex * (me.anim_ms / 1000 * Math.abs(speed));

			var nextScrollPos;
			if (fraction >= 1) {
				me.pause();
			}

			nextScrollPos = me.startScrollPos +
					(me.newScrollPos - me.startScrollPos) * me.ease(fraction);

			if (options['vertical']) {
				$ticker.scrollTop(nextScrollPos);
				me.lastAutoScrollPos = $ticker.scrollTop();
			} else {
				$ticker.scrollLeft(nextScrollPos);
				me.lastAutoScrollPos = $ticker.scrollLeft();
			}

			++me.frameIndex;
		} else {
			me.lastScrollPos = curScrollPos;
		}

		// This makes sure the scrolling events don't happen twice
		if (! ('lastScrollPos' in me) || me.lastScrollPos != curScrollPos) {
			me.lastScrollPos = curScrollPos;
		}

		return 0;
	};

	this.getFirstScrollPos = function(curScrollPos) {
		var ticker = document.getElementById(options['element_id']);
		var speed = options['speed'];
		var i;

		me.calculateDimensions();

		if (speed >= 0) {
			return 0;
		}

		if (options['vertical']) {
			var bottom = 0;
			var top = 0;
			for (i = 0; i < me.heights.length; ++i) {
				bottom += me.heights[i];
			}
			for (i = 0; i < me.heights.length; ++i) {
				top += me.heights[i];
				if (top + me.heights[i] > bottom - $ticker.innerHeight()) {
					return top;
				}
			}
			return top;
		} else {
			var right = 0;
			var left = 0;
			for (i = 0; i < me.widths.length; ++i) {
				right += me.widths[i];
			}
			for (i = 0; i < me.widths.length; ++i) {
				left += me.widths[i];
				if (left + me.widths[i] > right - $ticker.innerWidth()) {
					return left;
				}
			}
			return left;
		}
		return -2;
	};

	this.getNextScrollPos = function(curScrollPos) {
		var speed = options['speed'];
		var i;

		me.calculateDimensions();

		if (speed === 0) {
			return -1;
		}

		if (options['vertical']) {
			var top = 0;
			for (i = 0; i < me.heights.length; ++i) {
				if (speed > 0) {
					if (top + me.heights[i] > curScrollPos)
						return top + me.heights[i];
				} else {
					if (top + me.heights[i] >= curScrollPos - 1)
						return top;
				}
				top += me.heights[i];
			}
		} else {
			var left = 0;
			for (i = 0; i < me.widths.length; ++i) {
				if (speed > 0) {
					if (left + me.widths[i] > curScrollPos) {
						return left + me.widths[i];
					}
				} else {
					if (left + me.widths[i] >= curScrollPos - 1) {
						return left;
					}
				}
				left += me.widths[i];
			}
		}
		return -2;
	};

	this.ease = ('ease' in options)
			? Q.Animation.ease[options['ease']]
			: Q.Animation.ease.smooth;

	this.calculateDimensions = function () {
		me.widths = [];
		me.heights = [];

		$children.each(function(index){
			var $el = $(this);
			me.widths.push($el.outerWidth());
			me.heights.push($el.outerHeight());
		});
	};

	this.ready();
},

{
	vertical: true,
	speed: 1,
	pause_ms: 2000,
	scrollbars: true,
	scrollbars_pause_ms: 500,
	anim_ms: 100
}

);

})(Q, jQuery);