Saturday, January 4, 2014

Google Apps Script execution model

I was wondering if Google Apps Script web applications retained state between invocations, so I did a small experiment.

Using a Script created in Google Drive:

Code.gs:
function doGet() {
  return HtmlService.createTemplateFromFile('page')
    .evaluate()
    .setSandboxMode(HtmlService.SandboxMode.NATIVE);
}

var date = new Date();
var last = -1;

function func(value) {
  Utilities.sleep(100);
  var out = "date = " + date + ", last = " + last;
  last = value;
  return out;
}

page.html:
<style type="text/css">
pre {
  border: 1px solid green;
}
</style>

<div id="out">
<pre>loading...</pre>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(function() {
  for(var i = 0; i < 2; i++) {
    launch(i);
  }
});

setTimeout(function() {
  for(var i = 2; i < 4; i++) {
    launch(i);
  }
}, 2000);

function launch(id) {
  $("<pre />").text("started " + id).appendTo('#out');
  google.script.run
    .withSuccessHandler(function(result) {
      $("<pre />").text("finished " + id + ": " + result).appendTo('#out');
    })
    .func(id);
}
</script>

This output was produced:
loading...
started 0
started 1
finished 0: date = Sat Jan 04 2014 12:03:06 GMT-0400 (AST), last = -1
finished 1: date = Sat Jan 04 2014 12:03:06 GMT-0400 (AST), last = -1
started 2
started 3
finished 2: date = Sat Jan 04 2014 12:03:08 GMT-0400 (AST), last = -1
finished 3: date = Sat Jan 04 2014 12:03:08 GMT-0400 (AST), last = -1

Which leads me to the conclusion that the server is stateless, at least from the point of view of the scripts running on it.

Friday, January 3, 2014

lazy initialization in JavaScript

I have a function that is not often used and has a long initialization step, when it is used it is used often, making it a very good candidate for lazy initialization. This function is in a Google Apps Script web application, so minimizing processing time is desirable.

Functions in JavaScript are assignable closures, so it is easy to write a lazy initializing function without any if statements.

var getImage = function(name) {
  var images = { };

  var files = MESSAGES_FOLDER.getFiles();
  while(files.hasNext()) {
    var file = files.next();
    images[file.getName()] = file.getDownloadUrl();
  }

  return (getImage = function(name) {
    return images[name];
  })(name);
}

After the initialization part of the function (making a map of file names to download urls) it is replaced by a new function that does not include the initialization, and that new function is called to get the result for the first use.

This is also an implementation of the State pattern.

Thursday, December 5, 2013

Simple and super practical note book

All the notebooks I have kept in my bag so far seemed lacking in little ways: I couldn't easily tuck things into them; sometimes finding the next blank page was a challenge; they consume a lot more space than I feel they need to; taking pages out of them quickly became unsightly.

So I started pondering how to make one where I could freely add and remove pages without having an effect on the feel of the note book.

I made the notebook from plastic canvas, and sewed it with strips of shopping bags.  The shell does not care about getting wet, but the pages inside do.


It is just larger than a quarter sheet of letter sized paper, so it is very easy to make new pages as I need them.


The pages are folded over a strip of plastic, so they can be completely rearranged, and anything thin that can be folded can be tucked between any of the pages.  I did not realize in advance that I can always be writing on the top few pages by just moving blank pages up to the top of the stack.  I had printed driving directions on the left side earlier in the week, and now they have been flipped over to be reused as note pages.

In the future I am going to try some graph paper or lined paper in here, but this is also working very well.

Thursday, November 21, 2013

Swing Components in HTML GUI in Java

I would like to use Swing components in a HTML based layout. Initially I tried to have empty elements with id attributes in the loaded text, but they were pruned from the internal representation, so I put text inside of them and replaced the whole element with the Swing component.

HtmlGui.java:
package ca.sarah_happy.sandbox;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

public class HtmlGui implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new HtmlGui());
    }

    private JTextPane viewer;
    private HTMLDocument document;

    @Override
    public void run() {
        try {
            viewer = new JTextPane();
            viewer.setEditable(false);
            viewer.setPreferredSize(new Dimension(400, 300));

            JFrame frame = new JFrame("screen");
            frame.setContentPane(new JScrollPane(viewer));
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

            URL screen = HtmlGui.class.getResource("screen.html");
            viewer.setPage(screen);
            document = (HTMLDocument) viewer.getDocument();
            viewer.addPropertyChangeListener("page", onLoad);

            frame.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private JSlider slider;

    private PropertyChangeListener onLoad = new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent e) {
            JButton button = new JButton("Button");
            button.addActionListener(onButton);
            insertComponent("button", button);

            slider = new JSlider();
            slider.addChangeListener(onSlider);
            insertComponent("slider", slider);
        }
    };

    private ChangeListener onSlider = new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            addOutput("changed slider: " + slider.getValue());
        }
    };

    private void insertComponent(String id, Component component) {
        Element e = document.getElement(id);
        viewer.setCaretPosition(e.getStartOffset());
        viewer.moveCaretPosition(e.getEndOffset());
        viewer.insertComponent(component);
    }

    private ActionListener onButton = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            addOutput("pressed button");
        }
    };

    private void addOutput(String text) {
        try {
            Element out = document.getElement("output");
            document.insertString(out.getEndOffset() - 1, text + "\n", null);
        } catch (BadLocationException ex) {
            ex.printStackTrace();
        }
    }
}

screen.html:
<html><body>
Some stuff...

<p><span id="button">button</span> <span id="slider">slider</span></p>

<p><i>Output:</i>
<div id="output"></div></p>
</body></html>

first HTML GUI in Java

Java GUI layout can be cumbersome, and HTML layout is less cumbersome. I wanted to have a screen in a Java application based on a HTML document.

For an initial attempt I made a screen with two buttons and an output area using a JEditorPane, the buttons are made from links and the output area is made from a document element.

HtmlText.java:
package ca.sarah_happy.sandbox;

import java.awt.Dimension;
import java.net.URL;

import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

public class HtmlText implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new HtmlText());
    }

    private JEditorPane viewer;
    private HTMLDocument document;

    @Override
    public void run() {
        try {
            URL text = HtmlText.class.getResource("screen.html");
            viewer = new JEditorPane(text);
            viewer.setEditable(false);
            viewer.setPreferredSize(new Dimension(400, 300));

            JFrame frame = new JFrame("screen");
            frame.setContentPane(new JScrollPane(viewer));
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

            document = (HTMLDocument) viewer.getDocument();
            viewer.addHyperlinkListener(onLink);

            frame.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private HyperlinkListener onLink = new HyperlinkListener() {
        @Override
        public void hyperlinkUpdate(HyperlinkEvent e) {
            if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
                return;
            }

            String message = "pressed <" + e.getDescription() + ">";

            try {
                Element out = document.getElement("output");
                document.insertString(out.getEndOffset() - 1,
                        message + "\n", null);
            } catch (BadLocationException ex) {
                ex.printStackTrace();
            }
        }
    };
}

screen.html:
<html><body>
This is a test.
<p><a href="#link1">link 1</a>
<a href="#link2">link 2</a></p>

<p>output:
<div id="output"></div>
</p>
</body></html>

Friday, November 8, 2013

YouTube to iTunes

Sometimes I come across a video on YouTube that I would like to turn into a song in iTunes.

When thinking about puzzles, especially programming puzzles, often my thoughts are around what I have, what I want, and how I can bring the haves and wants closer to each other. As I put effort into the ways to bring the haves and wants together I end up with more haves and more options for wants. I am done when I find that I already have what I want.

I have a video on YouTube. I want a song in iTunes. iTunes can import mp3 files as songs, so I can also want a mp3 file.  I also have Chrome, and a freshly installed copy of Mac OSX Snow Leopard.

One option is YouTube to MP3 Converter, but it can only handle videos up to 20 minutes, and I want to convert a longer one.

I found a Chrome plugin that allows downloads of the YouTube media file that gets streamed to the browser, YouTube Options.  After installing that I can also have a .mp4 or .flv video file.

I have a script that I wrote a while ago that turns video files into mp3 files, but to run the script wants ffmpeg installed.  MacPorts has package for ffmpeg, but to install MacPorts it wants Xcode installed. Xcode is on the Snow Leopard Installation DVD, so I install it. Now I can install MacPorts, so I do. Now I can install ffmpeg, so I do (sudo port install ffmpeg) .

Now the script works, so I have what I wanted when I started, almost like magic.

A video on YouTube --> download the video from YouTube  --> convert the downloaded video to mp3 --> import the generated mp3 into iTunes --> a song in iTunes.

Thursday, November 7, 2013

fleece mittens

The mittens I used last winter shrank in the wash, so they did not fit well anymore, so I pondered how to make some out of fleece for a while, made a pattern by tracing another pair of mittens, and made myself some mittens.


They are symmetric so I don't need to figure out which one goes on which hand.