Move new dajaxice into place.
- Legacy-Id: 7623
This commit is contained in:
parent
4a2c615c3f
commit
59a244b884
1
dajaxice/__init__.py
Normal file
1
dajaxice/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__version__ = (0, 5, 5, 'beta')
|
137
dajaxice/core/Dajaxice.py
Normal file
137
dajaxice/core/Dajaxice.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
import logging
|
||||
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
log = logging.getLogger('dajaxice')
|
||||
|
||||
|
||||
class DajaxiceFunction(object):
|
||||
""" Basic representation of a dajaxice ajax function."""
|
||||
|
||||
def __init__(self, function, name, method):
|
||||
self.function = function
|
||||
self.name = name
|
||||
self.method = method
|
||||
|
||||
def call(self, *args, **kwargs):
|
||||
""" Call the function. """
|
||||
return self.function(*args, **kwargs)
|
||||
|
||||
|
||||
class DajaxiceModule(object):
|
||||
""" Basic representation of a dajaxice module. """
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
self.functions = {}
|
||||
self.submodules = {}
|
||||
|
||||
def add(self, name, function):
|
||||
""" Add this function at the ``name`` deep. If the submodule already
|
||||
exists, recusively call the add method into the submodule. If not,
|
||||
create the module and call the add method."""
|
||||
|
||||
# If this is not the final function name (there are more modules)
|
||||
# split the name again an register a new submodule.
|
||||
if '.' in name:
|
||||
module, extra = name.split('.', 1)
|
||||
if module not in self.submodules:
|
||||
self.submodules[module] = DajaxiceModule(module)
|
||||
self.submodules[module].add(extra, function)
|
||||
else:
|
||||
self.functions[name] = function
|
||||
|
||||
|
||||
class Dajaxice(object):
|
||||
|
||||
def __init__(self):
|
||||
self._registry = {}
|
||||
self._modules = None
|
||||
|
||||
def register(self, function, name=None, method='POST'):
|
||||
"""
|
||||
Register this function as a dajaxice function.
|
||||
|
||||
If no name is provided, the module and the function name will be used.
|
||||
The final (customized or not) must be unique. """
|
||||
|
||||
method = self.clean_method(method)
|
||||
|
||||
# Generate a default name
|
||||
if not name:
|
||||
module = ''.join(str(function.__module__).rsplit('.ajax', 1))
|
||||
name = '.'.join((module, function.__name__))
|
||||
|
||||
if ':' in name:
|
||||
log.error('Ivalid function name %s.' % name)
|
||||
return
|
||||
|
||||
# Check for already registered functions
|
||||
if name in self._registry:
|
||||
log.error('%s was already registered.' % name)
|
||||
return
|
||||
|
||||
# Create the dajaxice function.
|
||||
function = DajaxiceFunction(function=function,
|
||||
name=name,
|
||||
method=method)
|
||||
|
||||
# Register this new ajax function
|
||||
self._registry[name] = function
|
||||
|
||||
def is_callable(self, name, method):
|
||||
""" Return if the function callable or not. """
|
||||
return name in self._registry and self._registry[name].method == method
|
||||
|
||||
def clean_method(self, method):
|
||||
""" Clean the http method. """
|
||||
method = method.upper()
|
||||
if method not in ['GET', 'POST']:
|
||||
method = 'POST'
|
||||
return method
|
||||
|
||||
def get(self, name):
|
||||
""" Return the dajaxice function."""
|
||||
return self._registry[name]
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
""" Return an easy to loop module hierarchy with all the functions."""
|
||||
if not self._modules:
|
||||
self._modules = DajaxiceModule()
|
||||
for name, function in self._registry.items():
|
||||
self._modules.add(name, function)
|
||||
return self._modules
|
||||
|
||||
LOADING_DAJAXICE = False
|
||||
|
||||
|
||||
def dajaxice_autodiscover():
|
||||
"""
|
||||
Auto-discover INSTALLED_APPS ajax.py modules and fail silently when
|
||||
not present. NOTE: dajaxice_autodiscover was inspired/copied from
|
||||
django.contrib.admin autodiscover
|
||||
"""
|
||||
global LOADING_DAJAXICE
|
||||
if LOADING_DAJAXICE:
|
||||
return
|
||||
LOADING_DAJAXICE = True
|
||||
|
||||
import imp
|
||||
from django.conf import settings
|
||||
|
||||
for app in settings.INSTALLED_APPS:
|
||||
|
||||
try:
|
||||
app_path = import_module(app).__path__
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
try:
|
||||
imp.find_module('ajax', app_path)
|
||||
except ImportError:
|
||||
continue
|
||||
|
||||
import_module("%s.ajax" % app)
|
||||
|
||||
LOADING_DAJAXICE = False
|
38
dajaxice/core/__init__.py
Normal file
38
dajaxice/core/__init__.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
from django.conf import settings
|
||||
|
||||
from Dajaxice import Dajaxice, dajaxice_autodiscover
|
||||
|
||||
|
||||
class DajaxiceConfig(object):
|
||||
""" Provide an easy to use way to read the dajaxice configuration and
|
||||
return the default values if no configuration is present."""
|
||||
|
||||
default_config = {'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT': True,
|
||||
'DAJAXICE_JSON2_JS_IMPORT': True,
|
||||
'DAJAXICE_EXCEPTION': 'DAJAXICE_EXCEPTION',
|
||||
'DAJAXICE_MEDIA_PREFIX': 'dajaxice'}
|
||||
|
||||
def __getattr__(self, name):
|
||||
""" Return the customized value for a setting (if it exists) or the
|
||||
default value if not. """
|
||||
|
||||
if name in self.default_config:
|
||||
if hasattr(settings, name):
|
||||
return getattr(settings, name)
|
||||
return self.default_config.get(name)
|
||||
return None
|
||||
|
||||
@property
|
||||
def dajaxice_url(self):
|
||||
return r'^%s/' % self.DAJAXICE_MEDIA_PREFIX
|
||||
|
||||
@property
|
||||
def django_settings(self):
|
||||
return settings
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
return dajaxice_functions.modules
|
||||
|
||||
dajaxice_functions = Dajaxice()
|
||||
dajaxice_config = DajaxiceConfig()
|
48
dajaxice/decorators.py
Normal file
48
dajaxice/decorators.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
import functools
|
||||
|
||||
from dajaxice.core import dajaxice_functions
|
||||
|
||||
|
||||
def dajaxice_register(*dargs, **dkwargs):
|
||||
""" Register some function as a dajaxice function
|
||||
|
||||
For legacy purposes, if only a function is passed register it a simple
|
||||
single ajax function using POST, i.e:
|
||||
|
||||
@dajaxice_register
|
||||
def ajax_function(request):
|
||||
...
|
||||
|
||||
After 0.5, dajaxice allow to customize the http method and the final name
|
||||
of the registered function. This decorator covers both the legacy and
|
||||
the new functionality, i.e:
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def ajax_function(request):
|
||||
...
|
||||
|
||||
@dajaxice_register(method='GET', name='my.custom.name')
|
||||
def ajax_function(request):
|
||||
...
|
||||
|
||||
You can also register the same function to use a different http method
|
||||
and/or use a different name.
|
||||
|
||||
@dajaxice_register(method='GET', name='users.get')
|
||||
@dajaxice_register(method='POST', name='users.update')
|
||||
def ajax_function(request):
|
||||
...
|
||||
"""
|
||||
|
||||
if len(dargs) and not dkwargs:
|
||||
function = dargs[0]
|
||||
dajaxice_functions.register(function)
|
||||
return function
|
||||
|
||||
def decorator(function):
|
||||
@functools.wraps(function)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
return function(request, *args, **kwargs)
|
||||
dajaxice_functions.register(function, *dargs, **dkwargs)
|
||||
return wrapper
|
||||
return decorator
|
10
dajaxice/exceptions.py
Normal file
10
dajaxice/exceptions.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
class DajaxiceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class FunctionNotCallableError(DajaxiceError):
|
||||
pass
|
||||
|
||||
|
||||
class DajaxiceImportError(DajaxiceError):
|
||||
pass
|
75
dajaxice/finders.py
Normal file
75
dajaxice/finders.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.template import Context
|
||||
from django.template.loader import get_template
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
|
||||
|
||||
class VirtualStorage(finders.FileSystemStorage):
|
||||
"""" Mock a FileSystemStorage to build tmp files on demand."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._files_cache = {}
|
||||
super(VirtualStorage, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_or_create_file(self, path):
|
||||
if path not in self.files:
|
||||
return ''
|
||||
|
||||
data = getattr(self, self.files[path])()
|
||||
|
||||
try:
|
||||
current_file = open(self._files_cache[path])
|
||||
current_data = current_file.read()
|
||||
current_file.close()
|
||||
if current_data != data:
|
||||
os.remove(path)
|
||||
raise Exception("Invalid data")
|
||||
except Exception:
|
||||
handle, tmp_path = tempfile.mkstemp()
|
||||
tmp_file = open(tmp_path, 'w')
|
||||
tmp_file.write(data)
|
||||
tmp_file.close()
|
||||
self._files_cache[path] = tmp_path
|
||||
|
||||
return self._files_cache[path]
|
||||
|
||||
def exists(self, name):
|
||||
return name in self.files
|
||||
|
||||
def listdir(self, path):
|
||||
folders, files = [], []
|
||||
for f in self.files:
|
||||
if f.startswith(path):
|
||||
f = f.replace(path, '', 1)
|
||||
if os.sep in f:
|
||||
folders.append(f.split(os.sep, 1)[0])
|
||||
else:
|
||||
files.append(f)
|
||||
return folders, files
|
||||
|
||||
def path(self, name):
|
||||
try:
|
||||
path = self.get_or_create_file(name)
|
||||
except ValueError:
|
||||
raise SuspiciousOperation("Attempted access to '%s' denied." % name)
|
||||
return os.path.normpath(path)
|
||||
|
||||
|
||||
class DajaxiceStorage(VirtualStorage):
|
||||
|
||||
files = {os.path.join('dajaxice', 'dajaxice.core.js'): 'dajaxice_core_js'}
|
||||
|
||||
def dajaxice_core_js(self):
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
|
||||
dajaxice_autodiscover()
|
||||
|
||||
c = Context({'dajaxice_config': dajaxice_config})
|
||||
return get_template(os.path.join('dajaxice', 'dajaxice.core.js')).render(c)
|
||||
|
||||
|
||||
class DajaxiceFinder(finders.BaseStorageFinder):
|
||||
storage = DajaxiceStorage()
|
1
dajaxice/models.py
Normal file
1
dajaxice/models.py
Normal file
|
@ -0,0 +1 @@
|
|||
# Don't delete me
|
145
dajaxice/templates/dajaxice/dajaxice.core.js
Normal file
145
dajaxice/templates/dajaxice/dajaxice.core.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
{% load url from future %}
|
||||
var Dajaxice = {
|
||||
|
||||
{% with module=dajaxice_config.modules top='top' %}
|
||||
{% include "dajaxice/dajaxice_function_loop.js" %}
|
||||
{% endwith %}
|
||||
|
||||
{% for name, module in dajaxice_config.modules.submodules.items %}
|
||||
{% include "dajaxice/dajaxice_module_loop.js" %},
|
||||
{% endfor %}
|
||||
|
||||
get_cookie: 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 = cookies[i].toString().replace(/^\s+/, "").replace(/\s+$/, "");
|
||||
// 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;
|
||||
},
|
||||
|
||||
call: function(dajaxice_function, method, dajaxice_callback, argv, custom_settings)
|
||||
{
|
||||
var custom_settings = custom_settings || {},
|
||||
error_callback = Dajaxice.get_setting('default_exception_callback');
|
||||
|
||||
if('error_callback' in custom_settings && typeof(custom_settings['error_callback']) == 'function'){
|
||||
error_callback = custom_settings['error_callback'];
|
||||
}
|
||||
|
||||
var send_data = 'argv='+encodeURIComponent(JSON.stringify(argv)),
|
||||
oXMLHttpRequest = new XMLHttpRequest,
|
||||
endpoint = '{% url 'dajaxice-endpoint' %}'+dajaxice_function+'/';
|
||||
|
||||
if(method == 'GET'){
|
||||
endpoint = endpoint + '?' + send_data;
|
||||
}
|
||||
oXMLHttpRequest.open(method, endpoint);
|
||||
oXMLHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
oXMLHttpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
oXMLHttpRequest.setRequestHeader("X-CSRFToken", Dajaxice.get_cookie('{{ dajaxice_config.django_settings.CSRF_COOKIE_NAME }}'));
|
||||
oXMLHttpRequest.onreadystatechange = function() {
|
||||
if (this.readyState == XMLHttpRequest.DONE) {
|
||||
if(this.responseText == Dajaxice.EXCEPTION || !(this.status in Dajaxice.valid_http_responses())){
|
||||
error_callback();
|
||||
}
|
||||
else{
|
||||
var response;
|
||||
try {
|
||||
response = JSON.parse(this.responseText);
|
||||
}
|
||||
catch (exception) {
|
||||
response = this.responseText;
|
||||
}
|
||||
dajaxice_callback(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(method == 'POST'){
|
||||
oXMLHttpRequest.send(send_data);
|
||||
}
|
||||
else{
|
||||
oXMLHttpRequest.send();
|
||||
}
|
||||
return oXMLHttpRequest;
|
||||
},
|
||||
|
||||
setup: function(settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
},
|
||||
|
||||
get_setting: function(key){
|
||||
if(this.settings == undefined || this.settings[key] == undefined){
|
||||
return Dajaxice.default_settings[key];
|
||||
}
|
||||
return this.settings[key];
|
||||
},
|
||||
|
||||
valid_http_responses: function(){
|
||||
return {200: null, 301: null, 302: null, 304: null}
|
||||
},
|
||||
|
||||
EXCEPTION: '{{ dajaxice_config.DAJAXICE_EXCEPTION }}',
|
||||
default_settings: {'default_exception_callback': function(){ console.log('Dajaxice: Something went wrong.')}}
|
||||
};
|
||||
|
||||
window['Dajaxice'] = Dajaxice;
|
||||
|
||||
{% comment %}
|
||||
/*
|
||||
XMLHttpRequest.js Compiled with Google Closure
|
||||
|
||||
XMLHttpRequest.js Copyright (C) 2008 Sergey Ilinsky (http://www.ilinsky.com)
|
||||
|
||||
This work is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This work is distributed in the hope that it will be useful,
|
||||
but without any warranty; without even the implied warranty of
|
||||
merchantability or fitness for a particular purpose. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
{% endcomment %}
|
||||
{% if dajaxice_config.DAJAXICE_XMLHTTPREQUEST_JS_IMPORT %}
|
||||
(function(){function n(){this._object=h&&!p?new h:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function a(){return new n}function j(b){a.onreadystatechange&&a.onreadystatechange.apply(b);b.dispatchEvent({type:"readystatechange",bubbles:!1,cancelable:!1,timeStamp:new Date+0})}function o(b){try{b.responseText=b._object.responseText}catch(a){}try{var d;var g=b._object,c=g.responseXML,f=g.responseText;i&&(f&&c&&!c.documentElement&&g.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/))&&
|
||||
(c=new window.ActiveXObject("Microsoft.XMLDOM"),c.async=!1,c.validateOnParse=!1,c.loadXML(f));d=c&&(i&&0!==c.parseError||!c.documentElement||c.documentElement&&"parsererror"==c.documentElement.tagName)?null:c;b.responseXML=d}catch(h){}try{b.status=b._object.status}catch(k){}try{b.statusText=b._object.statusText}catch(j){}}function l(b){b._object.onreadystatechange=new window.Function}var h=window.XMLHttpRequest,m=!!window.controllers,i=window.document.all&&!window.opera,p=i&&window.navigator.userAgent.match(/MSIE 7.0/);
|
||||
a.prototype=n.prototype;m&&h.wrapped&&(a.wrapped=h.wrapped);a.UNSENT=0;a.OPENED=1;a.HEADERS_RECEIVED=2;a.LOADING=3;a.DONE=4;a.prototype.readyState=a.UNSENT;a.prototype.responseText="";a.prototype.responseXML=null;a.prototype.status=0;a.prototype.statusText="";a.prototype.priority="NORMAL";a.prototype.onreadystatechange=null;a.onreadystatechange=null;a.onopen=null;a.onsend=null;a.onabort=null;a.prototype.open=function(b,e,d,g,c){delete this._headers;arguments.length<3&&(d=true);this._async=d;var f=
|
||||
this,h=this.readyState,k=null;if(i&&d){k=function(){if(h!=a.DONE){l(f);f.abort()}};window.attachEvent("onunload",k)}a.onopen&&a.onopen.apply(this,arguments);arguments.length>4?this._object.open(b,e,d,g,c):arguments.length>3?this._object.open(b,e,d,g):this._object.open(b,e,d);this.readyState=a.OPENED;j(this);this._object.onreadystatechange=function(){if(!m||d){f.readyState=f._object.readyState;o(f);if(f._aborted)f.readyState=a.UNSENT;else if(f.readyState==a.DONE){delete f._data;l(f);i&&d&&window.detachEvent("onunload",
|
||||
k);h!=f.readyState&&j(f);h=f.readyState}}}};a.prototype.send=function(b){a.onsend&&a.onsend.apply(this,arguments);arguments.length||(b=null);if(b&&b.nodeType){b=window.XMLSerializer?(new window.XMLSerializer).serializeToString(b):b.xml;this._headers["Content-Type"]||this._object.setRequestHeader("Content-Type","application/xml")}this._data=b;a:{this._object.send(this._data);if(m&&!this._async){this.readyState=a.OPENED;for(o(this);this.readyState<a.DONE;){this.readyState++;j(this);if(this._aborted)break a}}}};
|
||||
a.prototype.abort=function(){a.onabort&&a.onabort.apply(this,arguments);if(this.readyState>a.UNSENT)this._aborted=true;this._object.abort();l(this);this.readyState=a.UNSENT;delete this._data};a.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};a.prototype.getResponseHeader=function(b){return this._object.getResponseHeader(b)};a.prototype.setRequestHeader=function(b,a){if(!this._headers)this._headers={};this._headers[b]=a;return this._object.setRequestHeader(b,
|
||||
a)};a.prototype.addEventListener=function(a,e,d){for(var g=0,c;c=this._listeners[g];g++)if(c[0]==a&&c[1]==e&&c[2]==d)return;this._listeners.push([a,e,d])};a.prototype.removeEventListener=function(a,e,d){for(var g=0,c;c=this._listeners[g];g++)if(c[0]==a&&c[1]==e&&c[2]==d)break;c&&this._listeners.splice(g,1)};a.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){},
|
||||
initEvent:function(){}};a.type=="readystatechange"&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var e=0,d;d=this._listeners[e];e++)d[0]==a.type&&!d[2]&&(d[1].handleEvent||d[1]).apply(this,[a])};a.prototype.toString=function(){return"[object XMLHttpRequest]"};a.toString=function(){return"[XMLHttpRequest]"};window.Function.prototype.apply||(window.Function.prototype.apply=function(a,e){e||(e=[]);a.__func=this;a.__func(e[0],e[1],e[2],e[3],
|
||||
e[4]);delete a.__func});window.XMLHttpRequest=a})();
|
||||
{% endif %}
|
||||
|
||||
{% comment %}
|
||||
/*
|
||||
http://www.json.org/json2.js Compiled with Google Closure
|
||||
This code was released under Public Domain by json.org , Thanks!
|
||||
I hope that in the future this code won't be neccessary, but today all browsers doesn't supports JSON.stringify().
|
||||
*/
|
||||
{% endcomment %}
|
||||
{% if dajaxice_config.DAJAXICE_JSON2_JS_IMPORT %}
|
||||
var JSON;JSON||(JSON={});
|
||||
(function(){function k(a){return 10>a?"0"+a:a}function o(a){p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function(a){var c=r[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function m(a,j){var c,d,h,n,g=e,f,b=j[a];b&&("object"===typeof b&&"function"===typeof b.toJSON)&&(b=b.toJSON(a));"function"===typeof i&&(b=i.call(j,a,b));switch(typeof b){case "string":return o(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null";
|
||||
e+=l;f=[];if("[object Array]"===Object.prototype.toString.apply(b)){n=b.length;for(c=0;c<n;c+=1)f[c]=m(c,b)||"null";h=0===f.length?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(i&&"object"===typeof i){n=i.length;for(c=0;c<n;c+=1)"string"===typeof i[c]&&(d=i[c],(h=m(d,b))&&f.push(o(d)+(e?": ":":")+h))}else for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=m(d,b))&&f.push(o(d)+(e?": ":":")+h);h=0===f.length?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+g+"}":"{"+f.join(",")+
|
||||
"}";e=g;return h}}"function"!==typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+k(this.getUTCMonth()+1)+"-"+k(this.getUTCDate())+"T"+k(this.getUTCHours())+":"+k(this.getUTCMinutes())+":"+k(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var q=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e,l,r={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},i;"function"!==typeof JSON.stringify&&(JSON.stringify=function(a,j,c){var d;l=e="";if(typeof c==="number")for(d=0;d<c;d=d+1)l=l+" ";else typeof c==="string"&&(l=c);if((i=j)&&typeof j!=="function"&&(typeof j!=="object"||typeof j.length!=="number"))throw Error("JSON.stringify");return m("",{"":a})});
|
||||
"function"!==typeof JSON.parse&&(JSON.parse=function(a,e){function c(a,d){var g,f,b=a[d];if(b&&typeof b==="object")for(g in b)if(Object.prototype.hasOwnProperty.call(b,g)){f=c(b,g);f!==void 0?b[g]=f:delete b[g]}return e.call(a,d,b)}var d,a=String(a);q.lastIndex=0;q.test(a)&&(a=a.replace(q,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
|
||||
"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){d=eval("("+a+")");return typeof e==="function"?c({"":d},""):d}throw new SyntaxError("JSON.parse");})})();
|
||||
{% endif %}
|
5
dajaxice/templates/dajaxice/dajaxice_function_loop.js
Normal file
5
dajaxice/templates/dajaxice/dajaxice_function_loop.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% for function_name, function in module.functions.items %}
|
||||
{{ function_name }}: function(callback_function, argv, custom_settings){
|
||||
return Dajaxice.call('{{ function.name }}', '{{ function.method }}', callback_function, argv, custom_settings);
|
||||
}{% if not forloop.last or top or module.submodules %},{% endif %}
|
||||
{% endfor %}
|
11
dajaxice/templates/dajaxice/dajaxice_module_loop.js
Normal file
11
dajaxice/templates/dajaxice/dajaxice_module_loop.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
{{ name }}: {
|
||||
{% include "dajaxice/dajaxice_function_loop.js" %}
|
||||
{% with parent_foorloop=forloop %}
|
||||
{% for name, sub_module in module.submodules.items %}
|
||||
{% with filename="dajaxice/dajaxice_module_loop.js" module=sub_module %}
|
||||
{% include filename %}
|
||||
{% endwith %}
|
||||
{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endwith %}
|
0
dajaxice/templatetags/__init__.py
Normal file
0
dajaxice/templatetags/__init__.py
Normal file
35
dajaxice/templatetags/dajaxice_templatetags.py
Normal file
35
dajaxice/templatetags/dajaxice_templatetags.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import logging
|
||||
|
||||
from django import template
|
||||
from django.middleware.csrf import get_token
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import get_storage_class
|
||||
|
||||
staticfiles_storage = get_storage_class(settings.STATICFILES_STORAGE)()
|
||||
|
||||
register = template.Library()
|
||||
|
||||
log = logging.getLogger('dajaxice')
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def dajaxice_js_import(context, csrf=True):
|
||||
""" Return the js script tag for the dajaxice.core.js file
|
||||
If the csrf argument is present and it's ``nocsrf`` dajaxice will not
|
||||
try to mark the request as if it need the csrf token. By default use
|
||||
the dajaxice_js_import template tag will make django set the csrftoken
|
||||
cookie on the current request."""
|
||||
|
||||
csrf = csrf != 'nocsrf'
|
||||
request = context.get('request')
|
||||
|
||||
if request and csrf:
|
||||
get_token(request)
|
||||
elif csrf:
|
||||
log.warning("The 'request' object must be accesible within the "
|
||||
"context. You must add 'django.contrib.messages.context"
|
||||
"_processors.request' to your TEMPLATE_CONTEXT_PROCESSORS "
|
||||
"and render your views using a RequestContext.")
|
||||
|
||||
url = staticfiles_storage.url('dajaxice/dajaxice.core.js')
|
||||
return '<script src="%s" type="text/javascript" charset="utf-8"></script>' % url
|
165
dajaxice/tests/__init__.py
Normal file
165
dajaxice/tests/__init__.py
Normal file
|
@ -0,0 +1,165 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from dajaxice.core import DajaxiceConfig
|
||||
from dajaxice.core.Dajaxice import DajaxiceModule, DajaxiceFunction, Dajaxice
|
||||
from dajaxice.exceptions import FunctionNotCallableError
|
||||
|
||||
|
||||
class DajaxiceModuleTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.module = DajaxiceModule()
|
||||
|
||||
def test_constructor(self):
|
||||
self.assertEqual(self.module.functions, {})
|
||||
self.assertEqual(self.module.submodules, {})
|
||||
|
||||
def test_add_function(self):
|
||||
function = lambda x: x
|
||||
|
||||
self.module.add('test', function)
|
||||
self.assertEqual(self.module.functions, {'test': function})
|
||||
self.assertEqual(self.module.submodules, {})
|
||||
|
||||
self.module.add('foo.bar', function)
|
||||
self.assertEqual(self.module.functions, {'test': function})
|
||||
self.assertEqual(self.module.submodules.keys(), ['foo'])
|
||||
self.assertEqual(self.module.submodules['foo'].functions, {'bar': function})
|
||||
|
||||
|
||||
class DajaxiceFunctionTest(TestCase):
|
||||
|
||||
def test_constructor(self):
|
||||
|
||||
class CalledEception(Exception):
|
||||
pass
|
||||
|
||||
def callback():
|
||||
raise CalledEception
|
||||
|
||||
function = DajaxiceFunction(callback, 'foo', 'POST')
|
||||
|
||||
self.assertEqual(function.function, callback)
|
||||
self.assertEqual(function.name, 'foo')
|
||||
self.assertEqual(function.method, 'POST')
|
||||
self.assertRaises(CalledEception, function.call)
|
||||
|
||||
|
||||
class DajaxiceTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dajaxice = Dajaxice()
|
||||
self.function = lambda x: x
|
||||
|
||||
def test_constructor(self):
|
||||
self.assertEqual(self.dajaxice._registry, {})
|
||||
|
||||
def test_register(self):
|
||||
self.dajaxice.register(self.function, 'foo')
|
||||
self.assertTrue('foo' in self.dajaxice._registry)
|
||||
self.assertEqual(type(self.dajaxice._registry['foo']), DajaxiceFunction)
|
||||
|
||||
def bar_function():
|
||||
return
|
||||
|
||||
self.dajaxice.register(bar_function)
|
||||
self.assertTrue('dajaxice.tests.bar_function' in self.dajaxice._registry)
|
||||
self.assertEqual(type(self.dajaxice._registry['dajaxice.tests.bar_function']), DajaxiceFunction)
|
||||
|
||||
def test__is_callable(self):
|
||||
self.dajaxice.register(self.function, 'foo')
|
||||
self.dajaxice.register(self.function, 'bar', method='GET')
|
||||
|
||||
self.assertTrue(self.dajaxice.is_callable('foo', 'POST'))
|
||||
self.assertTrue(self.dajaxice.is_callable('bar', 'GET'))
|
||||
self.assertFalse(self.dajaxice.is_callable('foo', 'GET'))
|
||||
self.assertFalse(self.dajaxice.is_callable('bar', 'POST'))
|
||||
self.assertFalse(self.dajaxice.is_callable('test', 'POST'))
|
||||
self.assertFalse(self.dajaxice.is_callable('test', 'GET'))
|
||||
|
||||
def test_clean_method(self):
|
||||
self.assertEqual(self.dajaxice.clean_method('post'), 'POST')
|
||||
self.assertEqual(self.dajaxice.clean_method('get'), 'GET')
|
||||
self.assertEqual(self.dajaxice.clean_method('POST'), 'POST')
|
||||
self.assertEqual(self.dajaxice.clean_method('GET'), 'GET')
|
||||
self.assertEqual(self.dajaxice.clean_method('other'), 'POST')
|
||||
|
||||
def test_modules(self):
|
||||
self.dajaxice.register(self.function, 'foo')
|
||||
self.dajaxice.register(self.function, 'bar')
|
||||
|
||||
self.assertEqual(type(self.dajaxice.modules), DajaxiceModule)
|
||||
self.assertEqual(self.dajaxice.modules.functions.keys(), ['foo', 'bar'])
|
||||
|
||||
|
||||
class DajaxiceConfigTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.config = DajaxiceConfig()
|
||||
|
||||
def test_defaults(self):
|
||||
self.assertTrue(self.config.DAJAXICE_XMLHTTPREQUEST_JS_IMPORT)
|
||||
self.assertTrue(self.config.DAJAXICE_JSON2_JS_IMPORT)
|
||||
self.assertEqual(self.config.DAJAXICE_EXCEPTION, 'DAJAXICE_EXCEPTION')
|
||||
self.assertEqual(self.config.DAJAXICE_MEDIA_PREFIX, 'dajaxice')
|
||||
|
||||
dajaxice_url = r'^%s/' % self.config.DAJAXICE_MEDIA_PREFIX
|
||||
self.assertEqual(self.config.dajaxice_url, dajaxice_url)
|
||||
|
||||
self.assertEqual(type(self.config.modules), DajaxiceModule)
|
||||
|
||||
|
||||
class DjangoIntegrationTest(TestCase):
|
||||
|
||||
urls = 'dajaxice.tests.urls'
|
||||
|
||||
def setUp(self):
|
||||
settings.INSTALLED_APPS += ('dajaxice.tests',)
|
||||
|
||||
def test_calling_not_registered_function(self):
|
||||
self.failUnlessRaises(FunctionNotCallableError, self.client.post, '/dajaxice/dajaxice.tests.this_function_not_exist/')
|
||||
|
||||
def test_calling_registered_function(self):
|
||||
response = self.client.post('/dajaxice/dajaxice.tests.test_foo/')
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "bar"}')
|
||||
|
||||
def test_calling_registered_function_with_params(self):
|
||||
|
||||
response = self.client.post('/dajaxice/dajaxice.tests.test_foo_with_params/', {'argv': '{"param1":"value1"}'})
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"param1": "value1"}')
|
||||
|
||||
def test_bad_function(self):
|
||||
|
||||
response = self.client.post('/dajaxice/dajaxice.tests.test_ajax_exception/')
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, "DAJAXICE_EXCEPTION")
|
||||
|
||||
def test_get_register(self):
|
||||
|
||||
response = self.client.get('/dajaxice/dajaxice.tests.test_get_register/')
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "user"}')
|
||||
|
||||
def test_get_custom_name_register(self):
|
||||
|
||||
response = self.client.get('/dajaxice/get_user_data/')
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"bar": "user"}')
|
||||
|
||||
def test_multi_register(self):
|
||||
|
||||
response = self.client.get('/dajaxice/get_multi/')
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "multi"}')
|
||||
|
||||
response = self.client.post('/dajaxice/post_multi/')
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "multi"}')
|
43
dajaxice/tests/ajax.py
Normal file
43
dajaxice/tests/ajax.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_registered_function(request):
|
||||
return ""
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_string(request):
|
||||
return simplejson.dumps({'string': 'hello world'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_ajax_exception(request):
|
||||
raise Exception()
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_foo(request):
|
||||
return simplejson.dumps({'foo': 'bar'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_foo_with_params(request, param1):
|
||||
return simplejson.dumps({'param1': param1})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def test_get_register(request):
|
||||
return simplejson.dumps({'foo': 'user'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET', name="get_user_data")
|
||||
def test_get_with_name_register(request):
|
||||
return simplejson.dumps({'bar': 'user'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET', name="get_multi")
|
||||
@dajaxice_register(name="post_multi")
|
||||
def test_multi_register(request):
|
||||
return simplejson.dumps({'foo': 'multi'})
|
1
dajaxice/tests/requirements.txt
Normal file
1
dajaxice/tests/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Django
|
10
dajaxice/tests/urls.py
Normal file
10
dajaxice/tests/urls.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from django.conf.urls.defaults import *
|
||||
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
|
||||
dajaxice_autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#Dajaxice URLS
|
||||
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
)
|
7
dajaxice/urls.py
Normal file
7
dajaxice/urls.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.conf.urls import patterns, url, include
|
||||
from .views import DajaxiceRequest
|
||||
|
||||
urlpatterns = patterns('dajaxice.views',
|
||||
url(r'^(.+)/$', DajaxiceRequest.as_view(), name='dajaxice-call-endpoint'),
|
||||
url(r'', DajaxiceRequest.as_view(), name='dajaxice-endpoint'),
|
||||
)
|
8
dajaxice/utils.py
Normal file
8
dajaxice/utils.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.http import QueryDict
|
||||
|
||||
|
||||
def deserialize_form(data):
|
||||
"""
|
||||
Create a new QueryDict from a serialized form.
|
||||
"""
|
||||
return QueryDict(query_string=unicode(data).encode('utf-8'))
|
60
dajaxice/views.py
Normal file
60
dajaxice/views.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
import json
|
||||
from django.views.generic.base import View
|
||||
from django.http import HttpResponse, Http404
|
||||
|
||||
from dajaxice.exceptions import FunctionNotCallableError
|
||||
from dajaxice.core import dajaxice_functions, dajaxice_config
|
||||
|
||||
log = logging.getLogger('dajaxice')
|
||||
|
||||
|
||||
def safe_dict(d):
|
||||
"""
|
||||
Recursively clone json structure with UTF-8 dictionary keys
|
||||
http://www.gossamer-threads.com/lists/python/bugs/684379
|
||||
"""
|
||||
if isinstance(d, dict):
|
||||
return dict([(k.encode('utf-8'), safe_dict(v)) for k, v in d.iteritems()])
|
||||
elif isinstance(d, list):
|
||||
return [safe_dict(x) for x in d]
|
||||
else:
|
||||
return d
|
||||
|
||||
|
||||
class DajaxiceRequest(View):
|
||||
""" Handle all the dajaxice xhr requests. """
|
||||
|
||||
def dispatch(self, request, name=None):
|
||||
|
||||
if not name:
|
||||
raise Http404
|
||||
|
||||
# Check if the function is callable
|
||||
if dajaxice_functions.is_callable(name, request.method):
|
||||
|
||||
function = dajaxice_functions.get(name)
|
||||
data = getattr(request, function.method).get('argv', '')
|
||||
|
||||
# Clean the argv
|
||||
if data != 'undefined':
|
||||
try:
|
||||
data = safe_dict(json.loads(data))
|
||||
except Exception:
|
||||
data = {}
|
||||
else:
|
||||
data = {}
|
||||
|
||||
# Call the function. If something goes wrong, handle the Exception
|
||||
try:
|
||||
response = function.call(request, **data)
|
||||
except Exception:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
response = dajaxice_config.DAJAXICE_EXCEPTION
|
||||
|
||||
return HttpResponse(response, content_type="application/x-json")
|
||||
else:
|
||||
raise FunctionNotCallableError(name)
|
33
django-dajaxice/.travis.yml
Normal file
33
django-dajaxice/.travis.yml
Normal file
|
@ -0,0 +1,33 @@
|
|||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.2"
|
||||
|
||||
env:
|
||||
- DJANGO=django==1.3
|
||||
- DJANGO=django==1.4
|
||||
- DJANGO=https://github.com/django/django/tarball/stable/1.5.x
|
||||
|
||||
install:
|
||||
- pip install -q $DJANGO --use-mirrors
|
||||
- pip install https://github.com/jorgebastida/django-app-test-runner/zipball/master
|
||||
|
||||
script:
|
||||
- app-test-runner dajaxice dajaxice
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
# Django < 1.5 not supported on python 3
|
||||
- python: "3.2"
|
||||
env: DJANGO=django==1.3
|
||||
- python: "3.2"
|
||||
env: DJANGO=django==1.4
|
||||
# Django 1.5 strongly recommends python 2.7 or later (so skip 2.6)
|
||||
- python: "2.6"
|
||||
env: DJANGO=https://github.com/django/django/tarball/stable/1.5.x
|
18
django-dajaxice/AUTHORS
Normal file
18
django-dajaxice/AUTHORS
Normal file
|
@ -0,0 +1,18 @@
|
|||
Glue is mainly developed and maintained by Jorge Bastida <me@jorgebastida.com>
|
||||
|
||||
A big thanks to all the contributors:
|
||||
Angel Abad for the Debian and Ubuntu distribution package.
|
||||
Denis Darii <denis.darii@gmail.com>
|
||||
Florian Le Goff <florian@9h37.fr>
|
||||
Youen Péron <@youen>
|
||||
Clément Nodet <clement.nodet@gmail.com>
|
||||
Francisco Vianna <@fvianna>
|
||||
Paweł Krawczyk <@kravietz>
|
||||
Michael Fladischer <michael@fladi.at>
|
||||
Anton Agestam <msn@antonagestam.se>
|
||||
Michal Hořejšek <horejsekmichal@gmail.com>
|
||||
Ken <@kkansky>
|
||||
|
||||
And the authors of:
|
||||
XmlHttpRequest.js project (License inside COPYING)
|
||||
json2.js Library (License inside COPYING)
|
62
django-dajaxice/COPYING
Normal file
62
django-dajaxice/COPYING
Normal file
|
@ -0,0 +1,62 @@
|
|||
# django-dajaxixe License ################################################
|
||||
|
||||
Copyright (c) 2009-2012 Benito Jorge Bastida
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
3. Neither the name of the author nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
# XMLHttpRequest.js License ################################################
|
||||
|
||||
XMLHttpRequest.js Copyright (C) 2008 Sergey Ilinsky (http://www.ilinsky.com)
|
||||
|
||||
This work is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This work is distributed in the hope that it will be useful,
|
||||
but without any warranty; without even the implied warranty of
|
||||
merchantability or fitness for a particular purpose. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
# json2.js License ###########################################################
|
||||
|
||||
http://www.json.org/json2.js 2009-09-29
|
||||
|
||||
Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
See http://www.JSON.org/js.html
|
||||
You are free to copy, modify, or redistribute.
|
||||
|
||||
################################################################################
|
8
django-dajaxice/MANIFEST.in
Normal file
8
django-dajaxice/MANIFEST.in
Normal file
|
@ -0,0 +1,8 @@
|
|||
include *.py
|
||||
include COPYING
|
||||
include AUTHORS
|
||||
recursive-include dajaxice *
|
||||
recursive-exclude dajaxice *.pyc
|
||||
recursive-include examples *
|
||||
recursive-exclude examples *.pyc
|
||||
recursive-include docs *
|
19
django-dajaxice/README.rst
Normal file
19
django-dajaxice/README.rst
Normal file
|
@ -0,0 +1,19 @@
|
|||
django-dajaxice
|
||||
===============
|
||||
|
||||
Dajaxice is the communication core of dajaxproject. It's main goal is to trivialize the asynchronous communication within the django server side code and your js code.
|
||||
|
||||
dajaxice is JS-framework agnostic and focuses on decoupling the presentation logic from the server-side logic. dajaxice only requieres 5 minutes to start working.
|
||||
|
||||
Project Aims
|
||||
------------
|
||||
|
||||
* Isolate the communication between the client and the server.
|
||||
* JS Framework agnostic (No Prototype, JQuery... needed ).
|
||||
* Presentation logic outside the views (No presentation code inside ajax functions).
|
||||
* Lightweight.
|
||||
* Crossbrowsing ready.
|
||||
* Unobtrusive standard-compliant (W3C) XMLHttpRequest 1.0 object usage.
|
||||
|
||||
Official site http://dajaxproject.com
|
||||
Documentation http://readthedocs.org/projects/django-dajaxice/
|
1
django-dajaxice/dajaxice/__init__.py
Normal file
1
django-dajaxice/dajaxice/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__version__ = (0, 5, 5, 'beta')
|
137
django-dajaxice/dajaxice/core/Dajaxice.py
Normal file
137
django-dajaxice/dajaxice/core/Dajaxice.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
import logging
|
||||
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
log = logging.getLogger('dajaxice')
|
||||
|
||||
|
||||
class DajaxiceFunction(object):
|
||||
""" Basic representation of a dajaxice ajax function."""
|
||||
|
||||
def __init__(self, function, name, method):
|
||||
self.function = function
|
||||
self.name = name
|
||||
self.method = method
|
||||
|
||||
def call(self, *args, **kwargs):
|
||||
""" Call the function. """
|
||||
return self.function(*args, **kwargs)
|
||||
|
||||
|
||||
class DajaxiceModule(object):
|
||||
""" Basic representation of a dajaxice module. """
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
self.functions = {}
|
||||
self.submodules = {}
|
||||
|
||||
def add(self, name, function):
|
||||
""" Add this function at the ``name`` deep. If the submodule already
|
||||
exists, recusively call the add method into the submodule. If not,
|
||||
create the module and call the add method."""
|
||||
|
||||
# If this is not the final function name (there are more modules)
|
||||
# split the name again an register a new submodule.
|
||||
if '.' in name:
|
||||
module, extra = name.split('.', 1)
|
||||
if module not in self.submodules:
|
||||
self.submodules[module] = DajaxiceModule(module)
|
||||
self.submodules[module].add(extra, function)
|
||||
else:
|
||||
self.functions[name] = function
|
||||
|
||||
|
||||
class Dajaxice(object):
|
||||
|
||||
def __init__(self):
|
||||
self._registry = {}
|
||||
self._modules = None
|
||||
|
||||
def register(self, function, name=None, method='POST'):
|
||||
"""
|
||||
Register this function as a dajaxice function.
|
||||
|
||||
If no name is provided, the module and the function name will be used.
|
||||
The final (customized or not) must be unique. """
|
||||
|
||||
method = self.clean_method(method)
|
||||
|
||||
# Generate a default name
|
||||
if not name:
|
||||
module = ''.join(str(function.__module__).rsplit('.ajax', 1))
|
||||
name = '.'.join((module, function.__name__))
|
||||
|
||||
if ':' in name:
|
||||
log.error('Ivalid function name %s.' % name)
|
||||
return
|
||||
|
||||
# Check for already registered functions
|
||||
if name in self._registry:
|
||||
log.error('%s was already registered.' % name)
|
||||
return
|
||||
|
||||
# Create the dajaxice function.
|
||||
function = DajaxiceFunction(function=function,
|
||||
name=name,
|
||||
method=method)
|
||||
|
||||
# Register this new ajax function
|
||||
self._registry[name] = function
|
||||
|
||||
def is_callable(self, name, method):
|
||||
""" Return if the function callable or not. """
|
||||
return name in self._registry and self._registry[name].method == method
|
||||
|
||||
def clean_method(self, method):
|
||||
""" Clean the http method. """
|
||||
method = method.upper()
|
||||
if method not in ['GET', 'POST']:
|
||||
method = 'POST'
|
||||
return method
|
||||
|
||||
def get(self, name):
|
||||
""" Return the dajaxice function."""
|
||||
return self._registry[name]
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
""" Return an easy to loop module hierarchy with all the functions."""
|
||||
if not self._modules:
|
||||
self._modules = DajaxiceModule()
|
||||
for name, function in self._registry.items():
|
||||
self._modules.add(name, function)
|
||||
return self._modules
|
||||
|
||||
LOADING_DAJAXICE = False
|
||||
|
||||
|
||||
def dajaxice_autodiscover():
|
||||
"""
|
||||
Auto-discover INSTALLED_APPS ajax.py modules and fail silently when
|
||||
not present. NOTE: dajaxice_autodiscover was inspired/copied from
|
||||
django.contrib.admin autodiscover
|
||||
"""
|
||||
global LOADING_DAJAXICE
|
||||
if LOADING_DAJAXICE:
|
||||
return
|
||||
LOADING_DAJAXICE = True
|
||||
|
||||
import imp
|
||||
from django.conf import settings
|
||||
|
||||
for app in settings.INSTALLED_APPS:
|
||||
|
||||
try:
|
||||
app_path = import_module(app).__path__
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
try:
|
||||
imp.find_module('ajax', app_path)
|
||||
except ImportError:
|
||||
continue
|
||||
|
||||
import_module("%s.ajax" % app)
|
||||
|
||||
LOADING_DAJAXICE = False
|
38
django-dajaxice/dajaxice/core/__init__.py
Normal file
38
django-dajaxice/dajaxice/core/__init__.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
from django.conf import settings
|
||||
|
||||
from Dajaxice import Dajaxice, dajaxice_autodiscover
|
||||
|
||||
|
||||
class DajaxiceConfig(object):
|
||||
""" Provide an easy to use way to read the dajaxice configuration and
|
||||
return the default values if no configuration is present."""
|
||||
|
||||
default_config = {'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT': True,
|
||||
'DAJAXICE_JSON2_JS_IMPORT': True,
|
||||
'DAJAXICE_EXCEPTION': 'DAJAXICE_EXCEPTION',
|
||||
'DAJAXICE_MEDIA_PREFIX': 'dajaxice'}
|
||||
|
||||
def __getattr__(self, name):
|
||||
""" Return the customized value for a setting (if it exists) or the
|
||||
default value if not. """
|
||||
|
||||
if name in self.default_config:
|
||||
if hasattr(settings, name):
|
||||
return getattr(settings, name)
|
||||
return self.default_config.get(name)
|
||||
return None
|
||||
|
||||
@property
|
||||
def dajaxice_url(self):
|
||||
return r'^%s/' % self.DAJAXICE_MEDIA_PREFIX
|
||||
|
||||
@property
|
||||
def django_settings(self):
|
||||
return settings
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
return dajaxice_functions.modules
|
||||
|
||||
dajaxice_functions = Dajaxice()
|
||||
dajaxice_config = DajaxiceConfig()
|
48
django-dajaxice/dajaxice/decorators.py
Normal file
48
django-dajaxice/dajaxice/decorators.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
import functools
|
||||
|
||||
from dajaxice.core import dajaxice_functions
|
||||
|
||||
|
||||
def dajaxice_register(*dargs, **dkwargs):
|
||||
""" Register some function as a dajaxice function
|
||||
|
||||
For legacy purposes, if only a function is passed register it a simple
|
||||
single ajax function using POST, i.e:
|
||||
|
||||
@dajaxice_register
|
||||
def ajax_function(request):
|
||||
...
|
||||
|
||||
After 0.5, dajaxice allow to customize the http method and the final name
|
||||
of the registered function. This decorator covers both the legacy and
|
||||
the new functionality, i.e:
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def ajax_function(request):
|
||||
...
|
||||
|
||||
@dajaxice_register(method='GET', name='my.custom.name')
|
||||
def ajax_function(request):
|
||||
...
|
||||
|
||||
You can also register the same function to use a different http method
|
||||
and/or use a different name.
|
||||
|
||||
@dajaxice_register(method='GET', name='users.get')
|
||||
@dajaxice_register(method='POST', name='users.update')
|
||||
def ajax_function(request):
|
||||
...
|
||||
"""
|
||||
|
||||
if len(dargs) and not dkwargs:
|
||||
function = dargs[0]
|
||||
dajaxice_functions.register(function)
|
||||
return function
|
||||
|
||||
def decorator(function):
|
||||
@functools.wraps(function)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
return function(request, *args, **kwargs)
|
||||
dajaxice_functions.register(function, *dargs, **dkwargs)
|
||||
return wrapper
|
||||
return decorator
|
10
django-dajaxice/dajaxice/exceptions.py
Normal file
10
django-dajaxice/dajaxice/exceptions.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
class DajaxiceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class FunctionNotCallableError(DajaxiceError):
|
||||
pass
|
||||
|
||||
|
||||
class DajaxiceImportError(DajaxiceError):
|
||||
pass
|
75
django-dajaxice/dajaxice/finders.py
Normal file
75
django-dajaxice/dajaxice/finders.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import os
|
||||
import tempfile
|
||||
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.template import Context
|
||||
from django.template.loader import get_template
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
|
||||
|
||||
class VirtualStorage(finders.FileSystemStorage):
|
||||
"""" Mock a FileSystemStorage to build tmp files on demand."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._files_cache = {}
|
||||
super(VirtualStorage, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_or_create_file(self, path):
|
||||
if path not in self.files:
|
||||
return ''
|
||||
|
||||
data = getattr(self, self.files[path])()
|
||||
|
||||
try:
|
||||
current_file = open(self._files_cache[path])
|
||||
current_data = current_file.read()
|
||||
current_file.close()
|
||||
if current_data != data:
|
||||
os.remove(path)
|
||||
raise Exception("Invalid data")
|
||||
except Exception:
|
||||
handle, tmp_path = tempfile.mkstemp()
|
||||
tmp_file = open(tmp_path, 'w')
|
||||
tmp_file.write(data)
|
||||
tmp_file.close()
|
||||
self._files_cache[path] = tmp_path
|
||||
|
||||
return self._files_cache[path]
|
||||
|
||||
def exists(self, name):
|
||||
return name in self.files
|
||||
|
||||
def listdir(self, path):
|
||||
folders, files = [], []
|
||||
for f in self.files:
|
||||
if f.startswith(path):
|
||||
f = f.replace(path, '', 1)
|
||||
if os.sep in f:
|
||||
folders.append(f.split(os.sep, 1)[0])
|
||||
else:
|
||||
files.append(f)
|
||||
return folders, files
|
||||
|
||||
def path(self, name):
|
||||
try:
|
||||
path = self.get_or_create_file(name)
|
||||
except ValueError:
|
||||
raise SuspiciousOperation("Attempted access to '%s' denied." % name)
|
||||
return os.path.normpath(path)
|
||||
|
||||
|
||||
class DajaxiceStorage(VirtualStorage):
|
||||
|
||||
files = {os.path.join('dajaxice', 'dajaxice.core.js'): 'dajaxice_core_js'}
|
||||
|
||||
def dajaxice_core_js(self):
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
|
||||
dajaxice_autodiscover()
|
||||
|
||||
c = Context({'dajaxice_config': dajaxice_config})
|
||||
return get_template(os.path.join('dajaxice', 'dajaxice.core.js')).render(c)
|
||||
|
||||
|
||||
class DajaxiceFinder(finders.BaseStorageFinder):
|
||||
storage = DajaxiceStorage()
|
1
django-dajaxice/dajaxice/models.py
Normal file
1
django-dajaxice/dajaxice/models.py
Normal file
|
@ -0,0 +1 @@
|
|||
# Don't delete me
|
145
django-dajaxice/dajaxice/templates/dajaxice/dajaxice.core.js
Normal file
145
django-dajaxice/dajaxice/templates/dajaxice/dajaxice.core.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
{% load url from future %}
|
||||
var Dajaxice = {
|
||||
|
||||
{% with module=dajaxice_config.modules top='top' %}
|
||||
{% include "dajaxice/dajaxice_function_loop.js" %}
|
||||
{% endwith %}
|
||||
|
||||
{% for name, module in dajaxice_config.modules.submodules.items %}
|
||||
{% include "dajaxice/dajaxice_module_loop.js" %},
|
||||
{% endfor %}
|
||||
|
||||
get_cookie: 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 = cookies[i].toString().replace(/^\s+/, "").replace(/\s+$/, "");
|
||||
// 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;
|
||||
},
|
||||
|
||||
call: function(dajaxice_function, method, dajaxice_callback, argv, custom_settings)
|
||||
{
|
||||
var custom_settings = custom_settings || {},
|
||||
error_callback = Dajaxice.get_setting('default_exception_callback');
|
||||
|
||||
if('error_callback' in custom_settings && typeof(custom_settings['error_callback']) == 'function'){
|
||||
error_callback = custom_settings['error_callback'];
|
||||
}
|
||||
|
||||
var send_data = 'argv='+encodeURIComponent(JSON.stringify(argv)),
|
||||
oXMLHttpRequest = new XMLHttpRequest,
|
||||
endpoint = '{% url 'dajaxice-endpoint' %}'+dajaxice_function+'/';
|
||||
|
||||
if(method == 'GET'){
|
||||
endpoint = endpoint + '?' + send_data;
|
||||
}
|
||||
oXMLHttpRequest.open(method, endpoint);
|
||||
oXMLHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
oXMLHttpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
oXMLHttpRequest.setRequestHeader("X-CSRFToken", Dajaxice.get_cookie('{{ dajaxice_config.django_settings.CSRF_COOKIE_NAME }}'));
|
||||
oXMLHttpRequest.onreadystatechange = function() {
|
||||
if (this.readyState == XMLHttpRequest.DONE) {
|
||||
if(this.responseText == Dajaxice.EXCEPTION || !(this.status in Dajaxice.valid_http_responses())){
|
||||
error_callback();
|
||||
}
|
||||
else{
|
||||
var response;
|
||||
try {
|
||||
response = JSON.parse(this.responseText);
|
||||
}
|
||||
catch (exception) {
|
||||
response = this.responseText;
|
||||
}
|
||||
dajaxice_callback(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(method == 'POST'){
|
||||
oXMLHttpRequest.send(send_data);
|
||||
}
|
||||
else{
|
||||
oXMLHttpRequest.send();
|
||||
}
|
||||
return oXMLHttpRequest;
|
||||
},
|
||||
|
||||
setup: function(settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
},
|
||||
|
||||
get_setting: function(key){
|
||||
if(this.settings == undefined || this.settings[key] == undefined){
|
||||
return Dajaxice.default_settings[key];
|
||||
}
|
||||
return this.settings[key];
|
||||
},
|
||||
|
||||
valid_http_responses: function(){
|
||||
return {200: null, 301: null, 302: null, 304: null}
|
||||
},
|
||||
|
||||
EXCEPTION: '{{ dajaxice_config.DAJAXICE_EXCEPTION }}',
|
||||
default_settings: {'default_exception_callback': function(){ console.log('Dajaxice: Something went wrong.')}}
|
||||
};
|
||||
|
||||
window['Dajaxice'] = Dajaxice;
|
||||
|
||||
{% comment %}
|
||||
/*
|
||||
XMLHttpRequest.js Compiled with Google Closure
|
||||
|
||||
XMLHttpRequest.js Copyright (C) 2008 Sergey Ilinsky (http://www.ilinsky.com)
|
||||
|
||||
This work is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This work is distributed in the hope that it will be useful,
|
||||
but without any warranty; without even the implied warranty of
|
||||
merchantability or fitness for a particular purpose. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
{% endcomment %}
|
||||
{% if dajaxice_config.DAJAXICE_XMLHTTPREQUEST_JS_IMPORT %}
|
||||
(function(){function n(){this._object=h&&!p?new h:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}function a(){return new n}function j(b){a.onreadystatechange&&a.onreadystatechange.apply(b);b.dispatchEvent({type:"readystatechange",bubbles:!1,cancelable:!1,timeStamp:new Date+0})}function o(b){try{b.responseText=b._object.responseText}catch(a){}try{var d;var g=b._object,c=g.responseXML,f=g.responseText;i&&(f&&c&&!c.documentElement&&g.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/))&&
|
||||
(c=new window.ActiveXObject("Microsoft.XMLDOM"),c.async=!1,c.validateOnParse=!1,c.loadXML(f));d=c&&(i&&0!==c.parseError||!c.documentElement||c.documentElement&&"parsererror"==c.documentElement.tagName)?null:c;b.responseXML=d}catch(h){}try{b.status=b._object.status}catch(k){}try{b.statusText=b._object.statusText}catch(j){}}function l(b){b._object.onreadystatechange=new window.Function}var h=window.XMLHttpRequest,m=!!window.controllers,i=window.document.all&&!window.opera,p=i&&window.navigator.userAgent.match(/MSIE 7.0/);
|
||||
a.prototype=n.prototype;m&&h.wrapped&&(a.wrapped=h.wrapped);a.UNSENT=0;a.OPENED=1;a.HEADERS_RECEIVED=2;a.LOADING=3;a.DONE=4;a.prototype.readyState=a.UNSENT;a.prototype.responseText="";a.prototype.responseXML=null;a.prototype.status=0;a.prototype.statusText="";a.prototype.priority="NORMAL";a.prototype.onreadystatechange=null;a.onreadystatechange=null;a.onopen=null;a.onsend=null;a.onabort=null;a.prototype.open=function(b,e,d,g,c){delete this._headers;arguments.length<3&&(d=true);this._async=d;var f=
|
||||
this,h=this.readyState,k=null;if(i&&d){k=function(){if(h!=a.DONE){l(f);f.abort()}};window.attachEvent("onunload",k)}a.onopen&&a.onopen.apply(this,arguments);arguments.length>4?this._object.open(b,e,d,g,c):arguments.length>3?this._object.open(b,e,d,g):this._object.open(b,e,d);this.readyState=a.OPENED;j(this);this._object.onreadystatechange=function(){if(!m||d){f.readyState=f._object.readyState;o(f);if(f._aborted)f.readyState=a.UNSENT;else if(f.readyState==a.DONE){delete f._data;l(f);i&&d&&window.detachEvent("onunload",
|
||||
k);h!=f.readyState&&j(f);h=f.readyState}}}};a.prototype.send=function(b){a.onsend&&a.onsend.apply(this,arguments);arguments.length||(b=null);if(b&&b.nodeType){b=window.XMLSerializer?(new window.XMLSerializer).serializeToString(b):b.xml;this._headers["Content-Type"]||this._object.setRequestHeader("Content-Type","application/xml")}this._data=b;a:{this._object.send(this._data);if(m&&!this._async){this.readyState=a.OPENED;for(o(this);this.readyState<a.DONE;){this.readyState++;j(this);if(this._aborted)break a}}}};
|
||||
a.prototype.abort=function(){a.onabort&&a.onabort.apply(this,arguments);if(this.readyState>a.UNSENT)this._aborted=true;this._object.abort();l(this);this.readyState=a.UNSENT;delete this._data};a.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};a.prototype.getResponseHeader=function(b){return this._object.getResponseHeader(b)};a.prototype.setRequestHeader=function(b,a){if(!this._headers)this._headers={};this._headers[b]=a;return this._object.setRequestHeader(b,
|
||||
a)};a.prototype.addEventListener=function(a,e,d){for(var g=0,c;c=this._listeners[g];g++)if(c[0]==a&&c[1]==e&&c[2]==d)return;this._listeners.push([a,e,d])};a.prototype.removeEventListener=function(a,e,d){for(var g=0,c;c=this._listeners[g];g++)if(c[0]==a&&c[1]==e&&c[2]==d)break;c&&this._listeners.splice(g,1)};a.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){},
|
||||
initEvent:function(){}};a.type=="readystatechange"&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var e=0,d;d=this._listeners[e];e++)d[0]==a.type&&!d[2]&&(d[1].handleEvent||d[1]).apply(this,[a])};a.prototype.toString=function(){return"[object XMLHttpRequest]"};a.toString=function(){return"[XMLHttpRequest]"};window.Function.prototype.apply||(window.Function.prototype.apply=function(a,e){e||(e=[]);a.__func=this;a.__func(e[0],e[1],e[2],e[3],
|
||||
e[4]);delete a.__func});window.XMLHttpRequest=a})();
|
||||
{% endif %}
|
||||
|
||||
{% comment %}
|
||||
/*
|
||||
http://www.json.org/json2.js Compiled with Google Closure
|
||||
This code was released under Public Domain by json.org , Thanks!
|
||||
I hope that in the future this code won't be neccessary, but today all browsers doesn't supports JSON.stringify().
|
||||
*/
|
||||
{% endcomment %}
|
||||
{% if dajaxice_config.DAJAXICE_JSON2_JS_IMPORT %}
|
||||
var JSON;JSON||(JSON={});
|
||||
(function(){function k(a){return 10>a?"0"+a:a}function o(a){p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function(a){var c=r[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function m(a,j){var c,d,h,n,g=e,f,b=j[a];b&&("object"===typeof b&&"function"===typeof b.toJSON)&&(b=b.toJSON(a));"function"===typeof i&&(b=i.call(j,a,b));switch(typeof b){case "string":return o(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);case "object":if(!b)return"null";
|
||||
e+=l;f=[];if("[object Array]"===Object.prototype.toString.apply(b)){n=b.length;for(c=0;c<n;c+=1)f[c]=m(c,b)||"null";h=0===f.length?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(i&&"object"===typeof i){n=i.length;for(c=0;c<n;c+=1)"string"===typeof i[c]&&(d=i[c],(h=m(d,b))&&f.push(o(d)+(e?": ":":")+h))}else for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=m(d,b))&&f.push(o(d)+(e?": ":":")+h);h=0===f.length?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+g+"}":"{"+f.join(",")+
|
||||
"}";e=g;return h}}"function"!==typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+k(this.getUTCMonth()+1)+"-"+k(this.getUTCDate())+"T"+k(this.getUTCHours())+":"+k(this.getUTCMinutes())+":"+k(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var q=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e,l,r={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},i;"function"!==typeof JSON.stringify&&(JSON.stringify=function(a,j,c){var d;l=e="";if(typeof c==="number")for(d=0;d<c;d=d+1)l=l+" ";else typeof c==="string"&&(l=c);if((i=j)&&typeof j!=="function"&&(typeof j!=="object"||typeof j.length!=="number"))throw Error("JSON.stringify");return m("",{"":a})});
|
||||
"function"!==typeof JSON.parse&&(JSON.parse=function(a,e){function c(a,d){var g,f,b=a[d];if(b&&typeof b==="object")for(g in b)if(Object.prototype.hasOwnProperty.call(b,g)){f=c(b,g);f!==void 0?b[g]=f:delete b[g]}return e.call(a,d,b)}var d,a=String(a);q.lastIndex=0;q.test(a)&&(a=a.replace(q,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
|
||||
"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){d=eval("("+a+")");return typeof e==="function"?c({"":d},""):d}throw new SyntaxError("JSON.parse");})})();
|
||||
{% endif %}
|
|
@ -0,0 +1,5 @@
|
|||
{% for function_name, function in module.functions.items %}
|
||||
{{ function_name }}: function(callback_function, argv, custom_settings){
|
||||
return Dajaxice.call('{{ function.name }}', '{{ function.method }}', callback_function, argv, custom_settings);
|
||||
}{% if not forloop.last or top or module.submodules %},{% endif %}
|
||||
{% endfor %}
|
|
@ -0,0 +1,11 @@
|
|||
{{ name }}: {
|
||||
{% include "dajaxice/dajaxice_function_loop.js" %}
|
||||
{% with parent_foorloop=forloop %}
|
||||
{% for name, sub_module in module.submodules.items %}
|
||||
{% with filename="dajaxice/dajaxice_module_loop.js" module=sub_module %}
|
||||
{% include filename %}
|
||||
{% endwith %}
|
||||
{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endwith %}
|
0
django-dajaxice/dajaxice/templatetags/__init__.py
Normal file
0
django-dajaxice/dajaxice/templatetags/__init__.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import logging
|
||||
|
||||
from django import template
|
||||
from django.middleware.csrf import get_token
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import get_storage_class
|
||||
|
||||
staticfiles_storage = get_storage_class(settings.STATICFILES_STORAGE)()
|
||||
|
||||
register = template.Library()
|
||||
|
||||
log = logging.getLogger('dajaxice')
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def dajaxice_js_import(context, csrf=True):
|
||||
""" Return the js script tag for the dajaxice.core.js file
|
||||
If the csrf argument is present and it's ``nocsrf`` dajaxice will not
|
||||
try to mark the request as if it need the csrf token. By default use
|
||||
the dajaxice_js_import template tag will make django set the csrftoken
|
||||
cookie on the current request."""
|
||||
|
||||
csrf = csrf != 'nocsrf'
|
||||
request = context.get('request')
|
||||
|
||||
if request and csrf:
|
||||
get_token(request)
|
||||
elif csrf:
|
||||
log.warning("The 'request' object must be accesible within the "
|
||||
"context. You must add 'django.contrib.messages.context"
|
||||
"_processors.request' to your TEMPLATE_CONTEXT_PROCESSORS "
|
||||
"and render your views using a RequestContext.")
|
||||
|
||||
url = staticfiles_storage.url('dajaxice/dajaxice.core.js')
|
||||
return '<script src="%s" type="text/javascript" charset="utf-8"></script>' % url
|
165
django-dajaxice/dajaxice/tests/__init__.py
Normal file
165
django-dajaxice/dajaxice/tests/__init__.py
Normal file
|
@ -0,0 +1,165 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from dajaxice.core import DajaxiceConfig
|
||||
from dajaxice.core.Dajaxice import DajaxiceModule, DajaxiceFunction, Dajaxice
|
||||
from dajaxice.exceptions import FunctionNotCallableError
|
||||
|
||||
|
||||
class DajaxiceModuleTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.module = DajaxiceModule()
|
||||
|
||||
def test_constructor(self):
|
||||
self.assertEqual(self.module.functions, {})
|
||||
self.assertEqual(self.module.submodules, {})
|
||||
|
||||
def test_add_function(self):
|
||||
function = lambda x: x
|
||||
|
||||
self.module.add('test', function)
|
||||
self.assertEqual(self.module.functions, {'test': function})
|
||||
self.assertEqual(self.module.submodules, {})
|
||||
|
||||
self.module.add('foo.bar', function)
|
||||
self.assertEqual(self.module.functions, {'test': function})
|
||||
self.assertEqual(self.module.submodules.keys(), ['foo'])
|
||||
self.assertEqual(self.module.submodules['foo'].functions, {'bar': function})
|
||||
|
||||
|
||||
class DajaxiceFunctionTest(TestCase):
|
||||
|
||||
def test_constructor(self):
|
||||
|
||||
class CalledEception(Exception):
|
||||
pass
|
||||
|
||||
def callback():
|
||||
raise CalledEception
|
||||
|
||||
function = DajaxiceFunction(callback, 'foo', 'POST')
|
||||
|
||||
self.assertEqual(function.function, callback)
|
||||
self.assertEqual(function.name, 'foo')
|
||||
self.assertEqual(function.method, 'POST')
|
||||
self.assertRaises(CalledEception, function.call)
|
||||
|
||||
|
||||
class DajaxiceTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dajaxice = Dajaxice()
|
||||
self.function = lambda x: x
|
||||
|
||||
def test_constructor(self):
|
||||
self.assertEqual(self.dajaxice._registry, {})
|
||||
|
||||
def test_register(self):
|
||||
self.dajaxice.register(self.function, 'foo')
|
||||
self.assertTrue('foo' in self.dajaxice._registry)
|
||||
self.assertEqual(type(self.dajaxice._registry['foo']), DajaxiceFunction)
|
||||
|
||||
def bar_function():
|
||||
return
|
||||
|
||||
self.dajaxice.register(bar_function)
|
||||
self.assertTrue('dajaxice.tests.bar_function' in self.dajaxice._registry)
|
||||
self.assertEqual(type(self.dajaxice._registry['dajaxice.tests.bar_function']), DajaxiceFunction)
|
||||
|
||||
def test__is_callable(self):
|
||||
self.dajaxice.register(self.function, 'foo')
|
||||
self.dajaxice.register(self.function, 'bar', method='GET')
|
||||
|
||||
self.assertTrue(self.dajaxice.is_callable('foo', 'POST'))
|
||||
self.assertTrue(self.dajaxice.is_callable('bar', 'GET'))
|
||||
self.assertFalse(self.dajaxice.is_callable('foo', 'GET'))
|
||||
self.assertFalse(self.dajaxice.is_callable('bar', 'POST'))
|
||||
self.assertFalse(self.dajaxice.is_callable('test', 'POST'))
|
||||
self.assertFalse(self.dajaxice.is_callable('test', 'GET'))
|
||||
|
||||
def test_clean_method(self):
|
||||
self.assertEqual(self.dajaxice.clean_method('post'), 'POST')
|
||||
self.assertEqual(self.dajaxice.clean_method('get'), 'GET')
|
||||
self.assertEqual(self.dajaxice.clean_method('POST'), 'POST')
|
||||
self.assertEqual(self.dajaxice.clean_method('GET'), 'GET')
|
||||
self.assertEqual(self.dajaxice.clean_method('other'), 'POST')
|
||||
|
||||
def test_modules(self):
|
||||
self.dajaxice.register(self.function, 'foo')
|
||||
self.dajaxice.register(self.function, 'bar')
|
||||
|
||||
self.assertEqual(type(self.dajaxice.modules), DajaxiceModule)
|
||||
self.assertEqual(self.dajaxice.modules.functions.keys(), ['foo', 'bar'])
|
||||
|
||||
|
||||
class DajaxiceConfigTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.config = DajaxiceConfig()
|
||||
|
||||
def test_defaults(self):
|
||||
self.assertTrue(self.config.DAJAXICE_XMLHTTPREQUEST_JS_IMPORT)
|
||||
self.assertTrue(self.config.DAJAXICE_JSON2_JS_IMPORT)
|
||||
self.assertEqual(self.config.DAJAXICE_EXCEPTION, 'DAJAXICE_EXCEPTION')
|
||||
self.assertEqual(self.config.DAJAXICE_MEDIA_PREFIX, 'dajaxice')
|
||||
|
||||
dajaxice_url = r'^%s/' % self.config.DAJAXICE_MEDIA_PREFIX
|
||||
self.assertEqual(self.config.dajaxice_url, dajaxice_url)
|
||||
|
||||
self.assertEqual(type(self.config.modules), DajaxiceModule)
|
||||
|
||||
|
||||
class DjangoIntegrationTest(TestCase):
|
||||
|
||||
urls = 'dajaxice.tests.urls'
|
||||
|
||||
def setUp(self):
|
||||
settings.INSTALLED_APPS += ('dajaxice.tests',)
|
||||
|
||||
def test_calling_not_registered_function(self):
|
||||
self.failUnlessRaises(FunctionNotCallableError, self.client.post, '/dajaxice/dajaxice.tests.this_function_not_exist/')
|
||||
|
||||
def test_calling_registered_function(self):
|
||||
response = self.client.post('/dajaxice/dajaxice.tests.test_foo/')
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "bar"}')
|
||||
|
||||
def test_calling_registered_function_with_params(self):
|
||||
|
||||
response = self.client.post('/dajaxice/dajaxice.tests.test_foo_with_params/', {'argv': '{"param1":"value1"}'})
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"param1": "value1"}')
|
||||
|
||||
def test_bad_function(self):
|
||||
|
||||
response = self.client.post('/dajaxice/dajaxice.tests.test_ajax_exception/')
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, "DAJAXICE_EXCEPTION")
|
||||
|
||||
def test_get_register(self):
|
||||
|
||||
response = self.client.get('/dajaxice/dajaxice.tests.test_get_register/')
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "user"}')
|
||||
|
||||
def test_get_custom_name_register(self):
|
||||
|
||||
response = self.client.get('/dajaxice/get_user_data/')
|
||||
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"bar": "user"}')
|
||||
|
||||
def test_multi_register(self):
|
||||
|
||||
response = self.client.get('/dajaxice/get_multi/')
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "multi"}')
|
||||
|
||||
response = self.client.post('/dajaxice/post_multi/')
|
||||
self.failUnlessEqual(response.status_code, 200)
|
||||
self.failUnlessEqual(response.content, '{"foo": "multi"}')
|
43
django-dajaxice/dajaxice/tests/ajax.py
Normal file
43
django-dajaxice/dajaxice/tests/ajax.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_registered_function(request):
|
||||
return ""
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_string(request):
|
||||
return simplejson.dumps({'string': 'hello world'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_ajax_exception(request):
|
||||
raise Exception()
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_foo(request):
|
||||
return simplejson.dumps({'foo': 'bar'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def test_foo_with_params(request, param1):
|
||||
return simplejson.dumps({'param1': param1})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def test_get_register(request):
|
||||
return simplejson.dumps({'foo': 'user'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET', name="get_user_data")
|
||||
def test_get_with_name_register(request):
|
||||
return simplejson.dumps({'bar': 'user'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET', name="get_multi")
|
||||
@dajaxice_register(name="post_multi")
|
||||
def test_multi_register(request):
|
||||
return simplejson.dumps({'foo': 'multi'})
|
1
django-dajaxice/dajaxice/tests/requirements.txt
Normal file
1
django-dajaxice/dajaxice/tests/requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Django
|
10
django-dajaxice/dajaxice/tests/urls.py
Normal file
10
django-dajaxice/dajaxice/tests/urls.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from django.conf.urls.defaults import *
|
||||
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
|
||||
dajaxice_autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#Dajaxice URLS
|
||||
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
)
|
7
django-dajaxice/dajaxice/urls.py
Normal file
7
django-dajaxice/dajaxice/urls.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.conf.urls import patterns, url, include
|
||||
from .views import DajaxiceRequest
|
||||
|
||||
urlpatterns = patterns('dajaxice.views',
|
||||
url(r'^(.+)/$', DajaxiceRequest.as_view(), name='dajaxice-call-endpoint'),
|
||||
url(r'', DajaxiceRequest.as_view(), name='dajaxice-endpoint'),
|
||||
)
|
8
django-dajaxice/dajaxice/utils.py
Normal file
8
django-dajaxice/dajaxice/utils.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.http import QueryDict
|
||||
|
||||
|
||||
def deserialize_form(data):
|
||||
"""
|
||||
Create a new QueryDict from a serialized form.
|
||||
"""
|
||||
return QueryDict(query_string=unicode(data).encode('utf-8'))
|
60
django-dajaxice/dajaxice/views.py
Normal file
60
django-dajaxice/dajaxice/views.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
import json
|
||||
from django.views.generic.base import View
|
||||
from django.http import HttpResponse, Http404
|
||||
|
||||
from dajaxice.exceptions import FunctionNotCallableError
|
||||
from dajaxice.core import dajaxice_functions, dajaxice_config
|
||||
|
||||
log = logging.getLogger('dajaxice')
|
||||
|
||||
|
||||
def safe_dict(d):
|
||||
"""
|
||||
Recursively clone json structure with UTF-8 dictionary keys
|
||||
http://www.gossamer-threads.com/lists/python/bugs/684379
|
||||
"""
|
||||
if isinstance(d, dict):
|
||||
return dict([(k.encode('utf-8'), safe_dict(v)) for k, v in d.iteritems()])
|
||||
elif isinstance(d, list):
|
||||
return [safe_dict(x) for x in d]
|
||||
else:
|
||||
return d
|
||||
|
||||
|
||||
class DajaxiceRequest(View):
|
||||
""" Handle all the dajaxice xhr requests. """
|
||||
|
||||
def dispatch(self, request, name=None):
|
||||
|
||||
if not name:
|
||||
raise Http404
|
||||
|
||||
# Check if the function is callable
|
||||
if dajaxice_functions.is_callable(name, request.method):
|
||||
|
||||
function = dajaxice_functions.get(name)
|
||||
data = getattr(request, function.method).get('argv', '')
|
||||
|
||||
# Clean the argv
|
||||
if data != 'undefined':
|
||||
try:
|
||||
data = safe_dict(json.loads(data))
|
||||
except Exception:
|
||||
data = {}
|
||||
else:
|
||||
data = {}
|
||||
|
||||
# Call the function. If something goes wrong, handle the Exception
|
||||
try:
|
||||
response = function.call(request, **data)
|
||||
except Exception:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
response = dajaxice_config.DAJAXICE_EXCEPTION
|
||||
|
||||
return HttpResponse(response, content_type="application/x-json")
|
||||
else:
|
||||
raise FunctionNotCallableError(name)
|
130
django-dajaxice/docs/Makefile
Normal file
130
django-dajaxice/docs/Makefile
Normal file
|
@ -0,0 +1,130 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-dajaxice.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-dajaxice.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/django-dajaxice"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-dajaxice"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
38
django-dajaxice/docs/available-settings.rst
Normal file
38
django-dajaxice/docs/available-settings.rst
Normal file
|
@ -0,0 +1,38 @@
|
|||
Available Settings
|
||||
==================
|
||||
|
||||
DAJAXICE_MEDIA_PREFIX
|
||||
---------------------
|
||||
|
||||
This will be the namespace that dajaxice will use as endpoint.
|
||||
|
||||
Defaults to ``dajaxice``
|
||||
|
||||
Optional: ``True``
|
||||
|
||||
DAJAXICE_XMLHTTPREQUEST_JS_IMPORT
|
||||
---------------------------------
|
||||
|
||||
Include XmlHttpRequest.js inside dajaxice.core.js
|
||||
|
||||
Defaults to ``True``
|
||||
|
||||
Optional: ``True``
|
||||
|
||||
DAJAXICE_JSON2_JS_IMPORT
|
||||
------------------------
|
||||
|
||||
Include json2.js inside dajaxice.core.js
|
||||
|
||||
Defaults to ``True``
|
||||
|
||||
Optional: ``True``
|
||||
|
||||
DAJAXICE_EXCEPTION
|
||||
------------------
|
||||
|
||||
Default data sent when an exception occurs.
|
||||
|
||||
Defaults to ``"DAJAXICE_EXCEPTION"``
|
||||
|
||||
Optional: ``True``
|
124
django-dajaxice/docs/changelog.rst
Normal file
124
django-dajaxice/docs/changelog.rst
Normal file
|
@ -0,0 +1,124 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
0.5.5
|
||||
^^^^^
|
||||
* Return XMLHttpRequest from concreate functions as well as from function call.
|
||||
* Fixed django 1.5 compatibility: Content-Type have to be application/x-www-form-urlencoded otherwise Django discards POST data.
|
||||
* Fix JS generation errors
|
||||
* Fix @dajaxice_register legacy decorator
|
||||
|
||||
0.5.4.1
|
||||
^^^^^^^
|
||||
* Fix JS generation errors.
|
||||
|
||||
0.5.4
|
||||
^^^^^
|
||||
* Fix JS generation errors.
|
||||
|
||||
0.5.3
|
||||
^^^^^
|
||||
* Fix some Windows bugs.
|
||||
* Fix some JS generation errors.
|
||||
* Make dajaxice use CSRF_COOKIE_NAME.
|
||||
|
||||
0.5.2
|
||||
^^^^^
|
||||
* Fix GET dajaxice requests in order to send args as part of the url.
|
||||
|
||||
0.5.1
|
||||
^^^^^
|
||||
* Make django-dajaxice work with django 1.3
|
||||
* Fix installation steps
|
||||
* Update json2.js
|
||||
|
||||
0.5
|
||||
^^^
|
||||
* General Project clean-up
|
||||
* Django>=1.3 is now a requirement
|
||||
* Fixed numerous CSRF issues
|
||||
* Dajaxice now use django.contrib.staticfiles
|
||||
* Fix SERVER_ROOT_URL issues
|
||||
* Fixed js_core issues accepting multiple arguments
|
||||
* New upgraded documentation
|
||||
* Marketing site (http://dajaxproject.com) is now open-source
|
||||
* Fix JS generation issues
|
||||
* Travis-ci integration
|
||||
|
||||
|
||||
0.2
|
||||
^^^
|
||||
* Fix bug with the 'is_callback_a_function' variable in dajaxice.core.js
|
||||
* Fix bug with csrftoken in landing pages using dajaxice.
|
||||
* Improve reliability handling server errors.
|
||||
* Exception handling was fully rewritten. Dajaxice default_error_callback is now configurable using Dajaxice.setup.
|
||||
* Custom error messages per dajaxice call.
|
||||
* Dajaxice now propagate docstrings to javascript dajaxice functions.
|
||||
* Added DAJAXICE_JS_DOCSTRINGS to configure docstrings propagation behaviour, default=False.
|
||||
* Updated installation guide for compatibility with django 1.3
|
||||
* dajaxice now uses the logger 'dajaxice' and not 'dajaxice.DajaxiceRequest'
|
||||
* Documentation Updated.
|
||||
|
||||
0.1.8.1
|
||||
^^^^^^^
|
||||
* Fixed bug #25 related to CSRF verification on Django 1.2.5
|
||||
|
||||
0.1.8
|
||||
^^^^^
|
||||
* Add build dir to ignores
|
||||
* Remove MANIFEST file and auto-generate it through MANIFEST.in
|
||||
* Add MANIFEST to ignores
|
||||
* Include examples and docs dirs to source distribution
|
||||
* Add long_description to setup.py
|
||||
* Fixed Flaw in AJAX CSRF handling (X-CSRFToken Django 1.2.5)
|
||||
|
||||
0.1.7
|
||||
^^^^^
|
||||
* Fixing dajaxice callback model to improve security against XSS attacks.
|
||||
* Dajaxice callbacks should be passed as functions and not as strings.
|
||||
* Old string-callback maintained for backward compatibility.(usage not recommended)
|
||||
* New documentation using Sphinx
|
||||
* Adding a decorators.py file with a helper decorator to register functions (Douglas Soares de Andrade)
|
||||
|
||||
0.1.6
|
||||
^^^^^
|
||||
* Fixing registration bugs
|
||||
* Added some tests
|
||||
|
||||
0.1.5
|
||||
^^^^^
|
||||
* Now dajaxice functions must be registered using dajaxice_functions.register instead of adding that functions to DAJAXICE_FUNCTIONS list inside settings.py. This pattern is very similar to django.contrib.admin model registration.
|
||||
* Now dajaxice functions could be placed inside any module depth.
|
||||
* With this approach dajaxice app reusability was improved.
|
||||
* Old style registration (using DAJAXICE_FUNCTIONS) works too, but isn't recommended.
|
||||
* New tests added.
|
||||
|
||||
0.1.3
|
||||
^^^^^
|
||||
* CSRF middleware buf fixed
|
||||
* Improved production and development logging
|
||||
* New custom Exception message
|
||||
* New notify_exception to send traceback to admins
|
||||
* Fixed semicolon issues
|
||||
* Fixed unicode errors
|
||||
* Fixed generate_static_dajaxice before easy_install usage
|
||||
* Fixed IE6 bug in dajaxice.core.js
|
||||
|
||||
0.1.2
|
||||
^^^^^
|
||||
* New and cleaned setup.py
|
||||
|
||||
0.1.1
|
||||
^^^^^
|
||||
* json2.js and XMLHttpRequest libs included
|
||||
* New settings DAJAXICE_XMLHTTPREQUEST_JS_IMPORT and DAJAXICE_JSON2_JS_IMPORT
|
||||
|
||||
0.1.0
|
||||
^^^^^
|
||||
* dajaxice AJAX functions now receive parameters as function arguments.
|
||||
* dajaxice now uses standard python logging
|
||||
* some bugs fixed
|
||||
|
||||
0.0.1
|
||||
^^^^^
|
||||
* First Release
|
224
django-dajaxice/docs/conf.py
Normal file
224
django-dajaxice/docs/conf.py
Normal file
|
@ -0,0 +1,224 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# django-dajaxice documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri May 25 08:02:23 2012.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'django-dajaxice'
|
||||
copyright = u'2012, Jorge Bastida'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
import pkg_resources
|
||||
try:
|
||||
release = pkg_resources.get_distribution('django-dajaxice').version
|
||||
except pkg_resources.DistributionNotFound:
|
||||
print 'To build the documentation, The distribution information of django-dajaxice'
|
||||
print 'Has to be available. Either install the package into your'
|
||||
print 'development environment or run "setup.py develop" to setup the'
|
||||
print 'metadata. A virtualenv is recommended!'
|
||||
sys.exit(1)
|
||||
del pkg_resources
|
||||
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'nature'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'django-dajaxicedoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'django-dajaxice.tex', u'django-dajaxice Documentation',
|
||||
u'Jorge Bastida', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'django-dajaxice', u'django-dajaxice Documentation',
|
||||
[u'Jorge Bastida'], 1)
|
||||
]
|
31
django-dajaxice/docs/custom-error-callbacks.rst
Normal file
31
django-dajaxice/docs/custom-error-callbacks.rst
Normal file
|
@ -0,0 +1,31 @@
|
|||
Custom error callbacks
|
||||
======================
|
||||
|
||||
|
||||
How dajaxice handle errors
|
||||
--------------------------
|
||||
|
||||
When one of your functions raises an exception dajaxice returns as response the ``DAJAXICE_EXCEPTION`` message.
|
||||
On every response ``dajaxice.core.js`` checks if that response was an error or not and shows the user a default
|
||||
error message ``Something goes wrong``.
|
||||
|
||||
|
||||
Customize the default error message
|
||||
-----------------------------------
|
||||
This behaviour is configurable using the new ``Dajaxice.setup`` function.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
Dajaxice.setup({'default_exception_callback': function(){ alert('Error!'); }});
|
||||
|
||||
Customize error message per call
|
||||
--------------------------------
|
||||
In this new version you can also specify an error callback per dajaxice call.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
function custom_error(){
|
||||
alert('Custom error of my_function.');
|
||||
}
|
||||
|
||||
Dajaxice.simple.my_function(callback, {'user': 'tom'}, {'error_callback': custom_error});
|
45
django-dajaxice/docs/index.rst
Normal file
45
django-dajaxice/docs/index.rst
Normal file
|
@ -0,0 +1,45 @@
|
|||
.. django-dajaxice documentation master file, created by
|
||||
sphinx-quickstart on Fri May 25 08:02:23 2012.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
|
||||
django-dajaxice
|
||||
===============
|
||||
|
||||
Dajaxixe is an Easy to use AJAX library for django. Its main goal is to trivialize the asynchronous communication within the django server code and your js code. Dajaxice uses the unobtrusive standard-compliant (W3C) XMLHttpRequest 1.0 object.
|
||||
|
||||
django-dajaxice is a **JS-framework agnostic** library and focuses on decoupling the presentation logic from the server-side logic. dajaxice only requieres **5 minutes to start working.**
|
||||
|
||||
Dajaxice has the following aims:
|
||||
|
||||
* Isolate the communication between the client and the server.
|
||||
* JS Framework agnostic (No Prototype, JQuery... needed ).
|
||||
* Presentation logic outside the views (No presentation code inside ajax functions).
|
||||
* Lightweight.
|
||||
* Crossbrowsing ready.
|
||||
* `Unobtrusive standard-compliant (W3C) XMLHttpRequest 1.0 <http://code.google.com/p/xmlhttprequest/>`_ object usage.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
quickstart
|
||||
|
||||
custom-error-callbacks
|
||||
utils
|
||||
production-environment
|
||||
migrating-to-05
|
||||
available-settings
|
||||
|
||||
changelog
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
92
django-dajaxice/docs/installation.rst
Normal file
92
django-dajaxice/docs/installation.rst
Normal file
|
@ -0,0 +1,92 @@
|
|||
Installation
|
||||
============
|
||||
Follow this instructions to start using dajaxice in your django project.
|
||||
|
||||
Installing dajaxice
|
||||
-------------------
|
||||
|
||||
Add `dajaxice` in your project settings.py inside ``INSTALLED_APPS``::
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'dajaxice',
|
||||
...
|
||||
)
|
||||
|
||||
Ensure that your ``TEMPLATE_LOADERS``, looks like the following. Probably you'll only need to uncomment the last line.::
|
||||
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
Ensure that ``TEMPLATE_CONTEXT_PROCESSORS`` has ``django.core.context_processors.request``. Probably you'll only need to add the last line::
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.core.context_processors.debug',
|
||||
'django.core.context_processors.i18n',
|
||||
'django.core.context_processors.media',
|
||||
'django.core.context_processors.static',
|
||||
'django.core.context_processors.request',
|
||||
'django.contrib.messages.context_processors.messages'
|
||||
)
|
||||
|
||||
Add ``dajaxice.finders.DajaxiceFinder`` to ``STATICFILES_FINDERS``::
|
||||
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'dajaxice.finders.DajaxiceFinder',
|
||||
)
|
||||
|
||||
Configure dajaxice url
|
||||
----------------------
|
||||
|
||||
Add the following code inside urls.py::
|
||||
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
dajaxice_autodiscover()
|
||||
|
||||
Add a new line in urls.py urlpatterns with this code::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
...
|
||||
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
...
|
||||
)
|
||||
|
||||
If you aren't using ``django.contrib.staticfiles``, you should also enable it importing::
|
||||
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
and adding this line to the bottom of your urls.py::
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
Install dajaxice in your templates
|
||||
----------------------------------
|
||||
Dajaxice needs some JS to work. To include it in your templates, you should load ``dajaxice_templatetags`` and use ``dajaxice_js_import`` TemplateTag inside your head section. This TemplateTag will print needed js.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
{% load dajaxice_templatetags %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>My base template</title>
|
||||
...
|
||||
{% dajaxice_js_import %}
|
||||
</head>
|
||||
...
|
||||
</html>
|
||||
|
||||
This templatetag will include all the js dajaxice needs.
|
||||
|
||||
Use Dajaxice!
|
||||
-------------
|
||||
Now you can create your first ajax function following the :doc:`quickstart`.
|
55
django-dajaxice/docs/migrating-to-05.rst
Normal file
55
django-dajaxice/docs/migrating-to-05.rst
Normal file
|
@ -0,0 +1,55 @@
|
|||
Migrating to 0.5
|
||||
=================
|
||||
|
||||
Upgrade to django 1.3 or 1.4
|
||||
----------------------------
|
||||
|
||||
Dajaxice ``0.5`` requires ``django>=1.3``, so in order to make dajaxice work you'll need to upgrade your app to any of these ones.
|
||||
|
||||
* `Django 1.3 release notes <https://docs.djangoproject.com/en/dev/releases/1.3/>`_
|
||||
* `Django 1.4 release notes <https://docs.djangoproject.com/en/dev/releases/1.4/>`_
|
||||
|
||||
|
||||
Make django static-files work
|
||||
-----------------------------
|
||||
|
||||
Add this at the beginning of your ``urls.py`` file::
|
||||
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
and add this line to the bottom of your urls.py::
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
Add a new staticfiles finder named ``dajaxice.finders.DajaxiceFinder`` to the list of ``STATICFILES_FINDERS``::
|
||||
|
||||
STATICFILES_FINDERS = ('django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'dajaxice.finders.DajaxiceFinder')
|
||||
|
||||
Update dajaxice core url
|
||||
------------------------
|
||||
|
||||
Add ``dajaxice_config`` to the list of modules to import::
|
||||
|
||||
# Old import
|
||||
from dajaxice.core import dajaxice_autodiscover
|
||||
|
||||
# New import
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
|
||||
|
||||
And replate your old dajaxice url with the new one::
|
||||
|
||||
# Old style
|
||||
(r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, include('dajaxice.urls')),
|
||||
|
||||
# New style
|
||||
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
|
||||
|
||||
Done!
|
||||
-----
|
||||
|
||||
Your app should be working now!
|
||||
You can now read the :doc:`quickstart <quickstart>` to discover some of the new dajaxice features.
|
7
django-dajaxice/docs/production-environment.rst
Normal file
7
django-dajaxice/docs/production-environment.rst
Normal file
|
@ -0,0 +1,7 @@
|
|||
Production Environment
|
||||
======================
|
||||
|
||||
Since ``0.5`` dajaxice takes advantage of ``django.contrib.staticfiles`` so deploying a dajaxice application live is much easy than in previous versions.
|
||||
|
||||
You need to remember to run ``python manage.py collectstatic`` before deploying your code live. This command will collect all the static files your application need into ``STATIC_ROOT``. For further information, this is the `Django static files docuemntation <https://docs.djangoproject.com/en/dev/howto/static-files/>`_
|
||||
|
80
django-dajaxice/docs/quickstart.rst
Normal file
80
django-dajaxice/docs/quickstart.rst
Normal file
|
@ -0,0 +1,80 @@
|
|||
Quickstart
|
||||
==========
|
||||
|
||||
Create your first ajax function
|
||||
-------------------------------
|
||||
Create a file named ``ajax.py`` inside any of your apps. For example ``example/ajax.py``.
|
||||
|
||||
Inside this file create a simple function that return json.::
|
||||
|
||||
from django.utils import simplejson
|
||||
|
||||
def sayhello(request):
|
||||
return simplejson.dumps({'message':'Hello World'})
|
||||
|
||||
Now you'll need to register this function as a dajaxice function using the ``dajaxice_register`` decorator::
|
||||
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
@dajaxice_register
|
||||
def sayhello(request):
|
||||
return simplejson.dumps({'message':'Hello World'})
|
||||
|
||||
Invoque it from your JS
|
||||
-----------------------
|
||||
|
||||
You can invoque your ajax fuctions from javascript using:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
onclick="Dajaxice.example.sayhello(my_js_callback);"
|
||||
|
||||
The function ``my_js_callback`` is your JS function that will use your example return data. For example alert the message:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
function my_js_callback(data){
|
||||
alert(data.message);
|
||||
}
|
||||
|
||||
That callback will alert the message ``Hello World``.
|
||||
|
||||
|
||||
How can I do a GET request instead of a POST one?
|
||||
-------------------------------------------------
|
||||
|
||||
When you register your functions as ajax functions, you can choose the http method using::
|
||||
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def saybye(request):
|
||||
return simplejson.dumps({'message':'Bye!'})
|
||||
|
||||
This function will be executed doing a GET request and not a POST one.
|
||||
|
||||
|
||||
Can I combine both?
|
||||
-------------------
|
||||
|
||||
Yes! You can register a function as many times as you want, for example::
|
||||
|
||||
from django.utils import simplejson
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
@dajaxice_register(method='POST', name='user.update')
|
||||
@dajaxice_register(method='GET', name='user.info')
|
||||
def list_user(request):
|
||||
if request.method == 'POST':
|
||||
...
|
||||
else:
|
||||
...
|
||||
|
||||
In this case you'll be able to call this two JS functions::
|
||||
|
||||
Dajaxice.user.info( callback );
|
||||
Dajaxice.user.update( callback );
|
||||
|
||||
The first one will be a GET call and the second one a POST one.
|
16
django-dajaxice/docs/utils.rst
Normal file
16
django-dajaxice/docs/utils.rst
Normal file
|
@ -0,0 +1,16 @@
|
|||
Utils
|
||||
=====
|
||||
|
||||
dajaxice.utils.deserialize_form
|
||||
-------------------------------
|
||||
|
||||
Using ``deserialize_form`` you will be able to deserialize a query_string and use it as input of a Form::
|
||||
|
||||
from dajaxice.utils import deserialize_form
|
||||
|
||||
@dajaxice_register
|
||||
def send_form(request, form):
|
||||
form = ExampleForm(deserialize_form(form))
|
||||
if form.is_valid():
|
||||
...
|
||||
...
|
0
django-dajaxice/examples/examples/__init__.py
Normal file
0
django-dajaxice/examples/examples/__init__.py
Normal file
177
django-dajaxice/examples/examples/settings.py
Normal file
177
django-dajaxice/examples/examples/settings.py
Normal file
|
@ -0,0 +1,177 @@
|
|||
# Django settings for examples project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@example.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
||||
'NAME': '', # Or path to database file if using sqlite3.
|
||||
'USER': '', # Not used with sqlite3.
|
||||
'PASSWORD': '', # Not used with sqlite3.
|
||||
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
||||
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
||||
}
|
||||
}
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# On Unix systems, a value of None will cause Django to use the same
|
||||
# timezone as the operating system.
|
||||
# If running in a Windows environment this must be set to the same as your
|
||||
# system time zone.
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# If you set this to False, Django will not format dates, numbers and
|
||||
# calendars according to the current locale.
|
||||
USE_L10N = True
|
||||
|
||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
||||
USE_TZ = True
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/home/media/media.lawrence.com/media/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/home/media/media.lawrence.com/static/"
|
||||
STATIC_ROOT = 'static'
|
||||
|
||||
# URL prefix for static files.
|
||||
# Example: "http://media.lawrence.com/static/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = '$zr@-0lstgzehu)k(-pbg7wz=mv8%n%o7+j_@h&-yy&sx)pyau'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Uncomment the next line for simple clickjacking protection:
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'examples.urls'
|
||||
|
||||
# Python dotted path to the WSGI application used by Django's runserver.
|
||||
WSGI_APPLICATION = 'examples.wsgi.application'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'dajaxice',
|
||||
'simple'
|
||||
# Uncomment the next line to enable the admin:
|
||||
# 'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
},
|
||||
'console': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.StreamHandler'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
'dajaxice': {
|
||||
'handlers': ['console'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
STATICFILES_FINDERS = ("django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
"dajaxice.finders.DajaxiceFinder")
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
|
||||
"django.core.context_processors.debug",
|
||||
"django.core.context_processors.i18n",
|
||||
"django.core.context_processors.media",
|
||||
"django.core.context_processors.static",
|
||||
"django.core.context_processors.request",
|
||||
"django.contrib.messages.context_processors.messages")
|
25
django-dajaxice/examples/examples/urls.py
Normal file
25
django-dajaxice/examples/examples/urls.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from django.conf.urls import patterns, include, url
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
|
||||
dajaxice_autodiscover()
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
# from django.contrib import admin
|
||||
# admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Examples:
|
||||
# url(r'^$', 'examples.views.home', name='home'),
|
||||
# url(r'^examples/', include('examples.foo.urls')),
|
||||
|
||||
# Uncomment the admin/doc line below to enable admin documentation:
|
||||
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
|
||||
# Uncomment the next line to enable the admin:
|
||||
# url(r'^admin/', include(admin.site.urls)),
|
||||
(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
|
||||
url(r'', 'simple.views.index')
|
||||
)
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
28
django-dajaxice/examples/examples/wsgi.py
Normal file
28
django-dajaxice/examples/examples/wsgi.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
"""
|
||||
WSGI config for examples project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "examples.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
10
django-dajaxice/examples/manage.py
Normal file
10
django-dajaxice/examples/manage.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "examples.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
0
django-dajaxice/examples/simple/__init__.py
Normal file
0
django-dajaxice/examples/simple/__init__.py
Normal file
26
django-dajaxice/examples/simple/ajax.py
Normal file
26
django-dajaxice/examples/simple/ajax.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import simplejson
|
||||
|
||||
from dajaxice.decorators import dajaxice_register
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
@dajaxice_register(method='POST', name='other_post')
|
||||
def hello(request):
|
||||
return simplejson.dumps({'message': 'hello'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
@dajaxice_register(method='POST', name="more.complex.bye")
|
||||
def bye(request):
|
||||
raise Exception("PUMMMM")
|
||||
return simplejson.dumps({'message': 'bye'})
|
||||
|
||||
|
||||
@dajaxice_register
|
||||
def lol(request):
|
||||
return simplejson.dumps({'message': 'lol'})
|
||||
|
||||
|
||||
@dajaxice_register(method='GET')
|
||||
def get_args(request, foo):
|
||||
return simplejson.dumps({'message': 'hello get args %s' % foo})
|
3
django-dajaxice/examples/simple/models.py
Normal file
3
django-dajaxice/examples/simple/models.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
13
django-dajaxice/examples/simple/templates/simple/index.html
Normal file
13
django-dajaxice/examples/simple/templates/simple/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% load dajaxice_templatetags %}
|
||||
<html>
|
||||
<head>
|
||||
{% dajaxice_js_import 'nocsrf' %}
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="Dajaxice.simple.hello(function(d){alert(d.message);})">Hello</button>
|
||||
<button onclick="Dajaxice.simple.bye(function(d){alert(d.message);})">Bye</button>
|
||||
<button onclick="Dajaxice.more.complex.bye(function(d){alert(d.message);})">Complex Bye</button>
|
||||
<button onclick="Dajaxice.simple.lol(function(d){alert(d.message);})">LOL</button>
|
||||
<button onclick="Dajaxice.simple.get_args(function(d){alert(d.message);}, {'foo': 'var'})">GET args</button>
|
||||
</body>
|
||||
</html>
|
16
django-dajaxice/examples/simple/tests.py
Normal file
16
django-dajaxice/examples/simple/tests.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
9
django-dajaxice/examples/simple/views.py
Normal file
9
django-dajaxice/examples/simple/views.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Create your views here.
|
||||
from django.shortcuts import render
|
||||
|
||||
from dajaxice.core import dajaxice_functions
|
||||
|
||||
|
||||
def index(request):
|
||||
|
||||
return render(request, 'simple/index.html')
|
31
django-dajaxice/setup.py
Normal file
31
django-dajaxice/setup.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name='django-dajaxice',
|
||||
version='0.5.5',
|
||||
author='Jorge Bastida',
|
||||
author_email='me@jorgebastida.com',
|
||||
description='Agnostic and easy to use ajax library for django',
|
||||
url='http://dajaxproject.com',
|
||||
license='BSD',
|
||||
packages=['dajaxice',
|
||||
'dajaxice.templatetags',
|
||||
'dajaxice.core'],
|
||||
package_data={'dajaxice': ['templates/dajaxice/*']},
|
||||
long_description=("Easy to use AJAX library for django, all the "
|
||||
"presentation logic resides outside the views and "
|
||||
"doesn't require any JS Framework. Dajaxice uses the "
|
||||
"unobtrusive standard-compliant (W3C) XMLHttpRequest "
|
||||
"1.0 object."),
|
||||
install_requires=[
|
||||
'Django>=1.3'
|
||||
],
|
||||
classifiers=['Development Status :: 4 - Beta',
|
||||
'Environment :: Web Environment',
|
||||
'Framework :: Django',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Utilities']
|
||||
)
|
Loading…
Reference in a new issue