Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2da330561c | ||
|
|
5ae3117659 | ||
|
|
b4a6a429e7 | ||
|
|
e1ddfdec1d | ||
|
|
8b6da4e9ee | ||
|
|
a4c6184a57 | ||
|
|
0744983c5b | ||
|
|
e7bb750482 | ||
|
|
0e11e42f44 | ||
|
|
b811797b6f | ||
|
|
aa6c3cab49 | ||
|
|
2b2f66899d | ||
|
|
ffe7afb738 | ||
|
|
cf6e007056 | ||
|
|
beb102cca5 | ||
|
|
2c1b0d6985 | ||
|
|
8f03a152bc | ||
|
|
fc719cc110 | ||
|
|
22fefbe149 | ||
|
|
65b67d335c | ||
|
|
921cde96ed | ||
|
|
4f21d616e1 | ||
|
|
65ac129305 | ||
|
|
f8e65fe938 | ||
|
|
ba32956bd1 | ||
|
|
d01217658d | ||
|
|
10eef18238 | ||
|
|
19bc78dc51 | ||
|
|
34d82e4027 | ||
|
|
cb6d7c346c | ||
|
|
c4233d3ce6 | ||
|
|
606f3fff45 | ||
|
|
b35256ac11 | ||
|
|
41388efd14 | ||
|
|
37aa2445cb | ||
|
|
09ccd1a267 | ||
|
|
070d054f8e | ||
|
|
d76889c46f | ||
|
|
69eb380110 | ||
|
|
178a7acc2d | ||
|
|
b10c8bede8 | ||
|
|
201df6fb02 | ||
|
|
74afc0e04d | ||
|
|
dcd41101ff | ||
|
|
a7b012f314 | ||
|
|
50af7e8dae | ||
|
|
4035435bdf | ||
|
|
80982b7e42 | ||
|
|
f87bf1b3d9 | ||
|
|
021d7f09e5 | ||
|
|
9371c0cfd1 | ||
|
|
03ee056063 | ||
|
|
879cf05d56 | ||
|
|
cd0b268529 | ||
|
|
410a08201b | ||
|
|
c8c8d4f74a | ||
|
|
a9b611ddcb | ||
|
|
0588d53248 | ||
|
|
ef1a20a479 | ||
|
|
3a43fd24e4 | ||
|
|
02c90c59f9 | ||
|
|
dbdda9efaf | ||
|
|
137a014fdf | ||
|
|
fdc90557c1 | ||
|
|
ce18c48ef6 | ||
|
|
38f63450f5 | ||
|
|
78c79dd810 | ||
|
|
2ab33d10d8 | ||
|
|
c2aef302e7 | ||
|
|
e9a5b213e5 | ||
|
|
163d3f2076 | ||
|
|
d4f82339c2 | ||
|
|
6654c20234 | ||
|
|
4390c88744 | ||
|
|
352a84a4b0 | ||
|
|
8ba6439ab9 | ||
|
|
8b34da2553 | ||
|
|
e4edcca09a | ||
|
|
688edda4db | ||
|
|
c7aa18be51 | ||
|
|
70a4c0a37d | ||
|
|
6ebddea01e | ||
|
|
f629a723d5 | ||
|
|
e8131dc766 | ||
|
|
beac00e65b | ||
|
|
09b36389e8 | ||
|
|
2d3976f524 | ||
|
|
1cb6fccc24 | ||
|
|
6bb4035fb3 | ||
|
|
ca5a3edf03 | ||
|
|
3efdef50e9 | ||
|
|
2fe6114fcc | ||
|
|
e5daf4d111 | ||
|
|
8fdfe690a8 | ||
|
|
c9942d8425 | ||
|
|
e6a399699d | ||
|
|
2e242e28ec | ||
|
|
06a9f053cf | ||
|
|
1683952344 | ||
|
|
a5f865d7a2 | ||
|
|
0f1214852e | ||
|
|
c4d0f8ee97 | ||
|
|
7e068490fb | ||
|
|
188d44d313 | ||
|
|
40bb604810 | ||
|
|
d62ff85341 | ||
|
|
85621f5edd | ||
|
|
fcab4ae568 | ||
|
|
92d39cf7c9 | ||
|
|
0d1e308799 | ||
|
|
e36ed18d4d | ||
|
|
3a0f05948d | ||
|
|
5658c958cc | ||
|
|
05cb6c116d | ||
|
|
8467a1e4e5 | ||
|
|
d258ce4452 | ||
|
|
1a60576404 | ||
|
|
e706bf3db9 | ||
|
|
442d201244 | ||
|
|
1d23f91ecd | ||
|
|
a8b4be8b7b | ||
|
|
45620c6819 | ||
|
|
70ef1d0fcb | ||
|
|
a4d4648c41 | ||
|
|
2803675461 | ||
|
|
f161d52e9b | ||
|
|
f0c05b4156 | ||
|
|
7925caaec8 | ||
|
|
411cd5afc8 | ||
|
|
676b387e5d | ||
|
|
5c8616615b | ||
|
|
0a747284e3 | ||
|
|
ebed0c1e85 | ||
|
|
a97974edb5 | ||
|
|
68d2bce1f8 | ||
|
|
bf1d1abc04 | ||
|
|
d40e3c9e51 | ||
|
|
7f245526bf | ||
|
|
2ed5d29965 | ||
|
|
c032c68eeb | ||
|
|
8809ba9caf | ||
|
|
6220684150 | ||
|
|
295eac02e4 | ||
|
|
bd40bcacba | ||
|
|
e91c6389c9 | ||
|
|
e00b0a1025 | ||
|
|
36fd68cb01 | ||
|
|
10549b0e31 | ||
|
|
f940ba2ccb | ||
|
|
e05dbc1eb2 | ||
|
|
55d3b7d327 | ||
|
|
9a609528f0 | ||
|
|
d5a4e15c18 | ||
|
|
a17b05a557 | ||
|
|
4695104081 | ||
|
|
a083ccb86c | ||
|
|
843d0d285f | ||
|
|
220d34cb73 | ||
|
|
4900cbbedd | ||
|
|
a000e79f22 | ||
|
|
ba4743df96 | ||
|
|
4cb0277c87 | ||
|
|
6c7ce5d421 | ||
|
|
a86c15980e | ||
|
|
c67c206853 | ||
|
|
4b2bf27922 | ||
|
|
8350109e75 | ||
|
|
e519cbce7b | ||
|
|
17688d112f | ||
|
|
6f81bb892e | ||
|
|
00d65a4095 | ||
|
|
b95d2bc013 | ||
|
|
0f7880d550 | ||
|
|
dc82c05e09 | ||
|
|
ecabab5ce9 | ||
|
|
c2caa9deb4 | ||
|
|
fb1eb39bf2 | ||
|
|
e1b0023ba1 | ||
|
|
7488622e26 | ||
|
|
91fece8a49 | ||
|
|
a0519a3fbf | ||
|
|
891831bf81 | ||
|
|
e07136175a | ||
|
|
8b99724c14 | ||
|
|
b91e0eddd6 | ||
|
|
5e187c7993 | ||
|
|
6062411e34 | ||
|
|
7758b557ae | ||
|
|
07a488c06e | ||
|
|
4b1747ac51 | ||
|
|
6ce9f11f5c | ||
|
|
fd7150c766 | ||
|
|
357ca0eabb | ||
|
|
4ddaed818b | ||
|
|
3332932d00 | ||
|
|
0e79690a8f | ||
|
|
7224a12096 | ||
|
|
6683a93caa | ||
|
|
241fbb8f87 | ||
|
|
bc7bb1ac98 | ||
|
|
7bcc9ba898 | ||
|
|
c228da4ab0 | ||
|
|
9519b9ebfe | ||
|
|
9192946247 | ||
|
|
d1243ab6a4 | ||
|
|
bb7dc1f0aa | ||
|
|
d06ef88033 | ||
|
|
53e6ec4cb7 | ||
|
|
429d19459d | ||
|
|
ca103c8e4b | ||
|
|
e3bbc2f762 | ||
|
|
2a68c0f310 | ||
|
|
332d8b739e | ||
|
|
9c00fe6aff | ||
|
|
baccdff96d | ||
|
|
8a8012dd11 | ||
|
|
2c6e65536d | ||
|
|
1bac5af60f | ||
|
|
33386e2986 | ||
|
|
c96c4b86a8 | ||
|
|
87fd3055c1 | ||
|
|
a858b62180 | ||
|
|
219c74b04e | ||
|
|
8cf1b56e2a | ||
|
|
a16e98f60e | ||
|
|
1b7d292a09 | ||
|
|
6c30474036 | ||
|
|
18f8b10644 | ||
|
|
55bf804695 | ||
|
|
e8ff298146 | ||
|
|
111d1c6ad9 | ||
|
|
130bfbbab4 | ||
|
|
3eb2875b30 | ||
|
|
3209ab0d40 | ||
|
|
e55880749f | ||
|
|
ce125ce25b | ||
|
|
f93256dfb5 | ||
|
|
7edc9e671b | ||
|
|
3c544ae11b | ||
|
|
12989debb8 | ||
|
|
db00c74910 | ||
|
|
f911b8b6a9 | ||
|
|
ad7e3ba6bf | ||
|
|
062d126179 | ||
|
|
5a145b75b2 | ||
|
|
725d3bb9dc | ||
|
|
2aab4ed4cc | ||
|
|
4efeb959f9 | ||
|
|
ace78132e4 | ||
|
|
eb2fa6ce7d | ||
|
|
92f25a8338 | ||
|
|
4db526fcae | ||
|
|
dcd3a543bc | ||
|
|
4288a72b45 | ||
|
|
63bd9456dc | ||
|
|
51b616edc6 | ||
|
|
5d8a609740 | ||
|
|
c12198dff8 | ||
|
|
56d510d6e8 | ||
|
|
f989f7a958 | ||
|
|
885e25253a | ||
|
|
786ac7effa | ||
|
|
aac6394ddf | ||
|
|
a8369b74e4 | ||
|
|
5a764587cc | ||
|
|
7f1c8333e1 | ||
|
|
bdb58bc768 | ||
|
|
ba2c6bca9a | ||
|
|
c20d5ffa9e | ||
|
|
a2f9ae1c45 | ||
|
|
a8ac66512e | ||
|
|
31b1004c86 | ||
|
|
d6fff8bc8b | ||
|
|
3206891065 | ||
|
|
e6ee8fd35e | ||
|
|
f21d662654 | ||
|
|
66c7103805 | ||
|
|
9b1fc400f9 | ||
|
|
986518e0dc | ||
|
|
87b4c36fed | ||
|
|
3ee56f29e2 | ||
|
|
02d88c41d7 | ||
|
|
8e553a2a6e | ||
|
|
a9f4532d9d | ||
|
|
85bd738dff | ||
|
|
c79d5f9bdc | ||
|
|
c432106438 | ||
|
|
de4d7acf32 | ||
|
|
4c25021db1 | ||
|
|
324eeff0d8 | ||
|
|
b1bd58361d | ||
|
|
66cb44e830 | ||
|
|
dba56930fd | ||
|
|
f214a1321d | ||
|
|
d5d0dcfcbb | ||
|
|
dde48e417b | ||
|
|
72026641d1 | ||
|
|
531a603d5a | ||
|
|
67d50fe8d9 | ||
|
|
50cc39cdf0 | ||
|
|
2b69e2a344 | ||
|
|
4f62737346 | ||
|
|
46c72a9070 | ||
|
|
62ce7588d5 | ||
|
|
2516eae5ae | ||
|
|
59f1171aa9 | ||
|
|
8279946285 | ||
|
|
ed2aa8627a | ||
|
|
6e8a3331a9 | ||
|
|
005194eb23 | ||
|
|
3d210f275e | ||
|
|
724c259c28 | ||
|
|
4048159b16 | ||
|
|
5fff74a66f | ||
|
|
91514b34b0 | ||
|
|
9a7371f4ed | ||
|
|
2ed4d3ec45 | ||
|
|
20471d960e | ||
|
|
07264740b2 | ||
|
|
8747a8caa3 | ||
|
|
daf6f022be | ||
|
|
c8044c2dbf | ||
|
|
ecfb5016a9 | ||
|
|
b3a920907c | ||
|
|
a53eb58fd6 | ||
|
|
aee4e0c952 | ||
|
|
42e832311c | ||
|
|
7e029d284c | ||
|
|
fb13138350 | ||
|
|
28eaa28a0b | ||
|
|
4660110d3c | ||
|
|
acbd5c71a4 | ||
|
|
3228bf4078 | ||
|
|
210c6de5d0 | ||
|
|
6f4ece5875 | ||
|
|
296295bb01 | ||
|
|
e4d748ad2f | ||
|
|
6ff5f01400 | ||
|
|
d1068913d3 | ||
|
|
05c5914ad3 | ||
|
|
6971809738 | ||
|
|
c07c2ff79c | ||
|
|
d1d39715f1 | ||
|
|
1daf20f2a7 | ||
|
|
2ed04957a4 | ||
|
|
bf167b091e | ||
|
|
c3db4dd097 | ||
|
|
a43bc3ae6e | ||
|
|
3108b62179 | ||
|
|
2c64991b8b | ||
|
|
09368925f1 | ||
|
|
2f6ea35e62 | ||
|
|
590fcf6d2e | ||
|
|
c9f3a19564 | ||
|
|
1a29a24b4c | ||
|
|
66dbfa7136 | ||
|
|
f6e5a71607 | ||
|
|
1b250ae810 | ||
|
|
b789aa8fec | ||
|
|
8e6fa2a5de | ||
|
|
6691d9a820 | ||
|
|
952b5cca37 | ||
|
|
7bb9918df9 | ||
|
|
017680349e | ||
|
|
d0b5482585 | ||
|
|
499ae6edd9 | ||
|
|
0cce97c904 | ||
|
|
afa1344012 | ||
|
|
8c71c7e361 | ||
|
|
cfbe5be9ca | ||
|
|
eb76837657 | ||
|
|
36e90e7487 | ||
|
|
afdac12bd7 | ||
|
|
7e84932d0b | ||
|
|
5ea23f71a7 | ||
|
|
c2d1d46b50 | ||
|
|
52eaf759d0 | ||
|
|
f4dbd23dc4 | ||
|
|
4e4bad04b4 | ||
|
|
92113c03e1 | ||
|
|
aacd8c45dc | ||
|
|
1473676a5a | ||
|
|
43a0e13c2a | ||
|
|
9d11d9b85a | ||
|
|
bb57b11f24 | ||
|
|
0f8f1574ac | ||
|
|
78fef3d989 | ||
|
|
19916d2c1f | ||
|
|
ff0c5e3ad9 | ||
|
|
d34d211b51 | ||
|
|
9c35d68d3c | ||
|
|
253a3fd9c2 | ||
|
|
15143d67c6 | ||
|
|
284d081285 | ||
|
|
c5bf36faee | ||
|
|
2281d5d3c2 | ||
|
|
3bbd9a45e5 | ||
|
|
cec1f5f864 | ||
|
|
ceec8b0e97 | ||
|
|
350656513d | ||
|
|
f1bf3bca44 | ||
|
|
d7bea27b6b | ||
|
|
1a6acf4800 | ||
|
|
b4b45d9929 | ||
|
|
593a5ddbfa | ||
|
|
e18fce4c96 | ||
|
|
85b8d63c6f | ||
|
|
a770de2d6d | ||
|
|
4aea115447 | ||
|
|
8ba8bbdc03 | ||
|
|
9b7844058d | ||
|
|
7eb81df825 | ||
|
|
70d9858b44 | ||
|
|
82c7125e19 | ||
|
|
b91e543166 | ||
|
|
ddbf79aac9 | ||
|
|
3fb562160e | ||
|
|
619311af13 | ||
|
|
5a1353ee93 | ||
|
|
b132671f8c | ||
|
|
fa42355d32 | ||
|
|
545be74a61 | ||
|
|
3a060a7769 | ||
|
|
99d8c4e65d | ||
|
|
d1554f29de | ||
|
|
30f8d1a392 | ||
|
|
a181262a0f | ||
|
|
81af88aa3f | ||
|
|
8ef8555b0c | ||
|
|
987e1edd7f | ||
|
|
e2728ae4d9 | ||
|
|
b97bda3281 | ||
|
|
63558f920f | ||
|
|
ca0bbe11de | ||
|
|
48cde028db | ||
|
|
15dc3cc10c | ||
|
|
498ad55190 | ||
|
|
cfce2edaae | ||
|
|
0cd267734d | ||
|
|
0a954915e3 | ||
|
|
3bd09bb064 | ||
|
|
1c24c21447 | ||
|
|
733d781350 | ||
|
|
034c15eca4 | ||
|
|
ee1c751d77 | ||
|
|
888fcdfb74 | ||
|
|
121ece1b38 | ||
|
|
ee07c812e6 | ||
|
|
db759605e5 | ||
|
|
fb6e7a5fb4 | ||
|
|
d4ca272801 | ||
|
|
e7c957545c | ||
|
|
1ef55dbab0 | ||
|
|
faebfac4c6 | ||
|
|
6975988c67 | ||
|
|
0200b3c50f | ||
|
|
3af67c09e0 | ||
|
|
752c9887d0 | ||
|
|
e2c13c76c8 | ||
|
|
3f5676e0f8 | ||
|
|
627d0b5db6 | ||
|
|
62f61b185a | ||
|
|
6e02c4937a | ||
|
|
712715859c | ||
|
|
479dbf3eac | ||
|
|
fe96e7d4fc | ||
|
|
e8fa5daeea | ||
|
|
b52f553c73 | ||
|
|
1eb7d52255 | ||
|
|
c2dfcc0e9c | ||
|
|
bb182e36cf | ||
|
|
bceeedd900 | ||
|
|
a13d45ab1e | ||
|
|
6c2caf717d | ||
|
|
741a29f599 | ||
|
|
ebceee564e | ||
|
|
c09eec16b5 | ||
|
|
04bb9629a4 | ||
|
|
d2ec351488 | ||
|
|
41065594fe | ||
|
|
3c8a8c3615 | ||
|
|
a8ee9d881b | ||
|
|
d60cf1222c | ||
|
|
284fe4a35d | ||
|
|
46a79fec4a | ||
|
|
e90f86ff40 | ||
|
|
df8d0a5468 | ||
|
|
043ff32e2a | ||
|
|
39c5b42c7a | ||
|
|
5100e221cd | ||
|
|
8c7a73463b | ||
|
|
4bcf693ed5 | ||
|
|
b3c1e6c85a | ||
|
|
d5af8f65ea | ||
|
|
af63d49af4 | ||
|
|
a83b7d10fc | ||
|
|
35bb091756 | ||
|
|
02cb0210fd | ||
|
|
061c5de807 | ||
|
|
7ee5d9605b | ||
|
|
a2ff666164 | ||
|
|
4c1bdaf4cd | ||
|
|
ea9297b137 | ||
|
|
8bcbd738f2 | ||
|
|
1fa5f0a449 | ||
|
|
86030967e4 | ||
|
|
64bdf86355 | ||
|
|
f673aaa5cf | ||
|
|
2e180fd0ed | ||
|
|
c4f5239b04 | ||
|
|
044a7a9bec | ||
|
|
9397981cb4 | ||
|
|
3dce59ff3d | ||
|
|
e2ed071a3a | ||
|
|
58fc80eef2 | ||
|
|
256dbbc2b7 | ||
|
|
e3d2fb4b20 | ||
|
|
d1f699681f | ||
|
|
97d4cc6432 | ||
|
|
aa1666e57f | ||
|
|
30f1c7d09e | ||
|
|
f414422601 | ||
|
|
72286fc6c2 | ||
|
|
f126fdb5b8 | ||
|
|
c59ec6f0b6 | ||
|
|
98294be09f | ||
|
|
9993628881 | ||
|
|
078d21e9aa | ||
|
|
c22fd32b8f | ||
|
|
2d9081dd23 | ||
|
|
4c82e650f7 | ||
|
|
9dc534c721 | ||
|
|
f7e6a14d4f | ||
|
|
289d6beb93 | ||
|
|
8bd04b26a0 | ||
|
|
21c4311f42 | ||
|
|
22d0027238 | ||
|
|
49c4909916 | ||
|
|
6009eebd21 | ||
|
|
60fd98224e | ||
|
|
9f9fe3a65d | ||
|
|
bc6dddc7da | ||
|
|
cdb5eadbfc | ||
|
|
52e0179435 | ||
|
|
8983c90f0b | ||
|
|
88104091c5 | ||
|
|
7f019b5dd2 | ||
|
|
57b08764f1 | ||
|
|
c225acaf2f | ||
|
|
a59a5c5e14 | ||
|
|
a85e5a542a | ||
|
|
dfbb44f8c1 | ||
|
|
ed64ca9e8e | ||
|
|
8cfba1d6c0 | ||
|
|
d68ca6e03f | ||
|
|
16cb540283 | ||
|
|
6e7db37878 | ||
|
|
b4511ebd70 | ||
|
|
99c806bf97 | ||
|
|
a4fd77f699 | ||
|
|
a79513a8b7 | ||
|
|
4f0721dadc | ||
|
|
890553c53c | ||
|
|
d3555a7571 | ||
|
|
b90e57341d | ||
|
|
e7462057e5 | ||
|
|
211ff13866 | ||
|
|
1797e88452 | ||
|
|
0118573746 | ||
|
|
97190e5e3e | ||
|
|
e984d2ebad | ||
|
|
cff1864aec | ||
|
|
0553eabc81 | ||
|
|
55fbafe0cc | ||
|
|
26df4c8ba6 | ||
|
|
2ce43fa526 | ||
|
|
09f01839bb | ||
|
|
4084e4dfb5 | ||
|
|
3f85a8fb0b | ||
|
|
5ee1bdfdc5 | ||
|
|
06fd5ea8b6 | ||
|
|
172c78657b | ||
|
|
ff4c399f91 | ||
|
|
ab97d4ae84 | ||
|
|
a219cf5160 | ||
|
|
effcf98f59 | ||
|
|
93c3b14344 | ||
|
|
b2c2f5dad4 | ||
|
|
f899bad57f | ||
|
|
384ca553c3 | ||
|
|
2231f4aa3c | ||
|
|
73c3bae99e | ||
|
|
441f6e3e41 | ||
|
|
d6011c2f0a | ||
|
|
1c98d314f2 | ||
|
|
90a895f54f | ||
|
|
f7be7f25a9 | ||
|
|
1295946a09 | ||
|
|
d13216505c | ||
|
|
1493cfe3ec | ||
|
|
89695bcce8 | ||
|
|
c322b46255 | ||
|
|
b41546d6f5 | ||
|
|
ced79a086d | ||
|
|
1cc0afc7d3 | ||
|
|
9120f629a9 | ||
|
|
adc689538e | ||
|
|
805a812487 | ||
|
|
cb302af71f | ||
|
|
8f5853bac6 | ||
|
|
5b5692cc6b | ||
|
|
3b64572e5e | ||
|
|
edbd269181 | ||
|
|
79b364e15b | ||
|
|
67d6f8af86 | ||
|
|
4165c8750e | ||
|
|
5247230939 | ||
|
|
5be9842d6b | ||
|
|
18281c009e | ||
|
|
6d85a696e7 | ||
|
|
36c2446f41 | ||
|
|
4bea1c5368 | ||
|
|
5073c4551a | ||
|
|
ebfe6e64a3 | ||
|
|
ca1605beab | ||
|
|
151ebccd14 | ||
|
|
294b880cab | ||
|
|
d1b20eb4a1 | ||
|
|
41d4232664 | ||
|
|
79231c1cd4 | ||
|
|
be2fbf3e05 | ||
|
|
ab73ca2b91 | ||
|
|
323e0314d9 | ||
|
|
839971da74 | ||
|
|
b2ffe48f85 | ||
|
|
96aec7d085 | ||
|
|
ff3316289c | ||
|
|
49d0ad07a1 | ||
|
|
e531fd628c | ||
|
|
21a3398035 | ||
|
|
9b242fa476 | ||
|
|
1b6dac990c | ||
|
|
7e100d9735 | ||
|
|
a2dc789f98 | ||
|
|
77d251b880 | ||
|
|
2250cd9ad2 | ||
|
|
d0bcb393f1 | ||
|
|
f7789170a7 | ||
|
|
31e4d877ab | ||
|
|
7e9d844f37 | ||
|
|
0104269729 | ||
|
|
ce8172780c | ||
|
|
b008a868a3 | ||
|
|
06aff10acd | ||
|
|
49533dd69a | ||
|
|
ca5182d1b7 | ||
|
|
16a022bca6 | ||
|
|
213281c522 | ||
|
|
1d37415ec8 | ||
|
|
bda3e4adc8 | ||
|
|
8e165db548 | ||
|
|
f9bb231582 | ||
|
|
7967c60d13 | ||
|
|
d1a4877fe3 | ||
|
|
1ffb1cf7b5 | ||
|
|
75dc6ffe8d | ||
|
|
4b6f7e0525 | ||
|
|
4c47332086 | ||
|
|
a8fa9ac035 | ||
|
|
4cbebe54bd | ||
|
|
2818bb0ca2 | ||
|
|
7cfdafdc0d | ||
|
|
bc4a2ba85b | ||
|
|
6dd24e35c6 | ||
|
|
a751c51b91 | ||
|
|
409d8c865d | ||
|
|
84fc9a3b91 | ||
|
|
f9a52a2c81 | ||
|
|
78d97b53dc | ||
|
|
b791072e61 | ||
|
|
cb3a71974c | ||
|
|
0add6b4291 | ||
|
|
4764903039 |
@@ -0,0 +1 @@
|
||||
service_name: travis-ci
|
||||
@@ -0,0 +1,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,5 @@
|
||||
# Protect whitespace line ending.
|
||||
* text=auto
|
||||
|
||||
# Ensure line endings are always LF.
|
||||
text eol=lf
|
||||
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
test/report
|
||||
test/tmp
|
||||
bower_components
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
|
||||
"maximumLineLength": 80,
|
||||
|
||||
"validateJSDoc": {
|
||||
"checkParamNames": true,
|
||||
"checkRedundantParams": true,
|
||||
"requireParamTypes": true
|
||||
},
|
||||
|
||||
"validateLineBreaks": "LF",
|
||||
"validateQuoteMarks": { "mark": "\"", "escape": true },
|
||||
|
||||
"requireCurlyBraces": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"try",
|
||||
"catch"
|
||||
],
|
||||
|
||||
"requireSpaceAfterKeywords": [
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"switch",
|
||||
"return",
|
||||
"try",
|
||||
"catch"
|
||||
],
|
||||
|
||||
"requireOperatorBeforeLineBreak": [
|
||||
"?",
|
||||
"+",
|
||||
"-",
|
||||
"/",
|
||||
"*",
|
||||
"=",
|
||||
"==",
|
||||
"===",
|
||||
"!=",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
],
|
||||
|
||||
"requireSpaceBeforeBinaryOperators": [
|
||||
"+",
|
||||
"-",
|
||||
"/",
|
||||
"*",
|
||||
"=",
|
||||
"==",
|
||||
"===",
|
||||
"!=",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
],
|
||||
|
||||
"requireSpaceAfterBinaryOperators": [
|
||||
",",
|
||||
"+",
|
||||
"-",
|
||||
"/",
|
||||
"*",
|
||||
"=",
|
||||
"==",
|
||||
"===",
|
||||
"!=",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
],
|
||||
|
||||
"disallowSpaceBeforeBinaryOperators": [
|
||||
","
|
||||
],
|
||||
|
||||
"requireSpacesInConditionalExpression": true,
|
||||
|
||||
"requireKeywordsOnNewLine": [
|
||||
"catch",
|
||||
"while"
|
||||
],
|
||||
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
|
||||
"disallowSpacesInFunctionExpression": {
|
||||
"beforeOpeningRoundBrace": true
|
||||
},
|
||||
|
||||
"requireParenthesesAroundIIFE": true,
|
||||
"requireCommaBeforeLineBreak": true,
|
||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
||||
"requireCapitalizedConstructors": true,
|
||||
"requireDotNotation": true,
|
||||
|
||||
"disallowSpaceAfterPrefixUnaryOperators": [
|
||||
"++",
|
||||
"--",
|
||||
"+",
|
||||
"-",
|
||||
"~",
|
||||
"!"
|
||||
],
|
||||
|
||||
"disallowSpaceBeforePostfixUnaryOperators": [
|
||||
"++",
|
||||
"--"
|
||||
],
|
||||
|
||||
"disallowImplicitTypeConversion": [
|
||||
"numeric",
|
||||
"boolean",
|
||||
"binary",
|
||||
"string"
|
||||
],
|
||||
|
||||
"disallowKeywords": [
|
||||
"with"
|
||||
],
|
||||
|
||||
"disallowMultipleLineStrings": true,
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowEmptyBlocks": true,
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"disallowQuotedKeysInObjects": true,
|
||||
"disallowSpaceAfterObjectKeys": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"disallowMixedSpacesAndTabs": "smart"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"browser": true,
|
||||
"boss": true,
|
||||
"immed": false,
|
||||
"eqnull": true,
|
||||
"maxlen": 80,
|
||||
"es3": true,
|
||||
"curly": true,
|
||||
"quotmark": "double",
|
||||
"trailing": true,
|
||||
"unused": true,
|
||||
"undef": true,
|
||||
"node": true,
|
||||
"globals": {
|
||||
"define": true
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "0.10"
|
||||
- 0.12
|
||||
- iojs
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- wip
|
||||
|
||||
before_script:
|
||||
- npm install -g grunt-cli
|
||||
|
||||
after_success: cat test/report/lcov.info | node_modules/.bin/coveralls --verbose
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
## 0.10.0 ##
|
||||
|
||||
* Updated dependencies and locked Backbone to 1.1.2
|
||||
* Removed `then` method, if you want to use promises, call `.promise().then`
|
||||
|
||||
## 0.9.7 ##
|
||||
|
||||
* Adds in a `cleanup` event whenever a view has been removed
|
||||
* Fully support & test against LoDash 3.0 and Underscore
|
||||
|
||||
## 0.9.6 ##
|
||||
|
||||
* Full Browserify support.
|
||||
* The options object is now cloned when passed into a Layout.
|
||||
* Fixes a bug with `setView` removing deeply nested views.
|
||||
* Fixes compatibility with Backbone Epoxy.
|
||||
* Fixes a bug where a removed view could still be rendered asynchronously.
|
||||
* `renderViews` now accepts an array of Views to render batched.
|
||||
* Added an `empty` event that triggers when all Views are removed from a parent.
|
||||
|
||||
## 0.9.5 ##
|
||||
|
||||
* Removed `partial` shim from Node compatibility layer. No longer necessary.
|
||||
* Removed `getAllOptions`, no longer necessary with 1.1.0.
|
||||
* Now run our tests against the latest stable and unstable Node.js.
|
||||
* `setView` will never render a View now.
|
||||
* Broke out Grunt tasks into separate files.
|
||||
* Enforcing code style with JSCS.
|
||||
|
||||
## 0.9.4 ##
|
||||
|
||||
* Fixed trimming for incoming templates so that multiple top level elements
|
||||
warning isn't triggered annoyingly.
|
||||
* Upgraded to work with Backbone 1.1.0.
|
||||
* Minor housekeeping commits.
|
||||
|
||||
## 0.9.3 ##
|
||||
|
||||
* Style/spelling updates to markdown files.
|
||||
* Broke out `_viewRender` and `_applyTemplate` from their closures and exposed
|
||||
them on the LM object.
|
||||
* Parsing with `$.parseHTML` to allow leading whitespace and retain whitespace
|
||||
in templates.
|
||||
* Using Bower to manage testing depenencies.
|
||||
* Fixes for Underscore 1.5.
|
||||
|
||||
## 0.9.2 ##
|
||||
|
||||
* Updated AMD define shim to use UMD which fixes issues in some R.js builds.
|
||||
* Updated views property to accept functions that return values, see #367.
|
||||
* Fixed Node.js file lookup that was scoping to `__dirname`.
|
||||
* Ensured all examples work.
|
||||
* Hooked up Coveralls for monitoring and displaying code coverage during builds.
|
||||
|
||||
## 0.9.1 ##
|
||||
|
||||
* Fixed regression from 0.8->0.9 where the new AMD wrapper would fail to work
|
||||
correctly in an r.js build.
|
||||
* Fixed regression from 0.8->0.9 where the Node.js branch code was not updated
|
||||
correctly and lack of default `fetchTemplate` tests didn't report the
|
||||
failure.
|
||||
|
||||
## 0.9.0 ##
|
||||
|
||||
* Upgraded all internal dependencies.
|
||||
* Batch rendering via jQuery collections has been added. Check out the
|
||||
`htmlBatch` method.
|
||||
* Full on AMD support. No more shimming necessary!
|
||||
* `render` now returns the View instance to maintain parity with other
|
||||
`Backbone.View` tutorials and plugins.
|
||||
* `fetch` and `render` overrides have been renamed to `fetchTemplate` and
|
||||
`renderTemplate` respectively.
|
||||
* New method `renderViews` which will only render nested Views and not the
|
||||
parent view, useful for top level layouts.
|
||||
* Event bubbling has been removed.
|
||||
* Named regions for selectors using the `section` object.
|
||||
* Greatly refactored how the `async()` methods work. This allows every method
|
||||
of the `render` lifecycle to be asynchronous and maintain the context of the
|
||||
View.
|
||||
|
||||
## 0.8.8 ##
|
||||
|
||||
* Upgraded Cheerio dependency to v0.11.0.
|
||||
* Many optimizations and fixes to `getViews` by @jugglinmike. This includes
|
||||
fixes for issues like `removeView` on an invalid selector.
|
||||
* `cleanup` is now called with the proper context.
|
||||
* @SBoudrias added `.gitattributes` and `.editconfig` to ease development
|
||||
stress for our contributors.
|
||||
* Fix provided by @adamdicarlo to fix a bug where re-rendering with
|
||||
empty rendered contents would not replace the existing content.
|
||||
|
||||
## 0.8.7 ##
|
||||
|
||||
* Upgraded Cheerio dependency.
|
||||
* Trim templates in `el: false` Views to fix an issue with passing contents
|
||||
into `$()`.
|
||||
|
||||
## 0.8.6 ##
|
||||
|
||||
* We now log a warning if you use mutliple top level elements inside a View's
|
||||
template with `el: false`. This will not affect most developers. If you
|
||||
wish to suppress the warnings you can run `Backbone.Layout.configure({
|
||||
suppressWarnings: true });`. This is documented in the Wiki:
|
||||
https://github.com/tbranyen/backbone.layoutmanager/wiki/Configuration#el
|
||||
* Fixed minor bug with `cleanup` being defined on `options` instead of the instance.
|
||||
* Changed behavior so that __all__ views are removed when you call `remove`.
|
||||
* Upgraded to Backbone 1.0.
|
||||
* Upgraded to Node 0.10.
|
||||
|
||||
## 0.8.5 ##
|
||||
|
||||
* Fixed View duplication bug with multiple top level elements (`el: false`).
|
||||
* Fixed the deletion of the template property in initialize that would cause
|
||||
bugs with View inheritance.
|
||||
* Fixed issue with `getView` and `_.where` object.
|
||||
|
||||
## 0.8.4 ##
|
||||
|
||||
* Fixed package dependency issues with jQuery.
|
||||
* Updated Cheerio version, which includes fixes to `filter`.
|
||||
* Several fixes to the `el: false` implementation.
|
||||
* Removed custom `$.contains` Node.js implementation.
|
||||
|
||||
## 0.8.3 ##
|
||||
|
||||
* Fix for deep nested `el: false` elements.
|
||||
|
||||
## 0.8.2 ##
|
||||
|
||||
* Fixed parts of the Node build.
|
||||
* Changed from parallel rendering to serial loading which will make the render
|
||||
process significantly more stable.
|
||||
* Changed signature to `options.partial` and greatly fixed the way `noel` is
|
||||
handled.
|
||||
|
||||
## 0.8.1 ##
|
||||
|
||||
* Updated JamJS configuration settings.
|
||||
* Fixed event delegation when self managing the View element.
|
||||
* Fixed edge case with `afterRender` causes the element to not be in the
|
||||
parent container.
|
||||
* Fixed re-rendering when self managing the View element.
|
||||
|
||||
## 0.8.0 ##
|
||||
|
||||
* Massive Node.js refactor, including: unit testing, significantly better
|
||||
browser parity, and allowing more seamless browser/server View sharing.
|
||||
* Ability to disable `Backbone.View#el` wrapper element using `el: false` and
|
||||
opt for the first child inside the template instead. Very experimental!
|
||||
* Added Backbone event bubbling from nested Views to parent.
|
||||
* The `getView` function can receive a `_.where` object now, to easily filter
|
||||
through the Views.
|
||||
* Updated `getView` to have an `undefined` first argument to be passed allowing
|
||||
for an optional selector.
|
||||
* Changed Grunt configuration from JavaScript to CoffeeScript.
|
||||
* Added a `removeView` function to match `setView`, `getView`, etc.
|
||||
* Removed `LayoutManager` and `LayoutView` aliases.
|
||||
* Removed `data` alias in favor of `serialize`.
|
||||
* Removed `append` alias in favor of `insert`.
|
||||
* Renamed `_options` to `getAllOptions` making it an endorsed method to use.
|
||||
* Upgraded minimum support to Backbone 0.9.9, which will invoke `stopListening`
|
||||
automatically for you.
|
||||
* Internally using `Backbone.Events#once` to solve `afterRender` woes.
|
||||
* Class method `cache` now allows you to override contents.
|
||||
* Class method `setupViews` now allows you to manage many Views by passing an
|
||||
array of Views.
|
||||
* Can now directly set a template function to the `template` property and not
|
||||
have to override fetch with an `_.identity` function.
|
||||
* Miscellaneous code cleanup and minor bug/refactor fixes.
|
||||
|
||||
## 0.7.5 ##
|
||||
|
||||
* Updated lookup for `serialize` and `data` to also look on the instance so
|
||||
that `configure` doesn't always override.
|
||||
|
||||
### 0.7.4 ###
|
||||
|
||||
* Simplified insertion methods.
|
||||
* Fixed named function expression bug in IE.
|
||||
|
||||
### 0.7.3 ###
|
||||
|
||||
* Refactored `_.extend` to `LayoutManager.augment` to work with Lo-Dash and
|
||||
underscore.
|
||||
* Normalized rendering order, when the parent has already rendered.
|
||||
* Added better error handling for node.js build.
|
||||
* Updated error message for node.js build.
|
||||
* Added in Travis-CI and README updates.
|
||||
|
||||
### 0.7.2 ###
|
||||
|
||||
* Fixed cheerio rendering bug with the latest version.
|
||||
* Explicity depend and require underscore in NPM.
|
||||
* Repeated subViews are a thing of the past :D.
|
||||
* Fixed serialize/data priority.
|
||||
* Fixed lodash issues and added in separate lodash testing.
|
||||
|
||||
### 0.7.1 ###
|
||||
|
||||
* Views that have already been rendered can now be inserted into existing Views.
|
||||
* Fixed the logic that decides when to remove Views.
|
||||
* Fixed issue with `cleanup` not being on nested Views.
|
||||
* Miscellaneous fixes (inc. null checks, perf. updates, etc).
|
||||
* Removed minified build file from the repo.
|
||||
* `insertViews` can now accept an array of Views.
|
||||
* `render` can now be asynchronous to allow template engines like dust.js to
|
||||
work seamlessly, works identical to `fetch`.
|
||||
* Overriding the template option at initialization level wil now override the
|
||||
constructor option.
|
||||
|
||||
### 0.7.0 ###
|
||||
|
||||
* Refactored source to be less cryptic.
|
||||
* 100% test code coverage.
|
||||
* Re-wrote much of the internals.
|
||||
* Performance greatly improved, especially with large lists.
|
||||
* Removed `swapLayout`.
|
||||
* `serialize` is deprecated and replaced by `data`.
|
||||
* `render(callback)` is deprecated to `render().done()` deferreds only.
|
||||
* Automatically unbind `model` and `collection` events inside `cleanup`.
|
||||
* `Backbone.LayoutView, `Backbone.Layout`, and `Backbone.View` (with `manage`
|
||||
set to `true`) are now all identical.
|
||||
* `paths` is now a String `prefix`, set this to the folder where your templates live.
|
||||
* `afterRender` now occurs after the `render()` deferred.
|
||||
* `myView.render().view` now allows you to attach `view.el` after a render for
|
||||
some nice one-liners.
|
||||
`
|
||||
new Backbone.LayoutView({ template: "#t" }).render().view.$el.appendTo("body");
|
||||
`
|
||||
|
||||
### 0.6.6 ###
|
||||
|
||||
* Fixed asynchronous fetching bug that could in some circumstances duplicate
|
||||
list items
|
||||
* Calling `remove` on a View now completely removes it from LayoutManager
|
||||
* Fixed issues with `afterRender`
|
||||
* Better error handling for when `manage: true` is missing
|
||||
|
||||
### 0.6.5 ###
|
||||
|
||||
* Corrected event binding behaviors depending on the parent.
|
||||
|
||||
### 0.6.4 ###
|
||||
|
||||
* Fixed regression with `afterRender`
|
||||
|
||||
### 0.6.3 ###
|
||||
|
||||
* Fixed a memory leak that existed with appended Views
|
||||
* Updated all examples
|
||||
* Fixed regression with events being bound to Layouts
|
||||
* General performance tweaks
|
||||
* Updated cheerio dependency to 0.9.2 for the Node implementation
|
||||
|
||||
### 0.6.2 ###
|
||||
|
||||
* Updated to support jQuery 1.8
|
||||
* Fixed missing events when calling `view.render`
|
||||
* Fixed issues with `afterRender` not triggering
|
||||
* No longer copying options to the View instance
|
||||
|
||||
### 0.6.1 ###
|
||||
|
||||
* Patch release fixing issues and upgrading Node.js dependencies
|
||||
* Fixed error where inserted views rendered out of order #116, #117
|
||||
* Fixed issue where events were not being bound correctly #118
|
||||
|
||||
### 0.6.0 ###
|
||||
|
||||
* Overriding `render` is now deprecated. Refer to `beforeRender` and
|
||||
`afterRender`.
|
||||
* Added a `swap` method that can be used on a Layout which allows you to
|
||||
dynamically swap out the entire contents, but retain the rendered subViews.
|
||||
* Code cleanup, removing unused code paths, properties, features, etc.
|
||||
* Fixes to setView to not break when moving from an append mode to non-append
|
||||
mode.
|
||||
* Several bug fixes.
|
||||
* Added more unit tests.
|
||||
|
||||
### 0.5.2 ###
|
||||
|
||||
* More methods can be overwritten from initialization
|
||||
* Render can now be bound like `this.collection.on("reset", this.render, this);`
|
||||
* Fixes to `keep: true` and append example, only checking `keep` if View has
|
||||
already rendered
|
||||
|
||||
### 0.5.1 ###
|
||||
|
||||
* Patched massive memory leak and missing remove on setView
|
||||
* Fixed bug with Named Function Expression breaking IE compatibility when
|
||||
minified
|
||||
* Added `keep: true` to View's to stop them from being removed pre-render
|
||||
|
||||
### 0.5.0 ###
|
||||
|
||||
* Tons of new unit tests
|
||||
* More API normalization
|
||||
* Collection rendering bug fixes
|
||||
* New View methods
|
||||
+ insertView & insertViews
|
||||
+ getView and getViews
|
||||
|
||||
### 0.4.1 ###
|
||||
|
||||
* Fixed major regression regarding list duplication
|
||||
|
||||
### 0.4.0 ###
|
||||
|
||||
* Detach no longer internally happens on the root Layout
|
||||
* `manage` function inside a custom render has a new property `raw` for getting
|
||||
at the actual View instance
|
||||
* Collection lists bugs solved
|
||||
* Made `makeAsync` a private class method
|
||||
* Cleanup method is now called whenever `remove` is called
|
||||
* Major fixes allowing for single View append
|
||||
* View function no longer requires "" for inserting into parent View
|
||||
* SubView render functions only resolve after the element has been attached
|
||||
to its parent View.
|
||||
|
||||
### 0.3.0 ###
|
||||
|
||||
* Context is now consistent in either callbacks or deferreds
|
||||
* `setViews` is now chainable
|
||||
* `view` and `setViews` are now available at all times on `Backbone.View`'s
|
||||
* Bug fixes regarding list duplication
|
||||
|
||||
### 0.2.1 ###
|
||||
|
||||
* Made `template` optional inside of `Backbone.View`
|
||||
* Added custom cleanup function to handle the removal of any custom events
|
||||
* insert no longer requires a selector
|
||||
* Several performance improvements along with general stability changes
|
||||
|
||||
### 0.2.0 ###
|
||||
|
||||
* Major re-write to codebase that eliminated a lot of repetitive and cludgy
|
||||
code
|
||||
* Deprecated the need to extend `Backbone.LayoutManager.View`
|
||||
* All View `render` methods return promises and can accept a callback
|
||||
* Views now render themselves first and then resolve/trigger the callback once
|
||||
all the children are rendered. This helps with jQuery plugins.
|
||||
|
||||
### 0.1.2 ###
|
||||
|
||||
* Major patch for re-rendering layouts that would lose events
|
||||
* Added new API method detach
|
||||
|
||||
### 0.1.1 ###
|
||||
|
||||
* Minor patch release (that removes an undocumented feature)
|
||||
* Fixed issue with events not being registered correctly within lists
|
||||
|
||||
### 0.1.0 ###
|
||||
|
||||
* Lots of bug fixes!
|
||||
* Ability to insert views dynamically using the new `view.insert` method.
|
||||
Useful for collections.
|
||||
* Setting/resetting sub views possible with new `view.setViews` method.
|
||||
* All views now have the `view/setViews` methods.
|
||||
* Updates to allow LayoutManager to be extended easier, along with `events`
|
||||
being bound automatically during initialization.
|
||||
|
||||
### 0.0.4 ###
|
||||
|
||||
* Adding views turned into a reusable function called `view`.
|
||||
* Templates no longer are required to be a string, this allows passing of
|
||||
compiled template functions.
|
||||
|
||||
### 0.0.3 ###
|
||||
|
||||
* View renders are internally wrapped to expose a new `render()` method that
|
||||
when called, re-renders.
|
||||
* Nested views are possible by adding a `views` sub property which is an object
|
||||
that contains nested views.
|
||||
|
||||
### 0.0.2 ###
|
||||
|
||||
* Changed layout `name` property to `template` for consistency.
|
||||
* Internal second deferred replaces the `viewDeferred` to determine when an
|
||||
element enters the DOM.
|
||||
|
||||
### 0.0.1 ###
|
||||
|
||||
* Open sourced on GitHub.
|
||||
* Initial unit tests.
|
||||
* Released introduction screencast.
|
||||
@@ -0,0 +1,74 @@
|
||||
## Contributing to LayoutManager ##
|
||||
|
||||
### Filing an issue ###
|
||||
|
||||
You're using LayoutManager and something doesn't appear to function the way
|
||||
it's described or you're thinking something could be done better. Please let
|
||||
us know. Issues are how the project moves forward; by letting us know what's
|
||||
bothering you.
|
||||
|
||||
* Search the [issues
|
||||
list](https://github.com/tbranyen/backbone.layoutmanager/issues)
|
||||
for existing similar issues. Consider adding to an existing issue if you
|
||||
find one.
|
||||
* Choose an applicable title. This will be used for a corresponding unit test.
|
||||
* Provide a unit test or snippet that illustrates the problem. This small
|
||||
gesture goes a long way in a speedy patch.
|
||||
|
||||
### Sending a pull request ###
|
||||
|
||||
If you feel confident that you can correct a bug or add a new feature, feel
|
||||
free to submit a pull request with your changes. Everyone helping makes the
|
||||
project community instead of a sole proprietorship.
|
||||
|
||||
* Link to an existing issue if applicable, create one to start discussion to
|
||||
ensure everyone is on board with the change and so you don't spend time
|
||||
writing a fantastic patch we cannot accept.
|
||||
* Provide a description of what the patch is/does.
|
||||
* Provide unit tests in the correct file:
|
||||
+ `test/configure.js` for configuration changes.
|
||||
+ `test/dom.js` for DOM specific changes.
|
||||
+ `test/expose.js` for code exposure in various environments.
|
||||
+ `test/setup.js` for changes that impact `Backbone.View` and
|
||||
`Backbone.Layout` mirroring eachother.
|
||||
+ `test/view.js` for all changes that impact the API of LayoutManager
|
||||
Views.
|
||||
* Make sure you open the pull request onto the `wip` branch and not `master`
|
||||
for requests that break the API or add significant functionality changes.
|
||||
* Open pull requests on `master` only with bug fixes and patch related updates.
|
||||
|
||||
### Code style ###
|
||||
|
||||
I am very sensitive to maintaining a holistic codebase and that includes a
|
||||
single code style. Pull requests may be rejected or modified to ensure this
|
||||
code style is maintained.
|
||||
|
||||
* Follow close to [BSD KNF
|
||||
style](http://en.wikipedia.org/wiki/Indent_style#BSD_KNF_style).
|
||||
* Use two space, expanded/soft tabs. Use `\t` if you need a tab character in a
|
||||
string.
|
||||
* No trailing whitespace, except in markdown files where a linebreak must be
|
||||
forced.
|
||||
* Don't go overboard with the whitespace.
|
||||
* No more than one assignment per var statement.
|
||||
* Delimit strings with double-quotes ", not single-quotes '.
|
||||
* Comments must always contain proper punctuation and end with a correct
|
||||
sentence terminator. Put before the line of code, not at the end of the
|
||||
line.
|
||||
* Prefer if and else to "clever" uses of ? : conditional or ||, && logical
|
||||
operators.
|
||||
* When in doubt, follow the conventions you see used in the source already.
|
||||
|
||||
### Documentation ###
|
||||
|
||||
We strive to make the project as easy to consume and use as possible which
|
||||
means having consistent and up-to-date documentation. If you notice something
|
||||
is outdated or incorrect, please file an issue, pull request, or make the
|
||||
change yourself in the Wiki.
|
||||
|
||||
* Add documentation to the
|
||||
[Wiki](https://github.com/tbranyen/backbone.layoutmanager/wiki) if
|
||||
applicable.
|
||||
* Release notes are added to the
|
||||
[readme.md](https://github.com/tbranyen/backbone.layoutmanager/blob/master/README.md)
|
||||
if applicable.
|
||||
@@ -0,0 +1,11 @@
|
||||
module.exports = ->
|
||||
@loadTasks "build/tasks"
|
||||
|
||||
@registerTask "default", [
|
||||
"clean"
|
||||
"jscs"
|
||||
"jshint"
|
||||
"browserify"
|
||||
"qunit"
|
||||
"nodequnit"
|
||||
]
|
||||
@@ -1,19 +1,19 @@
|
||||
Copyright (c) 2011 Tim Branyen
|
||||
Copyright (c) 2016 Tim Branyen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,584 +1,76 @@
|
||||
backbone.layoutmanager
|
||||
=======================
|
||||
LayoutManager
|
||||
-------------
|
||||
|
||||
Created by Tim Branyen [@tbranyen](http://twitter.com/tbranyen) with contributions from [@nedcampion](https://github.com/nedcampion)
|
||||
## No longer maintained (7/31/18)
|
||||
|
||||
Provides a logical structure for assembling layouts with Backbone Views.
|
||||
Designed to be adaptive and configurable for painless integration.
|
||||
If you have an existing application that has an issue with LayoutManager, or
|
||||
wish to champion ownership, please contact
|
||||
[@tbranyen](https://github.com/tbranyen/backbone.layoutmanager).
|
||||
|
||||
Depends on Underscore, Backbone and jQuery. You can swap out the
|
||||
jQuery dependency completely with a custom configuration.
|
||||
---
|
||||
|
||||
## Tutorials, Screencasts, & Examples ##
|
||||
[![NPM version][npm-image]][npm-url] [![Build status][travis-image]][travis-url] [![Code coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
* [Initial Screencast](http://vimeo.com/32765088)
|
||||
* Example Application: [Demo](http://dev.tbranyen.com/LayoutManager/) & [Source](http://github.com/tbranyen/layoutmanager-example)
|
||||
## Have a problem? Come chat with us! ##
|
||||
|
||||
## Download & Include ##
|
||||
[](https://gitter.im/tbranyen/backbone.layoutmanager?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Development is fully commented source, Production is minified and stripped of
|
||||
all comments except for license/credits.
|
||||
Maintained by Tim Branyen [@tbranyen](http://twitter.com/tbranyen), Mike
|
||||
Pennisi [@jugglinmike](http://twitter.com/jugglinmike), Simon Boudrias
|
||||
[@SBoudrias](http://twitter.com/Vaxilart), and
|
||||
[@ssafejava](https://github.com/ssafejava) with help from [awesome
|
||||
contributors](https://github.com/tbranyen/backbone.layoutmanager/contributors)!
|
||||
|
||||
* [Development](https://raw.github.com/tbranyen/backbone.layoutmanager/master/backbone.layoutmanager.js)
|
||||
* [Production](https://raw.github.com/tbranyen/backbone.layoutmanager/master/dist/backbone.layoutmanager.min.js)
|
||||
Provides a logical foundation for assembling layouts and views within Backbone.
|
||||
Designed to be adaptive and configurable for painless integration. Well
|
||||
tested, with full code coverage, in both the browser and Node.js environments.
|
||||
|
||||
Include in your application *after* jQuery, Underscore, and Backbone have been included.
|
||||
Depends on Underscore, Backbone, and jQuery. You can swap out the dependencies
|
||||
with a custom configuration.
|
||||
|
||||
``` html
|
||||
<script src="/js/jquery.js"></script>
|
||||
<script src="/js/underscore.js"></script>
|
||||
<script src="/js/backbone.js"></script>
|
||||
#### Documentation ####
|
||||
|
||||
<script src="/js/backbone.layoutmanager.js"></script>
|
||||
http://layoutmanager.org/
|
||||
|
||||
#### Migrating from 0.8 ####
|
||||
|
||||
http://layoutmanager.org/From-0.8-to-0.9
|
||||
|
||||
#### Release notes ####
|
||||
|
||||
* Backbone 1.2.0 support
|
||||
* Ensures cancelAnimationFrame does not exist before polyfilling
|
||||
|
||||
[Full Release
|
||||
Log](https://github.com/tbranyen/backbone.layoutmanager/blob/master/CHANGELOG.md)
|
||||
|
||||
#### Contributing ####
|
||||
|
||||
Please read and follow the [contribution
|
||||
guide](https://github.com/tbranyen/backbone.layoutmanager/blob/master/CONTRIBUTING.md)
|
||||
before contributing.
|
||||
|
||||
#### Running the unit tests ####
|
||||
|
||||
Open `test/index.html` in your browser to run the test suite.
|
||||
|
||||
**Headless:**
|
||||
|
||||
Install [Node.js](http://nodejs.org), [Grunt.js 0.4](http://gruntjs.com), and
|
||||
run `npm install -q` inside the project directory.
|
||||
|
||||
Make sure you've installed `grunt-cli` globally with:
|
||||
|
||||
``` bash
|
||||
npm install grunt-cli -gq
|
||||
```
|
||||
|
||||
## Usage ##
|
||||
|
||||
This example renders a View into a template which is injected into a layout.
|
||||
|
||||
### Create and render a layout ###
|
||||
|
||||
This code typically resides in a route callback. If you want to provide a
|
||||
custom object for your template engine to the layout, use the `serialize`
|
||||
property.
|
||||
|
||||
``` javascript
|
||||
// Create a new layout using the #main template.
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main-layout",
|
||||
|
||||
// In the secondary column, put a new Login View.
|
||||
views: {
|
||||
".secondary": new LoginView()
|
||||
}
|
||||
});
|
||||
|
||||
// Render into <body>.
|
||||
main.render(function(el) {
|
||||
$("body").html(el);
|
||||
});
|
||||
```
|
||||
|
||||
Views may also be alternatively defined later:
|
||||
|
||||
``` javascript
|
||||
main.view(".header", new HeaderView);
|
||||
main.view(".footer", new FooterView);
|
||||
```
|
||||
|
||||
Use the above syntax to change out views at a later time as well, remember to
|
||||
call the View's `render` method after swapping out to have it displayed.
|
||||
|
||||
The `view` method shown above is available on all layout and template views.
|
||||
|
||||
### Nested Views ###
|
||||
|
||||
You may have a situation where a View is defined that encapsulates other nested
|
||||
Views. In these cases you should use nested views inside your LayoutManager
|
||||
View assignments.
|
||||
|
||||
Check out this example to see how easy this is:
|
||||
|
||||
``` javascript
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#some-layout",
|
||||
|
||||
views: {
|
||||
".partial": new PartialView({
|
||||
views: {
|
||||
".inner": new InnerView()
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You can nest Views infinitely.
|
||||
|
||||
#### Alternative method of assigning nested views ####
|
||||
|
||||
You may be writing an application that uses a shared LayoutManager, you may
|
||||
find the `setViews` method handy. This is identical to how views are being
|
||||
assigned above, it can be used in the following way:
|
||||
|
||||
Note: `setViews` is available on all layout and template views.
|
||||
|
||||
``` javascript
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#some-layout"
|
||||
});
|
||||
|
||||
// Bulk reset of all sub views
|
||||
main.setViews({
|
||||
".partial": new PartialView({
|
||||
views: {
|
||||
".inner": new InnerView()
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Structuring a View ###
|
||||
|
||||
Each View needs to have a template associated, via the `template` property.
|
||||
This name by default is a jQuery selector, but if you have a custom
|
||||
configuration this could potentially be a filename or JST function name.
|
||||
|
||||
``` javascript
|
||||
var LoginView = Backbone.View.extend({
|
||||
// Tell LayoutManager what template to associate with this View.
|
||||
template: "#login-template",
|
||||
|
||||
// The render function will be called internally by LayoutManager.
|
||||
render: function(layout) {
|
||||
// Wrap the layout with this View and call render.
|
||||
return layout(this).render();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Optionally, you can extend from `LayoutManager.View` and omit the `render`
|
||||
method. If you need to do custom logic in `render`, you should use the
|
||||
other pattern above.
|
||||
|
||||
``` javascript
|
||||
var LoginView = Backbone.LayoutManager.View.extend({
|
||||
template: "#login-template"
|
||||
});
|
||||
```
|
||||
|
||||
#### Re-rendering Views ####
|
||||
|
||||
Instead of re-rendering the entire layout after data in a single View changes,
|
||||
you can simply call `render()` on the View and it will automatically update
|
||||
the DOM. You **cannot** bind to the initial render reference, like so
|
||||
|
||||
*Assume that you have a model that when changed, causes a redraw.*
|
||||
|
||||
``` javascript
|
||||
var MyView = Backbone.LayoutManager.View.extend({
|
||||
initialize: function() {
|
||||
this.model.bind("change", this.render, this);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You must use this syntax instead, calling it from a function:
|
||||
|
||||
``` javascript
|
||||
var MyView = Backbone.LayoutManager.View.extend({
|
||||
initialize: function() {
|
||||
this.model.bind("change", function() {
|
||||
this.render();
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This necessity may be alleviated in a future version.
|
||||
|
||||
#### Rendering consecutive views ####
|
||||
|
||||
There are many times in which you will end up with a list of nested views
|
||||
that result from either iterating a `Backbone.Collection` or array
|
||||
and will need to dynamically add these nested views into a main view.
|
||||
|
||||
LayoutManager solves this by exposing a method to change the insert mode
|
||||
from replacing the `innerHTML` to `appendChild` instead. Whenever you
|
||||
use the `insert` method inside a render function you will put the
|
||||
nested view into this special mode.
|
||||
|
||||
All sub views are inserted in order, regardless if the `fetch` method has
|
||||
been overwritten to be asynchronous.
|
||||
|
||||
An example will illustrate the pattern easier:
|
||||
|
||||
``` javascript
|
||||
// An example item View
|
||||
// You may find it easier to have Backbone render the LI/TD/etc element
|
||||
// instead of including this in your template. This is purely convention
|
||||
// use what works for you.
|
||||
var SomeItem = Backbone.LayoutManager.View.extend({
|
||||
template: "#list",
|
||||
|
||||
// In this case we'll say the item is an <LI>
|
||||
tagName: "li"
|
||||
});
|
||||
|
||||
// The view that contains the <ul> <table> w/e is used to contain your element.
|
||||
//
|
||||
// Since this method is used inside a custom render method, LayoutManager.View
|
||||
// isn't useful for rendering a collection list.
|
||||
var SomeList = Backbone.View.extend({
|
||||
template: "#item",
|
||||
|
||||
render: function(layout) {
|
||||
// Assign the layout view custom object to the view variable
|
||||
var view = layout(this);
|
||||
|
||||
// Iterate over the passed collection and create a view for each item
|
||||
this.collection.each(function(model) {
|
||||
// Pass the model to the new SomeItem view
|
||||
view.insert("ul", new SomeItem({
|
||||
model: model
|
||||
}));
|
||||
});
|
||||
|
||||
// You still must return this view to render, works identical to the
|
||||
// existing functionality.
|
||||
return view.render();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Working with context ####
|
||||
|
||||
Template engines bind data to a template. The term context refers to the
|
||||
data object passed.
|
||||
|
||||
`LayoutManager` will look for a `serialize` method or object automatically:
|
||||
|
||||
``` javascript
|
||||
var LoginView = Backbone.LayoutManager.View.extend({
|
||||
template: "#login-template",
|
||||
|
||||
// Provide data to the template
|
||||
serialize: function() {
|
||||
return this.model.toJSON();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You can also pass the context object inside the `render` method:
|
||||
|
||||
``` javascript
|
||||
var LoginView = Backbone.View.extend({
|
||||
template: "#login-template",
|
||||
|
||||
render: function(layout) {
|
||||
// Provide data to the template
|
||||
return layout(this).render(this.model.toJSON());
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Defining the layout and template ###
|
||||
|
||||
These example templates are defined using a common pattern which leverages
|
||||
how browsers treat `<script></script>` tags with custom `type` attributes.
|
||||
|
||||
This is how `LayoutManager` expects templates to be defined by default (using script tags).
|
||||
|
||||
#### Main layout ####
|
||||
|
||||
``` plain
|
||||
<script id="main-layout" type="layout">
|
||||
<section class="content twelve columns"></section>
|
||||
|
||||
<!-- Template below will be injected here -->
|
||||
<aside class="secondary four columns"></aside>
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Login template ####
|
||||
|
||||
``` plain
|
||||
<script id="login-template" type="template">
|
||||
<form class="login">
|
||||
<p><label for="user">Username</label><input type="text" name="user"></p>
|
||||
<p><label for="pass">Password</label><input type="text" name="pass"></p>
|
||||
<p><input class="loginBtn" type="submit" value="Login"></p>
|
||||
</form>
|
||||
</script>
|
||||
```
|
||||
|
||||
## Using jQuery Plugins ##
|
||||
|
||||
Attaching jQuery plugins should happen inside the `render` methods. You can
|
||||
attach at either the layout render or the view render. To attach in the
|
||||
layout render:
|
||||
|
||||
``` javascript
|
||||
main.render(function(el) {
|
||||
$(el).find(".some-element").somePlugin();
|
||||
$(".container").html(el);
|
||||
});
|
||||
```
|
||||
|
||||
In the above example its entirely possible the elements are not in the DOM yet.
|
||||
This happens when you fetch templates asynchronously. Using the following
|
||||
method, elements will be added into the DOM. To attach in the layout render,
|
||||
you will need to override the `render` method like so:
|
||||
|
||||
``` javascript
|
||||
render: function(layout) {
|
||||
return layout(this).render().then(function(el) {
|
||||
$(el).find(".some-element").somePlugin();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This is a very cool example of the power in using deferreds. =)
|
||||
|
||||
## Configuration ##
|
||||
|
||||
Overriding `LayoutManager` options has been designed to work just like
|
||||
`Backbone.sync`. You can override at a global level using
|
||||
`LayoutManager.configure` or you can specify when instantiating a
|
||||
`LayoutManager` instance.
|
||||
|
||||
|
||||
### Global level ###
|
||||
|
||||
Lets say you wanted to use `Handlebars` for templating in all your Views.
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
// Override render to use Handlebars
|
||||
render: function(template, context) {
|
||||
return Handlebars.compile(template)(context);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Instance level ###
|
||||
|
||||
In this specific layout, define custom prefixed paths for template paths.
|
||||
|
||||
``` javascript
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main",
|
||||
|
||||
// Custom paths for this layout
|
||||
paths: {
|
||||
template: "/assets/templates/"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Defaults ###
|
||||
|
||||
* __Paths__:
|
||||
An empty object. Two valid property names: `template` and `layout`.
|
||||
|
||||
``` javascript
|
||||
paths: {}
|
||||
```
|
||||
|
||||
* __Deferred__:
|
||||
Uses jQuery deferreds for internal operation, this may be overridden to use
|
||||
a different Promises/A compliant deferred.
|
||||
|
||||
``` javascript
|
||||
deferred: function() {
|
||||
return $.Deferred();
|
||||
}
|
||||
```
|
||||
|
||||
* __Fetch__:
|
||||
Uses jQuery to find a selector and returns its `innerHTML` content.
|
||||
|
||||
``` javascript
|
||||
fetch: function(path) {
|
||||
return $(path).html();
|
||||
}
|
||||
```
|
||||
|
||||
* __Partial__:
|
||||
Uses jQuery to find the View's location and inserts the rendered
|
||||
element there. The append property determines if the View should
|
||||
append, defaults to replace via innerHTML.
|
||||
|
||||
``` javascript
|
||||
partial: function(layout, name, template, append) {
|
||||
if (append) {
|
||||
this.append($(layout).find(name), template);
|
||||
} else {
|
||||
this.html($(layout).find(name), template);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* __HTML__:
|
||||
Override this with a custom HTML method, passed a root element and an
|
||||
element to replace the innerHTML with.
|
||||
|
||||
``` javascript
|
||||
html: function(root, el) {
|
||||
$(root).html(el);
|
||||
}
|
||||
```
|
||||
|
||||
* __Append__:
|
||||
Very similar to HTML except this one will appendChild.
|
||||
|
||||
``` javascript
|
||||
append: function(root, el) {
|
||||
$(root).append(el);
|
||||
}
|
||||
```
|
||||
|
||||
* __Detach__:
|
||||
Remove an element from the DOM, but maintain events.
|
||||
|
||||
``` javascript
|
||||
detach: function(el) {
|
||||
$(el).detach();
|
||||
}
|
||||
```
|
||||
|
||||
* __Render__:
|
||||
Renders a template with Underscore.
|
||||
|
||||
``` javascript
|
||||
render: function(template, context) {
|
||||
return _.template(template)(context);
|
||||
}
|
||||
```
|
||||
|
||||
### Asynchronous & Synchronous fetching ###
|
||||
|
||||
The `fetch` method is overridden to get the contents of layouts and templates.
|
||||
If you can instantly get the contents (DOM/JST) you can return the
|
||||
contents inside the function.
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
fetch: function(name) {
|
||||
return $("script#" + name).html();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
If you need to fetch the contents asynchronously, you will need to put the
|
||||
method into "asynchronous mode". To do this, assign `this.async()`
|
||||
to a variable and call that variable with the contents when you are done.
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
fetch: function(name) {
|
||||
var done = this.async();
|
||||
|
||||
$.get(name, function(contents) {
|
||||
done(contents);
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Sample Configurations ##
|
||||
|
||||
You may need to combine a mix of **Engines** and **Transports** to integrate.
|
||||
|
||||
### Engines (Mustache, Handlebars, etc.) ###
|
||||
|
||||
Custom templating engines can be used by overriding `render`.
|
||||
|
||||
#### Underscore ####
|
||||
|
||||
``` plain
|
||||
No configuration necessary for this engine.
|
||||
```
|
||||
|
||||
#### Mustache ####
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
render: function(template, context) {
|
||||
return Mustache.to_html(template, context);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Handlebars ####
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
render: function(template, context) {
|
||||
return Handlebars.compile(template)(context);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Transports (DOM, AJAX, etc.) ###
|
||||
|
||||
You can swap out how templates are loaded by overriding `fetch`.
|
||||
|
||||
#### DOM ####
|
||||
|
||||
``` plain
|
||||
No configuration necessary for this transport.
|
||||
```
|
||||
|
||||
#### AJAX ####
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
fetch: function(path) {
|
||||
var done = this.async();
|
||||
|
||||
$.get(path, function(contents) {
|
||||
done(contents);
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Using an Engine and Transport Override for JST ###
|
||||
|
||||
Whatever you decide to return as a template in `fetch`, can be used in the
|
||||
`render` method.
|
||||
|
||||
``` javascript
|
||||
Backbone.LayoutManager.configure({
|
||||
fetch: function(name) {
|
||||
return window.JST[name];
|
||||
},
|
||||
|
||||
render: function(template, context) {
|
||||
return template(context);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Release History ##
|
||||
|
||||
### 0.1.2 ###
|
||||
|
||||
* Major patch for re-rendering layouts that would lose events
|
||||
* Added new API method detach
|
||||
|
||||
### 0.1.1 ###
|
||||
|
||||
* Minor patch release (that removes an undocumented feature)
|
||||
* Fixed issue with events not being registered correctly within lists
|
||||
|
||||
### 0.1.0 ###
|
||||
|
||||
* Lots of bug fixes!
|
||||
* Ability to insert views dynamically using the new `view.insert` method. Useful for collections.
|
||||
* Setting/resetting sub views possible with new `view.setViews` method.
|
||||
* All views now have the `view/setViews` methods.
|
||||
* Updates to allow LayoutManager to be extended easier, along with `events`
|
||||
being bound automatically during initialization.
|
||||
|
||||
### 0.0.4 ###
|
||||
|
||||
* Adding views turned into a reusable function called `view`.
|
||||
* Templates no longer are required to be a string, this allows passing of compiled template functions.
|
||||
|
||||
### 0.0.3 ###
|
||||
|
||||
* View renders are internally wrapped to expose a new `render()` method that when called, re-renders.
|
||||
* Nested views are possible by adding a `views` sub property which is an object that contains nested views.
|
||||
|
||||
### 0.0.2 ###
|
||||
|
||||
* Changed layout `name` property to `template` for consistency.
|
||||
* Internal second deferred replaces the `viewDeferred` to determine when an element enters the DOM.
|
||||
|
||||
### 0.0.1 ###
|
||||
|
||||
* Open sourced on GitHub.
|
||||
* Initial unit tests.
|
||||
* Released introduction screencast.
|
||||
Run `grunt` inside a terminal in the project directory to run the tests in both
|
||||
a headless browser and Node.js environment.
|
||||
|
||||
[travis-url]: http://travis-ci.org/tbranyen/backbone.layoutmanager
|
||||
[travis-image]: https://img.shields.io/travis/tbranyen/backbone.layoutmanager.svg
|
||||
[npm-url]: https://npmjs.org/package/backbone.layoutmanager
|
||||
[npm-image]: https://img.shields.io/npm/v/backbone.layoutmanager.svg
|
||||
[coveralls-url]: https://coveralls.io/r/tbranyen/backbone.layoutmanager
|
||||
[coveralls-image]: https://img.shields.io/coveralls/tbranyen/backbone.layoutmanager.svg
|
||||
|
||||
+1050
-405
File diff suppressed because it is too large
Load Diff
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "layoutmanager",
|
||||
"version": "1.0.0",
|
||||
"main": "backbone.layoutmanager.js",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.6",
|
||||
"underscore": ">=1.5",
|
||||
"backbone": "~1.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = ->
|
||||
#@loadNpmTasks "grunt-benchmark"
|
||||
|
||||
@config "benchmark",
|
||||
options:
|
||||
displayResults: true
|
||||
|
||||
all:
|
||||
src: ["test/benchmark/*.js"]
|
||||
dest: "test/report/benchmark_results.csv"
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = ->
|
||||
@loadNpmTasks "grunt-browserify"
|
||||
|
||||
@config "browserify",
|
||||
|
||||
dist:
|
||||
options:
|
||||
browserifyOptions:
|
||||
standalone: 'browserifyLM'
|
||||
|
||||
src: 'backbone.layoutmanager.js',
|
||||
dest: 'test/tmp/backbone.layoutmanager.browserify.js'
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = ->
|
||||
@loadNpmTasks "grunt-contrib-clean"
|
||||
|
||||
@config "clean", [
|
||||
"test/report"
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = ->
|
||||
@loadNpmTasks "grunt-jscs"
|
||||
|
||||
@config "jscs",
|
||||
src: "backbone.layoutmanager.js"
|
||||
@@ -0,0 +1,37 @@
|
||||
module.exports = ->
|
||||
@loadNpmTasks "grunt-contrib-jshint"
|
||||
|
||||
@config "jshint",
|
||||
files: ["backbone.layoutmanager.js"]
|
||||
options: @file.readJSON ".jshintrc"
|
||||
|
||||
node:
|
||||
files:
|
||||
src: ["node/index.js"]
|
||||
|
||||
options:
|
||||
node: true
|
||||
|
||||
test:
|
||||
files:
|
||||
src: ["test/spec/*.js", "test/util/*.js"]
|
||||
|
||||
options:
|
||||
maxlen: false
|
||||
globals:
|
||||
global: true
|
||||
$: true
|
||||
Backbone: true
|
||||
_: true
|
||||
require: true
|
||||
QUnit: true
|
||||
start: true
|
||||
stop: true
|
||||
ok: true
|
||||
equal: true
|
||||
deepEqual: true
|
||||
notEqual: true
|
||||
asyncTest: true
|
||||
test: true
|
||||
expect: true
|
||||
testUtil: true
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = ->
|
||||
@loadNpmTasks "grunt-nodequnit"
|
||||
|
||||
@config "nodequnit",
|
||||
files: ["test/spec/*.js", "!test/spec/dom.js"]
|
||||
|
||||
options:
|
||||
deps: ["test/util/util.js"]
|
||||
code: "."
|
||||
testsDir: "test/spec/"
|
||||
@@ -0,0 +1,16 @@
|
||||
module.exports = ->
|
||||
@loadNpmTasks "grunt-qunit-istanbul"
|
||||
|
||||
@config "qunit",
|
||||
options:
|
||||
"--web-security": "no"
|
||||
|
||||
coverage:
|
||||
src: ["backbone.layoutmanager.js"]
|
||||
instrumentedFiles: "test/tmp/coverage"
|
||||
htmlReport: "test/report/coverage"
|
||||
coberturaReport: "test/report"
|
||||
lcovReport: "test/report"
|
||||
linesThresholdPct: 85
|
||||
|
||||
files: ["test/index.html"]
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
(function(a,b,c){function g(a){b.each(a,function(a,b){this.view(b,a)},this)}function h(a,c){function i(a){var b=f.deferred();return b.async=function(){return b._isAsync=!0,a},b.partial=f.deferred(),b}function j(a){function n(b,e){d.cache(c,e),f.html(a.el,f.render(e,b)),j.partial.resolve(a.el),l(a,a.views)}var c,j,k,m;return a.views={},{insert:function(b,c){var d=a.views,e=d[b]=d[b]||[];return e.push(c),c.view=h,c.setViews=g,c},render:function(g){return!g&&b.isFunction(a.serialize)?g=a.serialize.call(a):!g&&b.isObject(a.serialize)&&(g=a.serialize),j=i(b.bind(n,e,g)),k=f.paths&&f.paths.template||"",b.isString(a.template)&&(c=k+a.template),(m=d.cache(c))?(n(g,m,c),j):(b.isString(a.template)?m=f.fetch.call(j,k+a.template):m=f.fetch.call(j,a.template),j._isAsync||n(g,m),j)}}}function k(a,c,d){var e,g=d.render;return function(){var h=g.call(d,j);return e?(b.isObject(d.options.views)&&l(d,d.options.views),h.partial.then(function(a){return h.resolve(a)}),h):(h.partial.then(function(g){f.partial(a.el,c,g,d.options.append),e=!0,h.resolve(d.el),b.isObject(d.options.views)&&l(d,d.options.views)}),h)}}function l(a,c){function d(b,c,d){b.remove(),b.render._wrapped||(b.render=k(a,c,b),b.render._wrapped=!0),b.render().then(d).then(function(){b.delegateEvents()})}b.each(c,function(a,c){function e(a){var b=a.shift();b.options.append=!0,d(b,c,function(){a.length&&e(a)})}b.isArray(a)?e(b.clone(a)):d(a,c)})}var e=this,f=b.extend({},this.options,d.prototype.options);return c.render._wrapped||(c.render=k(e,a,c),c.render._wrapped=!0),c.view=h,c.setViews=g,this.views[a]=c}var d,e,f;d=a.LayoutManager=a.View.extend({initialize:function(){var c,d,e={},f=this;b.isFunction(this.options.views)?(b.extend(e,this.options.views.call(this)),delete this.options.views):b.isObject(this.options.views)&&(b.extend(e,this.options.views),delete this.options.views),this.views={},b.each(e,function(a,b){f.view(b,a)}),this.options=b.extend({},a.LayoutManager,this.options),b.isFunction(this.options.initialize)&&this.options.initialize.apply(this,arguments),b.bindAll(this),b.each(["template","serialize"],function(a){!this.options[a]&&this[a]&&(this.options[a]=this[a])},this),this.options.events&&(this.events=this.options.events,delete this.options.events,b.each(this.events,function(a){this[a]=this.options[a],delete this.options[a]},this),this.delegateEvents())},view:h,setViews:g,render:function(a){function j(a){var b=i.deferred();return b.async=function(){return b._isAsync=!0,a},b.partial=i.deferred(),b}function k(c){var e={};d.cache(f,c),b.isFunction(i.serialize)?e=i.serialize.call(h):b.isObject(i.serialize)&&(e=i.serialize),i.html(h.el,i.render(c,e)),i.detach(h.el),b.each(h.views,function(a){a.render()}),a(h.el)}var c,e,f,g,h=this,i=this.options;e=i.paths&&i.paths.layout||"",b.isString(i.template)?f=e+i.template:f=i.template;if(c=d.cache(f))return k(c);g=j(k),c=i.fetch.call(g,f),g._isAsync||k(c)}},{_cache:{},cache:function(a,b){if(a in this._cache)return this._cache[a];if(a!=null&&b!=null)return this._cache[a]=b},configure:function(c){var d=a.LayoutManager.prototype.options;b.isObject(c)&&b.extend(d,c)},View:a.View.extend({render:function(a){return a(this).render()}})}),a.LayoutManager.prototype.options={paths:{},deferred:function(){return c.Deferred()},fetch:function(a){return c(a).html()},partial:function(a,b,d,e){e?this.append(c(a).find(b),d):this.html(c(a).find(b),d)},html:function(a,b){c(a).html(b)},append:function(a,b){c(a).append(b)},detach:function(a){c(a).detach()},render:function(a,c){return b.template(a)(c)}}})(this.Backbone,this._,this.jQuery)
|
||||
@@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>01.Layout</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="template" type="template" id="layout">
|
||||
<h1>Hello world!</h1>
|
||||
<p></p>
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Create a new Layout.
|
||||
var layout = new Backbone.Layout({
|
||||
// Attach the Layout to the main container.
|
||||
el: ".main",
|
||||
|
||||
// Use the previous defined template.
|
||||
template: "#layout"
|
||||
});
|
||||
|
||||
// Render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="02.View.html">Next example: Add a nested View.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,60 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>02.View</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="template" type="template" id="layout">
|
||||
<h1>Hello world!</h1>
|
||||
<p></p>
|
||||
</script>
|
||||
|
||||
<!-- View template -->
|
||||
<script class="template" type="template" id="view">
|
||||
This is some content...
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Create a View to be used with the Layout below.
|
||||
var View = Backbone.Layout.extend({
|
||||
template: "#view"
|
||||
});
|
||||
|
||||
// Create a new Layout.
|
||||
var layout = new Backbone.Layout({
|
||||
// Attach the Layout to the main container.
|
||||
el: ".main",
|
||||
|
||||
// Use the previous defined template.
|
||||
template: "#layout",
|
||||
|
||||
// Declaratively bind a nested View to the Layout.
|
||||
views: {
|
||||
"p": new View()
|
||||
}
|
||||
});
|
||||
|
||||
// Render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="03.Events.html">Next example: Adding events.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>03.Events</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="template" type="template" id="layout">
|
||||
<h1>Hello world!</h1>
|
||||
<p></p>
|
||||
</script>
|
||||
|
||||
<!-- View template -->
|
||||
<script class="template" type="template" id="view">
|
||||
This is some content...
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Create a View to be used with the Layout below.
|
||||
var View = Backbone.Layout.extend({
|
||||
template: "#view",
|
||||
|
||||
// When you click the View contents, it will wrap them in a bold tag.
|
||||
events: {
|
||||
"click": "wrapElement"
|
||||
},
|
||||
|
||||
wrapElement: function() {
|
||||
this.$el.wrap("<b>");
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new Layout.
|
||||
var layout = new Backbone.Layout({
|
||||
// Attach the Layout to the main container.
|
||||
el: ".main",
|
||||
|
||||
// Use the previous defined template.
|
||||
template: "#layout",
|
||||
|
||||
// Declaratively bind a nested View to the Layout.
|
||||
views: {
|
||||
"p": new View()
|
||||
}
|
||||
});
|
||||
|
||||
// Render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="04.Append.html">Next example: Appending a View.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,89 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>04.Append</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="template" type="template" id="layout">
|
||||
<h1>Hello world!</h1>
|
||||
<p></p>
|
||||
</script>
|
||||
|
||||
<!-- View template -->
|
||||
<script class="template" type="template" id="view">
|
||||
This is some content...
|
||||
</script>
|
||||
|
||||
<!-- Display template. -->
|
||||
<script class="template" type="template" id="display">
|
||||
What a twist!
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
var DisplayView = Backbone.View.extend({
|
||||
manage: true,
|
||||
|
||||
template: "#display"
|
||||
});
|
||||
|
||||
// Create a View to be used with the Layout below.
|
||||
var View = Backbone.Layout.extend({
|
||||
template: "#view",
|
||||
|
||||
// When you click the View contents, it will wrap them in a bold tag.
|
||||
events: {
|
||||
"click": "wrapElement",
|
||||
"mouseenter": "insertElement",
|
||||
"mouseleave": "removeElement"
|
||||
},
|
||||
|
||||
wrapElement: function() {
|
||||
this.$el.wrap("<b>");
|
||||
},
|
||||
|
||||
insertElement: function() {
|
||||
this.insertView(new DisplayView()).render();
|
||||
},
|
||||
|
||||
removeElement: function() {
|
||||
// Removes the inserted DisplayView.
|
||||
this.removeView("");
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new Layout.
|
||||
var layout = new Backbone.Layout({
|
||||
// Attach the Layout to the main container.
|
||||
el: ".main",
|
||||
|
||||
// Use the previous defined template.
|
||||
template: "#layout",
|
||||
|
||||
// Declaratively bind a nested View to the Layout.
|
||||
views: {
|
||||
"p": new View()
|
||||
}
|
||||
});
|
||||
|
||||
// Render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,57 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>01.Augment</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Content template. -->
|
||||
<script class="template" type="template" id="content">
|
||||
This is some content...
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Setting this option augments `Backbone.View` to work like `Layout`.
|
||||
Backbone.Layout.configure({ manage: true });
|
||||
|
||||
// Now you can create a nested View that is managed by LayoutManager and
|
||||
// has all the benefits.
|
||||
var View = Backbone.View.extend({
|
||||
template: "#content",
|
||||
|
||||
// Override the render method with a custom syntax.
|
||||
render: function(template, context) {
|
||||
return template(context);
|
||||
},
|
||||
|
||||
// Once the View has finished render, stick it in the Document.
|
||||
afterRender: function() {
|
||||
this.$el.appendTo(".main");
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize the View.
|
||||
var view = new View();
|
||||
|
||||
// Render it!
|
||||
view.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="02.Swap_Layout.html">Next example: Swap Layout.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,93 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>02.Swap Layouts</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Content template. -->
|
||||
<script class="template" type="template" id="content">
|
||||
This is some content...
|
||||
</script>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="layout" type="template" id="hello">
|
||||
<h1>Hello</h1>
|
||||
<p></p>
|
||||
|
||||
<button>Swap</button>
|
||||
</script>
|
||||
|
||||
<!-- Another Layout template. -->
|
||||
<script class="layout" type="template" id="goodbye">
|
||||
<h1>Goodbye</h1>
|
||||
<p></p>
|
||||
<button>Swap back</button>
|
||||
</script>
|
||||
|
||||
<!-- Content template. -->
|
||||
<script class="template" type="template" id="item">
|
||||
Heyo
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Setting this option augments `Backbone.View` to work like `Layout`.
|
||||
Backbone.Layout.configure({ manage: true });
|
||||
|
||||
// This will be inserted into both Layouts.
|
||||
var ContentView = Backbone.View.extend({
|
||||
template: "#content"
|
||||
});
|
||||
|
||||
// The Layout that swaps between templates.
|
||||
var layout = new Backbone.Layout({
|
||||
template: "#hello",
|
||||
|
||||
// Insert the nested View into the paragraph tag.
|
||||
views: {
|
||||
p: new ContentView()
|
||||
},
|
||||
|
||||
events: {
|
||||
"click button": "swap"
|
||||
},
|
||||
|
||||
swap: function(ev) {
|
||||
// Toggle the layout template.
|
||||
if (this.template === "#hello") {
|
||||
this.template = "#goodbye";
|
||||
} else {
|
||||
this.template = "#hello";
|
||||
}
|
||||
|
||||
// Re-render with the new template.
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
|
||||
// Attach the Layout to the main container.
|
||||
layout.$el.appendTo(".main");
|
||||
|
||||
// Initially render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="03.Remove_View_Element.html">Next example: Remove View Element.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>03.Remove View Element</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Click on this to remove the View from the Document. -->
|
||||
<button>Remove View</button>
|
||||
|
||||
<!-- Content template. -->
|
||||
<script class="template" type="template" id="content">
|
||||
<!-- Example comment. -->
|
||||
|
||||
<h1>My super important list...</h1>
|
||||
<p>Sup</p>
|
||||
|
||||
Example Text Node
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Setting this option augments `Backbone.View` to work like `Layout`.
|
||||
Backbone.Layout.configure({ manage: true });
|
||||
|
||||
// This will be inserted into both Layouts.
|
||||
var ContentView = Backbone.View.extend({
|
||||
template: "#content",
|
||||
|
||||
events: {
|
||||
"click": "clicked"
|
||||
},
|
||||
|
||||
clicked: function() {
|
||||
this.$el.fadeOut().fadeIn();
|
||||
},
|
||||
|
||||
// Dynamically create a View without a container element.
|
||||
views: {
|
||||
p: new Backbone.Layout({
|
||||
template: _.template("<span>My Test</span><b>lol</b>"),
|
||||
el: false
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
var contentView = new ContentView();
|
||||
|
||||
// Attach the View to the main container.
|
||||
contentView.$el.appendTo(".main");
|
||||
|
||||
// Initially render the Content View.
|
||||
contentView.render();
|
||||
|
||||
// When the button is clicked, remove the nested View.
|
||||
$("button").click(function() {
|
||||
contentView.getView("p").remove();
|
||||
|
||||
// Remove this as well.
|
||||
$(this).remove();
|
||||
});
|
||||
</script>
|
||||
|
||||
<h3><a href="04.jQuery_Plugins.html">Next example: jQuery Plugins.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,85 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>04.jQuery Plugins</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="layout" type="template" id="layout">
|
||||
<h1>Hello</h1>
|
||||
<p></p>
|
||||
</script>
|
||||
|
||||
<!-- Content template. -->
|
||||
<script class="template" type="template" id="content">
|
||||
<span></span>
|
||||
</script>
|
||||
|
||||
<!-- Sub Content template. -->
|
||||
<script class="template" type="template" id="sub-content">
|
||||
<span>I'm sub-content!</span>
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Setting this option augments `Backbone.View` to work like `Layout`.
|
||||
Backbone.Layout.configure({ manage: true });
|
||||
|
||||
// This will be inserted into both Layouts.
|
||||
var ContentView = Backbone.View.extend({
|
||||
template: "#content",
|
||||
|
||||
// Initialize your jQuery plugins here.
|
||||
afterRender: function() {
|
||||
console.log("Content width: " + $(this.el).width());
|
||||
}
|
||||
});
|
||||
|
||||
var SubContentView = Backbone.View.extend({
|
||||
template: "#sub-content",
|
||||
|
||||
afterRender: function() {
|
||||
console.log("Sub-content width: " + $(this.el).width());
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new Layout with a sub view for content.
|
||||
var layout = new Backbone.Layout({
|
||||
template: "#layout",
|
||||
|
||||
// This will place the contents of the ContentView into the Layout's
|
||||
// `<p>`.
|
||||
views: {
|
||||
p: new ContentView({
|
||||
views: {
|
||||
span: new SubContentView()
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// Attach the Layout to the main container.
|
||||
layout.$el.appendTo(".main");
|
||||
|
||||
// Initially render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="05.No_Template.html">Next example: No Template.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>05.No Template</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
var Layout = Backbone.Layout.extend({
|
||||
// Use the `.main` element.
|
||||
el: ".main",
|
||||
|
||||
// Change the content in `beforeRender`.
|
||||
beforeRender: function() {
|
||||
this.$el.html("Hello World!");
|
||||
},
|
||||
|
||||
// Change the content *after* rendering.
|
||||
afterRender: function() {
|
||||
this.$el.append(" How ya doin'?");
|
||||
}
|
||||
});
|
||||
|
||||
var layout = new Layout();
|
||||
|
||||
// Render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
|
||||
<h3><a href="06.Override_Render.html">Next example: Override Render.</a></h3>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,64 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<title>06.Override Render</title>
|
||||
<link rel="stylesheet" href="../_assets/theme.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout will be inserted here. -->
|
||||
<div class="main"></div>
|
||||
|
||||
<!-- Layout template. -->
|
||||
<script class="layout" type="template" id="layout">
|
||||
<h1>Hello</h1>
|
||||
<p></p>
|
||||
</script>
|
||||
|
||||
<!-- Content template. -->
|
||||
<script class="template" type="template" id="content">
|
||||
This is some content...
|
||||
</script>
|
||||
|
||||
<!-- Dependencies. -->
|
||||
<script src="../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="../../bower_components/underscore/underscore.js"></script>
|
||||
<script src="../../bower_components/backbone/backbone.js"></script>
|
||||
|
||||
<!-- LayoutManager library. -->
|
||||
<script src="../../backbone.layoutmanager.js"></script>
|
||||
|
||||
<!-- Example code. -->
|
||||
<script contenteditable="true">
|
||||
// Create a Content view to be used with the Layout below.
|
||||
var ContentView = Backbone.Layout.extend({
|
||||
template: "#content"
|
||||
});
|
||||
|
||||
// Create a new Layout with a sub view for content.
|
||||
var layout = new Backbone.Layout({
|
||||
template: "#layout",
|
||||
|
||||
// This will place the contents of the Content View into the main
|
||||
// Layout's <p></p>.
|
||||
views: {
|
||||
// Appending a new content view using the array syntax
|
||||
p: new ContentView({
|
||||
// Custom render function that reverses everything.
|
||||
render: function(template, context) {
|
||||
return template(context).split("").reverse().join("");
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// Attach the Layout to the main container.
|
||||
layout.$el.appendTo(".main");
|
||||
|
||||
// Render the Layout.
|
||||
layout.render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
<h1>This is my header!</h1>
|
||||
@@ -0,0 +1,25 @@
|
||||
// If you installed LayoutManager globally, you would do:
|
||||
// var LayoutManager = require("backbone.layoutmanager");
|
||||
|
||||
// Since we want this to run without having to install LayoutManager...
|
||||
var LayoutManager = require("../../node");
|
||||
|
||||
// Create a header View and notice that simply providing a path to a template
|
||||
// will automatically load it.
|
||||
var Header = Backbone.Layout.extend({
|
||||
template: "header"
|
||||
});
|
||||
|
||||
var layout = new Backbone.Layout({
|
||||
template: "test",
|
||||
|
||||
views: {
|
||||
"header": new Header({ el: false })
|
||||
}
|
||||
});
|
||||
|
||||
// Render the layout and echo out the contents.
|
||||
layout.render().promise().then(function() {
|
||||
// Should echo out: <header><h1>This is my header!</h1></header>
|
||||
console.log(this.$el.html());
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
<header></header>
|
||||
@@ -0,0 +1,40 @@
|
||||
body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
script[contenteditable=true], script.template {
|
||||
display: block;
|
||||
white-space: pre;
|
||||
border: 1px solid #CCC;
|
||||
padding: 20px;
|
||||
outline: none;
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
font-family: Courier New;
|
||||
font-size: 16px;
|
||||
padding-left: 0px;
|
||||
border-radius: 10px;
|
||||
max-width: 50%;
|
||||
min-width: 820px;
|
||||
position: relative;
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
script[contenteditable=true]:before {
|
||||
content: "Source:";
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
z-index: 10;
|
||||
color: #000;
|
||||
font-family: Helvetica, Arial;
|
||||
}
|
||||
|
||||
/* Hide templates by default. */
|
||||
script.template:before {
|
||||
content: "Template:";
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
z-index: 10;
|
||||
color: #000;
|
||||
font-family: Helvetica, Arial;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*global config:true, task:true*/
|
||||
config.init({
|
||||
lint: {
|
||||
files: ["grunt.js", "backbone.layoutmanager.js"]
|
||||
},
|
||||
min: {
|
||||
"dist/backbone.layoutmanager.min.js": "backbone.layoutmanager.js"
|
||||
},
|
||||
watch: {
|
||||
files: "<config:lint.files>",
|
||||
tasks: "lint test"
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
boss: true,
|
||||
curly: true,
|
||||
eqeqeq: true,
|
||||
immed: false,
|
||||
latedef: true,
|
||||
newcap: true,
|
||||
noarg: true,
|
||||
sub: true,
|
||||
undef: true,
|
||||
eqnull: true,
|
||||
node: true
|
||||
},
|
||||
globals: {}
|
||||
}
|
||||
});
|
||||
|
||||
// Default task.
|
||||
task.registerTask("default", "lint min");
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"node": true
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
var fs = require("fs");
|
||||
|
||||
// Common dependencies to get LayoutManager running.
|
||||
var Backbone = require("backbone");
|
||||
var _ = require("underscore");
|
||||
|
||||
// Using Cheerio instead of jQuery, because Cheerio emulates a loosely
|
||||
// compatible API that we further augment to ensure unit tests pass. It is
|
||||
// also much faster than jsdom.
|
||||
var $ = require("cheerio");
|
||||
|
||||
// Add cheerio hooks
|
||||
Backbone.View.prototype._createElement = function(tagName) {
|
||||
var tag = "<" + tagName + ">" + "</" + tagName + ">";
|
||||
return $.load(tag).root()[0].children[0];
|
||||
};
|
||||
|
||||
// This is to avoid unwanted errors thrown when using
|
||||
// `Backbone.View#setElement`.
|
||||
$.prototype.unbind = $.prototype.off = function() { return this; };
|
||||
|
||||
// Since jQuery is not being used and LayoutManager depends on a Promise
|
||||
// implementation close to jQuery, we use `underscore.deferred` here which
|
||||
// matches jQuery's Deferred API exactly. This is mixed into Cheerio to make
|
||||
// it more seamless.
|
||||
_.extend($, require("underscore.deferred"));
|
||||
|
||||
// Get Backbone and _ into the global scope.
|
||||
_.defaults(global, { Backbone: Backbone, _: _ });
|
||||
|
||||
// Set the Backbone DOM library to be Cheerio.
|
||||
Backbone.$ = $;
|
||||
|
||||
// Include the LayoutManager source, without eval.
|
||||
require("../backbone.layoutmanager");
|
||||
|
||||
// Configure LayoutManager with some very useful defaults for Node.js
|
||||
// environments. This allows the end user to simply consume instead of
|
||||
// fighting with the desirable configuration.
|
||||
Backbone.Layout.configure({
|
||||
// Sensible default for Node.js is to load templates from the filesystem.
|
||||
// This is similar to how we default to script tags in browser-land.
|
||||
fetchTemplate: function(template) {
|
||||
// Automatically add the `.html` extension.
|
||||
template = template + ".html";
|
||||
|
||||
// Put this fetch into `async` mode to work better in the Node environment.
|
||||
var done = this.async();
|
||||
|
||||
// By default read in the file from the filesystem relative to the code
|
||||
// being executed.
|
||||
fs.readFile(template, function(err, contents) {
|
||||
// Ensure the contents are a String.
|
||||
contents = String(contents);
|
||||
|
||||
// Any errors should be reported.
|
||||
if (err) {
|
||||
console.error("Unable to load file " + template + " : " + err);
|
||||
|
||||
return done(null);
|
||||
}
|
||||
|
||||
// Pass the template contents back up.
|
||||
done(_.template(contents));
|
||||
});
|
||||
},
|
||||
// In Node, we likely want templates to render as quickly as possible - using
|
||||
// requestAnimationFrame doesn't make much sense in a Node context.
|
||||
useRAF: false
|
||||
});
|
||||
|
||||
module.exports = Backbone.Layout;
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"author": "Tim Branyen (@tbranyen)",
|
||||
"name": "backbone.layoutmanager",
|
||||
"description": "A layout and template manager for Backbone.js applications.",
|
||||
"version": "1.0.0",
|
||||
"homepage": "http://layoutmanager.org/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/tbranyen/backbone.layoutmanager.git"
|
||||
},
|
||||
"main": "node/index",
|
||||
"files": [
|
||||
"backbone.layoutmanager.js",
|
||||
"node/index.js",
|
||||
"test/"
|
||||
],
|
||||
"browser": "./backbone.layoutmanager.js",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"backbone": "1.2.3",
|
||||
"underscore.deferred": "^0.4.0",
|
||||
"cheerio": "^0.19.0",
|
||||
"underscore": "^1.8.3",
|
||||
"lodash": "^3.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jquery": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "^2.9.34",
|
||||
"browserify": "^10.2.4",
|
||||
"colors": "^1.1.2",
|
||||
"coveralls": "^2.11.2",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-benchmark": "^0.3.0",
|
||||
"grunt-browserify": "^3.8.0",
|
||||
"grunt-contrib-clean": "^0.6.0",
|
||||
"grunt-contrib-jshint": "^0.11.2",
|
||||
"grunt-jscs": "^1.8.0",
|
||||
"grunt-nodequnit": "^0.2.0",
|
||||
"grunt-qunit-istanbul": "^0.4.7",
|
||||
"jquery": "^2.1.4",
|
||||
"qunitjs": "^1.18.0",
|
||||
"requirejs": "^2.1.18"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt"
|
||||
},
|
||||
"jam": {
|
||||
"dependencies": {
|
||||
"underscore": ">=1.5",
|
||||
"backbone": "~1.2",
|
||||
"jquery": ">=1.6"
|
||||
},
|
||||
"include": [
|
||||
"backbone.layoutmanager.js"
|
||||
],
|
||||
"main": "backbone.layoutmanager.js"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"maxlen": 120,
|
||||
"globals": {
|
||||
"Backbone" : true,
|
||||
"QUnit" : true,
|
||||
"asyncTest" : true,
|
||||
"deepEqual" : true,
|
||||
"equal" : true,
|
||||
"expect" : true,
|
||||
"global" : true,
|
||||
"module" : true,
|
||||
"notDeepEqual" : true,
|
||||
"notEqual" : true,
|
||||
"notStrictEqual" : true,
|
||||
"ok" : true,
|
||||
"QUnit" : true,
|
||||
"raises" : true,
|
||||
"start" : true,
|
||||
"stop" : true,
|
||||
"strictEqual" : true,
|
||||
"test" : true,
|
||||
"_" : true
|
||||
},
|
||||
"browser": true,
|
||||
"boss": true,
|
||||
"immed": false,
|
||||
"eqnull": true,
|
||||
"es3": true,
|
||||
"curly": true,
|
||||
"quotmark": "double",
|
||||
"trailing": true,
|
||||
"unused": true,
|
||||
"undef": true
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
exports.name = "Collection rendering";
|
||||
|
||||
exports.tests = {
|
||||
|
||||
"10 models": {
|
||||
maxTime: 2,
|
||||
defer: true,
|
||||
|
||||
setup: function() {
|
||||
|
||||
},
|
||||
|
||||
fn: function(deferred) {
|
||||
setTimeout(function() {
|
||||
deferred.resolve();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
module("configure", {
|
||||
setup: function() {
|
||||
this.options = Backbone.LayoutManager.prototype.options;
|
||||
},
|
||||
teardown: function() {
|
||||
this.options.paths = {};
|
||||
}
|
||||
});
|
||||
|
||||
test("defaults", function() {
|
||||
deepEqual(this.options.paths, {}, "No paths");
|
||||
});
|
||||
|
||||
test("invalid", function() {
|
||||
Backbone.LayoutManager.configure("key", "val");
|
||||
|
||||
equal(this.options.key, undefined, "Invalid assignment");
|
||||
});
|
||||
|
||||
test("global", function() {
|
||||
Backbone.LayoutManager.configure({
|
||||
paths: {
|
||||
template: "/templates/"
|
||||
}
|
||||
});
|
||||
|
||||
equal(this.options.paths.template, "/templates/", "Override paths globally");
|
||||
});
|
||||
|
||||
test("instance", function() {
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "main-layout",
|
||||
|
||||
paths: {
|
||||
layout: "/layouts/"
|
||||
}
|
||||
});
|
||||
|
||||
equal(main.options.paths.layout, "/layouts/", "Override paths locally");
|
||||
|
||||
notEqual(this.options.paths.template, "/templates/", "Do not override globals");
|
||||
});
|
||||
+33
-27
@@ -1,18 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>backbone.layoutmanager Test Suite</title>
|
||||
<link rel="stylesheet" href="qunit/qunit.css">
|
||||
<script src="qunit/qunit.js"></script>
|
||||
<script src="vendor/jquery.js"></script>
|
||||
<script src="vendor/underscore.js"></script>
|
||||
<script src="vendor/backbone.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css">
|
||||
<script src="../node_modules/qunitjs/qunit/qunit.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.js"></script>
|
||||
<script src="../node_modules/underscore/underscore.js"></script>
|
||||
<script src="../node_modules/lodash/index.js"></script>
|
||||
<script>
|
||||
var lodash = _.noConflict();
|
||||
var underscore = _;
|
||||
</script>
|
||||
<script src="../node_modules/backbone/backbone.js"></script>
|
||||
<script src="../node_modules/bluebird/js/browser/bluebird.js"></script>
|
||||
<script src="../backbone.layoutmanager.js"></script>
|
||||
<script src="../node_modules/requirejs/require.js"></script>
|
||||
|
||||
<!-- Util scripts -->
|
||||
<script src="util/util.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
<script src="configure.js"></script>
|
||||
<script src="views.js"></script>
|
||||
<script src="spec/expose.js"></script>
|
||||
<script src="spec/configure.js"></script>
|
||||
<script src="spec/setup.js"></script>
|
||||
<script src="spec/views.js"></script>
|
||||
<script src="spec/dom.js"></script>
|
||||
|
||||
<script type="text/template" id="dom-template">
|
||||
This template lives in the <b>DOM</b>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="test">
|
||||
Sample template.
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="qunit-header">backbone.layoutmanager Test Suite</h1>
|
||||
@@ -24,24 +46,8 @@
|
||||
|
||||
<div id="container" style="display:none"></div>
|
||||
|
||||
<!-- Layouts -->
|
||||
<script id="main" type="layout">
|
||||
<div class="left">Left</div>
|
||||
<div class="right"></div>
|
||||
</script>
|
||||
|
||||
<!-- Templates -->
|
||||
<script id="test" type="template">
|
||||
<span class="inner-left"><%=text%></span>
|
||||
<span class="inner-right"></span>
|
||||
</script>
|
||||
|
||||
<script id="test-sub" type="template">
|
||||
<%=text%>
|
||||
</script>
|
||||
|
||||
<script id="list" type="template">
|
||||
<ul></ul>
|
||||
</script>
|
||||
<div id="prefilled" style="display:none">
|
||||
<span class="test"></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/**
|
||||
* QUnit - A JavaScript Unit Testing Framework
|
||||
*
|
||||
* http://docs.jquery.com/QUnit
|
||||
*
|
||||
* Copyright (c) 2011 John Resig, Jörn Zaefferer
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt)
|
||||
* or GPL (GPL-LICENSE.txt) licenses.
|
||||
*/
|
||||
|
||||
/** Font Family and Sizes */
|
||||
|
||||
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
|
||||
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
|
||||
#qunit-tests { font-size: smaller; }
|
||||
|
||||
|
||||
/** Resets */
|
||||
|
||||
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
/** Header */
|
||||
|
||||
#qunit-header {
|
||||
padding: 0.5em 0 0.5em 1em;
|
||||
|
||||
color: #8699a4;
|
||||
background-color: #0d3349;
|
||||
|
||||
font-size: 1.5em;
|
||||
line-height: 1em;
|
||||
font-weight: normal;
|
||||
|
||||
border-radius: 15px 15px 0 0;
|
||||
-moz-border-radius: 15px 15px 0 0;
|
||||
-webkit-border-top-right-radius: 15px;
|
||||
-webkit-border-top-left-radius: 15px;
|
||||
}
|
||||
|
||||
#qunit-header a {
|
||||
text-decoration: none;
|
||||
color: #c2ccd1;
|
||||
}
|
||||
|
||||
#qunit-header a:hover,
|
||||
#qunit-header a:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#qunit-banner {
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
#qunit-testrunner-toolbar {
|
||||
padding: 0.5em 0 0.5em 2em;
|
||||
color: #5E740B;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#qunit-userAgent {
|
||||
padding: 0.5em 0 0.5em 2.5em;
|
||||
background-color: #2b81af;
|
||||
color: #fff;
|
||||
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
|
||||
}
|
||||
|
||||
|
||||
/** Tests: Pass/Fail */
|
||||
|
||||
#qunit-tests {
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
#qunit-tests li {
|
||||
padding: 0.4em 0.5em 0.4em 2.5em;
|
||||
border-bottom: 1px solid #fff;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#qunit-tests li strong {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#qunit-tests li a {
|
||||
padding: 0.5em;
|
||||
color: #c2ccd1;
|
||||
text-decoration: none;
|
||||
}
|
||||
#qunit-tests li a:hover,
|
||||
#qunit-tests li a:focus {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#qunit-tests ol {
|
||||
margin-top: 0.5em;
|
||||
padding: 0.5em;
|
||||
|
||||
background-color: #fff;
|
||||
|
||||
border-radius: 15px;
|
||||
-moz-border-radius: 15px;
|
||||
-webkit-border-radius: 15px;
|
||||
|
||||
box-shadow: inset 0px 2px 13px #999;
|
||||
-moz-box-shadow: inset 0px 2px 13px #999;
|
||||
-webkit-box-shadow: inset 0px 2px 13px #999;
|
||||
}
|
||||
|
||||
#qunit-tests table {
|
||||
border-collapse: collapse;
|
||||
margin-top: .2em;
|
||||
}
|
||||
|
||||
#qunit-tests th {
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
padding: 0 .5em 0 0;
|
||||
}
|
||||
|
||||
#qunit-tests td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#qunit-tests pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#qunit-tests del {
|
||||
background-color: #e0f2be;
|
||||
color: #374e0c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#qunit-tests ins {
|
||||
background-color: #ffcaca;
|
||||
color: #500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*** Test Counts */
|
||||
|
||||
#qunit-tests b.counts { color: black; }
|
||||
#qunit-tests b.passed { color: #5E740B; }
|
||||
#qunit-tests b.failed { color: #710909; }
|
||||
|
||||
#qunit-tests li li {
|
||||
margin: 0.5em;
|
||||
padding: 0.4em 0.5em 0.4em 0.5em;
|
||||
background-color: #fff;
|
||||
border-bottom: none;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
/*** Passing Styles */
|
||||
|
||||
#qunit-tests li li.pass {
|
||||
color: #5E740B;
|
||||
background-color: #fff;
|
||||
border-left: 26px solid #C6E746;
|
||||
}
|
||||
|
||||
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
|
||||
#qunit-tests .pass .test-name { color: #366097; }
|
||||
|
||||
#qunit-tests .pass .test-actual,
|
||||
#qunit-tests .pass .test-expected { color: #999999; }
|
||||
|
||||
#qunit-banner.qunit-pass { background-color: #C6E746; }
|
||||
|
||||
/*** Failing Styles */
|
||||
|
||||
#qunit-tests li li.fail {
|
||||
color: #710909;
|
||||
background-color: #fff;
|
||||
border-left: 26px solid #EE5757;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#qunit-tests > li:last-child {
|
||||
border-radius: 0 0 15px 15px;
|
||||
-moz-border-radius: 0 0 15px 15px;
|
||||
-webkit-border-bottom-right-radius: 15px;
|
||||
-webkit-border-bottom-left-radius: 15px;
|
||||
}
|
||||
|
||||
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
|
||||
#qunit-tests .fail .test-name,
|
||||
#qunit-tests .fail .module-name { color: #000000; }
|
||||
|
||||
#qunit-tests .fail .test-actual { color: #EE5757; }
|
||||
#qunit-tests .fail .test-expected { color: green; }
|
||||
|
||||
#qunit-banner.qunit-fail { background-color: #EE5757; }
|
||||
|
||||
|
||||
/** Result */
|
||||
|
||||
#qunit-testresult {
|
||||
padding: 0.5em 0.5em 0.5em 2.5em;
|
||||
|
||||
color: #2b81af;
|
||||
background-color: #D2E0E6;
|
||||
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
/** Fixture */
|
||||
|
||||
#qunit-fixture {
|
||||
position: absolute;
|
||||
top: -10000px;
|
||||
left: -10000px;
|
||||
}
|
||||
-1510
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"browser": true,
|
||||
"boss": true,
|
||||
"immed": false,
|
||||
"eqnull": true,
|
||||
"maxlen": 80,
|
||||
"es3": true,
|
||||
"curly": true,
|
||||
"quotmark": "double",
|
||||
"trailing": true,
|
||||
"unused": true,
|
||||
"undef": true,
|
||||
"node": true,
|
||||
"globals": {
|
||||
"asyncTest" : true
|
||||
"deepEqual" : true
|
||||
"equal" : true
|
||||
"expect" : true
|
||||
"module" : true
|
||||
"notDeepEqual" : true
|
||||
"notEqual" : true
|
||||
"notStrictEqual" : true
|
||||
"ok" : true
|
||||
"QUnit" : true
|
||||
"raises" : true
|
||||
"start" : true
|
||||
"stop" : true
|
||||
"strictEqual" : true
|
||||
"test" : true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Test Module: Configure
|
||||
* Ensures that configuration settings have correct defaults, initialization,
|
||||
* overriding, and functionality.
|
||||
*
|
||||
*/
|
||||
QUnit.module("configure", {
|
||||
setup: function() {
|
||||
// Backbone.Layout constructor.
|
||||
this.Layout = Backbone.Layout;
|
||||
|
||||
// Normal Backbone.View.
|
||||
this.View = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
// Set up this View with Layout.
|
||||
Backbone.Layout.setupView(this, options);
|
||||
}
|
||||
});
|
||||
|
||||
// Save a copy of console.warn.
|
||||
this.warn = window.console.warn;
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
// Ensure the prefix object is restored correctly.
|
||||
this.Layout.prototype.prefix = "";
|
||||
|
||||
// Remove `manage: true`.
|
||||
delete this.Layout.prototype.manage;
|
||||
delete Backbone.View.prototype.manage;
|
||||
|
||||
// Remove `el: false`.
|
||||
delete this.Layout.prototype.el;
|
||||
delete Backbone.View.prototype.el;
|
||||
|
||||
// Remove `supressWarnings: true`.
|
||||
delete this.Layout.prototype.suppressWarnings;
|
||||
delete Backbone.View.prototype.suppressWarnings;
|
||||
|
||||
// Not necessary for our testing purposes.
|
||||
window.console.trace = function() {};
|
||||
|
||||
// Restore console.warn.
|
||||
window.console.warn = this.warn;
|
||||
delete this.warn;
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the correct defaults are set for all Layout and View options.
|
||||
test("defaults", 20, function() {
|
||||
// Create a new Layout to test.
|
||||
var layout = new this.Layout();
|
||||
// Create a new Layout to test.
|
||||
var view = new this.View();
|
||||
|
||||
// Paths should be an empty object.
|
||||
deepEqual(layout.prefix, "", "Layout: No prefix");
|
||||
// The deferred property should be a function.
|
||||
ok(_.isFunction(layout.deferred), "Layout: deferred is a function");
|
||||
// The fetchTemplate property should be a function.
|
||||
ok(_.isFunction(layout.fetchTemplate), "Layout: fetchTemplate is a function");
|
||||
// The renderTemplate property should be a function.
|
||||
ok(_.isFunction(layout.renderTemplate), "Layout: renderTemplate is a function");
|
||||
// The partial property should be a function.
|
||||
ok(_.isFunction(layout.partial), "Layout: partial is a function");
|
||||
// The html property should be a function.
|
||||
ok(_.isFunction(layout.html), "Layout: html is a function");
|
||||
// The insert property should be a function.
|
||||
ok(_.isFunction(layout.insert), "Layout: insert is a function");
|
||||
// The append property should be a function.
|
||||
ok(_.isFunction(layout.insert), "Layout: append is a function");
|
||||
// The when property should be a function.
|
||||
ok(_.isFunction(layout.when), "Layout: when is a function");
|
||||
// Paths should be an empty object.
|
||||
deepEqual(view.prefix, "", "View: No prefix");
|
||||
// The deferred property should be a function.
|
||||
ok(_.isFunction(view.deferred), "View: deferred is a function");
|
||||
// The fetchTemplate property should be a function.
|
||||
ok(_.isFunction(view.fetchTemplate), "View: fetchTemplate is a function");
|
||||
// The renderTemplate property should be a function.
|
||||
ok(_.isFunction(view.renderTemplate), "View: renderTemplate is a function");
|
||||
// The partial property should be a function.
|
||||
ok(_.isFunction(view.partial), "View: partial is a function");
|
||||
// The html property should be a function.
|
||||
ok(_.isFunction(view.html), "View: html is a function");
|
||||
// The insert property should be a function.
|
||||
ok(_.isFunction(view.insert), "View: insert is a function");
|
||||
// The append property should be a function.
|
||||
ok(_.isFunction(view.insert), "View: append is a function");
|
||||
// The when property should be a function.
|
||||
ok(_.isFunction(view.when), "View: when is a function");
|
||||
// The serialize property should be a function.
|
||||
ok(_.isFunction(view.serialize), "View: serialize is a function");
|
||||
// RequestAnimationFrame should be used by default, except in node.
|
||||
var isNode = typeof global !== "undefined" &&
|
||||
{}.toString.call(global) == "[object global]";
|
||||
ok(view.useRAF !== isNode, "View: UseRAF should be true in the browser, false in Node.");
|
||||
});
|
||||
|
||||
// Test overriding a single property to ensure propagation works as expected.
|
||||
test("global", 4, function() {
|
||||
// Configure prefix property globally.
|
||||
Backbone.Layout.configure({
|
||||
prefix: "/templates/",
|
||||
|
||||
manage: true
|
||||
});
|
||||
|
||||
// Create a new Layout to test.
|
||||
var layout = new this.Layout();
|
||||
// Create a new View to test.
|
||||
var view = new this.View();
|
||||
|
||||
// The template property set inside prefix should be default for all new
|
||||
// Layouts.
|
||||
equal(layout.prefix, "/templates/",
|
||||
"Layout: Override paths globally for Layouts");
|
||||
// The template property set inside paths should be default for all new
|
||||
// Views.
|
||||
equal(view.prefix, "/templates/",
|
||||
"View: Override paths globally for Views");
|
||||
// Ensure the global configuration was updated to reflect this update.
|
||||
equal(this.Layout.prototype.prefix, "/templates/",
|
||||
"Override globals");
|
||||
// Ensure that `manage: true` works.
|
||||
ok(this.Layout.prototype.manage, "Manage was set.");
|
||||
});
|
||||
|
||||
// Ensure that options can be overwritten at an instance level and make sure
|
||||
// that this does not impact the global configuration.
|
||||
test("override at invocation", 3, function() {
|
||||
// Create a new Layout to test.
|
||||
var layout = new this.Layout({
|
||||
prefix: "/templates/layouts/"
|
||||
});
|
||||
// Create a new View to test.
|
||||
var view = new this.View({
|
||||
prefix: "/templates/raw/"
|
||||
});
|
||||
|
||||
// The prefix property should be successfully overwritten for the
|
||||
// Layout instance.
|
||||
equal(layout.prefix, "/templates/layouts/",
|
||||
"Override paths locally");
|
||||
// The paths.template property should be successfully overwritten for the
|
||||
// View instance.
|
||||
equal(view.prefix, "/templates/raw/",
|
||||
"Override paths locally");
|
||||
// Ensure the global configuration was NOT updated, local change only.
|
||||
notEqual(Backbone.Layout.prototype.prefix,
|
||||
"/templates/", "Do not override globals");
|
||||
});
|
||||
|
||||
// Render broke in 0.5.1 so this test will ensure this always works.
|
||||
asyncTest("override renderTemplate", 1, function() {
|
||||
var hit = false;
|
||||
var layout = new Backbone.Layout({
|
||||
template: _.template(testUtil.templates.main),
|
||||
fetchTemplate: _.identity,
|
||||
|
||||
renderTemplate: function() {
|
||||
hit = true;
|
||||
}
|
||||
});
|
||||
|
||||
layout.render().promise().then(function() {
|
||||
ok(hit, "The renderTemplate method was hit correctly");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("Fetch works on a View during definition", 1, function() {
|
||||
var hit = false;
|
||||
|
||||
var View = Backbone.Layout.extend({
|
||||
// A template is required to hit fetchTemplate.
|
||||
template: "a",
|
||||
|
||||
fetchTemplate: function() {
|
||||
hit = true;
|
||||
}
|
||||
});
|
||||
|
||||
new View().render().promise().then(function() {
|
||||
ok(hit, "Fetch gets called on a View.");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("Fetch works on a View during invocation", 1, function() {
|
||||
var hit = false;
|
||||
|
||||
new Backbone.Layout({
|
||||
// A template is required to hit fetchTemplate.
|
||||
template: "a",
|
||||
|
||||
fetchTemplate: function() {
|
||||
hit = true;
|
||||
}
|
||||
}).render().promise().then(function() {
|
||||
ok(hit, "Fetch gets called on a View.");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
test("Collection should exist on the View", 1, function() {
|
||||
var m = new Backbone.Collection();
|
||||
var D = Backbone.Layout.extend({
|
||||
initialize: function() {
|
||||
this.collection.reset([]);
|
||||
ok(true, "This works!");
|
||||
}
|
||||
});
|
||||
|
||||
var V = Backbone.Layout.extend({
|
||||
template: "<p></p>",
|
||||
|
||||
fetchTemplate: function(path) { return _.template(path); },
|
||||
|
||||
initialize: function() {
|
||||
this.setViews({
|
||||
p: new D({ collection: this.collection })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var v = new V({
|
||||
collection: m
|
||||
});
|
||||
|
||||
v.render();
|
||||
});
|
||||
|
||||
asyncTest("Custom template function", 1, function() {
|
||||
var T = Backbone.Layout.extend({
|
||||
template: function(contents) {
|
||||
return contents;
|
||||
},
|
||||
|
||||
serialize: "hi"
|
||||
});
|
||||
|
||||
new T().render().promise().done(function() {
|
||||
equal(testUtil.trim(this.$el.text()), "hi", "Correct text");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("Default `serialize` implementation", 2, function() {
|
||||
var T = Backbone.Layout.extend({
|
||||
fetchTemplate: function(t) { return t; }
|
||||
});
|
||||
var t = new T({
|
||||
template: _.template("bar")
|
||||
});
|
||||
var t2 = new T({
|
||||
model: new Backbone.Model({ name : "foo" }),
|
||||
template: _.template("<%= name %>")
|
||||
});
|
||||
t.when([
|
||||
t.render().promise().then(function() {
|
||||
equal(t.$el.html(), "bar", "Should not break rendering if no model is in use");
|
||||
}),
|
||||
t2.render().promise().then(function() {
|
||||
equal(t2.$el.html(), "foo", "Should pass model attributes to the template");
|
||||
})
|
||||
]).then(start);
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/201
|
||||
test("Options passed at instance level overwritten by class level options.", 2, function() {
|
||||
var layout = new Backbone.Layout();
|
||||
var TestView = Backbone.View.extend({
|
||||
template: "template-one",
|
||||
lol: "test",
|
||||
|
||||
manage: true
|
||||
});
|
||||
|
||||
layout.setView("", new TestView({ template: "template-two", lol: "hi" }));
|
||||
|
||||
equal(layout.getView("").options.template, "template-two", "Property overwritten properly");
|
||||
equal(layout.getView("").options.lol, "hi", "Property overwritten properly");
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/209
|
||||
asyncTest("If you use 'data' as a variable in a view it won't render", 1, function() {
|
||||
var Test = Backbone.View.extend({
|
||||
manage: true,
|
||||
|
||||
data: {},
|
||||
serialize: { name: "test" },
|
||||
fetchTemplate: _.identity,
|
||||
template: _.template("<%=name%>")
|
||||
});
|
||||
|
||||
new Test().render().promise().done(function() {
|
||||
equal(this.$el.html(), "test", "Correct property set.");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/237
|
||||
asyncTest("View `serialize` not used", 1, function() {
|
||||
// Configure options.
|
||||
Backbone.Layout.configure({
|
||||
serialize: { top: true }
|
||||
});
|
||||
|
||||
// Setup View.
|
||||
var View = Backbone.Layout.extend({
|
||||
template: _.template("<%=top%>"),
|
||||
fetchTemplate: _.identity,
|
||||
serialize: { top: false },
|
||||
|
||||
renderTemplate: function(template, context) {
|
||||
equal(context.top, false, "Local serialize should override configure");
|
||||
start();
|
||||
}
|
||||
});
|
||||
|
||||
// Render the View.
|
||||
new View().render();
|
||||
});
|
||||
|
||||
test("Setting `el: false` globally works as expected", 2, function() {
|
||||
Backbone.Layout.configure({ el: false });
|
||||
|
||||
var l = new Backbone.Layout();
|
||||
equal(l.__manager__.noel, true, "No element was triggered");
|
||||
|
||||
var m = new Backbone.Layout({ el: true });
|
||||
ok(!m.__manager__.noel, "No element was overwritten");
|
||||
});
|
||||
|
||||
test("Setting `suppressWarnings: true` works as expected", 5, function() {
|
||||
// Start by testing that warn works.
|
||||
var l = new Backbone.Layout({
|
||||
template: _.template("<h1></h1><b></b>"),
|
||||
el: false,
|
||||
useRAF: false
|
||||
});
|
||||
|
||||
// Nothing errors if trace is undefined.
|
||||
window.console.trace = undefined;
|
||||
window.console.warn = function() { ok(true, "warn should be called"); };
|
||||
|
||||
l.render();
|
||||
|
||||
// Nothing errors if warn is undefined.
|
||||
window.console.trace = undefined;
|
||||
window.console.warn = undefined;
|
||||
|
||||
l.render();
|
||||
|
||||
window.console.warn = function() { ok(true, "warn should be called"); };
|
||||
window.console.trace = function() { ok(true, "trace should be called"); };
|
||||
|
||||
l.render();
|
||||
|
||||
ok(!l.__manager__.suppressWarnings, "New Views do not suppress warnings by default");
|
||||
|
||||
// Globally configure.
|
||||
Backbone.Layout.configure({ suppressWarnings: true });
|
||||
|
||||
// Now test warn suppression.
|
||||
var m = new Backbone.Layout({
|
||||
template: _.template("<h1></h1><b></b>"),
|
||||
el: false,
|
||||
suppressWarnings: true
|
||||
});
|
||||
|
||||
window.console.warn = function() { ok(false, "warn should not be called"); };
|
||||
window.console.trace = function() { ok(false, "trace should not be called"); };
|
||||
|
||||
m.render();
|
||||
|
||||
ok(m.__manager__.suppressWarnings, "After globally configured, Views suppress warnings.");
|
||||
});
|
||||
|
||||
test("ensure `insertViews` sets a single View correctly", 2, function() {
|
||||
var view = new Backbone.Layout();
|
||||
|
||||
view.insertViews({
|
||||
"": new Backbone.Layout({ test: "property" })
|
||||
});
|
||||
|
||||
ok(_.isArray(view.views[""]), "Ensure the section is an array");
|
||||
equal(view.views[""][0].options.test, "property", "Correct View");
|
||||
});
|
||||
|
||||
})(typeof global !== "undefined" ? global : this);
|
||||
@@ -0,0 +1,457 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
QUnit.module("dom", {
|
||||
setup: function() {
|
||||
this.LM = Backbone.Layout.extend();
|
||||
|
||||
this.SubView = Backbone.Layout.extend({
|
||||
template: _.template(testUtil.templates.testSub),
|
||||
fetchTemplate: _.identity,
|
||||
|
||||
serialize: function() {
|
||||
return { text: "Right" };
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
asyncTest("test default fetchTemplate implementation", 1, function() {
|
||||
// Create a new layout with a sample template.
|
||||
var layout = new this.LM({ template: "#test" });
|
||||
|
||||
// Render and check.
|
||||
layout.render().promise().then(function() {
|
||||
equal(this.$el.html().trim(), "Sample template.", "Correct render output.");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("use layout without a template property", function() {
|
||||
expect(1);
|
||||
|
||||
var main = new Backbone.Layout({
|
||||
el: "#prefilled"
|
||||
});
|
||||
|
||||
main.setViews({
|
||||
".test": new this.SubView()
|
||||
});
|
||||
|
||||
main.render().promise().then(function() {
|
||||
equal(testUtil.trim( this.$(".test").text() ), "Right",
|
||||
"Able to use an existing DOM element");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("afterRender inside Document", function() {
|
||||
var inDocument = false;
|
||||
|
||||
var ProblemView = Backbone.Layout.extend({
|
||||
template: "not-real",
|
||||
|
||||
fetchTemplate: function() {
|
||||
setTimeout(this.async(), 10);
|
||||
},
|
||||
|
||||
afterRender: function() {
|
||||
var doc = document.body;
|
||||
inDocument = this.contains(doc, this.el);
|
||||
|
||||
ok(inDocument, "element in is in the page Document");
|
||||
|
||||
start();
|
||||
}
|
||||
});
|
||||
|
||||
var NestedView = Backbone.Layout.extend({
|
||||
template: "not-real",
|
||||
|
||||
fetchTemplate: function() {
|
||||
setTimeout(this.async(), 10);
|
||||
},
|
||||
|
||||
beforeRender: function() {
|
||||
this.insertView(new ProblemView());
|
||||
}
|
||||
});
|
||||
|
||||
var NewView = Backbone.Layout.extend({
|
||||
template: "not-even-close-to-real",
|
||||
|
||||
fetchTemplate: function() {
|
||||
setTimeout(this.async(), 20);
|
||||
}
|
||||
});
|
||||
|
||||
var newView = new NewView();
|
||||
|
||||
$("body").append(newView.el);
|
||||
|
||||
newView.insertView(new NestedView());
|
||||
newView.render();
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/118
|
||||
asyncTest("events not correctly bound", 1, function() {
|
||||
var hit = false;
|
||||
|
||||
var m = new Backbone.Model();
|
||||
|
||||
var EventView = Backbone.Layout.extend({
|
||||
events: {
|
||||
click: "myFunc"
|
||||
},
|
||||
|
||||
myFunc: function() {
|
||||
hit = true;
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
this.model.off(null, null, this);
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.model.on("change", this.render, this);
|
||||
}
|
||||
});
|
||||
|
||||
var Layout = Backbone.Layout.extend({
|
||||
template: "<p></p>",
|
||||
|
||||
fetchTemplate: function(name) {
|
||||
return _.template(name);
|
||||
},
|
||||
|
||||
beforeRender: function() {
|
||||
this.insertView("p", new EventView({
|
||||
model: m
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
var view = new Layout();
|
||||
|
||||
view.$el.appendTo("#container");
|
||||
|
||||
view.render().promise().then(function() {
|
||||
view.views.p[0].$el.click();
|
||||
|
||||
ok(hit, "Event was fired");
|
||||
start();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/126
|
||||
asyncTest("render works when called late", 1, function() {
|
||||
var hit = false;
|
||||
var A = Backbone.Model.extend({});
|
||||
var ACollection = Backbone.Collection.extend({ model: A });
|
||||
var View = Backbone.Layout.extend({
|
||||
template: "<div>Click Here</div>",
|
||||
className: "hitMe",
|
||||
|
||||
fetchTemplate: function(path) {
|
||||
return _.template(path);
|
||||
},
|
||||
|
||||
events: {
|
||||
click: "onClick"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.collection.on("reset", function() {
|
||||
this.render();
|
||||
}, this);
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
hit = true;
|
||||
}
|
||||
});
|
||||
|
||||
var collection = new ACollection([]);
|
||||
var layout = new Backbone.Layout({
|
||||
template: "<div class='button'></div>",
|
||||
|
||||
fetchTemplate: function(path) {
|
||||
return _.template(path);
|
||||
},
|
||||
|
||||
views: {
|
||||
".button": new View({ collection: collection })
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
layout.render().promise().then(function(){
|
||||
layout.getView(".button").once("afterRender", function(){
|
||||
// Simulate click.
|
||||
layout.$(".hitMe").click();
|
||||
|
||||
ok(hit, "render works as expected when called late");
|
||||
start();
|
||||
});
|
||||
collection.reset();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/126
|
||||
asyncTest("render works when assigned early", 1, function() {
|
||||
var hit = false;
|
||||
var A = Backbone.Model.extend({});
|
||||
var ACollection = Backbone.Collection.extend({ model: A });
|
||||
var View = Backbone.Layout.extend({
|
||||
template: "<div>Click Here</div>",
|
||||
className: "hitMe",
|
||||
|
||||
fetchTemplate: function(path) {
|
||||
return _.template(path);
|
||||
},
|
||||
|
||||
events: {
|
||||
click: "onClick"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.collection.on("reset", this.render, this);
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
hit = true;
|
||||
}
|
||||
});
|
||||
|
||||
var collection = new ACollection([]);
|
||||
var layout = new Backbone.Layout({
|
||||
template: "<div class='button'></div>",
|
||||
|
||||
fetchTemplate: function(path) {
|
||||
return _.template(path);
|
||||
},
|
||||
|
||||
views: {
|
||||
".button": new View({ collection: collection })
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
layout.render().promise().then(function(){
|
||||
layout.getView(".button").once("afterRender", function(){
|
||||
// Simulate click.
|
||||
layout.$(".hitMe").click();
|
||||
|
||||
ok(hit, "render works as expected when assigned early");
|
||||
start();
|
||||
});
|
||||
collection.reset();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/134
|
||||
asyncTest("Ensure events are copied over properly", 1, function() {
|
||||
var hit = false;
|
||||
var layout = new Backbone.Layout({
|
||||
template: "<p></p>",
|
||||
fetchTemplate: function(path) { return _.template(path); },
|
||||
|
||||
events: {
|
||||
"click p": "test"
|
||||
},
|
||||
|
||||
test: function() {
|
||||
hit = true;
|
||||
}
|
||||
});
|
||||
|
||||
layout.render().promise().then(function(){
|
||||
layout.$("p").click();
|
||||
|
||||
ok(hit, "Events were bound and triggered correctly");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/134
|
||||
// https://github.com/tbranyen/backbone.layoutmanager/issues/139
|
||||
asyncTest("events are bound correctly", 1, function() {
|
||||
var hit = 0;
|
||||
|
||||
var l = new Backbone.Layout({
|
||||
template: "<p></p>",
|
||||
fetchTemplate: function(path) { return _.template(path); }
|
||||
});
|
||||
|
||||
l.render();
|
||||
|
||||
var V = Backbone.Layout.extend({
|
||||
keep: true,
|
||||
template: "<span>hey</span>",
|
||||
fetchTemplate: function(path) { return _.template(path); },
|
||||
|
||||
events: {
|
||||
click: "hit"
|
||||
},
|
||||
|
||||
hit: function() {
|
||||
hit++;
|
||||
}
|
||||
});
|
||||
|
||||
// Insert two views.
|
||||
l.insertView("p", new V());
|
||||
l.insertView("p", new V());
|
||||
|
||||
// Render twice.
|
||||
l.render();
|
||||
l.render().promise().then(function() {
|
||||
l.$("p div").trigger("click");
|
||||
|
||||
equal(hit, 2, "Event handler is bound and fired correctly");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("more events issues", 1, function() {
|
||||
var hit = 0;
|
||||
|
||||
var V = Backbone.Layout.extend({
|
||||
template: "<span>hey</span>",
|
||||
fetchTemplate: function(path) { return _.template(path); },
|
||||
|
||||
events: {
|
||||
click: "hit"
|
||||
},
|
||||
|
||||
hit: function() {
|
||||
hit++;
|
||||
}
|
||||
});
|
||||
|
||||
var S = Backbone.Layout.extend({
|
||||
template: "<p></p>",
|
||||
fetchTemplate: function(path) { return _.template(path); },
|
||||
|
||||
beforeRender: function() {
|
||||
// Insert two views.
|
||||
this.insertView("p", new V());
|
||||
this.insertView("p", new V());
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
|
||||
// Create a sub-layout.
|
||||
var s = new S();
|
||||
|
||||
// Create a main layout.
|
||||
var l = new Backbone.Layout({
|
||||
views: {
|
||||
"": s
|
||||
}
|
||||
});
|
||||
|
||||
// Render the layout.
|
||||
l.render().promise().then(function(){
|
||||
s.once("afterRender", function(){
|
||||
l.$("p div").trigger("click");
|
||||
|
||||
equal(hit, 2, "Event handler is bound and fired correctly");
|
||||
start();
|
||||
});
|
||||
|
||||
// Re-render.
|
||||
s.reset();
|
||||
});
|
||||
});
|
||||
|
||||
// Explicitly excersize the default `fetchTemplate` implementation (most tests override
|
||||
// that functionality to use in-memory templates)
|
||||
asyncTest("default `fetchTemplate` method retrieves template from element specified by DOM selector", 1, function() {
|
||||
|
||||
var vyou = new Backbone.Layout({
|
||||
template: "#dom-template"
|
||||
});
|
||||
var expected, actual;
|
||||
|
||||
vyou.render().promise().then(function(){
|
||||
|
||||
expected = "This template lives in the <b>DOM</b>";
|
||||
actual = testUtil.trim(vyou.$el.html());
|
||||
|
||||
equal(actual, expected, "Correctly fetches template string from the DOM");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("events delegated correctly when managing your own view element", 1, function() {
|
||||
var view = new Backbone.View({
|
||||
manage: true, el: false,
|
||||
|
||||
events: { "click": "onClick" },
|
||||
|
||||
onClick: function() {
|
||||
this.clicked = true;
|
||||
},
|
||||
|
||||
template: _.template(testUtil.templates.item),
|
||||
|
||||
serialize: { text: "lol" }
|
||||
});
|
||||
|
||||
view.render().promise().then(function() {
|
||||
view.$el.click();
|
||||
equal(view.clicked, true, "onClick event was fired");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("afterRender callback is triggered too early", 2, function() {
|
||||
var inDocument = false;
|
||||
|
||||
var ProblemView = Backbone.Layout.extend({
|
||||
template: "not-real",
|
||||
|
||||
fetchTemplate: function() {
|
||||
setTimeout(this.async(), 10);
|
||||
},
|
||||
|
||||
afterRender: function() {
|
||||
var doc = document.body;
|
||||
inDocument = $.contains(doc, this.el);
|
||||
|
||||
ok(inDocument, "element in is in the page Document");
|
||||
}
|
||||
});
|
||||
|
||||
var NewView = Backbone.Layout.extend({
|
||||
template: "not-real",
|
||||
|
||||
fetchTemplate: function() {
|
||||
setTimeout(this.async(), 10);
|
||||
},
|
||||
|
||||
beforeRender: function() {
|
||||
this.insertView(new ProblemView());
|
||||
}
|
||||
});
|
||||
|
||||
var newView = new NewView();
|
||||
|
||||
$("body").append(newView.el);
|
||||
|
||||
newView.render().promise().then(function() {
|
||||
newView.render().promise().then(function() {
|
||||
start();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,83 @@
|
||||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Test Module: Expose
|
||||
* Ensures that LayoutManager is exposed properly.
|
||||
*
|
||||
*/
|
||||
QUnit.module("expose", {
|
||||
setup: function() {
|
||||
// Backbone.Layout constructor for convenience.
|
||||
this.Layout = Backbone.Layout;
|
||||
// Backbone.LayoutManager constructor for main usage.
|
||||
this.LayoutManager = Backbone.LayoutManager;
|
||||
}
|
||||
});
|
||||
|
||||
asyncTest("AMD support", 1, function() {
|
||||
var requirejs = require;
|
||||
var useLM = true;
|
||||
|
||||
// Node
|
||||
if (!window.define) {
|
||||
requirejs = require("requirejs");
|
||||
useLM = false;
|
||||
}
|
||||
|
||||
requirejs.config({
|
||||
baseUrl: "../",
|
||||
|
||||
paths: {
|
||||
underscore: "node_modules/underscore/underscore",
|
||||
jquery: "node_modules/jquery/dist/jquery",
|
||||
backbone: "node_modules/backbone/backbone"
|
||||
}
|
||||
});
|
||||
|
||||
if (!useLM) {
|
||||
requirejs.define("backbone.layoutmanager", function() {
|
||||
return require("../../");
|
||||
});
|
||||
}
|
||||
|
||||
requirejs(["backbone.layoutmanager"], function(LayoutManager) {
|
||||
ok(LayoutManager.VERSION, "Version property exists");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
// Ensure the correct defaults are set for all Layout and View options.
|
||||
test("attached", 2, function() {
|
||||
// Layout should be a function.
|
||||
ok(_.isFunction(this.Layout), "Layout shortcut is a function");
|
||||
// LayoutManager should be a function.
|
||||
ok(!_.isFunction(this.LayoutManager),
|
||||
"LayoutManager shortcut is not a function");
|
||||
});
|
||||
|
||||
// Browser only - ensure browserified module can be loaded
|
||||
if (window.define) {
|
||||
asyncTest("Browserify support", 2, function() {
|
||||
var requirejs = require;
|
||||
// Ensure that the browserify-built LM version works.
|
||||
requirejs(["test/tmp/backbone.layoutmanager.browserify"], function(LayoutManager) {
|
||||
ok(LayoutManager.VERSION, "Version property exists");
|
||||
|
||||
// Create a new layout with a sample template.
|
||||
var layout = new LayoutManager({
|
||||
template: testUtil.templates.main
|
||||
});
|
||||
|
||||
// Render and check.
|
||||
layout.render().promise().then(function() {
|
||||
equal(this.$el.html().trim(), "Left", "Correct render output.");
|
||||
start();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
})(typeof global !== "undefined" ? global : this);
|
||||
@@ -0,0 +1,28 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Test Module: Configure
|
||||
* Ensures that configuration settings have correct defaults, initialization,
|
||||
* overriding, and functionality.
|
||||
*
|
||||
*/
|
||||
QUnit.module("configure", {
|
||||
setup: function() {
|
||||
this.LM = require("../../");
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the correct defaults are set for all Layout and View options.
|
||||
asyncTest("can render a basic template", 1, function() {
|
||||
// Create a new layout with a sample template.
|
||||
var layout = new this.LM({ template: "test/templates/test" });
|
||||
|
||||
// Render and check.
|
||||
layout.render().promise().then(function() {
|
||||
equal(this.$el.html().trim(), "Sample template.", "Correct render output.");
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,99 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Test Module: Setup
|
||||
* Ensures that Layouts and Views can be set up correctly to work with
|
||||
* LayoutManager.
|
||||
*
|
||||
*/
|
||||
QUnit.module("setup", {
|
||||
setup: function() {
|
||||
// Backbone.LayoutManager constructor.
|
||||
this.Layout = Backbone.Layout;
|
||||
|
||||
// Enhanced Backbone.View.
|
||||
this.View = Backbone.View.extend({
|
||||
manage: true
|
||||
});
|
||||
|
||||
// Normal Backbone.View.
|
||||
this.NormalView = Backbone.View.extend();
|
||||
|
||||
// Shortcut the setupView function.
|
||||
this.setupView = this.Layout.setupView;
|
||||
}
|
||||
});
|
||||
|
||||
test("layout constructor", 6, function() {
|
||||
var layout = new this.Layout({
|
||||
template: "test"
|
||||
});
|
||||
|
||||
// Is a Backbone.View.
|
||||
ok(layout instanceof Backbone.View, "Is a Backbone.View");
|
||||
// Ensure the layout has a views object container.
|
||||
ok(_.isObject(layout.views), "Contains a views object");
|
||||
// Ensure the layout has a __manager__ object.
|
||||
ok(_.isObject(layout.__manager__), "Contains a __manager__ object");
|
||||
// Has the correct template property set.
|
||||
equal(layout.options.template, "test", "Has the correct template property");
|
||||
// Has the setViews function.
|
||||
ok(_.isFunction(layout.setViews), "Has the setViews function");
|
||||
// Has the view function.
|
||||
ok(_.isFunction(layout.setView), "Has the setView function");
|
||||
});
|
||||
|
||||
test("view setupView", 7, function() {
|
||||
var view = new this.View({
|
||||
template: "test"
|
||||
});
|
||||
|
||||
// Is a Backbone.View.
|
||||
ok(view instanceof Backbone.View, "Is a Backbone.View");
|
||||
// Ensure the view has a views object container.
|
||||
ok(_.isObject(view.views), "Contains a views object");
|
||||
// Ensure the view has a sections object container
|
||||
ok(_.isObject(view.sections), "Contains a sections object");
|
||||
// Ensure the view has a __manager__ object.
|
||||
ok(_.isObject(view.__manager__), "Contains a __manager__ object");
|
||||
// Has the correct template property set.
|
||||
equal(view.options.template, "test", "Has the correct template property");
|
||||
// Has the setViews function.
|
||||
ok(_.isFunction(view.setViews), "Has the setViews function");
|
||||
// Has the view function.
|
||||
ok(_.isFunction(view.setView), "Has the setView function");
|
||||
});
|
||||
|
||||
test("setupView does not copy all options to instance", 1, function() {
|
||||
var view = new Backbone.View({
|
||||
test: "this"
|
||||
});
|
||||
|
||||
Backbone.Layout.setupView(view);
|
||||
|
||||
notEqual(view.test, "this", "View should not have options copied to instance");
|
||||
});
|
||||
|
||||
test("Error exception is properly raised when vanilla View is used", 1, function() {
|
||||
var layout = new this.Layout({
|
||||
template: "test"
|
||||
});
|
||||
|
||||
var view = new this.NormalView();
|
||||
|
||||
try {
|
||||
layout.insertView(view);
|
||||
} catch (ex) {
|
||||
|
||||
equal(ex.message, "The argument associated with selector '' is defined and a View. Set `manage` property to true for Backbone.View instances.", "Correct message");
|
||||
}
|
||||
});
|
||||
|
||||
test("`setView` exists on `Backbone.View` with `manage:true` set", 1, function() {
|
||||
var view = new Backbone.View({ manage: true });
|
||||
|
||||
equal(typeof view.setView, "function", "setView is a function");
|
||||
});
|
||||
|
||||
})();
|
||||
+2845
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
Sample template.
|
||||
@@ -0,0 +1,41 @@
|
||||
/*jshint node:true */
|
||||
var testUtil = {
|
||||
trim: function(str) {
|
||||
return str ? str.replace(/^\s+|\s+$/g, "") : "";
|
||||
},
|
||||
templates: {
|
||||
main: "<div class='left'>Left</div><div class='right'></div>",
|
||||
test: "<span class='inner-left'><%=text%></span><span class" +
|
||||
"='inner-right'></span>",
|
||||
testSub: "<%=text%>",
|
||||
list: "<ul></ul>",
|
||||
item: "<li><%=text%></li>",
|
||||
view0: "<div class='view0'>0</div>",
|
||||
view1: "<div class='view1'>1</div>",
|
||||
view2: "<div class='view2'>2</div>",
|
||||
view3: "<div class='view3'>3</div>",
|
||||
listItem: "<div class='listItem'><%= item %></div>"
|
||||
},
|
||||
inNodeJs: function() {
|
||||
return typeof module !== "undefined" && module.exports;
|
||||
},
|
||||
// isDomNode
|
||||
// Determine if the supplied object is a DOM node (in Node.js, DOM nodes are
|
||||
// simulated by Cheerio objects)
|
||||
isDomNode: function(obj) {
|
||||
if (testUtil.inNodeJs()) {
|
||||
return obj && "type" in obj && "children" in obj && "parent" in obj;
|
||||
} else {
|
||||
return obj && obj.nodeType != null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If this code is running as a Node.js module, attach the utilities to the
|
||||
// module.
|
||||
if (testUtil.inNodeJs()) {
|
||||
exports.testUtil = testUtil;
|
||||
exports.lodash = require("lodash");
|
||||
exports.underscore = require("underscore");
|
||||
exports.bluebird = require("bluebird");
|
||||
}
|
||||
Vendored
-1231
File diff suppressed because it is too large
Load Diff
Vendored
-8981
File diff suppressed because it is too large
Load Diff
Vendored
-839
@@ -1,839 +0,0 @@
|
||||
// Underscore.js 1.1.7
|
||||
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
|
||||
(function() {
|
||||
|
||||
// Baseline setup
|
||||
// --------------
|
||||
|
||||
// Establish the root object, `window` in the browser, or `global` on the server.
|
||||
var root = this;
|
||||
|
||||
// Save the previous value of the `_` variable.
|
||||
var previousUnderscore = root._;
|
||||
|
||||
// Establish the object that gets returned to break out of a loop iteration.
|
||||
var breaker = {};
|
||||
|
||||
// Save bytes in the minified (but not gzipped) version:
|
||||
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
||||
|
||||
// Create quick reference variables for speed access to core prototypes.
|
||||
var slice = ArrayProto.slice,
|
||||
unshift = ArrayProto.unshift,
|
||||
toString = ObjProto.toString,
|
||||
hasOwnProperty = ObjProto.hasOwnProperty;
|
||||
|
||||
// All **ECMAScript 5** native function implementations that we hope to use
|
||||
// are declared here.
|
||||
var
|
||||
nativeForEach = ArrayProto.forEach,
|
||||
nativeMap = ArrayProto.map,
|
||||
nativeReduce = ArrayProto.reduce,
|
||||
nativeReduceRight = ArrayProto.reduceRight,
|
||||
nativeFilter = ArrayProto.filter,
|
||||
nativeEvery = ArrayProto.every,
|
||||
nativeSome = ArrayProto.some,
|
||||
nativeIndexOf = ArrayProto.indexOf,
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||
nativeIsArray = Array.isArray,
|
||||
nativeKeys = Object.keys,
|
||||
nativeBind = FuncProto.bind;
|
||||
|
||||
// Create a safe reference to the Underscore object for use below.
|
||||
var _ = function(obj) { return new wrapper(obj); };
|
||||
|
||||
// Export the Underscore object for **CommonJS**, with backwards-compatibility
|
||||
// for the old `require()` API. If we're not in CommonJS, add `_` to the
|
||||
// global object.
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = _;
|
||||
_._ = _;
|
||||
} else {
|
||||
// Exported as a string, for Closure Compiler "advanced" mode.
|
||||
root['_'] = _;
|
||||
}
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.1.7';
|
||||
|
||||
// Collection Functions
|
||||
// --------------------
|
||||
|
||||
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
||||
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||
if (obj == null) return;
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (obj.length === +obj.length) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
}
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (hasOwnProperty.call(obj, key)) {
|
||||
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Return the results of applying the iterator to each element.
|
||||
// Delegates to **ECMAScript 5**'s native `map` if available.
|
||||
_.map = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
results[results.length] = iterator.call(context, value, index, list);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
||||
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
||||
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
||||
var initial = memo !== void 0;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduce && obj.reduce === nativeReduce) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||
}
|
||||
each(obj, function(value, index, list) {
|
||||
if (!initial) {
|
||||
memo = value;
|
||||
initial = true;
|
||||
} else {
|
||||
memo = iterator.call(context, memo, value, index, list);
|
||||
}
|
||||
});
|
||||
if (!initial) throw new TypeError("Reduce of empty array with no initial value");
|
||||
return memo;
|
||||
};
|
||||
|
||||
// The right-associative version of reduce, also known as `foldr`.
|
||||
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
||||
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
||||
}
|
||||
var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
|
||||
return _.reduce(reversed, iterator, memo, context);
|
||||
};
|
||||
|
||||
// Return the first value which passes a truth test. Aliased as `detect`.
|
||||
_.find = _.detect = function(obj, iterator, context) {
|
||||
var result;
|
||||
any(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Return all the elements that pass a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
||||
// Aliased as `select`.
|
||||
_.filter = _.select = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Return all the elements for which a truth test fails.
|
||||
_.reject = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
each(obj, function(value, index, list) {
|
||||
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Determine whether all of the elements match a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||
// Aliased as `all`.
|
||||
_.every = _.all = function(obj, iterator, context) {
|
||||
var result = true;
|
||||
if (obj == null) return result;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if at least one element in the object matches a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `some` if available.
|
||||
// Aliased as `any`.
|
||||
var any = _.some = _.any = function(obj, iterator, context) {
|
||||
iterator = iterator || _.identity;
|
||||
var result = false;
|
||||
if (obj == null) return result;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (result |= iterator.call(context, value, index, list)) return breaker;
|
||||
});
|
||||
return !!result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using `===`.
|
||||
// Aliased as `contains`.
|
||||
_.include = _.contains = function(obj, target) {
|
||||
var found = false;
|
||||
if (obj == null) return found;
|
||||
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||
any(obj, function(value) {
|
||||
if (found = value === target) return true;
|
||||
});
|
||||
return found;
|
||||
};
|
||||
|
||||
// Invoke a method (with arguments) on every item in a collection.
|
||||
_.invoke = function(obj, method) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (method.call ? method || value : value[method]).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Convenience version of a common use case of `map`: fetching a property.
|
||||
_.pluck = function(obj, key) {
|
||||
return _.map(obj, function(value){ return value[key]; });
|
||||
};
|
||||
|
||||
// Return the maximum element or (element-based computation).
|
||||
_.max = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
var result = {computed : -Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed >= result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Return the minimum element (or element-based computation).
|
||||
_.min = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||
var result = {computed : Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed < result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = function(obj, iterator, context) {
|
||||
return _.pluck(_.map(obj, function(value, index, list) {
|
||||
return {
|
||||
value : value,
|
||||
criteria : iterator.call(context, value, index, list)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Groups the object's values by a criterion produced by an iterator
|
||||
_.groupBy = function(obj, iterator) {
|
||||
var result = {};
|
||||
each(obj, function(value, index) {
|
||||
var key = iterator(value, index);
|
||||
(result[key] || (result[key] = [])).push(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator || (iterator = _.identity);
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
// Safely convert anything iterable into a real, live array.
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return slice.call(iterable);
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
|
||||
// Return the number of elements in an object.
|
||||
_.size = function(obj) {
|
||||
return _.toArray(obj).length;
|
||||
};
|
||||
|
||||
// Array Functions
|
||||
// ---------------
|
||||
|
||||
// Get the first element of an array. Passing **n** will return the first N
|
||||
// values in the array. Aliased as `head`. The **guard** check allows it to work
|
||||
// with `_.map`.
|
||||
_.first = _.head = function(array, n, guard) {
|
||||
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
||||
};
|
||||
|
||||
// Returns everything but the first entry of the array. Aliased as `tail`.
|
||||
// Especially useful on the arguments object. Passing an **index** will return
|
||||
// the rest of the values in the array from that index onward. The **guard**
|
||||
// check allows it to work with `_.map`.
|
||||
_.rest = _.tail = function(array, index, guard) {
|
||||
return slice.call(array, (index == null) || guard ? 1 : index);
|
||||
};
|
||||
|
||||
// Get the last element of an array.
|
||||
_.last = function(array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
_.compact = function(array) {
|
||||
return _.filter(array, function(value){ return !!value; });
|
||||
};
|
||||
|
||||
// Return a completely flattened version of an array.
|
||||
_.flatten = function(array) {
|
||||
return _.reduce(array, function(memo, value) {
|
||||
if (_.isArray(value)) return memo.concat(_.flatten(value));
|
||||
memo[memo.length] = value;
|
||||
return memo;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
return _.difference(array, slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
// been sorted, you have the option of using a faster algorithm.
|
||||
// Aliased as `unique`.
|
||||
_.uniq = _.unique = function(array, isSorted) {
|
||||
return _.reduce(array, function(memo, el, i) {
|
||||
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;
|
||||
return memo;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Produce an array that contains the union: each distinct element from all of
|
||||
// the passed-in arrays.
|
||||
_.union = function() {
|
||||
return _.uniq(_.flatten(arguments));
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
||||
_.intersection = _.intersect = function(array) {
|
||||
var rest = slice.call(arguments, 1);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
return _.indexOf(other, item) >= 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Take the difference between one array and another.
|
||||
// Only the elements present in just the first array will remain.
|
||||
_.difference = function(array, other) {
|
||||
return _.filter(array, function(value){ return !_.include(other, value); });
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
var args = slice.call(arguments);
|
||||
var length = _.max(_.pluck(args, 'length'));
|
||||
var results = new Array(length);
|
||||
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
||||
return results;
|
||||
};
|
||||
|
||||
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
||||
// we need this function. Return the position of the first occurrence of an
|
||||
// item in an array, or -1 if the item is not included in the array.
|
||||
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
||||
// If the array is large and already in sort order, pass `true`
|
||||
// for **isSorted** to use binary search.
|
||||
_.indexOf = function(array, item, isSorted) {
|
||||
if (array == null) return -1;
|
||||
var i, l;
|
||||
if (isSorted) {
|
||||
i = _.sortedIndex(array, item);
|
||||
return array[i] === item ? i : -1;
|
||||
}
|
||||
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||
for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
|
||||
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
||||
_.lastIndexOf = function(array, item) {
|
||||
if (array == null) return -1;
|
||||
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||
var i = array.length;
|
||||
while (i--) if (array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Generate an integer Array containing an arithmetic progression. A port of
|
||||
// the native Python `range()` function. See
|
||||
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
||||
_.range = function(start, stop, step) {
|
||||
if (arguments.length <= 1) {
|
||||
stop = start || 0;
|
||||
start = 0;
|
||||
}
|
||||
step = arguments[2] || 1;
|
||||
|
||||
var len = Math.max(Math.ceil((stop - start) / step), 0);
|
||||
var idx = 0;
|
||||
var range = new Array(len);
|
||||
|
||||
while(idx < len) {
|
||||
range[idx++] = start;
|
||||
start += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
// Function (ahem) Functions
|
||||
// ------------------
|
||||
|
||||
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||
// optionally). Binding with arguments is also known as `curry`.
|
||||
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
||||
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
||||
_.bind = function(func, obj) {
|
||||
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
||||
var args = slice.call(arguments, 2);
|
||||
return function() {
|
||||
return func.apply(obj, args.concat(slice.call(arguments)));
|
||||
};
|
||||
};
|
||||
|
||||
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||
// all callbacks defined on an object belong to it.
|
||||
_.bindAll = function(obj) {
|
||||
var funcs = slice.call(arguments, 1);
|
||||
if (funcs.length == 0) funcs = _.functions(obj);
|
||||
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Memoize an expensive function by storing its results.
|
||||
_.memoize = function(func, hasher) {
|
||||
var memo = {};
|
||||
hasher || (hasher = _.identity);
|
||||
return function() {
|
||||
var key = hasher.apply(this, arguments);
|
||||
return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||
};
|
||||
};
|
||||
|
||||
// Delays a function for the given number of milliseconds, and then calls
|
||||
// it with the arguments supplied.
|
||||
_.delay = function(func, wait) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
};
|
||||
|
||||
// Defers a function, scheduling it to run after the current call stack has
|
||||
// cleared.
|
||||
_.defer = function(func) {
|
||||
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
||||
};
|
||||
|
||||
// Internal function used to implement `_.throttle` and `_.debounce`.
|
||||
var limit = function(func, wait, debounce) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var throttler = function() {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
if (debounce) clearTimeout(timeout);
|
||||
if (debounce || !timeout) timeout = setTimeout(throttler, wait);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function, that, when invoked, will only be triggered at most once
|
||||
// during a given window of time.
|
||||
_.throttle = function(func, wait) {
|
||||
return limit(func, wait, false);
|
||||
};
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
_.debounce = function(func, wait) {
|
||||
return limit(func, wait, true);
|
||||
};
|
||||
|
||||
// Returns a function that will be executed at most one time, no matter how
|
||||
// often you call it. Useful for lazy initialization.
|
||||
_.once = function(func) {
|
||||
var ran = false, memo;
|
||||
return function() {
|
||||
if (ran) return memo;
|
||||
ran = true;
|
||||
return memo = func.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns the first function passed as an argument to the second,
|
||||
// allowing you to adjust arguments, run code before and after, and
|
||||
// conditionally execute the original function.
|
||||
_.wrap = function(func, wrapper) {
|
||||
return function() {
|
||||
var args = [func].concat(slice.call(arguments));
|
||||
return wrapper.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that is the composition of a list of functions, each
|
||||
// consuming the return value of the function that follows.
|
||||
_.compose = function() {
|
||||
var funcs = slice.call(arguments);
|
||||
return function() {
|
||||
var args = slice.call(arguments);
|
||||
for (var i = funcs.length - 1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will only be executed after being called N times.
|
||||
_.after = function(times, func) {
|
||||
return function() {
|
||||
if (--times < 1) { return func.apply(this, arguments); }
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Object Functions
|
||||
// ----------------
|
||||
|
||||
// Retrieve the names of an object's properties.
|
||||
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
||||
_.keys = nativeKeys || function(obj) {
|
||||
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
||||
var keys = [];
|
||||
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
|
||||
return keys;
|
||||
};
|
||||
|
||||
// Retrieve the values of an object's properties.
|
||||
_.values = function(obj) {
|
||||
return _.map(obj, _.identity);
|
||||
};
|
||||
|
||||
// Return a sorted list of the function names available on the object.
|
||||
// Aliased as `methods`
|
||||
_.functions = _.methods = function(obj) {
|
||||
var names = [];
|
||||
for (var key in obj) {
|
||||
if (_.isFunction(obj[key])) names.push(key);
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
if (source[prop] !== void 0) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Fill in a given object with default properties.
|
||||
_.defaults = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
if (obj[prop] == null) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = function(obj) {
|
||||
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
||||
};
|
||||
|
||||
// Invokes interceptor with the obj, and then returns obj.
|
||||
// The primary purpose of this method is to "tap into" a method chain, in
|
||||
// order to perform operations on intermediate results within the chain.
|
||||
_.tap = function(obj, interceptor) {
|
||||
interceptor(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = function(a, b) {
|
||||
// Check object identity.
|
||||
if (a === b) return true;
|
||||
// Different types?
|
||||
var atype = typeof(a), btype = typeof(b);
|
||||
if (atype != btype) return false;
|
||||
// Basic equality test (watch out for coercions).
|
||||
if (a == b) return true;
|
||||
// One is falsy and the other truthy.
|
||||
if ((!a && b) || (a && !b)) return false;
|
||||
// Unwrap any wrapped objects.
|
||||
if (a._chain) a = a._wrapped;
|
||||
if (b._chain) b = b._wrapped;
|
||||
// One of them implements an isEqual()?
|
||||
if (a.isEqual) return a.isEqual(b);
|
||||
if (b.isEqual) return b.isEqual(a);
|
||||
// Check dates' integer values.
|
||||
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
|
||||
// Both are NaN?
|
||||
if (_.isNaN(a) && _.isNaN(b)) return false;
|
||||
// Compare regular expressions.
|
||||
if (_.isRegExp(a) && _.isRegExp(b))
|
||||
return a.source === b.source &&
|
||||
a.global === b.global &&
|
||||
a.ignoreCase === b.ignoreCase &&
|
||||
a.multiline === b.multiline;
|
||||
// If a is not an object by this point, we can't handle it.
|
||||
if (atype !== 'object') return false;
|
||||
// Check for different array lengths before comparing contents.
|
||||
if (a.length && (a.length !== b.length)) return false;
|
||||
// Nothing else worked, deep compare the contents.
|
||||
var aKeys = _.keys(a), bKeys = _.keys(b);
|
||||
// Different object sizes?
|
||||
if (aKeys.length != bKeys.length) return false;
|
||||
// Recursive comparison of contents.
|
||||
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given array or object empty?
|
||||
_.isEmpty = function(obj) {
|
||||
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
||||
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given value a DOM element?
|
||||
_.isElement = function(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// Is a given value an array?
|
||||
// Delegates to ECMA5's native Array.isArray
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) === '[object Array]';
|
||||
};
|
||||
|
||||
// Is a given variable an object?
|
||||
_.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return !!(obj && hasOwnProperty.call(obj, 'callee'));
|
||||
};
|
||||
|
||||
// Is a given value a function?
|
||||
_.isFunction = function(obj) {
|
||||
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
};
|
||||
|
||||
// Is a given value a string?
|
||||
_.isString = function(obj) {
|
||||
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
|
||||
};
|
||||
|
||||
// Is a given value a number?
|
||||
_.isNumber = function(obj) {
|
||||
return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
|
||||
};
|
||||
|
||||
// Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
|
||||
// that does not equal itself.
|
||||
_.isNaN = function(obj) {
|
||||
return obj !== obj;
|
||||
};
|
||||
|
||||
// Is a given value a boolean?
|
||||
_.isBoolean = function(obj) {
|
||||
return obj === true || obj === false;
|
||||
};
|
||||
|
||||
// Is a given value a date?
|
||||
_.isDate = function(obj) {
|
||||
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
|
||||
};
|
||||
|
||||
// Is the given value a regular expression?
|
||||
_.isRegExp = function(obj) {
|
||||
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
|
||||
};
|
||||
|
||||
// Is a given value equal to null?
|
||||
_.isNull = function(obj) {
|
||||
return obj === null;
|
||||
};
|
||||
|
||||
// Is a given variable undefined?
|
||||
_.isUndefined = function(obj) {
|
||||
return obj === void 0;
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
// -----------------
|
||||
|
||||
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
||||
// previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = function() {
|
||||
root._ = previousUnderscore;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Keep the identity function around for default iterators.
|
||||
_.identity = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Run a function **n** times.
|
||||
_.times = function (n, iterator, context) {
|
||||
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||
};
|
||||
|
||||
// Add your own custom functions to the Underscore object, ensuring that
|
||||
// they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = function(obj) {
|
||||
each(_.functions(obj), function(name){
|
||||
addToWrapper(name, _[name] = obj[name]);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a unique integer id (unique within the entire client session).
|
||||
// Useful for temporary DOM ids.
|
||||
var idCounter = 0;
|
||||
_.uniqueId = function(prefix) {
|
||||
var id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
};
|
||||
|
||||
// By default, Underscore uses ERB-style template delimiters, change the
|
||||
// following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
evaluate : /<%([\s\S]+?)%>/g,
|
||||
interpolate : /<%=([\s\S]+?)%>/g
|
||||
};
|
||||
|
||||
// JavaScript micro-templating, similar to John Resig's implementation.
|
||||
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
||||
// and correctly escapes quotes within interpolated code.
|
||||
_.template = function(str, data) {
|
||||
var c = _.templateSettings;
|
||||
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||
'with(obj||{}){__p.push(\'' +
|
||||
str.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(c.interpolate, function(match, code) {
|
||||
return "'," + code.replace(/\\'/g, "'") + ",'";
|
||||
})
|
||||
.replace(c.evaluate || null, function(match, code) {
|
||||
return "');" + code.replace(/\\'/g, "'")
|
||||
.replace(/[\r\n\t]/g, ' ') + "__p.push('";
|
||||
})
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
+ "');}return __p.join('');";
|
||||
var func = new Function('obj', tmpl);
|
||||
return data ? func(data) : func;
|
||||
};
|
||||
|
||||
// The OOP Wrapper
|
||||
// ---------------
|
||||
|
||||
// If Underscore is called as a function, it returns a wrapped object that
|
||||
// can be used OO-style. This wrapper holds altered versions of all the
|
||||
// underscore functions. Wrapped objects may be chained.
|
||||
var wrapper = function(obj) { this._wrapped = obj; };
|
||||
|
||||
// Expose `wrapper.prototype` as `_.prototype`
|
||||
_.prototype = wrapper.prototype;
|
||||
|
||||
// Helper function to continue chaining intermediate results.
|
||||
var result = function(obj, chain) {
|
||||
return chain ? _(obj).chain() : obj;
|
||||
};
|
||||
|
||||
// A method to easily add functions to the OOP wrapper.
|
||||
var addToWrapper = function(name, func) {
|
||||
wrapper.prototype[name] = function() {
|
||||
var args = slice.call(arguments);
|
||||
unshift.call(args, this._wrapped);
|
||||
return result(func.apply(_, args), this._chain);
|
||||
};
|
||||
};
|
||||
|
||||
// Add all of the Underscore functions to the wrapper object.
|
||||
_.mixin(_);
|
||||
|
||||
// Add all mutator Array functions to the wrapper.
|
||||
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
method.apply(this._wrapped, arguments);
|
||||
return result(this._wrapped, this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Add all accessor Array functions to the wrapper.
|
||||
each(['concat', 'join', 'slice'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Start chaining a wrapped Underscore object.
|
||||
wrapper.prototype.chain = function() {
|
||||
this._chain = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Extracts the result from a wrapped and chained object.
|
||||
wrapper.prototype.value = function() {
|
||||
return this._wrapped;
|
||||
};
|
||||
|
||||
})();
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
function isNode(obj) {
|
||||
if (obj && obj.nodeType != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module("views", {
|
||||
setup: function() {
|
||||
var setup = this;
|
||||
|
||||
// Custom View
|
||||
this.View = Backbone.LayoutManager.View.extend({
|
||||
template: "#test",
|
||||
|
||||
serialize: function() {
|
||||
return { text: this.msg };
|
||||
},
|
||||
|
||||
initialize: function(opts) {
|
||||
this.msg = opts.msg;
|
||||
}
|
||||
});
|
||||
|
||||
this.SubView = Backbone.LayoutManager.View.extend({
|
||||
template: "#test-sub",
|
||||
|
||||
serialize: function() {
|
||||
return { text: "Right" };
|
||||
}
|
||||
});
|
||||
|
||||
this.ListView = Backbone.View.extend({
|
||||
template: "#list",
|
||||
|
||||
render: function(layout) {
|
||||
var view = layout(this);
|
||||
|
||||
// Iterate over the passed collection and insert into the view
|
||||
_.each(this.collection, function(model) {
|
||||
view.insert("ul", new setup.ItemView({ model: model }));
|
||||
}, this);
|
||||
|
||||
return view.render();
|
||||
}
|
||||
});
|
||||
|
||||
this.ItemView = Backbone.LayoutManager.View.extend({
|
||||
template: "#test-sub",
|
||||
tagName: "li",
|
||||
|
||||
serialize: function() {
|
||||
return this.model;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
asyncTest("render outside defined partial", function() {
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main"
|
||||
});
|
||||
|
||||
main.view(".right", new this.View({ msg: "Right" }));
|
||||
|
||||
main.render(function(el) {
|
||||
var trimmed = $.trim( $(el).find(".inner-left").html() );
|
||||
|
||||
ok(isNode(el), "Contents is a DOM Node");
|
||||
equal(trimmed, "Right", "Correct render");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("render inside defined partial", function() {
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main",
|
||||
|
||||
views: {
|
||||
".right": new this.View({ msg: "Right" })
|
||||
}
|
||||
});
|
||||
|
||||
main.render(function(el) {
|
||||
var trimmed = $.trim( $(el).find(".inner-left").html() );
|
||||
|
||||
ok(isNode(el), "Contents is a DOM Node");
|
||||
equal(trimmed, "Right", "Correct render");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("re-render a view defined after initialization", function(){
|
||||
var trimmed;
|
||||
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main"
|
||||
});
|
||||
|
||||
main.view(".right", new this.View({ msg: "Right" }));
|
||||
|
||||
main.render(function(el) {
|
||||
$('#container').html(el);
|
||||
});
|
||||
|
||||
main.views[".right"].render();
|
||||
trimmed = $.trim( $("#container .inner-left").html() );
|
||||
equal(trimmed, "Right", "Correct re-render");
|
||||
|
||||
main.view(".right", new this.View({ msg: "Right Again" })).render();
|
||||
trimmed = $.trim( $("#container .inner-left").html() );
|
||||
equal(trimmed, "Right Again", "Correct re-render");
|
||||
|
||||
start();
|
||||
})
|
||||
|
||||
asyncTest("nested views", function() {
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main",
|
||||
|
||||
views: {
|
||||
".right": new this.View({
|
||||
msg: "Left",
|
||||
|
||||
views: {
|
||||
".inner-right": new this.SubView()
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
main.render(function(el) {
|
||||
var trimmed = $.trim( $(el).find(".inner-right div").html() );
|
||||
|
||||
ok(isNode(el), "Contents is a DOM Node");
|
||||
equal(trimmed, "Right", "Correct render");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
test("serialize on LayoutManager is a function", function() {
|
||||
var testText = "test text";
|
||||
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#test-sub",
|
||||
serialize: { text: "test text" }
|
||||
});
|
||||
|
||||
main.render(function(el) {
|
||||
equal($.trim( $(el).text() ), testText, "correct serialize");
|
||||
});
|
||||
});
|
||||
|
||||
test("serialize on LayoutManager is an object", function() {
|
||||
var testText = "test text";
|
||||
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#test-sub",
|
||||
serialize: { text: "test text" }
|
||||
});
|
||||
|
||||
main.render(function(el) {
|
||||
equal($.trim( $(el).text() ), testText, "correct serialize");
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("insert views", function() {
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main",
|
||||
|
||||
views: {
|
||||
".right": new this.ListView({
|
||||
collection: [{ text: "one" }, { text: "two" }]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
main.render(function(el) {
|
||||
ok(isNode(el), "Contents is a DOM Node");
|
||||
|
||||
equal($(el).find("ul li").length, 2, "Correct number of nested li's");
|
||||
equal($.trim( $(el).find("ul li:eq(0)").html() ), "one", "Correct first li content");
|
||||
equal($.trim( $(el).find("ul li:eq(1)").html() ), "two", "Correct second li content");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("nested views using setViews", function() {
|
||||
var main = new Backbone.LayoutManager({
|
||||
template: "#main"
|
||||
});
|
||||
|
||||
main.setViews({
|
||||
".right": new this.View({
|
||||
msg: "Left",
|
||||
|
||||
views: {
|
||||
".inner-right": new this.SubView()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
main.render(function(el) {
|
||||
var trimmed = $.trim( $(el).find(".inner-right div").html() );
|
||||
|
||||
ok(isNode(el), "Contents is a DOM Node");
|
||||
equal(trimmed, "Right", "Correct render");
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
test("extend layoutmanager", function() {
|
||||
var testText = "test text";
|
||||
|
||||
var BaseLayout = Backbone.LayoutManager.extend({
|
||||
template: "#test-sub",
|
||||
serialize: { text: "test text" }
|
||||
});
|
||||
|
||||
var main = new BaseLayout();
|
||||
|
||||
main.render(function(el) {
|
||||
equal($.trim( $(el).text() ), testText, "correct serialize");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user