Implements the screensaver of ChromeOS.

Previously the screensaver was a video including text captions.  We split the
video into the background video and HTML texts, which enables us to translate
the text easily.

BUG=chromium:314734
TEST=None

Change-Id: I34c7a87a2366c98a36f01140c8e5f95612e7d5dc
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/background-video-with-text.webm b/background-video-with-text.webm
new file mode 100644
index 0000000..6190d8a
--- /dev/null
+++ b/background-video-with-text.webm
Binary files differ
diff --git a/packages.pem b/packages.pem
new file mode 100644
index 0000000..4b3cc1f
--- /dev/null
+++ b/packages.pem
@@ -0,0 +1,27 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwZfi/rzC804cE0
+t4EmfYG8RUffZT6mx2so/XbNM/i/GEMLFQWj6NTTyO9fN90NLBXAif3gymVBmeNuN
+ruCrB2xlqB+UbzpFQfUD41aGnm1DHZKavIQ16/Yb8mIItjTasZtJ6K9HIvB9XX2LU
+YOd/Sx7IY6dwFAvI5cceOIFR4TGz3ceO3pJ12K8lsjUH+oIBip3LdvzQUd0GndbK5
+x0vumajItn2kMXYRL2ejWEjvE+AbTWu5c0CxZxrJb+2HQN/ygDCBc6fmAlrxY4eN4
+TgXppUt4hgVYwBHfkvij1SXTD/2QNBUhA/mLoCK4Oh/1u83HdEeSK3tFaW23ZEA4s
+15AgMBAAECggEADKn1dsV8CUJiYh1FZS0sdYkfTBh29prmQXuv7eHXvuMBkJ8qFki
+PoC6BUVxCqpQRyN1VwzY+aP2Bt7H6vbY0vwI0gzpDNsCobWsUXD83wyOexIlyVLlJ
+dxHsaC9T8ngj/X9zFmTAQdIdLxbWwXt+LyK5DYLUkeOJQkiB3BlEFzK+xmAW6PgpJ
+srsi0lOx9wyCXHfolnLYd7Ej3uJ9z3xgXwDwZ3rrD3LqVOE3kj+Xd70zFd5aXlh/p
+NlkwbukOtaD25kxEJ98qXKPbTqzv6tmPPBJrImnyUWiSQLyWgZpzWVLRHegVgl6Ee
+rVuy1pdMnZeKHmq66zL0VpttzOkaigQKBgQDpZlIDAqiHO6MqMLSmRmm8KH6ve111
+gWdl2yXhFu7lrXfBl8iN6wKqKu2woyD8Mx4HytJMQTU96fXH7CrMI/V1qOM9SWm4e
+noG+eMR3IuCnAQuXmZxsiB8OuDlGdEzgNH38Z+k8hu6qtCd/pGM34z4LeoFvvCzgZ
+tktGFCSIsZaQKBgQDBeqkVswWoDO8O3z9+i+ziEESr3eyPM5BaH/1BFINeuPhEJNB
+EYHf7xhcnzckHejcZztxr+SayopXu/6E28UFZtElrbg3XauTU1mlqixzDfIpK0bKI
+VlN0a80Rm4ix9x/ij9zhbegu52FjTgH0mTn0zZKa07V7rElRY+2/hwABkQKBgFkqU
+th9kn6fZPVASDNhoRFV8xf9LDYw6px5/V2hkkDCZYbbAq5dAtaZsdaSa46NxMI7VN
+32520wzUnESpFUh3icvbtzKWVlvOqfWoU/WTjbe6lvPPngkBKFt9cuZsKjSxPLBi9
+QoxFMIojJcTd9S/CgMdBiIrihIgl189YLusBJAoGAZuDyR7eJqnI2K5JKEOAWdZ5w
+5XJ79ylT+JhJ8fEWaaexW4q3s6QYKKiMZ33lBzTVV8PHMSKqkRIi55LGBpECTtCuu
+ZEriwAr7YCVTTHreYcfgek/JX/BoIYx5MrvRLJDIhWdPcFTVJ10C3nPUGCpkKI+8l
+Tgi4Jw+fZI1S7TsgECgYEAk1Zx3hWrjCFvUemkt1F4yGvwaSEHDNs5kl+rjQjK96z
+uDTZhtcXDlDVhTSWqVhE9e89pHsVI0AORxgVQulu7hSeBo9+gG6Ax9uXiXXxSd8Cb
+EEM0qX5YsUdApOfrssk3dSVStTgYHYTm7Tp8ZasncxmLE3sSaivCI/JD1Wda9y8=
+-----END PRIVATE KEY-----
diff --git a/packages/_locales/en/messages.json b/packages/_locales/en/messages.json
new file mode 100644
index 0000000..50f50c7
--- /dev/null
+++ b/packages/_locales/en/messages.json
@@ -0,0 +1,6 @@
+{
+  "captions": {
+    "message": "We rethought the computer || as the Chromebook || lightweight || fast || easy to use || easy to share || Chromebook \n updates itself \n automatically || so it's new now || and in a year || it will be even newer || so you're always \n one click away || from the \n best of Google || Gmail || YouTube || Hangouts || Google Docs || Google Play Music || there are thousands of apps || to help get it done || Chromebook boots || in seconds || it has virus protection built-in || and you're connected to the cloud || so your stuff is everywhere \n you need it to be || safe and secure || and always at your fingertips || work online || work offline || on a plane || on a train || in a tree || wherever || for dads || for sisters || for aunts || for you || for everyone",
+    "description": "The string parts split by || are shown in a screensaver video one by one. Please keep and do not add/remove the marker ||."
+  }
+}
diff --git a/packages/background-video.webm b/packages/background-video.webm
new file mode 100644
index 0000000..70c2a05
--- /dev/null
+++ b/packages/background-video.webm
Binary files differ
diff --git a/packages/background.js b/packages/background.js
new file mode 100644
index 0000000..04e9bc7
--- /dev/null
+++ b/packages/background.js
@@ -0,0 +1,38 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * Root class of the background page.
+ * @param {boolean} fullscreen Whether the window is fullscreen or not.
+ * @constructor
+ */
+function Background(fullscreen) {
+  /**
+   * Flag to use fullscreen or not.
+   * @type {boolean}
+   * @private
+   */
+  this.fullscreen_ = fullscreen;
+
+  Object.freeze(this);
+
+  chrome.app.runtime.onLaunched.addListener(this.onLaunched_.bind(this));
+}
+
+/**
+ * Handles launched events.
+ * @private
+ */
+Background.prototype.onLaunched_ = function() {
+  chrome.app.window.create(
+      'screen.html',
+      {
+        bounds: {width: 800, height: 600},
+        state: this.fullscreen_ ? 'fullscreen' : 'normal'
+      });
+};
+
+var background = new Background(true);
diff --git a/packages/manifest.json b/packages/manifest.json
new file mode 100644
index 0000000..5d8ab2e
--- /dev/null
+++ b/packages/manifest.json
@@ -0,0 +1,11 @@
+{
+  "manifest_version": 2,
+  "name": "Screensaver",
+  "description": "Chrome OS Screensaver",
+  "version": "0.1",
+  "default_locale": "en",
+  "kiosk_enabled": true,
+  "app": {
+    "background": {"scripts": ["background.js"]}
+  }
+}
diff --git a/packages/screen.html b/packages/screen.html
new file mode 100644
index 0000000..f9e0c82
--- /dev/null
+++ b/packages/screen.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<meta charset="UTF-8">
+<title>Screensaver</title>
+<style>
+  html {
+    height: 100%;
+  }
+
+  body {
+    align-items: center;
+    background: black;
+    display: flex;
+    height: 100%;
+    margin: 0;
+    width: 100%;
+  }
+
+  #wrapper {
+    position: relative;
+    width: 100%;
+  }
+
+  #background-video {
+    height: auto;
+    width: 100%;
+  }
+
+  #text-layer {
+    align-items: center;
+    display: flex;
+    height: 100%;
+    justify-content: center;
+    position: absolute;
+    top: 0;
+    width: 100%;
+  }
+
+  #text {
+    color: rgb(44, 103, 234);
+    position: relative;
+    text-align: center;
+    white-space: pre;
+  }
+</style>
+<script src="screen.js"></script>
+
+<div id="wrapper">
+  <video id="background-video" src="background-video.webm" autoplay loop>
+  </video>
+  <div id="text-layer"><span id="text"></span></div>
+</div>
diff --git a/packages/screen.js b/packages/screen.js
new file mode 100644
index 0000000..d227a1c
--- /dev/null
+++ b/packages/screen.js
@@ -0,0 +1,225 @@
+// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * Text track.
+ * @constructor
+ */
+function TextTrack() {
+  /**
+   * List of actions.
+   * @param {Array.<Object>}
+   * @private
+   */
+  this.actions_ = [{type: 'clearText', time: 0}];
+
+  /**
+   * Current index of the actions_.
+   * @param {number}
+   * @private
+   */
+  this.index_ = 0;
+
+  Object.seal(this);
+}
+
+/**
+ * Adds an entry to show the text.
+ * @param {number} tiem Time in seconds to show the text.
+ * @param {string} text Text to be shown.
+ * @param {number} size Ratio of the font size to the screen width.
+ * @param {number} x Horizontal position of the center of the text. 0 is the
+ *     center. -50 is the left edge. +50 is the right edge.
+ * @param {number} y Vertical position of hte center of the text. 0 is the
+ *     center. -50 is the top edge. +50 is the bottom edge.
+ */
+TextTrack.prototype.showText = function(time, text, size, x, y) {
+  this.actions_.push({
+    type: 'showText',
+    time: time,
+    text: text,
+    size: size,
+    x: x,
+    y: y
+  });
+};
+
+/**
+ * Adds an entry to clear the text.
+ * @param {number} tiem Time in seconds to show the text.
+ */
+TextTrack.prototype.clearText = function(time) {
+  this.actions_.push({type: 'clearText', time: time});
+};
+
+/**
+ * Apply the text of the given time to the element.
+ * @param {time} time Current play position of the video.
+ * @param {DOMElement} element Element to be applied the text.
+ * @param {number} width Width of the screen.
+ */
+TextTrack.prototype.play = function(time, element, width) {
+  while (true) {
+    // Usually the current time is satisfy the inequation.
+    // actions_[index] <= time && time < actions_[index + 1]
+    // If the time is out of the range, it updates the index to satisfy the
+    // inequation.
+
+    var action = this.actions_[this.index_];
+    if (action.time > time) {
+      this.index_--;
+      continue;
+    }
+
+    var nextAction = this.actions_[this.index_ + 1];
+    if (!nextAction || nextAction.time > time)
+      break;
+    switch (nextAction.type) {
+      case 'showText':
+        element.textContent = nextAction.text;
+        element.style.fontSize = (width * nextAction.size) + 'px';
+        element.style.left = nextAction.x + '%';
+        element.style.top = nextAction.y + '%';
+        break;
+      case 'clearText':
+        element.textContent = '';
+        break;
+    }
+    this.index_++;
+  }
+};
+
+/**
+ * Root class of the application window.
+ * @constructor
+ */
+function Screen() {
+  /**
+   * Background video.
+   * @type {HTMLVideoElement}
+   * @private
+   */
+  this.backgroundVideo_ = null;
+
+  /**
+   * Text element.
+   * @type {HTMLElement}
+   * @private
+   */
+  this.text_ = null;
+
+  /**
+   * Text track.
+   * @type {TextTrack}
+   * @private
+   */
+  this.track_ = null;
+
+  /**
+   * Bounded onFrame_ function.
+   * @type {function()}
+   * @private
+   */
+  this.onFrameBound_ = this.onFrame_.bind(this);
+
+  Object.seal(this);
+  window.addEventListener('load', this.onLoad_.bind(this));
+};
+
+/**
+ * Creates the text track of the scrennsaver video.
+ * @private
+ */
+Screen.createTextTrack_ = function() {
+  var captions = chrome.i18n.getMessage('captions').split(/\s+\|\|\s+/);
+  var track = new TextTrack();
+  track.showText(0.5, captions[0], 0.05, 0, 0);  // rethought
+  track.clearText(1.8);
+  track.showText(2.4, captions[1], 0.04, 0, 0);  // chromebook
+  track.clearText(3.8);
+  track.showText(4.5, captions[2], 0.05, -20, 0);  // lightweight
+  track.clearText(7);
+  track.showText(8, captions[3], 0.05, 20, 0);  // fast
+  track.clearText(10);
+  track.showText(10.8, captions[4], 0.05, 0, 0);  // easy_to_use
+  track.showText(11.8, captions[5], 0.05, 0, 0);  // easy_to_share
+  track.clearText(12.8);
+  track.showText(13, captions[6], 0.045, 0, 0);  // update_itself
+  track.showText(14.6, captions[7], 0.05, 0, 0);  // new_now
+  track.clearText(16.2);
+  track.showText(17, captions[8], 0.05, 0, -35);  // in_a_year
+  track.showText(19, captions[9], 0.05, 0, -35);  // even_newer
+  track.clearText(21);
+  track.showText(22, captions[10], 0.05, 0, -3);  // one_click
+  track.clearText(23.1);
+  track.showText(24.5, captions[11], 0.05, -32, 0);  // best_of_google
+  track.clearText(27.5);
+  track.showText(29, captions[12], 0.05, 20, 0);  // gmail
+  track.clearText(31);
+  track.showText(33, captions[13], 0.05, 0, -42);  // youtube
+  track.clearText(37);
+  track.showText(38.3, captions[14], 0.05, -15, 0);  // hangouts
+  track.clearText(41.5);
+  track.showText(42, captions[15], 0.05, 0, -40);  // docs
+  track.showText(47, captions[16], 0.05, 0, -40);  // music
+  track.clearText(51);
+  track.showText(52.5, captions[17], 0.05, 0, 0);  // thousands
+  track.showText(55, captions[18], 0.05, 0, 0);  // get_it_done
+  track.clearText(57);
+  track.showText(58, captions[19], 0.05, 0, -40);  // boots
+  track.showText(60, captions[20], 0.05, 0, -40);  // in_seconds
+  track.showText(62, captions[21], 0.05, 0, -40);  // virus_protection
+  track.clearText(64.5);
+  track.showText(66.5, captions[22], 0.05, 0, 0);  // cloud
+  track.clearText(69);
+  track.showText(71, captions[23], 0.05, 0, -35);  // everywhere
+  track.showText(74, captions[24], 0.05, 0, -40);  // safe_and_secure
+  track.clearText(77);
+  track.showText(77.5, captions[25], 0.05, 0, -32);  // fingertips
+  track.clearText(81.3);
+  track.showText(82.1, captions[26], 0.05, 0, 0);  // online
+  track.showText(83.6, captions[27], 0.05, 0, 0);  // offline
+  track.clearText(84.5);
+  track.showText(85.5, captions[28], 0.05, 0, -3);  // plane
+  track.clearText(88);
+  track.showText(88.9, captions[29], 0.05, 0, -3);  // train
+  track.showText(91, captions[30], 0.05, 0, -3);  // tree
+  track.clearText(93.7);
+  track.showText(94.8, captions[31], 0.05, 0, 0);  // wherever
+  track.clearText(97);
+  track.showText(98, captions[32], 0.05, 20, 0);  // dads
+  track.showText(99, captions[33], 0.05, 20, 0);  // sisters
+  track.showText(100, captions[34], 0.05, 20, 0);  // aunts
+  track.showText(101, captions[35], 0.05, 20, 0);  // you
+  track.clearText(102);
+  track.showText(103, captions[36], 0.05, -20, 0);  // everyone
+  track.clearText(105);
+  return track;
+};
+
+/**
+ * Handles load events.
+ * @private
+ */
+Screen.prototype.onLoad_ = function() {
+  this.track_ = Screen.createTextTrack_();
+  this.text_ = document.querySelector('#text');
+  this.backgroundVideo_ = document.querySelector('#background-video');
+  this.onFrame_();
+};
+
+/**
+ * Handles animation frames and updates the text.
+ * @private
+ */
+Screen.prototype.onFrame_ = function() {
+  this.track_.play(this.backgroundVideo_.currentTime,
+                   this.text_,
+                   document.querySelector('body').offsetWidth);
+  webkitRequestAnimationFrame(this.onFrameBound_);
+};
+
+var screen = new Screen();