--- /dev/null
+/* $Id: th.js,v 1.1 2015/09/09 23:58:12 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * TODO:
+ * - Implement getopt-like class and allow to provide command line arguments
+ * - Generate multiple indexes with list of index links at top and bottom
+ * for large directories
+ * - Work on better presentation
+ */
+
+function thumbnail_jpeg(fh)
+{
+ this.jpeg(fh, 50);
+}
+
+function thumbnail(from, to, xmax, ymax)
+{
+ var ofh, tfd, nfd, oimg, ox, oy, timg, tfh;
+ var ret = false;
+ var type = undefined;
+ var i, to;
+
+ /* XXX */
+ stderr.write('Processing: ' + from + "\n");
+
+ /*
+ * Set load and save functions relative to image file type.
+ */
+ if (from.match(/\.jpg$|\.jpeg$/)) {
+ GD.load = GD.createFromJpeg;
+ GDImage.prototype.save = thumbnail_jpeg;
+ } else if (from.match(/\.png$/)) {
+ GD.load = GD.createFromPng;
+ GDImage.prototype.save = GDImage.prototype.png;
+ } else if (from.match(/\.gif$/)) {
+ GD.load = GD.createFromGif;
+ GDImage.prototype.save = GDImage.prototype.gif;
+ }
+
+ /*
+ * We use a single try block which breaks out of for before setting
+ * res to true. We use a dummy for loop since goto doesn't work.
+ */
+ for (;;) {
+ try {
+ var otime, tstat;
+
+ /*
+ * First open descriptors so that we may fstat(2)
+ * to determine if the thumbnail is more recent than
+ * the original image, in which case we don't avoid to
+ * create a thumb for nothing. We'll use File's
+ * fdopen(3) functionality afterwards if need be.
+ * We do this because image resizing is a rather CPU
+ * intensive operation.
+ */
+ ofd = new FD();
+ ofd.open(from, FD.O_RDONLY);
+ otime = (ofd.fstat()).st_mtime;
+
+ tfd = new FD();
+ tfd.open(to, FD.O_WRONLY | FD.O_CREAT);
+ tstat = tfd.fstat();
+
+ if (otime <= tstat.st_mtime && tstat.st_size != 0) {
+ ofd.close();
+ tfd.close();
+ return true;
+ }
+
+ /*
+ * Load original image and determine thumb size.
+ */
+ ofh = new File(ofd.fd, 'r');
+ oimg = GD.load(ofh);
+
+ ox = oimg.imageSX();
+ oy = oimg.imageSY();
+ if (ox <= xmax && oy <= ymax) {
+ tx = ox;
+ ty = oy;
+ } else if (ox < oy) {
+ tx = Math.round((xmax / oy) * ox);
+ ty = ymax;
+ } else {
+ tx = xmax;
+ ty = Math.round((ymax / ox) * oy);
+ }
+
+ /*
+ * Create and save new thumbnail. It's important to
+ * first truncate the file to zero length which
+ * fopen(3) would have done for us but which fdopen(3)
+ * won't.
+ */
+ tfd.ftruncate(0);
+ tfh = new File(tfd.fd, 'w');
+
+ timg = GD.createTrueColor(tx, ty);
+ //oimg.copyResampled(timg, 0, 0, 0, 0, tx, ty, ox, oy);
+ oimg.copyResized(timg, 0, 0, 0, 0, tx, ty, ox, oy);
+
+ timg.save(tfh);
+ } catch (x) {
+ Syslog.log(Syslog.LOG_NOTICE, x + ' (' + from + ')');
+ break;
+ }
+
+ /* Success */
+ ret = true;
+ break;
+ }
+
+ /*
+ * We close/destroy the objects in the same order they appeared since
+ * if we get an exception the others won't execute. Although the
+ * language supports a GC and objects will eventually be destroyed,
+ * there's no point in leaving them dangling unnecessary resources for
+ * a while. File descriptors shouldn't be open for nothing, and
+ * images take up a lot of memory.
+ */
+ ofh.close();
+ oimg.destroy();
+ tfh.close();
+ timg.destroy();
+
+ delete GD.load;
+ delete GDImage.prototype.save;
+
+ return ret;
+}
+
+function thumbnail_dir(path, xmax, ymax)
+{
+ var dir, fh;
+ var ret = false;
+ var i, f, index = [];
+
+ for (;;) {
+ try {
+
+ dir = new Dir(path);
+ while ((e = dir.read()) != null) {
+ if ((e.type & Dir.DT_DIR) != 0 &&
+ e.name.charAt(0) != '.') {
+ index.push({t: 'DIR', l: e.name});
+ if (!thumbnail_dir(path + '/' + e.name, xmax,
+ ymax))
+ break;
+ } else if ((e.type & Dir.DT_REG) != 0 && e.name.match(
+ /\.jpg$|\.jpeg$|\.png$|\.gif$/)) {
+ if (e.name.match(/_thumb/))
+ continue;
+ i = e.name.lastIndexOf('.');
+ newname = e.name.substring(0, i) + '_thumb' +
+ e.name.substring(i);
+ if (!thumbnail(path + '/' + e.name, path +
+ '/' + newname, xmax, ymax))
+ break;
+ index.push({t: 'IMG', o: e.name, n: newname});
+ }
+ }
+ if (index.length == 0) {
+ ret = true;
+ break;
+ }
+ /* XXX sort */
+ fh = new File(path + '/index.html', 'w');
+ fh.write("<html><head></head><body>\n" +
+ '<br><a href="../index.html">../ (parent directory)' +
+ "</a><br><br>\n");
+ for (i in index) {
+ f = index[i];
+ if (f.t == 'DIR') {
+ var t;
+
+ fh.write('<table border="1"><tr><td width="' +
+ xmax + '" height="' + ymax +
+ '" valign="center" align="center">' +
+ '<a href="' + f.l + '/index.html">');
+ if ((t = dir_findthumb(path + '/' + f.l))
+ != null)
+ fh.write('<img src="' + f.l + '/' + t +
+ '" alt="' + t + '">');
+ fh.write('</a></td></tr><tr><td align="' +
+ 'center" valign="center"><a href="' + f.l +
+ '/index.html">' + f.l + '/</a></td>' +
+ "</tr></table>\n");
+ }
+ }
+ for (i in index) {
+ f = index[i];
+ if (f.t == 'IMG')
+ fh.write('<a target="_blank" href="' + f.o +
+ '"><img src="' + f.n + '" alt="' + f.n +
+ '"></a>' + "\n");
+ }
+ fh.write('<br><sub>Thumb gallery generated by: ' +
+ '$Id: th.js,v 1.1 2015/09/09 23:58:12 mmondor Exp $' +
+ '<br><a href="http://cvs.pulsar-zone.net/cgi-bin/' +
+ 'cvsweb.cgi/mmondor/mmsoftware/js/js-sh/app/thumb/' +
+ 'thumb.js">(source)</a></sub>' + "\n");
+ fh.write("</body></html>\n");
+ fh.close();
+
+ } catch (x) {
+ Syslog.log(Syslog.LOG_NOTICE, x.toString());
+ break;
+ }
+
+ ret = true;
+ break;
+ }
+
+ try {
+ dir.close();
+ fh.close();
+ } catch (x) {}
+
+ return ret;
+}
+
+function dir_findthumb(path)
+{
+ var ret = null;
+ var dir, e, o, s = 0;
+
+ for (;;) {
+ try {
+ dir = new Dir(path);
+ while ((e = dir.read()) != null) {
+ if (e.name.match(/_thumb/)) {
+ o = FS.stat(path + '/' + e.name);
+ if (o.st_size > s)
+ ret = e.name;
+ }
+ }
+ } catch (x) {
+ Syslog.log(Syslog.LOG_NOTICE, x.toString());
+ break;
+ }
+ break;
+ }
+
+ try {
+ dir.close();
+ } catch (x) {}
+
+ return ret;
+}
+
+thumbnail_dir('/home/data/www/lesbolides.org/htdocs/commanditaires', 150, 150);