aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorewinslow <ewinslow@36083f99-b078-4883-b0ff-0f9b5a30f544>2010-11-02 16:06:53 +0000
committerewinslow <ewinslow@36083f99-b078-4883-b0ff-0f9b5a30f544>2010-11-02 16:06:53 +0000
commit441c9e85dcb824dba2a44657a31fa29ad71a4ee1 (patch)
treee3558477d639a95bb5a24807d2a8174c73e1ac83 /js
parent99b267a9fc4b023e54a9d2c8ec9bd9fe42a32e54 (diff)
downloadelgg-441c9e85dcb824dba2a44657a31fa29ad71a4ee1.tar.gz
elgg-441c9e85dcb824dba2a44657a31fa29ad71a4ee1.tar.bz2
Refs #2538: Moved js directory to elgg root.
git-svn-id: http://code.elgg.org/elgg/trunk@7189 36083f99-b078-4883-b0ff-0f9b5a30f544
Diffstat (limited to 'js')
-rw-r--r--js/classes/ElggEntity.js20
-rw-r--r--js/classes/ElggPriorityList.js60
-rw-r--r--js/classes/ElggUser.js14
-rw-r--r--js/lib/ajax.js252
-rw-r--r--js/lib/configuration.js3
-rw-r--r--js/lib/elgglib.js187
-rw-r--r--js/lib/events.js65
-rw-r--r--js/lib/languages.js83
-rw-r--r--js/lib/security.js70
-rw-r--r--js/lib/session.js116
-rw-r--r--js/lib/ui.js121
-rw-r--r--js/lib/ui.widgets.js131
-rw-r--r--js/tests/ElggAjaxOptionsTest.js62
-rw-r--r--js/tests/ElggAjaxTest.js59
-rw-r--r--js/tests/ElggEventsTest.js28
-rw-r--r--js/tests/ElggLanguagesTest.js45
-rw-r--r--js/tests/ElggLibTest.js88
-rw-r--r--js/tests/ElggPriorityListTest.js47
-rw-r--r--js/tests/ElggSecurityTest.js51
-rw-r--r--js/tests/ElggSessionTest.js36
20 files changed, 1538 insertions, 0 deletions
diff --git a/js/classes/ElggEntity.js b/js/classes/ElggEntity.js
new file mode 100644
index 000000000..9461a463f
--- /dev/null
+++ b/js/classes/ElggEntity.js
@@ -0,0 +1,20 @@
+/**
+ * Create a new ElggEntity
+ *
+ * @class Represents an ElggEntity
+ * @property {number} guid
+ * @property {string} type
+ * @property {string} subtype
+ * @property {number} owner_guid
+ * @property {number} site_guid
+ * @property {number} container_guid
+ * @property {number} access_id
+ * @property {number} time_created
+ * @property {number} time_updated
+ * @property {number} last_action
+ * @property {string} enabled
+ *
+ */
+elgg.ElggEntity = function(o) {
+ $.extend(this, o);
+}; \ No newline at end of file
diff --git a/js/classes/ElggPriorityList.js b/js/classes/ElggPriorityList.js
new file mode 100644
index 000000000..521fbbb64
--- /dev/null
+++ b/js/classes/ElggPriorityList.js
@@ -0,0 +1,60 @@
+elgg.ElggPriorityList = function() {
+ this.length = 0;
+ this.priorities_ = [];
+};
+
+elgg.ElggPriorityList.prototype.insert = function(obj, opt_priority) {
+ if (opt_priority == undefined) {
+ opt_priority = 500;
+ }
+
+ opt_priority = parseInt(opt_priority);
+ if (opt_priority < 0) {
+ opt_priority = 0;
+ }
+
+ if (this.priorities_[opt_priority] == undefined) {
+ this.priorities_[opt_priority] = [];
+ }
+
+ this.priorities_[opt_priority].push(obj);
+ this.length++;
+};
+
+elgg.ElggPriorityList.prototype.forEach = function(callback) {
+ elgg.assertTypeOf('function', callback);
+
+ var index = 0;
+ for (var p in this.priorities_) {
+ var elems = this.priorities_[p];
+ for (var i in elems) {
+ callback(elems[i], index);
+ index++;
+ }
+ }
+};
+
+elgg.ElggPriorityList.prototype.every = function(callback) {
+ elgg.assertTypeOf('function', callback);
+
+ var index = 0;
+ for (var p in this.priorities_) {
+ var elems = this.priorities_[p];
+ for (var i in elems) {
+ if (!callback(elems[i], index)) {
+ return false;
+ };
+ }
+ }
+
+ return true;
+};
+
+elgg.ElggPriorityList.prototype.remove = function(obj) {
+ this.priorities_.forEach(function(elems, priority) {
+ var index;
+ while ((index = elems.indexOf(obj)) != -1) {
+ elems.splice(index, 1);
+ }
+ });
+}; \ No newline at end of file
diff --git a/js/classes/ElggUser.js b/js/classes/ElggUser.js
new file mode 100644
index 000000000..8a7a8b7eb
--- /dev/null
+++ b/js/classes/ElggUser.js
@@ -0,0 +1,14 @@
+/**
+ * Create a new ElggUser
+ *
+ * @param {Object} o
+ * @extends ElggEntity
+ * @class Represents an ElggUser
+ * @property {string} name
+ * @property {string} username
+ */
+elgg.ElggUser = function(o) {
+ elgg.ElggEntity.call(this, o);
+};
+
+elgg.inherit(elgg.ElggUser, elgg.ElggEntity); \ No newline at end of file
diff --git a/js/lib/ajax.js b/js/lib/ajax.js
new file mode 100644
index 000000000..184fd0da3
--- /dev/null
+++ b/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.normalize_url(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/js/lib/configuration.js b/js/lib/configuration.js
new file mode 100644
index 000000000..8ed326116
--- /dev/null
+++ b/js/lib/configuration.js
@@ -0,0 +1,3 @@
+elgg.provide('elgg.config');
+
+elgg.config.wwwroot = '/'; \ No newline at end of file
diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js
new file mode 100644
index 000000000..32dbb1ec3
--- /dev/null
+++ b/js/lib/elgglib.js
@@ -0,0 +1,187 @@
+/**
+ *
+ *
+ */
+
+/**
+ * @namespace Namespace for elgg javascript functions
+ */
+var elgg = elgg || {};
+
+elgg.assertTypeOf = function(type, param) {
+ if (typeof param !== type) {
+ throw new TypeError("Expecting param to be a(n) " + type + ". Was a(n) " + typeof param + ".");
+ }
+};
+
+/**
+ * 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) {
+ elgg.assertTypeOf('string', 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>
+ *
+ * @example elgg.provide('elgg.config.translations')
+ *
+ * @param {string} pkg The package name.
+ */
+elgg.provide = function(pkg) {
+ elgg.assertTypeOf('string', 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];
+ }
+};
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * @example
+ * <pre>
+ * function ParentClass(a, b) { }
+ *
+ * ParentClass.prototype.foo = function(a) { alert(a); }
+ *
+ * function ChildClass(a, b, c) {
+ * //equivalent of parent::__construct(a, b); in PHP
+ * ParentClass.call(this, a, b);
+ * }
+ *
+ * elgg.inherit(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo('boo!'); // alert('boo!');
+ * </pre>
+ *
+ * @param {Function} childCtor Child class.
+ * @param {Function} parentCtor Parent class.
+ */
+elgg.inherit = function(Child, Parent) {
+ Child.prototype = new Parent();
+ Child.prototype.constructor = Child;
+};
+
+/**
+ * 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.normalize_url = function(url) {
+ url = url || '';
+ elgg.assertTypeOf('string', url);
+
+ if(/(^(https?:)?\/\/)/.test(url)) {
+ return url;
+ }
+
+ return elgg.config.wwwroot + 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;
+ }
+
+ classes = ['elgg_system_message', 'radius8'];
+ if (type == 'error') {
+ classes.push('messages_error');
+ }
+
+ //Handle non-arrays
+ if (msgs.constructor.toString().indexOf("Array") == -1) {
+ msgs = [msgs];
+ }
+
+ var messages_html = [];f
+
+ for (var i in msgs) {
+ messages_html.push('<div class="' + classes.join(' ') + '"><p>' + msgs[i] + '</p></div>');
+ }
+
+ $(messages_html.join('')).appendTo('#elgg_system_messages').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.normalize_url(url);
+}; \ No newline at end of file
diff --git a/js/lib/events.js b/js/lib/events.js
new file mode 100644
index 000000000..358dd6280
--- /dev/null
+++ b/js/lib/events.js
@@ -0,0 +1,65 @@
+elgg.provide('elgg.config.events');
+elgg.provide('elgg.config.events.all');
+elgg.provide('elgg.config.events.all.all');
+
+elgg.register_event_handler = function(event, type, callback, priority) {
+ elgg.assertTypeOf('string', event);
+ elgg.assertTypeOf('string', event);
+ elgg.assertTypeOf('function', callback);
+
+ if (!event || !type) {
+ return false;
+ }
+
+ elgg.provide('elgg.config.events.' + event + '.' + type);
+
+ var events = elgg.config.events;
+
+ if (!(events[event][type] instanceof elgg.ElggPriorityList)) {
+ events[event][type] = new elgg.ElggPriorityList();
+ }
+
+ return events[event][type].insert(callback, priority);
+};
+
+elgg.trigger_event = function(event, type, object) {
+ elgg.assertTypeOf('string', event);
+ elgg.assertTypeOf('string', event);
+
+ elgg.provide('elgg.config.events.' + event + '.' + type);
+ elgg.provide('elgg.config.events.all.' + type);
+ elgg.provide('elgg.config.events.' + event + '.all');
+ elgg.provide('elgg.config.events.all.all');
+
+ var events = elgg.config.events;
+
+ var callEventHandler = function(handler) {
+ return handler(event, type, object) !== false;
+ };
+
+ if (events[event][type] instanceof elgg.ElggPriorityList) {
+ if (!events[event][type].every(callEventHandler)) {
+ return false;
+ }
+ }
+
+ if (events['all'][type] instanceof elgg.ElggPriorityList) {
+ if (!events['all'][type].every(callEventHandler)) {
+ return false;
+ }
+ }
+
+ if (events[event]['all'] instanceof elgg.ElggPriorityList) {
+ if (!events[event]['all'].every(callEventHandler)) {
+ return false;
+ }
+ }
+
+ if (events['all']['all'] instanceof elgg.ElggPriorityList) {
+ if (!events['all']['all'].every(callEventHandler)) {
+ return false;
+ }
+ }
+
+ return true;
+}; \ No newline at end of file
diff --git a/js/lib/languages.js b/js/lib/languages.js
new file mode 100644
index 000000000..3231cf77d
--- /dev/null
+++ b/js/lib/languages.js
@@ -0,0 +1,83 @@
+/**
+ * Provides language-related functionality
+ */
+elgg.provide('elgg.config.translations');
+
+elgg.config.language = 'en';
+
+elgg.add_translation = function(lang, translations) {
+ elgg.provide('elgg.config.translations.' + lang);
+
+ $.extend(elgg.config.translations[lang], translations);
+}
+
+/**
+ * Load the translations for the given language.
+ *
+ * If no language is specified, the default language is used.
+ * @param {string} language
+ * @return {XMLHttpRequest}
+ */
+elgg.reload_all_translations = function(language) {
+ var lang = language || elgg.get_language();
+ elgg.getJSON('_css/js.php', {
+ data: {
+ 'js': 'languages/'+lang,
+ 'viewtype': 'default',
+ 'lastcache': elgg.config.lastcache
+ },
+ success: function(json) {
+ elgg.add_translation(lang, json);
+ }
+ });
+};
+
+/**
+ * Get the current language
+ * @return {String}
+ */
+elgg.get_language = function() {
+ var user = elgg.get_loggedin_user();
+
+ if (user && user.language) {
+ return user.language;
+ }
+
+ return elgg.config.language;
+};
+
+/**
+ * Translates a string
+ *
+ * @param {String} key The string to translate
+ * @param {String} language The language to display it in
+ * @return {String} The translation
+ */
+elgg.echo = function(key, language) {
+ var translations,
+ dlang = elgg.get_language();
+
+ language = language || dlang;
+
+ translations = elgg.config.translations[language];
+ if (translations && translations[key]) {
+ return translations[key];
+ }
+
+ if (language == dlang) {
+ return undefined;
+ }
+
+ translations = elgg.config.translations[dlang];
+ if (translations && translations[key]) {
+ return translations[key];
+ }
+
+ return undefined;
+};
+
+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
diff --git a/js/lib/security.js b/js/lib/security.js
new file mode 100644
index 000000000..bdd762560
--- /dev/null
+++ b/js/lib/security.js
@@ -0,0 +1,70 @@
+/**
+ * Hold security-related data here
+ */
+elgg.provide('elgg.security');
+
+elgg.security.token = {};
+
+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");
+};
+
+elgg.security.init = function() {
+ //refresh security token every 5 minutes
+ setInterval(elgg.security.refreshToken, elgg.security.interval);
+};
+
+elgg.register_event_handler('boot', 'system', elgg.security.init); \ No newline at end of file
diff --git a/js/lib/session.js b/js/lib/session.js
new file mode 100644
index 000000000..227c607eb
--- /dev/null
+++ b/js/lib/session.js
@@ -0,0 +1,116 @@
+/**
+ * @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('; ');
+};
+
+/**
+ * @return {ElggUser} The logged in user
+ */
+elgg.get_loggedin_user = function() {
+ return elgg.session.user;
+};
+
+/**
+ * @return {number} The GUID of the logged in user
+ */
+elgg.get_loggedin_userid = function() {
+ var user = elgg.get_loggedin_user();
+ return user ? user.guid : 0;
+};
+
+/**
+ * @return {boolean} Whether there is a user logged in
+ */
+elgg.isloggedin = function() {
+ return (elgg.get_loggedin_user() instanceof elgg.ElggUser);
+};
+
+/**
+ * @return {boolean} Whether there is an admin logged in
+ */
+elgg.isadminloggedin = function() {
+ var user = elgg.get_loggedin_user();
+ return (user instanceof ElggUser) && user.isAdmin();
+};
+
+/**
+ * @deprecated Use elgg.session.cookie instead
+ */
+$.cookie = elgg.session.cookie; \ No newline at end of file
diff --git a/js/lib/ui.js b/js/lib/ui.js
new file mode 100644
index 000000000..4a9c64e70
--- /dev/null
+++ b/js/lib/ui.js
@@ -0,0 +1,121 @@
+elgg.provide('elgg.ui');
+
+elgg.ui.init = function () {
+ //if the user clicks a system message, make it disappear
+ $('.elgg_system_message').live('click', function() {
+ $(this).stop().fadeOut('fast');
+ });
+
+ $('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);
+
+elgg.register_event_handler('init', 'system', elgg.ui.init); \ No newline at end of file
diff --git a/js/lib/ui.widgets.js b/js/lib/ui.widgets.js
new file mode 100644
index 000000000..1e3163709
--- /dev/null
+++ b/js/lib/ui.widgets.js
@@ -0,0 +1,131 @@
+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;
+
+elgg.register_event_handler('init', 'system', elgg.ui.widgets.init); \ No newline at end of file
diff --git a/js/tests/ElggAjaxOptionsTest.js b/js/tests/ElggAjaxOptionsTest.js
new file mode 100644
index 000000000..8a2b7f574
--- /dev/null
+++ b/js/tests/ElggAjaxOptionsTest.js
@@ -0,0 +1,62 @@
+/**
+ * Tests elgg.ajax.handleOptions() with all of the possible valid inputs
+ */
+ElggAjaxOptionsTest = TestCase("ElggAjaxOptionsTest");
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsNoArgs = function() {
+ assertNotUndefined(elgg.ajax.handleOptions());
+};
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsUrl = function() {
+ var url = 'url',
+ result = elgg.ajax.handleOptions(url);
+
+ assertEquals(url, result.url);
+};
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsDataOnly = function() {
+ var options = {},
+ result = elgg.ajax.handleOptions(options);
+
+ assertEquals(options, result.data);
+};
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsOptions = function() {
+ var options = {data:{arg:1}},
+ result = elgg.ajax.handleOptions(options);
+
+ assertEquals(options, result);
+
+ function func() {}
+ options = {success: func};
+ result = elgg.ajax.handleOptions(options);
+
+ assertEquals(options, result);
+};
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsUrlThenDataOnly = function() {
+ var url = 'url',
+ options = {arg:1},
+ result = elgg.ajax.handleOptions(url, options);
+
+ assertEquals(url, result.url);
+ assertEquals(options, result.data);
+};
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsUrlThenSuccessOnly = function() {
+ var url = 'url',
+ success = function() {},
+ result = elgg.ajax.handleOptions(url, success);
+
+ assertEquals(url, result.url);
+ assertEquals(success, result.success);
+};
+
+ElggAjaxOptionsTest.prototype.testHandleOptionsAcceptsUrlThenOptions = function() {
+ var url = 'url',
+ options = {data:{arg:1}},
+ result = elgg.ajax.handleOptions(url, options);
+
+ assertEquals(url, result.url);
+ assertEquals(options.data, result.data);
+}; \ No newline at end of file
diff --git a/js/tests/ElggAjaxTest.js b/js/tests/ElggAjaxTest.js
new file mode 100644
index 000000000..1fa5daca5
--- /dev/null
+++ b/js/tests/ElggAjaxTest.js
@@ -0,0 +1,59 @@
+/**
+ * Makes sure that each of the helper ajax functions ends up calling $.ajax
+ * with the right options.
+ */
+ElggAjaxTest = TestCase("ElggAjaxTest");
+
+ElggAjaxTest.prototype.setUp = function() {
+
+ this.wwwroot = elgg.config.wwwroot;
+ this.ajax = $.ajax;
+
+ elgg.config.wwwroot = 'http://www.elgg.org/';
+
+ $.ajax = function(options) {
+ return options;
+ };
+};
+
+ElggAjaxTest.prototype.tearDown = function() {
+ $.ajax = this.ajax;
+ elgg.config.wwwroot = this.wwwroot;
+};
+
+ElggAjaxTest.prototype.testElggAjax = function() {
+ assertEquals(elgg.config.wwwroot, elgg.ajax().url);
+};
+
+ElggAjaxTest.prototype.testElggGet = function() {
+ assertEquals('get', elgg.get().type);
+};
+
+ElggAjaxTest.prototype.testElggGetJSON = function() {
+ assertEquals('json', elgg.getJSON().dataType);
+};
+
+ElggAjaxTest.prototype.testElggPost = function() {
+ assertEquals('post', elgg.post().type);
+};
+
+ElggAjaxTest.prototype.testElggAction = function() {
+ assertException(function() { elgg.action(); });
+ assertException(function() { elgg.action({}); });
+
+ var result = elgg.action('action');
+ assertEquals('post', result.type);
+ assertEquals('json', result.dataType);
+ assertEquals(elgg.config.wwwroot + 'action/action', result.url);
+ assertEquals(elgg.security.token.__elgg_ts, result.data.__elgg_ts);
+};
+
+ElggAjaxTest.prototype.testElggAPI = function() {
+ assertException(function() { elgg.api(); });
+ assertException(function() { elgg.api({}); });
+
+ var result = elgg.api('method');
+ assertEquals('json', result.dataType);
+ assertEquals('method', result.data.method);
+ assertEquals(elgg.config.wwwroot + 'services/api/rest/json/', result.url);
+};
diff --git a/js/tests/ElggEventsTest.js b/js/tests/ElggEventsTest.js
new file mode 100644
index 000000000..cc30e8418
--- /dev/null
+++ b/js/tests/ElggEventsTest.js
@@ -0,0 +1,28 @@
+ElggEventsTest = TestCase("ElggEventsTest");
+
+ElggEventsTest.prototype.setUp = function() {
+ elgg.config.events = {};
+ elgg.provide('elgg.config.events.all.all');
+};
+
+ElggEventsTest.prototype.testEventHandlersMustBeFunctions = function() {
+ assertException(function() { elgg.register_event_handler('str', 'str', 'oops'); });
+};
+
+ElggEventsTest.prototype.testReturnValueDefaultsToTrue = function() {
+ assertTrue(elgg.trigger_event('fee', 'fum'));
+
+ elgg.register_event_handler('fee', 'fum', function() {});
+ assertTrue(elgg.trigger_event('fee', 'fum'));
+};
+
+ElggEventsTest.prototype.testCanGlomEventsWithAll = function() {
+ elgg.register_event_handler('all', 'bar', function() { throw new Error(); });
+ assertException("all,bar", function() { elgg.trigger_event('foo', 'bar'); });
+
+ elgg.register_event_handler('foo', 'all', function() { throw new Error(); });
+ assertException("foo,all", function() { elgg.trigger_event('foo', 'baz'); });
+
+ elgg.register_event_handler('all', 'all', function() { throw new Error(); });
+ assertException("all,all", function() { elgg.trigger_event('pinky', 'winky'); });
+}; \ No newline at end of file
diff --git a/js/tests/ElggLanguagesTest.js b/js/tests/ElggLanguagesTest.js
new file mode 100644
index 000000000..950d5d3b8
--- /dev/null
+++ b/js/tests/ElggLanguagesTest.js
@@ -0,0 +1,45 @@
+ElggLanguagesTest = TestCase("ElggLanguagesTest");
+
+ElggLanguagesTest.prototype.setUp = function() {
+ this.ajax = $.ajax;
+
+ //Immediately execute some dummy "returned" javascript instead of sending
+ //an actual ajax request
+ $.ajax = function(settings) {
+ var lang = settings.data.js.split('/')[1];
+ elgg.config.translations[lang] = {'language':lang};
+ };
+};
+
+ElggLanguagesTest.prototype.tearDown = function() {
+ $.ajax = this.ajax;
+
+ //clear translations
+ elgg.config.translations['en'] = undefined;
+ elgg.config.translations['aa'] = undefined;
+};
+
+ElggLanguagesTest.prototype.testLoadTranslations = function() {
+ assertUndefined(elgg.config.translations['en']);
+ assertUndefined(elgg.config.translations['aa']);
+
+ elgg.reload_all_translations();
+ elgg.reload_all_translations('aa');
+
+ assertNotUndefined(elgg.config.translations['en']['language']);
+ assertNotUndefined(elgg.config.translations['aa']['language']);
+};
+
+ElggLanguagesTest.prototype.testElggEchoTranslates = function() {
+ elgg.reload_all_translations('en');
+ elgg.reload_all_translations('aa');
+
+ assertEquals('en', elgg.echo('language'));
+ assertEquals('aa', elgg.echo('language', 'aa'));
+};
+
+ElggLanguagesTest.prototype.testElggEchoFallsBackToDefaultLanguage = function() {
+ elgg.reload_all_translations('en');
+ assertEquals('en', elgg.echo('language', 'aa'));
+};
+
diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js
new file mode 100644
index 000000000..035b60325
--- /dev/null
+++ b/js/tests/ElggLibTest.js
@@ -0,0 +1,88 @@
+/**
+ * Test basic elgg library functions
+ */
+ElggLibTest = TestCase("ElggLibTest");
+
+ElggLibTest.prototype.testGlobal = function() {
+ assertTrue(window === elgg.global);
+};
+
+ElggLibTest.prototype.testAssertTypeOf = function() {
+ var noexceptions = [
+ ['string', ''],
+ ['object', {}],
+ ['boolean', true],
+ ['boolean', false],
+ ['undefined', undefined],
+ ['number', 0],
+ ['function', function() {}],
+ ];
+
+ for (var i in noexceptions) {
+ assertNoException(function() {
+ elgg.assertTypeOf.apply(elgg, noexceptions[i]);
+ });
+ }
+
+ var exceptions = [
+ ['function', {}],
+ ['object', function() {}],
+ ];
+
+ for (var i in exceptions) {
+ assertException(function() {
+ elgg.assertTypeOf.apply(elgg, exceptions[i]);
+ });
+ }
+};
+
+ElggLibTest.prototype.testProvide = function() {
+ elgg.provide('foo.bar.baz');
+
+ assertNotUndefined(foo);
+ assertNotUndefined(foo.bar);
+ assertNotUndefined(foo.bar.baz);
+
+ var str = foo.bar.baz.oof = "don't overwrite me";
+
+ elgg.provide('foo.bar.baz');
+
+ assertEquals(str, foo.bar.baz.oof);
+};
+
+ElggLibTest.prototype.testRequire = function() {
+ /* Try requiring bogus input */
+ assertException(function(){ elgg.require(''); });
+ assertException(function(){ elgg.require('garbage'); });
+ assertException(function(){ elgg.require('gar.ba.ge'); });
+
+ assertNoException(function(){ elgg.require('jQuery'); });
+ assertNoException(function(){ elgg.require('elgg'); });
+ assertNoException(function(){ elgg.require('elgg.config'); });
+ assertNoException(function(){ elgg.require('elgg.security'); });
+};
+
+ElggLibTest.prototype.testInherit = function() {
+ function Base() {}
+ function Child() {}
+
+ elgg.inherit(Child, Base);
+
+ assertInstanceOf(Base, new Child());
+ assertEquals(Child, Child.prototype.constructor);
+};
+
+ElggLibTest.prototype.testExtendUrl = function() {
+ elgg.config.wwwroot = "http://elgg.org/";
+
+ var inputs = [
+ [elgg.config.wwwroot, ''],
+ [elgg.config.wwwroot + 'pg/test', 'pg/test'],
+ ['http://google.com', 'http://google.com'],
+ ['//example.com', '//example.com'],
+ ];
+
+ for (var i in inputs) {
+ assertEquals(inputs[i][0], elgg.normalize_url(inputs[i][1]));
+ }
+}; \ No newline at end of file
diff --git a/js/tests/ElggPriorityListTest.js b/js/tests/ElggPriorityListTest.js
new file mode 100644
index 000000000..2549e0ee0
--- /dev/null
+++ b/js/tests/ElggPriorityListTest.js
@@ -0,0 +1,47 @@
+ElggPriorityListTest = TestCase("ElggPriorityListTest");
+
+ElggPriorityListTest.prototype.setUp = function() {
+ this.list = new elgg.ElggPriorityList();
+};
+
+ElggPriorityListTest.prototype.tearDown = function() {
+ this.list = null;
+};
+
+ElggPriorityListTest.prototype.testInsert = function() {
+ this.list.insert('foo');
+
+ assertEquals('foo', this.list.priorities_[500][0]);
+
+ this.list.insert('bar', 501);
+
+ assertEquals('foo', this.list.priorities_[501][0]);
+};
+
+ElggPriorityListTest.prototype.testInsertRespectsPriority = function() {
+ var values = [5, 4, 3, 2, 1, 0];
+
+ for (var i in values) {
+ this.list.insert(values[i], values[i]);
+ }
+
+ this.list.forEach(function(elem, idx)) {
+ assertEquals(elem, idx);
+ }
+};
+
+ElggPriorityListTest.prototype.testInsertHandlesDuplicatePriorities = function() {
+ values = [0, 1, 2, 3, 4, 5, 6, 7, 8 , 9];
+
+ for (var i in values) {
+ this.list.insert(values[i], values[i]/3);
+ }
+
+ this.list.forEach(function(elem, idx) {
+ assertEquals(elem, idx);
+ });
+};
+
+ElggPriorityListTest.prototype.testEveryDefaultsToTrue = function() {
+ assertTrue(this.list.every(function() {}));
+}; \ No newline at end of file
diff --git a/js/tests/ElggSecurityTest.js b/js/tests/ElggSecurityTest.js
new file mode 100644
index 000000000..4324f5671
--- /dev/null
+++ b/js/tests/ElggSecurityTest.js
@@ -0,0 +1,51 @@
+ElggSecurityTest = TestCase("ElggSecurityTest");
+
+ElggSecurityTest.prototype.setUp = function() {
+ //fill with fake, but reasonable, values for testing
+ this.ts = elgg.security.token.__elgg_ts = 12345;
+ this.token = elgg.security.token.__elgg_token = 'abcdef';
+};
+
+ElggSecurityTest.prototype.testAddTokenAcceptsUndefined = function() {
+ var input,
+ expected = {
+ __elgg_ts: this.ts,
+ __elgg_token: this.token
+ };
+
+ assertEquals(expected, elgg.security.addToken(input));
+};
+
+ElggSecurityTest.prototype.testAddTokenAcceptsObject = function() {
+ var input = {},
+ expected = {
+ __elgg_ts: this.ts,
+ __elgg_token: this.token
+ };
+
+ assertEquals(expected, elgg.security.addToken(input));
+};
+
+ElggSecurityTest.prototype.testAddTokenAcceptsString = function() {
+ var input,
+ str = "__elgg_ts=" + this.ts + "&__elgg_token=" + this.token;
+
+ input = "";
+ assertEquals(str, elgg.security.addToken(input));
+
+ input = "data=sofar";
+ assertEquals(input+'&'+str, elgg.security.addToken(input));
+
+};
+
+ElggSecurityTest.prototype.testSetTokenSetsElggSecurityToken = function() {
+ var json = {
+ __elgg_ts: 4567,
+ __elgg_token: 'abcdef'
+ };
+
+ elgg.security.setToken(json);
+ assertEquals(json, elgg.security.token);
+};
+
+
diff --git a/js/tests/ElggSessionTest.js b/js/tests/ElggSessionTest.js
new file mode 100644
index 000000000..0245e9e90
--- /dev/null
+++ b/js/tests/ElggSessionTest.js
@@ -0,0 +1,36 @@
+ElggSessionTest = TestCase("ElggSessionTest");
+
+ElggSessionTest.prototype.testGetCookie = function() {
+ assertEquals(document.cookie, elgg.session.cookie());
+};
+
+ElggSessionTest.prototype.testGetCookieKey = function() {
+ document.cookie = "name=value";
+ assertEquals('value', elgg.session.cookie('name'));
+
+ document.cookie = "name=value2";
+ assertEquals('value2', elgg.session.cookie('name'));
+
+ document.cookie = "name=value";
+ document.cookie = "name2=value2";
+ assertEquals('value', elgg.session.cookie('name'));
+ assertEquals('value2', elgg.session.cookie('name2'));
+};
+
+ElggSessionTest.prototype.testSetCookieKey = function() {
+ elgg.session.cookie('name', 'value');
+ assertEquals('value', elgg.session.cookie('name'));
+
+ elgg.session.cookie('name', 'value2');
+ assertEquals('value2', elgg.session.cookie('name'));
+
+ elgg.session.cookie('name', 'value');
+ elgg.session.cookie('name2', 'value2');
+ assertEquals('value', elgg.session.cookie('name'));
+ assertEquals('value2', elgg.session.cookie('name2'));
+
+ elgg.session.cookie('name', null);
+ elgg.session.cookie('name2', null);
+ assertUndefined(elgg.session.cookie('name'));
+ assertUndefined(elgg.session.cookie('name2'));
+}; \ No newline at end of file