Skip to content
ahwitz edited this page Nov 18, 2014 · 38 revisions

This page contains information and notes on how some of the internal functions of Diva work. If you wish to getting started with developing Diva, read on.

Table of Contents

Tools

Starting with Diva version 2.0, we have moved to a more tool-based development environment. We use the Closure compiler for unifying and minifying the JavaScript source and the LESS CSS language for developing the styles of the front-end.

You should see build.sh to see how these are being used and to customize the paths to these tools in your own environment. The commands available are:

  • ./build.sh less: Compiles and minifies the LESS files into CSS.
  • ./build.sh minify: Minifies the JavaScript files using the Closure compiler.
  • ./build.sh all: Performs both less and minify. (Also copies relevant source files to the build directory.)
  • ./build.sh test: Runs Diva's unit tests with PhantomJS and QUnit.
  • ./build.sh release VERSION: Builds the release package. VERSION is the release name, so ./build.sh release 3.0.0 will create diva-3.0.0.tar.gz.

Debugging setup

In order to effectively debug Diva (i.e. using your browser's developer tools), you may wish to load the un-minified source code as opposed to the compiled, minified versions. Since the build process compiles several source files into diva.min.js, if you wish to debug, you'll need to include more than just diva.js in your <head>. Once you include the sources as below, you may make full use of your browser's error reporting and debugging functions.

In order to do this, you may wish to create a new HTML file based off of demo/diva/single.html. To include the full sources, the head of your HTML file should look like this:

<head>
    <meta charset="utf-8" />
    <title>Diva.js Demo Page</title>
    <link rel="stylesheet" href="/css/diva.css" />
    <!--[if IE]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"></script>
    <script src="/js/utils.js" type="text/javascript"></script>
    <script src="/js/diva.js" type="text/javascript"></script>
    <script src="/js/plugins/highlight.js" type="text/javascript"></script>
</head>

Critically, we've replaced diva.min.js with utils.js, diva.js, and highlight.js. For CSS debugging, we've replaced diva.min.css with diva.css.

Once you have the source files included as above, you can run ./build.sh from the diva.js project root directory (as documented above), which will copy the relevant source files to the build/ directory. Then you can simply launch your webserver with build/ as your webserver root directory. I like to use $ python -m SimpleHTTPServer in build/ as my development server.

Now, since the unminified source is included, errors and console.log statements will print out with the correct line numbers in the console, and you can click on the line number to explore the source code or set breakpoints using the debugging tools.

Compiling a minified version

We use the Closure Compiler to create the minified file diva-min.js, which contains both utils.js and diva.js. This is done via the command line, using the .jar file available from the Closure Compiler website (See 'How Do I Start'), as follows:

cat diva-utils.js diva.js > diva-full.js && java -jar /path/to/compiler/compiler.jar --js diva-full.js --js_output_file diva-min.js && rm diva-full.js

The compilation level SIMPLE_OPTIMIZATION is used, as the ADVANCED_OPTIMIZATION level results in non-functioning code due to overly aggressive renaming. This compilation level results in a file size decrease of ~70%.

./build.sh minify does this automatically.

Container IDs

In order to allow more than one document viewer on a single page, we use the $.generateId plugin (included in diva-utils.js) to generate a prefix for the IDs of the elements of the document viewer. The first document viewer instantiated on the page will have an ID prefix of 1-diva-, the second 2-diva-, and so on.

To target all instances of a specific type of element using CSS, you can use $= (i.e., ends with). For example, to make the text within all title elements grey, you could do something like this:

div[id$=diva-title] {
    color: #CCC;
}

Data received through AJAX request

The following data is returned through each AJAX request:

  • item_title: The title of the document, as derived from the image directory name. The title is derived by substituting spaces for hyphens and then applying title case. For example,
this-is-an-image-directory-name

would result in a title of This Is An Image Directory Name.

  • dims: An array holding information related to the dimensions of the document. Contains the following information: * a_hei: The average height of all the images. This is used for calculating the vertical padding if [[settings.adaptivePadding|Settings#adaptivepadding]] is enabled. * a_wid: The average width of all the images. This is used for calculating the horizontal padding if [[settings.adaptivePadding|Settings#adaptivepadding]] is enabled. * mx_h: The maximum height among all the images. Not actually used at the moment. * mx_w: The maximum width among all the images. Necessary for calculating left offsets for each page (i.e. left and right padding). * t_hei: The total height of all the images stacked together, vertically. Necessary for calculating the total height of the containing element. * t_wid: The total width of all the images stacked together, horizontally.

  • max_zoom: the lowest maximum zoom level among all the pages of the document. The usage of this variable is explained in the section below

  • pgs: An array holding the data for all the pages in the document. See the explanation for the pages array in the Settings documentation for more information.

The contents of the pgs array should be sorted according on their filename prior to the request, and returned in a sorted order, so that the user doesn't have to wait for it to be dynamically sorted.

Maximum zoom level calculations

Each document is tied to a specific maximum zoom level. This "maximum" zoom level actually corresponds to the lowest maximum zoom level among all the pages in the document, and it is this value that is used for calculating dimensions at a particular zoom level. This is necessary to prevent zooming in more than is actually possible based on the number of zoom levels of a particular page. This is important to keep in mind when modifying the PHP script or porting it to another language/environment.

Furthermore, although pre-release versions of the document viewer required the user to determine and input the maximum zoom level of a document, later versions incorporated this maximum zoom level directly into the JSON data, without any action on the part of the user necessary. However, to avoid sizing issues in documents whose pages do not all have the same maximum zoom level, it was necessary to determine, within divaserve.php, the maximum zoom level of each page and output that in the JSON response. This fact may be relevant in terms of support for other tile image formats or processing libraries, as the VIPS library has a fairly specific way of calculating the number of zoom levels - based on the dimensions of the image and the tile size - while other libraries or formats may use different formulae. Currently, divaserve.php assumes that the images are being processed using the VIPS library. In later versions, we may add a layer of abstraction and increase the number of processing libraries supported, but for now it is recommended to use VIPS.

Virtual rendering

The first versions of diva.js worked like this: all the pages were loaded initially, but as blank images, and were only replaced with the actual tiles once they entered the viewport. However, not only did this result in an initial load time of several seconds, it also made scrolling fairly slow, especially once a large portion of the document had been loaded in the browser's memory. In the search for alternatives, I came across SlickGrid, whose implementation of virtual rendering resulted in an extremely fast way to page through large datasets, and managed to implement a similar technique in diva.js, whereby pages that were scrolled out of the viewport are deleted from the DOM, and pages that should be visible in the viewport are appended to it. This considerably speeded up initial load time and scrolling speed, and resulted in improved memory handling within the browser. Although the use of this technique should not affect ordinary installation and usage, the knowledge that it is being used might be important should it be necessary to make any edits to the appending/deleting functions. See Private Functions for more.

loadRow image sizing

When requesting images for grid mode within loadRow, images are requested at a particular height, and the full image is always returned, instead of disparate tiles (as in loadPage). However, the height of the images returned by IIPImage is often one or two pixels less than the requested height. This results in an unseemly white border along the bottom and right edges. To counter this, the requested height is 2 pixels greater than it should be, which should result in an image that may be cut off vertically by a few pixels but which will not be too short to fill the container. This appears to be an issue with IIPImage and the way image dimensions are calculated, so this workaround will have to do for now.

Panel size changes

As noted in https://github.com/DDMAL/diva.js/issues/235, the adjustBrowserDims/updatePanelSize function can not be called dynamically when an element is resized, as jQuery doesn't support that without creating a custom setTimeout and seeing if the size of the element has changed. Our workaround for this, as of https://github.com/DDMAL/diva.js/commit/f21c0c7c17fce2e23d8d56b0a76f428ebfed8f8c, is to create a Diva event called PanelSizeDidChange that will serve as an automatic call to the private adjustBrowserDims/updatePanelSize function. This will force the panel size to update so that all internal functions relying on it will respond with correct values; external code can be hooked off this event as well.

Clone this wiki locally