diff options
| author | ewinslow <ewinslow@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-11-01 07:34:24 +0000 | 
|---|---|---|
| committer | ewinslow <ewinslow@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-11-01 07:34:24 +0000 | 
| commit | f3fa93acb1063be2a2de88a6d2841b5d3a982d85 (patch) | |
| tree | 50ab3704976d7416baa929673395867d3b07f39a /engine/js/lib | |
| parent | 7e54b4efb86eecf550c23aa21146d7e191a2da0e (diff) | |
| download | elgg-f3fa93acb1063be2a2de88a6d2841b5d3a982d85.tar.gz elgg-f3fa93acb1063be2a2de88a6d2841b5d3a982d85.tar.bz2 | |
Refs #2538: Pulled in elgg JS object and unit tests
git-svn-id: http://code.elgg.org/elgg/trunk@7173 36083f99-b078-4883-b0ff-0f9b5a30f544
Diffstat (limited to 'engine/js/lib')
| -rw-r--r-- | engine/js/lib/ajax.js | 252 | ||||
| -rw-r--r-- | engine/js/lib/configuration.js | 3 | ||||
| -rw-r--r-- | engine/js/lib/elgglib.js | 165 | ||||
| -rw-r--r-- | engine/js/lib/security.js | 72 | ||||
| -rw-r--r-- | engine/js/lib/session.js | 86 | ||||
| -rw-r--r-- | engine/js/lib/ui.js | 118 | ||||
| -rw-r--r-- | engine/js/lib/ui.widgets.js | 133 | 
7 files changed, 829 insertions, 0 deletions
| diff --git a/engine/js/lib/ajax.js b/engine/js/lib/ajax.js new file mode 100644 index 000000000..bce0d31d3 --- /dev/null +++ b/engine/js/lib/ajax.js @@ -0,0 +1,252 @@ +elgg.provide('elgg.ajax');
 +
 +/**
 + * @author Evan Winslow
 + * Provides a bunch of useful shortcut functions for making ajax calls
 + */
 +
 +/**
 + * Wrapper function for jQuery.ajax which ensures that the url being called
 + * is relative to the elgg site root.
 + * 
 + * You would most likely use elgg.get or elgg.post, rather than this function
 + * 
 + * @param {string} url Optionally specify the url as the first argument
 + * @param {Object} options Optional. {@see jQuery#ajax}
 + * @return {XmlHttpRequest}
 + */
 +elgg.ajax = function(url, options) {
 +	options = elgg.ajax.handleOptions(url, options);
 +	
 +	options.url = elgg.extendUrl(options.url);
 +	return $.ajax(options);
 +};
 +/**
 + * @const
 + */
 +elgg.ajax.SUCCESS = 0;
 +
 +/**
 + * @const
 + */
 +elgg.ajax.ERROR = -1;
 +
 +/**
 + * Handle optional arguments and return the resulting options object
 + * 
 + * @param url
 + * @param options
 + * @return {Object}
 + * @private
 + */
 +elgg.ajax.handleOptions = function(url, options) {
 +	//elgg.ajax('example/file.php', {...});
 +	if(typeof url == 'string') {
 +		options = options || {};
 +	
 +	//elgg.ajax({...});
 +	} else {
 +		options = url || {};
 +		url = options.url;
 +	}
 +	
 +	var data_only = true;
 +
 +	//elgg.ajax('example/file.php', function() {...});
 +	if (typeof options == 'function') {
 +		data_only = false;
 +		options = {success: options};
 +	}
 +	
 +	//elgg.ajax('example/file.php', {data:{...}});
 +	if(options.data) {
 +		data_only = false;
 +	} else {
 +		for (var member in options) {
 +			//elgg.ajax('example/file.php', {callback:function(){...}});
 +			if(typeof options[member] == 'function') {
 +				data_only = false;
 +			}
 +		}
 +	}
 +
 +	//elgg.ajax('example/file.php', {notdata:notfunc});
 +	if (data_only) {
 +		var data = options;
 +		options = {data: data};
 +	}
 +	
 +	if (url) {
 +		options.url = url;
 +	}
 +	
 +	return options;
 +};
 +
 +/**
 + * Wrapper function for elgg.ajax which forces the request type to 'get.'
 + * 
 + * @param {string} url Optionally specify the url as the first argument
 + * @param {Object} options {@see jQuery#ajax}
 + * @return {XmlHttpRequest}
 + */
 +elgg.get = function(url, options) {
 +	options = elgg.ajax.handleOptions(url, options);
 +	
 +	options.type = 'get';
 +	return elgg.ajax(options);
 +};
 +
 +/**
 + * Wrapper function for elgg.get which forces the dataType to 'json.'
 + * 
 + * @param {string} url Optionally specify the url as the first argument
 + * @param {Object} options {@see jQuery#ajax}
 + * @return {XmlHttpRequest}
 + */
 +elgg.getJSON = function(url, options) {
 +	options = elgg.ajax.handleOptions(url, options);
 +	
 +	options.dataType = 'json';
 +	return elgg.get(options);
 +};
 +
 +/**
 + * Wrapper function for elgg.ajax which forces the request type to 'post.'
 + * 
 + * @param {string} url Optionally specify the url as the first argument
 + * @param {Object} options {@see jQuery#ajax}
 + * @return {XmlHttpRequest}
 + */
 +elgg.post = function(url, options) {
 +	options = elgg.ajax.handleOptions(url, options);
 +	
 +	options.type = 'post';
 +	return elgg.ajax(options);
 +};
 +
 +/**
 + * Perform an action via ajax
 + * 
 + * @example Usage 1:
 + * At its simplest, only the action name is required (and anything more than the
 + * action name will be invalid).
 + * <pre>
 + * elgg.action('name/of/action');
 + * </pre>
 + * Note that it will *not* love you if you specify the full url as the action
 + * (i.e. elgg.yoursite.com/action/name/of/action), but why would you want to do
 + * that anyway, when you can just specify the action name?
 + * 
 + * @example Usage 2:
 + * If you want to pass some data along with it, use the second parameter
 + * <pre>
 + * elgg.action('friend/add', { friend: some_guid });
 + * </pre>
 + * 
 + * @example Usage 3:
 + * Of course, you will have no control over what happens when the request
 + * completes if you do it like that, so there's also the most verbose method
 + * <pre>
 + * elgg.action('friend/add', {
 + *     data: {
 + *         friend: some_guid
 + *     },
 + *     success: function(json) {
 + *         //do something
 + *     },
 + * }
 + * </pre>
 + * You can pass any of your favorite $.ajax arguments into this second parameter.
 + * 
 + * Note: If you intend to use the second field in the "verbose" way, you must
 + * specify a callback method or the data parameter.  If you do not, elgg.action
 + * will think you mean to send the second parameter as data.
 + * 
 + * @param {String} action The action to call.
 + * @param {Object} options {@see jQuery#ajax}
 + * @return {XMLHttpRequest}
 + */
 +elgg.action = function(action, options) {
 +	if(!action) {
 +		throw new TypeError("action must be specified");
 +	} else if (typeof action != 'string') {
 +		throw new TypeError("action must be a string");
 +	}
 +	
 +	options = elgg.ajax.handleOptions('action/' + action, options);
 +	
 +	options.data = elgg.security.addToken(options.data);
 +	options.dataType = 'json';
 +	
 +	//Always display system messages after actions
 +	var custom_success = options.success || function(){};
 +	options.success = function(json, two, three, four) {
 +		if (json.system_messages) {
 +			elgg.register_error(json.system_messages.errors);
 +			elgg.system_message(json.system_messages.messages);
 +		}
 +		custom_success(json, two, three, four);
 +	};
 +	
 +	return elgg.post(options);
 +};
 +
 +/**
 + * Make an API call
 + * 
 + * @example Usage:
 + * <pre>
 + * elgg.api('system.api.list', {
 + *     success: function(data) {
 + *         console.log(data);
 + *     }
 + * });
 + * </pre>
 + * 
 + * @param {String} method The API method to be called
 + * @param {Object} options {@see jQuery#ajax}
 + * @return {XmlHttpRequest}
 + */
 +elgg.api = function(method, options) {
 +	if (!method) {
 +		throw new TypeError("method must be specified");
 +	} else if (typeof method != 'string') {
 +		throw new TypeError("method must be a string");
 +	}
 +	
 +	var defaults = {
 +		dataType: 'json',
 +		data: {}
 +	};
 +	
 +	options = elgg.ajax.handleOptions(method, options);
 +	options = $.extend(defaults, options);
 +	
 +	options.url = 'services/api/rest/' + options.dataType + '/';
 +	options.data.method = method;
 +	
 +	return elgg.ajax(options);
 +};
 +
 +/**
 + * @param {string} selector a jQuery selector
 + * @param {Function} complete A function to execute when the refresh is done
 + * @return {XMLHttpRequest}
 + */
 +elgg.refresh = function(selector, complete) {
 +	$(selector).html('<div align="center" class="ajax_loader"></div>');
 +	return $(selector).load(location.href + ' ' + selector + ' > *', complete);
 +};
 +
 +/**
 + * @param {string} selector a jQuery selector (usually an #id)
 + * @param {number} interval The refresh interval in seconds
 + * @param {Function} complete A function to execute when the refresh is done
 + * @return {number} The interval identifier
 + */
 +elgg.feed = function(selector, interval, complete) {
 +	return setInterval(function() {
 +		elgg.refresh(selector, complete);
 +	}, interval);
 +};
\ No newline at end of file diff --git a/engine/js/lib/configuration.js b/engine/js/lib/configuration.js new file mode 100644 index 000000000..8ed326116 --- /dev/null +++ b/engine/js/lib/configuration.js @@ -0,0 +1,3 @@ +elgg.provide('elgg.config'); + +elgg.config.wwwroot = '/';
\ No newline at end of file diff --git a/engine/js/lib/elgglib.js b/engine/js/lib/elgglib.js new file mode 100644 index 000000000..c0ce69fab --- /dev/null +++ b/engine/js/lib/elgglib.js @@ -0,0 +1,165 @@ +/**
 + * @author Evan Winslow
 + * 
 + * $Id: elgglib.js 76 2010-07-17 02:08:02Z evan.b.winslow $
 + */
 +
 +/**
 + * @namespace Namespace for elgg javascript functions
 + */
 +var elgg = elgg || {};
 +
 +elgg.init = function() {
 +	//if the user clicks a system message, make it disappear
 +	$('.elgg_system_message').live('click', function() {
 +		$(this).stop().fadeOut('fast');
 +	});
 +};
 +
 +/**
 + * Pointer to the global context
 + * {@see elgg.require} and {@see elgg.provide}
 + */
 +elgg.global = this;
 +
 +/**
 + * Throw an error if the required package isn't present
 + * 
 + * @param {String} pkg The required package (e.g., 'elgg.package')
 + */
 +elgg.require = function(pkg) {
 +	var parts = pkg.split('.'),
 +		cur = elgg.global,
 +		part;
 +
 +	for (var i = 0; i < parts.length; i++) {
 +		part = parts[i];
 +		cur = cur[part];
 +		if(typeof cur == 'undefined') {
 +			throw new Error("Missing package: " + pkg);
 +		}
 +	}
 +};
 +
 +/**
 + * Generate the skeleton for a package.
 + * 
 + * <pre>
 + * elgg.provide('elgg.package.subpackage');
 + * </pre>
 + * 
 + * is equivalent to
 + * 
 + * <pre>
 + * elgg = elgg || {};
 + * elgg.package = elgg.package || {};
 + * elgg.package.subpackage = elgg.package.subpackage || {};
 + * </pre>
 + */
 +elgg.provide = function(pkg) {
 +	var parts = pkg.split('.'),
 +		cur = elgg.global,
 +		part;
 +	
 +	for (var i = 0; i < parts.length; i++) {
 +		part = parts[i];
 +		cur[part] = cur[part] || {};
 +		cur = cur[part];
 +	}
 +};
 +
 +/**
 + * Prepend elgg.config.wwwroot to a url if the url doesn't already have it.
 + * 
 + * @param {String} url The url to extend
 + * @return {String} The extended url
 + * @private
 + */
 +elgg.extendUrl = function(url) {
 +	url = url || '';
 +	if(url.indexOf(elgg.config.wwwroot) == -1) {
 +		url = elgg.config.wwwroot + url;
 +	}
 +	
 +	return url;
 +};
 +
 +/**
 + * Displays system messages via javascript rather than php.
 + * 
 + * @param {String} msgs The message we want to display
 + * @param {Number} delay The amount of time to display the message in milliseconds. Defaults to 6 seconds.
 + * @param {String} type The type of message (typically 'error' or 'message')
 + * @private
 + */
 +elgg.system_messages = function(msgs, delay, type) {
 +	if (msgs == undefined) {
 +		return;
 +	}
 +	
 +	//validate delay.  Must be a positive integer. 
 +	delay = parseInt(delay);
 +	if (isNaN(delay) || delay <= 0) {
 +		delay = 6000;
 +	}
 +	
 +	var messages_class = 'messages';
 +	if (type == 'error') {
 +		messages_class = 'messages_error';
 +	}
 +
 +	//Handle non-arrays
 +	if (msgs.constructor.toString().indexOf("Array") == -1) {
 +		msgs = [msgs];
 +	}
 +	
 +	var messages_html = '<div class="' + messages_class + '">' 
 +		+ '<span class="closeMessages">'
 +			+ '<a href="#">' 
 +				+ elgg.echo('systemmessages:dismiss')
 +			+ '</a>'
 +		+ '</span>'
 +		+ '<p>' + msgs.join('</p><p>') + '</p>'
 +	+ '</div>';
 +	
 +	$(messages_html).insertAfter('#layout_header').click(function () {
 +		$(this).stop().fadeOut('slow');
 +		return false;
 +	}).show().animate({opacity:'1.0'},delay).fadeOut('slow');
 +};
 +
 +/**
 + * Wrapper function for system_messages. Specifies "messages" as the type of message
 + * @param {String} msg The message to display
 + * @param {Number} delay How long to display the message (milliseconds)
 + */
 +elgg.system_message = function(msgs, delay) {
 +	elgg.system_messages(msgs, delay, "message");
 +};
 +
 +/**
 + * Wrapper function for system_messages.  Specifies "errors" as the type of message
 + * @param {String} error The error message to display
 + * @param {Number} delay How long to dispaly the error message (milliseconds)
 + */
 +elgg.register_error = function(errors, delay) {
 +	elgg.system_messages(errors, delay, "error");
 +};
 +
 +/**
 + * Meant to mimic the php forward() function by simply redirecting the
 + * user to another page.
 + * 
 + * @param {String} url The url to forward to
 + */
 +elgg.forward = function(url) {
 +	location.href = elgg.extendUrl(url);
 +};
 +
 +/**
 + * Initialise Elgg
 + * @todo How should plugins, etc. initialize themselves?
 + */
 +$(function() {
 +	elgg.init();
 +});
 diff --git a/engine/js/lib/security.js b/engine/js/lib/security.js new file mode 100644 index 000000000..f4494111b --- /dev/null +++ b/engine/js/lib/security.js @@ -0,0 +1,72 @@ +/**
 + * Hold security-related data here
 + */
 +elgg.provide('elgg.security');
 +
 +elgg.security.token = {};
 +
 +elgg.security.init = function() {
 +	//refresh security token every 5 minutes
 +	setInterval(elgg.security.refreshToken, elgg.security.interval);
 +};
 +
 +elgg.security.setToken = function(json) {
 +	//update the convenience object
 +	elgg.security.token = json;
 +	
 +	//also update all forms
 +	$('[name=__elgg_ts]').val(json.__elgg_ts);
 +	$('[name=__elgg_token]').val(json.__elgg_token);
 +	
 +	//also update all links
 +	$('[href]').each(function() {
 +		this.href = this.href
 +			.replace(/__elgg_ts=\d*/, '__elgg_ts=' + json.__elgg_ts)
 +			.replace(/__elgg_token=[0-9a-f]*/, '__elgg_token=' + json.__elgg_token);
 +	});
 +};
 +
 +/**
 + * Security tokens time out, so lets refresh those every so often
 + * @todo handle error and bad return data
 + */
 +elgg.security.refreshToken = function() {
 +	elgg.action('ajax/securitytoken', function(data) {
 +		elgg.security.setToken(data.output);
 +	});
 +};
 +
 +
 +/**
 + * Add elgg action tokens to an object or string (assumed to be url data)
 + * 
 + * @param {Object|string} data
 + * @return {Object} The new data object including action tokens
 + * @private
 + */
 +elgg.security.addToken = function(data) {
 +
 +	//addToken('data=sofar')
 +	if (typeof data == 'string') {
 +		var args = [];
 +		if(data) {
 +			args.push(data);
 +		}
 +		args.push("__elgg_ts=" + elgg.security.token.__elgg_ts);
 +		args.push("__elgg_token=" + elgg.security.token.__elgg_token)
 +		
 +		return args.join('&');
 +	}
 +	
 +	//addToken({...})
 +	if (typeof data == 'object' || typeof data == 'undefined') {
 +		return $.extend(data, elgg.security.token);
 +	}
 +
 +	//addToken(???)
 +	throw new TypeError("elgg.security.addToken not implemented for " + (typeof data) + "s");
 +};
 +
 +$(function() {
 +	elgg.security.init();
 +});
\ No newline at end of file diff --git a/engine/js/lib/session.js b/engine/js/lib/session.js new file mode 100644 index 000000000..446dbfac1 --- /dev/null +++ b/engine/js/lib/session.js @@ -0,0 +1,86 @@ +/**
 + * @todo comment
 + */
 +elgg.provide('elgg.session');
 +
 +/**
 + * Helper function for setting cookies
 + * @param {string} name
 + * @param {string} value
 + * @param {Object} options
 + *  {number|Date} options[expires]
 + * 	{string} options[path]
 + * 	{string} options[domain]
 + * 	{boolean} options[secure]
 + * 
 + * @return {string} The value of the cookie, if only name is specified
 + */
 +elgg.session.cookie = function(name, value, options) {
 +	//elgg.session.cookie()
 +	if(typeof name == 'undefined') {
 +		return document.cookie;
 +	}
 +	
 +	//elgg.session.cookie(name)
 +	if (typeof value == 'undefined') {
 +		if (document.cookie && document.cookie != '') {
 +			var cookies = document.cookie.split(';');
 +			for (var i = 0; i < cookies.length; i++) {
 +				var cookie = jQuery.trim(cookies[i]).split('=');
 +				if (cookie[0] == name) {
 +					return decodeURIComponent(cookie[1]);
 +				}
 +			}
 +		}
 +		return undefined;
 +	}
 +	
 +	// elgg.session.cookie(name, value[, opts])
 +	var cookies = [];
 +
 +	options = options || {};
 +	
 +	if (value === null) {
 +		value = '';
 +		options.expires = -1;
 +	}
 +	
 +	cookies.push(name + '=' + value);
 +	
 +	if (typeof options.expires == 'number') {
 +		var date, valid = true;
 +		
 +		if (typeof options.expires == 'number') {
 +			date = new Date();
 +			date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
 +		} else if(options.expires.toUTCString) {
 +			date = options.expires;
 +		} else {
 +			valid = false;
 +		}
 +		
 +		valid ? cookies.push('expires=' + date.toUTCString()) : 0;
 +	}
 +	
 +	// CAUTION: Needed to parenthesize options.path and options.domain
 +	// in the following expressions, otherwise they evaluate to undefined
 +	// in the packed version for some reason.
 +	if (options.path) {
 +		cookies.push('path=' + (options.path));
 +	}
 +
 +	if (options.domain) {
 +		cookies.push('domain=' + (options.domain));
 +	}
 +	
 +	if (options.secure) {
 +		cookies.push('secure');
 +	}
 +	
 +	document.cookie = cookies.join('; ');
 +};
 +
 +/**
 + * @deprecated Use elgg.session.cookie instead
 + */
 +$.cookie = elgg.session.cookie;
\ No newline at end of file diff --git a/engine/js/lib/ui.js b/engine/js/lib/ui.js new file mode 100644 index 000000000..b584d66d1 --- /dev/null +++ b/engine/js/lib/ui.js @@ -0,0 +1,118 @@ +elgg.provide('elgg.ui');
 +
 +elgg.ui.init = function () {
 +	$('a.collapsibleboxlink').click(elgg.ui.toggleCollapsibleBox);
 +
 +	// set-up hover class for dragged widgets
 +	var cols = [
 +		"#rightcolumn_widgets",
 +		"#middlecolumn_widgets",
 +		"#leftcolumn_widgets"
 +	].join(',');
 +	
 +	$(cols).droppable({
 +		accept: ".draggable_widget",
 +		hoverClass: 'droppable-hover'
 +	});
 +};
 +
 +// reusable generic hidden panel
 +elgg.ui.toggleCollapsibleBox = function () {
 +	$(this.parentNode.parentNode).children(".collapsible_box").slideToggle("fast");
 +	return false;
 +};
 +
 +//define some helper jquery plugins
 +(function($) {
 +	
 +	// ELGG TOOLBAR MENU
 +	$.fn.elgg_topbardropdownmenu = function(options) {
 +		var defaults = {
 +			speed: 350
 +		};
 +		
 +		options = $.extend(defaults, options || {});
 +	
 +		this.each(function() {
 +		
 +			var root = this, zIndex = 5000;
 +		
 +			function getSubnav(ele) {
 +				if (ele.nodeName.toLowerCase() == 'li') {
 +					var subnav = $('> ul', ele);
 +					return subnav.length ? subnav[0] : null;
 +				} else {
 +					return ele;
 +				}
 +			}
 +		
 +			function getActuator(ele) {
 +				if (ele.nodeName.toLowerCase() == 'ul') {
 +					return $(ele).parents('li')[0];
 +				} else {
 +					return ele;
 +				}
 +			}
 +		
 +			function hide() {
 +				var subnav = getSubnav(this);
 +				if (!subnav) {
 +					return;
 +				}
 +			
 +				$.data(subnav, 'cancelHide', false);
 +				setTimeout(function() {
 +					if (!$.data(subnav, 'cancelHide')) {
 +						$(subnav).slideUp(100);
 +					}
 +				}, 250);
 +			}
 +		
 +			function show() {
 +				var subnav = getSubnav(this);
 +				if (!subnav) {
 +					return;
 +				}
 +				
 +				$.data(subnav, 'cancelHide', true);
 +				
 +				$(subnav).css({zIndex: zIndex++}).slideDown(options.speed);
 +				
 +				if (this.nodeName.toLowerCase() == 'ul') {
 +					var li = getActuator(this);
 +					$(li).addClass('hover');
 +					$('> a', li).addClass('hover');
 +				}
 +			}
 +		
 +			$('ul, li', this).hover(show, hide);
 +			$('li', this).hover(
 +				function() { $(this).addClass('hover'); $('> a', this).addClass('hover'); },
 +				function() { $(this).removeClass('hover'); $('> a', this).removeClass('hover'); }
 +			);
 +		
 +		});
 +	};
 +	
 +	//Make delimited list
 +	$.fn.makeDelimitedList = function(elementAttribute) {
 +	
 +		var delimitedListArray = [];
 +		var listDelimiter = "::";
 +	
 +		// Loop over each element in the stack and add the elementAttribute to the array
 +		this.each(function(e) {
 +				var listElement = $(this);
 +				// Add the attribute value to our values array
 +				delimitedListArray[delimitedListArray.length] = listElement.attr(elementAttribute);
 +			}
 +		);
 +	
 +		// Return value list by joining the array
 +		return(delimitedListArray.join(listDelimiter));
 +	};
 +})(jQuery);
 +
 +$(function() {
 +	elgg.ui.init();
 +});
\ No newline at end of file diff --git a/engine/js/lib/ui.widgets.js b/engine/js/lib/ui.widgets.js new file mode 100644 index 000000000..02a6d0e16 --- /dev/null +++ b/engine/js/lib/ui.widgets.js @@ -0,0 +1,133 @@ +elgg.provide('elgg.ui.widgets');
 +
 +elgg.ui.widgets.init = function() {
 +	// COLLAPSABLE WIDGETS (on Dashboard & Profile pages)
 +	$('a.toggle_box_contents').live('click', elgg.ui.widgets.toggleContent);
 +	$('a.toggle_box_edit_panel').live('click', elgg.ui.widgets.toggleEditPanel);
 +	$('a.toggle_customise_edit_panel').live('click', elgg.ui.widgets.toggleCustomizeEditPanel);
 +	
 +	// WIDGET GALLERY EDIT PANEL
 +	// Sortable widgets
 +	var els = [
 +		'#leftcolumn_widgets',
 +		'#middlecolumn_widgets',
 +		'#rightcolumn_widgets',
 +		'#widget_picker_gallery'
 +	].join(',');
 +	
 +	$(els).sortable({
 +		items: '.draggable_widget',
 +		handle: '.drag_handle',
 +		forcePlaceholderSize: true,
 +		placeholder: 'ui-state-highlight',
 +		cursor: 'move',
 +		opacity: 0.9,
 +		appendTo: 'body',
 +		connectWith: els,
 +		stop: function(e,ui) {
 +			// refresh list before updating hidden fields with new widget order
 +			$(this).sortable("refresh");
 +
 +			var widgetNamesLeft = outputWidgetList('#leftcolumn_widgets');
 +			var widgetNamesMiddle = outputWidgetList('#middlecolumn_widgets');
 +			var widgetNamesRight = outputWidgetList('#rightcolumn_widgets');
 +
 +			$('#debugField1').val(widgetNamesLeft);
 +			$('#debugField2').val(widgetNamesMiddle);
 +			$('#debugField3').val(widgetNamesRight);
 +		}
 +	});
 +
 +	// bind more info buttons - called when new widgets are created
 +	elgg.ui.widgets.moreinfo();
 +};
 +
 +//List active widgets for each page column
 +elgg.ui.widgets.outputList = function(forElement) {
 +	return( $("input[name='handler'], input[name='guid']", forElement ).makeDelimitedList("value") );
 +};
 +
 +//Read each widgets collapsed/expanded state from cookie and apply
 +elgg.ui.widgets.state = function(forWidget) {
 +
 +	var thisWidgetState = elgg.session.cookie(forWidget);
 +
 +	if (thisWidgetState == 'collapsed') {
 +		forWidget = "#" + forWidget;
 +		$(forWidget).find("div.collapsable_box_content").hide();
 +		$(forWidget).find("a.toggle_box_contents").html('+');
 +		$(forWidget).find("a.toggle_box_edit_panel").fadeOut('medium');
 +	}
 +};
 +
 +//More info tooltip in widget gallery edit panel
 +elgg.ui.widgets.moreinfo = function() {
 +	$("img.more_info").hover(function(e) {
 +		var widgetdescription = $("input[name='description']", this.parentNode.parentNode.parentNode).val();
 +		$("body").append("<p id='widget_moreinfo'><b>"+ widgetdescription +" </b></p>");
 +
 +		if (e.pageX < 900) {
 +			$("#widget_moreinfo")
 +				.css("top",(e.pageY + 10) + "px")
 +				.css("left",(e.pageX + 10) + "px")
 +				.fadeIn("medium");
 +		} else {
 +			$("#widget_moreinfo")
 +				.css("top",(e.pageY + 10) + "px")
 +				.css("left",(e.pageX - 210) + "px")
 +				.fadeIn("medium");
 +		}
 +	}, function() {
 +		$("#widget_moreinfo").remove();
 +	});
 +};
 +
 +//Toggle widgets contents and save to a cookie
 +elgg.ui.widgets.toggleContent = function(e) {
 +	var targetContent = $('div.collapsable_box_content', this.parentNode.parentNode);
 +	if (targetContent.css('display') == 'none') {
 +		targetContent.slideDown(400);
 +		$(this).html('-');
 +		$(this.parentNode).children(".toggle_box_edit_panel").fadeIn('medium');
 +
 +		// set cookie for widget panel open-state
 +		var thisWidgetName = $(this.parentNode.parentNode.parentNode).attr('id');
 +		$.cookie(thisWidgetName, 'expanded', { expires: 365 });
 +
 +	} else {
 +		targetContent.slideUp(400);
 +		$(this).html('+');
 +		$(this.parentNode).children(".toggle_box_edit_panel").fadeOut('medium');
 +		// make sure edit pane is closed
 +		$(this.parentNode.parentNode).children(".collapsable_box_editpanel").hide();
 +
 +		// set cookie for widget panel closed-state
 +		var thisWidgetName = $(this.parentNode.parentNode.parentNode).attr('id');
 +		$.cookie(thisWidgetName, 'collapsed', { expires: 365 });
 +	}
 +	return false;
 +};
 +
 +// toggle widget box edit panel
 +elgg.ui.widgets.toggleEditPanel = function () {
 +	$(this.parentNode.parentNode).children(".collapsable_box_editpanel").slideToggle("fast");
 +	return false;
 +};
 +
 +// toggle customise edit panel
 +elgg.ui.widgets.toggleCustomizeEditPanel = function () {
 +	$('#customise_editpanel').slideToggle("fast");
 +	return false;
 +};
 +
 +/**
 + * @deprecated Use elgg.ui.widgets.*
 + */
 +var toggleContent =    elgg.ui.widgets.toggleContent,
 +    widget_moreinfo =  elgg.ui.widgets.moreinfo,
 +    widget_state =     elgg.ui.widgets.state,
 +    outputWidgetList = elgg.ui.widgets.outputList;
 +
 +$(function() {
 +	elgg.ui.widgets.init();
 +});
 | 
