/fa/jquery/renderers.py
https://bitbucket.org/gawel/fajquery · Python · 506 lines · 493 code · 10 blank · 3 comment · 6 complexity · 66a8a2cc1c901bb4bba07fad1cfdf3e1 MD5 · raw file
- # -*- coding: utf-8 -*-
- import os
- from simplejson import dumps
- from webhelpers.html import literal
- from webhelpers.html.tools import strip_tags
- from webhelpers import text
- from formalchemy import helpers as h
- from formalchemy import types
- from formalchemy import fields
- from formalchemy import config
- from postmarkup import render_bbcode
- from textile import textile as render_textile
- from markdown import markdown as render_markdown
- from utils import TemplateEngine
- from utils import templates
- from utils import load_datas
- from utils import url
- __doc__ = """
- This is the predefined renderers. You can have a look at the :doc:`../demo`.
- If you need your own, use the :class:`~fa.jquery.renderers.jQueryFieldRenderer`
- as base class.
- Some plugins use extra resources stored at the
- :class:`~fa.jquery.wsgi.StaticApp`. By default the prefix used is ``/jquery``.
- If you want to change this you can use this snippet:
- .. sourcecode:: python
- >>> from fa.jquery import renderers
- >>> renderers.url.root_url = '/jquery'
- """
- def alias(obj, **alias_kwargs):
- """decorator to make aliases with docs"""
- def wrapped(func):
- if hasattr(obj, 'func_name'):
- def wrapper(*args, **kwargs):
- """Alias for :func:`~fa.jquery.renderers.%s` with preset options %r""" % (obj.func_name, alias_kwargs)
- kwargs.update(alias_kwargs)
- return obj(*args, **kwargs)
- wrapper.func_name = func.func_name
- wrapper.func_doc = """Alias for :func:`~fa.jquery.renderers.%s` with preset options %r""" % (obj.func_name, alias_kwargs)
- return wrapper
- else:
- doc = """Alias for :class:`~fa.jquery.renderers.%s` with preset options %r""" % (obj.__name__, alias_kwargs)
- alias_kwargs['__doc__'] = doc
- return type(func.func_name, (obj,), alias_kwargs)
- return wrapped
- def jQueryFieldRenderer(plugin, show_input=False, tag='div', renderer=fields.TextFieldRenderer,
- resources_prefix=None, resources=[], **jq_options):
- """Extending jQuery.fa:
- .. sourcecode:: python
- >>> from testing import fs
- >>> renderer = jQueryFieldRenderer('myplugin', option1=True, option2=['a', 'b'])
- >>> field = fs.title.set(renderer=renderer)
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <div style="display:none;"><input autocomplete="off" id="Sample--title" name="Sample--title" type="text" /></div>
- <div id="Sample--title_myplugin"></div>
- <script type="text/javascript">
- jQuery.fa.myplugin('Sample--title', {"option2": ["a", "b"], "options": [], "option1": true});
- </script>...
- Then in your javascript code:
- .. sourcecode:: javascript
- jQuery.fa.extend({
- myplugin: function(field, plugin, options) {
- // do what you want
- }
- });
- Where field is the input, plugin the empty div and options the jq_options passed to the renderer.
- """
- template_name = jq_options.get('_template', 'jquery')
- template=templates.get_template('/renderers/%s.mako' % template_name)
- class Renderer(renderer):
- def render(self, **kwargs):
- if 'autocomplete' in kwargs:
- kwargs.pop('autocomplete')
- html = renderer.render(self, autocomplete='off', **kwargs)
- kwargs.update(self.jq_options)
- options = dict(
- tag=tag,
- html=html,
- plugin=plugin,
- name=self.name,
- show_input=show_input,
- resources=[url(r, prefix=self.resources_prefix) for r in resources],
- )
- try:
- options.update(options=dumps(kwargs))
- except TypeError:
- options.update(options={})
- try:
- return literal(self.template.render(**options))
- except:
- raise ValueError('Invalid options: %s' % options)
- return type('%sPluginRenderer' % plugin.title(), (Renderer,),
- dict(template=template,
- jq_options=jq_options,
- resources_prefix=resources_prefix))
- @alias(jQueryFieldRenderer)
- def plugin(): pass
- def AutoCompleteFieldRenderer(url_or_data, renderer=fields.TextFieldRenderer, **jq_options):
- """Use http://docs.jquery.com/UI/Autocomplete:
- .. sourcecode:: python
- >>> from testing import fs
- >>> field = fs.title.set(renderer=AutoCompleteFieldRenderer(['aa', 'bb']))
- With more advanced options:
- .. sourcecode:: python
- >>> field = fs.title.set(
- ... renderer=AutoCompleteFieldRenderer(
- ... '/my/uri',
- ... width=320,
- ... scroll=True,
- ... scrollHeight=300,
- ... ))
- """
- jq_options.update(source=url_or_data, show_input=False)
- return jQueryFieldRenderer('autocomplete', renderer=renderer, **jq_options)
- @alias(AutoCompleteFieldRenderer)
- def autocomplete(): pass
- def SortableTokenTextFieldRenderer(sep=';', show_input=False, **jq_options):
- """Sortable token using http://jqueryui.com/demos/sortable/:
- .. sourcecode:: python
- >>> from testing import fs
- >>> field = fs.sortable.set(renderer=SortableTokenTextFieldRenderer())
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <input type="hidden" value="fisrt;second" id="Sample--sortable" name="Sample--sortable" />
- <ul id="Sample--sortable_sortable" class="fa_sortable">
- <li class="ui-state-default" alt="fisrt"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>fisrt</li>
- <li class="ui-state-default" alt="second"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>second</li>
- </ul>
- <script type="text/javascript">
- jQuery.fa.sortable('Sample--sortable', {sep:';'});
- </script>...
- """
- class Renderer(fields.TextFieldRenderer):
- template=templates.get_template('/renderers/sortable.mako')
- def render_readonly(self):
- return ', '.join(self.raw_value.split(sep))
- def render(self, **kwargs):
- value=self.value.strip(sep)
- tokens = value and value.split(sep) or ''
- tokens = [(v, v) for v in tokens]
- kwargs.update(
- name=self.name,
- sep=sep,
- value=value,
- tokens=tokens,
- show_input=show_input,
- jq_options=dumps(jq_options),
- )
- return literal(self.template.render(**kwargs))
- return Renderer
- @alias(SortableTokenTextFieldRenderer)
- def sortable_token(): pass
- def ColorPickerFieldRenderer(colors=[], **jq_options):
- """Color Picker using http://www.syronex.com/software/jquery-color-picker:
- .. sourcecode:: python
- >>> from testing import fs
- >>> print fs.color.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <div style="display:none;"><input autocomplete="off" id="Sample--color" name="Sample--color" type="text" /></div>
- <div id="Sample--color_colorpicker"></div>
- <script type="text/javascript">
- jQuery.fa.colorpicker('Sample--color', {"color": ["#FFFFFF", ...]});
- </script>
- <BLANKLINE>
-
- <div style="display:none;"><input autocomplete="off" id="Sample--color" name="Sample--color" type="text" /></div>
- <div id="Sample--color_colors"></div>
- <script type="text/javascript">
- jQuery.fa.colorpicker('Sample--color', {"color": ["#FFFFFF", ..., "#FF0096", "#B02B2C", "#000000"]});
- </script>...
- """
- jq_options['color'] = colors or [
- "#FFFFFF", "#EEEEEE", "#FFFF88", "#FF7400", "#CDEB8B", "#6BBA70",
- "#006E2E", "#C3D9FF", "#4096EE", "#356AA0", "#FF0096", "#B02B2C",
- "#000000"
- ]
- class Renderer(fields.TextFieldRenderer):
- def render_readonly(self, **kwargs):
- v = self.raw_value
- if not v:
- return ''
- return h.literal('<div style="background:%s;">%s</div>' % (v, v))
- return jQueryFieldRenderer('colorpicker', renderer=Renderer, **jq_options)
- @alias(ColorPickerFieldRenderer)
- def colorpicker(): pass
- class DateFieldRenderer(fields.DateFieldRenderer):
- """Use http://jqueryui.com/demos/datepicker/:
- .. sourcecode:: python
- >>> from testing import fs
- >>> print fs.date.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <input type="text" autocomplete="off" size="10" value="" id="Sample--date" name="Sample--date" />
- <script type="text/javascript">
- jQuery.fa.datepicker('Sample--date', {"dateFormat": "yy-mm-dd"});
- </script>...
- """
- template = templates.get_template('/renderers/date.mako')
- jq_options = dict(dateFormat='yy-mm-dd')
- def render(self, **kwargs):
- value = self.value or ''
- value = value and value.split()[0] or ''
- kwargs.update(
- name=self.name,
- value=value,
- jq_options=dumps(self.jq_options),
- )
- return literal(self.template.render(**kwargs))
- def _serialized_value(self):
- value = self.params.getone(self.name) or ''
- return value
- @alias(DateFieldRenderer)
- def date(): pass
- class DateTimeFieldRenderer(DateFieldRenderer, fields.TimeFieldRenderer):
- """Use http://jqueryui.com/demos/datepicker/"""
- format = '%Y-%m-%d %H:%M:%S'
- template = templates.get_template('/renderers/date.mako')
- jq_options = dict(dateFormat='yy-mm-dd')
- def render(self, **kwargs):
- return h.content_tag('span', DateFieldRenderer.render(self, **kwargs) + literal(' ') + \
- literal(fields.TimeFieldRenderer._render(self, **kwargs)))
- def _serialized_value(self):
- date = DateFieldRenderer._serialized_value(self)
- if date:
- return date + ' ' + fields.TimeFieldRenderer._serialized_value(self)
- else:
- return ''
- @alias(DateTimeFieldRenderer)
- def datetime(): pass
- def SliderFieldRenderer(min=0, max=100, show_value=True, **jq_options):
- """Fill an integer field using http://jqueryui.com/demos/slider/:
- .. sourcecode:: python
- >>> from testing import fs
- >>> field = fs.slider.set(renderer=SliderFieldRenderer(min=10, max=150))
- """
- jq_options.update(min=min, max=max, show_value=show_value)
- return jQueryFieldRenderer('slider', renderer=fields.IntegerFieldRenderer, **jq_options)
- @alias(SliderFieldRenderer)
- def slider(): pass
- def SelectableFieldRenderer(multiple=False, **jq_options):
- """Fill a list field using http://jqueryui.com/demos/selectable/:
- .. sourcecode:: python
- >>> from testing import fs
- >>> print fs.selectable.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <div style="display:none;"><select autocomplete="off" id="Sample--selectable" name="Sample--selectable">
- <option value="a">a</option>
- <option value="b">b</option>
- <option value="c">c</option>
- <option value="d">d</option>
- <option value="e">e</option>
- <option value="f">f</option>
- </select></div>
- <div id="Sample--selectable_selectable"></div>
- <script type="text/javascript">
- jQuery.fa.selectable('Sample--selectable', {"multiple": false, "options": ["a", "b", "c", "d", "e", "f"]});
- </script>
- """
- jq_options.update(multiple=multiple)
- class Renderer(fields.SelectFieldRenderer):
- def render(self, *args, **kwargs):
- kwargs['multiple'] = self.multiple
- return fields.SelectFieldRenderer.render(self, *args, **kwargs)
- return jQueryFieldRenderer('selectable',
- renderer=type('SelectableFieldRenderer', (Renderer,), dict(multiple=multiple)),
- **jq_options)
- @alias(SelectableFieldRenderer, multiple=False)
- def selectable():pass
- @alias(SelectableFieldRenderer, multiple=True)
- def selectables():pass
- def ButtonSetFieldRenderer(multiple=False, **jq_options):
- """Fill a list field using http://jqueryui.com/demos/button/:
- .. sourcecode:: python
- >>> from testing import fs
- >>> print fs.radioset.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <div style="display:none;"><input autocomplete="off" id="Sample--radioset_0"
- name="Sample--radioset" type="radio"
- value="a" /><label for="Sample--radioset_0">a</label>...</div>
- <div id="Sample--radioset_buttonset"></div>
- <script type="text/javascript">
- jQuery.fa.buttonset('Sample--radioset', {"multiple": false, "options": ["a", "b", "c", "d", "e", "f"]});
- </script>
- <BLANKLINE>
-
- """
- jq_options.update(multiple=multiple)
- Renderer = multiple and fields.CheckBoxSet or fields.RadioSet
- return jQueryFieldRenderer('buttonset', renderer=Renderer, **jq_options)
- @alias(ButtonSetFieldRenderer, multiple=False)
- def radioset():pass
- @alias(ButtonSetFieldRenderer, multiple=True)
- def checkboxset():pass
- def RichTextFieldRenderer(use='tinymce', resources_prefix=None, **jq_options):
- """RichTextFieldRenderer using TinyMCE or MarkitUp!:
- .. sourcecode:: python
- >>> from testing import fs
- >>> field = fs.rich.set(renderer=RichTextFieldRenderer(use='tinymce', theme='advanced'))
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <script type="text/javascript">
- jQuery.fa.add_resource("/jquery/tiny_mce/tiny_mce.js");
- jQuery.fa.add_resource("/jquery/tiny_mce/jquery.tinymce.js");
- </script>
- <textarea autocomplete="off" id="Sample--rich" name="Sample--rich"></textarea>
- <div id="Sample--rich_tinymce"></div>
- <script type="text/javascript">
- jQuery.fa.tinymce('Sample--rich', {... "theme": "advanced", ...});
- </script>
- If you want to use your own TinyMCE/MarkitUp! version:
- .. sourcecode:: python
- >>> field = fs.rich.set(renderer=RichTextFieldRenderer(use='tinymce', resources_prefix='/my_js'))
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <script type="text/javascript">
- jQuery.fa.add_resource("/my_js/tiny_mce/tiny_mce.js");
- jQuery.fa.add_resource("/my_js/tiny_mce/jquery.tinymce.js");
- </script>
- ...
- There is also some aliases:
- .. sourcecode:: python
- >>> field = fs.rich.set(renderer=tinymce())
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <script type="text/javascript">
- jQuery.fa.add_resource("/jquery/tiny_mce/tiny_mce.js");
- jQuery.fa.add_resource("/jquery/tiny_mce/jquery.tinymce.js");
- </script>
- <textarea autocomplete="off" id="Sample--rich" name="Sample--rich"></textarea>
- ...
- >>> field = fs.rich.set(renderer=textile())
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <script type="text/javascript">
- jQuery.fa.add_resource("/jquery/markitup/jquery.markitup.pack.js");
- jQuery.fa.add_resource("/jquery/markitup/skins/simple/style.css");
- jQuery.fa.add_resource("/jquery/markitup/sets/textile/style.css");
- jQuery.fa.add_resource("/jquery/markitup/sets/textile/set.js");
- </script>
- <textarea autocomplete="off" id="Sample--rich" name="Sample--rich"></textarea>
- ...
-
- >>> field = fs.rich.set(renderer=markdown())
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <script type="text/javascript">
- jQuery.fa.add_resource("/jquery/markitup/jquery.markitup.pack.js");
- jQuery.fa.add_resource("/jquery/markitup/skins/simple/style.css");
- jQuery.fa.add_resource("/jquery/markitup/sets/markdown/style.css");
- jQuery.fa.add_resource("/jquery/markitup/sets/markdown/set.js");
- </script>
- <textarea autocomplete="off" id="Sample--rich" name="Sample--rich"></textarea>
- ...
- >>> field = fs.rich.set(renderer=bbcode())
- >>> print field.render() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
- <script type="text/javascript">
- jQuery.fa.add_resource("/jquery/markitup/jquery.markitup.pack.js");
- jQuery.fa.add_resource("/jquery/markitup/skins/simple/style.css");
- jQuery.fa.add_resource("/jquery/markitup/sets/bbcode/style.css");
- jQuery.fa.add_resource("/jquery/markitup/sets/bbcode/set.js");
- </script>
- <textarea autocomplete="off" id="Sample--rich" name="Sample--rich"></textarea>
- ...
- """
- plugin_name = use
- defaults = {}
- if use == 'tinymce':
- resources = ['tiny_mce/tiny_mce.js', 'tiny_mce/jquery.tinymce.js']
- defaults['theme'] = 'advanced'
- defaults['theme_advanced_toolbar_location'] = "top"
- defaults['theme_advanced_toolbar_align'] = "left"
- defaults['theme_advanced_statusbar_location'] = "bottom"
- defaults['theme_advanced_resizing'] = True
- if 'content_css' in jq_options:
- jq_options['content_css'] = [url(c, prefix=resources_prefix) for c in jq_options['content_css']]
- elif use in ('textile', 'bbcode', 'markdown'):
- plugin_name = 'markitup'
- defaults['nameSpace'] = use
- defaults['resizeHandle'] = True
- defaults['previewAutoRefresh'] = True
- defaults['previewParserPath'] = url('markup_parser.html?markup=%s' % use)
- resources = ['markitup/jquery.markitup.pack.js',
- 'markitup/skins/simple/style.css',
- 'markitup/sets/%s/style.css' % use,
- 'markitup/sets/%s/set.js' % use]
- else:
- resources = []
- for k, v in defaults.items():
- if k not in jq_options:
- jq_options[k] = v
- class Renderer(fields.TextAreaFieldRenderer):
- markup = use
- def render_textile(self, **kwargs):
- value = self.raw_value
- return value and render_textile(value) or ''
- def render_bbcode(self, **kwargs):
- value = self.raw_value
- return value and render_bbcode(value) or ''
- def render_markdown(self, **kwargs):
- value = self.raw_value
- return value and render_markdown(value) or ''
- def render_readonly(self, **kwargs):
- meth = getattr(self, 'render_%s' % self.markup, None)
- if meth is not None:
- return meth()
- return fields.TextAreaFieldRenderer.render_readonly(self, **kwargs)
- return jQueryFieldRenderer(plugin_name, show_input=True, renderer=Renderer,
- resources_prefix=resources_prefix, resources=resources, **jq_options)
- @alias(RichTextFieldRenderer, use='tinymce')
- def tinymce(): pass
- @alias(RichTextFieldRenderer, use='textile')
- def textile(): pass
- @alias(RichTextFieldRenderer, use='markdown')
- def markdown(): pass
- @alias(RichTextFieldRenderer, use='bbcode')
- def bbcode(): pass
- def ellipsys(renderer):
- """Update a renderer to remove tags and strip text"""
- renderer = type(renderer)
- class EllipsysFieldRenderer(renderer):
- def render_readonly(self, **kwargs):
- value = super(EllipsysFieldRenderer, self).render_readonly(**kwargs)
- value = text.truncate(strip_tags(value), 30) if value else ''
- return value
- return EllipsysFieldRenderer
- default_renderers = {
- types.Date:date,
- types.DateTime:datetime,
- types.HTML: markdown(),
- types.Slider: slider,
- types.Color: ColorPickerFieldRenderer,
- types.Selectable: selectable(),
- types.Selectables: selectables(),
- }