Вы находитесь на странице: 1из 285

jQuery-

OREILLY

^'

Alex MacCaw

JavaScript
Web Applications

OREILLY*
Beijing Cambridge Farnham Koln Sebastopol Tokyo

JavaScript

^'
- - -
-- - - -
- -

2012

32.988.02-018
004.738.5
15

15

- JavaScript. .: , 2012. 288 .: .


ISBN 978-6-459-01504-1
HTML5 CSS3 ,
, ,
. JavaScript rich-,
, .
, JavaScript-,
, MVC, , - ,
.
JavaScript-,
.
.

32.988.02-018
004.738.5

OReilly. .

.
, , ,
. , ,

, .

ISBN 978-1449303518 .

ISBN 978-5-459-01504-1

Authorized Russian translation of the English edition of titled JavaScript Web


Applications (ISBN 978-1449303518) 2011 Alex MacCaw
This translation is published and sold by permission of OReilly Media, Inc., which
owns or controls all rights to publish and sell the same.
, 2012
, , 2012

..........................................................................................13

1. MVC ....................................................................... 21

2. ........................................................40

3. ................................................................ 53

4. .................................................. 73

5. ...................... 91

. ............................................... 100

7. ............................................................. 109

8. - ................................. 126

9. ...................................................137

10. ............................ ....................... .............. 164


11. Spine.............................................................. 172
12. Backbone........................................................ 198
13. JavaScriptMVC..................................................220
. jQuery......................................................... 245
. CSS-...................................................... 257
. CS$3............................................... 262


........................................................................................13
?...............................................................................13
.............................................................................................................. 14
, ............................................................... 17
.............................................................................................18
, ............................................................18
j Query...............................................................................................................19
Holla................................................................................................................................19
......................................................................................................... 19
............................................................................................................... 20
1. MVC ................................................................. 21
................................................................................................................... 21
....................................................................................................22
MVC?.............................................................................................................. 23
.............................................................................................................................. 23
................................................................................................................. 24
...................................................................................................................... 25
, .................................................................. 26
................................................................................... 28
.................................................... 28
Prototype............................. 31
............................................ 32
............................................................................................................ 33
.................................35
................................................................................... 37
........................................................................................................ 38
2. .................................................40
............................................................................................... 40
..................................................................................... 41
...........................................................................................................42

................................................................................................................ 42
................................................................................................... 44
...................................................................................................45
.............................................................................................. 45
.........................................................................................46
j Q uery............................47
, DOM .................................................. 49

3. ............................................................. 53
MVC ......................................................................53
ORM .................................................................................................................55
.........................................................................................55
O RM .......................................................................................... 56
..................................................................................................58
ID ..........................................................................................59
........................................................................ ................................60
......................................................................................................... 61
........................................................................... 62
Ajax..............................................................................63
JSO N P.............................................................................................................................66
................................... 67
ORM ...................................................................................67
.......................................................................................68
ORM......................................70
............................................................................71
4. ...............................................73
............................................................................................................. 74
..................................................................................................... 74
..................................................................................................... 74
..................................................................................................75
.................................................................................. 76
................................................................. 78
........................................................................................... 79
.............................................................................................. 81
...................................................................................................... 83
........................................................................................................... 85
URL-..........................................................................85
.................................................................................. 86
Ajax Crawling.................................................................................................................87
History API HTML5.........................................................................88

5. ............... 91
.......................................................91
...........................................................................................................................93
.................................................................................................95
..................................................................................................... 95
....................................................................................................................97
........................................................................................................98
6. ....................................... 100
CommonJS....................................................................................................................... 101
....................................................................................................... 102
....................................................................................................... 102
...................................................................................................103
Yabble............................................................................................................................103
RequireJS...................................................................................................................... 104
.............................................................................................. 106
............................................................................. ................107
LABjs.............................................................................................................................107
FU B C............................................................................................................................108
7. ........................................................ 109
..................................................................................................109
............................................................................... 110
....................................................................................................................110
......................................................... .....................................................111
............................................................................................112
..................................................................... 114
......................................... 115
..................................................................................................115
................................................................................................................ 116
...................................................................................... 116
................................................................................................................ 117
.............................................................................................................. 119
..................................................................................120
.......................................................................:.................................. 120
Ajax.... ..............................................................122
j Query............ 124
............................ 124
...........................................................................................................125

8. - ........................ 126
.......................................................... 126
WebSockets..................................................................................................................... 127
Node.js Socket.IO...................................................................................................... 131
................................................................................. 133
.................................................................................................... 135
9. ................................................137
...................................................................................................139
............................................................................................................... 139
QUnit.............................................................................................................................140
Jasmine..........................................................................................................................144
.........................................................................................................................146
............................................................................................ 149
Zombie...........................................................................................................................149
Ichabod....................................................................................................... ...................151
..................................................................................... 152
..........................................................................................153
.................................................................................................................... 153
-............................................................................................................. 154
Firebug..........................................................................................................................155
............................................................................................................................156
....................................................!................ 157
............................................................................................ 158
.............................................................................................. 160
................................................................................ 161
10. ..........................................................164
..................................................................................................... 164
............................................................................................................... 165
............................................................................................................. 167
Gzip............................................................................................ 169
CDN...................................................................................................169
.........................................................................................................................170
............................................................................................................................171
11. Spine.......................................................172
........................................................................................................................ 172
......................... ....................................................................................................173
..................................................................................................173

10

..................................................................................................174
...................................................................................................................... 175
..........................................................................................................................176
............................................................................................. ;............................ 177
...................................................................................................178
....................................................................................................... 179
..................................................................................................................... 180
.............................................................................................. 180
..................................................................................................................183
..........................................................................184
.................................................................................................................... 184
............................................................................................185
............................................................................................... 185
..................................................................................................186
..................................................................................................186
...........................................................................................................187
.........................................................189
C ontact..........................................................................................................190
Sidebar....................................................................................................191
Contacts................................................................................................193
..........................................................................................................196

12. Backbone................................................. 198


............................................................................................................................199
................................................................................................... 200
...................................................................................................................... 201
............202
...............................................................................................................203
................................................................................. 204
........................................................................................... 204
................................................................................................. 205
................................................................................................................. 206
......................................................................................... 208
.............................................................................................210
........................................................... ................................... 210
........................................................... .................................... 211
To-Do ( ).....................................................................213
13. JavaScriptMVC.........................................220
....................................................................................................................... 221
............................................................................................................................ 221
................................................................................................ 222
......................................................................................... 222

11

.................................................................................................... 222
.......................................................................................223
.......................................................................................................223
...........................................................................................................223
.............................................................................................................................224
.........................................................................224
................................................................................................227
-............................................................................................... 227
...................................................................................................................228
-..................................................................................................228
................................................................................................229
................................................................ ........................................230
...................................................................................................... 231
......................................................................................................... 231
.....................................................................................................231
.........................................................................................................232
.............................................................................................. 232
CRUD-..........................................................................................................233
........................... 233
............................................................................................... 234
j Query.............................................................................................. 234
script-............................................................................................... 235
S.View ................................................................................................235
Deferred-.......................................................................................................235
, .............................236
S.Controller:
j Query.................................................................................................... 237
................................................................................................239
........................................................................ 240
..................................................................................................... 241
................................................................................................ 241
: CRUD-.......................................243

. jQuery................................................... 245
DOM-................................................................................. 247
DOM................................................................................................................248
..........................................................................................................................250
Ajax............................................................................................................................... ...251
......................................................252
................................................................................................................... 253
jQuery Growl................................................... 254

12

. CSS-............................................... 257
.................................................................................................................... 257
......................................................................................................................... 258
.......................................................................................................258
.............................................................................. 259
................................................................................................................................259
Less?...............................................................................259
....................................................................... 260
Rack........................................................................................... 260
JavaScript................................................................................................260
Less.app.........................................................................................................................260
. CSS3......................................... 262
........................................................................................................................263
................................................................................................................................263
......................................................................................................... 264
.....................................................................................................265
..............................................................................................................266
........................................................................................................................267
................................................................................................................268
.......................................................................................................................268
N- ................................................................................................269
.............................................................'......................................... 269
..............................................................................................270
.........................................................................................................................270
................................................................271
...................................272
............................................................................................................. 272
......................................................................273
...........................................................................................................................274
...............................................................................................275
Modemizr..................................................................................................................... 276
Google Chrome Frame.......................................................................... 277
............................................................................................................. 278

1995 JavaScript
Netscape JIT -.
, Ajax
,
JavaScript- .
JavaScript-,
,
.
. JavaScript
,
,
.
JavaScript: CSS3
HTML5, ,
, Safari, Chrome, Firefox ,
, IE9.
,
,
. HTML5 CSS3
, , ,
.
.

. ,
, ,
.
. ,
JavaScript-.

?
JavaScript, ,
JavaScript: (,
2012).

14

JavaScript, , jQuery ,
JavaScript-.
, , ,
JavaScript-.


1
JavaScript
,
. MVC,
JavaScript -,
.
2
, , API
. jQuery,
.
, DOM-,
PubSub.
3
MVC- ,
.
MVC ,
ORM- .
JSONP Ajax. ,
HTML5 ,
RESTful.
4

.

,
, DOM. ,
,
URL, , API
HTML5. .
5

JavaScript.
,
( , s c r ip t-

15

).

.
6
JavaScript- CommonJS. ,
CommonJS, CommonJS
, Yabble RequireJS.
,
. ,
CommonJS, Sprockets LABjs.
7
, HTML5: API
. ,
, , ,
.
() .

XMLHttpRequest Level , ,

j Query Ajax API.
8

WebSockets.

.
WebSockets ,
API JavaScript. RPC-,
WebSockets .
Socket. I
.
9
,
- JavaScript.
, ,
,
, QUnit Jasmine.

, Selenium.
, - Firefox WebKit,
JavaScript.

16

10
,
- JavaScript: .

, , gzip- ,
.
(CDN)

,
.
11
JavaScript, . Spine
VC- ,
, .
: , , .
,
, , .
12

Backbone, JavaScript-.
Backbone, ,
, .
RESTful JSON Backbone
. ,
.
13
JavaScriptMVC,
, j Query
- JavaScript.
JavaScriptMVC, , ,

. , CRUD, ,
, JavaScriptMVC.

j Query,
,
. j Query
, .
API, DOM, DOM,
, .

17

jQuery Ajax API, GET POST JSON. jQuery



-. :
jQuery Growl.

Less, CSS,
, ,
. Less
CSS,
CSSS-.
, Less,
JavaScript
Less- CSS.

CSS3.
CSS3, ,
.
CSS3 , rgba, , , .

Modernizr
box-sizing.

,
,
:

URL, , ,
.

,
, , , , , , ,
, , , , , , ,
, , , , , , XML-,
HTML-, , .

, ,
, , .

18


GitHub ( https://github.com/
maccman/book-assets).
zip- ( https://glthub.com/maccman/book-assets/zipball/master)
.
. , ,
.

assets/_ /.

,

assert() assertEqual().
assert () () ,
true;
, .
assert () : .
true, :
var assert = function(value, msg) {
if ( lvalue )
throw(msg || (value + " true));

};
assertEqual() ,
.
, assert (), .
, :
var assertEqual = function(vall, val2, msg) {
if (vail !== val2)
throw(msg || (vail + " " + val2));

};
, ,
. ,
:
assert( true );
// assertEqual()
assert( false === false )j
assertEqual( 1, 1 );

assertEqual(), , ,
,

jQuery

19

. ,
assets/chOO/deep_equality.html.

jQuery
jQuery,
JavaScript-, ,
DOM-,
Ajax.
, , , jQuery
JavaScript,
.
jQuery,
. API,
DOM-. jQuery
.

Holla
, Holla ,
JavaScript. Holla ,
,
. , Holla , :

CSS3 HTML5 ;
;
Sprockets Less;
WebSockets ;
JavaScript-, .

Holla GitHub ( http://github.com/


maccman/holla) .
Holla (. . 0.1).


, .
,

. ,
,
, .
.
(Stuart Eccles), (Tim Malbon),

20

Holla
|

*,

) (+

'

ttp /local host 3000/ria

Coogle

# CD

H o lla

Share

. 0.1. Holla, -

(Ben Griffins) (Sean OHalpin),


; (James Adam),
(Paul Battley) (Jonah Fox)
.
,
: (Henrik Joreteg),
(Justin Meyer), (Lea Verou), (Addy Osmani),
(Alex Barbara), (Max Williams) (Julio
Cesar Ody).
, , .


, ,
comp@piter.com ( , ).
!
, , GitHub
(https://github.com/maccman/book-assets).
- http://www.piter.com
.

MVC


JavaScript
. ,
Netscape ,
Google V8. ,
ECMAScript.
.
JavaScript, -
. ,
- , ,
, ,
. JavaScript?
- , JavaScript,
, Java
Java,
. - , ,
. ,
Python Ruby,
, .
JavaScript .
JavaScript ,
, ,
, , -
. , ,
, ,
.
JavaScript .
JavaScript- ,
JavaScript

22

1. MVC

, . , Gmail
Google Maps,
, .
JavaScript . JavaScript
,
,
, ,
.
, JavaScript. , , -
. - ,
JavaScript, . ,
-- (Model View Controller, MVC),
HTML JavaScript.
JavaScript
, ,
(Douglas Crockford) JavaScript: The Good Parts (
OReilly). ,
JavaScript-,
-.


JavaScript-
, ,
. ,
, ,
JavaScript-, HTML-.
,
.

, .
JavaScript . ,
, ,
, Python Ruby.
, ,
?
, MVC,
,
. ,
JavaScript-.

MVC?

23

MVC?
MVC , :
(Model, ), (View, )
(Controller, ). ,
.
1.
2.
3.
4.

.
.
, .
.

, , . 1.1 ,
- Holla.

H olla

3 CD

Profile

Hello there!
Activity

Settings

V.

___________ J

Share

. 1.1. - Holla

1.
2.
3.
4.
5.

-.
.
Chat Model.
.
- ^-.

MVC
. MVC-
, .
, .
MVC .

, .
, User, ,
, .
.
, .

24

1. MVC

, ,
, .
, : MVC- .
.
,
. ,
-, ,
, .
, :
var user = users["foo"];
destroyUser(user);

:
var user = User.find("foo");
user.destroy();

.
destroyllser(),
.
. destroy()
User,
. ,
, .
, ,
destroy(), .
3,
, (object-relational mapper, ORM).

, , ,
, . JavaScript
HTML, CSS JavaScript.
, ,
.
, ,
.
.
.
, MVC ,
.

25

: ,
, .
, , ,
:
// template.html
<div>
<script>
function formatDate(date) {

/*...*/
};
</script>
${ formatDate(this.date) }
</div>

formatDate() ,
MVC, -
, .
, , , ,
MVC-.
// helper.js
var helper = {};
helper.formatDate = function(){ /* ... */ };
// template.html
<div>
${ helper.formatDate(this.date) }
</div>


helper,
.
,
5.
MVC.

.
, ,
(, )
.

. , ,
.

26

1. MVC

-
. , jQuery:
var Controller = {};
//
(Controller.users = function($){
var nameClick = function(){

/*...*/
>;
//
$(function(){
$("#view .name").click(nameClick);

>)(jQuery);

users,
Controller.
,
.
.
, .
MVC
.
4.

,
MVC,
, JavaScript.
.
JavaScript ,
,
. , JavaScript
, , .
.
JavaScript , , ,
JavaScript , , , .
jQuery,
, . JavaScript ,
, . . .
, , , ,
JavaScript , .
JavaScript
new. -

27

.
JavaScript.
- new.
new , return.
, new
:
var Person - function(name) {
this.name = name;

};

// Person
var alice =^new Person('alice');
//
assert( alice instanceof Person );

, -
, ,
. , new.
// !
Person('bob'); //=> undefined ( )

undefined,
( ), name.
- , new.
- new,
() ,
. , th is
. , ,
.
, -,
th is.
, , , ,
:
var Class = function(){
var klass = function(){
this.init.apply(this, arguments);

};

klass.prototype.init
return klass;

= function(){};

};

var Person = new Class;


Person.prototype.init = function(){
// Person

>;

// :
var person = new Person;

28

1. MVC

:
JavaScript 2, class .
class _class
klass.



JavaScript:
Person.find = function(id){ /*...*/ };
var person = Person.find(l);


prototype:
Person.prototype.breath = function(){ /*...*/ };
var person = new Person;
person.breath();


fn:
Person.fn = Person.prototype;
Person.fn.run = function(){ /*...*/ };

jQuery,
jQuery, jQuery .fn.




. ,
-.
, ,
- :
var Person = new Class;
// , .
Person.find = function(id){ /* ... *1 };
//
var person = Person.find(l);

29

, ,
:
var Person = new Class;
// ,
Person.prototype.save = function(){ /* ... */ };
//
var person = new Person;
person.save();

,
.
, .

: extend() include():
var Class = function(){
var klass = function(){
this.init.apply(this, arguments);

};
klass.prototype.init

= function(){};

//
klass.fn = klass.prototype;
//
klass.fn.parent = klass;
//
klass.extend = function(obj){
var extended = obj.extended;
for(var i in obj){
klassfi] = obj[i];

>

if (extended) extended(klass)

};
//
klass.include = function(obj){
var included = obj.included;
for(var i in obj){
klass.fn[i] = obj[i];

>

if (included) included(klass)

};

return klass;

>;

30

1. MVC


extend(), .

:
var Person = new Class;
Person.extend({
find:
function(id) { /* ... */ },
exists: functions(id) { /* ... */ }

});

var person = Person.find(l);

include() , ,
, .
, ,
.
var Person = new Class;
Person.include({
save:
function(id) { /* ... */ },
destroy: functions(id) { /* ... */ }

});

var person = new Person;


person.save();

(extended)
(included). ,
:
Person.extend({
extended: function(klass) {
console.log(klass, " !");

}
});
Ruby,
. ,
. ,

.
var ORMModule = {
save: function(){
//

}
};

var Person = new Class;


var Asset = new Class;
Person.include(ORMModule);
Asset.include(ORMModule);

31


Prototype
prototype,
. ,

.
JavaScript , ,
, . .
-: , ,
.
, .

.
JavaScript
. , JavaScript
,
Object .prototype. , , ,
undefined ( ).
, Array.prototype,
JavaScript.
,
-.
-.
:
var Animal = function(){};
Animal.prototype.breath = function(){
console.log('');

};
var Dog = function(){};
// Dog () Animal ()
Dog.prototype = new Animal;
Dog.prototype.wag = function(){
console.log(' ');

};
:
var dog = new Dog;
dog.wag();
dog.breath(); //

32

1. MVC



.

(parent):
var Class = function(parent){
var klass = function(){
this.init.apply(this, arguments);

};
// klass
if (parent) {
var subclass = function() { };
subclass.prototype = parent.prototype;
klass.prototype = new subclass;

>;
klass.prototype.init = function(){};
//
klass.fn = klass.prototype;
klass.fn.parent = klass;
klass._super = klass._proto__;
/* (include) (extend)... */
return klass;

};
Class parent,
.

. ,
, .
_proto__;. , Super, js,
,
.
,
Class:
var Animal = new Class;
Animal.include({
breath: function(){
console.log('');

});
var Cat = new Class(Animal)
//
var tommy = new Cat;
tommy.breath();


JavaScript, . ,
, . , . .
th is, , .
, : apply()
11(). ,
.
apply () : .
, . :
function.apply(this, [1, 23 3])
call() , -.

,
. ,
, apply(), .
function.call(this, 1, 2, 3);

? ,

. JavaScript
,
. (
, .
- , , .)
j Query
apply() call () API,

each(). , ,
, :
$(*.clicky1).click(function(){
// 'this*
$(this).hide();

});
$(' ').each(function(){
// 'this'
$(this).remove();

34

1. MVC


th is . :
var clicky * {
wasClicked: function(){

/*...*/

addListeners: function(){
var self * this;
$('.clicky').click(function(){
self.wasClicked()

});
}
>;
clicky.addListeners();

, apply,
, :
var proxy = function(func, thisObject){
return(function(){
return func.apply(thisObject, arguments);

;
};
var clicky = {
wasClicked: function(){

/*...*/
b
addListeners: function(){
var self = this;
$('.clicky').click(proxy(this.wasClicked, this));

}
};
,
click; jQuery, , .
API jQuery -,
( , , , jQuery.proxy ()):
$('.clicky').click($.proxy(function(){ /* ... */ }, this));

1() 11()
.
:
var {
log: function(){
if (typeof console == "undefined") return;

35

//

var args * jQuery.makeArray(arguments);


//

arg s. u n sh ift(" (App)");


//

console.log.apply(console, args);
>
};
,
, , , console.log().
arguments,
, ,
. ,
, j query.
makeArray().



-, ,
, .
- , ,
,
. .:
var Class = function(parent){
var klass = function(){
t h i s . i n i t . app ly (th is, arguments);
};
k la ss.p ro to ty p e .in it = function(){};
klass.fn = klass.prototype;
// -

klass.proxy = function(func){
var s e lf = th is ;
return(function(){
return fu n c.ap p ly (self, arguments);
});
}
//

klass.fn.proxy = klass.proxy;
return klass;
};

36

1. MVC

proxy () ,
:
var Button = new Class;
Button.include({
init: function(element){
this.element = jQuery(element);
// (click)
this.element.click(this.proxy(this.click));

h
click: function(){ / * . . . * / }

;
click() -,
this.element, Button,
. JavaScript ECMAScript,
(ES5)
bind().
,
this. :
Button.include({
init: function(element){
this.element = jQuery(element);
// click
this.element.click(this.click.bind(this));

L
click: function(){ / * . . . * / }

});
()
click() .
bind(),
.
,
ES5 ,
. , ,
bind (), :
if ( !Function.prototype.bind ) {
Function.prototype.bind = function( obj ) {
var slice = [].slice,
args = slice.call(argumentsi 1),
self = this,
nop = function () {},

37

bound function () {
return self.apply( this instanceof nop ? this : ( obj || {} ),
args.concat( slice.call(arguments) ) );

};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;

};
}
Function prototype ,
: -
. (shimming)
, ,
JavaScript. es5-shim,
, ES5.


, ,
.
.

(_).
, API.
, .
JavaScript ,
, ,
. ,
,
JavaScript:
var Person = function(){};
(function(){

var findByld = function(){ /* ... */ };


Person.find = function(id){
if (typeof id == "integer")
return findByld(id);

};
})();

38

1. MVC

,
(findByld),
. Person
, .
var,
.
,
, :
(function(exports){
var foo = "bar";
//
exports.foo = foo;
})(window);
assertEqual(foo, "bar"); 7


,
, ,
. jQuery ,
HJS.
, $. Class.
create:
var Person = $.Class.create({
//
initialize: function(name) {
this.name = name;

}
});

:
var Student = $.Class.create(Personi {
price: function() { /* ... */ }

});

var alex = new Student("Alex");


alex.pay();

:
Person.find = function(id){ /* ... */ };

39

API HJS ,
clone() equal():
var alex = new Student("Alex");
var bill = alex.clone();
assert( alex.equal(bill) );

Ho HJS
Spine.
spine.js:
<script src="http://maccman.github.com/spine/spine.js"> </script>
<script>
var Person = Spine.Class.create();
Person.extend({
find: function() { /* ... */ }

});
Person.include({
init: function(atts){
this.attributes = atts || {};

}
});

var person = Person.init();


</script>

Spine API, ,
.
extend(),
include().
,
Spine.Class.
jQuery,
Prototype. API ,
.
jQuery (John Resig)
(classical inheritance with
the library). , ,
JavaScript.


JavaScript-,

.
JavaScript.
Netscape Microsoft ,
.
W3C, Internet Explorer
IE9.
, , jQuery Prototype,
, API,
. ,
,
, W3C.


, addEventListener ()
: (, click), , . .
() , useCapture (
). ,
DOM- , ,
, :
var button = document.getElementById("createButton");
button.addEventListener("click", function(){ /* ... */ }, false);

, removeEventListener()
,
addEventListener(). -
, :
var div = document.getElementByld("div");
var listener = function(event) { /* ... */ };
div.addEventListener("click", listener, false);
div.removeEventListener("click", listener, false);

41

- (listener)
(event),
, , .

.
, ,
:

click ()
dblclick ( )
mousemove ( )
mouseover ( )
mouseout ( )
focus ( )
blur ( )
change (, )
submit ( )

- Quirksmode.


, .

,
? , , , Netscape Microsoft
.
Netscape 4 ,
, -
, . . .
Microsoft ,
, ,
. . .
, , ,
, . W3C
.
, W3C, ,
, .

, useCapture
addEventListener(). ,
addEventListener(), true,

42

2.

, false,
:
// false
button.addEventListener("click", function(){ / * . . . * / } , false);

, , .
, addEventListener()
false.


,
stopPropagation(), .
, :
button.addEventListener("click", function(e){
e .stopPropagation();

/*...*/
b fa ls e );
, , jQuery, stoplmmediatePropagation(),
, .
. ,
, ,
.
.

.
preventDefault(), .
false:
form.addEventListener("submit", function(e){

/*...*/
return confirm("Bbi ?");
b false);

confirm() false, . .
, ,
, false, .


stopPropagation() preventDefault(),
. ,

43

W3C, ,
.
:
bubbles
, DOM
( )
, :
button
, , ( )
( - )
ctrlKey
, , Ctrl
altKey
, , Alt
shiftKey
, , Shift
metaKey
, , Meta
, :
isChar
, ,
charCode
(
keypress)
keyCode

which
,

, :
pageXypageY
(. . )
screenXt screenY

44

2.

, :
currentTarget
DOM-
target, originalTarget
DOM-
relatedTarget
DOM-, ( )
, W3C,
. , , jQuery Prototype,
.


, ,
JavaScript,
. , API
jQuery,
, Prototype, MooTools YUI.
API, .
API jQuery
bind().
jQuery, :
jQuery("#element").bind(eventName, handler);

,
:
jQuery("#element").bind("click", function(event) {

//
});

...

jQuery , (click),
(submit) (mouseover).
:
$("#myDiv").click(function(){

//
});

...

,
, . . ,
.
(load) :
jQuery(window).bind("load", function() {
$("#signinForm").submit(checkForm);

});

45

,
DOM DOMContentLoaded.
DOM,
. , ,
.
DOMContent Loaded , jQuery
ready(),
:
jQuery.ready(function($){
$("#myForm").bind("submit", function(){ /* ... */ });

});
ready ()
jQuery:
jQuery(function($){
// ,

});


, ,
.
addEventListener()
::
new function(){
this.appName = "wem";
document.body.addEventListener("click", function(e){
// , appName
alert(this.appName);
}, false);

>;

, . 1,
- .
, jQuery proxy (),
,
:
$("signinForm").submit($.proxy(function(){ /* ... */ }, this));


, ,

.

46

2.

, SproutCore,
:
// ul
list.addEventListener("click", function(e){
if (e.currentTarget.tagName == "li") {

/*...*/
return false;

}
}, fa ls e );
jQuery :
delegate() , .
click
li. , delegate(), ,
:
// ! 'li' ( )
$("ul li").click(function(){ /* ... */ });
//
$("ul").delegate("li", "click", /* ... */);

,
,
, .
li, ,
click.


, ,
. ,
,
jQuery. W3C, ,
,
, jQuery
Prototype.
jQuery , t r i g
ger (). ,
. :
//
$(".class").bind("refresh.widget", function(){});
//
$(".lass").trigger("refresh,widget");

47


t rigger ().
:
$(".class").bind("frob.widget", function(event, dataNumber){
console.log(dataNumber);

});
$(".class").trigger("frob.widget", 5);

,
DOM.


jQuery
,
jQuery,
, DOM.
jQuery, ,
jQuery.
-
,
.
.
, jQuery
. u l-,
(click). ,
active, active
:
<ul id="tabs">
<li data-tab="users">Users</li>
<li data-tab="groups">Groups</li>
</ul>
<div id="tabsContent">
<div data-tab="users"> ... </div>
<div data-tab="groups"> ... </div>
</div>

, div- tabsContent,
.
active div-, ,
.

48

2.

CSS,
active:
jQuery.fn.tabs = function(control){
var element = $(this);
control = $(control);
element.find("li").bind("click", function(){
// active

element.find("li").removeClass("active");
$(this).addClass("active");
// active tabContent
var tabName = $(this).attr("data-tab");
control.find(">[data-tab]").removeClass("active");
control.find(">[data-tab='" + tabName + "']").addClass("active");

});
//
element.find("li:first").addClass("active");
// 'this'
return this;

>;
prototype jQuery,
jQuery:
$("ul#tabs").tabs("#tabContent");

?
,
.
delegate().
, , . ,
, , ,
.
, ,
. change.tabs
,
active :
jQuery.fn.tabs = function(control){
var element = $(this);
control = $(control);
element.delegate("li", "click", function(){
//
var tabName = $(this).attr("data-tab");

, DOM

49

//
element.trigger("change.tabs", tabName);

});
//
element.bind("change.tabs", function(e, tabName){
element.find("li").removeClass("active");
element.find(">[data-tab='" + tabName + "']").addClass("active");

});
element.bind("change.tabs", function(e, tabName){
control.find(">[data-tab]").removeClass("active");
control.find(">[data-tab=" + tabName + "']").addClass("active");

});
//
var firstName = element.find("li:first").attr("data-tab");
element.trigger("change.tabs", firstName);
return this;

>;
, ?
, ,
,
. ,
change.tabs :
$("#tabs").trigger("change.tabs", "users");

,
:
$("#tabs").bind("change.tabs", function(e, tabName){
window.location.hash = tabName;

});
$(window).bind("hashchange", function(){
var tabName = window.location.hash.slice(l);
$("#tabs").trigger("change.tabs", tabName);

});

.

,
DOM
, , ,
,

50

2.

.
(DOM),
.
-, .
- (Publish/Subscribe, Pub/Sub)
, : .
,
, .
,
, . ,
.

,
.
, Pub/Sub ?
, ,
. PubSub,
:
var PubSub = {
subscribe: function(ev; callback) {
// _callbacks,
var calls = this._callbacks || (this._callbacks = {});
// ,
// ,
(this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);
return this;

publish: function() {
//
var args = Array.prototype.slice.call(arguments, 0);
// ( )
var ev
= args.shift();
// , _callbacks
//
var list, calls, i, 1;
if (!(calls = this._callbacks)) return this;
if (!(list = this._callbacks[ev])) return this;
//
for (i * 0, 1 = list.length; i < 1; i++)
list[i].apply(this, args);
return this;

, DOM

51

>
>;
//
PubSub.subscribe("wem", function(){
alert("Wem!");
5
PubSub.publish("wem");

,
, (:).
PubSub.subscribe("user:create", function(){ /* ... */ });

jQuery, ,
(Ben Alman). ,
:
/*!
* jQuery Tiny Pub/Sub - V0.3 - 11/4/2010
* http://benalman.com/

* Copyright () 2010 "Cowboy" Ben Alman


* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/

*/
(function($){
var = $({});
$.subscribe = function() {
o.bind.apply( o, arguments );

};
$.unsubscribe = function() {
o.unbind.apply( o, arguments );

};
$.publish = function() {
o.trigger.apply( o, arguments );

>;

})(jQuery);

API , jQuery bind() trigger().


,
jQuery publish() subscribe():
$.subscribe( "/some/topic", function( event, a, b, ) {
console.log( event.type, a + b + );

>);
$.publish( "/some/topic", "a", "b", "c" );

52

2.

Pub/Sub ,
. PubSub
:
var Asset = {};
// PubSub
jQuery.extend(Asset, PubSub);
// publish subscribe
Asset.subscribe("create", function(){

//
>);

...

Asset , PubSub,
extend(), jQuery.
publish() subscribe() Asset.
, -
(ORM), ()
Ajax-3anpoca.


.
,
.
JavaScript-
. -,
.
.
,
. ,
, .

,
.
,
. ,
,
.

.

MVC
,

. MVC,
( MVC).
. ,
,
.

54

3.

JavaScript ,
. :
var User = {
records: [ / * . . . * / ]

>;

User.records. , ,
User. ,
fetchRemote() :
var User = {
records: [],
fetchRemote: function(){ / * . . . * / }

>;

- MVC.
(
) .

, ,
. , destroy()
. , ,
User:
var user = new User;
user.destroy()

, User ,
:
var User = function(atts){
this.attributes = atts || {};

};
User.prototype.destroy = function(){

/*...*/
};
, ,
, User:
User.fetchRemote = function(){

/* . . . */
};

, (Peter Michaux),
.

ORM

55

ORM
- (ORM)
, JavaScript.
,
JavaScript-. ORM, , ,
,
, Ajax- .
HTML ,
, .
, ORM.
ORM ,
. ORM
SQL, ORM
JavaScript.
,

. ,
, ,
,
.


ORM Object.
create(), 1 ,
. -
new , .
Object.create() , -,
-. ,
, ,
.
Object.create() ECMAScript, ,
, IE, .
, :
if (typeof Object.create !== "function")
Object.create = function(o) {
function F() {>
F.prototype = o;
return new F();

};
(Douglas
Crockford) Prototypal Inheritance. ,
JavaScript.

56

3.

Model,
:
var Model = {
inherited: function(){}>
created: function(){},
prototype: {
init: function(){}

b
create: function(){
var object = Object.create(this);
object.parent = this;
object.prototype = object.fn = Object.create(this.prototype);
object.created();
this.inherited(object);
return object;

b
init: function(){
var instance = Object.create(this.prototype);
instance.parent = this;
instance.init.apply(instance, arguments);
return instance;

}
>;
Object.reate(),
, . create()
, Model,
. init() ,
Model.prototype, . . Model:
var Asset = Model.create();
var User = Model.create();
var user = User.initQ;

ORM
, Model, :
//
jQuery.extend(Model, {
find: function(){}

ORM

57

//
jQuery.extend(Model.prototype, {
init: function(atts) {
if (atts) this.load(atts);

b
load: function(attributes){
for(var name in attributes)
this[name] - attributes[name];

>
;
jQuery.extend()
for ,
, load().
:
assertEqual( typeof Asset.find, "function" );

,
extend() include() Model:
var Model = {
/* ... ... */
extend: function(o){
var extended = o.extended;
jQuery.extend(this, o);
if (extended) extended(this);

b
include: function(o){
var included = o.included;
jQuery.extend(this-prototype, o);
if (included) included(this);

>
};
//
Model.extend({
find: function(){}

;
//
Model.include({
init: function(atts) { /* ... */ },
load: function(attributes){ / * . . . * / }

;
(assets) :
var asset = Asset.init({name: "foo.png"});

58

3.


, . .
, .
records, Model.
,
:
//
Model.records - {};
Model.include({
newRecord: true,
create: function(){
this.newRecord = false;
this.parent.records[this.id] = this;

b
destroy: function(){
delete this.parent.records[this.id];

>
;
? ,
:
Model.include({
update: function(){
this.parent.records[this.id] = this;

>
>);
,
, :
//
Model.include({
save: function(){
this.newRecord ? this.create() : this.update();

}
;
fin d (), ID?
Model.extend({
// no ID
find: function(id){
return this.records[id] || throw(" ");

>
});

ORM

59

, ORM, :
var asset = Asset.init();
asset.name = "same, same";
asset.id
= 1
asset.save();
var asset2 = Asset.init();
asset2.name = "but different";
asset2.id
= 2;
asset2.save();
assertEqual( Asset.find(l).name, "same, same" );
asset2.destroy();

ID
ID .
, , , .

GUID- (Globally Unique Identifier
). , JavaScript
128- GUID-
API, .
- GUID-
, ,
-, BIOS,
,
,
! JavaScript
Math.random().
(Robert Kieffer) GUID, Math.random() GUID. ,
:
Math.guid = function(){
return '--4--'.replace(/[xy]/g,
function(c) {
var r = Math.random()*16|0, v = == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
}).toUpperCase();

>;

60

3.

, GUID-,
ORM .
create():
Model.extend({
create: function(){
if :( !this.id ) this.id = Math.guid();
this.newRecord = false;
this.parent.records[this.id] = this;

}
});
ID GUID-:
var asset = Asset.init();
asset.save();
asset.id //=> "54E52592-313E-4F8B-869B-58D61F00DC74"


ORM, ,
. ,
find () , ,
, .
,
update():
var asset = new Asset({name: "foo"});
asset.save();
//
assertEqual( Asset.find(asset.id).name, "foo" );
// , update()
asset.name = "wem";
// ! ,
// "wem"
assertEqual( Asset.find(asset.id).name, "foo" );

, fin d ().

:
Asset.extend({
find: function(id){
var record = this.records[id];
if ( !record ) throw("Hen3BecTHafl ");
return record.dup();

ORM

61

});
Asset.include({
create: function(){
this.newRecord = false;
this.parent.records[this.id] = this.dup();

b
update: function(){
this.parent.records[this.id] = this.dup();

dup: function(){
return jQuery.extend(true, {}, this);

}
;
: Model.records ,
:
assertEqual( Asset.records, Person.records );

:
var asset = Asset.init();
asset.save();
assert( asset in Person.records );

records
. Model.created()
, ,
:
Model.extend({
created: function(){
this.records = {};

}
});


- ,
.
,
.
,
. ,
.
,
.

62

3.


,
.

, .
, (
).
, ,

? , ,
( ).
, .
,
. - ,
. ,
,
.

HTTP- Ajax JSONP.
,
,
. AJAX
JSON HTML-,
.


,
, ,
.
.
JSON- .
, Ruby on Rails :
<script type="text/javascript">
var User = {};
User.records = <%= raw @users.to_json %>;
</script>

ERB- JSON- .
raw JSON.
HTML :
<script type="text/javascript">
var User = {};
User.records = [{"first_name": "Alex"}];
</script>

ORM

63

JavaScript JSON,
, JavaScript.

Ajax
, ,
, , :
, .
, Ajax
API ,
.
Ajax XMLHttpRequest,
Getting Started Mozilla. ,
, , , ,
jQuery, Ajax API, .
API jQuery,
XMLHttpRequest .
jQuery Ajax API jQuery.
ajax() , ,
. jQuery.ajax()
,
. ,
.
url
URL. ,
success
, .
, .
contentType
Content-Type.
, application/x-www-formurlencoded, .
data
, . , jQuery
URL.
type
HTTP-: GET, POST DELETE.
GET.
dataType
, . jQuery , ,
. dataType , jQuery
MIME- . :

64

3.

text
; ,
script
jQuery JavaScript,
json
jQuery JSON, ,
jsonp
JSONP-, .
, , Ajax-,
, :
jQuery.ajax({
url: "/ajax/endpoint",
type: "GET",
success: function(data) {
alert(data);

}
});
. , jQuery
. jQuery .get () URL,
:
jQuery.get("/ajax/endpoint", function(data){
$(".ajaxResult").text(data);

;
GET-:
jQuery.get("/ajax/endpoint", {foo: "bar"}, function(data){

/*...*/
;
JSON,
jQuery.getDSON(), data "json":
jQuery.getDSON("/json/endpoint", function(j son){

});
jQuery .post (), URL,
:
jQuery.post(H/usersH, {first_name: "Alex"}, function(result){
/* Ajax POST */

});

ORM

65

HTTP- DELETE, HEAD OP


TIONS,
jQuery.ajax().

Ajax API, jQuery,


, .
Ajax (same origin poli
cy), , ,
, . :
Ajax- cookie . ,
.

Gmail, Facebook
,
.

, ,
. ,
Adobe Flash Java, ,
, Ajax ,
CORS
(cross-origin resource sharing).
CORS ,
.
,
IE6, .
CORS :

IE >= 8 ( )
Firefox >= 3
Safari:
Chrome:
Opera:

CORS .
, :
Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET,POST

GET POST- example.com.


, GET, POST.

66

3.

,
Access-Control-Allow-Origin, .
,
(origin) (*).
, Safari, OPTIONS,
. Firefox , CORS , .
.
,
Access-Control-Request-Headers:
Access-Control-Request-Headers: Authorization

, Ajax-
,
OAuth:
var req = new XMLHttpRequest();
req.open("POST", "/endpoint", true);
req.setRequestHeader("Authorization", oauth_signature);

, CORS Internet Explorer 8


, Microsoft
. Microsoft , XDomainRequest,
XMLHttpRequest
. XMLHttpRequest,
. , GET POST,
,
, , Content-Type: text/
plain. , ,
Access-Control, CORS IE.

JSONP
JSONP, JSON (padding),
CORS
. script,
JSON,
. script -
, .
, scrip t, :
<script src="http://example.com/data.json"> </script>

, data.json, JSON-,
:
jsonCallback({"data": "foo"})

ORM

67

.
, :
window.jsonCallback = function(result){
//

>
. , jQuery API:
jQuery.getJSON("http://example.com/data.json?callback=?H, function(result)

//

;
jQuery URL
,
.
,
.


JSONP
, .
, , API
, CORS JSONP
. Ajax- cookie-
, , API .

, .
CORS/JSONP ,
API,
:
, ,
;
(, ).
,
,
Auth.

ORM
ORM
. ,
. Model populate(),

68

3.

,
records:
Model.extend({
populate: function(values){
// model records
this.records = {};
for (var i=0, il = values.length; i < il; i++) {
var record = this.init(values[i]);
record.newRecord = false;
this.records[record.id] = record;

}
}
});
Model.populate()
:
jQuery.getJSON("/assets", function(result){
Asset.populate(result);

});
ORM.


.
cookie-
Adobe Flash. Cookie- API
, ,
, .
Flash,
.
, HTML5,
. cookie-,
.
, ,
(, ,
), 5 :

IE > * 8
Firefox >* 3.5
Safari >= 4
Chrome > = 4
Opera >= 10.6

HTML5 HTML5 - (HTML5 Web Storage) : (local

ORM

69

storage) (session storage).


,
.
,
.

, , , localStorage sessionStorage.
API JavaScript ,
, ,
:
//
localStorage[,,someDataM] = "wem";

API - :
//
var itemsStored = localStorage.length;
// ( -)
localStorage.setltem("someData", "wem");
// , null,

localStorage.getItem("someData"); //=> "wem";


// , null,
localStorage.removeItem("someData");
// ( )
localStorage.clear();

,
, .
JSON,
JSON- ( ),
JSON- :
var object = {some: "object"};
//
localStorage.setItem("seriData", JSON.stringify(object));
//
var result = DSON.parse(loc^lStorage.getItem(MseriDataM));

/
( 5 ),
QUOTA_EXCEEDED_
ERR.

70

3.


ORM
ORM,
.
localStorage,
JSON-. ,
:
var json = JSON.stringify(Asset.init({name: "foo"}));
json //=> "{"parent":{"parent":{"prototype":{}},"records":[]},"name":"foo"}"

, JSON- .
,
. Model ,
:
Model.extend({
created: function(){
this.records = {};
this.attributes = [];

>
});

Asset.attributes = ["name", "ext"];

,
, attributes
Model.
, , records.
attributes(),
:
Model.include({
attributes: function(){
var result = {};
for(var i in this.parent.attributes) {
var attr = this.parent.attributes[i];
result[attr] = this[attr];

>

result.id = this.id;
return result;

}
});
:
Asset.attributes = ["name", "ext"];

attributes () :
var asset = Asset.init({name: "document", ext: ".txt"});
asset.attributesQ; //=> {name: "document", ext: ".txt"};

ORM

71

JSON.stringify(),
to3SON () . JSON
,
records :
Model.include({
toJSON: function(){
return(this.attributes());

>
;
.
JSON- :
var json = JSON.stringif(Asset.records);
json //= M{M7B2A9E8D...":"{"name":"document" ,"ext":".
txt","id":"7B2A9E8D..."}"}"

JSON-,
.
Model : savel_ocal()H loadLocalQ.
Model.records ,
localStorage:
var Model.LocalStorage = {
saveLocal: function(name){
//
var result = [];
for (var i in this.records)
result.push(this.records[i])
localStorage[name] = JSON.stringify(result);

loadLocal: function(name){
var result = DSON.parse(localStorage[name]);
this.populate(result);

>
>;
Asset.extend(Model.LocalStorage);

,
.
.


jQuery- post()
. : URL, :
jQuery.post("/users", {first_name: "Alex"}, function(result){
/* Ajax POST */

});

72

3.

, attributes(),
, POST- :
jQuery.post("/assets", asset.attributesQ, function(result){
/* Ajax POST */

;
REST-, HTTP POST-
PUT- .
Model : createRemoteQ updateRemoteQ,
HTTP- :
Model.include({
createRemote: function(url, callback){
$.post(url, this.attributesQ, callback);

updateRemote: function(url, callback){


$.ajax({
url:
url,
data:
this.attributes(),
success: callback,
type:
"PUT"

;
>
;
createRemote() Asset,
POST- :
// :
Asset.init({name: "jason.txt"}).createRemote("/assets");

,
- .
,
cookie-. JavaScript-
, ,
.

.
,
.
, JavaScript-
.
.
? ? , DOM?
,
.
DOM.
, ! ,
MVC,
.
?
. ,
.

,
.
,
.
.
,
. .

74

4.



.
, , ,
JavaScript.
. ,
, ,
:
(function(){

/*...*/
>)();
() ,
. JavaScript ,
.


,
.
,
.
, ,
.
, ,
JavaScript
.
.
, .

, ,
, :
(function($){

/*...*/
})(jQuery);

jQuery
, $. ,
, , ,
. jQuery $
.

75

, - ,
. window
, ,
:
(function($, exports){
exports.Foo = "wem";
})(jQuery, window);
assertEqual( Foo, "wem" );

, exports,

, .



,
.
this window:
(function(){
assertEqual( this, window );

>)();
,
. :
(function(){
var mod = {};
mod.contextFunction = function(){
assertEqual( this, mod );

};
mod.contextFunction();

();
contextFunctionQ
mod. this,
. ,
,
:
(function($){
var mod = {};
mod.load = function(func){
$($.proxy(func, this));

&

76

4.

mod.load(function(){
this.view = $("#view");

>);
mod.assetsClick = function(e){
//

};

mod.load(function(){
this.view.find(".assets").click(
$.proxy(this.assetsClick, this)

);
});

})(jQuery);

load(), ,
. ,
jQuery.
proxy ().

, ,
assetsClick().
. ,
.


,
.
load() proxy() include():
(function($, exports){
var mod = function(includes){
if (includes) this.include(includes);

};

mod.fn = mod.prototype;
mod.fn.proxy = function(func){
return $.proxy(func, this);

>;
mod.fn.load = function(func){
$(this.proxy(func));

>;
mod.fn.include = function(ob){
$.extend(this, ob);

};
exports.Controller = mod;
})(jQuery, window);

77

proxy () ,
,
. includeQ
, .
exports,
Controller.
Controller, -.
, class-
:
(function($, Controller){
var mod = new Controller;
mod.toggleClass = function(e){
this.view.toggleClass("over", e .data);

};
mod.load(function(){
this.view = $("#view");
this.view.mouseover(this.proxy(this.toggleClass), true);
this.view.mouseout(this.proxy(this.toggleClass), false);

>);

})(jQuery, Controller);

, view
. , , toggleC lass(),
, c la ss- .
, ,
assets/ch04/modules.html.

, th is.

. ,
Controller, :
Controller.fn.unload = function(func){
jQuery(window).bind("unload", this.proxy(func));

};

includeQ , :
var mod = new Controller;
mod.include(StateMachine);

StateMachine
,
don't repeat yourself DRY ( ).

78

4.


,
DOM, ,
.
,
,
.

DOM. , ,
,
DOM .
,
. Controller -,
,
:
// window
//
var exports = this;
(function($){
var mod = {};
mod.create = function(includes){
var result = function(){
this.init.apply(this, arguments);

};
result.fn = result.prototype;
result.fn.init = function(){};
result.proxy = function(func){ return $.proxy(func, this); };
result.fn.proxy = result.proxy;
result.include = function(ob){ $.extend(this.fn, ob); };
result.extend = function(ob){ $.extend(this, ob); };
if (includes) result.include(includes)
return result;

>;
exports.Controller = modj

})(jQuery);

Controller.createQ
, .
, jQuery(function(){ /* ... * /}).

79

jQuery.ready (), ,
DOM :
jQuery(function($){
var ToggleView = Controller.create({
init: function(view){
this.view = $(view);
this.view.mouseover(this.proxy(this.toggleClass), true);
this.view.mouseout(this.proxy(this.toggleClass), false);

b
this.toggleClass: function(e){
this.view.toggleClass("over", e .data);

>
>);
// Instantiate controller, calling init()
new ToggleViewC'tview");

>);

.
, ,
, .


,
. (ID),
.
ID class,
.
, .

jQuery(),
.
,
:
//

...

init: function(view){
this.view = $(view);
this.form = this.view.find("form");

>
, ,
DOM. ,

80

4.

,
:
elements: {
"form.searchForm": "searchForm",
"form input[type=text]": "searchlnput"

}
, this.searchForm this.searchlnput

. jQuery,
,
.

,
. init(),
:
var exports = this;
jQuery(function($){
exports.SearchView = Controller.create({
//
elements: {
"input[type=search]": "searchlnput",
"form": "searchForm"

//
init: function(element){
this.el = $(element);
this.refreshElements();
this.searchForm.submit(this.proxy(this.search));

search: function(){
console.log("Searching:", this.searchlnput.val());

//
$: function(selector){
// 'el'
return $(selector, this.el);

//
refreshElements: function(){
for (var key in this.elements) {
this[this.elements[key]] = this.$(key);

}
}
});

new SearchView("#users");

});

81

refreshElements()
el, .
refreshElements()
this.searchForm this.searchlnput,
DOM.
, ,
assets/ch04/views.html.



(proxying) events,
.
elements, , :
events: {
"submit form": "submit"

}
SearchView.
refreshElements(), delegateEvents(),
.
,
. Search View search(),
<form /> :
var exports = this;
jQuery(function($){
exports.SearchView = Controller.create({
// ,
//
events: {
"submit form": "search"

init: function(){

/ / ...
this.delegateEvents();

b
search: function(e){ / * . . . * / } ,
//
//
eventSplitter: /A(\w+)\s*(.*)$/,
delegateEvents: function(){

&

82

4.

for (var key in this.events) {


var methodName = this.events[key];
var method = this.proxy(this[methodName]);
var match * key.match(this.eventSplitter);
var eventName = matchfl], selector = match[2];
if (selector === '') {
this.el.bind(eventName, method);
} else {
this.el.delegate(selector, eventName, method);

}
}
}
;
, delegate(),
bindQ delegateEvents().
, el.
, ,
, .
, . .
,
.

Controller,
.
( assets/ch04/finished_
controller.html):
var exports = this;
jQuery(function($){
exports.SearchView = Controller.create({
elements: {
"input[type=search]": "searchlnput",
"form": "searchForm"

events: {
"submit form": "search"

init: function(){ /* ... */ },


search: function(){
alert("Searching: " + this.searchlnput.valQ);
return false;

b
;

new SearchView({el: "#users"});

83


(State machines, , , Finite
State Machines (FSM))
(UI). ,
,
. ?
, : .
, .
.
? ,
, , ,
.

, .
,
.
, ,
,
.
,
, . ,
, . -,
Events, API ,
jQuery (. 2),
:
var Events = {
bind: function(){
if ( Ithis.o ) this. = $({});
this. .bind.apply(this.o, arguments);

b
trigger: function(){
if ( Ithis.o ) this. = $({});
this..trigger.apply(this., arguments);

}
};
Events, , jQuery
DOM,
. StateMachine,
add():
var StateMachine = function(){};
StateMachine.fn = StateMachine.prototype;

&

84

4.

//
$.extend(StateMachine.fn, Events);

StateMachine.fn.add = function(controller){
this.bind("change", function(e, current){
if (controller == current)
controller.activate();
else
controller.deactivate();

});
controller.active = $.proxy(function(){
this.trigger("change", controller);
b this);

};
add ()
active().
active(), .
activate() ,
deactivate() .
, , ,
, :
var conl = {
activate: function(){ /* ... */
deactivate: function(){ / * . . . * / }

};
var con2 = {
activate: function(){ /* ... */ },
deactivate: function(){ / * . . . * / }

};
// StateMachine
var sm = new StateMachine;
sm.add(conl);
sm.add(con2);
//
conl.active();

add(), ,
change, activate()
deactivate(), , .
active(), ,
change:
sm.trigger("change", con2);

85

a c tiv a te ()
, . ,
d eactiv ate() -, . CSS
.
, , . active, ,
:
var conl = {
activ ate: function(){
$( "#conl"). addClass ( "activ e");
b
deactivate: function(){
$("#conl").removeClass("active");

}
};
var con2 = {
activ ate: function(){
$("#con2").addClass("active");

b
deactivate: function(){
$ ("#con2") . removeClass( "activ e");
}
};
.activ e
, :
#1, #con2 { display: none; }
# conl.active, #con2.active { display: block; }
assets/ch04/state_machine.html.

,
URL . ,
URL
. ,
, .
, URL.
URL.
URL
.
URL .

URL-
URL
, - . ,

86

4.

. URL .
,
. , URL
#! /maccman:
http://twitter.com/#!/maccman

, location:
//
window.location.hash = "foo";
assertEqual( window.location.hash , "#foo" );
// "#"
var hashValue = window.location.hash.slice(l);
assertEqual( hashValue, "foo" );

URL , location.hash .
location.hash - URL
#.

, .
, , , ,
.


, .
, hashchange.
, ,
:
window.addEventListener("hashchange", function(){ /* ... */ }, false);

jQuery:
$(window).bind("hashchange", function(event){
// ,

});
hashchange
.
:

IE > - 8
Firefox >= 3.6
Chrome
Safari >= 5
Opera > - 10.6

87

,
jQuery, hashchange
.
,
.
,
:
jQuery(function(){
var hashValue = location.hash.slice(l);
if (hashValue)
$(window).trigger("hashchange");

Ajax Crawling
(search engine crawlers) JavaScript
, .
- :
URL , -
.
, JavaScript-
, Google,
.
.
HTML- ,
JavaScript- .

, . , Google
: Ajax Crawling.
(
):
http://twitter.com/#!/maccman

Google,
Ajax Crawling. URL
, , , , URL :
http://twitter./?_escaped_fragment_=/maccman

URL- _escaped_f ragment_.


URL (ugly URL),
. URL.
- URL-, ,

.

88

4.

URL ,
, HTML ,
.
, .
curl -v http://twitter.com/P_escaped_fragment_s/maccman
302 redirected to http://twitter.com/maccman

(302),
(301), URL, ,
-, . . JavaScript- (http://twitter.eom/#!/
maccman). ,
HTML , URL-
_escaped_f ragment_.
Ajax Crawling,
, Fetch as Googlebot.
,
- ,
. -
, Google, JavaScript
, .

History API HTML5


History API HTML5 , ,
URL. ,
URL ,
. ,,
, ,
.
:

Firefox >= 4.0


Safari >=5.0
Chrome >= 7.0
IE:
Opera >=11.5

API histo
ry .pushState(). : ,
URL-:
// ,
// popstate
var dataObject = {
createdAt: *2011-10-10*,

89

author: 'donnamoss'

};

var url = '/posts/new-url';


history.pushState(dataObject, document.title, url);

, ,
.
data

. popstate (
).
title
, , ,

.
url
, URL
. , URL
, , .
URL,
, URL .
History API JavaScript-
, URL HTML-.
history.pushState()
URL, . ,
URL, API, ,
, .
HTML-, ,
, JavaScript.
, JavaScript-,
URL. ,
404 ( ), URL
.
, URL ,
.
History API . history.replaceState()
, history.pushState(),
. ,
history.back() history.forwardQ.
popstate
history.pushState(). event

90

4.

state, data,
history.pushState():
window.addEventListener("popstate", function(event){
if (event.state) {
// history.pushState()

}
>);

URL. jQuery ,
.
:
$(window).bind("popstate", function(event){
event = event.originalEvent;
if (event.state) {
// history.pushState()

>
});

,
.
HTML-,
,
. MVC,
.
! , .
, ,
,
.
HTML-, .
JavaScript- .
-, , ,
,
. Ajax-, JSON-,
.
HTML ,
. ,
,
.

DOM- JavaScript,
. .


JavaScript. DOM-,
document .createElement(), .

92

5.

,
:
var views = document.getElementById("views");
views.innerHTML =
//
var container = document.createElementC'div");
container.id = "user";
var name = document.createElement("span");
name.innerHTML = data.name;
container.appendChild(name);
views.appendChild(container);

API jQuery:
$("#views").empty();
var container = $("<div />").attr({id: "user"});
var name
= $("<span />").text(data.name);
$("#views").append(container.append(name));

, ,
, , , ,
.
MVC- .
HTML
, .
, ,
,
.
, , HTML-,
:
<div id="views">
<div class="groups"> ... </div>
<div class="user">
<spanx/span>
</div>
</div>


jQuery:
$("#views div").hide();
var container = $("#views .user");
container.find("span").text(data.name);
container.show();

,
.

93

HTML,
. ,
, , DOM-.
,
.
JavaScript- , HTML-
, ,
JavaScript, .
JavaScript
, , Smarty , ERB
Ruby, Python.

jQuery.tmpl. jQuery
,
,
, .
, Mustache,
, JavaScript.
Microsoft jQuery .tmpl
,
(Jhn Resig).
jQuery. jQuery.
tmpl(), .
, .
, ,
;
:
var object = {
u r l : "h ttp ://example. com",
getName: function(){ return "Trevor"; }
};
var template = '< l i x a href="${url}">${getName()}</a></li>, ;
var element = jQuery.tmpl(template, o b ject);
// :

< lix a href="http://exam ple.com ">T revor</ax/li>


$("body" ) . append(element);
, ${}. ,
, ,
jQuery. tmpl(), , .
,
.

94

5.

,
. , i f else,
, JavaScript. ,
,
:
{{if url
${url>
{{/if

if ,
false, 0, null,
Nan undefined. ,
{{/if}}, !
, , ,
, :
{{if messages.length}}
<!-- ... -->

{{else}}
<>, </>
{{/if}}


. JS,
JavaScript-THna Object Array,
{{each}}. Object {{each}},
.
.
,
, $value.
, ,
${$value}. :
var object = {
foo: "bar",
messages: ["Hi there", "Foo bar"]

};

, .
$index .
<ul>
{{each messages}}
<li>${$index + 1}: <em>${$value}</emx/li>.
{{/each}}
</ul>

, jQuery.tmpl API
. ,

95

API,
, lambda-, .


, .
MVC-
. , ,
</>. ,
:
<div>
${ this.data.replace(
/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"*-]*>))/g,
'<a target^Jjlank" href=,,$l,,>$l</a> *) >
</div>

,
, ^
.
helpers.js, -,
autoLink(). -
:
// helper.js
var helper = {};
helper.autoLink = function(data){
var re = /((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g;
return(data.replace(re, '<a target="_blank" href="$l">$l</a> ') );

};

// template.html
<div>
${ helper.autoLink(this.data) }
</div>

: autoLink()

.

JavaScript
s c rip t-

HTML

96

5.

MVC-.
s c rip t-,
.
JavaScript.
,
, MVC-.
Ajax-,
.
, ,
.
JavaScript-
,
.
HTML- .
,
.
, .
,
. ,
, .
s c rip t-,
JavaScript ID.
, .
s c rip t- ,
, .
,
jQuery.fn.tmpl(data) . . tmpl()
jQuery:
<script type="text/x-jquery-tmpl" id="someTemplate">
<span>${getName()}</span>
</script>
<script>
var data = {
getName: function(){ return "Bob" }

>;

var element = $("#someTemplate").tmpl(data);


element.appendTo($("body"));
</script>

, jQuery.tmpl
. ,
.
,
.

97

, ,
.
,
,
. ( ),
, , .
( RequireJS,
6) .


. ,
JavaScript ( ).
JavaScript ,
. ,
,
.
. ,
,
.

,
8.
JavaScript
,
. , JavaScript
.
, methodjnissing Ruby
Python, JavaScript
(getter) (setter).
JavaScript ,
change:
var addChange = function(ob){
ob.change = function(callback){
if (callback) {
if ( !this._change ) this._change = [];
this._change.push(callback);

} else {
if ( !this._change ) return;
for (var i=0; i < this._change.length; i++)
this._change[i].apply(this);

>
>;
>;

98

5.

addChange() change()
. change() , change jQuery.
, change() ,
, change() .
:
var object = {};
object.name = "Foo";
addChange(object);
object.change(function(){
console.log("M3MeHeHo!", this);
//

});

object.change();
object.name = "Bar";
object.change();

, change() ,
change.


.
,
change, .
User,
, change,
, :
<script>
var User = function(name){
this.name = name;

>;
User.records = []
User.bind = function(ev, callback) {
var calls = this._callbacks || (this._callbacks = {});
(this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);

>;

User.trigger = function(ev) {
var list, calls, i, 1;
if (!(calls = this._callbacks)) return this;
if (!(list = this._callbacks[ev])) return this;
jQuery.each(list, function(){ this() })

>;

User.create = function(name){
this.records.push(new this(name));
this.trigger("change")

};

jQuery(function($){

99

User.bind("change", function(){
var template = $("#userTmpl").tmpl(User.records);
$("#users").empty();
$("#users").append(template);

});
</script>
<script id="userTmpl" type="text/x-jquery-tmpl">
<li>${name}</li>
</script>
<ul id="users">
</ul>

, User,
change User, ,
.
, ,
,
. , , User:
User.create("Sam Seaborn");

change User,
,
. assets/
ch05/model.html.

, JavaScript ,
.
, JavaScript
. , jQuery,
- ,
.
JavaScript,
. ?
function() {
function() {
function() {
function() {

>
>
>
>
,

. ,
s c rip t-, JavaScript, ,
. JavaScript-
.
, s c r ip t- .
, :
<script src="jquery.js" type="text/javascript" charset="utf-8"x/script>
<script src="jquery.ui.js" type="text/javascript" charset="utf-8"x/script>
<script src="application.utils.js" type="text/javascript"
charset="utf-8"x/script>
<script src="application.js" type="text/javascript" charset="utf-8"x/script>
<script src="models/asset.js" type="text/javascript" charset="utf-8"x/script>
<script src="models/activity.js" type="text/javascript" charset="utf-8">
</script>

CommonJS

101

<script src="states/loading.js" type="text/javascript" charset="utf-8">


</script>
<script src="states/search.js" type="text/javascript" charset="utf-8">
</script>

<! -- . . . -->
,
, .
HTTP- JavaScript-,
, ,
. , cookie-,
TCP. ,
SSL.

CommonJS
JavaScript ,
. SpiderMonkey
Rhino load(),
. Node.js require(),
,
. ,
, , Rhino Node.js?
,
, JavaScript,
.
(Kevin Dangoor) CommonJS.
JavaScript
,
:
JavaScript ,
.
',
( !).
[] . ,

- .
, CommonJS.
.
JavaScript
, -, - (Socket streams)
.

102

6.


CommonJS .
;

exports:
// maths.js
exports.per = function(value, total) {
return( (value / total) * 100 );

};

// application.js
var Maths = require("./maths");
assertEqual( Maths.per(50, 100), 50 );

, ,
require(),
. ,
maths.js, Maths.
,
CommonJS- JavaScript, Narwhal
Node.js.


JavaScript- ?
,
, , CommonJS
. JavaScript,
, ,

, (eval-based compilation),
. CommonJS
module transport formaty .
CommonJS- ,
.
.
,
:
// maths.js
require.define("maths", function(require, exports){
exports.per = function(value, total) {
return( (value / total) * 100 );

};
});

103

// application.js
require.define("application", function(require, exports){
var per = require("./maths").per;
assertEqual( per(50, 100), 50 );
} b ["./maths"]); // (maths.js)


. .
,
,
, .
, , ,
CommonJS- .
,
, !


CommonJS-
. ,
.
, ,
.
CommonJS- ,
. ,
, , ,
.
: Transport Transport D. -
, ,
,
.
,

.

Yabble
Yabble .
XHR,
s c rip t-. XHR
, , . . .

eval(), . , CDN.

104

6.

XHR
, :
<script src="https://github.com/jbrantly/yabble/raw/master/lib/yabble.js">
</script>
<script>
require.setModuleRoot("javascripts");
// ,
// script-
require.useScriptTags();
require.ensure(["application"], function(require) {
//

</script>


, , , .
, require():
<script>
require.ensure(["application", "utils"], function(require) {
var utils = require("utils");
assertEqual( utils.per( 50, 200 ), 25 );

</script>

utils , require.
ensure() ,
, .
.

RequireJS
Yabble RequireJS,
. RequireJS
,
Asynchronous Module Definition, AMD. ,
, , API
. RequireJS CommonJS, .
JavaScript require(),
, ,
:
<script>
require(["lib/application", "lib/utils"], function(application, utils) {
// !

;
</script>

105

, utils

require().
, RequireJS
JavaScript-, jQuery Dojo.
,
.
, , :
require(["lib/jquery.js"], function($) {
// jQuery
$("#el").show();

});
, require(),
, /. ,
RequireJS
. :
data-main:
<script data-main="lib/application" src="lib/require.js"x/script>

data-main RequireJS s c rip t-


req u ire() ,
. lib/application.js, ,
, :
// lib/application.js
require(["jquery", "models/asset", "models/user"], function($, Asset,
User) {

//...
;
, , ?
, RequireJS
. require.defineQ,
define(). ,
.
, ,
. RequireJS- require(),
:
define(["underscore", "./utils"], function(_, Utils) {
return({
size: 10

;
exports .
. RequireJS-

106

6.

, ,
. ,
API CommonJS, . .
Node.js . : RequireJS
, CommonJS-,
def in e ():
define(function(require, exports) {
var mod = require("./relative/name");
exports.value = "exposed";

});
, ,
, , . . require exports.
, - .



, - :
HTTP-. , ,
, ,
,
, .
, ,
, , .
,
. ,
,
. ,
,
.
, .

, . . .
, rack-modulr Transporter,
-,
.
, , Rack- CommonJS, rackmodulr:
require "rack/modulr"
use Rack:rModulr, :source => "lib", :hosted_at => "/lib"
run Rack::Directory.new("public")

rackup.
lib CommonJS-

107

.
,
:
>> curl "http://localhost:9292/lib/application.js"
require.define("maths"___

Ruby,
. Fly Script CommonJS-,
, Transporter , JSGI, a Stitch Node.js-.


,
,
, . ,
, Sprockets.
require() JavaScript.
, //=,
Sprockets. , //= require Sprockets
, :
//= require <jquery>
//= require "./states"

jquery.js , a states.js
. Sprockets
, , ,
. CommonJS-,
Sprockets .

. , JavaScript
,
.
Sprockets ,
Rack Rails, rack-sprockets. . Sprockets , ,
, JavaScript
, , .

LABjs
LABjs
. -
CommonJS-. LABjs
,

108

6.

. LABjs

. , :
<script>
$LAB

. s c r ip t( ' / js /js o n 2 . j s ')


. s c r ip t( '/ j s / j q u e r y . j s ') .w ait()
.s c r i p t ( '/ j s / j q u e r y - u i . j s ')
.s c r i p t ( '/ j s / v a p o r .j s ');
</script>
, LABjs
jquery.js jquery-ui.js vapor.js. API
, ,
, .

FUBC

:
, (flash of unbehaviored content,
FUBC) . e. ,
JavaScript. ,
JavaScript .
,
CSS, ,
.

-
, -
,
, Adobe Flash. HTML5,
,
-.
,
,
, ,
.


API, HTML5, ,
,
.

Firefox>=3.6
Safari >= 6.0
Chrome >= 7.0
IE:
Opera >= 11.1

IE ,
,
,
. ,
:
if (window.File && window.FileReader && window.FileList) {
// API

>

110

7.


, HTML5,
, ,
. ,
-.
JavaScript ,
. , JavaScript

.
HTML5 File,
,
name
,
size

,
type
MIME- , ,
(""),

.
FileList, , ,
File.


, -,

. HTML5 , .

.
Adobe Flash.
HTML5 multiple. m ultiple
, ,
. ,
HTML5, :
<input type="file" multiple>
; ,
Shift. ,

. Facebook , 85% ,

111

, . , ,
(. . 7.1),
85 40%.
, .
,
. HTML5
, f ile s .
files FileList,
, ,
:
var input = $("in p u t[ty p e= file]");
in p u t. change(function(){
var f il e s = t h i s .f i l e s ;
for (var i=0; i < file s .le n g th ; i++)
a ssert( files[i].ty p e.m atch (/im ag e.* /) )
});
Upload photos
fe cmyk_wallpaper.png

U p lo a d in g tip
You can select multiple photos in the dialogue by holding
the 'command' key down while clicking on the photos
Trouble uploading photos Try the Simple Upfoader

______________________________

- 1

(_ Cancel )

il
<

(3 * ^ 3

Select photos

Cancel

. 7.1. Facebook


. , ,
.
,
, Ajax
. - .


Microsoft 1999 Internet Explorer 5.0, IE
. HTML5 ,
, Safari, Firefox Chrome ,
Microsoft. , ,

112

7.


.
, : dragstart, drag, dragover, dragenter, dragleave, drop dragend.
.
API, HTML5,
, API .
:

Firefox>=3.5
Safari >=3.2
Chrome >=7.0
IE >= 6.0
Opera:


.
, draggable true.
<div id="dragme" draggable="true">nepeTauiH MeHfl!</div>

- .
, dragstart
setData():
var element = $("#dragme");
element.bind("dragstart", function(event){
// jQuery
event = event.originalEvent;
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", $(this).text());
event.dataTransfer.setData("text/html", $(this).html());
event.dataTransfer.setDragImage("/images/drag.png", -10, -10);

});
jQuery ,
dataTransfer.
originalEvent, API .
, dataTransfer,
. setData()
mimetype . drag
text text/html.
drop, . ,
,
,
.

113

text/plain.

, .
: text/plain text/uri-list.

:
//
event.dataTransf.setData("text/uri-list", "http://example.com");
event.dataT ransfer.setData("text/plain", "http://example.com");
//
event.dataTransfer.setData("text/uri-list",
"http://example.com\nhttp://google.com");
event.dataTransfer.setData("text/plain",
"http://example.com\nhttp://google.com");

setDraglmageQ ,
.
/ .
,
. setDraglmageQ
addElement (element, , ),
. ,
, .

, DownloadURL. URL
, . Gmail
,
.
,
Chrome .
, ,
. DownloadURL
: mime-, ,
(:).
$("#preview").bind("dragstart", function(e){
e .originalEvent.dataT ransfer.setData("DownloadURL", [
"application/octet-stream",
// MIME-
"File.exe",
//
"http://example.com/file.png" //
].join(":"));

});
API , HTML5,
assets/ch07/drag.html.

114

7.


API (drop)
.
API .
drop *
: dragover dragenter. , , :
var element = $("#dropzone");
element.bind("dragenter", function(e){
//
e .stopPropagation();
e .preventDefault();

});

element.bind("dragover", function(e){
//
e.originalEvent.dataTransfer.dropEffect = "copy";
//
e .stopPropagation();
e .preventDefault();

});
dropEffect, . e.
dragover, . dragenter dragleave
,

.
dragenter dragover,
drop. drop ,
. dataTransfer
drop files, FileList,
:
element.bind("drop", function(event){
//
event.stopPropagation();
event.preventDefault();
event = event.originalEvent;
//
var files = event.dataTransfer.files;
for (var i=0; i < files.length; i++)
alert("Dropped " + files[i].name);

});
, ,
dataTransfer.getData(), .
, undefined (),
var text = event.dataTransfer.getData("Text");

115

dataTransfer types,
, DOMStringList ( ) mime, dragstart. ,
- , "Files".
var dt = event.dataTransfer
for (var i=0; i < dt.types.length; i++)
console.log( dt.types[i], dt.getData(dt.types[i]) );

, drop, assets/ch07/drop.html.


-
. ,
, ,
,
. ,
(body) dragover.
$("body").bind("dragover", function(e){
e .stopPropagation();
e .preventDefault();
return false;

});



. API
HTML5,
, .
IE, , ,
, IE 5.0. WebKit API Microsoft
, API .
, :
dataTransfer clipboardData.
Firefox , API
, , . WebKit
(Safari Chrome) , , W3C
, API
. :

Safari >= 6.0


Chrome ( )
Firefox:
IE >= 5.0 ( API)

116

7.

, , , :

beforecopy

beforecut
cut

, beforecopy beforecut
, .
- , ,
clipboardData,
. dataTransfer,
clipboardData setData(), mime . ,
, .
IE clipboardData .
,
, .
Firefox , Data,
. Chrome ,
.
$("textarea").bind("copy", function(event){
event.stopPropagation();
event.preventDefault();
var cd = event.originalEvent.clipboardData;
// IE
if ( !cd ) cd = window.clipboardData;
// Firefox
if ( led ) return;
cd.setData("text/plain", $(this).text());

});
, ,
.
- ,
, .


: beforepaste paste. paste
, ,

117

- .
. Chrome ,
. A IE Safari ,
.
API API drop. clipboardData,
getData(),
mime-. , ,
types null, , mime-
. ,
,
, :
$("textarea").bind("paste", function(event){
event.stopPropagation();
event.preventDefault();
event = event.originalEvent;
var cd = event.clipboardData;
// IE
if ( !cd ) cd = window.clipboardData;
// Firefox
if ( !cd ) return;
$("#result").text(cd.getData("text/plain"));
// Safari
var files = cd.files;

});
WebKit files clip
boardData, . ,
, .
, - ? , , .
, Cappuccino ,
. Command/
Ctrl + v ,
. ,
, , .


File,
FileReader .
FileReader
, , .
FileReader .
, , .

118

7.

readAsBinaryString(Blob|File)

.
0 255.
readAsDataURL(Blob|File)

, URL .
,
src, .
readAsText(Blob|File, encodings'UTF-8')

.
UTF-8.
readAsArrayBuffer(Blob|File)

ArrayBuffer.
.
FileReader ,
. ,
, :
t
onerror


onprogress


onload


FileReader ,
. onload
result,
:
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;

};
reader.readAsDataURL(file);

, data, ,
, ,
:
var preview = $("img#preview")
// , image
//
// -

119

if (file.type.match(/image.*/) &&
file.size < 50000000) {
var reader = new FileReader();
reader.onload = function(e) {
var data
= e.target.result;
preview.attr("src", data);

};
reader.readAsDataURL(file);


,
. API, HTML5,
slice(). ,
( ).
Blob, ,
File, FileReader. ,
, :
var bufferSize = 1024;
var pos = 0;
var onload = function(e){
console.log("npo4HTaHo: ", e.target.result);

};
var onerror = function(e){
console.log("0mn6Ka!", e);

};
while (pos < file.size) {
var blob = file.slice(pos, bufferSize);
var reader = new FileReader();
reader.onload = onload;
reader.onerror = onerror;
reader.readAsText(blob);
pos += bufferSize;

}
, FileReader
, .
assets/ch07/slices.html. ,
, .
, slices.html , ,
onerror.

120

7.



. ,
(Browse) (Attachment)
,
. ,
. ,
, Firefox,
.
,
.
, ,
, .
, .
assets/ch07 jquery.browse.js,
j Query,
.
jQuery browseElement ().
, change,
- .
var input = $("#attach"),browseElement();
input.change(function(){
var files = $(this).attr("files");

});
!


XMLHttpRequest
Level 2. .
,
, ,
, - , . .
. , XHR 2 .

,
, .
:
Safari >= 5.0
Firefox >= 4.0
Chrome >= 7.0

121

IE:
Opera:

XMLHttpRequest API send() , ,
FormData. FormData
.
FormData form
:
var formData = new FormData($("form")[0]);
//
formData.append("stringKey", "stringData");
// File
formData.append("fileKey", file);

FormData
POST , XMLHttpRequest. Ajax-
jQuery, processData
false, jQuery
. Content- ,
multipart/form-data,
:
jQuery.ajax({
data: formData,
processData: false,
url: "http://example.com",
type: "POST"

})
FormData
send () XHR-:
var req = new XMLHttpRequestQ;
req.open("POST", "http://example.com", true);
req.send(file);

, Ajax API, jQuery,


:
$.ajax({
url: "http://example.com",
type: "POST",
success: function(){ /* ... */ },
processData: false,
data: file

});

122

7.

,
multipart/formdata. ,
, .
.
, X-File-Name.
:
$.ajax({
url: "http://example.com",
type: "POST",
success: function(){ /* ... */ },
processData: false,
contentType: "multipart/form-data",
beforeSend: function(xhr, settings){
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-File-Name", file.fileName);
xhr.setRequestHeader("X-File-Size", file.fileSize);

b
data: file

});
,
,
, multipart URL .

.
FormData ,
multipart/formdata. assets/ch07 jquery.upload.js,
jQuery,
$.upload(url, file).

Ajax
XHR Level 2
progress, , .
,
, ,
.

XHR-:
var req = new XMLHttpRequestQ;
req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.open();

123

progress ,
upload XHR-:
var req = new XMLHttpRequestQ;
req.upload.addEventListener("progress", updateProgress, false);
req.upload.addEventListener("load", transferComplete, false);
req.open();

load , ,
. jQuery, XHR beforeSend.
, , :
$.ajax({
url: "http://example.com",
type: "POST",
success: function(){ /* ... */ L
processData: false,
dataType: "multipart/form-data",
beforeSend: function(xhr, settings){
var upload = xhr.upload;
if (settings.progress)
upload.addEventListener("progress", settings.progress, false);
if (settings.load)
upload.addEventListener("load", settings.load, false);
var fd = new FormData;
for (var key in settings.data)
fd.append(key, settings.data[key]);
settings.data = fd;

data: file

});
progress (. .
) ( ).
:
var progress = function(event){
var percentage = Math.round((event.position / event.total) * 100);
//

}
,
,
estimated time of completion (ETA):
var startStamp
var progress =
var lapsed =
var eta
=

};

= new DateQ;
function(e){
startStamp - e.timestamp;
lapsed * e.total / e.position - lapsed;

124

7.

( ) .
, ,
. ,
,
.


jQuery
,
.
: jquery.js , jquery.ui.js , jquery.drop.js
API jquery. upload .js
Ajax. jQuery.ready(),
DOM-:
//=
//=
//=
//=

require
require
require
require

<jquery>
<jquery.ui>
<jquery.drop>
<jquery.upload>

jQuery.ready(function($){

});

, #drop,
.
drop, ,
uploadFile():
var view = $("#drop");
view.dropAreaQ;
view.bind("drop", function(e){
e .stopPropagation();
e .preventDefault();
var files = e.originalEvent.dataTransfer.files;
for ( var i = 0; i < files.length; i++)
uploadFile(files[i]);
return false;

});

jQuery

125


uploadFileQ, .
Ajax-
$ .upload() jquery.upload.js. progress
jQuery UI.
, :
var uploadFile = function(file){
var element = $("<div />");
element.text(file.fileName);
var bar = $("<div />");
element.append(bar);
$("#progress").append(element);
var onProgress = function(e){
var per = Math.round((e.position / e.total) * 100);
bar.progressbar({value: per});

};
var onSuccess = function(){
element.text(" ");
element.delay(1000).fade();

};
$.upload("/uploads", file, {upload: {progress: onProgress},
success: onSuccess});

};
! assets/ch07/
dragdropupload.html.

- ?
, , -
. ,
. ,
,
. , Google, Facebook Twitter,

. ,
.


- - : -, ,
, .
Ajax, -
.
,
,
.
. :
.
.
,
,
TCP-,
HTTP-. , .

Comet. (iframes),
, xhr-multipart, htmlfile
(long polling).

WebSockets

127

XMLHttpRequest- (XHR) ,
, . , ,
, , .
, .
Comet ,
. ,
. ,
. Comet ,
.

, Adobe Flash Java.
TCP- ,
.
,
,
, .
HTML5.
, , Internet
Explorer, . Comet

.

WebSockets
WebSockets HTML5,
TCP-. ,
,

, .
,
.

, . ,
WebSockets,
, Comet (polling).
WebSockets
,
,
HTTP- , , .
Comet HTTP,
HTTP-.

128

8. -

, TCP-,
.
WebSockets,
, . ,
. ,
,
( ). ,
,
, -.
WebSockets HTML5
Google (Ian Hickson):
2 ...
150 50 .
, WebSockets
Google .
, WebSocket :

Chrome > = 4
Safari >= 5
iOS >= 4.2
Firefox >= 4*
Opera >= 11*

Firefox Opera WebSocket,


- , .
.
Comet Adobe
Flash. IE ,
IE9.
WebSockets :
var supported = ("WebSocket" in window);
if (supported) alert(" WebSockets ");

API WebSocket .
, WebSocket,
, ws://example.com:
var socket = new WebSocket("ws://example.com");

:
//
socket.onopen = function(){ /* ... */ }

WebSockets

129

//
socket.onmessage = function(data){ /* ... */ }
//
socket.onclose = function(){ / * . . . * / }

, onmessage. ,
, send()
. , ,
:
socket.onmessage = function(msg){
console.log("New data - ", msg);

};
socket.onopen = function(){
socket.send(", - ").

};
.
JSON,
, ,
:
var = {
test: function(argl, arg2) { /* ... */ }

};
socket.onmessage = function(data){
// DSON
var msg = DSON.parse(data);
// RPC
rpc[msg.method].apply(rpc, msg.args);

};

(RPC). JSON,
, :
{"method": "test", "args": [1, 2]}

, .
,
JavaScript.
close():
var socket = new WebSocket("ws://localhost:8000/server");

130

8. -

, , , WebSocket
WebSocket, ws://, http://. WebSockets

TLS, wss://. WebSockets
80 443
. , URL ,
. , ,
.
: ,
, , IE
. , , , , ,
. Web-socket-js, WebSocket,
Adobe Flash.
WebSocket- Flash,
, .
API WebSocket, , WebSockets ,
, .
API,
. WebSocket
: (drafts) 75 76. ,
, .
WebSockets . WebSocket,
WebSocket- .

( ). WebSocket , . . , ,
, .
, WebSockets
-,
HTTP-.
. -
WebSockets, . -
,
.
( 76)
- . WebSockets
, .
WebSocket- (wss). -
,
.
WebSocket-cepeepaMH , HTTP-.

WebSockets

131

HTTP-
WebSocket.
, WebSocket,
.
, ,
, , Comet
.
, ? ,
, Ruby, Python Java. ,
, , 76
, .
Node.js:
node-Websocket-server;
Socket.IO.
Ruby:
EventMachine;
Cramp;
Sunshowers.
Python:
Twisted;
Apache module.
PHP:
php-Websocket.
Java:
Jetty.
Google Go:
Native.

Node.js Socket.IO
Node.js , ,
. Node.js JavaScript, Google- V8 JS. ,
,
, WebSocket-cepeepa.
Socket.IO Node.js WebSockets. , ,
. :
Socket.IO
,
.

132

8. -

WebSockets , Socket.IO
,
.
, :

WebSocket;
Adobe Flash Socket;
ActiveX HTMLFile (IE);
XHR multipart-;
XHR ;
JSONP- ( ).

Socket.IO .
,
Socket.IO ,
.
:

Safari >= 4
Chrome >= 5
IE > - 6
iOS
Firefox >= 3
Opera > - 10.61

Socket.IO Node.js,
, Ruby (Rack), Python (Tornado),
Java Google Go.
API .
API API, WebSocket:
var socket = new io.Socket();
socket.on("connect", function(){
socket.send('!');

});
socket.on("message", function(data){
alert(data);

});
socket.on("disconnect", function(){});

Socket.IO .
readme , Socket.IO ,
, .

133

, Socket.,
Juggernaut, .
Juggernaut : ,
, . .
PubSub (-).
, ,
TLS .
,
Pusher.
, :
-. ,
JavaScript- .
, HTTP-
REST API.



, JavaScript-?
, .
, ,
PubSub. , ,
.
.
:
, ,
, .
,
:
?

?
,
.
,
.
, .
.
.
1. .
2. Ajax-, Chat.

134

8. -

3. Chat
,
.
4. , ,
. . , .
5. , ( chat),
.
.
Rails, Holla.
Message, 3uggernaut0bserver
.
:
?
PubSub: ,
.
, ,
ID ,
,
.
, :
/observer/0765F0ED-96E6-476D-B82D-8EBDA33F4EC4


, .
.
, PubSub
, WebSockets Comet. ,
, Juggernaut Pusher. PubSub
WebSockets, API
, .
,
MVC-. .
:
{
"klass":
"type":
"id":
"record":

"Chat",
"create",
"3,
{"body": "New chat"}

}
, ,
. ,
Chat. UI,

135


.
,
Chat. ,
,
.
. ,
,
, .

. , ,
.


,
(user
experience, UX) ,
. :
Amazon
100 1%
(: (Greg Linden), Amazon).
Google
500 20%
(: (Marrissa Mayer), Google).
Yahoo!
400 5-9%
,
, (: (Nicole
Sullivan), Yahool).
, ,
.
, , ,
.
JavaScript ,
.
.
, Ajax- .
,
, .

136

8. -

.
, , , .
,
? ,
.
( ),
.
.
,
.
, ,
, .

, .
. ,
.


. ,
(
). (UX)
, .
, -
. ,
.
, .


.
.
JavaScript, . . ,
.
,
.
,
. , ,

JavaScript, .
JavaScript -
, JavaScript-
. , ,
JavaScript , .
, , jQuery.
10 ,
.
. , jQuery:

Safari: 3.2,4, 5
Chrome: 8,9,10,11
Internet Explorer: 6, 7, 8,9
Firefox: 2, 3, 3.5,3.6
Opera: 9.6,10,11

, ,
. ,
, ,
, !
.
, jQuery , ,
. ,

138

9.

, j Query,
. ,
,
.
,
, ,
-. ,
, , .
.
, .

- . , , ,
Statcounter.com 2011 :

Safari: 4%
Chrome: 15%
IE: 36%
Firefox: 37%
Opera: 2%

IE
Firefox Chrome. , IE6,
, .

,
.
, : ,
. . ,
, IE 5% ,
. ,
.
, ,
Firefox Chrome,
,
. ,
, ,
. , ,
:

IE 8,9
Firefox 3.6
Safari 5
Chrome 11

139

,
, ,
,
.

.


,
.
,
.
,
,
.
,
.
,
, .
,
, , , ,
- -
.

JavaScript, .
,
.


, ,
. ,
.
, , -
.
, assert(),
:
var asset = function(value, msg) {
if ( lvalue )
throw(msg || (value + " "));

};

140

9.

.
(true), :
//
assert( false );
assert( "" );
assert( 0 );

JavaScript
undefined, 0 null false. , a ssert
n u ll-:
// null,
assert( User.first() );


,
JavaScript,
.
.
,
, .
, assert Equal (),
:
var assertEqual = function(vall, val2, msg) {
if (vail !== val2)
throw(msg || (vail + " " + val2));

};
//
assertEqual("one", "one");

, ,
API
.

Q Unit
QUnit
, jQuery. ,
QUnit?
,
:
<!DOCTYPE html>
<html>
<head>
<title>QUnit Test Suite</title>

141

<link rel="stylesheet" href="qunit/qunit.css" type="text/css"


media=,,screen">
<script type="text/javascript" src="qunit/qunit.js"x/script>
<!-- include tests here... -->
</head>
<body>
<hl id="qunit-header">QUnit Test Suite</hl>
<h2 id="qunit-banner,,x/h2>
<div id=,,qunit-testrunner-toolbar,,x/div>
<h2 id="qunit-userAgent,,x/h2>
<ol id="qunit-tests"x/ol>
<div id="qunit-fixture">test markup</div>
</body>
</html>

. ,
, -
(ORM), 3:
test("load()", function(){
var Asset = Model.setup();
var a = Asset.init();
a.load({
local: true,
name: "test.pdf"

});
ok(a.local, "Load sets properties");
equals(a.name, "test.pdf", "load() sets properties (2)");
var b = Asset.init({
name: "test2.pdf"

});

equals(b.name, "test2.pdf", "Calls load() on instantiation");

});
testQ
(
). :
() , true,
equals() .
, ,
, , .
, ,
. 9.1.
! ,
, , .

142

9.

,
,
.

QUnit Test Suite


Hide passed tests
Mozitta/5.0 (Macintosh; U; Intel M ac O S X 10 6 6; en*US) AppteWebKit/534.13
(KHTM L, like Gecko) Chrome/9.0.597.107 Safari/534.13

Tests completed m 22 mSseconds.


3 tests of 3 passed. 0 faied
1 MoctoUwfctoadO ( 0 ,3 ,3

. 9.1. QUnit

module(),
. ,
setup module(),
, , .
Asset,
setup:
module("Model test", {
setup: function(){
this.Asset = Model.setup();

}
test ("loadQ", function(){
var a = this.Asset.init();
a.load({
local: true,
name: "test.pdf"

});
ok(a.local, "Load sets properties");
equals(a.name, "test.pdf", "loadQ sets properties (2)");
var b = this.Asset.init({
name: "test2.pdf"

});
equals(b.name, "test2.pdf", "Calls loadQ on instantiation");

});
,
. module()
teardown (), ,
.
:
test("attributes()", function(){

143

th is .A s s e t.a ttrib u te s = ["name"];


var a = th is .A s s e t.in it( ) ;
a.name = " te st.p d f";
a .id
= 1;
e q u a ls(a .a ttrib u te s (), {
name: " te st.p d f
id: 1
});
, , , ,
. 9.2. equals ()
==,
. same(),
, :
test("attributes()", function(){
this.Asset.attributes = ["name"];
var a = this.Asset.init();
a.name = "test.pdf";
a.id
= 1;

sam e(a .attrib u tes(), {


name: " te st.p d f",
id: 1
});
QUnit : notEqual() raisesQ .
assets/ch09/qunit/model.test.js
QUnit.

QUnit Test Suite


Q Hkde passed tests
MoziHa/5.0 (Macintosh; U; Intel M ac O S X 10 6; en-US) AppleWebKrt/534.13
{KHTML, like Gecko) Chrome/9.0.597.107 Safarl/534.13

Testis completed n 19 mftseconda,


3 tes+s of 4 passed 1 faded.
1. Model

toadQ (0. 3, $

. 9.2. QUnit

144

9.

Jasm ine
(
) Jasmine. Jasmine
, .
, .
Jasmine , ,
DOM. , JavaScript,
Node.js.
QUnit, HTML, ,
:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN
"http://www.w3 .org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Test Runner</title>
<link rel=,,stylesheet" type="text/css" href="lib/jasmine.css,,>
<script type="text/javascript" src="lib/jasmine.js"x/script>
<script type="text/javascript" src="lib/jasmine-html. js"x/script>
<!-- ... -->
<1-- ... -->
</head>
<body>
<script type="text/javascript">
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
jasmine.getEnv().execute();
</script>
</body>
</html>

, Jasmine. -
ORM 3:
describe("Model", function(){
var Asset;
beforeEach(function(){
Asset = Model.setup();
Asset.attributes = ["name"];

});
it("can create records", function(){
var asset = Asset.create({name: "test.pdf"});
expect(Asset.first()).toEqual(asset);

});

it("can update records", function(){


var asset = Asset.create({name: "test.pdf"});
expect(Asset.first().name).toEqual("test.pdf");
asset.name = "wem.pdf";

145

asset.save();
expect(Asset.first().name).toEqual("wem.pdf");

});
it("can destroy records", function(){
var asset = Asset.create({name: "test.pdf"});
expect(Asset.first()).toEqual(asset);
asset.destroy();
expect (Asset.first ().).toBeFalsy ();

});
});
describe(),
.
, ,
beforeEach(). Jasmine afterEach(),
. beforetach() Asset,
, .
it(),
, .
expect(),
, , :
expect(x).toEqual(y)


.
expect(x).toBe(y)

, ,
.
expect(x).toMatch(pattern)


, .
expect(x) .toBeNullQ

, null.
expect().toBeTruthy()

, true.
expect(x).toBeFalsy()

, false.
expect(x).toContain(y)

, .
expect(fn).toThrow(e)

, f
.

146

9.

Jasmine
. . 9.3
Jasmine Test Runner,
.
ia s m in e

1.0.2 revision 12988378S8

S .s e e c s . 0 fa ilu r e s in .Q .Q 2 1 s

Show

passed

skipped

Rftfjhtd 1! fri Mar 11 2011 02:51 M G M T * 1300(N20T)

1 M

run

M a tte l
can cre e

re co rd s

run

c a a -M B d A te r e c o r d s

run

c a n .d e s ir o v .r e c Q r d s

run

. 9.3. , Jasmine

-
,
.
,
.
.
,
. JavaScript
.

,
,
JavaScript-,
.
W atir ( ) Ruby
, Chrome, Firefox, Safari Internet Explorer (
). W atir
Ruby- , ,
.
, :
# FireWatir Firefox
require "firewatir"
browser = Watir::Browser.new
browser.goto("http://bit.ly/watir-example")
browser.text_field(:name => "entry.0.single").set "Watir"
browser.button(:name => "logon").click

147

-
,
Internet Explorer,
Windows. Safari
, Mac OS X.

Selenium. (domain
scripting language, DSL)
, #, Java, Groovy, Perl, PHP, Python Ruby. Selenium
.
,
, . Selenium
, Selenium IDE,
Firefox, ,
.
. 9.4 Selenium IDE
, .
, .
,
.
Selenium IDE 1.0.10 *
Ease

URL

____

file.///Users/Alex/book/as sets/ch09/selenium/index.html

>1

H I

Test Case

Table

Source

Untitled 2

fCommand

Target

open
cHckAndWait

Runs:

Value

ffle7//Users/A!ex/book/as.
nk Login.htm

type

email

test@example.com

type

password

test

dickAndWait

//mput(@value'Contnue .

Command

open

Target

file ///Users/Alex/book assets/ch09/se emum/index html

Value

Failures

opcmurl)
Arguments:
url the U R L to open; may be relative or absolute
Opens an U R L in the test frame. This accepts both relative and absolute URLs. The open" command waits
for the page to load before proceeding, ie the AndW t" suffix is implicit. Note: The U R L must be on the

. 9.4. Selenium

148

9.

, . 9.5,
(Test Case) .
F irefox

M31BI

Edit

Options

Window

Help

New Test Case


Open...
Save Test Case
!

tfxo.n /*tiien&source*hp&biw 1658&1

Recent Test Cases

Add Test Case...

3SD

HTML
Ruby (Test::Unit)
RSpec
JUnit 3 (Remote Control)

New Test Suite

JUnit 4 (Remote Control)

Open Test Suite...

TestNG (Remote Control)

Save Test Suite


Save Test Suite As...

Groovy (JUnit)
Perl

Export Test Suite As...

Recent Test Suites

FHP
Python 2 (Remote Control)
C# (Remote Control)

Close

-----

W J
ckkAndWait

link Login.html

. 9.5. Selenium

,
Test: :Unit Ruby. , Selenium IDE

,
, :
class SeleniumTest < Test::Unit::TestCase
def setup
^selenium = Selenium:-.Client::Driver.new \
:host => "localhost",
:port => 4444,
:browser => "chrome",
:url => "http://example.com/index.html",
:timeout_in_second => 60
@selenium.start_new_browser_session
end
def test_selenium
gselenium.open "http://example.com/index.html"
gselenium.click "link=Login.html"
@selenium.wait_for_page_to_load "30000"
^selenium.type "email", "test@example.com"
@selenium.type "password", "test"
gselenium.click "//input[@value='Continue
@selenium.wait_for_page_to_load "30000"
end

149

@selenium,
:
def test_selenium
# ...
assert @selenium.is_text_present("elvis")
end

Selenium - selemiumhq.
org, screencast.


JavaScript-, Node.js Rhino,

.
,
.
.
, , .
, , JavaScript
. ,
, j Query, ,
DOM .

(
).
Envjs Qohn Resig),
JavaScript- j Query.
DOM API Rhino, JavaScript Java,
Mozilla. env.js Rhino
.

Zom bie
Zombie.js ,
Node.js.
:
,
.
, JavaScript ,
,
JavaScript. Google JavaScript- V8
.

150

9.

JavaScript
, Zombie V8 ,
,
. , Chrome
.


Browser. DOM,
,
. ,
, Vows.js, ,
,
.
Zombie.js Browser,
-: (cookie-,
, -) (
).
, ( ,
, . .) (
XPath CSS ).
, ,
:
// email, password ,
browser.
fill("email, "zombiegunderworld.dead").
fill("password", "eat-the-living").
pressButton("Sign Me Up!", function(err, browser) {
// , .
assert.equal<browser.text("title"), "Welcome to Brains Depot");

;
. ,
Zombie.js, Browser
. err.
-, Zombie.js :
,
.
, loaded error, .
, Zombie ,
. ,
null ,
Browser. ,
Error.

151

, -
.
Node.js ,
, Vows.js.
,
, error null.
null, .
, , , Zombie.js Vows.js.
- brains (,
):
var zombie = require("zombie");
vows.describe("Zombie lunch").addBatch({
"visiting home page": {
topic: functionQ {
var browser = new zombie.Browser;
browser.cookies("localhost").update("session_id=5678");
browser.visit("http://localhost:3003/", this.callback);

"should find no brains": function(browser) {


assert.isEmpty(browser.css(".brains"));

}
}
});
Zombie.js . ,
(cookie-, , - . .)
(
).
DOM ,
.
, Resources
Web Inspector WebKit. Zombie Node.js,
HTTP- -, , ,
Ruby- Python-.

Ichabod
Ichabod
,
.
Ichabod , DOM
WebKit ,
Safari Chrome. Ichabod OS X,
MacRuby OS X WebView API.

152

9.

. MacRuby,
, rvm. gem- Ichabod:
$ gem install ichabod

Ichabod Jasmine QUnit,


.
ichabod:
$ ichabod --jasmine http:////jasmine/specs.html
$ ichabod --qunit http://nymb/K/qunit/tests.html

-,
:
$ ichabod --jasmine ./tests/index.html
Finished in 0.393 seconds
1 test, 5 assertions, 0 failures

Ichabod WebKit,
, .



.
TestSwarm:
TestSwarm
JavaScript
.
,
JavaScript.

TestSwarm .
TestSwarm
.

TestSwarm.

.
(swarm) . ,
, . 9.6,
, , ,
, .

&

S
Sr

&

153

. 9.6. TestSwarm

, Sauce Labs,
.
Selenium,
,
, .


,
- . , ,

.
,
.

JavaScript-
a le r t () .
, ,
-.
,

.

154

9.

- Safari Google Chrome.


,
.
Safari , , . 9.7, Show
Develop menu in menu bar Advanced preferences.
^

_; .

General Appearance

Advanced

Bookmarks

Tabs

RSS

AutoFill

Security Extensions Advanced

Universal Access: 0 Never use font sizes smaller than 9 3 )


G

Tab to highlight each item on a webpage


Option Tab highlights each item.

Style sheet: ( None Selected


Proxies: ( Change Settings.
0 Show Develop menu in menu bar

. 9.7. Safari

Chrome ,
.

.
-, . 9.8, ,
HTML-, ,
JavaScript . ,
JavaScript-.

Elements

Resources

Network

* Scripts

Tmnetine

Profiles

&

Audits

^Consote

! Computed Style
[ t Styles

<ht*l>
<head>_</head>
*<body>
<div id="container*>
header
<hl~/hl>
/header

Show

nherited

elem ent.style {

<div id post*'.</div>
<div id*4coeents*,..</div>
</div>
</body>
</htl>

li .
Matched CSS Rute>
screen.css:59
t ontent {
: isargin: 0 2 209px;
-width: 90;
i>
[div {
user agent stylesheet'
d isplay: block;
j
t

A
r

inhe rit from body

>3

Q,

html

body

divcontamer 2 2 1

. 9.8. - Safari DOM

91

155

,
-.
Elements
HTML-,
Resources

Network
HTTP-
Scripts
JavaScript-
Timeline

Audits

Console
JavaScript

Firebug
Firefox JavaScript-HHcneKTopa,
: Firebug (. . 9.9.)
*> J* }*
ii Edtl hl.ttle < dv*post
<!>

HTML

CSS

< divcontni

Script

DOM

< divfconumer

.......... .....

1
body

Styles

Computed

U your

DOM

^content {
screen css (line 59)
margin:
200px 200px,
mox width: 900px;

I* <htral>
<heod>
<body>

}
Inherited from body
?

^ <div id ^ c o o t o in c r V
<hecder>

body {
screen css (line 1)
font-fo m ily: Georg a ,s e r if ;
fonfc-sixe: 18px;

<div \d"post">

>

^ <dtv id*-"cowncnts*>
</div>

</div>
</body>
</ht*l>

___ ___

. 9.9. DOM CSS Firebug

Firebug
-, ,
.
Console
JavaScript

156

9.

HTML
,
CSS
CSS
Script
JavaScript-
DOM

Net
HTTP-
Firebug Firefox- , Firebug Lite.
, Firebug,
,
. Firebug Lite Internet Explorer (,
, IE). Firebug Lite
, - s c rip t-:
<script type="text/javascript" src="https://getfirebug.com/firebug-lite.js">
</script>

,
- Firebug Lite.

JavaScript
, .
,
, console.log().
,
, :
console.log("test");
console.log(l, 2, {3: "three"});

.
, ,
console.warn() console.error(-):
console.(" ");
console.error("- !");
try {
// -
} catch(e) {
console.error(" !", );

157

, :
var = {trace: true};
App.log = function(){
if (!this.trace) return;
if (typeof console == "undefined") return;
var slice = Array.prototype.slice;
var args = slice.call(arguments, 0);
args.unshift("(App)");
console.log.apply(console, args);

};
App.log() "App" ,
console.log().
,
console . ,
, Internet Explorer Firefox Firebug, console
,
.
- App. lo g ().
console.traceQ ,
.
, ,
, ,
:
//
console. traceQ;

, , JIT -
,
.


. , $0
$4 -
Firebug. , ,
:
// $0
$0.style.color = "green";
// , jQuery
jQuery($0).css({background: "black"});

$() ID. ,
document.getElementBy!d(). ,

158

9.

jQuery, Prototype , $,
:
$("user").addEventListener("click", function(){ /* ... */});

$$() ,
CSS-. document.querySelectorAllQ.
, Prototype MooTools,
:
// class, .users
var users = $$(".users");
users.forEach(function(){ /* ... */ });

$x() ,
XPath:
//
var checkboxes = $x("/html/body//form");

clearQ :
clearQ;

dir() :
dir({one: 1});

inspect () ,
,
:
inspect($("user"));

keys () :
// ["two"]
keys({two: 2>);

values() ,
, . . keys():
// [2]
values({two: 2});


JavaScript JavaScript-.
,
, ,
, .

159

,
debugger - ,
:
var test = function(){

// ...
debugger
};
Scripts,
,
. . 9.10.
, , ,
, debugger.
JavaScript ,
, .
. 9.11.

<
73

Resources

Network 1

Jqu eryjs

i n i t : f u n c tio n ( s e le c t o r ,
v a r m a tc h , e le w , r e t ,

//

14

Profiles

Audits

'_j| Console

Not Paused

B r e a k p o in ts
C j q u f iy j s 5
C o p y r ig h t

86
87
80
09
90

) {

Scripts

Mj (1
f f f*
fat
W a tc h E x p r e s s io n s
^ C a ll S ta ck
v S c o p e V a r ia b le s

o r $ ( u n d e fm e d )

H a n d le $ (0 0 H E le e n t
( s e le c t o r . n o d e T y p e
th is .c o n te x t
t h i s 101
t h is .le n g t h
1;
re tu rn t h i s ;

if

-?

*(""). $ ( n u l l ) ,
) {

>
//

Tim eline

) {

c o n te x t
doc;

H a n d le
s e le c t o r
re tu rn t h i s :

if

80
81
?
83
84

Script*
J

J* "1 jVW**' / .pi

75
76
77
78

Elements

20 10 ,

jqueryJs 85
th is .c o n te x t

s e le c t o r ;

Joh n R e s ig

t h i s [01

s e l.

D O M B r e a k p o in ts

>
/ / T he body e le m e n t o n ly e x i s t s
Q1
if 1 u lartn r
.
"hnHis" XX
'
-* * w
*
> >2

X H R B r e a k p o in ts

E v e n t lis t e n e r
v Workers
+
4

once,

o p t im i z e

t /

f in d in g

it

B r e a k p o in ts

_ Debuq

. 9.10. - Safari

<
sm

*, Elements

Resources

chO O htm l
*/coCe></pre>

( j ) Network

Sc'pxs

Profiles

y f Timebne

^ 4 Audits

Q Search Scripts

^ ^ C o n s o le

|
M

379
80 s c r ip t ty p e nt e x t / i a v a s c r i p t M c h a r s e t Mu t f - 8 >
381
war v a lu e * 'one*'
fu n c tio n

Mo n e H
____

>

Pained

S c o p e V a r ia b le s

Local

t e s t lH

l
bugger
38
>
307 < / s c r ip t >

C a ll S ta ck

383
384

W a tch E x p r e s s io n s

-------~----- ? 5 ~ v ~ rr

~ * rr~

G lo b a l

------

...

DOHWindow

B r e a k p o in ts
D O M B r e a k p o in ts

384 i
3*0 < poebugging
391 scre e n sh o ts< / p >
392

4
v

X H R B r e a k p o in ts

E v e n t L is te n e r B r e a k p o in ts
W o rk e rs

G D ebug

&

>3

01

. 9.11. - Safari

160

9.

Scripts
(call stack),
.
, .
,
.
,
,
.
, ,
, .
,
. ,
.
JavaScript console.log(),
, .


. 9.12, , , , ,
.
TQSearchRv^ovfc*s

^ Benvtms |
2

lC3

D o cu m e n ts

>Z

I-

S ty le sh e e ts

Im ages

S c rip ts

XHR

F o n ts

O th e r

Sort b y R e s p o n s e T im e

. 9.12. -

,
. , ,
. jQuery
, ,
, .

161

async defer
(. 10), , JavaScript
, . ,
.
.
.
DOMContentLoaded,
, DOM-. ,
load,
.

, , ,
(. 9.13).

Elements

I D o c u m e n ts

S ty le sh e e ts

lm * g e s

S c r ip ts

C 2 5 3 3 I

C o n te n t

XHR

F o n ts

O th e r

R e q u e st URL: h t t p : / / l o c a l h o s t : 9 2 9 2 / s t y l e s h e e t s / h i g h l i g h t . c s s
R e q u e st M e th o d : G ET
S ta tu s C o d e : 20 0 OK
v R e q u e st H e a d e rs
A cce p t: t e x t / c s s , / * ; q

0 .1

C a c h e - C o n t r o l: m a x -a g e * 0
R e fe r e r h t t p : / / l o c a I h o s t : 9 2 9 2 / p o s t s / c h 0 9 . h t I
U s e r A g e n t. H o z i l l a / 5 . 0 ( M a c in t o s h ; U ; I n t e l OS X 1 0 _ 6 _ 6 ;
KHTML, I x k e G e c k o ) V e r s i o n / 5 . 0 . 3 S a f a r i / 5 3 3 . 1 9 . 4

e n -u s )

A p p le W e b K a t / 5 3 3 .1 9 .4

R e s p o n s e H e a d e rs
C a c h e - C o m r o l: n a x - a g e 3 1 5 3 6 0 0 0 ,

p u b lic

C o n n e ctio n : K e e p - A l i v e
C o n te n t- L e n g th : 1 616
C o n te n t- T y p e : t e x t / c s s
D ate: T h u ,

10 H a r 20 1 1 1 9 : 0 9 : 3 7 GHT

E x p ire s: S a t ,

10 M a r 2012 0 7 : 4 2 : 4 2

S erver: W E B r ic k / 1 . 3 . 1

>a f

I?

S o n b y R e s p o n s e T im e

GHT

( R u b y / 1 .9 .2 / 2 0 1 1 - 0 2 - 1 0 )

02

. 9.13. ,


JavaScript-
, .
,
, -, Firebug.
,
co n so le.p ro file() console.
profileEnd():
console. p ro file ( );
II . . .
console. profileEnd();

162

9.

profileEnd(), ,
( , ),
(. 9.14).

Elements

Resources

Network

Scripts

( J

Tim eline I

ProTOes

Audits

fQ,'Search''proftSei

Console

Function

(program)
totiectedScript_popoUttPfop*rryN4m
injectedScript evaluateOn
IfOectedScript getProperties
(anonymous function)
InjectedScript evaluate
lr\jectedScnpt getCompiettons
injectedScript .eviluateAndWrap

Heavy (Bottom Up) J

. 9.14. -

record ()
,
.
, ,
.

, . 9.15.
, , , .
,
,
.

Dements

^ R eso u rces

(^ N e tw o rk

jp

Scripts

HEA P SNAPSHO TS

<S>

Profiles

4 ^ A u d ts

Console

18
IS

1127

>2

1 Count
3327
13300
2677
1547
295
352
1S17

Code

(Jf Timelirw

i Constructor
I(code)
IP String
I(closure)
iPOtyect
ay
RegExp
(global property)
DOMWinbow
Error
HTHLElemem

CPU PROFILES

C o m p a red to S napsh ot I J

Count
Objects
19874

Total
23^01

Code
1G3M!

Q, S*aich Proves
* Count ! Size jr

oj
" 08 IIT
08 1I
410.46KB

0
404.12KB
OB
16862KB
08
0
0
08
14.53KB
12.38KB
08
0
11.85KB
0
08
6.23KB
0
08
08 S'
504B
0
4208
08
*
Si/e
Objects
135MB

Tout

238M8

. 9.15. -

,
. API,
, console.

163

time(uAw) console.time End ().


, JavaScript ,

:
console.time("timeName");
II ...
console.timeEnd( "timeName");
timeEnd () ,
,
. API ,
,
,
.

- ,
. Facebook,
.
, .
JavaScript HTML , ,
, , .
- .
, ,
JavaScript-
. , .
-.


: HTTP-.
HTTP- ,
TCP.
.
, ,
.
,
-.
CSS
HTTP-,
.
.
-.
CSS-
.

165

CSS- background-image background-position.


.
HTTP-
. , ,
- ( /) URL,
. , http://facebook.com
http://facebook.com/. Apache
, Alias mod_rewrite.
, .

. ,
.
, ,
JavaScript-.
DOM-
, ,
. ,
, ,
. ,
defer, ,
DOM, :
<script src="foo.js" type= "text/javascript" charset="utf-8" d e fe rx /sc rip t>
defer, "defer",

. HTML5
, async. async
.
( ) async-
, ,
. , async
. , Google Analytics
:
<script src="h ttp ://www.google-analytics. com/ga. j s " asy n cx /scrip t>


, -
.
,
, .
. , Chrome,
, .

166

10.

,
Expires, .
, ,
, ,
.
Expires: Thu, 20 March 2015 00:00:00 GMT


. ,
20 2015 . - Apache,
, ExpiresDefault:
ExpiresDefault "access plus 5 years"

, ?
( mtime)
URL-, . ,
Rails . , , URL
, .
<link rel="stylesheet" href="master.css1296085785" type="text/css">

HTTP 1.1. Cache-Cont


rol
Expires. Cache-Control
, :
Cache-Control: max-age=3600j must-revalidate

- . ,
, , , .
max-age

,
. Expires,
,
.
public

. ,
SSL -,
.
no-store

,
.
must-revalidate

-, ,
.
HTTP -

167

. , , .
LastModif ied.
If-Modified-Since, .
, 304
( ). - ,
, :
#
GET /example.gif /1.1
Host:www.example.com
If-Modified-Since:Thu, 29 Apr 2010 12:09:05 GMT
#
HTTP/1.1 200 OK
Date: Thu, 20 March 2009 00:00:00 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 17 March 2009 00:00:00 GMT
Content-Length: 1040
Content-Type: text/html

Last-Modified : ETags. ETags


ETags , ,
. ,
Last-Modified. ETag ETag,
ETag If-None-Match:
#
GET /example.gif HTTP/1.1
Host:www.example.com
If-Modified-Since:Thu, 29 Apr 2010 12:09:05 GMT
If-None-Match:,,48ef9-14al-4855efe32ba40"
#
HTTP/1.1 304 Not Modified
ETags , , . .
ETags.
,
. Last-Modified
ETags.


JavaScript ,
. ,

168

10.

.
JavaScript.
, .
, , .
JavaScript.
HTML.
. ,
, . ,
,
.
,
. ,
, , ,
.
, ,
, JavaScript,
. YUI Compressor,
. Yahoo!
(Julien Lecomte) JavaScript, JSMin,
. , :
function per(value, total) {
return( (value / total) * 100 );

}
YUI Compressor
, :
function per(b,a){return((/)*100)};

YUI Compressor JavaScript,


, . ,
,
.
eval () with (). ,
, . , eval (),
with () JIT -
, .
.
YUI Compressor ,
Java, :
java -jar yuicompressor-x.y.z.jar foo.js | foo.min.js

169

.
, Sprockets Less, .
YUI Compressor,
gem- (Sam Stephenson) Ruby-YUI-compressor
J ammit.

Gzip
Gzip ,
-. GNU-,
H T T P /1.1. - ,
Accept-Encoding:
Accept-Encoding: gzip, deflate

-
,
Content-Encoding:
Content-Encoding: gzip

. ,
,
. Gzip 70%,
.
,
gzip. gzip
, HTML, JSON, JavaScript .
, PDF-,
gzip, .
gzip , Apache 2.x
, m oddeflate. -
.

CDN
, CDN (content delivery network),
, .
- .
CDN-
, ,
( , ).
Yahoo! , CDN-
20% .
, ,
, CDN-,

170

10.

Akamai Technologies, Limelight Networks, EdgeCast Level 3 Commu


nications. Amazon Web Services
Cloud Front, S3
-.
Google CDN-
JavaScript- , j Query j Query UI.
CDN Google ,
,
JavaScript-.
. , ,
j Query, JavaScript Google, , , s c rip t-:
<!-- jQuery -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">
</script>
<!-- jQuerylll -->

<script src="//ajax.googleapis. com /ajax/libs/jqueryui/1.8 .6 /jquery-ui.min.js " >


</script>
, ,
//. ,
,
-. ,
HTTPS,
,
. URL- ,
RTF-.
: URL-
Internet Explorer 3.0.

,
. YSlow Firebug,
, , Firefox.
, .
-.
, , , . gzip
CDN-. ,
,
.
Google Chrome Safari ,
. . 10.1, Chrome

171

Audits - Run ().


, ,
.

^ 'E le m e n ts

Netwok

httpc//iocalhsuS2*>2/p.

Profiles

^ A u d it s

9 Console

'Q

gzip compression (5)


9 Leverage browser caching (6)
4 Leverage proxy caching (1)
Consider adding a "Cache-Control: public* header to the following resources
loader,ts
v Minimize cookie size
The average cookie size for all requests on this page is 468
W ee
9

>2

C ^t Timehnc

s e a rc h A ucflw

Enable

RtSUITS

j p Scripts

v N etw ork U tilizatio n

Audits

(g j

jW _ Resources

Page

Perform ance

4 Optimize the order of styles and scripts (2)

lil

A
t f.

. 10.1. - -

Yahoo I, Google -. ,
,
. Google
Pagerank ,
. ,
.
Google Yahoo!.

Spine

Spine JavaScript, ,
, MVC, .
, 500
JavaScript, 2 .
- . Spine
JavaScript-,
.
Spine , MVC, ,
.
, , , , Holla,
, Spine.
, ,
Cappuccino SproutCore, Spine , .
. Spine ,
,
.
Spine Spine.
Class, Spine.Events, ORM Spine.Model
Spine.Controller. , ,
DOM-, ,
, . , Spine
j Query Zepto.js, .
Spine . ,
,
. ,
.

Spine .
Spine :
<script src="spine.js" type="text/javascript" charset="utf-8"x/script>

173

Spine,
, . ,
j Query, Zepto Prototype, .

Spine . Spine
Object.create() ,
3,
.
Spine.Class.create([instanceProperties, classProperties]),
(instanceProperties) (classProperties):
var User = Spine.Class.create({
name: "Caroline"

>);
User
name. createQ ,
Spine.Class, . . .
, createQ
:
var Friend = User.createQ;

Friend User,
:
assertEqual( Friend.prototype.name, "Caroline" );


-
,
new. Spine init():
var user = User.initQ;
assertEqual( user.name,

"Caroline" );

user.name "Irish";
assertEqual( user.name, "Irish" );

, initQ,
initQ:
var User - Spine.Class.create({
init: function(name){
this.name = name;

&

174

11. Spine

>
;
var user User.init(Martina");
assertEqual( user.name, "Martina" );


,
include() extend(), :
User.include({
//

});
User.extend({
//

});
includeQ extendQ ,
,
:
var ORM * {
extended: function(){
//
//
this ==* User

find: function(){ /* ... */


first: function(){ /* ... */ }

};
User.extend( ORM );


. extended
User.extend() User. ,
included, .
, ,
,
:
var Friend = User.createQ;
User.include({
email: "infogeribium.org

});
assertEqual( Friend.initQ.email, "infogeribium.org" );

.
, ,
. ,

175

,
. created(), Spine
:
// , records
var User = Spine.Class.create({
//
init: function(){
this.attributes = {};

}
b {

//
created: function(){
this.records = [];

}
;

JavaScript ,
Spine.Class
. :
var Controller Spine.Class.create({
init: function(){
//
$("#destroy").click(this.destroy);

L
destroy: function(){
// destroy ,
// 'this'
// :
assertEqual( this, Controller.fn );

}
}>;
destroy()
#destroy, Controller.
,
, , .
Spine ():
var Controller Spine.Class.create({
init: function(){
$("#destroy").click(this.proxy(this.destroy));

b
destroy: function(){ }

176

11. Spine

,
, ,
. Spine 11():
var Controller Spine.Class.create({
init: function(){
this.proxyAll("destroy", "render")
$("#destroy").click(this.destroy);

b
//
destroy: function(){ },
render: function(){ }

});
proxyAllQ ,
, .
destroy ()
render () .

Spine,
. Spine
Spine.Events, ,
. , ,
Spine:
var User Spine.Class.createQ;
User.extend(Spine.Events);

Spine. Events :
bind(eventName, callback)
trigger(eventName, [*data])
unbind(eventName, [callback])
API , jQuery,
. , , User
:
User.bind("create", function(){ /* ... */ });
User.trigger("create");


:
User.bind("create update", function(){ /* ... */ });

177

trigger() ,
, :
User.bind("countChange", function(count){
// 'count' trigger
assertEqual(count, 5);

;
User.trigger("countChange", 5);

Spine ,
.
.

Spine, ,
, ,
MVC-.
, a Spine ,
- (ORM).
createQ,
, Spine.Model.setup(name,
attrs), :
// Task.
var Task Spine.Model.setup("Task", ["name", "done"]);


include() extend():
Task.extend({
// ,
done: function(){ / * . . . * / }

});
Task.include({
//
name: "...
done: false,
toggle: function(){
this.done = !this.done;

>
;

178

11. Spine

,
:
var task = Task.init({name: " "});
assertEqual( task.name, " " );

,
. , a ttrib u te s Q
, :
var task = Task.initQ;
task.name = " ";
assertEqual( task.attributesQ, {name: " "});


saveQ. , ID , ,
:
var task = Task.init({name: " "});
task.saveQ;
task.id //=> "44E1DB33-2455-4728-AEA2-ECBD724B5E7B"

findQ ,
ID :
var task = Task.find("44ElDB33-2455-4728-AEA2-ECBD724B5E7B");
assertEqual( task.name, " ");

ID, .
, ,
e x is ts ():
var taskExists = Task.exists("44ElDB33-2455-4728-AEA2-ECBD724B5E7B");
assert( taskExists );

-,
destroyQ:
var task = Task.create({name: " "});
assert( task.existsQ );
task. destroyQ;
assertEqual( task.existsQ, false );


ID .

. Spine 11(),
selec tQ eachQ:
// (tasks)
Task.allQ; //=> []

179

// (done), false
var pending * Task.select(function(task){ return Itask.done });
//
Task.each(function(task){ /* ... */ });

, Spine
:
//
Task.findByAttribut(, );
//*>
//
Task.findAllByAttribut(, ); //=> []


,
:
Task.bind("save", function(record){
console.log(record.name, "!");

});
,
. ,
,
:
Task.first().bind("save", function(){
console.log(this.name, "!")

});

Task.firstQ.updateAttributes({name: " "});

tr ig g e r () , ,
, .
save
( , )
update

create

destroy

change
, . . (, )

180

11. Spine

refresh

error

,
.

:
validate(), . validate()
. validate() - ,
.
, :
Task.include({
validate: function(){
if ( !this.name ) return " ";

}
});
, validate()
, .

:
Task.bindC'error", furtction(record; msg){
//
alert(" : " + msg);

});
error .
.


Spine ,
: , HTML5
(Local Storage) Ajax.
.
JavaScript- spine.model.local.js
Spine.Model.Local:
//
Task.extend(Spine.Model.Local);
Task.fetch();

181

,
fetch(),
.
.
, refresh:
Task.bind("refresh", function(){
// !
renderTemplate(Task.all());

;
Ajax
, spine.model.ajax.js
Spine.Model.Ajax:
//
Task.extend(Spine.Model.Ajax);

Spine
URL
. URL- Task ^
/tasks. ,
URL:
// URL
Task.extend({
url: "/tasks"

;
// (tasks)
Task.fetch();

Task.fetch(), Spine Ajax


GET- /tasks, JSON,
. ,
refresh.
Spine Ajax- ,
, . ,
RESTful,
, ,
. Spine
:
read
-*GET
create
POST
update -*PUT
destroy -*DELETE

/
/
//id
//id

Spine HTTP POST-


, JSON- .

182

11. Spine

. , ,
:
POST /tasks HTTP/1.0
Host: localhost:3000
Origin: http://localhost:3000
Content-Length: 66
Content-Type: application/json
{"id": "44E1DB33-2455-4728-AEA2-ECBD724B$E7B", "name": " ")

,
DELETE, PUT. PUT
DELETE URL ID :
PUT /tasks/44ElDB33-2455-4728-AEA2-ECBD724B5E7B HTTP/1.0
Host: localhost:3000
Origin: http://localhost:3000
Content-Length: 71
Content-Type: application/json
{"id": "44E1DB33-2455-4728-AEA2-ECBD724B5E7B", "name": " ")

Spine Ajax , .
,
, . ,
, . .
.
. -,
, ,
. -,
,
,
- . -,
,
.
? Spine ,
.
, ,
( ),
.
,
ajaxError, , XMLHttpRequest, Ajax-,
:
Task.bind("ajaxError", function(record, xhr, settings, error){
//

});

183

Spine,

. DOM-
,
. Spine-
Spine.Controller createQ:
jQuery(function(){
window.Tasks = Spine.Controller.create({
//

});
});
,
,
. Spine
, jQuery().
.
Spine
, ,
, .
,
.

init ():
var tasks = Tasks.init();

DOM-,
el. .
,
div-:
var tasks = Tasks.init({el: $("#tasks")});
assertEqual( tasks.el.attr("id"), "tasks" );


:
window.Tasks = Spine.Controller.create({
init: function(){
this.el.html(" ");

}
});
var tasks = Tasks.init();
$("body").append(tasks.el);

184

11. Spine

, , init (),
. :
var tasks = Tasks.init({item: Task.first()>);
assertEqual( Task.first(), tasks.item );


,
, , this.proxy(),
.
, Spine
proxied. proxied,
,
:
// proxyAll
var Tasks = Spine.Controller.create({
proxied: ["render, "addAll"],
render: function(){ /* ... */ },
addAll: function(){ / * . . . * / }

});
, render(),
, .
.


. Spine :
elements. elements,
.
this.input , form input [type=text].
, (el):
// 'input'
var Tasks = Spine.Controller.create({
elements: {
"form input[type=text]": "input"

b
init: function(){
// this.input
console.log( this.input.val() );

}
});
, HTML-
(el) refreshElements()
.

185


Spine events
. Spine
, (el)
. events,
el.
events {" ": "__6"}. , ,
el.
, ,
, .
, el
:
var Tasks = Spine.Controller.create({
events: {
"keydown form input[type=text]": "keydown"

keydown: function(e){ /* ... */ }

, ,
keydown,
keydown.
.
,
, ,
. , , ,
target.


, Spine- ,
.
Spine. Events, ,
, bind() trigger().
,
:
var Sidebar = Spine.Controller.create({
events: {
"click [data-name]": this.click

b
init: function(){
this.bind("change", this.change);

186

11. Spine

change: function(name){ /* ... */ }, *


click: function(e){
this.trigger("change", $(e.target).attr("data-name"));

>

n ...
});
var sidebar = Sidebar.init({el: $("#sidebar")});
sidebar.bind("change", function(name){
console.log("Sidebar changed:", name);

})

Sidebar . 2
. ,
,
.


Spine .
PubSub, ,
, .
Spine.,
:
var Sidebar = Spine.Controller.create({
proxied: ["change"],
init: function(){
this.App.bind("change", this.change);

change: function(name){ / * . . . * / }

;
Spine- Spine.App
this., .
, Sidebar
change.
:
Spine..trigger("change", "messages");


, ,
.

.
,

187

.
el, , :
var Tasks = Spine.Controller.create({
init: function(){
Task.bind("refresh change", this.proxy(this.render));

b
template: function(items){
return($("#tasksTemplate").tmpl(items));

b
render: function(){
this.el.html(this.template(Task.all()));

}
});
,
. ,
,
,
.
.


,
, .
: , , ,
.
, , :
var Tasksltem = Spine.Controller.create({
// click
events: {
"click": "click"

b
//
proxied: ["render", "remove"],
//
init: function(){
this.item.bind("update", this.render);
this.item.bind("destroy", this.remove);

b
//
render: function(item){
if (item) this.item = item;

&

188

11. Spine

this.el.html(this.template(this.item));
return this;

L
// , jQuery.tmpl.js
template: function(items){
return($("#tasksTemplate").tmpl(items));

b
//
remove: function(){
this.el.removeO;

b
// ,
//
click: function(){ / * . . . * / }

});
var Tasks = Spine.Controller.create({
proxied: ["addAll", "addOne"],
init: function(){
Task.bind("refresh", this.addAll);
Task.bind("create", this.addOne);

b
addOne: function(item){
var task = Tasksltem.init({item: item});
this.el.append(task.render().el);

b
addAll: function(){
Task.each(this.addOne);

}
});
Tasks
, Tasksltem
update () destroy (),
. ,
.
, : . ,
. ,
click,
.

189


API Spine
- , .
, , ,
.
. 11.1 , ,
.
& ^ >
1

index.html
1

* file // Users/Alex/spinexontacts/ ndex

-______ - ____

tml

LfEX* Google

Spine Contacts

Edit contact

Search
A le x M a c C a w

Email contact

Name
A le x M a c C a w

R ich a rd M a c C a w

L !

T rish Fogarty

M artina C u d d y

Email
info@eribiunn.org
Mobile number

000-000-000-00
Work number
000 0
Address
1 Infinity Lo o p

Blank

. 11.1. Spine-

Spine-,
.
.
. 11.1 ,
, .
Sidebar Contacts.
, : Contact.

190

11. Spine

^ ,
:
<div id"sidebar">
<ul class"items">
</ul>

<footer>
<button>New contact</button>

</footer>
</div>
<div class*"vdivide"x/div>

<div id"contacts">
<div class"show">
<ul class*"options">
<li class"optEdit">Edit contact</li>
<11 class*"optEmail>Email contact</li>
</ul>

<div class"content"x/div>
</div>
<div class"edit">
<ul class"options">
<11 class*"optSave default>Save contact</li>
<11 class"optDestroy">Delete contact</li>
</ul>

<div class"content"x/div>
</div>
</div>
, , div- #sidebar div #contacts.
. items, ,
#contacts. .optEmail .optSave,
.
, .optDestroy,
.

Contact
, .
: f irst_name, last_name email.
,
:
//
var Contact = Spine.Model.setup("Contact,

[ "first_nam e", "last_name", "email"]);

191

//
Contact.extend(Spine.Model.Local);
//
Contact.include({
fullName: function(){
if ( !this.first_name && !this.last_name ) return;
return(this.first_name + " " + this.last_name);

>
;
, Spine.Model.Local.
,
.

Sidebar
Sidebar,
.
Sidebar , .
, New contact ( ),
, .
.
,
Spine, ,
:
jQuery(function($){
window.Sidebar = Spine.Controller.create({
// :
// this.items //=> <ulx/ul>
elements: {
.items: "items

b
//
events: {
"click button": "create"

b
// ,
//
//
proxied: ["render"],
//
template: function(items){
return($("#contactsTemplate").tmpl(items));
}j

&

192

11. Spine

init: function(){
this.list = Spine.List.init({
el: this.items,
template: this.template

}>S
//
this.list.bind("change", this.proxy(function(item){
this.App.trigger("show:contact", item);

};
//
// , . .
this.App.bind("show:contact edit:contact", this.list.change);
//
Contact.bind("refresh change", this.render);

b
render: function(){
var items = Contact.all();
this.list.render(items);

b
// 'Create'
create: function(){
var item = Contact.create();
this.App.trigger("edit:contact", item);

}
});
});
, init ()
Spine.List, . Spine.List
, . Spine.
List ,
change, .

. , ,
, , ,
.
#contactsTemplate, template(), script,
:
<script type="text/x-jquery-tmpl" id="contactsTemplate">
<li class="item">

193

{{if fullName()}}
<span>${fullName()}</span>
{{else}}
<span>No Name</span>
{{/if}}
</li>
</script>

j Query.tmpl,
5. Spine.List
,
current class- <>,
.

Contacts
Sidebar ,
.
? Contacts:
jQuery(function($){
window.Contacts = Spine.Controller.create({
//
elements: {
".show": "showEl",
".show .content": "showContent",
".edit: "editEl"

b
proxied: ["render", "show"],
init: function(){
//
this.show();
//
Contact.bind("change", this.render);
//
this.App.bind("show:contact", this.show);

b
change: function(item){
this.current = item;
this.render();

re n d e r: fu n c tio n ( ) {

&

194

11. Spine

this.showContent.html($("#contactTemplate").tmpl(this.current));

b
show: function(item){
if (item && item.model) this.change(item);
this.showEl.show();
this.editEl.hide();

}
});
,
show: contact. Contacts,
show(), .
div- showContent,
.
#contactTemplate,
Contacts .
:
<script type"text/x-jquery-tmpl" id="contactTemplate">
<label>
<span>Name</span>
${first_name} ${last_name}
</label>
<label>
<span>Email</span>
{{if email}}
${email}
{{else}}
<div class="emptyH>Blank</div>
{{/if}}
</label>
</script>


, ?
Contacts, .
,
,
.optEdit .optSave. :
#editContactTemplate.
:
jQuery(function($){
window.Contacts = Spine.Controller.create({
//

elements: {
".show": "showEl",
".edit": "editEl",
".show .content": "showContent",
".edit .content": "editContent"

b
//
events: {
"click .optEdit": "edit",
"click .optDestroy": "destroy",
"click .optSave": "save"

b
proxied: ["render", "show", "edit"],
init: function(){
this.show();
Contact.bind("change", this.render);
this.App.bind("show:contact", this.show);
this.App.bind("edit:contact", this.edit);

b
change: function(item){
this.current * item;
this.render();

b
render: function(){
this.showContent.html($("#contactTemplate").tmpl(this.current));
this.editContent.html($("#editContactTemplate").tmpl(this.current));

b
show: function(item){
if (item && item.model) this.change(item);
this.showEl.show();
this.editEl.hide();

b
// 'edit'
edit: function(item){
if (item && item.model) this.change(item);
this.showEl.hide();
this.editEl.show();

b
// 'delete'

&

196

11. Spine

destroy: function(){
this.current.destroy();

b
// 'save'
save: function(){
var atts = this.editEl.serializeForm();
this.current.updateAttributes(atts);
this.show();

}
});
});
,
#editContactTemplate. ,
. , #editContactTemplate
#contactTemplate, ,

:
<script type="text/x-jquery-tmpl" id="editContactTemplate">
<label>
<span>First name</span>
<input type="text" name="first_name" value="${first_name}" autofocus>
</label>
<label>
<span>Last name</span>
cinput type="text" name="last_name" value="${last_name}">
</label>
<label>
<span>Email</span>
<input type="text" name=email value="${email}">
</label>
</script>

App
, Sidebar Contacts,
, Contact.
,
, :
jQuery(function($){
window.App = Spine.Controller.create({
el: $("body"),
elements: {
"#sidebar": "sidebarEl",

197

"#contacts": "contactsEl"

init: function(){
this.sidebar = Sidebar.init({el: this.sidebarEl});
this.contact = -Contacts.init({el: this.contactsEl});
//
Contact.fetch();

}
}).init();

});
, .init()
. fetch() Contact,
.
, , ! (Sidebar Contacts),
(Contact) . ,
. 11.2.

' 4 ,

mdex.html
I + I * file:///Users/Atex/sp<ne.comacts/index.htmt

C l

c v Coogle

<*Search
Alex M acCaw

Juiia

Martina C u d d y

Stina

1
1

Fir** name
R ichard]

Tim M acC aw

M acCaw
Email

Mobile number

Trish Fogarty

Work number

C"
Address
New contact
act

. 11.2. Spine-

Backbone

Backbone JavaScript-.
.

. , MVC,
Backbone.
, ,
.
Backbone , SproutCore
Cappuccino?
Backbone. SproutCore Cappuccino
,
HTML. gzip-
,
JavaScript, CSS , . ,
Backbone 4 ,
, , , ,
.
Backbone underscore.js,
, JavaScript
. Underscore 60 , ,
, , JavaScript
.
API, Underscore,
. Underscore , Backbone
, j Query
Zepto.js.
Backbone ,
.
,
.

199

Backbone, .
Backbone , .

, , MVC: .
.

. -
extend() Backbone.Model:
var User = Backbone.Model.extend({
initialize: function() {

/ / ...
}
});
, extend(), ,
.
. ,
, extend ()
:
var User = Backbone.Model.extend({
//
instanceProperty: "foo"

b {

//
classProperty: "bar"

}>;
assertEqual( User.instanceProperty, "foo" );
assertEqual( User.prototype.classProperty, "bar" );


initialize(), ,
. - ,
new:
var User = Backbone.Model.extend({
initialize: function(name) {
this.set({name: name});

}
});
var user = new User("Leo McGarry");
assertEqual( user.get("name"), "Leo McGarry");

200

12. Backbone



set() get():
var user = new User();
user.set({name: "Donna Moss"})
assertEqual( user.get("name"), "Donna Moss" );
assertEqual( user.attributes, {name: "Donna Moss"} );

set(att rs, [options]) ,


, get (attr)
, .
- attributes.
, get() set(),
.

validate(). ,
, :
var User = Backbone.Model.extend({
validate: function(atts){
if (latts.email || atts.email.length < 3) {
return " 3 ";

}
}
});
, -
validate() , ,
, Error.
, set() save()
error.
error,

:
var user * new User;
user.bind("error", fundtion(model, error) {
//

});
user.set({email: "ga"});
//
user.set({"email": "ga"}, {error: function(model, error){

II...
}});

201

default.

:
var Chat = Backbone.Model.extend({
defaults: {
from: "anonymous"

}
});
assertEqual( (new Chat).get("from"), "anonymous" );

Backbone . ,
, ,
, , . , ,
Twitter, : Followers
Followees, User.
,
User,
.
, Back
bone. Collection:
var Users = Backbone.Collection.extend({
model: User

});
,
model, ,
User.

, ,
, - .
, .

. -,
, :
var users = new Users([{name: "Toby Ziegler"}, {name: "Josh Lyman"}]);

, add():
var users = new Users;
//
users.add({name: "Donna Moss"});

&

202

12. Backbone

//
users.add([{name: "Josiah Bartlet"}, {name: "Charlie Young"}]);

add:
users.bind("add", function(user) {
alert("Ahoy " + user.get("name") + "!");

;
, remove (),
remove:
users.bind("remove", function(user) {
alert(" " + user.get("name") + "!");

});
users.remove( users.models[0] );

. ID ,
get ():
var user = users.get("some-guid");

ID , cid
(client ID), Backbone :
var user = users.getByCid("HeKUU_cicT);

add remove ,
, change:
var user = new User({name: "Adam Buxton"});
var users = new Backbone.Collection;
users.bind("change", function(rec){
// !

});
users.add(user);
user.set({name: "Joe Cornish"});



comparator(), ,
,
:
var Users = Backbone.Collection.extend({
comparator: function(user){
return user.get("name");

}
});

203

, ,
( JavaScript).
Users
.
, -
, . sort().

- ,
, .
, MVC- ,
HTML ,
. , Backbone ,
,
DOM-.
,
Backbone , Backbone.
View:
var UserView = Backbone.View.extend({
initialize: function(){ /* ... */ },
render: function(){ / * . . . * / }

;
DOM-,
this.el, , .
el
tagName, className id. , el
div-:
var UserView = Backbone.View.extend({
tagName: "span",
className: "users"

;
assertEqual( (new UserView).el.className, "users" );


, el . , ,
,
:
var UserView = Backbone.View.extend({
el: $(".users")

});

204

12. Backbone

el
, tagName, className id:
new UserView({id: "followers"});


render(),
no-op ( ).
.
,
el HTML:
var TodoView = Backbone.View.extend({
template: template($("#todo-template").html()),
render: function() {
$(this.el).html(this.template(this.model.toJS0N()));
return this;

}
});
Backbone ,
.
. ,
HTML JavaScript-nporpaMM. Underscore.js,
, Backbone, ,
template (), .
, , ,
this.model.

:
new TodoView({model: new Todo});

toJS0N(), ,
, .


-
e l .

:
var TodoView = Backbone.View.extend({
events: {
"change input[type=checkbox]" : "toggleDone",
"click .destroy"
: "clear",

205

toggleDone: function(e){ /* ... */},


clear: function(e){ /* ... */}

});
{" : "__"}. , ,
el. ,
, , ,
el , .
, , -
, el.
,
.
, ,
,
. ,
this.el this.model,
toggleDone() clear().


render()?
, ,
, change. ,
HTML ( )
:
var TodoView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render', 'close');
this.model.bind('change', this.render);

b
close: function(){ / * . . . * / }

;
, ,
. Underscore
: _. bindAll(//, *).
( ). _.bindAll()
,
. ,
, .
render() close()
TodoView.

206

12. Backbone


. delete,
el :
var TodoView Backbone.View.extend({
initialize: function() {
bindAll(this, 'render', 'remove');
this.model.bind('change', this.render);
this.model.bind('delete', this.remove);

b
remove: function(){
$(this.el).remove();

>
, -
, .
render() initialize(),
.
,
, Backbone.

- URL,
, ,
. ,
, .
: , (splats), , :
routes: {
"help":
"help",
"search/:query":
"search",
"search/:query/p:page": "search"
"file/*path":
"file"

// :
// #help
// #search/kiwis
// #search/kiwis/p7
// #f ile//Ho66/e_cuAi6o/76//path.txt

}
,
:, . ,
, . ,
*, , -,
. ,
.
, ,
. , ,
, .

207

Backbone.Controllers,
, :
var PageController = Backbone.Controller.extend({
routes: {
I i i .
index,
"help":
help",
I I #help
"search/:query":
search", // #search/kiwis
"search/:query/p:page'
search"
// #search/kiwis/p7

b
index: function(){ /* ...'*// },
h
help: function() {

/ / ...
b
search: function(query, page) {

// ...
>
;
, http://example.
com#search/coconut, ,
, search() query,
"coconut".

Ajax Crawling
( 4),
!/, :
var PageController = Backbone.Controller.extend({
routes: {
"!/page/:title": "page", // #!/page/foo-title

>
// ...
}):
,
.
,
,
route () :
var PageController = Backbone.Controller.extend({
initialize: function(){
this.route(/pages\/(\d+)/, 'id', function(pageId){

II . . .
});
>
:

208

12. Backbone

, URL- ,
? window,
location.hash , Backbone
saveLocation(fragment):
Backbone.history.saveLocation("/page/" + this.model.id);

saveLocation() URL-
. ,
saveLocation() initialize() ,
, - .
Backbone
onhashchange , ,
, i f - .
Backbone:
Backbone.history.start();

Backbone ,
, . Backbone
API HTML5,
pushState() replaceState(). ,
,
Internet Explorer.
Backbone, .
- URL.


Backbone
Ajax-, : jQuery, Zepto.js. Backbone
Backbone.sync() ,
. Backbone RESTful JSON-
, , , .
,
url REST- .
Backbone:
var User = Backbone.Model.extend({
url: '/users'

});
url , , .
,
.

209

Backbone , , (CRUD)
:
create -> POST /
read
-> GET /[/11]
update -> PUT
//id
delete
DELETE
//id

, User Backbone POST-


/users. , User PUT-
/users/id, id . POST-, PUT GET- Backbone JSON-
, .

save([attrs], [options]),
. id, ,
, save() PUT-
(update) . save() POST-
(create):
var user = new User();
user.set({name: "Bernard"});
user.save(null, {success: function(){
//

}});
save() ,
Ajax-, success failure.
, Backbone j Query, ,
save(), $.ajax(). ,
Ajax- j Query,
timeout.
,
error. ,
:
var user = new User();
user.bind("error", function(e){
// !

});
user.save({email: " "});

, fetch(),
( GET-).

210

12. Backbone

,
change:
var user * Users.get(l);
user.fetch();


, ,
? Backbone,
.
,
url. , Backbone
url :
var Followers = Backbone.Collection.extend({
model: User,
url: "/followers"

;
Followers.fetch();

fetch() GET- ,
/followers, .
, , refresh.
refresh()
, .
. GET-
, ,
refresh () JSON-. , ,
Rails:
<script type="text/javascript">
Followers.refresh(<%= @users.to_json %>);
</script>


, Backbone
RESTful:
create
read
read
update
delete

POST
GET
GET
PUT
DELETE

/
/
//id
//id
//id

Backbone JSON.
User :
{"name": "Yasmine"}

211

, ,
, Rails-.
Rails Backbone, ,
, .
CRUD-
. , Rails- update,
:
def update
user ** User.find(params[:id])
user.update_attributesl(params)
render :json *> user
end

,
attr_accessible,
, .
, destroy, JSON- .
JSON ,
Rails ,
:
{"user: {"name": "Daniela"

, Backbone .
, Rails
JSON- , -:
# config/initializers/json.rb
ActiveRecord::Base.include_root_in_json * false


Backbone.sync() Backbone,
.
( Ajax-)
, WebSockets, XML-
. , , Backbone.sync() -, ,
:
Backbone.sync = function(method, model, options) {
console.log(method, model, options);
options.success(model);

};
, Backbone.sync()
(method), (model) (options),
:

212

12. Backbone

method

CRUD- ( create, read, update


delete)
model

( )
options

, ,

, Backbone ,
: opt ions. success(), opt ions. error().
sync
:
Todo.prototype.sync = function(method, mod^l, options){ /* ... */ };

Backbone.sync()
.
localStorage
Backbone
HTML5 localStorage. , Backbone, sync()
CRUD- store, ,
options.success () :
// todo
// "todos" localStorage.
Todos.prototype.localStorage = new Store("todos");
// Backbone.sync()
// localStorage
// , Store.
Backbone.sync = function(method, model, options) {
var resp;
var store = model.localStorage || model.collection.localStorage;
switch (method) {
case "read":
resp =
break;
case "create": resp =
break;
case "update": resp =
break;
case "delete": resp =
break;

model.id ? store.find(model) : store.findAll();


store.create(model);
store.update(model);
store.destroy(model);

To-Do ( )

213

if (resp) {
options.success(resp);
} else {
options.error(" ");

>
};

To-Do ( )
, Backbone,
, . ,
CRUD-
. ,
,
assets/chl2/todos.
, CSS, JavaScript- -, todos.js,
:
<html>
<head>
<link href="todos.css" media="all" rel="stylesheet" type="text/css"/>
<script src="lib/json2. js"x/script>
<script src="lib/jquery. js"x/script>
<script src="lib/jquery.tmpl.js"x/script>
<script src="lib/underscore.js"x/script>
<script src="lib/backbone. js"x/script>
<script src="lib/backbone.localStorage.js"x/script>
<script src="todos.js"x/script>
</head>
<body>
<div id="todoapp,,>
<div class="title">
<hl>Todos</hl>
</div>
<div class="content">
<div id="create-todo">
<input id="new-todo" placeh0lder="4T0 ?"
type="text" />
</div>
<div id="todos">
<ul id="todo-list"x/ul>
</div>
</div>
</div>
</body>
</html>

214

12. Backbone

:
(#new-todo) ,
(#todo-list).
todos.js, Backbone. jQuery () , ,
:
// todos.js
jQuery(function($){
// ...

})
Todo content done.
done,
toggle ():
window.Todo = Backbone.Model.extend({
defaults: {
done: false

b
toggle: function() {
this.save({done: !this.get("done")});

}
});
, Todo
window. ,
, ,
window.
TodoList,
Todo:
window.TodoList = Backbone.Collection.extend({
model: Todo,
// to-do "todos".
localStorage: new Store("todos"),
//
// ,
done: function() {
return this.filter(function(todo){ return todo.get('done'); });

b
remaining: function() {
return this.without.apply(this, this.done());

}
});

To-Do ( )

215

// Todos.
window.Todos = new TodoList;

- (backbone.localStorage. js),
localStorage
, . TodoList, done()
remaining(), ,
, .
TodoList, :
window.Todos.

, , TodoView.
change Todo
:
window.TodoView = Backbone.View.extend({
// .
tagName: "",
// template ,
template: $("#item-template").template(),
//
events: {
"change
.check"
: "toggleDone",
"dblclick .todo-content" : "edit",
"click .todo-destroy" : "destroy",
"keypress .todo-input"
: "updateOnEnter",
"blur
.todo-input"
: "close"

b
initialize: function() {
//
_.bindAll(this, 'render', 'close', 'remove');
//
this.model.bind('change', this.render);
this.model.bind('destroy', this.remove);

b
render: function() {
// el
var element = jQuery.tmpl(this.template, this.model.toJSON());
$(this.el).html(element);
return this;

b
// done ()
toggleDone: function() {

&

216

12. Backbone

this.model.toggle();

b
// '"editing"' (),
// ,
edit: function() {
$(this.el).addClass("editing");
this.input.focus();

b
// '"editing"', .
close: function(e) {
this.model.save({content: this.input.val()});
$(this.el).removeClass("editing");

b
// '', .
// blur ,
// close()
updateOnEnter: function(e) {
if (e.keyCode == 13) e.target.blur();

b
//
remove: function() {
$(this.el).remove();

b
// .todo-destroy1
destroy: functionQ {
this.model.destroy();

>
;
, ,
, . ,
toggleDone(),
done. , , change,
.
HTML- jQuery.tmpl,
e l .
, ID, #item-template,
. , body-
index.html:
<script type="text/template" id="item-template">
<div class="todo {{if done}}done{{/if}}">
<div class="display" title=" ...">

To-Do ( )

217

<input class="check" type="checkbox"


{{if done}}checked="checked"{{/if}} />
<div class="todo-content">${content}</div>
<span class="todo-destroy"x/span>
</div>
<div class="edit">
<input class="todo-input" type="text" value="${content}" />
</div>
</div>
</script>


5, j Query.tmpl . ,
#todo-content #todo-input.
, "checked".
TodoView
el
. AppView,
TodoView.
AppView Todo,
, #new-todo:
// AppView UI ,
window.AppView = Backbone.View.extend({
//
// , HTML,
el: $("#todoapp"),
events: {
"keypress #new-todo": "createOnEnter",
"click .todo-clear a": "clearCompleted"

b
//
// 'Todos', .
//
// ,
// *localStorage*.
initialize: function() {
_.bindAll(this, 'addOne', 'addAll', 'render');
this.input = this.$("#new-todo");
Todos.bind('add',
this.addOne);
Todos.bind('refresh', this.addAll);

Todos.fetch();

b
&

218

12. Backbone

11
// '<ul>'.
addOne: function(todo) {
var view * new TodoView({model: todo});
this.$("#todo-list").append(view.render().el);

// Todos.
addAll: function() {
Todos.each(this.addOne);

b
// ,
//
// Todo
createOnEnter: function(e) {
if (e.keyCode != 13) return;
var value = this.input.val();
if ( lvalue ) return;
Todos.create({content: value});
this.input.val(11);

clearCompleted: functionQ {
_.each(Todos.done(), function(todo){ todo.destroy(); });
return false;

>
});
// , .
window.App = new AppView;

Todos ,
refresh. addAll(),
Todo, TodoView
#todo-list. , Todo Todos,
Todos, addOne()
TodoView . ,
Todo AppView, TodoView
.

. ,
- . 12.1.
, ,
,
. - ,
.

219

To-Do ( )

Backbone, ,
.
assets/chl2/todos.
Backbone Demo: To do s

til 4 1* .'f

I http://example.com

Coogife

Todos
What needs to be done?
Have tea with the Queen
Circumnavigate the world
Race a cheetah
Clim b Mount Everest
3 items left.

//

. 12.1. - Todo

JavaScriptMVC

(Justin Meyer),
JavaScriptMVC
JavaScriptMVC (JMVC) JavaScript- ,
j Query. ()
, ,
,
j Query.
JavaScriptMVC
, . , ,

7 , .
JavaScriptMVC
,
.
JavaScriptMVC $. Class,
$ .Model, $.View $ .Controller. .
$ .Class
, JavaScript
$ .Model

$.View

$ .Controller
j Query
JavaScriptMVC
-- (MVC).
, $ .C ontroller
, ,

221

,
.

JavaScript MVC ,
.
MVC-, (download builder, http://
javascriptmvc.com/builder.html), Controller, Model View
EJS templates Download.
j Query
. s c rip t :
<script type='text/javascript' src='jquery-1.6.1.js'></script>
<script type='text/javascript' src='jquerymx-1.0.custom.js'></script>

J MVC-
$. Class. $. Class (, [, ] ]):
$.Class("Animal,{
breathe : function(){
console.log(breathe);

}
});
Animal breathe().
Animal
breathe():
var man = new Animal();
man.breathe();

,
:
Animal("Dog",{
wag : function(){
console.log('wag');

}
})
var dog = new Dog;
dog.wag();
dog.breathe();

222

13. JavaScriptMVC


init
, -:
$.Class('Person',{
init : function(name){
this.name * name;

b
speak : function(){
return "I am " + this.name +

>
;
var payal ^ new Person("Payal");
assertEqual( payal.speak() , 'I am Payal.' );


this ._super.
person, :
Person("ClassyPerson", {
speak : function(){
return "Salutations, " + this._super();

}
});
var fancypants * new ClassyPerson("Mr. Fancy");
assertEquals( fancypants.speak() , 'Salutations, I am Mr. Fancy.')

,
this, $.proxy.
Clicky, :
$.Class("Clicky",{
init : function(){
this.clickCount = 0;

b
clicked: function(){
this.clickCount++;

listen: function(el){
el.click( this.callback('clicked') );

>
})

223

var clicky = new ClickyQ;


clicky.listen( $('#foo') );
clicky.listen( $('#bar') ) ;


.
person
Per son. findOne( ID, success(person) ).
Person, speak:
$.Class("Person",{
findOne : functioned, success){
$.get('/person/'+id, function(attrs){
success( new Person( attrs ) );
b json)

init : function(attrs){
$.extend(this, attrs)
b
speak : function(){
return "I am "+this.name+".

}
})
Person.findOne(5, function(person){
assertEqual( person.speak(), "I am Payal." );

:
$.Class("Jupiter.Person");
Jupiter.Person.shortName; //-> 'Person'
Jupiter.Person.fullName; //-> 'Jupiter.Person'
Jupiter.Person.namespace; //-> Jupiter
var person new Jupiter.Person();
person.Class.shortName; //-> 'Person'


,
ORM-. Model

224

13. JavaScriptMVC

REST-
Model:
$.Class("Model",{
findOne : functioned, success){
$.get('/' + this.fullName.toLowerCase() + '/' + id,
this.callback(function(attrs){
success( new this( attrs ) );

})
b'json')

>
}>{

init : function(attrs){
$.extend(this, attrs)

}
})
Model("Person",{
speak : function(){
return "I am "+this.name+".

>
;
Person.findOne(5, function(person){
alert( person.speak() );

;
Model("Task");
Task.find0ne(7,function(task){
alert(task.name);

;
, JavaScriptMVC.

JavaScriptMVC
, ,
, , .
,
.


, ,

. , MVC .

225

, JavaScript MVC
. .

. ,
Next () Previous (),
,
(, 1-20).

.
offset


limit


count


JavaScript MVC $. Model:
var paginate = new $.Model({
offset: 0,
limit: 20,
count: 200

;
paginate .
,
, .
model.attr(HAW):
assertEqual( paginate.offset, 0 );
assertEqual( paginate.attr('limit') , 20 );

,
(offset).
model.attr(HAftf, ).
offset :
paginate.attr('offset', 20);


, .

model, bind(ATTR, success( ev, newVal ) ):
paginate.bind('offset', function(ev, newVal){
$('#details').text( ' ' + (newVal + 1 ) +
'-' + this.count )
})

226

13. JavaScriptMVC


'updated.attr':
paginate.bind('updated.attr', function(ev, newVal){
$('tdetails').text( ' ' + (newVal+1 )+ '-' +
this.count )

})
j Query ,
:
J.fn.nextPrev = function(paginate){
this.delegate('.next','click', function(){
var nextOffset = paginate.offset + paginate.limit;
if( nextOffset < paginate.count){
paginate.attr('offset', nextOffset );

}
});
this.delegate('.prev','click', function(){
var nextOffset * paginate.offset-paginate.limit;
if( 0 < paginate.offset ){
paginate.attr('offset', Math.max(0, nextOffset) );

}
});
var self * this;
paginate.bind('updated.attr', function(){
var next = self.find('.next'),
prev = self.find('.prev');
if( this.offset == 0 ){
prev.removeClass('enabled');
} else {
prev.removeClass('disabled');

>
if( this.offset > this.count - this.limit ){
next.removeClass('enabled');
} else {
next.removeClass('disabled');

}
;
};
.
-, ,
paginate. ,
.

227

-,
(offset:) , ,
. .
,
,
limit, offset count.


JavaScriptMVC $ . Class.
$. Model (, [STATIC, ] ):
$.Model('Paginate', {
staticProperty: 'f'

prototypeProperty: 'bar'

Paginate.
-
,
.

-
- ,
set. val, model. attr(HAftf,
val), ,
. ,
, (error)
. (success)
-, .
Paginate -
(count)
(offset). ,
:
$.Model('Paginate', {
setCount : function(newCount, success, error){
return newCount < 0 ? 0 : newCount;

b
setOffset : function(newOffset, success, error){
return newOffset < 0 ? 0 :
Math.min(newOffset, !isNaN(this.count - 1) ?
this.count : Infinity )

}
});

228

13. JavaScriptMVC

nextPrev
(offset),
:
this.delegate('.next','click', function(){
paginate.attr('offset', paginate.offset+paginate.limit);

;
this.delegate('.prev','click', function(){
paginate.attr('offset', paginate.offset-paginate.limit );

});

Paginate defaults.
paginate ,
:
$.Model('Paginate',{
defaults : {
count: Infinity,
offset: 0,
limit: 100

setCount : function(newCount, success, error){ ... },


setOffset : function(newOffset, success, error){ ... }

;
var paginate = new Paginate({count: 500});
assertEqual(paginate.limit, 100);
assertEqual(paginate.count, 500);


Paginate
-.

-
,
, .
Paginate next prev,
, .
canNext canPrev,
:
$.Model('Paginate',{
defaults : {
count: Infinity,

229

offset: 0,
limit: 100

setCount : function( newCount ){


return Math.max(0, newCount );

b
b

setOffset : function( newOffset ){


return Math.max( 0 , Math.min(newOffset, this.count ) )
next : function(){
this.attr('offset', this.offset+this.limit);

prev : function(){
this.attr('offset', this.offset - this.limit )

canNext : function(){
return this.offset > this.count - this.limit

canPrev : function(){
return this.offset > 0

}
})
, j Query- :
J.fn.nextPrev = function(paginate){
this.delegate('.next','click', function(){
paginate.attr('offset', paginate.offset+paginate.limit);

this.delegate('.prev','click', function(){
paginate.attr('offset', paginate.offset-paginate.limit );

});

var self = this;


paginate.bind('updated.attr', function(){
self.find('.prev')[paginate.canPrevQ ? 'addClass' :
'removeClass']('enabled')
self.find('.next')[paginate.canNext() ? 'addClass' :
'removeClass']('enabled');

})


, $ .Model
.
, . ,
, ( CRUD-) ,
.
, $ .Model .

230

13. JavaScriptMVC

$ .Model .
.
$ . Model
: Representational
State Transfer (REST) JSON.
REST URL- HTTP- POST, GET, PUT
DELETE , ,
. , , , ,
(tasks), :
create
POST /tasks
read all
GET /tasks
read
GET /tasks/2
update
PUT /tasks/2
delete - DELETE /tasks/2

,
, , , :
$.Model("Task,{
create : "POST /tasks.json",
findOne : "GET /tasks/{id}.json",
findAll : "GET /tasks.json",
update : "PUT /tasks/{id}.json",
destroy : "DELETE /tasks/{id}.json"

;
, Task
CRUD- .


new Task({ name: 'do the dishes'}).save(
success( task, data ),
error( jqXHR)
) //=> taskDeferred


new Model (attributes). save(). save()
, ID. ,
save() .
.
success

, , . success
, .
error

, ,
. XHR-, jQuery.

231

save() deferred- (
), .


Task.findOne(params, ~
success( task ),
error( jqXHR)
) //=> taskDeferred

. ,
params

; ID { id : 2}.
success

, , . success
.
error

, , .
findOneQ deferred-, .


Task.findAll(params,
success( tasks ),
error( jqXHR)
) //=> tasksDeferred

. ,
params

. ({})
{limit: 20, offset: 100}.

success
, , . success
.
error

, , .
findAll () deferred-, .


task.attr('name','take out recycling');
task.save(
success( task, data ),
error( jqXHR)
) //=> taskDeferred

232

13. JavaScriptMVC

attr
. save(). save()
,
.


task.destroy(
success( task, data ),
error( jqXHR)
) //=> taskDeferred

destroy() . ,
success

, , . success
, .
error

, , .
save(), destroy () deferred-,
. , Task !


,
(createdAt) , 1303173531164?
18 2011 .
task.createdAt ,
JavaScript, new Date(1303173531164).
- setCreatedAt,
(Date)
.
, $. Model ,
.
attributes,
convert:
$.Model('Task'/{
attributes : {
createdAt : 'date'

convert : {
date : function(date){
return typeof date == 'number' ? new Date(date) : date;

>
>

233

Task createdAt Date.


:
Task.findAll({}, function(tasks){
$.each(tasks, function(){
console.log( "Year = "+this.createdAt.fullYearQ )

>);

CRU D-
, .

. ,
MOD L.bind (,
callback( ev, instance ) ).

,
. ,
.
:
Task.bind('created', function(ev, task){
var el = $('<li>').html(todo.name);
el.appendTo($('#todos'));
task.bind('updated', function(){
el.html(this.name);
}).bind('destroyed', function(){
el.remove();

>);



JavaScriptMVC-
, ,
. HTML,
DOM.
$.View ,
, . :

;
HTML- ;
;
;

234

13. JavaScriptMVC

;
;
$. Deferred.
JavaScriptMVC
:

EJS
JAML
Micro
Tmpl

EJS-,
(
).



. jQuery.View
j Query-,
:
$("#f" ).html('mytemplate.js ',{message: 'hello world'})

:
1. mytemplate.ejs. :
<h2><%= message %></h2>

2. {message: fhello world'},


:
<h2>hello world</h2>

3. foo, :
<div id='foo'><h2>hello world</h2x/div>

jQ uery
-
j Query:
$('#bar').after('temp.ejs',{});
$('#bar').append('temp.ejs',{});
$('#bar').before('temp.ejs',{});

$('#bar').html('temp.ejs',{});
$('#bar').prepend('temp.ejs',{});
$('#bar').replaceWith('temp.ejs',{});
$('#bar').text('temp.ejs',{});

235

script-
s c rip t- .
s c rip t- s c rip t- type,
(te x t/e js), id, :
<script type='text/ejs' id='recipesEDS'>
<% for(var i=0; i < recipes.length; i++){ %>
<lix%=recipes[i] .name %></li>

<%} %>
</script>

:
JC'tfoo").html('recipesEDS', recipeData)

, id ,
.

$.View
.
$. View ( , ) . $.View ,
, :
var html = J.ViewC'template/items.ejs", items );

.
(items.ejs).
template/items.ejs ,
template/item.ejs:
<% for( var i = 0; i < this.length; i++){ %>
<li>
<%= J.ViewC'template/item.ejs", this[i]);
</li>

< % } %>
th is , . template/
items.ejs th is . template/item.ejs th is
.

D eferred-
Ajax-
-. Task
, $ .Model,
:
Task.findAll({}, function(tasks){
$(,#tasks,).html(,,views/tasks.ejs" , tasks )

236

13. JavaScriptMVC

$ .View deferred- ( http://api.jquery.com/category/deferred4)bjecl/),


,
. , $.View j Query, deferred-, $.View
, , ,
deferred- .
Deferred- findAll, findOne, save d e st
roy. :
$ ( '# ta s k s ') . html("view s/tasks.ejs" , Task.findA ll() )
deferred-:
$('#') .htm l("views/app.ejs" , {

tasks: Task.findA llQ ,


users: U ser.findA ll()

,

$. View , , :
s c rip t-;
JavaScript;
.
JavaScriptMVC s c rip t- .
s c rip t-:
JavaScript-.
,
.
JavaScriptMVC
JavaScript- ,
, .
JavaScriptMVC StealJS
, .
s te a l, views (PATH, . . . ) :
steal.v iew s( 't a s k s .e j s ', 't a s k . e j s ');
, $.View ,
, -, Ajax.
, ,
jQuery.get.
URL dataType 'view'

(.Controller: jQuery

237

(
):
$(window).load(function(){
setT imeout(function(){
$.get('users.ejs',function(){},'view');
$.get('user.ejs',function(){},'view');
>,500)

$.Controller:
jQuery
JavaScriptMVC- .

jQuery. ,

. ,
. ,

.
,
:

jQuery;
;
, ;
.


JavaScript.
, ,
, :
$.fn.tooltip = function(){
var el = this[0];
{(document).click(function(ev){
if (ev.target !== el)
$(el).remove();

>);
$(el).show();
return this;

>);

238

13. JavaScriptMVC

, ,
, tooltip:
$(H<div class='tooltip'> <^1>")
.appendTo(document.body)
.tooltip()

. , ?
: ,
?
, !

. ,
.
, j Query
, ,
.
, ,
.
, -- (MVC),
, .
. , nextPrev
$ .Model paginate:
paginate.bind('updated.attr', function(){
self.find('.prev')[this.canPrev() ? 'addClass1 :
'removeClass']('enabled')
self.find('.next')[this.canNext() ? 'addClass' :
'removeClass']('enabled');

paginate! , ,
, .
tooltip, nextPrev .
. , $. Controller
.
tooltip:
$.Controller('Tooltip',{
init: function(){
this.element.show()

"{document} click": functional, ev){


if(ev.target !== this.element[0]){
this.element.remove()

>
>

(.Controller: jQuery

239

DOM, $. Control
ler .
$ . Controller nextPrev,
Paginate:
$.Controller('Nextprev',{
".next click" : function(){
var paginate = this.options.paginate;
paginate.attr('offset', paginate.offset+paginate.limit);

b
".prev click" : function(){
var paginate = this.options.paginate;
paginate.attr('offset', paginate.offset-paginate.limit );

b
"{paginate} updated.attr" : function(ev, paginate){
this.find('.prev')[paginate.canPrev() ? 'addClass' :
'removeClass']('enabled')
this.find('.next')[paginate.canNext() ? 'addClass' :
'removeClass']('enabled');

>

// nextprev
$('#pagebuttons').nextprev({ paginate: new Paginate() })

#pagebuttons , NextPrev Paginate.


, , , ,
, $ . Con
troller.


$ . Controller $ . Class.
$. Controller (, , )
, .
:
$.Controller("List", {
defaults : {}

init : function(){ },
"li click" : function(){ }

- jQuery,
. , ,
, .

240

13. JavaScriptMVC

, . ,
$. Controller ( 'App.FooBar') $(el) .app_foo_bar().


new t roller (, _), HTML-
, j Query, ,
. :
new List($('ul#tasks'), {model : Task});

List #tasks
- j Query:
$('ul#tasks').list({model : Task})

init
.
this.element

HTML-, jQuery.
this.options

, ,
defaults.
List
,
.
$.Controller("List", {
defaults : {
template: "items.ej s"

}
L {
init : function(){
this.element.html( this.options.template, this.options.model.findAll()

);
b
"li click" : function(){ }

;
List
. ,
!
$('#tasks').list({model: Task, template: "tasks.ejs"});
$('#users').list({model: User, template: "users.ejs"})

, List items.ejs.

$.Controller: jQuery

241


$. Controller,

.
.
, ,
"li click". jQuery.bind jQuery.
delegate.
,
, .
, .
"li click"

li
.
"mousemove"

.
"{window} click"

.
,
jQuery, , , .
:
"li click": function( el, ev ) {
assertEqual(el[0].nodeName, "li" )
assertEqual(ev.type, "click")

>


$. Controller .
,
.
, {}, ,
.
,
.
$.Controller("Menu",{
"li {openEvent}" : function(){
//

>
;

242

13. JavaScriptMVC

// , (click)
$("#clickMenu").menu({openEvent: 'click'});
// ,
// ( mouseenter)

$ ("#hoverMenu" ) .menu({openEvent: ' mouseenter'} );


,
menu:
$.Controller("Menu",{
defaults : {menuTag : "li"}

"{menuTag} {openEvent}" : function(){


//

>
;
$("#divMenu").menu({menuTag : "div"})


. , Task
$ . Model created Task.
,
:
$.Controller("List", {
defaults : {
template: "items.ejs"

>

init : function(){
this.element.html( this.options.template, this.options.model.findAll() );
"{Task} created" : function(Task, ev, newTask){
this.element.append(this.options.template, [newTask])

}
})
"{Task} created" Task,
Task.
( )
HTML .
List .

:
$.Controller("List", {
defaults : {
template: "items.ejs",

: CRUD-

243

model: null

>

init

: function(){
this.element.html( this.options.template, this.options.model.findAll()

>;
b

"{model} created" : function(Model, ev, newltem){


this.element.append(this.options.template, [newltem])

}
});
//
$('#tasks').list({model: Task, template: "tasks.ejs"});

:
CRUD-
,
, .
:
"{model} updated" : function(Model, ev, updatedltem){
// LI updatedltem

"{model} destroyed" : function(Model, ev, destroyedItem){


// LI destroyedltem

}
. -
, .
. ,
$ . Model $.View

.

EJS-,
. tasks.ejs:
<% for(var i =0 ; i < this.length; i++){ %>
<% var task = thisfi]; %>
<li <%= task %> > <%= task.name %> </li>

<% } %>
tasks.ejs .
(task) li . ,
<lix%=task %></li> jQuery- .

244

13. JavaScriptMVC


modelInstance.elements([CONTEXT]).
, jQuery, .
, :
$.Controller("List", {
defaults : {
template: "items.ejs",
model: null

>

init : function(){
this.element.html( this.options.template, this.options.model.findAll()

);
b

"{model} created" : function(Model, ev, newltem){


this.element.append(this.options.template, [newItem])

"{model} updated" : function(Model, ev, updatedltem){


updatedltem.elements(this.element)
.replaceWith(this.options.template, [updatedltem])

"{model} destroyed" : function(Model, ev, destroyedItem){


destroyedltem.elements(this.element)
.remove()

>
;
//
$('#tasks').list({model: Task, template: "tasks.ejs"});

, JavaScriptMVC
, .

jQuery

DOM ,
j Query.
: j Query API,
,
. , j Query
.
, JavaScript
.
j Query jQuery,
($). , Prototype,
j Query - JavaScript,

.
j Query .
CSS, .
j Query , ,
. ,
j Query,
.
,
selected foo.
JavaScript, j Query:
// JavaScript
var elements = document.getElementsByClassName("foo");
for (var i=0; i < elements.length; i++) {
elementsfi].className += " selected";

>
// jQuery
$(".foo").addClass("selected");

246

. jQuery

, , API jQuery
. .
ID CSS (#),
jQuery:
// ID (wem)
var element = document.getElementById("wem");
var element = $("#wem");
// (bar)
var elements = document.getElementsByClassName("bar");
var elements = $(".bar");
// ()
var elements = document.getElementsByTagName("p");
var elements = $("p");

CSS,
:
// , 'foo' 'bar'
var foo = $(".bar .foo");

:
var username = $("input[name='username']");

:
var example = $(".wemifirst");

,
:
// 'foo'
$(".foo").addClass("bar");

, jQuery
, DOM, :
// !
var element = document.getElementByld("wem");
element.addClass("bar");

, API jQuery,
jQuery:
var element = document.getElementById("wem");
{(element).addClass("bar");

DOM-

247

DOM-
j Query
, :
var wem = $("#wem"); '
//
wem.find(".test");
//
wem.parent();
//

//
wem.parents(".optionalSelector");
// ( )
wem.children();

:
var wem = $("#wem");
// (0)
wem.eq( 0 );
// ( $.fn.eq(0))
wem.first();
// ,
(".foo")
wem.filter(".foo");
// ,
//
wem.filter(function(){
// this
return $(this).hasClass(".foo");

});
// , ,

// (".selected")
wem.has(".selected");

248

. jQuery

j Query () each(),
:
var wem = $("#wem");
// ,
//
wem.map(function(element, index){
assertEqual(this, element);
return this.id;

}>;
// ,
// 'for'.
wem.each(function(index, element){
assertEqual(this, element);

/*...*/
;
:
//
var wem = $("#wem");
wem.add( $("p") );

DOM
Ho j Query , API
DOM. j Query
HTML-,
:
var element = $("");
element.addClass("bar")
element.text(" ");

DOM ,
: append() prepend().
,
DOM:
//
var newDiv = $("<div />");
$("body").append(newDiv);
//
$(".container").prepend($("<hr />"));

DOM

249

:
//
$(".container").after( $("< />") );
//
$(".container").before( $("< />") );

:
//
$("wem").remove();

? j Query
. , ,
addClass():
$("#f00 ").addClass("bar");
//
$("#f").removeClass("bar");
// ?
var hasBar = $("#foo").hasClass("bar");

CSS. css ()
, ,
:
var getColor = $(".foo").css("color");
//
$(".f").ss("lo", "#000");
//
$(".foo").css({color: "#000", backgroundColor: "#FFF"});

j Query
:
// display ,
$(".bar").hide();
// display block,
$(".bar").show();

, :
$(".foo").fadeOut();
$(".foo").fadeln();

250

. jQuery

j Query - - CSS
. , , ,
html():
// HTML
var getHTML = $(#bar).html();
// HTML
$(#bar").html("<p>Hi</p>");

text(), :
var getText = $("#bar").text();
$("#bar).text(", ");

, , empty ():
$("#bar").empty();

,
API. j Query ,
API.
j Query,
2 (http://api.jquery.com/category/events).
, bindQ,
:
$(".clicky").bind("click", function(event){
//

});
j Query ,
bind() , , :
$(".clicky").click(function(){ /* ... */ });

, , , , document.
ready. , DOM ,
, , . j Query
:
jQuery:
jQuery(function($){
// document.ready

});
j Query
. ,

Ajax

251

,
$(". clicky"):
$(".clicky").click(function(){
// 'this'
assert( $(this).hasGlass(".clicky") );

});
this
.
, self:
var self = this;
$(".clicky").click(function(){
self.clickedClick();

;
, -,
jQuery.proxy():
*(".clicky").click($.proxy(function(){
// Context isn't changed
L this));

2.

Ajax
Ajax, XMLHttpRequest ,
. jQuery
,
API. jQuery Ajax API 3,
.
jQuery ajax()
. ajax() ,
URL,
(success):
$.ajax({
url: "http://example.com",
type: "GET",
success: function(){ / * . . . * / }

});
jQuery API :
$.get("http://example.com", function(){ /* *./ })
$.post("http://example.com", {some: "data"});

252

. jQuery

j Query dataType j Query,


Ajax-. , j Query
, . ,
, :
// 3S0N
$.ajax({
url: "http://example.com/endpoint.jso",
type: "GET",
dataType: "json",
success: function(json){ / * . . . * / }

;
j Query
, getDSONQ,
ajax():
$.get3S0N("http://example,com/endpoint.json", function(json){ / * . . * / });

j Query Ajax API


3, .

j Query , -.

. j Query
$, ,
Prototype. , ,
jQuery noConflict
$:
var $3 = jQuery.noConflict();
assertEqual( $, undefined );

j Query- ,
j Query $ jQuery.
$ ,
:
(function($){
// $
$(".foo").addClass("wem");
})(jQuery);

253

, j Query
document.ready:
jQuery(function($){
//
assertEqual( $, jQuery );

});

j Query, , .
,
jQuery:
jQuery.myExt = function(argl){ /*...*/ };
// ,
$.myExt("anyArgs");

,
, jQuery.fn,
jQuery.prototype.
, (. . this),
:
jQuery.fn.wemExt = function(argl){
$(this).html("Bar");
return this;

};
$("#element").wemExt(1).addClass("foo");


,
. ,
:
(function($){
//
var replaceLinks = function(){
var re =
/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g;
$(this).html(
$(this).htmlQ.replace(re, '<a target="_blank" href="$l">$l</a> ')

)i

};
$.fn.autolink = function() {
return this.each(replaceLinks);

};

})(jQuery);

254

. jQuery


jQuery Growl
jQuery Growl.
, Growl, Mac OS X,

. OS X
JavaScript ,
. .1.

There s no pleasing some


people*

That s it

I m on strike!

Lorem ipsum dolor sit amet.


consectetur adipiscing eht.

. .1. Growl-

div- #container,
. ,
, jQuery, jQuery UI,
. ,
div-:
//= require <jquery>
//= require <jquery.ui>
(function($){
var container = $("<div />");
container.attr({id: "growl"});
$(function(){
// div
$("body").append(container);

});
/*...*/
}) (jQuery);

.
, div container.

jQuery Growl

255

(drop), , - ,
, Growl OS X:
$ .growl = function(body){
// div- Growl
var msg = $("<div />,").addClass("msg");
msg.html(body);
//
container.append(msg);
//
msg.show("drop", {
direction: "down",
distance: 50
300). .
delay(2000).
fadeOut(300, function(){
$(this).remove();

});
return msg;
>;
JavaScript.
, CSS3.
div- #container
:
#growl {
position: absolute;
bottom: 10px;
right: 20px;
overflow: hidden;

}
.
HUD Growl, .
, rgba, (inset
box-shadow), :
#growl .msg {
width: 200;
min-height: 30;
padding: 10;
margin-bottom: 10;
border: lpx solid #171717;
color: #E4E4E4;
text-shadow: 0 -lpx lpx #0A131A;

&

256

. jQuery

font-weight: bold;
font-size: 15px;
background: #141517;
background: -webkit-gradient(
linear, left top, left bottom,
from(rgba(255, 255, 255, 0.3)),
color-stop(0.8, rgba(255, 255, 255, 0))),
rgba(0, 0, 0, 0.8);
-webkit-box-shadow: inset 0 lpx lpx #8E8E8E;
-moz-box-shadow: inset 0 lpx lpx #8E8E8E;
box-shadow: inset 0 lpx lpx #8E8E8E;
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
border-radius: 7px;

}
. , j Query
. ,
assets/appA/growI.html.

CSS-

, (Alexis Sellier), Less (http://lesscss.org)


,
CSS. Less CSS,
, , .

CSS, CSS3,
. Less-
CSS.
, :
.panel {
background: #;
background: -webkit-gradient(linear, left top, left bottom, from(#FFF),
to(#CCC));
background: -moz-linear-gradient(top, #FFF, #CCC);

}
:
.panel {
.vbg-gradient(#FFF, #);

,
Less- ,
.
:
@panel-color: #;

:
header {
color: @panel-color;

258

. CSS-

Less- . ,
,
:
.vbg-gradient(@f : #FFF, @tc: #) {
background: @fc;
background: -webkit-gradient(linear, left top, left bottom, from(fc),
to(@tc));
background: -moz-linear-gradient(top, fc, @tc);
background: linear-gradient(top, fc, @tc);

>
: f tc,
#FFF # .
. ,
.
CSS3 ,
, , ,
-webkit -moz. , , ,
;

.
, , , Less
, ,
, .
, :
/* */
.border-radius(@r: ) {
-moz-border-radius: @;
-webkit-border-radius: @;
border-radius: @;

}
/* */
.box-shadow (@h: 0px, @v: 0px, @b: 4, @: #333) {
-moz-box-shadow: @h @v @b @c;
-webkit-box-shadow: @h @v @b @c;
box-shadow: @h @v @b @c;

259

,
:
button {
.border-radius();
.box-shadow(0, lpx,- lpx, #FFF);
.vbg-gradient(#F9F9F9, #E3E3E3);
:active {
.vbg-gradient(#E3E3E3, #F9F9F9);

}
}
:
,
, .


,
, ^import
. Less
, ,
-.
. ,
CSS3, :
^import "utils";

Less ,
, , . Less
, :
background:
background:
background:
background:

saturate(#319, 10%);
desaturate(#319, 10%);
darken(#319, 10%);
lighten(#319, 10%)

,
. ,
, .

Less?
Less CSS.

260

. CSS-


gem- Less, lessc:
gem install less
lessc sty^.less

Rack
Rack-, Rails 3,
: gem- rack-less.
gem- Gemfile:
gem "rack-less"

application.rb:
require "rack/less"
config.middleware.use "Rack::Less"

Less, //stylesheets,
. ,
rack-less production.rb:
Rack::Less.configure do |config|
config.cache * true
config.compress = :yui
end

JavaScript
, , Ruby-, , ,
: Xess.js, Less,
JavaScript. Less
JavaScript- less.js,
:
<link rel="stylesheet/less" href="main.less" type="text/css">
<script src="less.js" type="text/javascript"x/script>

Less.js 40 Ruby- .
Less,
. Node.js,
:
node bin/lessc style.less

Less.app
, Mac OS X,
Less. Less.js ,

Less?

261

, . . Less
CSS (. .1).

f i i 1

fiB MyWbrte

| Pul Thurro<

John Cnitar

* Zulu

A O utp u t

/Th* Yankces.lss

SuckJess
OwtjMft

/* Co*

. .1. Less- Less.app

CSS3

CSS2.1 ,
,
JavaScript-. CSS3
-
, .
- Photoshop
CSS3 HTML5. ,
, PSD- .
,
HTML- ,
.
: ?
, CSS3 ?
.
CSSS-, . , Chrome
,
, IE7 , ,
, .
Internet Explorer 6,
, Facebook, Amazon Google,
IE6
.
IE, Firefox, Chrome Safari. Chrome Safari
JavaScript,
WebKit.
, -
Chrome WebKit, .
Microsoft IE9. ,
,
, ,
CSS3.

263

,

.

CSS3
. , ,
CSS3-cm ra . ,
CSS3 Firefox Safari : Firefox
-moz-linear-gradient, a Safari (WebKit) -webkit-gradient;
, .
:

O
O

Chrome: -webkitSafari: -webkitFirefox: -mozIE: -msOpera: -o-

- ,
. ,
CSSS-,
, :
#prefix-example {
-moz-box-shadow: 0 5 #FFF;
-webkit-box-shadow: 0 5px #FFF;
box-shadow: 0 5px #FFF;

CSS3 ,
- (alpha transparency).

1 x 1 ,
.
Rgb- ,
, , .
- Safari
, Styles.

264

. CSS3


#FFF, :
#rgb-example {
//
(, , );
color: rgb(255, 255, 255);

>
h sl-, (hue),
(saturation) (lightness).
HSL :
Hue
; 0 ( 360) , 120 , 240
. .
Saturation
; 100% .
Lightness
; 0% () , 100% (
) , 50% .
rgb hsl - ,
, , rgba hsla. - 0 () 1 ().
#alpha-example {
background: hsla(324, 100%, 50%, .5);
border: lem solid rgba(0, 0, 0, .3);
color: rgba(255, 255, 255, .8);

>
:

Firefox:
Chrome:
Opera:
Safari:
IE:
Opera:


CSS2.1 ,
, JavaScript-.
borderradius. padding margin,

265

, , ,
,
. ,
:
border-radius: 20;
// ,
border-radius: 20 20;
// , , ,
border-radius: 20 20 20 20;

Firefox:
Chrome:
Safari: -webkitIE >= 9.0:
Opera:


CSS3 ,
. CSS3
box-shadow, .
, ,
.
box-shadow : ,
, ,
. inset
, ,
. ,
, :
// , , ,
box-shadow: 10 5 15 #000;
//
box-shadow: 10 5 15 #000 inset;
// , , ,
// ,
box-shadow: 10 5 15 15 #000;
//
box-shadow: 0 lpx lpx #FFF inset, 5px 5px 10px #000;

266

. CSS3

,
.
box-shadow
.
; :
#shadow-example {
-moz-box-shadow: 0 lpx lpx #FFF inset;
-webkit-box-shadow: 0 lpx lpx #FFF inset;
box-shadow: 0 lpx lpx #FFF inset;

}
:

Firefox:
Chrome: -webkitSafari: -webkitIE >= 9.0:
Opera:


CSS3
, . CSS3
text-shadow.
, ,
:
// , ,
text-shadow: lpx lpx #FFF;
// , , ,
text-shadow: lpx lpx . rgba(255, 255, 255, .8);

(box-shadow),
.

.
, . ,
, .
:

Firefox:
Chrome:
Safari:
IE:
Opera:

267

.
, ,
.
CSS3 ,
.
CSS-,
, .
lineargradient , :
linear-gradient(#CCC, #DDD, white)

,
, :
//
linear-gradient(left, #, #DDD, #FFF);
//
linear-gradient(-45deg , # , #FFF)

,
.
:
linear-gradient(white , #DDD 20% , black)

:
radial-gradient( rgba(255, 255, 255, .8) , transparent )

Safari .
,
:
// -webkit-gradient(<Twi>, <> [, <>]?, <> [, <>]?
// [, <>]*);
-webkit-gradient(linear, left top, left bottom,
from(#FFF), color-stop(10%, #DDD), to(#CCC));

CSS- ,
:

Firefox: -mozChrome: -webkitSafari:


IE >= 10: -msOpera >= 11.1: --

268

. CSS3

:
#gradient-example {
/* */
background: #FFF;
/* Chrome < 10, Safari < 5.1 */
background: -webkit-gradient(linear, left top, left bottom,
from(#FFF), to(#CCC));
/* Chrome >= 10, Safari >= 5.1 */
background: -webkit-linear-gradient(#FFF, #CCC);
/* Firefox >= 3.6 */
background: -moz-linear-gradient(#FFF, #CCC);
/* Opera >* 11.1 */
background: -o-linear-gradient(#FFF, #CCC);
/* IE >= 10 */
background: -ms-linear-gradient(#FFF, #CCC);
/* */
background: linear-gradient(#FFF, #CCC);

}
!
, Less Sass, .


CSS3 , . ,

, . .
. CSS3
, :
background: url(snowflakes.png) top repeat-x,
url(chimney.png) bottom no-repeat,
-moz-linear-gradient(white, #CCC),
#CCC;

Firefox:
Chrome:
Safari:
IE >* 9.0:
Opera:

CSS3 :

269

:first-child


:last-child


:only-child


.target

, URL
:checked


,
.

N-
: nth -child
n- . ,
:
#example:nth-child( ) { / * . . . * / }


:
/* */
#example:nth-child( 2 )
{/*...*/}
#example:nth-child( even ) { / * . . . * / }
/* */
#example:nth-child( 2n+l ) { / * . . . * / }
#example:nth-child( odd ) { / * . . . * / }

:
/* */
#example:nth-last-child( 1 )

:first-child :nth-child(l),
a :last-child :nth-last-child(l).


,
,
(>):
/* div-, */
#example > div { }

270

. CSS3


,
: not, .
, :not (hi + ), :
/* , , class
"current" */
#example > *:not(.current) {
display: none

>
:

Firefox:
Chrome:
Safari:
IE >= 9.0:
Opera:

CSS3 ,
. ,
.
(s) (ms):
/* , , () */
transition: 1.5s opacity ease-out
/* */
transition: 2s opacity , .5s height ease-in
transition: .5s height , .5s .5s width

(,
), .

(timing functions):

linear
ease-in
ease-out
ease-in-out


, ,
, , .

271

#transition-example {
position: absolute;

/* cubic-bezier(xl, y l, x2, y2) */


transition: 5s left cubic-bezier(0.0, 0.35, .5, 1.3);

>
Safari Chrome, ,
WebKitTransitionEvent. Firefox
transitionend. , -
:
.
,
(, Safari) :
#transition-example {
width: 50;
height: 50;
background: red;
-webkit-transition: 2s background ease-in-out;
-moz-transition: 2s background ease-in-out;
-o-transition: 2s background ease-in-out;
transition: 2s background ease-in-out;

>
#transition-example:hover {
background: blue;

>

, - . ,
height:0 height:auto.
:

Firefox: -mozChrome: -webkit^


Safari: -webkitIE >= 10.0: -msOpera: --


border-image
. URL ,
.
,

272

. CSS3

.
round ( ), repeat
() stretch ():
border-image: url(border.png) 14 14 14 14 round round;
border-image: url(border.png) 14 14 14 14 stretch stretch;

:
Firefox: -moz Chrome: -webkit Safari: -webkit IE:
Opera: --



- 100% ,
?
CSS ,
. ,
100%, , ,
.
box-sizing border-box
content-box,
, , , :
.border-box {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;

>
,
, ,
, Internet Explorer 8.

CSS3, 2-,
, , .
, 30 :
transform: rotate( -30deg );

273

,
:
transform: skew( 30deg , -10deg );


translateX translated
translateX(30px);
translateY(50opx);


. 1:
transform: scale(1.2);

, :
trartsform: rotate(30deg) skewX(30deg);

Firefox: -mozChrome: -webkitSafari: -webkitIE >= 9: -msOpera: --


CSS3 (flexible box model),
.
CSS ,
GUI-, Adobe Flex. ,
flo ats.
, .
:
.hbox {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: stretch;
-webkit-box-pack: left;
display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-align: stretch;
-moz-box-pack: left;

&

274

. CSS3

.vbox {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: stretch;
display: -moz-box;
-moz-box-orient: vertical;
-moz-box-align: stretch;

>
display -webkit-box -moz-box,
.
,
. ,
box-flex.

box-flex 0, ,
, 1 , ,
, . ,
flex 0,
flex 1:
#sidebar {
-webkit-box-flex: 0;
-moz-box-flex: 0;
box-flex: 0;
width: 200px;

>
#content {
-webkit-box-flex: 1;
-moz-box-flex: 1;
box-flex: 1;

>
:

Firefox: -mozChrome: -webkitSafari: -webkitIE >= 9: -msOpera:

@font-face
. ,
, .

275'

TrueType .
,
, .
@font-face , ,
font-family URL- :
@font-face {
font-family: "Bitstream Vera Serif Bold";
src: url("/fonts/VeraSeBd.ttf");

>
, :
#font-example {
font-family: "Bitstream Vera Serif Bold";

>

. , ,
, ,
. -
, .
:

Firefox:
Chrome:
Safari:
IE >= 9:
Opera:


CSS
. , CSS3,
.
,
, , CSS-,
. CSS- , -
, .
, CSS2.1,
, rgba:
#example-gd {
background: white;
background: rgba(255, 255, 255, .75);

276

. CSS3

? .
,
, .
, ,
CSS3 :
#example-gd {
background: #FFF;
background: -webkit-gradient(linear, left top, left bottom,
from(#FFF), to(#CCC));
background: -webkit-linear-gradient(#FFF, #CCC);
background: -moz-linear-gradient(#FFF, #CCC);
background: linear-gradient(#FFF, #CCC);

>

M odernizr
Modernizr (http://www.modemizr.com)
CSS3-CB0iicTB,
:
multiplebgs div {
/* ,
*/

.no-multiplebgs di vp {
/*
, */

Modernizr :
@font-face
rgba()
hsla()
border-image:
border-radius:
box-shadow:
text-shadow:

(Multiple backgrounds)
(Flexible box model)
CSS-
CSS-
CSS 2-
CSS-

Modernizr
(http://www.modemizr.com).

277

Modemizr , JavaScript
<html>-Tery c la ss- no-js:
<script src= "/jav ascrip ts/m o d ern izr.js"x /scrip t>
<html class="no-js">
Modernizr <html>-Tery,

:
/*
*/
.no-flexbox #content {
float: left;

>

Google Chrom e Fram e


Google Chrome Frame (GCF) Internet Explorer,
IE Google Chrome
Chromium (http://www.chromium.org).
GCF
meta-Tera :
<meta http-equiv="X-UA-Compatible" content="chrome=l">

- :
X-UA-Compatible: chrome=l

, GCF- -
. GCF ,
, IE (
).
GCF,
.
JavaScript- GCF:
<script src="http://jax.googleapis.com/ajax/libs/chrome-f/1/
CFInstall.min.js"


CFInstall:
<script>
jQuery(function(){
CFInstall.check({
mode: "overlay",

;
;
</script>

278

. CSS3

CFInstall :
mode

(inline), (overlay) (popup)


destination

,
node

ID ,

GCF User-Agent
chromef rame. URL- GCF
Internet Explorer . ,
GCF cookie-, SSL-, ,
, .

(http://www.chromium.org/developers/how-tos/chrome-framegetting-started).



, Holla.
.

sidebar main:
<body>
<header id="title">
<hl>Holla</hl>
</header>
<div id="content">
<div class="sidebar"x/div>
<div class="main"x/div>
</div>
</body>


(body):
body, html {
margin: 0;
padding: 0;

>
body {
font-family: Helvetica, Arial, "MS Trebuchet", sans-serif;
font-size: 16px;

279

color: #363636;
background: #D2D2D2;
line-height: 1.2em;

>
h-:
hi, h2 {
font-weight: bold;
text-shadow: 0 lpx lpx #ffffff;

>
hi {
font-size: 21pt;
color: #404040;

>
h2 {
font-size: 24pt;
color: #404040;
margin: lem 0 0.7em 0;

}
h3 {
font-size: 15px;
color: #404040;
text-shadow: 0 lpx lpx #ffffff;

>
.
CSS3,
, :
#title {
border-bottom: lpx solid #535353;
overflow: hidden;
height: 50px;
line-height: 50px;
background: #575859;
background: -webkit-gradient(linear, left top, left bottom,
from(#575859), to(#272425));
background: -webkit-linear-gradient(top, #575859, #272425);
background: -moz-linear-gradient(top, #575859, #272425);
background: linear-gradient(top, #575859, #272425);

>
#title hi {
color: #ffffff;
text-shadow: 0 lpx lpx #000000;
margin: 0 10px;

>

280

. CSS3

, , . .1,
.
CSS3 Example

* J

J + l file:///Users/Alex/book/assets/css3 html

ifC fr Googte

li

H olla

I
|

. .1. CSS

div- #content,
. ,
, .
,
(flexible box):
#content {
overflow: hidden;

/*
div- ,
.

*/
position: absolute;
left: 0;
right: 0;
top: 50px;
bottom: 0;
/* */
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: stretch;
-webkit-box-pack: left;
display: -moz-box;

281

-moz-box-orient: horizontal;
-moz-box-align: stretch;
-moz-box-pack: left;

>
.sidebar.
, box-flex 0:
#content .sidebar {
background: #EDEDED;
width: 200px;

/*
,

*/
-webkit-box-flex: 0;
-moz-box-flex: 0;
box-flex: 0;

>
.sidebar .
, h3. ,
CSS3, -
. ,
Less-:
#content .sidebar ul {
margin: 0;
padding: 0;
list-style: none;

}
#content .sidebar ul li {
display: block;
padding: 10px 10px 7px 20px;
border-bottom: lpx solid #cdcdcc;
cursor: pointer;
-moz-box-shadow: 0 lpx lpx #fcfcfc;
-webkit-box-shadow: 0 lpx lpx #fcfcfc;
box-shadow: 0 lpx lpx #fcfcfc;

>
#content .sidebar ul li.active {
color: #ffffff;
text-shadow: 0 lpx lpx #46677f;
-webkit-box-shadow: none;
-moz-box-shadow: none;
background: #7bb5db;
background: -webkit-gradient(linear, left top, left bottom,
from(#7bb5db), to(#4775b8));

282

. CSS3

background: -webkit-linear-gradient(top, #7bb5db, #4775b8);


background: -moz-linear-gradient(top, #7bb5db, #4775b8);
background: linear-gradient(top, #7bb5db, #4775b8);

>
HTML- :
<div class=,,sidebar,,>
<h3>Channels</h3>
<ul>
<li class="active">Developers</li>
<li>Sales</li>
<li>Marketing</li>
<li>Ops</li>
</ul>
</div>

CSS div- .main,


:
#content .main {
-moz-box-shadow: inset 0 lpx #7f7f7f;
-webkit-box-shadow: inset 0 lpx 3px #7f7f7f;
box-shadow: inset 0 lpx 3px #7f7f7f;
/* , .main */
-webkit-box-flex: 1;
-moz-box-flex: 1;
box-flex: 1;

>
. . .2,
, .
CSS3 Example
[ < j

j + J * file:///Users/A!ex/book/assets/css3.html

O r Coogle

H o lla
Channels

Sales
Marketing
Ops

. .2.

283

, CSS3
- .
Less-. :
#content .sidebar h3 {
.vbg-gradient(#FFF, #DEDFE0);
.box-shadow(0, -5px, 10px, #E4E4E4);

>

Holla.


- JavaScript

.
.
.
.
.
.
.

, 198206, -, , 73, . 29.


005-93, 2; 95 3005 .
20.04.12. 70x100/16. . . . 23,220. 2000. 339.
CtP 1111 .
180004, , . , 34.