django-js-reverse-0.7.3/0000755000076500000240000000000013026013277015450 5ustar boernistaff00000000000000django-js-reverse-0.7.3/PKG-INFO0000644000076500000240000002141413026013277016547 0ustar boernistaff00000000000000Metadata-Version: 1.1 Name: django-js-reverse Version: 0.7.3 Summary: Javascript url handling for Django that doesn't hurt. Home-page: https://github.com/ierror/django-js-reverse Author: Bernhard Janetzki Author-email: boerni@gmail.com License: MIT Download-URL: http://pypi.python.org/pypi/django-js-reverse/ Description: ================= Django JS Reverse ================= .. image:: https://img.shields.io/pypi/v/django-js-reverse.svg :target: https://pypi.python.org/pypi/django-js-reverse/ .. image:: https://img.shields.io/travis/ierror/django-js-reverse/master.svg :target: https://travis-ci.org/ierror/django-js-reverse .. image:: https://img.shields.io/coveralls/ierror/django-js-reverse/master.svg :alt: Coverage Status :target: https://coveralls.io/r/ierror/django-js-reverse?branch=master .. image:: https://img.shields.io/github/license/ierror/django-js-reverse.svg :target: https://raw.githubusercontent.com/ierror/django-js-reverse/develop/LICENSE .. image:: https://img.shields.io/pypi/wheel/django-js-reverse.svg **Javascript url handling for Django that doesn’t hurt.** Overview -------- Django JS Reverse is a small django app that makes url handling of `named urls `_ in javascript easy and non-annoying.. For example you can retrieve a named url: urls.py: :: url(r'^/betterliving/(?P[-\w]+)/(?P\d+)/$', 'get_house', name='betterliving_get_house'), in javascript like: :: Urls.betterliving_get_house('house', 12) Result: :: /betterliving/house/12/ Changelog _________ 0.7.3 New: Support for Django 1.10 Chg: Renamed "production" branch to "master" Fix: `#48 `_ - "Change False to 'window' in global object name in README." Thank you `karamanolev `_ Fix: `PR #45 `_ - "Fix: collectstatic_js_reverse usage message" Thank you `ghedsouza `_ Fix: `PR #44 `_ - "Remove duplicate _get_url call" Thank you `razh `_ 0.7.2 Fix: `#42 `_ - "Templatetag js_reverse_inline breaks on Django 1.9" Thank you `tommikaikkonen `_ Optimized imports 0.7.1 Fix: `#41 `_ - make it possible to use number 0 as url argument `Full changelog `_ Requirements ------------ - Python (2.6, 2.7, 3.1, 3.3, 3.4, 3.5) - Django (1.5, 1.6, 1.7, 1.8, 1.9, 1.10) Installation ------------ Install using ``pip`` … :: pip install django-js-reverse … or clone the project from github. :: git clone https://github.com/ierror/django-js-reverse.git Add ``'django_js_reverse'`` to your ``INSTALLED_APPS`` setting. :: INSTALLED_APPS = ( ... 'django_js_reverse', ) Usage as static file -------------------- First generate static file by :: ./manage.py collectstatic_js_reverse If you change some urls or add an app and want to update the reverse.js file, run the command again. After this add the file to your template :: Usage with views ---------------- Include none-cached view … :: urlpatterns = patterns('', url(r'^jsreverse/$', 'django_js_reverse.views.urls_js', name='js_reverse'), ) … or a cached one that delivers the urls javascript :: from django_js_reverse.views import urls_js urlpatterns = patterns('', url(r'^jsreverse/$', cache_page(3600)(urls_js), name='js_reverse'), ) Include javascript in your template :: or, if you are using Django > 1.5 :: Usage as template tag _____________________ {% load js_reverse %} Use the urls in javascript -------------------------- If your url names are valid javascript identifiers ([$A-Z\_][-Z\_$]\*)i you can access them by the Dot notation: :: Urls.betterliving_get_house('house', 12) If the named url contains invalid identifiers use the Square bracket notation instead: :: Urls['betterliving-get-house']('house', 12) Urls['namespace:betterliving-get-house']('house', 12) Options ------- Optionally, you can overwrite the default javascript variable ‘Urls’ used to access the named urls by django setting :: JS_REVERSE_JS_VAR_NAME = 'Urls' Optionally, you can change the name of the global object the javascript variable used to access the named urls is attached to. Default is :code:`this` :: JS_REVERSE_JS_GLOBAL_OBJECT_NAME = 'window' Optionally, you can disable the minfication of the generated javascript file by django setting :: JS_REVERSE_JS_MINIFY = False By default all namespaces are included :: JS_REVERSE_EXCLUDE_NAMESPACES = [] To exclude any namespaces from the generated javascript file, add them to the `JS_REVERSE_EXCLUDE_NAMESPACES` setting :: JS_REVERSE_EXCLUDE_NAMESPACES = ['admin', 'djdt', ...] If you want to include only specific namespaces add them to the `JS_REVERSE_INCLUDE_ONLY_NAMESPACES` setting tips: * Use "" for urls without namespace * Use "foo\0" to include urls just from "foo" namaspace and not from any subnamespaces (e.g. "foo:bar") :: JS_REVERSE_INCLUDE_ONLY_NAMESPACES = ['poll', 'calendar', ...] If you run your application under a subpath, the collectstatic_js_reverse needs to take care of this. Define the prefix in your django settings: :: JS_REVERSE_SCRIPT_PREFIX = '/myprefix/' By default collectstatic_js_reverse writes its output (reverse.js) to your project's STATIC_ROOT. You can change the output path: :: JS_REVERSE_OUTPUT_PATH = 'some_path' Running the test suite ---------------------- :: make test License ------- `MIT `_ Contact ------- `@i_error `_ -------------- Enjoy! Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Framework :: Django Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License django-js-reverse-0.7.3/README.rst0000644000076500000240000001402313026012327017132 0ustar boernistaff00000000000000================= Django JS Reverse ================= .. image:: https://img.shields.io/pypi/v/django-js-reverse.svg :target: https://pypi.python.org/pypi/django-js-reverse/ .. image:: https://img.shields.io/travis/ierror/django-js-reverse/master.svg :target: https://travis-ci.org/ierror/django-js-reverse .. image:: https://img.shields.io/coveralls/ierror/django-js-reverse/master.svg :alt: Coverage Status :target: https://coveralls.io/r/ierror/django-js-reverse?branch=master .. image:: https://img.shields.io/github/license/ierror/django-js-reverse.svg :target: https://raw.githubusercontent.com/ierror/django-js-reverse/develop/LICENSE .. image:: https://img.shields.io/pypi/wheel/django-js-reverse.svg **Javascript url handling for Django that doesn’t hurt.** Overview -------- Django JS Reverse is a small django app that makes url handling of `named urls `_ in javascript easy and non-annoying.. For example you can retrieve a named url: urls.py: :: url(r'^/betterliving/(?P[-\w]+)/(?P\d+)/$', 'get_house', name='betterliving_get_house'), in javascript like: :: Urls.betterliving_get_house('house', 12) Result: :: /betterliving/house/12/ Changelog _________ 0.7.3 New: Support for Django 1.10 Chg: Renamed "production" branch to "master" Fix: `#48 `_ - "Change False to 'window' in global object name in README." Thank you `karamanolev `_ Fix: `PR #45 `_ - "Fix: collectstatic_js_reverse usage message" Thank you `ghedsouza `_ Fix: `PR #44 `_ - "Remove duplicate _get_url call" Thank you `razh `_ 0.7.2 Fix: `#42 `_ - "Templatetag js_reverse_inline breaks on Django 1.9" Thank you `tommikaikkonen `_ Optimized imports 0.7.1 Fix: `#41 `_ - make it possible to use number 0 as url argument `Full changelog `_ Requirements ------------ - Python (2.6, 2.7, 3.1, 3.3, 3.4, 3.5) - Django (1.5, 1.6, 1.7, 1.8, 1.9, 1.10) Installation ------------ Install using ``pip`` … :: pip install django-js-reverse … or clone the project from github. :: git clone https://github.com/ierror/django-js-reverse.git Add ``'django_js_reverse'`` to your ``INSTALLED_APPS`` setting. :: INSTALLED_APPS = ( ... 'django_js_reverse', ) Usage as static file -------------------- First generate static file by :: ./manage.py collectstatic_js_reverse If you change some urls or add an app and want to update the reverse.js file, run the command again. After this add the file to your template :: Usage with views ---------------- Include none-cached view … :: urlpatterns = patterns('', url(r'^jsreverse/$', 'django_js_reverse.views.urls_js', name='js_reverse'), ) … or a cached one that delivers the urls javascript :: from django_js_reverse.views import urls_js urlpatterns = patterns('', url(r'^jsreverse/$', cache_page(3600)(urls_js), name='js_reverse'), ) Include javascript in your template :: or, if you are using Django > 1.5 :: Usage as template tag _____________________ {% load js_reverse %} Use the urls in javascript -------------------------- If your url names are valid javascript identifiers ([$A-Z\_][-Z\_$]\*)i you can access them by the Dot notation: :: Urls.betterliving_get_house('house', 12) If the named url contains invalid identifiers use the Square bracket notation instead: :: Urls['betterliving-get-house']('house', 12) Urls['namespace:betterliving-get-house']('house', 12) Options ------- Optionally, you can overwrite the default javascript variable ‘Urls’ used to access the named urls by django setting :: JS_REVERSE_JS_VAR_NAME = 'Urls' Optionally, you can change the name of the global object the javascript variable used to access the named urls is attached to. Default is :code:`this` :: JS_REVERSE_JS_GLOBAL_OBJECT_NAME = 'window' Optionally, you can disable the minfication of the generated javascript file by django setting :: JS_REVERSE_JS_MINIFY = False By default all namespaces are included :: JS_REVERSE_EXCLUDE_NAMESPACES = [] To exclude any namespaces from the generated javascript file, add them to the `JS_REVERSE_EXCLUDE_NAMESPACES` setting :: JS_REVERSE_EXCLUDE_NAMESPACES = ['admin', 'djdt', ...] If you want to include only specific namespaces add them to the `JS_REVERSE_INCLUDE_ONLY_NAMESPACES` setting tips: * Use "" for urls without namespace * Use "foo\0" to include urls just from "foo" namaspace and not from any subnamespaces (e.g. "foo:bar") :: JS_REVERSE_INCLUDE_ONLY_NAMESPACES = ['poll', 'calendar', ...] If you run your application under a subpath, the collectstatic_js_reverse needs to take care of this. Define the prefix in your django settings: :: JS_REVERSE_SCRIPT_PREFIX = '/myprefix/' By default collectstatic_js_reverse writes its output (reverse.js) to your project's STATIC_ROOT. You can change the output path: :: JS_REVERSE_OUTPUT_PATH = 'some_path' Running the test suite ---------------------- :: make test License ------- `MIT `_ Contact ------- `@i_error `_ -------------- Enjoy! django-js-reverse-0.7.3/django_js_reverse/0000755000076500000240000000000013026013277021141 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/__init__.py0000755000076500000240000000005413026012327023247 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- VERSION = (0, 7, 3) django-js-reverse-0.7.3/django_js_reverse/core.py0000700000076500000240000001151512717411652022444 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- import re import sys from django.conf import settings from django.core import urlresolvers from django.core.exceptions import ImproperlyConfigured from django.template import loader from . import rjsmin from .js_reverse_settings import (JS_EXCLUDE_NAMESPACES, JS_GLOBAL_OBJECT_NAME, JS_INCLUDE_ONLY_NAMESPACES, JS_MINIFY, JS_VAR_NAME) if sys.version < '3': text_type = unicode # NOQA else: text_type = str JS_IDENTIFIER_RE = re.compile(r'^[$A-Z_][\dA-Z_$]*$') def prepare_url_list(urlresolver, namespace_path='', namespace=''): """ returns list of tuples [(, ), ...] """ exclude_ns = getattr(settings, 'JS_REVERSE_EXCLUDE_NAMESPACES', JS_EXCLUDE_NAMESPACES) include_only_ns = getattr(settings, 'JS_REVERSE_INCLUDE_ONLY_NAMESPACES', JS_INCLUDE_ONLY_NAMESPACES) if exclude_ns and include_only_ns: raise ImproperlyConfigured( 'Neither use JS_REVERSE_EXCLUDE_NAMESPACES nor JS_REVERSE_INCLUDE_ONLY_NAMESPACES setting') if namespace[:-1] in exclude_ns: return include_only_allow = True # include_only state varible if include_only_ns != []: # True mean that ns passed the test in_on_empty_ns = False in_on_is_in_list = False in_on_null = False # Test urls without ns if namespace == '' and '' in include_only_ns: in_on_empty_ns = True # check if nestead ns isn't subns of include_only ns # e.g. ns = "foo:bar" include_only = ["foo"] -> this ns will be used # works for ns = "lorem:ipsum:dolor" include_only = ["lorem:ipsum"] # ns "lorem" will be ignored but "lorem:ipsum" & "lorem:ipsum:.." won't for ns in include_only_ns: if ns != "" and namespace[:-1].startswith(ns): in_on_is_in_list = True break # Test if isn't used "\0" flag # use "foo\0" to add urls just from "foo" not from subns "foo:bar" if namespace[:-1] + '\0' in include_only_ns: in_on_null = True include_only_allow = in_on_empty_ns or in_on_is_in_list or in_on_null if include_only_allow: for url_name in urlresolver.reverse_dict.keys(): if isinstance(url_name, (text_type, str)): url_patterns = [] for url_pattern in urlresolver.reverse_dict.getlist(url_name): url_patterns += [ [namespace_path + pat[0], pat[1]] for pat in url_pattern[0]] yield [namespace + url_name, url_patterns] for inner_ns, (inner_ns_path, inner_urlresolver) in \ urlresolver.namespace_dict.items(): inner_ns_path = namespace_path + inner_ns_path inner_ns = namespace + inner_ns + ':' # if we have inner_ns_path, reconstruct a new resolver so that we can # handle regex substitutions within the regex of a namespace. if inner_ns_path: inner_urlresolver = urlresolvers.get_ns_resolver(inner_ns_path, inner_urlresolver) inner_ns_path = '' for x in prepare_url_list(inner_urlresolver, inner_ns_path, inner_ns): yield x def generate_js(default_urlresolver): js_var_name = getattr(settings, 'JS_REVERSE_JS_VAR_NAME', JS_VAR_NAME) if not JS_IDENTIFIER_RE.match(js_var_name.upper()): raise ImproperlyConfigured( 'JS_REVERSE_JS_VAR_NAME setting "%s" is not a valid javascript identifier.' % (js_var_name)) js_global_object_name = getattr(settings, 'JS_REVERSE_JS_GLOBAL_OBJECT_NAME', JS_GLOBAL_OBJECT_NAME) if not JS_IDENTIFIER_RE.match(js_global_object_name.upper()): raise ImproperlyConfigured( 'JS_REVERSE_JS_GLOBAL_OBJECT_NAME setting "%s" is not a valid javascript identifier.' % ( js_global_object_name)) minfiy = getattr(settings, 'JS_REVERSE_JS_MINIFY', JS_MINIFY) if not isinstance(minfiy, bool): raise ImproperlyConfigured( 'JS_REVERSE_JS_MINIFY setting "%s" is not a valid. Needs to be set to True or False.' % (minfiy)) script_prefix_via_config = getattr(settings, 'JS_REVERSE_SCRIPT_PREFIX', None) if script_prefix_via_config: script_prefix = script_prefix_via_config if not script_prefix.endswith('/'): script_prefix = '{0}/'.format(script_prefix) else: script_prefix = urlresolvers.get_script_prefix() js_content = loader.render_to_string('django_js_reverse/urls_js.tpl', { 'urls': sorted(list(prepare_url_list(default_urlresolver))), 'url_prefix': script_prefix, 'js_var_name': js_var_name, 'js_global_object_name': js_global_object_name, }) if minfiy: js_content = rjsmin.jsmin(js_content) return js_content django-js-reverse-0.7.3/django_js_reverse/js_reverse_settings.py0000700000076500000240000000030612717404673025604 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- JS_VAR_NAME = 'Urls' JS_MINIFY = True JS_EXCLUDE_NAMESPACES = [] JS_INCLUDE_ONLY_NAMESPACES = [] JS_SCRIPT_PREFIX = None JS_GLOBAL_OBJECT_NAME = 'this' JS_OUTPUT_PATH = None django-js-reverse-0.7.3/django_js_reverse/management/0000755000076500000240000000000013026013277023255 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/management/__init__.py0000700000076500000240000000002612561411307025354 0ustar boernistaff00000000000000__author__ = 'boerni' django-js-reverse-0.7.3/django_js_reverse/management/commands/0000755000076500000240000000000013026013277025056 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/management/commands/__init__.py0000700000076500000240000000002612561411307027155 0ustar boernistaff00000000000000__author__ = 'boerni' django-js-reverse-0.7.3/django_js_reverse/management/commands/collectstatic_js_reverse.py0000755000076500000240000000302213026012327032507 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- import os import sys from django.conf import settings from django.core import urlresolvers from django.core.exceptions import ImproperlyConfigured from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage from django.core.management.base import BaseCommand from django_js_reverse.core import generate_js from django_js_reverse.js_reverse_settings import JS_OUTPUT_PATH class Command(BaseCommand): help = 'Creates a static urls-js file for django-js-reverse' def get_location(self): output_path = getattr(settings, 'JS_REVERSE_OUTPUT_PATH', JS_OUTPUT_PATH) if output_path: return output_path if not hasattr(settings, 'STATIC_ROOT') or not settings.STATIC_ROOT: raise ImproperlyConfigured('The collectstatic_js_reverse command needs settings.JS_REVERSE_OUTPUT_PATH or settings.STATIC_ROOT to be set.') return os.path.join(settings.STATIC_ROOT, 'django_js_reverse', 'js') def handle(self, *args, **options): location = self.get_location() file = 'reverse.js' fs = FileSystemStorage(location=location) if fs.exists(file): fs.delete(file) default_urlresolver = urlresolvers.get_resolver(None) content = generate_js(default_urlresolver) fs.save(file, ContentFile(content)) if len(sys.argv) > 1 and sys.argv[1] in ['collectstatic_js_reverse']: self.stdout.write('js-reverse file written to %s' % (location)) # pragma: no cover django-js-reverse-0.7.3/django_js_reverse/models.py0000700000076500000240000000000012717404673022767 0ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/rjsmin.py0000644000076500000240000004071713000124020023003 0ustar boernistaff00000000000000#!/usr/bin/env python # -*- coding: ascii -*- r""" ===================== Javascript Minifier ===================== rJSmin is a javascript minifier written in python. The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\\. :Copyright: Copyright 2011 - 2014 Andr\xe9 Malo or his licensors, as applicable :License: Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. The module is a re-implementation aiming for speed, so it can be used at runtime (rather than during a preprocessing step). Usually it produces the same results as the original ``jsmin.c``. It differs in the following ways: - there is no error detection: unterminated string, regex and comment literals are treated as regular javascript code and minified as such. - Control characters inside string and regex literals are left untouched; they are not converted to spaces (nor to \\n) - Newline characters are not allowed inside string and regex literals, except for line continuations in string literals (ECMA-5). - "return /regex/" is recognized correctly. - "+ +" and "- -" sequences are not collapsed to '++' or '--' - Newlines before ! operators are removed more sensibly - Comments starting with an exclamation mark (``!``) can be kept optionally - rJSmin does not handle streams, but only complete strings. (However, the module provides a "streamy" interface). Since most parts of the logic are handled by the regex engine it's way faster than the original python port of ``jsmin.c`` by Baruch Even. The speed factor varies between about 6 and 55 depending on input and python version (it gets faster the more compressed the input already is). Compared to the speed-refactored python port by Dave St.Germain the performance gain is less dramatic but still between 3 and 50 (for huge inputs). See the docs/BENCHMARKS file for details. rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more. Both python 2 and python 3 are supported. .. _jsmin.c by Douglas Crockford: http://www.crockford.com/javascript/jsmin.c """ if __doc__: # pylint: disable = W0622 __doc__ = __doc__.encode('ascii').decode('unicode_escape') __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape') __docformat__ = "restructuredtext en" __license__ = "Apache License, Version 2.0" __version__ = '1.0.10' __all__ = ['jsmin'] import re as _re def _make_jsmin(python_only=False): """ Generate JS minifier based on `jsmin.c by Douglas Crockford`_ .. _jsmin.c by Douglas Crockford: http://www.crockford.com/javascript/jsmin.c :Parameters: `python_only` : ``bool`` Use only the python variant. If true, the c extension is not even tried to be loaded. :Return: Minifier :Rtype: ``callable`` """ # pylint: disable = R0912, R0914, W0612 if not python_only: try: import _rjsmin # pylint: disable = F0401 except ImportError: pass else: return _rjsmin.jsmin try: xrange except NameError: xrange = range # pylint: disable = W0622 space_chars = r'[\000-\011\013\014\016-\040]' line_comment = r'(?://[^\r\n]*)' space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' space_comment_nobang = r'(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)' bang_comment = r'(?:/\*![^*]*\*+(?:[^/*][^*]*\*+)*/)' string1 = \ r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)' string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")' strings = r'(?:%s|%s)' % (string1, string2) charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])' nospecial = r'[^/\\\[\r\n]' regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % ( nospecial, charclass, nospecial ) space = r'(?:%s|%s)' % (space_chars, space_comment) space_nobang = r'(?:%s|%s)' % (space_chars, space_comment_nobang) newline = r'(?:%s?[\r\n])' % line_comment def fix_charclass(result): """ Fixup string of chars to fit into a regex char class """ pos = result.find('-') if pos >= 0: result = r'%s%s-' % (result[:pos], result[pos + 1:]) def sequentize(string): """ Notate consecutive characters as sequence (1-4 instead of 1234) """ first, last, result = None, None, [] for char in map(ord, string): if last is None: first = last = char elif last + 1 == char: last = char else: result.append((first, last)) first = last = char if last is not None: result.append((first, last)) return ''.join(['%s%s%s' % ( chr(first), last > first + 1 and '-' or '', last != first and chr(last) or '' ) for first, last in result]) return _re.sub( r'([\000-\040\047])', # \047 for better portability lambda m: '\\%03o' % ord(m.group(1)), ( sequentize(result) .replace('\\', '\\\\') .replace('[', '\\[') .replace(']', '\\]') ) ) def id_literal_(what): """ Make id_literal like char class """ match = _re.compile(what).match result = ''.join([ chr(c) for c in xrange(127) if not match(chr(c)) ]) return '[^%s]' % fix_charclass(result) def not_id_literal_(keep): """ Make negated id_literal like char class """ match = _re.compile(id_literal_(keep)).match result = ''.join([ chr(c) for c in xrange(127) if not match(chr(c)) ]) return r'[%s]' % fix_charclass(result) not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]') preregex1 = r'[(,=:\[!&|?{};\r\n]' preregex2 = r'%(not_id_literal)sreturn' % locals() id_literal = id_literal_(r'[a-zA-Z0-9_$]') id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]') id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]') dull = r'[^\047"/\000-\040]' space_sub_simple = _re.compile(( # noqa pylint: disable = C0330 r'(%(dull)s+)' r'|(%(strings)s%(dull)s*)' r'|(?<=%(preregex1)s)' r'%(space)s*(?:%(newline)s%(space)s*)*' r'(%(regex)s%(dull)s*)' r'|(?<=%(preregex2)s)' r'%(space)s*(?:%(newline)s%(space)s)*' r'(%(regex)s%(dull)s*)' r'|(?<=%(id_literal_close)s)' r'%(space)s*(?:(%(newline)s)%(space)s*)+' r'(?=%(id_literal_open)s)' r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)' r'|(?<=\+)(%(space)s)+(?=\+)' r'|(?<=-)(%(space)s)+(?=-)' r'|%(space)s+' r'|(?:%(newline)s%(space)s*)+' ) % locals()).sub #print space_sub_simple.__self__.pattern def space_subber_simple(match): """ Substitution callback """ # pylint: disable = R0911 groups = match.groups() if groups[0]: return groups[0] elif groups[1]: return groups[1] elif groups[2]: return groups[2] elif groups[3]: return groups[3] elif groups[4]: return '\n' elif groups[5] or groups[6] or groups[7]: return ' ' else: return '' space_sub_banged = _re.compile(( # noqa pylint: disable = C0330 r'(%(dull)s+)' r'|(%(strings)s%(dull)s*)' r'|(%(bang_comment)s%(dull)s*)' r'|(?<=%(preregex1)s)' r'%(space)s*(?:%(newline)s%(space)s*)*' r'(%(regex)s%(dull)s*)' r'|(?<=%(preregex2)s)' r'%(space)s*(?:%(newline)s%(space)s)*' r'(%(regex)s%(dull)s*)' r'|(?<=%(id_literal_close)s)' r'%(space)s*(?:(%(newline)s)%(space)s*)+' r'(?=%(id_literal_open)s)' r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)' r'|(?<=\+)(%(space)s)+(?=\+)' r'|(?<=-)(%(space)s)+(?=-)' r'|%(space)s+' r'|(?:%(newline)s%(space)s*)+' ) % dict(locals(), space=space_nobang)).sub #print space_sub_banged.__self__.pattern def space_subber_banged(match): """ Substitution callback """ # pylint: disable = R0911 groups = match.groups() if groups[0]: return groups[0] elif groups[1]: return groups[1] elif groups[2]: return groups[2] elif groups[3]: return groups[3] elif groups[4]: return groups[4] elif groups[5]: return '\n' elif groups[6] or groups[7] or groups[8]: return ' ' else: return '' def jsmin(script, keep_bang_comments=False): # pylint: disable = W0621 r""" Minify javascript based on `jsmin.c by Douglas Crockford`_\. Instead of parsing the stream char by char, it uses a regular expression approach which minifies the whole script with one big substitution regex. .. _jsmin.c by Douglas Crockford: http://www.crockford.com/javascript/jsmin.c :Parameters: `script` : ``str`` Script to minify `keep_bang_comments` : ``bool`` Keep comments starting with an exclamation mark? (``/*!...*/``) :Return: Minified script :Rtype: ``str`` """ if keep_bang_comments: return space_sub_banged( space_subber_banged, '\n%s\n' % script ).strip() else: return space_sub_simple( space_subber_simple, '\n%s\n' % script ).strip() return jsmin jsmin = _make_jsmin(python_only=True) def jsmin_for_posers(script, keep_bang_comments=False): r""" Minify javascript based on `jsmin.c by Douglas Crockford`_\. Instead of parsing the stream char by char, it uses a regular expression approach which minifies the whole script with one big substitution regex. .. _jsmin.c by Douglas Crockford: http://www.crockford.com/javascript/jsmin.c :Warning: This function is the digest of a _make_jsmin() call. It just utilizes the resulting regexes. It's here for fun and may vanish any time. Use the `jsmin` function instead. :Parameters: `script` : ``str`` Script to minify `keep_bang_comments` : ``bool`` Keep comments starting with an exclamation mark? (``/*!...*/``) :Return: Minified script :Rtype: ``str`` """ if not keep_bang_comments: rex = ( r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]' r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]' r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?' r'{};\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*' r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0' r'14\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r' r'\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r' r'\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<' r'=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\016-\04' r'0]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[' r'\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^' r'*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:' r'\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[' r'^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|~])(?:[\000' r'-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?' r':((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]|(?' r':/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,.' r'/:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\0' r'13\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\00' r'0-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]' r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-' r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?' r'=-)|(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]' r'*\*+)*/))+|(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\0' r'16-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+' ) def subber(match): """ Substitution callback """ groups = match.groups() return ( groups[0] or groups[1] or groups[2] or groups[3] or (groups[4] and '\n') or (groups[5] and ' ') or (groups[6] and ' ') or (groups[7] and ' ') or '' ) else: rex = ( r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]' r'|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]' r'|\r?\n|\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|((?:/\*![^*]*\*' r'+(?:[^/*][^*]*\*+)*/)[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r' r'\n])(?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*' r'][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\0' r'14\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(' r'?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:' r'\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]' r'*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\0' r'16-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[' r'^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*' r']*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(' r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/' r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\' r'^`{|~])(?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[' r'^/*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011' r'\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+' r'(?=[^\000-\040"#%-\047)*,./:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@' r'\[-^`{-~-])((?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*' r'+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:-@\[-^`{-~-])|(?<=\+)' r'((?:[\000-\011\013\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^' r'*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-\040]|(?:' r'/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013' r'\014\016-\040]|(?:/\*(?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?' r':(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*(' r'?!!)[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+' ) def subber(match): """ Substitution callback """ groups = match.groups() return ( groups[0] or groups[1] or groups[2] or groups[3] or groups[4] or (groups[5] and '\n') or (groups[6] and ' ') or (groups[7] and ' ') or (groups[8] and ' ') or '' ) return _re.sub(rex, subber, '\n%s\n' % script).strip() if __name__ == '__main__': def main(): """ Main """ import sys as _sys keep_bang_comments = ( '-b' in _sys.argv[1:] or '-bp' in _sys.argv[1:] or '-pb' in _sys.argv[1:] ) if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \ or '-pb' in _sys.argv[1:]: global jsmin # pylint: disable = W0603 jsmin = _make_jsmin(python_only=True) _sys.stdout.write(jsmin( _sys.stdin.read(), keep_bang_comments=keep_bang_comments )) main() django-js-reverse-0.7.3/django_js_reverse/templates/0000755000076500000240000000000013026013277023137 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/templates/django_js_reverse/0000755000076500000240000000000013026013277026630 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/templates/django_js_reverse/urls_js.tpl0000755000076500000240000000725413026012327031040 0ustar boernistaff00000000000000{{ js_global_object_name }}.{{ js_var_name }} = (function () { var Urls = {}; var self = { url_patterns:{} }; var _get_url = function (url_pattern) { return function () { var _arguments, index, url, url_arg, url_args, _i, _len, _ref, _ref_list, match_ref, provided_keys, build_kwargs; _arguments = arguments; _ref_list = self.url_patterns[url_pattern]; if (arguments.length == 1 && typeof (arguments[0]) == "object") { // kwargs mode var provided_keys_list = Object.keys (arguments[0]); provided_keys = {}; for (_i = 0; _i < provided_keys_list.length; _i++) provided_keys[provided_keys_list[_i]] = 1; match_ref = function (ref) { var _i; // Verify that they have the same number of arguments if (ref[1].length != provided_keys_list.length) return false; for (_i = 0; _i < ref[1].length && ref[1][_i] in provided_keys; _i++); // If for loop completed, we have all keys return _i == ref[1].length; } build_kwargs = function (keys) {return _arguments[0];} } else { // args mode match_ref = function (ref) { return ref[1].length == _arguments.length; } build_kwargs = function (keys) { var kwargs = {}; for (var i = 0; i < keys.length; i++) { kwargs[keys[i]] = _arguments[i]; } return kwargs; } } for (_i = 0; _i < _ref_list.length && !match_ref(_ref_list[_i]); _i++); // can't find a match if (_i == _ref_list.length) return null; _ref = _ref_list[_i]; url = _ref[0], url_args = build_kwargs(_ref[1]); for (url_arg in url_args) { var url_arg_value = url_args[url_arg]; if (url_arg_value === undefined || url_arg_value === null) { url_arg_value = ''; } else { url_arg_value = url_arg_value.toString(); } url = url.replace("%(" + url_arg + ")s", url_arg_value); } return '{{url_prefix|escapejs}}' + url; }; }; var name, pattern, url, url_patterns, _i, _len, _ref; url_patterns = [ {% for name, patterns in urls %} [ '{{name|escapejs}}', [ {% for path, args in patterns %} [ '{{path|escapejs}}', [ {% for arg in args %} '{{arg|escapejs}}', {% endfor %} ]{% if not forloop.last %},{% endif %} ]{% if not forloop.last %},{% endif %} {% endfor %} ]{% if not forloop.last %},{% endif %} ]{% if not forloop.last %},{% endif %} {% endfor %} ]; self.url_patterns = {}; for (_i = 0, _len = url_patterns.length; _i < _len; _i++) { _ref = url_patterns[_i], name = _ref[0], pattern = _ref[1]; self.url_patterns[name] = pattern; url = _get_url(name); Urls[name] = url; Urls[name.replace(/-/g, '_')] = url; } return Urls; })(); django-js-reverse-0.7.3/django_js_reverse/templatetags/0000755000076500000240000000000013026013277023633 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/templatetags/__init__.py0000700000076500000240000000000012561411307025722 0ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse/templatetags/js_reverse.py0000700000076500000240000000123612717411652026354 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- from django import template from django.core import urlresolvers from django.utils.safestring import mark_safe from django_js_reverse.core import generate_js register = template.Library() @register.simple_tag(takes_context=True) def js_reverse_inline(context): """ Outputs a string of javascript that can generate URLs via the use of the names given to those URLs. """ if 'request' in context: default_urlresolver = urlresolvers.get_resolver(getattr(context['request'], 'urlconf', None)) else: default_urlresolver = urlresolvers.get_resolver(None) return mark_safe(generate_js(default_urlresolver)) django-js-reverse-0.7.3/django_js_reverse/views.py0000700000076500000240000000061212717411652022645 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- from django.core import urlresolvers from django.http import HttpResponse from django_js_reverse.core import generate_js def urls_js(request): default_urlresolver = urlresolvers.get_resolver(getattr(request, 'urlconf', None)) response_body = generate_js(default_urlresolver) return HttpResponse(response_body, **{'content_type': 'application/javascript'}) django-js-reverse-0.7.3/django_js_reverse.egg-info/0000755000076500000240000000000013026013277022633 5ustar boernistaff00000000000000django-js-reverse-0.7.3/django_js_reverse.egg-info/PKG-INFO0000700000076500000240000002141413026013257023721 0ustar boernistaff00000000000000Metadata-Version: 1.1 Name: django-js-reverse Version: 0.7.3 Summary: Javascript url handling for Django that doesn't hurt. Home-page: https://github.com/ierror/django-js-reverse Author: Bernhard Janetzki Author-email: boerni@gmail.com License: MIT Download-URL: http://pypi.python.org/pypi/django-js-reverse/ Description: ================= Django JS Reverse ================= .. image:: https://img.shields.io/pypi/v/django-js-reverse.svg :target: https://pypi.python.org/pypi/django-js-reverse/ .. image:: https://img.shields.io/travis/ierror/django-js-reverse/master.svg :target: https://travis-ci.org/ierror/django-js-reverse .. image:: https://img.shields.io/coveralls/ierror/django-js-reverse/master.svg :alt: Coverage Status :target: https://coveralls.io/r/ierror/django-js-reverse?branch=master .. image:: https://img.shields.io/github/license/ierror/django-js-reverse.svg :target: https://raw.githubusercontent.com/ierror/django-js-reverse/develop/LICENSE .. image:: https://img.shields.io/pypi/wheel/django-js-reverse.svg **Javascript url handling for Django that doesn’t hurt.** Overview -------- Django JS Reverse is a small django app that makes url handling of `named urls `_ in javascript easy and non-annoying.. For example you can retrieve a named url: urls.py: :: url(r'^/betterliving/(?P[-\w]+)/(?P\d+)/$', 'get_house', name='betterliving_get_house'), in javascript like: :: Urls.betterliving_get_house('house', 12) Result: :: /betterliving/house/12/ Changelog _________ 0.7.3 New: Support for Django 1.10 Chg: Renamed "production" branch to "master" Fix: `#48 `_ - "Change False to 'window' in global object name in README." Thank you `karamanolev `_ Fix: `PR #45 `_ - "Fix: collectstatic_js_reverse usage message" Thank you `ghedsouza `_ Fix: `PR #44 `_ - "Remove duplicate _get_url call" Thank you `razh `_ 0.7.2 Fix: `#42 `_ - "Templatetag js_reverse_inline breaks on Django 1.9" Thank you `tommikaikkonen `_ Optimized imports 0.7.1 Fix: `#41 `_ - make it possible to use number 0 as url argument `Full changelog `_ Requirements ------------ - Python (2.6, 2.7, 3.1, 3.3, 3.4, 3.5) - Django (1.5, 1.6, 1.7, 1.8, 1.9, 1.10) Installation ------------ Install using ``pip`` … :: pip install django-js-reverse … or clone the project from github. :: git clone https://github.com/ierror/django-js-reverse.git Add ``'django_js_reverse'`` to your ``INSTALLED_APPS`` setting. :: INSTALLED_APPS = ( ... 'django_js_reverse', ) Usage as static file -------------------- First generate static file by :: ./manage.py collectstatic_js_reverse If you change some urls or add an app and want to update the reverse.js file, run the command again. After this add the file to your template :: Usage with views ---------------- Include none-cached view … :: urlpatterns = patterns('', url(r'^jsreverse/$', 'django_js_reverse.views.urls_js', name='js_reverse'), ) … or a cached one that delivers the urls javascript :: from django_js_reverse.views import urls_js urlpatterns = patterns('', url(r'^jsreverse/$', cache_page(3600)(urls_js), name='js_reverse'), ) Include javascript in your template :: or, if you are using Django > 1.5 :: Usage as template tag _____________________ {% load js_reverse %} Use the urls in javascript -------------------------- If your url names are valid javascript identifiers ([$A-Z\_][-Z\_$]\*)i you can access them by the Dot notation: :: Urls.betterliving_get_house('house', 12) If the named url contains invalid identifiers use the Square bracket notation instead: :: Urls['betterliving-get-house']('house', 12) Urls['namespace:betterliving-get-house']('house', 12) Options ------- Optionally, you can overwrite the default javascript variable ‘Urls’ used to access the named urls by django setting :: JS_REVERSE_JS_VAR_NAME = 'Urls' Optionally, you can change the name of the global object the javascript variable used to access the named urls is attached to. Default is :code:`this` :: JS_REVERSE_JS_GLOBAL_OBJECT_NAME = 'window' Optionally, you can disable the minfication of the generated javascript file by django setting :: JS_REVERSE_JS_MINIFY = False By default all namespaces are included :: JS_REVERSE_EXCLUDE_NAMESPACES = [] To exclude any namespaces from the generated javascript file, add them to the `JS_REVERSE_EXCLUDE_NAMESPACES` setting :: JS_REVERSE_EXCLUDE_NAMESPACES = ['admin', 'djdt', ...] If you want to include only specific namespaces add them to the `JS_REVERSE_INCLUDE_ONLY_NAMESPACES` setting tips: * Use "" for urls without namespace * Use "foo\0" to include urls just from "foo" namaspace and not from any subnamespaces (e.g. "foo:bar") :: JS_REVERSE_INCLUDE_ONLY_NAMESPACES = ['poll', 'calendar', ...] If you run your application under a subpath, the collectstatic_js_reverse needs to take care of this. Define the prefix in your django settings: :: JS_REVERSE_SCRIPT_PREFIX = '/myprefix/' By default collectstatic_js_reverse writes its output (reverse.js) to your project's STATIC_ROOT. You can change the output path: :: JS_REVERSE_OUTPUT_PATH = 'some_path' Running the test suite ---------------------- :: make test License ------- `MIT `_ Contact ------- `@i_error `_ -------------- Enjoy! Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Framework :: Django Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License django-js-reverse-0.7.3/django_js_reverse.egg-info/SOURCES.txt0000700000076500000240000000144613026013277024515 0ustar boernistaff00000000000000README.rst setup.cfg setup.py django_js_reverse/__init__.py django_js_reverse/core.py django_js_reverse/js_reverse_settings.py django_js_reverse/models.py django_js_reverse/rjsmin.py django_js_reverse/views.py django_js_reverse.egg-info/PKG-INFO django_js_reverse.egg-info/SOURCES.txt django_js_reverse.egg-info/dependency_links.txt django_js_reverse.egg-info/requires.txt django_js_reverse.egg-info/top_level.txt django_js_reverse/management/__init__.py django_js_reverse/management/commands/__init__.py django_js_reverse/management/commands/collectstatic_js_reverse.py django_js_reverse/templates/django_js_reverse/urls_js.tpl django_js_reverse/templatetags/__init__.py django_js_reverse/templatetags/js_reverse.py tests/__init__.py tests/settings.py tests/test_urls.py tests/unit_tests.py tests/utils.pydjango-js-reverse-0.7.3/django_js_reverse.egg-info/dependency_links.txt0000700000076500000240000000000113026013257026670 0ustar boernistaff00000000000000 django-js-reverse-0.7.3/django_js_reverse.egg-info/requires.txt0000700000076500000240000000001413026013257025215 0ustar boernistaff00000000000000Django>=1.5 django-js-reverse-0.7.3/django_js_reverse.egg-info/top_level.txt0000700000076500000240000000003013026013257025345 0ustar boernistaff00000000000000django_js_reverse tests django-js-reverse-0.7.3/setup.cfg0000644000076500000240000000015713026013277017274 0ustar boernistaff00000000000000[bdist_wheel] universal = 1 [wheel] universal = 1 [egg_info] tag_svn_revision = 0 tag_build = tag_date = 0 django-js-reverse-0.7.3/setup.py0000644000076500000240000000244013000124020017137 0ustar boernistaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import codecs import os from distutils.core import setup from setuptools import find_packages def read(*parts): filename = os.path.join(os.path.dirname(__file__), *parts) with codecs.open(filename, encoding='utf-8') as fp: return fp.read() version_tuple = __import__('django_js_reverse').VERSION version = '.'.join([str(v) for v in version_tuple]) setup( name='django-js-reverse', version=version, classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: Implementation :: PyPy', 'Framework :: Django', 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: MIT License', ], license='MIT', description='Javascript url handling for Django that doesn\'t hurt.', long_description=read('README.rst'), author='Bernhard Janetzki', author_email='boerni@gmail.com', url='https://github.com/ierror/django-js-reverse', download_url='http://pypi.python.org/pypi/django-js-reverse/', packages=find_packages(), package_data={ 'django_js_reverse': [ 'templates/django_js_reverse/*', ] }, install_requires=[ 'Django>=1.5', ] ) django-js-reverse-0.7.3/tests/0000755000076500000240000000000013026013277016612 5ustar boernistaff00000000000000django-js-reverse-0.7.3/tests/__init__.py0000644000076500000240000000033113000124020020675 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- from tests.unit_tests import JSReverseViewTestCaseMinified, JSReverseViewTestCaseNotMinified, \ JSReverseViewTestCaseGlobalObjectName, JSReverseStaticFileSaveTest, JSReverseTemplateTagTest django-js-reverse-0.7.3/tests/settings.py0000644000076500000240000000074213000124020021004 0ustar boernistaff00000000000000import os DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', }, } SECRET_KEY = 'wtf' ROOT_URLCONF = None USE_TZ = True INSTALLED_APPS = ( 'django_js_reverse', ) ALLOWED_HOSTS = ['testserver'] MIDDLEWARE_CLASSES = () TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, }, ] STATIC_ROOT = os.path.join(os.path.dirname(__file__), 'tmp', 'static_root') django-js-reverse-0.7.3/tests/test_urls.py0000644000076500000240000000506613026011116021205 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- import sys from copy import copy from django.conf.urls import include, url from django_js_reverse.views import urls_js if sys.version < '3': import codecs def u(x): return codecs.unicode_escape_decode(x)[0] else: def u(x): return x def dummy_view(*args, **kwargs): pass basic_patterns = [ url(r'^jsreverse/$', urls_js, name='js_reverse'), # test urls url(r'^test_no_url_args/$', dummy_view, name='test_no_url_args'), url(r'^test_one_url_args/(?P[-\w]+)/$', dummy_view, name='test_one_url_args'), url(r'^test_two_url_args/(?P[-\w]+)-(?P[-\w]+)/$', dummy_view, name='test_two_url_args'), url(r'^test_optional_url_arg/(?:1_(?P[-\w]+)-)?2_(?P[-\w]+)/$', dummy_view, name='test_optional_url_arg'), url(r'^test_unicode_url_name/$', dummy_view, name=u('test_unicode_url_name')), url(r'^test_duplicate_name/(?P[-\w]+)/$', dummy_view, name='test_duplicate_name'), url(r'^test_duplicate_name/(?P[-\w]+)-(?P[-\w]+)/$', dummy_view, name='test_duplicate_name'), url(r'^test_duplicate_argcount/(?P[-\w]+)?-(?P[-\w]+)?/$', dummy_view, name='test_duplicate_argcount'), ] urlpatterns = copy(basic_patterns) # test exclude namespaces urls urlexclude = [ url(r'^test_exclude_namespace/$', dummy_view, name='test_exclude_namespace_url1') ] # test namespace pattern_ns_1 = [ url(r'', include(basic_patterns)) ] pattern_ns_2 = [ url(r'', include(basic_patterns)) ] pattern_ns_arg = [ url(r'', include(basic_patterns)) ] pattern_nested_ns = [ url(r'^ns1/', include(pattern_ns_1, namespace='ns1')) ] pattern_dubble_nested2_ns = [ url(r'^ns1/', include(pattern_ns_1, namespace='ns1'))] pattern_dubble_nested_ns = [ url(r'^ns1/', include(pattern_ns_1, namespace='ns1')), url(r'^nsdn2/', include(pattern_dubble_nested2_ns, namespace='nsdn2'))] pattern_only_nested_ns = [ url(r'^ns1/', include(pattern_ns_1)), url(r'^nsdn0/', include(pattern_dubble_nested2_ns, namespace='nsdn0'))] urlpatterns += [ url(r'^ns1/', include(pattern_ns_1, namespace='ns1')), url(r'^ns2/', include(pattern_ns_2, namespace='ns2')), url(r'^ns_ex/', include(urlexclude, namespace='exclude_namespace')), url(r'^ns(?P[^/]*)/', include(pattern_ns_arg, namespace='ns_arg')), url(r'^nestedns/', include(pattern_nested_ns, namespace='nestedns')), url(r'^nsdn/', include(pattern_dubble_nested_ns, namespace='nsdn')), url(r'^nsno/', include(pattern_only_nested_ns, namespace='nsno')) ] django-js-reverse-0.7.3/tests/unit_tests.py0000755000076500000240000003135513026012327021372 0ustar boernistaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import unicode_literals import os import sys sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..') + os.sep) os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' import unittest import warnings import django from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.management import call_command from django.template import Context, RequestContext, Template from django.test import TestCase from django.test.client import Client from django.test.utils import override_settings from django.utils.encoding import smart_str from selenium.webdriver.phantomjs.webdriver import WebDriver from utils import script_prefix # Raise errors on DeprecationWarnings #warnings.simplefilter('error', DeprecationWarning) class AbstractJSReverseTestCase(object): client = Client() selenium = WebDriver() @classmethod def setUpClass(cls): if hasattr(django, 'setup'): # for django >= 1.7 django.setup() cls.selenium = WebDriver() super(AbstractJSReverseTestCase, cls).setUpClass() @classmethod def tearDownClass(cls): cls.selenium.quit() super(AbstractJSReverseTestCase, cls).tearDownClass() def assertEqualJSUrlEval(self, url_call, expected_url): response = self.client.post('/jsreverse/') self.assertEqual(self.selenium.execute_script('%s return %s;' % (smart_str(response.content), url_call)), expected_url) @override_settings(ROOT_URLCONF='tests.test_urls') class JSReverseViewTestCaseMinified(AbstractJSReverseTestCase, TestCase): def test_view_no_url_args(self): self.assertEqualJSUrlEval('Urls.test_no_url_args()', '/test_no_url_args/') def test_view_one_url_arg(self): self.assertEqualJSUrlEval('Urls.test_one_url_args("arg_one")', '/test_one_url_args/arg_one/') def test_view_two_url_args(self): self.assertEqualJSUrlEval('Urls.test_two_url_args("arg_one", "arg_two")', '/test_two_url_args/arg_one-arg_two/') def test_view_optional_url_arg(self): self.assertEqualJSUrlEval('Urls.test_optional_url_arg("arg_two")', '/test_optional_url_arg/2_arg_two/') self.assertEqualJSUrlEval('Urls.test_optional_url_arg("arg_one", "arg_two")', '/test_optional_url_arg/1_arg_one-2_arg_two/') def test_unicode_url_name(self): self.assertEqualJSUrlEval('Urls.test_unicode_url_name()', '/test_unicode_url_name/') @override_settings(JS_REVERSE_JS_VAR_NAME='Foo') def test_js_var_name_changed_valid(self): self.assertEqualJSUrlEval('Foo.test_no_url_args()', '/test_no_url_args/') @override_settings(JS_REVERSE_JS_VAR_NAME='1test') def test_js_var_name_changed_to_invalid(self): with self.assertRaises(ImproperlyConfigured): self.client.post('/jsreverse/') def test_namespaces(self): self.assertEqualJSUrlEval('Urls["ns1:test_two_url_args"]("arg_one", "arg_two")', '/ns1/test_two_url_args/arg_one-arg_two/') self.assertEqualJSUrlEval('Urls["ns2:test_two_url_args"]("arg_one", "arg_two")', '/ns2/test_two_url_args/arg_one-arg_two/') def test_namespaces_with_args(self): self.assertEqualJSUrlEval('Urls["ns_arg:test_two_url_args"]("arg_one", "arg_two", "arg_three")', '/nsarg_one/test_two_url_args/arg_two-arg_three/') def test_namespaces_nested(self): self.assertEqualJSUrlEval('Urls["nestedns:ns1:test_two_url_args"]("arg_one", "arg_two")', '/nestedns/ns1/test_two_url_args/arg_one-arg_two/') def test_content_type(self): response = self.client.post('/jsreverse/') self.assertEqual(response['Content-Type'], 'application/javascript') @override_settings(JS_REVERSE_JS_MINIFY='invalid') def test_js_minfiy_changed_to_invalid(self): with self.assertRaises(ImproperlyConfigured): self.client.post('/jsreverse/') def test_namespace_in_urls(self): response = self.client.get('/jsreverse/') self.assertContains(response, 'exclude_namespace', status_code=200) @override_settings(JS_REVERSE_EXCLUDE_NAMESPACES=['exclude_namespace']) def test_namespace_not_in_response(self): response = self.client.get('/jsreverse/') self.assertNotContains(response, 'exclude_namespace', status_code=200) @override_settings(JS_REVERSE_INCLUDE_ONLY_NAMESPACES=['ns1']) def test_only_namespace_in_response(self): response = self.client.get('/jsreverse/') self.assertContains(response, 'ns1', status_code=200) self.assertNotContains(response, 'ns2', status_code=200) self.assertNotContains(response, 'ns_arg', status_code=200) self.assertNotContains(response, 'nesteadns', status_code=200) self.assertNotContains(response, 'exclude_namespace', status_code=200) self.assertNotContains(response, 'nsdn', status_code=200) self.assertNotContains(response, 'nsno', status_code=200) @override_settings(JS_REVERSE_INCLUDE_ONLY_NAMESPACES=['nsdn:nsdn']) def test_only_namespace_nestead_in_response(self): response = self.client.get('/jsreverse/') self.assertContains(response, 'nsdn:nsdn2:ns1', status_code=200) self.assertNotContains(response, 'nsdn:ns1', status_code=200) @override_settings(JS_REVERSE_INCLUDE_ONLY_NAMESPACES=['']) def test_only_empty_namespaces(self): response = self.client.get('/jsreverse/') self.assertEqualJSUrlEval('Urls["test_two_url_args"]("arg_one", "arg_two")', '/test_two_url_args/arg_one-arg_two/') self.assertNotContains(response, 'ns1', status_code=200) self.assertNotContains(response, 'ns2', status_code=200) @override_settings(JS_REVERSE_INCLUDE_ONLY_NAMESPACES=['nsno\0']) def test_only_namespaces_without_subnss(self): response = self.client.get('/jsreverse/') self.assertEqualJSUrlEval('Urls["nsno:test_two_url_args"]("arg_one", "arg_two")', '/nsno/ns1/test_two_url_args/arg_one-arg_two/') self.assertNotContains(response, 'nsno:nsdn0', status_code=200) def test_script_prefix(self): with script_prefix('/foobarlala/'): self.assertEqualJSUrlEval('Urls["nestedns:ns1:test_two_url_args"]("arg_one", "arg_two")', '/foobarlala/nestedns/ns1/test_two_url_args/arg_one-arg_two/') def test_duplicate_name(self): self.assertEqualJSUrlEval('Urls.test_duplicate_name("arg_one")', '/test_duplicate_name/arg_one/') self.assertEqualJSUrlEval('Urls.test_duplicate_name("arg_one", "arg_two")', '/test_duplicate_name/arg_one-arg_two/') def test_duplicate_argcount(self): self.assertEqualJSUrlEval('Urls.test_duplicate_argcount({arg_one: "arg_one"})', '/test_duplicate_argcount/arg_one-/') self.assertEqualJSUrlEval('Urls.test_duplicate_argcount({arg_two: "arg_two"})', '/test_duplicate_argcount/-arg_two/') self.assertEqualJSUrlEval('Urls.test_duplicate_argcount({arg_one: "arg_one", arg_two: "arg_two"})', '/test_duplicate_argcount/arg_one-arg_two/') @override_settings(JS_REVERSE_INCLUDE_ONLY_NAMESPACES=['nsno\0']) @override_settings(JS_REVERSE_EXCLUDE_NAMESPACES=['exclude_namespace']) def test_include_exclude_configuration(self): with self.assertRaises(ImproperlyConfigured): self.client.post('/jsreverse/') def test_int_args(self): self.assertEqualJSUrlEval('Urls.test_two_url_args(0, 5)', '/test_two_url_args/0-5/') self.assertEqualJSUrlEval('Urls.test_two_url_args("", 5)', '/test_two_url_args/-5/') def test_float_args(self): self.assertEqualJSUrlEval('Urls.test_two_url_args(0, 5.5)', '/test_two_url_args/0-5.5/') self.assertEqualJSUrlEval('Urls.test_two_url_args(0.00001, 5.5)', '/test_two_url_args/0.00001-5.5/') @override_settings(JS_REVERSE_JS_MINIFY=False) @override_settings(ROOT_URLCONF='tests.test_urls') class JSReverseViewTestCaseNotMinified(JSReverseViewTestCaseMinified): def test_minification(self): js_not_minified = smart_str(self.client.post('/jsreverse/').content) with override_settings(JS_REVERSE_JS_MINIFY=True): js_minified = smart_str(self.client.post('/jsreverse/').content) self.assertTrue(len(js_minified) < len(js_not_minified)) @override_settings(ROOT_URLCONF='tests.test_urls') class JSReverseViewTestCaseGlobalObjectName(JSReverseViewTestCaseMinified): def test_global_object_name_default(self): js_content = smart_str(self.client.post('/jsreverse/').content) self.assertTrue(js_content.startswith('this.')) @override_settings(JS_REVERSE_JS_GLOBAL_OBJECT_NAME='window') def test_global_object_name_change(self): js_content = smart_str(self.client.post('/jsreverse/').content) self.assertTrue(js_content.startswith('window.')) @override_settings(JS_REVERSE_JS_GLOBAL_OBJECT_NAME='1test') def test_global_object_name_change_invalid_identifier(self): with self.assertRaises(ImproperlyConfigured): self.client.post('/jsreverse/') @override_settings(ROOT_URLCONF='tests.test_urls') class JSReverseStaticFileSaveTest(AbstractJSReverseTestCase, TestCase): def test_reverse_js_file_save(self): call_command('collectstatic_js_reverse') path = os.path.join(settings.STATIC_ROOT, 'django_js_reverse', 'js', 'reverse.js') f = open(path) content1 = f.read() if hasattr(content1, 'decode'): content1 = content1.decode() r2 = self.client.get('/jsreverse/') content2 = r2.content if hasattr(content2, 'decode'): content2 = content2.decode() self.assertEqual(len(content1), len(content2), 'Static file don\'t match http response content_1') self.assertEqual(content1, content2, 'Static file don\'t match http response content_2') # test for excpetion if STATIC_ROOT is not set with override_settings(STATIC_ROOT=None): with self.assertRaises(ImproperlyConfigured): call_command('collectstatic_js_reverse') def test_reverse_js_file_save_with_output_path_option(self): js_output_path = os.path.join(os.path.dirname(__file__), 'tmp', 'some_path') with override_settings(JS_REVERSE_OUTPUT_PATH=js_output_path): call_command('collectstatic_js_reverse') f = open(os.path.join(js_output_path, 'reverse.js')) content1 = f.read() if hasattr(content1, 'decode'): content1 = content1.decode() r2 = self.client.get('/jsreverse/') content2 = r2.content if hasattr(content2, 'decode'): content2 = content2.decode() self.assertEqual(len(content1), len(content2), 'Static file don\'t match http response content_1') self.assertEqual(content1, content2, 'Static file don\'t match http response content_2') # should not raise ImproperlyConfigured exception if STATIC_ROOT is not set with override_settings(STATIC_ROOT=None): try: call_command('collectstatic_js_reverse') except ImproperlyConfigured: self.fail( 'should not raise ImproperlyConfigured exception if STATIC_ROOT is not set and JS_REVERSE_OUTPUT_PATH is set') def test_script_prefix(self): script_prefix = '/test/foo/bar/' with override_settings(JS_REVERSE_SCRIPT_PREFIX=script_prefix): self.assertEqualJSUrlEval('Urls.test_no_url_args()', '{0}test_no_url_args/'.format(script_prefix)) @override_settings(ROOT_URLCONF='tests.test_urls') class JSReverseTemplateTagTest(AbstractJSReverseTestCase, TestCase): def test_tpl_tag_with_request_in_context(self): context_instance = RequestContext(self.client.request) tpl = Template('{% load js_reverse %}{% js_reverse_inline %}') js_from_tag = tpl.render(context_instance) js_from_view = smart_str(self.client.post('/jsreverse/').content) self.assertEqual(js_from_tag, js_from_view) def test_tpl_tag_without_request_in_context(self): context_instance = Context() tpl = Template('{% load js_reverse %}{% js_reverse_inline %}') js_from_tag = tpl.render(context_instance) js_from_view = smart_str(self.client.post('/jsreverse/').content) self.assertEqual(js_from_tag, js_from_view) if __name__ == '__main__': unittest.main() django-js-reverse-0.7.3/tests/utils.py0000644000076500000240000000061313000124020020301 0ustar boernistaff00000000000000# -*- coding: utf-8 -*- from django.core.urlresolvers import get_script_prefix, set_script_prefix class script_prefix(object): def __init__(self, newpath): self.newpath = newpath self.oldprefix = get_script_prefix() def __enter__(self): set_script_prefix(self.newpath) def __exit__(self, type, value, traceback): set_script_prefix(self.oldprefix)