Removing redundant code

This commit is contained in:
knut 2009-01-11 19:23:08 +00:00
parent 01e246023d
commit 4b91f0a6e3
15 changed files with 3 additions and 885 deletions

View file

@ -1,113 +0,0 @@
<!--
- Resin 3.1 configuration file.
-->
<resin xmlns="http://caucho.com/ns/resin"
xmlns:resin="http://caucho.com/ns/resin/core">
<!--
- Logging configuration for the JDK logging API.
-->
<log name="" level="all" path="stdout:"
timestamp="[%H:%M:%S.%s] {%{thread}} "/>
<!--
- 'info' for production
- 'fine' or 'finer' for development and troubleshooting
-->
<logger name="com.caucho" level="info"/>
<logger name="com.caucho.java" level="config"/>
<logger name="com.caucho.loader" level="config"/>
<!--
- For production sites, change dependency-check-interval to something
- like 600s, so it only checks for updates every 10 minutes.
-->
<dependency-check-interval>2s</dependency-check-interval>
<!--
- You can change the compiler to "javac", "eclipse" or "internal".
-->
<javac compiler="javac" args="-source 1.5"/>
<!-- Security providers.
- <security-provider>
- com.sun.net.ssl.internal.ssl.Provider
- </security-provider>
-->
<cluster id="app-tier">
<!-- sets the content root for the cluster, relative to server.root -->
<root-directory>.</root-directory>
<server-default>
<!-- The http port -->
<http address="*" port="5080"/>
<!--
- Configures the minimum free memory allowed before Resin
- will force a restart.
-->
<memory-free-min>1M</memory-free-min>
<!-- Maximum number of threads. -->
<thread-max>256</thread-max>
<!-- Configures the socket timeout -->
<socket-timeout>65s</socket-timeout>
<!-- Configures the keepalive -->
<keepalive-max>128</keepalive-max>
<keepalive-timeout>120s</keepalive-timeout>
</server-default>
<!-- define the servers in the cluster -->
<server id="" address="127.0.0.1" port="5079">
<watchdog-port>5078</watchdog-port>
<jvm-arg>-Djava.awt.headless=true</jvm-arg>
</server>
<!-- includes the web-app-default for default web-app behavior -->
<resin:import path="${resin.home}/conf/app-default.xml"/>
<!--
- Default host configuration applied to all virtual hosts.
-->
<host-default>
<!--
- With another web server, like Apache, this can be commented out
- because the web server will log this information.
-->
<access-log path="build/access.log"
format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
rollover-period="1W"/>
</host-default>
<!-- configures the default host, matching any host name -->
<host id=''>
<!-- configures the root web-app -->
<web-app id='/album' document-directory='webapp'>
<temp-dir>target/resin-tmp</temp-dir>
<work-dir>target/resin-work</work-dir>
<session-config>
<!-- 30 min timeout -->
<session-timeout>30</session-timeout>
</session-config>
<class-loader>
<compiling-loader path="target"/>
</class-loader>
<path-mapping url-pattern="/demo/*" real-path="${resin.root}/photos" />
</web-app>
</host>
</cluster>
</resin>

View file

@ -1,60 +0,0 @@
Configuration
=============
A (hidden) file exists in each album directory, holding information
about each image and each sub-album. For both images and albums the key is the
filename and the order in which each is listed determines the ordering
in the album. Common to both we have the following attributes:
- Caption, a text fragment explaining what's in the picture/album.
Special to images additional attributes stored are:
- Rotation (floating point number)
- Mask, represented by the four border sizes; top, left, bottom,
right. All floating point.
- Thumbnail mask. Same as above, but applies to thumbnail images.
As these are very small there may be a need to focus on a detail
of the original image to have a visual cue about the subject of
the photo.
For albums the additional attributes are:
- Cover picture, the picture (or album) representing the album as a
whole for the purpose of a thumbnail.
Web Interface
=============
The theory of operation is that the directory holding all the
subdirectories with albums maps to a webapp. The URLs accepted by the
webapp reflect the directory structure of the photo directories, but
adds some twists to make it possible to represent thumbnails, medium
sized images and large images in addition to the album pages
themselves. URLs for the albums are:
/album/index-num.html
There is a configuration switch indicating how many thumbnails to
show per album page. This makes "num" in index-num.html represent the
associated subset of images. There are also one picture per page
pages. These have the pattern:
/album/filename.html
The individual pictures are accessed with URLs like the following:
/album/.../size/filename.ext
In this URL size has to be one of the following: "thumb", "medium",
"large". The (global?) configuration determines the actual size
constraints for each of the sizes. Only certain file extentions are
supported. Notably "jpeg" and "png". "html" can not be allowed as it
would destroy the page per picture setup.

11
pom.xml
View file

@ -70,12 +70,6 @@
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<!-- <scope>test</scope> -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
@ -101,11 +95,6 @@
<artifactId>commons-lang</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.1-408.jdbc3</version>
</dependency>
<dependency>
<groupId>velocity</groupId>
<artifactId>velocity</artifactId>

View file

@ -78,15 +78,6 @@
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.1-408.jdbc3</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>velocity</groupId>
<artifactId>velocity</artifactId>
@ -129,114 +120,6 @@
<version>2.4</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.1</version>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>resin</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>resin-util</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jta-101</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>webbeans-16</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>ejb-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jpa-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jsdk-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>hessian</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jca-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jstl-11</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>resin-source</artifactId>
<type>source</type>
<jar>resin-3.1.5-src.zip</jar>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>

View file

@ -1,49 +0,0 @@
package org.forkalsrud.album.config;
import java.io.*;
public class Album
extends Item
{
String frontPic;
Entry[] contents;
public Album(String name) {
super(name);
frontPic = null;
contents = null;
}
public void scan(File dir)
{
File[] dirs = dir.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
});
File[] files = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".JPEG")
|| name.endsWith(".jpeg")
|| name.endsWith(".JPG")
|| name.endsWith(".jpg");
}
});
contents = new Entry[dirs.length + files.length];
int idx = 0;
for (File d : dirs) {
contents[idx++] = new Reference(d.getName());
}
for (File f : files) {
contents[idx++] = new Photo(f.getName());
}
}
}
// eof

View file

@ -1,11 +0,0 @@
package org.forkalsrud.album.config;
public abstract class Entry {
String name;
public Entry(String name) {
this.name = name;
}
}
// eof

View file

@ -1,17 +0,0 @@
package org.forkalsrud.album.config;
public class Item
extends Entry
{
String caption;
boolean hidden;
public Item(String name) {
super(name);
caption = null;
hidden = false;
}
}
// eof

View file

@ -1,16 +0,0 @@
package org.forkalsrud.album.config;
public class Photo
extends Item
{
Transform xform;
public Photo(String name) {
super(name);
xform = null;
}
}
// eof

View file

@ -1,11 +0,0 @@
package org.forkalsrud.album.config;
public class Reference
extends Entry
{
public Reference(String name) {
super(name);
}
}
// eof

View file

@ -1,20 +0,0 @@
package org.forkalsrud.album.config;
import java.io.*;
public class Test
{
public static void main(String args[])
throws Exception
{
File dir = new File(args[0]);
Album a = new Album(dir.getName());
a.scan(dir);
}
}
// eof

View file

@ -1,12 +0,0 @@
package org.forkalsrud.album.config;
import java.awt.Dimension;
import java.awt.Rectangle;
public class Transform {
Dimension size;
float rotation;
Rectangle region;
}
// eof

View file

@ -1,72 +0,0 @@
package org.forkalsrud.album.exif;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileFilter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import org.junit.Test;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifDirectory;
public class DirectoryTest {
@Test
public void testDirectoryReader() throws Exception {
File myDir = new File("photos");
assertTrue(myDir.exists());
assertTrue(myDir.isDirectory());
File[] files = myDir.listFiles(new FileFilter() {
public boolean accept(File file) {
return !file.isHidden() && !file.isDirectory();
}
});
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
NumberFormat nf = new DecimalFormat("0");
Properties imgProps = new Properties();
for (File f : files) {
String name = f.getName();
String base = "file." + name + ".";
Metadata metadata = JpegMetadataReader.readMetadata(f);
Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);
if (exifDirectory.containsTag(ExifDirectory.TAG_ORIENTATION)) {
int orientation = exifDirectory.getInt(ExifDirectory.TAG_ORIENTATION);
imgProps.setProperty(base + "orientation", nf.format(orientation));
}
if (exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) &&
exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) {
int width = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH);
int height = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT);
imgProps.setProperty(base + "dimensions", new Dimension(width, height).toString());
}
if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
Date captureDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME_ORIGINAL);
imgProps.setProperty(base + "captureDate", sdf.format(captureDate));
}
if (exifDirectory.containsTag(ExifDirectory.TAG_USER_COMMENT)) {
String comment = exifDirectory.getString(ExifDirectory.TAG_USER_COMMENT);
imgProps.setProperty(base + "comment", comment);
}
}
imgProps.store(System.out, "Extra Comments");
}
}

View file

@ -1,226 +0,0 @@
package org.forkalsrud.album.exif;
import static org.junit.Assert.assertEquals;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.swing.JComponent;
import javax.swing.JFrame;
import org.junit.Test;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.Tag;
import com.drew.metadata.exif.ExifDirectory;
public class ExifDataTest {
String readOrientation(String imgFile) throws IOException {
ImageInputStream iis = ImageIO.createImageInputStream(new File(imgFile));
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
ImageReader reader = readers.next();
reader.setInput(iis);
IIOMetadata meta = reader.getImageMetadata(0);
return displayMetadata(meta);
}
@Test
public void testOrientation() throws Exception {
assertEquals("Top, left side (Horizontal / normal)", readOrientation2("photos/IMG_0129.JPG"));
assertEquals("Right side, top (Rotate 90 CW)", readOrientation2("photos/IMG_0139.JPG"));
framePicture("photos/l1000729.jpg");
framePicture("photos/l1000802.jpg");
// framePicture("photos/IMG_0129.JPG");
// framePicture("photos/IMG_0139.JPG");
Thread.sleep(50000);
}
String readOrientation2(String imgFile) throws JpegProcessingException, MetadataException {
String ret = null;
Metadata metadata = JpegMetadataReader.readMetadata(new File(imgFile));
// iterate through metadata directories
Iterator directories = metadata.getDirectoryIterator();
while (directories.hasNext()) {
Directory directory = (Directory) directories.next();
// iterate through tags and print to System.out
Iterator tags = directory.getTagIterator();
while (tags.hasNext()) {
Tag tag = (Tag) tags.next();
// use Tag.toString()
System.out.println(tag);
if ("Orientation".equals(tag.getTagName())) {
ret = String.valueOf(tag.getDescription());
}
}
}
return ret;
}
public String displayMetadata(IIOMetadata metadata) {
String ret = null;
String [] names = metadata.getMetadataFormatNames();
for (int i = 0; i < names.length; ++i) {
System.out.println();
System.out.println("METADATA FOR FORMAT: " + names[i]);
String r1 = displayTree(metadata.getAsTree(names[i]), 0);
if (r1 != null) {
ret = r1;
}
}
return ret;
}
public static String displayTree(Node node, int indent) {
String ret = null;
indent(indent);
String name = node.getNodeName();
System.out.print("<" + name);
NamedNodeMap nm = node.getAttributes();
for (int i = 0; i < nm.getLength(); i++) {
Node an = nm.item(i);
System.out.print(" " + an.getNodeName() + "=\"" + an.getNodeValue() + "\"");
if ("ImageOrientation".equals(node.getNodeName()) && "value".equals(an.getNodeName())) {
ret = an.getNodeValue();
}
}
if (node.hasChildNodes()) {
System.out.println(">");
NodeList children = node.getChildNodes();
for (int i = 0, ub = children.getLength(); i < ub; ++i) {
String r1 = displayTree(children.item(i), indent + 4);
if (r1 != null) {
ret = r1;
}
}
indent(indent);
System.out.println("</" + name + ">");
} else {
System.out.println("/>");
}
return ret;
}
static void indent(int indent) {
for (int i = 0; i < indent; ++i)
System.out.print(' ');
}
@Test
public void testThumbnail() throws Exception {
assertEquals(1, getOrientationID("photos/IMG_0129.JPG"));
assertEquals(6, getOrientationID("photos/IMG_0139.JPG"));
}
// case 1: return "Top, left side (Horizontal / normal)";
// case 2: return "Top, right side (Mirror horizontal)";
// case 3: return "Bottom, right side (Rotate 180)";
// case 4: return "Bottom, left side (Mirror vertical)";
// case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)";
// case 6: return "Right side, top (Rotate 90 CW)";
// case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)";
// case 8: return "Left side, bottom (Rotate 270 CW)";
/**
* @param imgFile
* @return
* @throws JpegProcessingException
* @throws MetadataException
*/
int getOrientationID(String imgFile) throws JpegProcessingException, MetadataException {
Metadata metadata = JpegMetadataReader.readMetadata(new File(imgFile));
Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);
int orientation = exifDirectory.getInt(ExifDirectory.TAG_ORIENTATION);
return orientation;
}
void framePicture(String imgFile) throws Exception {
JFrame frame = new JFrame(imgFile);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
Container c = frame.getContentPane();
c.add(new ImgComponent(imgFile));
frame.pack();
frame.setVisible(true);
}
class ImgComponent extends JComponent {
BufferedImage img;
public ImgComponent(String imgFile) throws IOException, JpegProcessingException, MetadataException {
Iterator readers = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = (ImageReader)readers.next();
ImageInputStream iis = ImageIO.createImageInputStream(new File(imgFile));
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceSubsampling(8, 8, 0, 0);
img = reader.read(0, param);
if (getOrientationID(imgFile) == 6) {
BufferedImage buf2 = new BufferedImage(img.getHeight(), img.getWidth(), img.getType());
Graphics2D g2 = buf2.createGraphics();
g2.translate(img.getHeight() / 2, img.getWidth() / 2);
g2.rotate(Math.PI / 2);
g2.translate(-img.getWidth() / 2, -img.getHeight() / 2);
g2.drawImage(img, 0, 0, this);
img = buf2;
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(this), img.getHeight(this));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, this);
}
}
}

View file

@ -55,34 +55,19 @@ public class AlbumServlet
res.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
String page = null;
if (pathInfo == null || "/".equals(pathInfo)) {
page = "album";
} else if (pathInfo.endsWith(".album")) {
if (pathInfo.endsWith(".album")) {
pathInfo = pathInfo.substring(0, pathInfo.length() - ".album".length());
page = "album";
}
if (page != null) {
DirectoryEntry entry = null;
entry = (DirectoryEntry)resolveEntry(pathInfo);
handleAlbum(req, res, entry);
handleAlbum(req, res, (DirectoryEntry)resolveEntry(pathInfo));
return;
}
if (pathInfo.endsWith(".photo")) {
pathInfo = pathInfo.substring(0, pathInfo.length() - ".photo".length());
page = "photo";
handlePhoto(req, res, (FileEntry)resolveEntry(pathInfo));
return;
}
/*
int parentPos = pathInfo.substring(0, pathInfo.length() - 1).lastIndexOf('/');
if (parentPos >= 0) {
req.setAttribute("parent", req.getServletPath() + pathInfo.substring(0, parentPos) + "/");
}
req.setAttribute("assets", "/" + req.getContextPath() + "assets");
*/
File file = new File(base, pathInfo);
if (!file.canRead()) {
res.setStatus(HttpServletResponse.SC_FORBIDDEN);

View file

@ -1,132 +0,0 @@
/*
$Id: pxapplication.css,v 1.1 2008/03/17 01:01:36 knut Exp $
Pixory - the personal image server
Copyright 2004-2005 Joe Panico
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* predefined elements */
html { height: 100%; text-align:center; font-family: verdana, arial, helvetica, sans-serif; }
body { height: 100%; margin: 0px; padding: 1px; }
hr { width: 100%; height: 1px; border: none; background-color:#7070a1; }
/* pseudo classes */
a:link {color:#606091;}
a:visited {color:#606091;}
/* force vertical scrollbar in Moz */
#force-scrollbar { position: absolute; top: 0; bottom: -1px; width: 1em; z-index: -1; }
/* generic classes */
/* i.e. css side orderings: top, right, bottom, left */
.thintable { width: 100%; border: none; border-collapse: collapse; padding: 0px; }
.error_message { font-size: medium; font-style: italic; }
/* a piece of text styled to look like a button */
.text_button { border: 1px solid black; color: black; background-color: rgb(80%,80%,80%); text-align: center; padding: 3px 5px 3px 5px; text-decoration: none;}
/* currently used only on ViewerHome */
.dropShadowContent { float: left; position: relative; left: -2px; top: -2px; border: none; background: #9fc;}
.dropShadowContainer { float: left; position: relative; background: #666; margin: 4px; border: none;}
/* PXShowMessage */
.show_message_table { border: none; height: 35px;}
.show_message_table .success_message_cell { color: black;}
.show_message_table .running_message_cell { color: black;}
.show_message_table .error_message_cell { color: red;}
.show_message_table .warn_message_cell { color: red;}
/* PXPageDecorator */
#page_decorator_table { height: 100%; width: 100%; border: none; color:#606091; background-color: #fffffb; vertical-align: top; text-align: left; margin: 0px; padding: 0px;}
#page_decorator_cell { height: 90%; border:none; vertical-align: top;}
#footer_cell { border: none; text-align: right; vertical-align: bottom; font-size: small; font-style: italic; color: #666; padding: 0px 4px 4px 0px; }
/* Message page */
#message_table {width: 30%; height: 30%; vertical-align: middle; text-align: center; margin-left: auto; margin-right:auto; border: 1px solid orange; background-color: #ddd;}
#message_table td { padding: 10px; }
/* all pages */
#content_table { width: 100%; border: none; text-align: center; border-collapse: collapse; }
/* pxtapestry:PXPropertiesTable */
table.properties_table { border: 1px dotted #ccc; text-align: left; font-size: xx-small; margin: 0px 7px 0px 7px; }
table.properties_table caption { font-size: small; font-weight: bold; text-decoration: underline; text-align: center; }
table.properties_table tr.even { background-color: #ccb; }
table.properties_table td { padding: 0px 5px 0px 5px; }
/* all/any viewer pages */
.image { border: 1px solid #606091; padding: 0px;}
.caption { border: none; font-size: xx-small; padding: 0px; vertical-align: top;}
.filename { border: none; font-size: xx-small; font-style: italic; padding: 0px; vertical-align: top;}
.photo_grid_table { width: 100%; height: 100%; vertical-align: top; padding: 0px 0px 0px 0px; border: none; border-spacing: 7px; empty-cells: hide; background-color: #ddd; }
.photo_grid_cell { vertical-align: top; background-color: #fffffb; border: 1px dotted #ccc; padding: 0px 0px 0px 0px; }
/* ViewerHome */
#viewer_home_title_cell {border: none; text-align: left; font-size: x-large; padding: 10px 20px 10px 10px; }
#viewer_home_greeting_cell {border: none; text-align: right; font-size: xx-small; padding: 5px 20px 5px 0px; }
#viewer_home_noalbums_cell {border: none; text-align: center; padding: 60px 0px 30px 0px; }
td.home_year { text-align: left; font-size: large; font-weight: bold; padding: 5px 5px 5px 5px; }
table.home_entry_table { width: 100%; vertical-align: top; text-align: center; font-size: xx-small; color:#606091; }
table.home_entry_table td.caption { padding: 0px 5px 5px 5px; }
/* album/index pages */
#album_header_table { border: none; width: 100%; padding: 0px; vertical-align: top; border-collapse: collapse; }
#album_name_cell { width: 50%; text-align: left; font-weight: bold; }
#album_control_cell { width: 50%; text-align: left; font-weight: bold; }
#album_control_cell .button { width: 15% }
#album_control_cell .control { width: 35%; text-align: right; }
#album_control_cell .label { width: 35%; text-align: center; font-size: small; color: #888; }
/* album pages */
.album_image_table { border: none; border-collapse: collapse; margin-left: auto; margin-right:auto; }
.album_image_table td { padding: 0px; }
.album_image_table .image_properties { height: 16px; font-size: xx-small; color: #888; }
.album_narrative_cell { padding: 0px 10px 20px 10px; white-space: normal; border: none; }
/* index page */
table.index_entry_table { border: none; height: 100%; width: 100%; vertical-align: middle; text-align: center; font-size: xx-small; color:#606091; }
/* FullPhoto page */
table.full_photo { border: none; width: 100%; height: 100%; vertical-align: middle; text-align: center; border-collapse: collapse; padding: 0px; margin-left: auto; margin-right:auto;}
/* AboutPanel */
#about_table { border: 1px dotted #ccc; text-align: left; font-size: small; margin: 0px 7px 0px 0px; }
#about_table .heading { text-align: center; padding: 7px; }
#about_table .properties_table { text-align: left; border-top: 1px solid #ccc;}
#about_table .author { font-weight: bold; }
#about_table .label { width: 20%; text-align: right; }
#about_table .odd { background-color: #ccb; }
#about_table .centered { text-align: center; }
/* AlbumDownload */
#download_table { border: 1px dotted #ccc; text-align: center; font-size: small; margin: 0px 7px 0px 7px; }
/* Login, SendCredentials */
.icon_table { width: 100%; border: none; text-align: right; }
.centering_table { width: 100%; height: 100%; vertical-align: middle; text-align: center; border: none; }
#login_table { border: 2px solid orange; background-color: #ddd; margin-left: auto; margin-right:auto; text-align: left; empty-cells: show;}
#login_table .error { height: 35px; }
#login_table .label { text-align: right; }
#login_table .submit { text-align: right; }
.submit_button { padding: 0px ; border: 1px solid black; background-color: orange; }
.form_field { padding: 0px 0px 0px 5px; border: 1px solid #888; }