text:
%% bare_conf_compsoc.tex
%% V1.4b
%% 2015/08/26
%% by Michael Shell
%% See:
%% http://www.michaelshell.org/
%% for current contact information.
%%
%% This is a skeleton file demonstrating the use of IEEEtran.cls
%% (requires IEEEtran.cls version 1.8b or later) with an IEEE Computer
%% Society conference paper.
%%
%% Support sites:
%% http://www.michaelshell.org/tex/ieeetran/
%% http://www.ctan.org/pkg/ieeetran
%% and
%% http://www.ieee.org/

%%*************************************************************************
%% Legal Notice:
%% This code is offered as-is without any warranty either expressed or
%% implied; without even the implied warranty of MERCHANTABILITY or
%% FITNESS FOR A PARTICULAR PURPOSE!
%% User assumes all risk.
%% In no event shall the IEEE or any contributor to this code be liable for
%% any damages or losses, including, but not limited to, incidental,
%% consequential, or any other damages, resulting from the use or misuse
%% of any information contained here.
%%
%% All comments are the opinions of their respective authors and are not
%% necessarily endorsed by the IEEE.
%%
%% This work is distributed under the LaTeX Project Public License (LPPL)
%% ( http://www.latex-project.org/ ) version 1.3, and may be freely used,
%% distributed and modified. A copy of the LPPL, version 1.3, is included
%% in the base LaTeX documentation of all distributions of LaTeX released
%% 2003/12/01 or later.
%% Retain all contribution notices and credits.
%% ** Modified files should be clearly indicated as such, including  **
%% ** renaming them and changing author support contact information. **
%%*************************************************************************

% *** Authors should verify (and, if needed, correct) their LaTeX system  ***
% *** with the testflow diagnostic prior to trusting their LaTeX platform ***
% *** with production work. The IEEE's font choices and paper sizes can   ***
% *** trigger bugs that do not appear when using other class files.       ***                          ***
% The testflow support page is at:
% http://www.michaelshell.org/tex/testflow/

\documentclass[conference,compsoc]{IEEEtran}
% Some/most Computer Society conferences require the compsoc mode option,
% but others may want the standard conference format.
%
% If IEEEtran.cls has not been installed into the LaTeX system files,
% manually specify the path to it like:
% \documentclass[conference,compsoc]{../sty/IEEEtran}

% Some very useful LaTeX packages include:
% (uncomment the ones you want to load)

% *** MISC UTILITY PACKAGES ***
%
%\usepackage{ifpdf}
% Heiko Oberdiek's ifpdf.sty is very useful if you need conditional
% compilation based on whether the output is pdf or dvi.
% usage:
% \ifpdf
%   % pdf code
% \else
%   % dvi code
% \fi
% The latest version of ifpdf.sty can be obtained from:
% http://www.ctan.org/pkg/ifpdf
% Also, note that IEEEtran.cls V1.7 and later provides a builtin
% \ifCLASSINFOpdf conditional that works the same way.
% When switching from latex to pdflatex and vice-versa, the compiler may
% have to be run twice to clear warning/error messages.

% *** CITATION PACKAGES ***
%
\ifCLASSOPTIONcompsoc
  % IEEE Computer Society needs nocompress option
  % requires cite.sty v4.0 or later (November 2003)
  \usepackage[nocompress]{cite}
\else
  % normal IEEE
  \usepackage{cite}
\fi
% cite.sty was written by Donald Arseneau
% V1.6 and later of IEEEtran pre-defines the format of the cite.sty package
% \cite{} output to follow that of the IEEE. Loading the cite package will
% result in citation numbers being automatically sorted and properly
% "compressed/ranged". e.g., [1], [9], [2], [7], [5], [6] without using
% cite.sty will become [1], [2], [5]--[7], [9] using cite.sty. cite.sty's
% \cite will automatically add leading space, if needed. Use cite.sty's
% noadjust option (cite.sty V3.8 and later) if you want to turn this off
% such as if a citation ever needs to be enclosed in parenthesis.
% cite.sty is already installed on most LaTeX systems. Be sure and use
% version 5.0 (2009-03-20) and later if using hyperref.sty.
% The latest version can be obtained at:
% http://www.ctan.org/pkg/cite
% The documentation is contained in the cite.sty file itself.
%
% Note that some packages require special options to format as the Computer
% Society requires. In particular, Computer Society  papers do not use
% compressed citation ranges as is done in typical IEEE papers
% (e.g., [1]-[4]). Instead, they list every citation separately in order
% (e.g., [1], [2], [3], [4]). To get the latter we need to load the cite
% package with the nocompress option which is supported by cite.sty v4.0
% and later.

% *** GRAPHICS RELATED PACKAGES ***
%
\ifCLASSINFOpdf
  % \usepackage[pdftex]{graphicx}
  % declare the path(s) where your graphic files are
  % \graphicspath{{../pdf/}{../jpeg/}}
  % and their extensions so you won't have to specify these with
  % every instance of \includegraphics
  % \DeclareGraphicsExtensions{.pdf,.jpeg,.png}
\else
  % or other class option (dvipsone, dvipdf, if not using dvips). graphicx
  % will default to the driver specified in the system graphics.cfg if no
  % driver is specified.
  % \usepackage[dvips]{graphicx}
  % declare the path(s) where your graphic files are
  % \graphicspath{{../eps/}}
  % and their extensions so you won't have to specify these with
  % every instance of \includegraphics
  % \DeclareGraphicsExtensions{.eps}
\fi
% graphicx was written by David Carlisle and Sebastian Rahtz. It is
% required if you want graphics, photos, etc. graphicx.sty is already
% installed on most LaTeX systems. The latest version and documentation
% can be obtained at:
% http://www.ctan.org/pkg/graphicx
% Another good source of documentation is "Using Imported Graphics in
% LaTeX2e" by Keith Reckdahl which can be found at:
% http://www.ctan.org/pkg/epslatex
%
% latex, and pdflatex in dvi mode, support graphics in encapsulated
% postscript (.eps) format. pdflatex in pdf mode supports graphics
% in .pdf, .jpeg, .png and .mps (metapost) formats. Users should ensure
% that all non-photo figures use a vector format (.eps, .pdf, .mps) and
% not a bitmapped formats (.jpeg, .png). The IEEE frowns on bitmapped formats
% which can result in "jaggedy"/blurry rendering of lines and letters as
% well as large increases in file sizes.
%
% You can find documentation about the pdfTeX application at:
% http://www.tug.org/applications/pdftex

% *** MATH PACKAGES ***
%
%\usepackage{amsmath}
% A popular package from the American Mathematical Society that provides
% many useful and powerful commands for dealing with mathematics.
%
% Note that the amsmath package sets \interdisplaylinepenalty to 10000
% thus preventing page breaks from occurring within multiline equations. Use:
%\interdisplaylinepenalty=2500
% after loading amsmath to restore such page breaks as IEEEtran.cls normally
% does. amsmath.sty is already installed on most LaTeX systems. The latest
% version and documentation can be obtained at:
% http://www.ctan.org/pkg/amsmath

% *** SPECIALIZED LIST PACKAGES ***
%
%\usepackage{algorithmic}
% algorithmic.sty was written by Peter Williams and Rogerio Brito.
% This package provides an algorithmic environment fo describing algorithms.
% You can use the algorithmic environment in-text or within a figure
% environment to provide for a floating algorithm. Do NOT use the algorithm
% floating environment provided by algorithm.sty (by the same authors) or
% algorithm2e.sty (by Christophe Fiorio) as the IEEE does not use dedicated
% algorithm float types and packages that provide these will not provide
% correct IEEE style captions. The latest version and documentation of
% algorithmic.sty can be obtained at:
% http://www.ctan.org/pkg/algorithms
% Also of interest may be the (relatively newer and more customizable)
% algorithmicx.sty package by Szasz Janos:
% http://www.ctan.org/pkg/algorithmicx

% *** ALIGNMENT PACKAGES ***
%
%\usepackage{array}
% Frank Mittelbach's and David Carlisle's array.sty patches and improves
% the standard LaTeX2e array and tabular environments to provide better
% appearance and additional user controls. As the default LaTeX2e table
% generation code is lacking to the point of almost being broken with
% respect to the quality of the end results, all users are strongly
% advised to use an enhanced (at the very least that provided by array.sty)
% set of table tools. array.sty is already installed on most systems. The
% latest version and documentation can be obtained at:
% http://www.ctan.org/pkg/array

% IEEEtran contains the IEEEeqnarray family of commands that can be used to
% generate multiline equations as well as matrices, tables, etc., of high
% quality.

% *** SUBFIGURE PACKAGES ***
%\ifCLASSOPTIONcompsoc
%  \usepackage[caption=false,font=footnotesize,labelfont=sf,textfont=sf]{subfig}
%\else
%  \usepackage[caption=false,font=footnotesize]{subfig}
%\fi
% subfig.sty, written by Steven Douglas Cochran, is the modern replacement
% for subfigure.sty, the latter of which is no longer maintained and is
% incompatible with some LaTeX packages including fixltx2e. However,
% subfig.sty requires and automatically loads Axel Sommerfeldt's caption.sty
% which will override IEEEtran.cls' handling of captions and this will result
% in non-IEEE style figure/table captions. To prevent this problem, be sure
% and invoke subfig.sty's "caption=false" package option (available since
% subfig.sty version 1.3, 2005/06/28) as this is will preserve IEEEtran.cls
% handling of captions.
% Note that the Computer Society format requires a sans serif font rather
% than the serif font used in traditional IEEE formatting and thus the need
% to invoke different subfig.sty package options depending on whether
% compsoc mode has been enabled.
%
% The latest version and documentation of subfig.sty can be obtained at:
% http://www.ctan.org/pkg/subfig

% *** FLOAT PACKAGES ***
%
%\usepackage{fixltx2e}
% fixltx2e, the successor to the earlier fix2col.sty, was written by
% Frank Mittelbach and David Carlisle. This package corrects a few problems
% in the LaTeX2e kernel, the most notable of which is that in current
% LaTeX2e releases, the ordering of single and double column floats is not
% guaranteed to be preserved. Thus, an unpatched LaTeX2e can allow a
% single column figure to be placed prior to an earlier double column
% figure.
% Be aware that LaTeX2e kernels dated 2015 and later have fixltx2e.sty's
% corrections already built into the system in which case a warning will
% be issued if an attempt is made to load fixltx2e.sty as it is no longer
% needed.
% The latest version and documentation can be found at:
% http://www.ctan.org/pkg/fixltx2e

%\usepackage{stfloats}
% stfloats.sty was written by Sigitas Tolusis. This package gives LaTeX2e
% the ability to do double column floats at the bottom of the page as well
% as the top. (e.g., "\begin{figure*}[!b]" is not normally possible in
% LaTeX2e). It also provides a command:
%\fnbelowfloat
% to enable the placement of footnotes below bottom floats (the standard
% LaTeX2e kernel puts them above bottom floats). This is an invasive package
% which rewrites many portions of the LaTeX2e float routines. It may not work
% with other packages that modify the LaTeX2e float routines. The latest
% version and documentation can be obtained at:
% http://www.ctan.org/pkg/stfloats
% Do not use the stfloats baselinefloat ability as the IEEE does not allow
% \baselineskip to stretch. Authors submitting work to the IEEE should note
% that the IEEE rarely uses double column equations and that authors should try
% to avoid such use. Do not be tempted to use the cuted.sty or midfloat.sty
% packages (also by Sigitas Tolusis) as the IEEE does not format its papers in
% such ways.
% Do not attempt to use stfloats with fixltx2e as they are incompatible.
% Instead, use Morten Hogholm'a dblfloatfix which combines the features
% of both fixltx2e and stfloats:
%
% \usepackage{dblfloatfix}
% The latest version can be found at:
% http://www.ctan.org/pkg/dblfloatfix

% *** PDF, URL AND HYPERLINK PACKAGES ***
%
%\usepackage{url}
% url.sty was written by Donald Arseneau. It provides better support for
% handling and breaking URLs. url.sty is already installed on most LaTeX
% systems. The latest version and documentation can be obtained at:
% http://www.ctan.org/pkg/url
% Basically, \url{my_url_here}.

% *** Do not adjust lengths that control margins, column widths, etc. ***
% *** Do not use packages that alter fonts (such as pslatex).         ***
% There should be no need to do such things with IEEEtran.cls V1.6 and later.
% (Unless specifically asked to do so by the journal or conference you plan
% to submit to, of course. )

% correct bad hyphenation here
\hyphenation{op-tical net-works semi-conduc-tor}

\begin{document}
%
% paper title
% Titles are generally capitalized except for words such as a, an, and, as,
% at, but, by, for, in, nor, of, on, or, the, to and up, which are usually
% not capitalized unless they are the first or last word of the title.
% Linebreaks \\ can be used within to get better formatting as desired.
% Do not put math or special symbols in the title.
\title{Bare Demo of IEEEtran.cls for\\ IEEE Computer Society Conferences}

% author names and affiliations
% use a multiple column layout for up to three different
% affiliations
\author{\IEEEauthorblockN{Michael Shell}
\IEEEauthorblockA{School of Electrical and\\Computer Engineering\\
Georgia Institute of Technology\\
Atlanta, Georgia 30332--0250\\
Email: http://www.michaelshell.org/contact.html}
\and
\IEEEauthorblockN{Homer Simpson}
\IEEEauthorblockA{Twentieth Century Fox\\
Springfield, USA\\
Email: homer@thesimpsons.com}
\and
\IEEEauthorblockN{James Kirk\\ and Montgomery Scott}
\IEEEauthorblockA{Starfleet Academy\\
San Francisco, California 96678-2391\\
Telephone: (800) 555--1212\\
Fax: (888) 555--1212}}

% conference papers do not typically use \thanks and this command
% is locked out in conference mode. If really needed, such as for
% the acknowledgment of grants, issue a \IEEEoverridecommandlockouts
% after \documentclass

% for over three affiliations, or if they all won't fit within the width
% of the page (and note that there is less available width in this regard for
% compsoc conferences compared to traditional conferences), use this
% alternative format:
%
%\author{\IEEEauthorblockN{Michael Shell\IEEEauthorrefmark{1},
%Homer Simpson\IEEEauthorrefmark{2},
%James Kirk\IEEEauthorrefmark{3},
%Montgomery Scott\IEEEauthorrefmark{3} and
%Eldon Tyrell\IEEEauthorrefmark{4}}
%\IEEEauthorblockA{\IEEEauthorrefmark{1}School of Electrical and Computer Engineering\\
%Georgia Institute of Technology,
%Atlanta, Georgia 30332--0250\\ Email: see http://www.michaelshell.org/contact.html}
%\IEEEauthorblockA{\IEEEauthorrefmark{2}Twentieth Century Fox, Springfield, USA\\
%Email: homer@thesimpsons.com}
%\IEEEauthorblockA{\IEEEauthorrefmark{3}Starfleet Academy, San Francisco, California 96678-2391\\
%Telephone: (800) 555--1212, Fax: (888) 555--1212}
%\IEEEauthorblockA{\IEEEauthorrefmark{4}Tyrell Inc., 123 Replicant Street, Los Angeles, California 90210--4321}}

% use for special paper notices
%\IEEEspecialpapernotice{(Invited Paper)}

% make the title area
\maketitle

% As a general rule, do not put math, special symbols or citations
% in the abstract
\begin{abstract}
The abstract goes here.
\end{abstract}

% no keywords

% For peer review papers, you can put extra information on the cover
% page as needed:
% \ifCLASSOPTIONpeerreview
% \begin{center} \bfseries EDICS Category: 3-BBND \end{center}
% \fi
%
% For peerreview papers, this IEEEtran command inserts a page break and
% creates the second title. It will be ignored for other modes.
\IEEEpeerreviewmaketitle

\section{Introduction}
% no \IEEEPARstart
This demo file is intended to serve as a ``starter file''
for IEEE Computer Society conference papers produced under \LaTeX\ using
IEEEtran.cls version 1.8b and later.
% You must have at least 2 lines in the paragraph with the drop letter
% (should never be an issue)
I wish you the best of success.

\hfill mds

\hfill August 26, 2015

\subsection{Subsection Heading Here}
Subsection text here.

\subsubsection{Subsubsection Heading Here}
Subsubsection text here.

% An example of a floating figure using the graphicx package.
% Note that \label must occur AFTER (or within) \caption.
% For figures, \caption should occur after the \includegraphics.
% Note that IEEEtran v1.7 and later has special internal code that
% is designed to preserve the operation of \label within \caption
% even when the captionsoff option is in effect. However, because
% of issues like this, it may be the safest practice to put all your
% \label just after \caption rather than within \caption{}.
%
% Reminder: the "draftcls" or "draftclsnofoot", not "draft", class
% option should be used if it is desired that the figures are to be
% displayed while in draft mode.
%
%\begin{figure}[!t]
%\centering
%\includegraphics[width=2.5in]{myfigure}
% where an .eps filename suffix will be assumed under latex,
% and a .pdf suffix will be assumed for pdflatex; or what has been declared
% via \DeclareGraphicsExtensions.
%\caption{Simulation results for the network.}
%\label{fig_sim}
%\end{figure}

% Note that the IEEE typically puts floats only at the top, even when this
% results in a large percentage of a column being occupied by floats.

% An example of a double column floating figure using two subfigures.
% (The subfig.sty package must be loaded for this to work.)
% The subfigure \label commands are set within each subfloat command,
% and the \label for the overall figure must come after \caption.
% \hfil is used as a separator to get equal spacing.
% Watch out that the combined width of all the subfigures on a
% line do not exceed the text width or a line break will occur.
%
%\begin{figure*}[!t]
%\centering
%\subfloat[Case I]{\includegraphics[width=2.5in]{box}%
%\label{fig_first_case}}
%\hfil
%\subfloat[Case II]{\includegraphics[width=2.5in]{box}%
%\label{fig_second_case}}
%\caption{Simulation results for the network.}
%\label{fig_sim}
%\end{figure*}
%
% Note that often IEEE papers with subfigures do not employ subfigure
% captions (using the optional argument to \subfloat[]), but instead will
% reference/describe all of them (a), (b), etc., within the main caption.
% Be aware that for subfig.sty to generate the (a), (b), etc., subfigure
% labels, the optional argument to \subfloat must be present. If a
% subcaption is not desired, just leave its contents blank,
% e.g., \subfloat[].

% An example of a floating table. Note that, for IEEE style tables, the
% \caption command should come BEFORE the table and, given that table
% captions serve much like titles, are usually capitalized except for words
% such as a, an, and, as, at, but, by, for, in, nor, of, on, or, the, to
% and up, which are usually not capitalized unless they are the first or
% last word of the caption. Table text will default to \footnotesize as
% the IEEE normally uses this smaller font for tables.
% The \label must come after \caption as always.
%
%\begin{table}[!t]
%% increase table row spacing, adjust to taste
%\renewcommand{\arraystretch}{1.3}
% if using array.sty, it might be a good idea to tweak the value of
% \extrarowheight as needed to properly center the text within the cells
%\caption{An Example of a Table}
%\label{table_example}
%\centering
%% Some packages, such as MDW tools, offer better commands for making tables
%% than the plain LaTeX2e tabular which is used here.
%\begin{tabular}{|c||c|}
%\hline
%One & Two\\
%\hline
%Three & Four\\
%\hline
%\end{tabular}
%\end{table}

% Note that the IEEE does not put floats in the very first column
% - or typically anywhere on the first page for that matter. Also,
% in-text middle ("here") positioning is typically not used, but it
% is allowed and encouraged for Computer Society conferences (but
% not Computer Society journals). Most IEEE journals/conferences use
% top floats exclusively.
% Note that, LaTeX2e, unlike IEEE journals/conferences, places
% footnotes above bottom floats. This can be corrected via the
% \fnbelowfloat command of the stfloats package.

\section{Conclusion}
The conclusion goes here.

% conference papers do not normally have an appendix

% use section* for acknowledgment
\ifCLASSOPTIONcompsoc
  % The Computer Society usually uses the plural form
  \section*{Acknowledgments}
\else
  % regular IEEE prefers the singular form
  \section*{Acknowledgment}
\fi

The authors would like to thank...

% trigger a \newpage just before the given reference
% number - used to balance the columns on the last page
% adjust value as needed - may need to be readjusted if
% the document is modified later
%\IEEEtriggeratref{8}
% The "triggered" command can be changed if desired:
%\IEEEtriggercmd{\enlargethispage{-5in}}

% references section

% can use a bibliography generated by BibTeX as a .bbl file
% BibTeX documentation can be easily obtained at:
% http://mirror.ctan.org/biblio/bibtex/contrib/doc/
% The IEEEtran BibTeX style support page is at:
% http://www.michaelshell.org/tex/ieeetran/bibtex/
%\bibliographystyle{IEEEtran}
% argument is your BibTeX string definitions and bibliography database(s)
%\bibliography{IEEEabrv,../bib/paper}
%
% <OR> manually copy in the resultant .bbl file
% set second argument of \begin to the number of references
% (used to reserve space for the reference number labels box)
\begin{thebibliography}{1}

\bibitem{IEEEhowto:kopka}
H.~Kopka and P.~W. Daly, \emph{A Guide to \LaTeX}, 3rd~ed.\hskip 1em plus
  0.5em minus 0.4em\relax Harlow, England: Addison-Wesley, 1999.

\end{thebibliography}

% that's all folks
\end{document}


text:
<!doctype html>

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,minimal-ui">

    <title>When to Use HTML and Plain Text Emails</title>
	<meta name="description" content="Don't let fancy HTML emails fool you. They are not always the best solution.">
	<link rel="canonical" href="https://www.caretjuice.com/blog/html-plain-text-email.html" />
    <link href="../stylesheets/app-8d837d4a.css" rel="stylesheet" />

	<link href="../assets/images/favicon-d706ba9e.png" rel="shortcut icon" type="image/png">
	<link href="../assets/images/favicon-152-da840e5c.png" rel="apple-touch-icon-precomposed">
	<meta name="msapplication-TileColor" content="#FFFFFF">
	<meta name="msapplication-TileImage" content="../assets/images/favicon-144-ac9bfb6e.png">
	<link rel="alternate" type="application/atom+xml" title="Caret Juice Blog Atom feed" href="https://www.caretjuice.com/feed.xml" />
	<meta property="fb:app_id" content="349018238896517"/>
	<meta property="og:url" content="https://www.caretjuice.com/blog/html-plain-text-email.html" />
	<meta property="og:type" content="article" />
	<meta property="og:title" content="When to Use HTML and Plain Text Emails" />
	<meta property="og:description" content="Don't let fancy HTML emails fool you. They are not always the best solution." />

	<meta property="og:image" content="https://www.caretjuice.com/blog/html-plain-text-email/html-plain-text-email-facebook.png" />
	<meta property="og:site_name" content="Caret Juice Marketing" />
	<meta name="twitter:card" content="summary_large_image">
	<meta name="twitter:site" content="@CaretJuice">
	<meta name="twitter:creator" content="@damongudaitis">
	<meta name="twitter:title" content="When to Use HTML and Plain Text Emails">
	<meta name="twitter:description" content="Caret Juice Marketing">
	<meta name="twitter:image" content="https://www.caretjuice.com/blog/html-plain-text-email/html-plain-text-email-twitter.png">

Audience Experience Optimization

Home

Services

Data Collection

Data Analysis

Experience Optimization

Blog

Contact

Caret Juice Marketing

        MENU

Home

Services

Data Collection

Data Analysis

Experience Optimization

Blog

Contact

When to Use HTML and Plain Text Emails

by Damon Gudaitis | under Reasons

Don't let fancy HTML emails fool you. They are not always the best solution.

				Tweet This

HTML emails are prettier than plain text. They require more expertise to produce than is seen in the average email user. As a result, business owners and managers are attracted to highly-designed HTML emails.

But this barrier to entry often means that HTML emails are the wrong choice for communicating with customers and leads.

When HTML is the Wrong Answer

The main problem with highly-designed emails is that they don't look like the kind of email you get from friends and colleagues. They immediately put their recipients on guard for marketing messages and the quality of design and messaging needs to overcome this disadvantage.

This makes HTML a bad choice for drip emails. Typically drip emails are automated emails chains that take on the tone of a conversation between the prospect and someone at the company.

The email then needs to seem like it is coming from the company representative and any design beyond proper punctuation is going to undermine the purpose of the email.

When HTML is the Right Answer

A highly-designed HTML newsletter is a different situation. Even if your language is friendly and personal, the recipients expect that they are receiving a message from a company.

There's another important consideration with respect to design and branding when planning an HTML email—and that's content. In particular, how the content of the email reflects on the brand.

A strong and consistent visual brand brand combined with excellent content trains recipients to respond favorably to the brand.

Remember that people receiving your emails are going to be most strongly imprinted with your brand as they are exposed to it repeatedly.

Conversely, if you send emails with strongly branded visuals but poor content then you are going to train these same people to react unfavorably. Until they unsubscribe, but people stay subscribed to emails they don't care for much too long.

Designing emails is a lot of work. Trust me, if you don't know how much work they are, then you don't want to find out. HTML emails are a nightmare.

But it is still relatively inexpensive for a manager or owner to spend on some nice email templates compared with the consistent on-going effort required to produce excellent content for those emails.

Until you're in a position where you can consistently produce excellent content, spending on emails with a strong brand design is going to hurt your brand more than it will help.

Your brand is more than just design and logos. The content plays a part too.

I've seen too many strong brands send bad emails to stay silent on the subject.

Share:

 LinkedIn

 Twitter

 Facebook

Practical Business Automation

Guide to business automation in the golden age of software as a service.

Learn about the Constellation Approach to Business Technology—the conceptual framework that underpins Caret Juice services. Follow our recipes and set up your own automation.

		Read the Guide
FREE - NO EMAIL REQUIRED

Caret Juice Marketing uses cookies to learn how people use the site and improve your experience. By continuing you consent to the cookie policy outlined in our privacy policy.

			Accept and Continue

© Caret Juice Marketing

Privacy

Contact

redpajama_set_name: RedPajamaGithub


text:
#include "graphd/graphd.h"
#include "graphd/graphd-hash.h"

#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#define HAS_GUID(guidcon)                                                    \
  ((guidcon).guidcon_include_valid && (guidcon).guidcon_include.gs_n == 1 && \
   !GRAPH_GUID_IS_NULL((guidcon).guidcon_include.gs_guid[0]))

unsigned int graphd_constraint_linkage_pattern(graphd_constraint const* con) {
  unsigned int pattern = 0;
  size_t i;

  if (con == NULL) return 0;

  if (graphd_linkage_is_my(con->con_linkage))
    pattern |= 1 << GRAPHD_PATTERN_LINKAGE(graphd_linkage_my(con->con_linkage));

  for (i = 0; i < PDB_LINKAGE_N; i++)
    if (con->con_linkcon[i].guidcon_include_valid)
      pattern |= 1 << GRAPHD_PATTERN_LINKAGE(i);

  for (con = con->con_head; con != NULL; con = con->con_next)
    if (graphd_linkage_is_i_am(con->con_linkage))
      pattern |=
          1 << GRAPHD_PATTERN_LINKAGE(graphd_linkage_i_am(con->con_linkage));

  return pattern;
}

void graphd_constraint_initialize(graphd_handle* g, graphd_constraint* con) {
  memset(con, 0, sizeof(*con));

  con->con_head = NULL;
  con->con_tail = &con->con_head;

  con->con_assignment_head = NULL;
  con->con_assignment_tail = &con->con_assignment_head;

  con->con_variable_declaration_valid = false;
  con->con_pframe_temporary = (size_t)-1;

  con->con_cc_head = NULL;
  con->con_cc_tail = &con->con_cc_head;

  con->con_or_head = NULL;
  con->con_or_tail = &con->con_or_head;

  con->con_type.strqueue_head = NULL;
  con->con_type.strqueue_tail = &con->con_type.strqueue_head;

  con->con_name.strqueue_head = NULL;
  con->con_name.strqueue_tail = &con->con_name.strqueue_head;

  con->con_value.strqueue_head = NULL;
  con->con_value.strqueue_tail = &con->con_value.strqueue_head;

  con->con_high = PDB_ITERATOR_HIGH_ANY;

  con->con_valuetype = GRAPH_DATA_UNSPECIFIED;
  con->con_archival = GRAPHD_FLAG_UNSPECIFIED;
  con->con_live = GRAPHD_FLAG_UNSPECIFIED;
  con->con_meta = GRAPHD_META_UNSPECIFIED;
  con->con_forward = true;
  con->con_sort_valid = true;

  con->con_setsize = pdb_primitive_n(g->g_pdb);

  con->con_key_dup = NULL;
  con->con_unique_dup = NULL;

  con->con_comparator = graphd_comparator_unspecified;
  con->con_it = NULL;
  graphd_bad_cache_initialize(&con->con_bad_cache);
}

int graphd_constraint_use_result_instruction(graphd_request* greq,
                                             graphd_constraint const* con,
                                             graphd_pattern const* pat) {
  cl_handle* cl = graphd_request_cl(greq);

  /*  Can't use "contents" in a constraint that doesn't have
   *  subconstraints.
   */
  if (!con->con_head) {
    if (pat->pat_type == GRAPHD_PATTERN_CONTENTS) {
      cl_cover(cl);
      graphd_request_error(greq,
                           "SEMANTICS can't use \"contents\" return "
                           "instruction in template without "
                           "contained templates");
      return GRAPHD_ERR_SEMANTICS;
    }
    if (pat->pat_type == GRAPHD_PATTERN_LIST) {
      graphd_pattern const* ric;

      for (ric = pat->pat_data.data_list.list_head; ric != NULL;
           ric = ric->pat_next) {
        int err;
        err = graphd_constraint_use_result_instruction(greq, con, ric);
        if (err) {
          cl_cover(cl);
          return err;
        }
      }
    }

    cl_cover(cl);
  }

  cl_cover(cl);
  return 0;
}

void graphd_constraint_append(graphd_constraint* parent,
                              graphd_constraint* child) {
  child->con_parent = parent;
  child->con_next = NULL;
  *parent->con_tail = child;
  parent->con_tail = &child->con_next;
  parent->con_subcon_n++;
}

pdb_id graphd_constraint_dateline_first(graphd_handle* g,
                                        graphd_constraint* con) {
  unsigned long long ull;
  graph_dateline const* dl;

  if ((dl = con->con_dateline.dateline_min) != NULL &&
      graph_dateline_get(dl, pdb_database_id(g->g_pdb), &ull) == 0)
    return ull;
  return 0;
}

bool graphd_constraint_dateline_too_young(graphd_handle* g,
                                          graphd_constraint* con, pdb_id id) {
  unsigned long long ull;
  graph_dateline const* dl;

  return (dl = con->con_dateline.dateline_max) != NULL &&
         graph_dateline_get(dl, pdb_database_id(g->g_pdb), &ull) == 0 &&
         id >= ull;
}

void graphd_constraint_free(graphd_request* greq, graphd_constraint* con) {
  graphd_constraint *sub, *sub_next;
  graphd_constraint_or *cor, *cor_next;
  graphd_handle* g = graphd_request_graphd(greq);

  if (con == NULL) return;

  if (con->con_unique_dup != NULL)
    graphd_constraint_free(greq, con->con_unique_dup);
  if (con->con_key_dup != NULL) graphd_constraint_free(greq, con->con_key_dup);
  if (con->con_anchor_dup != NULL)
    graphd_constraint_free(greq, con->con_anchor_dup);

  pdb_iterator_destroy(g->g_pdb, &con->con_it);

  /*  "Or" alternatives do not free their subconstraints;
   *  they're considered part of their non-alternative
   *  prototype.
   */
  if (con->con_or == NULL) {
    sub_next = con->con_head;
    while ((sub = sub_next) != NULL) {
      sub_next = sub->con_next;
      graphd_constraint_free(greq, sub);
    }
  }

  cor_next = con->con_or_head;
  while ((cor = cor_next) != NULL) {
    cor_next = cor->or_next;

    if (cor->or_tail != NULL) graphd_constraint_free(greq, cor->or_tail);
    graphd_constraint_free(greq, &cor->or_head);

    cm_free(greq->greq_req.req_cm, cor);
  }

  if (con->con_pframe != NULL) {
    cm_free(greq->greq_req.req_cm, con->con_pframe);
    con->con_pframe = NULL;
  }

  if (con->con_sort_comparators.gcl_comp != NULL) {
    cm_free(greq->greq_req.req_cm, con->con_sort_comparators.gcl_comp);
  }

  /*  Unless this constraint is embedded in the request
   *  or a containing "or" alternative branch, free it.
   */
  if (con != greq->greq_constraint_buf &&
      con != greq->greq_constraint_buf + 1 &&
      (con->con_or == NULL || con != &con->con_or->or_head))
    cm_free(greq->greq_req.req_cm, con);
}

/**
 * @brief Do constraints a and b match the same result set?
 *
 * @param cl	log and assert through here.
 * @param a	a constraint
 * @param b	another constraint
 *
 * @return true if they definitely match the same result set;
 *	false if they may or may not match the same result set.
 */
bool graphd_constraint_equal(cl_handle* const cl, graphd_constraint const* a,
                             graphd_constraint const* b) {
  int linkage;
  graphd_constraint_or const *a_or, *b_or;

  if (a == NULL || b == NULL) return a == NULL && b == NULL;

  cl_assert(cl, a != NULL);
  cl_assert(cl, b != NULL);

  if (a->con_subcon_n != b->con_subcon_n || a->con_linkage != b->con_linkage ||
      a->con_valuetype != b->con_valuetype ||
      a->con_archival != b->con_archival || a->con_live != b->con_live ||
      a->con_key != b->con_key || a->con_unique != b->con_unique ||
      a->con_resultpagesize_valid != b->con_resultpagesize_valid ||
      (a->con_resultpagesize_valid &&
       a->con_resultpagesize != b->con_resultpagesize) ||
      a->con_countlimit_valid != b->con_countlimit_valid ||
      (a->con_countlimit_valid && a->con_countlimit != b->con_countlimit) ||
      a->con_pagesize_valid != b->con_pagesize_valid ||
      (a->con_pagesize_valid && a->con_pagesize != b->con_pagesize) ||
      a->con_start != b->con_start)
    return false;

  /*  Parent links enter into the comparison only if they're
   *  a single GUID literal.
   */
  if ((a->con_parent != NULL && HAS_GUID(a->con_parent->con_guid))) {
    if (b->con_parent == NULL || !HAS_GUID(b->con_parent->con_guid) ||
        !graphd_guid_constraint_equal(cl, &a->con_parent->con_guid,
                                      &b->con_parent->con_guid))
      return false;
  } else {
    if (b->con_parent != NULL && HAS_GUID(b->con_parent->con_guid))
      return false;
  }

  if (!graphd_string_constraint_queue_equal(cl, &a->con_type, &b->con_type) ||
      !graphd_string_constraint_queue_equal(cl, &a->con_name, &b->con_name) ||
      !graphd_string_constraint_queue_equal(cl, &a->con_value, &b->con_value))
    return false;

  if (!graphd_guid_constraint_generational_equal(cl, &a->con_newest,
                                                 &b->con_newest) ||
      !graphd_guid_constraint_generational_equal(cl, &a->con_oldest,
                                                 &b->con_oldest))
    return false;

  if (!graphd_guid_constraint_equal(cl, &a->con_guid, &b->con_guid))
    return false;

  if (!graphd_guid_constraint_equal(cl, &a->con_version_next,
                                    &b->con_version_next) ||
      !graphd_guid_constraint_equal(cl, &a->con_version_previous,
                                    &b->con_version_previous))
    return false;

  for (linkage = 0; linkage < PDB_LINKAGE_N; linkage++)
    if (!graphd_guid_constraint_equal(cl, a->con_linkcon + linkage,
                                      b->con_linkcon + linkage))
      return false;

  if (a->con_timestamp_valid != b->con_timestamp_valid) return false;
  if (a->con_timestamp_valid) {
    if (a->con_timestamp_max != b->con_timestamp_max ||
        a->con_timestamp_min != b->con_timestamp_min)
      return false;
  }

  if (a->con_count.countcon_min_valid != b->con_count.countcon_min_valid ||
      a->con_count.countcon_max_valid != b->con_count.countcon_max_valid)
    return false;
  if (a->con_count.countcon_min_valid &&
      a->con_count.countcon_min != b->con_count.countcon_min)
    return false;
  if (a->con_count.countcon_max_valid &&
      a->con_count.countcon_max != b->con_count.countcon_max)
    return false;

  if (!graphd_pattern_equal(cl, a, a->con_result, b, b->con_result) ||
      !graphd_pattern_equal(cl, a, a->con_sort_valid ? a->con_sort : NULL, b,
                            b->con_sort_valid ? b->con_sort : NULL))
    return false;

  if (!graph_dateline_equal(a->con_dateline.dateline_min,
                            b->con_dateline.dateline_min) ||
      !graph_dateline_equal(a->con_dateline.dateline_max,
                            b->con_dateline.dateline_max))
    return false;

  if ((a->con_cursor_s == NULL) != (b->con_cursor_s == NULL)) return false;

  if (a->con_cursor_s != NULL &&
      memcmp(a->con_cursor_s, b->con_cursor_s,
             (size_t)(b->con_cursor_e - b->con_cursor_s)) != 0)
    return false;

  if (!graphd_assignments_equal(cl, a, a->con_assignment_head, b,
                                b->con_assignment_head))
    return false;

  for (a_or = a->con_or_head, b_or = b->con_or_head;
       a_or != NULL && b_or != NULL;
       a_or = a_or->or_next, b_or = b_or->or_next) {
    if (!graphd_constraint_equal(cl, &a_or->or_head, &b_or->or_head))
      return false;

    if (!graphd_constraint_equal(cl, a_or->or_tail, b_or->or_tail))
      return false;
  }

  for (a = a->con_head, b = b->con_head; a != NULL && b != NULL;
       a = a->con_next, b = b->con_next) {
    if (!graphd_constraint_equal(cl, a, b)) return false;
  }
  return a == NULL && b == NULL;
}

/**
 * @brief Hash a constraint.
 *
 *  Hashes of equal constraints are equal.  Hashes of unequal
 *  constraints may or may not be equal.
 *
 * @param cl		log and assert through here.
 * @param con		a constraint
 * @param hash_inout	hash accumulator.
 */
void graphd_constraint_hash(cl_handle* const cl, graphd_constraint const* con,
                            unsigned long* const hash_inout) {
  int linkage;

  cl_assert(cl, con != NULL);

  GRAPHD_HASH_VALUE(*hash_inout, con->con_subcon_n);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_linkage);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_valuetype);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_archival);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_live);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_key);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_unique);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_pagesize_valid);

  if (con->con_pagesize_valid)
    GRAPHD_HASH_VALUE(*hash_inout, con->con_pagesize);
  GRAPHD_HASH_VALUE(*hash_inout, con->con_start);

  graphd_string_constraint_hash(cl, &con->con_type, hash_inout);
  graphd_string_constraint_hash(cl, &con->con_name, hash_inout);
  graphd_string_constraint_hash(cl, &con->con_value, hash_inout);

  graphd_guid_constraint_generational_hash(cl, &con->con_newest, hash_inout);
  graphd_guid_constraint_generational_hash(cl, &con->con_oldest, hash_inout);

  /*  Parent links enter into the comparison only if they're
   *  a single GUID literal.
   */
  if ((con->con_parent != NULL && HAS_GUID(con->con_parent->con_guid)))
    graphd_guid_constraint_hash(cl, &con->con_parent->con_guid, hash_inout);

  graphd_guid_constraint_hash(cl, &con->con_guid, hash_inout);
  graphd_guid_constraint_hash(cl, &con->con_version_next, hash_inout);
  graphd_guid_constraint_hash(cl, &con->con_version_previous, hash_inout);

  for (linkage = 0; linkage < PDB_LINKAGE_N; linkage++)
    graphd_guid_constraint_hash(cl, con->con_linkcon + linkage, hash_inout);

  GRAPHD_HASH_BIT(*hash_inout, con->con_timestamp_valid);
  if (con->con_timestamp_valid) {
    GRAPHD_HASH_VALUE(*hash_inout, con->con_timestamp_max);
    GRAPHD_HASH_VALUE(*hash_inout, con->con_timestamp_min);
  }

  GRAPHD_HASH_BIT(*hash_inout, con->con_count.countcon_min_valid);
  GRAPHD_HASH_BIT(*hash_inout, con->con_count.countcon_max_valid);

  if (con->con_count.countcon_min_valid)
    GRAPHD_HASH_VALUE(*hash_inout, con->con_count.countcon_min);

  if (con->con_count.countcon_max_valid)
    GRAPHD_HASH_VALUE(*hash_inout, con->con_count.countcon_max);

  graphd_pattern_hash(cl, con->con_result, hash_inout);

  /* Include con_sort in the hash, even if it has been declared
   * invalid by the optimizer.
   */
  graphd_pattern_hash(cl, con->con_sort, hash_inout);

  graphd_dateline_constraint_hash(cl, &con->con_dateline, hash_inout);

  if (con->con_cursor_s != NULL)
    GRAPHD_HASH_BYTES(*hash_inout, con->con_cursor_s, con->con_cursor_e);

  graphd_assignments_hash(cl, con->con_assignment_head, hash_inout);

  for (con = con->con_head; con != NULL; con = con->con_next)
    graphd_constraint_hash(cl, con, hash_inout);
}

/**
 * @brief Does this constraint use the "count" pattern?
 */
bool graphd_constraint_uses_pattern(graphd_constraint const* con, int pat) {
  graphd_assignment const* a;

  if (con->con_result != NULL && graphd_pattern_lookup(con->con_result, pat))
    return true;

  if (con->con_sort != NULL && con->con_sort_valid &&
      graphd_pattern_lookup(con->con_sort, pat))
    return true;

  for (a = con->con_assignment_head; a != NULL; a = a->a_next)
    if (a->a_result != NULL && graphd_pattern_lookup(a->a_result, pat))
      return true;

  return false;
}

/**
 * @brief Does this constraint use the "contents" pattern?
 *
 *  This is valid only after NULL result constraints have been
 *  filled in with defaults.
 *
 */
bool graphd_constraint_uses_contents(graphd_constraint const* con) {
  return graphd_constraint_uses_pattern(con, GRAPHD_PATTERN_CONTENTS);
}

/* Pull branch invariants from the branches into their prototype.
 */
static int graphd_constraint_branch_invariants(graphd_request* greq,
                                               graphd_constraint* con) {
  graphd_constraint* proto = NULL;
  graphd_constraint_or* cor;
  int err;

  if (con->con_or != NULL) proto = con->con_or->or_prototype;

  for (cor = con->con_or_head; cor != NULL; cor = cor->or_next) {
    err = graphd_constraint_branch_invariants(greq, &cor->or_head);
    if (err != 0) return err;

    if (cor->or_tail) {
      err = graphd_constraint_branch_invariants(greq, cor->or_tail);
      if (err != 0) return err;
    }

    /*  If both sides of the "or" have something in common,
     *  that thing is true for the combined constraint, too.
     */
    if (cor->or_tail != NULL &&
        cor->or_head.con_linkage == cor->or_tail->con_linkage &&
        cor->or_head.con_linkage != 0) {
      con->con_linkage = cor->or_head.con_linkage;

      /* etc. */
    }
  }
  return 0;
}

/**
 * @brief Fill in defaults of a constraint from its prototype.
 */
static int graphd_constraint_branch_defaults(graphd_request* greq,
                                             graphd_constraint* proto,
                                             graphd_constraint* con) {
  graphd_constraint_or* cor;

  if (proto == NULL) return 0;

  /*  Generational constraints
   *
   *  If neither oldest nor newest have been set, the
   *  constraint defaults to newest=0
   */
  if (!con->con_newest.gencon_assigned && !con->con_oldest.gencon_assigned) {
    con->con_newest = proto->con_newest;
    con->con_oldest = proto->con_oldest;
  }

  /*  Dateline constraints
   */
  if (con->con_dateline.dateline_min == NULL &&
      con->con_dateline.dateline_max == NULL) {
    con->con_dateline.dateline_min = proto->con_dateline.dateline_min;
    con->con_dateline.dateline_max = proto->con_dateline.dateline_max;
  }

  con->con_countlimit_valid = proto->con_countlimit_valid;
  con->con_countlimit = proto->con_countlimit;

  con->con_resultpagesize = proto->con_resultpagesize;
  con->con_resultpagesize_valid = proto->con_resultpagesize_valid;

  con->con_resultpagesize_parsed = proto->con_resultpagesize_parsed;
  con->con_resultpagesize_parsed_valid = proto->con_resultpagesize_parsed_valid;

  if (proto->con_linkage != 0) con->con_linkage = proto->con_linkage;

  /*  Fill in the defaults of our subbranches from ourselves.
   */
  for (cor = con->con_or_head; cor != NULL; cor = cor->or_next) {
    int err = graphd_constraint_branch_defaults(greq, con, &cor->or_head);
    if (err != 0) return err;

    if (cor->or_tail != NULL) {
      err = graphd_constraint_branch_defaults(greq, con, &cor->or_head);
      if (err != 0) return err;
    }
  }
  return 0;
}

/**
 * @brief Fill in defaults of a constraint.
 *
 *  XXX finish and call..
 */
int graphd_constraint_defaults(graphd_request* greq, graphd_constraint* con) {
  /*  Promote common aspects of branches up into the
   *  prototype.
   */
  graphd_constraint_branch_invariants(greq, con);

  /*  If this is a branch, inherit from the prototype.
   */
  if (con->con_or != NULL) {
    /*  Inherit from our prototype.
     */
    int err =
        graphd_constraint_branch_defaults(greq, con->con_or->or_prototype, con);
    if (err != 0) return err;
  }

  /* Actual defalting starts here.
   */

  /* Generational constraints
   *
   *  If neither oldest nor newest have been set, the
   *  constraint defaults to newest=0
   */
  if (!con->con_newest.gencon_assigned && !con->con_oldest.gencon_assigned) {
    /*  Default to newest=0
     */
    con->con_newest.gencon_valid = true;
    con->con_newest.gencon_min = con->con_newest.gencon_max = 0;
    con->con_oldest.gencon_valid = false;
  }
  return 0;
}

static graphd_constraint* graphd_constraint_by_id_recursive(
    graphd_constraint* par, size_t id) {
  graphd_constraint* con;

  if (par->con_id == id) return par;

  for (par = par->con_head; par != NULL; par = par->con_next) {
    con = graphd_constraint_by_id_recursive(par, id);
    if (con != NULL) return con;
  }
  return NULL;
}

graphd_constraint* graphd_constraint_by_id(graphd_request const* greq,
                                           size_t id) {
  return graphd_constraint_by_id_recursive(greq->greq_constraint, id);
}

int graphd_constraint_get_heatmap(graphd_request const* greq,
                                  graphd_constraint* con, cm_buffer* buf) {
  int err;

  if (con == NULL) return 0;

  err =
      cm_buffer_sprintf(buf, "(nn=%llu nc=%lld cn=%llu cc=%lld fn=%llu fc=%lld",
                        con->con_iterator_account.ia_next_n,
                        (long long)con->con_iterator_account.ia_next_cost,
                        con->con_iterator_account.ia_check_n,
                        (long long)con->con_iterator_account.ia_check_cost,
                        con->con_iterator_account.ia_find_n,
                        (long long)con->con_iterator_account.ia_find_cost);
  if (err != 0) return err;

  if (con->con_head) {
    for (con = con->con_head; con != NULL; con = con->con_next) {
      err = cm_buffer_add_string(buf, " ");
      if (err != 0) return err;

      err = graphd_constraint_get_heatmap(greq, con, buf);
      if (err != 0) return err;
    }
  }
  err = cm_buffer_add_string(buf, ")");
  if (err != 0) return err;

  return 0;
}


text:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#b71c1c</color>
    <color name="colorPrimaryDark">#b71c1c</color>
    <color name="colorAccent">#c54949</color>

    <color name="gray_light">#e0e0e0</color>
    <color name="white">#ffffff</color>

    <color name="translucent_scrim_top">#26000000</color>
    <color name="translucent_scrim_top_center">#0C000000</color>
    <color name="translucent_scrim_bottom">#2A000000</color>
    <color name="translucent_scrim_bottom_center">#0D000000</color>

</resources>


text:
package com.hiliaox;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import  android.media.ThumbnailUtils;

import com.sina.weibo.sdk.api.ImageObject;
import com.sina.weibo.sdk.api.TextObject;
import com.sina.weibo.sdk.api.VideoObject;
import com.sina.weibo.sdk.api.WebpageObject;
import com.sina.weibo.sdk.api.WeiboMultiMessage;
import com.sina.weibo.sdk.api.share.IWeiboShareAPI;
import com.sina.weibo.sdk.api.share.SendMultiMessageToWeiboRequest;
import com.sina.weibo.sdk.api.share.WeiboShareSDK;

import com.sina.weibo.sdk.auth.AuthInfo;
import com.sina.weibo.sdk.auth.Oauth2AccessToken;
import com.sina.weibo.sdk.auth.WeiboAuthListener;
import com.sina.weibo.sdk.auth.sso.SsoHandler;
import com.sina.weibo.sdk.exception.WeiboException;

import com.sina.weibo.sdk.utils.Utility;

public class Weibo extends CordovaPlugin {

    private static final int WEIBO_NOT_INSTALLED = 1;
    private static final int WEIBO_AUTHORIZE_CANCELED = 2;
    private static final int WEIBO_INVALID_TOKEN = 3;
    private static final int WEIBO_UNKNOWN_ERROR = 99;

    private static final String TAG = "Cordova-Weibo-SSO";

    private String appKey;

    private SsoHandler mSsoHandler = null;

    private IWeiboShareAPI mWeiboShareAPI = null;

    @Override
    public boolean execute(String action, final JSONArray args,
            final CallbackContext context) throws JSONException {
        boolean result = false;
        Activity activity = this.cordova.getActivity();
        try {
            if (action.equals("init")) {
                this.init(args, context);
                result = true;
            } else if (action.equals("login")) {
                this.login(context);
                result = true;
            } else if (action.equals("isInstalled")) {
                this.checkWeibo(context);
                result = true;
            } else if (action.equals("share")) {
                final Weibo me = this;
                cordova.getThreadPool().execute(new Runnable() {
                    public void run() {
                        try {

                            JSONObject cfg = args.getJSONObject(0);
                            if (cfg.getString("type").equals("text")) {
                                me.sendText(cfg.getString("text"),context);
                                context.success(1);
                            } else if (cfg.getString("type").equals("image")) {
                                me.sendImage(cfg.getString("data"),
                                        cfg.getString("text"),context);
                                context.success(1);
                            } else if (cfg.getString("type").equals("webpage")){
                                me.sendPage(cfg.getString("page_url"),
                                        cfg.getString("thumb_url"),
                                        cfg.getString("title"),
                                        cfg.getString("desc"),
                                        cfg.getString("text"),
                                        cfg.getString("image_url"));
                                context.success(1);
                            }
                        } catch (MalformedURLException e) {
                            context.error("JSON Exception:MalformedURLException:"+e.toString());
                            e.printStackTrace();
                        } catch (IOException e) {
                            context.error("JSON Exception:IOException:"+e.toString());
                            e.printStackTrace();
                        } catch (JSONException e) {
                            context.error("JSON Exception:JSONException:"+e.toString());
                            e.printStackTrace();
                        }
                    }
                });
                result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            context.error(new ErrorMessage(10000,e.getMessage()));
            result = false;
        }
        return result;
    }

    private void checkWeibo(final CallbackContext context) {
        context.success(this.isWeiboInstalled() ? 1 : 0);
    }

    private boolean isWeiboInstalled() {
        return mSsoHandler != null && mSsoHandler.isWeiboAppInstalled();
    }

    public void init(JSONArray json, final CallbackContext context) {
        appKey = getData(json, "appKey");
        String redirectURI = getData(json, "redirectURI");
        String scope = "all";

        Activity activity = this.cordova.getActivity();

        AuthInfo authInfo = new AuthInfo(activity, appKey, redirectURI, scope);

        this.mSsoHandler = new SsoHandler(activity, authInfo);
        context.success(1);
    }

    public void login(final CallbackContext context) {
        Activity activity = this.cordova.getActivity();
        this.cordova.setActivityResultCallback(this);
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mSsoHandler.authorize(new AuthListener(context));
            }
        });
    }

    public void sendText(String text,final CallbackContext context) throws MalformedURLException, IOException {

        mWeiboShareAPI = WeiboShareSDK.createWeiboAPI(
                this.cordova.getActivity(), appKey);
        mWeiboShareAPI.registerApp();
        WeiboMultiMessage weiboMessage = new WeiboMultiMessage();
        if (text != null) {
            TextObject textObject = new TextObject();
            textObject.text = text;
            weiboMessage.textObject = textObject;
        }
        SendMultiMessageToWeiboRequest request = new SendMultiMessageToWeiboRequest();
        request.transaction = String.valueOf(System.currentTimeMillis());
        request.multiMessage = weiboMessage;

        try {
            mWeiboShareAPI.sendRequest(this.cordova.getActivity(), request);
        } catch (Exception e) {
            context.error("this is the end");
        }

    }

    // me.sendPage(cfg.getString("page_url"),
    //             cfg.getString("thumb_url"),
    //             cfg.getString("title"),
    //             cfg.getString("desc"),
    //             cfg.getString("text"),
    //             cfg.getString("image_url"));
    public void sendPage(String url, String thumb_url,String title,String desc,String text, String image_url)
            throws MalformedURLException, IOException{

        mWeiboShareAPI = WeiboShareSDK.createWeiboAPI(
                this.cordova.getActivity(), appKey);

        mWeiboShareAPI.registerApp();
        WeiboMultiMessage weiboMessage = new WeiboMultiMessage();

        //分享的网页
        WebpageObject mediaObject = new WebpageObject();
        mediaObject.identify = Utility.generateGUID();

        if(title != null){
            mediaObject.title = title;
        }else{

        }

        if(desc != null){
            mediaObject.description = desc;
        }else{

        }

        mediaObject.actionUrl = url;
        final int THUMBSIZE = 64;

        //网页缩略图
        if(thumb_url != null){
            if (!thumb_url.startsWith("data")) {
                Bitmap bmp = null;
                bmp = BitmapFactory.decodeStream(new URL(thumb_url)
                        .openStream());

                Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(bmp,
                            THUMBSIZE, THUMBSIZE);
                bmp.recycle();
                // 设置 Bitmap 类型的图片到对象里
                mediaObject.setThumbImage(ThumbImage);
            }else if(thumb_url.startsWith("data")){
                String dataUrl = image_url;
                String encodingPrefix = "base64,";
                int contentStartIndex = dataUrl.indexOf(encodingPrefix)
                        + encodingPrefix.length();
                String resData = dataUrl.substring(contentStartIndex);

                byte[] bytes = null;
                try {
                    bytes = Base64.decode(resData, 0);
                } catch (Exception ignored) {
                    Log.e("Weibo", "Invalid Base64 string");
                }

                Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(bmp,
                            THUMBSIZE, THUMBSIZE);
                bmp.recycle();
                // 设置 Bitmap 类型的图片到对象里
                mediaObject.setThumbImage(ThumbImage);
            }
        }

        mediaObject.defaultText = "网页分享默认文字";
        weiboMessage.mediaObject = mediaObject;

        //额外分享的图片
        if (image_url != null) {
            ImageObject imageObject = new ImageObject();
            if (image_url.startsWith("data")) {
                String dataUrl = image_url;
                String encodingPrefix = "base64,";
                int contentStartIndex = dataUrl.indexOf(encodingPrefix)
                        + encodingPrefix.length();
                String resData = dataUrl.substring(contentStartIndex);

                byte[] bytes = null;
                try {
                    bytes = Base64.decode(resData, 0);
                } catch (Exception ignored) {
                    Log.e("Weibo", "Invalid Base64 string");
                }
                imageObject.imageData = bytes;
            } else {
                Bitmap bmp = null;
                bmp = BitmapFactory.decodeStream(new URL(image_url)
                        .openStream());
                imageObject.setImageObject(bmp);
            }
            weiboMessage.imageObject = imageObject;
        }

        //分享的文字
        if (text != null && text.length() > 0) {
            TextObject textObject = new TextObject();
            textObject.text = text;
            weiboMessage.textObject = textObject;
        }

        SendMultiMessageToWeiboRequest request = new SendMultiMessageToWeiboRequest();
        request.transaction = String.valueOf(System.currentTimeMillis());
        request.multiMessage = weiboMessage;
        mWeiboShareAPI.sendRequest(this.cordova.getActivity(), request);
    }

    public void sendImage(String data, String text,final CallbackContext context)
            throws MalformedURLException, IOException {

        String image_path = data;
        mWeiboShareAPI = WeiboShareSDK.createWeiboAPI(
                this.cordova.getActivity(), appKey);
        mWeiboShareAPI.registerApp();
        WeiboMultiMessage weiboMessage = new WeiboMultiMessage();
        if (text != null) {
            TextObject textObject = new TextObject();
            textObject.text = text;
            weiboMessage.textObject = textObject;
        }
        if (image_path != null) {
            ImageObject imageObject = new ImageObject();
            if (image_path.startsWith("data")) {
                String dataUrl = image_path;
                String encodingPrefix = "base64,";
                int contentStartIndex = dataUrl.indexOf(encodingPrefix)
                        + encodingPrefix.length();
                String resData = dataUrl.substring(contentStartIndex);

                byte[] bytes = null;
                try {
                    bytes = Base64.decode(resData, 0);
                } catch (Exception ignored) {
                    Log.e("Weibo", "Invalid Base64 string");
                }
                imageObject.imageData = bytes;
            } else if (image_path.startsWith("http://")) {
                Bitmap bmp = null;
                bmp = BitmapFactory.decodeStream(new URL(image_path)
                        .openStream());
                imageObject.setImageObject(bmp);
            } else {
                imageObject.imagePath = image_path;
            }
            weiboMessage.imageObject = imageObject;
        }
        SendMultiMessageToWeiboRequest request = new SendMultiMessageToWeiboRequest();
        request.transaction = String.valueOf(System.currentTimeMillis());
        request.multiMessage = weiboMessage;
        mWeiboShareAPI.sendRequest(this.cordova.getActivity(), request);
    }

    public static JSONObject getObjectFromArray(JSONArray jsonArray,
            int objectIndex) {
        JSONObject jsonObject = null;
        if (jsonArray != null && jsonArray.length() > 0) {
            try {
                jsonObject = new JSONObject(jsonArray.get(objectIndex)
                        .toString());
            } catch (JSONException e) {

            }
        }
        return jsonObject;
    }

    public static String getData(JSONArray ary, String key) {
        String result = null;
        try {
            result = getObjectFromArray(ary, 0).getString(key);
        } catch (JSONException e) {

        }
        return result;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (mSsoHandler != null) {
            mSsoHandler.authorizeCallBack(requestCode, resultCode, data);
        }
    }

    class ErrorMessage extends JSONObject {
        public ErrorMessage(int code, String message) {
            try {
                this.put("code", code);
                this.put("message", message);
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
    }

    class AuthListener implements WeiboAuthListener {
        private CallbackContext context;

        public AuthListener(CallbackContext context) {
            this.context = context;
        }

        @Override
        public void onComplete(Bundle values) {
            Oauth2AccessToken accessToken = Oauth2AccessToken.parseAccessToken(values);
            if (accessToken.isSessionValid()) {
                JSONObject res = new JSONObject();
                try {
                    res.put("uid", accessToken.getUid());
                    res.put("token", accessToken.getToken());
                    res.put("expire_at", accessToken.getExpiresTime());
                    res.put("refresh_token",accessToken.getRefreshToken());
                    context.success(res);
                } catch (JSONException e) {
                    throw new RuntimeException(e);
                }
            } else {
                String code = values.getString("code");
                context.error(new ErrorMessage(WEIBO_INVALID_TOKEN, code));
            }
        }

        @Override
        public void onWeiboException(WeiboException e) {
            String message = e.getMessage();
            context.error(new ErrorMessage(WEIBO_UNKNOWN_ERROR, message));
            Log.e(TAG, message, e);
        }

        @Override
        public void onCancel() {
            String message = "authorize cancelled";
            context.error(new ErrorMessage(WEIBO_AUTHORIZE_CANCELED, message));
            Log.i(TAG, message);
        }
    }
}


text:
#ifndef CHECKSUMHASHGENERATOR_H
#define CHECKSUMHASHGENERATOR_H

#include "pandabase.h"

#include "hashGeneratorBase.h"

/**
 * This is a specific kind of HashGenerator that simply adds up all of the
 * ints.  Nothing fancy, and pretty quick.
 */
class EXPCL_PANDA_EXPRESS ChecksumHashGenerator : public HashGeneratorBase {
public:
  INLINE void add_int(long num);
  INLINE void add_bool(bool flag);
  INLINE void add_fp(float num, float threshold);
  INLINE void add_fp(double num, double threshold);
  INLINE void add_pointer(void *ptr);
  void add_string(const std::string &str);
};

#include "checksumHashGenerator.I"

#endif


text:
"""init

Revision ID: 1545cca4986c
Revises: None
Create Date: 2014-09-20 10:55:07.035626

"""

# revision identifiers, used by Alembic.
revision = '1545cca4986c'
down_revision = None

from alembic import op
import sqlalchemy as sa

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('work_type',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=20), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('museum',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.Column('address', sa.String(length=200), nullable=True),
    sa.Column('image', sa.String(length=200), nullable=True),
    sa.Column('desc', sa.Text(), nullable=True),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('dynasty',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.Column('abbr', sa.String(length=50), nullable=True),
    sa.Column('desc', sa.Text(), nullable=True),
    sa.Column('start_year', sa.Integer(), nullable=True),
    sa.Column('end_year', sa.Integer(), nullable=True),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('abbr')
    )
    op.create_table('artist',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.Column('avatar', sa.String(length=200), nullable=True),
    sa.Column('desc', sa.Text(), nullable=True),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('dynasty_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['dynasty_id'], ['dynasty.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('work',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('title', sa.String(length=50), nullable=True),
    sa.Column('desc', sa.Text(), nullable=True),
    sa.Column('annotation', sa.Text(), nullable=True),
    sa.Column('create_time', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.Column('work_type_id', sa.Integer(), nullable=True),
    sa.Column('artist_id', sa.Integer(), nullable=True),
    sa.Column('museum_id', sa.Integer(), nullable=True),
    sa.Column('dynasty_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['artist_id'], ['artist.id'], ),
    sa.ForeignKeyConstraint(['dynasty_id'], ['dynasty.id'], ),
    sa.ForeignKeyConstraint(['museum_id'], ['museum.id'], ),
    sa.ForeignKeyConstraint(['work_type_id'], ['work_type.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('work_image',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('image', sa.String(length=200), nullable=True),
    sa.Column('create_time', sa.DateTime(), nullable=True),
    sa.Column('order', sa.Integer(), nullable=True),
    sa.Column('work_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['work_id'], ['work.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('work_image')
    op.drop_table('work')
    op.drop_table('artist')
    op.drop_table('dynasty')
    op.drop_table('museum')
    op.drop_table('work_type')
    ### end Alembic commands ###