File: sitesearch.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<!--GENHTML INSERT ^^ ================================================================-->
<!--
12/15: Add a doctype spec on line 1 so IE does fixed footer positioning at bottom.
But use HTML 4 (not 5) spec, so images in tables look as they did with no doctype.
Makes some things render diff (e.g, tables at top, and having hdrs), but no worse.
More details: https://en.wikipedia.org/wiki/Quirks_mode#Comparison_of_document_types.
-->
<!--END INSERT========================================================================-->


<HTML>

<HEAD>

<!--GENHTML INSERT====================================================================-->

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<!--
For this file only: UTF-8 Unicode encoding, for non-ASCII or non-English characters.
If omitted, browsers may guess, but often fall back on US-ASCII or user settings.
Update Dec-2018: move the <meta> encoding declaration above this comment, and 
the _META_ genhtml tag to the top of <head> if needed, so <meta> is in the first 
1024 bytes of the file per HTML5 standard.  Nonconforming pages work fine in all 
20+ browsers tested, though Firefox issues a warning in its web console, and some
may restart the page parse - a minor performance penalty.  Not an issue in thumbspage 
output, app user guides, showcode pages, or unzipped programs' Apache index pages. 
-->
<!--END INSERT========================================================================-->


<!--GENHTML INSERT====================================================================-->
<!--
Recoded Aug-18 to use relative path, after broken by https:// conversion: can't mix 
http:// page resources (see .htaccess note).  This required regenerating and uploading 
all .html files, but not hundreds of manual page edits - genhtml works!  Formerly 
used ICON-DEFAULT, which has now-dated docs on icon links and filetypes in general.
An icon file in the root folder suffices on some (but not all) web servers.
-->

<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />

<!--END INSERT========================================================================-->


<!--GENHTML INSERT====================================================================-->
<!--this insert serves two purposes: stylesheet and mobile viewport-->

<!--
Nov-15: CSS file, code in <HEAD>, for fonts, borders, navbars, code, etc.
This could also use an in-page <STYLE> sheet, as genhtml pastes any text.
-->

<link rel="stylesheet" type="text/css" href="_main.css">

<!--
Feb-18: Add a mobile-friendly viewport tag to every page's HEAD.
Fits content to device width, no zoom (user may), impacts MANY formats.
Extend STYLE instead of adding a new insert to avoid all-page updates.
Mobile support required other redesign: toolbar, code blocks, images,... 
-->
 
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<!--END INSERT========================================================================-->


<!--GENHTML INSERT====================================================================-->
<!--
7/6/14: Google Analytics JavaScript (JS) code in <HEAD> (normally).
Used only to isolate high-traffic pages for dev work, iff JS and tracker enabled. 
The former training sites's id: ga('create', 'UA-52578333-1', 'auto').
The book site's id, now used for all: ga('create', 'UA-52579036-1', 'auto').
JavaScript is also used if enabled for thumbspage dynamic image scaling.
Jun-2019: tell Google Analytics to anonymize IP address on receipt for privacy.
More: https://developers.google.com/analytics/devguides/collection/analyticsjs/.
-->

<SCRIPT>
  // Start async JS-file fetch, if not already cached

  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  // Queue actions to run in order after async JS-file fetch finished

  ga('create', 'UA-52579036-1', 'auto');       // Create tracker object (and queue)
  ga('set', 'anonymizeIp', true);              // Anonymize IP addr (&aip) [Jun-2019]
  ga('send', 'pageview');                      // Send page-view event now 
</SCRIPT>

<!--END INSERT========================================================================-->


<!--GENHTML INSERT====================================================================-->
<!--Style a floating to-index button, used iff JS enabled (else prior static buttons) -->
<!--This also requires an insert in the body to make button: see the other insert file-->
<!--Added Nov19; could be in main.css, but it's used by only a handful of pages today -->
<!--See also strings30.html, which copies the styling here for a more custom use case -->
<!--Also copied by some pages outside genhtml's scope (e.g., Android pages, app docs) -->
<!--==================================================================================-->

<!--
To install a floating Top button:
1) Add <noscript>...</noscript> around any static Tops
2) Add <A name="TOC">Contents</A>
3) Add <head> insert (this file),  via genhtml tag or manual
4) Add <body> insert (other file), via genhtml tag or manual
	(AFTER top banner or del initial <P>, else Firefox alone increases top spacing)
5) Add <class=finalparagraph> at end, so space for Top without covering last content
-->

<style>

#tocBtn {
    display: none;             /* initially hidden (change on scroll) */
    position: fixed;           /* float/persist */
    bottom: 36px;              /* above site toolbar */
    right: 8px;                /* left of scroll area */
    z-index: 99;               /* covering priority */
    font-size: 15px;
    border: none;
    outline: none;
    cursor: pointer;           /* change on mouse-over */
    padding: 10px;
    border-radius: 4px;        /* rounded corners */
    color: white;              /* foreground (text color) */
    background-color: #999;    /* which is #999999, which is rgb(153, 153, 153) */ 
}

/*  
Transparency? - PASS: obscures button's text always, and obscures page text whenever
visible below button; better to fully obscure and let user scroll to read normally.
    opacity: 0.6;                         ---no: less portable (IE) & blurs button text
    background: rgba(153, 153, 153, 0.6); ---punt: obscures both button & text below it 
*/

.finalparagraph {           /* extra space above Top bottom at end of page */
    margin-bottom: 50px;    /* else text at end may be overlayed and unviewable */
}

/* 
-------------------------------------------------------------------------------------------
THE TALE OF THE FAILED HOVER SHADING...
Shade on hover, but try to disable for touch/mobile, else recessed color may be stuck "on"
until a tap elsewhere on the page.  Mobiles fire "hover" on taps to support desktop widgets
(e.g., menus), but don't seem to clear it.  This is a hack that causes problems, and there
seems no good solution for all devices (e.g., devices with both touch+mouse), and dropping
":hover" altogether penalizes other devices unfairly.   Alternatives: fix one of the failed 
attempts below, don't use hover events, or use untested ad-hoc context-sniffing code like:
[var isTouch =  !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;].

PUNT: none of the following attempts works universally, this is just plain busted in CSS
and JS/DOM, and life is too short to haggle further; don't shade on hover anywhere for now.
Really, hover on mobile works only for widgets that naturally deactivate with another tap;
this may be the case for pull-down menus, but doesn't generally apply to widget shading.
For the floating button: as is, the cursor changes on mouse-over for desktop, most mobiles
blink on tap, and the button appears dynamically on scroll down - these seem sufficient;

POSTSCRIPT: in addition to stuck buttons on mobiles in general, ":hover" changes can 
require two taps to trigger events on iOS Safari.  In short, on a control tap, that 
platform/browser combo trigger "hover" then "click", but does not trigger the "click"
if the "hover" handler changes content in any way (e.g., colors and italics).  This
is supposed to allow hover emulation on iOS mobiles, but in reality breaks many sites
that use ":hover" when used on iOS (or at least requires bogus double taps).  The only 
universal workaround seems to be to avoid ":hover" altogether, at the expense of 
functionality on desktops and other hoverable devices.  A truly rude hack, Apple!
This is bad enough that ignoring iOS Safari seems a perfectly reasonable response
(and this site's traffic today is 75%+ desktop, ~18% Android, and ~2%-3% iOS Safari).
TBD: using mouseover "cursor: pointer" may suffice to require double taps on iOS too?

Refs:
https://css-tricks.com/annoying-mobile-double-tap-link-issue/
https://stackoverflow.com/questions/2741816/is-it-possible-to-force-ignore-the-hover-pseudoclass-for-iphone-ipad-users
https://stackoverflow.com/questions/17710893/why-when-do-i-have-to-tap-twice-to-trigger-click-on-ios
https://stackoverflow.com/questions/3038898/ipad-iphone-hover-problem-causes-the-user-to-double-click-a-link
etc: try a search on something like "ios hover two taps" or "ios hover problem"
-------------------------------------------------------------------------------------------
*/

/* Failed attempts follow */

/* 
-------------------------------------------------------------------------------------------
1) Disable hover coloring on small screens by size, and assume mobiles return a small size.
This is a horrible heuristic that addresses the horrible lack of support in CSS and JS DOMs 
for hovers on mobiles.  It's also used by the main site-wide toolbar to prevent hover 
italics being stuck on mobiles, but the effect is less grievous there: a tap on mobile opens
a new page which clears the italics, and mobiles that can hover clear the italics properly. 
As coded, this solution allows hover styles to be applied in landscape mode on some mobiles 
too (e.g., galaxy note 10+ with a stylus), though they may still appear 'stuck' if a stylus 
is lifted off-screen just after a click.  BUT: buttons are still stuck on for mobiles in 
landscape mode that don't have a stylus and don't hover (e.g., galaxy s8+)...

#tocBtn:hover {
    background-color: #444;
}

@media screen and (max-device-width: 640px) {
#tocBtn:hover {
    background-color: #999;
}
}
-------------------------------------------------------------------------------------------
*/

/*
-------------------------------------------------------------------------------------------
2) Turn on hover coloring iff host device supports hover and pointer device.  This would be 
a better solution and _almost_ suffices, EXCEPT that some device/browser combos claim to 
support hover falsely (e.g., touch-only galaxy s8+/firefox), and some devices with both
touch and pointer say they don't hover but could (e.g., galaxy note 10+ with stylus)...

@media(hover: hover) and (pointer: fine) {
#tocBtn:hover {
    background-color: #444;
}
}
-------------------------------------------------------------------------------------------
*/

/*
-------------------------------------------------------------------------------------------
3) This, and a plethora of variations on it, failed altogether for reasons unknown...

@media (hover: hover) {
#tocBtn:hover {
    background-color: #444;
}
}

@media (hover: none) {
#tocBtn:hover {
    background-color: #999;
}
}
-------------------------------------------------------------------------------------------
*/

/* 
-------------------------------------------------------------------------------------------
4) This doesn't work at all either; mobile hover seem a web-development black hole...

#tocBtn:hover {
    background-color: #444;
}

#tocBtn:active {
    background-color: #999;
}
-------------------------------------------------------------------------------------------
*/

</style>
<!--END INSERT========================================================================-->



<STYLE>

/* sep18: or use "max-width: N%;" for input field - see pylotto.html (but not 95%) */

.inputsize {                              /* 451+: default large (last setting wins) */
    width: 250px;
    border-width: thin;                   /* sep18: or Android Chrome omits at high res */ 
    /* background-color: green; */        /* to test media-query 'clicks' */
}

@media screen and (max-width: 450px) {    /* 351..450: medium input field for mobile */
.inputsize {
    width: 155px;
    /* background-color: blue; */
}
}

@media screen and (max-width: 350px) {    /* 0..350: small input field for mobile */
.inputsize {
    width: 125px;
    /* background-color: red; */
}
}

LI {
    margin-bottom: 8px;    /* add space between, else hard to read+tap (all LI) */
}

A.emph {
    color: black;
}
SPAN.emph {
    background-color: white;     /* wheat? cornsilk? */
    font-weight: bold;           /* bold? normal? */
}

DT {
    font-weight: bold;           /* nov20: couldn't pull this off without a <dl> */
    margin-top: 1em;
}

DD {
    margin-top: 8px;
}
</STYLE>


<TITLE>Search learning-python.com</TITLE>

</HEAD>

<A name=TOC>
<BODY>
</A>

<!--GENHTML INSERT====================================================================-->
<!--Build+configure a floating to-index button, used iff JS enabled (else old buttons)-->
<!--If JS is not enabled, a <noscript> insert adds static buttons at each part instead-->
<!--Added Nov19; tbd: is this more distracting than prior static buttons at each part?-->
<!--See also strings30.html, which extends some logic here for a more custom use case -->
<!--Also copied by some pages outside genhtml's scope (e.g., Android pages, app docs) -->
<!--See the corresponding <head> insert file for instructions on installing this code -->
<!--==================================================================================-->

<!-- Button: "display: none" in CSS makes invisible initially, JS or not -->
<!-- this originally used inline onclick="window.location.href = '#TOC';"-->

<button onclick="tocClick();" 
        id="tocBtn" 
        title="Go to index">Top</button>

<!-- Set up click/scroll callbacks iff JS is enabled -->
<script>

// Get and save the button in global (window) scope
var tocButton = document.getElementById("tocBtn");    // i.e., "Top" goes to toc

// Set the default scroll constant: global so pages can change if needed
var tocButtonShowAt = 500;    // px from top, show Top below this cutoff

function tocClick() {
    //
    // Go to the toc, clear hover shading? (else recessed color gets stuck on mobile).
    // Unhover doesn't work as tried here: see the CSS code in <head> for more details.
    //
    window.location.href = '#TOC';                 // go/scroll to toc tag
    //tocButton.style.backgroundColor = '#999';    // no: unhover for mobile?
}

function scrollFunction() {
    //
    // Adapted from a w3 example; the odd || test is for browser interoperability:
    // see, for instance, https://dev.opera.com/articles/fixing-the-scrolltop-bug/.
    //
    // Clients should reset global tocButtonShowAt for larger preambles; else the 
    // Top button may appear above/before the actual #TOC content, especially 
    // on mobile.  See python-activities-history.html for an example.  Optional 
    // arguments with anonymous functions may work here too, but seem overkill.
    //
    var showat = tocButtonShowAt;  // local
    // N pixels from the top after scroll?
    if (document.body.scrollTop > showat || document.documentElement.scrollTop > showat) {
        tocButton.style.display = "block";   // show Top
    } else {
        tocButton.style.display = "none";    // hide Top
    }
}

// Show/hide button whenever user scrolls below/above N px from top
window.onscroll = scrollFunction;   // source had "function() {scrollFunction()};"; why?

</script>
<!--END INSERT========================================================================-->


<SCRIPT>
function tocClick() {
    //
    // redef to jump to absolute top of page (else best is start of first element;
    // this should ideally use a global that can be changed, but it's too late...
    //
    window.location.href = '#';                 // go/scroll to opt (not tag)
}
</SCRIPT>


<!------------------------------------------------------------------------------------>

<DIV style="background-color: cornsilk;
            border: thin solid black;
            border-left:  0px;
            border-right: 0px;    /* last wins */
">

<H2 style="padding-top: 2px; padding-left: 2px;">Search learning-python.com</H2>

<!-- Jun 2018: add accept-charset to avoid HTML-entity encoding of chars  -->
<!-- outside page's default (though it also changed to UTF-8 for new tip) -->
  
<P>
<FORM method=POST accept-charset="UTF-8" action="cgi/sitesearch.py">
<TABLE border=0 cellpadding=5 bgcolor=tan>

<TR>
  <TH align=right>Search this site:
  <TD><select name=searchsite>
      <option>Entire site
      <!-- Jun 2017: now just one site to rule them all... -->
      <!-- <option>Books only --> 
      <!-- <option>Training only -->
      </select>

<TR>
  <TH align=right>Search for this:
  <!-- Feb 2018: smaller on mobile (was size=50) -->
  <TD><input type=text name=searchterm class=inputsize>    

<TR>
  <!-- in order of preference: see below -->
  <TH align=right>Search provider:
  <TD><select name=searchhost>
      <option>DuckDuckGo
      <option>StartPage
      <option>Google
      <option>Bing
      <option>Yahoo
      <option>Baidu         <!-- Jun 2018: added -->
      <option>Yandex        <!-- Jun 2018: added -->
      <option>Privado       <!-- Sep 2020: added -->
      <option>OneSearch     <!-- Sep 2020: added -->
      <!-- Jun 2018: <option>Ixquick - merged into StartPage in 2016 -->
      </select>

<TR>
  <TH>
  <TD><input type=submit value="Search">

</TABLE>
</FORM>
</P>

</DIV>

<!-- dec18: use border instead of hr so ok in chrome at low zooms
<HR>
-->


<!------------------------------------------------------------------------------------>

<H2 style="margin-top: 30px;">How This Page Works</H2>

<!-- Feb 2018: mobile-friendly viewer, not HREF="sitesearch.py"; -->
<!-- removed the extra copy of the .py in "."; redundancy kills; -->
<P>
When you press this page's Search button, it triggers a Python script on 
the server, which in turn builds a <code><I>term</I> site:<I>site</I></code> 
site-specific search query and passes it to a selected search provider using
an HTTP redirect.  
You can view and fetch the code of the first two hops in this interaction 
with the links in the following:

<P style="margin-left: 26px;">
<SPAN class=emph>
<A class=emph HREF="cgi/showcode.py?name=sitesearch.html">This page</A>
</SPAN>
&nbsp;⇒&nbsp;
<SPAN class=emph>
<A class=emph HREF="cgi/showcode.py?name=cgi%2Fsitesearch.py">Python script</A>
</SPAN>
&nbsp;⇒&nbsp;
Provider

<P>  
The net effect displays results in the provider's search-results
<A HREF="sitesearch-result.png">page</A>.  The script in the middle uses 
basic CGI, and its redirect is a simple technique that leverages work 
already performed by search providers that have crawled the site 
(invited or not).  

<P>Assorted usage tips:

<!-- Jun 2018: add tip about searching for non-ASCII Unicode chars; -->
<!-- use HTML entities for non-BMPs, so works in tools like PyEdit; --> 

<!-- Sep 2020: add line-height to make the list's vertical spacing  -->
<!-- even/uniform when large Unicode glyphs are present             -->

<UL style="line-height: 1.3em;"> 
<LI>
Most providers do well on searches for non-emoji Unicode text
(like spam and 真Л⇨).

<LI>
Some do better than others on searches for  
emojis and symbols (like 
&#128586;,  <!-- see-no-evil monkey -->
&#128522;,  <!-- a mandatory smiley -->
and ☞).

<LI>  
You can return to this site after a search by using your browser's 
Back button or gesture. 

<LI>
All the usual search syntax works, including quotes for focus 
(e.g., <code>"Pandora's box"</code>).
</UL>

<P>
To learn more about the redirect techniques used here, see 
<A HREF="https://en.wikipedia.org/wiki/URL_redirection#Implementation">this</A>,
<A HREF="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">this</A>, and
<A HREF="http://www.w3.org/Protocols/rfc2616/rfc2616.html">this</A>.
To explore other ways to add search to sites, try 
<A HREF="https://duckduckgo.com/search_box">here</A> and 
<A HREF="https://duckduckgo.com/?q=adding+search+to+web+site">here</A>, though
many require JavaScript on the client or PHP on the server, and come with some 
of the blacker tradeoffs described in the next section.
</P>

<!-- original: some other sites seem to do emojis better today...
<UL>
<LI>To search for emojis and symbols (like 
&#128586;,  
&#128522;,  
and ☞), try Google or StartPage first.
etc...
</UL>
-->



<!------------------------------------------------------------------------------------>

<H2><A name="search-providers">Search-Provider Cautions</A></H2>

<P>
Because this page delegates searches to third parties, it sports some 
inherent caveats:

<DL>
<DT>Online sites only
<DD>This page's scheme naturally works only for sites available on the 
general Internet, because it relies on search-provider crawls.
Most off-line and intranet sites won't qualify.

<DT>Provider quirks
<DD>Some search providers support this page's scheme better than others.
As of 2020, for instance, exiting Bing results may require multiple Backs 
(this seems to happen randomly and exclusively in desktop Firefox);
Yandex may ask you to verify the automated search (with an 
occasional challenge reply); and Google and StartPage seem best at 
search terms with emojis (Baidu and Yandex often work too).

<DT>Darker themes
<DD>Most grievously, search providers may track searchers, 
and taint result pages with advertising, unrelated photos, 
or both.  This also varies by provider (and changes over time),
but the effects can range from blatantly rude to outright silly.  
In earlier testing, Google's search results for "fortran" at this site 
listed paid ads for Fortran compilers first, and Bing displayed pictures 
of pastry chefs for "decorators" (yes, really).
</DL>

<!-- Jun 2018: Ixquick was merged into StartPage in 2016 -->
<P>
Some caveats can be lessened by user choices.
<I>Tracking</I>, for example, can be addressed by using privacy-respecting sites, 
which are strongly recommended for users of this page.
Among these, 
<A href="https://duckduckgo.com">DuckDuckGo</A> 
seems the furthest along, 
<A HREF="https://www.startpage.com/eng/aboutstartpage/">StartPage</A> 
seems a close runner-up, and 
<A HREF="https://www.privado.com/">Privado</A> and 
<A HREF="https://www.onesearch.com/">OneSearch</A> are promising newcomers.   
Although other search providers can be selected above for comparison, 
their tracking focus makes them innately perilous, 
and a use-at-your-own-risk proposition.  

<P>
Regardless of their tracking policies, some search providers still 
<I>piggyback</I> on others (your results may come from Google or Bing,
even if you're searching elsewhere), and most&mdash;if not all&mdash;will 
still sometimes show you <I>sponsored links or ads</I>.  There is no known 
silver bullet for all the annoying ads, unfortunately, but non-tracking 
providers at least won't treat you like a resource to be covertly exploited.

<P class=finalparagraph>
There's more on this story in this site's full privacy policy 
<A HREF="privacy-policy.html">here</A>.
Search would ideally avoid third parties altogether, of course, but a 
more custom solution is currently outside this site's proprietor's schedule. 
For more on web-search privacy options, see 
<A HREF="https://www.expressvpn.com/blog/ixquick-startpage/">this</A>;  
or try a web search...
</P>

<!------------------------------------------------------------------------------------>


<!--GENHTML INSERT====================================================================-->
<!--
Nov15: added via genhtml.py, to avoid pasting footer in every file on changes.
See learning-python.com/genhtml.html for other options and their tradeoffs.
-->

</P>

<!--Feb18: make table responsive, for mobile-friendly scrollbar on small screens -->
<BR><BR>
<DIV class=footerdiv>

<TABLE class=footertable bgcolor=tan>

<!-- NESTED INSERT: can't nest > 2 deep -->      

<!-- gif now is Home, not python.org -->
<!-- scale larger gif for better res -->
<!-- border=0 only for IE img links  -->

<TD>
  <A class=blocklinkbar href="index.html">
  <IMG SRC="PythonPowered.gif" border=0 width=55 height=22
       ALT="[Home page]" TITLE="Home page"></A>  
<TD>
  <A class=blocklinkbar HREF="index-book-links.html">Books</A>
<TD>
  <A class=blocklinkbar HREF="programs.html">Code</A>
<TD>
  <A class=blocklinkbar HREF="posts.html">Blog</A>
<TD>
  <A class=blocklinkbar HREF="about-python.html">Python</A>
<TD>
  <A class=blocklinkbar HREF="formalbio.html">Author</A>
<TD>
  <A class=blocklinkbar HREF="training.html">Training</A>
<TD>
  <A class=blocklinkbar HREF="sitesearch.html">Search</A>
<TD>
  <A class=blocklinkbar HREF="mailto:lutz@learning-python.com">&copy;M.Lutz</A>

<!--DEFUNCT
<TD>
  <A class=blocklinkbar HREF="mailto:lutz@learning-python.com">Email</A>
<TD>
  <A class=blocklinkbar HREF="#">Top</A>
DEFUNCT-->

<!-- END NESTED INSERT -->

</TABLE>

</DIV>

<!--END INSERT========================================================================-->


</BODY></HTML>



[Home page] Books Code Blog Python Author Training Search ©M.Lutz