From 8a2611e5002982cbfd2843240c181c0796b043b2 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Tue, 13 Sep 2011 22:21:12 -0400 Subject: Fixes #3539 two conditions for triggering init,system now - dom is ready and languages loaded --- js/lib/elgglib.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index f2545fb6c..9a372738d 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -379,4 +379,19 @@ elgg.getSelectorFromUrlFragment = function(url) { } } return ''; -}; \ No newline at end of file +}; + +/** + * 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'); + } +} \ No newline at end of file -- cgit v1.2.3 From 518083ff5d13b6bec789bbdac03f4004b457c0ed Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Fri, 14 Oct 2011 22:30:33 -0700 Subject: Refs #3927, #3976. Added elgg.parse_url() and elgg.parse_str(). --- js/lib/elgglib.js | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index 9a372738d..96bd04d7c 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -352,6 +352,145 @@ elgg.forward = function(url) { location.href = elgg.normalize_url(url); }; +/** + * 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 = { + 'mailto': { + 4: "scheme", + 5: "user", + 6: "host", + 9: "path", + 12: "query", + 13: "fragment" + }, + + 'standard': { + 1: "scheme", + 4: "user", + 5: "pass", + 6: "host", + 7: "port", + 9: "path", + 12: "query", + 13: "fragment" + } + }, + results = {}, + match_keys, + is_mailto = false; + + var re = new RegExp(re_str); + var matches = re.exec(url); + + // if the scheme field is undefined it means we're using a protocol + // without :// and an @. Feel free to fix this in the re if you can >:O + if (matches[1] == undefined) { + match_keys = keys['mailto']; + is_mailto = true; + } else { + match_keys = keys['standard']; + } + + for (var i in match_keys) { + if (matches[i]) { + results[match_keys[i]] = matches[i]; + } + } + + // merge everything to path if not standard + if (is_mailto) { + var path = '', + new_results = {}; + + if (typeof(results['user']) != 'undefined' && typeof(results['host']) != 'undefined') { + path = results['user'] + '@' + results['host']; + delete results['user']; + delete results['host']; + } else if (typeof(results['user'])) { + path = results['user']; + delete results['user']; + } else if (typeof(results['host'])) { + path = results['host']; + delete results['host']; + } + + if (typeof(results['path']) != 'undefined') { + results['path'] = path + results['path']; + } else { + results['path'] = path; + } + + for (var prop in results) { + new_results[prop] = results[prop]; + } + + results = new_results; + } + + 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]) + value = decodeURIComponent(result[2]) + params[key] = value; + } + + return params; +}; + /** * Returns a jQuery selector from a URL's fragement. Defaults to expecting an ID. * -- cgit v1.2.3 From 52fe9265add0b01699f63dd9b2aeeb4b248b02c1 Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Sat, 15 Oct 2011 01:02:01 -0700 Subject: Fixes #3540. Added "instant hooks" to JS hooks engine. elgg.register_instant_hook(name, type) will cause any handler registering to that hook to be immediately executed if the hook has been previously triggered. Open for better suggestions about the name. --- js/lib/elgglib.js | 36 ++++++++++++++++++++++++++ js/lib/hooks.js | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 3 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index 96bd04d7c..0f17eeced 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -520,6 +520,42 @@ elgg.getSelectorFromUrlFragment = function(url) { 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 (object[parent].indexOf(value) < 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' && object[parent].indexOf(value) >= 0; +} + /** * Triggers the init hook when the library is ready * diff --git a/js/lib/hooks.js b/js/lib/hooks.js index ab3a8a224..edfd28f24 100644 --- a/js/lib/hooks.js +++ b/js/lib/hooks.js @@ -3,13 +3,18 @@ */ elgg.provide('elgg.config.hooks'); +elgg.provide('elgg.config.instant_hooks'); +elgg.provide('elgg.config.triggered_hooks'); /** - * Registers an hook handler with the event system. + * 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 @@ -33,6 +38,11 @@ elgg.register_hook_handler = function(name, type, handler, priority) { 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); }; @@ -43,7 +53,9 @@ elgg.register_hook_handler = function(name, type, handler, priority) { * 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 to the PHP version! + * 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) @@ -62,6 +74,9 @@ 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; @@ -101,4 +116,58 @@ elgg.trigger_hook = function(name, type, params, value) { }); return (tempReturnValue !== null) ? tempReturnValue : returnValue; -}; \ No newline at end of file +}; + +/** + * 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'); -- cgit v1.2.3 From 41842ae982bdea00f8b52a9d610837febe3230ec Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Sat, 29 Oct 2011 18:44:54 -0700 Subject: Fixes #4010, refs #3927. Fixed elgg.security.addToken() to work with query strings, relative URLs, and full URLs. --- js/lib/elgglib.js | 2 +- js/lib/security.js | 31 ++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index 0f17eeced..628adccfc 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -357,7 +357,7 @@ elgg.forward = function(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. + * @param {Bool} expand Expand the query into an object? Else it's a string. * * @return {Object} The parsed URL */ diff --git a/js/lib/security.js b/js/lib/security.js index d14ddff95..726c6b767 100644 --- a/js/lib/security.js +++ b/js/lib/security.js @@ -60,7 +60,7 @@ elgg.security.refreshToken = function() { /** - * 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. * * @param {Object|string} data * @return {Object} The new data object including action tokens @@ -70,22 +70,31 @@ elgg.security.addToken = function(data) { // 'http://example.com?data=sofar' if (elgg.isString(data)) { - var args = {}, + // is this a full URL, relative URL, or just the query string? + var parts = elgg.parse_url(data), + args = {}, base = ''; - - // check for query strings - if (data.indexOf('?') != -1) { - var split = data.split('?'); - base = split[0]; - args = elgg.parse_str(split[1]); + + if (parts['host'] == data) { + if (data.indexOf('=') > -1) { + // query string + args = elgg.parse_str(data); + } else { + // relative URL + base = data + '?'; + } } else { - base = data; + // a URL + if (typeof parts['query'] != 'undefined') { + args = elgg.parse_str(parts['query']); + } + var split = data.split('?'); + base = split[0] + '?'; } - args["__elgg_ts"] = elgg.security.token.__elgg_ts; args["__elgg_token"] = elgg.security.token.__elgg_token; - return base + '?' + jQuery.param(args); + return base + jQuery.param(args); } // no input! acts like a getter -- cgit v1.2.3 From 5c0bd8a19a294f178ddd6607dd3f624ca34fa5b7 Mon Sep 17 00:00:00 2001 From: cash Date: Tue, 1 Nov 2011 19:36:27 -0400 Subject: added semi-colons after function expressions --- .../coding_standards/javascript_coding_standards.txt | 11 +++++++++++ js/lib/avatar_cropper.js | 6 +++--- js/lib/configuration.js | 2 +- js/lib/elgglib.js | 8 ++++---- js/lib/hooks.js | 8 ++++---- js/lib/ui.js | 14 +++++++------- js/lib/ui.widgets.js | 12 ++++++------ js/lib/userpicker.js | 10 +++++----- mod/blog/views/default/js/blog/save_draft.php | 4 ++-- mod/bookmarks/views/default/bookmarks/js.php | 2 +- mod/developers/views/default/js/developers/developers.php | 4 ++-- mod/embed/views/default/js/embed/embed.php | 8 ++++---- mod/messageboard/views/default/messageboard/js.php | 6 +++--- mod/thewire/views/default/js/thewire.php | 6 +++--- mod/tinymce/views/default/js/tinymce.php | 4 ++-- .../views/default/uservalidationbyemail/js.php | 2 +- 16 files changed, 59 insertions(+), 48 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/documentation/coding_standards/javascript_coding_standards.txt b/documentation/coding_standards/javascript_coding_standards.txt index 7d3b842ec..9939e80ab 100644 --- a/documentation/coding_standards/javascript_coding_standards.txt +++ b/documentation/coding_standards/javascript_coding_standards.txt @@ -1,2 +1,13 @@ *** JAVASCRIPT CODING STANDARDS *** +* Same formatting standards as PHP. + +* All functions should be in the elgg namespace. + +* Function expressions should end with a semi-colon: + + elgg.ui.toggles = function(event) { + event.preventDefault(); + $(target).slideToggle('medium'); + }; + diff --git a/js/lib/avatar_cropper.js b/js/lib/avatar_cropper.js index df6ba7866..fc32a0832 100644 --- a/js/lib/avatar_cropper.js +++ b/js/lib/avatar_cropper.js @@ -32,7 +32,7 @@ elgg.avatarCropper.init = function() { var selection = ias.getSelection(); elgg.avatarCropper.preview($('#user-avatar-cropper'), selection); } -} +}; /** * Handler for changing select area. @@ -57,7 +57,7 @@ elgg.avatarCropper.preview = function(img, selection) { marginLeft: '-' + Math.round(scaleX * selection.x1) + 'px', marginTop: '-' + Math.round(scaleY * selection.y1) + 'px' }); -} +}; /** * Handler for updating the form inputs after select ends @@ -71,6 +71,6 @@ elgg.avatarCropper.selectChange = function(img, selection) { $('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/configuration.js b/js/lib/configuration.js index f724a2f01..6e221c957 100644 --- a/js/lib/configuration.js +++ b/js/lib/configuration.js @@ -7,4 +7,4 @@ elgg.provide('elgg.config'); */ 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 628adccfc..d963a62be 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -467,7 +467,7 @@ elgg.parse_url = function(url, component, expand) { } } return results; -} +}; /** * Returns an object with key/values of the parsed query string. @@ -540,7 +540,7 @@ elgg.push_to_object_array = function(object, parent, value) { } return false; -} +}; /** * Tests if object[parent] contains child @@ -554,7 +554,7 @@ elgg.is_in_object_array = function(object, parent, value) { elgg.assertTypeOf('string', parent); return typeof(object[parent]) != 'undefined' && object[parent].indexOf(value) >= 0; -} +}; /** * Triggers the init hook when the library is ready @@ -569,4 +569,4 @@ elgg.initWhenReady = function() { elgg.trigger_hook('init', 'system'); elgg.trigger_hook('ready', 'system'); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/js/lib/hooks.js b/js/lib/hooks.js index edfd28f24..7bac471f6 100644 --- a/js/lib/hooks.js +++ b/js/lib/hooks.js @@ -136,7 +136,7 @@ elgg.register_instant_hook = function(name, type) { elgg.assertTypeOf('string', type); return elgg.push_to_object_array(elgg.config.instant_hooks, name, type); -} +}; /** * Is this hook registered as an instant hook? @@ -146,7 +146,7 @@ elgg.register_instant_hook = function(name, 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. @@ -156,7 +156,7 @@ elgg.is_instant_hook = function(name, 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? @@ -166,7 +166,7 @@ elgg.set_triggered_hook = function(name, 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'); diff --git a/js/lib/ui.js b/js/lib/ui.js index 6cc1bc78a..16ef2dc96 100644 --- a/js/lib/ui.js +++ b/js/lib/ui.js @@ -25,7 +25,7 @@ elgg.ui.init = function () { if ($('.elgg-input-date').length) { elgg.ui.initDatePicker(); } -} +}; /** * Toggles an element based on clicking a separate element @@ -43,7 +43,7 @@ elgg.ui.toggles = function(event) { var target = $(this).toggleClass('elgg-state-active').attr('href'); $(target).slideToggle('medium'); -} +}; /** * Pops up an element based on clicking a separate element @@ -105,7 +105,7 @@ elgg.ui.popsUp = function(event) { $('body') .die('click', elgg.ui.popupClose) .live('click', elgg.ui.popupClose); -} +}; /** * Catches clicks that aren't in a popup and closes all popups. @@ -143,7 +143,7 @@ elgg.ui.popupClose = function(event) { $('body').die('click', elgg.ui.popClose); } -} +}; /** * Toggles a child menu when the parent is clicked @@ -155,7 +155,7 @@ elgg.ui.toggleMenu = function(event) { $(this).siblings().slideToggle('medium'); $(this).toggleClass('elgg-menu-closed elgg-menu-opened'); event.preventDefault(); -} +}; /** * Initialize the hover menu @@ -215,7 +215,7 @@ elgg.ui.initHoverMenu = function(parent) { $(".elgg-menu-hover").fadeOut(); } }); -} +}; /** * Calls a confirm() and prevents default if denied. @@ -276,7 +276,7 @@ elgg.ui.initDatePicker = function() { } } }); -} +}; elgg.register_hook_handler('init', 'system', elgg.ui.init); elgg.register_hook_handler('getOptions', 'ui.popup', elgg.ui.LoginHandler); \ No newline at end of file diff --git a/js/lib/ui.widgets.js b/js/lib/ui.widgets.js index fb256672a..6435d2147 100644 --- a/js/lib/ui.widgets.js +++ b/js/lib/ui.widgets.js @@ -65,7 +65,7 @@ elgg.ui.widgets.add = function(event) { } }); event.preventDefault(); -} +}; /** * Persist the widget's new position @@ -96,7 +96,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 @@ -134,7 +134,7 @@ elgg.ui.widgets.remove = function(event) { } }); 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,7 +178,7 @@ elgg.ui.widgets.saveSettings = function(event) { } }); event.preventDefault(); -} +}; /** * Make all elements have the same min-height @@ -197,6 +197,6 @@ elgg.ui.widgets.equalHeight = function(selector) { } }) $(selector).css('min-height', maxHeight); -} +}; elgg.register_hook_handler('init', 'system', elgg.ui.widgets.init); diff --git a/js/lib/userpicker.js b/js/lib/userpicker.js index ae2add53f..8287ba91c 100644 --- a/js/lib/userpicker.js +++ b/js/lib/userpicker.js @@ -34,7 +34,7 @@ elgg.userpicker.init = function() { }); $('.elgg-userpicker-remove').live('click', elgg.userpicker.removeUser); -} +}; /** * Adds a user to the select user list @@ -59,7 +59,7 @@ elgg.userpicker.addUser = function(event, ui) { $(this).val(''); event.preventDefault(); -} +}; /** * Remove a user from the selected user list @@ -75,7 +75,7 @@ elgg.userpicker.removeUser = function(event) { item.remove(); event.preventDefault(); -} +}; /** * Render the list item for insertion into the selected user list @@ -96,7 +96,7 @@ elgg.userpicker.viewUser = function(info) { html += " Date: Thu, 3 Nov 2011 03:44:33 +0100 Subject: Fixes #3976. elgg.normalize_url() js function has the fix in #3747. --- js/lib/elgglib.js | 33 ++++++++++++++++++++++++++++++--- js/tests/ElggLibTest.js | 32 +++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 8 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index d963a62be..caef4d0f1 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -250,8 +250,35 @@ 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; } @@ -569,4 +596,4 @@ elgg.initWhenReady = function() { elgg.trigger_hook('init', 'system'); elgg.trigger_hook('ready', 'system'); } -}; \ No newline at end of file +}; diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js index dd0267c5c..688a1016c 100644 --- a/js/tests/ElggLibTest.js +++ b/js/tests/ElggLibTest.js @@ -73,12 +73,34 @@ ElggLibTest.prototype.testNormalizeUrl = function() { [ ['', elgg.config.wwwroot], - ['test', elgg.config.wwwroot + 'test'], - ['http://google.com', 'http://google.com'], + ['http://example.com', 'http://example.com'], + ['https://example.com', 'https://example.com'], + ['http://example-time.com', 'http://example-time.com'], ['//example.com', '//example.com'], - ['/page', elgg.config.wwwroot + 'page'], - ['mod/plugin/index.php', elgg.config.wwwroot + 'mod/plugin/index.php'], + + ['ftp://example.com/file', 'ftp://example.com/file', + ['mailto:brett@elgg.org', 'mailto:brett@elgg.org', + ['javascript:alert("test")', 'javascript:alert("test")', + ['app://endpoint', 'app://endpoint', + + ['example.com', 'http://example.com', + ['example.com/subpage', 'http://example.com/subpage', + + ['page/handler', elgg.config.wwwroot + 'page/handler', + ['page/handler?p=v&p2=v2', elgg.config.wwwroot + 'page/handler?p=v&p2=v2', + ['mod/plugin/file.php', elgg.config.wwwroot + 'mod/plugin/file.php', + ['mod/plugin/file.php?p=v&p2=v2', elgg.config.wwwroot + 'mod/plugin/file.php?p=v&p2=v2', + ['rootfile.php', elgg.config.wwwroot + 'rootfile.php', + ['rootfile.php?p=v&p2=v2', elgg.config.wwwroot + 'rootfile.php?p=v&p2=v2', + + ['/page/handler', elgg.config.wwwroot + 'page/handler', + ['/page/handler?p=v&p2=v2', elgg.config.wwwroot + 'page/handler?p=v&p2=v2', + ['/mod/plugin/file.php', elgg.config.wwwroot + 'mod/plugin/file.php', + ['/mod/plugin/file.php?p=v&p2=v2', elgg.config.wwwroot + 'mod/plugin/file.php?p=v&p2=v2', + ['/rootfile.php', elgg.config.wwwroot + 'rootfile.php', + ['/rootfile.php?p=v&p2=v2', elgg.config.wwwroot + 'rootfile.php?p=v&p2=v2', + ].forEach(function(args) { assertEquals(args[1], elgg.normalize_url(args[0])); }); -}; \ No newline at end of file +}; -- cgit v1.2.3 From 5a33143dc4d88eb2bc658cb28ca4189a1b773a56 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Thu, 3 Nov 2011 20:37:50 -0400 Subject: Fixes #4034 now using approach that exists in IE to find elements in an Array --- js/lib/elgglib.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index d963a62be..85251c1e8 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -535,7 +535,7 @@ elgg.push_to_object_array = function(object, parent, value) { object[parent] = [] } - if (object[parent].indexOf(value) < 0) { + if ($.inArray(value, object[parent]) < 0) { return object[parent].push(value); } @@ -553,7 +553,7 @@ elgg.is_in_object_array = function(object, parent, value) { elgg.assertTypeOf('object', object); elgg.assertTypeOf('string', parent); - return typeof(object[parent]) != 'undefined' && object[parent].indexOf(value) >= 0; + return typeof(object[parent]) != 'undefined' && $.inArray(value, object[parent]) >= 0; }; /** -- cgit v1.2.3 From 604a3bc06281de721e27561341299f1bb9f8ea51 Mon Sep 17 00:00:00 2001 From: cash Date: Sat, 5 Nov 2011 15:05:13 -0400 Subject: Fixes #3976 fixed unit tests for normalizing urls and standardized the code from the previous merge --- js/lib/elgglib.js | 14 ++++++------- js/tests/ElggLibTest.js | 55 +++++++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 34 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index 3e38bbad6..ca7914e7c 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!'); * * - * @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(); @@ -250,17 +250,17 @@ elgg.normalize_url = function(url) { url = url || ''; elgg.assertTypeOf('string', url); - validated = (function(url){ + validated = (function(url) { url = elgg.parse_url(url); - if(url.scheme){ + if (url.scheme){ url.scheme = url.scheme.toLowerCase(); } - if(url.scheme == 'http' || url.scheme == 'https') { - if(!url.host) { + 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) == '.'){ + if (!(new RegExp("^([a-zA-Z0-9][a-zA-Z0-9\\-\\.]*)$", "i")).test(url.host) || url.host.charAt(-1) == '.') { return false; } } diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js index 688a1016c..c53c6331d 100644 --- a/js/tests/ElggLibTest.js +++ b/js/tests/ElggLibTest.js @@ -72,33 +72,34 @@ ElggLibTest.prototype.testNormalizeUrl = function() { elgg.config.wwwroot = "http://elgg.org/"; [ - ['', elgg.config.wwwroot], - ['http://example.com', 'http://example.com'], - ['https://example.com', 'https://example.com'], - ['http://example-time.com', 'http://example-time.com'], - ['//example.com', '//example.com'], - - ['ftp://example.com/file', 'ftp://example.com/file', - ['mailto:brett@elgg.org', 'mailto:brett@elgg.org', - ['javascript:alert("test")', 'javascript:alert("test")', - ['app://endpoint', 'app://endpoint', - - ['example.com', 'http://example.com', - ['example.com/subpage', 'http://example.com/subpage', - - ['page/handler', elgg.config.wwwroot + 'page/handler', - ['page/handler?p=v&p2=v2', elgg.config.wwwroot + 'page/handler?p=v&p2=v2', - ['mod/plugin/file.php', elgg.config.wwwroot + 'mod/plugin/file.php', - ['mod/plugin/file.php?p=v&p2=v2', elgg.config.wwwroot + 'mod/plugin/file.php?p=v&p2=v2', - ['rootfile.php', elgg.config.wwwroot + 'rootfile.php', - ['rootfile.php?p=v&p2=v2', elgg.config.wwwroot + 'rootfile.php?p=v&p2=v2', - - ['/page/handler', elgg.config.wwwroot + 'page/handler', - ['/page/handler?p=v&p2=v2', elgg.config.wwwroot + 'page/handler?p=v&p2=v2', - ['/mod/plugin/file.php', elgg.config.wwwroot + 'mod/plugin/file.php', - ['/mod/plugin/file.php?p=v&p2=v2', elgg.config.wwwroot + 'mod/plugin/file.php?p=v&p2=v2', - ['/rootfile.php', elgg.config.wwwroot + 'rootfile.php', - ['/rootfile.php?p=v&p2=v2', elgg.config.wwwroot + 'rootfile.php?p=v&p2=v2', + ['', elgg.config.wwwroot], + ['test', elgg.config.wwwroot + 'test'], + ['http://example.com', 'http://example.com'], + ['https://example.com', 'https://example.com'], + ['http://example-time.com', 'http://example-time.com'], + ['//example.com', '//example.com'], + + ['ftp://example.com/file', 'ftp://example.com/file'], + ['mailto:brett@elgg.org', 'mailto:brett@elgg.org'], + ['javascript:alert("test")', 'javascript:alert("test")'], + ['app://endpoint', 'app://endpoint'], + + ['example.com', 'http://example.com'], + ['example.com/subpage', 'http://example.com/subpage'], + + ['page/handler', elgg.config.wwwroot + 'page/handler'], + ['page/handler?p=v&p2=v2', elgg.config.wwwroot + 'page/handler?p=v&p2=v2'], + ['mod/plugin/file.php', elgg.config.wwwroot + 'mod/plugin/file.php'], + ['mod/plugin/file.php?p=v&p2=v2', elgg.config.wwwroot + 'mod/plugin/file.php?p=v&p2=v2'], + ['rootfile.php', elgg.config.wwwroot + 'rootfile.php'], + ['rootfile.php?p=v&p2=v2', elgg.config.wwwroot + 'rootfile.php?p=v&p2=v2'], + + ['/page/handler', elgg.config.wwwroot + 'page/handler'], + ['/page/handler?p=v&p2=v2', elgg.config.wwwroot + 'page/handler?p=v&p2=v2'], + ['/mod/plugin/file.php', elgg.config.wwwroot + 'mod/plugin/file.php'], + ['/mod/plugin/file.php?p=v&p2=v2', elgg.config.wwwroot + 'mod/plugin/file.php?p=v&p2=v2'], + ['/rootfile.php', elgg.config.wwwroot + 'rootfile.php'], + ['/rootfile.php?p=v&p2=v2', elgg.config.wwwroot + 'rootfile.php?p=v&p2=v2'], ].forEach(function(args) { assertEquals(args[1], elgg.normalize_url(args[0])); -- cgit v1.2.3 From 6b6cb8e8f70b254d100ba494ea913d99be95fa7d Mon Sep 17 00:00:00 2001 From: cash Date: Thu, 10 Nov 2011 21:24:47 -0500 Subject: Fixes #4010 not sending naked query strings into add ajax tokens and also fixed a few related bugs in JavaScript --- js/lib/ajax.js | 6 +++- js/lib/elgglib.js | 68 +++++++++----------------------------------- js/lib/security.js | 18 ++++++------ js/tests/ElggLibTest.js | 22 ++++++++++++++ js/tests/ElggSecurityTest.js | 40 ++++++++++++++++++++------ 5 files changed, 82 insertions(+), 72 deletions(-) (limited to 'js/lib/elgglib.js') diff --git a/js/lib/ajax.js b/js/lib/ajax.js index 6f6ae052f..b3f39cc42 100644 --- a/js/lib/ajax.js +++ b/js/lib/ajax.js @@ -187,7 +187,11 @@ 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 diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index ca7914e7c..81209ebd0 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -410,16 +410,6 @@ elgg.parse_url = function(url, component, expand) { // fragment + '(?:#(.*))?)', keys = { - 'mailto': { - 4: "scheme", - 5: "user", - 6: "host", - 9: "path", - 12: "query", - 13: "fragment" - }, - - 'standard': { 1: "scheme", 4: "user", 5: "pass", @@ -428,58 +418,28 @@ elgg.parse_url = function(url, component, expand) { 9: "path", 12: "query", 13: "fragment" - } }, - results = {}, - match_keys, - is_mailto = false; + results = {}; - var re = new RegExp(re_str); - var matches = re.exec(url); - - // if the scheme field is undefined it means we're using a protocol - // without :// and an @. Feel free to fix this in the re if you can >:O - if (matches[1] == undefined) { - match_keys = keys['mailto']; - is_mailto = true; - } else { - match_keys = keys['standard']; + if (url.indexOf('mailto:') === 0) { + results['scheme'] = 'mailto'; + results['path'] = url.replace('mailto:', ''); + return results; } - for (var i in match_keys) { - if (matches[i]) { - results[match_keys[i]] = matches[i]; - } + if (url.indexOf('javascript:') === 0) { + results['scheme'] = 'javascript'; + results['path'] = url.replace('javascript:', ''); + return results; } - // merge everything to path if not standard - if (is_mailto) { - var path = '', - new_results = {}; - - if (typeof(results['user']) != 'undefined' && typeof(results['host']) != 'undefined') { - path = results['user'] + '@' + results['host']; - delete results['user']; - delete results['host']; - } else if (typeof(results['user'])) { - path = results['user']; - delete results['user']; - } else if (typeof(results['host'])) { - path = results['host']; - delete results['host']; - } - - if (typeof(results['path']) != 'undefined') { - results['path'] = path + results['path']; - } else { - results['path'] = path; - } + var re = new RegExp(re_str); + var matches = re.exec(url); - for (var prop in results) { - new_results[prop] = results[prop]; + for (var i in keys) { + if (matches[i]) { + results[keys[i]] = matches[i]; } - - results = new_results; } if (expand && typeof(results['query']) != 'undefined') { diff --git a/js/lib/security.js b/js/lib/security.js index 726c6b767..61aa1cfcd 100644 --- a/js/lib/security.js +++ b/js/lib/security.js @@ -60,7 +60,7 @@ elgg.security.refreshToken = function() { /** - * Add elgg action tokens to an object, URL, or query string. + * 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 @@ -75,17 +75,17 @@ elgg.security.addToken = function(data) { args = {}, base = ''; - if (parts['host'] == data) { - if (data.indexOf('=') > -1) { + if (parts['host'] == undefined) { + if (data.indexOf('?') === 0) { // query string - args = elgg.parse_str(data); - } else { - // relative URL - base = data + '?'; + base = '?'; + args = elgg.parse_str(parts['query']); } } else { - // a URL - if (typeof parts['query'] != 'undefined') { + // full or relative URL + + if (parts['query'] != undefined) { + // with query string args = elgg.parse_str(parts['query']); } var split = data.split('?'); diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js index c53c6331d..a29ebf743 100644 --- a/js/tests/ElggLibTest.js +++ b/js/tests/ElggLibTest.js @@ -105,3 +105,25 @@ ElggLibTest.prototype.testNormalizeUrl = function() { assertEquals(args[1], elgg.normalize_url(args[0])); }); }; + +ElggLibTest.prototype.testParseUrl = function() { + + [ + ["http://www.elgg.org/test/", {'scheme': 'http', 'host': 'www.elgg.org', 'path': '/test/'}], + ["https://www.elgg.org/test/", {'scheme': 'https', 'host': 'www.elgg.org', 'path': '/test/'}], + ["ftp://www.elgg.org/test/", {'scheme': 'ftp', 'host': 'www.elgg.org', 'path': '/test/'}], + ["http://elgg.org/test?val1=one&val2=two", {'scheme': 'http', 'host': 'elgg.org', 'path': '/test', 'query': 'val1=one&val2=two'}], + ["http://elgg.org:8080/", {'scheme': 'http', 'host': 'elgg.org', 'port': 8080, 'path': '/'}], + ["http://elgg.org/test#there", {'scheme': 'http', 'host': 'elgg.org', 'path': '/test', 'fragment': 'there'}], + + ["test?val=one", {'host': 'test', 'query': 'val=one'}], + ["?val=one", {'query': 'val=one'}], + + ["mailto:joe@elgg.org", {'scheme': 'mailto', 'path': 'joe@elgg.org'}], + ["javascript:load()", {'scheme': 'javascript', 'path': 'load()'}] + + ].forEach(function(args) { + assertEquals(args[1], elgg.parse_url(args[0])); + }); +}; + diff --git a/js/tests/ElggSecurityTest.js b/js/tests/ElggSecurityTest.js index c7309d55f..107c0adbd 100644 --- a/js/tests/ElggSecurityTest.js +++ b/js/tests/ElggSecurityTest.js @@ -26,16 +26,42 @@ ElggSecurityTest.prototype.testAddTokenAcceptsObject = function() { assertEquals(expected, elgg.security.addToken(input)); }; -ElggSecurityTest.prototype.testAddTokenAcceptsString = function() { +ElggSecurityTest.prototype.testAddTokenAcceptsRelativeUrl = function() { var input, str = "__elgg_ts=" + this.ts + "&__elgg_token=" + this.token; - - input = ""; - assertEquals('?' + str, elgg.security.addToken(input)); - + + input = "test"; + assertEquals(input + '?' + str, elgg.security.addToken(input)); +}; + +ElggSecurityTest.prototype.testAddTokenAcceptsFullUrl = function() { + var input, + str = "__elgg_ts=" + this.ts + "&__elgg_token=" + this.token; + + input = "http://elgg.org/"; + assertEquals(input + '?' + str, elgg.security.addToken(input)); +}; + +ElggSecurityTest.prototype.testAddTokenAcceptsQueryString = function() { + var input, + str = "__elgg_ts=" + this.ts + "&__elgg_token=" + this.token; + input = "?data=sofar"; assertEquals(input + '&' + str, elgg.security.addToken(input)); - + + input = "test?data=sofar"; + assertEquals(input + '&' + str, elgg.security.addToken(input)); + + input = "http://elgg.org/?data=sofar"; + assertEquals(input + '&' + str, elgg.security.addToken(input)); +}; + +ElggSecurityTest.prototype.testAddTokenAlreadyAdded = function() { + var input, + str = "__elgg_ts=" + this.ts + "&__elgg_token=" + this.token; + + input = "http://elgg.org/?" + str + "&data=sofar"; + assertEquals(input, elgg.security.addToken(input)); }; ElggSecurityTest.prototype.testSetTokenSetsElggSecurityToken = function() { @@ -47,5 +73,3 @@ ElggSecurityTest.prototype.testSetTokenSetsElggSecurityToken = function() { elgg.security.setToken(json); assertEquals(json, elgg.security.token); }; - - -- cgit v1.2.3