I/O session presentation app.
13
io2012-presentation/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
This is the slide deck for the Google I/O 2012 "The Next Evolution of Chrome Apps" session. It itself is a Chrome packaged app. It currently runs in Chrome 22.0.1190.0 or later (as of 6/29/2012, this means canary channel only).
|
||||
|
||||
The session itself (including live demos) is [available on YouTube](https://www.youtube.com/watch?v=j8oFAr1YR-0).
|
||||
|
||||
The text editor used in the presentation is itself a Chrome packaged app. It's [available in the samples repository](https://github.com/GoogleChrome/chrome-app-samples/tree/master/mini-code-edit).
|
||||
|
||||
The `helloworld` directory contains the "Hello World!" demo from the session, including the variant with the XSS issue (which can't be exploited due to CSP).
|
||||
|
||||
The `diff-sample-files` directory contains the two local files that were diff-ed during the offline diff tool demo (the diff tool itself is also [available in the samples repository](https://github.com/GoogleChrome/chrome-app-samples/tree/master/diff)).
|
||||
|
||||
The `servo` directory contains the standalone version of the serial port API spinner demo. It only does writes to the serial port. The [complete version in the samples repository](https://github.com/GoogleChrome/chrome-app-samples/tree/master/servo) has the full read/write implementation.
|
||||
|
||||
The `windowing_api` directory contains the source for the custom window frame and windowing API documentation (it's launched via the "Demo" link on slide 7).
|
||||
248
io2012-presentation/css/presentation.css
Executable file
@@ -0,0 +1,248 @@
|
||||
@font-face {
|
||||
font-family: 'Inconsolata';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Inconsolata'), url(../theme/inconsolata.woff) format('woff');
|
||||
}
|
||||
.link {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#remember_icons {
|
||||
padding: 100px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#remember_icons .icon {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
#remember_icons.build .to-build {
|
||||
-webkit-transform: translateY(300px);
|
||||
}
|
||||
|
||||
#remember_icons > * {
|
||||
-webkit-transition: all 0.5s ease-in-out 0.2s;
|
||||
transition: all 0.5s ease-in-out 0.2s;
|
||||
}
|
||||
|
||||
#ecosystem {
|
||||
background: url(../images/ecosystem.png) no-repeat bottom right;
|
||||
background-size: 350px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#ecosystem_blocks {
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
#ecosystem_blocks > * {
|
||||
-webkit-transition: all 0.5s ease-in-out 0.2s;
|
||||
}
|
||||
|
||||
.ecoblock {
|
||||
display: block;
|
||||
min-height: 100px;
|
||||
width: 600px;
|
||||
vertical-align: top;
|
||||
Xcolor: #515151;
|
||||
}
|
||||
|
||||
.ecoblock.to-build {
|
||||
-webkit-transform: rotateY(90deg);
|
||||
}
|
||||
|
||||
.ecoblock_left {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.vdivider {
|
||||
margin: 0px 10px;
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
height: 40px;
|
||||
background-color: #a9a9a9;
|
||||
}
|
||||
|
||||
.ecoblock_right {
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
vertical-align: top;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.ecoblock_right div {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ecoblock_right div:nth-child(1) {
|
||||
font-size: 40px;
|
||||
color: #4387fd;
|
||||
}
|
||||
|
||||
.ecoblock_right div:nth-child(2) {
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.bignumber {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#evolution_blocks {
|
||||
margin: 150px auto;
|
||||
}
|
||||
|
||||
#evolution_blocks > * {
|
||||
-webkit-transition: all 0.5s ease-in-out 0.2s;
|
||||
}
|
||||
|
||||
.evoblock {
|
||||
padding: 5px 0px 0px 0px;
|
||||
display: inline-block;
|
||||
min-height: 120px;
|
||||
width: 180px;
|
||||
vertical-align: top;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.evoblock.to-build {
|
||||
-XXwebkit-transform: rotateY(90deg);
|
||||
}
|
||||
|
||||
.bluebg {
|
||||
border-top: 10px solid #4387fd;
|
||||
}
|
||||
|
||||
.redbg {
|
||||
border-top: 10px solid #f44a3f;
|
||||
}
|
||||
|
||||
.greenbg {
|
||||
border-top: 10px solid #0da861;
|
||||
}
|
||||
|
||||
.yellowbg {
|
||||
border-top: 10px solid #ffd14d;
|
||||
}
|
||||
|
||||
.topborder hgroup {
|
||||
margin-top: -20px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.chromelogo {
|
||||
font-size: 60px;
|
||||
display: -webkit-box !important;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-align: center;
|
||||
-webkit-box-pack: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chromelogo img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#chrometext {
|
||||
position: relative;
|
||||
top: -30px;
|
||||
left: 10px;
|
||||
display: inline-block;
|
||||
letter-spacing: -3px;
|
||||
}
|
||||
|
||||
.spinner-demo {
|
||||
-webkit-transition: all .5s ease-in-out;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 90px;
|
||||
right: 10px;
|
||||
width: 640px;
|
||||
height: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.spinner-demo.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#spinner-input {
|
||||
-webkit-appearance:none !important;
|
||||
width: 400px;
|
||||
height: 6px;
|
||||
background: rgb(13, 168, 97);
|
||||
}
|
||||
|
||||
#spinner-input::-webkit-slider-thumb {
|
||||
-webkit-appearance:none !important;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 15px;
|
||||
background: rgb(67, 135, 253);
|
||||
}
|
||||
|
||||
#spinner-error {
|
||||
-webkit-transition: all .5s ease-in-out;
|
||||
opacity: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#spinner-error.visible {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
aside.demobutton {
|
||||
height: 67px;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: 125px;
|
||||
-webkit-border-radius: 10px 0px 0px 10px;
|
||||
background: -webkit-linear-gradient(left, #e6e6e6, #e6e6e6) no-repeat;
|
||||
-webkit-transition: all 0.1s ease-out 0.1s;
|
||||
padding: 30px 20px 0px 20px;
|
||||
}
|
||||
|
||||
.demobutton:after {
|
||||
content: "Demo";
|
||||
}
|
||||
|
||||
.demobutton:hover {
|
||||
color: #4387fd;
|
||||
}
|
||||
|
||||
.demobutton.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ul {
|
||||
margin-left: 1.2em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.li:before {
|
||||
content: '·';
|
||||
margin-left: -1em;
|
||||
position: absolute;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#sandbox img {
|
||||
height: 80px;
|
||||
margin: 20px 20px 20px 0px;
|
||||
display: block;
|
||||
}
|
||||
1485
io2012-presentation/css/theme.css
Executable file
13
io2012-presentation/diff-sample-files/new-manifest.json
Executable file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Hello world",
|
||||
"version": "0.0.1",
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["main.js"]
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"experimental"
|
||||
]
|
||||
}
|
||||
9
io2012-presentation/diff-sample-files/old-manifest.json
Executable file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "Hello world",
|
||||
"version": "0.0.1",
|
||||
"app": {
|
||||
"launch": {
|
||||
"local_path": "main.html"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
io2012-presentation/helloworld/main.js
Executable file
@@ -0,0 +1,6 @@
|
||||
chrome.experimental.app.onLaunched.addListener(function() {
|
||||
chrome.appWindow.create('window.html', {
|
||||
width: 400,
|
||||
height: 400
|
||||
});
|
||||
});
|
||||
13
io2012-presentation/helloworld/manifest.json
Executable file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Hello world",
|
||||
"version": "0.0.1",
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["main.js"]
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"experimental"
|
||||
]
|
||||
}
|
||||
56
io2012-presentation/helloworld/snippets
Executable file
@@ -0,0 +1,56 @@
|
||||
manifest:
|
||||
{
|
||||
"name": "Hello world",
|
||||
"version": "0.0.1",
|
||||
"manifest_version": 2,
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["main.js"]
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"appWindow",
|
||||
"experimental"
|
||||
]
|
||||
}
|
||||
|
||||
main.js:
|
||||
chrome.experimental.app.onLaunched.addListener(function() {
|
||||
chrome.appWindow.create('window.html', {
|
||||
width: 400,
|
||||
height: 400
|
||||
});
|
||||
});
|
||||
|
||||
window.html:
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Hello World</title>
|
||||
<script src="window.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<form id="main-form">
|
||||
<input id="your-name" size="30">
|
||||
<input type="submit">
|
||||
</form>
|
||||
|
||||
Your name is: <div id="output"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
window.js:
|
||||
onload = function() {
|
||||
document.getElementById('main-form').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
document.getElementById('output').innerHTML =
|
||||
document.getElementById('your-name').value;
|
||||
};
|
||||
};
|
||||
|
||||
XSS input
|
||||
<h1 onclick="document.body.style.background='red'">hello</1>
|
||||
18
io2012-presentation/helloworld/window.html
Executable file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Hello World</title>
|
||||
<script src="window.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<form id="main-form">
|
||||
<input id="your-name" size="30">
|
||||
<input type="submit">
|
||||
</form>
|
||||
|
||||
Your name is: <div id="output"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
8
io2012-presentation/helloworld/window.js
Executable file
@@ -0,0 +1,8 @@
|
||||
onload = function() {
|
||||
document.getElementById('main-form').onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
document.getElementById('output').innerHTML =
|
||||
document.getElementById('your-name').value;
|
||||
};
|
||||
};
|
||||
BIN
io2012-presentation/images/chrome-logo-tiny.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
io2012-presentation/images/chrome-logo.png
Executable file
|
After Width: | Height: | Size: 13 KiB |
232
io2012-presentation/images/chrome-logo.svg
Executable file
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 256 256"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
width="256"
|
||||
height="256"
|
||||
sodipodi:docname="Google_Chrome_2011_Logo SVG.svg">
|
||||
<metadata
|
||||
id="metadata69">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview67"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.140625"
|
||||
inkscape:cx="128"
|
||||
inkscape:cy="128"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="blueFade"
|
||||
x1="0%"
|
||||
y1="0%"
|
||||
x2="0%"
|
||||
y2="100%">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#81b4e0"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#0c5a94"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="redFade"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="128"
|
||||
y1="16"
|
||||
x2="128"
|
||||
y2="107">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#f06b59"
|
||||
id="stop12" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#df2227"
|
||||
id="stop14" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="greenFade"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="72"
|
||||
y1="225"
|
||||
x2="118"
|
||||
y2="149">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#388b41"
|
||||
id="stop17" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#4cb749"
|
||||
id="stop19" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="yellowFade"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="187"
|
||||
y1="220"
|
||||
x2="138"
|
||||
y2="102">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#e4b022"
|
||||
id="stop22" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="#fcd209"
|
||||
id="stop24" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="redShadow"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="80"
|
||||
y1="140"
|
||||
x2="80"
|
||||
y2="60">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="black"
|
||||
stop-opacity=".15"
|
||||
id="stop27" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="black"
|
||||
stop-opacity=".06"
|
||||
id="stop29" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="black"
|
||||
stop-opacity=".03"
|
||||
id="stop31" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="greenShadow"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="164"
|
||||
y1="161"
|
||||
x2="109"
|
||||
y2="217">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="black"
|
||||
stop-opacity=".15"
|
||||
id="stop34" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="black"
|
||||
stop-opacity=".06"
|
||||
id="stop36" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="black"
|
||||
stop-opacity=".03"
|
||||
id="stop38" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="yellowShadow"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="139"
|
||||
y1="179"
|
||||
x2="147"
|
||||
y2="100">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="black"
|
||||
stop-opacity=".15"
|
||||
id="stop41" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="black"
|
||||
stop-opacity=".06"
|
||||
id="stop43" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="black"
|
||||
stop-opacity=".03"
|
||||
id="stop45" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle
|
||||
cx="128"
|
||||
cy="128"
|
||||
r="110"
|
||||
fill="white"
|
||||
id="circle47" />
|
||||
<path
|
||||
fill="black"
|
||||
fill-opacity=".1"
|
||||
d="M 198 148 A 70 70 0 0 0 58 148 L 78 148 A 50 50 0 0 1 178 148"
|
||||
id="path49" />
|
||||
<circle
|
||||
fill="black"
|
||||
fill-opacity=".1"
|
||||
cx="133"
|
||||
cy="142"
|
||||
r="38"
|
||||
id="circle51" />
|
||||
<circle
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
r="41"
|
||||
fill="url(#blueFade)"
|
||||
id="circle53" />
|
||||
<path
|
||||
fill="url(#redFade)"
|
||||
d="M 228 78 A 112 112 0 0 0 35 65 L 80 143 A 50 50 0 0 1 127 78"
|
||||
id="path55" />
|
||||
<path
|
||||
fill="url(#greenFade)"
|
||||
d="M 35 65 A 112 112 0 0 0 119 239 L 166 159 A 50 50 0 0 1 80 143"
|
||||
id="path57" />
|
||||
<path
|
||||
fill="url(#yellowFade)"
|
||||
d="M 119 239 A 112 112 0 0 0 228 78 L 127 78 A 50 50 0 0 1 166 159"
|
||||
id="path59" />
|
||||
<path
|
||||
fill="url(#redShadow)"
|
||||
d="M 35 65 L 80 143 A 50 50 0 0 1 82 109 L 37 62"
|
||||
id="path61" />
|
||||
<path
|
||||
fill="url(#greenShadow)"
|
||||
d="M 119 239 L 166 159 A 50 50 0 0 1 137 176 L 117 239"
|
||||
id="path63" />
|
||||
<path
|
||||
fill="url(#yellowShadow)"
|
||||
d="M 228 78 L 127 78 A 50 50 0 0 1 166 97 L 230 81"
|
||||
id="path65" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
BIN
io2012-presentation/images/ecosystem.png
Executable file
|
After Width: | Height: | Size: 404 KiB |
BIN
io2012-presentation/images/icon-128.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
io2012-presentation/images/icon-16.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
io2012-presentation/images/io2012-logo.png
Executable file
|
After Width: | Height: | Size: 13 KiB |
BIN
io2012-presentation/images/remember-angry-birds.png
Executable file
|
After Width: | Height: | Size: 108 KiB |
BIN
io2012-presentation/images/remember-chrome.png
Executable file
|
After Width: | Height: | Size: 42 KiB |
BIN
io2012-presentation/images/remember-i18n.png
Executable file
|
After Width: | Height: | Size: 148 KiB |
BIN
io2012-presentation/images/remember-wallet.png
Executable file
|
After Width: | Height: | Size: 50 KiB |
BIN
io2012-presentation/images/technical-difficulties.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
80
io2012-presentation/js/main.js
Executable file
@@ -0,0 +1,80 @@
|
||||
var PRESENTATION_WIDTH = 900;
|
||||
var PRESENTATION_HEIGHT = 700;
|
||||
|
||||
var presentationWindow;
|
||||
|
||||
chrome.experimental.app.onLaunched.addListener(function() {
|
||||
if (presentationWindow && !presentationWindow.closed) {
|
||||
presentationWindow.chrome.appWindow.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
var left = Math.max((screen.width - PRESENTATION_WIDTH)/2, 0);
|
||||
var top = Math.max((screen.height - PRESENTATION_HEIGHT)/2, 0);
|
||||
|
||||
chrome.appWindow.create('presentation.html?presentme=true', {
|
||||
frame: 'chrome',
|
||||
left: left, top: top,
|
||||
width: PRESENTATION_WIDTH, height: PRESENTATION_HEIGHT,
|
||||
minWidth: PRESENTATION_WIDTH, minHeight: PRESENTATION_HEIGHT,
|
||||
maxWidth: PRESENTATION_WIDTH, maxHeight: PRESENTATION_HEIGHT
|
||||
}, function(w) {
|
||||
presentationWindow = w;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var windowingApiDemo = {
|
||||
windows: [],
|
||||
|
||||
clear: function() {
|
||||
if (windowingApiDemo.updateInterval) {
|
||||
clearInterval(windowingApiDemo.updateInterval);
|
||||
}
|
||||
|
||||
windowingApiDemo.windows.forEach(function(w) {w.close()});
|
||||
windowingApiDemo.windows = [];
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
windowingApiDemo.clear();
|
||||
chrome.appWindow.create('windowing_api/original.html', {
|
||||
top: 128,
|
||||
left: 128,
|
||||
width: 256,
|
||||
height: 256
|
||||
}, function(originalWindow) {
|
||||
windowingApiDemo.windows.push(originalWindow);
|
||||
chrome.appWindow.create('windowing_api/copycat.html', {
|
||||
top: 128,
|
||||
left: 384 + 5,
|
||||
width: 256,
|
||||
height: 256,
|
||||
frame: 'none'
|
||||
}, function(copycatWindow) {
|
||||
windowingApiDemo.windows.push(copycatWindow);
|
||||
|
||||
windowingApiDemo.updateInterval = setInterval(function() {
|
||||
if (originalWindow.closed || copycatWindow.closed) {
|
||||
windowingApiDemo.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
copycatWindow.moveTo(
|
||||
originalWindow.screenX + originalWindow.outerWidth + 5,
|
||||
originalWindow.screenY);
|
||||
copycatWindow.resizeTo(
|
||||
originalWindow.outerWidth,
|
||||
originalWindow.outerHeight);
|
||||
}, 10);
|
||||
|
||||
originalWindow.chrome.appWindow.focus();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
minimizeAll: function() {
|
||||
windowingApiDemo.windows.forEach(function(w) {w.chrome.appWindow.minimize()});
|
||||
setTimeout(windowingApiDemo.clear, 2000);
|
||||
}
|
||||
}
|
||||
93
io2012-presentation/js/servo.js
Executable file
@@ -0,0 +1,93 @@
|
||||
var servo = {
|
||||
|
||||
connectionId: -1,
|
||||
|
||||
onWrite: function(writeInfo) {
|
||||
if (writeInfo.bytesWritten == -1) {
|
||||
servo.fail('Could not write to serial device.');
|
||||
}
|
||||
},
|
||||
|
||||
setPosition: function(position) {
|
||||
var buffer = new ArrayBuffer(1);
|
||||
var uint8View = new Uint8Array(buffer);
|
||||
uint8View[0] = 48 + position;
|
||||
chrome.experimental.serial.write(servo.connectionId, buffer, servo.onWrite);
|
||||
},
|
||||
|
||||
onOpen: function(openInfo) {
|
||||
servo.connectionId = openInfo.connectionId;
|
||||
if (servo.connectionId == -1) {
|
||||
servo.fail('Could not open device');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('opened, got connection id: ' + servo.connectionId);
|
||||
servo.setPosition(0);
|
||||
},
|
||||
|
||||
onGetPorts: function(ports) {
|
||||
var eligiblePorts = ports.filter(function(port) {
|
||||
if (servo.shouldSkipPort(port)) {
|
||||
console.log('Skipping port ' + port);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('Maybe using port ' + port);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (eligiblePorts.length == 0) {
|
||||
servo.fail('Serial port not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
var port = eligiblePorts[eligiblePorts.length - 1];
|
||||
if (eligiblePorts.length > 1) {
|
||||
servo.fail(eligiblePorts.length + ' eligible ports found, trying ' + port);
|
||||
}
|
||||
|
||||
chrome.experimental.serial.open(port, servo.onOpen);
|
||||
},
|
||||
|
||||
onSliderChange: function() {
|
||||
var value = parseInt(this.value);
|
||||
servo.setPosition(value);
|
||||
},
|
||||
|
||||
shouldSkipPort: function(portName) {
|
||||
if (navigator.platform.indexOf('Linux') == 0) {
|
||||
return !portName.match(/ACM/);
|
||||
}
|
||||
return portName.match(/[Bb]luetooth/);
|
||||
},
|
||||
|
||||
init: function() {
|
||||
if (servo.connectionId != -1) {
|
||||
chrome.experimental.serial.close(servo.connectionId, function() {
|
||||
servo.connectionId = -1;
|
||||
servo.init();
|
||||
});
|
||||
return;
|
||||
}
|
||||
document.getElementById('spinner-error').classList.remove('visible');
|
||||
document.getElementById('spinner-input').onchange = servo.onSliderChange;
|
||||
|
||||
chrome.experimental.serial.getPorts(servo.onGetPorts);
|
||||
},
|
||||
|
||||
shutDown: function() {
|
||||
if (servo.connectionId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.experimental.serial.close(servo.connectionId, function() {
|
||||
servo.connectionId = -1;
|
||||
});
|
||||
},
|
||||
|
||||
fail: function(message) {
|
||||
document.getElementById('spinner-error').classList.add('visible');
|
||||
document.getElementById('spinner-error').textContent = message;
|
||||
}
|
||||
}
|
||||
31
io2012-presentation/js/slide-config.js
Executable file
@@ -0,0 +1,31 @@
|
||||
var SLIDE_CONFIG = {
|
||||
// Slide settings
|
||||
settings: {
|
||||
title: 'The Next Evolution of Chrome Apps',
|
||||
//subtitle: '',
|
||||
useBuilds: true, // Default: true. False will turn off slide animation builds.
|
||||
enableSlideAreas: false, // Default: true. False turns off the click areas on either slide of the slides.
|
||||
favIcon: 'images/chrome-logo-tiny.png',
|
||||
fonts: [
|
||||
'Open Sans:regular,semibold,italic,italicsemibold',
|
||||
'Inconsolata'
|
||||
],
|
||||
//theme: ['mytheme'], // Add your own custom themes or styles in /theme/css. Leave off the .css extension.
|
||||
},
|
||||
|
||||
// Author information
|
||||
presenters: [{
|
||||
name: 'Erik Kay',
|
||||
company: 'Engineering Manager',
|
||||
gplus: 'https://plus.google.com/106818816637347055983',
|
||||
github: 'http://github.com/erikkay'
|
||||
}, {
|
||||
name: 'Mihai Parparita',
|
||||
gplus: 'https://plus.google.com/111567061469336027617',
|
||||
company: 'Software Engineer',
|
||||
twitter: '@mihai',
|
||||
www: 'http://persistent.info',
|
||||
github: 'http://github.com/mihaip'
|
||||
}]
|
||||
};
|
||||
|
||||
58
io2012-presentation/js/slide-controller.js
Executable file
@@ -0,0 +1,58 @@
|
||||
(function(window) {
|
||||
|
||||
var ORIGIN_ = location.protocol + '//' + location.host;
|
||||
|
||||
function SlideController() {
|
||||
this.popup = null;
|
||||
this.isPopup = window.opener;
|
||||
|
||||
if (this.setupDone()) {
|
||||
window.addEventListener('message', this.onMessage_.bind(this), false);
|
||||
}
|
||||
}
|
||||
|
||||
SlideController.prototype.setupDone = function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
SlideController.prototype.onMessage_ = function(e) {
|
||||
var data = e.data;
|
||||
|
||||
// Restrict messages to being from this origin. Allow local developmet
|
||||
// from file:// though.
|
||||
// TODO: It would be dope if FF implemented location.origin!
|
||||
if (e.origin != ORIGIN_ && ORIGIN_.indexOf('file://') != 0) {
|
||||
alert('Someone tried to postMessage from an unknown origin');
|
||||
return;
|
||||
}
|
||||
|
||||
// if (e.source.location.hostname != 'localhost') {
|
||||
// alert('Someone tried to postMessage from an unknown origin');
|
||||
// return;
|
||||
// }
|
||||
|
||||
if ('keyCode' in data) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('keydown', true, true);
|
||||
evt.keyCode = data.keyCode;
|
||||
document.dispatchEvent(evt);
|
||||
}
|
||||
};
|
||||
|
||||
SlideController.prototype.sendMsg = function(msg) {
|
||||
// // Send message to popup window.
|
||||
// if (this.popup) {
|
||||
// this.popup.postMessage(msg, ORIGIN_);
|
||||
// }
|
||||
|
||||
// Send message to main window.
|
||||
if (this.isPopup) {
|
||||
// TODO: It would be dope if FF implemented location.origin.
|
||||
window.opener.postMessage(msg, '*');
|
||||
}
|
||||
};
|
||||
|
||||
window.SlideController = SlideController;
|
||||
|
||||
})(window);
|
||||
|
||||
688
io2012-presentation/js/slide-deck.js
Executable file
@@ -0,0 +1,688 @@
|
||||
/**
|
||||
* @authors Luke Mahe
|
||||
* @authors Eric Bidelman
|
||||
* @fileoverview TODO
|
||||
*/
|
||||
document.cancelFullScreen = document.webkitCancelFullScreen ||
|
||||
document.mozCancelFullScreen;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function SlideDeck(el) {
|
||||
this.curSlide_ = 0;
|
||||
this.prevSlide_ = 0;
|
||||
this.config_ = null;
|
||||
this.container = el || document.querySelector('slides');
|
||||
this.slides = [];
|
||||
this.controller = null;
|
||||
|
||||
this.getSavedSlideNumber_((function(slideNumber) {
|
||||
this.curSlide_ = slideNumber;
|
||||
this.init_();
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.SLIDE_CLASSES_ = [
|
||||
'far-past', 'past', 'current', 'next', 'far-next'];
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.CSS_DIR_ = 'theme/css/';
|
||||
|
||||
/**
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.loadSlide = function(slideNo) {
|
||||
if (slideNo) {
|
||||
this.curSlide_ = slideNo - 1;
|
||||
this.updateSlides_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.init_ = function(e) {
|
||||
document.body.classList.add('loaded'); // Add loaded class for templates to use.
|
||||
|
||||
this.slides = this.container.querySelectorAll('slide:not([hidden]):not(.backdrop)');
|
||||
|
||||
this.loadConfig_(SLIDE_CONFIG);
|
||||
this.addEventListeners_();
|
||||
this.updateSlides_();
|
||||
|
||||
// Add slide numbers and total slide count metadata to each slide.
|
||||
var that = this;
|
||||
for (var i = 0, slide; slide = this.slides[i]; ++i) {
|
||||
slide.dataset.slideNum = i + 1;
|
||||
slide.dataset.totalSlides = this.slides.length;
|
||||
|
||||
slide.addEventListener('click', function(e) {
|
||||
if (document.body.classList.contains('overview')) {
|
||||
that.loadSlide(this.dataset.slideNum);
|
||||
e.preventDefault();
|
||||
window.setTimeout(function() {
|
||||
that.toggleOverview();
|
||||
}, 500);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
// Note: this needs to come after addEventListeners_(), which adds a
|
||||
// 'keydown' listener that this controller relies on.
|
||||
this.controller = new SlideController(this);
|
||||
if (this.controller.isPopup) {
|
||||
document.body.classList.add('popup');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.addEventListeners_ = function() {
|
||||
document.addEventListener('keydown', this.onBodyKeyDown_.bind(this), false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Event} e
|
||||
*/
|
||||
SlideDeck.prototype.onBodyKeyDown_ = function(e) {
|
||||
if (/^(input|textarea)$/i.test(e.target.nodeName) ||
|
||||
e.target.isContentEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward keydowns to the main slides if we're the popup.
|
||||
if (this.controller && this.controller.isPopup) {
|
||||
this.controller.sendMsg({keyCode: e.keyCode});
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 13: // Enter
|
||||
if (document.body.classList.contains('overview')) {
|
||||
this.toggleOverview();
|
||||
}
|
||||
break;
|
||||
|
||||
case 39: // right arrow
|
||||
case 32: // space
|
||||
case 34: // PgDn
|
||||
this.nextSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 37: // left arrow
|
||||
case 8: // Backspace
|
||||
case 33: // PgUp
|
||||
this.prevSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 40: // down arrow
|
||||
this.nextSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 38: // up arrow
|
||||
this.prevSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 72: // H: Toggle code highlighting
|
||||
document.body.classList.toggle('highlight-code');
|
||||
break;
|
||||
|
||||
case 79: // O: Toggle overview
|
||||
this.toggleOverview();
|
||||
break;
|
||||
|
||||
case 80: // P
|
||||
if (this.controller && this.controller.isPopup) {
|
||||
document.body.classList.toggle('with-notes');
|
||||
} else if (this.controller && !this.controller.popup) {
|
||||
document.body.classList.toggle('with-notes');
|
||||
}
|
||||
break;
|
||||
|
||||
case 82: // R
|
||||
// TODO: implement refresh on main slides when popup is refreshed.
|
||||
break;
|
||||
|
||||
case 27: // ESC: Hide notes and highlighting
|
||||
document.body.classList.remove('with-notes');
|
||||
document.body.classList.remove('highlight-code');
|
||||
|
||||
if (document.body.classList.contains('overview')) {
|
||||
this.toggleOverview();
|
||||
}
|
||||
break;
|
||||
|
||||
case 70: // F: Toggle fullscreen
|
||||
// Only respect 'f' on body. Don't want to capture keys from an <input>.
|
||||
// Also, ignore browser's fullscreen shortcut (cmd+shift+f) so we don't
|
||||
// get trapped in fullscreen!
|
||||
if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
|
||||
if (document.mozFullScreen !== undefined && !document.mozFullScreen) {
|
||||
document.body.mozRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else if (document.webkitIsFullScreen !== undefined && !document.webkitIsFullScreen) {
|
||||
document.body.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else {
|
||||
document.cancelFullScreen();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 87: // W: Toggle widescreen
|
||||
// Only respect 'w' on body. Don't want to capture keys from an <input>.
|
||||
if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
|
||||
this.container.classList.toggle('layout-widescreen');
|
||||
}
|
||||
break;
|
||||
|
||||
case 77: // M: Minimize
|
||||
chrome.appWindow.minimize();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SlideDeck.prototype.focusOverview_ = function() {
|
||||
var overview = document.body.classList.contains('overview');
|
||||
|
||||
for (var i = 0, slide; slide = this.slides[i]; i++) {
|
||||
slide.style.webkitTransform = overview ?
|
||||
'translateZ(-2500px) translate(' + (( i - this.curSlide_ ) * 105) +
|
||||
'%, 0%)' : '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
SlideDeck.prototype.toggleOverview = function() {
|
||||
document.body.classList.toggle('overview');
|
||||
|
||||
this.focusOverview_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.loadConfig_ = function(config) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.config_ = config;
|
||||
|
||||
var settings = this.config_.settings;
|
||||
|
||||
this.loadTheme_(settings.theme || []);
|
||||
|
||||
if (settings.favIcon) {
|
||||
this.addFavIcon_(settings.favIcon);
|
||||
}
|
||||
|
||||
if (settings.fonts) {
|
||||
this.addFonts_(settings.fonts);
|
||||
}
|
||||
|
||||
// Builds. Default to on.
|
||||
if (!!!('useBuilds' in settings) || settings.useBuilds) {
|
||||
this.makeBuildLists_();
|
||||
}
|
||||
|
||||
if (settings.title) {
|
||||
document.title = settings.title.replace(/<br\/?>/, ' ') + ' - Google IO 2012';
|
||||
document.querySelector('[data-config-title]').innerHTML = settings.title;
|
||||
}
|
||||
|
||||
if (settings.subtitle) {
|
||||
document.querySelector('[data-config-subtitle]').innerHTML = settings.subtitle;
|
||||
}
|
||||
|
||||
if (this.config_.presenters) {
|
||||
var presenters = this.config_.presenters;
|
||||
var dataConfigContact = document.querySelector('[data-config-contact]');
|
||||
|
||||
var html = [];
|
||||
if (presenters.length == 1) {
|
||||
var p = presenters[0];
|
||||
|
||||
html = [p.name, p.company].join('<br>');
|
||||
|
||||
var gplus = p.gplus ? '<span>g+</span><a href="' + p.gplus +
|
||||
'">' + p.gplus.replace(/https?:\/\//, '') + '</a>' : '';
|
||||
|
||||
var twitter = p.twitter ? '<span>twitter</span>' +
|
||||
'<a href="http://twitter.com/' + p.twitter + '">' +
|
||||
p.twitter + '</a>' : '';
|
||||
|
||||
var www = p.www ? '<span>www</span><a href="' + p.www +
|
||||
'">' + p.www.replace(/https?:\/\//, '') + '</a>' : '';
|
||||
|
||||
var github = p.github ? '<span>github</span><a href="' + p.github +
|
||||
'">' + p.github.replace(/https?:\/\//, '') + '</a>' : '';
|
||||
|
||||
var html2 = [gplus, twitter, www, github].join('<br>');
|
||||
|
||||
if (dataConfigContact) {
|
||||
dataConfigContact.innerHTML = html2;
|
||||
}
|
||||
} else {
|
||||
for (var i = 0, p; p = presenters[i]; ++i) {
|
||||
html.push(p.name + ' - ' + p.company);
|
||||
}
|
||||
html = html.join('<br>');
|
||||
if (dataConfigContact) {
|
||||
dataConfigContact.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
var dataConfigPresenter = document.querySelector('[data-config-presenter]');
|
||||
if (dataConfigPresenter) {
|
||||
document.querySelector('[data-config-presenter]').innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
/* Left/Right tap areas. Default to including. */
|
||||
if (!!!('enableSlideAreas' in settings) || settings.enableSlideAreas) {
|
||||
var el = document.createElement('div');
|
||||
el.classList.add('slide-area');
|
||||
el.id = 'prev-slide-area';
|
||||
el.addEventListener('click', this.prevSlide.bind(this), false);
|
||||
this.container.appendChild(el);
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.classList.add('slide-area');
|
||||
el.id = 'next-slide-area';
|
||||
el.addEventListener('click', this.nextSlide.bind(this), false);
|
||||
this.container.appendChild(el);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Array.<string>} fonts
|
||||
*/
|
||||
SlideDeck.prototype.addFonts_ = function(fonts) {
|
||||
/* You need to add local fonts manually due to CSP.
|
||||
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'stylesheet';
|
||||
el.href = ('https:' == document.location.protocol ? 'https' : 'http') +
|
||||
'://fonts.googleapis.com/css?family=' + fonts.join('|') + '&v2';
|
||||
document.querySelector('head').appendChild(el);
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.buildNextItem_ = function() {
|
||||
var slide = this.slides[this.curSlide_];
|
||||
var toBuild = slide.querySelector('.to-build');
|
||||
var built = slide.querySelector('.build-current');
|
||||
|
||||
if (built) {
|
||||
built.classList.remove('build-current');
|
||||
if (built.classList.contains('fade')) {
|
||||
built.classList.add('build-fade');
|
||||
}
|
||||
}
|
||||
|
||||
if (!toBuild) {
|
||||
var items = slide.querySelectorAll('.build-fade');
|
||||
for (var j = 0, item; item = items[j]; j++) {
|
||||
item.classList.remove('build-fade');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toBuild.classList.remove('to-build');
|
||||
toBuild.classList.add('build-current');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
SlideDeck.prototype.prevSlide = function() {
|
||||
if (this.curSlide_ > 0) {
|
||||
var bodyClassList = document.body.classList;
|
||||
bodyClassList.remove('highlight-code');
|
||||
|
||||
// Toggle off speaker notes if they're showing when we move backwards on the
|
||||
// main slides. If we're the speaker notes popup, leave them up.
|
||||
if (this.controller && !this.controller.isPopup) {
|
||||
bodyClassList.remove('with-notes');
|
||||
} else if (!this.controller) {
|
||||
bodyClassList.remove('with-notes');
|
||||
}
|
||||
|
||||
this.prevSlide_ = this.curSlide_--;
|
||||
|
||||
this.updateSlides_();
|
||||
}
|
||||
};
|
||||
|
||||
SlideDeck.prototype.nextSlide = function() {
|
||||
if (!document.body.classList.contains('overview') && this.buildNextItem_()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.curSlide_ < this.slides.length - 1) {
|
||||
var bodyClassList = document.body.classList;
|
||||
bodyClassList.remove('highlight-code');
|
||||
|
||||
// Toggle off speaker notes if they're showing when we advanced on the main
|
||||
// slides. If we're the speaker notes popup, leave them up.
|
||||
if (this.controller && !this.controller.isPopup) {
|
||||
bodyClassList.remove('with-notes');
|
||||
} else if (!this.controller) {
|
||||
bodyClassList.remove('with-notes');
|
||||
}
|
||||
|
||||
this.prevSlide_ = this.curSlide_++;
|
||||
|
||||
this.updateSlides_();
|
||||
}
|
||||
};
|
||||
|
||||
/* Slide events */
|
||||
|
||||
/**
|
||||
* Triggered when a slide enter/leave event should be dispatched.
|
||||
*
|
||||
* @param {string} type The type of event to trigger
|
||||
* (e.g. 'slideenter', 'slideleave').
|
||||
* @param {number} slideNo The index of the slide that is being left.
|
||||
*/
|
||||
SlideDeck.prototype.triggerSlideEvent = function(type, slideNo) {
|
||||
var el = this.getSlideEl_(slideNo);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call onslideenter/onslideleave if the attribute is defined on this slide.
|
||||
var func = el.getAttribute(type);
|
||||
if (func) {
|
||||
new Function(func).call(el); // TODO: Don't use new Function() :(
|
||||
}
|
||||
|
||||
// Dispatch event to listeners setup using addEventListener.
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent(type, true, true);
|
||||
evt.slideNumber = slideNo + 1; // Make it readable
|
||||
evt.slide = el;
|
||||
|
||||
el.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.updateSlides_ = function() {
|
||||
var curSlide = this.curSlide_;
|
||||
for (var i = 0; i < this.slides.length; ++i) {
|
||||
switch (i) {
|
||||
case curSlide - 2:
|
||||
this.updateSlideClass_(i, 'far-past');
|
||||
break;
|
||||
case curSlide - 1:
|
||||
this.updateSlideClass_(i, 'past');
|
||||
break;
|
||||
case curSlide:
|
||||
this.updateSlideClass_(i, 'current');
|
||||
break;
|
||||
case curSlide + 1:
|
||||
this.updateSlideClass_(i, 'next');
|
||||
break;
|
||||
case curSlide + 2:
|
||||
this.updateSlideClass_(i, 'far-next');
|
||||
break;
|
||||
default:
|
||||
this.updateSlideClass_(i);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
this.triggerSlideEvent('slideleave', this.prevSlide_);
|
||||
this.triggerSlideEvent('slideenter', curSlide);
|
||||
|
||||
// window.setTimeout(this.disableSlideFrames_.bind(this, curSlide - 2), 301);
|
||||
//
|
||||
// this.enableSlideFrames_(curSlide - 1); // Previous slide.
|
||||
// this.enableSlideFrames_(curSlide + 1); // Current slide.
|
||||
// this.enableSlideFrames_(curSlide + 2); // Next slide.
|
||||
|
||||
// Enable current slide's iframes (needed for page loat at current slide).
|
||||
this.enableSlideFrames_(curSlide + 1);
|
||||
|
||||
// No way to tell when all slide transitions + auto builds are done.
|
||||
// Give ourselves a good buffer to preload the next slide's iframes.
|
||||
window.setTimeout(this.enableSlideFrames_.bind(this, curSlide + 2), 1000);
|
||||
|
||||
this.saveSlideNumber_();
|
||||
|
||||
if (document.body.classList.contains('overview')) {
|
||||
this.focusOverview_();
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.enableSlideFrames_ = function(slideNo) {
|
||||
var el = this.slides[slideNo - 1];
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frames = el.querySelectorAll('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
this.enableFrame_(frame);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.enableFrame_ = function(frame) {
|
||||
var src = frame.dataset.src;
|
||||
if (src && frame.src != src) {
|
||||
frame.src = src;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.disableSlideFrames_ = function(slideNo) {
|
||||
var el = this.slides[slideNo - 1];
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frames = el.querySelectorAll('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
this.disableFrame_(frame);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Node} frame
|
||||
*/
|
||||
SlideDeck.prototype.disableFrame_ = function(frame) {
|
||||
frame.src = 'about:blank';
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.getSlideEl_ = function(no) {
|
||||
if ((no < 0) || (no >= this.slides.length)) {
|
||||
return null;
|
||||
} else {
|
||||
return this.slides[no];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
* @param {string} className
|
||||
*/
|
||||
SlideDeck.prototype.updateSlideClass_ = function(slideNo, className) {
|
||||
var el = this.getSlideEl_(slideNo);
|
||||
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (className) {
|
||||
el.classList.add(className);
|
||||
}
|
||||
|
||||
for (var i = 0, slideClass; slideClass = this.SLIDE_CLASSES_[i]; ++i) {
|
||||
if (className != slideClass) {
|
||||
el.classList.remove(slideClass);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.makeBuildLists_ = function () {
|
||||
for (var i = this.curSlide_, slide; slide = this.slides[i]; ++i) {
|
||||
var items = slide.querySelectorAll('.build > *');
|
||||
for (var j = 0, item; item = items[j]; ++j) {
|
||||
if (item.classList) {
|
||||
item.classList.add('to-build');
|
||||
if (item.parentNode.classList.contains('fade')) {
|
||||
item.classList.add('fade');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the current slide into persistent storage.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.saveSlideNumber_ = function() {
|
||||
this.setStoredValue_(StorageKey.SLIDE_NUMBER, this.curSlide_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current slide from persistent storage.
|
||||
*
|
||||
* @private
|
||||
*/SlideDeck.prototype.getSavedSlideNumber_ = function(callback) {
|
||||
this.getStoredValue_(StorageKey.SLIDE_NUMBER, function(slideNumber) {
|
||||
callback(slideNumber || 0);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Persistent storage keys.
|
||||
*
|
||||
* @enum{string}
|
||||
*/
|
||||
var StorageKey = {
|
||||
SLIDE_NUMBER: 'slide-number'
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves data into persistent storage.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {*} value
|
||||
* @param {Function} opt_callback
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.setStoredValue_ = function(key, value, opt_callback) {
|
||||
var data = {};
|
||||
data[key] = value;
|
||||
if (chrome.storage) {
|
||||
chrome.storage.local.set(data, opt_callback);
|
||||
} else {
|
||||
// We're running in a web page, not an app.
|
||||
if (opt_callback) {
|
||||
opt_callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets data from persistent storage.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {Function.<*>} opt_callback
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.getStoredValue_ = function(key, callback) {
|
||||
if (chrome.storage) {
|
||||
chrome.storage.local.get(key, function(data) {
|
||||
callback(data[key]);
|
||||
});
|
||||
} else {
|
||||
// We're running in a web page, not an app.
|
||||
callback("1");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} favIcon
|
||||
*/
|
||||
SlideDeck.prototype.addFavIcon_ = function(favIcon) {
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'icon';
|
||||
el.type = 'image/png';
|
||||
el.href = favIcon;
|
||||
document.querySelector('head').appendChild(el);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} theme
|
||||
*/
|
||||
SlideDeck.prototype.loadTheme_ = function(theme) {
|
||||
var styles = [];
|
||||
if (theme.constructor.name === 'String') {
|
||||
styles.push(theme);
|
||||
} else {
|
||||
styles = theme;
|
||||
}
|
||||
|
||||
for (var i = 0, style; themeUrl = styles[i]; i++) {
|
||||
var style = document.createElement('link');
|
||||
style.rel = 'stylesheet';
|
||||
style.type = 'text/css';
|
||||
if (themeUrl.indexOf('http') == -1) {
|
||||
style.href = this.CSS_DIR_ + themeUrl + '.css';
|
||||
} else {
|
||||
style.href = themeUrl;
|
||||
}
|
||||
document.querySelector('head').appendChild(style);
|
||||
}
|
||||
};
|
||||
|
||||
var slidedeck = new SlideDeck();
|
||||
49
io2012-presentation/js/slides.js
Executable file
@@ -0,0 +1,49 @@
|
||||
var spinnerDemoRunning = false;
|
||||
|
||||
document.getElementById('windows-demo').onclick = function() {
|
||||
opener.windowingApiDemo.launch();
|
||||
};
|
||||
|
||||
document.getElementById('exit-fullscreen').onclick = function() {
|
||||
document.webkitCancelFullScreen();
|
||||
};
|
||||
|
||||
var minimizeAndHideButton = function(e) {
|
||||
chrome.appWindow.minimize();
|
||||
e.target.classList.toggle('hidden');
|
||||
};
|
||||
|
||||
var minimize = function () { chrome.appWindow.minimize(); };
|
||||
|
||||
document.getElementById("apis-slide").addEventListener("slideleave", function() {
|
||||
if (spinnerDemoRunning) {
|
||||
servo.shutDown();
|
||||
spinnerDemoRunning = false;
|
||||
document.getElementById('spinner-demo').classList.remove('visible')
|
||||
document.getElementById('technical-difficulties').classList.remove('visible');
|
||||
}
|
||||
document.getElementById('spinner-demo-button').classList.remove('hidden');
|
||||
});
|
||||
|
||||
document.getElementById('offline-demo').onclick = minimize;
|
||||
document.getElementById('programming-demo').onclick = minimize;
|
||||
document.getElementById('security-demo').onclick = minimize;
|
||||
document.getElementById('spinner-demo-button2').onclick = minimize;
|
||||
|
||||
document.getElementById('spinner-demo-button').onclick = function() {
|
||||
document.getElementById('spinner-demo-button').classList.add('hidden');
|
||||
if (!spinnerDemoRunning) {
|
||||
navigator.webkitGetUserMedia({video: true}, function(stream) {
|
||||
document.getElementById('camera-output').src =
|
||||
webkitURL.createObjectURL(stream);
|
||||
servo.init();
|
||||
spinnerDemoRunning = true;
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('spinner-demo').classList.add('visible');
|
||||
}, 1000);
|
||||
}, function(e) {
|
||||
document.getElementById('technical-difficulties').classList.add('visible');
|
||||
});
|
||||
}
|
||||
};
|
||||
20
io2012-presentation/manifest.json
Executable file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Apps IO 2012",
|
||||
"description": "Chrome Apps IO 2012 presentation.",
|
||||
"version": "1.1",
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["js/main.js"]
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "images/icon-16.png",
|
||||
"128": "images/icon-128.png"
|
||||
},
|
||||
"permissions": [
|
||||
"experimental",
|
||||
"storage",
|
||||
"videoCapture"
|
||||
]
|
||||
}
|
||||
294
io2012-presentation/presentation.html
Executable file
@@ -0,0 +1,294 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Google IO 2012</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="css/theme.css">
|
||||
<link rel="stylesheet" href="css/presentation.css">
|
||||
</head>
|
||||
<body style="opacity: 0">
|
||||
|
||||
<!-- removed class="layout-widescreen" since we're using a chromebook -->
|
||||
<slides>
|
||||
|
||||
<slide class="logoslide nobackground">
|
||||
<article class="chromelogo">
|
||||
<img src="images/chrome-logo.png"><div id="chrometext">chrome</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide class="title-slide segue nobackground">
|
||||
<aside class="gdbar"><img src="images/chrome-logo.png"></aside>
|
||||
<!-- The content of this hgroup is replaced programmatically through the slide_config.json. -->
|
||||
<hgroup class="auto-fadein">
|
||||
<h1 data-config-title><!-- populated from slide_config.json --></h1>
|
||||
<h2 data-config-subtitle><!-- populated from slide_config.json --></h2>
|
||||
<p data-config-presenter><!-- populated from slide_config.json --></p>
|
||||
</hgroup>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>Remember Google I/O 2011?</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div id="remember_icons" class="build">
|
||||
<img class="icon" src="images/remember-chrome.png" alt="Chrome reached 160 million users">
|
||||
<img class="icon" src="images/remember-wallet.png" alt="Launched in-app payments">
|
||||
<img class="icon" src="images/remember-i18n.png" alt="Launched the store in 42 languages">
|
||||
<img class="icon" src="images/remember-angry-birds.png" alt="Launched Angry Birds in Chrome">
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>Ecosystem Is Thriving</h2>
|
||||
</hgroup>
|
||||
<article id="ecosystem">
|
||||
<div id="ecosystem_blocks" class="build">
|
||||
<div class="ecoblock">
|
||||
<div class="ecoblock_left">Native Client</div>
|
||||
<div class="vdivider"></div>
|
||||
<div class="ecoblock_right">Amazing Games</div>
|
||||
</div>
|
||||
<div class="ecoblock">
|
||||
<div class="ecoblock_left">Web Store</div>
|
||||
<div class="vdivider"></div>
|
||||
<div class="ecoblock_right">
|
||||
<div>750</div>
|
||||
<div>
|
||||
<span class="blue">Million</span><br/>App Installs
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ecoblock">
|
||||
<div class="ecoblock_left">Chrome</div>
|
||||
<div class="vdivider"></div>
|
||||
<div class="ecoblock_right">
|
||||
<div>310</div>
|
||||
<div>
|
||||
<span class="blue">Million</span><br/>Active Users
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>Apps Evolution</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div id="evolution_blocks" class="build">
|
||||
<div class="evoblock bluebg">
|
||||
Breaking Out of the Browser
|
||||
</div>
|
||||
<div class="evoblock redbg">
|
||||
Enhanced User Interface
|
||||
</div>
|
||||
<div class="evoblock yellowbg">
|
||||
Offline by Default
|
||||
</div>
|
||||
<div class="evoblock greenbg">
|
||||
New APIs
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide class="topborder">
|
||||
<hgroup class="bluebg">
|
||||
<h2>Breaking Out Of the Browser</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div class="ul build">
|
||||
<div class="li">Launch from outside of the browser.</div>
|
||||
<div class="li">First class OS windows (alt-tab, etc.)</div>
|
||||
<aside id="exit-fullscreen" class="demobutton"></aside>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide class="topborder">
|
||||
<hgroup class="redbg">
|
||||
<h2>Enhanced User Interface</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div class="ul build">
|
||||
<div class="li">Full control over multiple windows.</div>
|
||||
<div class="li">Custom window frame without browser chrome.</div>
|
||||
<div class="li"><a href="http://www.google.com" target="_blank">Links</a> open in browsers, not in the app.</div>
|
||||
<aside id="windows-demo" class="demobutton"></aside>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide class="topborder">
|
||||
<hgroup class="yellowbg">
|
||||
<h2>Offline by Default</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div class="ul build">
|
||||
<div class="li">Packaged app UI and logic is loaded and run locally.</div>
|
||||
<div class="li">Enforced separation of client UI and data.</div>
|
||||
<div class="li">APIs degrade gracefully when offline.</div>
|
||||
<div class="li">Apps are launched from outside of the browser.</div>
|
||||
<aside id="offline-demo" class="demobutton"></aside>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide id="apis-slide" class="topborder">
|
||||
<div id="spinner-demo" class="spinner-demo">
|
||||
<video id="camera-output" width="640" height="480" autoplay></video>
|
||||
<input id="spinner-input" type="range" min="0" max="9" value="0">
|
||||
<div id="spinner-error"></div>
|
||||
</div>
|
||||
|
||||
<div id="technical-difficulties" class="spinner-demo">
|
||||
<img src="images/technical-difficulties.png" width="640" height="480" alt="Please stand by">
|
||||
</div>
|
||||
|
||||
<hgroup class="greenbg">
|
||||
<h2>New APIs</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div class="ul build">
|
||||
<div class="li">System</div>
|
||||
<div class="li">Shared Data</div>
|
||||
<div class="li">Services</div>
|
||||
<aside id="spinner-demo-button" class="demobutton"></aside>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>The Programming Model</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div class="ul build">
|
||||
<div class="li">Packaged apps.</div>
|
||||
<div class="li">Background page as the hub.</div>
|
||||
<div class="li">App lifetime controlled by runtime; event-driven.</div>
|
||||
<aside id="programming-demo" class="demobutton"></aside>
|
||||
<div class="li">"Single-page", no navigation.</div>
|
||||
<div class="li">Some web features deprecated.</div>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>The Security Model</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div class="ul build">
|
||||
<div class="li">Process isolation.</div>
|
||||
<div class="li">Sandboxing.</div>
|
||||
<div class="li">Permissions model.</div>
|
||||
<div class="li">Content Security Policy (CSP).</div>
|
||||
<aside id="security-demo" class="demobutton"></aside>
|
||||
<div class="li">Storage isolation.</div>
|
||||
<div class="li">Explicit shared data APIs.</div>
|
||||
<div class="li">No extension APIs.</div>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2><browser></h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div>
|
||||
<pre><browser src="http://news.google.com/" width="750" height="300"></pre>
|
||||
<object src="http://news.google.com/news/section?pz=1&cf=all&ned=us&topic=s&ict=ln" type="application/browser-plugin"
|
||||
width="750" height="400"></object>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>Take it For a Spin</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<img style="height: 500px; display:block; margin: auto;" src="images/chrome-logo.svg">
|
||||
<aside id="spinner-demo-button2" class="demobutton"></aside>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>Apps Evolved</h2>
|
||||
</hgroup>
|
||||
<article>
|
||||
<div id="evolution_blocks" class="build">
|
||||
<div>
|
||||
<div class="evoblock bluebg">
|
||||
Breaking Out of the Browser
|
||||
</div>
|
||||
<div class="evoblock redbg">
|
||||
Enhanced User Interface
|
||||
</div>
|
||||
<div class="evoblock yellowbg">
|
||||
Offline by Default
|
||||
</div>
|
||||
<div class="evoblock greenbg">
|
||||
New APIs
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 50px;">· Available for testing on Canary</div>
|
||||
<div style="margin-top: 10px;">· System Applications working group</div>
|
||||
<div style="margin-top: 10px;">· Mobile coming</div>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide>
|
||||
<hgroup>
|
||||
<h2>Check Out More Demos</h2>
|
||||
</hgroup>
|
||||
<article id="sandbox">
|
||||
<div class="ul">
|
||||
<div class="li">Media player - Sencha</div>
|
||||
<div class="li">Photobooth - Kendo UI</div>
|
||||
<div class="li">Text Editor - AngularJS (Google)</div>
|
||||
<div class="li">"Johnny" - Google</div>
|
||||
<div class="li">github.com/GoogleChrome</div>
|
||||
</div>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide class="thank-you-slide segue nobackground">
|
||||
<aside class="gdbar right"><img src="images/chrome-logo.png"></aside>
|
||||
<article class="flexbox vleft auto-fadein">
|
||||
<h2>Thank You!</h2>
|
||||
<p>Try out the developer preview and send us feedback.</p>
|
||||
<p>developer.chrome.com/apps</p>
|
||||
<p>chromium-apps@chromium.org</p>
|
||||
<p>#chromium-apps (freenode)</p>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
<slide class="logoslide dark nobackground">
|
||||
<article class="chromelogo">
|
||||
<img src="images/chrome-logo.png"><div id="chrometext">chrome</div>
|
||||
</article>
|
||||
</slide>
|
||||
</slide>
|
||||
|
||||
<slide class="backdrop"></slide>
|
||||
|
||||
</slides>
|
||||
|
||||
<script src="js/slide-config.js"></script>
|
||||
<script src="js/slide-controller.js"></script>
|
||||
<script src="js/slide-deck.js"></script>
|
||||
<script src="js/servo.js"></script>
|
||||
<script src="js/slides.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
io2012-presentation/servo/background.js
Executable file
@@ -0,0 +1,8 @@
|
||||
chrome.experimental.app.onLaunched.addListener(function() {
|
||||
chrome.appWindow.create('main.html', {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 640,
|
||||
height: 720
|
||||
});
|
||||
})
|
||||
232
io2012-presentation/servo/chrome-logo.svg
Executable file
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 256 256"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
width="256"
|
||||
height="256"
|
||||
sodipodi:docname="Google_Chrome_2011_Logo SVG.svg">
|
||||
<metadata
|
||||
id="metadata69">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview67"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.140625"
|
||||
inkscape:cx="128"
|
||||
inkscape:cy="128"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="blueFade"
|
||||
x1="0%"
|
||||
y1="0%"
|
||||
x2="0%"
|
||||
y2="100%">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#81b4e0"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#0c5a94"
|
||||
id="stop9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="redFade"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="128"
|
||||
y1="16"
|
||||
x2="128"
|
||||
y2="107">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#f06b59"
|
||||
id="stop12" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#df2227"
|
||||
id="stop14" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="greenFade"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="72"
|
||||
y1="225"
|
||||
x2="118"
|
||||
y2="149">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#388b41"
|
||||
id="stop17" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="#4cb749"
|
||||
id="stop19" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="yellowFade"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="187"
|
||||
y1="220"
|
||||
x2="138"
|
||||
y2="102">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="#e4b022"
|
||||
id="stop22" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="#fcd209"
|
||||
id="stop24" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="redShadow"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="80"
|
||||
y1="140"
|
||||
x2="80"
|
||||
y2="60">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="black"
|
||||
stop-opacity=".15"
|
||||
id="stop27" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="black"
|
||||
stop-opacity=".06"
|
||||
id="stop29" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="black"
|
||||
stop-opacity=".03"
|
||||
id="stop31" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="greenShadow"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="164"
|
||||
y1="161"
|
||||
x2="109"
|
||||
y2="217">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="black"
|
||||
stop-opacity=".15"
|
||||
id="stop34" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="black"
|
||||
stop-opacity=".06"
|
||||
id="stop36" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="black"
|
||||
stop-opacity=".03"
|
||||
id="stop38" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="yellowShadow"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="139"
|
||||
y1="179"
|
||||
x2="147"
|
||||
y2="100">
|
||||
<stop
|
||||
offset="0%"
|
||||
stop-color="black"
|
||||
stop-opacity=".15"
|
||||
id="stop41" />
|
||||
<stop
|
||||
offset="30%"
|
||||
stop-color="black"
|
||||
stop-opacity=".06"
|
||||
id="stop43" />
|
||||
<stop
|
||||
offset="100%"
|
||||
stop-color="black"
|
||||
stop-opacity=".03"
|
||||
id="stop45" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle
|
||||
cx="128"
|
||||
cy="128"
|
||||
r="110"
|
||||
fill="white"
|
||||
id="circle47" />
|
||||
<path
|
||||
fill="black"
|
||||
fill-opacity=".1"
|
||||
d="M 198 148 A 70 70 0 0 0 58 148 L 78 148 A 50 50 0 0 1 178 148"
|
||||
id="path49" />
|
||||
<circle
|
||||
fill="black"
|
||||
fill-opacity=".1"
|
||||
cx="133"
|
||||
cy="142"
|
||||
r="38"
|
||||
id="circle51" />
|
||||
<circle
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
r="41"
|
||||
fill="url(#blueFade)"
|
||||
id="circle53" />
|
||||
<path
|
||||
fill="url(#redFade)"
|
||||
d="M 228 78 A 112 112 0 0 0 35 65 L 80 143 A 50 50 0 0 1 127 78"
|
||||
id="path55" />
|
||||
<path
|
||||
fill="url(#greenFade)"
|
||||
d="M 35 65 A 112 112 0 0 0 119 239 L 166 159 A 50 50 0 0 1 80 143"
|
||||
id="path57" />
|
||||
<path
|
||||
fill="url(#yellowFade)"
|
||||
d="M 119 239 A 112 112 0 0 0 228 78 L 127 78 A 50 50 0 0 1 166 159"
|
||||
id="path59" />
|
||||
<path
|
||||
fill="url(#redShadow)"
|
||||
d="M 35 65 L 80 143 A 50 50 0 0 1 82 109 L 37 62"
|
||||
id="path61" />
|
||||
<path
|
||||
fill="url(#greenShadow)"
|
||||
d="M 119 239 L 166 159 A 50 50 0 0 1 137 176 L 117 239"
|
||||
id="path63" />
|
||||
<path
|
||||
fill="url(#yellowShadow)"
|
||||
d="M 228 78 L 127 78 A 50 50 0 0 1 166 97 L 230 81"
|
||||
id="path65" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
33
io2012-presentation/servo/main.html
Executable file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="servo.js"></script>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<label>
|
||||
Port:
|
||||
<select id="port-picker"></select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Status:
|
||||
<span id="status">Loading</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Input:
|
||||
<input id="position-input" type="range" min="0" max="9" value="0">
|
||||
</label>
|
||||
|
||||
<div id="image"></div>
|
||||
</div>
|
||||
|
||||
<div id="tv" class="off">
|
||||
<video id="camera-output" width="640" height="480" autoplay></video>
|
||||
<img src="technical-difficulties.png" width="640" height="480" alt="Please stand by">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
17
io2012-presentation/servo/manifest.json
Executable file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Servo",
|
||||
"version": "0.3",
|
||||
"manifest_version": 2,
|
||||
"description": "Show off serial functionality.",
|
||||
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": [ "background.js" ]
|
||||
}
|
||||
},
|
||||
|
||||
"permissions": [
|
||||
"experimental",
|
||||
"videoCapture"
|
||||
]
|
||||
}
|
||||
73
io2012-presentation/servo/servo.js
Executable file
@@ -0,0 +1,73 @@
|
||||
var connectionId = -1;
|
||||
|
||||
function setPosition(position) {
|
||||
var buffer = new ArrayBuffer(1);
|
||||
var uint8View = new Uint8Array(buffer);
|
||||
uint8View[0] = '0'.charCodeAt(0) + position;
|
||||
chrome.experimental.serial.write(connectionId, buffer, function() {});
|
||||
};
|
||||
|
||||
function onOpen(openInfo) {
|
||||
connectionId = openInfo.connectionId;
|
||||
if (connectionId == -1) {
|
||||
setStatus('Could not open');
|
||||
return;
|
||||
}
|
||||
setStatus('Connected');
|
||||
|
||||
setPosition(0);
|
||||
};
|
||||
|
||||
function setStatus(status) {
|
||||
document.getElementById('status').innerText = status;
|
||||
}
|
||||
|
||||
function buildPortPicker(ports) {
|
||||
var eligiblePorts = ports.filter(function(port) {
|
||||
return !port.match(/[Bb]luetooth/);
|
||||
});
|
||||
|
||||
var portPicker = document.getElementById('port-picker');
|
||||
eligiblePorts.forEach(function(port) {
|
||||
var portOption = document.createElement('option');
|
||||
portOption.value = portOption.innerText = port;
|
||||
portPicker.appendChild(portOption);
|
||||
});
|
||||
|
||||
portPicker.onchange = function() {
|
||||
if (connectionId != -1) {
|
||||
chrome.experimental.serial.close(connectionId, openSelectedPort);
|
||||
return;
|
||||
}
|
||||
openSelectedPort();
|
||||
};
|
||||
}
|
||||
|
||||
function openSelectedPort() {
|
||||
var portPicker = document.getElementById('port-picker');
|
||||
var selectedPort = portPicker.options[portPicker.selectedIndex].value;
|
||||
chrome.experimental.serial.open(selectedPort, onOpen);
|
||||
}
|
||||
|
||||
onload = function() {
|
||||
var tv = document.getElementById('tv');
|
||||
navigator.webkitGetUserMedia(
|
||||
{video: true},
|
||||
function(stream) {
|
||||
tv.classList.add('working');
|
||||
document.getElementById('camera-output').src =
|
||||
webkitURL.createObjectURL(stream);
|
||||
},
|
||||
function() {
|
||||
tv.classList.add('broken');
|
||||
});
|
||||
|
||||
document.getElementById('position-input').onchange = function() {
|
||||
setPosition(parseInt(this.value, 10));
|
||||
};
|
||||
|
||||
chrome.experimental.serial.getPorts(function(ports) {
|
||||
buildPortPicker(ports)
|
||||
openSelectedPort();
|
||||
});
|
||||
};
|
||||
84
io2012-presentation/servo/styles.css
Executable file
@@ -0,0 +1,84 @@
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
font-family: "helvetica neue", helvetica, sans-serif;
|
||||
font-size: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
border-bottom: solid 1px #ddd;
|
||||
border-right: solid 1px #ddd;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
label {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#port-picker,
|
||||
#status {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#port-picker {
|
||||
max-width: 250px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#position-input {
|
||||
display: block;
|
||||
-webkit-appearance:none !important;
|
||||
width: 90%;
|
||||
margin: 20px auto;
|
||||
height: 6px;
|
||||
background: rgb(13, 168, 97);
|
||||
}
|
||||
|
||||
#position-input::-webkit-slider-thumb {
|
||||
-webkit-appearance:none !important;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 15px;
|
||||
background: rgb(67, 135, 253);
|
||||
}
|
||||
|
||||
#image {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: -10px;
|
||||
background-image: url(chrome-logo.svg);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
-webkit-transition: all .2s linear;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 640px;
|
||||
}
|
||||
|
||||
#tv {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
border-left: solid 1px #ddd;
|
||||
}
|
||||
|
||||
#tv video,
|
||||
#tv img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tv.working video,
|
||||
#tv.broken img {
|
||||
display: block;
|
||||
}
|
||||
BIN
io2012-presentation/servo/technical-difficulties.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
io2012-presentation/windowing_api/close-copycat.png
Executable file
|
After Width: | Height: | Size: 1.2 KiB |
23
io2012-presentation/windowing_api/copycat.html
Executable file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Untitled</title>
|
||||
<script src="window.js"></script>
|
||||
<link rel="stylesheet" href="window.css">
|
||||
</head>
|
||||
<body class="copycat">
|
||||
<div id="titlebar">
|
||||
<div id="close"></div>
|
||||
</div>
|
||||
|
||||
<h1>Bizarro World</h1>
|
||||
|
||||
<pre>
|
||||
<b> x</b>: <span id="screenX"></span>
|
||||
<b> y</b>: <span id="screenY"></span>
|
||||
<b> width</b>: <span id="innerWidth"></span>
|
||||
<b>height</b>: <span id="innerHeight"></span>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
24
io2012-presentation/windowing_api/original.html
Executable file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Untitled</title>
|
||||
<script src="window.js"></script>
|
||||
<link rel="stylesheet" href="window.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Original</h1>
|
||||
|
||||
<pre>
|
||||
<b> x</b>: <span id="screenX"></span>
|
||||
<b> y</b>: <span id="screenY"></span>
|
||||
<b> width</b>: <span id="innerWidth"></span>
|
||||
<b>height</b>: <span id="innerHeight"></span>
|
||||
</pre>
|
||||
|
||||
<p style="text-align: center">
|
||||
<button id="minimize-button">Minimize me!</button>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
55
io2012-presentation/windowing_api/window.css
Executable file
@@ -0,0 +1,55 @@
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.copycat {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.copycat #titlebar {
|
||||
height: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.copycat #close {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(close-copycat.png) no-repeat;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
background: #eee;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
border-top: solid 1px #ccc;
|
||||
border-bottom: solid 1px #ccc;
|
||||
}
|
||||
|
||||
.copycat h1 {
|
||||
background: #111;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
font-size: 35px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.copycat pre {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 12px 0;
|
||||
}
|
||||
25
io2012-presentation/windowing_api/window.js
Executable file
@@ -0,0 +1,25 @@
|
||||
onload = function() {
|
||||
function update() {
|
||||
['screenX', 'screenY', 'innerWidth', 'innerHeight'].forEach(function(prop) {
|
||||
document.getElementById(prop).innerText = window[prop];
|
||||
});
|
||||
|
||||
webkitRequestAnimationFrame(update);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
var minimizeNode = document.getElementById('minimize-button');
|
||||
if (minimizeNode) {
|
||||
minimizeNode.onclick = function() {
|
||||
opener.windowingApiDemo.minimizeAll();
|
||||
};
|
||||
}
|
||||
|
||||
var closeNode = document.getElementById('close');
|
||||
if (closeNode) {
|
||||
closeNode.onclick = function() {
|
||||
window.close();
|
||||
};
|
||||
}
|
||||
}
|
||||