48 |
49 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/ex9/js/tutorial.js:
--------------------------------------------------------------------------------
1 | $('a').click(function(evt) {
2 | $('#'+evt.target.getAttribute('data-soundname'))[0].play();
3 | });
4 |
5 | var canvas = document.getElementById("main");
6 | var context = canvas.getContext("2d");
7 |
8 | var characters = new Image();
9 | characters.src = "media/characters.gif";
10 |
11 | characters.onload = function() {
12 | $(window).keyup(move);
13 | };
14 |
15 | var height = $(canvas).height();
16 | var width = $(canvas).width();
17 |
18 | var x = 0;
19 | var y = 0;
20 |
21 | draw();
22 |
23 | var ws = new WebSocket("ws://localhost:8080");
24 | ws.onmessage = handleMessage;
25 |
26 | function handleMessage(event) {
27 | var msg = JSON.parse(event.data);
28 | console.info(msg);
29 | }
30 |
31 | function move(event) {
32 | switch (event.which) {
33 | case 38:
34 | if (y > 0) { y -= 10; }
35 | break;
36 | case 40:
37 | if (y < height) { y += 10; }
38 | break;
39 | case 37:
40 | if (x > 0) { x -= 10; }
41 | break;
42 | case 39:
43 | if (x < width) { x += 10; }
44 | break;
45 | default:
46 | break;
47 | }
48 |
49 | ws.send(JSON.stringify({ name: name, x: x, y: y, type: "move" }));
50 | draw();
51 | }
52 |
53 | function draw() {
54 | context.clearRect(0,0,width,height);
55 | context.drawImage(characters,33,0,32,32,x,y,32,32);
56 | }
57 |
58 | var nameEl = $('#username');
59 | var name = localStorage.getItem('username');
60 |
61 | nameEl.change(function() {
62 | name = nameEl.val();
63 | localStorage.setItem('username',name);
64 | });
65 |
66 | if (name !== "null") {
67 | nameEl.val(name);
68 | }
69 |
--------------------------------------------------------------------------------
/ex9/media/bubble.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/bubble.mp3
--------------------------------------------------------------------------------
/ex9/media/bubble.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/bubble.ogg
--------------------------------------------------------------------------------
/ex9/media/bubble.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/bubble.wav
--------------------------------------------------------------------------------
/ex9/media/characters.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/characters.gif
--------------------------------------------------------------------------------
/ex9/media/ray_gun.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/ray_gun.mp3
--------------------------------------------------------------------------------
/ex9/media/ray_gun.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/ray_gun.ogg
--------------------------------------------------------------------------------
/ex9/media/ray_gun.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/ray_gun.wav
--------------------------------------------------------------------------------
/ex9/media/short.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/short.mov
--------------------------------------------------------------------------------
/ex9/media/short.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/short.mp4
--------------------------------------------------------------------------------
/ex9/media/short.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/short.ogg
--------------------------------------------------------------------------------
/ex9/media/water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/ex9/media/water.jpg
--------------------------------------------------------------------------------
/js/libs/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | http://www.JSON.org/json2.js
3 | 2011-02-23
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 |
12 | This code should be minified before deployment.
13 | See http://javascript.crockford.com/jsmin.html
14 |
15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
16 | NOT CONTROL.
17 |
18 |
19 | This file creates a global JSON object containing two methods: stringify
20 | and parse.
21 |
22 | JSON.stringify(value, replacer, space)
23 | value any JavaScript value, usually an object or array.
24 |
25 | replacer an optional parameter that determines how object
26 | values are stringified for objects. It can be a
27 | function or an array of strings.
28 |
29 | space an optional parameter that specifies the indentation
30 | of nested structures. If it is omitted, the text will
31 | be packed without extra whitespace. If it is a number,
32 | it will specify the number of spaces to indent at each
33 | level. If it is a string (such as '\t' or ' '),
34 | it contains the characters used to indent at each level.
35 |
36 | This method produces a JSON text from a JavaScript value.
37 |
38 | When an object value is found, if the object contains a toJSON
39 | method, its toJSON method will be called and the result will be
40 | stringified. A toJSON method does not serialize: it returns the
41 | value represented by the name/value pair that should be serialized,
42 | or undefined if nothing should be serialized. The toJSON method
43 | will be passed the key associated with the value, and this will be
44 | bound to the value
45 |
46 | For example, this would serialize Dates as ISO strings.
47 |
48 | Date.prototype.toJSON = function (key) {
49 | function f(n) {
50 | // Format integers to have at least two digits.
51 | return n < 10 ? '0' + n : n;
52 | }
53 |
54 | return this.getUTCFullYear() + '-' +
55 | f(this.getUTCMonth() + 1) + '-' +
56 | f(this.getUTCDate()) + 'T' +
57 | f(this.getUTCHours()) + ':' +
58 | f(this.getUTCMinutes()) + ':' +
59 | f(this.getUTCSeconds()) + 'Z';
60 | };
61 |
62 | You can provide an optional replacer method. It will be passed the
63 | key and value of each member, with this bound to the containing
64 | object. The value that is returned from your method will be
65 | serialized. If your method returns undefined, then the member will
66 | be excluded from the serialization.
67 |
68 | If the replacer parameter is an array of strings, then it will be
69 | used to select the members to be serialized. It filters the results
70 | such that only members with keys listed in the replacer array are
71 | stringified.
72 |
73 | Values that do not have JSON representations, such as undefined or
74 | functions, will not be serialized. Such values in objects will be
75 | dropped; in arrays they will be replaced with null. You can use
76 | a replacer function to replace those with JSON values.
77 | JSON.stringify(undefined) returns undefined.
78 |
79 | The optional space parameter produces a stringification of the
80 | value that is filled with line breaks and indentation to make it
81 | easier to read.
82 |
83 | If the space parameter is a non-empty string, then that string will
84 | be used for indentation. If the space parameter is a number, then
85 | the indentation will be that many spaces.
86 |
87 | Example:
88 |
89 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
90 | // text is '["e",{"pluribus":"unum"}]'
91 |
92 |
93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
95 |
96 | text = JSON.stringify([new Date()], function (key, value) {
97 | return this[key] instanceof Date ?
98 | 'Date(' + this[key] + ')' : value;
99 | });
100 | // text is '["Date(---current time---)"]'
101 |
102 |
103 | JSON.parse(text, reviver)
104 | This method parses a JSON text to produce an object or array.
105 | It can throw a SyntaxError exception.
106 |
107 | The optional reviver parameter is a function that can filter and
108 | transform the results. It receives each of the keys and values,
109 | and its return value is used instead of the original value.
110 | If it returns what it received, then the structure is not modified.
111 | If it returns undefined then the member is deleted.
112 |
113 | Example:
114 |
115 | // Parse the text. Values that look like ISO date strings will
116 | // be converted to Date objects.
117 |
118 | myData = JSON.parse(text, function (key, value) {
119 | var a;
120 | if (typeof value === 'string') {
121 | a =
122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
123 | if (a) {
124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
125 | +a[5], +a[6]));
126 | }
127 | }
128 | return value;
129 | });
130 |
131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
132 | var d;
133 | if (typeof value === 'string' &&
134 | value.slice(0, 5) === 'Date(' &&
135 | value.slice(-1) === ')') {
136 | d = new Date(value.slice(5, -1));
137 | if (d) {
138 | return d;
139 | }
140 | }
141 | return value;
142 | });
143 |
144 |
145 | This is a reference implementation. You are free to copy, modify, or
146 | redistribute.
147 | */
148 |
149 | /*jslint evil: true, strict: false, regexp: false */
150 |
151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
154 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
155 | test, toJSON, toString, valueOf
156 | */
157 |
158 |
159 | // Create a JSON object only if one does not already exist. We create the
160 | // methods in a closure to avoid creating global variables.
161 |
162 | var JSON;
163 | if (!JSON) {
164 | JSON = {};
165 | }
166 |
167 | (function () {
168 | "use strict";
169 |
170 | function f(n) {
171 | // Format integers to have at least two digits.
172 | return n < 10 ? '0' + n : n;
173 | }
174 |
175 | if (typeof Date.prototype.toJSON !== 'function') {
176 |
177 | Date.prototype.toJSON = function (key) {
178 |
179 | return isFinite(this.valueOf()) ?
180 | this.getUTCFullYear() + '-' +
181 | f(this.getUTCMonth() + 1) + '-' +
182 | f(this.getUTCDate()) + 'T' +
183 | f(this.getUTCHours()) + ':' +
184 | f(this.getUTCMinutes()) + ':' +
185 | f(this.getUTCSeconds()) + 'Z' : null;
186 | };
187 |
188 | String.prototype.toJSON =
189 | Number.prototype.toJSON =
190 | Boolean.prototype.toJSON = function (key) {
191 | return this.valueOf();
192 | };
193 | }
194 |
195 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
196 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
197 | gap,
198 | indent,
199 | meta = { // table of character substitutions
200 | '\b': '\\b',
201 | '\t': '\\t',
202 | '\n': '\\n',
203 | '\f': '\\f',
204 | '\r': '\\r',
205 | '"' : '\\"',
206 | '\\': '\\\\'
207 | },
208 | rep;
209 |
210 |
211 | function quote(string) {
212 |
213 | // If the string contains no control characters, no quote characters, and no
214 | // backslash characters, then we can safely slap some quotes around it.
215 | // Otherwise we must also replace the offending characters with safe escape
216 | // sequences.
217 |
218 | escapable.lastIndex = 0;
219 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
220 | var c = meta[a];
221 | return typeof c === 'string' ? c :
222 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
223 | }) + '"' : '"' + string + '"';
224 | }
225 |
226 |
227 | function str(key, holder) {
228 |
229 | // Produce a string from holder[key].
230 |
231 | var i, // The loop counter.
232 | k, // The member key.
233 | v, // The member value.
234 | length,
235 | mind = gap,
236 | partial,
237 | value = holder[key];
238 |
239 | // If the value has a toJSON method, call it to obtain a replacement value.
240 |
241 | if (value && typeof value === 'object' &&
242 | typeof value.toJSON === 'function') {
243 | value = value.toJSON(key);
244 | }
245 |
246 | // If we were called with a replacer function, then call the replacer to
247 | // obtain a replacement value.
248 |
249 | if (typeof rep === 'function') {
250 | value = rep.call(holder, key, value);
251 | }
252 |
253 | // What happens next depends on the value's type.
254 |
255 | switch (typeof value) {
256 | case 'string':
257 | return quote(value);
258 |
259 | case 'number':
260 |
261 | // JSON numbers must be finite. Encode non-finite numbers as null.
262 |
263 | return isFinite(value) ? String(value) : 'null';
264 |
265 | case 'boolean':
266 | case 'null':
267 |
268 | // If the value is a boolean or null, convert it to a string. Note:
269 | // typeof null does not produce 'null'. The case is included here in
270 | // the remote chance that this gets fixed someday.
271 |
272 | return String(value);
273 |
274 | // If the type is 'object', we might be dealing with an object or an array or
275 | // null.
276 |
277 | case 'object':
278 |
279 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
280 | // so watch out for that case.
281 |
282 | if (!value) {
283 | return 'null';
284 | }
285 |
286 | // Make an array to hold the partial results of stringifying this object value.
287 |
288 | gap += indent;
289 | partial = [];
290 |
291 | // Is the value an array?
292 |
293 | if (Object.prototype.toString.apply(value) === '[object Array]') {
294 |
295 | // The value is an array. Stringify every element. Use null as a placeholder
296 | // for non-JSON values.
297 |
298 | length = value.length;
299 | for (i = 0; i < length; i += 1) {
300 | partial[i] = str(i, value) || 'null';
301 | }
302 |
303 | // Join all of the elements together, separated with commas, and wrap them in
304 | // brackets.
305 |
306 | v = partial.length === 0 ? '[]' : gap ?
307 | '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
308 | '[' + partial.join(',') + ']';
309 | gap = mind;
310 | return v;
311 | }
312 |
313 | // If the replacer is an array, use it to select the members to be stringified.
314 |
315 | if (rep && typeof rep === 'object') {
316 | length = rep.length;
317 | for (i = 0; i < length; i += 1) {
318 | if (typeof rep[i] === 'string') {
319 | k = rep[i];
320 | v = str(k, value);
321 | if (v) {
322 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
323 | }
324 | }
325 | }
326 | } else {
327 |
328 | // Otherwise, iterate through all of the keys in the object.
329 |
330 | for (k in value) {
331 | if (Object.prototype.hasOwnProperty.call(value, k)) {
332 | v = str(k, value);
333 | if (v) {
334 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
335 | }
336 | }
337 | }
338 | }
339 |
340 | // Join all of the member texts together, separated with commas,
341 | // and wrap them in braces.
342 |
343 | v = partial.length === 0 ? '{}' : gap ?
344 | '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
345 | '{' + partial.join(',') + '}';
346 | gap = mind;
347 | return v;
348 | }
349 | }
350 |
351 | // If the JSON object does not yet have a stringify method, give it one.
352 |
353 | if (typeof JSON.stringify !== 'function') {
354 | JSON.stringify = function (value, replacer, space) {
355 |
356 | // The stringify method takes a value and an optional replacer, and an optional
357 | // space parameter, and returns a JSON text. The replacer can be a function
358 | // that can replace values, or an array of strings that will select the keys.
359 | // A default replacer method can be provided. Use of the space parameter can
360 | // produce text that is more easily readable.
361 |
362 | var i;
363 | gap = '';
364 | indent = '';
365 |
366 | // If the space parameter is a number, make an indent string containing that
367 | // many spaces.
368 |
369 | if (typeof space === 'number') {
370 | for (i = 0; i < space; i += 1) {
371 | indent += ' ';
372 | }
373 |
374 | // If the space parameter is a string, it will be used as the indent string.
375 |
376 | } else if (typeof space === 'string') {
377 | indent = space;
378 | }
379 |
380 | // If there is a replacer, it must be a function or an array.
381 | // Otherwise, throw an error.
382 |
383 | rep = replacer;
384 | if (replacer && typeof replacer !== 'function' &&
385 | (typeof replacer !== 'object' ||
386 | typeof replacer.length !== 'number')) {
387 | throw new Error('JSON.stringify');
388 | }
389 |
390 | // Make a fake root object containing our value under the key of ''.
391 | // Return the result of stringifying the value.
392 |
393 | return str('', {'': value});
394 | };
395 | }
396 |
397 |
398 | // If the JSON object does not yet have a parse method, give it one.
399 |
400 | if (typeof JSON.parse !== 'function') {
401 | JSON.parse = function (text, reviver) {
402 |
403 | // The parse method takes a text and an optional reviver function, and returns
404 | // a JavaScript value if the text is a valid JSON text.
405 |
406 | var j;
407 |
408 | function walk(holder, key) {
409 |
410 | // The walk method is used to recursively walk the resulting structure so
411 | // that modifications can be made.
412 |
413 | var k, v, value = holder[key];
414 | if (value && typeof value === 'object') {
415 | for (k in value) {
416 | if (Object.prototype.hasOwnProperty.call(value, k)) {
417 | v = walk(value, k);
418 | if (v !== undefined) {
419 | value[k] = v;
420 | } else {
421 | delete value[k];
422 | }
423 | }
424 | }
425 | }
426 | return reviver.call(holder, key, value);
427 | }
428 |
429 |
430 | // Parsing happens in four stages. In the first stage, we replace certain
431 | // Unicode characters with escape sequences. JavaScript handles many characters
432 | // incorrectly, either silently deleting them, or treating them as line endings.
433 |
434 | text = String(text);
435 | cx.lastIndex = 0;
436 | if (cx.test(text)) {
437 | text = text.replace(cx, function (a) {
438 | return '\\u' +
439 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
440 | });
441 | }
442 |
443 | // In the second stage, we run the text against regular expressions that look
444 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
445 | // because they can cause invocation, and '=' because it can cause mutation.
446 | // But just to be safe, we want to reject all unexpected forms.
447 |
448 | // We split the second stage into 4 regexp operations in order to work around
449 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
450 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
451 | // replace all simple value tokens with ']' characters. Third, we delete all
452 | // open brackets that follow a colon or comma or that begin the text. Finally,
453 | // we look to see that the remaining characters are only whitespace or ']' or
454 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
455 |
456 | if (/^[\],:{}\s]*$/
457 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
458 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
459 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
460 |
461 | // In the third stage we use the eval function to compile the text into a
462 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
463 | // in JavaScript: it can begin a block or an object literal. We wrap the text
464 | // in parens to eliminate the ambiguity.
465 |
466 | j = eval('(' + text + ')');
467 |
468 | // In the optional fourth stage, we recursively walk the new structure, passing
469 | // each name/value pair to a reviver function for possible transformation.
470 |
471 | return typeof reviver === 'function' ?
472 | walk({'': j}, '') : j;
473 | }
474 |
475 | // If the text is not JSON parseable, then a SyntaxError is thrown.
476 |
477 | throw new SyntaxError('JSON.parse');
478 | };
479 | }
480 | }());
481 |
--------------------------------------------------------------------------------
/js/worker.js:
--------------------------------------------------------------------------------
1 | function factorizer(num) {
2 | var factors = [];
3 | var output = [];
4 | var x,c,d;
5 |
6 | for (x=1; x <= num; x++) {
7 | c = num / x;
8 | d = Math.floor(c);
9 |
10 | if (c === d){
11 | factors.push(x);
12 | }
13 | }
14 |
15 | return "множители " + num + " - " + factors.join(", ");
16 | }
17 |
18 | self.addEventListener('message', function(e) {
19 | var num = e.data;
20 | self.postMessage(factorizer(num));
21 | },false);
22 |
--------------------------------------------------------------------------------
/manifest/demo.manifest:
--------------------------------------------------------------------------------
1 | CACHE MANIFEST
2 | index.html
3 | favicon.ico
4 | js/libs/modernizr-1.7.js
5 | js/libs/jquery-1.5.2.js
6 | media/water.jpg
7 |
--------------------------------------------------------------------------------
/manifest/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ivliaspiration/html5tutorial/30310c8b6e425ee37f9aa14a82e3dc2487ba329e/manifest/favicon.ico
--------------------------------------------------------------------------------
/manifest/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML5 Больше, чем модная фишка
6 |
7 |
14 |
15 |
16 |
17 |
Цель этого практикума - показать, как новые возможности HTML5 могут быть использованы в современных веб-приложениях без участия дополнительных зависимостей и противоречий с уже существующим кодом. Курс рассчитан на то, чтобы дать вам достаточно знаний для самостоятельного использования новых возможностей в своих приложениях.
41 |
42 |
ПРЕДВАРИТЕЛЬНЫЕ ТРЕБОВАНИЯ
43 |
44 |
45 |
Все упражнения проверялись в последних версиях браузера Chrome, но они должны работать и в последних версиях Safari и Firefox. Некоторые упражнения работают в Firefox не так, как задумано, из-за ограничений браузера.
46 | Я не проверял работоспособность в IE или Opera, но большая часть кода должна работать и там.
47 |
Ваш браузер должен иметь консоль JavaScript. Такая консоль уже имеется в Safari и Chrome. Если вы используете Firefox, не забудьте установить Firebug.
48 |
Вам понадобится текстовый редактор (Vi, Emacs, Textmate, Notepad, etc.) для внесения изменений в файлы проекта.
49 |
50 |
51 |
52 |
УСТАНОВКА
53 |
54 |
55 |
Скопируйте папку start из этого проекта в какое-нибудь другое место. Эта папка содержит все необходимые зависимости и послужит основой для игры, которую мы будем делать.
56 |
Изучите файл index.html в скопированной директории start. Он представляет собой упрощенный шаблон, основанный на HTML5 Boilerplate. Здесь вы можете найти полный шаблон. Это отличный способ изучить особенности HTML5.
57 |
58 |
Обратите внимание на DOCTYPE, с которого начинается этот файл. Это все, что нужно, чтобы указать браузеру, что вы используете последнюю, самую современную версию HTML.
59 | Кроме того, в шаблоне используются новые семантические тэги <header> и <footer>. Их можно использовать вместо менее семантической разметки вроде <div id="header"></div>.
60 |
Откройте index.html в вашем браузере. Затем откройте консоль JavaScript (в Chrome это делается через меню Вид > Инструменты > Консоль Javascript; в Safari достаточно нажать option-Apple-C). В консоли должно быть сообщение "Ваша консоль работает". Если вы не видите это сообщение, попробуйте установить Firebug или открыть файл в другом браузере. Без работающей консоли следовать этому руководству крайне затруднительно.
61 |
62 |
Это сообщение было отправлено в консоль в файле js/tutorial.js. Я буду использовать этот файл для всего кода приложения.
63 |
64 |
65 |
66 |
УПРАЖНЕНИЕ 1
67 |
68 |
Определение возможностей
69 |
70 |
71 |
Изучите файл js/libs/modernizr. Это библиотека Modernizr, которая выполняет определение возможностей браузера и помогает применять стили к новым семантическим элементам HTML5. Пока рано полагаться на то, что браузеры полностью поддерживают новые возможности, поэтому крайне важно проводить определение возможностей браузера и выдавать осмысленные сообщения об ошибках в случае необходимости.
72 |
Чтобы подключить Modernizr, откройте текстовый редактор и добавьте в тэг <HEAD> файла index.html следующую строку:
73 |
74 |
<script src="js/libs/modernizr-1.7.js"></script>
75 |
76 |
Затем добавьте класс no-js к тэгу <HTML> во второй строке файла index.html.
77 |
Откройте index.html в браузере и посмотрите на тэг <HTML>. Обратите внимание на то, что класс no-js был заменен на ряд объявлений CSS. Эти объявления можно использовать, чтобы показывать сообщения об ошибках или для стилизации контента, предъявлемого при недоступности некоторого функционала.
78 |
Откройте консоль JavaScript и изучите объект Modernizr. Вы сможете выполнить все упражнения этого учебника, если все нижеприведенные свойства будут иметь значение true:
79 |
80 |
81 |
Modernizr.canvas
82 |
Modernizr.websockets
83 |
Modernizr.audio
84 |
Modernizr.geolocation
85 |
Modernizr.localstorage
86 |
87 |
88 |
89 |
Если какие-либо из этих свойств не имеют значения "true", установите браузер Chrome.
90 |
91 |
92 |
93 |
Дополнительное задание
94 |
95 |
Используя только CSS и HTML и полагаясь на классы touch и no-touch элемента <HEAD>, выведите соотвествующее сообщение о том, поддерживает ли устройство пользователя тач-интерфейс. Если у вас установлен симулятор мобильной ОС (например, iOS Simulator), проверьте оба случая.
96 |
97 |
УПРАЖНЕНИЕ 2
98 |
99 |
Основы рисования на canvas
100 |
101 |
102 |
Добавьте тэг <canvas id="main" width="400" height="400"></canvas> сразу после открывающего тэга <body> в файле index.html.
103 |
Чтобы нарисовать на canvas прямоугольник, добавьте в js/tutorial.js следующий код:
104 |
var canvas = document.getElementById("main");
105 | var context = canvas.getContext("2d");
106 | context.fillRect(0,0,20,20);
107 |
108 |
Рисование на canvas осуществляется в контексте canvas, а не на самом объекте canvas. В настоящее время доступен только контекст "2d". В будущем возможно появление 3D-контекста.
109 |
Откройте index.html в браузере. Вы должны увидеть черный прямоугольник.
110 |
Вместо fillRect вызовите strokeRect и обновите страницу в браузере. Теперь прямоугольник не закрашен.
111 |
Задайте свойству fillStyle контекста какой-нибудь цвет CSS, например, красный - "red", нарисуйте закрашенный прямоугольник и обновите страницу. Вы увидите красный прямоугольник.
112 |
113 |
context.fillStyle = "red";
114 |
Чтобы отобразить текст, добавьте в tutorial.js следующий код и обновите страницу.
Попробуйте залить прямоугольник градиентом. Для этого сначала вам нужно создать градиент (что объяснено здесь: Dive Into HTML5), а затем установить этот градиент значением свойства fillStyle.
125 |
126 |
УПРАЖНЕНИЕ 3
127 |
128 |
Работа с изображениями на canvas
129 |
130 |
131 |
Скопируйте файл media/water.jpg из папки media практикума в папку media вашего проекта. (Это изображение распростряняется на условиях лицензии Creative Commons-licensed)
132 |
Выведите это изображение на canvas, добавив следующий код в tutorial.js:
Обновите страницу в браузере, чтобы увидеть картинку.
140 |
Добавьте два дополнительных параметра после 3 уже имеющихся, ширину и высоту области вывода для картинки:
141 |
142 |
context.drawImage(img,0,110,200,100);
143 |
Обновите страницу. Размер картинки будет другим.
144 |
Для нашей простой игры нам понадобится файл со спрайтами media/characters.gif. Скопируйте его в папку media вашего проекта. (Я получил этот файл, лицензированный на условиях CC, от David E. Gervais, участника проекта TomeTik)
145 |
Чтобы вырезать одного персонажа из файла и отобразить его на canvas, вам придется вызвать drawImage с 9 аргументами:
146 |
147 |
drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh);
148 |
149 |
Назначение каждого из этих агрументов объяснено на картинке:
150 |
151 |
152 |
Каждый спрайт имеет ширину и высоту в 32 пикселя. Таким образом, если вы хотите вырезать второго персонажа в верхнем ряду, да-да, этого, в красном капюшоне, вам нужно вызвать drawImage с такими параметрами:
153 |
154 |
155 |
sx = 33
156 |
sy = 0
157 |
sw = 32
158 |
sh = 32
159 |
160 |
161 |
162 |
Добавьте dx, dy, dw и dh по вкусу, затем обновите страницу. Если что-то не получается, посмотрите решение в папке ex3.
163 |
164 |
165 |
166 |
УПРАЖНЕНИЕ 4
167 |
168 |
Простая анимация
169 |
170 |
171 |
Скопируйте js/libs/jquery-1.5.2.js в папку js/libs вашего проекта. jQuery понадобится нам для обработки событий клавиатуры и некоторых других задач.
172 |
Чтобы подключить jQuery, добавьте следующий тэг в конец файла index.html, перед загрузкой файла tutorial.js:
173 |
174 |
<script src="js/libs/jquery-1.5.2.js"></script>
175 |
Закомментируйте большую часть кода, отвечающего за рисование. В этом упражнении мы начнем с чистого листа. Оставшийся в tutorial.js код должен выглядеть примерно так:
176 |
177 |
var canvas = document.getElementById("main");
178 | var context = canvas.getContext("2d");
179 |
180 | var characters = new Image();
181 | characters.src = "media/characters.gif";
182 |
Задайте значения переменных x и y равными нулю. В этих переменных будет храниться текущая позиция игрока.
183 |
Задайте переменные для хранения ширины и высоты canvas. В дальнейшем мы будем менять размеры canvas, поэтому имеет смысл не задавать эти переменные константами.
184 |
185 |
var height = $(canvas).height();
186 |
var width = $(canvas).width();
187 |
Задайте обработчик для обработки события onload объекта characters. После загрузки изображения персонажа клавиатурные события будут обрабатываться функцией, которую мы напишем на следующем шаге:
188 |
189 |
$(window).keyup(move);
190 |
Напишите функцию move, которая изменяет позицию персонажа на экране в зависимости от нажатой кнопки. На предыдущем шаге мы задали обрабочик клавиатурных событий. Этот обработчик должен принимать объект event, передаваемый jQuery. Этот объект имеет свойство which, указывающий на то, какая клавиша была нажата.
191 |
192 |
Коды клавиш:
193 |
194 |
195 |
Вверх = 38
196 |
Вниз = 40
197 |
Влево = 37
198 |
Вправо = 39
199 |
200 |
201 |
202 |
Увеличивайте значение x или y на 10, в зависимости от того, какая клавиша была нажата. Проверяйте значения x и y на предмет выхода за границы canvas (значения не должны быть меньше нуля или больше размеров canvas).
203 |
204 |
Если вы запутались, посмотрите как реализована эта функция в файле ex4/js/tutorial.js.
205 |
В конце тела функции move вызовите context.drawImage, чтобы вырезать изображение из файла со спрайтами и отобразить его на canvas в координатах, заданных x и y.
206 |
207 |
Если вы запутались, посмотрите, как это сделано в файле ex4/js/tutorial.js.
208 |
Обновите страницу и опробуйте анимацию. Персонаж должен неторопливо перемещаться по экрану при нажатии на клавиши со стрелками.
209 |
Персонаж оставляет за собой некрасивый след. Исправить это можно путем очистки экрана перед каждым выводом персонажа. Добавьте следующий код перед вызовом drawImage:
210 |
211 |
context.clearRect(0,0,width,height);
212 |
Если хотите, заставьте персонажа перемещаться на большее расстояние при каждом нажатии клавиши.
213 |
214 |
215 |
216 |
Дополнительное задание
217 |
218 |
Попробуйте создать цикл анимации, который осуществлял бы вывод на экран несколько раз в секунду. Это поможет отделить обработку событий клавиатуры от отображения. Для запуска цикла вам понадобится что-то вроде:
219 |
220 |
setInterval(runLoopFunction,interval);
221 |
222 |
Где runLoopFunction - имя вашей функции, а interval - количество миллисекунд, которые браузер должен выдерживать между вызовами функции.
223 |
224 |
УПРАЖНЕНИЕ 5
225 |
226 |
Работаем с формами
227 |
228 |
229 |
Если мы сделаем нашу игру многопользовательской, нам нужно будет знать, как зовут игроков. Давайте добавим поле, которое принимает имя пользователя и использует новые возможности HTML5. Добавьте в файл index.html следующую строку:
230 |
231 |
<input id="username" placeholder="Ваше имя">
232 |
Обновите страницу. Если вы не видите текста "Ваше имя" в поле, проверьте значение Modernizr.input.placeholder в консоли JavaScript.
233 |
Добавьте к полю ввода аттрибут autofocus и обновите страницу. Если браузер поддерживает такую возможность, поле автоматически получит фокус.
234 |
Опробуем в деле новый элемент формы, ползунок, с помощью которого мы можем изменять размеры персонажа. Добавьте в index.html следующий код (1):
Добавьте обработчик событий ползунка в файл tutorial.js. Обработчик должен изменять размер изображения, выводимого вызовом drawImage. Задать обработчик можно так:
238 |
239 |
$('#size').change(function() { ... });
240 |
241 |
Получить значение ползунка можно с помощью $('#size').val()
242 |
243 |
244 |
245 |
(1) Firefox не отображает этот элемент. Выполните задание в Safari или Chrome.
246 |
247 |
Дополнительное задание
248 |
249 |
Попробуйте использовать другие элементы формы, описанные в книге Dive Into HTML5. В HTMl5 есть даже элемент для выбора цвета!
250 |
251 |
УПРАЖНЕНИЕ 6
252 |
253 |
Локальное хранилище
254 |
255 |
Примечание: Это упражнение работает некорректно в Firefox из-за бага в обработке ссылок file://. Если вы хотите, чтобы все заработало и в Firefox, придется отдавать веб-страницу с сервера. Подробности
256 |
257 |
258 |
Сохраните значение в локальном хранилище из консоли JavaScript:
259 |
260 |
localStorage.setItem('shaz','bot')
261 |
Закройте страницу, затем откройте ее в новом окне и введите в консоли JavaScript такую команду:
262 |
263 |
localStorage.getItem('shaz')
264 |
265 |
Если ваш браузер поддерживает локальные хранилища, функция должна вернуть значение shaz.
266 |
Попробуйте проделать то же самое с объектом sessionStorage. Чем sessionStorage отличается от localStorage? Что происходит, если вы просто обновляете страницу? Одинаково ли ведут себя объекты?
267 |
Вызовите localStorage.clear() и попробуйте еще раз найти элемент 'shaz'.
268 |
Добавьте обработчик события change для поля ввода имени пользователя, которое мы создали в упражнении 5:
269 |
270 |
$('#username').change(function() { ... });
271 |
В обработчике сохраните имя пользователя с помощью localStorage. Чтобы вызвать событие change вам, возможно, нужно будет снять фокус с поля ввода. Чтобы получить значение поля ввода, используйте $("#username").val().
272 |
Добавьте в tutorial.js код для получения имени пользователя из localStorage (которое мы сохранили на предыдущем шаге) при загрузке страницы. Если имя пользователя найдено в хранилище, восстановите ранее сохраненное имя пользователя в поле ввода, например так: $("#username").val(nameStr).
273 |
Обновите страницу. Введите имя пользователя, снимите фокус с поля (чтобы убедиться, что событие change вызвано), затем снова обновите страницу. На этот раз поле ввода должно содержать имя пользователя вместо текста по умолчанию.
274 |
Попробуйте сохранять и извлекать из локального хранилища числа и хэши. Сохраняет ли локальное хранилище тип?
275 |
276 |
277 |
278 |
Консоль JavaScript может обладать функцией просмотра локального хранилища и хранилища сессии. В инструменте для разработчиков движка WebKit содержимое хранилищ можно посмотреть на соответствующей вкладке.
279 |
280 |
Дополнительное задание
281 |
282 |
283 |
Добавьте к объекту window обработчик события storage, чтобы отслеживать добавление новых элементов в локальное хранилище. Если не знаете, с чего начать, начните отсюда: Dive Into HTML5.
284 |
Что произойдет, если попытаться обратиться к элементу, которые не был сохранен в хранилище?
285 |
286 |
287 |
288 |
УПРАЖНЕНИЕ 7
289 |
290 |
Очистка canvas
291 |
292 |
293 |
Прежде, чем добавить в нашу "игру" возможности мультиплеера, поработаем над ее внешним видом. Установим для canvas темный фон. Добавьте этот код в тэг <head> файла index.html:
Если хотите, можно увеличить ширину и высоту canvas.
304 |
Увеличьте скорость перемещения персонажа так, чтобы он перемещался на 5 или 10 пикселей на одно нажатие клавиши.
305 |
306 |
307 |
308 |
УПРАЖНЕНИЕ 8
309 |
310 |
Web Sockets
311 |
312 |
Примечание: WebSockets не работают в Firefox. Вы можете исправить это (частично) с помощью socket.io.
313 |
314 |
315 |
Подключим websocket-сервер для обмена информацией с другими игроками. Добавьте в tutorial.js код, приведенный ниже. Я заметил, что если этот код выполняется до загрузки characters.gif, это иногда приводит к ошибкам. Возможно, имеет смысл вставить этот код в обработчик события onload картинки. Если вам непонятно, о чем речь, загляните в решение для упражнения 8.
316 |
317 |
var ws = new WebSocket("ws://exp.subelsky.com:8011");
318 | ws.onmessage = handleMessage;
319 |
320 | function handleMessage(event) {
321 | console.info(event.data);
322 | }
323 |
Обновите страницу. Каждые 10 секунд с сервера должно приходить сообщение "ping". Обратите внимание, что это сообщение - JSON-строка, поэтому прежде чем работать с ней, ее надо десериализовать.
324 |
Откройте server/server.rb, чтобы понять, к чему мы подключаемся.
325 |
Измените функцию handleMessage, добавив разбор JSON-строки. Если ваш браузер не имеет встроенной поддержки JSON, подключите к проекту скрипт js/libs/json2.js.
326 |
327 |
var msg = JSON.parse(event.data);
328 | console.info(msg);
329 |
Обновите страницу, подождите 10 секунд и проверьте консоль. Вы должны увидеть десериализованный JSON-объект.
330 |
Измените функцию move, добавив в нее отправку JavaScript-объекта в websocket каждый раз при перемещении персонажа. Воспользуйтесь методом websocket.send:
331 |
332 |
ws.send(JSON.stringify({ name: name, x: x, y: y, type: "move" }));
333 |
Посмотрите логи сервера, выводимые на экран. Каждый раз при нажатии клавиши перемещения в логе должны появляется сообщения о перемещении персонажа.
334 |
335 |
336 |
337 |
Если вы подумываете о создании приложения с использованием websocket, обязательно посмотрите проект Pusher. Возможно, вам не придется писать собственный сервер.
338 |
339 |
Дополнительное задание
340 |
341 |
Сервер передает события передвижения всему классу (практикум проводился на конференции, компьютеры студентов были объединены в сеть - прим. пер.). Чтобы отобразить перемещения других студентов на экране, нужно отследить их имена и координаты x и y (реализовать это лучше с помощью кэша, ключами которого будут имена пользователей, а значениями - координаты). Измените вывод handleMessage так, чтобы он давал информацию о нескольких игроках, показывая другую картинку для чужих персонажей.
342 |
343 |
Мы не проверяем имена пользователей на уникальность, поэтому постарайтесь сразу договориться с другими студентами о разных именах.
344 |
345 |
УПРАЖНЕНИЕ 9
346 |
347 |
Работа с мультимедия (и пользовательскими аттрибутами)
348 |
349 |
Добавим в игру звуковые эффекты и воспользуемся пользовательскими аттрибутами HTML5 для упрощения управления.
С помощью jQuery добавьте обработчик события click для тэга <a>. Определить, какой звук требуется проиграть, можно спомощью значения пользовательского аттрибута события, как это показано ниже. Здесь я использовал кросс-браузерное решение, а также вариант, предложенный в спецификации HTML5, который, похоже, поддерживает только Chrome.
360 |
361 |
$('a').click(function(evt) {
362 | // не слишком элегантно, зато работает во всех браузерах
363 | $('#'+evt.target.getAttribute('data-soundname'))[0].play();
364 |
365 | // спецификация HTML5 предлагает изящный API, но работает это, по-видимому, только в Chrome
366 | // $('#'+evt.target.dataset.soundname)[0].play();
367 | });
368 |
369 |
370 |
Не стоит путать пользовательские аттрибуты с микроданными - они не предназначены для внешней обработки. Подробне о микроданных можно почитать в Dive Into HTML5.
371 |
Обновите страницу. Щелкните каждую ссылку, чтобы убедиться, что свойство dataset доступно и получает нужное значение.
372 |
Воспроизведение звука и видео в HTML5 подразумевает возню с кодеками. Обычно контент делают доступным в нескольких форматах. Я добавил звуковые файлы в четырех разных форматах. Скопируйте звуковые файлы из папки media в ваш проект. Если вы отдаете эти файлы с сервера (а не через file://), возможно, вам придется повозиться с MIME, так как HTML5 не будет воспроизводить файлы, отданные с неверным MIME типом. Подробнее см. MIME Types.
373 |
374 |
Следующий код должен работать для большинства пользователей. Спецификация предприсывает браузеру воспроизводить первый источник, который он способен воспроизвести.
Я решил добавить звуковые файлы прямо в страницу. В этом случае, если HTML5-аудио не сработает, браузер обработает их как обычно. А вообще аудио-объекты можно создавать также, как и картинки:
392 |
393 |
var audio = new Audio;
394 | audio.src = "http://...";
395 |
396 |
Обновите страницу. Попробуйте проиграть оба файла в консоли:
Измените ссылку так, чтобы указанные файлы воспроизводились автоматически. Воспользуйтесь кодом из предыдущего шага.
401 |
Чтобы посмотреть, как выглядят элементы управления звуком по умолчанию, уберите стиль display:hidden для <div> и добавьте аттрибут controls после preload. Обновите страницу.
402 |
Вставка видео происходит также. Для обеспечения совместимости придется предоставить несколько форматов видео. Скопируйте файлы short.mov, short.mp4, short.ogv и short.webm из папки media в папку media вашего проекта.
403 |
404 |
Видео было сконвертировано из файла QuickTime с помощью ffmpeg2theora, ffmpeg и HandBrakeCLI. Настройки конвертации были подсмотрены в Dive Into HTML5.
Обновите страницу. Один из видеофайлов должен отобразиться в вашем браузере.
414 |
415 |
Потрясающий пример использования canvas для манипулирования изображением из видео можно найти здесь. В этом демо показано использование событий вставленного медиаконтента для отображения таймера.
416 |
417 |
418 |
419 |
Дополнительное задание
420 |
421 |
Если воспроизведение мультимедия на поддерживается браузером, воспользуйтесь флеш-плеером FlowPlayer (для этого нужно добавить тэг <object> после тэгов <source>. Подробно эта техника освещена в статье Video for Everybody).
Посмотрите на объект локации в консоле. Если поискать эти координаты на Google Maps, то результат окажется довольно точным! Интегрировать геолокацию с Google Maps очень легко, и это позволит показывать положение пользователя на карте. К сожалению, из-за особенностей аутентификации Google Maps API сделать это с localhost не получится.
432 | Здесь расположено простое демо - обязательно изучите исходный код страницы.
433 |
434 |
Первая функция обратного вызова вызывается, если браузер может определить свое положения, вторая - если не может. На моей машине вторая функция вызывается в Safari, если компьютер подключен к сети через Ethernet.
435 |
436 |
437 |
438 |
Дополнительное задание
439 |
440 |
Посмотрите на SimpleGeo примеры того, что можно сделать, если знать примерные координаты пользователя.
441 |
442 |
УПРАЖНЕНИЕ 11
443 |
444 |
Web Workers
445 |
446 |
447 |
Изучите файл js/worker.js и скопируйте его в свой проект. Это простой алгоритм перебора, призванный найти все множители данного целого числа.
448 |
Обновите страницу и введите в консоль JavaScript следующие команды:
Если вы используете Chrome, при открытии index.html как file:// будет вызвано исключение безопасности. Отключить его можно, запустив Chrome из консоли с особым ключом.
459 | На OS X это выглядит так:
460 |
461 |
open -n -a 'Google Chrome.app' --args --allow-file-access-from-files
462 |
463 |
А можно просто воспользоваться другим браузером.
464 |
Worker должен отправить в консоль сообщение. Попробуйте передадать в worker.postMessage большее число. Достаточно большое число (например, 1 000 000) приведет к длительным вычислениям. Обратите внимание, что страница останется отзывчивой при выполнении такого фонового задания.
Изучите файл index.html в папке manifest. Это сокращенная версия решения упражнения 11. Обратите внимание на тэг <html>, который в этой версии содержит ссылку на demo.manifest.
476 |
Изучите demo.manifest.
477 |
Если у вас установлен сниффер, натравите его на порт 80. Если сниффера нет, убедитесь, что консоль JavaScript отслеживает сетевой траффик.
Посмотрите в сниффере или консоли JavaScript, какие файлы скачиваются. Обратите внимание на MIME-тип файла demo.manifest (text/cache-manifest).
480 |
Теперь обновите страницу. Если все заработает как надо, траффик будет состоять из единственного запроса проверки файла demo.manifest, который даже не будет скачан, т.к. он не изменялся (о чем свидетельствует статус 304 HTTP-ответа).
481 |
482 |
Используя эту технику, можно "установить" HTML5-приложение на смартфон.
483 |
484 |
485 |
486 |
Дополнительное задание
487 |
488 |
Запустите manifest/index.html на локальном сервере. Папка с приложением должна отдаваться с веб-сервера (а не через файл file://), а файл с манифестом должен иметь правильный MIME-тип. В Apache это достигается следующей директивой в httpd.conf:
489 |
490 |
AddType text/cache-manifest .manifest
491 |
492 |
493 |
СЛИШКОМ ПРОСТО?
494 |
495 |
Вот еще несколько возможностей HTML5, о который стоит знать, если этот практикум вам не подошел, или если вы считаете, что пока эти технологии слишком нестабильны для практического применения:
Автор благодарит Jeff Casimir за помощь в подготовке этого практикума и Mark Pilgrim за написание книги Dive Into HTML5, которая стала большим подспорьем. Разумеется, все ошибки в этом практикуме на совести самого автора!
522 |
523 |
ОБРАТНАЯ СВЯЗЬ
524 |
525 |
Спасибо за то, что заинтересовались этим практикумом. Связаться со мной можно тут: mike@subelsky.com, или в твиттере: @subelsky. Я обожаю разговоры о HTML5, поэтому не стесняйтесь связываться со мной, если у вас возникли вопросы, или вы хотите обсудить интересные задачи.
526 |
527 |
Большинству приемов, которые обсуждаются в этом практикуме, я научился во время работы над игрой для программистов, использующей возможности HTML5. Игра эта называется EXP, и нам нужны бета-тестеры. Заявку на участие можно оставить здесь: exp.subelsky.com.
528 |
529 |
530 |
531 |
--------------------------------------------------------------------------------
/tutorial.markdown:
--------------------------------------------------------------------------------
1 | # HTML5: Больше, чем модная фишка
2 |
3 | # ЗАДАЧИ
4 |
5 | Цель этого практикума - показать, как новые возможности HTML5 могут быть использованы в современных веб-приложениях без участия дополнительных зависимостей и противоречий с уже существующим кодом. Курс рассчитан на то, чтобы дать вам достаточно знаний для самостоятельного использования новых возможностей в своих приложениях.
6 |
7 | # ПРЕДВАРИТЕЛЬНЫЕ ТРЕБОВАНИЯ
8 |
9 | - Все упражнения проверялись в последних версиях браузера Chrome, но они должны работать и в последних версиях Safari и Firefox. Некоторые упражнения работают в Firefox не так, как задумано, из-за ограничений браузера.
10 | Я не проверял работоспособность в IE или Opera, но большая часть кода должна работать и там.
11 |
12 | - Ваш браузер должен иметь консоль JavaScript. Такая консоль уже имеется в Safari и Chrome. Если вы используете Firefox, не забудьте установить [Firebug](http://getfirebug.com).
13 |
14 | - Вам понадобится текстовый редактор (Vi, Emacs, Textmate, Notepad, etc.) для внесения изменений в файлы проекта.
15 |
16 | # УСТАНОВКА
17 |
18 | 1. Скопируйте папку `start` из этого проекта в какое-нибудь другое место. Эта папка содержит все необходимые зависимости и послужит основой для игры, которую мы будем делать.
19 |
20 | 2. Изучите файл index.html в скопированной директории `start`. Он представляет собой упрощенный шаблон, основанный на [HTML5 Boilerplate](http://html5boilerplate.com/). Здесь вы можете найти [полный шаблон](https://github.com/paulirish/html5-boilerplate/blob/master/index.html). Это отличный способ изучить особенности HTML5.
21 |
22 | Обратите внимание на DOCTYPE, с которого начинается этот файл. Это все, что нужно, чтобы указать браузеру, что вы используете последнюю, самую современную версию HTML.
23 | Кроме того, в шаблоне используются новые семантические тэги `` и `