/**
 * jQuery plugin to shorten text content of given elements.
 * Returns a control object, rather than a jquery set.
 * 
 * @param {Object} opt
 */
(function () {
	
	var instances = [];
	
	var common = {
		options: {
			api: true, // true if controls should be returned instead of jquery object
			exclusive: false, // show short version of other instances on showing full version
			classShortened: 'shortText', // class of container when displaying shortened version
			classFull: 'fullText', // class of container when displaying long version
			selectToggleElement: null, // jquery collection/global selector containing elements that shall toggle text state on click
			classToggleOpen: 'open', // class to be added to the toggle element when viewing long version
			classToggleInactive: 'absHidden', // class to be added to the toggle element if no short text is available
			namespace: '.abbreviate',  // namespace to add to data and event binding
			maxCharacters: 200, // maximum number of characters for shortened version
			regexClean: /[,;:]*\s+\S*$/, // a regex to trim superfluous characters
			textTrail: '...' // text/jquery to append to the shorter versrion 
		},
		instances: instances
	};
	
	var PluginClass = function (el, opt) {
		var obj = this;
		el = obj.el = $(el);
		obj.opt = opt;
		
		// bind an events to toggle display of long text
		el.bind('longtext' + opt.namespace, function (e, data) {
			obj.longText(data);
		}).bind('shorttext' + opt.namespace, function (e, data) {
			obj.shortText(data);
		});
		
		// store instance for element
		this.el.data('shorten' + this.opt.namespace, obj);
		
		$.extend(obj, {
			error: false,
			fullText: el.text(),
			shortText: null,
			hasShort: false,
			toggleElement: typeof opt.selectToggleElement == 'string' ? $(opt.selectToggleElement) : opt.selectToggleElement,
			setShortText: function (s) {
				this.hasShort = s.length > this.opt.maxCharacters;
				if (this.hasShort) {
					s = s.substring(0, this.opt.maxCharacters).replace(this.opt.regexClean, '');
				}
				if (!s) {
					this.error = true;
					this.hasShort = false;
					s = this.fullText;
				}
				return this.shortText = s;
			},
			showFullText: function (data) {
				if (this.hasShort) {
					this.el.addClass(this.opt.classFull).removeClass(this.opt.classShortened).html(obj.fullText);
					if (this.toggleElement) {
						this.toggleElement.addClass(this.opt.classToggleOpen);
					}
					this.closeAllOthers();
				}
			},
			showShortText: function (data) {
				if (this.hasShort) {
					var trail = typeof this.opt.textTrail == 'string' ? this.opt.textTrail : this.opt.textTrail.clone(true);
					el.addClass(opt.classShortened).html(obj.shortText).append(trail);
					this.el.removeClass(this.opt.classFull).addClass(this.opt.classShortened);
					if (this.toggleElement) {
						this.toggleElement.removeClass(this.opt.classToggleOpen);
					}
				} else {
					this.el.addClass(this.opt.classFull).removeClass(this.opt.classShortened);
					if (this.toggleElement) {
						this.toggleElement.addClass(this.opt.classToggleInactive);
					}
				}
			},
			toggleText: function () {
				if (this.el.hasClass(this.opt.classFull)) {
					this.showShortText();
				} else {
					this.showFullText();
				}
			},
			closeAllOthers: function () {
				if (this.opt.exclusive) {
					var obj = this;
					$.each(instances, function () {
						if (this != obj) {
							this.showShortText();
						}
					});
				}
			},
			remove: function () {
				this.el.unbind(this.opt.namespace).removeClass(this.opt.classShortened).removeClass(this.opt.classFull);
				this.el.removeData('shorten' + this.opt.namespace);
			},
			init: function () {
				var obj = this;
				if (this.toggleElement) {
					this.toggleElement.bind('click' + this.opt.namespace, function (e) {
						obj.toggleText();
						return false;
					});
				}
				this.setShortText(this.fullText);
				this.showShortText();
			}
		});
		
		obj.init();
		
		instances.push(obj);
	};
	
	common.fn = PluginClass;
	$.shorten = common;
	
	$.fn.shorten = function (opt) {
		opt = $.extend({}, common.options, opt || {});
		var jq = this, firstInstance = null;
		this.each(function (i) {
			
			var instance = $(this).data('shorten' + opt.namespace) || new PluginClass(this, opt);
			if (i == 0) {
				firstInstance = instance;
			}
			
		});
		if (opt.api) {
			return firstInstance;	
		} else {
			return jq;
		}
	};
	
	
})(jQuery);

