Skip to content

Commit

Permalink
[609027] Video modal
Browse files Browse the repository at this point in the history
* Adds a common function for video/image hooks
* Adds support for modal + placeholder html text + title
* Multiple modals on the same page
* Close modals with escape or by clicking on overlay
  • Loading branch information
Paul Craciunoiu committed Nov 19, 2010
1 parent 783b681 commit 926a86a
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 122 deletions.
80 changes: 43 additions & 37 deletions apps/sumo/parser.py
Expand Up @@ -24,7 +24,9 @@
'video': ['height', 'width', 'controls', 'data-fallback'],
'source': ['src', 'type'],
}
IMAGE_PARAMS = {
IMAGE_PARAMS = ['alt', 'align', 'caption', 'valign', 'frame', 'page', 'link',
'width', 'height']
IMAGE_PARAM_VALUES = {
'align': ('none', 'left', 'center', 'right'),
'valign': ('baseline', 'sub', 'super', 'top', 'text-top', 'middle',
'bottom', 'text-bottom'),
Expand Down Expand Up @@ -82,34 +84,56 @@ def _get_wiki_link(title, locale):
title=title)}


def _build_image_params(items, locale):
"""
Builds a list of items and return image-relevant parameters in a dict.
def build_hook_params(string, locale, allowed_params=[],
allowed_param_values={}):
"""Parses a string of the form 'some-title|opt1|opt2=arg2|opt3...'
Builds a list of items and returns relevant parameters in a dict.
"""
if not '|' in string: # No params? Simple and easy.
string = string.strip()
return (string, {'alt': string})

items = [i.strip() for i in string.split('|')]
title = items.pop(0)
params = {}
# Empty items returns empty params
if not items:
return params

for item in items:
if item.find('=') != -1:
last_item = ''
for item in items: # this splits by = or assigns the dict key to True
if '=' in item:
param, value = item.split('=', 1)
params[param] = value
else:
params[item] = True
last_item = item

if 'caption' in allowed_params:
params['caption'] = title
# Allowed parameters are not caption. All else is.
if last_item and last_item not in allowed_params:
params['caption'] = items.pop()
del params[last_item]
elif last_item == 'caption':
params['caption'] = last_item

# Validate params allowed
for p in params.keys():
if p not in allowed_params:
del params[p]

# Validate params with limited # of values
for p in allowed_param_values:
if p in params and params[p] not in allowed_param_values[p]:
del params[p]

# Handle page as a special case
if 'page' in params and params['page'] is not True:
link = _get_wiki_link(params['page'], locale)
params['link'] = link['url']
params['found'] = link['found']

# Validate params with limited # of values
for param_allowed in IMAGE_PARAMS:
if (param_allowed in params and
not (params[param_allowed] in IMAGE_PARAMS[param_allowed])):
del params[param_allowed]

return params
return (title, params)


class WikiParser(Parser):
Expand Down Expand Up @@ -172,32 +196,14 @@ def _hook_internal_link(self, parser, space, name):

def _hook_image_tag(self, parser, space, name):
"""Adds syntax for inserting images."""
title = name
caption = name
params = {}

# Parse the inner syntax, e.g. [[Image:src|option=val|caption]]
separator = name.find('|')
items = []
if separator != -1:
items = title.split('|')
title = items[0]
# If the last item contains '=', it's not a caption
if items[-1].find('=') == -1:
caption = items[-1]
items = items[1:-1]
else:
caption = title
items = items[1:]
title, params = build_hook_params(name, self.locale, IMAGE_PARAMS,
IMAGE_PARAM_VALUES)

message = _lazy('The image "%s" does not exist.') % title
image = get_object_fallback(Image, title, self.locale, message)
if isinstance(image, basestring):
return image

# parse the relevant items
params = _build_image_params(items, self.locale)

template = jingo.env.get_template('wikiparser/hook_image.html')
r_kwargs = {'image': image, 'caption': caption, 'params': params}
r_kwargs = {'image': image, 'params': params}
return template.render(**r_kwargs)
8 changes: 4 additions & 4 deletions apps/sumo/templates/wikiparser/hook_image.html
Expand Up @@ -22,8 +22,8 @@

<img {% if not has_frame %}class="frameless"{% endif %}
src="{{ image.file.url }}"
alt="{% if 'alt' in params %}{{ params['alt'] }}{% else %}{{ caption }}{% endif %}"
title="{{ caption }}"
alt="{% if 'alt' in params %}{{ params['alt'] }}{% else %}{{ params['caption'] }}{% endif %}"
title="{{ params['caption'] }}"
{% if params['width'] and params['width']|int(None) %}
width="{{ params['width']|int('') }}"
{% endif %}
Expand All @@ -41,12 +41,12 @@

{% if has_frame %}

{% if caption %}
{% if params['caption'] %}
<div class="caption"
{% if params['width'] %}
style="width:{{ params['width']|int('') }}px"
{% endif %}
>{{ caption }}</div>
>{{ params['caption'] }}</div>
{% endif %}
</div>
</div>{# /classes #}
Expand Down
51 changes: 22 additions & 29 deletions apps/sumo/tests/test_parser.py
Expand Up @@ -6,8 +6,8 @@
from pyquery import PyQuery as pq

from gallery.tests import image
from sumo.parser import (WikiParser, _build_image_params, _get_wiki_link,
get_object_fallback)
from sumo.parser import (WikiParser, build_hook_params, _get_wiki_link,
get_object_fallback, IMAGE_PARAMS, IMAGE_PARAM_VALUES)
from sumo.tests import TestCase
from wiki.models import Document
from wiki.tests import document, revision
Expand All @@ -31,8 +31,10 @@ def doc_rev_parser(content, title='Installing Firefox', parser_cls=WikiParser):
return (d, r, p)


_build_image_params_default = partial(_build_image_params,
locale=settings.WIKI_DEFAULT_LANGUAGE)
build_hook_params_default = partial(build_hook_params,
locale=settings.WIKI_DEFAULT_LANGUAGE,
allowed_params=IMAGE_PARAMS,
allowed_param_values=IMAGE_PARAM_VALUES)


class TestWikiParser(TestCase):
Expand Down Expand Up @@ -98,69 +100,60 @@ def test_get_object_fallback_translated(self):
eq_(fr_d, obj)

def test_image_params_page(self):
"""_build_image_params handles wiki pages."""
items = ['page=Installing Firefox']
params = _build_image_params_default(items)
"""build_hook_params handles wiki pages."""
_, params = build_hook_params_default('t|page=Installing Firefox')
eq_('/en-US/kb/installing-firefox', params['link'])
assert params['found']

def test_image_params_link(self):
"""_build_image_params handles external links."""
items = ['link=http://example.com']
params = _build_image_params_default(items)
_, params = build_hook_params_default('t|link=http://example.com')
eq_('http://example.com', params['link'])

def test_image_params_page_link(self):
"""_build_image_params - wiki page overrides link."""
items = ['page=Installing Firefox', 'link=http://example.com']
params = _build_image_params_default(items)
text = 't|page=Installing Firefox|link=http://example.com'
_, params = build_hook_params_default(text)
eq_('/en-US/kb/installing-firefox', params['link'])

def test_image_params_align(self):
"""Align valid options."""
align_vals = ('none', 'left', 'center', 'right')
for align in align_vals:
items = ['align=' + align]
params = _build_image_params_default(items)
_, params = build_hook_params_default('test.jpg|align=' + align)
eq_(align, params['align'])

def test_image_params_align_invalid(self):
"""Align invalid options."""
items = ['align=zzz']
params = _build_image_params_default(items)
_, params = build_hook_params_default('align=zzz')
assert not 'align' in params, 'Align is present in params'

def test_image_params_valign(self):
"""Vertical align valid options."""
valign_vals = ('baseline', 'sub', 'super', 'top', 'text-top',
'middle', 'bottom', 'text-bottom')
for valign in valign_vals:
items = ['valign=' + valign]
params = _build_image_params_default(items)
_, params = build_hook_params_default('title|valign=' + valign)
eq_(valign, params['valign'])

def test_image_params_valign_invalid(self):
"""Vertical align invalid options."""
items = ['valign=zzz']
params = _build_image_params_default(items)
_, params = build_hook_params_default('valign=zzz')
assert not 'valign' in params, 'Vertical align is present in params'

def test_image_params_alt(self):
"""Image alt override."""
items = ['alt=some alternative text']
params = _build_image_params_default(items)
_, params = build_hook_params_default('t|alt=some alternative text')
eq_('some alternative text', params['alt'])

def test_image_params_frameless(self):
"""Frameless image."""
items = ['frameless']
params = _build_image_params_default(items)
assert params['frameless']
def test_image_params_frame(self):
"""Framed image."""
_, params = build_hook_params_default('title|frame')
assert params['frame']

def test_image_params_width_height(self):
"""Image width."""
items = ['width=10', 'height=20']
params = _build_image_params_default(items)
_, params = build_hook_params_default('t|width=10|height=20')
eq_('10', params['width'])
eq_('20', params['height'])

Expand Down Expand Up @@ -478,7 +471,7 @@ def test_height_invalid(self):

eq_(None, img.attr('height'))

def test_frameless(self):
def test_frame(self):
"""Image has frame if specified."""
img_div = pq_img(self.p, '[[Image:test.jpg|frame|caption]]', 'div.img')
assert not img_div('img').hasClass('frameless')
Expand Down
38 changes: 18 additions & 20 deletions apps/wiki/parser.py
Expand Up @@ -8,27 +8,22 @@
from html5lib.serializer.htmlserializer import HTMLSerializer
from html5lib.treebuilders import getTreeBuilder
from html5lib.treewalkers import getTreeWalker
import jingo
from lxml.etree import Element

from tower import ugettext as _, ugettext_lazy as _lazy

from gallery.models import Video
import sumo.parser
from sumo.parser import ALLOWED_ATTRIBUTES, get_object_fallback
from sumo.parser import (ALLOWED_ATTRIBUTES, get_object_fallback,
build_hook_params)


BLOCK_LEVEL_ELEMENTS = ['table', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5',
'h6', 'td', 'th', 'div', 'hr', 'pre', 'p', 'li', 'ul',
'ol', 'center'] # from Parser.doBlockLevels

VIDEO_PARAMS = ['height', 'width', 'modal', 'title', 'placeholder']
TEMPLATE_ARG_REGEX = re.compile('{{{([^{]+?)}}}')
SOURCE_TEMPLATE = '<source src="%(src)s" type="video/%(type)s">'
VIDEO_TEMPLATE = ('<div class="video">'
'<video%(fallback)s height="%(height)s"'
'width="%(width)s" controls="">'
'%(sources)s'
'</video>'
'</div>')


def wiki_to_html(wiki_markup, locale=settings.WIKI_DEFAULT_LANGUAGE):
Expand Down Expand Up @@ -367,27 +362,30 @@ def _hook_template(self, parser, space, title):
def _hook_video(self, parser, space, title):
"""Handles [[Video:video title]] with locale from parser."""
message = _lazy('The video "%s" does not exist.') % title

# params, only modal supported for now
title, params = build_hook_params(title, self.locale, VIDEO_PARAMS)

v = get_object_fallback(Video, title, self.locale, message)
if isinstance(v, basestring):
return v

return generate_video(v)
return generate_video(v, params)


def generate_video(v):
def generate_video(v, params=[]):
"""Takes a video object and returns HTML markup for embedding it."""
sources = []
if v.webm:
sources.append(SOURCE_TEMPLATE % {'src': v.webm.url,
'type': 'webm'})
sources.append({'src': v.webm.url, 'type': 'webm'})
if v.ogv:
sources.append(SOURCE_TEMPLATE % {'src': v.ogv.url,
'type': 'ogg'})
sources.append({'src': v.ogv.url, 'type': 'ogg'})
data_fallback = ''
# Flash fallback
if v.flv:
data_fallback = ' data-fallback="' + v.flv.url + '"'
return VIDEO_TEMPLATE % {'fallback': data_fallback,
'sources': ''.join(sources),
'height': settings.WIKI_VIDEO_HEIGHT,
'width': settings.WIKI_VIDEO_WIDTH}
data_fallback = v.flv.url
return jingo.env.get_template('wikiparser/hook_video.html').render(
{'fallback': data_fallback, 'sources': sources, 'params': params,
'video': v,
'height': settings.WIKI_VIDEO_HEIGHT,
'width': settings.WIKI_VIDEO_WIDTH})
37 changes: 37 additions & 0 deletions apps/wiki/templates/wikiparser/hook_video.html
@@ -0,0 +1,37 @@
{# vim: set ts=2 et sts=2 sw=2: #}

<div class="video{% if params['modal'] %} modal-trigger{% endif %}">
{% if params['modal'] %}
<a href="{% if sources %}{{ sources[0].src }}{% else %}{{ fallback }}{% endif %}" class="video-modal-trigger" data-modal-selector="">
<span class="video-placeholder">
{% if params['placeholder'] %}
{{ params['placeholder']|safe }}
{% else %}
<img class="video-thumbnail" src="{{ video.thumbnail_url_if_set() }}"/>
{% endif %}
</span>
</a>{# /video-modal#}
<div class="video-modal fixed-modal pop-in">
<h1>
{% if params['title'] %}
{{ params['title'] }}
{% else %}
{{ video.title }}
{% endif %}
</h1>
<div class="wrap">
{% endif %}

<video data-fallback="{{ fallback }}" height="{{ height }}" width="{{ width }}" controls="">
{% for s in sources %}
<source src="{{ s.src }}" type="video/{{ s.type }}">
{% endfor %}
</video>

{% if params['modal'] %}
</div>
</div>
{% endif %}
</div>


0 comments on commit 926a86a

Please sign in to comment.