mobile personas previewer (bug 654307) + details page (bug 654310)
This commit is contained in:
Родитель
2305d58019
Коммит
b30b42424e
|
@ -218,6 +218,24 @@ def persona_preview(context, persona, size='large', linked=True, extra=None,
|
|||
return c
|
||||
|
||||
|
||||
@register.inclusion_tag('addons/mobile/persona_preview.html')
|
||||
@jinja2.contextfunction
|
||||
def mobile_persona_preview(context, persona):
|
||||
addon = persona.addon
|
||||
c = dict(context.items())
|
||||
c.update({'persona': persona, 'addon': addon})
|
||||
return c
|
||||
|
||||
|
||||
@register.inclusion_tag('addons/mobile/persona_confirm.html')
|
||||
@jinja2.contextfunction
|
||||
def mobile_persona_confirm(context, persona, size='large'):
|
||||
addon = persona.addon
|
||||
c = dict(context.items())
|
||||
c.update({'persona': persona, 'addon': addon, 'size': size})
|
||||
return c
|
||||
|
||||
|
||||
@register.inclusion_tag('addons/persona_grid.html')
|
||||
@jinja2.contextfunction
|
||||
def persona_grid(context, addons):
|
||||
|
|
|
@ -40,18 +40,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if addon.total_reviews %}
|
||||
<a class="listview" href="{{ url('reviews.list', addon.slug) }}">
|
||||
<div class="icon">
|
||||
{{ addon.average_rating|stars }}
|
||||
</div>
|
||||
{% trans num=addon.total_reviews, cnt=addon.total_reviews|numberfmt %}
|
||||
See All Reviews
|
||||
{% pluralize %}
|
||||
See All {{ cnt }} Reviews
|
||||
{% endtrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{{ mobile_reviews_link(addon) }}
|
||||
|
||||
<details>
|
||||
<table>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<header id="home-header">
|
||||
{% include "mobile/header.html" %}
|
||||
<div class="get-fx-message">
|
||||
{{ _('You need Firefox to install addons. <a href="http://mozilla.com/firefox">Learn More »</a>') }}
|
||||
{{ _('You need Firefox to install add-ons. <a href="http://mozilla.com/firefox">Learn More »</a>') }}
|
||||
</div>
|
||||
<hgroup>
|
||||
<h1 class="site-title">
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<div class="persona-confirm{% if size == 'small' %} persona-slider{% endif %}">
|
||||
{% if size == 'large' %}
|
||||
<a href="#" class="button preview">{{ _('Try it on!') }}</a>
|
||||
{% endif %}
|
||||
<div class="confirm-buttons">
|
||||
{{ mobile_install_button(addon, show_warning=False) }}
|
||||
<a href="#" class="button cancel">{{ _('Cancel') }}</a>
|
||||
</div>
|
||||
{% if size == 'small' %}
|
||||
<a href="{{ addon.get_url_path() }}"
|
||||
class="more">{{ _('Persona Info »') }}</a>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -1,18 +1,66 @@
|
|||
{% extends "mobile/base.html" %}
|
||||
|
||||
|
||||
{% block title %}{{ page_title(addon.name) }}{% endblock %}
|
||||
|
||||
{% block page %}
|
||||
{% set persona = addon.persona %}
|
||||
{% block back_link %}
|
||||
<a href="{{ url('browse.personas') }}" id="home">
|
||||
{{ _('« All Personas') }}</a>
|
||||
{% endblock %}
|
||||
|
||||
<div class="listview">
|
||||
{{ persona_preview(addon.persona, size='small') }}
|
||||
{# TODO: button #}
|
||||
{% block page %}
|
||||
{% set author = users_list(addon.listed_authors) or persona.display_username %}
|
||||
|
||||
<div id="persona" class="persona-previewer infobox item">
|
||||
{{ mobile_persona_preview(persona) }}
|
||||
<h3>{{ addon.name }}</h3>
|
||||
<h4 class="author">{{ _('by') }} {{ author }}</h4>
|
||||
<p id="summary"{{ addon.summary|locale_html }}>{{ addon.summary|nl2br }}</p>
|
||||
{{ mobile_persona_confirm(persona) }}
|
||||
</div>
|
||||
|
||||
<table>
|
||||
{% include "addons/persona_detail_table.html" %}
|
||||
</table>
|
||||
{{ mobile_reviews_link(addon) }}
|
||||
|
||||
<details>
|
||||
<table>
|
||||
{% include "addons/mobile/persona_detail_table.html" %}
|
||||
</table>
|
||||
</details>
|
||||
|
||||
<div id="more-personas">
|
||||
{% cache author_personas %}
|
||||
{% if author_personas %}
|
||||
<div id="more-artist" class="listview item">
|
||||
<ul>
|
||||
{% for other in author_personas %}
|
||||
<li class="persona-previewer">
|
||||
{{ mobile_persona_preview(other.persona) }}
|
||||
{{ mobile_persona_confirm(other.persona, size='small') }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li><a href="{{ author_gallery }}">
|
||||
{{ _('See all Personas by this Artist') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
|
||||
{% cache category_personas %}
|
||||
{% if category_personas %}
|
||||
<div id="more-category" class="listview item">
|
||||
<ul>
|
||||
{% for other in category_personas %}
|
||||
<li class="persona-previewer">
|
||||
{{ mobile_persona_preview(other.persona) }}
|
||||
{{ mobile_persona_confirm(other.persona, size='small') }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{# L10n: {0} is a category name, such as Nature #}
|
||||
<li><a href="{{ categories[0].get_url_path() }}">
|
||||
{{ _('See all {0} Personas')|f(categories[0].name) }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<tbody>
|
||||
<tr class="artist">
|
||||
<th>{{ _('Artist') }}</th>
|
||||
<td>{{ author }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ _('Updated') }}</th>
|
||||
<td>
|
||||
<time datetime="{{ addon.modified|isotime }}">{{
|
||||
addon.modified|datetime }}</time>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="meta-stats">
|
||||
<th>{{ _('Daily Users') }}</th>
|
||||
<td>
|
||||
<strong class="downloads">{{
|
||||
persona.popularity|numberfmt }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
{% if persona.license %}
|
||||
<tr>
|
||||
<th>{{ _('License') }}</th>
|
||||
<td>{{ license_link(persona.license) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
|
@ -0,0 +1,4 @@
|
|||
<div class="persona persona-large persona-preview">
|
||||
<div style="background-image:url('{{ persona.preview_url }}')"
|
||||
data-browsertheme="{{ persona.json_data }}"><p></p></div>
|
||||
</div>
|
|
@ -5,13 +5,14 @@ from pyquery import PyQuery
|
|||
|
||||
import amo
|
||||
from addons.helpers import (statusflags, flag, support_addon, contribution,
|
||||
performance_note)
|
||||
performance_note, mobile_persona_preview,
|
||||
mobile_persona_confirm)
|
||||
from addons.models import Addon
|
||||
|
||||
|
||||
class TestHelpers(test_utils.TestCase):
|
||||
fixtures = ['base/apps', 'base/addon_3615', 'base/addon_4664_twitterbar',
|
||||
'addons/featured.json']
|
||||
'addons/featured', 'addons/persona']
|
||||
|
||||
def setUp(self):
|
||||
# Addon._feature keeps an in-process cache we need to clear.
|
||||
|
@ -95,6 +96,48 @@ class TestHelpers(test_utils.TestCase):
|
|||
doc = PyQuery(s)
|
||||
eq_(doc('input[name=source]').attr('value'), 'browse')
|
||||
|
||||
def test_mobile_persona_preview(self):
|
||||
ctx = {'APP': amo.FIREFOX, 'LANG': 'en-US'}
|
||||
persona = Addon.objects.get(pk=15663).persona
|
||||
s = mobile_persona_preview(ctx, persona)
|
||||
doc = PyQuery(s)
|
||||
bt = doc('.persona-preview div[data-browsertheme]')
|
||||
assert bt
|
||||
assert persona.preview_url in bt.attr('style')
|
||||
eq_(persona.json_data, bt.attr('data-browsertheme'))
|
||||
assert bt.find('p')
|
||||
|
||||
def _test_mobile_persona_ctx(self):
|
||||
request = Mock()
|
||||
request.APP = amo.FIREFOX
|
||||
request.GET = {}
|
||||
request.user.is_authenticated.return_value = False
|
||||
request.amo_user.mobile_addons = []
|
||||
return {'APP': amo.FIREFOX, 'LANG': 'en-US', 'request': request}
|
||||
|
||||
def test_mobile_persona_confirm_large(self):
|
||||
persona = Addon.objects.get(id=15663).persona
|
||||
s = mobile_persona_confirm(self._test_mobile_persona_ctx(), persona)
|
||||
doc = PyQuery(s)
|
||||
assert not doc('.persona-slider')
|
||||
assert doc('.preview')
|
||||
assert doc('.confirm-buttons .add')
|
||||
assert doc('.confirm-buttons .cancel')
|
||||
assert not doc('.more')
|
||||
|
||||
def test_mobile_persona_confirm_small(self):
|
||||
persona = Addon.objects.get(id=15663).persona
|
||||
s = mobile_persona_confirm(self._test_mobile_persona_ctx(), persona,
|
||||
size='small')
|
||||
doc = PyQuery(s)
|
||||
assert doc('.persona-slider')
|
||||
assert not doc('.persona-slider .preview')
|
||||
assert doc('.confirm-buttons .add')
|
||||
assert doc('.confirm-buttons .cancel')
|
||||
more = doc('.more')
|
||||
assert more
|
||||
eq_(more.attr('href'), persona.addon.get_url_path())
|
||||
|
||||
|
||||
class TestPerformanceNote(test_utils.TestCase):
|
||||
listing = '<div class="performance-note">'
|
||||
|
|
|
@ -571,6 +571,12 @@ class TestDetailPage(test_utils.TestCase):
|
|||
assert '<script>alert("fff")</script>' in html
|
||||
assert '<script>' not in html
|
||||
|
||||
def test_personas_context(self):
|
||||
response = self.client.get(reverse('addons.detail', args=['a15663']))
|
||||
assert 'review_form' in response.context
|
||||
assert 'reviews' in response.context
|
||||
assert 'get_replies' in response.context
|
||||
|
||||
def test_unreviewed_robots(self):
|
||||
"""Check that unreviewed add-ons do not get indexed."""
|
||||
addon = Addon.objects.get(id=3615)
|
||||
|
@ -1266,11 +1272,14 @@ class TestMobileDetails(TestMobile):
|
|||
eq_(r.status_code, 200)
|
||||
self.assertTemplateUsed(r, 'addons/mobile/details.html')
|
||||
|
||||
def _test_persona(self):
|
||||
addon = Addon.objects.filter(type=amo.ADDON_PERSONA)[0]
|
||||
def test_persona(self):
|
||||
addon = Addon.objects.get(id=15679)
|
||||
r = self.client.get(addon.get_url_path())
|
||||
eq_(r.status_code, 200)
|
||||
self.assertTemplateUsed(r, 'addons/mobile/persona_detail.html')
|
||||
assert 'review_form' not in r.context
|
||||
assert 'reviews' not in r.context
|
||||
assert 'get_replies' not in r.context
|
||||
|
||||
def test_release_notes(self):
|
||||
a = Addon.objects.get(id=3615)
|
||||
|
|
|
@ -18,7 +18,7 @@ import jinja2
|
|||
import commonware.log
|
||||
import session_csrf
|
||||
from tower import ugettext as _, ugettext_lazy as _lazy
|
||||
from mobility.decorators import mobilized
|
||||
from mobility.decorators import mobilized, mobile_template
|
||||
|
||||
import amo
|
||||
from amo import messages
|
||||
|
@ -238,7 +238,8 @@ def _category_personas(qs, limit):
|
|||
return caching.cached(f, key)
|
||||
|
||||
|
||||
def persona_detail(request, addon):
|
||||
@mobile_template('addons/{mobile/}persona_detail.html')
|
||||
def persona_detail(request, addon, template=None):
|
||||
"""Details page for Personas."""
|
||||
persona = addon.persona
|
||||
|
||||
|
@ -250,9 +251,6 @@ def persona_detail(request, addon):
|
|||
else:
|
||||
category_personas = None
|
||||
|
||||
# tags
|
||||
dev_tags, user_tags = addon.tags_partitioned_by_developer
|
||||
|
||||
# other personas from the same author(s)
|
||||
author_personas = Addon.objects.valid().filter(
|
||||
persona__author=persona.author,
|
||||
|
@ -265,25 +263,23 @@ def persona_detail(request, addon):
|
|||
'categories': categories,
|
||||
'author_personas': author_personas,
|
||||
'category_personas': category_personas,
|
||||
'dev_tags': dev_tags,
|
||||
'user_tags': user_tags,
|
||||
'review_form': ReviewForm(),
|
||||
'reviews': Review.objects.latest().filter(addon=addon),
|
||||
'get_replies': Review.get_replies,
|
||||
# Remora users persona.author despite there being a display_username
|
||||
# Remora uses persona.author despite there being a display_username.
|
||||
'author_gallery': settings.PERSONAS_USER_ROOT % persona.author,
|
||||
'search_cat': 'personas',
|
||||
}
|
||||
if settings.REPORT_ABUSE:
|
||||
data['abuse_form'] = AbuseForm(request=request)
|
||||
|
||||
return jingo.render(request, 'addons/persona_detail.html', data)
|
||||
|
||||
|
||||
# @mobilized(persona_detail)
|
||||
# def persona_detail(request, addon):
|
||||
# return jingo.render(request, 'addons/mobile/persona_detail.html',
|
||||
# {'addon': addon})
|
||||
if not request.MOBILE:
|
||||
# tags
|
||||
dev_tags, user_tags = addon.tags_partitioned_by_developer
|
||||
data.update({
|
||||
'dev_tags': dev_tags,
|
||||
'user_tags': user_tags,
|
||||
'review_form': ReviewForm(),
|
||||
'reviews': Review.objects.latest().filter(addon=addon),
|
||||
'get_replies': Review.get_replies,
|
||||
'search_cat': 'personas'
|
||||
})
|
||||
if settings.REPORT_ABUSE:
|
||||
data['abuse_form'] = AbuseForm(request=request)
|
||||
return jingo.render(request, template, data)
|
||||
|
||||
|
||||
class BaseFilter(object):
|
||||
|
|
|
@ -28,6 +28,12 @@ def reviews_link(addon, collection_uuid=None, link_to_list=False):
|
|||
collection_uuid=collection_uuid))
|
||||
|
||||
|
||||
@jingo.register.function
|
||||
def mobile_reviews_link(addon):
|
||||
t = jingo.env.get_template('reviews/mobile/reviews_link.html')
|
||||
return jinja2.Markup(t.render(addon=addon))
|
||||
|
||||
|
||||
@jingo.register.inclusion_tag('reviews/report_review.html')
|
||||
@jinja2.contextfunction
|
||||
def report_review_popup(context):
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{% if addon.total_reviews %}
|
||||
<a class="listview" href="{{ url('reviews.list', addon.slug) }}">
|
||||
<div class="icon">
|
||||
{{ addon.average_rating|stars }}
|
||||
</div>
|
||||
{% trans num=addon.total_reviews, cnt=addon.total_reviews|numberfmt %}
|
||||
See All Reviews
|
||||
{% pluralize %}
|
||||
See All {{ cnt }} Reviews
|
||||
{% endtrans %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="listview" href="{{ url('reviews.add', addon.slug) }}">
|
||||
{{ _('Be the first to write a review.') }}</a>
|
||||
{% endif %}
|
|
@ -56,3 +56,25 @@ def test_reviews_link():
|
|||
s = render('{{ reviews_link(myaddon, link_to_list=True) }}',
|
||||
{'myaddon': a})
|
||||
eq_(PyQuery(s)('a').attr('href'), u)
|
||||
|
||||
|
||||
def test_mobile_reviews_link():
|
||||
s = lambda a: PyQuery(render('{{ mobile_reviews_link(myaddon) }}',
|
||||
{'myaddon': a}))
|
||||
|
||||
a = Addon(total_reviews=0, id=1, type=1, slug='xx')
|
||||
doc = s(a)
|
||||
eq_(doc('a').attr('href'), reverse('reviews.add', args=['xx']))
|
||||
|
||||
u = reverse('reviews.list', args=['xx'])
|
||||
|
||||
a = Addon(average_rating=4, total_reviews=37, id=1, type=1, slug='xx')
|
||||
doc = s(a)
|
||||
eq_(doc('a').attr('href'), u)
|
||||
eq_(doc('a').text(), 'Rated 4 out of 5 stars See All 37 Reviews')
|
||||
|
||||
a = Addon(average_rating=4, total_reviews=1, id=1, type=1, slug='xx')
|
||||
doc = s(a)
|
||||
doc.remove('div')
|
||||
eq_(doc('a').attr('href'), u)
|
||||
eq_(doc('a').text(), 'See All Reviews')
|
||||
|
|
|
@ -65,7 +65,8 @@ i {
|
|||
header:after,
|
||||
section:after,
|
||||
.menu:after,
|
||||
.grouped_ratings:after {
|
||||
.grouped_ratings:after,
|
||||
.persona-confirm:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
clear: both;
|
||||
|
@ -488,7 +489,7 @@ header #home, header .back-link {
|
|||
|
||||
.listview,
|
||||
.infobox {
|
||||
margin: 14px 14px 0;
|
||||
margin: 14px 14px 1em;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
|
@ -497,7 +498,6 @@ header #home, header .back-link {
|
|||
/* background-image: -moz-linear-gradient(#fff, rgba(255,255,255,0) 12px);
|
||||
-moz-box-shadow: 0 -3px rgba(0,0,0,.1) inset;
|
||||
-webkit-box-shadow: 0 -3px rgba(0,0,0,.1) inset;*/
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
}
|
||||
.infobox {
|
||||
|
@ -512,7 +512,7 @@ header #home, header .back-link {
|
|||
}
|
||||
.listview li > a,
|
||||
a.listview {
|
||||
padding: 14px 10px;
|
||||
padding: 14px;
|
||||
color: #444;
|
||||
font-family: Georgia, serif;
|
||||
font-size: 1.1em;
|
||||
|
@ -521,6 +521,10 @@ a.listview {
|
|||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
.html-rtl li > a,
|
||||
.html-rtl a.listview {
|
||||
padding-left: 34px;
|
||||
}
|
||||
.listview .item > a,
|
||||
.listview div.item {
|
||||
padding: 10px;
|
||||
|
@ -816,7 +820,6 @@ a.listview:before {
|
|||
-webkit-transition: .5s opacity ease;
|
||||
}
|
||||
#lightbox .close {
|
||||
font-family: sans;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
|
@ -906,6 +909,170 @@ td .versions li a {
|
|||
line-height: 12px;
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/* PERSONAS */
|
||||
/************************************/
|
||||
|
||||
#persona h3,
|
||||
#persona .author {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#persona h3 {
|
||||
color: #444;
|
||||
font-family: "Droid Sans", Helvetica, sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#persona .author,
|
||||
#persona .persona-large {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#persona .badges {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#persona .badges li {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
ul.license {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
ul.license li {
|
||||
display: block;
|
||||
float: left;
|
||||
list-style: none;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.html-rtl ul.license li {
|
||||
float: right;
|
||||
margin: 0 0 0 2px;
|
||||
}
|
||||
ul.license li.text {
|
||||
font-size: 90%;
|
||||
line-height: 15px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.html-rtl ul.license li.text {
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
ul.license li.icon {
|
||||
background: url(../../img/zamboni/licenses.png) no-repeat top left;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
ul.license li.cc-attrib { background-position: 0 0; }
|
||||
ul.license li.cc-noderiv { background-position: 0 -65px; }
|
||||
ul.license li.cc-noncom { background-position: 0 -130px; }
|
||||
ul.license li.cc-share { background-position: 0 -195px; }
|
||||
ul.license li.copyr { background-position: 0 -260px; }
|
||||
|
||||
.persona-preview [data-browsertheme] {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.persona-large [data-browsertheme],
|
||||
.persona-large p {
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.persona-large {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.persona-large [data-browsertheme] {
|
||||
background: transparent no-repeat right top;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.4);
|
||||
display: table;
|
||||
height: 64px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.persona-large p {
|
||||
background-image: url(../../img/zamboni/mobile/loading-white.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
-moz-background-size: auto 32px;
|
||||
-wekbkit-background-size: auto 32px;
|
||||
background-size: auto 32px;
|
||||
color: #fff;
|
||||
display: none;
|
||||
font: 18px Georgia, serif;
|
||||
pointer-events: none;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.persona-hover p {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.persona-previewing p,
|
||||
.persona-installed p {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#persona .confirm-buttons,
|
||||
.persona-slider .badges {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.persona-previewer .confirm-buttons .add {
|
||||
float: left;
|
||||
width: -moz-calc(50% - 63px); /* 63px = 39px + 24px (for plus-sign icon) */
|
||||
}
|
||||
|
||||
.persona-previewer .confirm-buttons .cancel {
|
||||
float: right;
|
||||
width: -moz-calc(50% - 39px); /* 39px = 32px + 14px / 2 (margin) */
|
||||
}
|
||||
|
||||
.persona-previewer .persona-installed p:before {
|
||||
background: url(../../img/zamboni/mobile/checkmark.png) no-repeat top left;
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
margin: 0 3px -3px 0;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
li.persona-previewer {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.persona-slider {
|
||||
background-color: #ccc;
|
||||
border-top: #999 1px solid;
|
||||
box-shadow: 0 -1px 1px rgba(0,0,0,.5);
|
||||
display: none;
|
||||
padding: 14px;
|
||||
position: relative;
|
||||
left: -14px;
|
||||
bottom: -14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.persona-slider .more {
|
||||
clear: both;
|
||||
color: #447bc4;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
padding-top: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/* VERSIONS */
|
||||
/************************************/
|
||||
|
@ -969,11 +1136,26 @@ td .versions li a {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.infobox .install-wrapper {
|
||||
.infobox .install-wrapper,
|
||||
#persona .persona-confirm {
|
||||
border-top: 2px solid #fff;
|
||||
box-shadow: 0 -1px #ccc;
|
||||
}
|
||||
|
||||
#persona .persona-confirm {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.persona-confirm .install-wrapper {
|
||||
margin: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#persona .persona-confirm .install-wrapper {
|
||||
border-top-width: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.button, a.button {
|
||||
-moz-transition: -moz-box-shadow 0.3s ease 0s;
|
||||
background-color: #669BE1;
|
||||
|
@ -1039,6 +1221,7 @@ td .versions li a {
|
|||
0 0 100px rgba(255, 255, 255, 0.2) inset;
|
||||
}
|
||||
|
||||
.button.preview,
|
||||
.button.affirmative,
|
||||
.button.add {
|
||||
background-color: #84C63C;
|
||||
|
@ -1064,6 +1247,9 @@ td .versions li a {
|
|||
);*/
|
||||
color: #fff;
|
||||
}
|
||||
.button.cancel {
|
||||
background-color: #b25951;
|
||||
}
|
||||
.button.add {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
@ -1312,9 +1498,7 @@ td .versions li a {
|
|||
width: 120px;
|
||||
}
|
||||
.num_ratings {
|
||||
font-size: .9em;
|
||||
color: #888;
|
||||
line-height: 22px;
|
||||
width: 1px;
|
||||
position: absolute;
|
||||
right: -6px;
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.4 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 32 KiB |
|
@ -201,6 +201,15 @@ $(function() {
|
|||
$("#eula .negative").click(_pd(z.eula.dismiss));
|
||||
$("#eula .affirmative").click(_pd(z.eula.dismiss));
|
||||
|
||||
$(".persona-previewer .preview").click(_pd(function() {
|
||||
var persona = new MobilePersona(this);
|
||||
persona.triggers().preview();
|
||||
}));
|
||||
$(".persona-previewer .cancel").click(_pd(function() {
|
||||
var persona = new MobilePersona(this);
|
||||
persona.triggers().cancel();
|
||||
}));
|
||||
|
||||
//review truncation
|
||||
if ($(".review").length) {
|
||||
$(".review p").each(function() {
|
||||
|
@ -258,3 +267,99 @@ z.eula = (function(){
|
|||
acceptButton: $("#eula-menu .affirmative")
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* MobilePersona: controls for mobile-friendly Persona previewer.
|
||||
* Configuration:
|
||||
* el: .button, .persona-preview, or any element in the .persona-previewer
|
||||
*/
|
||||
function MobilePersona(el) {
|
||||
this.el = el;
|
||||
this.outer = $(el).closest('.persona-previewer');
|
||||
this.persona = this.outer.find('.persona');
|
||||
this.personaPreview = this.persona.find('[data-browsertheme]');
|
||||
}
|
||||
MobilePersona.prototype.buttons = function() {
|
||||
var $slider = this.outer.find('.persona-slider'),
|
||||
$preview = this.outer.find('.button.preview'),
|
||||
$confirm = this.outer.find('.confirm-buttons'),
|
||||
$badges = this.outer.closest('#persona').find('.badges'),
|
||||
that = this;
|
||||
return {
|
||||
show: function(force) {
|
||||
if ($slider.length) {
|
||||
$slider.slideDown();
|
||||
} else {
|
||||
$confirm.show();
|
||||
}
|
||||
$preview.hide();
|
||||
$badges.hide();
|
||||
},
|
||||
hide: function(force) {
|
||||
if ($slider.length) {
|
||||
$slider.slideUp();
|
||||
} else {
|
||||
$confirm.hide();
|
||||
}
|
||||
$preview.show();
|
||||
$badges.show();
|
||||
},
|
||||
disable: function() {
|
||||
$preview.addClass('disabled');
|
||||
}
|
||||
};
|
||||
};
|
||||
MobilePersona.prototype.states = function() {
|
||||
var btns = this.buttons(),
|
||||
that = this;
|
||||
return {
|
||||
loading: function() {
|
||||
that.persona.find('p').show();
|
||||
},
|
||||
previewing: function() {
|
||||
that.persona.addClass('persona-previewing');
|
||||
that.persona.find('p').text(gettext("You're trying it on!"));
|
||||
btns.show();
|
||||
},
|
||||
installed: function() {
|
||||
var $installed = $('.persona-installed');
|
||||
if ($installed.length) {
|
||||
// If a different persona has already been installed, then
|
||||
// that persona should be able to be previewed again.
|
||||
$installed.removeClass('persona-installed').find('p').text('').hide();
|
||||
$('#persona .preview.disabled').removeClass('disabled');
|
||||
$installed.find('[data-browsertheme]').trigger('click');
|
||||
}
|
||||
that.persona.find('p').text(gettext('Added to Firefox'));
|
||||
that.persona.removeClass('persona-previewing').addClass('persona-installed');
|
||||
btns.hide();
|
||||
btns.disable();
|
||||
},
|
||||
cancelled: function() {
|
||||
that.persona.removeClass('persona-previewing');
|
||||
that.persona.find('p').text('').hide();
|
||||
btns.hide();
|
||||
}
|
||||
};
|
||||
};
|
||||
MobilePersona.prototype.triggers = function() {
|
||||
// Trigger events for Persona previews.
|
||||
var btns = this.buttons(),
|
||||
that = this;
|
||||
return {
|
||||
preview: function() {
|
||||
// Check if "Try it" button is disabled.
|
||||
if (that.outer.find('.button.preview').hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
that.personaPreview.trigger('click');
|
||||
btns.show();
|
||||
},
|
||||
cancel: function() {
|
||||
// Clicking again will cancel the Persona preview.
|
||||
that.personaPreview.trigger('click');
|
||||
btns.hide();
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
$(document).ready(function() {
|
||||
var personas = $('.persona-preview');
|
||||
if (!personas.length) return;
|
||||
personas.previewPersona();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Binds Personas preview events to the element.
|
||||
* Click - bubbles up PreviewPersona
|
||||
* Click again - bubbles up ResetPersona
|
||||
**/
|
||||
$.fn.previewPersona = function(o) {
|
||||
if (!$.hasPersonas()) {
|
||||
return;
|
||||
}
|
||||
o = $.extend({
|
||||
activeClass: 'persona-hover',
|
||||
disabledClass: 'persona-installed'
|
||||
}, o || {});
|
||||
$(this).click(function(e) {
|
||||
var $outer = $(this).closest('.persona-previewer'),
|
||||
$persona = $outer.find('.persona');
|
||||
if ($persona.hasClass(o.disabledClass)) {
|
||||
return;
|
||||
}
|
||||
var mp = new MobilePersona(this),
|
||||
states = mp.states();
|
||||
if ($persona.hasClass(o.activeClass)) {
|
||||
// Hide persona.
|
||||
$persona.removeClass(o.activeClass);
|
||||
dispatchPersonaEvent('ResetPersona', e.target,
|
||||
states.cancelled);
|
||||
} else {
|
||||
// Hide other active personas.
|
||||
$('.' + o.activeClass).each(function() {
|
||||
$(this).find('[data-browsertheme]').trigger('click');
|
||||
});
|
||||
// Load persona.
|
||||
$persona.addClass(o.activeClass);
|
||||
states.loading();
|
||||
dispatchPersonaEvent('PreviewPersona', e.target, states.previewing);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Should be called on an anchor. */
|
||||
$.fn.personasButton = function(trigger, callback) {
|
||||
$(this).closest('.persona').click(function(e) {
|
||||
dispatchPersonaEvent('SelectPersona', e.currentTarget, callback);
|
||||
return false;
|
||||
});
|
||||
};
|
|
@ -47,7 +47,9 @@
|
|||
'lite': gettext("Experimental <span>(Learn More)</span>"),
|
||||
'badApp': format(gettext("Not Available for {0}"), z.appName),
|
||||
'badPlatform': format(gettext("Not Available for {0}"), z.platformName),
|
||||
'experimental': gettext("Experimental")
|
||||
'experimental': gettext("Experimental"),
|
||||
'personasTooOld': format(gettext("Personas Require Newer Version of {0}"), z.appName),
|
||||
'personasLearnMore': format(gettext("Personas Require {0}"), z.appName)
|
||||
};
|
||||
|
||||
function Button(el) {
|
||||
|
@ -86,6 +88,9 @@
|
|||
versionPlatformCheck();
|
||||
|
||||
this.actionQueue.push([0, function() {
|
||||
if (self.classes.persona) {
|
||||
return;
|
||||
}
|
||||
var href = activeInstaller.attr('href'),
|
||||
hash = hashes[href],
|
||||
attr = self.attr,
|
||||
|
@ -187,9 +192,7 @@
|
|||
platformer = !!b.find('.platform').length,
|
||||
platformSupported = !platformer || dom.buttons.filter("." + z.platform).length,
|
||||
appSupported = z.appMatchesUserAgent && attr.min && attr.max,
|
||||
olderBrowser, newerBrowser,
|
||||
canInstall = true;
|
||||
|
||||
if (!attr.search) {
|
||||
// min and max only exist if the add-on is compatible with request[APP].
|
||||
if (appSupported && platformSupported) {
|
||||
|
@ -202,12 +205,15 @@
|
|||
if (self.tooOld) errors.push("tooOld");
|
||||
if (self.tooNew) errors.push("tooNew");
|
||||
} else {
|
||||
if (!appSupported && !z.badBrowser) errors.push("badApp");
|
||||
if (!z.appMatchesUserAgent && !z.badBrowser) {
|
||||
errors.push("badApp");
|
||||
canInstall = false;
|
||||
}
|
||||
if (!platformSupported) {
|
||||
errors.push("badPlatform");
|
||||
dom.buttons.hide().eq(0).show();
|
||||
canInstall = false;
|
||||
}
|
||||
canInstall = false;
|
||||
}
|
||||
|
||||
if (platformer) {
|
||||
|
@ -226,6 +232,25 @@
|
|||
self.actionQueue.push([1,z.eula.show]);
|
||||
z.eula.acceptButton.click(_pd(self.resumeInstall));
|
||||
}
|
||||
|
||||
if (classes.persona) {
|
||||
dom.buttons.removeClass("download").addClass("add");
|
||||
var persona = new MobilePersona(b);
|
||||
if ($.hasPersonas()) {
|
||||
dom.buttons.text(gettext("Keep it"));
|
||||
dom.buttons.personasButton("click",
|
||||
persona.states().installed);
|
||||
} else {
|
||||
persona.buttons().disable();
|
||||
dom.buttons.addClass("disabled");
|
||||
if (z.appMatchesUserAgent) {
|
||||
// Need upgrade.
|
||||
errors.push("personasTooOld");
|
||||
} else {
|
||||
errors.push("personasLearnMore");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (z.badBrowser) {
|
||||
|
|
|
@ -1,35 +1,9 @@
|
|||
$(document).ready(function() {
|
||||
var personas = $('.persona-preview');
|
||||
if (!personas.length) return;
|
||||
|
||||
personas.previewPersona(true);
|
||||
personas.previewPersona();
|
||||
});
|
||||
|
||||
/**
|
||||
* Bubbles up persona event to tell Firefox to load a persona
|
||||
**/
|
||||
function dispatchPersonaEvent(aType, aNode)
|
||||
{
|
||||
var aliases = {'PreviewPersona': 'PreviewBrowserTheme',
|
||||
'ResetPersona': 'ResetBrowserThemePreview',
|
||||
'SelectPersona': 'InstallBrowserTheme'};
|
||||
try {
|
||||
if (!aNode.hasAttribute("data-browsertheme"))
|
||||
return;
|
||||
|
||||
$(aNode).attr("persona", $(aNode).attr("data-browsertheme"));
|
||||
|
||||
var aliasEvent = aliases[aType];
|
||||
var events = [aType, aliasEvent];
|
||||
|
||||
for (var i=0; i<events.length; i++) {
|
||||
var event = events[i];
|
||||
var eventObject = document.createEvent("Events");
|
||||
eventObject.initEvent(event, true, false);
|
||||
aNode.dispatchEvent(eventObject);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds Personas preview events to the element.
|
||||
|
@ -37,29 +11,36 @@ function dispatchPersonaEvent(aType, aNode)
|
|||
* Mouseenter - bubbles up PreviewPersona
|
||||
* Mouseleave - bubbles up ResetPersona
|
||||
**/
|
||||
$.fn.previewPersona = function(resetOnClick) {
|
||||
if (resetOnClick) {
|
||||
$(this).click(function(e) {
|
||||
dispatchPersonaEvent('ResetPersona', e.originalTarget);
|
||||
$.fn.previewPersona = function(o) {
|
||||
if (!$.hasPersonas()) {
|
||||
return;
|
||||
}
|
||||
o = $.extend({
|
||||
resetOnClick: true,
|
||||
activeClass: 'persona-hover'
|
||||
}, o || {});
|
||||
var $this = $(this);
|
||||
if (o.resetOnClick) {
|
||||
$this.click(function(e) {
|
||||
dispatchPersonaEvent('ResetPersona', e.target);
|
||||
});
|
||||
}
|
||||
|
||||
$(this).hoverIntent({
|
||||
$this.hoverIntent({
|
||||
interval: 100,
|
||||
over: function(e) {
|
||||
$(this).closest('.persona').addClass('persona-hover');
|
||||
dispatchPersonaEvent('PreviewPersona', e.originalTarget);
|
||||
$(this).closest('.persona').addClass(o.activeClass);
|
||||
dispatchPersonaEvent('PreviewPersona', e.target);
|
||||
},
|
||||
out: function(e) {
|
||||
$(this).closest('.persona').removeClass('persona-hover');
|
||||
dispatchPersonaEvent('ResetPersona', e.originalTarget);
|
||||
$(this).closest('.persona').removeClass(o.activeClass);
|
||||
dispatchPersonaEvent('ResetPersona', e.target);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Should be called on an anchor. */
|
||||
$.fn.personasButton = function(options) {
|
||||
$.fn.personasButton = function(trigger, callback) {
|
||||
var persona_wrapper = $(this).closest('.persona');
|
||||
persona_wrapper.hoverIntent({
|
||||
interval: 100,
|
||||
|
@ -71,31 +52,12 @@ $.fn.personasButton = function(options) {
|
|||
}
|
||||
});
|
||||
persona_wrapper.click(function(e) {
|
||||
dispatchPersonaEvent('SelectPersona', e.currentTarget);
|
||||
dispatchPersonaEvent('SelectPersona', e.currentTarget, callback);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$.hasPersonas = function() {
|
||||
if (!jQuery.browser.mozilla) return false;
|
||||
|
||||
// Fx 3.6 has lightweight themes (aka personas)
|
||||
if (VersionCompare.compareVersions(
|
||||
$.browser.version, '1.9.2') > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var body = document.getElementsByTagName("body")[0];
|
||||
try {
|
||||
var event = document.createEvent("Events");
|
||||
event.initEvent("CheckPersonas", true, false);
|
||||
body.dispatchEvent(event);
|
||||
} catch(e) {}
|
||||
|
||||
return body.getAttribute("personas") == "true";
|
||||
};
|
||||
|
||||
// Vertical carousel component
|
||||
// Based on jQuery Infinite Carousel
|
||||
// http://jqueryfordesigners.com/jquery-infinite-carousel/
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Bubbles up persona event to tell Firefox to load a persona
|
||||
**/
|
||||
function dispatchPersonaEvent(aType, aNode, callback)
|
||||
{
|
||||
var aliases = {'PreviewPersona': 'PreviewBrowserTheme',
|
||||
'ResetPersona': 'ResetBrowserThemePreview',
|
||||
'SelectPersona': 'InstallBrowserTheme'};
|
||||
try {
|
||||
if (!aNode.hasAttribute("data-browsertheme"))
|
||||
return;
|
||||
|
||||
$(aNode).attr("persona", $(aNode).attr("data-browsertheme"));
|
||||
|
||||
var aliasEvent = aliases[aType];
|
||||
var events = [aType, aliasEvent];
|
||||
|
||||
for (var i=0; i<events.length; i++) {
|
||||
var event = events[i];
|
||||
var eventObject = document.createEvent("Events");
|
||||
eventObject.initEvent(event, true, false);
|
||||
aNode.dispatchEvent(eventObject);
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
|
||||
$.hasPersonas = function() {
|
||||
if (!jQuery.browser.mozilla) return false;
|
||||
|
||||
// Fx 3.6 has lightweight themes (aka personas)
|
||||
if (VersionCompare.compareVersions(
|
||||
$.browser.version, '1.9.2') > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var body = document.getElementsByTagName("body")[0];
|
||||
try {
|
||||
var event = document.createEvent("Events");
|
||||
event.initEvent("CheckPersonas", true, false);
|
||||
body.dispatchEvent(event);
|
||||
} catch(e) {}
|
||||
|
||||
return body.getAttribute("personas") == "true";
|
||||
};
|
|
@ -468,6 +468,7 @@ MINIFY_BUNDLES = {
|
|||
|
||||
# Personas
|
||||
'js/lib/jquery.hoverIntent.min.js',
|
||||
'js/zamboni/personas_core.js',
|
||||
'js/zamboni/personas.js',
|
||||
|
||||
# Collections
|
||||
|
@ -520,6 +521,7 @@ MINIFY_BUNDLES = {
|
|||
|
||||
# Personas
|
||||
'js/lib/jquery.hoverIntent.min.js',
|
||||
'js/zamboni/personas_core.js',
|
||||
'js/zamboni/personas.js',
|
||||
|
||||
# Collections
|
||||
|
@ -548,6 +550,7 @@ MINIFY_BUNDLES = {
|
|||
|
||||
# Personas
|
||||
'js/lib/jquery.hoverIntent.min.js',
|
||||
'js/zamboni/personas_core.js',
|
||||
'js/zamboni/personas.js',
|
||||
|
||||
'js/zamboni/debouncer.js',
|
||||
|
@ -598,7 +601,9 @@ MINIFY_BUNDLES = {
|
|||
'js/zamboni/format.js',
|
||||
'js/zamboni/mobile_buttons.js',
|
||||
'js/zamboni/truncation.js',
|
||||
'js/zamboni/mobile.js',
|
||||
'js/zamboni/personas_core.js',
|
||||
'js/zamboni/mobile/personas.js',
|
||||
'js/zamboni/mobile/general.js',
|
||||
),
|
||||
'zamboni/stats': (
|
||||
'js/lib/jquery-datepicker.js',
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
</h1>
|
||||
</hgroup>
|
||||
<div class="get-fx-message">
|
||||
{{ _('You need Firefox to install addons. <a href="http://mozilla.com/firefox">Learn More »</a>') }}
|
||||
{{ _('You need Firefox to install add-ons. <a href="http://mozilla.com/firefox">Learn More »</a>') }}
|
||||
</div>
|
||||
{% block back_link %}
|
||||
<a href="{{ url('home') }}" id="home">
|
||||
|
|
Загрузка…
Ссылка в новой задаче