django-recurrence-1.2.0/0000755000076500000240000000000012554754171015351 5ustar cezarstaff00000000000000django-recurrence-1.2.0/django_recurrence.egg-info/0000755000076500000240000000000012554754171022522 5ustar cezarstaff00000000000000django-recurrence-1.2.0/django_recurrence.egg-info/dependency_links.txt0000644000076500000240000000000112554754171026570 0ustar cezarstaff00000000000000 django-recurrence-1.2.0/django_recurrence.egg-info/not-zip-safe0000644000076500000240000000000112356415301024735 0ustar cezarstaff00000000000000 django-recurrence-1.2.0/django_recurrence.egg-info/PKG-INFO0000644000076500000240000000162012554754171023616 0ustar cezarstaff00000000000000Metadata-Version: 1.1 Name: django-recurrence Version: 1.2.0 Summary: Django utility wrapping dateutil.rrule Home-page: UNKNOWN Author: Tamas Kemenczy Author-email: tamas.kemenczy@gmail.com License: BSD Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Requires: Django Requires: pytz Requires: python_dateutil django-recurrence-1.2.0/django_recurrence.egg-info/requires.txt0000644000076500000240000000002512554754171025117 0ustar cezarstaff00000000000000pytz python-dateutil django-recurrence-1.2.0/django_recurrence.egg-info/SOURCES.txt0000644000076500000240000000310212554754171024402 0ustar cezarstaff00000000000000LICENSE MANIFEST.in setup.py django_recurrence.egg-info/PKG-INFO django_recurrence.egg-info/SOURCES.txt django_recurrence.egg-info/dependency_links.txt django_recurrence.egg-info/not-zip-safe django_recurrence.egg-info/requires.txt django_recurrence.egg-info/top_level.txt docs/Makefile docs/changelog.rst docs/conf.py docs/contributing.rst docs/index.rst docs/installation.rst docs/make.bat docs/usage/admin.png docs/usage/api.rst docs/usage/getting_started.rst docs/usage/index.rst docs/usage/recurrence_field.rst recurrence/__init__.py recurrence/base.py recurrence/choices.py recurrence/exceptions.py recurrence/fields.py recurrence/forms.py recurrence/managers.py recurrence/models.py recurrence/locale/en/LC_MESSAGES/django.mo recurrence/locale/en/LC_MESSAGES/django.po recurrence/locale/en/LC_MESSAGES/djangojs.mo recurrence/locale/en/LC_MESSAGES/djangojs.po recurrence/locale/es/LC_MESSAGES/django.mo recurrence/locale/es/LC_MESSAGES/django.po recurrence/locale/es/LC_MESSAGES/djangojs.mo recurrence/locale/es/LC_MESSAGES/djangojs.po recurrence/locale/fr/LC_MESSAGES/django.mo recurrence/locale/fr/LC_MESSAGES/django.po recurrence/locale/fr/LC_MESSAGES/djangojs.mo recurrence/locale/fr/LC_MESSAGES/djangojs.po recurrence/locale/nl/LC_MESSAGES/django.mo recurrence/locale/nl/LC_MESSAGES/django.po recurrence/locale/nl/LC_MESSAGES/djangojs.mo recurrence/locale/nl/LC_MESSAGES/djangojs.po recurrence/static/recurrence/css/recurrence.css recurrence/static/recurrence/img/recurrence-calendar-icon.png recurrence/static/recurrence/js/recurrence-widget.js recurrence/static/recurrence/js/recurrence.jsdjango-recurrence-1.2.0/django_recurrence.egg-info/top_level.txt0000644000076500000240000000001312554754171025246 0ustar cezarstaff00000000000000recurrence django-recurrence-1.2.0/docs/0000755000076500000240000000000012554754171016301 5ustar cezarstaff00000000000000django-recurrence-1.2.0/docs/changelog.rst0000644000076500000240000000346412554752650020771 0ustar cezarstaff00000000000000Changelog ========= 1.2.0 (as yet unreleased) ------------------------- * Added an option for events to occur on the fourth of a given weekday of the month (#29); * Fixed an off-by-one bug in the ``to_text`` method for events happening on a regular month each year (#30); * Fixed a bug in the JavaScript widget where the date for monthly events on a fixed date of the month had the description rendered incorrectly if the day selected was more than the number of days in the current calendar month (#31); * Added a French translation (#32) - this may be backwards incompatible if have overriden the widget JavaScript such that there is no ``language_code`` member of your recurrence object; * Added a Spanish translation (#49); * Added database migrations - running ``python manage.py migrate recurrence --fake`` should be sufficient for this version - nothing has changed about the database schema between 1.1.0 and 1.2.0; * Fix broken tests for Django 1.4. 1.1.0 ----- * Added experimental Python 3 support. * Added extensive test coverage (from 0% to 81%). * Added documentation (including this changelog). * Removed ``RecurrenceModelField`` and ``RecurrenceModelDescriptor``, which don't appear to have worked as expected for some time. * Fixed a bug introduced in 1.0.3 which prevented the django-recurrence JavaScript from working (#27). * Don't raise ``ValueError`` if you save ``None`` into a ``RecurrenceField`` with ``null=False`` (#22), for consistency with other field types. * Make sure an empty recurrence object is falsey (#25). * Fix a copy-paste error in ``to_recurrence_object`` which prevented exclusion rules from being populated correctly. * Fix a typo in ``create_from_recurrence_object`` which prevented it working with inclusion or exclusion rules. * Various other very minor bugfixes. django-recurrence-1.2.0/docs/conf.py0000644000076500000240000001755312554752650017613 0ustar cezarstaff00000000000000# -*- coding: utf-8 -*- # flake8: noqa # # 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 # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: try: import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except ImportError: pass # 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-recurrence' copyright = u'2014, django-recurrence developers' # 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. # # The short X.Y version. version = '1.2.0' # The full version, including alpha/beta/rc tags. release = '1.2.0' # 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 --------------------------------------------------- # 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 # " v 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 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-recurrencedoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'django-recurrence.tex', u'django-recurrence Documentation', u'django-recurrence developers', '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 # 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-recurrence', u'django-recurrence Documentation', [u'django-recurrence developers'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'django-recurrence', u'django-recurrence Documentation', u'django-recurrence developers', 'django-recurrence', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' django-recurrence-1.2.0/docs/contributing.rst0000644000076500000240000000336112425713022021530 0ustar cezarstaff00000000000000Contributing ============ Contributions to django-recurrence are very welcome - whether in the form of bug reports, feature requests, or patches. Bug reports and feature requests are tracked on our `GitHub issues page `_. If you want to make changes to django-recurrence, you'll need to fork our GitHub repository, make any changes you want, and send us a pull request. Feel free to `file an issue `_ if you want help getting set up. Running the tests ----------------- The easiest way to run the tests is to run:: make testall from the root of your local copy of the django-recurrence repository. This will require that you have tox installed. If you don't have tox installed, you can install it with ``pip install tox``. Running all the tests also requires that you have Python 2.6, Python 2.7, Python 3.3 and Python 3.4 installed locally. This will run tests against all supported Python and Django versions, check the documentation can be built, and will also run ``flake8``, an automated code-linting tool. If that sounds like too much work, feel free to just run tests on whatever your local version of Python is. You can do this by running:: pip install -r requirements_test.txt ! You only need to run this once make test If you want to see what our code coverage is like, install everything in ``requirements_test.txt`` (as shown above), then run:: make coverage Working with the documentation ------------------------------ Our documentation is written with Sphinx, and can be built using:: tox -e docs Once this command is run, it'll print out the folder the generated HTML documentation is available in. django-recurrence-1.2.0/docs/index.rst0000644000076500000240000000070312425713022020125 0ustar cezarstaff00000000000000django-recurrence ***************** django-recurrence is a utility for working with recurring dates in Django. It provides: - Recurrence/Rule objects using a subset of rfc2445 (wraps ``dateutil.rrule``) for specifying recurring date/times; - ``RecurrenceField`` for storing recurring datetimes in the database; - a JavaScript widget. Contents -------- .. toctree:: :maxdepth: 2 installation usage/index contributing changelog django-recurrence-1.2.0/docs/installation.rst0000644000076500000240000000406012554752650021534 0ustar cezarstaff00000000000000.. _install: Installation ============ .. contents:: :local: Download the library -------------------- Firstly, you'll need to install ``django-recurrence`` from PyPI. The easiest way to do this is with pip:: pip install django-recurrence Then, make sure ``recurrence`` is in your ``INSTALLED_APPS`` setting: .. code-block:: python INSTALLED_APPS = ( ... 'recurrence', ) Supported Django and Python versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Currently, django-recurrence supports Python 2.6, Python 2.7, Python 3.3 and Python 3.4. Python 3 support is experimental (we run our tests against Python 3, but have not yet tried it in production). django-recurrence works with Django from versions 1.4 to 1.7 (though note that Django 1.4 does not support Python 3, Django 1.7 does not support Python 2.6, and Python 3.4 is only supported with Django 1.7). Set up internationalization --------------------------- .. note:: If you just want to use the ``en`` translation, you can skip this step. If you want to use a translation of django-recurrence other than ``en``, you'll need to ensure django-recurrence's JavaScript can access the translation strings. This is handled with Django's built in ``javascript_catalog`` view, which you install by adding the following to your ``urls.py`` file: .. code-block:: python # If you already have a js_info_dict dictionary, just add # 'recurrence' to the existing 'packages' tuple. js_info_dict = { 'packages': ('recurrence', ), } # jsi18n can be anything you like here urlpatterns = patterns( '', (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), ) Configure static files ---------------------- django-recurrence includes some static files (all to do with rendering the JavaScript widget that makes handling recurring dates easier). To ensure these are served correctly, you'll probably want to ensure you also have ``django.contrib.staticfiles`` in your ``INSTALLED_APPS`` setting, and run:: python manage.py collectstatic django-recurrence-1.2.0/docs/make.bat0000644000076500000240000001177612425713022017705 0ustar cezarstaff00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ 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. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over 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 goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-recurrence.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-recurrence.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end django-recurrence-1.2.0/docs/Makefile0000644000076500000240000001275012425713022017731 0ustar cezarstaff00000000000000# 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) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where 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 " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @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-recurrence.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-recurrence.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-recurrence" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-recurrence" @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." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 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." django-recurrence-1.2.0/docs/usage/0000755000076500000240000000000012554754171017405 5ustar cezarstaff00000000000000django-recurrence-1.2.0/docs/usage/admin.png0000644000076500000240000003740212425713022021174 0ustar cezarstaff00000000000000PNG  IHDRy^q zTXtRaw profile type exifxڝk9s!0boǟ%x+RH̤?7+Cmc{UW]ys0LJ̟N?X}ߘ?i=/|˃~zw}hk9dkK*l>#Tǥt>3ys{EoυL>;?S W1?³aSaŸ*-ѵo3|_޽:gDݵF'LЈ;p^|R`^I3oWI=I|״CjvRy4r5drqξ֒ӎgbf4UVo+ ]׬+{YUKOXuo('nv$g'W $ 6Sisʱ:-a,Vz|#sՓ.#g,?O{1줐rc,<ً@Yvۮ#IF3qrKeSo7Ӧ J_4vw1G|5=;٪]]ZIulXdR71@˿I=& e+eji#}:m^~rPY,U[ښ^g5Pok[LK%KеZ"NeK.ENGML"xfq/Yl֓#spC왜pSJ+,ˢUϒr0tj^rQA}&DǩO6iF09pz]V{_#ݫnZ.Qe*V)<$,ڐi:(ƞ%vV?wf!q>nכ vYѩ 3sr"{0iYoJ+\Ѷ]A3;GK<㎸ոn- y-49JWlƮ[;L&"\_h,F@<½u^Yfz:=f;32efHKU Syu:C%J_Ym*=YunJHOmM_'"8:[٢Q,g:"rX&:+uLpk~`ըi֫:t Z,yc0 o-bx~Qmn['Ftnea: n6?jyυgbVICJů6ֲi- (X$#(⹒ss RA{PKJ\n Mlx`6\r4FS87{ ..$ AY>0w^D!:%ά+wkA˅*h#hře To;K0"Z5|=lxZa;Ky)Pj=MWV4vF6%kԄ*~)u B{Q3' ; (V\=X%o;h6owdKw:Y^+0>sBJi>&rqP.GꆾY3fvq!N[ #+4ʱ3\, YdՓ i"$OfwyLZ0+lhU jHRCօ~P1 {,T}_+X'57ܟ9ovU@LW6,$ROX\3Q &K3me G)=d~i^DPH:5qUҵgF&{ʑۗ! :e}}kRG@#[qo_oJ#3*+ SR^o ;z ,,schnX1AihsxQIWFpZBpLD ÃA$Ūu\3Y.zk_\ 7ej3|U4\ Ja| eGz FK0c=<@{gr`Ev\qrfEP6[%8 ЇoXp0jA-ٳF X\},`Zl6BGh1@8ځ(!ɜA=D|{|{{s"TUUBfLvB\?p:Y!`0B~@!Ft!DB0&!/aLB^!˜B1 y!c}]z. -OpBL1];?~O> US“[Kx'=򈌈 X3!bۢ"""H-w&ڍ1"8|W1m< ""E_CDDƚeBp0#- 9Z O[xd c2q3\kdEi磭tb_n3H5nlز@DL*y[6q'5"zWFW2%zd{e]e/Nfc=o<ϟdoK E"xh.o|-|sֳ,~|{F!$uC+͟+]u{պ?R{T3{T@՚~s*YUj]jiK5K֤zFU?kZ~uFU3=-6lJR|6j3{UЪc+0_-hl [$S<5bC~V#ck|޿W7c [HޜJ oقV#ubkkaZLlK6CH\C/kᣏQ2~r-ȲSz>'X.a^@pb!/o$}>#nzυ T+vnBsXqњ(o)梭yO`}^xb__ΊݮQʕm|8^֯`XwBLu#5﯏3P&uD=S5е2W} $Wd%s]RMU;Իk]|C5w<ۥB.QUUU۬&_R.!yt:?'Lc|݈t!y>>y}Sa6 x!Dx|$ȓ̅JL!˜B1 y!cB$"]tw͇~8YBq\zdC!q:CKwB1 y!cB$"I !DB0&!/aLB^!XHOrΝ;743godҥ̞=;BҐ?x so:&33siR?\EFf`&LYe)񖬛Z8 _0T[VѺlXYM93YvUIgɅ"{DDDꈿol*KW.OJd1͊.fBowq* IֺА]ݢGqSZ7:ADV[c\d-R@=R(lذVbI{:dSM>() /uT6KBd@i7^M ˶bk/66Wb3Xu#Z66h‹aX-S7rx[ۨE޼g 3QS28\QjLb;xcZIY2`[kĶSi;4 $+efmtz.^%fP:tH~_ǎS?+ ,_V5`P3ԬS)jγom}pc{TU=2[}o`խmSEu~aԭ9K=U1dƩ],+?Gxhp9m C,Ck`~f}]UUj=걁OQYGzL`ΰr^:rC썯M!ٜ,uAS3/|!wpQɧmQ RUz4[]甪>jԿ nAoS}gTw ^г9TߨH6^^!Wx'SV"=TŽ v\ h ;\( v+KB@D?0ʀ!ڎq. O$;֡݊kM,=}c-IeԻuڍK\r&&Knc-V1t^'ЎFAٸ)خГo Φx(wcV\2[} |q`+67PȞDt<Ƈ)*5`/N!l#BL;hѠϿ«++^e=&$b+ָDtQ:[ze%x6k4VoMAI:h!r5e>6NKJvVo*sū)+5RTAf"MY7>#Bv: 뺲^z{gL}>58B4rv>s6`I[C2\^T67^wS1}Wˆ01EkKs1(*,jBwc\rsMr (utLߍĂh"lĊRK7m,X;dg&k`xi,’k"d"b֭\XB fe-b &\Eո,T/lf\xZs*0@wwz}ia-eX.X,X"7Voc&K5nsڝzuPVDA7x]+XߜΊ"'bƭڊ؉(%(i bQ4q9u1o\+eƉ7 !FF#֡0cY݆ANS_CM8] ۑ S[]BFg1۸72)SOm}=VY\qY :JةdW}=TڲX奲=P~o,f=ॾQ?n;rk8E~hݟ%oB;Zgl6caM;Ç(|kjןc}h gvq֭[7aE_&P3M"Yz:4x,qaPJ  ;AQED:p)J݉۱ޟ__%y-ћwh)~UG(Quk.3 z݇5gTvl; Ǘ(}>[+x6{aʆY:?I|CxqS4+Aּ~z_5/uoT.ZOk4Fs9߮r{Gئ,T3ZϿsiF%~Ʃ @V|_$3sTDfk,nkY # UV8r]^RHC-IzJhy9.r|iV(u_ bl"TUU!Yˠg1fk" J,N)%sNs ]M9@6*?~[`wSTТUפ=9L/Eݪ6#}le/'] r99uuX\徹FgΜ^uru7%]ñ&z Y6n'[y>֒Ulh𲸨b\?vZҷAz'UutQk NS^{-wiNT:r&)ৗ [pMpɄJ ZI)8>`ךG+BLKS«Bk&!/aLk&Q?H'Oh !?Iխb-"TF!˜B1 y!cB$"I !DB0&!/aLB^!˜B1 y!cB$"I !DB0w:\BLI|D2t:!$-)wh+5B$"I !DB0&!/aLB^!˜B1 y!cBƦ~Ȼ+1/Y}`iø$"2I2Wшјɒ%KXb4b4-r(hfhO+N:.[_ZK,2a*s'Ni K2e]\\,X/9.&{ٌٜ)׊M#I:%s10qNŃrk 6*w].= Gh5--v-J6*'T-A~n3q-^Z\$Ž VZXq$kE iI.2JW%v4ԍ2[_`)&nlz89%7TXC]nb 5뤲=@]kb6c1[mԻS%`εR:5Z~%34bXZXiURMt(Ug9v.P\4tDaHo+E,V+B@LqQ]d&lp3} V^,b10YJh{YRBBa\,oAkBE@Y͘-̹f*](Ύ:q>  LF[u XME vx뭘(S8Kdz6vƕb0 lI~E5T]MiYV?u#W8\^sl>J;J;EZ;Cu 'bȈB7C,& ;mԥnijmlW6cNV3tNW<,hzxqk4(NVRfN bXL& 2C0^OI2++eų Hna PRj8;l6J0b.(^}T~Y2hZtVӱOu'Ihw)ǛpfaCK1IbL}@CSN'vY~( !)sBtvMƪO ʠ&D_Jz:q\bV5S=}.4)&aL# mqhHL(` T<@a(I>WKIm6NM8vPZMCB&vj_qיB;VgؖoF_TIYv٦Y 6!D;U^IZڈtkR.4$d8yR*Vo"Fg'lZ4dv'NIhQ(264sx)ڛ&c5)2$>y4ԫ^/NG\r&əZ4[[ULS]=Z\dPztͤXX^ڀ+;Ҕc0=kqd؁;MByn>}&S#Vgi<63S B{e !I$yjEM&Uݥ''IXlq9P~7%e8Z'm /teb\Ys0nx$cnG5}NƇױ\k鰻i(q DCxT`:޸gڴt3I~)z~UmLGKh^6)| &7i,((z3%P~ZHIIf@n1F]8U%yu 6,P٪QVm*zb]zF{XY]ZDvFO[YwM, YmVK-!~WF 3PQ܄GJ5bc[W@CBhiC[(q=HCKZR uN!&!/aLB^!˜B1 y!cB$"I !DB0&!/a,BUUtb0ʜNu]BLu ʮ !t!DB0&!/aLB^!˜B1 y!cB$"I !DB0&!/aLB^!˜B1 y!cB$"I !D 铡9x gΜܹs#N3sLnF.]ٳCY~UD}f;ZdSM>()yJ5zx^֭<n,cSi.?z6CBZJl&]l;NUXrlZ÷Vr6TPiNKS[FUCBmXO'odkq&ofgmڿ -Vv{MB'?ǒ:$䒨paPEvj :Pڱ=$~ެ]]p1u;fM-Ov_Q1pRjCF]&E&tZ76fRiacN5v%_Eѩ1SVNid*W&~]KUZGYHi3{(JB\Ґp} ?ytO>zNX&UBI"g<.~3U^yM: ;sE$iխݠբ;z Ŧ$tRbpx2F J%!MY} m7*69.5Q)UT`4~ 00Ʌ&JEK/ʊ-FPh-Y;* ZEqӡOI9-LXd#spz!1Vv[a݊k jrNҡK2䲳q𢯂n^?Qz} jd&Qʦ%6I+PE¼Ӣ(yr VGBӻ%?ڵzT㪴b-+\-5zk zYbf,.<(2IekM%chaG-^IiI2)$o(rDOFG>v՝hTU2['`jM$16ظ1uܼX2JvQ4ڤJv !. t:1 ue/sϘ=x = r" v̝;Ǐ_q:ELLLJ$-5w}7---`DGGs 7K__e]0<+|_?^W|Ax뭷PUKśo9<:ryEaΜ9kt 7/ߏk3:yAfϞMNNа4>qivMww f~{nj3gռK̛7o1SSSCdd$˖-W^v+]]]̘1oGÝ9CSAWT]]Mgg'[n%>>?jwwLMM 3gܹs,_;cҶyڅ|oo/;w^:::Ɲ>}$5k^w\!p뭷x̙3DFFry~_pQ^g?8f͢J Oos=7B\^'K_­zn? |_ ߾'SO=̙3_ɓ'y'h4>|g>lLMT kZD1ͼlذaiO~g͚5,_^{}aZﵵXVnTUEQQ .~E'O^6ɓDGG3{1W@DD;vN>͉'FT>|0'Od۶mCM!1>ɤ &{}d0W['O9~8=аÇ3s̋_ɝw9Aw7__TVVŋgϾ'z=g;K_xONN~y|͓Lގ;Ƽyh4444KQQ3g䷿-#wy.]>zhYYYWbΝ̜9^?4_jj*/xg[yyy˿ ΝCUU^.\ɓ''Bz<`R'g͚#Z8NjZ N7x<}Ioc}}NfZ#=EbʘvP !;iO!t1Iz$KLwR&B0&!/aLB^!˜B1 y!c]NV9B\HB18(5B$"I !DB0&!/aLB^!˜B1 $L&j"仰Gc /[S}]!DY߸g\OĦt=&ۇq2`>m1Z3P]ez7E9m"Rsܥ=qS~ו'nB]kf1|g~ێBp:RNچc|pu*B5-,ղ}艌gHG۾TuN]gy¾-wcj0n+ Xv]o60KO޲l[ń:C]ImlqL`fILL*(qq `mwG;/}w9[Db/Shzg!|6R颁}Ɓ_ml zvf,Z e|A$YŚX-o@Hw{Yk'ugcZe/fA yQc[U9mw'/ ﴿yIuc?ЧDs ]QgewaŎA]"{ed[n7H͢5lK-'יִT"3)YϱE,K>\'1ݶ(pғye԰;'ìxcXf3F|'vq!ͱ;0[q$+Ʈ.--\>zϬҿ@FOZZ ,H]ihil_^Oz=VGQs"D-cb8d%.j`ۉ@߻ Nt?'m? 968ZƖ]w$ꬕmEc\H96[m1 ̬`cGovY-1<c =mmi'C}Lۡ6KOqM7UÊgymhIeňisN~+4^϶P~+{Nv5+q :sH=z\ ͻ(fԞ-G׉}!=oK}|-崰>i |mc=0 .1{Cd̢@hmeDF黟BrUn2v/gY"7J̊)!2*́C?&ȸ+edQO=1zcb)RbԬZDO{E*!4=^]Լٌ~I9aC#אGNlcgONqq=$ո#ݾY-j'PƠOa߷~bPUUD dgZZBI7>> list(mypattern.occurrences()) [datetime.datetime(2014, 1, 2, 0, 0), datetime.datetime(2014, 1, 3, 0, 0)] Exclusion Rules ^^^^^^^^^^^^^^^ You can specify exclusion rules too, which are exactly the same as inclusion rules, but they represent rules which match dates which should *not* be included in the list of occurrences. Inclusion rules are provided to the ``Recurrence`` object using the kwarg ``rrules``, and exclusion rules are provided to the ``Recurrence`` object using the kwargs ``exrules``. Adding or Excluding Individual Dates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Similarly, you can specify individual dates to include or exclude using ``rdates`` and ``exdates``, both of which should be a list of ``datetime.datetime`` objects. .. code-block:: python from datetime import datetime import recurrence pattern = recurrence.Recurrence( rdates=[ datetime(2014, 1, 1, 0, 0, 0), datetime(2014, 1, 2, 0, 0, 0), ] ) django-recurrence-1.2.0/docs/usage/getting_started.rst0000644000076500000240000000157512554752650023336 0ustar cezarstaff00000000000000Getting started --------------- Once you've :ref:`installed django-recurrence `, you'll generally want to start by using it in one of your models, which can be done like this: .. code-block:: python :emphasize-lines: 5 from recurrence.fields import RecurrenceField class Course(models.Model): title = models.CharField(max_length=200) recurrences = RecurrenceField() If you use the ``Course`` model in Django's administrative interface, or in any forms, it should be rendered with a pretty form field, which makes selecting relatively complex recurrence patterns easy. .. figure:: admin.png :alt: The form field for recurrence fields Using this form it's possible to specify relatively complex recurrence rules - such as an event that happens every third Thursday of the month, unless that Thursday happens to be the 21st of the month, and so on. django-recurrence-1.2.0/docs/usage/index.rst0000644000076500000240000000013112554752650021241 0ustar cezarstaff00000000000000Usage ===== .. toctree:: :maxdepth: 2 getting_started recurrence_field api django-recurrence-1.2.0/docs/usage/recurrence_field.rst0000644000076500000240000001612012554752650023437 0ustar cezarstaff00000000000000Using ``RecurrenceField`` ------------------------- .. _between: Getting occurrences between two dates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Once you've created a model with a ``RecurrenceField``, you'll probably want to use it to figure out what dates are involved in a particular recurrence pattern. .. note:: Whether you want to use ``between`` or :ref:`occurrences ` will depend on what sort of rules you're dealing with. In reality, a model like `Course` probably has rules like "every Thursday for 10 weeks", so ``occurrences`` will work fine, since the rules have natural limits. For other uses (e.g. find me every date this year for a club that runs every Wednesday), you'll want to use ``between``. ``between`` takes two dates (the start and end date), and returns a list of ``datetime`` objects matching the recurrence pattern between those dates. It is used like this (using the ``Course`` model from above): .. code-block:: python from datetime import datetime from myapp.models import Course course = Course.objects.get(pk=1) course.recurrences.between( datetime(2010, 1, 1, 0, 0, 0), datetime(2014, 12, 31, 0, 0, 0) ) This won't include occurrences if they occur on the start and end dates specified. If you want to include those, pass in ``inc`` like this: .. code-block:: python course.recurrences.between( datetime(2010, 1, 1, 0, 0, 0), datetime(2014, 12, 31, 0, 0, 0), inc=True ) .. warning:: Slightly confusingly, ``between`` will only return you dates after the current date, if used as above (provided those dates fall between the two first parameters to ``between``). Read on for how to get all the occurrences between two dates. To get all the occurrences between two dates (including dates that are *before* the current time, but *after* the provided start date), you'll also need to set ``dtstart``, like this: .. code-block:: python course.recurrences.between( datetime(2010, 1, 1, 0, 0, 0), datetime(2014, 12, 31, 0, 0, 0), dtstart=datetime(2010, 1, 1, 0, 0, 0), inc=True ) That will get you all occurrences between 1st January 2010, and 31st December 2014, including any occurrences on 1st January 2010 and 31st December 2014, if your recurrence pattern matches those dates. The effective starting date for any recurrence pattern is essentially the later of the first argument and ``dtstart``. To minimize confusion, you probably want to set them both to the same value. .. _occurrences: Getting all occurrences ^^^^^^^^^^^^^^^^^^^^^^^ ``occurrences`` is particularly useful where your recurrence pattern is limited by the rules generating occurrences (e.g. "every Tuesday for 10 weeks", or "every Tuesday until 23rd April 2014"). You can get a generator which you can iterate over to get all occurrences using ``occurrences``: .. code-block:: python dates = course.recurrences.occurrences() You can optionally provide ``dtstart`` to specify the first occurrence, and ``dtend`` to specify the final occurrence. You can index into the returned object, to (for example) get the first session of our course model: .. code-block:: python dates = course.recurrences.occurrences() first_instance = dates[0] .. warning:: Looping over the entire generator returned by example above might be extremely slow and resource hungry if ``dtstart`` or ``dtend`` are not provided. Without ``dtstart``, we implicitly are looking for occurrences after the current date. Without ``dtend``, we'll look for all occurrences up to (and including) the year 9999, which is probably not what you want. The the code above counts all occurrences of our course from tomorrow until 31st December, 9999. .. _count: Counting occurrences ^^^^^^^^^^^^^^^^^^^^ The function ``count`` works fairly similarly: .. code-block:: python course.recurrences.count() It is roughly equivalent to: .. code-block:: python len(list(course.recurrences.occurrences())) Note the warning in :ref:`occurrences ` before using ``count`` (or converting the generator returned by ``occurrences()`` to a list), if you are not providing both ``dtstart`` and ``dtend``. .. _afterbefore: Getting the next or previous occurrences ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you want to get the next or previous occurrence in a given pattern, you can use ``after`` or ``before``, respectively. As with ``between``, you can choose whether you want to be inclusive of the ``datetime`` passed in by setting ``inc``. If no next or previous occurrence exists, ``None`` is returned. .. code-block:: python course = Course.objects.get(pk=1) # Get the first course on or after 1st January 2010 (this won't do # quite what you expect) course.recurrences.after( datetime(2010, 1, 1, 0, 0, 0), inc=True ) As with ``between``, if you don't specify a ``dtstart``, it will implicitly be the current time, so the above code will, to be more precise, give you the first course on or after 1st January 2010, or on or after the current date, whichever is later. Since you probably don't want that behaviour, you'll probably want to specify ``dtstart``, as follows: .. code-block:: python course = Course.objects.get(pk=1) # Get the first course on or after 1st January 2010 course.recurrences.after( datetime(2010, 1, 1, 0, 0, 0), inc=True, dtstart=datetime(2010, 1, 1, 0, 0, 0), ) For similar reasons, using ``before`` really requires that ``dtstart`` is provided, to give a start date to the recurrence pattern. This makes some sense if you consider a recurrence pattern like "every Monday, occurring 5 times". Without ``dtstart``, it's unclear what ``before`` should return - since it's impossible to know whether the pattern has started, and if so when. For example, if it started 5 years ago, ``before`` should return a date approximately 5 years ago, whereas if it started two weeks ago, ``before`` should return the last ``Monday`` (or the provided date, if ``inc`` is True, and the provided date is a Monday). .. _to-text: Getting textual descriptions of patterns ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Recurrence patterns can have multiple rules for inclusion (e.g. every week, on a Tuesday) and exclusion (e.g. except when it's the first Tuesday of the month), together with specific dates to include or exclude (regardless of whether they're part of the inclusion or exclusion rules). You'll often want to display a simple textual description of the rules involved. To take our ``Course`` example again, you can get access to the relevant inclusion rules by accessing the ``rrules`` member of the ``RecurrenceField`` attribute of your model (called ``recurrences`` in our example, though you can call it whatever you like), and to the exclusion rules by accessing the ``exrules`` member. From there you can get textual descriptions, like this: .. code-block:: python course = Course.objects.get(pk=1) text_rules_inclusion = [] for rule in course.recurrences.rrules: text_rules_inclusion.append(rule.to_text()) Similar code would work equally well for ``exrules``. django-recurrence-1.2.0/LICENSE0000644000076500000240000000301212356415033016341 0ustar cezarstaff00000000000000Copyright (c) Tamas Kemenczy and individual contributors. 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 Django nor the names of its 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. django-recurrence-1.2.0/MANIFEST.in0000644000076500000240000000021112554752650017101 0ustar cezarstaff00000000000000include LICENSE include MANIFEST.in recursive-include docs * recursive-include recurrence/static * recursive-include recurrence/locale * django-recurrence-1.2.0/PKG-INFO0000644000076500000240000000162012554754171016445 0ustar cezarstaff00000000000000Metadata-Version: 1.1 Name: django-recurrence Version: 1.2.0 Summary: Django utility wrapping dateutil.rrule Home-page: UNKNOWN Author: Tamas Kemenczy Author-email: tamas.kemenczy@gmail.com License: BSD Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Requires: Django Requires: pytz Requires: python_dateutil django-recurrence-1.2.0/recurrence/0000755000076500000240000000000012554754171017506 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/__init__.py0000644000076500000240000000106712425713022021606 0ustar cezarstaff00000000000000# flake8: noqa from recurrence.base import ( MO, TU, WE, TH, FR, SA, SU, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY, YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY, JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, validate, serialize, deserialize, to_weekday, from_dateutil_rrule, from_dateutil_rruleset, Recurrence, Rule, Weekday, ) from recurrence.exceptions import ( RecurrenceError, SerializationError, DeserializationError, ValidationError ) django-recurrence-1.2.0/recurrence/base.py0000644000076500000240000013627612554752650021011 0ustar cezarstaff00000000000000""" Wrapper around the ``dateutil.rrule`` module. Provides more consistent behavior with the rfc2445 specification, notably differing from `dateutil.rrule`` in the handling of the `dtstart` parameter and the additional handling of a `dtend` parameter. Also, the `byweekday` parameter in `dateutil.rrule` is `byday` in this package to reflect the specification. See the `Rule` and `Recurrence` class documentation for details on the differences. """ import re import datetime import calendar import pytz import dateutil.rrule from django.conf import settings from django.utils import dateformat from django.utils.translation import ugettext as _, pgettext as _p from django.utils.six import string_types from recurrence import exceptions YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY = range(7) (JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER) = range(1, 13) localtz = pytz.timezone(settings.TIME_ZONE) class Rule(object): """ A recurrence rule. `Rule` is a representation of a rfc2445 `RECUR` type, used in the `RRULE` and `EXRULE` properties. More information about the `RECUR` type specification can be found in the rfc at http://www.ietf.org/rfc/rfc2445.txt. An `Rrule` wraps the `dateutil.rrule.rrule` class while adhering to the rfc2445 spec. Notably a `dtstart` parameter cannot be specified with a `Rule` unlike `dateutil.rrule.rrule` as only one `dtstart` can be used with a set of `RRULE` and `EXRULE` rfc2445 properties, therefore the `Recurrence` class (which is based on `dateutil.rrule.rruleset`) accepts a `dtstart` parameter instead. `Recurrence` also accepts a `dtend` parameter. Documentation is largely sourced from the `dateutil.rrule.rrule` documentation at http://labix.org/python-dateutil :Variables: `freq` : int One of the enumerated constants `YEARLY`, `MONTHLY`, `WEEKLY`, `DAILY`, `HOURLY`, `MINUTELY`, or `SECONDLY`, specifying the base recurring frequency. `interval` : int The interval between each freq iteration. For example, when using YEARLY, an interval of 2 means once every two years, but with HOURLY, it means once every two hours. The default interval is 1. `wkst` : int The week start day. Must be one of the `MO`, `TU`, `WE`, `TH`, `FR`, `SA`, `SU` constants, or an integer, specifying the first day of the week. This will affect recurrences based on weekly periods. The default week start is got from `calendar.firstweekday()`, and may be modified by `calendar.setfirstweekday()`. `count` : int How many occurrences will be generated by this rule. `until` : datetime.datetime If given, this must be a `datetime.datetime` instance, that will specify the limit of the recurrence. If a recurrence instance happens to be the same as the `datetime.datetime` instance given in the `until` keyword, this will be the last occurrence. `bysetpos` : int or sequence If given, it must be either an integer, or a sequence of integers, positive or negative. Each given integer will specify an occurrence number, corresponding to the nth occurrence of the rule inside the frequency period. For example, a `bysetpos` of `-1` if combined with a `MONTHLY` frequency, and a `byday` of `(MO, TU, WE, TH, FR)`, will result in the last work day of every month. `bymonth` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the months to apply the recurrence to. `bymonthday` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the month days to apply the recurrence to. `byyearday` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the year days to apply the recurrence to. `byweekno` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the week numbers to apply the recurrence to. Week numbers have the meaning described in ISO8601, that is, the first week of the year is that containing at least four days of the new year. `byday` : int or sequence If given, it must be either an integer `(0 == MO)`, a sequence of integers, one of the weekday constants `(MO, TU, ...)`, or a sequence of these constants. When given, these variables will define the weekdays where the recurrence will be applied. It's also possible to use an argument n for the weekday instances, which will mean the nth occurrence of this weekday in the period. For example, with `MONTHLY`, or with `YEARLY` and `BYMONTH`, using `FR(1)` in byweekday will specify the first friday of the month where the recurrence happens. `byhour` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the hours to apply the recurrence to. `byminute` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the minutes to apply the recurrence to. `bysecond` : int or sequence If given, it must be either an integer, or a sequence of integers, meaning the seconds to apply the recurrence to. """ byparams = ( 'bysetpos', 'bymonth', 'bymonthday', 'byyearday', 'byweekno', 'byday', 'byhour', 'byminute', 'bysecond' ) frequencies = ( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY' ) weekdays = ( 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU' ) firstweekday = calendar.firstweekday() def __init__( self, freq, interval=1, wkst=None, count=None, until=None, **kwargs ): """ Create a new rule. See `Rule` class documentation for available `**kwargs` and parameter usage. """ self.freq = freq self.interval = interval self.wkst = wkst self.count = count self.until = until for param in self.byparams: if param in kwargs: value = kwargs[param] if hasattr(value, '__iter__'): value = list(value) if not value: value = [] elif value is not None: value = [value] else: value = [] setattr(self, param, value) else: setattr(self, param, []) def __hash__(self): byparam_values = [] for param in self.byparams: byparam_values.append(param) byparam_values.extend(getattr(self, param, []) or []) return hash(( self.freq, self.interval, self.wkst, self.count, self.until, tuple(byparam_values))) def __eq__(self, other): if not isinstance(other, Rule): raise TypeError('object to compare must be Rule object') return hash(self) == hash(other) def __ne__(self, other): return not self.__eq__(other) def to_text(self, short=False): return rule_to_text(self, short) def to_dateutil_rrule(self, dtstart=None, dtend=None, cache=False): """ Create a `dateutil.rrule.rrule` instance from this `Rule`. :Parameters: `dtstart` : datetime.datetime The date/time the recurrence rule starts. `dtend` : datetime.datetime The rule should not yield occurrences past this date. Replaces `until` if `until` is greater than `dtend`. Note: `dtend` in this case does not count for an occurrence itself. `cache` : bool If given, it must be a boolean value specifying to enable or disable caching of results. If you will use the same `dateutil.rrule.rrule` instance multiple times, enabling caching will improve the performance considerably. :Returns: A `dateutil.rrule.rrule` instance. """ kwargs = dict((p, getattr(self, p) or None) for p in self.byparams) # dateutil.rrule renames the parameter 'byweekday' by we're using # the parameter name originally specified by rfc2445. kwargs['byweekday'] = kwargs.pop('byday') until = self.until if until: until = normalize_offset_awareness(until, dtstart) if dtend: if until > dtend: until = dtend elif dtend: until = dtend return dateutil.rrule.rrule( self.freq, dtstart, self.interval, self.wkst, self.count, until, cache=cache, **kwargs) class Recurrence(object): """ A combination of `Rule` and `datetime.datetime` instances. A `Recurrence` instance provides the combined behavior of the rfc2445 `DTSTART`, `DTEND`, `RRULE`, `EXRULE`, `RDATE`, and `EXDATE` propeties in generating recurring date/times. This is a wrapper around the `dateutil.rrule.rruleset` class while adhering to the rfc2445 spec. Notably a `dtstart` parameter can be given which cascades to all `dateutil.rrule.rrule` instances generated by included `Rule` instances. A `dtend` parameter has also been included to reflect the `DTEND` rfc2445 parameter. :Variables: `dtstart` : datetime.datetime Optionally specify the first occurrence. This defaults to `datetime.datetime.now()` when the occurrence set is generated. `dtend` : datetime.datetime Optionally specify the last occurrence. `rrules` : list A list of `Rule` instances to include in the recurrence set generation. `exrules` : list A list of `Rule` instances to include in the recurrence set exclusion list. Dates which are part of the given recurrence rules will not be generated, even if some inclusive `Rule` or `datetime.datetime` instances matches them. `rdates` : list A list of `datetime.datetime` instances to include in the occurrence set generation. `exdates` : list A list of `datetime.datetime` instances to exclude in the occurrence set generation. Dates included that way will not be generated, even if some inclusive `Rule` or `datetime.datetime` instances matches them. """ def __init__( self, dtstart=None, dtend=None, rrules=[], exrules=[], rdates=[], exdates=[] ): """ Create a new recurrence. Parameters map directly to instance attributes, see `Recurrence` class documentation for usage. """ self._cache = {} self.dtstart = dtstart self.dtend = dtend self.rrules = list(rrules) self.exrules = list(exrules) self.rdates = list(rdates) self.exdates = list(exdates) def __iter__(self): return self.occurrences() def __unicode__(self): return serialize(self) def __hash__(self): return hash(( self.dtstart, self.dtend, tuple(self.rrules), tuple(self.exrules), tuple(self.rdates), tuple(self.exdates))) def __bool__(self): if (self.dtstart or self.dtend or tuple(self.rrules) or tuple(self.exrules) or tuple(self.rdates) or tuple(self.exdates)): return True else: return False def __nonzero__(self): # Required for Python 2 compatibility return type(self).__bool__(self) def __eq__(self, other): if type(other) != type(self): return False if not isinstance(other, Recurrence): raise TypeError('object to compare must be Recurrence object') return hash(self) == hash(other) def __ne__(self, other): return not self.__eq__(other) def occurrences( self, dtstart=None, dtend=None, cache=False ): """ Get a generator yielding `datetime.datetime` instances in this occurrence set. :Parameters: `dtstart` : datetime.datetime Optionally specify the first occurrence of the occurrence set. Defaults to `self.dtstart` if specified or `datetime.datetime.now()` if not when the occurrence set is generated. `dtend` : datetime.datetime Optionally specify the last occurrence of the occurrence set. Defaults to `self.dtend` if specified. `cache` : bool Whether to cache the occurrence set generator. :Returns: A sequence of `datetime.datetime` instances. """ return self.to_dateutil_rruleset(dtstart, dtend, cache) def count(self, dtstart=None, dtend=None, cache=False): """ Returns the number of occurrences in this occurrence set. :Parameters: `dtstart` : datetime.datetime Optionally specify the first occurrence of the occurrence set. Defaults to `self.dtstart` if specified or `datetime.datetime.now()` if not when the occurrence set is generated. `dtend` : datetime.datetime Optionally specify the last occurrence of the occurrence set. Defaults to `self.dtend` if specified. `cache` : bool Whether to cache the occurrence set generator. :Returns: The number of occurrences in this occurrence set. """ return self.to_dateutil_rruleset(dtstart, dtend, cache).count() def before( self, dt, inc=False, dtstart=None, dtend=None, cache=False ): """ Returns the last recurrence before the given `datetime.datetime` instance. :Parameters: `dt` : datetime.datetime The date to use as the threshold. `inc` : bool Defines what happens if `dt` is an occurrence. With `inc == True`, if `dt` itself is an occurrence, it will be returned. `dtstart` : datetime.datetime Optionally specify the first occurrence of the occurrence set. Defaults to `self.dtstart` if specified or `datetime.datetime.now()` if not when the occurrence set is generated. `dtend` : datetime.datetime Optionally specify the last occurrence of the occurrence set. Defaults to `self.dtend` if specified. `cache` : bool Whether to cache the occurrence set generator. :Returns: A `datetime.datetime` instance. """ return self.to_dateutil_rruleset( dtstart, dtend, cache).before(dt, inc) def after( self, dt, inc=False, dtstart=None, dtend=None, cache=False ): """ Returns the first recurrence after the given `datetime.datetime` instance. :Parameters: `dt` : datetime.datetime The date to use as the threshold. `inc` : bool Defines what happens if `dt` is an occurrence. With `inc == True`, if `dt` itself is an occurrence, it will be returned. `dtstart` : datetime.datetime Optionally specify the first occurrence of the occurrence set. Defaults to `self.dtstart` if specified or `datetime.datetime.now()` if not when the occurrence set is generated. `dtend` : datetime.datetime Optionally specify the last occurrence of the occurrence set. Defaults to `self.dtend` if specified. `cache` : bool Whether to cache the occurrence set generator. :Returns: A `datetime.datetime` instance. """ return self.to_dateutil_rruleset(dtstart, cache).after(dt, inc) def between( self, after, before, inc=False, dtstart=None, dtend=None, cache=False ): """ Returns the first recurrence after the given `datetime.datetime` instance. :Parameters: `after` : datetime.datetime Return dates after this date. `before` : datetime.datetime Return dates before this date. `inc` : bool Defines what happens if `after` and/or `before` are themselves occurrences. With `inc == True`, they will be included in the list, if they are found in the occurrence set. `dtstart` : datetime.datetime Optionally specify the first occurrence of the occurrence set. Defaults to `self.dtstart` if specified or `datetime.datetime.now()` if not when the occurrence set is generated. `dtend` : datetime.datetime Optionally specify the last occurrence of the occurrence set. Defaults to `self.dtend` if specified. `cache` : bool Whether to cache the occurrence set generator. :Returns: A sequence of `datetime.datetime` instances. """ return self.to_dateutil_rruleset( dtstart, dtend, cache).between(after, before, inc) def to_dateutil_rruleset(self, dtstart=None, dtend=None, cache=False): """ Create a `dateutil.rrule.rruleset` instance from this `Recurrence`. :Parameters: `dtstart` : datetime.datetime The date/time the recurrence rule starts. This value overrides the `dtstart` property specified by the `Recurrence` instance if its set. `dtstart` : datetime.datetime Optionally specify the first occurrence of the occurrence set. Defaults to `self.dtstart` if specified or `datetime.datetime.now()` if not when the occurrence set is generated. `cache` : bool If given, it must be a boolean value specifying to enable or disable caching of results. If you will use the same `dateutil.rrule.rrule` instance multiple times, enabling caching will improve the performance considerably. :Returns: A `dateutil.rrule.rruleset` instance. """ # all datetimes used in dateutil.rrule objects will need to be # normalized to either offset-aware or offset-naive datetimes # to avoid exceptions. dateutil will use the tzinfo from the # given dtstart, which will cascade to other datetime objects. dtstart = dtstart or self.dtstart dtend = dtend or self.dtend if dtend: dtend = normalize_offset_awareness(dtend or self.dtend, dtstart) if cache: # we need to cache an instance for each unique dtstart # value because the occurrence values will differ. cached = self._cache.get(dtstart) if cached: return cached rruleset = dateutil.rrule.rruleset(cache=cache) for rrule in self.rrules: rruleset.rrule(rrule.to_dateutil_rrule(dtstart, dtend, cache)) for exrule in self.exrules: rruleset.exrule(exrule.to_dateutil_rrule(dtstart, dtend, cache)) if dtstart is not None: rruleset.rdate(dtstart) for rdate in self.rdates: rdate = normalize_offset_awareness(rdate, dtstart) if dtend is not None and rdate < dtend: rruleset.rdate(rdate) elif not dtend: rruleset.rdate(rdate) if dtend is not None: rruleset.rdate(dtend) for exdate in self.exdates: exdate = normalize_offset_awareness(exdate, dtstart) if dtend is not None and exdate < dtend: rruleset.exdate(exdate) elif not dtend: rruleset.exdate(exdate) if cache: self._cache[dtstart] = rruleset return rruleset class Weekday(object): """ Representation of a weekday. A `Weekday` is essentially an integer from 0 to 6, with an optional `index` which indicates its position in a month. For example, an `number` of 6 and an `index` of ``-1`` means the last sunday of the month. Weekday's with a specific index can be created by calling the existing `MO`, `TU`, `WE`, `TH`, `FR`, `SA`, `SU` constants:: >>> SU(-1) -1SU `Weekday` objects have a smart equality test that can compare integers, other `Weekday` objects, and string constants as defined by rfc2445, such as '-1SU'. """ def __init__(self, number, index=None): """ Create a new weekday constant. :Parameters: `number` : int A number in `range(7)`. `index` : int An integer specifying the weekday's position in the month. A value of ``None`` or ``0`` means the index is ambiguous and represents all weekdays of that number. """ int(number) if number > 6: raise ValueError('number must be in range(7)') self.number = number self.index = index def __call__(self, index): if index == self.index: return self else: return Weekday(self.number, index) def __hash__(self): if self.index: return hash((self.number, self.index)) else: return hash(self.number) def __eq__(self, other): other = to_weekday(other) return (self.number, self.index) == (other.number, other.index) def __repr__(self): if self.index: return '%s%s' % (self.index, Rule.weekdays[self.number]) else: return Rule.weekdays[self.number] weekday = property(lambda self: self.number) n = property(lambda self: self.index) MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY = ( MO, TU, WE, TH, FR, SA, SU) = WEEKDAYS = list(map(lambda n: Weekday(n), range(7))) def to_weekday(token): """ Attempt to convert an object to a `Weekday` constant. :Parameters: `token` : str, int, dateutil.rrule.weekday or `Weekday` Can be values such as `MO`, `SU(-2)`, `"-2SU"`, or an integer like `1` for Tuesday. dateutil.rrule.weekday` are returned unchanged. :Returns: A `dateutil.rrule.weekday` instance. """ if isinstance(token, Weekday): return token if isinstance(token, dateutil.rrule.weekday): return Weekday(token.weekday, token.n) if isinstance(token, int): if token > 6: raise ValueError return WEEKDAYS[token] elif not token: raise ValueError elif isinstance(token, string_types) and token.isdigit(): if int(token) > 6: raise ValueError return WEEKDAYS[int(token)] elif isinstance(token, string_types): const = token[-2:].upper() if const not in Rule.weekdays: raise ValueError nth = token[:-2] if not nth: return Weekday(list(Rule.weekdays).index(const)) else: return Weekday(list(Rule.weekdays).index(const), int(nth)) def validate(rule_or_recurrence): if isinstance(rule_or_recurrence, Rule): obj = Recurrence(rrules=[rule_or_recurrence]) else: obj = rule_or_recurrence try: if not isinstance(obj, Rule) and not isinstance(obj, Recurrence): raise exceptions.ValidationError('incompatible object') except TypeError: raise exceptions.ValidationError('incompatible object') def validate_dt(dt): if not isinstance(dt, datetime.datetime): raise exceptions.ValidationError('invalid datetime: %r' % dt) def validate_iterable(rule, param): try: [v for v in getattr(rule, param, []) if v] except TypeError: # TODO: I'm not sure it's possible to get here - all the # places we call validate_iterable convert single ints to # sequences, and other types raise TypeErrors earlier. raise exceptions.ValidationError( '%s parameter must be iterable' % param) def validate_iterable_ints(rule, param, min_value=None, max_value=None): for value in getattr(rule, param, []): try: value = int(value) if min_value is not None: if value < min_value: raise ValueError if max_value is not None: if value > max_value: raise ValueError except ValueError: raise exceptions.ValidationError( 'invalid %s parameter: %r' % (param, value)) def validate_rule(rule): # validate freq try: Rule.frequencies[int(rule.freq)] except IndexError: raise exceptions.ValidationError( 'invalid freq parameter: %r' % rule.freq) except ValueError: raise exceptions.ValidationError( 'invalid freq parameter: %r' % rule.freq) # validate interval try: interval = int(rule.interval) if interval < 1: raise ValueError except ValueError: raise exceptions.ValidationError( 'invalid interval parameter: %r' % rule.interval) # validate wkst if rule.wkst: try: to_weekday(rule.wkst) except ValueError: raise exceptions.ValidationError( 'invalide wkst parameter: %r' % rule.wkst) # validate until if rule.until: try: validate_dt(rule.until) except ValueError: # TODO: I'm not sure it's possible to get here # (validate_dt doesn't raise ValueError) raise exceptions.ValidationError( 'invalid until parameter: %r' % rule.until) # validate count if rule.count: try: int(rule.count) except ValueError: raise exceptions.ValidationError( 'invalid count parameter: %r' % rule.count) # TODO: Should we check that you haven't specified both # rule.count and rule.until? Note that we only serialize # rule.until if there's no rule.count. # validate byparams for param in Rule.byparams: validate_iterable(rule, param) if param == 'byday': for value in getattr(rule, 'byday', []): try: to_weekday(value) except ValueError: raise exceptions.ValidationError( 'invalid byday parameter: %r' % value) elif param == 'bymonth': validate_iterable_ints(rule, param, 1, 12) elif param == 'bymonthday': validate_iterable_ints(rule, param, 1, 31) elif param == 'byhour': validate_iterable_ints(rule, param, 0, 23) elif param == 'byminute': validate_iterable_ints(rule, param, 0, 59) elif param == 'bysecond': validate_iterable_ints(rule, param, 0, 59) else: validate_iterable_ints(rule, param) if obj.dtstart: validate_dt(obj.dtstart) if obj.dtend: validate_dt(obj.dtend) if obj.rrules: list(map(lambda rule: validate_rule(rule), obj.rrules)) if obj.exrules: list(map(lambda rule: validate_rule(rule), obj.exrules)) if obj.rdates: list(map(lambda dt: validate_dt(dt), obj.rdates)) if obj.exdates: list(map(lambda dt: validate_dt(dt), obj.exdates)) def serialize(rule_or_recurrence): """ Serialize a `Rule` or `Recurrence` instance. `Rule` instances are wrapped as an rrule in a `Recurrence` instance before serialization, and will serialize as the `RRULE` property. All `datetime.datetime` objects will be converted and serialized as UTC. :Returns: A rfc2445 formatted unicode string. """ def serialize_dt(dt): if not dt.tzinfo: dt = localtz.localize(dt) dt = dt.astimezone(pytz.utc) return u'%s%s%sT%s%s%sZ' % ( str(dt.year).rjust(4, '0'), str(dt.month).rjust(2, '0'), str(dt.day).rjust(2, '0'), str(dt.hour).rjust(2, '0'), str(dt.minute).rjust(2, '0'), str(dt.second).rjust(2, '0'), ) def serialize_rule(rule): values = [] values.append((u'FREQ', [Rule.frequencies[rule.freq]])) if rule.interval != 1: values.append((u'INTERVAL', [str(int(rule.interval))])) if rule.wkst: values.append((u'WKST', [Rule.weekdays[rule.wkst]])) if rule.count is not None: values.append((u'COUNT', [str(rule.count)])) elif rule.until is not None: values.append((u'UNTIL', [serialize_dt(rule.until)])) if rule.byday: days = [] for d in rule.byday: d = to_weekday(d) # TODO - this if/else copies what Weekday's __repr__ # does - perhaps we should refactor it into a __str__ # method on Weekday? if d.index: days.append(u'%s%s' % (d.index, Rule.weekdays[d.number])) else: days.append(Rule.weekdays[d.number]) values.append((u'BYDAY', days)) remaining_params = list(Rule.byparams) remaining_params.remove('byday') for param in remaining_params: value_list = getattr(rule, param, None) if value_list: values.append((param.upper(), [str(n) for n in value_list])) return u';'.join(u'%s=%s' % (i[0], u','.join(i[1])) for i in values) if rule_or_recurrence is None: return None try: validate(rule_or_recurrence) except exceptions.ValidationError as error: raise exceptions.SerializationError(error.args[0]) obj = rule_or_recurrence if isinstance(obj, Rule): obj = Recurrence(rrules=[obj]) items = [] if obj.dtstart: if obj.dtstart.tzinfo: dtstart = serialize_dt(obj.dtstart.astimezone(pytz.utc)) else: dtstart = serialize_dt( localtz.localize(obj.dtstart).astimezone(pytz.utc)) items.append((u'DTSTART', dtstart)) if obj.dtend: if obj.dtend.tzinfo: dtend = serialize_dt(obj.dtend.astimezone(pytz.utc)) else: dtend = serialize_dt( localtz.localize(obj.dtend).astimezone(pytz.utc)) items.append((u'DTEND', dtend)) for rrule in obj.rrules: items.append((u'RRULE', serialize_rule(rrule))) for exrule in obj.exrules: items.append((u'EXRULE', serialize_rule(exrule))) for rdate in obj.rdates: if rdate.tzinfo: rdate = rdate.astimezone(pytz.utc) else: rdate = localtz.localize(rdate).astimezone(pytz.utc) items.append((u'RDATE', serialize_dt(rdate))) for exdate in obj.exdates: if exdate.tzinfo: exdate = exdate.astimezone(pytz.utc) else: exdate = localtz.localize(exdate).astimezone(pytz.utc) items.append((u'EXDATE', serialize_dt(exdate))) return u'\n'.join(u'%s:%s' % i for i in items) def deserialize(text): """ Deserialize a rfc2445 formatted string. This is a basic parser that is a partial implementation of rfc2445 which pertains to specifying recurring date/times. Limitations include: - Only collects `DTSTART`, `DTEND`, `RRULE`, `EXRULE`, `RDATE`, and `EXDATE` properties. - Does not capture parameter options (i.e. RDATE;VALUE=PERIOD). `dateutil.rrule` does not support anything other than `DATE-TIME` parameter types. - `VTIMEZONE` and `TZID` can't be specified, so dates without the 'Z' marker will be localized to `settings.TIME_ZONE`. `datetime.datetime` objects in `Recurrence`/`Rrule` objects will be serialized as UTC. - The `DTSTART`, `DTEND`, `RDATE` and `EXDATE` properties also only support the `DATE-TIME` type. :Returns: A `Recurrence` instance. """ def deserialize_dt(text): try: year, month, day = int(text[:4]), int(text[4:6]), int(text[6:8]) except ValueError: raise exceptions.DeserializationError('malformed date-time: %r' % text) if u'T' in text: # time is also specified try: hour, minute, second = ( int(text[9:11]), int(text[11:13]), int(text[13:15])) except ValueError: raise exceptions.DeserializationError('malformed date-time: %r' % text) else: # only date is specified, use midnight hour, minute, second = (0, 0, 0) if u'Z' in text: # time is in utc tzinfo = pytz.utc else: # right now there is no support for VTIMEZONE/TZID since # this is a partial implementation of rfc2445 so we'll # just use the time zone specified in the Django settings. tzinfo = localtz dt = datetime.datetime( year, month, day, hour, minute, second, tzinfo=tzinfo) dt = dt.astimezone(localtz) # set tz to settings.TIME_ZONE and return offset-naive datetime return datetime.datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) dtstart, dtend, rrules, exrules, rdates, exdates = None, None, [], [], [], [] tokens = re.compile( u'(DTSTART|DTEND|RRULE|EXRULE|RDATE|EXDATE)[^:]*:(.*)', re.MULTILINE).findall(text) if not tokens and text: raise exceptions.DeserializationError('malformed data') for label, param_text in tokens: if not param_text: raise exceptions.DeserializationError('empty property: %r' % label) if u'=' not in param_text: params = param_text else: params = {} param_tokens = filter(lambda p: p, param_text.split(u';')) for item in param_tokens: try: param_name, param_value = map( lambda i: i.strip(), item.split(u'=', 1)) except ValueError: raise exceptions.DeserializationError( 'missing parameter value: %r' % item) params[param_name] = list(map( lambda i: i.strip(), param_value.split(u','))) if label in (u'RRULE', u'EXRULE'): kwargs = {} for key, value in params.items(): if key == u'FREQ': try: kwargs[str(key.lower())] = list( Rule.frequencies).index(value[0]) except ValueError: raise exceptions.DeserializationError( 'bad frequency value: %r' % value[0]) elif key == u'INTERVAL': try: kwargs[str(key.lower())] = int(value[0]) except ValueError: raise exceptions.DeserializationError( 'bad interval value: %r' % value[0]) elif key == u'WKST': try: kwargs[str(key.lower())] = to_weekday(value[0]) except ValueError: raise exceptions.DeserializationError( 'bad weekday value: %r' % value[0]) elif key == u'COUNT': try: kwargs[str(key.lower())] = int(value[0]) except ValueError: raise exceptions.DeserializationError( 'bad count value: %r' % value[0]) elif key == u'UNTIL': kwargs[str(key.lower())] = deserialize_dt(value[0]) elif key == u'BYDAY': bydays = [] for v in value: try: bydays.append(to_weekday(v)) except ValueError: raise exceptions.DeserializationError( 'bad weekday value: %r' % v) kwargs[str(key.lower())] = bydays elif key.lower() in Rule.byparams: numbers = [] for v in value: try: numbers.append(int(v)) except ValueError: raise exceptions.DeserializationError( 'bad value: %r' % value) kwargs[str(key.lower())] = numbers else: raise exceptions.DeserializationError('bad parameter: %s' % key) if 'freq' not in kwargs: raise exceptions.DeserializationError( 'frequency parameter missing from rule') if label == u'RRULE': rrules.append(Rule(**kwargs)) else: exrules.append(Rule(**kwargs)) elif label == u'DTSTART': dtstart = deserialize_dt(params) elif label == u'DTEND': dtend = deserialize_dt(params) elif label == u'RDATE': rdates.append(deserialize_dt(params)) elif label == u'EXDATE': exdates.append(deserialize_dt(params)) return Recurrence(dtstart, dtend, rrules, exrules, rdates, exdates) def rule_to_text(rule, short=False): """ Render the given `Rule` as natural text. :Parameters: `short` : bool Use abbreviated labels, i.e. 'Fri' instead of 'Friday'. """ frequencies = ( _('annually'), _('monthly'), _('weekly'), _('daily'), _('hourly'), _('minutely'), _('secondly'), ) timeintervals = ( _('years'), _('months'), _('weeks'), _('days'), _('hours'), _('minutes'), _('seconds'), ) if short: positional_display = { 1: _('1st %(weekday)s'), 2: _('2nd %(weekday)s'), 3: _('3rd %(weekday)s'), -1: _('last %(weekday)s'), -2: _('2nd last %(weekday)s'), -3: _('3rd last %(weekday)s'), } weekdays_display = ( _('Mon'), _('Tue'), _('Wed'), _('Thu'), _('Fri'), _('Sat'), _('Sun'), ) months_display = ( _('Jan'), _('Feb'), _('Mar'), _('Apr'), _p('month name', 'May'), _('Jun'), _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec'), ) else: positional_display = { 1: _('first %(weekday)s'), 2: _('second %(weekday)s'), 3: _('third %(weekday)s'), 4: _('fourth %(weekday)s'), -1: _('last %(weekday)s'), -2: _('second last %(weekday)s'), -3: _('third last %(weekday)s'), } weekdays_display = ( _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday'), _('Sunday'), ) months_display = ( _('January'), _('February'), _('March'), _('April'), _p('month name', 'May'), _('June'), _('July'), _('August'), _('September'), _('October'), _('November'), _('December'), ) def get_positional_weekdays(rule): items = [] if rule.bysetpos and rule.byday: for setpos in rule.bysetpos: for byday in rule.byday: byday = to_weekday(byday) items.append( positional_display.get(setpos) % { 'weekday': weekdays_display[byday.number]}) elif rule.byday: for byday in rule.byday: byday = to_weekday(byday) items.append( positional_display.get(byday.index, '%(weekday)s') % { 'weekday': weekdays_display[byday.number]}) return _(', ').join(items) parts = [] if rule.interval > 1: parts.append( _('every %(number)s %(freq)s') % { 'number': rule.interval, 'freq': timeintervals[rule.freq] }) else: parts.append(frequencies[rule.freq]) if rule.freq == YEARLY: if rule.bymonth: # bymonths are 1-indexed (January is 1), months_display # are 0-indexed (January is 0). items = _(', ').join( [months_display[month] for month in [month_index - 1 for month_index in rule.bymonth]]) parts.append(_('each %(items)s') % {'items': items}) if rule.byday or rule.bysetpos: parts.append( _('on the %(items)s') % { 'items': get_positional_weekdays(rule)}) if rule.freq == MONTHLY: if rule.bymonthday: items = _(', ').join([ dateformat.format( datetime.datetime(1, 1, day), 'jS') for day in rule.bymonthday]) parts.append(_('on the %(items)s') % {'items': items}) elif rule.byday: if rule.byday or rule.bysetpos: parts.append( _('on the %(items)s') % { 'items': get_positional_weekdays(rule)}) if rule.freq == WEEKLY: if rule.byday: items = _(', ').join([ weekdays_display[to_weekday(day).number] for day in rule.byday]) parts.append(_('each %(items)s') % {'items': items}) # daily freqencies has no additional formatting, # hour/minute/second formatting not supported if rule.count: if rule.count == 1: parts.append(_('occuring once')) else: parts.append(_('occuring %(number)s times') % { 'number': rule.count}) elif rule.until: parts.append(_('until %(date)s') % { 'date': dateformat.format(rule.until, 'Y-m-d')}) return _(', ').join(parts) def normalize_offset_awareness(dt, from_dt=None): """ Given two `datetime.datetime` objects, return the second object as timezone offset-aware or offset-naive depending on the existence of the first object's tzinfo. If the second object is to be made offset-aware, it is assumed to be in the local timezone (with the timezone derived from the TIME_ZONE setting). If it is to be made offset-naive, It is first converted to the local timezone before being made naive. :Parameters: `dt` : `datetime.datetime` The datetime object to make offset-aware/offset-naive. `from_dt` : `datetime.datetime` The datetime object to test the existence of a tzinfo. If the value is nonzero, it will be understood as offset-naive """ if from_dt and from_dt.tzinfo and dt.tzinfo: return dt elif from_dt and from_dt.tzinfo and not dt.tzinfo: dt = localtz.localize(dt) elif dt.tzinfo: dt = dt.astimezone(localtz) dt = datetime.datetime( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) return dt def from_dateutil_rrule(rrule): """ Convert a `dateutil.rrule.rrule` instance to a `Rule` instance. :Returns: A `Rrule` instance. """ kwargs = {} kwargs['freq'] = rrule._freq kwargs['interval'] = rrule._interval if rrule._wkst != 0: kwargs['wkst'] = rrule._wkst kwargs['bysetpos'] = rrule._bysetpos if rrule._count is not None: kwargs['count'] = rrule._count elif rrule._until is not None: kwargs['until'] = rrule._until days = [] if (rrule._byweekday is not None and ( WEEKLY != rrule._freq or len(rrule._byweekday) != 1 or rrule._dtstart.weekday() != rrule._byweekday[0])): # ignore byweekday if freq is WEEKLY and day correlates # with dtstart because it was automatically set by # dateutil days.extend(Weekday(n) for n in rrule._byweekday) if rrule._bynweekday is not None: days.extend(Weekday(*n) for n in rrule._bynweekday) if len(days) > 0: kwargs['byday'] = days if rrule._bymonthday is not None and len(rrule._bymonthday) > 0: if not (rrule._freq <= MONTHLY and len(rrule._bymonthday) == 1 and rrule._bymonthday[0] == rrule._dtstart.day): # ignore bymonthday if it's generated by dateutil kwargs['bymonthday'] = list(rrule._bymonthday) if rrule._bynmonthday is not None and len(rrule._bynmonthday) > 0: kwargs.setdefault('bymonthday', []).extend(rrule._bynmonthday) if rrule._bymonth is not None and len(rrule._bymonth) > 0: if (rrule._byweekday is not None or len(rrule._bynweekday or ()) > 0 or not ( rrule._freq == YEARLY and len(rrule._bymonth) == 1 and rrule._bymonth[0] == rrule._dtstart.month)): # ignore bymonth if it's generated by dateutil kwargs['bymonth'] = list(rrule._bymonth) if rrule._byyearday is not None: kwargs['byyearday'] = list(rrule._byyearday) if rrule._byweekno is not None: kwargs['byweekno'] = list(rrule._byweekno) kwargs['byhour'] = list(rrule._byhour) kwargs['byminute'] = list(rrule._byminute) kwargs['bysecond'] = list(rrule._bysecond) if (rrule._dtstart.hour in rrule._byhour and rrule._dtstart.minute in rrule._byminute and rrule._dtstart.second in rrule._bysecond): # ignore byhour/byminute/bysecond automatically set by # dateutil from dtstart kwargs['byhour'].remove(rrule._dtstart.hour) kwargs['byminute'].remove(rrule._dtstart.minute) kwargs['bysecond'].remove(rrule._dtstart.second) return Rule(**kwargs) def from_dateutil_rruleset(rruleset): """ Convert a `dateutil.rrule.rruleset` instance to a `Recurrence` instance. :Returns: A `Recurrence` instance. """ rrules = [from_dateutil_rrule(rrule) for rrule in rruleset._rrule] exrules = [from_dateutil_rrule(exrule) for exrule in rruleset._exrule] rdates = rruleset._rdate exdates = rruleset._exdate dts = [r._dtstart for r in rruleset._rrule] + rruleset._rdate if len(dts) > 0: dts.sort() dtstart = dts[0] else: dtstart = None return Recurrence(dtstart, rrules, exrules, rdates, exdates) django-recurrence-1.2.0/recurrence/choices.py0000644000076500000240000000240212356415033021462 0ustar cezarstaff00000000000000from django.utils.translation import ugettext_lazy as _ import recurrence FREQUENCY_CHOICES = ( (recurrence.SECONDLY, _('Secondly')), (recurrence.MINUTELY, _('Minutely')), (recurrence.HOURLY, _('Hourly')), (recurrence.DAILY, _('Daily')), (recurrence.WEEKLY, _('Weekly')), (recurrence.MONTHLY, _('Monthly')), (recurrence.YEARLY, _('Yearly')), ) WEEKDAY_CHOICES = ( (recurrence.MONDAY, _('Monday')), (recurrence.TUESDAY, _('Tuesday')), (recurrence.WEDNESDAY, _('Wednesday')), (recurrence.THURSDAY, _('Thursday')), (recurrence.FRIDAY, _('Friday')), (recurrence.SATURDAY, _('Saturday')), (recurrence.SUNDAY, _('Sunday')), ) MONTH_CHOICES = ( (recurrence.JANUARY, _('January')), (recurrence.FEBRUARY, _('February')), (recurrence.MARCH, _('March')), (recurrence.APRIL, _('April')), (recurrence.MAY, _('May')), (recurrence.JUNE, _('June')), (recurrence.JULY, _('July')), (recurrence.AUGUST, _('August')), (recurrence.SEPTEMBER, _('September')), (recurrence.OCTOBER, _('October')), (recurrence.NOVEMBER, _('November')), (recurrence.DECEMBER, _('December')), ) EXCLUSION = False INCLUSION = True MODE_CHOICES = ( (INCLUSION, _('Inclusion')), (EXCLUSION, _('Exclusion')), ) django-recurrence-1.2.0/recurrence/exceptions.py0000644000076500000240000000031412425713022022222 0ustar cezarstaff00000000000000class RecurrenceError(Exception): pass class SerializationError(RecurrenceError): pass class DeserializationError(RecurrenceError): pass class ValidationError(RecurrenceError): pass django-recurrence-1.2.0/recurrence/fields.py0000644000076500000240000000267312425713022021321 0ustar cezarstaff00000000000000from django.db.models import fields from django.db.models.fields.subclassing import SubfieldBase from django.utils.six import string_types, with_metaclass import recurrence from recurrence import forms try: from south.modelsinspector import add_introspection_rules add_introspection_rules([], [ "^recurrence\.fields\.RecurrenceField", ]) except ImportError: pass class RecurrenceField(with_metaclass(SubfieldBase, fields.Field)): """ Field that stores a `recurrence.base.Recurrence` object to the database. """ def get_internal_type(self): return 'TextField' def to_python(self, value): if value is None: return value if isinstance(value, recurrence.Recurrence): return value value = super(RecurrenceField, self).to_python(value) or u'' return recurrence.deserialize(value) def get_db_prep_value(self, value, connection=None, prepared=False): if isinstance(value, string_types): value = recurrence.deserialize(value) return recurrence.serialize(value) def value_to_string(self, obj): return self.get_db_prep_value(self._get_val_from_obj(obj)) def formfield(self, **kwargs): defaults = { 'form_class': forms.RecurrenceField, 'widget': forms.RecurrenceWidget, } defaults.update(kwargs) return super(RecurrenceField, self).formfield(**defaults) django-recurrence-1.2.0/recurrence/forms.py0000644000076500000240000002042112554752650021205 0ustar cezarstaff00000000000000from django import forms from django.conf import settings from django.core import urlresolvers from django.views import i18n from django.utils import safestring from django.utils.translation import ugettext_lazy as _, get_language from django.contrib.staticfiles.storage import staticfiles_storage import recurrence from recurrence import exceptions # Django 1.5+ compatibility try: import json except ImportError: import django.utils.simplejson as json class RecurrenceWidget(forms.Textarea): def __init__(self, attrs=None, **kwargs): self.js_widget_options = kwargs defaults = {'class': 'recurrence-widget'} if attrs is not None: defaults.update(attrs) super(RecurrenceWidget, self).__init__(defaults) def render(self, name, value, attrs=None): if value is None: value = '' elif isinstance(value, recurrence.Recurrence): value = recurrence.serialize(value) widget_init_js = ( '' ) % (get_language(), attrs['id'], json.dumps(self.js_widget_options)) return safestring.mark_safe(u'%s\n%s' % ( super(RecurrenceWidget, self).render(name, value, attrs), widget_init_js)) def get_media(self): js = [ staticfiles_storage.url('recurrence/js/recurrence.js'), staticfiles_storage.url('recurrence/js/recurrence-widget.js'), ] i18n_media = find_recurrence_i18n_js_catalog() if i18n_media: js.insert(0, i18n_media) return forms.Media( js=js, css={ 'all': ( staticfiles_storage.url('recurrence/css/recurrence.css'), ), }, ) media = property(get_media) class RecurrenceField(forms.CharField): """ A Field that accepts the recurrence related parameters of rfc2445. Values are deserialized into `recurrence.base.Recurrence` objects. """ widget = RecurrenceWidget default_error_messages = { 'invalid_frequency': _( u'Invalid frequency.'), 'max_rrules_exceeded': _( u'Max rules exceeded. The limit is %(limit)s'), 'max_exrules_exceeded': _( u'Max exclusion rules exceeded. The limit is %(limit)s'), 'max_rdates_exceeded': _( u'Max dates exceeded. The limit is %(limit)s'), 'max_exdates_exceeded': _( u'Max exclusion dates exceeded. The limit is %(limit)s'), } def __init__( self, frequencies=None, accept_dtstart=True, accept_dtend=True, max_rrules=None, max_exrules=None, max_rdates=None, max_exdates=None, *args, **kwargs ): """ Create a recurrence field. A `RecurrenceField` takes the same parameters as a `CharField` field with some additional paramaters. :Parameters: `frequencies` : sequence A sequence of the frequency constants specifying which frequencies are valid for input. By default all frequencies are valid. `accept_dtstart` : bool Whether to accept a dtstart value passed in the input. `accept_dtend` : bool Whether to accept a dtend value passed in the input. `max_rrules` : int The max number of rrules to accept in the input. A value of ``0`` means input of rrules is disabled. `max_exrules` : int The max number of exrules to accept in the input. A value of ``0`` means input of exrules is disabled. `max_rdates` : int The max number of rdates to accept in the input. A value of ``0`` means input of rdates is disabled. `max_exdates` : int The max number of exdates to accept in the input. A value of ``0`` means input of exdates is disabled. """ self.accept_dtstart = accept_dtstart self.accept_dtend = accept_dtend self.max_rrules = max_rrules self.max_exrules = max_exrules self.max_rdates = max_rdates self.max_exdates = max_exdates if frequencies is not None: self.frequencies = frequencies else: self.frequencies = ( recurrence.YEARLY, recurrence.MONTHLY, recurrence.WEEKLY, recurrence.DAILY, recurrence.HOURLY, recurrence.MINUTELY, recurrence.SECONDLY, ) super(RecurrenceField, self).__init__(*args, **kwargs) def clean(self, value): """ Validates that ``value`` deserialized into a `recurrence.base.Recurrence` object falls within the parameters specified to the `RecurrenceField` constructor. """ try: recurrence_obj = recurrence.deserialize(value) except exceptions.DeserializationError as error: raise forms.ValidationError(error.args[0]) if not self.accept_dtstart: recurrence_obj.dtstart = None if not self.accept_dtend: recurrence_obj.dtend = None if self.max_rrules is not None: if len(recurrence_obj.rrules) > self.max_rrules: raise forms.ValidationError( self.error_messages['max_rrules_exceeded'] % { 'limit': self.max_rrules } ) if self.max_exrules is not None: if len(recurrence_obj.exrules) > self.max_exrules: raise forms.ValidationError( self.error_messages['max_exrules_exceeded'] % { 'limit': self.max_exrules } ) if self.max_rdates is not None: if len(recurrence_obj.rdates) > self.max_rdates: raise forms.ValidationError( self.error_messages['max_rdates_exceeded'] % { 'limit': self.max_rdates } ) if self.max_exdates is not None: if len(recurrence_obj.exdates) > self.max_exdates: raise forms.ValidationError( self.error_messages['max_exdates_exceeded'] % { 'limit': self.max_exdates } ) for rrule in recurrence_obj.rrules: if rrule.freq not in self.frequencies: raise forms.ValidationError( self.error_messages['invalid_frequency']) for exrule in recurrence_obj.exrules: if exrule.freq not in self.frequencies: raise forms.ValidationError( self.error_messages['invalid_frequency']) return recurrence_obj _recurrence_javascript_catalog_url = None def find_recurrence_i18n_js_catalog(): # used cached version global _recurrence_javascript_catalog_url if _recurrence_javascript_catalog_url: return _recurrence_javascript_catalog_url # first try to use the dynamic form of the javascript_catalog view try: return urlresolvers.reverse( i18n.javascript_catalog, kwargs={'packages': 'recurrence'}) except urlresolvers.NoReverseMatch: pass # then scan the entire urlconf for a javascript_catalague pattern # that manually selects recurrence as one of the packages to include def check_urlpatterns(urlpatterns): for pattern in urlpatterns: if hasattr(pattern, 'url_patterns'): match = check_urlpatterns(pattern.url_patterns) if match: return match elif (pattern.callback == i18n.javascript_catalog and 'recurrence' in pattern.default_args.get('packages', [])): if pattern.name: return urlresolvers.reverse(pattern.name) else: return urlresolvers.reverse(pattern.callback) root_urlconf = __import__(settings.ROOT_URLCONF, {}, {}, ['']) url = check_urlpatterns(root_urlconf.urlpatterns) # cache it for subsequent use _recurrence_javascript_catalog_url = url return url django-recurrence-1.2.0/recurrence/locale/0000755000076500000240000000000012554754171020745 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/en/0000755000076500000240000000000012554754171021347 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/en/LC_MESSAGES/0000755000076500000240000000000012554754171023134 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/en/LC_MESSAGES/django.mo0000644000076500000240000000055712356415033024731 0ustar cezarstaff00000000000000$,859Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2009-02-05 13:25-0600 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit django-recurrence-1.2.0/recurrence/locale/en/LC_MESSAGES/django.po0000644000076500000240000001240012554752650024733 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-02-05 13:25-0600\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: base.py:871 msgid "annually" msgstr "" #: base.py:871 msgid "monthly" msgstr "" #: base.py:871 msgid "weekly" msgstr "" #: base.py:871 msgid "daily" msgstr "" #: base.py:872 msgid "hourly" msgstr "" #: base.py:872 msgid "minutely" msgstr "" #: base.py:872 msgid "secondly" msgstr "" #: base.py:875 msgid "year" msgstr "" #: base.py:875 msgid "month" msgstr "" #: base.py:875 msgid "week" msgstr "" #: base.py:875 msgid "day" msgstr "" #: base.py:876 msgid "hour" msgstr "" #: base.py:876 msgid "minute" msgstr "" #: base.py:876 msgid "second" msgstr "" #: base.py:879 msgid "years" msgstr "" #: base.py:879 msgid "months" msgstr "" #: base.py:879 msgid "weeks" msgstr "" #: base.py:879 msgid "days" msgstr "" #: base.py:880 msgid "hours" msgstr "" #: base.py:880 msgid "minutes" msgstr "" #: base.py:880 msgid "seconds" msgstr "" #: base.py:885 #, python-format msgid "1st %(weekday)s" msgstr "" #: base.py:886 #, python-format msgid "2nd %(weekday)s" msgstr "" #: base.py:887 #, python-format msgid "3rd %(weekday)s" msgstr "" #: base.py:888 base.py:907 #, python-format msgid "last %(weekday)s" msgstr "" #: base.py:889 #, python-format msgid "2nd last %(weekday)s" msgstr "" #: base.py:890 #, python-format msgid "3rd last %(weekday)s" msgstr "" #: base.py:893 msgid "Mon" msgstr "" #: base.py:893 msgid "Tue" msgstr "" #: base.py:893 msgid "Wed" msgstr "" #: base.py:894 msgid "Thu" msgstr "" #: base.py:894 msgid "Fri" msgstr "" #: base.py:894 msgid "Sat" msgstr "" #: base.py:894 msgid "Sun" msgstr "" #: base.py:897 msgid "Jan" msgstr "" #: base.py:897 msgid "Feb" msgstr "" #: base.py:897 msgid "Mar" msgstr "" #: base.py:897 msgid "Apr" msgstr "" #: base.py:898 base.py:917 choices.py:31 msgid "May" msgstr "" #: base.py:898 msgid "Jun" msgstr "" #: base.py:898 msgid "Jul" msgstr "" #: base.py:898 msgid "Aug" msgstr "" #: base.py:899 msgid "Sep" msgstr "" #: base.py:899 msgid "Oct" msgstr "" #: base.py:899 msgid "Nov" msgstr "" #: base.py:899 msgid "Dec" msgstr "" #: base.py:904 #, python-format msgid "first %(weekday)s" msgstr "" #: base.py:905 #, python-format msgid "second %(weekday)s" msgstr "" #: base.py:906 #, python-format msgid "third %(weekday)s" msgstr "" #: base.py:906 #, python-format msgid "fourth %(weekday)s" msgstr "" #: base.py:908 #, python-format msgid "second last %(weekday)s" msgstr "" #: base.py:909 #, python-format msgid "third last %(weekday)s" msgstr "" #: base.py:912 choices.py:17 msgid "Monday" msgstr "" #: base.py:912 choices.py:18 msgid "Tuesday" msgstr "" #: base.py:912 choices.py:19 msgid "Wednesday" msgstr "" #: base.py:913 choices.py:20 msgid "Thursday" msgstr "" #: base.py:913 choices.py:21 msgid "Friday" msgstr "" #: base.py:913 choices.py:22 msgid "Saturday" msgstr "" #: base.py:913 choices.py:23 msgid "Sunday" msgstr "" #: base.py:916 choices.py:27 msgid "January" msgstr "" #: base.py:916 choices.py:28 msgid "February" msgstr "" #: base.py:916 choices.py:29 msgid "March" msgstr "" #: base.py:916 choices.py:30 msgid "April" msgstr "" #: base.py:917 choices.py:32 msgid "June" msgstr "" #: base.py:917 choices.py:33 msgid "July" msgstr "" #: base.py:917 choices.py:34 msgid "August" msgstr "" #: base.py:918 choices.py:35 msgid "September" msgstr "" #: base.py:918 choices.py:36 msgid "October" msgstr "" #: base.py:918 choices.py:37 msgid "November" msgstr "" #: base.py:918 choices.py:38 msgid "December" msgstr "" #: base.py:936 base.py:951 base.py:974 base.py:992 msgid ", " msgstr "" #: base.py:942 #, python-format msgid "every %(number)s %(freq)s" msgstr "" #: base.py:953 base.py:977 #, python-format msgid "each %(items)s" msgstr "" #: base.py:956 base.py:965 base.py:969 #, python-format msgid "on the %(items)s" msgstr "" #: base.py:984 msgid "occuring once" msgstr "" #: base.py:986 #, python-format msgid "occuring %(number)s times" msgstr "" #: base.py:989 #, python-format msgid "until %(date)s" msgstr "" #: choices.py:7 msgid "Secondly" msgstr "" #: choices.py:8 msgid "Minutely" msgstr "" #: choices.py:9 msgid "Hourly" msgstr "" #: choices.py:10 msgid "Daily" msgstr "" #: choices.py:11 msgid "Weekly" msgstr "" #: choices.py:12 msgid "Monthly" msgstr "" #: choices.py:13 msgid "Yearly" msgstr "" #: choices.py:44 msgid "Inclusion" msgstr "" #: choices.py:45 msgid "Exclusion" msgstr "" #: forms.py:64 msgid "Invalid frequency." msgstr "" #: forms.py:66 #, python-format msgid "Max rules exceeded. The limit is %(limit)s" msgstr "" #: forms.py:68 #, python-format msgid "Max exclusion rules exceeded. The limit is %(limit)s" msgstr "" #: forms.py:70 #, python-format msgid "Max dates exceeded. The limit is %(limit)s" msgstr "" #: forms.py:72 #, python-format msgid "Max exclusion dates exceeded. The limit is %(limit)s" msgstr "" django-recurrence-1.2.0/recurrence/locale/en/LC_MESSAGES/djangojs.mo0000644000076500000240000000055712356415033025266 0ustar cezarstaff00000000000000$,859Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2009-02-05 13:26-0600 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit django-recurrence-1.2.0/recurrence/locale/en/LC_MESSAGES/djangojs.po0000644000076500000240000001647712554752650025312 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-02-05 13:26-0600\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: media/js/recurrence-widget.js:1732 msgid "including" msgstr "" #: media/js/recurrence-widget.js:1732 msgid "excluding" msgstr "" #: media/js/recurrence-widget.js:1736 msgid "Frequency" msgstr "" #: media/js/recurrence-widget.js:1737 msgid "On the" msgstr "" #: media/js/recurrence-widget.js:1738 msgid "Each" msgstr "" #: media/js/recurrence-widget.js:1739 msgid "Every" msgstr "" #: media/js/recurrence-widget.js:1740 msgid "Until" msgstr "" #: media/js/recurrence-widget.js:1741 msgid "Occurs %(number)s time" msgstr "" #: media/js/recurrence-widget.js:1742 msgid "Occurs %(number)s times" msgstr "" #: media/js/recurrence-widget.js:1743 msgid "Date" msgstr "" #: media/js/recurrence-widget.js:1744 msgid "Time" msgstr "" #: media/js/recurrence-widget.js:1745 msgid "Repeat until" msgstr "" #: media/js/recurrence-widget.js:1746 msgid "Exclude these occurences" msgstr "" #: media/js/recurrence-widget.js:1747 msgid "Exclude this date" msgstr "" #: media/js/recurrence-widget.js:1748 msgid "Add rule" msgstr "" #: media/js/recurrence-widget.js:1749 msgid "Add date" msgstr "" #: media/js/recurrence-widget.js:1750 msgid "Remove" msgstr "" #: media/js/recurrence-widget.js:1751 msgid "Calendar" msgstr "" #: media/js/recurrence.js:995 msgid "midnight" msgstr "" #: media/js/recurrence.js:996 msgid "noon" msgstr "" #: media/js/recurrence.js:997 msgid "on the %(items)s" msgstr "" #: media/js/recurrence.js:998 msgid "every %(number)s %(freq)s" msgstr "" #: media/js/recurrence.js:999 msgid "each %(items)s" msgstr "" #: media/js/recurrence.js:1000 msgid "occuring %(number)s time" msgstr "" #: media/js/recurrence.js:1001 msgid "occuring %(number)s times" msgstr "" #: media/js/recurrence.js:1002 msgid "until %(date)s" msgstr "" #: media/js/recurrence.js:1006 msgid "year" msgstr "" #: media/js/recurrence.js:1006 msgid "month" msgstr "" #: media/js/recurrence.js:1006 msgid "week" msgstr "" #: media/js/recurrence.js:1006 msgid "day" msgstr "" #: media/js/recurrence.js:1007 msgid "hour" msgstr "" #: media/js/recurrence.js:1007 msgid "minute" msgstr "" #: media/js/recurrence.js:1007 msgid "second" msgstr "" #: media/js/recurrence.js:1010 msgid "years" msgstr "" #: media/js/recurrence.js:1010 msgid "months" msgstr "" #: media/js/recurrence.js:1010 msgid "weeks" msgstr "" #: media/js/recurrence.js:1010 msgid "days" msgstr "" #: media/js/recurrence.js:1011 msgid "hours" msgstr "" #: media/js/recurrence.js:1011 msgid "minutes" msgstr "" #: media/js/recurrence.js:1011 msgid "seconds" msgstr "" #: media/js/recurrence.js:1014 msgid "annually" msgstr "" #: media/js/recurrence.js:1014 msgid "monthly" msgstr "" #: media/js/recurrence.js:1014 msgid "weekly" msgstr "" #: media/js/recurrence.js:1014 msgid "daily" msgstr "" #: media/js/recurrence.js:1015 msgid "hourly" msgstr "" #: media/js/recurrence.js:1015 msgid "minutely" msgstr "" #: media/js/recurrence.js:1015 msgid "secondly" msgstr "" #: media/js/recurrence.js:1018 msgid "Monday" msgstr "" #: media/js/recurrence.js:1018 msgid "Tuesday" msgstr "" #: media/js/recurrence.js:1018 msgid "Wednesday" msgstr "" #: media/js/recurrence.js:1018 msgid "Thursday" msgstr "" #: media/js/recurrence.js:1019 msgid "Friday" msgstr "" #: media/js/recurrence.js:1019 msgid "Saturday" msgstr "" #: media/js/recurrence.js:1019 msgid "Sunday" msgstr "" #: media/js/recurrence.js:1022 msgid "Mon" msgstr "" #: media/js/recurrence.js:1022 msgid "Tue" msgstr "" #: media/js/recurrence.js:1022 msgid "Wed" msgstr "" #: media/js/recurrence.js:1022 msgid "Thu" msgstr "" #: media/js/recurrence.js:1023 msgid "Fri" msgstr "" #: media/js/recurrence.js:1023 msgid "Sat" msgstr "" #: media/js/recurrence.js:1023 msgid "Sun" msgstr "" #: media/js/recurrence.js:1026 msgid "M" msgstr "" #: media/js/recurrence.js:1026 msgid "T" msgstr "" #: media/js/recurrence.js:1026 msgid "W" msgstr "" #: media/js/recurrence.js:1027 msgid "F" msgstr "" #: media/js/recurrence.js:1027 msgid "S" msgstr "" #: media/js/recurrence.js:1030 msgid "first %(weekday)s" msgstr "" #: media/js/recurrence.js:1031 msgid "second %(weekday)s" msgstr "" #: media/js/recurrence.js:1032 msgid "third %(weekday)s" msgstr "" #: media/js/recurrence.js:1032 msgid "fourth %(weekday)s" msgstr "" #: media/js/recurrence.js:1033 media/js/recurrence.js.py:1041 msgid "last %(weekday)s" msgstr "" #: media/js/recurrence.js:1034 msgid "second last %(weekday)s" msgstr "" #: media/js/recurrence.js:1035 msgid "third last %(weekday)s" msgstr "" #: media/js/recurrence.js:1038 msgid "1st %(weekday)s" msgstr "" #: media/js/recurrence.js:1039 msgid "2nd %(weekday)s" msgstr "" #: media/js/recurrence.js:1040 msgid "3rd %(weekday)s" msgstr "" #: media/js/recurrence.js:1042 msgid "2nd last %(weekday)s" msgstr "" #: media/js/recurrence.js:1043 msgid "3rd last %(weekday)s" msgstr "" #: media/js/recurrence.js:1046 msgid "January" msgstr "" #: media/js/recurrence.js:1046 msgid "February" msgstr "" #: media/js/recurrence.js:1046 media/js/recurrence.js.py:1058 msgid "March" msgstr "" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1059 msgid "April" msgstr "" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1053 #: media/js/recurrence.js:1059 msgid "May" msgstr "" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1059 msgid "June" msgstr "" #: media/js/recurrence.js:1048 media/js/recurrence.js.py:1060 msgid "July" msgstr "" #: media/js/recurrence.js:1048 msgid "August" msgstr "" #: media/js/recurrence.js:1048 msgid "September" msgstr "" #: media/js/recurrence.js:1049 msgid "October" msgstr "" #: media/js/recurrence.js:1049 msgid "November" msgstr "" #: media/js/recurrence.js:1049 msgid "December" msgstr "" #: media/js/recurrence.js:1052 msgid "Jan" msgstr "" #: media/js/recurrence.js:1052 msgid "Feb" msgstr "" #: media/js/recurrence.js:1052 msgid "Mar" msgstr "" #: media/js/recurrence.js:1053 msgid "Apr" msgstr "" #: media/js/recurrence.js:1053 msgid "Jun" msgstr "" #: media/js/recurrence.js:1054 msgid "Jul" msgstr "" #: media/js/recurrence.js:1054 msgid "Aug" msgstr "" #: media/js/recurrence.js:1054 msgid "Sep" msgstr "" #: media/js/recurrence.js:1055 msgid "Oct" msgstr "" #: media/js/recurrence.js:1055 msgid "Nov" msgstr "" #: media/js/recurrence.js:1055 msgid "Dec" msgstr "" #: media/js/recurrence.js:1058 msgid "Jan." msgstr "" #: media/js/recurrence.js:1058 msgid "Feb." msgstr "" #: media/js/recurrence.js:1060 msgid "Aug." msgstr "" #: media/js/recurrence.js:1060 msgid "Sept." msgstr "" #: media/js/recurrence.js:1061 msgid "Oct." msgstr "" #: media/js/recurrence.js:1061 msgid "Nov." msgstr "" #: media/js/recurrence.js:1061 msgid "Dec." msgstr "" #: media/js/recurrence.js:1064 msgid "a.m." msgstr "" #: media/js/recurrence.js:1064 msgid "p.m." msgstr "" #: media/js/recurrence.js:1065 msgid "AM" msgstr "" #: media/js/recurrence.js:1065 msgid "PM" msgstr "" django-recurrence-1.2.0/recurrence/locale/es/0000755000076500000240000000000012554754171021354 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/es/LC_MESSAGES/0000755000076500000240000000000012554754171023141 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/es/LC_MESSAGES/django.mo0000644000076500000240000001054612554752650024746 0ustar cezarstaff00000000000000\!6:@DKQU ^hluy *44, *a                       " ) 2 8 < A P j |               2 J S [ m       U    ) A Q i m s w ~             ; BGB<   $1 5@DLP Xe itx    $ )39M T`h ly  0? FS[`E'R%B?=\ ;MJ[@NX V6H3IW#02,GQL!+.T 8 >$A* U9CK /PD1&Z<S:Y54-7)("FO, 1st %(weekday)s2nd %(weekday)s2nd last %(weekday)s3rd %(weekday)s3rd last %(weekday)sAprAprilAugAugustDailyDecDecemberExclusionFebFebruaryFriFridayHourlyInclusionInvalid frequency.JanJanuaryJulJulyJunJuneMarMarchMax dates exceeded. The limit is %(limit)sMax exclusion dates exceeded. The limit is %(limit)sMax exclusion rules exceeded. The limit is %(limit)sMax rules exceeded. The limit is %(limit)sMayMinutelyMonMondayMonthlyNovNovemberOctOctoberSatSaturdaySecondlySepSeptemberSunSundayThuThursdayTueTuesdayWedWednesdayWeeklyYearlyannuallydailydaydayseach %(items)severy %(number)s %(freq)sfirst %(weekday)sfourth %(weekday)shourhourlyhourslast %(weekday)sminuteminutelyminutesmonthmonthlymonthsoccuring %(number)s timesoccuring onceon the %(items)ssecondsecond %(weekday)ssecond last %(weekday)ssecondlysecondsthird %(weekday)sthird last %(weekday)suntil %(date)sweekweeklyweeksyearyearsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2009-02-05 13:25-0600 PO-Revision-Date: 2015-07-21 10:00+0200 Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Gtranslator 2.91.7 , 1º %(weekday)s2º %(weekday)s2º último %(weekday)s3º %(weekday)s3º último %(weekday)sAbrAbrilAgoAgostoA diarioDicDiciembreExclusiónFebFebreroVieViernesCada horaInclusiónFrecuencia incorrectaEneEneroJulJulioJunJunioMarMarzoNúmero máximo de fecha excedidas. El límite es %(limit)sMáximo de fechas de exclusión excedidas. El límite es %(limit)sMáximo de reglas de exclusión excedidas. El límite es %(limit)sNúmero máximo de reglas excedidas. El límite es %(limit)sMayCada minutoLunLunesMensualmenteNovNoviembre OctOctubreSabSábadoCada segundoSepSeptiembreDomDomingoJueJuevesMarMartesMierMiércolesSemanalmenteAnualmenteanualmenteddíadíascada %(items)scada %(number)s %(freq)sprimer %(weekday)scuarto %(weekday)shoracada horahorasúltimo %(weekday)sminutocada minutominutosmesmensualmentemesesocurre %(number)s vecesocurre una vezel %(items)ssegundosegundo %(weekday)ssegundo último %(weekday)scada segundosegundostercero %(weekday)stercer último %(weekday)shasta %(date)ssemanasemanalmentesemanasañoañosdjango-recurrence-1.2.0/recurrence/locale/es/LC_MESSAGES/django.po0000644000076500000240000001450312554752650024746 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # FULL NAME , 2015. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-02-05 13:25-0600\n" "PO-Revision-Date: 2015-07-21 10:00+0200\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Gtranslator 2.91.7\n" #: base.py:871 msgid "annually" msgstr "anualmente" #: base.py:871 msgid "monthly" msgstr "mensualmente" #: base.py:871 msgid "weekly" msgstr "semanalmente" #: base.py:871 msgid "daily" msgstr "d" #: base.py:872 msgid "hourly" msgstr "cada hora" #: base.py:872 msgid "minutely" msgstr "cada minuto" #: base.py:872 msgid "secondly" msgstr "cada segundo" #: base.py:875 msgid "year" msgstr "año" #: base.py:875 msgid "month" msgstr "mes" #: base.py:875 msgid "week" msgstr "semana" #: base.py:875 msgid "day" msgstr "día" #: base.py:876 msgid "hour" msgstr "hora" #: base.py:876 msgid "minute" msgstr "minuto" #: base.py:876 msgid "second" msgstr "segundo" #: base.py:879 msgid "years" msgstr "años" #: base.py:879 msgid "months" msgstr "meses" #: base.py:879 msgid "weeks" msgstr "semanas" #: base.py:879 msgid "days" msgstr "días" #: base.py:880 msgid "hours" msgstr "horas" #: base.py:880 msgid "minutes" msgstr "minutos" #: base.py:880 msgid "seconds" msgstr "segundos" #: base.py:885 #, python-format msgid "1st %(weekday)s" msgstr "1º %(weekday)s" #: base.py:886 #, python-format msgid "2nd %(weekday)s" msgstr "2º %(weekday)s" #: base.py:887 #, python-format msgid "3rd %(weekday)s" msgstr "3º %(weekday)s" #: base.py:888 base.py:907 #, python-format msgid "last %(weekday)s" msgstr "último %(weekday)s" #: base.py:889 #, python-format msgid "2nd last %(weekday)s" msgstr "2º último %(weekday)s" #: base.py:890 #, python-format msgid "3rd last %(weekday)s" msgstr "3º último %(weekday)s" #: base.py:893 msgid "Mon" msgstr "Lun" #: base.py:893 msgid "Tue" msgstr "Mar" #: base.py:893 msgid "Wed" msgstr "Mier" #: base.py:894 msgid "Thu" msgstr "Jue" #: base.py:894 msgid "Fri" msgstr "Vie" #: base.py:894 msgid "Sat" msgstr "Sab" #: base.py:894 msgid "Sun" msgstr "Dom" #: base.py:897 msgid "Jan" msgstr "Ene" #: base.py:897 msgid "Feb" msgstr "Feb" #: base.py:897 msgid "Mar" msgstr "Mar" #: base.py:897 msgid "Apr" msgstr "Abr" #: base.py:898 base.py:917 choices.py:31 msgid "May" msgstr "May" #: base.py:898 msgid "Jun" msgstr "Jun" #: base.py:898 msgid "Jul" msgstr "Jul" #: base.py:898 msgid "Aug" msgstr "Ago" #: base.py:899 msgid "Sep" msgstr "Sep" #: base.py:899 msgid "Oct" msgstr "Oct" #: base.py:899 msgid "Nov" msgstr "Nov" #: base.py:899 msgid "Dec" msgstr "Dic" #: base.py:904 #, python-format msgid "first %(weekday)s" msgstr "primer %(weekday)s" #: base.py:905 #, python-format msgid "second %(weekday)s" msgstr "segundo %(weekday)s" #: base.py:906 #, python-format msgid "third %(weekday)s" msgstr "tercero %(weekday)s" #: base.py:906 #, python-format msgid "fourth %(weekday)s" msgstr "cuarto %(weekday)s" #: base.py:908 #, python-format msgid "second last %(weekday)s" msgstr "segundo último %(weekday)s" #: base.py:909 #, python-format msgid "third last %(weekday)s" msgstr "tercer último %(weekday)s" #: base.py:912 choices.py:17 msgid "Monday" msgstr "Lunes" #: base.py:912 choices.py:18 msgid "Tuesday" msgstr "Martes" #: base.py:912 choices.py:19 msgid "Wednesday" msgstr "Miércoles" #: base.py:913 choices.py:20 msgid "Thursday" msgstr "Jueves" #: base.py:913 choices.py:21 msgid "Friday" msgstr "Viernes" #: base.py:913 choices.py:22 msgid "Saturday" msgstr "Sábado" #: base.py:913 choices.py:23 msgid "Sunday" msgstr "Domingo" #: base.py:916 choices.py:27 msgid "January" msgstr "Enero" #: base.py:916 choices.py:28 msgid "February" msgstr "Febrero" #: base.py:916 choices.py:29 msgid "March" msgstr "Marzo" #: base.py:916 choices.py:30 msgid "April" msgstr "Abril" #: base.py:917 choices.py:32 msgid "June" msgstr "Junio" #: base.py:917 choices.py:33 msgid "July" msgstr "Julio" #: base.py:917 choices.py:34 msgid "August" msgstr "Agosto" #: base.py:918 choices.py:35 msgid "September" msgstr "Septiembre" #: base.py:918 choices.py:36 msgid "October" msgstr "Octubre" #: base.py:918 choices.py:37 msgid "November" msgstr "Noviembre " #: base.py:918 choices.py:38 msgid "December" msgstr "Diciembre" #: base.py:936 base.py:951 base.py:974 base.py:992 msgid ", " msgstr ", " #: base.py:942 #, python-format msgid "every %(number)s %(freq)s" msgstr "cada %(number)s %(freq)s" #: base.py:953 base.py:977 #, python-format msgid "each %(items)s" msgstr "cada %(items)s" #: base.py:956 base.py:965 base.py:969 #, python-format msgid "on the %(items)s" msgstr "el %(items)s" #: base.py:984 msgid "occuring once" msgstr "ocurre una vez" #: base.py:986 #, python-format msgid "occuring %(number)s times" msgstr "ocurre %(number)s veces" #: base.py:989 #, python-format msgid "until %(date)s" msgstr "hasta %(date)s" #: choices.py:7 msgid "Secondly" msgstr "Cada segundo" #: choices.py:8 msgid "Minutely" msgstr "Cada minuto" #: choices.py:9 msgid "Hourly" msgstr "Cada hora" #: choices.py:10 msgid "Daily" msgstr "A diario" #: choices.py:11 msgid "Weekly" msgstr "Semanalmente" #: choices.py:12 msgid "Monthly" msgstr "Mensualmente" #: choices.py:13 msgid "Yearly" msgstr "Anualmente" #: choices.py:44 msgid "Inclusion" msgstr "Inclusión" #: choices.py:45 msgid "Exclusion" msgstr "Exclusión" #: forms.py:64 msgid "Invalid frequency." msgstr "Frecuencia incorrecta" #: forms.py:66 #, python-format msgid "Max rules exceeded. The limit is %(limit)s" msgstr "Número máximo de reglas excedidas. El límite es %(limit)s" #: forms.py:68 #, python-format msgid "Max exclusion rules exceeded. The limit is %(limit)s" msgstr "Máximo de reglas de exclusión excedidas. El límite es %(limit)s" #: forms.py:70 #, python-format msgid "Max dates exceeded. The limit is %(limit)s" msgstr "Número máximo de fecha excedidas. El límite es %(limit)s" #: forms.py:72 #, python-format msgid "Max exclusion dates exceeded. The limit is %(limit)s" msgstr "Máximo de fechas de exclusión excedidas. El límite es %(limit)s" django-recurrence-1.2.0/recurrence/locale/es/LC_MESSAGES/djangojs.mo0000644000076500000240000001041512554752650025276 0ustar cezarstaff00000000000000b,<HIYi~ % ' 0 : > E I N V [ ` b h l p w                    " , 5 ; ? D S m w                7 H O b z          U 6 F V n ~            ') 1<@HLQW]cekpt z    " - 8DIO^w     "5 MZbv  :C ;<9L @08!?7 Z#JG[\MIR^1 X`(Ba/PY+_&'UO H]E V-3%25>6$"4KTb)=*SFQN,.DAW1st %(weekday)s2nd %(weekday)s2nd last %(weekday)s3rd %(weekday)s3rd last %(weekday)sAdd dateAdd ruleAprAprilAugAug.AugustCalendarDateDecDec.DecemberEachEveryExclude these occurencesExclude this dateFFebruaryFrequencyFriFridayJanJan.JanuaryJulyJuneMMarchMayMonMondayNovemberOccurs %(number)s timeOccurs %(number)s timesOctoberOn theRemoveRepeat untilSSatSaturdaySeptemberSunSundayTThuThursdayTimeTueTuesdayUntilWWedWednesdayannuallydailydaydayseach %(items)severy %(number)s %(freq)sexcludingfirst %(weekday)sfourth %(weekday)shourhourlyhoursincludinglast %(weekday)smidnightminuteminutelyminutesmonthmonthlymonthsnoonoccuring %(number)s timeoccuring %(number)s timeson the %(items)ssecondsecond %(weekday)ssecond last %(weekday)ssecondlysecondsthird %(weekday)sthird last %(weekday)suntil %(date)sweekweeklyweeksyearyearsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2009-02-05 13:26-0600 PO-Revision-Date: 2015-07-21 09:45+0200 Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Gtranslator 2.91.7 1º %(weekday)s2º %(weekday)s2º último %(weekday)s3º %(weekday)s3º último %(weekday)sAñadir fechaAñadir reglaAbrAbrilAgoAgo.AgostoCalendarioFechaDicDic.DiciembreCadaCadaExcluir estas ocurrenciasExcluir esta fechaVFebreroFrecuenciaVieViernesEneEne.EneroJulioJunioMMarzoMayoLunLunesNoviembreOcurre %(number)s vezOcurre %(number)s vecesOctubreElEliminarRepetir hastaSSabSábadoSeptiembreDomDomingoJJueJuevesHoraMarMartesHastaXMieMiércolesanualmentediariamentedíadíascada %(items)scada %(number)s %(freq)sExcluidoprimer %(weekday)scuarto %(weekday)shoracada horahorasIncluidoúltimo %(weekday)sMedia nocheminutocada minutominutos mesmensualmentemesesMedio díaocurre %(number)s ocurre %(number)s vecesen %(items)ssegundosegundo %(weekday)ssegundo último %(weekday)scada segundosegundostercero %(weekday)stercer último %(weekday)shasta %(date)ssemanasemanalmentesemanasañoañosdjango-recurrence-1.2.0/recurrence/locale/es/LC_MESSAGES/djangojs.po0000644000076500000240000002036412554752650025305 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # FULL NAME , 2015. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-02-05 13:26-0600\n" "PO-Revision-Date: 2015-07-21 09:45+0200\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Gtranslator 2.91.7\n" #: media/js/recurrence-widget.js:1732 msgid "including" msgstr "Incluido" #: media/js/recurrence-widget.js:1732 msgid "excluding" msgstr "Excluido" #: media/js/recurrence-widget.js:1736 msgid "Frequency" msgstr "Frecuencia" #: media/js/recurrence-widget.js:1737 msgid "On the" msgstr "El" #: media/js/recurrence-widget.js:1738 msgid "Each" msgstr "Cada" #: media/js/recurrence-widget.js:1739 msgid "Every" msgstr "Cada" #: media/js/recurrence-widget.js:1740 msgid "Until" msgstr "Hasta" #: media/js/recurrence-widget.js:1741 msgid "Occurs %(number)s time" msgstr "Ocurre %(number)s vez" #: media/js/recurrence-widget.js:1742 msgid "Occurs %(number)s times" msgstr "Ocurre %(number)s veces" #: media/js/recurrence-widget.js:1743 msgid "Date" msgstr "Fecha" #: media/js/recurrence-widget.js:1744 msgid "Time" msgstr "Hora" #: media/js/recurrence-widget.js:1745 msgid "Repeat until" msgstr "Repetir hasta" #: media/js/recurrence-widget.js:1746 msgid "Exclude these occurences" msgstr "Excluir estas ocurrencias" #: media/js/recurrence-widget.js:1747 msgid "Exclude this date" msgstr "Excluir esta fecha" #: media/js/recurrence-widget.js:1748 msgid "Add rule" msgstr "Añadir regla" #: media/js/recurrence-widget.js:1749 msgid "Add date" msgstr "Añadir fecha" #: media/js/recurrence-widget.js:1750 msgid "Remove" msgstr "Eliminar" #: media/js/recurrence-widget.js:1751 msgid "Calendar" msgstr "Calendario" #: media/js/recurrence.js:995 msgid "midnight" msgstr "Media noche" #: media/js/recurrence.js:996 msgid "noon" msgstr "Medio día" #: media/js/recurrence.js:997 msgid "on the %(items)s" msgstr "en %(items)s" #: media/js/recurrence.js:998 msgid "every %(number)s %(freq)s" msgstr "cada %(number)s %(freq)s" #: media/js/recurrence.js:999 msgid "each %(items)s" msgstr "cada %(items)s" #: media/js/recurrence.js:1000 msgid "occuring %(number)s time" msgstr "ocurre %(number)s " #: media/js/recurrence.js:1001 msgid "occuring %(number)s times" msgstr "ocurre %(number)s veces" #: media/js/recurrence.js:1002 msgid "until %(date)s" msgstr "hasta %(date)s" #: media/js/recurrence.js:1006 msgid "year" msgstr "año" #: media/js/recurrence.js:1006 msgid "month" msgstr "mes" #: media/js/recurrence.js:1006 msgid "week" msgstr "semana" #: media/js/recurrence.js:1006 msgid "day" msgstr "día" #: media/js/recurrence.js:1007 msgid "hour" msgstr "hora" #: media/js/recurrence.js:1007 msgid "minute" msgstr "minuto" #: media/js/recurrence.js:1007 msgid "second" msgstr "segundo" #: media/js/recurrence.js:1010 msgid "years" msgstr "años" #: media/js/recurrence.js:1010 msgid "months" msgstr "meses" #: media/js/recurrence.js:1010 msgid "weeks" msgstr "semanas" #: media/js/recurrence.js:1010 msgid "days" msgstr "días" #: media/js/recurrence.js:1011 msgid "hours" msgstr "horas" #: media/js/recurrence.js:1011 msgid "minutes" msgstr "minutos\t" #: media/js/recurrence.js:1011 msgid "seconds" msgstr "segundos" #: media/js/recurrence.js:1014 msgid "annually" msgstr "anualmente" #: media/js/recurrence.js:1014 msgid "monthly" msgstr "mensualmente" #: media/js/recurrence.js:1014 msgid "weekly" msgstr "semanalmente" #: media/js/recurrence.js:1014 msgid "daily" msgstr "diariamente" #: media/js/recurrence.js:1015 msgid "hourly" msgstr "cada hora" #: media/js/recurrence.js:1015 msgid "minutely" msgstr "cada minuto" #: media/js/recurrence.js:1015 msgid "secondly" msgstr "cada segundo" #: media/js/recurrence.js:1018 msgid "Monday" msgstr "Lunes" #: media/js/recurrence.js:1018 msgid "Tuesday" msgstr "Martes" #: media/js/recurrence.js:1018 msgid "Wednesday" msgstr "Miércoles" #: media/js/recurrence.js:1018 msgid "Thursday" msgstr "Jueves" #: media/js/recurrence.js:1019 msgid "Friday" msgstr "Viernes" #: media/js/recurrence.js:1019 msgid "Saturday" msgstr "Sábado" #: media/js/recurrence.js:1019 msgid "Sunday" msgstr "Domingo" #: media/js/recurrence.js:1022 msgid "Mon" msgstr "Lun" #: media/js/recurrence.js:1022 msgid "Tue" msgstr "Mar" #: media/js/recurrence.js:1022 msgid "Wed" msgstr "Mie" #: media/js/recurrence.js:1022 msgid "Thu" msgstr "Jue" #: media/js/recurrence.js:1023 msgid "Fri" msgstr "Vie" #: media/js/recurrence.js:1023 msgid "Sat" msgstr "Sab" #: media/js/recurrence.js:1023 msgid "Sun" msgstr "Dom" #: media/js/recurrence.js:1026 msgid "M" msgstr "M" #: media/js/recurrence.js:1026 msgid "T" msgstr "J" #: media/js/recurrence.js:1026 msgid "W" msgstr "X" #: media/js/recurrence.js:1027 msgid "F" msgstr "V" #: media/js/recurrence.js:1027 msgid "S" msgstr "S" #: media/js/recurrence.js:1030 msgid "first %(weekday)s" msgstr "primer %(weekday)s" #: media/js/recurrence.js:1031 msgid "second %(weekday)s" msgstr "segundo %(weekday)s" #: media/js/recurrence.js:1032 msgid "third %(weekday)s" msgstr "tercero %(weekday)s" #: media/js/recurrence.js:1032 msgid "fourth %(weekday)s" msgstr "cuarto %(weekday)s" #: media/js/recurrence.js:1033 media/js/recurrence.js.py:1041 msgid "last %(weekday)s" msgstr "último %(weekday)s" #: media/js/recurrence.js:1034 msgid "second last %(weekday)s" msgstr "segundo último %(weekday)s" #: media/js/recurrence.js:1035 msgid "third last %(weekday)s" msgstr "tercer último %(weekday)s" #: media/js/recurrence.js:1038 msgid "1st %(weekday)s" msgstr "1º %(weekday)s" #: media/js/recurrence.js:1039 msgid "2nd %(weekday)s" msgstr "2º %(weekday)s" #: media/js/recurrence.js:1040 msgid "3rd %(weekday)s" msgstr "3º %(weekday)s" #: media/js/recurrence.js:1042 msgid "2nd last %(weekday)s" msgstr "2º último %(weekday)s" #: media/js/recurrence.js:1043 msgid "3rd last %(weekday)s" msgstr "3º último %(weekday)s" #: media/js/recurrence.js:1046 msgid "January" msgstr "Enero" #: media/js/recurrence.js:1046 msgid "February" msgstr "Febrero" #: media/js/recurrence.js:1046 media/js/recurrence.js.py:1058 msgid "March" msgstr "Marzo" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1059 msgid "April" msgstr "Abril" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1053 #: media/js/recurrence.js:1059 msgid "May" msgstr "Mayo" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1059 msgid "June" msgstr "Junio" #: media/js/recurrence.js:1048 media/js/recurrence.js.py:1060 msgid "July" msgstr "Julio" #: media/js/recurrence.js:1048 msgid "August" msgstr "Agosto" #: media/js/recurrence.js:1048 msgid "September" msgstr "Septiembre" #: media/js/recurrence.js:1049 msgid "October" msgstr "Octubre" #: media/js/recurrence.js:1049 msgid "November" msgstr "Noviembre" #: media/js/recurrence.js:1049 msgid "December" msgstr "Diciembre" #: media/js/recurrence.js:1052 msgid "Jan" msgstr "Ene" #: media/js/recurrence.js:1052 msgid "Feb" msgstr "" #: media/js/recurrence.js:1052 msgid "Mar" msgstr "" #: media/js/recurrence.js:1053 msgid "Apr" msgstr "Abr" #: media/js/recurrence.js:1053 msgid "Jun" msgstr "" #: media/js/recurrence.js:1054 msgid "Jul" msgstr "" #: media/js/recurrence.js:1054 msgid "Aug" msgstr "Ago" #: media/js/recurrence.js:1054 msgid "Sep" msgstr "" #: media/js/recurrence.js:1055 msgid "Oct" msgstr "" #: media/js/recurrence.js:1055 msgid "Nov" msgstr "" #: media/js/recurrence.js:1055 msgid "Dec" msgstr "Dic" #: media/js/recurrence.js:1058 msgid "Jan." msgstr "Ene." #: media/js/recurrence.js:1058 msgid "Feb." msgstr "" #: media/js/recurrence.js:1060 msgid "Aug." msgstr "Ago." #: media/js/recurrence.js:1060 msgid "Sept." msgstr "" #: media/js/recurrence.js:1061 msgid "Oct." msgstr "" #: media/js/recurrence.js:1061 msgid "Nov." msgstr "" #: media/js/recurrence.js:1061 msgid "Dec." msgstr "Dic." #: media/js/recurrence.js:1064 msgid "a.m." msgstr "" #: media/js/recurrence.js:1064 msgid "p.m." msgstr "" #: media/js/recurrence.js:1065 msgid "AM" msgstr "" #: media/js/recurrence.js:1065 msgid "PM" msgstr "" django-recurrence-1.2.0/recurrence/locale/fr/0000755000076500000240000000000012554754171021354 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/fr/LC_MESSAGES/0000755000076500000240000000000012554754171023141 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/fr/LC_MESSAGES/django.mo0000644000076500000240000001040712554752650024742 0ustar cezarstaff00000000000000Uql014DTiy   *$4O4*     ! * 3 7 A E L P Y ] e i s z               ! ; I Z m          u x              & * 3 E O c g o t |    : C I<X %) 2?F MW]n  -BTj *-CB3&LQ%. G# 5$7+D/<JP;AR@2(N>T64 O'1:I?!"FK ,M=S 0)H 9E8U, 1st %(weekday)s2nd %(weekday)s2nd last %(weekday)s3rd %(weekday)s3rd last %(weekday)sAprAprilAugAugustDailyDecDecemberExclusionFebFebruaryFriFridayHourlyInclusionInvalid frequency.JanJanuaryJulJulyJunJuneMarMarchMax dates exceeded. The limit is %(limit)sMax exclusion dates exceeded. The limit is %(limit)sMax exclusion rules exceeded. The limit is %(limit)sMax rules exceeded. The limit is %(limit)sMayMinutelyMonMondayMonthlyNovNovemberOctOctoberSatSaturdaySecondlySepSeptemberSunSundayThuThursdayTueTuesdayWedWednesdayWeeklyYearlyannuallydailydayseach %(items)severy %(number)s %(freq)sfirst %(weekday)sfourth %(weekday)shourlyhourslast %(weekday)sminutelyminutesmonthlymonthsoccuring %(number)s timesoccuring onceon the %(items)ssecond %(weekday)ssecond last %(weekday)ssecondlysecondsthird %(weekday)sthird last %(weekday)suntil %(date)sweeklyweeksyearsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2014-11-26 15:26+0000 PO-Revision-Date: 2014-11-26 16:28+0100 Last-Translator: Olivier Le Brouster Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); X-Generator: Poedit 1.5.4 , 1er %(weekday)s2ème %(weekday)savant dernier %(weekday)s3ème %(weekday)s3ème dernier %(weekday)sAvravrilAoûaoûtQuotidienDécdécembreExclusionFévfévrierVenvendrediToutes les heuresInclusionFréquence invalideJanjanvierJuiljuilletJuinjuinMarmarsNombre maximum de dates dépassé. La limite est %(limit)sNombre maximum de dates éxclues dépassé. La limite est %(limit)sNombre maximum de règles d'exclusion déppassé. La limite est %(limit)sNombre maximum de règles dépassé. La limite est %(limit)smaiToutes les minutesLunlundiMensuelNovnovembreOctoctobreSamsamediToutes les secondesSepseptembreDimdimancheJeujeudiMarmardiMermercrediHebdomadaireAnnuelannuelquotidienjourschaque %(items)stous les %(number)s %(freq)spremier %(weekday)squatrième %(weekday)stoutes les heuresheuresdernier %(weekday)stoutes les minutesminutesmensuellementmoisse répète %(number)s foisse répète une foispour le %(items)sdeuxième %(weekday)savant dernier %(weekday)stoutes les secondessecondestroisième %(weekday)stroisième dernier %(weekday)sjusqu'à %(date)shebdomadairesemainesannéesdjango-recurrence-1.2.0/recurrence/locale/fr/LC_MESSAGES/django.po0000644000076500000240000001470412554752650024751 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-26 15:26+0000\n" "PO-Revision-Date: 2014-11-26 16:28+0100\n" "Last-Translator: Olivier Le Brouster \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 1.5.4\n" #: base.py:1087 msgid "annually" msgstr "annuel" #: base.py:1087 msgid "monthly" msgstr "mensuellement" #: base.py:1087 msgid "weekly" msgstr "hebdomadaire" #: base.py:1087 msgid "daily" msgstr "quotidien" #: base.py:1088 msgid "hourly" msgstr "toutes les heures" #: base.py:1088 msgid "minutely" msgstr "toutes les minutes" #: base.py:1088 msgid "secondly" msgstr "toutes les secondes" #: base.py:1091 msgid "years" msgstr "années" #: base.py:1091 msgid "months" msgstr "mois" #: base.py:1091 msgid "weeks" msgstr "semaines" #: base.py:1091 msgid "days" msgstr "jours" #: base.py:1092 msgid "hours" msgstr "heures" #: base.py:1092 msgid "minutes" msgstr "minutes" #: base.py:1092 msgid "seconds" msgstr "secondes" #: base.py:1097 #, python-format msgid "1st %(weekday)s" msgstr "1er %(weekday)s" #: base.py:1098 #, python-format msgid "2nd %(weekday)s" msgstr "2ème %(weekday)s" #: base.py:1099 #, python-format msgid "3rd %(weekday)s" msgstr "3ème %(weekday)s" #: base.py:1100 base.py:1120 #, python-format msgid "last %(weekday)s" msgstr "dernier %(weekday)s" #: base.py:1101 #, python-format msgid "2nd last %(weekday)s" msgstr "avant dernier %(weekday)s" #: base.py:1102 #, python-format msgid "3rd last %(weekday)s" msgstr "3ème dernier %(weekday)s" #: base.py:1105 msgid "Mon" msgstr "Lun" #: base.py:1105 msgid "Tue" msgstr "Mar" #: base.py:1105 msgid "Wed" msgstr "Mer" #: base.py:1106 msgid "Thu" msgstr "Jeu" #: base.py:1106 msgid "Fri" msgstr "Ven" #: base.py:1106 msgid "Sat" msgstr "Sam" #: base.py:1106 msgid "Sun" msgstr "Dim" #: base.py:1109 msgid "Jan" msgstr "Jan" #: base.py:1109 msgid "Feb" msgstr "Fév" #: base.py:1109 msgid "Mar" msgstr "Mar" #: base.py:1109 msgid "Apr" msgstr "Avr" #: base.py:1110 msgid "Jun" msgstr "Juin" #: base.py:1110 msgid "Jul" msgstr "Juil" #: base.py:1110 msgid "Aug" msgstr "Aoû" #: base.py:1111 msgid "Sep" msgstr "Sep" #: base.py:1111 msgid "Oct" msgstr "Oct" #: base.py:1111 msgid "Nov" msgstr "Nov" #: base.py:1111 msgid "Dec" msgstr "Déc" #: base.py:1116 #, python-format msgid "first %(weekday)s" msgstr "premier %(weekday)s" #: base.py:1117 #, python-format msgid "second %(weekday)s" msgstr "deuxième %(weekday)s" #: base.py:1118 #, python-format msgid "third %(weekday)s" msgstr "troisième %(weekday)s" #: base.py:1119 #, python-format msgid "fourth %(weekday)s" msgstr "quatrième %(weekday)s" #: base.py:1121 #, python-format msgid "second last %(weekday)s" msgstr "avant dernier %(weekday)s" #: base.py:1122 #, python-format msgid "third last %(weekday)s" msgstr "troisième dernier %(weekday)s" #: base.py:1125 choices.py:17 msgid "Monday" msgstr "lundi" #: base.py:1125 choices.py:18 msgid "Tuesday" msgstr "mardi" #: base.py:1125 choices.py:19 msgid "Wednesday" msgstr "mercredi" #: base.py:1126 choices.py:20 msgid "Thursday" msgstr "jeudi" #: base.py:1126 choices.py:21 msgid "Friday" msgstr "vendredi" #: base.py:1126 choices.py:22 msgid "Saturday" msgstr "samedi" #: base.py:1126 choices.py:23 msgid "Sunday" msgstr "dimanche" #: base.py:1129 choices.py:27 msgid "January" msgstr "janvier" #: base.py:1129 choices.py:28 msgid "February" msgstr "février" #: base.py:1129 choices.py:29 msgid "March" msgstr "mars" #: base.py:1129 choices.py:30 msgid "April" msgstr "avril" #: base.py:1130 choices.py:32 msgid "June" msgstr "juin" #: base.py:1130 choices.py:33 msgid "July" msgstr "juillet" #: base.py:1130 choices.py:34 msgid "August" msgstr "août" #: base.py:1131 choices.py:35 msgid "September" msgstr "septembre" #: base.py:1131 choices.py:36 msgid "October" msgstr "octobre" #: base.py:1131 choices.py:37 msgid "November" msgstr "novembre" #: base.py:1131 choices.py:38 msgid "December" msgstr "décembre" #: base.py:1149 base.py:1166 base.py:1177 base.py:1190 base.py:1208 msgid ", " msgstr ", " #: base.py:1155 #, python-format msgid "every %(number)s %(freq)s" msgstr "tous les %(number)s %(freq)s" #: base.py:1169 base.py:1193 #, python-format msgid "each %(items)s" msgstr "chaque %(items)s" #: base.py:1172 base.py:1181 base.py:1185 #, python-format msgid "on the %(items)s" msgstr "pour le %(items)s" #: base.py:1200 msgid "occuring once" msgstr "se répète une fois" #: base.py:1202 #, python-format msgid "occuring %(number)s times" msgstr "se répète %(number)s fois" #: base.py:1205 #, python-format msgid "until %(date)s" msgstr "jusqu'à %(date)s" #: choices.py:7 msgid "Secondly" msgstr "Toutes les secondes" #: choices.py:8 msgid "Minutely" msgstr "Toutes les minutes" #: choices.py:9 msgid "Hourly" msgstr "Toutes les heures" #: choices.py:10 msgid "Daily" msgstr "Quotidien" #: choices.py:11 msgid "Weekly" msgstr "Hebdomadaire" #: choices.py:12 msgid "Monthly" msgstr "Mensuel" #: choices.py:13 msgid "Yearly" msgstr "Annuel" #: choices.py:31 msgid "May" msgstr "mai" #: choices.py:44 msgid "Inclusion" msgstr "Inclusion" #: choices.py:45 msgid "Exclusion" msgstr "Exclusion" #: forms.py:72 msgid "Invalid frequency." msgstr "Fréquence invalide" #: forms.py:74 #, python-format msgid "Max rules exceeded. The limit is %(limit)s" msgstr "Nombre maximum de règles dépassé. La limite est %(limit)s" #: forms.py:76 #, python-format msgid "Max exclusion rules exceeded. The limit is %(limit)s" msgstr "Nombre maximum de règles d'exclusion déppassé. La limite est %(limit)s" #: forms.py:78 #, python-format msgid "Max dates exceeded. The limit is %(limit)s" msgstr "Nombre maximum de dates dépassé. La limite est %(limit)s" #: forms.py:80 #, python-format msgid "Max exclusion dates exceeded. The limit is %(limit)s" msgstr "Nombre maximum de dates éxclues dépassé. La limite est %(limit)s" #~ msgid "year" #~ msgstr "année" #~ msgid "month" #~ msgstr "mois" #~ msgid "week" #~ msgstr "semaine" #~ msgid "day" #~ msgstr "jour" #~ msgid "hour" #~ msgstr "heure" #~ msgid "minute" #~ msgstr "minute" #~ msgid "second" #~ msgstr "seconde" django-recurrence-1.2.0/recurrence/locale/fr/LC_MESSAGES/djangojs.mo0000644000076500000240000001220112554752650025271 0ustar cezarstaff00000000000000ul     & ; K N W ` d j n s z                   " ' + 0 4 : > E [ _ d m                % ) 2 J O S [ r x |            ' , 3 9 C T ] d m u {          "*<Sbgntyi %7Qcfw     (,57;@HMUZ_chlrtx} #%)/17;ACLPY[` g q}  $+2EMRV^ch #BT \iryJB='t@TpM ($F?NQ&O [`!#%,-fu0 "65Yc^\DmUqrsXKEA9> ;2e4nhH]31*VaPojSGWIkdl Rg: L<7)C/b._Z8+i1st %(weekday)s2nd %(weekday)s2nd last %(weekday)s3rd %(weekday)s3rd last %(weekday)s4th %(weekday)sAMAdd dateAdd ruleAprAprilAugAug.AugustCalendarDateDecDec.DecemberEachEveryExclude these occurencesExclude this dateFebFeb.FebruaryFrequencyFriFridayFriday first letterFJanJan.JanuaryJulJulyJunJuneMarMarchMonMondayMonday first letterMNovNov.NovemberOccurs %(number)s timeOccurs %(number)s timesOctOct.OctoberOn thePMRemoveRepeat untilSatSaturdaySaturday first letterSSepSept.SeptemberSunSundaySunday first letterSThuThursdayThursday first letterTTimeTueTuesdayTuesday first letterTUntilWedWednesdayWednesday first letterWa.m.annuallydailydate%l, %F %j, %Ydaydayseach %(items)severy %(number)s %(freq)sexcludingfirst %(weekday)sfourth %(weekday)shourhourlyhoursincludinglast %(weekday)smidnightminuteminutelyminutesmonthmonth nameMaymonthlymonthsnoonoccuring %(number)s timeoccuring %(number)s timeson the %(items)sp.m.secondsecond %(weekday)ssecond last %(weekday)ssecondlysecondsthird %(weekday)sthird last %(weekday)suntil %(date)sweekweeklyweeksyearyearsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2014-11-26 15:26+0000 PO-Revision-Date: 2014-11-26 16:28+0100 Last-Translator: Olivier Le Brouster Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 1.5.4 1er %(weekday)s2ème %(weekday)savant dernier %(weekday)s3ème %(weekday)s3ème dernier %(weekday)s4ème %(weekday)sAMAjouter une dateAjouter une règleAvravrilAoûAoû.aoûtCalendrierDateDécDéc.décembreChaqueTou(te)s lesExclure ces occurrencesExclure cette dateFévFév.févrierFréquenceVenvendrediVJanJan.janvierJuiljuilletJuinjuinMarmarsLunlundiLNovNov.novembreSe répète %(number)s foisSe répète %(number)s foisOctOct.octobrePour lePMEnleverRépéter jusqu'àSamsamediSSepSept.septembreDimdimancheDJeujeudiJHeureMarmardiMJusqu'auMermercrediMa.m.annuelquotidien%l %j %F %Yjourjourschaque %(items)stou(te)s les %(number)s %(freq)shormispremier %(weekday)squatrième %(weekday)sheuretoutes les heuresheuresavecdernier %(weekday)sminuitminutetoutes les minutesminutesmoismaimensuelmoismidise répète %(number)s foisse répète %(number)s foispour le %(items)sp.m.secondedeuxième %(weekday)savant dernier %(weekday)stoutes les secondessecondestroisième %(weekday)stroisième dernier %(weekday)sjusqu'au %(date)ssemainehebdomadairesemainesannéeannéesdjango-recurrence-1.2.0/recurrence/locale/fr/LC_MESSAGES/djangojs.po0000644000076500000240000002451212554752650025304 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-26 15:26+0000\n" "PO-Revision-Date: 2014-11-26 16:28+0100\n" "Last-Translator: Olivier Le Brouster \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" #: static/recurrence/js/recurrence-widget.js:1610 msgctxt "date" msgid "%l, %F %j, %Y" msgstr "%l %j %F %Y" #: static/recurrence/js/recurrence-widget.js:1775 msgid "including" msgstr "avec" #: static/recurrence/js/recurrence-widget.js:1775 msgid "excluding" msgstr "hormis" #: static/recurrence/js/recurrence-widget.js:1779 msgid "Frequency" msgstr "Fréquence" #: static/recurrence/js/recurrence-widget.js:1780 msgid "On the" msgstr "Pour le" #: static/recurrence/js/recurrence-widget.js:1781 msgid "Each" msgstr "Chaque" #: static/recurrence/js/recurrence-widget.js:1782 msgid "Every" msgstr "Tou(te)s les" #: static/recurrence/js/recurrence-widget.js:1783 msgid "Until" msgstr "Jusqu'au" #: static/recurrence/js/recurrence-widget.js:1784 msgid "Occurs %(number)s time" msgstr "Se répète %(number)s fois" #: static/recurrence/js/recurrence-widget.js:1785 msgid "Occurs %(number)s times" msgstr "Se répète %(number)s fois" #: static/recurrence/js/recurrence-widget.js:1786 msgid "Date" msgstr "Date" #: static/recurrence/js/recurrence-widget.js:1787 msgid "Time" msgstr "Heure" #: static/recurrence/js/recurrence-widget.js:1788 msgid "Repeat until" msgstr "Répéter jusqu'à" #: static/recurrence/js/recurrence-widget.js:1789 msgid "Exclude these occurences" msgstr "Exclure ces occurrences" #: static/recurrence/js/recurrence-widget.js:1790 msgid "Exclude this date" msgstr "Exclure cette date" #: static/recurrence/js/recurrence-widget.js:1791 msgid "Add rule" msgstr "Ajouter une règle" #: static/recurrence/js/recurrence-widget.js:1792 msgid "Add date" msgstr "Ajouter une date" #: static/recurrence/js/recurrence-widget.js:1793 msgid "Remove" msgstr "Enlever" #: static/recurrence/js/recurrence-widget.js:1794 msgid "Calendar" msgstr "Calendrier" #: static/recurrence/js/recurrence.js:1006 msgid "midnight" msgstr "minuit" #: static/recurrence/js/recurrence.js:1007 msgid "noon" msgstr "midi" #: static/recurrence/js/recurrence.js:1008 msgid "on the %(items)s" msgstr "pour le %(items)s" #: static/recurrence/js/recurrence.js:1009 msgid "every %(number)s %(freq)s" msgstr "tou(te)s les %(number)s %(freq)s" #: static/recurrence/js/recurrence.js:1010 msgid "each %(items)s" msgstr "chaque %(items)s" #: static/recurrence/js/recurrence.js:1011 msgid "occuring %(number)s time" msgstr "se répète %(number)s fois" #: static/recurrence/js/recurrence.js:1012 msgid "occuring %(number)s times" msgstr "se répète %(number)s fois" #: static/recurrence/js/recurrence.js:1013 msgid "until %(date)s" msgstr "jusqu'au %(date)s" #: static/recurrence/js/recurrence.js:1017 msgid "year" msgstr "année" #: static/recurrence/js/recurrence.js:1017 msgid "month" msgstr "mois" #: static/recurrence/js/recurrence.js:1017 msgid "week" msgstr "semaine" #: static/recurrence/js/recurrence.js:1017 msgid "day" msgstr "jour" #: static/recurrence/js/recurrence.js:1018 msgid "hour" msgstr "heure" #: static/recurrence/js/recurrence.js:1018 msgid "minute" msgstr "minute" #: static/recurrence/js/recurrence.js:1018 msgid "second" msgstr "seconde" #: static/recurrence/js/recurrence.js:1021 msgid "years" msgstr "années" #: static/recurrence/js/recurrence.js:1021 msgid "months" msgstr "mois" #: static/recurrence/js/recurrence.js:1021 msgid "weeks" msgstr "semaines" #: static/recurrence/js/recurrence.js:1021 msgid "days" msgstr "jours" #: static/recurrence/js/recurrence.js:1022 msgid "hours" msgstr "heures" #: static/recurrence/js/recurrence.js:1022 msgid "minutes" msgstr "minutes" #: static/recurrence/js/recurrence.js:1022 msgid "seconds" msgstr "secondes" #: static/recurrence/js/recurrence.js:1025 msgid "annually" msgstr "annuel" #: static/recurrence/js/recurrence.js:1025 msgid "monthly" msgstr "mensuel" #: static/recurrence/js/recurrence.js:1025 msgid "weekly" msgstr "hebdomadaire" #: static/recurrence/js/recurrence.js:1025 msgid "daily" msgstr "quotidien" #: static/recurrence/js/recurrence.js:1026 msgid "hourly" msgstr "toutes les heures" #: static/recurrence/js/recurrence.js:1026 msgid "minutely" msgstr "toutes les minutes" #: static/recurrence/js/recurrence.js:1026 msgid "secondly" msgstr "toutes les secondes" #: static/recurrence/js/recurrence.js:1029 msgid "Monday" msgstr "lundi" #: static/recurrence/js/recurrence.js:1029 msgid "Tuesday" msgstr "mardi" #: static/recurrence/js/recurrence.js:1029 msgid "Wednesday" msgstr "mercredi" #: static/recurrence/js/recurrence.js:1029 msgid "Thursday" msgstr "jeudi" #: static/recurrence/js/recurrence.js:1030 msgid "Friday" msgstr "vendredi" #: static/recurrence/js/recurrence.js:1030 msgid "Saturday" msgstr "samedi" #: static/recurrence/js/recurrence.js:1030 msgid "Sunday" msgstr "dimanche" #: static/recurrence/js/recurrence.js:1033 msgid "Mon" msgstr "Lun" #: static/recurrence/js/recurrence.js:1033 msgid "Tue" msgstr "Mar" #: static/recurrence/js/recurrence.js:1033 msgid "Wed" msgstr "Mer" #: static/recurrence/js/recurrence.js:1033 msgid "Thu" msgstr "Jeu" #: static/recurrence/js/recurrence.js:1034 msgid "Fri" msgstr "Ven" #: static/recurrence/js/recurrence.js:1034 msgid "Sat" msgstr "Sam" #: static/recurrence/js/recurrence.js:1034 msgid "Sun" msgstr "Dim" #: static/recurrence/js/recurrence.js:1037 msgctxt "Monday first letter" msgid "M" msgstr "L" #: static/recurrence/js/recurrence.js:1038 msgctxt "Tuesday first letter" msgid "T" msgstr "M" #: static/recurrence/js/recurrence.js:1039 msgctxt "Wednesday first letter" msgid "W" msgstr "M" #: static/recurrence/js/recurrence.js:1040 msgctxt "Thursday first letter" msgid "T" msgstr "J" #: static/recurrence/js/recurrence.js:1041 msgctxt "Friday first letter" msgid "F" msgstr "V" #: static/recurrence/js/recurrence.js:1042 msgctxt "Saturday first letter" msgid "S" msgstr "S" #: static/recurrence/js/recurrence.js:1043 msgctxt "Sunday first letter" msgid "S" msgstr "D" #: static/recurrence/js/recurrence.js:1046 msgid "first %(weekday)s" msgstr "premier %(weekday)s" #: static/recurrence/js/recurrence.js:1047 msgid "second %(weekday)s" msgstr "deuxième %(weekday)s" #: static/recurrence/js/recurrence.js:1048 msgid "third %(weekday)s" msgstr "troisième %(weekday)s" #: static/recurrence/js/recurrence.js:1049 msgid "fourth %(weekday)s" msgstr "quatrième %(weekday)s" #: static/recurrence/js/recurrence.js:1050 #: static/recurrence/js/recurrence.js:1059 msgid "last %(weekday)s" msgstr "dernier %(weekday)s" #: static/recurrence/js/recurrence.js:1051 msgid "second last %(weekday)s" msgstr "avant dernier %(weekday)s" #: static/recurrence/js/recurrence.js:1052 msgid "third last %(weekday)s" msgstr "troisième dernier %(weekday)s" #: static/recurrence/js/recurrence.js:1055 msgid "1st %(weekday)s" msgstr "1er %(weekday)s" #: static/recurrence/js/recurrence.js:1056 msgid "2nd %(weekday)s" msgstr "2ème %(weekday)s" #: static/recurrence/js/recurrence.js:1057 msgid "3rd %(weekday)s" msgstr "3ème %(weekday)s" #: static/recurrence/js/recurrence.js:1058 msgid "4th %(weekday)s" msgstr "4ème %(weekday)s" #: static/recurrence/js/recurrence.js:1060 msgid "2nd last %(weekday)s" msgstr "avant dernier %(weekday)s" #: static/recurrence/js/recurrence.js:1061 msgid "3rd last %(weekday)s" msgstr "3ème dernier %(weekday)s" #: static/recurrence/js/recurrence.js:1064 msgid "January" msgstr "janvier" #: static/recurrence/js/recurrence.js:1064 msgid "February" msgstr "février" #: static/recurrence/js/recurrence.js:1064 #: static/recurrence/js/recurrence.js:1076 msgid "March" msgstr "mars" #: static/recurrence/js/recurrence.js:1065 #: static/recurrence/js/recurrence.js:1077 msgid "April" msgstr "avril" #: static/recurrence/js/recurrence.js:1065 #: static/recurrence/js/recurrence.js:1071 #: static/recurrence/js/recurrence.js:1077 msgctxt "month name" msgid "May" msgstr "mai" #: static/recurrence/js/recurrence.js:1065 #: static/recurrence/js/recurrence.js:1077 msgid "June" msgstr "juin" #: static/recurrence/js/recurrence.js:1066 #: static/recurrence/js/recurrence.js:1078 msgid "July" msgstr "juillet" #: static/recurrence/js/recurrence.js:1066 msgid "August" msgstr "août" #: static/recurrence/js/recurrence.js:1066 msgid "September" msgstr "septembre" #: static/recurrence/js/recurrence.js:1067 msgid "October" msgstr "octobre" #: static/recurrence/js/recurrence.js:1067 msgid "November" msgstr "novembre" #: static/recurrence/js/recurrence.js:1067 msgid "December" msgstr "décembre" #: static/recurrence/js/recurrence.js:1070 msgid "Jan" msgstr "Jan" #: static/recurrence/js/recurrence.js:1070 msgid "Feb" msgstr "Fév" #: static/recurrence/js/recurrence.js:1070 msgid "Mar" msgstr "Mar" #: static/recurrence/js/recurrence.js:1071 msgid "Apr" msgstr "Avr" #: static/recurrence/js/recurrence.js:1071 msgid "Jun" msgstr "Juin" #: static/recurrence/js/recurrence.js:1072 msgid "Jul" msgstr "Juil" #: static/recurrence/js/recurrence.js:1072 msgid "Aug" msgstr "Aoû" #: static/recurrence/js/recurrence.js:1072 msgid "Sep" msgstr "Sep" #: static/recurrence/js/recurrence.js:1073 msgid "Oct" msgstr "Oct" #: static/recurrence/js/recurrence.js:1073 msgid "Nov" msgstr "Nov" #: static/recurrence/js/recurrence.js:1073 msgid "Dec" msgstr "Déc" #: static/recurrence/js/recurrence.js:1076 msgid "Jan." msgstr "Jan." #: static/recurrence/js/recurrence.js:1076 msgid "Feb." msgstr "Fév." #: static/recurrence/js/recurrence.js:1078 msgid "Aug." msgstr "Aoû." #: static/recurrence/js/recurrence.js:1078 msgid "Sept." msgstr "Sept." #: static/recurrence/js/recurrence.js:1079 msgid "Oct." msgstr "Oct." #: static/recurrence/js/recurrence.js:1079 msgid "Nov." msgstr "Nov." #: static/recurrence/js/recurrence.js:1079 msgid "Dec." msgstr "Déc." #: static/recurrence/js/recurrence.js:1082 msgid "a.m." msgstr "a.m." #: static/recurrence/js/recurrence.js:1082 msgid "p.m." msgstr "p.m." #: static/recurrence/js/recurrence.js:1083 msgid "AM" msgstr "AM" #: static/recurrence/js/recurrence.js:1083 msgid "PM" msgstr "PM" django-recurrence-1.2.0/recurrence/locale/nl/0000755000076500000240000000000012554754171021356 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/nl/LC_MESSAGES/0000755000076500000240000000000012554754171023143 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/locale/nl/LC_MESSAGES/django.mo0000644000076500000240000001056312554752650024747 0ustar cezarstaff00000000000000[#'-18>B KUYbfm t~*44 *N y }                       % ) . = W i |               7 @ H Z q       8 G V o ~             !&*/309AjC5& *69 AMQZ^fi r      *=AINb iu}   * FS XbhmD&Q$A><[:LIZ?MWU5 G2HV"/1+FK *-S 7 =#@) T8BJP.OC0%Y ;R9X43,6('!EN1st %(weekday)s2nd %(weekday)s2nd last %(weekday)s3rd %(weekday)s3rd last %(weekday)sAprAprilAugAugustDailyDecDecemberExclusionFebFebruaryFriFridayHourlyInclusionInvalid frequency.JanJanuaryJulJulyJunJuneMarMarchMax dates exceeded. The limit is %(limit)sMax exclusion dates exceeded. The limit is %(limit)sMax exclusion rules exceeded. The limit is %(limit)sMax rules exceeded. The limit is %(limit)sMayMinutelyMonMondayMonthlyNovNovemberOctOctoberSatSaturdaySecondlySepSeptemberSunSundayThuThursdayTueTuesdayWedWednesdayWeeklyYearlyannuallydailydaydayseach %(items)severy %(number)s %(freq)sfirst %(weekday)sfourth %(weekday)shourhourlyhourslast %(weekday)sminuteminutelyminutesmonthmonthlymonthsoccuring %(number)s timesoccuring onceon the %(items)ssecondsecond %(weekday)ssecond last %(weekday)ssecondlysecondsthird %(weekday)sthird last %(weekday)suntil %(date)sweekweeklyweeksyearyearsProject-Id-Version: Django Recurrence 1.0.1 Report-Msgid-Bugs-To: POT-Creation-Date: 2009-02-05 13:25-0600 PO-Revision-Date: 2014-06-25 21:51+0100 Last-Translator: Bart Heesink Language-Team: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 1.5.7 Language: Dutch X-Poedit-SourceCharset: UTF-8 1e %(weekday)s2e %(weekday)s2 na laatste %(weekday)s3e %(weekday)s3 na laatste %(weekday)sapraprilaugaugustusDagelijksdecdecemberUitgezonderdfebfebruarivrvrijdagElk uurInclusiefOngeldige herhaling.janjanuarijuljulijunjunimrtmaartMaximaal aantal van %(limit)s data overschreden.Maximaal aantal van %(limit)s uitgezonderde data is overschreden.Maximaal aantal van %(limit)s uitgezonderde regels is overschreden.Maximaal aantal van %(limit)s regels is overschreden.meiElke minuutmamaandagMaandelijksnovnovemberoktoktoberzazaterdagElke secondesepseptemberzozondagdodonderdagdidinsdagwowoensdagWekelijksJaarlijksjaarlijksdagelijksdagdageniedere %(items)selke %(number)s %(freq)seerste %(weekday)svierde %(weekday)suurelk uururenlaatste %(weekday)sminuutelke minuutminutenmaandmaandelijksmaanden%(number)s keer herhalenéénmaligop de %(items)ssecondetweede %(weekday)stwee na laatste %(weekday)selke secondesecondenderde %(weekday)sdrie na laatste %(weekday)stot %(date)sweekwekelijkswekenjaarjarendjango-recurrence-1.2.0/recurrence/locale/nl/LC_MESSAGES/django.po0000644000076500000240000001451112554752650024747 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: Django Recurrence 1.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-02-05 13:25-0600\n" "PO-Revision-Date: 2014-06-25 21:51+0100\n" "Last-Translator: Bart Heesink \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.7\n" "Language: Dutch\n" "X-Poedit-SourceCharset: UTF-8\n" #: base.py:871 msgid "annually" msgstr "jaarlijks" #: base.py:871 msgid "monthly" msgstr "maandelijks" #: base.py:871 msgid "weekly" msgstr "wekelijks" #: base.py:871 msgid "daily" msgstr "dagelijks" #: base.py:872 msgid "hourly" msgstr "elk uur" #: base.py:872 msgid "minutely" msgstr "elke minuut" #: base.py:872 msgid "secondly" msgstr "elke seconde" #: base.py:875 msgid "year" msgstr "jaar" #: base.py:875 msgid "month" msgstr "maand" #: base.py:875 msgid "week" msgstr "week" #: base.py:875 msgid "day" msgstr "dag" #: base.py:876 msgid "hour" msgstr "uur" #: base.py:876 msgid "minute" msgstr "minuut" #: base.py:876 msgid "second" msgstr "seconde" #: base.py:879 msgid "years" msgstr "jaren" #: base.py:879 msgid "months" msgstr "maanden" #: base.py:879 msgid "weeks" msgstr "weken" #: base.py:879 msgid "days" msgstr "dagen" #: base.py:880 msgid "hours" msgstr "uren" #: base.py:880 msgid "minutes" msgstr "minuten" #: base.py:880 msgid "seconds" msgstr "seconden" #: base.py:885 #, python-format msgid "1st %(weekday)s" msgstr "1e %(weekday)s" #: base.py:886 #, python-format msgid "2nd %(weekday)s" msgstr "2e %(weekday)s" #: base.py:887 #, python-format msgid "3rd %(weekday)s" msgstr "3e %(weekday)s" #: base.py:888 base.py:907 #, python-format msgid "last %(weekday)s" msgstr "laatste %(weekday)s" #: base.py:889 #, python-format msgid "2nd last %(weekday)s" msgstr "2 na laatste %(weekday)s" #: base.py:890 #, python-format msgid "3rd last %(weekday)s" msgstr "3 na laatste %(weekday)s" #: base.py:893 msgid "Mon" msgstr "ma" #: base.py:893 msgid "Tue" msgstr "di" #: base.py:893 msgid "Wed" msgstr "wo" #: base.py:894 msgid "Thu" msgstr "do" #: base.py:894 msgid "Fri" msgstr "vr" #: base.py:894 msgid "Sat" msgstr "za" #: base.py:894 msgid "Sun" msgstr "zo" #: base.py:897 msgid "Jan" msgstr "jan" #: base.py:897 msgid "Feb" msgstr "feb" #: base.py:897 msgid "Mar" msgstr "mrt" #: base.py:897 msgid "Apr" msgstr "apr" #: base.py:898 base.py:917 choices.py:31 msgid "May" msgstr "mei" #: base.py:898 msgid "Jun" msgstr "jun" #: base.py:898 msgid "Jul" msgstr "jul" #: base.py:898 msgid "Aug" msgstr "aug" #: base.py:899 msgid "Sep" msgstr "sep" #: base.py:899 msgid "Oct" msgstr "okt" #: base.py:899 msgid "Nov" msgstr "nov" #: base.py:899 msgid "Dec" msgstr "dec" #: base.py:904 #, python-format msgid "first %(weekday)s" msgstr "eerste %(weekday)s" #: base.py:905 #, python-format msgid "second %(weekday)s" msgstr "tweede %(weekday)s" #: base.py:906 #, python-format msgid "third %(weekday)s" msgstr "derde %(weekday)s" #: base.py:906 #, python-format msgid "fourth %(weekday)s" msgstr "vierde %(weekday)s" #: base.py:908 #, python-format msgid "second last %(weekday)s" msgstr "twee na laatste %(weekday)s" #: base.py:909 #, python-format msgid "third last %(weekday)s" msgstr "drie na laatste %(weekday)s" #: base.py:912 choices.py:17 msgid "Monday" msgstr "maandag" #: base.py:912 choices.py:18 msgid "Tuesday" msgstr "dinsdag" #: base.py:912 choices.py:19 msgid "Wednesday" msgstr "woensdag" #: base.py:913 choices.py:20 msgid "Thursday" msgstr "donderdag" #: base.py:913 choices.py:21 msgid "Friday" msgstr "vrijdag" #: base.py:913 choices.py:22 msgid "Saturday" msgstr "zaterdag" #: base.py:913 choices.py:23 msgid "Sunday" msgstr "zondag" #: base.py:916 choices.py:27 msgid "January" msgstr "januari" #: base.py:916 choices.py:28 msgid "February" msgstr "februari" #: base.py:916 choices.py:29 msgid "March" msgstr "maart" #: base.py:916 choices.py:30 msgid "April" msgstr "april" #: base.py:917 choices.py:32 msgid "June" msgstr "juni" #: base.py:917 choices.py:33 msgid "July" msgstr "juli" #: base.py:917 choices.py:34 msgid "August" msgstr "augustus" #: base.py:918 choices.py:35 msgid "September" msgstr "september" #: base.py:918 choices.py:36 msgid "October" msgstr "oktober" #: base.py:918 choices.py:37 msgid "November" msgstr "november" #: base.py:918 choices.py:38 msgid "December" msgstr "december" #: base.py:936 base.py:951 base.py:974 base.py:992 msgid ", " msgstr "" #: base.py:942 #, python-format msgid "every %(number)s %(freq)s" msgstr "elke %(number)s %(freq)s" #: base.py:953 base.py:977 #, python-format msgid "each %(items)s" msgstr "iedere %(items)s" #: base.py:956 base.py:965 base.py:969 #, python-format msgid "on the %(items)s" msgstr "op de %(items)s" #: base.py:984 msgid "occuring once" msgstr "éénmalig" #: base.py:986 #, python-format msgid "occuring %(number)s times" msgstr "%(number)s keer herhalen" #: base.py:989 #, python-format msgid "until %(date)s" msgstr "tot %(date)s" #: choices.py:7 msgid "Secondly" msgstr "Elke seconde" #: choices.py:8 msgid "Minutely" msgstr "Elke minuut" #: choices.py:9 msgid "Hourly" msgstr "Elk uur" #: choices.py:10 msgid "Daily" msgstr "Dagelijks" #: choices.py:11 msgid "Weekly" msgstr "Wekelijks" #: choices.py:12 msgid "Monthly" msgstr "Maandelijks" #: choices.py:13 msgid "Yearly" msgstr "Jaarlijks" #: choices.py:44 msgid "Inclusion" msgstr "Inclusief" #: choices.py:45 msgid "Exclusion" msgstr "Uitgezonderd" #: forms.py:64 msgid "Invalid frequency." msgstr "Ongeldige herhaling." #: forms.py:66 #, python-format msgid "Max rules exceeded. The limit is %(limit)s" msgstr "Maximaal aantal van %(limit)s regels is overschreden." #: forms.py:68 #, python-format msgid "Max exclusion rules exceeded. The limit is %(limit)s" msgstr "Maximaal aantal van %(limit)s uitgezonderde regels is overschreden." #: forms.py:70 #, python-format msgid "Max dates exceeded. The limit is %(limit)s" msgstr "Maximaal aantal van %(limit)s data overschreden." #: forms.py:72 #, python-format msgid "Max exclusion dates exceeded. The limit is %(limit)s" msgstr "Maximaal aantal van %(limit)s uitgezonderde data is overschreden." django-recurrence-1.2.0/recurrence/locale/nl/LC_MESSAGES/djangojs.mo0000644000076500000240000001127112554752650025301 0ustar cezarstaff00000000000000m@ A Q a v                    # ( 1 ; ? F J O W [ ` d i k o u y }                   ! % . 3 7 ? E G K U ^ d h m |             ! ( - F ` q x               $*.2;DJNR[bhw 48<D J Vbdgpt y     ,?CK S] q{     " > KXj   NJeT [MU/0h=`9_WI a ] ?g:';VXb\K.!#2G &fZLD3%Y617E^*mkSP"R8>+l$jid5Oc4)C@,AH <(BQ-F1st %(weekday)s2nd %(weekday)s2nd last %(weekday)s3rd %(weekday)s3rd last %(weekday)sAdd dateAdd ruleAprAprilAugAug.AugustCalendarDateDecDec.DecemberEachEveryExclude these occurencesExclude this dateFFebFeb.FebruaryFrequencyFriFridayJanJan.JanuaryJulJulyJunJuneMMarMarchMayMonMondayNovNov.NovemberOccurs %(number)s timeOccurs %(number)s timesOctOct.OctoberOn theRemoveRepeat untilSSatSaturdaySepSept.SeptemberSunSundayTThuThursdayTimeTueTuesdayUntilWWedWednesdayannuallydailydaydayseach %(items)severy %(number)s %(freq)sexcludingfirst %(weekday)sfourth %(weekday)shourhourlyhoursincludinglast %(weekday)smidnightminuteminutelyminutesmonthmonthlymonthsnoonoccuring %(number)s timeoccuring %(number)s timeson the %(items)ssecondsecond %(weekday)ssecond last %(weekday)ssecondlysecondsthird %(weekday)sthird last %(weekday)suntil %(date)sweekweeklyweeksyearyearsProject-Id-Version: Django Recurrence 1.0.1 Report-Msgid-Bugs-To: bheesink@leukeleu.nl POT-Creation-Date: 2009-02-05 13:26-0600 PO-Revision-Date: 2014-06-27 16:27+0100 Last-Translator: Bart Heesink Language-Team: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: Dutch X-Generator: Poedit 1.5.7 X-Poedit-SourceCharset: UTF-8 1e %(weekday)s2e %(weekday)s2 na laatste %(weekday)s3e %(weekday)s3 na laatste %(weekday)sDatum toevoegenHerhaling toevoegenapraprilaugaugaugustusKalenderDatumdecdecdecemberIedereOm deSluit deze uitDeze datum uitsluitenffebfebfebruariFrequentievrvrijdagjanjanjanuarijuljulijunjunimmrtmaartmeimamaandagnovnovnovemberHerhaal %(number)s keerHerhaal %(number)s keeroktoktoktoberOp deVerwijderenHerhaal totzzazaterdagsepseptseptemberzozondagddodonderdagTijddidinsdagTotwwowoensdagjaarlijksdagelijksdagendagelijkselke %(items)siedere %(number)s %(freq)suitgezonderdeerste %(weekday)svierde %(weekday)suurelk uurelk uurinclusieflaatste %(weekday)s's avondsminuutelke minuutelke minuutmaandenmaandelijksmaandelijks's middagsherhaalt %(number)s keerherhaalt %(number)s keerop de %(items)ssecondetweede %(weekday)stwee na laatste %(weekday)selke secondeelke secondederde %(weekday)sdrie na laatste %(weekday)stot %(date)swekenwekelijkswekelijksjarenjarendjango-recurrence-1.2.0/recurrence/locale/nl/LC_MESSAGES/djangojs.po0000644000076500000240000002052012554752650025301 0ustar cezarstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: Django Recurrence 1.0.1\n" "Report-Msgid-Bugs-To: bheesink@leukeleu.nl\n" "POT-Creation-Date: 2009-02-05 13:26-0600\n" "PO-Revision-Date: 2014-06-27 16:27+0100\n" "Last-Translator: Bart Heesink \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: Dutch\n" "X-Generator: Poedit 1.5.7\n" "X-Poedit-SourceCharset: UTF-8\n" #: media/js/recurrence-widget.js:1732 msgid "including" msgstr "inclusief" #: media/js/recurrence-widget.js:1732 msgid "excluding" msgstr "uitgezonderd" #: media/js/recurrence-widget.js:1736 msgid "Frequency" msgstr "Frequentie" #: media/js/recurrence-widget.js:1737 msgid "On the" msgstr "Op de" #: media/js/recurrence-widget.js:1738 msgid "Each" msgstr "Iedere" #: media/js/recurrence-widget.js:1739 msgid "Every" msgstr "Om de" #: media/js/recurrence-widget.js:1740 msgid "Until" msgstr "Tot" #: media/js/recurrence-widget.js:1741 msgid "Occurs %(number)s time" msgstr "Herhaal %(number)s keer" #: media/js/recurrence-widget.js:1742 msgid "Occurs %(number)s times" msgstr "Herhaal %(number)s keer" #: media/js/recurrence-widget.js:1743 msgid "Date" msgstr "Datum" #: media/js/recurrence-widget.js:1744 msgid "Time" msgstr "Tijd" #: media/js/recurrence-widget.js:1745 msgid "Repeat until" msgstr "Herhaal tot" #: media/js/recurrence-widget.js:1746 msgid "Exclude these occurences" msgstr "Sluit deze uit" #: media/js/recurrence-widget.js:1747 msgid "Exclude this date" msgstr "Deze datum uitsluiten" #: media/js/recurrence-widget.js:1748 msgid "Add rule" msgstr "Herhaling toevoegen" #: media/js/recurrence-widget.js:1749 msgid "Add date" msgstr "Datum toevoegen" #: media/js/recurrence-widget.js:1750 msgid "Remove" msgstr "Verwijderen" #: media/js/recurrence-widget.js:1751 msgid "Calendar" msgstr "Kalender" #: media/js/recurrence.js:995 msgid "midnight" msgstr "'s avonds" #: media/js/recurrence.js:996 msgid "noon" msgstr "'s middags" #: media/js/recurrence.js:997 msgid "on the %(items)s" msgstr "op de %(items)s" #: media/js/recurrence.js:998 msgid "every %(number)s %(freq)s" msgstr "iedere %(number)s %(freq)s" #: media/js/recurrence.js:999 msgid "each %(items)s" msgstr "elke %(items)s" #: media/js/recurrence.js:1000 msgid "occuring %(number)s time" msgstr "herhaalt %(number)s keer" #: media/js/recurrence.js:1001 msgid "occuring %(number)s times" msgstr "herhaalt %(number)s keer" #: media/js/recurrence.js:1002 msgid "until %(date)s" msgstr "tot %(date)s" #: media/js/recurrence.js:1006 msgid "year" msgstr "jaren" #: media/js/recurrence.js:1006 msgid "month" msgstr "maanden" #: media/js/recurrence.js:1006 msgid "week" msgstr "weken" #: media/js/recurrence.js:1006 msgid "day" msgstr "dagen" #: media/js/recurrence.js:1007 msgid "hour" msgstr "uur" #: media/js/recurrence.js:1007 msgid "minute" msgstr "minuut" #: media/js/recurrence.js:1007 msgid "second" msgstr "seconde" #: media/js/recurrence.js:1010 msgid "years" msgstr "jaren" #: media/js/recurrence.js:1010 msgid "months" msgstr "maandelijks" #: media/js/recurrence.js:1010 msgid "weeks" msgstr "wekelijks" #: media/js/recurrence.js:1010 msgid "days" msgstr "dagelijks" #: media/js/recurrence.js:1011 msgid "hours" msgstr "elk uur" #: media/js/recurrence.js:1011 msgid "minutes" msgstr "elke minuut" #: media/js/recurrence.js:1011 msgid "seconds" msgstr "elke seconde" #: media/js/recurrence.js:1014 msgid "annually" msgstr "jaarlijks" #: media/js/recurrence.js:1014 msgid "monthly" msgstr "maandelijks" #: media/js/recurrence.js:1014 msgid "weekly" msgstr "wekelijks" #: media/js/recurrence.js:1014 msgid "daily" msgstr "dagelijks" #: media/js/recurrence.js:1015 msgid "hourly" msgstr "elk uur" #: media/js/recurrence.js:1015 msgid "minutely" msgstr "elke minuut" #: media/js/recurrence.js:1015 msgid "secondly" msgstr "elke seconde" #: media/js/recurrence.js:1018 msgid "Monday" msgstr "maandag" #: media/js/recurrence.js:1018 msgid "Tuesday" msgstr "dinsdag" #: media/js/recurrence.js:1018 msgid "Wednesday" msgstr "woensdag" #: media/js/recurrence.js:1018 msgid "Thursday" msgstr "donderdag" #: media/js/recurrence.js:1019 msgid "Friday" msgstr "vrijdag" #: media/js/recurrence.js:1019 msgid "Saturday" msgstr "zaterdag" #: media/js/recurrence.js:1019 msgid "Sunday" msgstr "zondag" #: media/js/recurrence.js:1022 msgid "Mon" msgstr "ma" #: media/js/recurrence.js:1022 msgid "Tue" msgstr "di" #: media/js/recurrence.js:1022 msgid "Wed" msgstr "wo" #: media/js/recurrence.js:1022 msgid "Thu" msgstr "do" #: media/js/recurrence.js:1023 msgid "Fri" msgstr "vr" #: media/js/recurrence.js:1023 msgid "Sat" msgstr "za" #: media/js/recurrence.js:1023 msgid "Sun" msgstr "zo" #: media/js/recurrence.js:1026 msgid "M" msgstr "m" #: media/js/recurrence.js:1026 msgid "T" msgstr "d" #: media/js/recurrence.js:1026 msgid "W" msgstr "w" #: media/js/recurrence.js:1027 msgid "F" msgstr "f" #: media/js/recurrence.js:1027 msgid "S" msgstr "z" #: media/js/recurrence.js:1030 msgid "first %(weekday)s" msgstr "eerste %(weekday)s" #: media/js/recurrence.js:1031 msgid "second %(weekday)s" msgstr "tweede %(weekday)s" #: media/js/recurrence.js:1032 msgid "third %(weekday)s" msgstr "derde %(weekday)s" #: media/js/recurrence.js:1032 msgid "fourth %(weekday)s" msgstr "vierde %(weekday)s" #: media/js/recurrence.js:1033 media/js/recurrence.js.py:1041 msgid "last %(weekday)s" msgstr "laatste %(weekday)s" #: media/js/recurrence.js:1034 msgid "second last %(weekday)s" msgstr "twee na laatste %(weekday)s" #: media/js/recurrence.js:1035 msgid "third last %(weekday)s" msgstr "drie na laatste %(weekday)s" #: media/js/recurrence.js:1038 msgid "1st %(weekday)s" msgstr "1e %(weekday)s" #: media/js/recurrence.js:1039 msgid "2nd %(weekday)s" msgstr "2e %(weekday)s" #: media/js/recurrence.js:1040 msgid "3rd %(weekday)s" msgstr "3e %(weekday)s" #: media/js/recurrence.js:1042 msgid "2nd last %(weekday)s" msgstr "2 na laatste %(weekday)s" #: media/js/recurrence.js:1043 msgid "3rd last %(weekday)s" msgstr "3 na laatste %(weekday)s" #: media/js/recurrence.js:1046 msgid "January" msgstr "januari" #: media/js/recurrence.js:1046 msgid "February" msgstr "februari" #: media/js/recurrence.js:1046 media/js/recurrence.js.py:1058 msgid "March" msgstr "maart" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1059 msgid "April" msgstr "april" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1053 #: media/js/recurrence.js:1059 msgid "May" msgstr "mei" #: media/js/recurrence.js:1047 media/js/recurrence.js.py:1059 msgid "June" msgstr "juni" #: media/js/recurrence.js:1048 media/js/recurrence.js.py:1060 msgid "July" msgstr "juli" #: media/js/recurrence.js:1048 msgid "August" msgstr "augustus" #: media/js/recurrence.js:1048 msgid "September" msgstr "september" #: media/js/recurrence.js:1049 msgid "October" msgstr "oktober" #: media/js/recurrence.js:1049 msgid "November" msgstr "november" #: media/js/recurrence.js:1049 msgid "December" msgstr "december" #: media/js/recurrence.js:1052 msgid "Jan" msgstr "jan" #: media/js/recurrence.js:1052 msgid "Feb" msgstr "feb" #: media/js/recurrence.js:1052 msgid "Mar" msgstr "mrt" #: media/js/recurrence.js:1053 msgid "Apr" msgstr "apr" #: media/js/recurrence.js:1053 msgid "Jun" msgstr "jun" #: media/js/recurrence.js:1054 msgid "Jul" msgstr "jul" #: media/js/recurrence.js:1054 msgid "Aug" msgstr "aug" #: media/js/recurrence.js:1054 msgid "Sep" msgstr "sep" #: media/js/recurrence.js:1055 msgid "Oct" msgstr "okt" #: media/js/recurrence.js:1055 msgid "Nov" msgstr "nov" #: media/js/recurrence.js:1055 msgid "Dec" msgstr "dec" #: media/js/recurrence.js:1058 msgid "Jan." msgstr "jan" #: media/js/recurrence.js:1058 msgid "Feb." msgstr "feb" #: media/js/recurrence.js:1060 msgid "Aug." msgstr "aug" #: media/js/recurrence.js:1060 msgid "Sept." msgstr "sept" #: media/js/recurrence.js:1061 msgid "Oct." msgstr "okt" #: media/js/recurrence.js:1061 msgid "Nov." msgstr "nov" #: media/js/recurrence.js:1061 msgid "Dec." msgstr "dec" #: media/js/recurrence.js:1064 msgid "a.m." msgstr "" #: media/js/recurrence.js:1064 msgid "p.m." msgstr "" #: media/js/recurrence.js:1065 msgid "AM" msgstr "" #: media/js/recurrence.js:1065 msgid "PM" msgstr "" django-recurrence-1.2.0/recurrence/managers.py0000644000076500000240000000773312425713022021652 0ustar cezarstaff00000000000000import pytz from django.db.models import manager import recurrence from recurrence import choices # All datetimes are stored as utc. def to_utc(dt): if not dt: return dt if dt.tzinfo: return dt.astimezone(pytz.utc) return pytz.utc.localize(dt) class RuleManager(manager.Manager): def to_rule_object(self, rule_model): rule_args = (rule_model.freq,) rule_kwargs = { 'interval': rule_model.interval, 'wkst': rule_model.wkst, 'count': rule_model.count, 'until': to_utc(rule_model.until), } for param in recurrence.Rule.byparams: if param == 'byday': # see recurrence.base docstrings about byday handling rule_kwargs[param] = (map( lambda v: recurrence.Weekday(*v), rule_model.params.filter(param=param).values_list( 'value', 'index')) or None) else: rule_kwargs[param] = (map( lambda v: v[0], rule_model.params.filter(param=param).values_list( 'value' ) ) or None) return recurrence.Rule(*rule_args, **rule_kwargs) def create_from_rule_object(self, mode, rule_obj, recurrence_model): until = to_utc(rule_obj.until) rule_model = self.create( recurrence=recurrence_model, mode=mode, freq=rule_obj.freq, interval=rule_obj.interval, wkst=rule_obj.wkst, count=rule_obj.count, until=until) for param in recurrence.Rule.byparams: value_list = getattr(rule_obj, param, None) if not value_list: continue if not hasattr(value_list, '__iter__'): value_list = [value_list] for value in value_list: if param == 'byday': # see recurrence.base docstrings about byday handling weekday = recurrence.to_weekday(value) rule_model.params.create( param=param, value=weekday.number, index=weekday.index) else: rule_model.params.create(param=param, value=value) return rule_model class RecurrenceManager(manager.Manager): def to_recurrence_object(self, recurrence_model): rrules, exrules, rdates, exdates = [], [], [], [] for rule_model in recurrence_model.rules.filter(mode=choices.INCLUSION): rrules.append(rule_model.to_rule_object()) for exrule_model in recurrence_model.rules.filter(mode=choices.EXCLUSION): exrules.append(exrule_model.to_rule_object()) for rdate_model in recurrence_model.dates.filter(mode=choices.INCLUSION): rdates.append(to_utc(rdate_model.dt)) for exdate_model in recurrence_model.dates.filter(mode=choices.EXCLUSION): exdates.append(to_utc(exdate_model.dt)) dtstart = to_utc(recurrence_model.dtstart) dtend = to_utc(recurrence_model.dtend) return recurrence.Recurrence( dtstart, dtend, rrules, exrules, rdates, exdates) def create_from_recurrence_object(self, recurrence_obj): from recurrence import models recurrence_model = self.create( dtstart=to_utc(recurrence_obj.dtstart), dtend=to_utc(recurrence_obj.dtend)) for rrule in recurrence_obj.rrules: models.Rule.objects.create_from_rule_object( choices.INCLUSION, rrule, recurrence_model) for exrule in recurrence_obj.exrules: models.Rule.objects.create_from_rule_object( choices.EXCLUSION, exrule, recurrence_model) for dt in recurrence_obj.rdates: recurrence_model.dates.create(mode=choices.INCLUSION, dt=to_utc(dt)) for dt in recurrence_obj.exdates: recurrence_model.dates.create(mode=choices.EXCLUSION, dt=to_utc(dt)) return recurrence_model django-recurrence-1.2.0/recurrence/models.py0000644000076500000240000000265312425713022021334 0ustar cezarstaff00000000000000from django.db import models import recurrence as recur from recurrence import managers, choices class Recurrence(models.Model): dtstart = models.DateTimeField(null=True, blank=True) dtend = models.DateTimeField(null=True, blank=True) objects = managers.RecurrenceManager() def to_recurrence_object(self): return Recurrence.objects.to_recurrence_object(self) class Rule(models.Model): recurrence = models.ForeignKey(Recurrence, related_name='rules') mode = models.BooleanField(default=True, choices=choices.MODE_CHOICES) freq = models.PositiveIntegerField(choices=choices.FREQUENCY_CHOICES) interval = models.PositiveIntegerField(default=1) wkst = models.PositiveIntegerField( default=recur.Rule.firstweekday, null=True, blank=True) count = models.PositiveIntegerField(null=True, blank=True) until = models.DateTimeField(null=True, blank=True) objects = managers.RuleManager() def to_rule_object(self): return self.__class__.objects.to_rule_object(self) class Date(models.Model): recurrence = models.ForeignKey(Recurrence, related_name='dates') mode = models.BooleanField(default=True, choices=choices.MODE_CHOICES) dt = models.DateTimeField() class Param(models.Model): rule = models.ForeignKey(Rule, related_name='params') param = models.CharField(max_length=16) value = models.IntegerField() index = models.IntegerField(default=0) django-recurrence-1.2.0/recurrence/static/0000755000076500000240000000000012554754171020775 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/static/recurrence/0000755000076500000240000000000012554754171023132 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/static/recurrence/css/0000755000076500000240000000000012554754171023722 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/static/recurrence/css/recurrence.css0000644000076500000240000001260112356415033026560 0ustar cezarstaff00000000000000div.recurrence-widget { float: left; border: 1px solid #ccc; } div.recurrence-widget div.panel { border-bottom: 1px solid #ddd; } div.recurrence-widget select, div.recurrence-widget input { margin: 0 .5em; } div.recurrence-widget a.recurrence-label { display: block; cursor: pointer; padding: .5em .75em; font-weight: bold; white-space: nowrap; padding-right: 3em; color: black; } div.recurrence-widget .remove, div.recurrence-widget .remove:link, div.recurrence-widget .remove:visited { float: left; padding: .25em .5em; font-size: 1.1em; font-weight: bold; cursor: pointer; text-decoration: none; color: #555; line-height: 1.4em; } div.recurrence-widget .remove:hover { color: #a00; } div.recurrence-widget .control { padding: .5em 1em .5em .5em; } div.recurrence-widget .add-button, div.recurrence-widget .add-button:link, div.recurrence-widget .add-button:visited { color: green; } div.recurrence-widget .plus { font-size: 1.2em; font-weight: bold; padding-right: .5em; } div.recurrence-widget .add-button:hover { color: green; } div.recurrence-widget .control .add-date { padding-left: 1em; margin-left: 1em; border-left: 1px solid #ddd; } div.recurrence-widget .freq { display: inline } div.recurrence-widget .interval { display: inline; padding-left: 1em; } div.recurrence-widget form { overflow: hidden; padding: .5em 2.2em; border-top: 1px solid #ddd; } div.recurrence-widget .form { padding: .5em 0; } div.recurrence-widget .radio { margin-left: 0; } div.recurrence-widget .form ul { margin: 0; padding: 0; } div.recurrence-widget .form ul li { list-style-type: none; } div.recurrence-widget .section { margin: .5em 1em; } div.recurrence-widget .until-count { margin: 0; padding: 0; } div.recurrence-widget .until-count li { margin: 0; padding: 0; display: inline; list-style-type: none; } div.recurrence-widget .until-count .count { padding-left: 1em; } div.recurrence-widget .monthday .grid { margin-left: .5em; } div.recurrence-widget .grid { border: 0; border-top: 1px solid #ccc; border-left: 1px solid #ccc; } div.recurrence-widget .grid td { text-align: center; border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; padding: .8em; cursor: pointer; } div.recurrence-widget .grid td.active { background-color: #ddd; } div.recurrence-widget .grid td.empty { border: 0; } div.recurrence-widget .grid-disabled td.active { background-color: #ddd; } div.recurrence-widget .disabled { opacity: .75; filter: alpha(opacity=75); } div.recurrence-widget .limit ul { margin-top: .5em; } div.recurrence-widget .weekly .section { margin: 1em 0; } div.recurrence-widget .weekly td { width: 2.5em; } div.recurrence-widget .yearly table { margin: .5em 0 1em 0; } div.recurrence-widget .yearly td { padding: 1em 1.5em; } div.recurrence-widget .checkbox { margin-left: 0; } div.recurrence-widget .until-count { margin-left: 1.5em; } div.recurrence-widget .mode { padding: .25em 0 1em 0; } div.recurrence-widget .date-selector .calendar-button { background: url('../img/recurrence-calendar-icon.png') no-repeat center; padding: .25em; } table.recurrence-calendar { position: absolute; background-color: white; border-top: 1px solid #ccc; border-left: 1px solid #ccc; /*outline: 3px solid white;*/ margin: 0; } table.recurrence-calendar td { padding: 0; } table.recurrence-calendar a, table.recurrence-calendar a:link, table.recurrence-calendar a:visited { color: #555; font-weight: bold; } table.recurrence-calendar .year, table.recurrence-calendar .navigation { border-right: 1px solid #ccc; } table.recurrence-calendar .body td { border: 0; border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; } table.recurrence-calendar .body td.header { border-top: 1px solid #ccc; font-weight: bold; color: #555; } table.recurrence-calendar .body td.header, table.recurrence-calendar .body td.day { padding: .25em; text-align: center; vertical-align: center; } table.recurrence-calendar .body td.day { cursor: pointer; } table.recurrence-calendar .body td.active { background-color: #ddd; } table.recurrence-calendar .body td.empty { background-color: #eee; } table.recurrence-calendar .year { padding-top: .25em; padding-right: 2em; text-align: center; } table.recurrence-calendar .navigation { width: 100%; } table.recurrence-calendar .navigation { text-align: center; } table.recurrence-calendar .navigation td.prev-year, table.recurrence-calendar .navigation td.next-year, table.recurrence-calendar .navigation td.prev-month, table.recurrence-calendar .navigation td.next-month { /*width: 1%;*/ font-size: .8em; } table.recurrence-calendar .remove, table.recurrence-calendar .remove:link, table.recurrence-calendar .remove:visited { float: left; padding: .1em .25em; padding-bottom: 0; font-size: 1.1em; font-weight: bold; cursor: pointer; text-decoration: none; color: #555; line-height: 1.2em; } table.recurrence-calendar .remove:hover { color: #a00; } table.recurrence-calendar .footer { height: .5em; overflow: hidden; border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; } django-recurrence-1.2.0/recurrence/static/recurrence/img/0000755000076500000240000000000012554754171023706 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/static/recurrence/img/recurrence-calendar-icon.png0000644000076500000240000000043512356415033031237 0ustar cezarstaff00000000000000PNG  IHDR&/sRGBbKGD pHYs  ~tIME+5:#IDAT(ϭ !D 2:,G3 P#/8S.ؿ| s~9I){1f^R R d ֮s$t%0cCWZkqR~tPkxAuw VwsQә72sIENDB`django-recurrence-1.2.0/recurrence/static/recurrence/js/0000755000076500000240000000000012554754171023546 5ustar cezarstaff00000000000000django-recurrence-1.2.0/recurrence/static/recurrence/js/recurrence-widget.js0000644000076500000240000016542012554752650027532 0ustar cezarstaff00000000000000if (!recurrence) var recurrence = {}; recurrence.widget = {}; recurrence.widget.Grid = function(cols, rows) { this.init(cols, rows); }; recurrence.widget.Grid.prototype = { init: function(cols, rows) { this.disabled = false; this.cells = []; this.cols = cols; this.rows = rows; this.init_dom(); }, init_dom: function() { var tbody = recurrence.widget.e('tbody'); for (var y=0; y < this.rows; y++) { var tr = recurrence.widget.e('tr'); tbody.appendChild(tr); for (var x=0; x < this.cols; x++) { var td = recurrence.widget.e('td'); tr.appendChild(td); this.cells.push(td); } } var table = recurrence.widget.e( 'table', { 'class': 'grid', 'cellpadding': 0, 'cellspacing': 0, 'border': 0}, [tbody]); this.elements = {'root': table, 'table': table, 'tbody': tbody}; }, cell: function(col, row) { return this.elements.tbody.childNodes[row].childNodes[col]; }, enable: function () { recurrence.widget.remove_class('disabled'); this.disabled = false; }, disable: function () { recurrence.widget.add_class('disabled'); this.disabled = true; } }; recurrence.widget.Calendar = function(date, options) { this.init(date, options); }; recurrence.widget.Calendar.prototype = { init: function(date, options) { this.date = date || recurrence.widget.date_today(); this.month = this.date.getMonth(); this.year = this.date.getFullYear(); this.options = options || {}; if (this.options.onchange) this.onchange = this.options.onchange; if (this.options.onclose) this.onclose = this.options.onclose; this.init_dom(); this.show_month(this.year, this.month); }, init_dom: function() { var calendar = this; // navigation var remove = recurrence.widget.e('a', { 'class': 'remove', 'href': 'javascript:void(0)', 'title': recurrence.display.labels.remove, 'onclick': function() { calendar.close(); } }, '×'); var year_prev = recurrence.widget.e( 'a', { 'href': 'javascript:void(0)', 'class': 'prev-year', 'onclick': function() {calendar.show_prev_year();}}, '<<'); var year_next = recurrence.widget.e( 'a', { 'href': 'javascript:void(0)', 'class': 'next-year', 'onclick': function() {calendar.show_next_year();}}, '>>'); var month_prev = recurrence.widget.e( 'a', { 'href': 'javascript:void(0)', 'class': 'prev-month', 'onclick': function() {calendar.show_prev_month();}}, '<'); var month_next = recurrence.widget.e( 'a', { 'href': 'javascript:void(0)', 'class': 'next-month', 'onclick': function() {calendar.show_next_month();}}, '>'); var month_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.months[this.month]); var header_elements = [ year_prev, month_prev, month_label, month_next, year_next]; var header_grid = new recurrence.widget.Grid(header_elements.length, 1); recurrence.array.foreach(header_elements, function(item, i) { header_grid.cells[i].appendChild(item); recurrence.widget.add_class( header_grid.cells[i], item.className); }); recurrence.widget.add_class(header_grid.elements.root, 'navigation'); // core var calendar_year = recurrence.widget.e( 'div', {'class': 'year'}, this.year); var calendar_navigation = header_grid.elements.root; // var calendar_week = week_grid.elements.root; var calendar_body = recurrence.widget.e('div', {'class': 'body'}); var calendar_footer = recurrence.widget.e('div', {'class': 'footer'}); var td = recurrence.widget.e( 'td', {}, [remove, calendar_year, calendar_navigation, calendar_body, calendar_footer]); var tr = recurrence.widget.e('tr', {}, [td]); var tbody = recurrence.widget.e('tbody', {}, [tr]); var root = recurrence.widget.e( 'table', {'class': 'recurrence-calendar'}, [tbody]); root.style.display = 'none'; this.elements = { 'root': root, 'year': calendar_year, 'year_prev': year_prev, 'year_next': year_next, 'month_prev': month_prev, 'month_next': month_next, 'month_label': month_label, 'calendar_body': calendar_body }; }, get_month_grid: function(year, month) { var calendar = this; var dt = new Date(year, month, 1); var start = dt.getDay(); var days = recurrence.date.days_in_month(dt); var rows = Math.ceil((days + start) / 7) + 1; var grid = new recurrence.widget.Grid(7, rows); var number = 1; recurrence.array.foreach( grid.cells, function(cell, i) { var cell = grid.cells[i]; if (i < 7) { var weekday_number = i - 1; if (weekday_number < 0) weekday_number = 6; else if (weekday_number > 6) weekday_number = 0; cell.innerHTML = recurrence.display.weekdays_oneletter[ weekday_number]; recurrence.widget.add_class(cell, 'header'); } else if (i - 7 < start || number > days) { recurrence.widget.add_class(cell, 'empty'); } else { recurrence.widget.add_class(cell, 'day'); if (this.date.getDate() == number && this.date.getFullYear() == dt.getFullYear() && this.date.getMonth() == dt.getMonth()) recurrence.widget.add_class(cell, 'active'); cell.innerHTML = number; number = number + 1; cell.onclick = function () { calendar.set_date( calendar.year, calendar.month, parseInt(this.innerHTML, 10)); }; } }, this); return grid; }, show_month: function(year, month) { if (this.elements.calendar_body.childNodes.length) this.elements.calendar_body.removeChild( this.elements.calendar_body.childNodes[0]); this.elements.month_grid = this.get_month_grid(year, month); this.elements.calendar_body.appendChild( this.elements.month_grid.elements.root); this.elements.month_label.firstChild.nodeValue = ( recurrence.display.months[this.month]); this.elements.year.firstChild.nodeValue = this.year; }, show_prev_year: function() { this.year = this.year - 1; this.show_month(this.year, this.month); }, show_next_year: function() { this.year = this.year + 1; this.show_month(this.year, this.month); }, show_prev_month: function() { this.month = this.month - 1; if (this.month < 0) { this.month = 11; this.year = this.year - 1; } this.show_month(this.year, this.month); }, show_next_month: function() { this.month = this.month + 1; if (this.month > 11) { this.month = 0; this.year = this.year + 1; } this.show_month(this.year, this.month); }, set_date: function(year, month, day) { if (year != this.date.getFullYear() || month != this.date.getMonth() || day != this.date.getDate()) { this.date.setFullYear(year); this.date.setMonth(month); this.date.setDate(day); recurrence.array.foreach( this.elements.month_grid.cells, function(cell) { if (recurrence.widget.has_class(cell, 'day')) { var number = parseInt(cell.innerHTML, 10); if (number == day) { recurrence.widget.add_class(cell, 'active'); } else { recurrence.widget.remove_class(cell, 'active'); } } }); if (this.onchange) this.onchange(this.date); } }, set_position: function(x, y) { this.elements.root.style.left = x + 'px'; this.elements.root.style.top = y + 'px'; }, show: function() { this.elements.root.style.display = ''; }, hide: function() { this.elements.root.style.display = 'none'; }, close: function() { if (this.elements.root.parentNode) { this.elements.root.parentNode.removeChild(this.elements.root); if (this.onclose) this.onclose(); } } }; recurrence.widget.DateSelector = function(date, options) { this.init(date, options); }; recurrence.widget.DateSelector.prototype = { init: function(date, options) { this.disabled = false; this.date = date; this.calendar = null; this.options = options || {}; if (this.options.onchange) this.onchange = this.options.onchange; this.init_dom(); }, init_dom: function() { var dateselector = this; if (this.date) var date_value = recurrence.date.format(this.date, '%Y-%m-%d'); else var date_value = ''; var date_field = recurrence.widget.e( 'input', { 'class': 'date-field', 'size': 10, 'value': date_value, 'onchange': function() {dateselector.set_date(this.value);}}); var calendar_button = recurrence.widget.e( 'a', { 'class': 'calendar-button', 'href': 'javascript:void(0)', 'title': recurrence.display.labels.calendar, 'onclick': function() { if (!dateselector.disabled) dateselector.show_calendar(); } }, '    '); var root = recurrence.widget.e( 'span', {'class': 'date-selector'}, [date_field, calendar_button]); this.elements = { 'root': root, 'date_field': date_field, 'calendar_button': calendar_button }; }, show_calendar: function() { var dateselector = this; var calendar_blur = function(event) { var element = event.target; var is_in_dom = recurrence.widget.element_in_dom( element, dateselector.calendar.elements.root); if (!is_in_dom && element != dateselector.elements.calendar_button) { // clicked outside of calendar dateselector.calendar.close(); if (window.detachEvent) window.detachEvent('onclick', calendar_blur); else window.removeEventListener('click', calendar_blur, false); } }; if (!this.calendar) { this.calendar = new recurrence.widget.Calendar( new Date((this.date || recurrence.widget.date_today()).valueOf()), { 'onchange': function() { dateselector.set_date( recurrence.date.format(this.date, '%Y-%m-%d')); dateselector.calendar.close(); }, 'onclose': function() { if (window.detachEvent) window.detachEvent('onclick', calendar_blur); else window.removeEventListener( 'click', calendar_blur, false); dateselector.hide_calendar(); } }); document.body.appendChild(this.calendar.elements.root); this.calendar.show(); this.set_calendar_position(); if (window.attachEvent) window.attachEvent('onclick', calendar_blur); else window.addEventListener('click', calendar_blur, false); } }, set_date: function(datestring) { var tokens = datestring.split('-'); var year = parseInt(tokens[0], 10); var month = parseInt(tokens[1], 10) - 1; var day = parseInt(tokens[2], 10); var dt = new Date(year, month, day); if (String(dt) == 'Invalid Date' || String(dt) == 'NaN') { if (this.date && !this.options.allow_null) { this.elements.date_field.value = recurrence.date.format( this.date, '%Y-%m-%d'); } else { if (this.elements.date_field.value != '') { if (this.onchange) this.onchange(null); } this.elements.date_field.value = ''; } } else { if (!this.date || (year != this.date.getFullYear() || month != this.date.getMonth() || day != this.date.getDate())) { if (!this.date) this.date = recurrence.widget.date_today(); this.date.setFullYear(year); this.date.setMonth(month); this.date.setDate(day); this.elements.date_field.value = datestring; if (this.onchange) this.onchange(this.date); } } }, set_calendar_position: function() { var loc = recurrence.widget.cumulative_offset( this.elements.calendar_button); var calendar_x = loc[0]; var calendar_y = loc[1]; var calendar_right = ( loc[0] + this.calendar.elements.root.clientWidth); var calendar_bottom = ( loc[1] + this.calendar.elements.root.clientHeight); if (calendar_right > document.scrollWidth) calendar_x = calendar_x - ( calendar_right - document.scrollWidth); if (calendar_bottom > document.scrollHeight) calendar_y = calendar_y - ( calendar_bottom - document.scrollHeight); this.calendar.set_position(calendar_x, calendar_y); }, hide_calendar: function() { this.calendar = null; }, enable: function () { this.disabled = false; this.elements.date_field.disabled = false; }, disable: function () { this.disabled = true; this.elements.date_field.disabled = true; if (this.calendar) this.calendar.close(); } }; recurrence.widget.Widget = function(textarea, options) { this.init(textarea, options); }; recurrence.widget.Widget.prototype = { init: function(textarea, options) { if (textarea.toLowerCase) textarea = document.getElementById(textarea); this.selected_panel = null; this.panels = []; this.data = recurrence.deserialize(textarea.value); this.textarea = textarea; this.options = options; this.default_freq = options.default_freq || recurrence.WEEKLY; this.init_dom(); this.init_panels(); }, init_dom: function() { var widget = this; var panels = recurrence.widget.e('div', {'class': 'panels'}); var control = recurrence.widget.e('div', {'class': 'control'}); var root = recurrence.widget.e( 'div', {'class': this.textarea.className}, [panels, control]); var add_rule = new recurrence.widget.AddButton( recurrence.display.labels.add_rule, { 'onclick': function () {widget.add_rule();} }); recurrence.widget.add_class(add_rule.elements.root, 'add-rule'); control.appendChild(add_rule.elements.root); var add_date = new recurrence.widget.AddButton( recurrence.display.labels.add_date, { 'onclick': function () {widget.add_date();} }); recurrence.widget.add_class(add_date.elements.root, 'add-date'); control.appendChild(add_date.elements.root); this.elements = { 'root': root, 'panels': panels, 'control': control }; // attach immediately this.textarea.style.display = 'none'; this.textarea.parentNode.insertBefore( this.elements.root, this.textarea); }, init_panels: function() { recurrence.array.foreach( this.data.rrules, function(item) { this.add_rule_panel(recurrence.widget.INCLUSION, item); }, this); recurrence.array.foreach( this.data.exrules, function(item) { this.add_rule_panel(recurrence.widget.EXCLUSION, item); }, this); recurrence.array.foreach( this.data.rdates, function(item) { this.add_date_panel(recurrence.widget.INCLUSION, item); }, this); recurrence.array.foreach( this.data.exdates, function(item) { this.add_date_panel(recurrence.widget.EXCLUSION, item); }, this); }, add_rule_panel: function(mode, rule) { var panel = new recurrence.widget.Panel(this); var form = new recurrence.widget.RuleForm(panel, mode, rule); panel.onexpand = function() { if (panel.widget.selected_panel) if (panel.widget.selected_panel != this) panel.widget.selected_panel.collapse(); panel.widget.selected_panel = this; }; panel.onremove = function() { form.remove(); }; this.elements.panels.appendChild(panel.elements.root); this.panels.push(panel); this.update(); return panel; }, add_date_panel: function(mode, date) { var panel = new recurrence.widget.Panel(this); var form = new recurrence.widget.DateForm(panel, mode, date); panel.onexpand = function() { if (panel.widget.selected_panel) if (panel.widget.selected_panel != this) panel.widget.selected_panel.collapse(); panel.widget.selected_panel = this; }; panel.onremove = function() { form.remove(); }; this.elements.panels.appendChild(panel.elements.root); this.panels.push(panel); this.update(); return panel; }, add_rule: function(rule) { var rule = rule || new recurrence.Rule(this.default_freq); this.data.rrules.push(rule); this.add_rule_panel(recurrence.widget.INCLUSION, rule).expand(); }, add_date: function(date) { var date = date || recurrence.widget.date_today(); this.data.rdates.push(date); this.add_date_panel(recurrence.widget.INCLUSION, date).expand(); }, update: function() { this.textarea.value = this.data.serialize(); } }; recurrence.widget.AddButton = function(label, options) { this.init(label, options); }; recurrence.widget.AddButton.prototype = { init: function(label, options) { this.label = label; this.options = options || {}; this.init_dom(); }, init_dom: function() { var addbutton = this; var plus = recurrence.widget.e( 'span', {'class': 'plus'}, '+'); var label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, this.label); var root = recurrence.widget.e( 'a', {'class': 'add-button', 'href': 'javascript:void(0)'}, [plus, label]); root.onclick = function() { addbutton.options.onclick(); }; this.elements = {'root': root, 'plus': plus, 'label': label}; } }; recurrence.widget.Panel = function(widget, options) { this.init(widget, options); }; recurrence.widget.Panel.prototype = { init: function(widget, options) { this.collapsed = false; this.widget = widget; this.options = options || {}; if (this.options.onremove) this.onremove = this.options.onremove; if (this.options.onexpand) this.onexpand = this.options.onexpand; if (this.options.oncollapse) this.oncollapse = this.options.oncollapse; this.init_dom(); }, init_dom: function() { var panel = this; var remove = recurrence.widget.e('a', { 'class': 'remove', 'href': 'javascript:void(0)', 'title': recurrence.display.labels.remove, 'onclick': function() { panel.remove(); } }, '×'); var label = recurrence.widget.e('a', { 'class': 'recurrence-label', 'href': 'javascript:void(0)', 'onclick': function() { if (panel.collapsed) panel.expand(); else panel.collapse(); } }, ' '); var header = recurrence.widget.e( 'div', {'class': 'header'}, [remove, label]); var body = recurrence.widget.e( 'div', {'class': 'body'}); var root = recurrence.widget.e( 'div', {'class': 'panel'}, [header, body]); this.elements = { 'root': root, 'remove': remove, 'label': label, 'header': header, 'body': body }; this.collapse(); }, set_label: function(label) { this.elements.label.innerHTML = label; }, set_body: function(element) { if (this.elements.body.childNodes.length) this.elements.body.removeChild(this.elements.body.childNodes[0]); this.elements.body.appendChild(element); }, expand: function() { this.collapsed = false; this.elements.body.style.display = ''; if (this.onexpand) this.onexpand(this); }, collapse: function() { this.collapsed = true; this.elements.body.style.display = 'none'; if (this.oncollapse) this.oncollapse(this); }, remove: function() { var parent = this.elements.root.parentNode; if (parent) parent.removeChild(this.elements.root); if (this.onremove) this.onremove(parent); } }; recurrence.widget.RuleForm = function(panel, mode, rule, options) { this.init(panel, mode, rule, options); }; recurrence.widget.RuleForm.prototype = { init: function(panel, mode, rule, options) { this.selected_freq = rule.freq; this.panel = panel; this.mode = mode; this.rule = rule; this.options = options || {}; var rule_options = { interval: rule.interval, until: rule.until, count: rule.count }; this.freq_rules = [ new recurrence.Rule(recurrence.YEARLY, rule_options), new recurrence.Rule(recurrence.MONTHLY, rule_options), new recurrence.Rule(recurrence.WEEKLY, rule_options), new recurrence.Rule(recurrence.DAILY, rule_options) ]; this.freq_rules[this.rule.freq].update(this.rule); this.init_dom(); this.set_freq(this.selected_freq); }, init_dom: function() { var form = this; // mode var mode_checkbox = recurrence.widget.e( 'input', {'class': 'checkbox', 'type': 'checkbox', 'name': 'mode'}); var mode_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.exclude_occurrences); var mode_container = recurrence.widget.e( 'div', {'class': 'mode'}, [mode_checkbox, mode_label]); if (this.mode == recurrence.widget.EXCLUSION) // delay for ie6 compatibility setTimeout(function() { mode_checkbox.checked = true; recurrence.widget.add_class(form.panel, 'exclusion'); }, 10); // freq var freq_choices = recurrence.display.frequencies.slice(0, 4); var freq_options = recurrence.array.foreach( freq_choices, function(item, i) { var option = recurrence.widget.e( 'option', {'value': i}, recurrence.string.capitalize(item)); return option; }); var freq_select = recurrence.widget.e( 'select', {'name': 'freq'}, freq_options); var freq_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.frequency + ':'); var freq_container = recurrence.widget.e( 'div', {'class': 'freq'}, [freq_label, freq_select]); // interval var interval_field = recurrence.widget.e( 'input', { 'name': 'interval', 'size': 1, 'value': this.rule.interval}); var interval_label1 = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.every); var interval_label2 = recurrence.widget.e( 'span', {'class': 'laebl'}, recurrence.display.timeintervals_plural[this.rule.freq]); var interval_container = recurrence.widget.e( 'div', {'class': 'interval'}, [interval_label1, interval_field, interval_label2]); // until if (this.rule.until) until_value = recurrence.date.format(this.rule.until, '%Y-%m-%d'); else until_value = ''; var until_radio = recurrence.widget.e( 'input', {'class': 'radio', 'type': 'radio', 'name': 'until_count', 'value': 'until'}); var until_date_selector = new recurrence.widget.DateSelector( this.rule.until, { 'onchange': function(date) {form.set_until(date);}, 'allow_null': true }); var until_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.date + ':'); var until_container = recurrence.widget.e( 'li', {'class': 'until'}, [until_radio, until_label, until_date_selector.elements.root]); // count if (this.rule.count) count_value = this.rule.count; else count_value = 1; var count_radio = recurrence.widget.e( 'input', { 'class': 'radio', 'type': 'radio', 'name': 'until_count', 'value': 'count'}); var count_field = recurrence.widget.e( 'input', {'name': 'count', 'size': 1, 'value': count_value}); if (this.rule.count && this.rule.count < 2) var token = recurrence.string.capitalize( recurrence.display.labels.count); else var token = recurrence.string.capitalize( recurrence.display.labels.count_plural); var count_label1 = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, token.split('%(number)s')[0]); var count_label2 = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, token.split('%(number)s')[1]); var count_container = recurrence.widget.e( 'li', {'class': 'count'}, [count_radio, count_label1, count_field, count_label2]); // limit container var until_count_container = recurrence.widget.e( 'ul', {'class': 'until-count'}, [until_container, count_container]); var limit_checkbox = recurrence.widget.e( 'input', { 'class': 'checkbox', 'type': 'checkbox', 'name': 'limit'}); var limit_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.repeat_until + ':'); var limit_container = recurrence.widget.e( 'div', {'class': 'limit'}, [limit_checkbox, limit_label, until_count_container]); if (this.rule.until || this.rule.count) { // compatibility with ie, we delay setTimeout(function() {limit_checkbox.checked = true;}, 10); } else { until_radio.disabled = true; count_radio.disabled = true; until_date_selector.disable(); recurrence.widget.add_class(until_count_container, 'disabled'); } // core var freq_form_container = recurrence.widget.e( 'div', {'class': 'form'}); var root = recurrence.widget.e( 'form', {}, [ mode_container, freq_container, interval_container, freq_form_container, limit_container]); // events mode_checkbox.onclick = function() { if (this.checked) form.set_mode(recurrence.widget.EXCLUSION); else form.set_mode(recurrence.widget.INCLUSION); }; freq_select.onchange = function() { form.set_freq(parseInt(this.value), 10); }; interval_field.onchange = function() { form.set_interval(parseInt(this.value), 10); }; limit_checkbox.onclick = function () { if (this.checked) { recurrence.widget.remove_class( until_count_container, 'disabled'); until_radio.disabled = false; count_radio.disabled = false; if (until_radio.checked) { until_date_selector.enable(); form.set_until(until_date_selector.date); } if (count_radio.checked) { count_field.disabled = false; form.set_count(parseInt(count_field.value)); } } else { recurrence.widget.add_class( until_count_container, 'disabled'); until_radio.disabled = true; count_radio.disabled = true; until_date_selector.disable(); count_field.disabled = true; recurrence.array.foreach( form.freq_rules, function(rule) { rule.until = null; rule.count = null; }); form.update(); } } // for compatibility with ie, use timeout setTimeout(function () { if (form.rule.count) { count_radio.checked = true; until_date_selector.disable(); } else { until_radio.checked = true; count_field.disabled = true; } }, 1); until_radio.onclick = function () { this.checked = true; until_date_selector.enable(); count_radio.checked = false; count_field.disabled = true; form.set_until(until_date_selector.date); }; count_radio.onclick = function () { this.checked = true; count_field.disabled = false; until_radio.checked = false; until_date_selector.disable(); form.set_count(parseInt(count_field.value), 10); }; count_field.onchange = function () { form.set_count(parseInt(this.value), 10); }; // freq forms var forms = [ recurrence.widget.RuleYearlyForm, recurrence.widget.RuleMonthlyForm, recurrence.widget.RuleWeeklyForm, recurrence.widget.RuleDailyForm ]; var freq_forms = recurrence.array.foreach( forms, function(form, i) { var rule = this.freq_rules[i]; var f = new form(this, rule); freq_form_container.appendChild(f.elements.root); return f; }, this); this.freq_forms = freq_forms; // install dom this.panel.set_label(this.get_display_text()); this.panel.set_body(root); this.elements = { 'root': root, 'mode_checkbox': mode_checkbox, 'freq_select': freq_select, 'interval_field': interval_field, 'freq_form_container': freq_form_container, 'until_radio': until_radio, 'count_field': count_field, 'count_radio': count_radio, 'limit_checkbox': limit_checkbox }; }, get_display_text: function() { var text = this.freq_rules[this.selected_freq].get_display_text(); if (this.mode == recurrence.widget.EXCLUSION) text = recurrence.display.mode.exclusion + ' ' + text; return recurrence.string.capitalize(text); }, set_until: function(until) { recurrence.array.foreach( this.freq_rules, function(rule) { rule.count = null; rule.until = until; }); this.update(); }, set_count: function(count) { if (count < 2) var token = recurrence.string.capitalize( recurrence.display.labels.count); else var token = recurrence.string.capitalize( recurrence.display.labels.count_plural); var label1 = this.elements.count_field.previousSibling; var label2 = this.elements.count_field.nextSibling; label1.firstChild.nodeValue = token.split('%(number)s')[0]; label2.firstChild.nodeValue = token.split('%(number)s')[1]; recurrence.array.foreach( this.freq_rules, function(rule) { rule.until = null; rule.count = count; }); this.update(); }, set_interval: function(interval) { interval = parseInt(interval, 10); if (String(interval) == 'NaN') { // invalid value, reset to previous value this.elements.interval_field.value = ( this.freq_rules[this.selected_freq].interval); return; } var label = this.elements.interval_field.nextSibling; if (interval < 2) label.firstChild.nodeValue = ( recurrence.display.timeintervals[this.selected_freq]); else label.firstChild.nodeValue = ( recurrence.display.timeintervals_plural[this.selected_freq]); recurrence.array.foreach( this.freq_rules, function(rule) { rule.interval = interval; }); this.elements.interval_field.value = interval; this.update(); }, set_freq: function(freq) { this.freq_forms[this.selected_freq].hide(); this.freq_forms[freq].show(); this.elements.freq_select.value = freq; this.selected_freq = freq; // need to update interval to display different label this.set_interval(parseInt(this.elements.interval_field.value), 10); this.update(); }, set_mode: function(mode) { if (this.mode != mode) { if (this.mode == recurrence.widget.INCLUSION) { recurrence.array.remove( this.panel.widget.data.rrules, this.rule); this.panel.widget.data.exrules.push(this.rule); recurrence.widget.remove_class( this.panel.elements.root, 'inclusion'); recurrence.widget.add_class( this.panel.elements.root, 'exclusion'); } else { recurrence.array.remove( this.panel.widget.data.exrules, this.rule); this.panel.widget.data.rrules.push(this.rule); recurrence.widget.remove_class( this.panel.elements.root, 'exclusion'); recurrence.widget.add_class( this.panel.elements.root, 'inclusion'); } this.mode = mode; } this.update(); }, update: function() { this.panel.set_label(this.get_display_text()); this.rule.update(this.freq_rules[this.selected_freq]); this.panel.widget.update(); }, remove: function() { var parent = this.elements.root.parentNode; if (parent) parent.removeChild(this.elements.root); if (this.mode == recurrence.widget.INCLUSION) recurrence.array.remove(this.panel.widget.data.rrules, this.rule); else recurrence.array.remove(this.panel.widget.data.exrules, this.rule); this.panel.widget.update(); } }; recurrence.widget.RuleYearlyForm = function(panel, rule) { this.init(panel, rule); }; recurrence.widget.RuleYearlyForm.prototype = { init: function(panel, rule) { this.panel = panel; this.rule = rule; this.init_dom(); }, init_dom: function() { var form = this; var grid = new recurrence.widget.Grid(4, 3); var number = 0; for (var y=0; y < 3; y++) { for (var x=0; x < 4; x++) { var cell = grid.cell(x, y); if (this.rule.bymonth.indexOf(number + 1) > -1) recurrence.widget.add_class(cell, 'active'); cell.value = number + 1; cell.innerHTML = recurrence.display.months_short[number]; cell.onclick = function () { if (recurrence.widget.has_class(this, 'active')) recurrence.widget.remove_class(this, 'active'); else recurrence.widget.add_class(this, 'active'); form.set_bymonth(); }; number += 1; } } // by weekday checkbox var byday_checkbox = recurrence.widget.e( 'input', { 'class': 'checkbox', 'type': 'checkbox', 'name': 'byday'}); var byday_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.string.capitalize( recurrence.display.labels.on_the) + ':'); var byday_container = recurrence.widget.e( 'div', {'class': 'byday'}, [byday_checkbox, byday_label]); // weekday-position var position_options = recurrence.array.foreach( [1, 2, 3, 4, -1, -2, -3], function(value) { var option = recurrence.widget.e( 'option', {'value': value}, recurrence.string.strip(recurrence.display.weekdays_position[ String(value)].split('%(weekday)s')[0])); return option; }); var position_select = recurrence.widget.e( 'select', {'name': 'position'}, position_options); var weekday_options = recurrence.array.foreach( recurrence.display.weekdays, function(weekday, i) { var option = recurrence.widget.e( 'option', {'value': i}, weekday); return option; }); var weekday_select = recurrence.widget.e( 'select', {'name': 'weekday'}, weekday_options); var weekday_position_container = recurrence.widget.e( 'div', {'class': 'section'}, [position_select, weekday_select]); // core var year = recurrence.widget.e('div'); year.appendChild(grid.elements.root); var root = recurrence.widget.e( 'div', {'class': 'yearly'}, [year, byday_container, weekday_position_container]); root.style.display = 'none'; if (this.rule.byday.length) { if (form.rule.bysetpos.length) { position_select.value = String(form.rule.bysetpos[0]); } else { position_select.value = String(form.rule.byday[0].index); } weekday_select.value = String(form.rule.byday[0].number); byday_checkbox.checked = true; } else { position_select.disabled = true; weekday_select.disabled = true; } // events byday_checkbox.onclick = function () { if (this.checked) { position_select.disabled = false; weekday_select.disabled = false; form.set_byday(); } else { position_select.disabled = true; weekday_select.disabled = true; form.rule.byday = []; form.panel.update(); } }; position_select.onchange = function () { form.set_byday(); }; weekday_select.onchange = function () { form.set_byday(); }; this.elements = { 'root': root, 'grid': grid, 'byday_checkbox': byday_checkbox, 'position_select': position_select, 'weekday_select': weekday_select }; }, get_weekday: function() { var number = parseInt(this.elements.weekday_select.value, 10); var index = parseInt(this.elements.position_select.value, 10); return new recurrence.Weekday(number, index); }, set_bymonth: function() { var bymonth = []; recurrence.array.foreach( this.elements.grid.cells, function(cell) { if (recurrence.widget.has_class(cell, 'active')) bymonth.push(cell.value); }) this.rule.bymonth = bymonth; this.panel.update(); }, set_byday: function() { this.rule.byday = [this.get_weekday()]; this.panel.update(); }, show: function() { this.elements.root.style.display = ''; }, hide: function() { this.elements.root.style.display = 'none'; } }; recurrence.widget.RuleMonthlyForm = function(panel, rule) { this.init(panel, rule); }; recurrence.widget.RuleMonthlyForm.prototype = { init: function(panel, rule) { this.panel = panel; this.rule = rule; this.init_dom(); }, init_dom: function() { var form = this; // monthday var monthday_grid = new recurrence.widget.Grid(7, Math.ceil(31 / 7)); var number = 0; for (var y=0; y < Math.ceil(31 / 7); y++) { for (var x=0; x < 7; x++) { number += 1; var cell = monthday_grid.cell(x, y); if (number > 31) { recurrence.widget.add_class(cell, 'empty'); continue; } else { cell.innerHTML = number; if (this.rule.bymonthday.indexOf(number) > -1) recurrence.widget.add_class(cell, 'active'); cell.onclick = function () { if (monthday_grid.disabled) return; var day = parseInt(this.innerHTML, 10) || null; if (day) { if (recurrence.widget.has_class(this, 'active')) recurrence.widget.remove_class(this, 'active'); else recurrence.widget.add_class(this, 'active'); form.set_bymonthday(); } } } } } var monthday_grid_container = recurrence.widget.e( 'div', {'class': 'section'}); monthday_grid_container.appendChild(monthday_grid.elements.root); var monthday_radio = recurrence.widget.e( 'input', { 'class': 'radio', 'type': 'radio', 'name': 'monthly', 'value': 'monthday'}); var monthday_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.each + ':'); var monthday_container = recurrence.widget.e( 'li', {'class': 'monthday'}, [monthday_radio, monthday_label, monthday_grid_container]); // weekday-position var position_options = recurrence.array.foreach( [1, 2, 3, 4, -1, -2, -3], function(value) { var option = recurrence.widget.e( 'option', {'value': value}, recurrence.string.strip( recurrence.display.weekdays_position[ String(value)].split('%(weekday)s')[0])); return option; }); var position_select = recurrence.widget.e( 'select', {'name': 'position'}, position_options); var weekday_options = recurrence.array.foreach( recurrence.display.weekdays, function(weekday, i) { var option = recurrence.widget.e( 'option', {'value': i}, weekday); return option; }); var weekday_select = recurrence.widget.e( 'select', {'name': 'weekday'}, weekday_options); var weekday_position_container = recurrence.widget.e( 'div', {'class': 'section'}, [position_select, weekday_select]); var weekday_radio = recurrence.widget.e( 'input', { 'class': 'radio', 'type': 'radio', 'name': 'monthly', 'value': 'weekday'}); var weekday_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.on_the + ':'); var weekday_container = recurrence.widget.e( 'li', {'class': 'weekday'}, [weekday_radio, weekday_label, weekday_position_container]); // core var monthday_weekday_container = recurrence.widget.e( 'ul', {'class': 'monthly'}, [monthday_container, weekday_container]); var root = recurrence.widget.e( 'div', {'class': 'monthly'}, [monthday_weekday_container]); root.style.display = 'none'; // events // for compatibility with ie, use timeout setTimeout(function () { if (form.rule.byday.length) { weekday_radio.checked = true; if (form.rule.bysetpos.length) { position_select.value = String(form.rule.bysetpos[0]); } else { position_select.value = String(form.rule.byday[0].index); } weekday_select.value = String(form.rule.byday[0].number); monthday_grid.disable(); } else { monthday_radio.checked = true; position_select.disabled = true; weekday_select.disabled = true; } }, 1); monthday_radio.onclick = function () { this.checked = true; weekday_radio.checked = false; position_select.disabled = true; weekday_select.disabled = true; monthday_grid.enable(); form.set_bymonthday(); }; weekday_radio.onclick = function () { this.checked = true; monthday_radio.checked = false; position_select.disabled = false; weekday_select.disabled = false; monthday_grid.disable(); form.set_byday(); }; position_select.onchange = function () { form.set_byday(); }; weekday_select.onchange = function () { form.set_byday(); }; this.elements = { 'root': root, 'monthday_grid': monthday_grid, 'monthday_radio': monthday_radio, 'weekday_radio': weekday_radio, 'position_select': position_select, 'weekday_select': weekday_select }; }, get_weekday: function() { var number = parseInt(this.elements.weekday_select.value, 10); var index = parseInt(this.elements.position_select.value, 10); return new recurrence.Weekday(number, index); }, set_byday: function() { this.rule.bymonthday = []; this.rule.bysetpos = []; this.rule.byday = [this.get_weekday()]; this.panel.update(); }, set_bymonthday: function() { this.rule.bysetpos = []; this.rule.byday = []; var monthdays = []; recurrence.array.foreach( this.elements.monthday_grid.cells, function(cell) { var day = parseInt(cell.innerHTML, 10) || null; if (day && recurrence.widget.has_class(cell, 'active')) monthdays.push(day); }); this.rule.bymonthday = monthdays; this.panel.update(); }, show: function() { this.elements.root.style.display = ''; }, hide: function() { this.elements.root.style.display = 'none'; } }; recurrence.widget.RuleWeeklyForm = function(panel, rule) { this.init(panel, rule); }; recurrence.widget.RuleWeeklyForm.prototype = { init: function(panel, rule) { this.panel = panel; this.rule = rule; this.init_dom(); }, init_dom: function() { var form = this; var weekday_grid = new recurrence.widget.Grid(7, 1); var days = []; var days = recurrence.array.foreach( this.rule.byday, function(day) { return recurrence.to_weekday(day).number; }); for (var x=0; x < 7; x++) { var cell = weekday_grid.cell(x, 0); if (days.indexOf(x) > -1) recurrence.widget.add_class(cell, 'active'); cell.value = x; cell.innerHTML = recurrence.display.weekdays_short[x]; cell.onclick = function () { if (weekday_grid.disabled) return; if (recurrence.widget.has_class(this, 'active')) recurrence.widget.remove_class(this, 'active'); else recurrence.widget.add_class(this, 'active'); form.set_byday(); }; } var weekday_container = recurrence.widget.e( 'div', {'class': 'section'}); weekday_container.appendChild(weekday_grid.elements.root); var root = recurrence.widget.e( 'div', {'class': 'weekly'}, [weekday_container]); root.style.display = 'none'; this.elements = { 'root': root, 'weekday_grid': weekday_grid }; }, set_byday: function() { var byday = []; recurrence.array.foreach( this.elements.weekday_grid.cells, function(cell) { if (recurrence.widget.has_class(cell, 'active')) byday.push(new recurrence.Weekday(cell.value)); }); this.rule.byday = byday; this.panel.update(); }, show: function() { this.elements.root.style.display = ''; }, hide: function() { this.elements.root.style.display = 'none'; } }; recurrence.widget.RuleDailyForm = function(panel, rule) { this.init(panel, rule); }; recurrence.widget.RuleDailyForm.prototype = { init: function(panel, rule) { this.panel = panel; this.rule = rule; this.init_dom(); }, init_dom: function() { var root = recurrence.widget.e('div', {'class': 'daily'}); root.style.display = 'none'; this.elements = {'root': root}; }, show: function() { // this.elements.root.style.display = ''; }, hide: function() { // this.elements.root.style.display = 'none'; } }; recurrence.widget.DateForm = function(panel, mode, date) { this.init(panel, mode, date); }; recurrence.widget.DateForm.prototype = { init: function(panel, mode, date) { this.collapsed = true; this.panel = panel; this.mode = mode; this.date = date; this.init_dom(); }, init_dom: function() { var form = this; // mode var mode_checkbox = recurrence.widget.e( 'input', { 'class': 'checkbox', 'type': 'checkbox', 'name': 'mode', 'onclick': function() { if (this.checked) form.set_mode(recurrence.widget.EXCLUSION); else form.set_mode(recurrence.widget.INCLUSION); } }); if (this.mode == recurrence.widget.EXCLUSION) mode_checkbox.checked = true; var mode_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.exclude_date); var mode_container = recurrence.widget.e( 'div', {'class': 'mode'}, [mode_checkbox, mode_label]); // date var date_label = recurrence.widget.e( 'span', {'class': 'recurrence-label'}, recurrence.display.labels.date + ':'); var date_selector = new recurrence.widget.DateSelector( this.date, {'onchange': function() {form.update();}}); var date_container = recurrence.widget.e( 'div', {'class': 'date'}, [date_label, date_selector.elements.root]); // core var root = recurrence.widget.e( 'form', {'class': 'date'}, [mode_container, date_container]); // init dom this.panel.set_label(this.get_display_text()); this.panel.set_body(root); this.elements = {'root': root}; }, get_display_text: function() { var text = recurrence.date.format(this.date, pgettext('date', '%l, %F %j, %Y')); if (this.mode == recurrence.widget.EXCLUSION) text = recurrence.display.mode.exclusion + ' ' + text; return recurrence.string.capitalize(text); }, set_mode: function(mode) { if (this.mode != mode) { if (this.mode == recurrence.widget.INCLUSION) { recurrence.array.remove( this.panel.widget.data.rdates, this.date); this.panel.widget.data.exdates.push(this.date); recurrence.widget.remove_class( this.elements.root, 'inclusion'); recurrence.widget.add_class( this.elements.root, 'exclusion'); this.update(); } else { recurrence.array.remove( this.panel.widget.data.exdates, this.date); this.panel.widget.data.rdates.push(this.date); recurrence.widget.remove_class( this.elements.root, 'exclusion'); recurrence.widget.add_class( this.elements.root, 'inclusion'); this.update(); } this.mode = mode; } this.update(); }, update: function() { this.panel.set_label(this.get_display_text()); this.panel.widget.update(); }, remove: function() { var parent = this.elements.root.parentNode; if (parent) parent.removeChild(this.elements.root); if (this.mode == recurrence.widget.INCLUSION) recurrence.array.remove(this.panel.widget.data.rdates, this.date); else recurrence.array.remove(this.panel.widget.data.exdates, this.date); this.panel.widget.update(); } }; recurrence.widget.e = function(tag_name, attrs, inner) { var element = document.createElement(tag_name); if (attrs) recurrence.widget.set_attrs(element, attrs); if (inner) { if (!inner.toLowerCase && inner.length) recurrence.array.foreach( inner, function(e) {element.appendChild(e);}); else element.innerHTML = inner; } return element; }; recurrence.widget.set_attrs = function(element, attrs) { for (var attname in attrs) if (attname.match(/^on/g)) element[attname] = attrs[attname]; else if (attname == 'class') element.className = attrs[attname]; else element.setAttribute(attname, attrs[attname]); }; recurrence.widget.add_class = function(element, class_name) { var names = (element.className || '').split(/[ \r\n\t]+/g); if (names.indexOf(class_name) == -1) { names.push(class_name); element.className = names.join(' '); } }; recurrence.widget.remove_class = function(element, class_name) { var names = (element.className || '').split(/[ \r\n\t]+/g); if (names.indexOf(class_name) > -1) { recurrence.array.remove(names, class_name); element.className = names.join(' '); } }; recurrence.widget.has_class = function(element, class_name) { var names = (element.className || '').split(/[ \r\n\t]+/g); if (names.indexOf(class_name) > -1) return true; else return false; }; recurrence.widget.element_in_dom = function(element, dom) { if (element == dom) { return true; } else { for (var i=0; i < dom.childNodes.length; i++) if (recurrence.widget.element_in_dom(element, dom.childNodes[i])) return true; } return false; }; recurrence.widget.cumulative_offset = function(element) { var y = 0, x = 0; do { y += element.offsetTop || 0; x += element.offsetLeft || 0; element = element.offsetParent; } while (element); return [x, y]; }; recurrence.widget.textareas_to_widgets = function(token) { var elements = []; if (!token) token = 'recurrence-widget'; if (token.toLowerCase) { var textareas = document.getElementsByTagName('textarea'); recurrence.array.foreach( textareas, function(textarea) { if (recurrence.widget.has_class(textarea, token)) elements.push(textarea); }); } recurrence.array.foreach( elements, function(e) { new recurrence.widget.Widget(e, window[e.id] || {}); }); }; recurrence.widget.date_today = function() { var date = new Date(); date.setHours(0); date.setMinutes(0); date.setSeconds(0); return date; }; recurrence.widget.INCLUSION = true; recurrence.widget.EXCLUSION = false; // display if (!recurrence.display) recurrence.display = {}; recurrence.display.mode = { 'inclusion': gettext('including'), 'exclusion': gettext('excluding') }; recurrence.display.labels = { 'frequency': gettext('Frequency'), 'on_the': gettext('On the'), 'each': gettext('Each'), 'every': gettext('Every'), 'until': gettext('Until'), 'count': gettext('Occurs %(number)s time'), 'count_plural': gettext('Occurs %(number)s times'), 'date': gettext('Date'), 'time': gettext('Time'), 'repeat_until': gettext('Repeat until'), 'exclude_occurrences': gettext('Exclude these occurences'), 'exclude_date': gettext('Exclude this date'), 'add_rule': gettext('Add rule'), 'add_date': gettext('Add date'), 'remove': gettext('Remove'), 'calendar': gettext('Calendar') }; django-recurrence-1.2.0/recurrence/static/recurrence/js/recurrence.js0000644000076500000240000010532012554752650026242 0ustar cezarstaff00000000000000if (!recurrence) var recurrence = {}; recurrence.Rule = function(freq, options) { this.init(freq, options); }; recurrence.Rule.prototype = { init: function(freq, options) { this.freq = freq; options = options || {}; this.interval = options.interval || 1; this.wkst = options.wkst || null; this.count = options.count || null; this.until = options.until || null; recurrence.array.foreach( recurrence.byparams, function (param) { if (options[param]) { var value = options[param]; if (value == null) value = []; this[param] = recurrence.array.from(value); } else { this[param] = []; } }, this); }, copy: function() { var until = this.until; if (until) until = new Date(until.valueOf()); var rule = new recurrence.Rule(this.freq, this); rule.until = until; return rule; }, update: function(rule) { rule = rule.copy(); this.freq = rule.freq; this.interval = rule.interval; this.wkst = rule.wkst; this.until = rule.until; this.count = rule.count; recurrence.array.foreach( recurrence.byparams, function(param) { this[param] = rule[param]; }, this); }, get_display_text: function(short) { short = short || false; var parts = []; var get_position_display = function(position) { if (short) return recurrence.display.weekdays_position_short[ String(position)]; else return recurrence.display.weekdays_position[ String(position)]; }; var get_weekday_display = function(number) { if (short) return recurrence.display.weekdays_short[number]; else return recurrence.display.weekdays[number]; }; var get_position_weekday = function(rule) { var items = []; if (rule.bysetpos.length && rule.byday.length) { recurrence.array.foreach( rule.bysetpos, function(x) { var label = get_position_display(x || 1); recurrence.array.foreach( rule.byday, function(y) { var weekday_display = get_weekday_display( recurrence.to_weekday(y).number); items.push( interpolate( label, {'weekday': weekday_display}, true)); }); }); } else if (rule.byday.length) { // TODO byday Weekday objects without index means // every weekday in the month, and so should appear in // plural. i.e. 'on sundays' instead of // 'on the first sunday'. recurrence.array.foreach( rule.byday, function(x, i) { var label = get_position_display(x.index || 1); var weekday_display = get_weekday_display( recurrence.to_weekday(x).number); items.push( interpolate( label, {'weekday': weekday_display}, true)); }); } return items.join(', '); } if (this.interval > 1) parts.push( interpolate( recurrence.display.tokens.every_number_freq, { 'number': this.interval, 'freq': recurrence.display.timeintervals_plural[this.freq] }, true)); else parts.push(recurrence.display.frequencies[this.freq]); if (this.freq == recurrence.YEARLY) { if (this.bymonth.length) { // i.e. 'each january, june' if (short) var months = recurrence.display.months_short; else var months = recurrence.display.months; var items = recurrence.array.foreach( this.bymonth, function(month, i) { return months[month - 1]; }); items = items.join(', '); parts.push( interpolate( recurrence.display.tokens.each, {'items': items}, true)); } if (this.byday.length || this.bysetpos.length) { var weekday_items = get_position_weekday(this); parts.push( interpolate( recurrence.display.tokens.on_the_items, {'items': weekday_items}, true)); } } if (this.freq == recurrence.MONTHLY) { if (this.bymonthday.length) { // i.e. 'on the 1st, 5th, 10th' var items = recurrence.array.foreach( this.bymonthday, function(day, i) { var dt = new Date(); dt.setMonth(0); dt.setDate(day); return recurrence.date.format(dt, '%j%S'); }); items = items.join(', '); parts.push( interpolate( recurrence.display.tokens.on_the_items, {'items': items}, true)); } else if (this.byday.length) { if (this.byday.length || this.bysetpos.length) { var weekday_items = get_position_weekday(this); parts.push( interpolate( recurrence.display.tokens.on_the_items, {'items': weekday_items}, true)); } } } if (this.freq == recurrence.WEEKLY) { if (this.byday.length) { // i.e. 'each tuesday, wednesday' var items = recurrence.array.foreach( this.byday, function(byday) { var weekday_number = recurrence.to_weekday(byday).number; if (short) var weekday = recurrence.display.weekdays_short[ weekday_number]; else var weekday = recurrence.display.weekdays[ weekday_number]; return weekday; }); items = items.join(', '); parts.push( interpolate( recurrence.display.tokens.each, {'items': items}, true)); } } // daily frequencies has no additional formatting, // hour/minute/second formatting not supported. if (this.count) { if (this.count == 1) parts.push( interpolate( recurrence.display.tokens.count, {'number': this.count}, true)); else parts.push( interpolate( recurrence.display.tokens.count_plural, {'number': this.count}, true)); } else if (this.until) { parts.push( interpolate( recurrence.display.tokens.until, {'date': recurrence.date.format(this.until, '%Y-%m-%d')}, true)); } return parts.join(', '); } }; recurrence.Recurrence = function(options) { this.init(options); }; recurrence.Recurrence.prototype = { init: function(options) { options = options || {}; this.dtstart = options.dtstart || null; this.dtend = options.dtend || null; this.rrules = recurrence.array.from(options.rrules || []); this.exrules = recurrence.array.from(options.exrules || []); this.rdates = recurrence.array.from(options.rdates || []); this.exdates = recurrence.array.from(options.exdates || []); }, copy: function() { return new recurrence.Recurrence({ 'rrules': recurrence.array.foreach( this.rrules, function(item) {return item.copy();}), 'exrules': recurrence.array.foreach( this.exrules, function(item) {return item.copy();}), 'rdates': recurrence.array.foreach( this.rdates, function(item) {return item.copy();}), 'exdates': recurrence.array.foreach( this.exdates, function(item) {return item.copy();}) }); }, serialize: function() { return recurrence.serialize(this); } }; recurrence.Weekday = function(number, index) { this.init(number, index); }; recurrence.Weekday.prototype = { init: function(number, index) { this.number = number; this.index = index || null; }, with_index: function(index) { if (index == this.index) return this; else return new recurrence.Weekday(this.number, index); }, equals: function(other) { if (this.number == other.number && this.index == other.index) return True; else return False; }, toString: function() { if (this.index) return this.index + recurrence.weekdays[this.number]; else return recurrence.weekdays[this.number]; } }; recurrence.DateFormat = function(date) { this.init(date); }; recurrence.DateFormat.prototype = { init: function(date) { this.data = date; }, format: function(format) { var tokens = format.match(recurrence.DateFormat.formatchars); recurrence.array.foreach(tokens, function(token) { if (this[token.charAt(1)]) format = format.replace(token, this[token.charAt(1)]()); }, this); return format; }, a: function() { if (this.data.getHours() > 11) return recurrence.display.ampm.am; else return recurrence.display.ampm.pm; }, A: function() { if (this.data.getHours() > 11) return recurrence.display.ampm.AM; else return recurrence.display.ampm.PM; }, f: function() { if (this.data.getMinutes() == 0) return this.g(); else return [this.g(), this.i()].join(':'); }, g: function() { if (this.data.getHours() == 0) return 12; else return this.data.getHours() - 12; return this.data.getHours(); }, G: function() { return this.data.getHours(); }, h: function() { return recurrence.string.rjust(String(this.g()), 2, '0'); }, H: function() { return recurrence.string.rjust(String(this.G()), 2, '0'); }, i: function() { return recurrence.string.rjust(String(this.data.getMinutes()), 2, '0'); }, P: function() { if (this.data.getMinutes() == 0 && this.data.getHours() == 0) return recurrence.display.tokens.midnight; if (this.data.getMinutes() == 0 && this.data.getHours() == 12) return recurrence.display.tokens.noon; }, s: function() { return recurrence.string.rjust(String(this.data.getSeconds()), 2, '0'); }, Y: function() { return this.data.getFullYear(); }, b: function() { return recurrence.display.months_short[this.data.getMonth()]; }, d: function() { return recurrence.string.rjust(String(this.data.getDate()), 2, '0'); }, D: function() { return recurrence.display.weekdays_short[ recurrence.date.weekday(this.data)]; }, F: function() { return recurrence.display.months[this.data.getMonth()]; }, I: function() { var now = new Date(); var date1 = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0); var date2 = new Date(now.getFullYear(), 6, 1, 0, 0, 0, 0); var temp = date1.toGMTString(); var date3 = new Date(temp.substring(0, temp.lastIndexOf(' ')-1)); var temp = date2.toGMTString(); var date4 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1)); var hours_diff_standard_time = (date1 - date3) / (1000 * 60 * 60); var hours_diff_daylight_time = (date2 - date4) / (1000 * 60 * 60); if (hours_diff_daylight_time == hours_diff_standard_time) return '0'; else return '1'; }, j: function() { return this.data.getDate(); }, l: function() { return recurrence.display.weekdays[ recurrence.date.weekday(this.data)]; }, L: function() { return recurrence.date.isleap(this.data); }, m: function() { return recurrence.string.rjust( String(this.data.getMonth() + 1), 2, '0'); }, M: function() { return recurrence.display.months_short[this.data.getMonth()]; }, n: function() { return this.data.getMonth() + 1; }, N: function() { return recurrence.display.months_ap[this.data.getMonth()]; }, O: function() { var seconds = this.Z(); return ( '+' + recurrence.string.rjust( String(Math.floor(seconds / 3600)), 2, '0') + recurrence.string.rjust( String(Math.floor((seconds / 60) % 60)), 2, '0')); }, r: function() { return recurrence.date.format(this, '%D, %j %M %Y %H:%i:%s %O'); }, S: function() { var day = this.data.getDate(); var ordinal_indicator = recurrence.display.ordinal_indicator; var language_code = recurrence.language_code; if (language_code in ordinal_indicator) return ordinal_indicator[language_code](day); return ''; }, t: function() { var month = this.data.getMonth() var ndays = recurrence.DateFormat.mdays[month] + (month == recurrence.FEBRUARY && this.L()); return recurrence.string.rjust(String(ndays), 2, '0'); }, T: function() { var tzname = String(this.data).match(/\([^\)]+\)/g).slice(1, -1); if (!tzname) tzname = recurrence.date.format(this, '%O'); return tzname; }, U: function() { return this.data.getTime(); }, w: function() { return recurrence.date.weekday(this.data.weekday); }, W: function() { var week_number = null; var jan1_weekday = new Date( this.data.getFullYear(), this.data.getMonth(), 1); var weekday = this.data.getDay(); var day_of_year = self.z(); var prev_year = new Date(this.data.getFullYear() - 1, 0, 1); if (day_of_year <= (8 - jan1_weekday) && jan1_weekday > 4) { if (jan1_weekday == 5 || (jan1_weekday == 6 && recurrence.date.isleap(prev_year))) week_number = 53; else week_number = 52; } else { if (recurrence.date.isleap(this.data)) var i = 366; else var i = 365; if ((i - day_of_year) < (4 - weekday)) { week_number = 1; } else { var j = day_of_year + (7 - weekday) + (jan1_weekday - 1); week_number = Math.floor(j / 7); if (jan1_weekday > 4) week_number = week_number - 1; } } return week_number; }, y: function() { return String(this.data.getFullYear()).slice(2); }, Y: function() { return this.data.getFullYear(); }, z: function() { var doy = recurrence.DateFormat.year_days[this.data.getMonth()] + this.data.getDate(); if (this.L() && this.data.getMonth() > 2) doy += 1; return doy; }, Z: function() { var offset = this.data.getTimezoneOffset(); return offset * 60; } }; recurrence.DateFormat.formatchars = RegExp( '%[aAbBdDfFgGhHiIjlLmMnNOPrsStTUwWyYzZ]', 'g'); recurrence.DateFormat.year_days = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; recurrence.DateFormat.mdays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; recurrence.to_weekday = function(token) { if (token.number && token.index) { return new recurrence.Weekday(token.number, token.index); } else if (String(token).match(/[^0-9\-]/g)) { var token = String(token); var constant = token.slice(-2, token.length); var nth = token.slice(0, -2); if (nth.match(/[^0-9\-]/g)) throw Error('Invalid weekday token.'); var weekday = recurrence.weekdays.indexOf(constant); if (weekday < 0) throw Error('Invalid weekday token.'); else return new recurrence.Weekday(weekday, nth || null); } else { return new recurrence.Weekday(parseInt(token, 10)); } throw Error('Invalid weekday token.'); }; recurrence.serialize = function(rule_or_recurrence) { var serialize_dt = function(dt) { var pad = function(initial, length) { initial = String(initial) var offset = length - initial.length; if (offset < 0) { return initial; } else { while (initial.length < length) { initial = '0' + initial; } return initial; } }; return pad(dt.getUTCFullYear(), 4) + pad(dt.getUTCMonth() + 1, 2) + pad(dt.getUTCDate(), 2) + 'T' + pad(dt.getUTCHours(), 2) + pad(dt.getUTCMinutes(), 2) + pad(dt.getUTCSeconds(), 2) + 'Z'; }; var serialize_rule = function(rule) { var map_to_string = function(sequence) { var new_sequence = []; recurrence.array.foreach(sequence, function(item) { new_sequence.push(String(item)); }); return new_sequence; }; var map_to_param = function(sequence) { var new_sequence = []; recurrence.array.foreach(sequence, function(item) { new_sequence.push(item[0] + '=' + item[1].join(',')); }); return new_sequence; }; var values = []; values.push(['FREQ', [recurrence.frequencies[rule.freq]]]); if (rule.interval != 1) values.push(['INTERVAL', [String(rule.interval)]]); if (rule.wkst) values.push(['WKST', [recurrence.weekdays[rule.wkst]]]); if (rule.count != null) values.push(['COUNT', [String(rule.count)]]); else if (rule.until != null) values.push(['UNTIL', [serialize_dt(rule.until)]]); if (rule.byday.length) { var days = recurrence.array.foreach(rule.byday, function(item) { return recurrence.to_weekday(item).toString(); }); values.push(['BYDAY', days]); } recurrence.array.foreach(recurrence.byparams, function(param) { if (param != 'byday') { var value_list = rule[param] || []; if (value_list.length) values.push([param.toUpperCase(), map_to_string(value_list)]); } }); return map_to_param(values).join(';'); }; var map_to_property = function(sequence) { var new_sequence = recurrence.array.foreach( sequence, function(item) { return item.join(':'); }); return new_sequence; }; var obj = rule_or_recurrence; if (obj.freq) obj = new recurrence.Recurrence({'rrules': [obj]}); var items = []; if (obj.dtstart) items.push(['DTSTART', serialize_dt(obj.dtstart)]); if (obj.dtend) items.push(['DTEND', serialize_dt(obj.dtend)]); recurrence.array.foreach( obj.rrules, function(item) { items.push(['RRULE', serialize_rule(item)]); }); recurrence.array.foreach( obj.exrules, function(item) { items.push(['EXRULE', serialize_rule(item)]); }); recurrence.array.foreach( obj.rdates, function(item) { items.push(['RDATE', serialize_dt(item)]); }); recurrence.array.foreach( obj.exdates, function(item) { items.push(['EXDATE', serialize_dt(item)]); }); return map_to_property(items).join('\n'); }; recurrence.deserialize = function(text) { var deserialize_dt = function(text) { var year = parseInt(text.slice(0, 4), 10); var month = parseInt(text.slice(4, 6), 10); var day = parseInt(text.slice(6, 8), 10); if (text.indexOf('T') > 0) { var hour = parseInt(text.slice(9, 11), 10); var minute = parseInt(text.slice(11, 13), 10); var second = parseInt(text.slice(13, 15), 10); } else { var hour = 0; var minute = 0; var second = 0; } var dt = new Date(); if (text.indexOf('Z') > 0) { dt.setUTCFullYear(year); dt.setUTCMonth(month - 1); dt.setUTCDate(day); dt.setUTCHours(hour); dt.setUTCMinutes(minute); dt.setUTCSeconds(second); } else { dt.setFullYear(year); dt.setMonth(month - 1); dt.setDate(day); dt.setHours(hour); dt.setMinutes(minute); dt.setSeconds(second); } return dt; }; var dtstart = null; var dtend = null; var rrules = []; var exrules = []; var rdates = []; var exdates = []; var pattern = /(DTSTART|DTEND|RRULE|EXRULE|RDATE|EXDATE)[^:]*:(.*)/g; var tokens = text.match(pattern) || []; recurrence.array.foreach( tokens, function(token) { var label = token.split(':', 2)[0]; var param_text = token.split(':', 2)[1]; if (param_text.indexOf('=') < 0) { var params = param_text; } else { var param_tokens = param_text.split(';'); var params = recurrence.array.foreach( param_tokens, function(item) { var param_name = recurrence.string.strip( item.split('=', 2)[0]); var param_value = recurrence.string.strip( item.split('=', 2)[1]); var value_list = param_value.split(','); var value_list = recurrence.array.foreach( param_value.split(','), function(item) { return recurrence.string.strip(item); }); return [param_name, value_list]; }); } if (label == 'RRULE' || label == 'EXRULE') { var freq = 0; var options = {}; recurrence.array.foreach( params, function(item) { var key = item[0]; var param = key.toLowerCase(); var value = item[1]; if (key == 'FREQ') { if (recurrence.frequencies.indexOf(value[0]) != -1) { freq = recurrence.frequencies.indexOf(value[0]); } } else if (key == 'INTERVAL') { options[param] = parseInt(value[0], 10); } else if (key == 'WKST') { options[param] = recurrence.to_weekday(value[0]); } else if (key == 'COUNT') { options[param] = parseInt(value[0], 10); } else if (key == 'UNTIL') { options[param] = deserialize_dt(value[0]); } else if (key == 'BYDAY') { options[param] = recurrence.array.foreach( value, function(item) { return recurrence.to_weekday(item); }); } else { options[param] = recurrence.array.foreach( value, function(item) { return parseInt(item, 10); }); } }); if (label == 'RRULE') rrules.push(new recurrence.Rule(freq, options)); else exrules.push(new recurrence.Rule(freq, options)); } else if (label == 'DTSTART') { dtstart = deserialize_dt(params); } else if (label == 'DTEND') { dtend = deserialize_dt(params); } else if (label == 'RDATE') { rdates.push(deserialize_dt(params)); } else if (label == 'EXDATE') { exdates.push(deserialize_dt(params)); } }); return new recurrence.Recurrence({ 'dtstart': dtstart, 'dtend': dtend, 'rrules': rrules, 'exrules': exrules, 'rdates': rdates, 'exdates': exdates }); }; recurrence.log = function(message) { var dom = document.createElement('div'); dom.innerHTML = message; document.body.insertBefore(dom, document.body.firstChild); }; recurrence.string = { format: function(string, map) { for (var key in map) string = string.replace(RegExp('%' + key, 'g'), map[key]); return string }, capitalize: function(string) { return ( string.charAt(0).toUpperCase() + string.slice(1, string.length)); }, strip: function(string) { return string.replace( /^[ \t\n\r]*/, '').replace(/[ \t\n\r]*$/, ''); }, rjust: function(string, length, character) { var initial = String(string); var offset = length - initial.length; character = character || ' '; if (offset < 0) { return initial; } else { while (initial.length < length) { initial = character.charAt(0) + initial; } return initial; } }, ljust: function(string, length, character) { var initial = String(string); var offset = length - initial.length; character = character || ' '; if (offset < 0) { return initial; } else { while (initial.length < length) { initial = initial + character[0]; } return initial; } } }; recurrence.date = { format: function(date, format) { return new recurrence.DateFormat(date).format(format); }, isleap: function(date) { var year = date.getFullYear(); return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); }, weekday: function(date) { var day = date.getDay() - 1; if (day < 0) day = day + 7; else if (day > 6) day = day - 7; return day; }, days_in_month: function(date) { var m = date.getMonth() + 1; var y = date.getFullYear(); if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) return 31; else if (m == 4 || m == 6 || m == 9 || m == 11) return 30; else if (m == 2 && recurrence.date.isleap(date)) return 29; else return 28; } }; recurrence.array = { foreach: function(array, func, bindto) { if (bindto) func = recurrence.func.bind(func, bindto); array = recurrence.array.from(array); var return_values = []; for (var i=0; i < array.length; i++) return_values.push(func(array[i], i)); return return_values; }, from: function(iterable) { if (!iterable) return []; if (iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i=0, length=iterable.length; i < length; i++) results.push(iterable[i]); return results; } }, remove: function(array, item) { array.splice(array.indexOf(item), 1); } }; recurrence.func = { bind: function() { var args = recurrence.array.from(arguments); var func = args.shift(); var object = args.shift(); return function() { return func.apply( object, args.concat(recurrence.array.from(arguments))); } } }; if (!Array.indexOf) { // ie doesn't have indexOf on arrays Array.prototype.indexOf = function(obj) { for (var i=0; i < this.length; i++) if (this[i] == obj) return i; return -1; }; } // frequencies recurrence.YEARLY = 0; recurrence.MONTHLY = 1; recurrence.WEEKLY = 2; recurrence.DAILY = 3; recurrence.HOURLY = 4; recurrence.MINUTELY = 5; recurrence.SECONDLY = 6; // months recurrence.JANUARY = 1; recurrence.FEBRUARY = 2; recurrence.MARCH = 3; recurrence.APRIL = 4; recurrence.MAY = 5; recurrence.JUNE = 6; recurrence.JULY = 7; recurrence.AUGUST = 8; recurrence.SEPTEMBER = 9; recurrence.OCTOBER = 10; recurrence.NOVEMBER = 11; recurrence.DECEMBER = 12; // weekdays recurrence.MONDAY = recurrence.MO = new recurrence.Weekday(0, null); recurrence.TUEDSAY = recurrence.TU = new recurrence.Weekday(1, null); recurrence.WEDNESDAY = recurrence.WE = new recurrence.Weekday(2, null); recurrence.THURSDAY = recurrence.TH = new recurrence.Weekday(3, null); recurrence.FRIDAY = recurrence.FR = new recurrence.Weekday(4, null); recurrence.SATURDAY = recurrence.SA = new recurrence.Weekday(5, null); recurrence.SUNDAY = recurrence.SU = new recurrence.Weekday(6, null); // enumerations recurrence.byparams = [ 'bysetpos', 'bymonth', 'bymonthday', 'byyearday', 'byweekno', 'byday', 'byhour', 'byminute', 'bysecond' ]; recurrence.frequencies = [ 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY' ]; recurrence.weekdays = [ 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU' ]; // recurrence.firstweekday = 0; // i18n no-ops if jsi18n not loaded if (typeof(catalog) === 'undefined') { var catalog = []; } else { var catalog = catalog; } var gettext = gettext || function(msgid) { var value = catalog[msgid]; if (typeof(value) == 'undefined') { return msgid; } else { return (typeof(value) == 'string') ? value : value[0]; } }; var interpolate = interpolate || function(fmt, obj, named) { if (named) { return fmt.replace(/%\(\w+\)s/g, function(match) { return String(obj[match.slice(2,-2)]) }); } else { return fmt.replace(/%s/g, function(match) { return String(obj.shift()) }); } }; // display if (!recurrence.display) recurrence.display = {}; recurrence.display.tokens = { 'midnight': gettext('midnight'), 'noon': gettext('noon'), 'on_the_items': gettext('on the %(items)s'), 'every_number_freq': gettext('every %(number)s %(freq)s'), 'each': gettext('each %(items)s'), 'count': gettext('occuring %(number)s time'), 'count_plural': gettext('occuring %(number)s times'), 'until': gettext('until %(date)s') }; recurrence.display.timeintervals = [ gettext('year'), gettext('month'), gettext('week'), gettext('day'), gettext('hour'), gettext('minute'), gettext('second') ]; recurrence.display.timeintervals_plural = [ gettext('years'), gettext('months'), gettext('weeks'), gettext('days'), gettext('hours'), gettext('minutes'), gettext('seconds') ]; recurrence.display.frequencies = [ gettext('annually'), gettext('monthly'), gettext('weekly'), gettext('daily'), gettext('hourly'), gettext('minutely'), gettext('secondly') ]; recurrence.display.weekdays = [ gettext('Monday'), gettext('Tuesday'), gettext('Wednesday'), gettext('Thursday'), gettext('Friday'), gettext('Saturday'), gettext('Sunday') ]; recurrence.display.weekdays_short = [ gettext('Mon'), gettext('Tue'), gettext('Wed'), gettext('Thu'), gettext('Fri'), gettext('Sat'), gettext('Sun') ]; recurrence.display.weekdays_oneletter = [ pgettext('Monday first letter', 'M'), pgettext('Tuesday first letter', 'T'), pgettext('Wednesday first letter', 'W'), pgettext('Thursday first letter', 'T'), pgettext('Friday first letter', 'F'), pgettext('Saturday first letter', 'S'), pgettext('Sunday first letter', 'S') ]; recurrence.display.weekdays_position = { '1': gettext('first %(weekday)s'), '2': gettext('second %(weekday)s'), '3': gettext('third %(weekday)s'), '4': gettext('fourth %(weekday)s'), '-1': gettext('last %(weekday)s'), '-2': gettext('second last %(weekday)s'), '-3': gettext('third last %(weekday)s') }; recurrence.display.weekdays_position_short = { '1': gettext('1st %(weekday)s'), '2': gettext('2nd %(weekday)s'), '3': gettext('3rd %(weekday)s'), '4': gettext('4th %(weekday)s'), '-1': gettext('last %(weekday)s'), '-2': gettext('2nd last %(weekday)s'), '-3': gettext('3rd last %(weekday)s') }; recurrence.display.months = [ gettext('January'), gettext('February'), gettext('March'), gettext('April'), pgettext('month name', 'May'), gettext('June'), gettext('July'), gettext('August'), gettext('September'), gettext('October'), gettext('November'), gettext('December') ]; recurrence.display.months_short = [ gettext('Jan'), gettext('Feb'), gettext('Mar'), gettext('Apr'), pgettext('month name', 'May'), gettext('Jun'), gettext('Jul'), gettext('Aug'), gettext('Sep'), gettext('Oct'), gettext('Nov'), gettext('Dec') ]; recurrence.display.months_ap = [ gettext('Jan.'), gettext('Feb.'), gettext('March'), gettext('April'), pgettext('month name', 'May'), gettext('June'), gettext('July'), gettext('Aug.'), gettext('Sept.'), gettext('Oct.'), gettext('Nov.'), gettext('Dec.') ]; recurrence.display.ampm = { 'am': gettext('a.m.'), 'pm': gettext('p.m.'), 'AM': gettext('AM'), 'PM': gettext('PM') }; recurrence.display.ordinal_indicator = { 'en-us': function(day) { if (day == 11 || day == 12 || day == 13) return 'th'; var last = day % 10; if (last == 1) return 'st'; if (last == 2) return 'nd'; if (last == 3) return 'rd'; return 'th'; }, 'fr-FR': function(day) { if (day == 1) return 'er'; return ''; } }; django-recurrence-1.2.0/setup.cfg0000644000076500000240000000007312554754171017172 0ustar cezarstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 django-recurrence-1.2.0/setup.py0000644000076500000240000000427012554752650017066 0ustar cezarstaff00000000000000import os import sys try: from setuptools import setup has_setuptools = True except ImportError: from distutils.core import setup has_setuptools = False if has_setuptools: from setuptools.command.test import test as TestCommand class PyTest(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): import pytest errno = pytest.main(self.test_args) sys.exit(errno) setup_options = dict( install_requires=( 'pytz', 'python-dateutil', ), zip_safe=False, include_package_data=True, cmdclass={'test': PyTest}, ) else: setup_options = dict( requires=( 'pytz', 'python_dateutil', ), ) setup( name='django-recurrence', version='1.2.0', license='BSD', description='Django utility wrapping dateutil.rrule', author='Tamas Kemenczy', author_email='tamas.kemenczy@gmail.com', classifiers=( 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', "Programming Language :: Python :: 2", 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", ), requires=( 'Django', 'pytz', 'python_dateutil', ), packages=( 'recurrence', ), package_dir={ 'recurrence': 'recurrence' }, package_data={ 'recurrence': [ os.path.join('static', '*.css'), os.path.join('static', '*.png'), os.path.join('static', '*.js'), os.path.join('locale', '*.po'), os.path.join('locale', '*.mo'), ], }, **setup_options )