diff options
Diffstat (limited to 'js/lib')
| -rw-r--r-- | js/lib/ajax.js | 10 | ||||
| -rw-r--r-- | js/lib/autocomplete.js | 39 | ||||
| -rw-r--r-- | js/lib/configuration.js | 9 | ||||
| -rw-r--r-- | js/lib/elgglib.js | 241 | ||||
| -rw-r--r-- | js/lib/events.js | 52 | ||||
| -rw-r--r-- | js/lib/hooks.js | 173 | ||||
| -rw-r--r-- | js/lib/languages.js | 29 | ||||
| -rw-r--r-- | js/lib/pageowner.js | 8 | ||||
| -rw-r--r-- | js/lib/prototypes.js | 22 | ||||
| -rw-r--r-- | js/lib/security.js | 63 | ||||
| -rw-r--r-- | js/lib/session.js | 43 | ||||
| -rw-r--r-- | js/lib/ui.autocomplete.js | 14 | ||||
| -rw-r--r-- | js/lib/ui.avatar_cropper.js | 76 | ||||
| -rw-r--r-- | js/lib/ui.friends_picker.js (renamed from js/lib/friends_picker.js) | 0 | ||||
| -rw-r--r-- | js/lib/ui.js | 218 | ||||
| -rw-r--r-- | js/lib/ui.river.js | 14 | ||||
| -rw-r--r-- | js/lib/ui.userpicker.js | 117 | ||||
| -rw-r--r-- | js/lib/ui.widgets.js | 57 | ||||
| -rw-r--r-- | js/lib/userpicker.js | 84 |
19 files changed, 959 insertions, 310 deletions
diff --git a/js/lib/ajax.js b/js/lib/ajax.js index 36f76b2c3..b3f39cc42 100644 --- a/js/lib/ajax.js +++ b/js/lib/ajax.js @@ -187,14 +187,18 @@ elgg.action = function(action, options) { options = elgg.ajax.handleOptions(action, options); - options.data = elgg.security.addToken(options.data); + // This is a misuse of elgg.security.addToken() because it is not always a + // full query string with a ?. As such we need a special check for the tokens. + if (!elgg.isString(options.data) || options.data.indexOf('__elgg_ts') == -1) { + options.data = elgg.security.addToken(options.data); + } options.dataType = 'json'; //Always display system messages after actions var custom_success = options.success || elgg.nullFunction; options.success = function(json, two, three, four) { - if (json.system_messages) { - elgg.register_error(json.system_messages.errors); + if (json && json.system_messages) { + elgg.register_error(json.system_messages.error); elgg.system_message(json.system_messages.success); } diff --git a/js/lib/autocomplete.js b/js/lib/autocomplete.js deleted file mode 100644 index eb59f51aa..000000000 --- a/js/lib/autocomplete.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * - */ -elgg.provide('elgg.autocomplete'); - -elgg.autocomplete.init = function() { - $('.elgg-input-autocomplete').autocomplete({ - source: elgg.autocomplete.url, //gets set by input/autocomplete - minLength: 1, - select: function(event, ui) { - var item = ui.item; - $(this).val(item.name); - - var hidden = $(this).next(); - hidden.val(item.guid); - } - }) - - //@todo This seems convoluted - .data("autocomplete")._renderItem = function(ul, item) { - switch (item.type) { - case 'user': - case 'group': - r = item.icon + item.name + ' - ' + item.desc; - break; - - default: - r = item.name + ' - ' + item.desc; - break; - } - - return $("<li/>") - .data("item.autocomplete", item) - .append(r) - .appendTo(ul); - }; -}; - -elgg.register_event_handler('init', 'system', elgg.autocomplete.init);
\ No newline at end of file diff --git a/js/lib/configuration.js b/js/lib/configuration.js index d0c852bd0..6e221c957 100644 --- a/js/lib/configuration.js +++ b/js/lib/configuration.js @@ -1,7 +1,10 @@ elgg.provide('elgg.config'); -elgg.config.wwwroot = '/'; - +/** + * Returns the current site URL + * + * @return {String} The site URL. + */ elgg.get_site_url = function() { return elgg.config.wwwroot; -}
\ No newline at end of file +};
\ No newline at end of file diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index 7a329621b..a8e187f1d 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -224,8 +224,8 @@ elgg.provide = function(pkg, opt_context) { * child.foo('boo!'); // alert('boo!'); * </pre> * - * @param {Function} childCtor Child class. - * @param {Function} parentCtor Parent class. + * @param {Function} Child Child class constructor. + * @param {Function} Parent Parent class constructor. */ elgg.inherit = function(Child, Parent) { Child.prototype = new Parent(); @@ -238,7 +238,7 @@ elgg.inherit = function(Child, Parent) { * If the url is already absolute or protocol-relative, no change is made. * * elgg.normalize_url(''); // 'http://my.site.com/' - * elgg.normalize_url('pg/dashboard'); // 'http://my.site.com/pg/dashboard' + * elgg.normalize_url('dashboard'); // 'http://my.site.com/dashboard' * elgg.normalize_url('http://google.com/'); // no change * elgg.normalize_url('//google.com/'); // no change * @@ -250,13 +250,40 @@ elgg.normalize_url = function(url) { url = url || ''; elgg.assertTypeOf('string', url); - // jslint complains if you use /regexp/ shorthand here... ?!?! - if ((new RegExp("^(https?:)?//", "i")).test(url)) { + validated = (function(url) { + url = elgg.parse_url(url); + if (url.scheme){ + url.scheme = url.scheme.toLowerCase(); + } + if (url.scheme == 'http' || url.scheme == 'https') { + if (!url.host) { + return false; + } + /* hostname labels may contain only alphanumeric characters, dots and hypens. */ + if (!(new RegExp("^([a-zA-Z0-9][a-zA-Z0-9\\-\\.]*)$", "i")).test(url.host) || url.host.charAt(-1) == '.') { + return false; + } + } + /* some schemas allow the host to be empty */ + if (!url.scheme || !url.host && url.scheme != 'mailto' && url.scheme != 'news' && url.scheme != 'file') { + return false; + } + return true; + })(url); + + // all normal URLs including mailto: + if (validated) { + return url; + } + + // '//example.com' (Shortcut for protocol.) + // '?query=test', #target + else if ((new RegExp("^(\\#|\\?|//)", "i")).test(url)) { return url; } // 'javascript:' - else if (url.indexOf('javascript:') === 0) { + else if (url.indexOf('javascript:') === 0 || url.indexOf('mailto:') === 0 ) { return url; } @@ -272,7 +299,7 @@ elgg.normalize_url = function(url) { return 'http://' + url; } - // 'pg/page/handler', 'mod/plugin/file.php' + // 'page/handler', 'mod/plugin/file.php' else { // trim off any leading / because the site URL is stored // with a trailing / @@ -296,7 +323,7 @@ elgg.system_messages = function(msgs, delay, type) { var classes = ['elgg-message'], messages_html = [], appendMessage = function(msg) { - messages_html.push('<li class="' + classes.join(' ') + '"><div class="elgg-text"><p>' + msg + '</p></div></li>'); + messages_html.push('<li class="' + classes.join(' ') + '"><p>' + msg + '</p></li>'); }, systemMessages = $('ul.elgg-system-messages'), i; @@ -320,18 +347,17 @@ elgg.system_messages = function(msgs, delay, type) { msgs.forEach(appendMessage); - // create the system messages ul element if needed - if (!systemMessages.length) { - systemMessages = $('<ul class="elgg-system-messages"></ul>').appendTo('body'); + if (type != 'error') { + $(messages_html.join('')).appendTo(systemMessages) + .animate({opacity: '1.0'}, delay).fadeOut('slow'); + } else { + $(messages_html.join('')).appendTo(systemMessages); } - - $(messages_html.join('')).appendTo(systemMessages) - .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 {String} msgs The message to display * @param {Number} delay How long to display the message (milliseconds) */ elgg.system_message = function(msgs, delay) { @@ -340,8 +366,8 @@ elgg.system_message = function(msgs, delay) { /** * 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) + * @param {String} errors 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"); @@ -355,4 +381,183 @@ elgg.register_error = function(errors, delay) { */ elgg.forward = function(url) { location.href = elgg.normalize_url(url); -};
\ No newline at end of file +}; + +/** + * Parse a URL into its parts. Mimicks http://php.net/parse_url + * + * @param {String} url The URL to parse + * @param {Int} component A component to return + * @param {Bool} expand Expand the query into an object? Else it's a string. + * + * @return {Object} The parsed URL + */ +elgg.parse_url = function(url, component, expand) { + // Adapted from http://blog.stevenlevithan.com/archives/parseuri + // which was release under the MIT + // It was modified to fix mailto: and javascript: support. + var + expand = expand || false, + component = component || false, + + re_str = + // scheme (and user@ testing) + '^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?' + // possibly a user[:password]@ + + '((?:(([^:@]*)(?::([^:@]*))?)?@)?' + // host and port + + '([^:/?#]*)(?::(\\d*))?)' + // path + + '(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))' + // query string + + '(?:\\?([^#]*))?' + // fragment + + '(?:#(.*))?)', + keys = { + 1: "scheme", + 4: "user", + 5: "pass", + 6: "host", + 7: "port", + 9: "path", + 12: "query", + 13: "fragment" + }, + results = {}; + + if (url.indexOf('mailto:') === 0) { + results['scheme'] = 'mailto'; + results['path'] = url.replace('mailto:', ''); + return results; + } + + if (url.indexOf('javascript:') === 0) { + results['scheme'] = 'javascript'; + results['path'] = url.replace('javascript:', ''); + return results; + } + + var re = new RegExp(re_str); + var matches = re.exec(url); + + for (var i in keys) { + if (matches[i]) { + results[keys[i]] = matches[i]; + } + } + + if (expand && typeof(results['query']) != 'undefined') { + results['query'] = elgg.parse_str(results['query']); + } + + if (component) { + if (typeof(results[component]) != 'undefined') { + return results[component]; + } else { + return false; + } + } + return results; +}; + +/** + * Returns an object with key/values of the parsed query string. + * + * @param {String} string The string to parse + * @return {Object} The parsed object string + */ +elgg.parse_str = function(string) { + var params = {}; + var result, + key, + value, + re = /([^&=]+)=?([^&]*)/g; + + while (result = re.exec(string)) { + key = decodeURIComponent(result[1].replace(/\+/g, ' ')); + value = decodeURIComponent(result[2].replace(/\+/g, ' ')); + params[key] = value; + } + + return params; +}; + +/** + * Returns a jQuery selector from a URL's fragement. Defaults to expecting an ID. + * + * Examples: + * http://elgg.org/download.php returns '' + * http://elgg.org/download.php#id returns #id + * http://elgg.org/download.php#.class-name return .class-name + * http://elgg.org/download.php#a.class-name return a.class-name + * + * @param {String} url The URL + * @return {String} The selector + */ +elgg.getSelectorFromUrlFragment = function(url) { + var fragment = url.split('#')[1]; + + if (fragment) { + // this is a .class or a tag.class + if (fragment.indexOf('.') > -1) { + return fragment; + } + + // this is an id + else { + return '#' + fragment; + } + } + return ''; +}; + +/** + * Adds child to object[parent] array. + * + * @param {Object} object The object to add to + * @param {String} parent The parent array to add to. + * @param {Mixed} value The value + */ +elgg.push_to_object_array = function(object, parent, value) { + elgg.assertTypeOf('object', object); + elgg.assertTypeOf('string', parent); + + if (!(object[parent] instanceof Array)) { + object[parent] = [] + } + + if ($.inArray(value, object[parent]) < 0) { + return object[parent].push(value); + } + + return false; +}; + +/** + * Tests if object[parent] contains child + * + * @param {Object} object The object to add to + * @param {String} parent The parent array to add to. + * @param {Mixed} value The value + */ +elgg.is_in_object_array = function(object, parent, value) { + elgg.assertTypeOf('object', object); + elgg.assertTypeOf('string', parent); + + return typeof(object[parent]) != 'undefined' && $.inArray(value, object[parent]) >= 0; +}; + +/** + * Triggers the init hook when the library is ready + * + * Current requirements: + * - DOM is ready + * - languages loaded + * + */ +elgg.initWhenReady = function() { + if (elgg.config.languageReady && elgg.config.domReady) { + elgg.trigger_hook('init', 'system'); + elgg.trigger_hook('ready', 'system'); + } +}; diff --git a/js/lib/events.js b/js/lib/events.js deleted file mode 100644 index c1aa6fd9a..000000000 --- a/js/lib/events.js +++ /dev/null @@ -1,52 +0,0 @@ -elgg.provide('elgg.config.events'); - -/** - * - */ -elgg.register_event_handler = function(event_name, event_type, handler, priority) { - elgg.assertTypeOf('string', event_name); - elgg.assertTypeOf('string', event_type); - elgg.assertTypeOf('function', handler); - - if (!event_name || !event_type) { - return false; - } - - var events = elgg.config.events; - - elgg.provide(event_name + '.' + event_type, events); - - - if (!(events[event_name][event_type] instanceof elgg.ElggPriorityList)) { - events[event_name][event_type] = new elgg.ElggPriorityList(); - } - - return events[event_name][event_type].insert(handler, priority); -}; - -/** - * - */ -elgg.trigger_event = function(event_name, event_type, opt_object) { - elgg.assertTypeOf('string', event_name); - elgg.assertTypeOf('string', event_type); - - var events = elgg.config.events, - callEventHandler = function(handler) { - return handler(event_name, event_type, opt_object) !== false; - }; - - elgg.provide(event_name + '.' + event_type, events); - elgg.provide('all.' + event_type, events); - elgg.provide(event_name + '.all', events); - elgg.provide('all.all', events); - - return [ - events[event_name][event_type], - events['all'][event_type], - events[event_name]['all'], - events['all']['all'] - ].every(function(handlers) { - return !(handlers instanceof elgg.ElggPriorityList) || handlers.every(callEventHandler); - }); -};
\ No newline at end of file diff --git a/js/lib/hooks.js b/js/lib/hooks.js new file mode 100644 index 000000000..5e1808e22 --- /dev/null +++ b/js/lib/hooks.js @@ -0,0 +1,173 @@ +/* + * Javascript hook interface + */ + +elgg.provide('elgg.config.hooks'); +elgg.provide('elgg.config.instant_hooks'); +elgg.provide('elgg.config.triggered_hooks'); + +/** + * Registers a hook handler with the event system. + * + * The special keyword "all" can be used for either the name or the type or both + * and means to call that handler for all of those hooks. + * + * Note that handlers registering for instant hooks will be executed immediately if the instant + * hook has been previously triggered. + * + * @param {String} name Name of the plugin hook to register for + * @param {String} type Type of the event to register for + * @param {Function} handler Handle to call + * @param {Number} priority Priority to call the event handler + * @return {Bool} + */ +elgg.register_hook_handler = function(name, type, handler, priority) { + elgg.assertTypeOf('string', name); + elgg.assertTypeOf('string', type); + elgg.assertTypeOf('function', handler); + + if (!name || !type) { + return false; + } + + var priorities = elgg.config.hooks; + + elgg.provide(name + '.' + type, priorities); + + if (!(priorities[name][type] instanceof elgg.ElggPriorityList)) { + priorities[name][type] = new elgg.ElggPriorityList(); + } + + // call if instant and already triggered. + if (elgg.is_instant_hook(name, type) && elgg.is_triggered_hook(name, type)) { + handler(name, type, null, null); + } + + return priorities[name][type].insert(handler, priority); +}; + +/** + * Emits a hook. + * + * Loops through all registered hooks and calls the handler functions in order. + * Every handler function will always be called, regardless of the return value. + * + * @warning Handlers take the same 4 arguments in the same order as when calling this function. + * This is different from the PHP version! + * + * @note Instant hooks do not support params or values. + * + * Hooks are called in this order: + * specifically registered (event_name and event_type match) + * all names, specific type + * specific name, all types + * all names, all types + * + * @param {String} name Name of the hook to emit + * @param {String} type Type of the hook to emit + * @param {Object} params Optional parameters to pass to the handlers + * @param {Object} value Initial value of the return. Can be mangled by handlers + * + * @return {Bool} + */ +elgg.trigger_hook = function(name, type, params, value) { + elgg.assertTypeOf('string', name); + elgg.assertTypeOf('string', type); + + // mark as triggered + elgg.set_triggered_hook(name, type); + + // default to true if unpassed + value = value || true; + + var hooks = elgg.config.hooks, + tempReturnValue = null, + returnValue = value, + callHookHandler = function(handler) { + tempReturnValue = handler(name, type, params, value); + }; + + elgg.provide(name + '.' + type, hooks); + elgg.provide('all.' + type, hooks); + elgg.provide(name + '.all', hooks); + elgg.provide('all.all', hooks); + + var hooksList = []; + + if (name != 'all' && type != 'all') { + hooksList.push(hooks[name][type]); + } + + if (type != 'all') { + hooksList.push(hooks['all'][type]); + } + + if (name != 'all') { + hooksList.push(hooks[name]['all']); + } + + hooksList.push(hooks['all']['all']); + + hooksList.every(function(handlers) { + if (handlers instanceof elgg.ElggPriorityList) { + handlers.forEach(callHookHandler); + } + return true; + }); + + return (tempReturnValue != null) ? tempReturnValue : returnValue; +}; + +/** + * Registers a hook as an instant hook. + * + * After being trigger once, registration of a handler to an instant hook will cause the + * handle to be executed immediately. + * + * @note Instant hooks must be triggered without params or defaults. Any params or default + * passed will *not* be passed to handlers executed upon registration. + * + * @param {String} name The hook name. + * @param {String} type The hook type. + * @return {Int} + */ +elgg.register_instant_hook = function(name, type) { + elgg.assertTypeOf('string', name); + elgg.assertTypeOf('string', type); + + return elgg.push_to_object_array(elgg.config.instant_hooks, name, type); +}; + +/** + * Is this hook registered as an instant hook? + * + * @param {String} name The hook name. + * @param {String} type The hook type. + */ +elgg.is_instant_hook = function(name, type) { + return elgg.is_in_object_array(elgg.config.instant_hooks, name, type); +}; + +/** + * Records that a hook has been triggered. + * + * @param {String} name The hook name. + * @param {String} type The hook type. + */ +elgg.set_triggered_hook = function(name, type) { + return elgg.push_to_object_array(elgg.config.triggered_hooks, name, type); +}; + +/** + * Has this hook been triggered yet? + * + * @param {String} name The hook name. + * @param {String} type The hook type. + */ +elgg.is_triggered_hook = function(name, type) { + return elgg.is_in_object_array(elgg.config.triggered_hooks, name, type); +}; + +elgg.register_instant_hook('init', 'system'); +elgg.register_instant_hook('ready', 'system'); +elgg.register_instant_hook('boot', 'system'); diff --git a/js/lib/languages.js b/js/lib/languages.js index e464fc0ff..d218cbc4f 100644 --- a/js/lib/languages.js +++ b/js/lib/languages.js @@ -4,6 +4,7 @@ */ elgg.provide('elgg.config.translations'); +// default language - required by unit tests elgg.config.language = 'en'; /** @@ -25,15 +26,21 @@ elgg.add_translation = function(lang, translations) { */ elgg.reload_all_translations = function(language) { var lang = language || elgg.get_language(); - elgg.getJSON('pg/js/languages%252f' + lang + '.js', { - data: { - 'viewtype': 'default', - 'lastcache': elgg.config.lastcache - }, - success: function(json) { - elgg.add_translation(lang, json); - } - }); + + var url, options; + url = 'ajax/view/js/languages'; + options = {data: {language: lang}}; + if (elgg.config.simplecache_enabled) { + options.data.lc = elgg.config.lastcache; + } + + options['success'] = function(json) { + elgg.add_translation(lang, json); + elgg.config.languageReady = true; + elgg.initWhenReady(); + }; + + elgg.getJSON(url, options); }; /** @@ -79,11 +86,11 @@ elgg.echo = function(key, argv, language) { return vsprintf(map[key], argv); } - return undefined; + return key; }; elgg.config.translations.init = function() { elgg.reload_all_translations(); }; -elgg.register_event_handler('boot', 'system', elgg.config.translations.init);
\ No newline at end of file +elgg.register_hook_handler('boot', 'system', elgg.config.translations.init);
\ No newline at end of file diff --git a/js/lib/pageowner.js b/js/lib/pageowner.js index 825898416..c695c41c3 100644 --- a/js/lib/pageowner.js +++ b/js/lib/pageowner.js @@ -6,9 +6,13 @@ */ /** - * @return {number} The GUID of the logged in user + * @return {number} The GUID of the page owner entity or 0 for no owner */ elgg.get_page_owner_guid = function() { - return elgg.page_owner.guid || 0; + if (elgg.page_owner !== undefined) { + return elgg.page_owner.guid; + } else { + return 0; + } }; diff --git a/js/lib/prototypes.js b/js/lib/prototypes.js index 0f0014818..cb6184097 100644 --- a/js/lib/prototypes.js +++ b/js/lib/prototypes.js @@ -1,5 +1,14 @@ /** + * Interates through each element of an array and calls a callback function. + * The callback should accept the following arguments: + * element - The current element + * index - The current index * + * This is different to Array.forEach in that if the callback returns false, the loop returns + * immediately without processing the remaining elements. + * + * @param {Function} callback + * @return {Bool} */ if (!Array.prototype.every) { Array.prototype.every = function(callback) { @@ -16,7 +25,16 @@ if (!Array.prototype.every) { } /** + * Interates through each element of an array and calls callback a function. + * The callback should accept the following arguments: + * element - The current element + * index - The current index + * + * This is different to Array.every in that the callback's return value is ignored and every + * element of the array will be parsed. * + * @param {Function} callback + * @return {Void} */ if (!Array.prototype.forEach) { Array.prototype.forEach = function(callback) { @@ -31,7 +49,11 @@ if (!Array.prototype.forEach) { } /** + * Left trim * + * Removes a character from the left side of a string. + * @param {String} str The character to remove + * @return {String} */ if (!String.prototype.ltrim) { String.prototype.ltrim = function(str) { diff --git a/js/lib/security.js b/js/lib/security.js index def775c05..9c12f8586 100644 --- a/js/lib/security.js +++ b/js/lib/security.js @@ -5,7 +5,17 @@ elgg.provide('elgg.security'); elgg.security.token = {}; -elgg.security.setToken = function(json) { +elgg.security.tokenRefreshFailed = false; + +elgg.security.tokenRefreshTimer = null; + +/** + * Sets the currently active security token and updates all forms and links on the current page. + * + * @param {Object} json The json representation of a token containing __elgg_ts and __elgg_token + * @return {Void} + */ +elgg.security.setToken = function(json) { //update the convenience object elgg.security.token = json; @@ -13,8 +23,8 @@ elgg.security.setToken = function(json) { $('[name=__elgg_ts]').val(json.__elgg_ts); $('[name=__elgg_token]').val(json.__elgg_token); - //also update all links - $('[href]').each(function() { + // also update all links that contain tokens and time stamps + $('[href*="__elgg_ts"][href*="__elgg_token"]').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); @@ -22,19 +32,23 @@ elgg.security.setToken = function(json) { }; /** - * Security tokens time out, so lets refresh those every so often + * Security tokens time out so we refresh those every so often. * - * @todo handle error and bad return data + * @private */ elgg.security.refreshToken = function() { elgg.action('security/refreshtoken', function(data) { - elgg.security.setToken(data.output); + if (data && data.output.__elgg_ts && data.output.__elgg_token) { + elgg.security.setToken(data.output); + } else { + clearInterval(elgg.security.tokenRefreshTimer); + } }); }; /** - * Add elgg action tokens to an object or string (assumed to be url data) + * Add elgg action tokens to an object, URL, or query string (with a ?). * * @param {Object|string} data * @return {Object} The new data object including action tokens @@ -44,14 +58,31 @@ elgg.security.addToken = function(data) { // 'http://example.com?data=sofar' if (elgg.isString(data)) { - var args = []; - if (data) { - args.push(data); + // is this a full URL, relative URL, or just the query string? + var parts = elgg.parse_url(data), + args = {}, + base = ''; + + if (parts['host'] == undefined) { + if (data.indexOf('?') === 0) { + // query string + base = '?'; + args = elgg.parse_str(parts['query']); + } + } else { + // full or relative URL + + if (parts['query'] != undefined) { + // with query string + args = elgg.parse_str(parts['query']); + } + var split = data.split('?'); + base = split[0] + '?'; } - args.push("__elgg_ts=" + elgg.security.token.__elgg_ts); - args.push("__elgg_token=" + elgg.security.token.__elgg_token); + args["__elgg_ts"] = elgg.security.token.__elgg_ts; + args["__elgg_token"] = elgg.security.token.__elgg_token; - return args.join('&'); + return base + jQuery.param(args); } // no input! acts like a getter @@ -69,8 +100,8 @@ elgg.security.addToken = function(data) { }; elgg.security.init = function() { - //refresh security token every 5 minutes - setInterval(elgg.security.refreshToken, elgg.security.interval); + // elgg.security.interval is set in the js/elgg PHP view. + elgg.security.tokenRefreshTimer = setInterval(elgg.security.refreshToken, elgg.security.interval); }; -elgg.register_event_handler('boot', 'system', elgg.security.init);
\ No newline at end of file +elgg.register_hook_handler('boot', 'system', elgg.security.init);
\ No newline at end of file diff --git a/js/lib/session.js b/js/lib/session.js index a1454aa50..a8d52733c 100644 --- a/js/lib/session.js +++ b/js/lib/session.js @@ -1,5 +1,5 @@ /** - * @todo comment + * Provides session methods. */ elgg.provide('elgg.session'); @@ -8,14 +8,15 @@ elgg.provide('elgg.session'); * @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 + * @return {string|undefined} The value of the cookie, if only name is specified. Undefined if no value set */ -elgg.session.cookie = function (name, value, options) { +elgg.session.cookie = function(name, value, options) { var cookies = [], cookie = [], i = 0, date, valid = true; //elgg.session.cookie() @@ -46,21 +47,19 @@ elgg.session.cookie = function (name, value, options) { } cookies.push(name + '=' + value); - - if (elgg.isNumber(options.expires)) { - if (elgg.isNumber(options.expires)) { - date = new Date(); - date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); - } else if (options.expires.toUTCString) { - date = options.expires; - } else { - valid = false; - } - - if (valid) { - cookies.push('expires=' + date.toUTCString()); - } - } + + if (options.expires) { + if (elgg.isNumber(options.expires)) { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else if (options.expires.toUTCString) { + date = options.expires; + } + + if (date) { + cookies.push('expires=' + date.toUTCString()); + } + } // CAUTION: Needed to parenthesize options.path and options.domain // in the following expressions, otherwise they evaluate to undefined @@ -81,6 +80,8 @@ elgg.session.cookie = function (name, value, options) { }; /** + * Returns the object of the user logged in. + * * @return {ElggUser} The logged in user */ elgg.get_logged_in_user_entity = function() { @@ -88,6 +89,8 @@ elgg.get_logged_in_user_entity = function() { }; /** + * Returns the GUID of the logged in user or 0. + * * @return {number} The GUID of the logged in user */ elgg.get_logged_in_user_guid = function() { @@ -96,6 +99,8 @@ elgg.get_logged_in_user_guid = function() { }; /** + * Returns if a user is logged in. + * * @return {boolean} Whether there is a user logged in */ elgg.is_logged_in = function() { @@ -103,6 +108,8 @@ elgg.is_logged_in = function() { }; /** + * Returns if the currently logged in user is an admin. + * * @return {boolean} Whether there is an admin logged in */ elgg.is_admin_logged_in = function() { diff --git a/js/lib/ui.autocomplete.js b/js/lib/ui.autocomplete.js new file mode 100644 index 000000000..46d72d146 --- /dev/null +++ b/js/lib/ui.autocomplete.js @@ -0,0 +1,14 @@ +/** + * + */ +elgg.provide('elgg.autocomplete'); + +elgg.autocomplete.init = function() { + $('.elgg-input-autocomplete').autocomplete({ + source: elgg.autocomplete.url, //gets set by input/autocomplete view + minLength: 2, + html: "html" + }) +}; + +elgg.register_hook_handler('init', 'system', elgg.autocomplete.init);
\ No newline at end of file diff --git a/js/lib/ui.avatar_cropper.js b/js/lib/ui.avatar_cropper.js new file mode 100644 index 000000000..fc32a0832 --- /dev/null +++ b/js/lib/ui.avatar_cropper.js @@ -0,0 +1,76 @@ +/** + * Avatar cropping + */ + +elgg.provide('elgg.avatarCropper'); + +/** + * Register the avatar cropper. + * + * If the hidden inputs have the coordinates from a previous cropping, begin + * the selection and preview with that displayed. + */ +elgg.avatarCropper.init = function() { + var params = { + selectionOpacity: 0, + aspectRatio: '1:1', + onSelectEnd: elgg.avatarCropper.selectChange, + onSelectChange: elgg.avatarCropper.preview + }; + + if ($('input[name=x2]').val()) { + params.x1 = $('input[name=x1]').val(); + params.x2 = $('input[name=x2]').val(); + params.y1 = $('input[name=y1]').val(); + params.y2 = $('input[name=y2]').val(); + } + + $('#user-avatar-cropper').imgAreaSelect(params); + + if ($('input[name=x2]').val()) { + var ias = $('#user-avatar-cropper').imgAreaSelect({instance: true}); + var selection = ias.getSelection(); + elgg.avatarCropper.preview($('#user-avatar-cropper'), selection); + } +}; + +/** + * Handler for changing select area. + * + * @param {Object} reference to the image + * @param {Object} imgareaselect selection object + * @return void + */ +elgg.avatarCropper.preview = function(img, selection) { + // catch for the first click on the image + if (selection.width == 0 || selection.height == 0) { + return; + } + + var origWidth = $("#user-avatar-cropper").width(); + var origHeight = $("#user-avatar-cropper").height(); + var scaleX = 100 / selection.width; + var scaleY = 100 / selection.height; + $('#user-avatar-preview > img').css({ + width: Math.round(scaleX * origWidth) + 'px', + height: Math.round(scaleY * origHeight) + 'px', + marginLeft: '-' + Math.round(scaleX * selection.x1) + 'px', + marginTop: '-' + Math.round(scaleY * selection.y1) + 'px' + }); +}; + +/** + * Handler for updating the form inputs after select ends + * + * @param {Object} reference to the image + * @param {Object} imgareaselect selection object + * @return void + */ +elgg.avatarCropper.selectChange = function(img, selection) { + $('input[name=x1]').val(selection.x1); + $('input[name=x2]').val(selection.x2); + $('input[name=y1]').val(selection.y1); + $('input[name=y2]').val(selection.y2); +}; + +elgg.register_hook_handler('init', 'system', elgg.avatarCropper.init);
\ No newline at end of file diff --git a/js/lib/friends_picker.js b/js/lib/ui.friends_picker.js index 9257c40fc..9257c40fc 100644 --- a/js/lib/friends_picker.js +++ b/js/lib/ui.friends_picker.js diff --git a/js/lib/ui.js b/js/lib/ui.js index 3118bb408..413078b4f 100644 --- a/js/lib/ui.js +++ b/js/lib/ui.js @@ -1,7 +1,7 @@ elgg.provide('elgg.ui'); elgg.ui.init = function () { - + // add user hover menus elgg.ui.initHoverMenu(); //if the user clicks a system message, make it disappear @@ -10,22 +10,24 @@ elgg.ui.init = function () { }); $('.elgg-system-messages li').animate({opacity: 0.9}, 6000); - $('.elgg-system-messages li').fadeOut('slow'); + $('.elgg-system-messages li.elgg-state-success').fadeOut('slow'); - $('.elgg-toggler').live('click', elgg.ui.toggles); + $('[rel=toggle]').live('click', elgg.ui.toggles); - $('.elgg-menu-page .elgg-menu-parent').live('click', elgg.ui.toggleMenu); + $('[rel=popup]').live('click', elgg.ui.popupOpen); - $('.elgg-like-toggle').live('click', elgg.ui.toggleLikes); + $('.elgg-menu-page .elgg-menu-parent').live('click', elgg.ui.toggleMenu); $('.elgg-requires-confirmation').live('click', elgg.ui.requiresConfirmation); -} + + $('.elgg-autofocus').focus(); +}; /** * Toggles an element based on clicking a separate element * - * Use .elgg-toggler on the toggler element - * Set the href to target the item you want to toggle (<a href="#id-of-target">) + * Use rel="toggle" on the toggler element + * Set the href to target the item you want to toggle (<a rel="toggle" href="#id-of-target">) * * @param {Object} event * @return void @@ -33,49 +35,123 @@ elgg.ui.init = function () { elgg.ui.toggles = function(event) { event.preventDefault(); + // @todo might want to switch this to elgg.getSelectorFromUrlFragment(). var target = $(this).toggleClass('elgg-state-active').attr('href'); $(target).slideToggle('medium'); -} +}; /** - * Toggles a child menu when the parent is clicked + * Pops up an element based on clicking a separate element + * + * Set the rel="popup" on the popper and set the href to target the + * item you want to toggle (<a rel="popup" href="#id-of-target">) + * + * This function emits the getOptions, ui.popup hook that plugins can register for to provide custom + * positioning for elements. The handler is passed the following params: + * targetSelector: The selector used to find the popup + * target: The popup jQuery element as found by the selector + * source: The jquery element whose click event initiated a popup. + * + * The return value of the function is used as the options object to .position(). + * Handles can also return false to abort the default behvior and override it with their own. * * @param {Object} event * @return void */ -elgg.ui.toggleMenu = function(event) { - $(this).siblings().slideToggle('medium'); - $(this).toggleClass('elgg-menu-closed elgg-menu-opened'); +elgg.ui.popupOpen = function(event) { event.preventDefault(); -} + event.stopPropagation(); + + var target = elgg.getSelectorFromUrlFragment($(this).toggleClass('elgg-state-active').attr('href')); + var $target = $(target); + + // emit a hook to allow plugins to position and control popups + var params = { + targetSelector: target, + target: $target, + source: $(this) + }; + + var options = { + my: 'center top', + at: 'center bottom', + of: $(this), + collision: 'fit fit' + } + + options = elgg.trigger_hook('getOptions', 'ui.popup', params, options); + + // allow plugins to cancel event + if (!options) { + return; + } + + // hide if already open + if ($target.is(':visible')) { + $target.fadeOut(); + $('body').die('click', elgg.ui.popupClose); + return; + } + + $target.appendTo('body') + .fadeIn() + .position(options); + + $('body') + .die('click', elgg.ui.popupClose) + .live('click', elgg.ui.popupClose); +}; /** - * Toggles the likes list + * Catches clicks that aren't in a popup and closes all popups. + */ +elgg.ui.popupClose = function(event) { + $eventTarget = $(event.target); + var inTarget = false; + var $popups = $('[rel=popup]'); + + // if the click event target isn't in a popup target, fade all of them out. + $popups.each(function(i, e) { + var target = elgg.getSelectorFromUrlFragment($(e).attr('href')) + ':visible'; + var $target = $(target); + + if (!$target.is(':visible')) { + return; + } + + // didn't click inside the target + if ($eventTarget.closest(target).length > 0) { + inTarget = true; + return false; + } + }); + + if (!inTarget) { + $popups.each(function(i, e) { + var $e = $(e); + var $target = $(elgg.getSelectorFromUrlFragment($e.attr('href')) + ':visible'); + if ($target.length > 0) { + $target.fadeOut(); + $e.removeClass('elgg-state-active'); + } + }); + + $('body').die('click', elgg.ui.popClose); + } +}; + +/** + * Toggles a child menu when the parent is clicked * * @param {Object} event * @return void */ -elgg.ui.toggleLikes = function(event) { - var $list = $(this).next(".elgg-likes-list"); - var position = $(this).position(); - var startTop = position.top; - var stopTop = position.top - $list.height(); - if ($list.css('display') == 'none') { - $('.elgg-likes-list').fadeOut(); - - $list.css('top', startTop); - $list.css('left', position.left - $list.width()); - $list.animate({opacity: "toggle", top: stopTop}, 500); - - $list.click(function(event) { - $list.fadeOut(); - }); - } else { - $list.animate({opacity: "toggle", top: startTop}, 500); - } +elgg.ui.toggleMenu = function(event) { + $(this).siblings().slideToggle('medium'); + $(this).toggleClass('elgg-menu-closed elgg-menu-opened'); event.preventDefault(); -} +}; /** * Initialize the hover menu @@ -103,7 +179,7 @@ elgg.ui.initHoverMenu = function(parent) { var $hovermenu = $(this).data('hovermenu') || null; if (!$hovermenu) { - var $hovermenu = $(this).parent().find(".elgg-menu-hover"); + $hovermenu = $(this).parent().find(".elgg-menu-hover"); $(this).data('hovermenu', $hovermenu); } @@ -135,19 +211,83 @@ elgg.ui.initHoverMenu = function(parent) { $(".elgg-menu-hover").fadeOut(); } }); -} +}; /** * Calls a confirm() and prevents default if denied. * - * @param {Object} event + * @param {Object} e * @return void */ elgg.ui.requiresConfirmation = function(e) { - var confirmText = $(this).attr('title') || elgg.echo('question:areyousure'); + var confirmText = $(this).attr('rel') || elgg.echo('question:areyousure'); if (!confirm(confirmText)) { e.preventDefault(); } }; -elgg.register_event_handler('init', 'system', elgg.ui.init);
\ No newline at end of file +/** + * Repositions the login popup + * + * @param {String} hook 'getOptions' + * @param {String} type 'ui.popup' + * @param {Object} params An array of info about the target and source. + * @param {Object} options Options to pass to + * + * @return {Object} + */ +elgg.ui.loginHandler = function(hook, type, params, options) { + if (params.target.attr('id') == 'login-dropdown-box') { + options.my = 'right top'; + options.at = 'right bottom'; + return options; + } + return null; +}; + +/** + * Initialize the date picker + * + * Uses the class .elgg-input-date as the selector. + * + * If the class .elgg-input-timestamp is set on the input element, the onSelect + * method converts the date text to a unix timestamp in seconds. That value is + * stored in a hidden element indicated by the id on the input field. + * + * @return void + */ +elgg.ui.initDatePicker = function() { + var loadDatePicker = function() { + $('.elgg-input-date').datepicker({ + // ISO-8601 + dateFormat: 'yy-mm-dd', + onSelect: function(dateText) { + if ($(this).is('.elgg-input-timestamp')) { + // convert to unix timestamp + var dateParts = dateText.split("-"); + var timestamp = Date.UTC(dateParts[0], dateParts[1] - 1, dateParts[2]); + timestamp = timestamp / 1000; + + var id = $(this).attr('id'); + $('input[name="' + id + '"]').val(timestamp); + } + } + }); + }; + + if ($('.elgg-input-date').length && elgg.get_language() == 'en') { + loadDatePicker(); + } else if ($('.elgg-input-date').length) { + elgg.get({ + url: elgg.config.wwwroot + 'vendors/jquery/i18n/jquery.ui.datepicker-'+ elgg.get_language() +'.js', + dataType: "script", + cache: true, + success: loadDatePicker, + error: loadDatePicker // english language is already loaded. + }); + } +}; + +elgg.register_hook_handler('init', 'system', elgg.ui.init); +elgg.register_hook_handler('init', 'system', elgg.ui.initDatePicker); +elgg.register_hook_handler('getOptions', 'ui.popup', elgg.ui.loginHandler); diff --git a/js/lib/ui.river.js b/js/lib/ui.river.js new file mode 100644 index 000000000..c103fabb3 --- /dev/null +++ b/js/lib/ui.river.js @@ -0,0 +1,14 @@ +elgg.provide('elgg.ui.river'); + +elgg.ui.river.init = function() { + $('#elgg-river-selector').change(function() { + var url = window.location.href; + if (window.location.search.length) { + url = url.substring(0, url.indexOf('?')); + } + url += '?' + $(this).val(); + elgg.forward(url); + }); +}; + +elgg.register_hook_handler('init', 'system', elgg.ui.river.init);
\ No newline at end of file diff --git a/js/lib/ui.userpicker.js b/js/lib/ui.userpicker.js new file mode 100644 index 000000000..669b84cdb --- /dev/null +++ b/js/lib/ui.userpicker.js @@ -0,0 +1,117 @@ +elgg.provide('elgg.userpicker'); + +/** + * Userpicker initialization + * + * The userpicker is an autocomplete library for selecting multiple users or + * friends. It works in concert with the view input/userpicker. + * + * @return void + */ +elgg.userpicker.init = function() { + + // binding autocomplete. + // doing this as an each so we can pass this to functions. + $('.elgg-input-user-picker').each(function() { + + $(this).autocomplete({ + source: function(request, response) { + + var params = elgg.userpicker.getSearchParams(this); + + elgg.get('livesearch', { + data: params, + dataType: 'json', + success: function(data) { + response(data); + } + }); + }, + minLength: 2, + html: "html", + select: elgg.userpicker.addUser + }) + }); + + $('.elgg-userpicker-remove').live('click', elgg.userpicker.removeUser); +}; + +/** + * Adds a user to the select user list + * + * elgg.userpicker.userList is defined in the input/userpicker view + * + * @param {Object} event + * @param {Object} ui The object returned by the autocomplete endpoint + * @return void + */ +elgg.userpicker.addUser = function(event, ui) { + var info = ui.item; + + // do not allow users to be added multiple times + if (!(info.guid in elgg.userpicker.userList)) { + elgg.userpicker.userList[info.guid] = true; + var users = $(this).siblings('.elgg-user-picker-list'); + var li = '<input type="hidden" name="members[]" value="' + info.guid + '" />'; + li += elgg.userpicker.viewUser(info); + $('<li>').html(li).appendTo(users); + } + + $(this).val(''); + event.preventDefault(); +}; + +/** + * Remove a user from the selected user list + * + * @param {Object} event + * @return void + */ +elgg.userpicker.removeUser = function(event) { + var item = $(this).closest('.elgg-user-picker-list > li'); + + var guid = item.find('[name="members[]"]').val(); + delete elgg.userpicker.userList[guid]; + + item.remove(); + event.preventDefault(); +}; + +/** + * Render the list item for insertion into the selected user list + * + * The html in this method has to remain synced with the input/userpicker view + * + * @param {Object} info The object returned by the autocomplete endpoint + * @return string + */ +elgg.userpicker.viewUser = function(info) { + + var deleteLink = "<a href='#' class='elgg-userpicker-remove'>X</a>"; + + var html = "<div class='elgg-image-block'>"; + html += "<div class='elgg-image'>" + info.icon + "</div>"; + html += "<div class='elgg-image-alt'>" + deleteLink + "</div>"; + html += "<div class='elgg-body'>" + info.name + "</div>"; + html += "</div>"; + + return html; +}; + +/** + * Get the parameters to use for autocomplete + * + * This grabs the value of the friends checkbox. + * + * @param {Object} obj Object for the autocomplete callback + * @return Object + */ +elgg.userpicker.getSearchParams = function(obj) { + if (obj.element.parent('.elgg-user-picker').find('input[name=match_on]').attr('checked')) { + return {'match_on[]': 'friends', 'term' : obj.term}; + } else { + return {'match_on[]': 'users', 'term' : obj.term}; + } +}; + +elgg.register_hook_handler('init', 'system', elgg.userpicker.init); diff --git a/js/lib/ui.widgets.js b/js/lib/ui.widgets.js index 1acc22928..26020bb4b 100644 --- a/js/lib/ui.widgets.js +++ b/js/lib/ui.widgets.js @@ -15,7 +15,7 @@ elgg.ui.widgets.init = function() { $(".elgg-widgets").sortable({ items: 'div.elgg-module-widget.elgg-state-draggable', connectWith: '.elgg-widgets', - handle: 'div.elgg-head', + handle: '.elgg-widget-handle', forcePlaceholderSize: true, placeholder: 'elgg-widget-placeholder', opacity: 0.8, @@ -29,7 +29,7 @@ elgg.ui.widgets.init = function() { $('.elgg-widget-edit > form ').live('submit', elgg.ui.widgets.saveSettings); $('a.elgg-widget-collapse-button').live('click', elgg.ui.widgets.collapseToggle); - elgg.ui.widgets.equalHeight(".elgg-widgets"); + elgg.ui.widgets.setMinHeight(".elgg-widgets"); }; /** @@ -58,6 +58,7 @@ elgg.ui.widgets.add = function(event) { handler: type, owner_guid: elgg.get_page_owner_guid(), context: $("input[name='widget_context']").val(), + show_access: $("input[name='show_access']").val(), default_widgets: $("input[name='default_widgets']").val() || 0 }, success: function(json) { @@ -65,7 +66,7 @@ elgg.ui.widgets.add = function(event) { } }); event.preventDefault(); -} +}; /** * Persist the widget's new position @@ -96,7 +97,7 @@ elgg.ui.widgets.move = function(event, ui) { // @hack fixes jquery-ui/opera bug where draggable elements jump ui.item.css('top', 0); ui.item.css('left', 0); -} +}; /** * Removes a widget from the layout @@ -107,7 +108,12 @@ elgg.ui.widgets.move = function(event, ui) { * @return void */ elgg.ui.widgets.remove = function(event) { - var $widget = $(this).parent().parent(); + if (confirm(elgg.echo('deleteconfirm')) == false) { + event.preventDefault(); + return; + } + + var $widget = $(this).closest('.elgg-module-widget'); // if widget type is single instance type, enable the add buton var type = $widget.attr('class'); @@ -124,17 +130,11 @@ elgg.ui.widgets.remove = function(event) { $widget.remove(); - // elgg-widget-delete-button-<guid> - var id = $(this).attr('id'); - id = id.substr(id.indexOf('elgg-widget-delete-button-') + "elgg-widget-delete-button-".length); + // delete the widget through ajax + elgg.action($(this).attr('href')); - elgg.action('widgets/delete', { - data: { - widget_guid: id - } - }); event.preventDefault(); -} +}; /** * Toggle the collapse state of the widget @@ -146,7 +146,7 @@ elgg.ui.widgets.collapseToggle = function(event) { $(this).toggleClass('elgg-widget-collapsed'); $(this).parent().parent().find('.elgg-body').slideToggle('medium'); event.preventDefault(); -} +}; /** * Save a widget's settings @@ -178,25 +178,32 @@ elgg.ui.widgets.saveSettings = function(event) { } }); event.preventDefault(); -} +}; /** - * Make all elements have the same min-height + * Set the min-height so that all widget column bottoms are the same * * This addresses the issue of trying to drag a widget into a column that does - * not have any widgets. + * not have any widgets or many fewer widgets than other columns. * * @param {String} selector * @return void */ -elgg.ui.widgets.equalHeight = function(selector) { - var maxHeight = 0; +elgg.ui.widgets.setMinHeight = function(selector) { + var maxBottom = 0; $(selector).each(function() { - if ($(this).height() > maxHeight) { - maxHeight = $(this).height(); + var bottom = parseInt($(this).offset().top + $(this).height()); + if (bottom > maxBottom) { + maxBottom = bottom; } }) - $(selector).css('min-height', maxHeight); -} + $(selector).each(function() { + var bottom = parseInt($(this).offset().top + $(this).height()); + if (bottom < maxBottom) { + var newMinHeight = parseInt($(this).height() + (maxBottom - bottom)); + $(this).css('min-height', newMinHeight + 'px'); + } + }) +}; -elgg.register_event_handler('init', 'system', elgg.ui.widgets.init); +elgg.register_hook_handler('init', 'system', elgg.ui.widgets.init); diff --git a/js/lib/userpicker.js b/js/lib/userpicker.js deleted file mode 100644 index 2b3d78d6b..000000000 --- a/js/lib/userpicker.js +++ /dev/null @@ -1,84 +0,0 @@ -elgg.provide('elgg.userpicker'); - -elgg.userpicker.init = function() { - // binding autocomplete. - // doing this as an each so we can past this to functions. - $('.elgg-input-user-picker').each(function() { - - var _this = this; - - $(this).autocomplete({ - source: function(request, response) { - var params = elgg.userpicker.getSearchParams(this); - - elgg.get('pg/livesearch', { - data: params, - dataType: 'json', - success: function(data) { - response(data); - } - }); - }, - minLength: 2, - select: elgg.userpicker.addUser - }) - - //@todo This seems convoluted - .data("autocomplete")._renderItem = elgg.userpicker.formatItem; - }); -}; - -elgg.userpicker.formatItem = function(ul, item) { - switch (item.type) { - case 'user': - case 'group': - r = item.icon + item.name + ' - ' + item.desc; - break; - - default: - r = item.name + ' - ' + item.desc; - break; - } - - return $("<li/>") - .data("item.autocomplete", item) - .append(r) - .appendTo(ul); -}; - -elgg.userpicker.addUser = function(event, ui) { - var info = ui.item; - - // do not allow users to be added multiple times - if (!(info.guid in elgg.userpicker.userList)) { - elgg.userpicker.userList[info.guid] = true; - - var picker = $(this).closest('.elgg-user-picker'); - var users = picker.find('.elgg-user-picker-entries'); - var internalName = users.find('[type=hidden]').attr('name'); - - // not sure why formatted isn't. - var formatted = elgg.userpicker.formatItem(data); - - // add guid as hidden input and to list. - var li = formatted + ' <div class="delete-button"><a onclick="elgg.userpicker.removeUser(this, ' + info.guid + ')"><strong>X</strong></a></div>' - + '<input type="hidden" name="' + internalName + '" value="' + info.guid + '" />'; - $('<li>').html(li).appendTo(users); - - $(this).val(''); - } -}; - -elgg.userpicker.removeUser = function(link, guid) { - $(link).closest('.elgg-user-picker-entries > li').remove(); -}; - -elgg.userpicker.getSearchParams = function(e) { - if ($(e).closest('.elgg-user-picker').find('[name=match_on]').attr('checked')) { - return {'match_on[]': 'friends'}; - } else { - return {'match_on[]': 'users'}; - } -} - -elgg.register_event_handler('init', 'system', elgg.userpicker.init);
\ No newline at end of file |
