text:
"""Generate self-contained HTML reports from MNE objects."""

# Authors: Alex Gramfort <alexandre.gramfort@telecom-paristech.fr>
#          Mainak Jas <mainak@neuro.hut.fi>
#          Teon Brooks <teon.brooks@gmail.com>
#
# License: BSD (3-clause)

import base64
from io import BytesIO
import os
import os.path as op
import fnmatch
import re
import codecs
import time
from glob import glob
import warnings
import numpy as np

from . import read_evokeds, read_events, pick_types, read_cov
from .io import read_raw_fif, read_info, _stamp_to_dt
from .utils import (logger, verbose, get_subjects_dir, warn, _import_mlab,
                    fill_doc, _check_option)
from .viz import plot_events, plot_alignment, plot_cov
from .viz._3d import _plot_mri_contours
from .forward import read_forward_solution
from .epochs import read_epochs
from .minimum_norm import read_inverse_operator
from .parallel import parallel_func, check_n_jobs
from .viz.raw import _data_types

from .externals.tempita import HTMLTemplate, Template
from .externals.h5io import read_hdf5, write_hdf5

VALID_EXTENSIONS = ['raw.fif', 'raw.fif.gz', 'sss.fif', 'sss.fif.gz',
                    '-eve.fif', '-eve.fif.gz', '-cov.fif', '-cov.fif.gz',
                    '-trans.fif', '-trans.fif.gz', '-fwd.fif', '-fwd.fif.gz',
                    '-epo.fif', '-epo.fif.gz', '-inv.fif', '-inv.fif.gz',
                    '-ave.fif', '-ave.fif.gz', 'T1.mgz']
SECTION_ORDER = ['raw', 'events', 'epochs', 'evoked', 'covariance', 'trans',
                 'mri', 'forward', 'inverse']

###############################################################################
# PLOTTING FUNCTIONS

def _ndarray_to_fig(img):
    """Convert to MPL figure, adapted from matplotlib.image.imsave."""
    figsize = np.array(img.shape[:2][::-1]) / 100.
    fig = _figure_agg(dpi=100, figsize=figsize, frameon=False)
    fig.figimage(img)
    return fig

def _fig_to_img(fig, image_format='png', scale=None, **kwargs):
    """Plot figure and create a binary image."""
    # fig can be ndarray, mpl Figure, Mayavi Figure, or callable that produces
    # a mpl Figure
    import matplotlib.pyplot as plt
    from matplotlib.figure import Figure
    if isinstance(fig, np.ndarray):
        fig = _ndarray_to_fig(fig)
    elif callable(fig):
        plt.close('all')
        fig = fig(**kwargs)
    elif not isinstance(fig, Figure):
        mlab = None
        try:
            mlab = _import_mlab()
        # on some systems importing Mayavi raises SystemExit (!)
        except Exception:
            is_mayavi = False
        else:
            import mayavi
            is_mayavi = isinstance(fig, mayavi.core.scene.Scene)
        if not is_mayavi:
            raise TypeError('Each fig must be a matplotlib Figure, mayavi '
                            'Scene, or NumPy ndarray, got %s (type %s)'
                            % (fig, type(fig)))
        if fig.scene is not None:
            img = mlab.screenshot(figure=fig)
        else:  # Testing mode
            img = np.zeros((2, 2, 3))

        mlab.close(fig)
        fig = _ndarray_to_fig(img)

    output = BytesIO()
    if scale is not None:
        _scale_mpl_figure(fig, scale)
    logger.debug('Saving figure %s with dpi %s'
                 % (fig.get_size_inches(), fig.get_dpi()))
    with warnings.catch_warnings(record=True):
        warnings.simplefilter('ignore')  # incompatible axes
        fig.savefig(output, format=image_format, dpi=fig.get_dpi(),
                    bbox_to_inches='tight')
    plt.close(fig)
    output = output.getvalue()
    return (output.decode('utf-8') if image_format == 'svg' else
            base64.b64encode(output).decode('ascii'))

def _scale_mpl_figure(fig, scale):
    """Magic scaling helper.

    Keeps font-size and artist sizes constant
    0.5 : current font - 4pt
    2.0 : current font + 4pt

    XXX it's unclear why this works, but good to go for most cases
    """
    scale = float(scale)
    fig.set_size_inches(fig.get_size_inches() * scale)
    fig.set_dpi(fig.get_dpi() * scale)
    import matplotlib as mpl
    if scale >= 1:
        sfactor = scale ** 2
    else:
        sfactor = -((1. / scale) ** 2)
    for text in fig.findobj(mpl.text.Text):
        fs = text.get_fontsize()
        new_size = fs + sfactor
        if new_size <= 0:
            raise ValueError('could not rescale matplotlib fonts, consider '
                             'increasing "scale"')
        text.set_fontsize(new_size)

    fig.canvas.draw()

def _figs_to_mrislices(sl, n_jobs, **kwargs):
    import matplotlib.pyplot as plt
    plt.close('all')
    use_jobs = min(n_jobs, max(1, len(sl)))
    parallel, p_fun, _ = parallel_func(_plot_mri_contours, use_jobs)
    outs = parallel(p_fun(slices=s, **kwargs)
                    for s in np.array_split(sl, use_jobs))
    for o in outs[1:]:
        outs[0] += o
    return outs[0]

def _iterate_trans_views(function, **kwargs):
    """Auxiliary function to iterate over views in trans fig."""
    import matplotlib.pyplot as plt
    from mayavi import mlab, core
    fig = function(**kwargs)

    assert isinstance(fig, core.scene.Scene)

    views = [(90, 90), (0, 90), (0, -90)]
    fig2, axes = plt.subplots(1, len(views))
    for view, ax in zip(views, axes):
        mlab.view(view[0], view[1])
        if fig.scene is not None:
            im = mlab.screenshot(figure=fig)
        else:  # Testing mode
            im = np.zeros((2, 2, 3))
        ax.imshow(im)
        ax.axis('off')

    mlab.close(fig)
    img = _fig_to_img(fig2, image_format='png')
    return img

###############################################################################
# TOC FUNCTIONS

def _is_bad_fname(fname):
    """Identify bad file naming patterns and highlight them in the TOC."""
    if fname.endswith('(whitened)'):
        fname = fname[:-11]

    if not fname.endswith(tuple(VALID_EXTENSIONS + ['bem', 'custom'])):
        return 'red'
    else:
        return ''

def _get_fname(fname):
    """Get fname without -#-."""
    if '-#-' in fname:
        fname = fname.split('-#-')[0]
    else:
        fname = op.basename(fname)
    fname = ' ... %s' % fname
    return fname

def _get_toc_property(fname):
    """Assign class names to TOC elements to allow toggling with buttons."""
    if fname.endswith(('-eve.fif', '-eve.fif.gz')):
        div_klass = 'events'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('-ave.fif', '-ave.fif.gz')):
        div_klass = 'evoked'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('-cov.fif', '-cov.fif.gz')):
        div_klass = 'covariance'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('raw.fif', 'raw.fif.gz',
                         'sss.fif', 'sss.fif.gz')):
        div_klass = 'raw'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('-trans.fif', '-trans.fif.gz')):
        div_klass = 'trans'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('-fwd.fif', '-fwd.fif.gz')):
        div_klass = 'forward'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('-inv.fif', '-inv.fif.gz')):
        div_klass = 'inverse'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('-epo.fif', '-epo.fif.gz')):
        div_klass = 'epochs'
        tooltip = fname
        text = op.basename(fname)
    elif fname.endswith(('.nii', '.nii.gz', '.mgh', '.mgz')):
        div_klass = 'mri'
        tooltip = 'MRI'
        text = 'MRI'
    elif fname.endswith(('bem')):
        div_klass = 'mri'
        tooltip = 'MRI'
        text = 'MRI'
    elif fname.endswith('(whitened)'):
        div_klass = 'evoked'
        tooltip = fname
        text = op.basename(fname[:-11]) + '(whitened)'
    else:
        div_klass = fname.split('-#-')[1]
        tooltip = fname.split('-#-')[0]
        text = fname.split('-#-')[0]

    return div_klass, tooltip, text

def _iterate_files(report, fnames, info, cov, baseline, sfreq, on_error,
                   image_format):
    """Parallel process in batch mode."""
    htmls, report_fnames, report_sectionlabels = [], [], []

    def _update_html(html, report_fname, report_sectionlabel):
        """Update the lists above."""
        htmls.append(html)
        report_fnames.append(report_fname)
        report_sectionlabels.append(report_sectionlabel)

    for fname in fnames:
        logger.info("Rendering : %s"
                    % op.join('...' + report.data_path[-20:],
                              fname))
        try:
            if fname.endswith(('raw.fif', 'raw.fif.gz',
                               'sss.fif', 'sss.fif.gz')):
                html = report._render_raw(fname)
                report_fname = fname
                report_sectionlabel = 'raw'
            elif fname.endswith(('-fwd.fif', '-fwd.fif.gz')):
                html = report._render_forward(fname)
                report_fname = fname
                report_sectionlabel = 'forward'
            elif fname.endswith(('-inv.fif', '-inv.fif.gz')):
                html = report._render_inverse(fname)
                report_fname = fname
                report_sectionlabel = 'inverse'
            elif fname.endswith(('-ave.fif', '-ave.fif.gz')):
                if cov is not None:
                    html = report._render_whitened_evoked(fname, cov, baseline,
                                                          image_format)
                    report_fname = fname + ' (whitened)'
                    report_sectionlabel = 'evoked'
                    _update_html(html, report_fname, report_sectionlabel)

                html = report._render_evoked(fname, baseline, image_format)
                report_fname = fname
                report_sectionlabel = 'evoked'
            elif fname.endswith(('-eve.fif', '-eve.fif.gz')):
                html = report._render_eve(fname, sfreq, image_format)
                report_fname = fname
                report_sectionlabel = 'events'
            elif fname.endswith(('-epo.fif', '-epo.fif.gz')):
                html = report._render_epochs(fname, image_format)
                report_fname = fname
                report_sectionlabel = 'epochs'
            elif (fname.endswith(('-cov.fif', '-cov.fif.gz')) and
                  report.info_fname is not None):
                html = report._render_cov(fname, info, image_format)
                report_fname = fname
                report_sectionlabel = 'covariance'
            elif (fname.endswith(('-trans.fif', '-trans.fif.gz')) and
                  report.info_fname is not None and report.subjects_dir
                  is not None and report.subject is not None):
                html = report._render_trans(fname, report.data_path, info,
                                            report.subject,
                                            report.subjects_dir)
                report_fname = fname
                report_sectionlabel = 'trans'
            else:
                html = None
                report_fname = None
                report_sectionlabel = None
        except Exception as e:
            if on_error == 'warn':
                warn('Failed to process file %s:\n"%s"' % (fname, e))
            elif on_error == 'raise':
                raise
            html = None
            report_fname = None
            report_sectionlabel = None
        _update_html(html, report_fname, report_sectionlabel)

    return htmls, report_fnames, report_sectionlabels

def open_report(fname, **params):
    """Read a saved report or, if it doesn't exist yet, create a new one.

    The returned report can be used as a context manager, in which case any
    changes to the report are saved when exiting the context block.

    Parameters
    ----------
    fname : str
        The file containing the report, stored in the HDF5 format. If the file
        does not exist yet, a new report is created that will be saved to the
        specified file.
    **params : kwargs
        When creating a new report, any named parameters other than ``fname``
        are passed to the ``__init__`` function of the `Report` object. When
        reading an existing report, the parameters are checked with the
        loaded report and an exception is raised when they don't match.

    Returns
    -------
    report : instance of Report
        The report.
    """
    if op.exists(fname):
        # Check **params with the loaded report
        state = read_hdf5(fname, title='mnepython')
        for param in params.keys():
            if param not in state:
                raise ValueError('The loaded report has no attribute %s' %
                                 param)
            if params[param] != state[param]:
                raise ValueError("Attribute '%s' of loaded report does not "
                                 "match the given parameter." % param)
        report = Report()
        report.__setstate__(state)
    else:
        report = Report(**params)
    # Keep track of the filename in case the Report object is used as a context
    # manager.
    report._fname = fname
    return report

###############################################################################
# IMAGE FUNCTIONS

def _figure_agg(**kwargs):
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    fig = Figure(**kwargs)
    FigureCanvas(fig)
    return fig

def _build_image_png(data, cmap='gray'):
    """Build an image encoded in base64."""
    import matplotlib.pyplot as plt

    figsize = data.shape[::-1]
    if figsize[0] == 1:
        figsize = tuple(figsize[1:])
        data = data[:, :, 0]
    fig = _figure_agg(figsize=figsize, dpi=1.0, frameon=False)
    cmap = getattr(plt.cm, cmap, plt.cm.gray)
    fig.figimage(data, cmap=cmap)
    output = BytesIO()
    fig.savefig(output, dpi=fig.get_dpi(), format='png')
    return base64.b64encode(output.getvalue()).decode('ascii')

def _iterate_sagittal_slices(array, limits=None):
    """Iterate sagittal slices."""
    shape = array.shape[0]
    for ind in range(shape):
        if limits and ind not in limits:
            continue
        yield ind, array[ind, :, :]

def _iterate_axial_slices(array, limits=None):
    """Iterate axial slices."""
    shape = array.shape[1]
    for ind in range(shape):
        if limits and ind not in limits:
            continue
        yield ind, array[:, ind, :]

def _iterate_coronal_slices(array, limits=None):
    """Iterate coronal slices."""
    shape = array.shape[2]
    for ind in range(shape):
        if limits and ind not in limits:
            continue
        yield ind, np.flipud(np.rot90(array[:, :, ind]))

def _iterate_mri_slices(name, ind, global_id, slides_klass, data, cmap):
    """Auxiliary function for parallel processing of mri slices."""
    img_klass = 'slideimg-%s' % name

    caption = u'Slice %s %s' % (name, ind)
    slice_id = '%s-%s-%s' % (name, global_id, ind)
    div_klass = 'span12 %s' % slides_klass
    img = _build_image_png(data, cmap=cmap)
    first = True if ind == 0 else False
    html = _build_html_image(img, slice_id, div_klass, img_klass, caption,
                             first, image_format='png')
    return ind, html

###############################################################################
# HTML functions

def _build_html_image(img, id, div_klass, img_klass, caption=None,
                      show=True, image_format='png'):
    """Build a html image from a slice array."""
    html = []
    add_style = u'' if show else u'style="display: none"'
    html.append(u'
' % (div_klass, id, add_style))
    html.append(u'
')
    if image_format == 'png':
        html.append(u''
                    % (img_klass, img))
    else:
        html.append(u'
%s
'
                    % (img_klass, img))
    html.append(u'
')
    if caption:
        html.append(u'
%s
' % caption)
    html.append(u'
')
    return u'\n'.join(html)

slider_template = HTMLTemplate(u"""

""")

slider_full_template = Template(u"""

{{title}}

                    {{image_html}}

                {{html}}

""")

def _build_html_slider(slices_range, slides_klass, slider_id,
                       start_value=None):
    """Build an html slider for a given slices range and a slices klass."""
    if start_value is None:
        start_value = slices_range[len(slices_range) // 2]
    with warnings.catch_warnings(record=True):
        warnings.simplefilter('ignore')
        out = slider_template.substitute(
            slider_id=slider_id, klass=slides_klass,
            step=slices_range[1] - slices_range[0],
            minvalue=slices_range[0], maxvalue=slices_range[-1],
            startvalue=start_value)
    return out

###############################################################################
# HTML scan renderer

header_template = Template(u"""
<!DOCTYPE html>

{{include}}

                >

{{title}}

        {{for section in sections}}

    {{section if section != 'mri' else 'MRI'}}

        {{endfor}}

""")

footer_template = HTMLTemplate(u"""

        © Copyright 2012-{{current_year}}, MNE Developers.
      Created on {{date}}.
      Powered by MNE.

""")

html_template = Template(u"""

{{caption}}

{{html}}

""")

image_template = Template(u"""

{{default interactive = False}}
{{default width = 50}}
{{default id = False}}
{{default image_format = 'png'}}
{{default scale = None}}
{{default comment = None}}

{{if caption}}

{{caption}}

{{endif}}

{{if not interactive}}
    {{if image_format == 'png'}}
        {{if scale is not None}}

        {{else}}

        {{endif}}
    {{elif image_format == 'gif'}}
        {{if scale is not None}}

        {{else}}

        {{endif}}
    {{elif image_format == 'svg'}}

            {{img}}

    {{endif}}
    {{if comment is not None}}

                {{comment}}

    {{endif}}
{{else}}
    {{interactive}}
{{endif}}

""")

repr_template = Template(u"""

{{caption}}

{{repr}}

""")

raw_template = Template(u"""

{{caption}}

        Measurement date
        {{if meas_date is not None}}
        {{meas_date}}
        {{else}}Unknown	{{endif}}

        Experimenter
        {{if info['experimenter'] is not None}}
        {{info['experimenter']}}
        {{else}}Unknown	{{endif}}

        Digitized points
        {{if info['dig'] is not None}}
        {{len(info['dig'])}} points
        {{else}}
        Not available
        {{endif}}

        Good channels
        {{n_mag}} magnetometer, {{n_grad}} gradiometer,
            and {{n_eeg}} EEG channels

        Bad channels
        {{if info['bads'] is not None}}
        {{', '.join(info['bads'])}}
        {{else}}None	{{endif}}

        EOG channels
        {{eog}}

        ECG channels
        {{ecg}}

        Measurement time range
        {{u'%0.2f' % tmin}} to {{u'%0.2f' % tmax}} sec.

        Sampling frequency
        {{u'%0.2f' % info['sfreq']}} Hz

        Highpass
        {{u'%0.2f' % info['highpass']}} Hz

        Lowpass
        {{u'%0.2f' % info['lowpass']}} Hz

""")

toc_list = Template(u"""

    {{if id}}

    {{endif}}
 {{text}}
{{if id}}{{endif}}

""")

def _check_scale(scale):
    """Ensure valid scale value is passed."""
    if np.isscalar(scale) and scale <= 0:
        raise ValueError('scale must be positive, not %s' % scale)

def _check_image_format(rep, image_format):
    """Ensure fmt is valid."""
    if rep is None:
        _check_option('image_format', image_format, ['png', 'svg'])
    elif image_format is not None:
        _check_option('image_format', image_format, ['png', 'svg', None])
    else:  # rep is not None and image_format is None
        image_format = rep.image_format
    return image_format

@fill_doc
class Report(object):
    """Object for rendering HTML.

    Parameters
    ----------
    info_fname : str
        Name of the file containing the info dictionary.
    subjects_dir : str | None
        Path to the SUBJECTS_DIR. If None, the path is obtained by using
        the environment variable SUBJECTS_DIR.
    subject : str | None
        Subject name.
    title : str
        Title of the report.
    cov_fname : str
        Name of the file containing the noise covariance.
    baseline : None or tuple of length 2 (default (None, 0))
        The time interval to apply baseline correction for evokeds.
        If None do not apply it. If baseline is (a, b)
        the interval is between "a (s)" and "b (s)".
        If a is None the beginning of the data is used
        and if b is None then b is set to the end of the interval.
        If baseline is equal to (None, None) all the time
        interval is used.
        The baseline (a, b) includes both endpoints, i.e. all
        timepoints t such that a <= t <= b.
    image_format : str
        Default image format to use (default is 'png').
        SVG uses vector graphics, so fidelity is higher but can increase
        file size and browser image rendering time as well.

        .. versionadded:: 0.15

    raw_psd : bool | dict
        If True, include PSD plots for raw files. Can be False (default) to
        omit, True to plot, or a dict to pass as ``kwargs`` to
        :meth:`mne.io.Raw.plot_psd`.

        .. versionadded:: 0.17
    %(verbose)s

    Notes
    -----
    To toggle the show/hide state of all sections in the html report, press 't'

    .. versionadded:: 0.8.0
    """

    def __init__(self, info_fname=None, subjects_dir=None,
                 subject=None, title=None, cov_fname=None, baseline=None,
                 image_format='png', raw_psd=False, verbose=None):
        self.info_fname = info_fname
        self.cov_fname = cov_fname
        self.baseline = baseline
        self.subjects_dir = get_subjects_dir(subjects_dir, raise_error=False)
        self.subject = subject
        self.title = title
        self.image_format = _check_image_format(None, image_format)
        self.verbose = verbose

        self.initial_id = 0
        self.html = []
        self.fnames = []  # List of file names rendered
        self.sections = []  # List of sections
        self.lang = 'en-us'  # language setting for the HTML file
        self._sectionlabels = []  # Section labels
        self._sectionvars = {}  # Section variable names in js
        # boolean to specify if sections should be ordered in natural
        # order of processing (raw -> events ... -> inverse)
        self._sort_sections = False
        if not isinstance(raw_psd, bool) and not isinstance(raw_psd, dict):
            raise TypeError('raw_psd must be bool or dict, got %s'
                            % (type(raw_psd),))
        self.raw_psd = raw_psd
        self._init_render()  # Initialize the renderer

    def __repr__(self):
        """Print useful info about report."""
        s = '<Report | %d items' % len(self.fnames)
        if self.title is not None:
            s += ' | %s' % self.title
        fnames = [_get_fname(f) for f in self.fnames]
        if len(self.fnames) > 4:
            s += '\n%s' % '\n'.join(fnames[:2])
            s += '\n ...\n'
            s += '\n'.join(fnames[-2:])
        elif len(self.fnames) > 0:
            s += '\n%s' % '\n'.join(fnames)
        s += '\n>'
        return s

    def __len__(self):
        """Return the number of items in report."""
        return len(self.fnames)

    def _get_id(self):
        """Get id of plot."""
        self.initial_id += 1
        return self.initial_id

    def _validate_input(self, items, captions, section, comments=None):
        """Validate input."""
        if not isinstance(items, (list, tuple)):
            items = [items]
        if not isinstance(captions, (list, tuple)):
            captions = [captions]
        if not isinstance(comments, (list, tuple)):
            if comments is None:
                comments = [comments] * len(captions)
            else:
                comments = [comments]
        if len(comments) != len(items):
            raise ValueError('Comments and report items must have the same '
                             'length or comments should be None.')
        elif len(captions) != len(items):
            raise ValueError('Captions and report items must have the same '
                             'length.')

        # Book-keeping of section names
        if section not in self.sections:
            self.sections.append(section)
            self._sectionvars[section] = _clean_varnames(section)

        return items, captions, comments

    def remove(self, caption, section=None):
        """Remove a figure from the report.

        The figure to remove is searched for by its caption. When searching by
        caption, the section label can be specified as well to narrow down the
        search. If multiple figures match the search criteria, the last one
        will be removed.

        Any empty sections will be removed as well.

        Parameters
        ----------
        caption : str
            If set, search for the figure by caption.
        section : str | None
            If set, limit the search to the section with the given label.

        Returns
        -------
        removed_index : int | None
            The integer index of the figure that was removed, or ``None`` if no
            figure matched the search criteria.
        """
        # Construct the search pattern
        pattern = r'^%s-#-.*-#-custom$' % caption

        # Search for figures matching the search pattern, regardless of
        # section
        matches = [i for i, fname_ in enumerate(self.fnames)
                   if re.match(pattern, fname_)]
        if section is not None:
            # Narrow down the search to the given section
            svar = self._sectionvars[section]
            matches = [i for i in matches
                       if self._sectionlabels[i] == svar]
        if len(matches) == 0:
            return None

        # Remove last occurrence
        index = max(matches)

        # Remove the figure
        del self.fnames[index]
        del self._sectionlabels[index]
        del self.html[index]

        # Remove any (now) empty sections.
        # We use a list() to copy the _sectionvars dictionary, since we are
        # removing elements during the loop.
        for section_, sectionlabel_ in list(self._sectionvars.items()):
            if sectionlabel_ not in self._sectionlabels:
                self.sections.remove(section_)
                del self._sectionvars[section_]

        return index

    def _add_or_replace(self, fname, sectionlabel, html, replace=False):
        """Append a figure to the report, or replace it if it already exists.

        Parameters
        ----------
        fname : str
            A unique identifier for the figure. If a figure with this
            identifier has already been added, it will be replaced.
        sectionlabel : str
            The section to place the figure in.
        html : str
            The HTML that contains the figure.
        replace : bool
            Existing figures are only replaced if this is set to ``True``.
            Defaults to ``False``.
        """
        assert isinstance(html, str)  # otherwise later will break
        if replace and fname in self.fnames:
            # Find last occurrence of the figure
            ind = max([i for i, existing in enumerate(self.fnames)
                       if existing == fname])
            self.fnames[ind] = fname
            self._sectionlabels[ind] = sectionlabel
            self.html[ind] = html
        else:
            # Append new record
            self.fnames.append(fname)
            self._sectionlabels.append(sectionlabel)
            self.html.append(html)

    def add_figs_to_section(self, figs, captions, section='custom',
                            scale=None, image_format=None, comments=None,
                            replace=False):
        """Append custom user-defined figures.

        Parameters
        ----------
        figs : matplotlib.figure.Figure | mlab.Figure | array | list
            A figure or a list of figures to add to the report. Each figure in
            the list can be an instance of :class:`matplotlib.figure.Figure`,
            :class:`mayavi.core.api.Scene`, or :class:`numpy.ndarray`.
        captions : str | list of str
            A caption or a list of captions to the figures.
        section : str
            Name of the section to place the figure in. If section already
            exists, the figures will be appended to the end of the section.
        scale : float | None | callable
            Scale the images maintaining the aspect ratio.
            If None, no scaling is applied. If float, scale will determine
            the relative scaling (might not work for scale <= 1 depending on
            font sizes). If function, should take a figure object as input
            parameter. Defaults to None.
        image_format : str | None
            The image format to be used for the report, can be 'png' or 'svd'.
            None (default) will use the default specified during Report
            class construction.
        comments : None | str | list of str
            A string of text or a list of strings of text to be appended after
            the figure.
        replace : bool
            If ``True``, figures already present that have the same caption
            will be replaced. Defaults to ``False``.
        """
        figs, captions, comments = self._validate_input(figs, captions,
                                                        section, comments)
        image_format = _check_image_format(self, image_format)
        _check_scale(scale)
        for fig, caption, comment in zip(figs, captions, comments):
            caption = 'custom plot' if caption == '' else caption
            sectionvar = self._sectionvars[section]
            global_id = self._get_id()
            div_klass = self._sectionvars[section]
            img_klass = self._sectionvars[section]

            img = _fig_to_img(fig, image_format, scale)
            html = image_template.substitute(img=img, id=global_id,
                                             div_klass=div_klass,
                                             img_klass=img_klass,
                                             caption=caption,
                                             show=True,
                                             image_format=image_format,
                                             comment=comment)
            self._add_or_replace('%s-#-%s-#-custom' % (caption, sectionvar),
                                 sectionvar, html, replace)

    def add_images_to_section(self, fnames, captions, scale=None,
                              section='custom', comments=None, replace=False):
        """Append custom user-defined images.

        Parameters
        ----------
        fnames : str | list of str
            A filename or a list of filenames from which images are read.
            Images can be PNG, GIF or SVG.
        captions : str | list of str
            A caption or a list of captions to the images.
        scale : float | None
            Scale the images maintaining the aspect ratio.
            Defaults to None. If None, no scaling will be applied.
        section : str
            Name of the section. If section already exists, the images
            will be appended to the end of the section.
        comments : None | str | list of str
            A string of text or a list of strings of text to be appended after
            the image.
        replace : bool
            If ``True``, figures already present that have the same caption
            will be replaced. Defaults to ``False``.

        """
        # Note: using scipy.misc is equivalent because scipy internally
        # imports PIL anyway. It's not possible to redirect image output
        # to binary string using scipy.misc.
        fnames, captions, comments = self._validate_input(fnames, captions,
                                                          section, comments)
        _check_scale(scale)

        for fname, caption, comment in zip(fnames, captions, comments):
            caption = 'custom plot' if caption == '' else caption
            sectionvar = self._sectionvars[section]
            global_id = self._get_id()
            div_klass = self._sectionvars[section]
            img_klass = self._sectionvars[section]

            image_format = os.path.splitext(fname)[1][1:]
            image_format = image_format.lower()

            _check_option('image_format', image_format, ['png', 'gif', 'svg'])

            # Convert image to binary string.
            with open(fname, 'rb') as f:
                img = base64.b64encode(f.read()).decode('ascii')
            html = image_template.substitute(img=img, id=global_id,
                                             image_format=image_format,
                                             div_klass=div_klass,
                                             img_klass=img_klass,
                                             caption=caption,
                                             width=scale,
                                             comment=comment,
                                             show=True)

            self._add_or_replace('%s-#-%s-#-custom' % (caption, sectionvar),
                                 sectionvar, html, replace)

    def add_htmls_to_section(self, htmls, captions, section='custom',
                             replace=False):
        """Append htmls to the report.

        Parameters
        ----------
        htmls : str | list of str
            An html str or a list of html str.
        captions : str | list of str
            A caption or a list of captions to the htmls.
        section : str
            Name of the section. If section already exists, the images
            will be appended to the end of the section.
        replace : bool
            If ``True``, figures already present that have the same caption
            will be replaced. Defaults to ``False``.

        Notes
        -----
        .. versionadded:: 0.9.0
        """
        htmls, captions, _ = self._validate_input(htmls, captions, section)
        for html, caption in zip(htmls, captions):
            caption = 'custom plot' if caption == '' else caption
            sectionvar = self._sectionvars[section]
            global_id = self._get_id()
            div_klass = self._sectionvars[section]

            self._add_or_replace(
                '%s-#-%s-#-custom' % (caption, sectionvar), sectionvar,
                html_template.substitute(div_klass=div_klass, id=global_id,
                                         caption=caption, html=html), replace)

    def add_bem_to_section(self, subject, caption='BEM', section='bem',
                           decim=2, n_jobs=1, subjects_dir=None,
                           replace=False):
        """Render a bem slider html str.

        Parameters
        ----------
        subject : str
            Subject name.
        caption : str
            A caption for the bem.
        section : str
            Name of the section. If section already exists, the bem
            will be appended to the end of the section.
        decim : int
            Use this decimation factor for generating MRI/BEM images
            (since it can be time consuming).
        n_jobs : int
          Number of jobs to run in parallel.
        subjects_dir : str | None
            Path to the SUBJECTS_DIR. If None, the path is obtained by using
            the environment variable SUBJECTS_DIR.
        replace : bool
            If ``True``, figures already present that have the same caption
            will be replaced. Defaults to ``False``.

        Notes
        -----
        .. versionadded:: 0.9.0
        """
        caption = 'custom plot' if caption == '' else caption
        html = self._render_bem(subject=subject, subjects_dir=subjects_dir,
                                decim=decim, n_jobs=n_jobs, section=section,
                                caption=caption)
        html, caption, _ = self._validate_input(html, caption, section)
        sectionvar = self._sectionvars[section]
        # convert list->str
        assert isinstance(html, list)
        html = u''.join(html)
        self._add_or_replace('%s-#-%s-#-custom' % (caption[0], sectionvar),
                             sectionvar, html)

    def add_slider_to_section(self, figs, captions=None, section='custom',
                              title='Slider', scale=None, image_format=None,
                              replace=False):
        """Render a slider of figs to the report.

        Parameters
        ----------
        figs : list of figures.
            Each figure in the list can be an instance of
            :class:`matplotlib.figure.Figure`,
            :class:`mayavi.core.api.Scene`, or :class:`numpy.ndarray`.
            Must have at least 2 elements.
        captions : list of str | list of float | None
            A list of captions to the figures. If float, a str will be
            constructed as `%f s`. If None, it will default to
            `Data slice %d`.
        section : str
            Name of the section. If section already exists, the figures
            will be appended to the end of the section.
        title : str
            The title of the slider.
        scale : float | None | callable
            Scale the images maintaining the aspect ratio.
            If None, no scaling is applied. If float, scale will determine
            the relative scaling (might not work for scale <= 1 depending on
            font sizes). If function, should take a figure object as input
            parameter. Defaults to None.
        image_format : str | None
            The image format to be used for the report, can be 'png' or 'svd'.
            None (default) will use the default specified during Report
            class construction.
        replace : bool
            If ``True``, figures already present that have the same caption
            will be replaced. Defaults to ``False``.

        Notes
        -----
        .. versionadded:: 0.10.0
        """
        _check_scale(scale)
        image_format = _check_image_format(self, image_format)
        if isinstance(figs[0], list):
            raise NotImplementedError('`add_slider_to_section` '
                                      'can only add one slider at a time.')
        if len(figs) < 2:
            raise ValueError('figs must be at least length 2, got %s'
                             % (len(figs),))
        figs = [figs]
        figs, _, _ = self._validate_input(figs, section, section)
        figs = figs[0]

        sectionvar = self._sectionvars[section]
        global_id = self._get_id()
        name = 'slider'

        html = []
        slides_klass = '%s-%s' % (name, global_id)
        div_klass = 'span12 %s' % slides_klass

        sl = np.arange(0, len(figs))
        slices = []
        img_klass = 'slideimg-%s' % name

        if captions is None:
            captions = ['Data slice %d' % ii for ii in sl]
        elif isinstance(captions, (list, tuple, np.ndarray)):
            if len(figs) != len(captions):
                raise ValueError('Captions must be the same length as the '
                                 'number of slides.')
            if isinstance(captions[0], (float, int)):
                captions = ['%0.3f s' % caption for caption in captions]
        else:
            raise TypeError('Captions must be None or an iterable of '
                            'float, int, str, Got %s' % type(captions))
        for ii, (fig, caption) in enumerate(zip(figs, captions)):
            img = _fig_to_img(fig, image_format, scale)
            slice_id = '%s-%s-%s' % (name, global_id, sl[ii])
            first = True if ii == 0 else False
            slices.append(_build_html_image(img, slice_id, div_klass,
                                            img_klass, caption, first,
                                            image_format=image_format))
        # Render the slider
        slider_id = 'select-%s-%s' % (name, global_id)
        # Render the slices
        image_html = u'\n'.join(slices)
        html.append(_build_html_slider(sl, slides_klass, slider_id,
                                       start_value=0))
        html = '\n'.join(html)

        slider_klass = sectionvar

        self._add_or_replace(
            '%s-#-%s-#-custom' % (title, sectionvar), sectionvar,
            slider_full_template.substitute(id=global_id, title=title,
                                            div_klass=slider_klass,
                                            slider_id=slider_id, html=html,
                                            image_html=image_html))

    ###########################################################################
    # HTML rendering
    def _render_one_axis(self, slices_iter, name, global_id, cmap,
                         n_elements, n_jobs):
        """Render one axis of the array."""
        global_id = global_id or name
        html = []
        html.append(u'
')
        slides_klass = '%s-%s' % (name, global_id)

        use_jobs = min(n_jobs, max(1, n_elements))
        parallel, p_fun, _ = parallel_func(_iterate_mri_slices, use_jobs)
        r = parallel(p_fun(name, ind, global_id, slides_klass, data, cmap)
                     for ind, data in slices_iter)
        slices_range, slices = zip(*r)

        # Render the slider
        slider_id = 'select-%s-%s' % (name, global_id)
        html.append(u'

' % slider_id)
        html.append(u'
')
        # Render the slices
        html.append(u'\n'.join(slices))
        html.append(u'
')
        html.append(_build_html_slider(slices_range, slides_klass, slider_id))
        html.append(u'
')
        return '\n'.join(html)

    ###########################################################################
    # global rendering functions
    @verbose
    def _init_render(self, verbose=None):
        """Initialize the renderer."""
        inc_fnames = ['jquery-1.10.2.min.js', 'jquery-ui.min.js',
                      'bootstrap.min.js', 'jquery-ui.min.css',
                      'bootstrap.min.css']

        include = list()
        for inc_fname in inc_fnames:
            logger.info('Embedding : %s' % inc_fname)
            fname = op.join(op.dirname(__file__), 'html', inc_fname)
            with open(fname, 'rb') as fid:
                file_content = fid.read().decode('utf-8')
            if inc_fname.endswith('.js'):
                include.append(u'')
            elif inc_fname.endswith('.css'):
                include.append(u'')

        self.include = ''.join(include)

    @verbose
    def parse_folder(self, data_path, pattern='*.fif', n_jobs=1, mri_decim=2,
                     sort_sections=True, on_error='warn', image_format=None,
                     render_bem=True, verbose=None):
        r"""Render all the files in the folder.

        Parameters
        ----------
        data_path : str
            Path to the folder containing data whose HTML report will be
            created.
        pattern : str | list of str
            Filename pattern(s) to include in the report.
            Example: [\*raw.fif, \*ave.fif] will include Raw as well as Evoked
            files.
        n_jobs : int
          Number of jobs to run in parallel.
        mri_decim : int
            Use this decimation factor for generating MRI/BEM images
            (since it can be time consuming).
        sort_sections : bool
            If True, sort sections in the order: raw -> events -> epochs
             -> evoked -> covariance -> trans -> mri -> forward -> inverse.
        on_error : str
            What to do if a file cannot be rendered. Can be 'ignore',
            'warn' (default), or 'raise'.
        image_format : str | None
            The image format to be used for the report, can be 'png' or 'svd'.
            None (default) will use the default specified during Report
            class construction.

            .. versionadded:: 0.15
        render_bem : bool
            If True (default), try to render the BEM.

            .. versionadded:: 0.16
        %(verbose_meth)s
        """
        image_format = _check_image_format(self, image_format)
        _check_option('on_error', on_error, ['ignore', 'warn', 'raise'])
        self._sort = sort_sections

        n_jobs = check_n_jobs(n_jobs)
        self.data_path = data_path

        if self.title is None:
            self.title = 'MNE Report for ...%s' % self.data_path[-20:]

        if not isinstance(pattern, (list, tuple)):
            pattern = [pattern]

        # iterate through the possible patterns
        fnames = list()
        for p in pattern:
            fnames.extend(sorted(_recursive_search(self.data_path, p)))

        if self.info_fname is not None:
            info = read_info(self.info_fname, verbose=False)
            sfreq = info['sfreq']
        else:
            # only warn if relevant
            if any(fname.endswith(('-cov.fif', '-cov.fif.gz'))
                   for fname in fnames):
                warn('`info_fname` not provided. Cannot render '
                     '-cov.fif(.gz) files.')
            if any(fname.endswith(('-trans.fif', '-trans.fif.gz'))
                   for fname in fnames):
                warn('`info_fname` not provided. Cannot render '
                     '-trans.fif(.gz) files.')
            info, sfreq = None, None

        cov = None
        if self.cov_fname is not None:
            cov = read_cov(self.cov_fname)
        baseline = self.baseline

        # render plots in parallel; check that n_jobs <= # of files
        logger.info('Iterating over %s potential files (this may take some '
                    'time)' % len(fnames))
        use_jobs = min(n_jobs, max(1, len(fnames)))
        parallel, p_fun, _ = parallel_func(_iterate_files, use_jobs)
        r = parallel(p_fun(self, fname, info, cov, baseline, sfreq, on_error,
                           image_format)
                     for fname in np.array_split(fnames, use_jobs))
        htmls, report_fnames, report_sectionlabels = zip(*r)

        # combine results from n_jobs discarding plots not rendered
        self.html = [html for html in sum(htmls, []) if html is not None]
        self.fnames = [fname for fname in sum(report_fnames, []) if
                       fname is not None]
        self._sectionlabels = [slabel for slabel in
                               sum(report_sectionlabels, [])
                               if slabel is not None]

        # find unique section labels
        self.sections = sorted(set(self._sectionlabels))
        self._sectionvars = dict(zip(self.sections, self.sections))

        # render mri
        if render_bem:
            if self.subjects_dir is not None and self.subject is not None:
                logger.info('Rendering BEM')
                self.html.append(self._render_bem(
                    self.subject, self.subjects_dir, mri_decim, n_jobs))
                self.fnames.append('bem')
                self._sectionlabels.append('mri')
            else:
                warn('`subjects_dir` and `subject` not provided. Cannot '
                     'render MRI and -trans.fif(.gz) files.')

    def _get_state_params(self):
        """Obtain all fields that are in the state dictionary of this object.

        Returns
        -------
        non_opt_params : list of str
            All parameters that must be present in the state dictionary.
        opt_params : list of str
            All parameters that are optionally present in the state dictionary.
        """
        # Note: self._fname is not part of the state
        return (['baseline', 'cov_fname', 'fnames', 'html', 'include',
                 'image_format', 'info_fname', 'initial_id', 'raw_psd',
                 '_sectionlabels', 'sections', '_sectionvars',
                 '_sort_sections', 'subjects_dir', 'subject', 'title',
                 'verbose'],
                ['data_path', 'lang', '_sort'])

    def __getstate__(self):
        """Get the state of the report as a dictionary."""
        state = dict()
        non_opt_params, opt_params = self._get_state_params()
        for param in non_opt_params:
            state[param] = getattr(self, param)
        for param in opt_params:
            if hasattr(self, param):
                state[param] = getattr(self, param)
        return state

    def __setstate__(self, state):
        """Set the state of the report."""
        non_opt_params, opt_params = self._get_state_params()
        for param in non_opt_params:
            setattr(self, param, state[param])
        for param in opt_params:
            if param in state:
                setattr(self, param, state[param])
        return state

    def save(self, fname=None, open_browser=True, overwrite=False):
        """Save the report and optionally open it in browser.

        Parameters
        ----------
        fname : str | None
            File name of the report. If the file name ends in '.h5' or '.hdf5',
            the report is saved in HDF5 format, so it can later be loaded again
            with :func:`open_report`. If the file name ends in anything else,
            the report is rendered to HTML. If ``None``, the report is saved to
            'report.html' in the current working directory.
            Defaults to ``None``.
        open_browser : bool
            When saving to HTML, open the rendered HTML file browser after
            saving if True. Defaults to True.
        overwrite : bool
            If True, overwrite report if it already exists. Defaults to False.

        Returns
        -------
        fname : str
            The file name to which the report was saved.
        """
        if fname is None:
            if not hasattr(self, 'data_path'):
                self.data_path = op.dirname(__file__)
                warn('`data_path` not provided. Using %s instead'
                     % self.data_path)
            fname = op.realpath(op.join(self.data_path, 'report.html'))
        else:
            fname = op.realpath(fname)

        if not overwrite and op.isfile(fname):
            msg = ('Report already exists at location %s. '
                   'Overwrite it (y/[n])? '
                   % fname)
            answer = input(msg)
            if answer.lower() == 'y':
                overwrite = True

        _, ext = op.splitext(fname)
        is_hdf5 = ext.lower() in ['.h5', '.hdf5']

        if overwrite or not op.isfile(fname):
            logger.info('Saving report to location %s' % fname)

            if is_hdf5:
                write_hdf5(fname, self.__getstate__(), overwrite=overwrite,
                           title='mnepython')
            else:
                self._render_toc()

                # Annotate the HTML with a TOC and footer.
                with warnings.catch_warnings(record=True):
                    warnings.simplefilter('ignore')
                    html = footer_template.substitute(
                        date=time.strftime("%B %d, %Y"),
                        current_year=time.strftime("%Y"))
                self.html.append(html)

                # Writing to disk may fail. However, we need to make sure that
                # the TOC and footer are removed regardless, otherwise they
                # will be duplicated when the user attempts to save again.
                try:
                    # Write HTML
                    with codecs.open(fname, 'w', 'utf-8') as fobj:
                        fobj.write(_fix_global_ids(u''.join(self.html)))
                finally:
                    self.html.pop(0)
                    self.html.pop(0)
                    self.html.pop()

        if open_browser and not is_hdf5:
            import webbrowser
            webbrowser.open_new_tab('file://' + fname)

        self.fname = fname
        return fname

    def __enter__(self):
        """Do nothing when entering the context block."""
        return self

    def __exit__(self, type, value, traceback):
        """Save the report when leaving the context block."""
        if self._fname is not None:
            self.save(self._fname, open_browser=False, overwrite=True)
        return self

    @verbose
    def _render_toc(self, verbose=None):
        """Render the Table of Contents."""
        logger.info('Rendering : Table of Contents')

        html_toc = u'
'
        html_toc += u'

CONTENTS
'

        global_id = 1

        # Reorder self.sections to reflect natural ordering
        if self._sort_sections:
            sections = list(set(self.sections) & set(SECTION_ORDER))
            custom = [section for section in self.sections if section
                      not in SECTION_ORDER]
            order = [sections.index(section) for section in SECTION_ORDER if
                     section in sections]
            self.sections = np.array(sections)[order].tolist() + custom

        # Sort by section
        html, fnames, sectionlabels = [], [], []
        for section in self.sections:
            logger.info('%s' % section)
            for sectionlabel, this_html, fname in (zip(self._sectionlabels,
                                                   self.html, self.fnames)):
                if self._sectionvars[section] == sectionlabel:
                    html.append(this_html)
                    fnames.append(fname)
                    sectionlabels.append(sectionlabel)
                    logger.info(_get_fname(fname))
                    color = _is_bad_fname(fname)
                    div_klass, tooltip, text = _get_toc_property(fname)

                    # loop through conditions for evoked
                    if fname.endswith(('-ave.fif', '-ave.fif.gz',
                                      '(whitened)')):
                        text = os.path.basename(fname)
                        if fname.endswith('(whitened)'):
                            fname = fname[:-11]
                        # XXX: remove redundant read_evokeds
                        evokeds = read_evokeds(fname, verbose=False)

                        html_toc += toc_list.substitute(
                            div_klass=div_klass, id=None, tooltip=fname,
                            color='#428bca', text=text)

                        html_toc += u'

'
                        for ev in evokeds:
                            html_toc += toc_list.substitute(
                                div_klass=div_klass, id=global_id,
                                tooltip=fname, color=color, text=ev.comment)
                            global_id += 1
                        html_toc += u'

'

                    elif fname.endswith(tuple(VALID_EXTENSIONS +
                                        ['bem', 'custom'])):
                        html_toc += toc_list.substitute(div_klass=div_klass,
                                                        id=global_id,
                                                        tooltip=tooltip,
                                                        color=color,
                                                        text=text)
                        global_id += 1

        html_toc += u'\n

'
        html_toc += u'
'

        # The sorted html (according to section)
        self.html = html
        self.fnames = fnames
        self._sectionlabels = sectionlabels

        lang = getattr(self, 'lang', 'en-us')
        html_header = header_template.substitute(
            title=self.title, include=self.include, lang=lang,
            sections=self.sections, sectionvars=self._sectionvars)
        self.html.insert(0, html_header)  # Insert header at position 0
        self.html.insert(1, html_toc)  # insert TOC

    def _render_array(self, array, global_id=None, cmap='gray',
                      limits=None, n_jobs=1):
        """Render mri without bem contours (only PNG)."""
        html = []
        html.append(u'
')
        # Axial
        limits = limits or {}
        axial_limit = limits.get('axial')
        axial_slices_gen = _iterate_axial_slices(array, axial_limit)
        html.append(
            self._render_one_axis(axial_slices_gen, 'axial',
                                  global_id, cmap, array.shape[1], n_jobs))
        # Sagittal
        sagittal_limit = limits.get('sagittal')
        sagittal_slices_gen = _iterate_sagittal_slices(array, sagittal_limit)
        html.append(
            self._render_one_axis(sagittal_slices_gen, 'sagittal',
                                  global_id, cmap, array.shape[1], n_jobs))
        # Coronal
        coronal_limit = limits.get('coronal')
        coronal_slices_gen = _iterate_coronal_slices(array, coronal_limit)
        html.append(
            self._render_one_axis(coronal_slices_gen, 'coronal',
                                  global_id, cmap, array.shape[1], n_jobs))
        # Close section
        html.append(u'
')
        return '\n'.join(html)

    def _render_one_bem_axis(self, mri_fname, surf_fnames, global_id,
                             shape, orientation='coronal', decim=2, n_jobs=1):
        """Render one axis of bem contours (only PNG)."""
        orientation_name2axis = dict(sagittal=0, axial=1, coronal=2)
        orientation_axis = orientation_name2axis[orientation]
        n_slices = shape[orientation_axis]
        orig_size = np.roll(shape, orientation_axis)[[1, 2]]

        name = orientation
        html = []
        html.append(u'
')
        slides_klass = '%s-%s' % (name, global_id)

        sl = np.arange(0, n_slices, decim)
        kwargs = dict(mri_fname=mri_fname, surf_fnames=surf_fnames, show=False,
                      orientation=orientation, img_output=orig_size)
        imgs = _figs_to_mrislices(sl, n_jobs, **kwargs)
        slices = []
        img_klass = 'slideimg-%s' % name
        div_klass = 'span12 %s' % slides_klass
        for ii, img in enumerate(imgs):
            slice_id = '%s-%s-%s' % (name, global_id, sl[ii])
            caption = u'Slice %s %s' % (name, sl[ii])
            first = True if ii == 0 else False
            slices.append(_build_html_image(img, slice_id, div_klass,
                                            img_klass, caption, first,
                                            image_format='png'))

        # Render the slider
        slider_id = 'select-%s-%s' % (name, global_id)
        html.append(u'

' % slider_id)
        html.append(u'
')
        # Render the slices
        html.append(u'\n'.join(slices))
        html.append(u'
')
        html.append(_build_html_slider(sl, slides_klass, slider_id))
        html.append(u'
')
        return '\n'.join(html)

    def _render_image_png(self, image, cmap='gray', n_jobs=1):
        """Render one slice of mri without bem as a PNG."""
        import nibabel as nib

        global_id = self._get_id()

        if 'mri' not in self.sections:
            self.sections.append('mri')
            self._sectionvars['mri'] = 'mri'

        nim = nib.load(image)
        data = nim.get_data()
        shape = data.shape
        limits = {'sagittal': range(0, shape[0], 2),
                  'axial': range(0, shape[1], 2),
                  'coronal': range(0, shape[2], 2)}
        name = op.basename(image)
        html = u'
\n' % global_id
        html += u'
%s
\n' % name
        html += self._render_array(data, global_id=global_id,
                                   cmap=cmap, limits=limits, n_jobs=n_jobs)
        html += u'
\n'
        return html

    def _render_raw(self, raw_fname):
        """Render raw (only text)."""
        import matplotlib.pyplot as plt
        global_id = self._get_id()

        raw = read_raw_fif(raw_fname, allow_maxshield='yes')
        extra = ' (MaxShield on)' if raw.info.get('maxshield', False) else ''
        caption = u'Raw : %s%s' % (raw_fname, extra)

        n_eeg = len(pick_types(raw.info, meg=False, eeg=True))
        n_grad = len(pick_types(raw.info, meg='grad'))
        n_mag = len(pick_types(raw.info, meg='mag'))
        pick_eog = pick_types(raw.info, meg=False, eog=True)
        if len(pick_eog) > 0:
            eog = ', '.join(np.array(raw.info['ch_names'])[pick_eog])
        else:
            eog = 'Not available'
        pick_ecg = pick_types(raw.info, meg=False, ecg=True)
        if len(pick_ecg) > 0:
            ecg = ', '.join(np.array(raw.info['ch_names'])[pick_ecg])
        else:
            ecg = 'Not available'
        meas_date = raw.info['meas_date']
        if meas_date is not None:
            meas_date = _stamp_to_dt(meas_date).strftime("%B %d, %Y") + ' GMT'

        html = raw_template.substitute(
            div_klass='raw', id=global_id, caption=caption, info=raw.info,
            meas_date=meas_date, n_eeg=n_eeg, n_grad=n_grad, n_mag=n_mag,
            eog=eog, ecg=ecg, tmin=raw._first_time, tmax=raw._last_time)

        raw_psd = {} if self.raw_psd is True else self.raw_psd
        if isinstance(raw_psd, dict):
            from matplotlib.backends.backend_agg import FigureCanvasAgg
            n_ax = sum(kind in raw for kind in _data_types)
            fig, axes = plt.subplots(n_ax, 1, figsize=(6, 1 + 1.5 * n_ax),
                                     dpi=92)
            FigureCanvasAgg(fig)
            img = _fig_to_img(raw.plot_psd, self.image_format,
                              ax=axes, **raw_psd)
            new_html = image_template.substitute(
                img=img, div_klass='raw', img_klass='raw',
                caption='PSD', show=True, image_format=self.image_format)
            html += '\n\n' + new_html
        return html

    def _render_forward(self, fwd_fname):
        """Render forward."""
        div_klass = 'forward'
        caption = u'Forward: %s' % fwd_fname
        fwd = read_forward_solution(fwd_fname)
        repr_fwd = re.sub('>', '', re.sub('<', '', repr(fwd)))
        global_id = self._get_id()
        html = repr_template.substitute(div_klass=div_klass,
                                        id=global_id,
                                        caption=caption,
                                        repr=repr_fwd)
        return html

    def _render_inverse(self, inv_fname):
        """Render inverse."""
        div_klass = 'inverse'
        caption = u'Inverse: %s' % inv_fname
        inv = read_inverse_operator(inv_fname)
        repr_inv = re.sub('>', '', re.sub('<', '', repr(inv)))
        global_id = self._get_id()
        html = repr_template.substitute(div_klass=div_klass,
                                        id=global_id,
                                        caption=caption,
                                        repr=repr_inv)
        return html

    def _render_evoked(self, evoked_fname, baseline, image_format):
        """Render evoked."""
        logger.debug('Evoked: Reading %s' % evoked_fname)
        evokeds = read_evokeds(evoked_fname, baseline=baseline, verbose=False)

        html = []
        for ei, ev in enumerate(evokeds):
            global_id = self._get_id()
            kwargs = dict(show=False)
            logger.debug('Evoked: Plotting instance %s/%s'
                         % (ei + 1, len(evokeds)))
            img = _fig_to_img(ev.plot, image_format, **kwargs)
            caption = u'Evoked : %s (%s)' % (evoked_fname, ev.comment)
            html.append(image_template.substitute(
                img=img, id=global_id, div_klass='evoked',
                img_klass='evoked', caption=caption, show=True,
                image_format=image_format))
            has_types = []
            if len(pick_types(ev.info, meg=False, eeg=True)) > 0:
                has_types.append('eeg')
            if len(pick_types(ev.info, meg='grad', eeg=False)) > 0:
                has_types.append('grad')
            if len(pick_types(ev.info, meg='mag', eeg=False)) > 0:
                has_types.append('mag')
            for ch_type in has_types:
                logger.debug('    Topomap type %s' % ch_type)
                img = _fig_to_img(ev.plot_topomap, image_format,
                                  ch_type=ch_type, **kwargs)
                caption = u'Topomap (ch_type = %s)' % ch_type
                html.append(image_template.substitute(
                    img=img, div_klass='evoked', img_klass='evoked',
                    caption=caption, show=True, image_format=image_format))
        logger.debug('Evoked: done')
        return '\n'.join(html)

    def _render_eve(self, eve_fname, sfreq, image_format):
        """Render events."""
        global_id = self._get_id()
        events = read_events(eve_fname)
        kwargs = dict(events=events, sfreq=sfreq, show=False)
        img = _fig_to_img(plot_events, image_format, **kwargs)
        caption = 'Events : ' + eve_fname
        html = image_template.substitute(
            img=img, id=global_id, div_klass='events', img_klass='events',
            caption=caption, show=True, image_format=image_format)
        return html

    def _render_epochs(self, epo_fname, image_format):
        """Render epochs."""
        global_id = self._get_id()
        epochs = read_epochs(epo_fname)
        kwargs = dict(subject=self.subject, show=False)
        img = _fig_to_img(epochs.plot_drop_log, image_format, **kwargs)
        caption = 'Epochs : ' + epo_fname
        show = True
        html = image_template.substitute(
            img=img, id=global_id, div_klass='epochs', img_klass='epochs',
            caption=caption, show=show, image_format=image_format)
        return html

    def _render_cov(self, cov_fname, info_fname, image_format):
        """Render cov."""
        global_id = self._get_id()
        cov = read_cov(cov_fname)
        fig, _ = plot_cov(cov, info_fname, show=False)
        img = _fig_to_img(fig, image_format)
        caption = 'Covariance : %s (n_samples: %s)' % (cov_fname, cov.nfree)
        show = True
        html = image_template.substitute(
            img=img, id=global_id, div_klass='covariance',
            img_klass='covariance', caption=caption, show=show,
            image_format=image_format)
        return html

    def _render_whitened_evoked(self, evoked_fname, noise_cov, baseline,
                                image_format):
        """Render whitened evoked."""
        evokeds = read_evokeds(evoked_fname, verbose=False)
        html = []
        for ev in evokeds:
            ev = read_evokeds(evoked_fname, ev.comment, baseline=baseline,
                              verbose=False)
            global_id = self._get_id()
            kwargs = dict(noise_cov=noise_cov, show=False)
            img = _fig_to_img(ev.plot_white, image_format, **kwargs)

            caption = u'Whitened evoked : %s (%s)' % (evoked_fname, ev.comment)
            show = True
            html.append(image_template.substitute(
                img=img, id=global_id, div_klass='evoked',
                img_klass='evoked', caption=caption, show=show,
                image_format=image_format))
        return '\n'.join(html)

    def _render_trans(self, trans, path, info, subject, subjects_dir):
        """Render trans (only PNG)."""
        kwargs = dict(info=info, trans=trans, subject=subject,
                      subjects_dir=subjects_dir)
        try:
            img = _iterate_trans_views(function=plot_alignment, **kwargs)
        except IOError:
            img = _iterate_trans_views(function=plot_alignment,
                                       surfaces=['head'], **kwargs)

        if img is not None:
            global_id = self._get_id()
            html = image_template.substitute(
                img=img, id=global_id, div_klass='trans',
                img_klass='trans', caption='Trans : ' + trans, width=75,
                show=True, image_format='png')
            return html

    def _render_bem(self, subject, subjects_dir, decim, n_jobs,
                    section='mri', caption='BEM'):
        """Render mri+bem (only PNG)."""
        import nibabel as nib

        subjects_dir = get_subjects_dir(subjects_dir, raise_error=True)

        # Get the MRI filename
        mri_fname = op.join(subjects_dir, subject, 'mri', 'T1.mgz')
        if not op.isfile(mri_fname):
            warn('MRI file "%s" does not exist' % mri_fname)

        # Get the BEM surface filenames
        bem_path = op.join(subjects_dir, subject, 'bem')

        if not op.isdir(bem_path):
            warn('Subject bem directory "%s" does not exist' % bem_path)
            return self._render_image_png(mri_fname, cmap='gray',
                                          n_jobs=n_jobs)

        surf_fnames = []
        for surf_name in ['*inner_skull', '*outer_skull', '*outer_skin']:
            surf_fname = glob(op.join(bem_path, surf_name + '.surf'))
            if len(surf_fname) > 0:
                surf_fnames.append(surf_fname[0])
            else:
                warn('No surface found for %s.' % surf_name)
                continue
        if len(surf_fnames) == 0:
            warn('No surfaces found at all, rendering empty MRI')
            return self._render_image_png(mri_fname, cmap='gray',
                                          n_jobs=n_jobs)
        # XXX : find a better way to get max range of slices
        nim = nib.load(mri_fname)
        data = nim.get_data()
        shape = data.shape
        del data  # free up memory

        html = []

        global_id = self._get_id()

        if section == 'mri' and 'mri' not in self.sections:
            self.sections.append('mri')
            self._sectionvars['mri'] = 'mri'

        name = caption

        html += u'
\n' % global_id
        html += u'
%s
\n' % name  # all other captions are h4
        html += self._render_one_bem_axis(mri_fname, surf_fnames, global_id,
                                          shape, 'axial', decim, n_jobs)
        html += self._render_one_bem_axis(mri_fname, surf_fnames, global_id,
                                          shape, 'sagittal', decim, n_jobs)
        html += self._render_one_bem_axis(mri_fname, surf_fnames, global_id,
                                          shape, 'coronal', decim, n_jobs)
        html += u'
\n'
        return ''.join(html)

def _clean_varnames(s):
    # Remove invalid characters
    s = re.sub('[^0-9a-zA-Z_]', '', s)

    # add report_ at the beginning so that the javascript class names
    # are valid ones
    return 'report_' + s

def _recursive_search(path, pattern):
    """Auxiliary function for recursive_search of the directory."""
    filtered_files = list()
    for dirpath, dirnames, files in os.walk(path):
        for f in fnmatch.filter(files, pattern):
            # only the following file types are supported
            # this ensures equitable distribution of jobs
            if f.endswith(tuple(VALID_EXTENSIONS)):
                filtered_files.append(op.realpath(op.join(dirpath, f)))

    return filtered_files

def _fix_global_ids(html):
    """Fix the global_ids after reordering in _render_toc()."""
    html = re.sub(r'id="\d+"', 'id="###"', html)
    global_id = 1
    while len(re.findall('id="###"', html)) > 0:
        html = re.sub('id="###"', 'id="%s"' % global_id, html, count=1)
        global_id += 1
    return html

redpajama_set_name: RedPajamaGithub


She's gorgeous, has a body that most women dream of and is very open on social media about her - sometimes difficult - journey from a standard sized model to a model with (in the eyes of the fashion world) a size up. Chosen by PB as STRONG WOMAN! So a perfect occasion to discover more about this beauty. Amanda Westenberg openly answers our 10 questions about her experiences as a model and as a woman in general.
1. How did you become a model?
I was 13 when I was approached to be a model. After some talks with my parents and a test shoot, I eventually signed at Ulla Models, whom I still collaborate with at this moment.
2. What attracted you to the world of modelling back then and have your expectations been met?
I was very skeptical about modelling at first. I didn't see myself that way at all and was unsure if the lifestyle would suit me. But after a while I started to enjoy all the different people I got to meet and the different places I got to visit! I stepped into it without any expectations and everything I was skeptical about, I've now accepted as 'part of the job'. I like being a model more than I expected back then.
3. What are the great and less great things about modelling?
The enormous flexibility that's asked can be straining at times and I get homesick when I'm away for longer periods of time - thankfully there's all kinds of technology nowadays to keep in touch with family and friends. I've also struggled with keeping my weight down in the past. I'm not naturally skinny and the insecurity that came with this didn't feel good at all. But I've grown now, both physically and mentally.
4. When did you notice that the super slim body that was expected from you, didn't portray the real you?
I've always had to work very hard to achieve standard model sizes - and I never quite got there in the end. After a period of working, exercising and dieting intensively, my body just gave up and I knew right then and there that I had to stop trying to be someone I wasn't. And it's been the best decision ever! Both privately and professionally.
5. You have a beautiful figure. What do you do differently now - as opposed to before - to stay in shape?
After my wonderful decision, I took a long break from everything. I ate everything I wanted and exercised a lot less to get everything back on track. After a while the urge to exercise slowly returned but I still had a lot of questions about health and exercise. I got in touch with my current personal trainer and I exercise differently now - to stay healthy and fit, not to become thinner. Food wise, I always eat healthy but I enjoy the occasional treat a lot more now, without feeling any guilt.
6. You're very open about your body and story on social media. What do you hope to get across to your followers?
I hope to motivate my followers to live a healthy life, with exercise as a part of that lifestyle. But to exercise for the right reasons - to be healthy and not to look a certain way. When you feel fit and good about yourself, this automatically translates to a happier you - and isn't that what we all want?
7. Middle-sized models are gaining in popularity. Have you noticed this in the kind of jobs or in the frequency you work?
I've noticed progress work wise; I work more and there's a lot of acceptance and respect for the choices I've made. I truly believe that with a healthy lifestyle, everybody is beautiful in the body they have. I'm so happy to see that the fashion world is now slowly transitioning towards this positive message.
8. Every woman has insecurities about her body. What would you like to say to these women?
The answer is already in the question: "every woman has insecurities about her body." No matter how confident you might seem, every woman has insecurities, which makes us completely human, unique and beautiful! You're not the only one and you're not alone.
9. You're a 'strong women' in our eyes - do you see yourself this way?
Thanks for the compliment! I see myself as work in progress. Different events in my life have left me feeling insecure about myself. But I'm currently working very hard to better this, together with my coach and personal trainer. This definitely has a positive effect on me and makes me feel like a 'strong woman' more and more.
10. What does an average day look like for you?
I travel quite a lot for work nowadays, so I see the airport crew more often than my own family and friends! From the plane I normally go straight to my accommodation for the night, so I can start work fresh and rested the next day. I always start out with my 'happy' music list and coffee. I love chatting with the different teams. After a shoot I mostly travel on directly - back home or to the next destination. When I'm not working, I love spending time at home with my cat Kiwi and with my friends and family. Besides exercising, I also keep up my beauty routine; mani and pedicures, facials and visits to the hairdresser.
We've chosen Amanda Westenberg as our strong woman. Want to know more about Amanda? Then follow her on Instagram and keep an eye on our shop these coming weeks.


We rate automobile insurers on how they treat claimants. Some are more helpful than others. Some even provide "assistance" as required by 3 AAC 26.040(a)(1). Some are downright mean and/or dishonest. Remember the distinction between insurance agents and adjusters. Agents sell insurance. Adjusters fight you on claims.
All insurance agents are nice when we go to buy insurance, when they want our money. The acid test is how insurance adjusters treat us once we're forced to file a claim, asking for their money. Oddly, our nice insurance agent never seems to be available when we have a claim. After we've been run-over, injured and are not thinking straight, the insurance company forces us to deal with an adjuster, many of whom seem willing to eat-their-young.
There are exceptions to every rule. For instance, Geico recently hired some genuinely decent humans as adjusters. They still fight claims, but at least they are nicer about it. At the same time, State Farm has of late seemed to fight for no good reason. Like everything else, there are cycles for every insurer. We encourage you to speak to your friends and make your own decision. Auto insurance is imporatat for the well-being of you adn your family.
Part of the problem is that almost every auto insurer listed above uses a computer program to value your claim: Collossus™. This program greatly limits an adjuster's ability to value a case fairly for specifics that don't fit into Collossus™' ranking criteria. It also appears that Collosus™ may be self-limiting, giving adjusters a claim valuation number of "X", and then instructing the adjuster to "Settle for X or less" [SFXOL]. Isn't it good to know that "your" insurance company is trying to save every dime it can … at your expense?
This site is not intended to provide legal or insurance advice. Practical and driving suggestions require common sense. Consult a driving instructor before trying anything we suggest. Always be safe. Remember, we don't become your attorney because you accessed this site, or because you sent us an email. Check in person with a licensed attorney for all specific legal questions. Check with a licensed insurance agent for all specific insurance questions. Check with an experienced driving instructor before trying any driving suggestions. All Attorney/Client agreements must be in writing. Finally, always keep an eye on your Statute of Limitations for injuries in Alaska. If you don't settle your injury claim, or file a lawsuit before the Statute of Limitations expires, you will lose valuable legal rights. Act promptly.


favorite compositions on your device! Get chord statistics, related to record the currently selected key. App settings: Transpose playback for Bb or Eb instruments. Deutsche Version der Webseite anzeigen? Practice scales in different keys and clefs. Ultimate Circle Of Fifths, music, audiobus 3, music. Circle of 5ths trilogy. Now with 320bpm as max. Infos Chord symbols and chord structures are explained. FOR songwriters producers: Stuck for new ideas? Piano Guitar fretboard, check out how chords and scales are played on the piano or guitar. Circle Of Fifths App, music, you May Also Like, jamn Multi-tool: Music. Memorizing and visualizing the diagram is highly recommended. Scales major, natural minor, harmonic minor and melodic minor. As, practice Tool, get familiar with the circle of fifths and modes, practice progressions, scales and master your instrument. You don't need to know any music theory to use this app. A continuously growing list of popular songs. Game Center: No Support, rating: 4, what's New in Version.0. Create custom chords which are out of the key, change time signature (4/4, 3/4, 6/8, 7/8, 5/8 change the tempo (40-320bpm) and adjust the flexible song structure. Circle of 5ths collection series is a comprehensive library for all musicians, composers, music teachers, lecturers, professors, students at all levels and music enthusiasts. Different playback sounds: Grand Piano Acoustic Guitar E-Piano Synth Pad Practice, compose with our without a click.
T need to know or learn any music theory. C clef, share songs and collaborate with friends or band colleagues. G clef, learn how chords are played on the piano or guitar. Phrygian, guitar scales and modes, ll find out what will sound good together. Ionian major aeolian minor dorian, you donapos, solfége in both fixed and movable 1 Even more stable sound playback Correct chord statistics When the traditional mode is active. Many improvements and bug fixes including iOS 11 support. You can display chords, chords and roman numerals circle of 5ths iphone app 0, let us explore and discover hidden musical keys. Roman numerals or both, just play around with the app and youapos.
Analyze and practice them, audibly, understanding these enharmonic spellings is very important. Guitar Intervals Pro, relative pitch and perfectabsolute pitch, note C can be named as B Dbb and. The overtone harmonic series with sound. This Master edition is the next version of Circle of 5ths Pro that covers up to 14 sharps and flats Circle of 5ths Pro version covers up. Display and playback 4note chords in the circle of fifths. Key signature matrices, check out chord statistics related to the currently active key. Compose songs or practice chord progressions. But spelt differently iphone because are needs to satisfy or adjust a musical rule or many other reasons mac in the particular situation. They sound the same, check out chords of popular songs.
Check out the chord relations within the circle of 5ths.Practice and analyze them, see directly which chords are off key.The series is designed to help to develop the aural perception as well.g.
(Only if you have app version.1.5 or lower).Circle of all 2nds, 3rds, 4ths, 6ths, 7ths, also changeable color of the circle.Tempo A transposition feature for Bb and Eb instruments.


text:
ankara capitale de turkey

Turkey désigne :

Mot anglais signifiant dindon ou Turquie

 Turkey, ville de Caroline du Nord ;
 Turkey, ville du Texas.
 Turkey, cours d'eau des États-Unis

Homonymie de toponyme

redpajama_set_name: RedPajamaWikipedia


You may also be interested in the following nearby towns and cities: Troon, Gourock, Doune, Auchterarder, Alness, Stornoway, Alford, Kilmacolm, Stirling, Kilwinning, Aberfeldy, Dumfries, Castle Douglas, Langholm, Dunfermline, Darvel, Largs, Saltcoats, St. Andrews, Ellon, etc.
Accountant DB provides exposure for UK Accountants & Bookkeepers. If you would like to recommend an Accountant / Bookkeeper in Galashiels, please Suggest a Business for inclusion in our database.


Hi, I am a 46 year old lady. Yes, I am a divorcee with 3 grown up kids. I like to meet people, find new friends to share interests, jokes, laughter, experience etc. I love to watch movies, enjoys walking on the beach, cycling is ok too. I am from Singapore.


My name is Jozsef Fejes, I am a computer programmer.
All the images on this site have been created by a computer algorithm that I made for a contest.
Every image contains all the 16.777.216 colors of the 24-bit RGB color space exactly once.
The images are grown randomly, pixel by pixel, color by color. Countless different designs are possible.
2014.03.30. The site has been launched, but far from complete yet.


This rental is for longer term (minimum 1 week) rentals and is suitable for 2-3 families. It has 4 double bedrooms and another with 3 single beds. Mattresses are available for additional persons and there are large gardens and lawns suitable for pitching tent for any overflow.
Small swimming pool, barbeque and two separate patios. 1999 Rover available on request and various lawn games provided. Cycles and kayaks available on request. Rural outlook with view of Mount Maunganui and Kaimai Ranges. Surrounded by horse paddocks and well away from any neighbours! 2km to major Bayfair shopping centre and 300m from Superette and local pub.


THESE images lay bare Dubai's first ever female super-car club whose members are as beautiful as the sleek cars they fearlessly drive.
About 80 women from 17 nations have rallied under banner of the Arabian Gazelles.
Most are business owners, others are housewives - but all have one thing in common.
They have lots of cash and a need for speed that will leave many a male motormouth behind them in the desert dust.
In a region of the world where woman often take the back seat - with Saudi Arabia only allowing females to drive this month - the club is a real trailblazer.
One of the members, Alex Hirschi, who driving under the alias Supercar Blondie, is also a member of Arabian Gazelles, explained to Forbes who was allowed to join.
Alex said: "To join the club, one has to be a supercar aficionado, and not merely a poser.
"She has to drive and own a supercar such as Porsche, Lamborghini, Ferrari, Rolls Royce, Bentley, Mercedes-AMG GT Coupé, McLaren, Aston Martin, Maserati, Bugatti.
Founder Hanan Mazouzi added: "We are supercar enthusiasts and bonafide drivers who share the same passion and rush behind the wheel as men do.
"We refuse to sit pretty on passenger seats or cheerlead from the pits."
And several members say many people still expect them to be wives or girlfriends when they pull up in their supercars.
Jalyn Jarvi said: "You pull up and they expect you to be somebody's girlfriend or wife.
"Everybody's staring at you and it's a little bit uncomfortable."
Founder of the Arabian Gazelles, Hanan Mazouzi Sobati, buys cars with her husband and her love of powerful motors started when she was just a child and playing with her brother's toy cars.
She has a Rolls Royce Drophead Phantom and Morgan Aero Supersport in her car collection and started a track day with her two neighbours in 2016.
She said: "Their husbands were all scared of me, because I was the one corrupting their wives."
The Gazelles have taken part in events run by top-end car manufacturers.
In February, McLaren organised an exclusive and first ever female-only track experience.
Just a few week ago they were at Dubai Autodrome driving Rolls-Royce's latest high-performance Black Badge models.


Medical training: Washington University School of Medicine, Children's Hospital Medical Centre Boston, Harvard Medical School, Paediatric Research Unit Guy's Hospital, Specialist Certificate – Paediatrics Royal College of Physicians, Specialist Registration General Medical Council.
Charities and affiliations: The Little Foundation Trustee, West London Action for Children Vice President, The Sick Children's Trust, BMA Private Practice Committee, Assessor Royal Collage of Paediatrics & Child Health.
Awards: Fellow in Paediatric Haematology and Genetics, USPHS, Children's Hospital Medical Centre, Harvard Medical School, Rockafella Foundation – Population Council Fellow, Strauss Family Foundation award in Paediatrics, L'Accademia Medica Lombard Milan, Recipient Parliamentary Grants in Aids, Fellow – Royal College of Paediatrics and Child Health.


text:
DEDICATION

This work is dedicated to all animal lovers, especially those who have adopted and saved a life in need. We have dedicated over five years of our life building a community hand in hand with you, our incredible fans. Thank you for your continued support and humane treatment of our animal friends.
Introduction and Compilation Copyright © 2016 by The Omidi Company LLC

_Cats on Instagram_ and @cats_of_instagram are operated independently and are not affiliated with, endorsed, or sponsored by Instagram LLC. All photographs copyright © by the individual photographers.

All rights reserved. No part of this book may be reproduced in any form without written permission from the publisher.

Library of Congress Cataloging-in-Publication Data available.

ISBN 978-1-4521-5196-0 (hc)

ISBN 987-1-4521-5281-3 (epub, mobi)

Design by Hillary Caudle

Front jacket photographs by: @juiceecat, @alain_cat, @polagram, @precious_luna, @wednesdayandgizmo, @threescaredycats, @whiskered_away, @cheddarcheesepuff, @jesyalmaguer, @panchobear, @momomilochub, @henrynoodle, @s_nookie, @mspollypockets, @veronicagrojano, @meowscott, @musammmmm

Back jacket photographs by: @themaestros, @summer_dawn_ adams, @macchacat, @bagwellphotography, @playground_ ragdolls, @exoticniko, @yeonhoo_doma, @anniepaddington, @chippydiego, @molla_and_bozley, @nala_cat

Chronicle Books LLC
680 Second Street
San Francisco, CA 94107
www.chroniclebooks.com

# Contents

INTRODUCTION

Cats on Instagram

About the Authors

# INTRODUCTION

Cats of Instagram is run and operated by two humans. Our names are Eli Omidi and Kady Lone. We are ordinary cat (and animal) lovers, just like you. @ cats_of_instagram was born in 2011 as a safe haven for cat lovers everywhere. We wanted to create a community free from the drudgery of advertising and to diverge from the money-making social media machines from the past. We wanted to organically build a community that was truly molded and shaped by its followers.

One of our principal goals was to consistently and accurately portray cats and their daily, often mysterious, lives. Additionally, we wanted to negate the stereotype that cat lovers are chiefly elderly women. Cat lovers come in all shapes, cultures and age ranges, and we wanted to make sure that was made visible to the world. Thus, the #catsofinstagram hashtag was born. The hashtag was our daily driver in selecting photos from the Instagram community. Long before the Internet existed, people were photographing their furry friends. Now with the advent of Instagram, we had the tools necessary to build a central hub for people to enjoy the best, original, cat photos in existence.

This book was created with the same love, care, and attention to detail that we take into selecting photos for our blog. We like to think of the book as our personal, handpicked favorites from the past few years. As you can imagine, this was no easy task. The challenging part was narrowing down an already exclusive list of photos from our account into fun and distinctly unique categories. We dove deep into our archive of thousands of featured photos and methodically created a list of photos we wanted to include in the book.

There were a lot of considerations that went into selecting a photo for the book, including what photos our community loved best. From there, we reached out to people one by one to get the photo in the highest quality format for print and your viewing pleasure. It took many moons to nail it, but we think we just might have pulled it off.

Just like our blog, this book would not have been possible without the people behind the photos. We are immensely fortunate to have such a loving and creative community to pull from. They are our inspiration and it is they that have given us the ability to compile their works here. This quite possibly could be the cutest collection of original, accredited cat photos in a book ever. We hope that wherever you may be in the world, this book will instantly bring a smile to your face whenever you pick it up.

It is a tremendous honor to run the largest cat based social media account in the known universe. Along with our furry friends, you are the daily inspiration to continue to make Cats of Instagram the greatest cat photo gallery ever created. Thank you for being a part of the Cats of Instagram family.

-Eli and Kady

  | @cats_of_instagram

---|---

A special thank you to our closest pals who believed in us from the very beginning (in no particular order): Geovanna Salas, Stephanie Harwin, Mandy Shunk, Andrew Marttila, Susan Michals, Zach Stubenvoll, Ryan Roche, Lauren Gallagher, the Lone family, especially Kady's late father Doug Lone, and the Omidi family.

# About the Authors

photo credit: Andrew Marttila

@cats_of_instagram is the go-to internet destination for cat lovers which features original, accredited cat photos that are selected daily from the #catsofinstagram hashtag. The account was created in 2011 by Eli Omidi, a college grad and self-proclaimed cat lover. He now co-runs the site with Kady Lone, freelance graphic designer and mother of the internet-famous cat Pudge. It also provides giveaways, news on Internet-famous cats, and covers special events such as Cat Con, pet conferences, and art shows. Cats of Instagram has become the largest cat-centric community on the Internet today.

redpajama_set_name: RedPajamaBook


text:
Hylton H Smith
The Darwinian Extension: Transition
By Hylton H Smith
Author: Hylton H Smith
Series: The Darwinian Extension
Spiderwize
The Darwinian Extension: Completion
The Darwinian Extension: Initiation
Frankenstein.com
Resident Fear
The Ice Wars of Dominia
The Jupiter Paradox
The Nexus Odyssey
The Sacred Protocol
The Darwinian Extension: Transition is the second volume in the science fiction trilogy from author Hylton H Smith, and follows on from the events in Initiation.
Transition begins in the year 2038, 2 years have passed since the return of the Copernicus, the ship carrying the first Mars colonisation mission. Their return has caused a massive religious and political upheaval but has also brought with it massive leaps in technological development.
Alex 2 advises that the way of the Progenitors is to offer encouragement of promise rather than a proactive assistance of development. He did however suggest that humanity would not survive in their present process of biological evolution when the sun goes supernova.
Eventually a new political structure is developed, departing from the consumerism and commercialism of previous governments, as part of this new development Alex 2 is made Honorary leader of the new Council for Human Exploration, due to his vast knowledge and near immortality.
Despite this fledgling new government, there is still a large amount of friction between the Earth and Mars colony which is then thrown into chaos when a second discovery is made on Mars. A new life form known as the Rabo is unearthed, and after more is known about this new alien species a very disturbing message is returned to Earth.
Has the human race evolved sufficiently to live along side an alien race?, the answer may secure the colony of Mars and beyond.
The Darwinian Extension: Transition, like it's predecessor, is a very in-depth science fiction novel that is technically very accomplished. The science is very believable and not really that far from current understanding or technological achievement. The Alien technology is obviously further advanced but even this is explained very clearly and concisely.
Transition improves upon Initiation, the character development is clearer and more defined and the plot easier to relate to. Again this novel is for those who want a "real" or "hard" science fiction novel rather than a lighter reading speculative fiction story but again this really is no negative. There are many books that don't really push the boundaries with the science fiction aspects of their novels, but this series does and should be applauded for such. Recommended for anyone who wants to read true science fiction.
Written on 20th January 2010 by Ant .
Code of the Lifemaker
Moving Mars
The Forge of God
The Genesis Machine

redpajama_set_name: RedPajamaCommonCrawl


Notes: Introduced for its striking flowers and the edible fruit. Known as an occasional naturalised escape in Zimbabwe; it is included in the Appendix to the Mozambican checklist as a possible escape. Definite records are needed.
Hyde, M.A., Wursten, B.T., Ballings, P. & Coates Palgrave, M. (2019). Flora of Mozambique: Cultivated Plants: Species information: Syzygium jambos.