From 7439ff1de08bd4e21c3ad82c11d798975faccec9 Mon Sep 17 00:00:00 2001 From: fefa4ka Date: Wed, 2 Nov 2016 03:15:58 +0300 Subject: [PATCH] Trademark update --- app/settings.py | 4 +- trademark/cms_app.py | 2 +- trademark/cms_appconfig.py | 81 ++++----- trademark/cms_plugins.py | 18 ++ trademark/lib/poiskznakov.py | 17 +- trademark/migrations/0001_initial.py | 6 +- trademark/models.py | 63 ++++++- trademark/templates/trademark/base.html | 13 +- trademark/templates/trademark/form.html | 152 ++++++++++++++++ trademark/templates/trademark/index.html | 11 ++ .../templates/trademark/registration.html | 23 +++ .../templates/trademark/search_detail.html | 90 +++++++++ trademark/urls.py | 7 +- trademark/views.py | 123 ++++++++++++- zsite/static/js/snippets.js | 14 ++ zsite/static/less/main.less | 3 + zsite/static/less/search.less | 171 ++++++++++++++++++ 17 files changed, 720 insertions(+), 78 deletions(-) create mode 100644 trademark/cms_plugins.py create mode 100644 trademark/templates/trademark/form.html create mode 100644 trademark/templates/trademark/index.html create mode 100644 trademark/templates/trademark/registration.html create mode 100644 zsite/static/less/search.less diff --git a/app/settings.py b/app/settings.py index b722b6d..13f1124 100644 --- a/app/settings.py +++ b/app/settings.py @@ -26,7 +26,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(__file__)) SECRET_KEY = '=%%a@whz46w1#=8ffk^a+8vya5fg-kz0ztyz!_@hdg-(5a9q+s' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = False TEMPLATE_DEBUG = False @@ -278,7 +278,7 @@ INSTALLED_APPS = ( 'easy_select2', 'promo', - # 'trademark', + 'trademark', ) SOUTH_MIGRATION_MODULES = { diff --git a/trademark/cms_app.py b/trademark/cms_app.py index 9cb4427..92d36fb 100644 --- a/trademark/cms_app.py +++ b/trademark/cms_app.py @@ -7,7 +7,7 @@ from .models import TrademarkConfig class TrademarkApp(CMSConfigApp): - app_config = TrademarkConfig + # app_config = TrademarkConfig name = _('Trademark') urls = ['trademark.urls'] app_name = 'trademark' diff --git a/trademark/cms_appconfig.py b/trademark/cms_appconfig.py index fdb112f..7d69520 100644 --- a/trademark/cms_appconfig.py +++ b/trademark/cms_appconfig.py @@ -16,12 +16,7 @@ from parler.models import TranslatedFields from cms.models.fields import PlaceholderField -class TrademarkConfig(TranslatableModel, AppHookConfig): - """Adds some translatable, per-app-instance fields.""" - translations = TranslatedFields( - app_title=models.CharField(_('application title'), max_length=234), - ) - +class TrademarkConfig(AppHookConfig): # search_PAGINATE_BY paginate_by = models.PositiveIntegerField( _('Paginate size'), @@ -30,43 +25,43 @@ class TrademarkConfig(TranslatableModel, AppHookConfig): help_text=_('When paginating list views, how many articles per page?'), ) - placeholder_base_top = PlaceholderField( - 'trademark_search_base_top', - related_name='search_base_top', - ) - - placeholder_base_sidebar = PlaceholderField( - 'trademark_search_base_sidebar', - related_name='search_base_sidebar', - ) - - placeholder_list_top = PlaceholderField( - 'trademark_search_list_top', - related_name='search_list_top', - ) - - placeholder_list_footer = PlaceholderField( - 'trademark_search_list_footer', - related_name='search_list_footer', - ) - - placeholder_detail_top = PlaceholderField( - 'trademark_search_detail_top', - related_name='search_detail_top', - ) - - placeholder_detail_bottom = PlaceholderField( - 'trademark_search_detail_bottom', - related_name='search_detail_bottom', - ) - - placeholder_detail_footer = PlaceholderField( - 'trademark_search_detail_footer', - related_name='search_detail_footer', - ) - - def get_app_title(self): - return getattr(self, 'app_title', _('untitled')) + # placeholder_base_top = PlaceholderField( + # 'trademark_search_base_top', + # related_name='search_base_top', + # ) + + # placeholder_base_sidebar = PlaceholderField( + # 'trademark_search_base_sidebar', + # related_name='search_base_sidebar', + # ) + + # placeholder_list_top = PlaceholderField( + # 'trademark_search_list_top', + # related_name='search_list_top', + # ) + + # placeholder_list_footer = PlaceholderField( + # 'trademark_search_list_footer', + # related_name='search_list_footer', + # ) + + # placeholder_detail_top = PlaceholderField( + # 'trademark_search_detail_top', + # related_name='search_detail_top', + # ) + + # placeholder_detail_bottom = PlaceholderField( + # 'trademark_search_detail_bottom', + # related_name='search_detail_bottom', + # ) + + # placeholder_detail_footer = PlaceholderField( + # 'trademark_search_detail_footer', + # related_name='search_detail_footer', + # ) + + # def get_app_title(self): + # return getattr(self, 'app_title', _('untitled')) # class TrademarkConfigForm(AppDataForm): diff --git a/trademark/cms_plugins.py b/trademark/cms_plugins.py new file mode 100644 index 0000000..691badb --- /dev/null +++ b/trademark/cms_plugins.py @@ -0,0 +1,18 @@ +# # -*- coding: utf-8 -*- + +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool +from django.utils.translation import ugettext as _ +# from .models import Promo +from django.db.models import Q + +class TrademarkSearchPluginPublisher(CMSPluginBase): + # model = PollPluginModel # model where plugin data are saved + module = _("Trademark") + name = _("Search Form Plugin") # name of the plugin in the interface + render_template = "trademark/form.html" + + def render(self, context, instance, placeholder): + return context + +plugin_pool.register_plugin(TrademarkSearchPluginPublisher) # register the plugin diff --git a/trademark/lib/poiskznakov.py b/trademark/lib/poiskznakov.py index 0c83f11..0939d9e 100644 --- a/trademark/lib/poiskznakov.py +++ b/trademark/lib/poiskznakov.py @@ -32,7 +32,7 @@ class TrademarkSearchAPI(): 'module': 'search', 'procedure': 'make_search', 'search_type': 'wdesc', - 'search_string': str(keyword), + 'search_string': keyword, 'databases': [21], 'use_translit': 'true', 'skip_expired': 'false', @@ -41,9 +41,13 @@ class TrademarkSearchAPI(): if similarity < 100: request['search_method'] = 'similarity' request['minimum_similarity'] = similarity - else: + + if similarity == 100: request['search_method'] = 'containment' + if similarity == 146: + request['search_method'] = 'identity' + return self.send_request(request) def get_status(self, search_id): @@ -58,11 +62,9 @@ class TrademarkSearchAPI(): def get_results(self, search_id): # Если не готов результат, то ничего не делаем status = self.get_status(search_id) - print status if status['search_status'] != 'finished': - print status - return {} + return {}, status request = { 'module': 'search', @@ -72,7 +74,10 @@ class TrademarkSearchAPI(): } data = self.send_request(request) - return data['records_list']['21']['records'] + if int(data['total_records_found']) > 0: + return data['records_list']['21']['records'], status + else: + return None, status def get_details(self, ids, keys): request = { diff --git a/trademark/migrations/0001_initial.py b/trademark/migrations/0001_initial.py index 41e9d34..66aefa7 100644 --- a/trademark/migrations/0001_initial.py +++ b/trademark/migrations/0001_initial.py @@ -4,10 +4,14 @@ from __future__ import unicode_literals from django.db import models, migrations import app_data.fields import cms.models.fields -import parler.models class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0012_auto_20150607_2207'), + ] + operations = [ migrations.CreateModel( name='Keyword', diff --git a/trademark/models.py b/trademark/models.py index a77341e..ffe5c0f 100644 --- a/trademark/models.py +++ b/trademark/models.py @@ -93,10 +93,18 @@ class Keyword(models.Model): def __str__(self): return self.request + @property + def contains_results(self): + search = self.searches.filter(similarity=100)[0] + + return search.results.all().order_by('similarity') + + def create_searches(self): + identity = Search(keyword_id=self.id, similarity=146) contains = Search(keyword_id=self.id, similarity=100) similar = Search(keyword_id=self.id, similarity=97) - searches = [contains, similar] + searches = [identity, contains, similar] for search in searches: search.save() @@ -131,6 +139,12 @@ class Search(models.Model): def __str__(self): return self.keyword.request + @property + def nices(self): + nices = self.results.values_list('nices__nice_id', flat=True).distinct() + + return nices + def send_request(self): data = TrademarkAPI.search_trademark(keyword=self.keyword.request, similarity=self.similarity) @@ -140,12 +154,36 @@ class Search(models.Model): def get_results(self): - results = TrademarkAPI.get_results(self.search_id) + results, status = TrademarkAPI.get_results(self.search_id) + + if status['search_status'] == 'failed': + self.send_request + return + + if results == None: + self.status = 'finished' + self.save() + + return + + if results == {}: + self.status = status['progress'] for key, trademark in results.iteritems(): - owner, created = Owner.objects.get_or_create(title=trademark['owner'][0]['name']) + if trademark['siml'] < 97 and self.similarity < 100: + continue + + try: + tm = Trademark.objects.get(ext_id=int(trademark['id'])) + result = SearchResult(search=self, trademark=tm, similarity=trademark['siml']) + result.save() + continue + except: + pass + + owner, created = Owner.objects.get_or_create(title=trademark['owner'][0]['name'].encode('utf-8')) - instance, created = Trademark.objects.get_or_create( + instance = Trademark( title=trademark['wdesc'], application_id=trademark['appnum'], cert_id=trademark['certnum'], @@ -156,16 +194,16 @@ class Search(models.Model): application_at=trademark['dateapp'], registration_at=trademark['datereg'], - ext_id=trademark['id'], + ext_id=int(trademark['id']), access_key=trademark['access_key'] ) + instance.save() + for nice_id in trademark['icgs']: nice, created = Nice.objects.get_or_create(nice_id=nice_id) instance.nices.add(nice) - instance.save() - result = SearchResult(search=self, trademark=instance, similarity=trademark['siml']) result.save() @@ -176,19 +214,22 @@ class Search(models.Model): if len(trademark_ids) == 0: return + self.status = 'finished' + self.save() + + details = TrademarkAPI.get_details(ids=trademark_ids, keys=trademark_keys) for key, trademark in details.iteritems(): instance = Trademark.objects.get(ext_id=trademark['id']) - instance.owner.name = trademark['owner'][0]['name'] + # instance.owner.name = trademark['owner'][0]['name'] instance.source_url = trademark.get('source', '') instance.expiration_at = trademark.get('dateexp', '') instance.renewed_at = trademark.get('renewed', '') instance.save() - self.status = 'finished' - self.save() + class SearchResult(models.Model): @@ -196,6 +237,8 @@ class SearchResult(models.Model): trademark = models.ForeignKey(Trademark, on_delete=models.CASCADE) similarity = models.IntegerField() + class Meta: + ordering = ('similarity',) @receiver(pre_save, sender=Keyword) def update_fields(sender, instance, **kwargs): diff --git a/trademark/templates/trademark/base.html b/trademark/templates/trademark/base.html index f43812a..11cfca2 100644 --- a/trademark/templates/trademark/base.html +++ b/trademark/templates/trademark/base.html @@ -1,6 +1,11 @@ -{% extends 'base.html' %} -{% block content %} - {% block trademark_content %} + +{% extends CMS_TEMPLATE %} + + +{% block content_safe %} +
+ {% block trademark_content %} {% endblock %} -{% endblock %} \ No newline at end of file +
+{% endblock content_safe %} diff --git a/trademark/templates/trademark/form.html b/trademark/templates/trademark/form.html new file mode 100644 index 0000000..98d1611 --- /dev/null +++ b/trademark/templates/trademark/form.html @@ -0,0 +1,152 @@ +{% block trademark_content %} + + + +{% endblock %} \ No newline at end of file diff --git a/trademark/templates/trademark/index.html b/trademark/templates/trademark/index.html new file mode 100644 index 0000000..f47cb1d --- /dev/null +++ b/trademark/templates/trademark/index.html @@ -0,0 +1,11 @@ +{% extends "trademark/base.html" %} + +{% block trademark_content %} + +

История поисков товарных знаков

+ +{% endblock %} \ No newline at end of file diff --git a/trademark/templates/trademark/registration.html b/trademark/templates/trademark/registration.html new file mode 100644 index 0000000..7e16897 --- /dev/null +++ b/trademark/templates/trademark/registration.html @@ -0,0 +1,23 @@ +{% if nice_available.count > 0 %} +
+

Зарегистрируйте товарный знак «{{ keyword.request|title }}»

+
+

+ Регистрация доступна в {{ nice_available.count }} классах товаров и услуг.

+ +

Мы готовы оказать вам услуги по оформлению и подаче заявки на регистрацию товарного знака и вести переписку с Роспатентом до получения свидетельства в России на товарный знак.

+ +
+ +
от 14 000 ₽
+ {% if nice_available.count < 45 and nice_available.count < nice_busy.count %} +
+

Доступные классы для регистрации:

+
    + {% for class in nice_available %} +
  • {{ class.title }}
  • + {% endfor %}
+
+ {% endif %} +
+{% endif %} \ No newline at end of file diff --git a/trademark/templates/trademark/search_detail.html b/trademark/templates/trademark/search_detail.html index 5aa0e76..e9b6597 100644 --- a/trademark/templates/trademark/search_detail.html +++ b/trademark/templates/trademark/search_detail.html @@ -1,2 +1,92 @@ {% extends "trademark/base.html" %} +{% block trademark_content %} + + +
+
+ + +{% if identity.count > 0 %} +

Товарный знак «{{ keyword.request|title }}» зарегистрирован {% if nice_busy.count == 45 %}во всех{% else %}в {{ nice_busy.count }}{% endif %} классах товаров и услуг

+

{% if nice_busy.count == 45 %}Знак нельзя использовать на территории России{% endif %}

+ +
    + {% for tm in identity %} +
  • + {% if tm.image_url %} + + {% else %} +

    {{ tm.title|title }}

    + {% endif %} +

    {{ tm.title|title }}

    + {% for class in tm.nices.all %}{% endfor %} +
  • + {% endfor %} +
+ +
+

«{{ keyword.request|title }}» нельзя использовать в следующих сферах

+
    + {% for class in nice_busy %} +
  • {{ class.title }}
  • + {% endfor %} +
+
+ {% else %} +

Товарный знак «{{ keyword.request|title }}» не зарегистрирован в базе свидетельств Роспатента

+

Знак, который вы выбрали, пока не зарегистрирован, но кто-то мог подать заявку на регистрацию сходного знака. Это означает, что существуюет вероятность, что вам могут отказать в регистрации вашего знака.

+

+ Так же есть ряд законодательных требований, которым должен соответствовать знак, не выполнение которых может привести к отказу в регистрации вашего знака.

+

+

Если же вы заинтересованы в регистрации товарного знака, мы проведем поиск сходных знаков, как уже зарегистрированных, так и заявок, поданных на регистрацию, а так же проверим ваш знак на соответствие требованиям законодательства. Это значительно (до 95%) увеличивает вероятность того, что ваш знак будет зарегистрирован.

+ {% include 'trademark/registration.html' with nice_available=nice_available nice_busy=nice_busy %} + {% endif %} +
+ +
+
+

Попробуйте проверить другой товарный знак

+{% include 'trademark/form.html' %} +
+ +{% if identity.count > 0 %} +{% include 'trademark/registration.html' with nice_available=nice_available nice_busy=nice_busy %} +{% endif %} + +
+
+ + {% if contains.count > 0 %} +

Похожие на «{{ keyword.request|title }}» торговые марки

+ + {% endif %} + + {% if similar.count > 0 %} +

Знаки включающие в себя части от «{{ keyword.request|title }}»

+ + {% endif %} + +{% endblock %} \ No newline at end of file diff --git a/trademark/urls.py b/trademark/urls.py index 3f236fc..7b99dad 100644 --- a/trademark/urls.py +++ b/trademark/urls.py @@ -5,7 +5,8 @@ from django.conf.urls import patterns, url from . import views urlpatterns = patterns('', - # url(r'^search/$', views.IndexView.as_view(), name='index'), - url(r'^online_search/(?P[\w-]+)/$', views.SearchDetailView.as_view(), name='search-detail'), - url(r'^online_search/$', views.Search.as_view(), name='search'), + url(r'^$', views.IndexView.as_view(), name='index'), + url(r'^results/(?P[\w-]+)/$', views.SearchDetailView.as_view(), name='search-detail'), + url(r'^request/(?P[\w-]+)/$', views.Search.as_view(), name='search-detail'), + url(r'^request/$', views.Search.as_view(), name='search'), ) diff --git a/trademark/views.py b/trademark/views.py index 3585a10..ad55df5 100644 --- a/trademark/views.py +++ b/trademark/views.py @@ -8,21 +8,46 @@ from django.http import HttpResponse import json from .models import * +from django.views.decorators.cache import never_cache + + class SearchDetailView(generic.DetailView): model = Keyword - context_object_name = 'post' + context_object_name = 'keyword' template_name = 'trademark/search_detail.html' slug_field = 'slug' view_url_name = 'trademark:search-detail' + def get_context_data(self, **kwargs): + context = super(SearchDetailView, self).get_context_data(**kwargs) + + identity = self.get_object().searches.filter(similarity=146)[0] + identity_results = identity.results.all() + context['identity'] = identity_results + identity_ids = identity_results.values_list('id', flat=True) + nice_busy_ids = identity_results.values_list('nices__nice_id', flat=True).distinct() + + contains = self.get_object().searches.filter(similarity=100)[0] + contains_results = contains.results.all().exclude(id__in=identity_ids) + context['contains'] = contains_results + contains_ids = contains_results.values_list('id', flat=True) + + similar = self.get_object().searches.filter(similarity=97)[0] + context['similar'] = similar.results.all().exclude(id__in=contains_ids | identity_ids) + + context['nice_busy'] = Nice.objects.filter(nice_id__in=nice_busy_ids).order_by('nice_id') + context['nice_available'] = Nice.objects.filter(nice_id__in=[x for x in range(1,46) if x not in nice_busy_ids]).order_by('nice_id') + + return context + class IndexView(generic.ListView): template_name = 'trademark/index.html' context_object_name = 'trademarks' def get_queryset(self): - return [] + return Keyword.objects.all() @@ -30,12 +55,66 @@ class Search(generic.View): # def get(self, request): # # return HttpResponse('result') + @never_cache def get(self, request, slug): keyword = get_object_or_404(Keyword, slug=slug) + identity = keyword.searches.filter(similarity=146)[0] + + contains = keyword.searches.filter(similarity=100)[0] + + similar = keyword.searches.filter(similarity=97)[0] + + response = { + 'identity': { + 'id': identity.id, + 'status': identity.status, + 'nices': identity.nices.count(), + 'count': identity.results.all().count() + }, + 'contains': { + 'id': contains.id, + 'status': contains.status, + 'count': contains.results.all().count() + }, + 'similar': { + 'id': similar.id, + 'status': similar.status, + 'count': similar.results.all().count() + }, + 'status': 'ok' + } + + return HttpResponse(json.dumps(response), content_type='application/json') + try: - ids = [str(key) for key in keyword.searches.all().values_list('search_id', flat=True)] - return HttpResponse(json.dumps({ 'search_ids': ids, 'status': 'finished' }), content_type='application/json') + identity = keyword.searches.filter(similarity=146)[0] + + contains = keyword.searches.filter(similarity=100)[0] + + similar = keyword.searches.filter(similarity=97)[0] + + response = { + 'identity': { + 'id': identity.id, + 'status': identity.status, + 'nices': identity.nices, + 'count': identity.results.all().count() + }, + 'contains': { + 'id': contains.id, + 'status': contains.status, + 'count': contains.results.all().count() + }, + 'similar': { + 'id': similar.id, + 'status': similar.status, + 'count': similar.results.all().count() + }, + 'status': 'ok' + } + + return HttpResponse(json.dumps(response), content_type='application/json') except: return HttpResponse(json.dumps({ 'status': 'error', 'description': 'Request not found', 'slug': slug }), content_type="application/json") @@ -44,9 +123,37 @@ class Search(generic.View): keyword = request.POST.get('keyword', '') if keyword: - k = Keyword.objects.get_or_create(request=keyword) - k.save() + k, created = Keyword.objects.get_or_create(request=keyword) else: - return HttpResponse(json.dumps({ 'status': 'error', 'description': 'Empty request', 'ext': json.dump(request.POST)}), content_type="application/json") + return HttpResponse(json.dumps({ 'status': 'error', 'description': 'Empty request'}), content_type="application/json") + + identity = k.searches.filter(similarity=146)[0] + + contains = k.searches.filter(similarity=100)[0] + + similar = k.searches.filter(similarity=97)[0] + + response = { + 'identity': { + 'id': identity.id, + 'status': identity.status, + 'nices': identity.nices.count(), + 'count': identity.results.all().count() + }, + 'contains': { + 'id': contains.id, + 'status': contains.status, + 'count': contains.results.all().count() + }, + 'similar': { + 'id': similar.id, + 'status': similar.status, + 'count': similar.results.all().count() + }, + 'slug': k.slug, + 'status': 'ok' + } + + return HttpResponse(json.dumps(response), content_type="application/json") + - return HttpResponse(json.dumps({ 'status': 'ok' }), content_type="application/json") diff --git a/zsite/static/js/snippets.js b/zsite/static/js/snippets.js index 66d68ff..07a8c26 100644 --- a/zsite/static/js/snippets.js +++ b/zsite/static/js/snippets.js @@ -121,6 +121,8 @@ detectRegion = function() { }; $(function() { + var csrftoken = getCookie('csrftoken'); + $( "[href^=mailto]" ).click(function() { yaCounter950321.reachGoal('EmailClick'); ga('send', 'event', 'Communication', 'email', document.title); @@ -136,6 +138,18 @@ $(function() { ga('send', 'event', 'Promo', 'click', document.title); }); + function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + } + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } + } + }); + detectRegion(); diff --git a/zsite/static/less/main.less b/zsite/static/less/main.less index df0beb6..951b3a4 100644 --- a/zsite/static/less/main.less +++ b/zsite/static/less/main.less @@ -30,8 +30,11 @@ @import 'nice.less'; +@import 'search.less'; + .container { width: 100%; + padding-left: 25px; padding-right: 30px; } diff --git a/zsite/static/less/search.less b/zsite/static/less/search.less new file mode 100644 index 0000000..027e36b --- /dev/null +++ b/zsite/static/less/search.less @@ -0,0 +1,171 @@ +.trademark-search { + .trademark-search-form { + margin-bottom: 5px; + } + + .trademark-search-field { + margin-left: -13px; + } + + example { + font-size: 12px; + margin: 2px 0 5px 0; + } +} + +.trademark-search-results { + .trademark-search-result { + + + margin-bottom: 15px; + margin-right: 5px; + + text-align: center; + + p { + margin-top: 10px; + border-top: 1px solid black; + width: 80%; + margin: 18px auto 0; + padding-top: 7px; + + } + + .btn-xs { + margin-right: 5px; + margin-top: 3px; + } + + } +} + +.trademark-search-available-classes { + .btn-xs { + width: 26px; + margin-bottom: 3px; + } +} + +.trademark-search-nice-busy, .trademark-register-block { + + border-radius: 5px; + padding: 15px 15px 10px; + margin-left: -15px; + margin-top: 30px; + + .description { + margin: 5px 0; + } + + .price { + display: block; + margin-top: 8px; + + font-size: larger; + font-weight: bold; + } +} + +.trademark-register-block { + background-color: #E8EEFC; +} + + +.trademark-search-nice-busy { + background-color: rgba(237, 208, 208, 0.36); + + padding: 1px 15px; + + h2 { + margin-top: 10px; + } +} + +.trademark-search-loading { + display: none; + + border: 1px solid silver; + border-radius: 6px; + background: white; + margin-left: -14px; + + .trademark-search-loading-header { + padding: 9px 13px; + } + + ul { + margin-left: 14px; + + li { + margin-bottom: 3px; + display: none; + } + } +} + +.block-hide-animation { + + opacity:0; + transition:opacity 0.5s linear; +} + + +.block-show-animation { + visibility:visible; + opacity:1; + transition:opacity 1s linear; +} + +.spinner { + width: 24px; + height: 14px; + text-align: center; + font-size: 8px; + display: inline-block; + padding-top: 5px +} + +.spinner > div { + background-color: #333; + height: 100%; + width: 3px; + display: inline-block; + + -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; + animation: sk-stretchdelay 1.2s infinite ease-in-out; +} + +.spinner .rect2 { + -webkit-animation-delay: -1.1s; + animation-delay: -1.1s; +} + +.spinner .rect3 { + -webkit-animation-delay: -1.0s; + animation-delay: -1.0s; +} + +.spinner .rect4 { + -webkit-animation-delay: -0.9s; + animation-delay: -0.9s; +} + +.spinner .rect5 { + -webkit-animation-delay: -0.8s; + animation-delay: -0.8s; +} + +@-webkit-keyframes sk-stretchdelay { + 0%, 40%, 100% { -webkit-transform: scaleY(0.4) } + 20% { -webkit-transform: scaleY(1.0) } +} + +@keyframes sk-stretchdelay { + 0%, 40%, 100% { + transform: scaleY(0.4); + -webkit-transform: scaleY(0.4); + } 20% { + transform: scaleY(1.0); + -webkit-transform: scaleY(1.0); + } +} \ No newline at end of file