text:
import json
import logging
import re
import time
from datetime import datetime, timedelta
from itertools import chain

from django.conf import settings
from django.contrib.sites.models import Site
from django.http import (
    HttpResponse, HttpResponseBadRequest, HttpResponseRedirect)
from django.shortcuts import render
from django.utils.html import escape
from django.utils.http import urlquote
from django.views.decorators.cache import cache_page

import bleach
import jinja2
from elasticutils.utils import format_explanation
from mobility.decorators import mobile_template
from statsd import statsd
from tower import ugettext as _, ugettext_lazy as _lazy

from kitsune import search as constants
from kitsune.forums.models import Forum, ThreadMappingType
from kitsune.products.models import Product
from kitsune.questions.models import QuestionMappingType
from kitsune.search.utils import locale_or_default, clean_excerpt, ComposedList
from kitsune.search import es_utils
from kitsune.search.forms import SimpleSearchForm, AdvancedSearchForm
from kitsune.search.es_utils import ES_EXCEPTIONS, F, AnalyzerS
from kitsune.sumo.helpers import Paginator
from kitsune.sumo.urlresolvers import reverse
from kitsune.sumo.utils import paginate, smart_int
from kitsune.wiki.facets import documents_for
from kitsune.wiki.models import DocumentMappingType

log = logging.getLogger('k.search')

EXCERPT_JOINER = _lazy(u'...', 'between search excerpts')

def jsonp_is_valid(func):
    func_regex = re.compile(r"""
        ^[a-zA-Z_\$]
        [a-zA-Z0-9_\$]*
        (\[[a-zA-Z0-9_\$]*\])*
        (\.[a-zA-Z0-9_\$]+
            (\[[a-zA-Z0-9_\$]*\])*
        )*$
    """, re.VERBOSE)
    return func_regex.match(func)

# Search "Expires" header format
EXPIRES_FMT = '%A, %d %B %Y %H:%M:%S GMT'

@mobile_template('search/{mobile/}results.html')
def simple_search(request, template=None):
    """ES-specific simple search view.

    This view is for end user searching of the Knowledge Base and
    Support Forum. Filtering options are limited to:
    * product (`product=firefox`, for example, for only Firefox results)
    * document type (`w=2`, for esample, for Support Forum questions only)
    """

    # Redirect to old Advanced Search URLs (?a={1,2}) to the new URL.
    a = request.GET.get('a')
    if a in ['1', '2']:
        new_url = reverse('search.advanced') + '?' + request.GET.urlencode()
        return HttpResponseRedirect(new_url)

    # JSON-specific variables
    is_json = (request.GET.get('format') == 'json')
    callback = request.GET.get('callback', '').strip()
    content_type = (
        'application/x-javascript' if callback else 'application/json')

    # Check callback is valid
    if is_json and callback and not jsonp_is_valid(callback):
        return HttpResponse(
            json.dumps({'error': _('Invalid callback function.')}),
            content_type=content_type, status=400)

    language = locale_or_default(
        request.GET.get('language', request.LANGUAGE_CODE))
    r = request.GET.copy()

    # TODO: Do we really need to add this to the URL if it isn't already there?
    r['w'] = r.get('w', constants.WHERE_BASIC)

    # TODO: Break out a separate simple search form.
    search_form = SimpleSearchForm(r, auto_id=False)

    if not search_form.is_valid():
        if is_json:
            return HttpResponse(
                json.dumps({'error': _('Invalid search data.')}),
                content_type=content_type,
                status=400)

        t = template if request.MOBILE else 'search/form.html'
        search_ = render(request, t, {
            'advanced': False, 'request': request,
            'search_form': search_form})
        cache_period = settings.SEARCH_CACHE_PERIOD
        search_['Cache-Control'] = 'max-age=%s' % (cache_period * 60)
        search_['Expires'] = (
            (datetime.utcnow() + timedelta(minutes=cache_period))
            .strftime(EXPIRES_FMT))
        return search_

    cleaned = search_form.cleaned_data

    # On mobile, we default to just wiki results.
    if request.MOBILE and cleaned['w'] == constants.WHERE_BASIC:
        cleaned['w'] = constants.WHERE_WIKI

    page = max(smart_int(request.GET.get('page')), 1)
    offset = (page - 1) * settings.SEARCH_RESULTS_PER_PAGE

    lang = language.lower()
    if settings.LANGUAGES_DICT.get(lang):
        lang_name = settings.LANGUAGES_DICT[lang]
    else:
        lang_name = ''

    # We use a regular S here because we want to search across
    # multiple doctypes.
    searcher = (AnalyzerS().es(urls=settings.ES_URLS)
                .indexes(es_utils.read_index('default')))

    wiki_f = F(model='wiki_document')
    question_f = F(model='questions_question')

    # Start - wiki filters

    if cleaned['w'] & constants.WHERE_WIKI:
        # Category filter
        wiki_f &= F(document_category__in=settings.SEARCH_DEFAULT_CATEGORIES)

        # Locale filter
        wiki_f &= F(document_locale=language)

        # Product filter
        products = cleaned['product']
        for p in products:
            wiki_f &= F(product=p)

        # Archived bit
        wiki_f &= F(document_is_archived=False)

    # End - wiki filters

    # Start - support questions filters

    if cleaned['w'] & constants.WHERE_SUPPORT:
        # Has helpful answers is set by default if using basic search
        cleaned['has_helpful'] = constants.TERNARY_YES

        # No archived questions in default search.
        cleaned['is_archived'] = constants.TERNARY_NO

        # These filters are ternary, they can be either YES, NO, or OFF
        ternary_filters = ('has_helpful', 'is_archived')
        d = dict(('question_%s' % filter_name,
                  _ternary_filter(cleaned[filter_name]))
                 for filter_name in ternary_filters if cleaned[filter_name])
        if d:
            question_f &= F(**d)

        # Product filter
        products = cleaned['product']
        for p in products:
            question_f &= F(product=p)

    # End - support questions filters

    # Done with all the filtery stuff--time  to generate results

    # Combine all the filters and add to the searcher
    doctypes = []
    final_filter = F()
    if cleaned['w'] & constants.WHERE_WIKI:
        doctypes.append(DocumentMappingType.get_mapping_type_name())
        final_filter |= wiki_f

    if cleaned['w'] & constants.WHERE_SUPPORT:
        doctypes.append(QuestionMappingType.get_mapping_type_name())
        final_filter |= question_f

    searcher = searcher.doctypes(*doctypes)
    searcher = searcher.filter(final_filter)

    if 'explain' in request.GET and request.GET['explain'] == '1':
        searcher = searcher.explain()

    documents = ComposedList()

    try:
        cleaned_q = cleaned['q']

        # Set up the highlights. Show the entire field highlighted.
        searcher = searcher.highlight(
            'question_content',  # support forum
            'document_summary',  # kb
            pre_tags=['<b>'],
            post_tags=['</b>'],
            number_of_fragments=0)

        # Set up boosts
        searcher = searcher.boost(
            question_title=4.0,
            question_content=3.0,
            question_answer_content=3.0,
            document_title=6.0,
            document_content=1.0,
            document_keywords=8.0,
            document_summary=2.0,

            # Text phrases in document titles and content get an extra
            # boost.
            document_title__match_phrase=10.0,
            document_content__match_phrase=8.0)

        # Build the query
        query_fields = chain(*[
            cls.get_query_fields() for cls in [
                DocumentMappingType,
                QuestionMappingType
            ]
        ])
        query = {}
        # Create match and match_phrase queries for every field
        # we want to search.
        for field in query_fields:
            for query_type in ['match', 'match_phrase']:
                query['%s__%s' % (field, query_type)] = cleaned_q

        # Transform the query to use locale aware analyzers.
        query = es_utils.es_query_with_analyzer(query, language)

        searcher = searcher.query(should=True, **query)

        num_results = min(searcher.count(), settings.SEARCH_MAX_RESULTS)

        # TODO - Can ditch the ComposedList here, but we need
        # something that paginate can use to figure out the paging.
        documents = ComposedList()
        documents.set_count(('results', searcher), num_results)

        results_per_page = settings.SEARCH_RESULTS_PER_PAGE
        pages = paginate(request, documents, results_per_page)

        # If we know there aren't any results, let's cheat and in
        # doing that, not hit ES again.
        if num_results == 0:
            searcher = []
        else:
            # Get the documents we want to show and add them to
            # docs_for_page
            documents = documents[offset:offset + results_per_page]

            if len(documents) == 0:
                # If the user requested a page that's beyond the
                # pagination, then documents is an empty list and
                # there are no results to show.
                searcher = []
            else:
                bounds = documents[0][1]
                searcher = searcher[bounds[0]:bounds[1]]

        results = []
        for i, doc in enumerate(searcher):
            rank = i + offset

            if doc['model'] == 'wiki_document':
                summary = _build_es_excerpt(doc)
                if not summary:
                    summary = doc['document_summary']
                result = {
                    'title': doc['document_title'],
                    'type': 'document'}

            elif doc['model'] == 'questions_question':
                summary = _build_es_excerpt(doc)
                if not summary:
                    # We're excerpting only question_content, so if
                    # the query matched question_title or
                    # question_answer_content, then there won't be any
                    # question_content excerpts. In that case, just
                    # show the question--but only the first 500
                    # characters.
                    summary = bleach.clean(
                        doc['question_content'], strip=True)[:500]

                result = {
                    'title': doc['question_title'],
                    'type': 'question',
                    'is_solved': doc['question_is_solved'],
                    'num_answers': doc['question_num_answers'],
                    'num_votes': doc['question_num_votes'],
                    'num_votes_past_week': doc['question_num_votes_past_week']}

            result['url'] = doc['url']
            result['object'] = doc
            result['search_summary'] = summary
            result['rank'] = rank
            result['score'] = doc.es_meta.score
            result['explanation'] = escape(format_explanation(
                doc.es_meta.explanation))
            results.append(result)

    except ES_EXCEPTIONS as exc:
        # Handle timeout and all those other transient errors with a
        # "Search Unavailable" rather than a Django error page.
        if is_json:
            return HttpResponse(json.dumps({'error': _('Search Unavailable')}),
                                content_type=content_type, status=503)

        # Cheating here: Convert from 'Timeout()' to 'timeout' so
        # we have less code, but still have good stats.
        exc_bucket = repr(exc).lower().strip('()')
        statsd.incr('search.esunified.{0}'.format(exc_bucket))

        log.exception(exc)

        t = 'search/mobile/down.html' if request.MOBILE else 'search/down.html'
        return render(request, t, {'q': cleaned['q']}, status=503)

    items = [(k, v) for k in search_form.fields for
             v in r.getlist(k) if v and k != 'a']
    items.append(('a', '2'))

    fallback_results = None
    if num_results == 0:
        fallback_results = _fallback_results(language, cleaned['product'])

    product = Product.objects.filter(slug__in=cleaned['product'])
    if product:
        product_titles = [_(p.title, 'DB: products.Product.title')
                          for p in product]
    else:
        product_titles = [_('All Products')]

    product_titles = ', '.join(product_titles)

    data = {
        'num_results': num_results,
        'results': results,
        'fallback_results': fallback_results,
        'product_titles': product_titles,
        'q': cleaned['q'],
        'w': cleaned['w'],
        'lang_name': lang_name, }

    if is_json:
        # Models are not json serializable.
        for r in data['results']:
            del r['object']
        data['total'] = len(data['results'])

        data['products'] = ([{'slug': p.slug, 'title': p.title}
                             for p in Product.objects.filter(visible=True)])

        if product:
            data['product'] = product[0].slug

        pages = Paginator(pages)
        data['pagination'] = dict(
            number=pages.pager.number,
            num_pages=pages.pager.paginator.num_pages,
            has_next=pages.pager.has_next(),
            has_previous=pages.pager.has_previous(),
            max=pages.max,
            span=pages.span,
            dotted_upper=pages.pager.dotted_upper,
            dotted_lower=pages.pager.dotted_lower,
            page_range=pages.pager.page_range,
            url=pages.pager.url,
        )
        if not results:
            data['message'] = _('No pages matched the search criteria')
        json_data = json.dumps(data)
        if callback:
            json_data = callback + '(' + json_data + ');'

        return HttpResponse(json_data, content_type=content_type)

    data.update({
        'product': product,
        'products': Product.objects.filter(visible=True),
        'pages': pages,
        'search_form': search_form,
        'advanced': False,
    })
    results_ = render(request, template, data)
    cache_period = settings.SEARCH_CACHE_PERIOD
    results_['Cache-Control'] = 'max-age=%s' % (cache_period * 60)
    results_['Expires'] = (
        (datetime.utcnow() + timedelta(minutes=cache_period))
        .strftime(EXPIRES_FMT))
    results_.set_cookie(settings.LAST_SEARCH_COOKIE, urlquote(cleaned['q']),
                        max_age=3600, secure=False, httponly=False)

    return results_

@mobile_template('search/{mobile/}results.html')
def advanced_search(request, template=None):
    """ES-specific Advanced search view"""

    # JSON-specific variables
    is_json = (request.GET.get('format') == 'json')
    callback = request.GET.get('callback', '').strip()
    content_type = (
        'application/x-javascript' if callback else 'application/json')

    # Check callback is valid
    if is_json and callback and not jsonp_is_valid(callback):
        return HttpResponse(
            json.dumps({'error': _('Invalid callback function.')}),
            content_type=content_type, status=400)

    language = locale_or_default(
        request.GET.get('language', request.LANGUAGE_CODE))
    r = request.GET.copy()
    # TODO: Figure out how to get rid of 'a' and do it.
    # It basically is used to switch between showing the form or results.
    a = request.GET.get('a', '2')
    # TODO: This is so the 'a=1' stays in the URL for pagination.
    r['a'] = 1

    # Search default values
    try:
        category = (map(int, r.getlist('category')) or
                    settings.SEARCH_DEFAULT_CATEGORIES)
    except ValueError:
        category = settings.SEARCH_DEFAULT_CATEGORIES
    r.setlist('category', category)

    r['language'] = language

    search_form = AdvancedSearchForm(r, auto_id=False)
    search_form.set_allowed_forums(request.user)

    # This is all we use a for now I think.
    if not search_form.is_valid() or a == '2':
        if is_json:
            return HttpResponse(
                json.dumps({'error': _('Invalid search data.')}),
                content_type=content_type,
                status=400)

        t = template if request.MOBILE else 'search/form.html'
        search_ = render(request, t, {
            'advanced': True, 'request': request,
            'search_form': search_form})
        cache_period = settings.SEARCH_CACHE_PERIOD
        search_['Cache-Control'] = 'max-age=%s' % (cache_period * 60)
        search_['Expires'] = (
            (datetime.utcnow() + timedelta(minutes=cache_period))
            .strftime(EXPIRES_FMT))
        return search_

    cleaned = search_form.cleaned_data

    if request.MOBILE and cleaned['w'] == constants.WHERE_BASIC:
        cleaned['w'] = constants.WHERE_WIKI

    page = max(smart_int(request.GET.get('page')), 1)
    offset = (page - 1) * settings.SEARCH_RESULTS_PER_PAGE

    lang = language.lower()
    if settings.LANGUAGES_DICT.get(lang):
        lang_name = settings.LANGUAGES_DICT[lang]
    else:
        lang_name = ''

    # We use a regular S here because we want to search across
    # multiple doctypes.
    searcher = (AnalyzerS().es(urls=settings.ES_URLS)
                .indexes(es_utils.read_index('default')))

    wiki_f = F(model='wiki_document')
    question_f = F(model='questions_question')
    discussion_f = F(model='forums_thread')

    # Start - wiki filters

    if cleaned['w'] & constants.WHERE_WIKI:
        # Category filter
        if cleaned['category']:
            wiki_f &= F(document_category__in=cleaned['category'])

        # Locale filter
        wiki_f &= F(document_locale=language)

        # Product filter
        products = cleaned['product']
        for p in products:
            wiki_f &= F(product=p)

        # Topics filter
        topics = cleaned['topics']
        for t in topics:
            wiki_f &= F(topic=t)

        # Archived bit
        if not cleaned['include_archived']:
            wiki_f &= F(document_is_archived=False)

    # End - wiki filters

    # Start - support questions filters

    if cleaned['w'] & constants.WHERE_SUPPORT:
        # These filters are ternary, they can be either YES, NO, or OFF
        ternary_filters = ('is_locked', 'is_solved', 'has_answers',
                           'has_helpful', 'is_archived')
        d = dict(('question_%s' % filter_name,
                  _ternary_filter(cleaned[filter_name]))
                 for filter_name in ternary_filters if cleaned[filter_name])
        if d:
            question_f &= F(**d)

        if cleaned['asked_by']:
            question_f &= F(question_creator=cleaned['asked_by'])

        if cleaned['answered_by']:
            question_f &= F(question_answer_creator=cleaned['answered_by'])

        q_tags = [t.strip() for t in cleaned['q_tags'].split(',')]
        for t in q_tags:
            if t:
                question_f &= F(question_tag=t)

        # Product filter
        products = cleaned['product']
        for p in products:
            question_f &= F(product=p)

        # Topics filter
        topics = cleaned['topics']
        for t in topics:
            question_f &= F(topic=t)

    # End - support questions filters

    # Start - discussion forum filters

    if cleaned['w'] & constants.WHERE_DISCUSSION:
        if cleaned['author']:
            discussion_f &= F(post_author_ord=cleaned['author'])

        if cleaned['thread_type']:
            if constants.DISCUSSION_STICKY in cleaned['thread_type']:
                discussion_f &= F(post_is_sticky=1)

            if constants.DISCUSSION_LOCKED in cleaned['thread_type']:
                discussion_f &= F(post_is_locked=1)

        valid_forum_ids = [
            f.id for f in Forum.authorized_forums_for_user(request.user)]

        forum_ids = None
        if cleaned['forum']:
            forum_ids = [f for f in cleaned['forum'] if f in valid_forum_ids]

        # If we removed all the forums they wanted to look at or if
        # they didn't specify, then we filter on the list of all
        # forums they're authorized to look at.
        if not forum_ids:
            forum_ids = valid_forum_ids

        discussion_f &= F(post_forum_id__in=forum_ids)

    # End - discussion forum filters

    # Created filter
    unix_now = int(time.time())
    interval_filters = (
        ('created', cleaned['created'], cleaned['created_date']),
        ('updated', cleaned['updated'], cleaned['updated_date']))
    for filter_name, filter_option, filter_date in interval_filters:
        if filter_option == constants.INTERVAL_BEFORE:
            before = {filter_name + '__gte': 0,
                      filter_name + '__lte': max(filter_date, 0)}

            discussion_f &= F(**before)
            question_f &= F(**before)
        elif filter_option == constants.INTERVAL_AFTER:
            after = {filter_name + '__gte': min(filter_date, unix_now),
                     filter_name + '__lte': unix_now}

            discussion_f &= F(**after)
            question_f &= F(**after)

    # Note: num_voted (with a d) is a different field than num_votes
    # (with an s). The former is a dropdown and the latter is an
    # integer value.
    if cleaned['num_voted'] == constants.INTERVAL_BEFORE:
        question_f &= F(question_num_votes__lte=max(cleaned['num_votes'], 0))
    elif cleaned['num_voted'] == constants.INTERVAL_AFTER:
        question_f &= F(question_num_votes__gte=cleaned['num_votes'])

    # Done with all the filtery stuff--time  to generate results

    # Combine all the filters and add to the searcher
    doctypes = []
    final_filter = F()
    if cleaned['w'] & constants.WHERE_WIKI:
        doctypes.append(DocumentMappingType.get_mapping_type_name())
        final_filter |= wiki_f

    if cleaned['w'] & constants.WHERE_SUPPORT:
        doctypes.append(QuestionMappingType.get_mapping_type_name())
        final_filter |= question_f

    if cleaned['w'] & constants.WHERE_DISCUSSION:
        doctypes.append(ThreadMappingType.get_mapping_type_name())
        final_filter |= discussion_f

    searcher = searcher.doctypes(*doctypes)
    searcher = searcher.filter(final_filter)

    if 'explain' in request.GET and request.GET['explain'] == '1':
        searcher = searcher.explain()

    documents = ComposedList()

    try:
        cleaned_q = cleaned['q']

        # Set up the highlights. Show the entire field highlighted.
        searcher = searcher.highlight(
            'question_content',  # support forum
            'document_summary',  # kb
            'post_content',  # contributor forum
            pre_tags=['<b>'],
            post_tags=['</b>'],
            number_of_fragments=0)

        # Set up boosts
        searcher = searcher.boost(
            question_title=4.0,
            question_content=3.0,
            question_answer_content=3.0,
            post_title=2.0,
            post_content=1.0,
            document_title=6.0,
            document_content=1.0,
            document_keywords=8.0,
            document_summary=2.0,

            # Text phrases in document titles and content get an extra
            # boost.
            document_title__match_phrase=10.0,
            document_content__match_phrase=8.0)

        # Apply sortby for advanced search of questions
        if cleaned['w'] == constants.WHERE_SUPPORT:
            sortby = cleaned['sortby']
            try:
                searcher = searcher.order_by(
                    *constants.SORT_QUESTIONS[sortby])
            except IndexError:
                # Skip index errors because they imply the user is
                # sending us sortby values that aren't valid.
                pass

        # Apply sortby for advanced search of kb documents
        if cleaned['w'] == constants.WHERE_WIKI:
            sortby = cleaned['sortby_documents']
            try:
                searcher = searcher.order_by(
                    *constants.SORT_DOCUMENTS[sortby])
            except IndexError:
                # Skip index errors because they imply the user is
                # sending us sortby values that aren't valid.
                pass

        # Build the query
        if cleaned_q:
            query_fields = chain(*[
                cls.get_query_fields() for cls in [
                    DocumentMappingType,
                    ThreadMappingType,
                    QuestionMappingType
                ]
            ])
            query = {}
            # Create a simple_query_search query for every field
            # we want to search.
            for field in query_fields:
                query['%s__sqs' % field] = cleaned_q

            # Transform the query to use locale aware analyzers.
            query = es_utils.es_query_with_analyzer(query, language)

            searcher = searcher.query(should=True, **query)

        num_results = min(searcher.count(), settings.SEARCH_MAX_RESULTS)

        # TODO - Can ditch the ComposedList here, but we need
        # something that paginate can use to figure out the paging.
        documents = ComposedList()
        documents.set_count(('results', searcher), num_results)

        results_per_page = settings.SEARCH_RESULTS_PER_PAGE
        pages = paginate(request, documents, results_per_page)

        # If we know there aren't any results, let's cheat and in
        # doing that, not hit ES again.
        if num_results == 0:
            searcher = []
        else:
            # Get the documents we want to show and add them to
            # docs_for_page
            documents = documents[offset:offset + results_per_page]

            if len(documents) == 0:
                # If the user requested a page that's beyond the
                # pagination, then documents is an empty list and
                # there are no results to show.
                searcher = []
            else:
                bounds = documents[0][1]
                searcher = searcher[bounds[0]:bounds[1]]

        results = []
        for i, doc in enumerate(searcher):
            rank = i + offset

            if doc['model'] == 'wiki_document':
                summary = _build_es_excerpt(doc)
                if not summary:
                    summary = doc['document_summary']
                result = {
                    'title': doc['document_title'],
                    'type': 'document'}

            elif doc['model'] == 'questions_question':
                summary = _build_es_excerpt(doc)
                if not summary:
                    # We're excerpting only question_content, so if
                    # the query matched question_title or
                    # question_answer_content, then there won't be any
                    # question_content excerpts. In that case, just
                    # show the question--but only the first 500
                    # characters.
                    summary = bleach.clean(
                        doc['question_content'], strip=True)[:500]

                result = {
                    'title': doc['question_title'],
                    'type': 'question',
                    'is_solved': doc['question_is_solved'],
                    'num_answers': doc['question_num_answers'],
                    'num_votes': doc['question_num_votes'],
                    'num_votes_past_week': doc['question_num_votes_past_week']}

            else:
                summary = _build_es_excerpt(doc, first_only=True)
                result = {
                    'title': doc['post_title'],
                    'type': 'thread'}

            result['url'] = doc['url']
            result['object'] = doc
            result['search_summary'] = summary
            result['rank'] = rank
            result['score'] = doc.es_meta.score
            result['explanation'] = escape(format_explanation(
                doc.es_meta.explanation))
            results.append(result)

    except ES_EXCEPTIONS as exc:
        # Handle timeout and all those other transient errors with a
        # "Search Unavailable" rather than a Django error page.
        if is_json:
            return HttpResponse(json.dumps({'error': _('Search Unavailable')}),
                                content_type=content_type, status=503)

        # Cheating here: Convert from 'Timeout()' to 'timeout' so
        # we have less code, but still have good stats.
        exc_bucket = repr(exc).lower().strip('()')
        statsd.incr('search.esunified.{0}'.format(exc_bucket))

        log.exception(exc)

        t = 'search/mobile/down.html' if request.MOBILE else 'search/down.html'
        return render(request, t, {'q': cleaned['q']}, status=503)

    items = [(k, v) for k in search_form.fields for
             v in r.getlist(k) if v and k != 'a']
    items.append(('a', '2'))

    fallback_results = None
    if num_results == 0:
        fallback_results = _fallback_results(language, cleaned['product'])

    product = Product.objects.filter(slug__in=cleaned['product'])
    if product:
        product_titles = [_(p.title, 'DB: products.Product.title')
                          for p in product]
    else:
        product_titles = [_('All Products')]

    product_titles = ', '.join(product_titles)

    data = {
        'num_results': num_results,
        'results': results,
        'fallback_results': fallback_results,
        'product_titles': product_titles,
        'q': cleaned['q'],
        'w': cleaned['w'],
        'lang_name': lang_name,
        'advanced': True,
    }

    if is_json:
        # Models are not json serializable.
        for r in data['results']:
            del r['object']
        data['total'] = len(data['results'])

        data['products'] = ([{'slug': p.slug, 'title': p.title}
                             for p in Product.objects.filter(visible=True)])

        if product:
            data['product'] = product[0].slug

        pages = Paginator(pages)
        data['pagination'] = dict(
            number=pages.pager.number,
            num_pages=pages.pager.paginator.num_pages,
            has_next=pages.pager.has_next(),
            has_previous=pages.pager.has_previous(),
            max=pages.max,
            span=pages.span,
            dotted_upper=pages.pager.dotted_upper,
            dotted_lower=pages.pager.dotted_lower,
            page_range=pages.pager.page_range,
            url=pages.pager.url,
        )
        if not results:
            data['message'] = _('No pages matched the search criteria')
        json_data = json.dumps(data)
        if callback:
            json_data = callback + '(' + json_data + ');'

        return HttpResponse(json_data, content_type=content_type)

    data.update({
        'product': product,
        'products': Product.objects.filter(visible=True),
        'pages': pages,
        'search_form': search_form, })
    results_ = render(request, template, data)
    cache_period = settings.SEARCH_CACHE_PERIOD
    results_['Cache-Control'] = 'max-age=%s' % (cache_period * 60)
    results_['Expires'] = (
        (datetime.utcnow() + timedelta(minutes=cache_period))
        .strftime(EXPIRES_FMT))
    results_.set_cookie(settings.LAST_SEARCH_COOKIE, urlquote(cleaned['q']),
                        max_age=3600, secure=False, httponly=False)

    return results_

@cache_page(60 * 15)  # 15 minutes.
def suggestions(request):
    """A simple search view that returns OpenSearch suggestions."""
    content_type = 'application/x-suggestions+json'

    term = request.GET.get('q')
    if not term:
        return HttpResponseBadRequest(content_type=content_type)

    site = Site.objects.get_current()
    locale = locale_or_default(request.LANGUAGE_CODE)
    try:
        query = dict(('%s__match' % field, term)
                     for field in DocumentMappingType.get_query_fields())
        # Upgrade the query to an analyzer-aware one.
        query = es_utils.es_query_with_analyzer(query, locale)

        wiki_s = (DocumentMappingType.search()
                  .filter(document_is_archived=False)
                  .filter(document_locale=locale)
                  .values_dict('document_title', 'url')
                  .query(or_=query)[:5])

        query = dict(('%s__match' % field, term)
                     for field in QuestionMappingType.get_query_fields())
        question_s = (QuestionMappingType.search()
                      .filter(question_has_helpful=True)
                      .values_dict('question_title', 'url')
                      .query(or_=query)[:5])

        results = list(chain(question_s, wiki_s))
    except ES_EXCEPTIONS:
        # If we have ES problems, we just send back an empty result
        # set.
        results = []

    def urlize(r):
        return u'https://%s%s' % (site, r['url'])

    def titleize(r):
        return r.get('document_title', r.get('document_title'))

    data = [term,
            [titleize(r) for r in results],
            [],
            [urlize(r) for r in results]]
    return HttpResponse(json.dumps(data), content_type=content_type)

@cache_page(60 * 60 * 168)  # 1 week.
def plugin(request):
    """Render an OpenSearch Plugin."""
    site = Site.objects.get_current()
    return render(request, 'search/plugin.html', {
        'site': site, 'locale': request.LANGUAGE_CODE},
        content_type='application/opensearchdescription+xml')

def _ternary_filter(ternary_value):
    """Return a search query given a TERNARY_YES or TERNARY_NO.

    Behavior for TERNARY_OFF is undefined.

    """
    return ternary_value == constants.TERNARY_YES

def _build_es_excerpt(result, first_only=False):
    """Return concatenated search excerpts.

    :arg result: The result object from the queryset results
    :arg first_only: True if we should show only the first bit, False
        if we should show all bits

    """
    bits = [m.strip() for m in chain(*result.es_meta.highlight.values())]

    if first_only and bits:
        excerpt = bits[0]
    else:
        excerpt = EXCERPT_JOINER.join(bits)

    return jinja2.Markup(clean_excerpt(excerpt))

def _fallback_results(locale, product_slugs):
    """Return the top 20 articles by votes for the given product(s)."""
    products = []
    for slug in product_slugs:
        try:
            p = Product.objects.get(slug=slug)
            products.append(p)
        except Product.DoesNotExist:
            pass

    docs, fallback = documents_for(locale, products=products)
    docs = docs + (fallback or [])

    return docs[:20]


text:
<?php

// Config
require_once "lib/spyc/Spyc.php";

// Check that config exists
if (!is_readable('config/config.yaml'))
{
    header("HTTP/1.1 500 Internal Server Error");
    echo "
500 Internal Server Error
";
    echo "
service/web/config/config.yaml does not exist or is not readable.
";
    die;
}

// Read config file
$config = Spyc::YAMLLoad('config/config.yaml');

// Path Configuration
$SUMA_SERVER_PATH = $config['SUMA_SERVER_PATH'];
$SUMA_CONTROLLER_PATH = $config['SUMA_CONTROLLER_PATH'];
$SUMA_BASE_URL = $config['SUMA_BASE_URL'];

// Debug Mode Setup
if ($config['SUMA_DEBUG'] == true)
{
    $SUMA_ERROR_REPORTING  = E_ERROR | E_WARNING | E_PARSE | E_NOTICE;
    $SUMA_DISPLAY_ERRORS   = 'on';
    $SUMA_THROW_EXCEPTIONS =  true;
    error_reporting($SUMA_ERROR_REPORTING);
}
else
{
    $SUMA_DISPLAY_ERRORS   = 'off';
    $SUMA_THROW_EXCEPTIONS =  false;
}

// Error Reporting
ini_set('display_errors', $SUMA_DISPLAY_ERRORS);

// Set paths
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . $SUMA_SERVER_PATH . PATH_SEPARATOR . $SUMA_SERVER_PATH . '/vendor/shardj/zf1-future/library');

try
{
    // Zend Framework CLass Loader
    if (!include_once('Zend/Loader.php'))
    {
        throw new Exception('Possible error in SUMA_SERVER_PATH.');
    }

    if (!include_once('Zend/Session.php'))
    {
        throw new Exception('Possible error in SUMA_SERVER_PATH.');
    }

    if (!include_once('config/Globals.php'))
    {
        throw new Exception('Possible error in SUMA_SERVER_PATH.');
    }

    try {
        Zend_Loader::loadClass('Zend_Controller_Front');

        $sessionFileBase = "../config/session";
        if (is_readable($sessionFileBase.'.yaml'))
        {
            $sessionConfig = new Zend_Config_Yaml($sessionFileBase.'.yaml', 'production');
        }
        elseif (is_readable($sessionFileBase.'.ini'))
        {
            $sessionConfig = new Zend_Config_Ini($sessionFileBase.'.ini', 'production');
        }
        else
        {
            $sessionConfig = null;
        }

        // If session config has been loaded properly, set it.
        // App shouldn't die if session options are not set
        if ($sessionConfig) {
            Zend_Session::setOptions($sessionConfig->toArray());
        }

        // Get front controller instance
        // Configure for Zone
        $front = Zend_Controller_Front::getInstance();
        $front->setControllerDirectory($SUMA_CONTROLLER_PATH)
              ->setBaseUrl($SUMA_BASE_URL);

        Zend_Registry::set('sumaDisplayExceptions', $SUMA_THROW_EXCEPTIONS);

        // Go
        $front->dispatch();
    }
    catch (Exception $e)
    {
        throw new Exception("Possible error in SUMA_CONTROLLER_PATH. " . $e->getMessage());
    }
}
catch (Exception $e)
{
    header("HTTP/1.1 500 Internal Server Error");
    print "Trouble loading Suma server application. This is often caused by configuration issues. Please refer to the troubleshooting docs at http://suma-project.github.io/Suma ";
    print "The raw output is below: ";
    print $e->getMessage();
    die;
}

redpajama_set_name: RedPajamaGithub


text:
using System;
using System.Collections.Generic;
using System.Web;
using ServiceStack.Common;
using ServiceStack;

using GatecoinServiceInterface.Response;
namespace GatecoinServiceInterface.Request{
[Route("/Account/ResidentInformation", "GET", Summary = @"Get Step2 Data", Notes = @"")]
public class GetResidentInformation : IReturn<ResidentInformationResponse>
{
}
}


text:
The web extension works by relying on on the background.js to make calls to the API, in response to listeners being triggered when a page loads, or tab is activated.

In both cases `doGreencheckForTabReplace` is called, calling `getGreencheck` which then calls `doRequest` to update the chrome for the given tab, with the grey/green icon.

# Updating the page DOM - links to green sites, and annotating search results

In addition to updating the browser chrome, the extension also adds extra markup on the page on browsers, using the content scripts, `search-all.js`, `search-bing.js` and so on.

These send messages to background.js with `runtime.sendMessage`, which triggers `chrome.runtime.onMessage` in background.js. This calls `doSearchRequest`, which triggers a batched API request with `doApiRequest`, to send info back to the tab, to update the DOM.

More info on:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webNavigation/onCommitted
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Communicating_with_background_scripts
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage


text:
Directory                |  Contents
-------------------------|----------
[`connector`](connector) | Connectors
[`ic`](ic)               | ICs including microcontrollers, amplifiers, sensors,etc
[`misc`](misc)           | Other parts including generic/drawing symbols
[`module`](module)       | PCB subassemblies and modules
[`power`](power)         | Power symbols
[`passive`](passive)     | Passive and other two-terminal devices including crystals, etc
[`ui`](ui)               | User interface elements including buzzers, switches, etc

The file `agg-kicad.kicad_sym` contains all of the symbols in this library.

## General Guidelines

### Enforced automatically

* One symbol per library
    * Exception for automatically generated libraries such as `conn.kicad_sym`
* Library filename the same as the part name
* All pins on 100mil grid
* 100mil pin length for ICs and similar symbols
* No missing numbers in pin numbering sequence
* Text size 50mil (fields, pin names, pin numbers)
    * Exception for pin numbers that are words, e.g. "PAD"
* Part reference above the part
* Part name (value field) below the part
* Fields must be horizontal
* Name and reference fields must be visible unless explicitly overridden
* Other fields (footprint, datasheet, order codes) must be invisible
* ICs and similar symbols to be filled with background colour

### Must be checked manually

* Part name follows the manufacturer or common generic name
* Part designator follows IEEE 315 where possible
* Origin on/close to centre of part
* Name and reference left-aligned with left edge on non-symmetrical symbols
* Name and reference centre-aligned on symmetrical symbols

### Helpful but not mandatory

* Include Farnell order code where applicable
* Include DigiKey and RS order codes where applicable and useful
* Pre-fill with a footprint field if one is very commonly used

## Associated documentation to include in README

* Link to datasheet/web page
* Common supplier order codes
* Names of available footprints
* Has the symbol been validated in practice? Where?
* Any other notes / gotchas


text:
namespace Urho3D
{

namespace OverLib
{

EVENT(E_STATEPRESTART, StatePreStart)
{
}

EVENT(E_STATESTART, StateStart)
{
}

EVENT(E_STATEEND, StateEnd)
{
}

EVENT(E_STATEPOSTEND, StatePostEnd)
{
}

EVENT(E_STATELOADINGUPDATE, StateLoadingUpdate)
{
    PARAM(P_MESSAGE, Message); // String
}

EVENT(E_STATELOADINGSTART, StateLoadingStart)
{
    PARAM(P_OLDSTATE, OldState); // Old StateObject
    PARAM(P_NEWSTATE, NewState); // New StateObject
}

EVENT(E_STATELOADINGEND, StateLoadingEnd)
{
    PARAM(P_NEWSTATE, NewState); // New StateObject
}

/// %StateObject registers event handlers by default
class URHO3D_API StateObject : public Urho3D::Object
{
    OBJECT(StateObject);

public:
    StateObject(Urho3D::Context* context);

protected:
    virtual void HandleStatePreStart(Urho3D::StringHash eventType, Urho3D::VariantMap& eventData);
    virtual void HandleStateStart(Urho3D::StringHash eventType, Urho3D::VariantMap& eventData);
    virtual void HandleStateEnd(Urho3D::StringHash eventType, Urho3D::VariantMap& eventData);
    virtual void HandleStatePostEnd(Urho3D::StringHash eventType, Urho3D::VariantMap& eventData);
    virtual void HandleLoadingUpdate(Urho3D::StringHash eventType, Urho3D::VariantMap& eventData);

protected:
    void PostLoadingUpdate(const Urho3D::String& msg);
    void PostLoadingComplete();
};

/// %StateManager able to be set as a SubSystem in Urho3D
class URHO3D_API StateManager : public Urho3D::Object
{
    OBJECT(StateManager);

public:
    /// Construct.
    StateManager(Urho3D::Context* context);
    /// Constructor with an initial loading state
    StateManager(Urho3D::Context* context, Urho3D::Object* loadingState);
    /// Destruct.
    ~StateManager();

public:
    void SetState(Urho3D::Object* state);
    Urho3D::Object* GetState();

    void SetLoadingState(Urho3D::Object* loadingState);
    Urho3D::Object* GetLoadingState();

public: // Only for use by the States themselves
    void PostLoadingUpdate(Urho3D::String msg);
    void PostLoadingComplete();

private:
    Urho3D::SharedPtr<Urho3D::Object> state_;
    Urho3D::SharedPtr<Urho3D::Object> loadingState_;

    enum InternalState {
        NO_TRANSITION,
        LOADINGSCREEN

    };

    InternalState internalState_;
};

}

}


text:
package com.travel.sibar.sibartravel;

import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Address;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.support.annotation.Nullable;

import java.util.List;

/**
 * Created by satriabagus on 7/18/16.
 */
public class MapsService extends Service implements LocationListener {

    private final Context context;

    boolean isGPSEnable = false;
    boolean canGetLocation = false;
    boolean isNetworkEnable = false;

    Location location;

    double latitude;
    double longitude;

    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;
    private static final long MIN_TIM_BW_UPDATES = 1000 * 60 * 1;

    protected LocationManager locationManager;

    public MapsService(Context context) {
        this.context = context;
        getLocation();

    }

    public Location getLocation() {
        try {
            locationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);
            isGPSEnable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            isNetworkEnable = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnable && !isNetworkEnable) {

            } else {
                this.canGetLocation = true;

                if (isNetworkEnable) {

                    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIM_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);

                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }

                if (isGPSEnable) {

                    if (location == null) {
                        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIM_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                        if (locationManager != null) {
                            location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }

                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return location;
    }

    public void stopUsingGPS() {
        if (locationManager != null) {

            locationManager.removeUpdates(MapsService.this);

        }
    }

    public double getLatitude(){
         if(location != null){
             latitude = location.getLatitude();
         }

        return latitude;
    }

    public double getLongitude(){
        if(location != null){
            longitude = location.getLongitude();
        }

        return longitude;
    }

    public boolean canGetLocation(){
        return this.canGetLocation;
    }

    public void showSettingAlert(){
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);

        alertDialog.setTitle("GPS Setting");

        alertDialog.setMessage("GPS is not enabled. Do you want enable it?");

        alertDialog.setPositiveButton("Setting", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                 Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                 context.startActivity(intent) ;
            }
        });

        alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        alertDialog.show();
    }

    @Override
    public void onLocationChanged(Location location) {

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}


text:
package com.cowthan.concurrent.c8;

public class SynchronizedEventGenerator extends IntGenerator {
	private int currentEvenValue = 0;

	public int next() {
		synchronized (this) {
			++currentEvenValue;
			++currentEvenValue;
			return currentEvenValue;
		}
	}

	public static void main(String[] args) {
		EvenChecker.test(new SynchronizedEventGenerator());
	}
}


text:
FROM ubuntu:latest

# Update OS
#RUN sed -i 's/# \(.*multiverse$\)/\1/g' /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y upgrade

# Install Python
RUN apt-get install -y python-dev python-pip

# Add requirements.txt
RUN mkdir /opt/chatterbot
ADD requirements.txt /opt/requirements.txt

# Install uwsgi Python web server
RUN pip install uwsgi
# Install app requirements
RUN pip install -r /opt/requirements.txt

# Create app directory
ADD . /opt/chatterbot

# Set the default directory for our environment
ENV HOME /opt/chatterbot
WORKDIR /opt/chatterbot

# Expose port 8000 for uwsgi
EXPOSE 9019

ENTRYPOINT ["uwsgi"]
CMD  ["--http", "0.0.0.0:9019", "--module", "app:app", "--processes", "1", "--threads", "8"]


text:
[![Build Status](https://github.com/googleapis/google-cloudevents-dotnet/actions/workflows/build.yml/badge.svg)](https://github.com/googleapis/google-cloudevents-dotnet/actions/workflows/build.yml)

[![Google.Events.Protobuf](https://img.shields.io/nuget/vpre/Google.Events.Protobuf?label=Google.Events.Protobuf)](https://nuget.org/packages/Google.Events.Protobuf)

## Overview

This repository contains .NET libraries for CloudEvents issued by
Google. The `Google.Events.Protobuf` contains the Google event data
types, using Protocol Buffers as the serialization framework. Each
type is decorated with `CloudEventFormatterAttribute` to indicate an
appropriate `CloudEventFormatter` to use. This allows for seamless
integration in frameworks such as the Functions Framework, without
those frameworks depending on any libraries in this repository
directly.

## Usage

From the .NET Functions Framework, these packages are automatically
used for CloudEvent functions implementing
`ICloudEventFunction<TData>`, where `TData` is one of the event data
classes.

If you wish to use these classes in a different context, code might
typically look something like this:

```csharp
CloudEvent cloudEvent = ...; // However you obtain a CloudEvent

// The event data type from any of the serialization-specific packages.
StorageObjectData data = CloudEventConverters.ConvertCloudEventData<StorageData>(cloudEvent);
```

## Development

See [BUILDING](BUILDING.md) for more information about developing
the code within this repository.


text:
/*
 * gstcreator
 *     LinkCommand.h
 *
 *  Created on: 24 sty 2014
 *     Author: Marcin Kolny <marcin.kolny@gmail.com>
 */

#ifndef LINKCOMMAND_H_
#define LINKCOMMAND_H_

#include "Command.h"

class LinkCommand : public Command
{
private:
	Glib::RefPtr<Gst::Object> dst_element;

	void link_pads(const Glib::RefPtr<Gst::Pad>& src_pad, const Glib::RefPtr<Gst::Pad>& dst_pad);

public:
	LinkCommand(const Glib::RefPtr<Gst::Object>& dst_element, const Glib::RefPtr<Gst::Object>& model);
	void run_command();
	CommandType get_type();

};

#endif /* LINKCOMMAND_H_ */


text:
module SqsWorker
  class Queue
    attr_reader :name

    def initialize(client, name)
      @client = client
      @name = name
    end

    def add(job)
      dispatch('add', job: job)
      @client.execute(Plan.new(job, url))
    end

    def schedule(job, run_at)
      dispatch('schedule', job: job, run_at: run_at)
      @client.execute(Plan.new(job, url, run_at: run_at))
    end

    def poll(&block)
      dispatch('poll')
      @client.poll(url, &block)
    end

    def url
      @url ||= @client.url(name)
    end

    private

    def dispatch(event, params = {})
      @client.observer.dispatch("queue.#{event}", params)
    end
  end
end


text:
using UnityEngine;
using System.Collections;

public class RotesMcGoats: MonoBehaviour {

	private Transform tr;
	private Rigidbody rb;
	private bool collision = false;

	// Use this for initialization
	void Start () {

		tr = transform;
		rb = rigidbody;

		// set floor black
		GameObject.Find("Floor").renderer.material.color = Color.blue;
	}

	// Update is called once per frame
	void Update () {

		if ( !collision ){
			//tr.Rotate(90.0f*Time.deltaTime, 0.0f, 45.0f*Time.deltaTime);
		}

		if ( Input.GetMouseButton(0) ){

			Vector3 temp = Input.mousePosition;
			temp.z = 10.0f;
			tr.position = Camera.main.ScreenToWorldPoint(temp);
			rb.useGravity = false;
			rb.velocity = Vector3.zero;

			//          Debug.Log (temp);

		} else {

			//          Vector3 temp = new Vector3(-0.45f, 10f, -1.1f);
			//          tr.position = temp;
			//          rb.useGravity = false;
			//          rb.velocity = Vector3.zero;

			rb.useGravity = true;

		}

	}

	void OnCollisionEnter() {
		collision = true;
	}

	void OnCollisionExit() {
		collision = false;
	}

}


text:
 Improve this doc View source
pfCard - Utilization

directive in module patternfly.card

Description

Directive for easily displaying a card with html content

Usage

as attribute
<ANY pf-card - -utilization
     head-title="{string}"
     [sub-title="{string}"]
     [show-top-border="{boolean}"]
     [show-titles-separator="{boolean}"]
     [footer="{object}"]
     [filter="{object}"]>
   ...
</ANY>

Parameters

Param	Type	Details

headTitle	string

Title for the card

subTitle
(optional)
	string

Sub-Title for the card

showTopBorder
(optional)
	boolean

Show/Hide the blue top border. True shows top border, false (default) hides top border

showTitlesSeparator
(optional)
	boolean

Show/Hide the grey line between the title and sub-title.
True (default) shows the line, false hides the line

footer
(optional)
	object

footer configuration properties:

.iconClass  - (optional) the icon to show on the bottom left of the footer panel

.text       - (optional) the text to show on the bottom left of the footer panel, to the right of the icon

.href       - (optional) the href link to navigate to when the footer href is clicked

.callBackFn - (optional) user defined function to call when the footer href is clicked

*Note: If a href link and a callBackFn are specified, the href link will be called

filter
(optional)
	object

filter configuration properties:

.filters    - drop down items for the filter.

 Ex:  'filters' : [{label:'Last 30 Days', value:'30'},
                   {label:'Last 15 Days', value:'15'},
                   {label:'Today', value:'today'}]

.defaultFilter - integer, 0 based index into the filters array

.callBackFn - user defined function to call when a filter is selected

Example

Source

Demo