├── .gitignore ├── README.md ├── db └── .gitignore ├── doc ├── Makefile ├── _build │ ├── doctrees │ │ ├── apidoc │ │ │ ├── modules.doctree │ │ │ └── rayleigh.doctree │ │ ├── environment.pickle │ │ ├── index.doctree │ │ └── intro.doctree │ └── singlehtml │ │ ├── .buildinfo │ │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── cloud.css │ │ ├── cloud.js │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── icon-deprecated.png │ │ ├── icon-note.png │ │ ├── icon-seealso.png │ │ ├── icon-todo.png │ │ ├── icon-warning.png │ │ ├── jquery.cookie.js │ │ ├── jquery.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ │ ├── index.html │ │ └── objects.inv ├── apidoc │ ├── modules.rst │ └── rayleigh.rst ├── conf.py ├── images │ ├── black_yellow.png │ ├── examples.key │ ├── examples1.png │ ├── examples2.png │ ├── exponential_smoothing.png │ ├── image_similarity.png │ ├── palette_14_3.png │ ├── palette_8_2.png │ ├── smoothing.key │ └── smoothing.png ├── index.rst ├── intro.rst └── requirements.txt ├── license.md ├── mongo.conf ├── rayleigh.wsgi ├── rayleigh ├── __init__.py ├── app.py ├── assemble_flickr_dataset.py ├── collection.py ├── image.py ├── palette.py ├── searchable_collection.py ├── static │ ├── jquery-1.8.3.js │ ├── main.css │ ├── sprintf-0.7-beta1.js │ └── underscore.js ├── templates │ ├── display_images.js │ ├── palette.html │ ├── search_by_image.html │ └── search_by_palette.html ├── tictoc.py └── util.py ├── requirements.txt └── test ├── collection.py ├── context.py ├── image.py ├── palette.py ├── palette_query.py ├── support ├── images │ ├── im1.jpg │ ├── im119.jpg │ ├── im12.jpg │ ├── im128.jpg │ ├── im131.jpg │ ├── im186.jpg │ ├── im187.jpg │ ├── im19.jpg │ ├── im2.jpg │ ├── im222.jpg │ ├── im272.jpg │ ├── im319.jpg │ ├── im32.jpg │ ├── im325.jpg │ ├── im344.jpg │ ├── im41.jpg │ ├── im42.jpg │ ├── im43.jpg │ ├── im434.jpg │ ├── im436.jpg │ ├── im440.jpg │ ├── im515.jpg │ ├── im6.jpg │ ├── im607.jpg │ ├── im625.jpg │ ├── im63.jpg │ ├── im646.jpg │ ├── im7.jpg │ ├── im73.jpg │ └── im80.jpg ├── matches.html ├── mirflickr_100.txt ├── mirflickr_10K.txt ├── mirflickr_1K.txt └── mirflickr_25K.txt └── util.py /.gitignore: -------------------------------------------------------------------------------- 1 | test/_temp 2 | data 3 | doc/_build 4 | flickr_api.key 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rayleigh: search images by multiple colors 2 | 3 | ## Introduction 4 | 5 | We present an open-source system for quickly searching large image collections by multiple colors given as a palette, or by color similarity to a query image. 6 | The system is running at [multicolorsearch.com](http://multicolorsearch.com). 7 | 8 | ### In brief 9 | 10 | - Images are represented as histograms over a fixed palette of colors in CIELab space. 11 | - Sets of colors used for searching are similarly represented as histograms. 12 | - Similarity of images to other images and to a query set of colors is formulated in terms of distance between histograms. 13 | - To make matching more robust to small differences in color, histograms are smoothed. 14 | 15 | ### System components 16 | 17 | - The back end processes image URLs to extract color information to store in a searchable database. 18 | - The web server provides REST access to the back end. 19 | - The client provides the UI for selecting a color palette to search with, or an image, and requests data from the web server. 20 | 21 | ### Documentation 22 | 23 | In addition to this high-level presentation, code is documented [here](singlehtml). 24 | 25 | A public [Trello](https://trello.com/board/rayleigh/50d36a9e0f87f42952000276) contains current and archived tasks. 26 | 27 | ### Screenshots 28 | 29 |
30 | Rayleigh in the search-by-palette view. 31 | 32 | --- 33 | 34 |
35 | Rayleigh in the search-by-image view. 36 | 37 | ## Prior Art 38 | 39 | 44 | 45 | The Content-Based Image Retrieval (CBIR) field has done much work on image similarity in the past couple of decades. 46 | My brief impression of the field is that color similarity was initially seen as proxy for general visual similarity, not an end to itself. 47 | More recent research focuses on closing the "semantic gap" in terms of nouns and verbs. 48 | 49 | I was able to find two implementations of multi-color searching, both closed-source: 50 | 51 | - the excellent [Idee Labs Multicolr Search](http://labs.tineye.com/multicolr), a rather expensive commercial service; 52 | - an experiment called [Chromatik](http://chromatik.labs.exalead.com/). 53 | 54 | There is an open-source project on top of Apache Lucene for general image retrieval, called [Lire](http://www.semanticmetadata.net/wiki/doku.php?id=lire:lire). 55 | I have not tried it. 56 | 57 | Additionally, a simple open-source implementation of a [single-color search](http://99designs.com/tech-blog/blog/2012/08/02/color-explorer/) was useful to see. 58 | 59 | ## Perception of Color 60 | 61 | All visual perception is highly complex. 62 | As countless optical illusions show, 63 | 64 | - we perceive objects of the same actual size in the image as being [different sizes](http://www.illusionspoint.com/wp-content/uploads/2010/09/relative-size-optical-illusion-21.jpg); 65 | - we see lines where there [aren't any](http://i.telegraph.co.uk/multimedia/archive/01121/300px-kanizsa-tria_1121094i.jpg); 66 | - and most relevantly for this project, we see things that are the same color in the image as being [different colors](http://brainden.com/color-illusions.htm). 67 | 68 | To get a machine to "see" the illusory contour of the triangle in the second example is an unsolved problem in practice. 69 | If we were developing a system to search images by shape, we would struggle to return the Kanizsa Triangle above as a result for "triangle." 70 | 71 | So it is with color. 72 | 73 | - As artists have [long understood](http://www.amazon.com/Interaction-Color-Revised-Josef-Albers/dp/0300018460), how we see a color depends on what other colors are around it. 74 | 75 | - As in the linked illusions above, the same color values in an image can be perceived as two different colors. 76 | 77 | - Or conversely, we may perceive an object as being the same color in different images where its actual color is different (for example, you perceive your face to be the same color in an outdoors photograph, bathroom mirror, fluorescent office, and in the darkness of evening). 78 | 79 | - On top of all this, there is a layer of language on top of all this mess: 80 | different languages deliniate colors in slightly different ways (for example, Russian has two distinct colors of "blue"), and it has been shown that it actually [affects color perception](http://boingboing.net/2011/08/12/how-language-affects-color-perception.html). 81 | 82 | ## Representing Color 83 | 84 | So the problem of searching images by color is hard, but we will still attempt it. 85 | How should we represent "color" in the search engine? 86 | 87 | ### Human eye 88 | 89 | In our eyes, there are two types of photorceptive cells: rods and cones. 90 | Rods respond only to intensity of light, not its color, and are far more numerous. 91 | Cones have three distinct types ('S', 'M', 'L'), each responding strongest to a specific wavelength of light. 92 | Our perception of color is derived from the response rates of the photoreceptors, as well as our experience with colors and expectations. 93 | 94 |
95 | Human retinal cell response curves. Image from Wikimedia Commons. 96 | 97 | ### RGB color space 98 | 99 | On your computer, color is represented as three values, representing intensity of three color channels: red, green, and blue. 100 | The pixels in the display are composed of these three basic lights. 101 | When all are fully on, the color is white; when all are off, the color is black. 102 | All of the millions of colors that a modern computer is able to display come from mixing the three intensities. 103 | 104 | The RGB system can be thought of as describing a three dimensional space, with the Red, Green, and Blue dimensions. 105 | A point in that space, given by the three coordinates, is a color. 106 | We can begin to think of distances between colors in this way, as a distance in 3D space between two points. 107 | 108 |
109 | RGB Color Cube. Image from Wikimedia Commons. 110 | 111 | ### HSV color space 112 | 113 | An additive mixture of three distinct colors does not match our intutive model of how color works: we can't easily visualize the effect of adding red, green, or blue to a color. 114 | 115 | Additionally, the distances in RGB spaces do not match up to perceptual judgements. 116 | That is, a color may be quite far away from another one in terms of RGB coordinates, but humans will judge the two colors as quite similar. 117 | Or the other way: two colors may *look* very different but be close together in RGB space. 118 | 119 | The rainbow is what we usually visualize when we think of color: hues of the visible spectrum from the almost-infrared to the almost-ultraviolet, roughly divided into less than a dozen words. 120 | A given hue can be imagined as more vibrant than baseline---deep red, midnight blue---or as more pastel-like---pink lipstick, robin's egg blue. 121 | 122 | The Hue-Saturation-Value color space is informed by this mental model, and strives to have one dimension corresponding to our intuitive notion of hue, and two dimensions which set the vibrancy and lightness. 123 | A point in HSV space is therefore more easily interpretable than a point in RGB space. 124 | 125 |
126 | HSV Color Cone. Image from Wikimedia Commons. 127 | 128 | ### Perceptually uniform color spaces 129 | 130 | Although it better matches our mental model of color, HSV space still suffers from a misalignment of perceptual judgements to 3D distance of colors. 131 | 132 | The international standards agency has formulated an alternative color space with the explicit goal of making distances in the color space correspond to human judgments of color similarity. 133 | Actually, it couldn't decide between two: CIELab and CIELuv. 134 | 135 | CIELab, roughly, is formed by an oval with two axes: a and b, which correspond to the "opponent" colors of Yellow-Blue and Red-Green. 136 | The third dimension of L\*a*b\* space is lightness, which is approximately self-describing. 137 | 138 | As an aside, the "opponent" colors are so named because of the [opponent process](http://en.wikipedia.org/wiki/Opponent_process) theory, which posits that color perception comes not from the asbolute values but from the *difference* in activation rates of the three types of cones in the retina. 139 | The opponent colors have no in-between point: we can imagine a point between blue and red, but not between blue and yellow; between red and yellow, but not between red and green; and so on. 140 | 141 | In the L\*a*b\* space, simple Euclidean distance (\\(\sqrt{L^2 + a^2 + b^2}\\) between two colors, which corresponds to the intuitive notion of a distance between 3D points, is a good approximation to perceptual judgements of their difference. 142 | 143 |
144 | CIELab color space. Image from couleur.org. 145 | 146 | ## Representing Multiple Colors 147 | 148 | At this point, we understand that we should use the CIELab space to represent colors as 3D points, and can treat distances between them as corresponding to perceptual judgements. 149 | 150 | But how do we represent a whole image, composed of many colors? 151 | 152 | Our solution is to introduce a canonical *palette* of colors: a small set of colors that approximately cover the color space. 153 | The color content of any image can then be represented as a histogram over the palette colors. 154 | To construct the histogram, for each color in the palette we find the percentage of pixels in the image that are *nearest* (in terms of Euclidean distance) to that color. 155 | 156 | We can represent this information in a slightly different way, by showing the top colors present in the image in a type of "palette image", with the area of a color in the palette image proportional to the prevalence of that color in the image. 157 | 158 |
159 | Example of four images, their raw color histograms, and their "palette images." 160 | 161 | ### Similarity of color content 162 | 163 | As described, we can represent the color content of an image with a histogram over a fixed palette, and note that images with similar color content should have similar histograms. 164 | 165 | A natural measure of similarity between histograms is the amount of overlap. 166 | Imagine superimposing one histogram on another; the areas that intersect form the overlap. 167 | 168 | Now look at the third image above, of a sunset over dark hills. 169 | The entirety of the sky seems to be in mostly two colors: a specific shade of yellow and a specific shade of light orange. 170 | 171 | But what if another image of a sunset, very similar to us perceptually, has two colors that are just slightly different? 172 | The histogram may in fact have no overlap at all then. 173 | 174 |
175 | An example of two images that are very similar perceptually having different raw histograms. 176 | 177 | ### Smoothing histograms 178 | 179 | The solution, as shown in the figure above, is to *smooth* the histograms---intuitively, spread the content of bins to nearby bins. 180 | Note that the smoothed histograms look practically the same for the two images, even though the unsmoothed histograms have almost no overlap. 181 | 182 | Therefore, our searchable representation of images will be in smoothed histograms. 183 | 184 |
185 |
186 | Example of four images, their raw color histograms, their "palette images.", and their smoothed histograms. 187 | 188 | We smooth histograms with a Gaussian kernel, with a parameter \\(\sigma\\) controlling the amount of smoothing. 189 | 190 |
191 | Effect of sigma on the amount of smoothing. 192 | 193 | Rayleigh offers a choice of a couple of settings of the sigma parameter. 194 | 195 | ### Histogram distance metrics 196 | 197 | How should we measure the distance between two color histograms? 198 | 199 | Let's say our canonical color palette has \\(K\\) colors. 200 | The histogram over those colors can be thought of as a point (or vector) in \\(K\\)-dimensional space, with the value of dimension \\(k\\) corresponding to the amount of the \\(k\\)'th color of the palette in the image histogram. 201 | 202 | **Euclidean** or \\(L_2\\) distance between two histograms \\(H\\) and \\(H'\\) is simply \\(\sqrt{\sum_{i=1}^K (H_i-H'_i)^2}\\) and corresponds to the intuitive notion of distance between the two vectors in \\(K\\)-dimensional space. 203 | The calculation of this distance is quite fast due to its possible implementation as simple dot product, for which there are very fast system-level libraries. 204 | 205 | A similar metric is **Manhattan** or \\(L_1\\) distance, which is simply \\(\sum_{i=1}^K \left|H_i-H'_i\right|\\). 206 | A better distance metric for histograms is **Chi-squared** distance: \\(\sum_{i=1}^K \frac{(H_i - H'_i)^2}{H_i + H'_i}\\), or the **histogram intersection** distance: \\(\sum_{i=1}^K \min(H_i, H'_i)\\). 207 | 208 | Rayleigh offers a choice between the first three distances. 209 | 210 | ## Forming the Palette 211 | 212 | Recall that before we could represent an image as a histogram, we had to decide on the canonical colors of the histogram. 213 | The palette should reasonably cover the space of all possible colors, and, as it will be used as part of the search-by-palette UI, should appear intuitively "correct." 214 | 215 | Representing the 3D space of colors in two dimensions is a problem that has no fully correct solution. 216 | My solution was to use the HSV color space and create a grid of colors by 217 | 218 | 1. picking N hues such that they span the 360 degrees of the color wheel, and presenting them as the middle row of the grid with full saturation and value. 219 | 2. for each hue, offering 220 | 1. K variations by decreasing saturation 221 | 2. K variations by decreasing lightness 222 | 3. An additional K-1 variations by decreasing both saturation and lightness. 223 | 3. Add as many grayscale levels as there are variations, as the last column. 224 | 225 |
226 | Palette of 8 hues and 2 variations. 227 | 228 |
229 | Palette of 14 hues and 3 variations. 230 | 231 | 236 | 237 | ## Constructing the dataset 238 | 239 | All the components are now in place to construct a system that allows searching a large number of images by multiple colors, or by color similarity to another image. 240 | 241 | We use the Flickr API to construct a large set of images. 242 | A [particular API call](http://www.flickr.com/services/api/flickr.interestingness.getList.html) returns up to 500 most "interesting" images, as judged by an internal Flickr algorithm that is based on user actions such as Favorites, comments, and addition to sets for a given date. 243 | We assemble a set of a million images using such calls (code is provided in this repository). 244 | 245 | ## Searching 246 | 247 | Downloading and processing a million images is not trivial. 248 | The images are loaded in parallel into a MongoDB database. 249 | For each image, a raw histogram over the canonical color palette is computed and stored alongside image dimensions, url, and Flickr ID. 250 | The pixel data is not stored, as we do not seek to replicate Flickr's data but point to it. 251 | 252 | A searchable colection is formed from this data by loading all the raw histograms into a big matrix and smoothing them with a given parameter sigma. 253 | The matrix forms the basis for the *Exact* search mode, where distances from the query to all images in the dataset are computed and the top K results are returned. 254 | 255 | Rayleigh's color palette consists of \\(D=88\\) colors, and the current production version has \\(N=100K\\) images loaded. 256 | This matrix easily fits in memory of even a basic computer, and so we can ignore disk seek effects and correctly estimate the cost of exact search as \\(O(DN)\\), as we have to look at all histograms and compute at least a dot product with each one. 257 | 258 | There are two ways to speed this up: 259 | 260 | 1. Reduce the dimensionality of the data \\(D\\) 261 | 2. Provide an efficient index to the data; accept potentially inexact results. 262 | 263 | ### Dimensionality reduction 264 | 265 | This speed-up does not yield us much, as the dimensionality of our data is already quite low. 266 | Nevertheless, Rayleigh implements PCA-based dimensionality reduction that works quite well if you'd like to experiment with larger palettes. 267 | An interesting question that I haven't looked at is what the PCA-reduced L\*a\*b\* colors look like. 268 | 269 | ### Indexing 270 | 271 | A better approach is to construct an index to the data, such that we can quickly find all data points that have low distance to the query data point, without having to look through the entire dataset. 272 | 273 | In addition to the Exact method above, Rayleigh implements SearchableImageCollection's using two packages. 274 | 275 | [**cKDTree**](http://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html) implements a standard data structure for such indexing tasks: the KD Tree, which splits the data in two, one dimension at a time, such that a group of nearest neighbors can quickly be found for a query. 276 | 277 | [**Fast Library for Approximate Nearest Neighbors**](http://www.cs.ubc.ca/~mariusm/index.php/FLANN/FLANN) is solidly-developed research code that is used and partially developed by the impressive [Point Cloud Library](http://www.pointclouds.org) project. 278 | 279 | The basic idea is automatic parameter tuning and comparison of two different methods for indexing data: multiple KD trees and hierarchical k-means. 280 | Parameters are tuned such that the index is fast but can return inexact results. 281 | 282 | We found that FLANN performs very well; since it includes kd trees, we did not extensively experiment with the cKDTree. 283 | 284 | ## System Architecture & Results 285 | 286 | The separation of concerns of our application are: 287 | 288 | - ImageCollection and SearchableImageCollection process images and construct index. 289 | 290 | - Flask web app provides a REST interface to the SearchableImageCollection and some utility methods, such as plotting color histograms. 291 | 292 | - HTML/JS UI is output by Flask, which fills in some things on the server side, but the search results are fetched with a separate AJAX call, to provide easy extension to loading multiple pages of results in a scrolling interface. 293 | 294 | Please use the [demo](http://ec2-204-236-191-162.us-west-1.compute.amazonaws.com) and play around with different settings and color queries. 295 | 296 | I would appreciate suggestions on how to effectively display a summary of results here. 297 | 298 | ## Next Steps 299 | 300 | 305 | 306 | - In constructing a histogram, weigh pixels by visual saliency. Often, the colors we perceive as important in the image actually do not take up much area and so are not well represented in the image histogram. 307 | 308 | - Provide UI for adjusting percentages of each color in the palette query. No backend work is needed. 309 | 310 | - How to represent "do-not-care" colors? 311 | 312 | - Threshold-and-re-normalize the color histograms: cut off normalized histogram mass above some threshold, and re-normalize. This is a trick used in constructing the [SIFT descriptor](http://en.wikipedia.org/wiki/Scale-invariant_feature_transform#Keypoint_descriptor), for example. 313 | -------------------------------------------------------------------------------- /db/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Rayleigh.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Rayleigh.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Rayleigh" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Rayleigh" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /doc/_build/doctrees/apidoc/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergeyk/rayleigh/db1228a078844771296c5a929699deb9e3218bb3/doc/_build/doctrees/apidoc/modules.doctree -------------------------------------------------------------------------------- /doc/_build/doctrees/apidoc/rayleigh.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergeyk/rayleigh/db1228a078844771296c5a929699deb9e3218bb3/doc/_build/doctrees/apidoc/rayleigh.doctree -------------------------------------------------------------------------------- /doc/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergeyk/rayleigh/db1228a078844771296c5a929699deb9e3218bb3/doc/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /doc/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergeyk/rayleigh/db1228a078844771296c5a929699deb9e3218bb3/doc/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /doc/_build/doctrees/intro.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergeyk/rayleigh/db1228a078844771296c5a929699deb9e3218bb3/doc/_build/doctrees/intro.doctree -------------------------------------------------------------------------------- /doc/_build/singlehtml/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 4 | tags: 5 | -------------------------------------------------------------------------------- /doc/_build/singlehtml/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergeyk/rayleigh/db1228a078844771296c5a929699deb9e3218bb3/doc/_build/singlehtml/_static/ajax-loader.gif -------------------------------------------------------------------------------- /doc/_build/singlehtml/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | /* -- relbar ---------------------------------------------------------------- */ 19 | 20 | div.related { 21 | width: 100%; 22 | font-size: 90%; 23 | } 24 | 25 | div.related h3 { 26 | display: none; 27 | } 28 | 29 | div.related ul { 30 | margin: 0; 31 | padding: 0 0 0 10px; 32 | list-style: none; 33 | } 34 | 35 | div.related li { 36 | display: inline; 37 | } 38 | 39 | div.related li.right { 40 | float: right; 41 | margin-right: 5px; 42 | } 43 | 44 | /* -- sidebar --------------------------------------------------------------- */ 45 | 46 | div.sphinxsidebarwrapper { 47 | padding: 10px 5px 0 10px; 48 | } 49 | 50 | div.sphinxsidebar { 51 | float: left; 52 | width: 0px; 53 | margin-left: -100%; 54 | font-size: 90%; 55 | } 56 | 57 | div.sphinxsidebar ul { 58 | list-style: none; 59 | } 60 | 61 | div.sphinxsidebar ul ul, 62 | div.sphinxsidebar ul.want-points { 63 | margin-left: 20px; 64 | list-style: square; 65 | } 66 | 67 | div.sphinxsidebar ul ul { 68 | margin-top: 0; 69 | margin-bottom: 0; 70 | } 71 | 72 | div.sphinxsidebar form { 73 | margin-top: 10px; 74 | } 75 | 76 | div.sphinxsidebar input { 77 | border: 1px solid #98dbcc; 78 | font-family: sans-serif; 79 | font-size: 1em; 80 | } 81 | 82 | div.sphinxsidebar #searchbox input[type="text"] { 83 | width: 170px; 84 | } 85 | 86 | div.sphinxsidebar #searchbox input[type="submit"] { 87 | width: 30px; 88 | } 89 | 90 | img { 91 | border: 0; 92 | } 93 | 94 | /* -- search page ----------------------------------------------------------- */ 95 | 96 | ul.search { 97 | margin: 10px 0 0 20px; 98 | padding: 0; 99 | } 100 | 101 | ul.search li { 102 | padding: 5px 0 5px 20px; 103 | background-image: url(file.png); 104 | background-repeat: no-repeat; 105 | background-position: 0 7px; 106 | } 107 | 108 | ul.search li a { 109 | font-weight: bold; 110 | } 111 | 112 | ul.search li div.context { 113 | color: #888; 114 | margin: 2px 0 0 30px; 115 | text-align: left; 116 | } 117 | 118 | ul.keywordmatches li.goodmatch a { 119 | font-weight: bold; 120 | } 121 | 122 | /* -- index page ------------------------------------------------------------ */ 123 | 124 | table.contentstable { 125 | width: 90%; 126 | } 127 | 128 | table.contentstable p.biglink { 129 | line-height: 150%; 130 | } 131 | 132 | a.biglink { 133 | font-size: 1.3em; 134 | } 135 | 136 | span.linkdescr { 137 | font-style: italic; 138 | padding-top: 5px; 139 | font-size: 90%; 140 | } 141 | 142 | /* -- general index --------------------------------------------------------- */ 143 | 144 | table.indextable { 145 | width: 100%; 146 | } 147 | 148 | table.indextable td { 149 | text-align: left; 150 | vertical-align: top; 151 | } 152 | 153 | table.indextable dl, table.indextable dd { 154 | margin-top: 0; 155 | margin-bottom: 0; 156 | } 157 | 158 | table.indextable tr.pcap { 159 | height: 10px; 160 | } 161 | 162 | table.indextable tr.cap { 163 | margin-top: 10px; 164 | background-color: #f2f2f2; 165 | } 166 | 167 | img.toggler { 168 | margin-right: 3px; 169 | margin-top: 3px; 170 | cursor: pointer; 171 | } 172 | 173 | div.modindex-jumpbox { 174 | border-top: 1px solid #ddd; 175 | border-bottom: 1px solid #ddd; 176 | margin: 1em 0 1em 0; 177 | padding: 0.4em; 178 | } 179 | 180 | div.genindex-jumpbox { 181 | border-top: 1px solid #ddd; 182 | border-bottom: 1px solid #ddd; 183 | margin: 1em 0 1em 0; 184 | padding: 0.4em; 185 | } 186 | 187 | /* -- general body styles --------------------------------------------------- */ 188 | 189 | a.headerlink { 190 | visibility: hidden; 191 | } 192 | 193 | h1:hover > a.headerlink, 194 | h2:hover > a.headerlink, 195 | h3:hover > a.headerlink, 196 | h4:hover > a.headerlink, 197 | h5:hover > a.headerlink, 198 | h6:hover > a.headerlink, 199 | dt:hover > a.headerlink { 200 | visibility: visible; 201 | } 202 | 203 | div.body p.caption { 204 | text-align: inherit; 205 | } 206 | 207 | div.body td { 208 | text-align: left; 209 | } 210 | 211 | .field-list ul { 212 | padding-left: 1em; 213 | } 214 | 215 | .first { 216 | margin-top: 0 !important; 217 | } 218 | 219 | p.rubric { 220 | margin-top: 30px; 221 | font-weight: bold; 222 | } 223 | 224 | img.align-left, .figure.align-left, object.align-left { 225 | clear: left; 226 | float: left; 227 | margin-right: 1em; 228 | } 229 | 230 | img.align-right, .figure.align-right, object.align-right { 231 | clear: right; 232 | float: right; 233 | margin-left: 1em; 234 | } 235 | 236 | img.align-center, .figure.align-center, object.align-center { 237 | display: block; 238 | margin-left: auto; 239 | margin-right: auto; 240 | } 241 | 242 | .align-left { 243 | text-align: left; 244 | } 245 | 246 | .align-center { 247 | text-align: center; 248 | } 249 | 250 | .align-right { 251 | text-align: right; 252 | } 253 | 254 | /* -- sidebars -------------------------------------------------------------- */ 255 | 256 | div.sidebar { 257 | margin: 0 0 0.5em 1em; 258 | border: 1px solid #ddb; 259 | padding: 7px 7px 0 7px; 260 | background-color: #ffe; 261 | width: 40%; 262 | float: right; 263 | } 264 | 265 | p.sidebar-title { 266 | font-weight: bold; 267 | } 268 | 269 | /* -- topics ---------------------------------------------------------------- */ 270 | 271 | div.topic { 272 | border: 1px solid #ccc; 273 | padding: 7px 7px 0 7px; 274 | margin: 10px 0 10px 0; 275 | } 276 | 277 | p.topic-title { 278 | font-size: 1.1em; 279 | font-weight: bold; 280 | margin-top: 10px; 281 | } 282 | 283 | /* -- admonitions ----------------------------------------------------------- */ 284 | 285 | div.admonition { 286 | margin-top: 10px; 287 | margin-bottom: 10px; 288 | padding: 7px; 289 | } 290 | 291 | div.admonition dt { 292 | font-weight: bold; 293 | } 294 | 295 | div.admonition dl { 296 | margin-bottom: 0; 297 | } 298 | 299 | p.admonition-title { 300 | margin: 0px 10px 5px 0px; 301 | font-weight: bold; 302 | } 303 | 304 | div.body p.centered { 305 | text-align: center; 306 | margin-top: 25px; 307 | } 308 | 309 | /* -- tables ---------------------------------------------------------------- */ 310 | 311 | table.docutils { 312 | border: 0; 313 | border-collapse: collapse; 314 | } 315 | 316 | table.docutils td, table.docutils th { 317 | padding: 1px 8px 1px 5px; 318 | border-top: 0; 319 | border-left: 0; 320 | border-right: 0; 321 | border-bottom: 1px solid #aaa; 322 | } 323 | 324 | table.field-list td, table.field-list th { 325 | border: 0 !important; 326 | } 327 | 328 | table.footnote td, table.footnote th { 329 | border: 0 !important; 330 | } 331 | 332 | th { 333 | text-align: left; 334 | padding-right: 5px; 335 | } 336 | 337 | table.citation { 338 | border-left: solid 1px gray; 339 | margin-left: 1px; 340 | } 341 | 342 | table.citation td { 343 | border-bottom: none; 344 | } 345 | 346 | /* -- other body styles ----------------------------------------------------- */ 347 | 348 | ol.arabic { 349 | list-style: decimal; 350 | } 351 | 352 | ol.loweralpha { 353 | list-style: lower-alpha; 354 | } 355 | 356 | ol.upperalpha { 357 | list-style: upper-alpha; 358 | } 359 | 360 | ol.lowerroman { 361 | list-style: lower-roman; 362 | } 363 | 364 | ol.upperroman { 365 | list-style: upper-roman; 366 | } 367 | 368 | dl { 369 | margin-bottom: 15px; 370 | } 371 | 372 | dd p { 373 | margin-top: 0px; 374 | } 375 | 376 | dd ul, dd table { 377 | margin-bottom: 10px; 378 | } 379 | 380 | dd { 381 | margin-top: 3px; 382 | margin-bottom: 10px; 383 | margin-left: 30px; 384 | } 385 | 386 | dt:target, .highlighted { 387 | background-color: #fbe54e; 388 | } 389 | 390 | dl.glossary dt { 391 | font-weight: bold; 392 | font-size: 1.1em; 393 | } 394 | 395 | .field-list ul { 396 | margin: 0; 397 | padding-left: 1em; 398 | } 399 | 400 | .field-list p { 401 | margin: 0; 402 | } 403 | 404 | .refcount { 405 | color: #060; 406 | } 407 | 408 | .optional { 409 | font-size: 1.3em; 410 | } 411 | 412 | .versionmodified { 413 | font-style: italic; 414 | } 415 | 416 | .system-message { 417 | background-color: #fda; 418 | padding: 5px; 419 | border: 3px solid red; 420 | } 421 | 422 | .footnote:target { 423 | background-color: #ffa; 424 | } 425 | 426 | .line-block { 427 | display: block; 428 | margin-top: 1em; 429 | margin-bottom: 1em; 430 | } 431 | 432 | .line-block .line-block { 433 | margin-top: 0; 434 | margin-bottom: 0; 435 | margin-left: 1.5em; 436 | } 437 | 438 | .guilabel, .menuselection { 439 | font-family: sans-serif; 440 | } 441 | 442 | .accelerator { 443 | text-decoration: underline; 444 | } 445 | 446 | .classifier { 447 | font-style: oblique; 448 | } 449 | 450 | abbr, acronym { 451 | border-bottom: dotted 1px; 452 | cursor: help; 453 | } 454 | 455 | /* -- code displays --------------------------------------------------------- */ 456 | 457 | pre { 458 | overflow: auto; 459 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 460 | } 461 | 462 | td.linenos pre { 463 | padding: 5px 0px; 464 | border: 0; 465 | background-color: transparent; 466 | color: #aaa; 467 | } 468 | 469 | table.highlighttable { 470 | margin-left: 0.5em; 471 | } 472 | 473 | table.highlighttable td { 474 | padding: 0 0.5em 0 0.5em; 475 | } 476 | 477 | tt.descname { 478 | background-color: transparent; 479 | font-weight: bold; 480 | font-size: 1.2em; 481 | } 482 | 483 | tt.descclassname { 484 | background-color: transparent; 485 | } 486 | 487 | tt.xref, a tt { 488 | background-color: transparent; 489 | font-weight: bold; 490 | } 491 | 492 | h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { 493 | background-color: transparent; 494 | } 495 | 496 | .viewcode-link { 497 | float: right; 498 | } 499 | 500 | .viewcode-back { 501 | float: right; 502 | font-family: sans-serif; 503 | } 504 | 505 | div.viewcode-block:target { 506 | margin: -1px -10px; 507 | padding: 0 10px; 508 | } 509 | 510 | /* -- math display ---------------------------------------------------------- */ 511 | 512 | img.math { 513 | vertical-align: middle; 514 | } 515 | 516 | div.body div.math p { 517 | text-align: center; 518 | } 519 | 520 | span.eqno { 521 | float: right; 522 | } 523 | 524 | /* -- printout stylesheet --------------------------------------------------- */ 525 | 526 | @media print { 527 | div.document, 528 | div.documentwrapper, 529 | div.bodywrapper { 530 | margin: 0 !important; 531 | width: 100%; 532 | } 533 | 534 | div.sphinxsidebar, 535 | div.related, 536 | div.footer, 537 | #top-link { 538 | display: none; 539 | } 540 | } -------------------------------------------------------------------------------- /doc/_build/singlehtml/_static/cloud.js: -------------------------------------------------------------------------------- 1 | /* ~~~~~~~~~~~~~~ 2 | * cloud.js_t 3 | * ~~~~~~~~~~~~~~ 4 | * 5 | * Various bits of javascript driving the moving parts behind various 6 | * parts of the cloud theme. Handles things such as toggleable sections, 7 | * collapsing the sidebar, etc. 8 | * 9 | * :copyright: Copyright 2011-2012 by Assurance Technologies 10 | * :license: BSD 11 | */ 12 | 13 | /* ========================================================================== 14 | * highlighter #2 15 | * ========================================================================== 16 | * 17 | * Sphinx's highlighter marks some objects when user follows link, 18 | * but doesn't include section names, etc. This catches those. 19 | */ 20 | $(document).ready(function (){ 21 | // helper to locate highlight target based on #fragment 22 | function locate_target(){ 23 | // find id referenced by #fragment 24 | var hash = document.location.hash; 25 | if(!hash) return null; 26 | var section = document.getElementById(hash.substr(1)); 27 | if(!section) return null; 28 | 29 | // could be div.section, or hidden span at top of div.section 30 | var name = section.nodeName.toLowerCase(); 31 | if(name != "div"){ 32 | if(name == "span" && section.innerHTML == "" && 33 | section.parentNode.nodeName.toLowerCase() == "div"){ 34 | section = section.parentNode; 35 | } 36 | } 37 | // now at section div and either way we have to find title element - h2, h3, etc. 38 | var children = $(section).children("h2, h3, h4, h5, h6"); 39 | return children.length ? children : null; 40 | } 41 | 42 | // init highlight 43 | var target = locate_target(); 44 | if(target) target.addClass("highlighted"); 45 | 46 | // update highlight if hash changes 47 | $(window).bind("hashchange", function () { 48 | if(target) target.removeClass("highlighted"); 49 | target = locate_target(); 50 | if(target) target.addClass("highlighted"); 51 | }); 52 | }); 53 | 54 | /* ========================================================================== 55 | * toggleable sections 56 | * ========================================================================== 57 | * 58 | * Added expand/collapse button to any collapsible RST sections. 59 | * Looks for sections with CSS class "html-toggle", 60 | * along with the optional classes "expanded" or "collapsed". 61 | * Button toggles "html-toggle.expanded/collapsed" classes, 62 | * and relies on CSS to do the rest of the job displaying them as appropriate. 63 | */ 64 | 65 | $(document).ready(function (){ 66 | function init(){ 67 | // get header & section, and add static classes 68 | var header = $(this); 69 | var section = header.parent(); 70 | header.addClass("html-toggle-button"); 71 | 72 | // helper to test if url hash is within this section 73 | function contains_hash(){ 74 | var hash = document.location.hash; 75 | return hash && (section[0].id == hash.substr(1) || 76 | section.find(hash.replace(/\./g,"\\.")).length>0); 77 | } 78 | 79 | // helper to control toggle state 80 | function set_state(expanded){ 81 | if(expanded){ 82 | section.addClass("expanded").removeClass("collapsed"); 83 | section.children().show(); 84 | }else{ 85 | section.addClass("collapsed").removeClass("expanded"); 86 | section.children().hide(); 87 | section.children("span:first-child:empty").show(); /* for :ref: span tag */ 88 | header.show(); 89 | } 90 | } 91 | 92 | // initialize state 93 | set_state(section.hasClass("expanded") || contains_hash()); 94 | 95 | // bind toggle callback 96 | header.click(function (){ 97 | set_state(!section.hasClass("expanded")); 98 | $(window).trigger('cloud-section-toggled', section[0]); 99 | }); 100 | 101 | // open section if user jumps to it from w/in page 102 | $(window).bind("hashchange", function () { 103 | if(contains_hash()) set_state(true); 104 | }); 105 | } 106 | 107 | $(".html-toggle.section > h2, .html-toggle.section > h3, .html-toggle.section > h4, .html-toggle.section > h5, .html-toggle.section > h6").each(init); 108 | }); 109 | /* ========================================================================== 110 | * collapsible sidebar 111 | * ========================================================================== 112 | * 113 | * Adds button for collapsing & expanding sidebar, 114 | * which toggles "document.collapsed-sidebar" CSS class, 115 | * and relies on CSS for actual styling of visible & hidden sidebars. 116 | */ 117 | 118 | $(document).ready(function (){ 119 | var holder = $('
'); 120 | var doc = $('div.document'); 121 | 122 | var show_btn = $('#sidebar-show', holder); 123 | var hide_btn = $('#sidebar-hide', holder); 124 | var copts = { expires: 7, path: DOCUMENTATION_OPTIONS.url_root }; 125 | 126 | show_btn.click(function (){ 127 | doc.removeClass("collapsed-sidebar"); 128 | hide_btn.show(); 129 | show_btn.hide(); 130 | $.cookie("sidebar", "expanded", copts); 131 | $(window).trigger("cloud-sidebar-toggled", false); 132 | }); 133 | 134 | hide_btn.click(function (){ 135 | doc.addClass("collapsed-sidebar"); 136 | show_btn.show(); 137 | hide_btn.hide(); 138 | $.cookie("sidebar", "collapsed", copts); 139 | $(window).trigger("cloud-sidebar-toggled", true); 140 | }); 141 | 142 | var state = $.cookie("sidebar"); 143 | 144 | 145 | doc.append(holder); 146 | 147 | if (state == "collapsed"){ 148 | doc.addClass("collapsed-sidebar"); 149 | show_btn.show(); 150 | hide_btn.hide(); 151 | } 152 | }); 153 | /* ========================================================================== 154 | * sticky sidebar 155 | * ========================================================================== 156 | * 157 | * Instrument sidebar so that it sticks in place as page is scrolled. 158 | */ 159 | $(document).ready(function (){ 160 | // initialize references to relevant elements 161 | var holder = $('.document'); // element that sidebar sits within 162 | var sidebar = $('.sphinxsidebar'); // element we're making "sticky" 163 | var toc_header = $('.sphinxlocaltoc h3'); // toc header + list control position 164 | if(!toc_header.length) toc_header = null; 165 | var toc_list = toc_header ? toc_header.next("ul") : null; 166 | var toggle = $('.sidebartoggle'); // also make collapse button sticky 167 | 168 | // initialize internal state 169 | var sticky_disabled = false; // whether sticky is disabled for given window size 170 | var sidebar_adjust = 0; // vertical offset within sidebar when sticky 171 | 172 | // offset() under jquery 1.4 is document-relative (sphinx 1.1), but 173 | // under jquery 1.5+ it's viewport-relative (sphinx 1.2 uses jquery 1.7). 174 | // since getBoundingClientRect is reasonbly cross-browser, using that instead. 175 | // getBoundClientRect is always viewport-relative. 176 | function left_offset($node){ return $node[0].getBoundingClientRect().left; } 177 | function top_offset($node){ return $node[0].getBoundingClientRect().top; } 178 | 179 | // function to set style for given state 180 | function set_style(target, value, adjust) 181 | { 182 | if(value <= adjust || sticky_disabled){ 183 | target.css({marginLeft: "", position: "", top: "", left: "", bottom: ""}); 184 | } 185 | else if (value <= holder.height() - target.height() + adjust){ 186 | target.css({marginLeft: 0, position: "fixed", top: -adjust, 187 | left: left_offset(holder), bottom: ""}); 188 | } 189 | else{ 190 | target.css({marginLeft: 0, position: "absolute", top: "", left: 0, bottom: 0}); 191 | } 192 | } 193 | 194 | // func to update sidebar position based on scrollbar & container positions 195 | function update_sticky(){ 196 | // set sidebar position 197 | var offset = -top_offset(holder); 198 | set_style(sidebar, offset, sidebar_adjust); 199 | // collapse button should follow along as well 200 | set_style(toggle, offset, 0); 201 | }; 202 | 203 | // func to update sidebar measurements, and then call update_sticky() 204 | function update_measurements(){ 205 | sticky_disabled = false; 206 | sidebar_adjust = 0; 207 | if(toc_header){ 208 | // check how much room we have to display top of sidebar -> end of toc list 209 | var leftover = $(window).height() - (toc_list.height() + top_offset(toc_list) - top_offset(sidebar)); 210 | if(leftover < 0){ 211 | // not enough room if we align top of sidebar to window, 212 | // try aligning to top of toc list instead 213 | sidebar_adjust = top_offset(toc_header) - top_offset(sidebar) - 8; 214 | if(leftover + sidebar_adjust < 0){ 215 | // still not enough room - disable sticky sidebar 216 | sticky_disabled = true; 217 | } 218 | } 219 | } 220 | update_sticky(); 221 | } 222 | 223 | // run function now, and every time window scrolls 224 | update_measurements(); 225 | $(window).scroll(update_sticky) 226 | .resize(update_measurements) 227 | .bind('cloud-section-toggled', update_measurements); 228 | }); 229 | 230 | 231 | /* ========================================================================== 232 | * sidebar toc highlighter 233 | * ========================================================================== 234 | * 235 | * highlights toc entry for current section being viewed. 236 | * only enabled under stick mode 237 | */ 238 | $(document).ready(function (){ 239 | // find all links in sidebar toc 240 | var links = $(".sphinxlocaltoc > ul a"); 241 | if(!links.length) return; 242 | 243 | // build list of elems, and various constant metadata 244 | var records = []; 245 | for(var i=0; i