>> Hello, everyone.
I'm Tim Hatcher.
Thanks for coming.
[ Applause ]
But it's had a long history.
It's been around since the '90s.
And it had a lot of momentum back in the '90s when it was first part of the browser wars, but it kind of sputtered and stalled.
And I'm happy to say that they finally started coming together and started releasing what is known as ECMAScript 6.
And this is just some of them.
There's some destructuring, maps, promises.
And let's go over some of these new features that are also now available in iOS 8 and OS X Yosemite.
So let's talk over some of these.
Let's first talk about some math additions.
You need these super fast math functions to do complex things like WebGL or complex scientific calculations.
And most of you will recognize a lot of things in this list that you've used in the past for your own applications.
So that's the math additions.
Now let's talk about maps and sets.
So, what do I mean by maps and sets?
This is something that you create as a generic object and it's really just a property bag.
You can throw whatever you want on this object.
You can set simple strings and any object can be the value.
And you can remove things from that map.
Well, these maps have some limitations.
There's string coercion when you're-when it comes to the key.
Everything that is the key needs to be coerced into a string, so it's not going to give you exactly what you want.
If you want to use it for a property bag that's tracking some node, it won't work.
And it does pretty much the same thing that you would do with an object.
It has a little bit different syntax and API, but it's pretty much all the same.
The most powerful thing you can do with the map object is you can use any value as the key.
And a lot of people will use this to track DOM nodes that they have in the page, adding some additional data on the side instead of tacking it onto the DOM node itself.
So it really allows you to compartmentalize data that you're tracking on the page.
But there are some issues with that.
And that means whenever the key disappears or gets garbage collected, it's also removed from the map.
So in this case, I have a node in my WeakMap and when that node is removed by doing a remove call or a removeChild in that page, it would no longer be in the map and the data that's associated with it will also get garbage collected.
So that's maps and sets.
And there's also a set object that I didn't cover but it's pretty straightforward.
It only allows the key to be added to the set.
So now let's talk about Destructuring.
It allows you to return like multiple objects in an array.
In this case, I have an array of two numbers here.
And traditionally, I would have to collect that in a result object.
And I would have to break each piece off from that array to use it in a separate variable that made more sense in the scope I was using it in.
But with destructuring, we can break that down into one line.
Now my function that's returning two results that maybe didn't make sense as array, but it was what I could use to return multiple things at once, I can now save those into the alpha and beta variables right there in one line.
And this might look like an array but when it's on-when it's on the left side, we're dealing with the destructuring syntax.
And it's not just for arrays.
You can destructure objects.
And this is when it becomes more of a pattern matching.
So in my example here, say I have a point object.
And normally, I would have to save it off into a result and break each piece out if I wanted to use some more concise syntax for those variables.
But now, I have object destructuring in this case.
And if I'd want a different variable name than the property, I can separate them by colons, so x is the value that's coming out of the object pattern matching and my x is the variable name in the local scope in this case.
But if I want to use the same name, I could omit the second piece and just use y.
And y is now a local variable in this scope.
And you can do some really fancy things that I don't examples for.
But you can deeply nest these pattern matches and objects with arrays and vice versa.
So you can really break apart your object if that-if that's what works for your application.
So that's destructuring.
Now let's talk about what we call the Spread Operator.
And this is something that is borrowed from many languages, but it comes in very, very handy and I love using it.
It's-It really takes away some of the common idioms that you would do before if you're building up arguments that you want to call in a function as an array, before you would have to resort to .call or .apply and pass in those arguments.
But now, with the Spread Operator which is represented by three dots prefixing an object.
And in this case it's-it's an array.
But secretly, the Spread Operator works on anything.
It even works on maps.
And it will break it out in key value sequences.
So, anything that's has support for iteration, which I'll talk about in a bit, can be spread.
And this will spread it out and call my function foo with the three arguments that are in the array.
You can now take your parts of a date and spread them across the date constructor instead of having to manually do 0, 1, 2 and break it out like that.
The Spread Operator is also useful in the construction of array literals.
Say you have something that's pretty repetitious.
You want to have a separate form and a combined form.
Normally you have to either repeat the data or push and pop and add everything, concatenate everything together.
But with the Spread Operator you can just spread those other arrays right out into the new array that you're constructing.
So this is a handy way to do pretty complex concatenation.
And you can understand what it's doing without having the parse different calls to different array functions.
So that's the Spread Operator.
Now let's talk about something new.
And you still might need to use this if you care about the index or the length.
And you're doing some complex operation based on those.
Some people have avoided this syntax because it's ugly.
And they use something for-called forEach on an array that lets you iterate over individual items and you handily get the item right there that you can work with.
But this creates a closure.
It does a lot of complex things that you might not want to deal with overhead-wise.
And this will iterate over every item in the array if you break early, it will stop.
But it starts at the first item.
But it's not just for arrays.
Say you have a set, you can iterate over that set.
And if you have a map you can iterate over the key and value.
Very similar to what you can do in Swift.
Now you can do it right here with for-of.
And for-of is the only way to iterate over the keys in values in maps.
For-in will not do what you expect and that-for reasons I'm not going to go into.
But maybe you want to iterate over a WeakMap.
That's not supported.
For reasons that involve the garbage collection, you cannot iterate over the keys and values of the WeakMap and this is part of the spec. So, that's for-of loops.
And say you have a function that you've written that loads a URL, maybe it does an XHR behind the scenes for you to wrap up all the different browser incompatibilities that are out there.
So this will load a URL and that's all it'll do.
You can't get the data.
To get the data, you might be inclined to do a return result that gives you the data that was loaded.
But this is synchronous.
You can't get away with this in a shipping product without blocking your user interface.
So, you have to resort to a callback function.
And it works and it's very usable and it's very handy.
But when it gets deeply nested and you're passing in multiple information and it just gets confusing and hard to follow.
You have all these load calls in this case.
Which one is handling which information?
Well, this is what Promises solved.
With Promises you can write something like this where the first load returns a promise.
And immediately on returning that promise, I can call the .then function.
And that's pretty much the only API you need to be concerned about with Promises.
And you can pass a call back to that .then function which will get called when that promise is fulfilled.
And that callback can return another promise.
And this where it's magical, you can chain-keep chaining along that.
So these callbacks are in line here just for clarity for you.
But they might be named functions that you have somewhere else.
And it makes it very clear that this is the order of operations.
So that's Promises.
And taking it to the next level you can then use another API, a global API, Promise.all.
And what this does is you can pass in an array of promises.
And it will return a new promise and that new promise that it returns is dependent on all of those promises finishing.
If any of them error out, your "then" object can get past the callback that gets called for errors.
But in this case, "done" will get called when only all of those promises succeed or fulfilled.
But maybe you only care about the first one.
You're loading a bunch of images and once you got one image you're ready to go.
That's where Promise.race comes in.
It will call your callback when the first one of those promises is fulfilled.
So that's a quick glance of Promises.
There's a lot that's being done in ECMAScript 6 that is not ready.
And it's going to be coming down the pipeline.
But these pieces we feel that you're ready to use in your application, in browsers that support them.
So now let's talk about the Web Inspector.
So if you're not familiar with the Web Inspector, this is what it looks like.
And we've had this UI for a while but we've done a lot.
We've fixed a lot of bugs.
We've added a lot of features.
But the main thing we've done this year is make the Web Inspector available in more places.
We've listened to your feedback and you want to be able to inspect your Mac application.
And that's exactly what you can do with iOS 8 and OS X Yosemite.
So with Safari, you've been able to inspect your iOS apps and iOS Safari by tethering up to your computer.
But now you can inspect your Mac application and right there from Safari see a list of your Mac applications.
So what's that look like?
But Web Inspector is all about the web.
And it's only been able to work with WebKit up until this point.
So let's-let's show you what it looks like.
So, Mac App inspection, like I said, will show up right next to your iOS devices in the Develop menu.
The first one is a web view but they're right there side by side.
They're first class citizens.
And like I said, you can also still connect to your iOS applications and iOS Safari from the same Develop menu.
But for Mac App inspection we require you to add an entitlement to your app.
So we require you to have this entitlement on your applications that you're developing locally.
And it's very simple to do and I'll give you a demo of it here in a little bit.
What's that look like?
Well, it looks just like the Web Inspector.
This is the same Web Inspector that you saw a little bit ago.
The other one was for the web view.
And you have the full console.
So if you have your own log function you can now abandon it and start using console.log and get all the great benefits of the Web Inspector's Log functions.
But if you don't do anything, the console is pretty much all you have available.
And I'll tell you why.
If you're evaluating large scripts, those are something you probably want to debug but you could be evaluating something every 200 milliseconds and that would quickly pollute the sidebar here.
So you-any script that you want to debug in the Web Inspector we will need you to supply a URL or just a plain name for that script.
And we've given you a couple new APIs for this.
There has always been the EvaluateScript functions.
We now have a new Objective-C one that takes a sourceURL, and this URL can be a file URL.
It can be any URL.
The Web Inspector really doesn't take it other than to get the name at this point.
But if it's a file URL, it will have a little bit extra benefit and I'll talk to you about that a little bit, but JSEvaluateScript has always supported the sourceURL but a lot of people just pass null to it.
So if you're using the old C API, make sure to start passing a sourceURL for things that you want to debug that way they will show up in the Web Inspector.
And that's for scripts.
But what about your context?
We don't know what it is.
By default it will just say JSContext in the Develop menu.
But if you have a multiple context, you don't want to see a list that says JSContext, JSContext, JSContext, you want to identify those.
And as the developer, you should know what you're using that context for and we provide this new API that allows you to name your context.
And the name is only used for debugging.
It will only show up in Safari's Develop menu.
So let's just give a demo of this.
So I have a sample application that we released a while ago at a previous WWDC: JSPong.
And it's a simple pong game.
But I have some things I need to do to it to make it work with JSContext inspection.
The first thing I need to do is make sure the entitlement is there.
And if you're in the Apps Store, you probably already have an entitlements file.
In this case, I already have an App Sandbox and that will give me an entitlement.
And I have it right here in the sidebar, JSPong.entitlements.
So let's go in and add the entitlement that we need.
And I said it was com.apple.security.
get-task-allow, and it's a Boolean.
And we'll go ahead and say "Yes."
So that's all I need to do to allow JSContext inspection in my application.
And I'll go ahead and save that.
And I'll go ahead and build and run it.
And it's a very simple pong game.
I'm not going to talk about that.
But now I can go to Safari and, in the Develop menu, I will see Mac Pro and I will see JSPong and my JSContext.
And I didn't name the context yet, so it's getting the default name.
And I'll go ahead and inspect that.
And you'll see the resources are empty.
I'm not seeing that that AI script that I've loaded.
But I-if I want to debug that, I'll need to give that a name.
But at this point, I have access to the console.
If you don't do anything, you can do evaluations here in the console.
You can get access to any globals that you might have available in your context.
And you will see exceptions in logs and errors.
So you can see exactly where that function or script was evaluated all the way to your native codes so you can trace it back to the exist-the origin.
So let's go back into Xcode and name some of these.
So it's pretty simple.
I'll just comment this out.
This will name my context, Pong Context.
I only have one context.
But if I had multiple ones, it would be imperative for me to name them.
And when I'm evaluating the AI script, all I have to do is construct a URL.
In this case, I'm just making a URL with a string and it really doesn't have a location.
And I'll just use the new withSourceURL method and pass in that URL.
Now let's build and run and see what happens in the Web Inspector.
We'll go ahead and reconnect to the pong game.
In the Resource sidebar, we now have that AI script.
And in the app, we didn't syntax highlight it.
So we can set breakpoints right here.
And since the script is called every iteration of the movement, it should break right away.
And now I'm in the debugger.
I have access to the call stack.
There's nothing other than this function being called.
But if there was a deeply nested call stack, you would see it right there in the sidebar.
And we can do what any debugger does and step through, and do anything you would do in a normal debugger, in addition to hovering over variables to see their current value or evaluate things in this current scope in the console.
So in this case, let's try modifying the ball here.
Maybe we want to modify the rules of pong here and make something really easy to hit.
So I've gone ahead and modified that.
And I have an update function here that will redraw my scene.
And you can see the ball increase in size as I may-after I made that change.
And this is working with Objective-C bridged objects.
So that's JSContext inspection.
And we'll go ahead and play it and the ball stays the same size and keeps on going.
And this also applies for iOS.
So only builds that you build and run on to your device or into the iOS simulator will be inspectable.
Any app from the App Store will not be allowed to be inspected by anyone else.
So that's all you need.
And again, name your context and name your scripts, so you can easily identify them when you're debugging.
So now let's talk about something that we've had in the Web Inspector for a while: the Timeline.
And something new this year is the Source Code Timelines.
So what do I mean by that?
What sorts of problems does it solve?
So Source Code Timelines help you answer this question.
What happened when I click on something on the page?
Maybe it's a new project that you're working on, maybe it's something you wrote years ago and you don't remember how it worked and you want to find that line of code that handles this click, so you can fix a bug or so you can just modify it for some new behavior that your client is asking you to add.
So, when this click happens, there's a lot of stuff that probably going on.
But you really only care about code that you wrote, maybe you have some analytics code or a lot of other code that it might be hard to decipher or see interleaved functions and event calls and all of these that happen in a big modern web application.
So when I click on that, you start wondering, "OK, what script is handling this button?"
And it turns out it's this one.
But with the new Web Inspector, we can dig deeper.
In the script, I can now see a timeline representation of what the script was doing.
So I can see over time where the mouse events are coming in, where the clicks events, where the timer was added, where the timers are firing, where I might be doing forced layout, which is a pretty evil thing to do in a modern web app.
So what's it looked like in the Web Inspector?
Well, here's the new Web Inspector Timeline.
And you can see in the sidebar here we have all of our scripts and resources from the page broken out in their hierarchy of how they're included by you, the developer, in the page and you can expand each script to see what that script was doing and where the line of code was.
And in this case, you'll see a whole timeline where things are happening repeatedly.
You'll start seeing patterns and allow you to identify problem areas if something is firing too much, too little, not at all, if you don't see it in the list.
And if you want to dig deeper into the code, all you have to do is click right there on the sidebar and it will take you right to that location, and you can mouse up and down through the sidebar and have your code on one side and your timeline data in the sidebar.
So you can get a good overview of what the script is doing really quick.
So that's the Source Code Timelines and I'll give you a demo here in a little minute.
So something else that's new in the Web Inspector is Breakpoint Probes.
So you might have some information that you want to know when that mouse is clicked or when your mouse is moving.
And those things are kind of hard to debug in the debugger.
You often have to resort to printf debugging because you don't want to constantly be switching context between your app in the page and the Web Inspector.
So you often result to printf debugging and you're cluttering up your console with thousands of lines and it's hard to decipher, especially if you're debugging multiple areas that are not related and you don't care about the interloop data.
So this is where Breakpoint Probes comes in.
It let's you see these properties that you might care about without having to breakout into the Web Inspector.
So let's just go ahead and give a demo of this.
So let's go to Safari here.
And I have a simple stack the block game that I want to-it's been a while since this was written so I want to see what's going on.
Let's go ahead and use the Timeline panel for this.
I'll go ahead and bring up the Web Inspector in the Develop menu, show Web Inspector.
And we'll go to the Timeline panel.
And by default, the Timeline is empty.
It's not recording all the time.
And it only automatically records when you reload the page or when you press the Record button up here in the corner.
And this can record any length of time that you would like to record.
But when you-when I reload the page, it will only record a little bit.
So I'm just going to go ahead and start recording here and see what this page is doing as I interact with this game.
And you'll see the events and all the work that the engine is doing in response to these events start filling up on the Timeline.
And I'll go ahead and stop this.
And my application has three scripts, but the logic is all in blocks.js, all the other stuff is stuff I didn't even write.
I don't even know what's going on there.
So I can ignore those, but they're doing some work.
And normally that would clutter up my UI.
But they're collapsed here, so they're not taking up any space.
I'll go ahead and expand the script that I care about here and I can see that Timers are firing pretty-at a pretty good clip as I'm interacting with the page and there's key events that are firing every time I press a key.
Some of the other new features of the timeline here, we can now zoom and scale the overview panel so if you want to dig in to see where things are happening across domains.
And all three play a key role in the performance of your page.
So if you see anything interesting happening in a specific time range, you can zoom in on it, make a selection and that information will show up below in the Timeline.
And you can also drag this around to scroll and scrub through your time as things were happening on the page.
So in this case, let me reload and I'll show you the networks.
And in this case, it's a local file.
But we now have network load information and you see those on the network timeline and you'll notice that the Timeline just automatically stops recording there.
It records up to a certain amount of time unless you manually hit the Record button again to record more information.
So let's look into the network timeline here.
And we can see all of the network details that you might be familiar with in the previous network timeline.
They're all there and you can sort-sort these columns by whatever information you find most interesting.
If you're looking for a large script, you can sort by transfer size and see the biggest script show up right there.
Let's go back to start time.
You can also do-click on the other timelines and get detailed tab-linear data of what was happening in that time range.
And again this is all time range-based.
So you can see, as these layout and rendering events filter out, they disappear from the display below.
And if you're looking for the old profile data, this is where it's moved to.
So whenever script is evaluated, you can expand these to see all of the functions that were called in your script and how much time each individual function took to make up that piece of that entry point.
So that's a brief glance at the Timeline.
But there is one cool thing that people always forget about when they're talking about the Timeline.
And that's the Filter.
The Filter bar is vastly underappreciated in the Web Inspector and even Xcode where it comes from, where you can filter down this list to find what exactly you want.
So, in this case I'd look-I did a filter for event and it found addEventListener right there and it brought it to the front.
It expanded those profiles and showed me that exact function I was looking for.
So that's the Filter Bar and I encourage you to start using filtering more often in the Web Inspector and even Xcode.
We'll go back to my block application and bring up the Web Inspector.
And let's go ahead and go to the Resource sidebar here.
And I really want to improve this key function listener-this key listener.
It's using key code, which is pretty arcane.
And there's some new API key identifier that's been out for years that I could use that would give string representations of these-for my cases instead of the numbers.
So what I'll do, like I said, when we set a breakpoint anytime that key is pressed it's going to interrupt my process.
So now to do another key, I have to hit Continue and log what I wanted.
And press another key, figure out what I'm doing here.
But with this breakpoint we can add a breakpoint action.
And I'll go ahead and edit this breakpoint, which will bring up the breakpoint Action popover.
And let's go ahead and add a probe.
So we have a few actions available.
I'm not going to cover them.
But they're pretty self-explanatory.
Probe Expression in this case is brand new.
And we'll go ahead and evaluate an expression that we're interested in.
And we have an an event object here and I want to know what that key identifier is.
And that's all I need to type.
And I want to automatically continue after evaluating so it doesn't pause in the debugger.
So we'll go ahead and break out in here.
And when I added that probe, it added a new sidebar item called Probes.
And this is where that data will show up in the table.
So I'm going to go back to my game and interact with it.
And now it's logging that key identifier every time I press the key and showing me the values that I can now take an input into my code and improve my code and I didn't-this would have been a back and forth process or a lot of assumption of what these identifiers were.
Now I can see them all in a table and go back to my case and modify them.
So that's Probes.
So that's a little bit about Breakpoint Probes and Source Code Timelines.
And I encourage you to play around with them and see how they can fit into your debugging workflow.
So now let's talk about Accessibility.
A new in the Web Inspector, we have an Accessibility Details View.
And this is pretty, pretty important.
If you're working on a major website, you want to be accessible to everyone.
Apple cares a lot about accessibility.
And we do a lot of work to make accessibility matter.
But it only goes so far as to how much you put into it when you're developing your own applications.
And that goes for iOS applications and web applications.
And on the web, we have something called ARIA.
And it's recently hit a milestone ARIA 1 or it's about to.
And that allows you to define for a DOM node what that DOM node does as a role.
And those roles are translated into things that the person using the computer with VoiceOver can hear or see with your-with your web application.
And in this case, I have a webpage that has an alert, an HTML alert.
And those are notoriously hard to debug or hard for a sighted-non-sighted person to see when they pop up in a webpage because they're not going to be able to see that content unless you annotate it with ARIA roles.
And in this case, the VoiceOver machinery and the Accessibility machinery in web view exposes a lot of details like child relationships.
And these relationships are not necessarily the same as a normal DOM node relationship where your parent could be someone that's deeply-a deep ancestor of you.
And we show those in the sidebar here as a parent-child relationship.
And they're all linkable.
So I can click on them and go directly to these other child nodes that are part of my alert.
And see that this button has the right role for a button.
And it will allow people-it won't allow people to focus it, but it allows people to click it.
And that's a brief summary of what you can do with the Web Inspector and Accessibility.
There's a session tomorrow morning that covers Accessibility and the web.
And I encourage you to go check it out.
If you have a website or a web view in your iOS or Mac application, please do make it accessible.
So that's accessibility details.
Now let's talk about something that's really fun, which is Color.
Color is one of those things-we as people can understand numbers.
But colors, there's millions of them.
And it's very visual and a very personal process of picking the right color.
And if you're like me and you're working on a webpage, you might have a client come in and say, "I want this color salmon" or "this color taupe."
It's hard for them to take those words and you can translate that into the right color.
Wouldn't it be great if you can just bring up the color, pick it right there in the webpage and modify it right there in front of them.
So that's what we've added in the Web Inspector, and I'll give you a demo.
So I have an icon that I've been mocking up.
And often I do these things in the web because that's something I understand.
I can play around with it a lot easier than maybe I could do in Photoshop.
And this icon is a little small so I'll just go ahead and bring up the Web Inspector here.
And I have a class that I can add to make it a little bigger for you.
So, now I have my icon.
It has a couple colors.
It's got this color for the glyph and it's got a gradient in the background.
I can bring up the Style sidebar and see those colors.
And in this case, I'm using some named colors.
CSS has hundreds of named colors.
But maybe it's not the right teal.
That's a little more green than I expected or the light blue is not enough sea foam or whatever your client is telling you to make this color.
So, I can click on these colors swatches and get a Color Editor.
Or I can switch over to the CSS resource and get the exact same experience.
But in this case, we want your source code to be front and center, so we don't even add those color swatches.
But we have a new feature that let you get a data detector-like experience.
When we detect that it's a color, we will show a color wheel next to it as you hover it.
And when you click on that wheel, we bring up the color popover.
And in this case we could just click around and you can see the color updating live right there in the web page.
And I can find the right color that I'm looking for, for this glyph.
And if it's a named color it will default back to the named color syntax.
But if it's not something that's named, it will just give you the RGB syntax or RGBa if you picked an alpha.
But we didn't stop there.
We now let you do gradients.
And gradients are even more hard to visualize than color because you have that interplay of the colors transitioning from one to another.
And it's hard to see those in your mind.
But we can go ahead and edit this gradient.
And it gives you a simple slider that you might be familiar with from other UIs.
And I can drag these around and you'll see it's updating the source code as I drag this.
The percentage is changing.
And when I let go, it updates the page live.
So I can see exactly what is applying to this page.
But you can't just edit the color stops.
You can edit the type of gradient or you can pick radial so it's coming out from the center.
And I can click on these colors and get the color wheel and start editing each of these color stops directly.
So I can find the exact look I'm looking for.
Drag this around.
Maybe we'll run it to be more of the starburst look.
A little bit more yellow.
Or the client might say, "Oh, I want a linear gradient in this case."
So let's go back to the traditional iOS look for gradients and start picking some pretty wild colors here.
So that's the gradient editor at the new Web Inspector.
And what I didn't show you here, let's go back and reload here.
If I make any of these changes, say, I want to go back and save it.
Let's make this a red and that looks good.
That looks like a sunset.
In the Web Inspector, I can just go ahead and hit Command+S that will bring up the traditional Save dialogue.
Go ahead and replace it.
And now, if I close the Web Inspector, reload the page, I have that gradient color right there.
The icon is a different size because I was modifying the DOM and it that doesn't save.
But if you're editing a CSS resource or any other local file resource you can save those right back to disk from the Web Inspector.
So that's Color and Gradient Editing.
And I hope you play around with it and try it out.
It's really fun to just get down and dirty with those colors.
And the gradient syntax is something that I still have trouble remembering.
But the Web inspector will just help you right there and get the right syntax and let you go on to do more important things in your code.
So that's the summary of all the new features, and just a brief introduction of all the new features in the Web Inspector.
As you're able to release and use the new modern browsers, or use iOS 8 exclusively, or Mac OS Yosemite exclusively, you could start using some of these new language and syntax features.
The Web Inspector is now available again from your Mac Applications.
If you have a web view that you've always wanted to see what it's doing in your application, there's been a few ways to do it.
But you can now officially use the web inspector in your Mac Application.
This is something that may seem simple.
Oh yeah, there's now a debugger.
But, yeah, now there's a debugger.
And again, it's really important to make your web applications accessible and your iOS application accessible.
And be a colorful person.
Enjoy color editing in the Web Inspector and gradient editing.
And again, for more information, you can contact Evangelism at this email address, or the Developer Technical support, and even the Apple Forums.
I'm in the Apple Forums if you have a question about the Web Inspector, or anything web-related, or one of our team will be able to answer it or I would.
You can go to the session tomorrow morning and learn all you need to know about accessibility in your web application.
And then stay in that same room and learn about responsive web design for making your web apps scale from desktop size to iPhone size.
And that's it.
Thanks a lot.
[ Applause ]