I/O session presentation app.

This commit is contained in:
Mihai Parparita
2012-06-29 12:50:58 -07:00
parent 029721c139
commit 2c989fcf53
42 changed files with 3988 additions and 0 deletions

View 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).

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"manifest_version": 2,
"name": "Hello world",
"version": "0.0.1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": [
"experimental"
]
}

View File

@@ -0,0 +1,9 @@
{
"name": "Hello world",
"version": "0.0.1",
"app": {
"launch": {
"local_path": "main.html"
}
}
}

View File

@@ -0,0 +1,6 @@
chrome.experimental.app.onLaunched.addListener(function() {
chrome.appWindow.create('window.html', {
width: 400,
height: 400
});
});

View File

@@ -0,0 +1,13 @@
{
"manifest_version": 2,
"name": "Hello world",
"version": "0.0.1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": [
"experimental"
]
}

View 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>

View 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>

View 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;
};
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

80
io2012-presentation/js/main.js Executable file
View 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
View 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;
}
}

View 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'
}]
};

View 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);

View 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();

View 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');
});
}
};

View 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"
]
}

View 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>&lt;browser&gt;</h2>
</hgroup>
<article>
<div>
<pre>&lt;browser src="http://news.google.com/" width="750" height="300"&gt;</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>

View File

@@ -0,0 +1,8 @@
chrome.experimental.app.onLaunched.addListener(function() {
chrome.appWindow.create('main.html', {
top: 0,
left: 0,
width: 640,
height: 720
});
})

View 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

View 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>

View 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"
]
}

View 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();
});
};

View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View 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>

View 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>

View 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;
}

View 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();
};
}
}