diff --git a/ajax/libs/django.js/0.8.1/django.js b/ajax/libs/django.js/0.8.1/django.js
new file mode 100644
index 000000000..ec94fe8ab
--- /dev/null
+++ b/ajax/libs/django.js/0.8.1/django.js
@@ -0,0 +1,245 @@
+(function($){
+
+ "use strict";
+
+ function DjangoJsError(message) {
+ this.name = "DjangoJsError";
+ this.message = (message || "");
+ }
+ DjangoJsError.prototype = new Error();
+ DjangoJsError.prototype.constructor = DjangoJsError;
+
+ var Django = window.Django = {
+
+ token_regex: /<\w*>/g,
+ named_token_regex: /<(\w+)>/g,
+
+ /**
+ * Initialize required attributes
+ */
+ initialize: function(params) {
+ params = params || {};
+ if (typeof params.urls === 'string' || params.urls instanceof String) {
+ $.get(params.urls, function(urls) {
+ Django.urls = urls;
+ });
+ } else {
+ this.urls = params.urls || window.DJANGO_JS_URLS;
+ }
+ if (typeof params.context === 'string' || params.context instanceof String) {
+ $.get(params.context, function(context) {
+ Django.set_context(context);
+ });
+ } else {
+ this.set_context(params.context || window.DJANGO_JS_CONTEXT);
+ }
+ },
+
+ set_context: function(context) {
+ this.context = context;
+ this.user = context.user;
+ if (this.user) {
+ /**
+ * Equivalent to ``User.has_perm`` function.
+ */
+ this.user.has_perm = function(permission) {
+ return this.permissions.indexOf(permission) > -1;
+ };
+ }
+ },
+
+ /**
+ * Reload context and user
+ */
+ reload: function(callback) {
+ $.get(this.url('django_js_context'), function(context) {
+ Django.set_context(context);
+ if (callback instanceof Function) {
+ callback();
+ }
+ });
+ },
+
+ /**
+ * Equivalent to ``reverse`` function and ``url`` template tag.
+ */
+ url: function(name, args) {
+ var pattern = this.urls[name] || false,
+ url = pattern,
+ key, regex, token, parts;
+
+ if (!url) {
+ throw new DjangoJsError('URL for view "' + name + '" not found');
+ }
+
+ if (args === undefined) {
+ return url;
+ }
+
+ if ($.isArray(args)) {
+ return this._url_from_array(name, pattern, args);
+ }
+ else if ($.isPlainObject(args)) {
+ return this._url_from_object(name, pattern, args);
+ }
+ else {
+ var argsArray = Array.prototype.slice.apply(arguments, [1, arguments.length]);
+ return this._url_from_array(name, pattern, argsArray);
+ }
+ return url;
+ },
+
+ _url_from_array: function(name, pattern, array) {
+ var matches = pattern.match(this.token_regex),
+ parts = pattern.split(this.token_regex),
+ url = parts[0];
+
+ if (!matches && array.length === 0) {
+ return url;
+ }
+
+ if (matches && matches.length != array.length) {
+ throw new DjangoJsError('Wrong number of argument for pattern "' + name + '"');
+ }
+
+
+ for (var idx=0; idx < array.length; idx++) {
+ url += array[idx] + parts[idx + 1];
+ }
+
+ return url;
+ },
+
+ _url_from_object: function(name, pattern, object) {
+ var url = pattern,
+ tokens = pattern.match(this.token_regex);
+
+ if (!tokens) {
+ return url;
+ }
+
+ for (var idx=0; idx < tokens.length; idx++) {
+ var token = tokens[idx],
+ prop = token.slice(1, -1),
+ value = object[prop];
+
+ if (value === undefined) {
+ throw new DjangoJsError('Property "' + prop + '" not found');
+ }
+
+ url = url.replace(token, value);
+ }
+
+ return url;
+ },
+
+ /**
+ * Equivalent to ``static`` template tag.
+ */
+ file: function(filename) {
+ return this.context.STATIC_URL + filename;
+ },
+
+ /**
+ * Equivalent to ``static`` template tag.
+ */
+ 'static': function(filename) {
+ return this.context.STATIC_URL + filename;
+ },
+
+ /**
+ * Return cookie value by name.
+ * cf. https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
+ */
+ _getCookie: function(name) {
+ var cookieValue = null;
+ if (document.cookie && document.cookie !== '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = $.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ },
+
+ /**
+ * Get the CSRF token from the cookie.
+ */
+ csrf_token: function() {
+ return this._getCookie('csrftoken');
+ },
+
+ /**
+ * Equivalent to ``{% csrf_token %}`` template tag.
+ */
+ csrf_element: function() {
+ var token = this.csrf_token(),
+ elem = [
+ ''
+ ];
+
+ return elem.join('');
+ },
+
+ /**
+ * Fix ajax request with CSRF Django middleware.
+ */
+ jquery_csrf: function() {
+ var getCookie = this._getCookie;
+ $(document).ajaxSend(function(event, xhr, settings) {
+ function sameOrigin(url) {
+ // url could be relative or scheme relative or absolute
+ var host = document.location.host; // host + port
+ var protocol = document.location.protocol;
+ var sr_origin = '//' + host;
+ var origin = protocol + sr_origin;
+ // Allow absolute or scheme relative URLs to same origin
+ return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
+ // or any other URL that isn't scheme relative or absolute i.e relative.
+ !(/^(\/\/|http:|https:).*/.test(url));
+ }
+
+ function safeMethod(method) {
+ return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
+ }
+
+ if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
+ xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
+ }
+ });
+ },
+
+ absolute: function(name, args) {
+ if (!this.context.hasOwnProperty('ABSOLUTE_ROOT')) {
+ throw new DjangoJsError('django-absolute needed to call absolute()');
+ }
+ return this.context.ABSOLUTE_ROOT + this.url(name, args);
+ },
+
+ site: function(name, args) {
+ if (!this.context.hasOwnProperty('SITE_ROOT')) {
+ throw new DjangoJsError('django-absolute needed to call site()');
+ }
+ return this.context.SITE_ROOT + this.url(name, args);
+ }
+
+ };
+
+ if (window.DJANGO_JS_INIT) {
+ Django.initialize();
+ }
+
+ if (window.DJANGO_JS_CSRF) {
+ Django.jquery_csrf();
+ }
+
+ return Django;
+
+}(window.jQuery));
diff --git a/ajax/libs/django.js/0.8.1/django.min.js b/ajax/libs/django.js/0.8.1/django.min.js
new file mode 100644
index 000000000..c27fb1b78
--- /dev/null
+++ b/ajax/libs/django.js/0.8.1/django.min.js
@@ -0,0 +1 @@
+(function(e){"use strict";function t(e){this.name="DjangoJsError",this.message=e||""}t.prototype=new Error,t.prototype.constructor=t;var n=window.Django={token_regex:/<\w*>/g,named_token_regex:/<(\w+)>/g,initialize:function(t){t=t||{},typeof t.urls=="string"||t.urls instanceof String?e.get(t.urls,function(e){n.urls=e}):this.urls=t.urls||window.DJANGO_JS_URLS,typeof t.context=="string"||t.context instanceof String?e.get(t.context,function(e){n.set_context(e)}):this.set_context(t.context||window.DJANGO_JS_CONTEXT)},set_context:function(e){this.context=e,this.user=e.user,this.user&&(this.user.has_perm=function(e){return this.permissions.indexOf(e)>-1})},reload:function(t){e.get(this.url("django_js_context"),function(e){n.set_context(e),t instanceof Function&&t()})},url:function(n,r){var i=this.urls[n]||!1,s=i,o,u,a,f;if(!s)throw new t('URL for view "'+n+'" not found');if(r===undefined)return s;if(e.isArray(r))return this._url_from_array(n,i,r);if(e.isPlainObject(r))return this._url_from_object(n,i,r);var l=Array.prototype.slice.apply(arguments,[1,arguments.length]);return this._url_from_array(n,i,l)},_url_from_array:function(e,n,r){var i=n.match(this.token_regex),s=n.split(this.token_regex),o=s[0];if(!i&&r.length===0)return o;if(i&&i.length!=r.length)throw new t('Wrong number of argument for pattern "'+e+'"');for(var u=0;u'];return t.join("")},jquery_csrf:function(){var t=this._getCookie;e(document).ajaxSend(function(e,n,r){function i(e){var t=document.location.host,n=document.location.protocol,r="//"+t,i=n+r;return e==i||e.slice(0,i.length+1)==i+"/"||e==r||e.slice(0,r.length+1)==r+"/"||!/^(\/\/|http:|https:).*/.test(e)}function s(e){return/^(GET|HEAD|OPTIONS|TRACE)$/.test(e)}!s(r.type)&&i(r.url)&&n.setRequestHeader("X-CSRFToken",t("csrftoken"))})},absolute:function(e,n){if(!this.context.hasOwnProperty("ABSOLUTE_ROOT"))throw new t("django-absolute needed to call absolute()");return this.context.ABSOLUTE_ROOT+this.url(e,n)},site:function(e,n){if(!this.context.hasOwnProperty("SITE_ROOT"))throw new t("django-absolute needed to call site()");return this.context.SITE_ROOT+this.url(e,n)}};return window.DJANGO_JS_INIT&&n.initialize(),window.DJANGO_JS_CSRF&&n.jquery_csrf(),n})(window.jQuery);
diff --git a/ajax/libs/django.js/package.json b/ajax/libs/django.js/package.json
new file mode 100644
index 000000000..a5b730740
--- /dev/null
+++ b/ajax/libs/django.js/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "django.js",
+ "filename": "django.min.js",
+ "description": "Django.js provides tools for JavaScript development with Django.",
+ "homepage": "https://github.com/noirbizarre/django.js/",
+ "author": {
+ "name": "Axel Haustant",
+ "url": "http://noirbizarre.info/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/noirbizarre/django.js.git"
+ },
+ "licenses": [
+ {
+ "type": "LGPL 3",
+ "url": "http://www.gnu.org/licenses/lgpl-3.0.txt"
+ }
+ ],
+ "version": "0.8.1",
+ "main": [
+ "djangojs/static/js/djangojs/django.js",
+ "djangojs/static/js/djangojs/django.min.js"
+ ],
+ "dependencies": {
+ "jquery": ">=1.8"
+ },
+ "ignore": [
+ "**/.*",
+ "**/*.py",
+ "**/*.sh",
+ "**/*.rc",
+ "**/*.pip",
+ "**/*.po",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests",
+ "requirements",
+ "templates",
+ "tox.ini"
+ ]
+}