Compare commits
2051 commits
Author | SHA1 | Date | |
---|---|---|---|
|
99fcb408fe | ||
|
479ef0ec82 | ||
|
9e01a1e18b | ||
|
2e3f0923f5 | ||
|
3550dfafd3 | ||
|
12de02938a | ||
|
8f093f54df | ||
|
6bdd186219 | ||
|
c54c292873 | ||
|
4c7d9a2ffa | ||
|
595066abd1 | ||
|
b4e14d4804 | ||
|
be87b47469 | ||
|
62fa378f51 | ||
|
c386eca94d | ||
|
8c6d1f4f88 | ||
|
3cb5152905 | ||
|
5cc76737b4 | ||
|
f312bae614 | ||
|
97d388be7d | ||
|
fd27327737 | ||
|
5c60f669ec | ||
|
6ceac75b7a | ||
|
2387c3afd5 | ||
|
5184ed6c9f | ||
|
b22b689dae | ||
|
138215e22f | ||
|
22e766bd1a | ||
|
337af91373 | ||
|
ced1bde338 | ||
|
dbc72d4d98 | ||
|
994f0e25a4 | ||
|
71c3c8437a | ||
|
6e81bedc39 | ||
|
a9562fbb64 | ||
|
b4c7e75109 | ||
|
301191a0b8 | ||
|
edb422007c | ||
|
f8aad5f237 | ||
|
1286fa4549 | ||
|
03b7c5b9a6 | ||
|
7dfde9da3a | ||
|
ffe12888cf | ||
|
8b30e7a307 | ||
|
306adb9b6a | ||
|
226a44c498 | ||
|
b1feb367b8 | ||
|
cb06a5c0fb | ||
|
9b8b4c2728 | ||
|
d6740dab25 | ||
|
003dd3a9da | ||
|
a1fa790e19 | ||
|
3aaa8fa721 | ||
|
39671f4d78 | ||
|
426700b1c6 | ||
|
6315662078 | ||
|
e193557e56 | ||
|
b8b6a9656a | ||
|
f5f9eab200 | ||
|
3f22b6aa3a | ||
|
641e085259 | ||
|
9527f15f52 | ||
|
865c3884b2 | ||
|
56d0d982da | ||
|
58f4e6a36e | ||
|
ef0f14a03d | ||
|
d31562e34f | ||
|
ad943e5472 | ||
|
4c2b0715af | ||
|
a5c569ab5d | ||
|
8fdd639bf0 | ||
|
a0fcfc20ad | ||
|
15627ee18e | ||
|
33c3d4b380 | ||
|
74ffb66f40 | ||
|
55f53e8369 | ||
|
acb7563257 | ||
|
c06e719198 | ||
|
64bb7e25c7 | ||
|
c392f2d205 | ||
|
45bd3f48f9 | ||
|
bee3c243fb | ||
|
563c7674c7 | ||
|
fec033f9d2 | ||
|
e12a349f52 | ||
|
bee5cb2471 | ||
|
c8c7b481b8 | ||
|
480de92d0a | ||
|
e9dd9e9dfc | ||
|
07502e6d58 | ||
|
1843bbd89c | ||
|
a5987db8d5 | ||
|
039ffbd37a | ||
|
1a7a5ee1d6 | ||
|
9b96707dec | ||
|
3ce307f309 | ||
|
d6bb8d3bd1 | ||
|
271f437568 | ||
|
5438fd65e1 | ||
|
85e8b381b8 | ||
|
ef7628f098 | ||
|
17f13885c7 | ||
|
6b4c0f873a | ||
|
8b16628950 | ||
|
04211fbb27 | ||
|
3885fa7bfc | ||
|
f03f2a5d31 | ||
|
ce4b16616d | ||
|
af9bf9e07f | ||
|
1851405fcf | ||
|
0bc7deccd7 | ||
|
2dc0e29647 | ||
|
0e3abe16f4 | ||
|
cd9e4e8c4e | ||
|
02805226d4 | ||
|
4163a46a24 | ||
|
16dbfc62be | ||
|
e24f7d1358 | ||
|
541fe2e4df | ||
|
e059b8982e | ||
|
3b1b2d6d23 | ||
|
535507568f | ||
|
cc7474ec9b | ||
|
9bfdd55b2e | ||
|
5fdeb382ea | ||
|
cf65f1a4f4 | ||
|
f127014f0f | ||
|
e1e0ec6b24 | ||
|
faf6d19db5 | ||
|
fab607584c | ||
|
cd60aff5dc | ||
|
207bc88aab | ||
|
6cbe359398 | ||
|
82314dd4bf | ||
|
12e4607a30 | ||
|
20177116e7 | ||
|
e403e29312 | ||
|
82e1160508 | ||
|
d5cdf59993 | ||
|
3c6c7e0ab7 | ||
|
68f0ed8901 | ||
|
b3cdeabf02 | ||
|
0cd17abafd | ||
|
1e47bd6add | ||
|
6c20101c8b | ||
|
bf25e2f069 | ||
|
06ac68d00e | ||
|
1b6fea72d8 | ||
|
0b63a943b6 | ||
|
f8e5cff117 | ||
|
b6693ee50d | ||
|
8bdc0a4146 | ||
|
3e0e39900e | ||
|
263a664e58 | ||
|
3d21ec897b | ||
|
2033814227 | ||
|
17c34a7985 | ||
|
53e56932bd | ||
|
2b010a9a59 | ||
|
4d4ccb99ba | ||
|
c0eae951c6 | ||
|
b28b77fdbd | ||
|
3b99665b32 | ||
|
50d7db595b | ||
|
fe7e9a3315 | ||
|
e3af370832 | ||
|
63180a1589 | ||
|
c04b6af937 | ||
|
29b01cf1bd | ||
|
a10b9086ab | ||
|
a9f81190dd | ||
|
f3b92e0562 | ||
|
cba40f4db8 | ||
|
1b6f79661b | ||
|
999ff30e74 | ||
|
d97ba581b0 | ||
|
f28b7fc116 | ||
|
a803e826a3 | ||
|
8d2fd759cc | ||
|
d4c9b601c8 | ||
|
7740accfaf | ||
|
8f6771ba91 | ||
|
980deca107 | ||
|
2db1c2f1da | ||
|
b87eaf3040 | ||
|
37ce7cc256 | ||
|
74b69d99de | ||
|
819d4cb20c | ||
|
c1c1d480dd | ||
|
2c5d2eb7c6 | ||
|
396fcb33a5 | ||
|
176fae11e5 | ||
|
fd9b701272 | ||
|
c0655ba370 | ||
|
c08ef0ee36 | ||
|
fc7b942bc2 | ||
|
2290e5d08d | ||
|
ee6751e02f | ||
|
3ed100d218 | ||
|
2c6b970f29 | ||
|
263dc85fbd | ||
|
79eecdcc76 | ||
|
44347fb2d0 | ||
|
72d7c5353e | ||
|
88d7eb8c60 | ||
|
db0ab10428 | ||
|
fd74ea3dca | ||
|
a96d186650 | ||
|
35d468c4db | ||
|
65c727079c | ||
|
feb5e13c32 | ||
|
94954a699c | ||
|
3f5735be67 | ||
|
834ed6ede5 | ||
|
0a035afc2d | ||
|
cdb89ff5ad | ||
|
65d30e7ce1 | ||
|
f7c12a716b | ||
|
be30b429f8 | ||
|
3f03bd6e20 | ||
|
9ef1c5952e | ||
|
5be44521b5 | ||
|
8d66acde53 | ||
|
f3a942c522 | ||
|
3d54f8f686 | ||
|
441ea71f40 | ||
|
d68a4024f8 | ||
|
8b401e65e3 | ||
|
9d6a785aac | ||
|
f2c8db5db0 | ||
|
f8ef6818de | ||
|
4f2bcbb1e3 | ||
|
7ce5499868 | ||
|
e809c9f198 | ||
|
f7c8e4cf33 | ||
|
41c3abb12a | ||
|
3d97ac8ae7 | ||
|
86395dfc78 | ||
|
49e2d7a987 | ||
|
5fefb8f976 | ||
|
6a4c3838c8 | ||
|
9f7b18adb6 | ||
|
6ea7779318 | ||
|
0ba2f75213 | ||
|
31c2c85c4f | ||
|
0621958ca2 | ||
|
4451131999 | ||
|
b0926b3e69 | ||
|
63d8d63c1f | ||
|
a2f6da52c0 | ||
|
5990e5649b | ||
|
6a47104b4f | ||
|
9b7fc464ca | ||
|
262d04c6c6 | ||
|
9a92d9e9ad | ||
|
553795e8fa | ||
|
afeea19403 | ||
|
029a40aa21 | ||
|
3e04c8854e | ||
|
e0f442209b | ||
|
2cb1835533 | ||
|
c177246675 | ||
|
e2c633a23b | ||
|
47419a090a | ||
|
52ef8156c0 | ||
|
a9b50a8fdb | ||
|
135fa76fde | ||
|
360f0d6796 | ||
|
4d26c800ba | ||
|
a42a0d1822 | ||
|
e4fd005fb0 | ||
|
1c0797e800 | ||
|
01e70ab7d8 | ||
|
b6501a3786 | ||
|
7dfc60a97f | ||
|
da5abc1b39 | ||
|
5df791fc82 | ||
|
ae5f08b9bd | ||
|
a0bcbc4d21 | ||
|
c6e3e06194 | ||
|
1442e5d4e4 | ||
|
99c35aa3cb | ||
|
02898cfc17 | ||
|
59645e3a0d | ||
|
4c331a2000 | ||
|
b62fba80d6 | ||
|
1c60e45a7f | ||
|
6e4914ada3 | ||
|
8967e2bb03 | ||
|
0a154a3b21 | ||
|
63f0adbc15 | ||
|
d91df0126b | ||
|
3f21f84df4 | ||
|
c734de531f | ||
|
d4e82d374e | ||
|
5b2c6d7ed9 | ||
|
8f483a3ba0 | ||
|
cae6ec6bee | ||
|
a97fe34d74 | ||
|
9ef2d46e75 | ||
|
fcf20be8ea | ||
|
3fa13570ef | ||
|
f7d86ac244 | ||
|
e3c3634266 | ||
|
3ced918fb4 | ||
|
23e50b6038 | ||
|
fdaa3e2f16 | ||
|
2e0868023a | ||
|
98769af678 | ||
|
e08274e2b0 | ||
|
68623ace26 | ||
|
921171ed2b | ||
|
84a55968ee | ||
|
1b44de3b68 | ||
|
f9a8b97cf9 | ||
|
52fc10ffa2 | ||
|
59519e0e96 | ||
|
d490592b99 | ||
|
024cee42f5 | ||
|
a55071f67b | ||
|
2add108539 | ||
|
c0104c7480 | ||
|
5be1ab6f7d | ||
|
4811dd0320 | ||
|
362a060512 | ||
|
d2376b571f | ||
|
4e9134f66a | ||
|
dc03989bbe | ||
|
3cdd5fd711 | ||
|
f6c35b44ed | ||
|
43de595c24 | ||
|
a2d31e9610 | ||
|
8271b6d8b2 | ||
|
aae82ab16a | ||
|
525d9b52fe | ||
|
bb676a8699 | ||
|
fd648c26e9 | ||
|
f6c67b2159 | ||
|
0a2657cb5b | ||
|
44b66be0a9 | ||
|
db11611d7d | ||
|
b902256a59 | ||
|
f563600635 | ||
|
e0fd78344b | ||
|
3bab3c2190 | ||
|
500c22feb1 | ||
|
8be2ca2dfb | ||
|
834795b4b8 | ||
|
9725ac0398 | ||
|
58f918aaa1 | ||
|
f3a90159c1 | ||
|
1239b22c30 | ||
|
1a1e2c20c0 | ||
|
22ecdfd75d | ||
|
bbee6e9605 | ||
|
40a4e42835 | ||
|
1588f6b715 | ||
|
55882b9215 | ||
|
d5a3cabf13 | ||
|
9616e26dd2 | ||
|
074b65bfcd | ||
|
3f7e7141b9 | ||
|
2d8c6fe4c3 | ||
|
a364c3f168 | ||
|
3d6eb1b91c | ||
|
cf2413e0c9 | ||
|
a690b50eee | ||
|
e50d3639ce | ||
|
1200f0b081 | ||
|
32f12b3ba5 | ||
|
9dc96390fc | ||
|
6b995c018b | ||
|
36d8b3d9ad | ||
|
c86158f0ca | ||
|
90ce0669c5 | ||
|
2c67860c61 | ||
|
a578b97d96 | ||
|
191ba39cbb | ||
|
23496cb049 | ||
|
f4efe2970a | ||
|
2a1090ffa8 | ||
|
219049976c | ||
|
c3ab9a08e7 | ||
|
023a864a36 | ||
|
6abcc76a93 | ||
|
c92f8ad65d | ||
|
c1038af37d | ||
|
89ae9c1376 | ||
|
a8d37d892e | ||
|
e836ffcc61 | ||
|
810987210c | ||
|
6447009c18 | ||
|
cd9d340038 | ||
|
5272cf092c | ||
|
b92a428e88 | ||
|
31f9a71109 | ||
|
174c4c709a | ||
|
e3024deb46 | ||
|
f796925ae5 | ||
|
750d45be81 | ||
|
5542ead48b | ||
|
f01a7a4b3c | ||
|
640526b444 | ||
|
8d2f8449ba | ||
|
ed49d46237 | ||
|
9b051f55de | ||
|
b15ba75551 | ||
|
17cd6b51ce | ||
|
cca81a37d5 | ||
|
0bdd807d9d | ||
|
f8ad0acfa4 | ||
|
55a05963a2 | ||
|
e07745d790 | ||
|
4a3fc5b325 | ||
|
2fcdcc3f6a | ||
|
fa8196ef4f | ||
|
1f47f5def1 | ||
|
77b5766fa2 | ||
|
13955b0bf6 | ||
|
c77a7e7386 | ||
|
17873001b8 | ||
|
054ad0d53b | ||
|
a907aaa160 | ||
|
b6273b95b8 | ||
|
d912ff3196 | ||
|
ca45e96142 | ||
|
046612f8da | ||
|
1e37745764 | ||
|
f4ded1630f | ||
|
3c3842bdce | ||
|
ccb4ae9f86 | ||
|
9684a83b8c | ||
|
7ad748799e | ||
|
f510301922 | ||
|
f50df84287 | ||
|
9f31919dc1 | ||
|
e51053a6a3 | ||
|
eec052f054 | ||
|
be4d5ad7f7 | ||
|
01a4ea6f2c | ||
|
6feed0a68e | ||
|
0a3aa6172d | ||
|
048b43e196 | ||
|
cc412f0a20 | ||
|
3a5a738508 | ||
|
83ec2bb1f0 | ||
|
24c4d2d2bc | ||
|
2ec2c0f2c7 | ||
|
386ca85db8 | ||
|
ffbd8f985a | ||
|
88f419330e | ||
|
3386a73e44 | ||
|
eaa41f7f05 | ||
|
128975ea0f | ||
|
0584ca7be9 | ||
|
37b8ef9519 | ||
|
7bb02d51d0 | ||
|
7dec7b9aba | ||
|
2e248baf91 | ||
|
354590a17a | ||
|
8fda8ceae7 | ||
|
8fe6a5f6e6 | ||
|
1bc93be3f2 | ||
|
36084703f0 | ||
|
1089f79a5b | ||
|
2a0af48bc1 | ||
|
dab8a9a3cd | ||
|
2f3bef0865 | ||
|
467597e2c2 | ||
|
5eece7d73a | ||
|
69c4ccf33e | ||
|
5328761877 | ||
|
f9ab63944b | ||
|
3fc156d513 | ||
|
c9cd7352c4 | ||
|
ebab356c20 | ||
|
f96db3cf0f | ||
|
f004ecf323 | ||
|
2c03c1bbe9 | ||
|
b405a904a6 | ||
|
c2a1df9ae0 | ||
|
c69950c6e4 | ||
|
c7df7be7f1 | ||
|
ff67cf1cae | ||
|
ad1179b9db | ||
|
ea54427f42 | ||
|
b9cb52a184 | ||
|
3d57722b9f | ||
|
ae9ad111c4 | ||
|
160cae7a4a | ||
|
884968139d | ||
|
324dc4ed30 | ||
|
dedd85e184 | ||
|
7fb5744495 | ||
|
d897e5454f | ||
|
01d940b938 | ||
|
e6f2d3e07b | ||
|
4060dee4f9 | ||
|
e983bd8108 | ||
|
857a825ed8 | ||
|
6fd828974d | ||
|
e51aa4a9c5 | ||
|
182c11481e | ||
|
6981cfed01 | ||
|
d4057aca12 | ||
|
528df37335 | ||
|
b23e8295d3 | ||
|
13603dd5f4 | ||
|
fe7a5eb01b | ||
|
3c2628f182 | ||
|
b1274515aa | ||
|
b088fc0da3 | ||
|
d36c76127c | ||
|
c5404006b2 | ||
|
5fd125dc92 | ||
|
877c3b68ed | ||
|
0c358c140d | ||
|
60a6a610e0 | ||
|
f91dbdd2c7 | ||
|
d18a4c3fd7 | ||
|
ebda5a909b | ||
|
4f737f1714 | ||
|
e6b9c79bc1 | ||
|
8b305546ed | ||
|
5b56d6fa16 | ||
|
2e99e48603 | ||
|
b45d500f1e | ||
|
cec0acafe1 | ||
|
073b2e0b1f | ||
|
3488d18e4c | ||
|
bff93af17e | ||
|
84d2dcf11d | ||
|
244e54da69 | ||
|
292b62ba21 | ||
|
dec2afabb2 | ||
|
2ae9fb0e13 | ||
|
56ae09fc8a | ||
|
5f816246e4 | ||
|
fb32cc7198 | ||
|
a01c230b6e | ||
|
8a4c95ef4b | ||
|
9821b4589d | ||
|
6d1895364f | ||
|
ade59513df | ||
|
641951aaed | ||
|
6159ca7c7c | ||
|
bf06f271c6 | ||
|
cbb0a009e7 | ||
|
25b4d7a3ca | ||
|
624609d800 | ||
|
afae0ec5ec | ||
|
a6e621b562 | ||
|
7c81d068e6 | ||
|
d1b8001adc | ||
|
2764e3ad9a | ||
|
5c12e1ce1c | ||
|
7d2ceac61a | ||
|
8a201c12b4 | ||
|
f393547d9e | ||
|
b0233f6a24 | ||
|
2932b0a800 | ||
|
59083b7669 | ||
|
f0d15e3e79 | ||
|
795e85bebb | ||
|
376ee38d3c | ||
|
c05af1d38b | ||
|
ab0ddcae3d | ||
|
09a119afe3 | ||
|
25aabbe766 | ||
|
f2fa9dbb41 | ||
|
9026c3a71d | ||
|
e765860aeb | ||
|
8ec6e55304 | ||
|
5a5420972c | ||
|
c7992411a5 | ||
|
cf3bf91b8d | ||
|
8442cc2b2b | ||
|
0ab81df9b8 | ||
|
6be459e451 | ||
|
78928e3510 | ||
|
4909320089 | ||
|
3a8738fd43 | ||
|
dac697c74e | ||
|
4bc9ea4573 | ||
|
cfa88b701f | ||
|
16fc72d8fb | ||
|
f36d44dc67 | ||
|
ead728dc35 | ||
|
3cf3fb50b2 | ||
|
37d4f62ce9 | ||
|
57486732b1 | ||
|
55e7d2439a | ||
|
81d86570da | ||
|
947109718c | ||
|
ca5cc84988 | ||
|
dbb60cf026 | ||
|
2484de5010 | ||
|
8550b4fea5 | ||
|
6f7e8c7a49 | ||
|
e6f7c78626 | ||
|
d22eceed66 | ||
|
b4700d28bd | ||
|
f0f12c459e | ||
|
5d70a807da | ||
|
77bc130636 | ||
|
377f2bd04f | ||
|
7a7acdbe57 | ||
|
de00939d8d | ||
|
a1004bac86 | ||
|
5a8e7ddcf8 | ||
|
f3a76a267c | ||
|
c8dbb2c757 | ||
|
d80b6ad4ce | ||
|
3d9ae49948 | ||
|
a9f0523b62 | ||
|
a111d0f616 | ||
|
8e6196a3f3 | ||
|
c93df98363 | ||
|
574f375467 | ||
|
1351ac018a | ||
|
c5588a2313 | ||
|
3342b7880d | ||
|
564121428c | ||
|
67fc9eafe8 | ||
|
9ccf93d767 | ||
|
9daa8feec1 | ||
|
9048abb05e | ||
|
69297c0181 | ||
|
ae70c88e52 | ||
|
e6341ac1e0 | ||
|
2d713783ba | ||
|
e122f372ba | ||
|
48bcd75f1a | ||
|
37a024f023 | ||
|
86c105beaa | ||
|
2afd61b05d | ||
|
7e1b5c5665 | ||
|
54af53cf4e | ||
|
dd2f132577 | ||
|
de4eac5a06 | ||
|
15b3e10727 | ||
|
524b42c778 | ||
|
44431beb13 | ||
|
c5c1fd156f | ||
|
66b4b29424 | ||
|
256514bca6 | ||
|
4f990c74b7 | ||
|
757bf8690e | ||
|
845cbced69 | ||
|
05997d359c | ||
|
f85c8ca4e9 | ||
|
5a11784173 | ||
|
78c6295acf | ||
|
5aca85fe21 | ||
|
1fe1630ab9 | ||
|
dfcfa4c02b | ||
|
77030096e4 | ||
|
8744a429f1 | ||
|
aede32c335 | ||
|
9c42062efb | ||
|
eb85b4498f | ||
|
ac18d5629d | ||
|
8ef5270d7f | ||
|
09544f795f | ||
|
c92fe116c5 | ||
|
bd8e8a9e7f | ||
|
601160e9fb | ||
|
5aa5d7ad56 | ||
|
11f70d54e8 | ||
|
b8cc7e171e | ||
|
554149cf4f | ||
|
968cb96bc4 | ||
|
6d35f6df2c | ||
|
c5639e32b5 | ||
|
da314c067c | ||
|
11b7205ea4 | ||
|
87f90c05b4 | ||
|
9150af8592 | ||
|
c25d15419c | ||
|
7670892ae7 | ||
|
6fa8e6695d | ||
|
27399beffe | ||
|
29384cd5ff | ||
|
d55ccdf557 | ||
|
22d4e62198 | ||
|
cdfd5a454b | ||
|
c3dec09d9c | ||
|
d7cd12679c | ||
|
7454d4323d | ||
|
69c4b31853 | ||
|
89a69ee1e0 | ||
|
733f501a94 | ||
|
5bba14a763 | ||
![]() |
7c44dc64bc | ||
|
2f174ad552 | ||
|
7f2725ba49 | ||
|
aea62de752 | ||
|
84bbc27a17 | ||
|
bfddf7355b | ||
![]() |
6540a57aa9 | ||
|
51807a77f0 | ||
![]() |
cb2fbe9b50 | ||
|
fccab2da56 | ||
|
e4d9dcdd03 | ||
|
3ebc03ab3a | ||
|
05b596ce32 | ||
|
ca88de8e7f | ||
|
d44fc85582 | ||
|
a0ac8666e0 | ||
|
af3801367a | ||
|
ed66c2d151 | ||
|
af564547f0 | ||
|
89ab847ef6 | ||
|
2f28970e32 | ||
|
e12a626efb | ||
|
2f2285711d | ||
|
9a4b802286 | ||
|
8336e8336b | ||
|
3a0bb0cd58 | ||
|
41a07b2250 | ||
|
cd9c5032f8 | ||
|
c470e7effd | ||
|
d5bcbcec3a | ||
|
38027e7588 | ||
|
bbac0f0355 | ||
|
aec24d6dd7 | ||
|
13330c4ede | ||
|
8bb600f6ae | ||
|
98972d22d1 | ||
|
333be9d6ca | ||
|
f87310c082 | ||
|
1a1b8e4510 | ||
|
476657ecad | ||
|
5baa8d3252 | ||
|
39625aacaa | ||
|
f787d941e8 | ||
|
2a90a2cdf4 | ||
|
e212edae54 | ||
|
6b92904178 | ||
|
c48d143fe8 | ||
|
0474d64ba1 | ||
|
38bc89f455 | ||
|
d504a7ac83 | ||
|
9ec91891e6 | ||
|
ce45e34265 | ||
|
6d74b684ec | ||
|
7497b83525 | ||
|
f88a1f0603 | ||
|
d3ef767487 | ||
|
8e55dc7217 | ||
|
5e7ce1afa5 | ||
|
5f50d9e59e | ||
|
9bce0e696c | ||
|
61c77a3440 | ||
|
298f069c7e | ||
|
12402f2f0f | ||
|
dafcf7bc14 | ||
|
45643b94e3 | ||
|
6d5a75bcf7 | ||
|
bdb44d84fb | ||
|
c449a8353d | ||
|
5829a9e892 | ||
|
ddd4a05e5e | ||
|
32af983910 | ||
|
3cdd94e465 | ||
|
1b27e59be8 | ||
|
0335f3f4ab | ||
|
d1f46148a7 | ||
|
1d327d673e | ||
|
2fbaf71448 | ||
|
0aadc23fcb | ||
|
bb1755613e | ||
|
71e9d5775c | ||
|
6494b49d67 | ||
|
ddd375b378 | ||
|
1b777c9e62 | ||
|
d35c0a6385 | ||
|
86dabe624b | ||
|
7a97d524f8 | ||
|
820564dd3e | ||
|
9988fcebc1 | ||
|
0114381ebe | ||
|
650b899de3 | ||
|
6eeccb530b | ||
|
bf395c1633 | ||
|
d956b2ae9d | ||
|
c20b2bbab9 | ||
|
71a1f06e35 | ||
|
3ca17c1508 | ||
|
ff4f88dc64 | ||
|
e8adefbe6e | ||
|
4ce2bdcdd1 | ||
|
f31179c9ce | ||
|
c1c380fa8f | ||
|
ee3fc2bd79 | ||
|
2afc028ab2 | ||
|
db9dcdb2cc | ||
|
7b759558b1 | ||
|
1d99a2d6be | ||
|
2007ba7ea5 | ||
|
df0e598e38 | ||
|
3661d6cae5 | ||
|
9772901d71 | ||
|
4a877d60f7 | ||
|
ca368797b0 | ||
|
d4dfcd2288 | ||
|
21146b6b2c | ||
|
e15c65ac09 | ||
|
5b7be6be05 | ||
|
995be3c37c | ||
|
15ef58a8f5 | ||
|
af26fdbff1 | ||
|
325f65fe5c | ||
|
7d6437b2cd | ||
|
a27b511ad3 | ||
|
f3f20fd30d | ||
|
a6d1472134 | ||
|
f1865c3878 | ||
|
165aaebd4c | ||
|
bfc4bc553a | ||
|
6e80e2d184 | ||
|
fe3b1c5a0d | ||
|
ad7f3449bb | ||
|
1700e2f060 | ||
|
8a2c998cb9 | ||
|
150e750ece | ||
|
2b13120fcc | ||
|
c0674b2924 | ||
|
569b2fdf41 | ||
|
5ed4d5f630 | ||
|
449c709dcc | ||
|
3aca527360 | ||
|
d82fd35323 | ||
|
eaa1a93755 | ||
|
45a7a4aaf3 | ||
|
92ad2c5552 | ||
|
9c6d22aa50 | ||
|
229bfe7946 | ||
|
a8233ca6e1 | ||
|
62e24ac1ab | ||
|
4f79f29238 | ||
|
b2eda881e8 | ||
|
3dc1fc87b1 | ||
|
334e8fbc67 | ||
|
ec430779f4 | ||
|
3b2d456a25 | ||
|
d8a8eff21a | ||
|
f70106e41b | ||
|
0976c67a29 | ||
|
e1b651b66c | ||
|
3a84b9369b | ||
|
e722c960bc | ||
|
2d7be96bdf | ||
|
2d890fb7a1 | ||
|
4679455720 | ||
|
99f73c548c | ||
|
1ec4d71db0 | ||
|
a2b915cc6d | ||
|
ee2404afe3 | ||
|
29d16bd535 | ||
|
eca51850e2 | ||
|
c16d1e3dff | ||
|
d62538f42e | ||
|
c2821b1775 | ||
|
2c9d244023 | ||
|
8e6369969b | ||
|
ef8e7ad9c5 | ||
|
fb8bafc6a0 | ||
|
dc2a1534d3 | ||
|
8746e8b243 | ||
|
38e91f1fa5 | ||
|
1cbc2d96c1 | ||
|
65957efe72 | ||
|
8519e30a3a | ||
|
2750100d24 | ||
|
7f784835a7 | ||
|
20594e5a51 | ||
|
aa0119be45 | ||
|
68126a82dc | ||
|
8506b8a486 | ||
|
fd8405dd6b | ||
|
7e64d28197 | ||
|
cf709b1c5c | ||
|
576a0f9c33 | ||
|
01a9e3b168 | ||
|
116d0a7001 | ||
|
7ee059d276 | ||
|
d175de1496 | ||
|
eeeccff1ba | ||
|
0f984bd3b3 | ||
|
5b87f9387e | ||
|
3e5e12722a | ||
|
322a22cb68 | ||
|
47cc04ff53 | ||
|
34fd856b4f | ||
|
2b6c8dd622 | ||
|
d4e03836dd | ||
|
7532075aab | ||
|
84257e3202 | ||
|
51e3ea7fa0 | ||
|
b1ec71e91c | ||
|
63ecaafa51 | ||
|
bd6ec09458 | ||
|
e236ff0ea8 | ||
|
aacae1ca3f | ||
|
745a788d84 | ||
|
055699a00a | ||
|
fab395dc02 | ||
|
12939d5bb9 | ||
|
458a16ce1f | ||
|
bfc0c4608e | ||
|
3fa67c317b | ||
|
2aebdbd066 | ||
|
5835b85d3f | ||
|
9ab6a5ac69 | ||
|
bdcd078b46 | ||
|
24b3d1caef | ||
|
b1cf63eaff | ||
|
d9494c6d7f | ||
|
c40a77f2de | ||
|
9da3fb8da9 | ||
|
3eded36fed | ||
|
77c47cdd09 | ||
|
e5b608d86a | ||
|
200bd607d8 | ||
|
3782ea2284 | ||
|
a11b5ce602 | ||
|
46099f3dfb | ||
|
0a06c5b6a4 | ||
|
a42e08c1ed | ||
|
5b8a22e848 | ||
|
f84d241367 | ||
|
1fde705785 | ||
|
c8f4934c8a | ||
|
d302daae48 | ||
|
3bde256d62 | ||
|
7686812336 | ||
|
2edf48e880 | ||
|
24d5d98548 | ||
|
f8c97143e8 | ||
|
0f1de455e2 | ||
|
664b707e5f | ||
|
45ee4cd7a9 | ||
|
de39597c14 | ||
|
91793ce752 | ||
|
b3196a9756 | ||
|
c4904fb460 | ||
|
f15d5a2bff | ||
|
1454ac791a | ||
|
63a4f87716 | ||
|
fcca75c57e | ||
|
f0d5d4bedd | ||
|
446382f5a1 | ||
|
57e97019d9 | ||
|
92bcfa5416 | ||
|
45bdd87224 | ||
|
11ef3fcfbb | ||
|
d1a5e926c6 | ||
|
267c8c65cd | ||
|
ea7057b601 | ||
|
0d1956de05 | ||
|
8e20ac8fb9 | ||
![]() |
2353f96d2c | ||
![]() |
9c2f46cb19 | ||
![]() |
803ade38d8 | ||
![]() |
8c16ba50b5 | ||
![]() |
ca736de339 | ||
|
ecfe380b17 | ||
|
6761bd9375 | ||
|
f097808c95 | ||
|
533a8ba532 | ||
|
2398369883 | ||
|
3c3b5e8150 | ||
|
2198dea47d | ||
|
9724174a5b | ||
|
db64c20837 | ||
|
6cb2c9e7bc | ||
|
fc7a4b93f4 | ||
|
22bb516bc0 | ||
|
1f73dde983 | ||
|
5889f34695 | ||
|
eff2ef6f1b | ||
|
4ce125993a | ||
|
932b826c4d | ||
|
577622a210 | ||
|
97b08645f6 | ||
|
b0fb8e836d | ||
|
69aba71f79 | ||
|
94f269d6d0 | ||
|
c639dd15e3 | ||
|
fd85d5e22f | ||
|
f6d3676ed6 | ||
|
1ca9b16158 | ||
|
ef297a2c2c | ||
|
cac0e5c9bd | ||
|
2d3c50065d | ||
|
e78e6646e6 | ||
|
d04ccddebe | ||
|
3265792c3b | ||
|
683749f5e6 | ||
|
b95da6801a | ||
|
5e079ee18b | ||
|
2d7b06fd4a | ||
|
39dfbcc735 | ||
|
2b70908dcb | ||
![]() |
b1845f9f6d | ||
|
f775526d81 | ||
|
dde7bbf1c6 | ||
![]() |
40623f1e99 | ||
![]() |
72fc528b3c | ||
![]() |
7fbff2cc27 | ||
![]() |
a53550ea0e | ||
![]() |
0f1432ce82 | ||
![]() |
4b4da7c4a1 | ||
![]() |
00187cb5a1 | ||
|
dba9bcd76c | ||
|
16a255ae33 | ||
|
60010628b4 | ||
|
a679213881 | ||
|
ed7da66b69 | ||
|
15f8e4c186 | ||
|
0a78916ff7 | ||
|
63f8f2e2a9 | ||
|
e7d4f87ca9 | ||
|
3fcd4aeee3 | ||
|
e6725871b8 | ||
|
47802c0bd7 | ||
|
f0cf90df76 | ||
|
99f87c0bc7 | ||
|
7ab493d18b | ||
|
eebfdc1391 | ||
|
409620fa38 | ||
|
7552806a47 | ||
|
3161808a9d | ||
|
53052c8c08 | ||
|
b2b95e8556 | ||
|
3c57063632 | ||
|
afa8c2d1bd | ||
|
4b13cf32fd | ||
|
58a90f7c39 | ||
|
7a9e496c64 | ||
|
34aa187e65 | ||
![]() |
a0ff35a153 | ||
![]() |
baeb70fdcc | ||
![]() |
6b846249e4 | ||
![]() |
b0447979f3 | ||
![]() |
f73420af9b | ||
![]() |
e8accfdfaf | ||
|
16ab337146 | ||
![]() |
8c29f705e6 | ||
![]() |
870d9df779 | ||
![]() |
23d00c92c1 | ||
|
733f851ad3 | ||
![]() |
f99c9a8a4f | ||
|
3caf4a0da4 | ||
|
dfd4ea3b04 | ||
![]() |
38a6189c20 | ||
![]() |
033c16ca85 | ||
![]() |
84c23c3729 | ||
![]() |
3947c81726 | ||
|
1ccefd9f6a | ||
|
118f4cfed6 | ||
|
54721608a4 | ||
|
96ce058d13 | ||
|
84b0aea54b | ||
|
dc753985b3 | ||
|
bbdcd0579b | ||
|
d4eaefbae1 | ||
|
89d4499c2f | ||
![]() |
d6cec05dee | ||
![]() |
aa6cc3f5c2 | ||
![]() |
57a98c5ec2 | ||
![]() |
0195c5a5ec | ||
![]() |
83788cbb75 | ||
![]() |
bed6cfa853 | ||
![]() |
802c3c23f7 | ||
|
145d742760 | ||
![]() |
3b2d5e640c | ||
|
a96e369c0f | ||
![]() |
f16a7308b4 | ||
![]() |
d7d7fda4af | ||
![]() |
2bbfa97063 | ||
![]() |
c38188927e | ||
|
515a155cf6 | ||
|
ab3f49abe3 | ||
![]() |
bf0b7842ca | ||
![]() |
4f94de0ef6 | ||
![]() |
5eea11e2a5 | ||
![]() |
a520da0d55 | ||
![]() |
6fa083d2e0 | ||
|
7ddc5c93e3 | ||
![]() |
459fcb9a4e | ||
|
1c6d67d2d7 | ||
|
6c317d0807 | ||
![]() |
ac97d5ff3c | ||
![]() |
a94841cf97 | ||
![]() |
631881cb8a | ||
![]() |
6b7fcbdc07 | ||
![]() |
5674cf47ad | ||
![]() |
6a1a6275ed | ||
![]() |
ce098f889c | ||
![]() |
72fe7a9025 | ||
![]() |
4b40400cd3 | ||
![]() |
73dc6895f1 | ||
![]() |
6f5eddc686 | ||
![]() |
9742c4880d | ||
|
323f653d2a | ||
![]() |
8800c96cdc | ||
|
38435faade | ||
|
4a51a4e47c | ||
|
e7497272fa | ||
|
eb113662e8 | ||
|
c410b82e65 | ||
|
39811d9300 | ||
|
0ede8992de | ||
|
a3d91fe237 | ||
|
066de5f13f | ||
|
9510312f5a | ||
|
6648e8fe88 | ||
|
c6f812bf8c | ||
|
8178687298 | ||
|
c969f85239 | ||
|
f7cd901672 | ||
|
35b1dcf362 | ||
|
5360e65ee9 | ||
|
4ab8cc7481 | ||
|
1772cab251 | ||
|
c22e624a3d | ||
|
e7bc65c0af | ||
|
f38e0c7967 | ||
|
11f4f31ba3 | ||
|
dc80264074 | ||
|
3630b4d365 | ||
|
b07d47011f | ||
|
899cd5fba7 | ||
|
6357aab370 | ||
|
3769af532b | ||
|
6b500f1ff2 | ||
|
8fc83c696a | ||
|
5a9874e4fa | ||
|
f567fbfeaf | ||
|
e74d83f104 | ||
|
5ea41bd346 | ||
|
47463161b4 | ||
|
820cdb90f8 | ||
|
e7a62c2717 | ||
|
f9fea572a5 | ||
|
e47f629a9e | ||
|
ef45f1df1d | ||
|
2d4a723854 | ||
|
d2bb480363 | ||
|
47e2dde3a1 | ||
|
84dd5cd6a7 | ||
|
94cce30cda | ||
|
f20b7e738c | ||
|
4492bdd6d8 | ||
|
c4351f64e8 | ||
|
48a18a6a7d | ||
|
df850c62e8 | ||
|
6b92e92e32 | ||
|
947afbb0af | ||
|
5f933b12bb | ||
|
2ed99077eb | ||
|
560b8f76ff | ||
|
c20f95ba94 | ||
|
0383f8fbeb | ||
|
db85f77c88 | ||
|
9998edb255 | ||
|
c2ed66415f | ||
|
344ddb924f | ||
|
6cb57aa66c | ||
|
798e49e4b2 | ||
|
abc11e3eb6 | ||
|
519cbaccde | ||
|
1dc6720d0d | ||
|
a700581493 | ||
|
2b6fec38c0 | ||
|
4c64042bac | ||
|
b8c5c42920 | ||
|
73e1bbf179 | ||
|
16706765a2 | ||
|
c4507497f2 | ||
|
eff33b64f7 | ||
|
4a6226361f | ||
|
0c2c506d88 | ||
|
3adb001b9f | ||
|
17cabdb923 | ||
|
4627d59609 | ||
|
3ec9b66bc9 | ||
|
d8651bd268 | ||
|
a885c33433 | ||
|
ff70878bb9 | ||
|
9d0c7bd9c9 | ||
|
956c830f3e | ||
|
7193f5b445 | ||
|
b4bf21cda8 | ||
|
dadef91ce0 | ||
|
94183f7f18 | ||
|
2d971af445 | ||
|
d97fe5b1eb | ||
|
898b8456d1 | ||
|
8afdce3e00 | ||
|
996cf610c3 | ||
|
d5d0b4ce6e | ||
|
11719baaaf | ||
|
456567af59 | ||
|
e851620df4 | ||
|
ddf42bf224 | ||
|
07cb95e99b | ||
|
8ae1edfd48 | ||
|
ae4f586714 | ||
|
9c561cc866 | ||
|
89268e1e57 | ||
|
c01887f3e3 | ||
|
cef269d323 | ||
|
e732b6b3aa | ||
|
0953bb4be8 | ||
|
f7f2f501f5 | ||
|
e46d7c64ae | ||
|
889bbce108 | ||
|
4a3f79e305 | ||
|
e79e132cd9 | ||
|
72585d9f43 | ||
|
831c06c499 | ||
|
c962613afc | ||
|
9767c027ba | ||
|
8f0e876d4f | ||
|
ce75ea9770 | ||
|
45a3980a79 | ||
|
70b2834839 | ||
|
68e9efebe6 | ||
|
3bbfbcbc0e | ||
|
c732ccd0e4 | ||
|
df45b0fd8c | ||
|
92904b7ac9 | ||
|
e0135d5b1d | ||
|
dffec1ff5c | ||
|
d9271a69a4 | ||
|
f803fb9ad4 | ||
|
0c4a22d301 | ||
|
a39d20bd70 | ||
|
d970be5f8e | ||
|
19d66892ed | ||
|
860d2f6ea4 | ||
|
bb559d064d | ||
|
cc28a0589f | ||
|
98d5e11fc7 | ||
|
c9b8f52e96 | ||
|
c5f7b07cb0 | ||
|
d1ad926d38 | ||
|
5a55ce4583 | ||
|
1b418be606 | ||
|
35a48acbcb | ||
|
3fa1068275 | ||
|
9506d6e539 | ||
|
704ac9bae4 | ||
|
ab33cd502e | ||
|
daa387f4b7 | ||
|
ae3b049bb5 | ||
|
3f0f167fac | ||
|
998439f29c | ||
|
c7aa6eda84 | ||
|
244790e2a7 | ||
|
eafdc452c9 | ||
|
079ae0fdcb | ||
|
0436795c3b | ||
|
e7108f2360 | ||
|
d23ee3479b | ||
|
378c2a160c | ||
|
90f416f1b9 | ||
|
229515554c | ||
|
1d3a1e0ccf | ||
|
517be7db49 | ||
|
f4c31690da | ||
|
fe3e7e5534 | ||
|
427ac45a8a | ||
|
db86ae28c8 | ||
|
3f91bafcf8 | ||
|
0810d97b69 | ||
|
81e030e653 | ||
|
f8022385f7 | ||
|
b38f5d609b | ||
|
e4987f9701 | ||
|
661fef19b5 | ||
|
6426f6fc21 | ||
|
71e3c08116 | ||
|
d003aab06f | ||
|
712e3d8dd1 | ||
|
ef068e9229 | ||
|
1e538e665d | ||
|
578ff36a06 | ||
|
869a3f6a13 | ||
|
f805974a04 | ||
|
81a634a618 | ||
|
a6e10e4ac4 | ||
|
21cfb0acf4 | ||
|
7abb2211eb | ||
|
6f54c67bae | ||
|
3049a81c94 | ||
|
f6559e6c17 | ||
|
3227a152eb | ||
|
0948a8b376 | ||
|
65c8ff71a2 | ||
|
dc8331e641 | ||
|
546efcc1db | ||
|
4fe5a84170 | ||
|
2670f62892 | ||
|
132c68c8bb | ||
|
bfd170689d | ||
|
cf20d9fb43 | ||
|
7a7ba07902 | ||
|
49e3ef6885 | ||
|
ab98173a13 | ||
|
defc2ccded | ||
|
f94aa78cc6 | ||
|
42060a49b5 | ||
|
e72a60e5bc | ||
|
6ea7ec261d | ||
|
e3775acbe0 | ||
|
0e2f5d49ef | ||
|
00551e6c2b | ||
|
144befb44c | ||
|
2dd8a65e63 | ||
|
105801b6fa | ||
|
746d6f4906 | ||
|
8eee617ed4 | ||
|
3bc3be5c19 | ||
|
959f8e739a | ||
|
b055f6fe54 | ||
|
9511cf2d00 | ||
|
d892dcfbc7 | ||
|
9968025782 | ||
|
17e25c0428 | ||
|
19f7b80dca | ||
|
d353580471 | ||
|
1cdda569d5 | ||
|
446af4dc94 | ||
|
2bc102de33 | ||
|
071b865765 | ||
|
452e50c8d8 | ||
|
68d08d8748 | ||
|
890d9bc599 | ||
|
47e95b4d26 | ||
|
e1bb9814a2 | ||
|
7b73768b3b | ||
|
9c2211f93c | ||
|
ed169086e7 | ||
|
0bdc2df8ff | ||
|
7108f40cce | ||
|
02ddb7a2f7 | ||
|
2eb6eaf52f | ||
|
0043956e2a | ||
|
c5085f92f5 | ||
|
beecde11d9 | ||
|
a691fbb37e | ||
|
6c2c5ba498 | ||
|
2a67484358 | ||
|
0c4eafb669 | ||
|
99b7c3d2e5 | ||
|
002c1fcc65 | ||
|
1024a2e1d8 | ||
|
cd0a4aee16 | ||
|
e01e36794d | ||
|
0023ae9801 | ||
|
532b0b95af | ||
|
8f0711e26e | ||
|
6a2cd1ff84 | ||
|
3615ad412f | ||
|
c3b9293036 | ||
|
bbe34e0816 | ||
|
e2d2215374 | ||
|
f509f43472 | ||
|
541c7c5fe5 | ||
|
ee1ed31d8c | ||
|
24be785bbd | ||
|
4ca7a94daa | ||
|
ceb828dd06 | ||
|
a31587df2f | ||
|
43d3949dd1 | ||
|
576d0eb317 | ||
|
26ec933d7d | ||
|
b63c77fdb9 | ||
|
ec0f55098a | ||
|
c2fd5322e2 | ||
|
b7badc7d32 | ||
|
194d1c0606 | ||
|
84dc60632c | ||
|
b8a4f7e7a0 | ||
|
6e2c1258df | ||
|
7e5c9e1897 | ||
|
7c7d714839 | ||
|
f65dbd3a39 | ||
|
6e4012976b | ||
|
4b36e27875 | ||
|
71fd747051 | ||
|
02a397638c | ||
|
7ba516b30a | ||
|
e5355abafe | ||
|
49fe216447 | ||
|
075a37ce8c | ||
|
00f9acf5e4 | ||
|
dccd038f6c | ||
|
053332e282 | ||
|
4889d63534 | ||
|
85d8d9ee32 | ||
|
251ab23cc0 | ||
|
57654cf1cf | ||
|
12be95c8c3 | ||
|
c915a5966f | ||
|
6c7fcb1063 | ||
|
027dac0d8e | ||
|
44ca4a79b8 | ||
|
34f691df28 | ||
|
2b878cbdbf | ||
|
784f470744 | ||
|
23ec3284b9 | ||
|
59f15508b9 | ||
|
d44e907910 | ||
|
8e06e5eb39 | ||
|
01408a4744 | ||
|
3a0e01479f | ||
|
fd81da2f7d | ||
|
c53bd18290 | ||
|
ac013513a2 | ||
|
aaec7c9526 | ||
|
80e8065e2f | ||
|
b8a28ce838 | ||
|
6f789e7e88 | ||
|
b096e369d0 | ||
|
52a275808f | ||
|
76669f8a65 | ||
|
9f39f7bf03 | ||
|
b4b1ec8825 | ||
|
73be93b995 | ||
|
c29715cbe8 | ||
|
2af7db43a4 | ||
|
bfb00656cc | ||
|
343e990738 | ||
|
6dfeddb4b7 | ||
|
517e1feae8 | ||
|
2155a0d157 | ||
|
c1edcac2a3 | ||
|
5ddcfa438e | ||
|
b0f6b5d049 | ||
|
854086a62f | ||
|
324ed1f045 | ||
|
a7cc7bbfba | ||
|
2db5cfeebd | ||
|
d59e237dda | ||
|
12e1869f99 | ||
|
1a6aac578b | ||
|
557ff005a2 | ||
|
a0beedc860 | ||
|
44b108f00e | ||
|
f64388b038 | ||
|
628cfad52a | ||
|
f471040ddf | ||
|
ed42a7feee | ||
|
365b3ee8ba | ||
|
08414cdada | ||
|
d13781baf9 | ||
|
1cb557b2cb | ||
|
a49bbbcb06 | ||
|
4301254fa0 | ||
|
d1ea179296 | ||
|
36dd37b931 | ||
|
a1b27bcb33 | ||
|
8a4ef0cc2e | ||
|
7f5a18e46a | ||
|
e4a3e0e5ef | ||
|
248a001bb6 | ||
|
cd4c4d38ed | ||
|
5196dc0450 | ||
|
db79a6af1e | ||
|
49ee98bbb0 | ||
|
a77ffd1133 | ||
|
8d2d0552a2 | ||
|
910eaf6da6 | ||
|
f268607b54 | ||
|
6265d389b9 | ||
|
175463a0f6 | ||
|
d8f761f509 | ||
|
3d8a03c177 | ||
|
bb8631bbe8 | ||
|
b0ed8444c1 | ||
|
ca4550d76e | ||
|
1d70f86ca1 | ||
|
3dec5bb98a | ||
|
b49fef332c | ||
|
e4fd781199 | ||
|
bc6e20e62f | ||
|
285f22d84c | ||
|
8195b3835c | ||
|
e37a73824d | ||
|
65b41c3f6e | ||
|
356ea78b26 | ||
|
62bc9e7958 | ||
|
d2c58c43aa | ||
|
e8971b2940 | ||
|
1f85a811e9 | ||
|
28f9e9e35e | ||
|
0bbb7c336d | ||
|
969faaab23 | ||
|
6282a9b717 | ||
|
d8b0aae823 | ||
|
0bded9f8e1 | ||
|
21f103e95d | ||
|
76f5a0ed33 | ||
|
6600aca3e4 | ||
|
4320f2d6c8 | ||
|
97c839b9f3 | ||
|
4e060d6ca2 | ||
|
41b9cc647b | ||
|
fd502dbdab | ||
|
31bee4028f | ||
|
aabfa32595 | ||
|
c3a65bab38 | ||
|
57103a5dd4 | ||
|
fd15789f6f | ||
|
d9bda20e59 | ||
|
413e40a64c | ||
|
2419a0111a | ||
|
f0a943281b | ||
|
818d445ec9 | ||
|
51cf6201cf | ||
|
0fe5dad769 | ||
|
f42322503d | ||
|
d6bf2db5d8 | ||
|
ec7e5e5419 | ||
|
4cb9dead89 | ||
|
e6438abe8d | ||
|
0f6fedb057 | ||
|
433752273d | ||
|
928404c520 | ||
|
74149a97b1 | ||
|
c7e3d9fc0e | ||
|
3ce663fe54 | ||
|
101a734aaf | ||
|
ad206a7738 | ||
|
191cf81d6b | ||
|
4808d7f54d | ||
|
f04631e47e | ||
|
bfa0ac0137 | ||
|
319d27f0c0 | ||
|
d71ceebd72 | ||
|
e4393cc4dc | ||
|
a783edc76f | ||
|
265625df75 | ||
|
e81e30c81c | ||
|
6f3b0a6d75 | ||
|
8d782e1003 | ||
|
8c21844352 | ||
|
eb26eabb46 | ||
|
1fb25f3e41 | ||
|
1ae3a9b961 | ||
|
7a4fda37b5 | ||
|
035f35a26a | ||
|
7d3a32d372 | ||
|
5dc6700f0c | ||
|
d815b5da64 | ||
|
326e7f3e36 | ||
|
5290d02d9a | ||
|
e4771234a2 | ||
|
6e66c2ace0 | ||
|
4eca8aca98 | ||
|
867837bfef | ||
|
82b1b3e599 | ||
|
52525f6697 | ||
|
d84102be24 | ||
|
8fe682f966 | ||
|
aaff33e267 | ||
|
063b05bd3d | ||
|
23cba35ea0 | ||
|
283b2974dd | ||
|
ed93a32912 | ||
|
1a60238c61 | ||
|
03e43998eb | ||
|
bb0846f1a5 | ||
|
29f973029f | ||
|
bf1b5fd793 | ||
|
bb68755699 | ||
|
85c204dece | ||
|
dc70acec55 | ||
|
77da306397 | ||
|
300ebbfe82 | ||
|
de2fcb1b74 | ||
|
fc254c1c6f | ||
|
f1e324a4a7 | ||
|
e55c5beb1c | ||
|
ce1360f5cb | ||
|
b08efcf169 | ||
|
9fa1ac2bf9 | ||
|
2a251b219a | ||
|
e2585bd66e | ||
|
2ed09bade2 | ||
|
4e6badc548 | ||
|
b909caaf45 | ||
|
8e755c389a | ||
|
11157999eb | ||
|
6fdf81f266 | ||
|
15fe35a589 | ||
|
ddab768696 | ||
|
b941864ac5 | ||
|
6ae0ad887c | ||
|
85e60b759b | ||
|
80611e999a | ||
|
bb229c24d3 | ||
|
ab6cdf102e | ||
|
be382a47b6 | ||
|
2f7ebc2d67 | ||
|
e771911058 | ||
|
a8dba2a494 | ||
|
f86848f965 | ||
|
c6add463d7 | ||
|
7f2a6eb74b | ||
|
741745433d | ||
|
bc5e235822 | ||
|
d5031835da | ||
|
f3198929b0 | ||
|
e6ae1ac4e1 | ||
|
2515efa414 | ||
|
fba70355cd | ||
|
199e960d22 | ||
|
68f1efb7d2 | ||
|
726bb62a2c | ||
|
70bdc8839e | ||
|
82dad491e9 | ||
|
3c9ce999a0 | ||
|
c2414da0a3 | ||
|
251f79b231 | ||
|
d5b62aabcd | ||
|
06f9804497 | ||
|
2bde6a2ec4 | ||
|
c81e10ba57 | ||
|
219e4a14c6 | ||
|
e888ddf109 | ||
|
ac12a3eed2 | ||
|
da2068a334 | ||
|
e65de72e3a | ||
|
7b7672ef96 | ||
|
8f2bb593c8 | ||
|
8103cc54d1 | ||
|
9d96c58eaf | ||
|
cce1e9ac37 | ||
|
8df10f2177 | ||
|
63a8a2ffa6 | ||
|
b62d4a7bc1 | ||
|
fc69c6568d | ||
|
cfc339edea | ||
|
30a6c4494b | ||
|
de25b68b43 | ||
|
537c3d04c1 | ||
|
8beac359a7 | ||
|
525232c873 | ||
|
08f299e9bf | ||
|
97a63db40c | ||
|
855bfbe168 | ||
|
86783a50b9 | ||
|
e0b4547143 | ||
|
8a2e9e8070 | ||
|
4eb3864639 | ||
|
43bd0e972f | ||
|
a23a709fb1 | ||
|
41b8956df6 | ||
|
286ba36bf3 | ||
|
18454ff3de | ||
|
6a3b2d52c5 | ||
|
ae3309fa8f | ||
|
b4d0104206 | ||
|
36aa63361d | ||
|
e2c43d64db | ||
|
c59a2be322 | ||
|
c27d424281 | ||
|
63e4daab50 | ||
|
ec46f85024 | ||
|
a74c9052bc | ||
|
870bb2a99a | ||
|
4a1ae35631 | ||
|
b162f197ea | ||
|
6370a6bafa | ||
|
3eee34a0f2 | ||
|
cf5825d1fa | ||
|
2915ee09c5 | ||
|
9548ad6a27 | ||
|
171591a7a3 | ||
|
c402a7bfdd | ||
|
0a44585457 | ||
|
3fc1948a44 | ||
|
1baf086e1b | ||
|
bac2cab8e9 | ||
|
c5d4e9f049 | ||
|
10dd723637 | ||
|
e910e93dbf | ||
|
0a292491cc | ||
|
3321fda1b9 | ||
|
04eafa9275 | ||
|
21884afff7 | ||
|
8240db3546 | ||
|
eb8b9ccd50 | ||
|
4b48717010 | ||
|
5ac1502ccd | ||
|
b992d54572 | ||
|
824c9ab211 | ||
|
73bea928c5 | ||
|
a9b60057ea | ||
|
77310b0229 | ||
|
5bfccec7a3 | ||
|
9e39ab7f5d | ||
|
6878bc8771 | ||
|
b04717ae4b | ||
|
e50beb435b | ||
|
bcd093e3e6 | ||
|
f7d660945c | ||
|
5c538c0967 | ||
|
3cc4ffd21b | ||
|
77ff2bfd05 | ||
|
cca6b47fc0 | ||
|
2821c8f67b | ||
|
ffa4bbc8df | ||
|
c5ad9b25bf | ||
|
e38c53ff31 | ||
|
c24e8ceedf | ||
|
861f4bdcd1 | ||
|
254b426b2b | ||
|
63430ecf6f | ||
|
0d4e17b627 | ||
|
52fd82d4be | ||
|
bde239d389 | ||
|
192c0e5c36 | ||
|
1f75fb8d0a | ||
|
a228126ddb | ||
|
fbcf78c894 | ||
|
62c96ebad7 | ||
|
33332bf774 | ||
|
eaddc1c33b | ||
|
2e3f57401f | ||
|
ff7dfac544 | ||
|
e8324627e1 | ||
|
7088dbf3c1 | ||
|
8e2358288b | ||
|
b506281a49 | ||
|
198e5b3461 | ||
|
f0cb707ff2 | ||
|
9e69462405 | ||
|
a9f5e7ec73 | ||
|
23f3a9fb0d | ||
|
65b59c08d6 | ||
|
168bbfea29 | ||
|
ae1e2757ba | ||
|
e7da167c4e | ||
|
f887b4fefc | ||
|
e80c05183b | ||
|
77726a684f | ||
|
03cc8ffa12 | ||
|
429a27960f | ||
|
d03b6a7569 | ||
|
b481a17298 | ||
|
bbf354b6b6 | ||
|
dde379ba12 | ||
|
23b955c6b4 | ||
|
03b2a86a9d | ||
|
0b28b5f152 | ||
|
0d9e2e8b96 | ||
|
0c43d76ed0 | ||
|
6f6207412c | ||
|
41c82c88a9 | ||
|
eb799bdc70 | ||
|
ecebc3305f | ||
|
2bcc1aa0f4 | ||
|
a4d97e1929 | ||
|
469341094d | ||
|
4ccabe78de | ||
|
922d247c7b | ||
|
0b8aca750f | ||
|
9ef153ca53 | ||
|
1e0ddcedc1 | ||
|
edd2464a88 | ||
|
eead018195 | ||
|
fb5e628add | ||
|
42c9cbaa06 | ||
|
babdefd100 | ||
|
35d9350e48 | ||
|
e3edf20e22 | ||
|
e9a0bbc8d9 | ||
|
b3a10aca3b | ||
|
7bf79eac77 | ||
|
18c90ed2e0 | ||
|
6f4235c7f3 | ||
|
36264c44ef | ||
|
c91039b545 | ||
|
0035b3f36f | ||
|
755d8f01d0 | ||
|
a81f3a9b31 | ||
|
373e61e0df | ||
|
0043c8a6ce | ||
|
d52d19cb7f | ||
|
033b650c29 | ||
|
96a4de7b87 | ||
|
5071540af4 | ||
|
bd743d688a | ||
|
8951ac3f5b | ||
|
12f9aa46b5 | ||
|
436ff0d086 | ||
|
af21d7b4cd | ||
|
ebf7cbf918 | ||
|
7935ab82d4 | ||
|
32f993f862 | ||
|
6ab3d5b33c | ||
|
ea32e1b090 | ||
|
c25bf0a239 | ||
|
a4dbae0754 | ||
|
b11edddf68 | ||
|
352bfbc3a5 | ||
|
6099962e40 | ||
|
c5fc9a6922 | ||
|
44874b25b1 | ||
|
1014d5767c | ||
|
14069a64e1 | ||
|
07d66ce655 | ||
|
0c398eacdb | ||
|
e4c29d649b | ||
|
7ea8733e5b | ||
|
521e020b9a | ||
|
1d02ba7853 | ||
|
07429f5890 | ||
|
cac7052bf8 | ||
|
684029bc79 | ||
|
3106d8f85b | ||
|
66ffa03132 | ||
|
b2dbf35dcb | ||
|
5785d10cbb | ||
|
1b39167780 | ||
|
79977b09a0 | ||
|
65a95d4a49 | ||
|
f8de9b33b0 | ||
|
3775a877e2 | ||
|
b1a0abd104 | ||
|
766e2b0e1f | ||
|
b9df7f3cde | ||
|
4bf7b62939 | ||
|
55e0811294 | ||
|
e0e6ce9b65 | ||
|
b8c87ddb61 | ||
|
686176f821 | ||
|
e436fc93fd | ||
|
ea86237a4a | ||
|
4fa7ed1590 | ||
|
5ebf84a580 | ||
|
eec0d4c9ff | ||
|
cd7bb8a8c4 | ||
|
81f439d430 | ||
|
bd4cabaec2 | ||
|
9e68f6c27b | ||
|
53d1534cc2 | ||
|
96a032164f | ||
|
55dfc146c9 | ||
|
824dc192bc | ||
|
5f9053fb6e | ||
|
fafb7f8db6 | ||
|
bd62d56a37 | ||
|
0685d8a3a4 | ||
|
c8ed3893ad | ||
|
05a7e5e467 | ||
|
15a0db045e | ||
|
70b39d681b | ||
|
577327a629 | ||
|
ffbb8b2cfe | ||
|
33bd943b7a | ||
|
f6c09ca716 | ||
|
cac5f6dec3 | ||
|
3e3ce188b1 | ||
|
d631d97545 | ||
|
430804ef16 | ||
|
24f552c1fb | ||
|
a52f4cadc5 | ||
|
3cacc8edcf | ||
|
9c461824b3 | ||
|
0f82eed0db | ||
|
55cf2bd6b9 | ||
|
fddf0fb938 | ||
|
e4fca14c33 | ||
|
d3b027bade | ||
|
2386900e29 | ||
|
43a9c14d3b | ||
|
2740792df3 | ||
|
030fec590e | ||
|
66f99a1d39 | ||
|
5ca04c56f7 | ||
|
7c5e7bf093 | ||
|
37dcb6947b | ||
|
ef1e5c7d06 | ||
|
a54d896a45 | ||
|
e993297f60 | ||
|
0fdebe052c | ||
|
4e1a56472c | ||
|
c9144c0add | ||
|
044a5b6794 | ||
|
8637388cab | ||
|
e670ad6e1f | ||
|
75e5365ecd | ||
|
e54f1a24f0 | ||
|
7d4eff0bb8 | ||
|
963986d187 | ||
|
d18485844d | ||
|
510c8c2b97 | ||
|
322bf28a3e | ||
|
48eee7e75c | ||
|
7e3320c167 | ||
|
b17b9c6f03 | ||
|
71f93a27cd | ||
|
ef8ac2ecf8 | ||
|
0737ba1a83 | ||
|
f51658f0af | ||
|
856f00994b | ||
|
cb1318de1f | ||
|
cf826ddd2b | ||
|
c1d9f6c5a8 | ||
|
655f5e5996 | ||
|
c44f134a6a | ||
|
5f3eebe4e9 | ||
|
97381b04a9 | ||
|
7bcb1cc59f | ||
|
458bbd61b6 | ||
|
57a52835a2 | ||
|
aa5dc63a20 | ||
|
015b1ca266 | ||
|
06d263b4be | ||
|
42a24e5697 | ||
|
1338847ac5 | ||
|
0725645344 | ||
|
eaab4fb4c5 | ||
|
925ba8a78e | ||
|
4634d90715 | ||
|
d43c854c9f | ||
|
deddfa2a18 | ||
|
0fbc71ef49 | ||
|
18b101ccf5 | ||
|
f1cdf90653 | ||
|
09afedb42f | ||
|
2f8bb4fbf5 | ||
|
110e7f17d7 | ||
|
4508405d42 | ||
|
d3e23a2986 | ||
|
a15b710e31 | ||
|
4434361033 | ||
|
3c8e79ded5 | ||
|
67b17b1fdf | ||
|
a7f255fad9 | ||
|
0a833805d8 | ||
|
949740d030 | ||
|
f3716ab2e2 | ||
|
a244d7ef86 | ||
|
9dbcd28a5a | ||
|
e10eced7d0 | ||
|
20756423af | ||
|
9258d33caf | ||
|
2d27944d4e | ||
|
c032b19944 | ||
|
4fea91e936 | ||
|
c17f22f82b | ||
|
baaadad34d | ||
|
ea6addb4c8 | ||
|
3e56092445 | ||
|
a1b7a5414c | ||
|
43bb0d7741 | ||
|
a12b74ec17 | ||
|
33021ac24f | ||
|
c639dd5123 | ||
|
8d453b5755 | ||
|
0a61e160dd | ||
|
69cce5770c | ||
|
91ecd80a4f | ||
|
1409091643 | ||
|
ac0438e07b | ||
|
41ee1e27fd | ||
|
c214e2f0c4 | ||
|
404e952711 | ||
|
efc9098ce4 | ||
|
8899ea2c30 | ||
|
3e5a897eb5 | ||
|
a3acaf4b8c | ||
|
746f79e3f1 | ||
|
5810f7378d | ||
|
176e13cb13 | ||
|
e89d1f4c97 | ||
|
3cfe8ecac2 | ||
|
7c4ef5c1e9 | ||
|
a0a44dbeb5 | ||
|
dd45e56dc4 | ||
|
5390cb96c7 | ||
|
f6ba891e8f | ||
|
11dad96eb7 | ||
|
f7c9585003 | ||
|
087507c823 | ||
|
a3f0216ad7 | ||
|
0d43f7e01a | ||
|
1e7153e88f | ||
|
62a81c9a27 | ||
|
ea833a780c | ||
|
af527e95b8 | ||
|
27c91204b6 | ||
|
fb222d9fe9 | ||
|
3524386543 | ||
|
3c446663c3 | ||
|
c7db21f8b4 | ||
|
3925d1cb14 | ||
|
ebbf4ea4a0 | ||
|
764d8f6f2c | ||
|
c4b1e05e25 | ||
|
0b411969ad | ||
|
2a430f3335 | ||
|
28d49df72f | ||
|
13bfb4ecfc | ||
|
64e90b919d | ||
|
4935fcd225 | ||
|
2dcd40afc6 | ||
|
4a6d31ae37 | ||
|
52f8ad6aa2 | ||
|
8f9d2a9bfe | ||
|
d1afae6ca0 | ||
|
c3c91ed612 | ||
|
2ab6f18cee | ||
|
703f922ed8 | ||
|
1d04ceadff | ||
|
5e362554e2 | ||
|
92be395d66 | ||
|
30c6e2b762 | ||
|
656d54cf3b | ||
|
fc919ff56d | ||
|
abc1168582 | ||
|
2e26ae5ff1 | ||
|
94186826af | ||
|
4bbcd63826 | ||
|
7244d96274 | ||
|
f3d5e4e155 | ||
|
0ded84f81a | ||
|
7f08f27e7e | ||
|
de29b56c59 | ||
|
ad8f12eeef | ||
|
174ff02904 | ||
|
f07c9a7026 | ||
|
9922d2a4bb | ||
|
7406727090 | ||
|
87f1c97295 | ||
|
b2c17b5604 | ||
|
fca0a7ca1b | ||
|
0e587f7c33 | ||
|
8bee1e7b8b | ||
|
7eda39c9a9 | ||
|
515c15bf98 | ||
|
8c4fcc9ca9 |
1
.browserslistrc
Normal file
|
@ -0,0 +1 @@
|
|||
defaults
|
286
.eslintrc.hound.json
Normal file
|
@ -0,0 +1,286 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"block-scoped-var": [
|
||||
"error"
|
||||
],
|
||||
"consistent-return": [
|
||||
"error"
|
||||
],
|
||||
"curly": [
|
||||
"error",
|
||||
"all"
|
||||
],
|
||||
"eqeqeq": [
|
||||
"error"
|
||||
],
|
||||
"no-eval": [
|
||||
"error"
|
||||
],
|
||||
"no-implicit-globals": [
|
||||
"error"
|
||||
],
|
||||
"no-implied-eval": [
|
||||
"error"
|
||||
],
|
||||
"no-invalid-this": [
|
||||
"error"
|
||||
],
|
||||
"no-multi-spaces": [
|
||||
"error",
|
||||
{
|
||||
"exceptions": {
|
||||
"Property": true,
|
||||
"VariableDeclarator": true,
|
||||
"AssignmentExpression": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"no-new-wrappers": [
|
||||
"error"
|
||||
],
|
||||
"no-new": [
|
||||
"error"
|
||||
],
|
||||
"no-return-assign": [
|
||||
"error"
|
||||
],
|
||||
"no-self-compare": [
|
||||
"error"
|
||||
],
|
||||
"no-unmodified-loop-condition": [
|
||||
"error"
|
||||
],
|
||||
"no-unused-expressions": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-call": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-concat": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-escape": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-return": [
|
||||
"error"
|
||||
],
|
||||
"no-with": [
|
||||
"error"
|
||||
],
|
||||
"radix": [
|
||||
"error"
|
||||
],
|
||||
"yoda": [
|
||||
"error"
|
||||
],
|
||||
"no-undef-init": [
|
||||
"error"
|
||||
],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{
|
||||
"functions": false
|
||||
}
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"block-spacing": [
|
||||
"error"
|
||||
],
|
||||
"brace-style": [
|
||||
"error"
|
||||
],
|
||||
"camelcase": [
|
||||
"error"
|
||||
],
|
||||
"comma-spacing": [
|
||||
"error"
|
||||
],
|
||||
"comma-style": [
|
||||
"error"
|
||||
],
|
||||
"computed-property-spacing": [
|
||||
"error"
|
||||
],
|
||||
"consistent-this": [
|
||||
"error",
|
||||
"self"
|
||||
],
|
||||
"eol-last": [
|
||||
"error"
|
||||
],
|
||||
"func-call-spacing": [
|
||||
"error"
|
||||
],
|
||||
"func-name-matching": [
|
||||
"error"
|
||||
],
|
||||
"key-spacing": [
|
||||
"error",
|
||||
{
|
||||
"mode": "minimum"
|
||||
}
|
||||
],
|
||||
"keyword-spacing": [
|
||||
"error"
|
||||
],
|
||||
"max-len": [
|
||||
"error",
|
||||
80
|
||||
],
|
||||
"max-statements-per-line": [
|
||||
"error",
|
||||
{
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"new-cap": [
|
||||
"error"
|
||||
],
|
||||
"new-parens": [
|
||||
"error"
|
||||
],
|
||||
"no-array-constructor": [
|
||||
"error"
|
||||
],
|
||||
"no-mixed-operators": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
[
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"~",
|
||||
"<<",
|
||||
">>",
|
||||
">>>"
|
||||
],
|
||||
[
|
||||
"==",
|
||||
"!=",
|
||||
"===",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
],
|
||||
[
|
||||
"&&",
|
||||
"||"
|
||||
],
|
||||
[
|
||||
"in",
|
||||
"instanceof"
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-new-object": [
|
||||
"error"
|
||||
],
|
||||
"no-tabs": [
|
||||
"error"
|
||||
],
|
||||
"no-trailing-spaces": [
|
||||
"error"
|
||||
],
|
||||
"no-unneeded-ternary": [
|
||||
"error"
|
||||
],
|
||||
"no-whitespace-before-property": [
|
||||
"error"
|
||||
],
|
||||
"object-curly-spacing": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"one-var-declaration-per-line": [
|
||||
"error"
|
||||
],
|
||||
"one-var": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"operator-assignment": [
|
||||
"error"
|
||||
],
|
||||
"operator-linebreak": [
|
||||
"error",
|
||||
"after"
|
||||
],
|
||||
"quote-props": [
|
||||
"error",
|
||||
"as-needed"
|
||||
],
|
||||
"semi-spacing": [
|
||||
"error"
|
||||
],
|
||||
"space-before-blocks": [
|
||||
"error"
|
||||
],
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"space-in-parens": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"space-infix-ops": [
|
||||
"error"
|
||||
],
|
||||
"space-unary-ops": [
|
||||
"error",
|
||||
{
|
||||
"words": false,
|
||||
"nonwords": false
|
||||
}
|
||||
],
|
||||
"unicode-bom": [
|
||||
"error"
|
||||
],
|
||||
"no-caller": [
|
||||
"error"
|
||||
],
|
||||
"no-loop-func": [
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"globals": {
|
||||
"OpenSeadragon": "writable",
|
||||
"define": "readonly",
|
||||
"module": "readonly",
|
||||
"Map": "readonly"
|
||||
}
|
||||
}
|
300
.eslintrc.json
Normal file
|
@ -0,0 +1,300 @@
|
|||
{
|
||||
"root": true,
|
||||
"plugins": ["compat"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:compat/recommended"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "script",
|
||||
"ecmaFeatures": {
|
||||
"globalReturn": false,
|
||||
"impliedStrict": false,
|
||||
"jsx": false
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"OpenSeadragon": "writable",
|
||||
"define": "readonly",
|
||||
"module": "readonly",
|
||||
"Map": "readonly"
|
||||
},
|
||||
"rules": {
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"indent": [
|
||||
"off",
|
||||
4
|
||||
],
|
||||
"quotes": [
|
||||
"off",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"block-scoped-var": [
|
||||
"error"
|
||||
],
|
||||
"consistent-return": [
|
||||
"error"
|
||||
],
|
||||
"curly": [
|
||||
"error",
|
||||
"all"
|
||||
],
|
||||
"eqeqeq": [
|
||||
"error"
|
||||
],
|
||||
"no-eval": [
|
||||
"error"
|
||||
],
|
||||
"no-implicit-globals": [
|
||||
"error"
|
||||
],
|
||||
"no-implied-eval": [
|
||||
"error"
|
||||
],
|
||||
"no-invalid-this": [
|
||||
"error"
|
||||
],
|
||||
"no-multi-spaces": [
|
||||
"error",
|
||||
{
|
||||
"ignoreEOLComments": true,
|
||||
"exceptions": {
|
||||
"Property": true,
|
||||
"VariableDeclarator": true,
|
||||
"AssignmentExpression": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"no-new-wrappers": [
|
||||
"error"
|
||||
],
|
||||
"no-new": [
|
||||
"error"
|
||||
],
|
||||
"no-return-assign": [
|
||||
"error"
|
||||
],
|
||||
"no-self-compare": [
|
||||
"error"
|
||||
],
|
||||
"no-unmodified-loop-condition": [
|
||||
"error"
|
||||
],
|
||||
"no-unused-expressions": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-call": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-concat": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-escape": [
|
||||
"error"
|
||||
],
|
||||
"no-useless-return": [
|
||||
"error"
|
||||
],
|
||||
"no-with": [
|
||||
"error"
|
||||
],
|
||||
"radix": [
|
||||
"error"
|
||||
],
|
||||
"yoda": [
|
||||
"off"
|
||||
],
|
||||
"no-undef-init": [
|
||||
"error"
|
||||
],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{
|
||||
"functions": false,
|
||||
"classes": true,
|
||||
"variables": true
|
||||
}
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
"off",
|
||||
"never"
|
||||
],
|
||||
"block-spacing": [
|
||||
"off"
|
||||
],
|
||||
"brace-style": [
|
||||
"off"
|
||||
],
|
||||
"camelcase": [
|
||||
"error"
|
||||
],
|
||||
"comma-spacing": [
|
||||
"error"
|
||||
],
|
||||
"comma-style": [
|
||||
"error"
|
||||
],
|
||||
"computed-property-spacing": [
|
||||
"off"
|
||||
],
|
||||
"consistent-this": [
|
||||
"off",
|
||||
"self"
|
||||
],
|
||||
"eol-last": [
|
||||
"error"
|
||||
],
|
||||
"func-call-spacing": [
|
||||
"error"
|
||||
],
|
||||
"func-name-matching": [
|
||||
"error"
|
||||
],
|
||||
"key-spacing": [
|
||||
"error",
|
||||
{
|
||||
"mode": "minimum"
|
||||
}
|
||||
],
|
||||
"keyword-spacing": [
|
||||
"off"
|
||||
],
|
||||
"max-len": [
|
||||
"off",
|
||||
80
|
||||
],
|
||||
"max-statements-per-line": [
|
||||
"error",
|
||||
{
|
||||
"max": 1
|
||||
}
|
||||
],
|
||||
"new-cap": [
|
||||
"error"
|
||||
],
|
||||
"new-parens": [
|
||||
"error"
|
||||
],
|
||||
"no-array-constructor": [
|
||||
"error"
|
||||
],
|
||||
"no-mixed-operators": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
[
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"~",
|
||||
"<<",
|
||||
">>",
|
||||
">>>"
|
||||
],
|
||||
[
|
||||
"==",
|
||||
"!=",
|
||||
"===",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
],
|
||||
[
|
||||
"&&",
|
||||
"||"
|
||||
],
|
||||
[
|
||||
"in",
|
||||
"instanceof"
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-new-object": [
|
||||
"error"
|
||||
],
|
||||
"no-tabs": [
|
||||
"error"
|
||||
],
|
||||
"no-trailing-spaces": [
|
||||
"error"
|
||||
],
|
||||
"no-unneeded-ternary": [
|
||||
"error"
|
||||
],
|
||||
"no-whitespace-before-property": [
|
||||
"error"
|
||||
],
|
||||
"object-curly-spacing": [
|
||||
"off",
|
||||
"always"
|
||||
],
|
||||
"one-var-declaration-per-line": [
|
||||
"error"
|
||||
],
|
||||
"one-var": [
|
||||
"off",
|
||||
"never"
|
||||
],
|
||||
"operator-assignment": [
|
||||
"error"
|
||||
],
|
||||
"operator-linebreak": [
|
||||
"error",
|
||||
"after"
|
||||
],
|
||||
"quote-props": [
|
||||
"error",
|
||||
"as-needed"
|
||||
],
|
||||
"semi-spacing": [
|
||||
"error"
|
||||
],
|
||||
"space-before-blocks": [
|
||||
"off"
|
||||
],
|
||||
"space-before-function-paren": [
|
||||
"off",
|
||||
"never"
|
||||
],
|
||||
"space-in-parens": [
|
||||
"off",
|
||||
"never"
|
||||
],
|
||||
"space-infix-ops": [
|
||||
"error"
|
||||
],
|
||||
"space-unary-ops": [
|
||||
"error",
|
||||
{
|
||||
"words": true,
|
||||
"nonwords": false
|
||||
}
|
||||
],
|
||||
"unicode-bom": [
|
||||
"error"
|
||||
],
|
||||
"no-caller": [
|
||||
"error"
|
||||
],
|
||||
"no-loop-func": [
|
||||
"error"
|
||||
]
|
||||
}
|
||||
}
|
12
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # iangilman
|
||||
patreon: # iangilman
|
||||
open_collective: openseadragon
|
||||
ko_fi: # iangilman
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
4
.gitignore
vendored
|
@ -3,7 +3,9 @@ node_modules
|
|||
build/
|
||||
sftp-config.json
|
||||
coverage/
|
||||
temp/
|
||||
instrumented/
|
||||
.idea
|
||||
/nbproject/private/
|
||||
.directory
|
||||
local-test
|
||||
.DS_Store
|
||||
|
|
3
.hound.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
eslint:
|
||||
enabled: true
|
||||
config_file: .eslintrc.hound.json
|
15
.jshintrc
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"browser": true,
|
||||
"curly": true,
|
||||
"eqeqeq": false,
|
||||
"loopfunc": false,
|
||||
"noarg": true,
|
||||
"trailing": true,
|
||||
"undef": true,
|
||||
"unused": false,
|
||||
|
||||
"globals": {
|
||||
"OpenSeadragon": true,
|
||||
"define": false
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
# Specify dist until Travis CI default Runner OS updates to one
|
||||
# with glibc version required by newer Node versions
|
||||
# See: https://github.com/nodejs/node/issues/42351#issuecomment-1068424442
|
||||
dist: jammy
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- 0.10
|
||||
before_script:
|
||||
- lts/*
|
||||
before_install:
|
||||
- npm install -g grunt-cli
|
||||
|
|
23
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
// The number of spaces a tab is equal to.
|
||||
"editor.tabSize": 4,
|
||||
|
||||
// Insert spaces when pressing Tab.
|
||||
"editor.insertSpaces": true,
|
||||
|
||||
// When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents.
|
||||
"editor.detectIndentation": false,
|
||||
|
||||
// Columns at which to show vertical rulers
|
||||
"editor.rulers": [80],
|
||||
|
||||
// The default character set encoding to use when reading and writing files.
|
||||
"files.encoding": "utf8",
|
||||
|
||||
// The default end of line character.
|
||||
"files.eol": "\n",
|
||||
|
||||
// When enabled, will trim trailing whitespace when you save a file.
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
26
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"command": "grunt",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"type": "grunt",
|
||||
"task": "build",
|
||||
"problemMatcher": [
|
||||
"$lessCompile",
|
||||
"$tsc",
|
||||
"$jshint"
|
||||
],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "test",
|
||||
"type": "grunt",
|
||||
"task": "test",
|
||||
"problemMatcher": [],
|
||||
"group": "test"
|
||||
}
|
||||
]
|
||||
}
|
25
.vscode/tasks.json.old
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "0.1.0",
|
||||
"command": "grunt",
|
||||
"isShellCommand": true,
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "build",
|
||||
"args": [],
|
||||
"isBuildCommand": true,
|
||||
"isWatching": false,
|
||||
"problemMatcher": [
|
||||
"$lessCompile",
|
||||
"$tsc",
|
||||
"$jshint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "test",
|
||||
"args": [],
|
||||
"isTestCommand": true
|
||||
}
|
||||
]
|
||||
}
|
39
CITATION.cff
Normal file
|
@ -0,0 +1,39 @@
|
|||
cff-version: 1.2.0
|
||||
title: OpenSeadragon
|
||||
message: "If you use this software, please cite it using the metadata from this file."
|
||||
type: software
|
||||
authors:
|
||||
- given-names: Ian
|
||||
family-names: Gilman
|
||||
email: ian@iangilman.com
|
||||
- given-names: Aseem
|
||||
family-names: Kishore
|
||||
- given-names: Chris
|
||||
family-names: Thatcher
|
||||
- given-names: Mark
|
||||
family-names: Salsbery
|
||||
- given-names: Antoine
|
||||
family-names: Vandecreme
|
||||
- given-names: Thomas
|
||||
family-names: Pearce
|
||||
identifiers:
|
||||
- type: url
|
||||
value: https://openseadragon.github.io/
|
||||
description: Homepage
|
||||
- type: url
|
||||
value: https://github.com/openseadragon/openseadragon
|
||||
description: Repository
|
||||
repository-code: https://github.com/openseadragon/openseadragon
|
||||
url: https://openseadragon.github.io/
|
||||
abstract: "An open-source, web-based viewer for high-resolution zoomable images, implemented in pure JavaScript, for desktop and mobile."
|
||||
keywords:
|
||||
- javascript
|
||||
- image
|
||||
- zooming
|
||||
- viewer
|
||||
- image-viewer
|
||||
- high-resolution
|
||||
- iiif
|
||||
license: BSD-3-Clause
|
||||
version: 5.0.1
|
||||
date-released: 2024-11-09
|
96
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,96 @@
|
|||
### Contributing
|
||||
|
||||
OpenSeadragon is truly a community project; we welcome your involvement!
|
||||
|
||||
When contributing, please attempt to match the code style already in the codebase.
|
||||
However, we are in the process of changing our code style (see issue [#456](https://github.com/openseadragon/openseadragon/issues/456)), so avoid spaces inside parentheses and square brackets. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](https://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see [idiomatic.js](https://github.com/rwldrn/idiomatic.js/).
|
||||
|
||||
When fixing bugs and adding features, when appropriate please also:
|
||||
|
||||
* Update related doc comments (we use [JSDoc 3](https://jsdoc.app/))
|
||||
* Add/update related unit tests
|
||||
|
||||
If you're new to the project, check out our [good first issues](https://github.com/openseadragon/openseadragon/issues?labels=good+first+issue&page=1&state=open) for some places to dip your toe in the water.
|
||||
|
||||
If you're new to open source in general, check out [GitHub's open source intro guide](https://guides.github.com/activities/contributing-to-open-source/).
|
||||
|
||||
### First Time Setup
|
||||
|
||||
All command-line operations for building and testing OpenSeadragon are scripted using [Grunt](https://gruntjs.com/) which is based on [Node.js](https://nodejs.org/). To get set up:
|
||||
|
||||
1. Install Node, if you haven't already (available at the link above)
|
||||
1. Install the Grunt command line runner (if you haven't already); on the command line, run `npm install -g grunt-cli`
|
||||
1. Clone the openseadragon repository
|
||||
1. On the command line, go in to the openseadragon folder
|
||||
1. Run `npm install`
|
||||
|
||||
You're set, all development dependencies should have been installed and the project built...
|
||||
continue reading for build and test instructions.
|
||||
|
||||
### Building from Source
|
||||
|
||||
To build, just run (on the command line, in the openseadragon folder):
|
||||
|
||||
grunt
|
||||
|
||||
If you want Grunt to watch your source files and rebuild every time you change one, use:
|
||||
|
||||
grunt watch
|
||||
|
||||
To have it watch your source files and also run a server for you to test in:
|
||||
|
||||
grunt dev
|
||||
|
||||
The built files appear in the `build` folder.
|
||||
|
||||
If you want to build tar and zip files for distribution (they will also appear in the `build` folder), use:
|
||||
|
||||
grunt package
|
||||
|
||||
Note that the `build` folder is masked with .gitignore; it's just for your local use, and won't be checked in to the repository.
|
||||
|
||||
You can also publish the built version to the site-build repository. This assumes you have cloned it next to this repository. The command is:
|
||||
|
||||
grunt publish
|
||||
|
||||
... which will delete the existing openseadragon folder, along with the .zip and .tar.gz files, out of the site-build folder and replace them with newly built ones from the source in this repository; you'll then need to commit the changes to site-build.
|
||||
|
||||
### Testing
|
||||
|
||||
Our tests are based on [QUnit](https://qunitjs.com/) and [Puppeteer](https://github.com/GoogleChrome/puppeteer); they're both installed when you run `npm install`. To run on the command line:
|
||||
|
||||
grunt test
|
||||
|
||||
To test a specific module (`navigator` here) only:
|
||||
|
||||
grunt test --module="navigator"
|
||||
|
||||
If you wish to work interactively with the tests or test your changes:
|
||||
|
||||
grunt connect watch
|
||||
|
||||
and open `http://localhost:8000/test/test.html` in your browser.
|
||||
|
||||
Another good page, if you want to interactively test out your changes, is `http://localhost:8000/test/demo/basic.html`.
|
||||
|
||||
|
||||
> Note: corresponding npm commands for the above are:
|
||||
> - npm run test
|
||||
> - npm run test -- --module="navigator"
|
||||
> - npm run dev
|
||||
|
||||
You can also get a report of the tests' code coverage:
|
||||
|
||||
grunt coverage
|
||||
|
||||
The report shows up at `coverage/html/index.html` viewable in a browser.
|
||||
|
||||
### Installing from forked Github repo/branch
|
||||
|
||||
This project is now compatible with direct installation of forked Github repos/branches via npm/yarn (possible because of the new [prepare](https://docs.npmjs.com/misc/scripts) command). This enables quick testing of a bugfix or feature addition via a forked repo. In order to do this:
|
||||
|
||||
1. Install the Grunt command line runner (if you haven't already); on the command line, run `npm install -g grunt-cli` (or `yarn global add grunt-cli`)
|
||||
1. Remove any currently installed openseadragon package via `npm uninstall openseadragon` or `yarn remove openseadragon`
|
||||
1. Add the specific forked repo/branch by running `npm install git://github.com/username/openseadragon.git#branch-name` or `yarn add git://github.com/username/openseadragon.git#branch-name`. Make sure to replace username and branch-name with proper targets.
|
||||
|
||||
During installation, the package should be correctly built via grunt and can then be used via `import Openseadragon from 'openseadragon'` or `var Openseadragon = require('openseadragon')` statements as if the official package were installed.
|
130
Gruntfile.js
|
@ -1,18 +1,22 @@
|
|||
/* eslint-disable no-redeclare */
|
||||
/* global module */
|
||||
|
||||
module.exports = function(grunt) {
|
||||
/* eslint-disable no-undef */
|
||||
var dateFormat = require('dateformat');
|
||||
|
||||
// ----------
|
||||
grunt.loadNpmTasks("grunt-contrib-compress");
|
||||
grunt.loadNpmTasks("grunt-contrib-concat");
|
||||
grunt.loadNpmTasks("grunt-contrib-jshint");
|
||||
grunt.loadNpmTasks("grunt-contrib-uglify");
|
||||
grunt.loadNpmTasks("grunt-qunit-istanbul");
|
||||
grunt.loadNpmTasks("grunt-contrib-qunit");
|
||||
grunt.loadNpmTasks("grunt-contrib-connect");
|
||||
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||
grunt.loadNpmTasks("grunt-contrib-clean");
|
||||
grunt.loadNpmTasks("grunt-eslint");
|
||||
grunt.loadNpmTasks("grunt-git-describe");
|
||||
grunt.loadNpmTasks('grunt-text-replace');
|
||||
grunt.loadNpmTasks('grunt-istanbul');
|
||||
|
||||
// ----------
|
||||
var packageJson = grunt.file.readJSON("package.json"),
|
||||
|
@ -21,13 +25,16 @@ module.exports = function(grunt) {
|
|||
packageDirName = "openseadragon-bin-" + packageJson.version,
|
||||
packageDir = "build/" + packageDirName + "/",
|
||||
releaseRoot = "../site-build/built-openseadragon/",
|
||||
coverageDir = 'coverage/' + dateFormat(new Date(), 'yyyymmdd-HHMMss'),
|
||||
sources = [
|
||||
"src/openseadragon.js",
|
||||
"src/matrix3.js",
|
||||
"src/fullscreen.js",
|
||||
"src/eventsource.js",
|
||||
"src/mousetracker.js",
|
||||
"src/control.js",
|
||||
"src/controldock.js",
|
||||
"src/placement.js",
|
||||
"src/viewer.js",
|
||||
"src/navigator.js",
|
||||
"src/strings.js",
|
||||
|
@ -38,9 +45,12 @@ module.exports = function(grunt) {
|
|||
"src/iiiftilesource.js",
|
||||
"src/osmtilesource.js",
|
||||
"src/tmstilesource.js",
|
||||
"src/zoomifytilesource.js",
|
||||
"src/legacytilesource.js",
|
||||
"src/imagetilesource.js",
|
||||
"src/tilesourcecollection.js",
|
||||
"src/priorityqueue.js",
|
||||
"src/datatypeconvertor.js",
|
||||
"src/button.js",
|
||||
"src/buttongroup.js",
|
||||
"src/rectangle.js",
|
||||
|
@ -50,18 +60,32 @@ module.exports = function(grunt) {
|
|||
"src/imageloader.js",
|
||||
"src/tile.js",
|
||||
"src/overlay.js",
|
||||
"src/drawer.js",
|
||||
"src/drawerbase.js",
|
||||
"src/htmldrawer.js",
|
||||
"src/canvasdrawer.js",
|
||||
"src/webgldrawer.js",
|
||||
"src/viewport.js",
|
||||
"src/tiledimage.js",
|
||||
"src/tilecache.js",
|
||||
"src/world.js"
|
||||
"src/world.js",
|
||||
];
|
||||
|
||||
var banner = "//! <%= pkg.name %> <%= pkg.version %>\n" +
|
||||
"//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n" +
|
||||
"//! Git commit: <%= gitInfo %>\n" +
|
||||
"//! http://openseadragon.github.io\n" +
|
||||
"//! License: http://openseadragon.github.io/license/\n\n";
|
||||
|
||||
// ----------
|
||||
grunt.event.once('git-describe', function (rev) {
|
||||
grunt.config.set('gitInfo', rev);
|
||||
});
|
||||
|
||||
let moduleFilter = '';
|
||||
if (grunt.option('module')) {
|
||||
moduleFilter = '?module=' + grunt.option('module')
|
||||
}
|
||||
|
||||
// ----------
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
|
@ -75,7 +99,7 @@ module.exports = function(grunt) {
|
|||
clean: {
|
||||
build: ["build"],
|
||||
package: [packageDir],
|
||||
coverage: ["coverage"],
|
||||
coverage: ["instrumented"],
|
||||
release: {
|
||||
src: [releaseRoot],
|
||||
options: {
|
||||
|
@ -85,12 +109,9 @@ module.exports = function(grunt) {
|
|||
},
|
||||
concat: {
|
||||
options: {
|
||||
banner: "//! <%= pkg.name %> <%= pkg.version %>\n" +
|
||||
"//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n" +
|
||||
"//! Git commit: <%= gitInfo %>\n" +
|
||||
"//! http://openseadragon.github.io\n" +
|
||||
"//! License: http://openseadragon.github.io/license/\n\n",
|
||||
process: true
|
||||
banner: banner,
|
||||
process: true,
|
||||
sourceMap: true
|
||||
},
|
||||
dist: {
|
||||
src: [ "<banner>" ].concat(sources),
|
||||
|
@ -111,12 +132,19 @@ module.exports = function(grunt) {
|
|||
},
|
||||
uglify: {
|
||||
options: {
|
||||
preserveComments: "some",
|
||||
preserveComments: false,
|
||||
banner: banner,
|
||||
compress: {
|
||||
sequences: false,
|
||||
/* eslint-disable camelcase */
|
||||
join_vars: false
|
||||
},
|
||||
sourceMap: true,
|
||||
sourceMapName: 'build/openseadragon/openseadragon.min.js.map'
|
||||
sourceMapName: 'build/openseadragon/openseadragon.min.js.map',
|
||||
sourceMapIn: 'build/openseadragon/openseadragon.js.map'
|
||||
},
|
||||
openseadragon: {
|
||||
src: [ distribution ],
|
||||
src: distribution,
|
||||
dest: minified
|
||||
}
|
||||
},
|
||||
|
@ -143,19 +171,24 @@ module.exports = function(grunt) {
|
|||
qunit: {
|
||||
normal: {
|
||||
options: {
|
||||
urls: [ "http://localhost:8000/test/test.html" ]
|
||||
}
|
||||
urls: [ "http://localhost:8000/test/test.html" + moduleFilter ],
|
||||
timeout: 10000,
|
||||
puppeteer: {
|
||||
headless: 'new'
|
||||
}
|
||||
},
|
||||
},
|
||||
coverage: {
|
||||
options: {
|
||||
urls: [ "http://localhost:8000/test/coverage.html" ],
|
||||
urls: [ "http://localhost:8000/test/coverage.html" + moduleFilter ],
|
||||
coverage: {
|
||||
src: ['src/*.js'],
|
||||
htmlReport: 'coverage/html/',
|
||||
instrumentedFiles: 'temp/',
|
||||
htmlReport: coverageDir + '/html/',
|
||||
instrumentedFiles: 'instrumented/src/',
|
||||
baseUrl: '.',
|
||||
disposeCollector: true
|
||||
}
|
||||
},
|
||||
timeout: 10000
|
||||
}
|
||||
},
|
||||
all: {
|
||||
|
@ -168,7 +201,12 @@ module.exports = function(grunt) {
|
|||
server: {
|
||||
options: {
|
||||
port: 8000,
|
||||
base: "."
|
||||
base: {
|
||||
path: ".",
|
||||
options: {
|
||||
stylesheet: 'style.css'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -176,16 +214,50 @@ module.exports = function(grunt) {
|
|||
files: [ "Gruntfile.js", "src/*.js", "images/*" ],
|
||||
tasks: "watchTask"
|
||||
},
|
||||
jshint: {
|
||||
eslint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
overrideConfigFile: '.eslintrc.json'
|
||||
},
|
||||
beforeconcat: sources,
|
||||
afterconcat: [ distribution ]
|
||||
target: sources
|
||||
},
|
||||
"git-describe": {
|
||||
options: {
|
||||
failOnError: false
|
||||
},
|
||||
build: {}
|
||||
}
|
||||
},
|
||||
gitInfo: "unknown",
|
||||
instrument: {
|
||||
files: sources,
|
||||
options: {
|
||||
lazy: false,
|
||||
basePath: 'instrumented/'
|
||||
}
|
||||
},
|
||||
reloadTasks: {
|
||||
rootPath: "instrumented/src/"
|
||||
},
|
||||
storeCoverage: {
|
||||
options: {
|
||||
dir: coverageDir,
|
||||
'include-all-sources': true
|
||||
}
|
||||
},
|
||||
makeReport: {
|
||||
src: "coverage/**/*.json",
|
||||
options: {
|
||||
type: [ "lcov", "html" ],
|
||||
dir: coverageDir,
|
||||
print: "detail"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.event.on("qunit.coverage", function(coverage) {
|
||||
var reportPath = coverageDir + "/coverage.json";
|
||||
|
||||
// Create the coverage file
|
||||
grunt.file.write(reportPath, JSON.stringify(coverage));
|
||||
});
|
||||
|
||||
// ----------
|
||||
|
@ -254,8 +326,8 @@ module.exports = function(grunt) {
|
|||
// Build task.
|
||||
// Cleans out the build folder and builds the code and images into it, checking lint.
|
||||
grunt.registerTask("build", [
|
||||
"clean:build", "jshint:beforeconcat", "git-describe", "concat", "jshint:afterconcat",
|
||||
"uglify", "replace:cleanPaths", "copy:build"
|
||||
"clean:build", "git-describe", "eslint", "concat", "uglify",
|
||||
"replace:cleanPaths", "copy:build"
|
||||
]);
|
||||
|
||||
// ----------
|
||||
|
@ -273,7 +345,7 @@ module.exports = function(grunt) {
|
|||
// ----------
|
||||
// Coverage task.
|
||||
// Outputs unit test code coverage report.
|
||||
grunt.registerTask("coverage", ["clean:coverage", "connect", "qunit:coverage"]);
|
||||
grunt.registerTask("coverage", ["clean:coverage", "instrument", "connect", "qunit:coverage", "makeReport"]);
|
||||
|
||||
// ----------
|
||||
// Package task.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Copyright (C) 2009 CodePlex Foundation
|
||||
Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
|
106
README.md
|
@ -1,95 +1,39 @@
|
|||
# OpenSeadragon
|
||||
[](https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
<!-- [![Gitter][gitter-badge]][gitter]
|
||||
[![Build Status][build-badge]][build] -->
|
||||
|
||||
An open-source, web-based viewer for zoomable images, implemented in pure JavaScript.
|
||||
|
||||
See it in action and get started using it at http://openseadragon.github.io/.
|
||||
See it in action and get started using it at [https://openseadragon.github.io/][openseadragon].
|
||||
|
||||
## Stable Builds
|
||||
|
||||
See the [GitHub releases page](https://github.com/openseadragon/openseadragon/releases).
|
||||
See the [GitHub releases page][github-releases].
|
||||
|
||||
## Development
|
||||
|
||||
If you want to use OpenSeadragon in your own projects, you can find the latest stable build, API documentation, and example code at http://openseadragon.github.io/. If you want to modify OpenSeadragon and/or contribute to its development, read on.
|
||||
|
||||
### First Time Setup
|
||||
|
||||
All command-line operations for building and testing OpenSeadragon are scripted using [Grunt](http://gruntjs.com/) which is based on [Node.js](http://nodejs.org/). To get set up:
|
||||
|
||||
1. Install Node, if you haven't already (available at the link above)
|
||||
1. Install the Grunt command line runner (if you haven't already); on the command line, run `npm install -g grunt-cli`
|
||||
1. Clone the openseadragon repository
|
||||
1. On the command line, go in to the openseadragon folder
|
||||
1. Run `npm install`
|
||||
|
||||
You're set... continue reading for build and test instructions.
|
||||
|
||||
### Building from Source
|
||||
|
||||
To build, just run (on the command line, in the openseadragon folder):
|
||||
|
||||
grunt
|
||||
|
||||
If you want Grunt to watch your source files and rebuild every time you change one, use:
|
||||
|
||||
grunt watch
|
||||
|
||||
To have it watch your source files and also run a server for you to test in:
|
||||
|
||||
grunt dev
|
||||
|
||||
The built files appear in the `build` folder.
|
||||
|
||||
If you want to build tar and zip files for distribution (they will also appear in the `build` folder), use:
|
||||
|
||||
grunt package
|
||||
|
||||
Note that the `build` folder is masked with .gitignore; it's just for your local use, and won't be checked in to the repository.
|
||||
|
||||
You can also publish the built version to the site-build repository. This assumes you have cloned it next to this repository. The command is:
|
||||
|
||||
grunt publish
|
||||
|
||||
... which will delete the existing openseadragon folder, along with the .zip and .tar.gz files, out of the site-build folder and replace them with newly built ones from the source in this repository; you'll then need to commit the changes to site-build.
|
||||
|
||||
### Testing
|
||||
|
||||
Our tests are based on [QUnit](http://qunitjs.com/) and [PhantomJS](http://phantomjs.org/); they're both installed when you run `npm install`. At the moment we don't have much in the way of tests, but we're working to fix that. To run on the command line:
|
||||
|
||||
grunt test
|
||||
|
||||
If you wish to work interactively with the tests or test your changes:
|
||||
|
||||
grunt connect watch
|
||||
|
||||
and open `http://localhost:8000/test/test.html` in your browser.
|
||||
|
||||
Another good page, if you want to interactively test out your changes, is `http://localhost:8000/test/demo/basic.html`.
|
||||
|
||||
You can also get a report of the tests' code coverage:
|
||||
|
||||
grunt coverage
|
||||
|
||||
The report shows up at `coverage/html/index.html` viewable in a browser.
|
||||
|
||||
### Contributing
|
||||
|
||||
OpenSeadragon is truly a community project; we welcome your involvement!
|
||||
|
||||
When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](http://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see [idiomatic.js](https://github.com/rwldrn/idiomatic.js/).
|
||||
|
||||
When fixing bugs and adding features, when appropriate please also:
|
||||
|
||||
* Update related doc comments (we use [JSDoc 3](http://usejsdoc.org/))
|
||||
* Add/update related unit tests
|
||||
|
||||
If you're new to the project, check out our [good first bug](https://github.com/openseadragon/openseadragon/issues?labels=good+first+bug&page=1&state=open) issues for some places to dip your toe in the water.
|
||||
|
||||
If you're new to open source in general, check out [GitHub's open source intro guide](https://guides.github.com/overviews/os-contributing/).
|
||||
If you want to use OpenSeadragon in your own projects, you can find the latest stable build, API documentation, and example code at [https://openseadragon.github.io/][openseadragon]. If you want to modify OpenSeadragon and/or contribute to its development, read the [contributing guide][github-contributing] for instructions.
|
||||
|
||||
## License
|
||||
|
||||
OpenSeadragon is released under the New BSD license. For details, see the file LICENSE.txt.
|
||||
OpenSeadragon is released under the New BSD license. For details, see the [LICENSE.txt file][github-license].
|
||||
|
||||
[](http://travis-ci.org/openseadragon/openseadragon)
|
||||
[openseadragon]: https://openseadragon.github.io/
|
||||
<!-- [gitter-badge]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitter]: https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[build-badge]: https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master
|
||||
[build]: https://travis-ci.org/openseadragon/openseadragon -->
|
||||
[github-releases]: https://github.com/openseadragon/openseadragon/releases
|
||||
[github-contributing]: https://github.com/openseadragon/openseadragon/blob/master/CONTRIBUTING.md
|
||||
[github-license]: https://github.com/openseadragon/openseadragon/blob/master/LICENSE.txt
|
||||
|
||||
## Sponsors
|
||||
|
||||
We are grateful for the (development or financial) contribution to the OpenSeadragon project.
|
||||
|
||||
<a href="https://www.bbmri-eric.eu"><img alt="BBMRI ERIC Logo" src="assets/logos/bbmri-logo.png" height="70" /></a>
|
||||
|
||||
<a href="https://www.pitt.edu/"><img alt="University of Pittsburgh Logo" src="assets/logos/pitt-logo.png" height="70" /></a>
|
||||
|
||||
<a href="https://www.stanford.edu/"><img alt="Stanford University Logo" src="assets/logos/stanford-logo.png" height="70" /></a>
|
||||
|
|
BIN
assets/logos/bbmri-logo.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
assets/logos/pitt-logo.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
assets/logos/stanford-logo.png
Normal file
After Width: | Height: | Size: 66 KiB |
370
changelog.txt
|
@ -1,6 +1,376 @@
|
|||
OPENSEADRAGON CHANGELOG
|
||||
=======================
|
||||
|
||||
6.0.0: (in progress...)
|
||||
|
||||
* NEW BEHAVIOR: OpenSeadragon Data Pipeline Overhaul (#2407, #2643 @Aiosa)
|
||||
* DEPRECATION: Properties on tile that manage drawer data, or store data to draw: Tile.[element|imgElement|style|context2D|getImage|getCanvasContext] and transitively Tile.getScaleForEdgeSmoothing
|
||||
* DEPRECATION: TileSource data lifecycle handlers: system manages these automatically: TileSource.[createTileCache|destroyTileCache|getTileCacheData|getTileCacheDataAsImage|getTileCacheDataAsContext2D]
|
||||
* Tiles data is driven by caches: tiles can have multiple caches and cache can reference multiple tiles.
|
||||
* Data types & conversion pipeline: caches support automated conversion between types, and call optionally destructors. These are asynchronous.
|
||||
* Data conversion reasoning: the system keeps costs of convertors and seeks the cheapest conversion to a target format (using Dijkstra).
|
||||
* Async support: events can now await handlers. Added OpenSeadragon.Promise proxy object. This object supports also synchronous mode.
|
||||
* Drawers define what data they are able to work with, and receive automatically data from one of the declared types.
|
||||
* Drawers now store data only inside cache, and provide optional type convertors to move data into a format they can work with.
|
||||
* TileSource equality operator. TileSource destructor support. TileSources now must output type of the data they download [context.finish].
|
||||
* Zombies: data can outlive tiles, and be kept in the system to wait if they are not suddenly needed. Turned on automatically with TiledImage addition with `replace: true` and equality test success.
|
||||
* ImagesLoadedPerFrame is boosted 10 times when system is fresh (reset / open) and then declines back to original value.
|
||||
* CacheRecord supports 'internal cache' of 'SimpleCache' type. This cache can be used by drawers to hide complex types used for rendering. Such caches are stored internally on CacheRecord objects.
|
||||
* CacheRecord drives asynchronous data management and ensures correct behavior through awaiting Promises.
|
||||
* TileCache adds new methods for cache modification: renameCache, cloneCache, injectCache, replaceCache, restoreTilesThatShareOriginalCache, safeUnloadCache, unloadCacheForTile and more. Used internally within invalidation events
|
||||
* Tiles have up to two 'originalCacheKey' and 'cacheKey' caches, which keep original data and target drawn data (if modified).
|
||||
* Invalidation Pipeline: New event 'tile-invalidated' and requestInvalidate methods on World and TiledImage. Tiles get methods to modify data to draw, system prepares data for drawing and swaps them with the current main tile cache.
|
||||
* New test suites for the new cache system, conversion pipeline and invalidation events.
|
||||
* New testing/demo utilities (MockSeadragon, DrawerSwitcher for switching drawers in demos, getBuiltInDrawersForTest for testing all drawers), serialization guard in tests to remove circular references.
|
||||
* New demos, demonstrating the new pipeline. New demos for older plugins to show how compatible new version is.
|
||||
* Misc: updated CSS for dev server, new dev & test commands.
|
||||
* New option: loadDestinationTilesOnAnimation. With it on, during animations, OSD loads tiles in the destination region, rather than the areas passed through on the way to the destination. This new feature is on by default. (#2686, #2690 @MichaelWGibson)
|
||||
* Overlay wrapper elements now have a "openseadragon-overlay-wrapper" class. If the overlay element has an ID, the wrapper gets a variant on that ID, but if the overlay element does not have an ID, we no longer give the wrapper an ID. (#2698 @lokaesshwar)
|
||||
* The functions the viewer uses to operate the zoom in and zoom out buttons are now accessible to be called programatically (#2702 @achu1998)
|
||||
* Improved how OpenSeadragon is imported in various environments (#2644 @Aiosa)
|
||||
* Improved documentation (#2676 @bennlich)
|
||||
* Improved unit tests (#2640 @harshkg23)
|
||||
* Fixed: Transparency detection didn't always work properly (#2636 @pcram-techcyte)
|
||||
* Fixed: MouseTracker's hasGestureHandlers and hasScrollHandler values were not getting updated upon dynamically adding/removing handlers (#2649 @Seafret)
|
||||
* Fixed: Sometimes images wouldn't update when you changed their opacity (#2652 @pearcetm)
|
||||
* Fixed: Possible MouseTracker hash collision (#2657 @cff29546)
|
||||
|
||||
5.0.1:
|
||||
|
||||
* Improved overlay handling so it plays better with other libraries (#2582 @BeebBenjamin)
|
||||
* WebGLDrawer now supports the imageSmoothingEnabled option (#2615 @pearcetm)
|
||||
* Fixed: If you switched from WebGL drawer to canvas drawer, it didn't clean up properly (#2570 @pearcetm)
|
||||
* Fixed: TiledImage.setClip would sometimes leave tiles unloaded (#2590 @pearcetm)
|
||||
* Fixed: The WebGL drawer didn't support viewportMargins (#2600, #2606 @pearcetm)
|
||||
* Fixed: If you set viewport rotation before flip, the navigator display region would be drawn wrong (#2619 @jbakarich)
|
||||
* Fixed: In some cases, the WebGL drawer would draw the image in white (#2620 @sbarex)
|
||||
* Fixed: If the user changed the page zoom or moved the window a different monitor, the image would disappear (#2627 @pearcetm)
|
||||
|
||||
5.0.0:
|
||||
|
||||
* BREAKING CHANGE: Dropped support for IE11 (#2300, #2361, #2553 @AndrewADev, @msalsbery)
|
||||
* DEPRECATION: The OpenSeadragon.createCallback function is no longer recommended (#2367 @akansjain)
|
||||
* The viewer now uses WebGL when available (#2310, #2462, #2466, #2468, #2469, #2472, #2478, #2488, #2492, #2521, #2537, #2557, #2558 @pearcetm, @Aiosa, @thec0keman)
|
||||
* Added webp to supported image formats (#2455 @BeebBenjamin)
|
||||
* Added avif to supported image formats (#2544 @msalsbery)
|
||||
* Introduced maxTilesPerFrame option to allow loading more tiles simultaneously (#2387 @jetic83)
|
||||
* Now when creating a viewer or navigator, we leave its position style alone if possible (#2393 @VIRAT9358)
|
||||
* Added getter & setter for Viewport.maxZoomPixelRatio (#2506 @eug-L)
|
||||
* Overlays are now positioned properly when the viewport is flipped (#2546 @BeebBenjamin)
|
||||
* Added overlayPreserveContentDirection option to keep overlays readable when viewport is flipped (#2546 @BeebBenjamin)
|
||||
* Test improvements (#2382 @AndrewADev)
|
||||
* MouseTracker options documentation fixes (#2389 @msalsbery)
|
||||
* Improved documentation and error message for Viewport.imageToViewportZoom (#2505 @eug-L)
|
||||
* Fixed documentation typos (#2507 @frameflare)
|
||||
* Additional documentation fixes (#2563 @msalsbery)
|
||||
* Fixed: Sometimes if the viewport was flipped and the user zoomed in far enough, it would flip back (#2364 @SebDelile)
|
||||
* Fixed: Two-finger tap on a Mac trackpad would zoom you out (#2431 @cavenel)
|
||||
* Fixed: dragToPan gesture could not be disabled when flickEnabled was activated (#2464 @jonasengelmann)
|
||||
* Fixed: placeholderFillStyle didn't work properly when the image was rotated (#2469 @pearcetm)
|
||||
* Fixed: Sometimes exponential springs wouldn't ever settle (#2469 @pearcetm)
|
||||
* Fixed: The navigator wouldn't update its tracking rectangle when the navigator was resized (#2491 @pearcetm)
|
||||
* Fixed: The drawer would improperly crop when the viewport was flipped and a tiled image was rotated (#2511 @pearcetm, @eug-L)
|
||||
* Fixed: Flipped viewport caused image to be flipped again when going fullscreen or resizing (#2518 @pearcetm)
|
||||
* Fixed: Viewer ajax options (loadTilesWithAjax, ajaxHeaders, and ajaxWithCredentials) weren't being propogated to the navigator. (#2539 @eug-L)
|
||||
|
||||
4.1.1:
|
||||
|
||||
* Fixed: Strange behavior if IIIF sizes were not in ascending order (#2416 @lutzhelm)
|
||||
|
||||
4.1.0:
|
||||
|
||||
* NEW BEHAVIOR: When `navigatorRotate` is false, while the navigator image doesn't rotate, the red outline now does (#2356 @lcl45)
|
||||
* The viewer no longer emits `canvas-key` events for both keydown and keypress events; canvas-key is now just for keydown, and the new `canvas-key-press` is for keypress (#2270 @hrghauri)
|
||||
* You can now specify a priority when calling addHandler, to control when your event handler gets called relative to others (#2273 @Aiosa)
|
||||
* Added tileRetryMax and tileRetryDelay options, so the viewer can retry loading failed tiles (#2238 @Ughuuu @paaddyy, #2334 @Ughuuu @Titan21)
|
||||
* All of the viewers keyboard handling is now in response to keydown events (it used to be split between keydown and keypress) (#2291 @MohitBansal321)
|
||||
* Added `canvas-focus` and `canvas-blur` events to Viewer (#2301 @MohitBansal321)
|
||||
* You can now more easily add custom buttons to the viewer (#2306 @MohitBansal321)
|
||||
* The fitBounds function now takes zoom constraints into account (#2293 @pearcetm)
|
||||
* The viewer now has an `after-resize` event what happens after the viewport bounds have been updated, to complement the `resize` event which happens before (#2317 @pearcetm)
|
||||
* IIIFTileSource now uses resolution level dimensions provided in the info.json "sizes" field for more accurate tile requests (#2337 @ruven)
|
||||
* Added setAjaxHeaders method to Viewer and TiledImage (#2346 @uschmidt83)
|
||||
* Improved documentation (#2297 @KevinBritten)
|
||||
* Fixed: The `tile-loaded` event's completionCallback could be called more than once in some circumstances (#2282 @Aiosa, @pearcetm)
|
||||
* Fixed: Navigator display rectangle was off if the page had `box-sizing: border-box` (#2276 @ambujsahu81)
|
||||
* Fixed: Code that required identifying functions would fail for async functions (#2273 @Aiosa)
|
||||
* Fixed: Reference strip click detection was not accurate for long reference strips (#2280 @damonsson)
|
||||
* Fixed: Translation problems in some circumstances with cropping polygons enabled (#2316 @pearcetm)
|
||||
* Fixed: The navigator area rectangle would grow larger when you zoom in very far (#2318 @donotloveshampo)
|
||||
* Fixed: JSON with embedded XML was being incorrectly identified as XML (#2328 @craigberry)
|
||||
* Fixed: Touch/pinch rotate was not working properly on some platforms (#2324 @rsimon, @pearcetm)
|
||||
* Fixed: Navigator rotation didn't honor `immediately` parameter (#2333 @robertjcolley)
|
||||
* Fixed: The navigator didn't update for its new size in certain circumstances (#2347 @pearcetm)
|
||||
|
||||
4.0.0:
|
||||
|
||||
* NEW BEHAVIOR: Setting the viewport rotation now animates by default (pass true for the new `immediately` parameter to disable) (#2136 @jonasengelmann)
|
||||
* NEW BEHAVIOR: The auto resize now takes both width and height into account when scaling the contents proportionally to the viewer (#2256 @pearcetm)
|
||||
* DEPRECATION: Don't access the viewport's degrees property directly anymore; instead use setRotation and getRotation (#2136 @jonasengelmann)
|
||||
* New gesture: Double-click and drag to zoom (on by default for touch) (#2225 @HamzaTatheer)
|
||||
* You can now provide a pivot point when rotating the viewport (#2233 #2253 @pearcetm)
|
||||
* Improved the constraints that keep the image in the viewer, specifically when zoomed out a lot (#2160 @joedf, #2246 @pearcetm)
|
||||
* You can now provide an element for the navigator (as an alternative to an ID) (#1303 @cameronbaney, #2166 #2175 @joedf)
|
||||
* Now supporting IIIF "id" and "identifier" in addition to "@id" (#2173 @ahankinson)
|
||||
* We now delegate tile fetching and caching to the TileSource, to allow for custom tile formats (#2148 @Aiosa)
|
||||
* Added support for dynamic URLs from tile sources (#2247 @JohnReagan)
|
||||
* The viewer now emits before-destroy and destroy events (#2239 @pearcetm)
|
||||
* Auto resize detection is now more efficient (#2256 @pearcetm)
|
||||
* Improved documentation (#2211 @shyamkumaryadav)
|
||||
* Fixed: Cropping tiled images with polygons was broken (#2183 @altert)
|
||||
* Fixed: Boundary constraints were wrong when the viewport was rotated (#2249 @pearcetm)
|
||||
* Fixed: IIIF tile sizes would be calculated wrong on rare occasions (#2206 @filak)
|
||||
* Fixed: Disabling buttons only changed their appearance, but they were still clickable (#2187 @pearcetm)
|
||||
* Fixed: ImageTileSource produced an error having to do with getTileHashKey (#2190 @Aiosa)
|
||||
* Fixed: On startup you would get an unnecessary "Viewer.buttons is deprecated" warning (#2201 @joedf, #2219 @jssullivan, #2212 @joedf)
|
||||
|
||||
3.1.0:
|
||||
|
||||
* Added subPixelRoundingForTransparency Viewer option to address seams that can appear in semi-transparent images (#2075 @TanukiSharp)
|
||||
* Added Viewer.isAnimating() (#2075 @TanukiSharp)
|
||||
* Added isFullScreen method to Viewer (#2067 @JachiOnuoha)
|
||||
* Added option to include POST data when loading files via Ajax (#2072 @Aiosa)
|
||||
* Exposed TiledImage's private functions for better maintainability (#2134 @Aiosa)
|
||||
* Tile cache keys are now generated by the tile source, so it's easier to override them as needed (#2138 @Aiosa)
|
||||
* Pinch to zoom now zooms around the center of the pinch, rather than the center of the viewer (#2158 @cavenel)
|
||||
* Added fallback and deprecation warning for Viewer.buttons (which got changed to buttonGroup in 3.0.0) (#2153 @devbyjonah)
|
||||
* Fixed an issue where turning off panVertical or panHorizontal would not affect the panning keyboard combos (#2069 @JachiOnuoha)
|
||||
* Cleaned up console.logs so that errors and warnings use console.error and console.warn as appropriate (#2073 @Abhishek-90)
|
||||
* Improved documentation (#2067 @JachiOnuoha, #2112 @shyamkumaryadav, #2152 @joedf, #2155 @samwilson)
|
||||
* Fixed: Setting useCanvas to false would break the viewer (#2116 @rvv-bouvet)
|
||||
* Allow silencing multi-image warnings on viewport coordinate conversion functions (#2120 @claycoleman)
|
||||
* Fixed: Swiping fast multiple times made contact points in MouseTracker out of sync for touch events (#2121 @ronnymikalsen)
|
||||
* Made MouseTracker more robust in certain scenarios (#2134, #2147 @Aiosa)
|
||||
* Fixed an issue where full page mode wouldn't grow properly if you resized the window (#2100 @TanukiSharp)
|
||||
* Now if you pass an error handler into makeAjaxRequest, it doesn't report errors into the console (#2142 @Aiosa)
|
||||
* Fixed error caused by attaching MouseTracker to the page's document element (#2145 @tdiprima)
|
||||
* Fixed an issue that would sometimes cause problems with freeing up ImageTileSource memory (#2162 @pearcetm)
|
||||
|
||||
3.0.0:
|
||||
|
||||
* BREAKING CHANGE: Dropped support for older browsers (IE < 11) (#1872 #1949 #1951 @msalsbery, #1950 @rmontroy)
|
||||
* BREAKING CHANGE: Removed deprecated OpenSeadragon.getEvent function (#1949 @msalsbery)
|
||||
* DEPRECATION: MouseTracker exitHandler deprecated for name change to leaveHandler for consistency with DOM event names (#1872 @msalsbery)
|
||||
* Now when "simple image" tile sources are removed from the viewer, they free the memory used by the pyramid they create (#1789 @TakumaKira)
|
||||
* Improvements to docs (#1814 @kenanchristian, #1872 @msalsbery, #1996 @tdiprima)
|
||||
* Better cleanup on destruction, to avoid memory leaks (#1832 @JoFrMueller)
|
||||
* Better handle destruction when navigator in custom location (#1884 @woodchuck)
|
||||
* Miscellaneous code cleanup (#1840 @msalsbery)
|
||||
* You can now specify tileSize for the Zoomify Tile Source (#1868 @abrlam)
|
||||
* Better use of IIIF "max" and "full" URL parameters (#1871 @MImranAsghar)
|
||||
* You can now specify the file format of the tiles in the Zoomify tile source (#1889 @abrlam)
|
||||
* Improved browser sniffing - detect EDGE and CHROMEEDGE browsers (#1872 @msalsbery)
|
||||
* Improved DOM event model feature detection (#1872 @msalsbery)
|
||||
* Added support for options parameter on addEvent()/removeEvent (to support passive option) (#1872 @msalsbery)
|
||||
* Added OpenSeadragon.eventIsCanceled() function for defaultPrevented detection on DOM events (#1872 @msalsbery)
|
||||
* MouseTracker: better PointerEvent model detection - removed use of deprecated window.navigator.pointerEnabled (#1872 @msalsbery)
|
||||
* MouseTracker: added overHandler/outHandler options for handling corresponding pointerover/pointerout events (#1872 @msalsbery)
|
||||
* MouseTracker: changed enterHandler/leaveHandler to use DOM pointerenter/pointerleave events instead of simulating using pointerover/pointerout (#1872 @msalsbery)
|
||||
* All internal uses of MouseTracker use pointerenter/pointerleave events instead of pointerover/pointerout events for more consistent pointer tracking (#1872 @msalsbery)
|
||||
* Fixed bug in Button class where two MouseTracker event handlers used an invalid "this" causing issues in some browsers (#1872 @msalsbery)
|
||||
* Added pointerType property to Viewer container-enter, container-exit, canvas-drag, canvas-drag-end, canvas-pinch events (#1872 @msalsbery)
|
||||
* MouseTracker: Fire dragEndHandler event even if release point same as initial contact point (#1872 @msalsbery)
|
||||
* MouseTracker: Pointer capture implemented with capture APIs where available. Only fallback to emulated capture on extremely old browsers (#1872 @msalsbery)
|
||||
* MouseTracker: Added preProcessEventHandler option to allow MouseTracker instances to control bubbling and default behavior of events on their associated element (#1872 @msalsbery)
|
||||
* MouseTracker: Improved handling of canceled events (#1872 @msalsbery)
|
||||
* MouseTracker: Improved releasing of tracked pointers on destroy()/stopTracking() (#1872 @msalsbery)
|
||||
* Updated Viewer, Button, Drawer, Navigator, ReferenceStrip DOM for proper DOM event handling (#1872 @msalsbery)
|
||||
* Added OpenSeadragon.setElementPointerEventsNone() for setting pointer-events:'none' on DOM elements (#1872 @msalsbery)
|
||||
* MouseTracker: added contextMenuHandler option for handling contextmenu events (#1872 @msalsbery)
|
||||
* Viewer: added a canvas-contextmenu event (#1872 @msalsbery)
|
||||
* Fixed simulated drag events in navigator tests (#1949 @msalsbery)
|
||||
* Added preventDefault option to MouseTracker.contextMenuHandler and Viewer 'canvas-contextmenu' event args (#1951 @msalsbery)
|
||||
* MouseTracker: Added preProcessEventHandler for keydown, keyup, keypress, focus, blur Events (#1951 @msalsbery)
|
||||
* Fixed preventDefaultAction functionality in viewer events (#1953 @msalsbery)
|
||||
* Added setImageFormatsSupported function (#1954 @pandaxtc)
|
||||
* Added dragToPan to the GestureSettings class, implemented in Viewer (#1956 @msalsbery)
|
||||
* Added preventDefault option to MouseTracker handlers: scrollHandler, keyDownHandler, keyUpHandler, keyHandler (#1957 @msalsbery)
|
||||
* Fixed test "Events: Viewer: preventDefaultAction in dblClickHandler". Fixes #1372 (#1960 @msalsbery)
|
||||
* ReferenceStrip: Fixed issue where its element was being removed from its parent element twice on destroy, causing an exception (#1958 @msalsbery)
|
||||
* ReferenceStrip: Made its element focusable for keyboard navigation (#1958 @msalsbery)
|
||||
* You can now flip individual images (not just the whole viewport) (#1903 @ali1234)
|
||||
* Accessibility: we now take the browser's zoom into account when choosing what detail level to draw (#1937 @ronnymikalsen)
|
||||
* Fixed a bug causing overlays to disappear in Sequence Mode (#1865 @gunmiosb)
|
||||
* Fixed a bug where the ajaxHeaders provided per-image were not being used for image requests (#1968 @maxshuty)
|
||||
* MouseTracker: Added workaround for WebKit Pointer Event Implicit Capture Bug (#1972 @msalsbery)
|
||||
* Removed test for move-leave (fly-over, no enter event)...not a valid, handleable event state, no longer supported (#1972 @msalsbery)
|
||||
* Added OpenSeadragon.setElementPointerEvents() for setting pointer-events to other values besides 'none' on DOM elements (#1972 @msalsbery)
|
||||
* Now ensuring the page body is display:block when in fullscreen (#1995 @thewilkybarkid)
|
||||
* Added a static method in OpenSeadragon to get an existing viewer (#2000 @HerCerM)
|
||||
* Now ensuring that the new item is already in the navigator when the "add-item" event fires (#2005 @RammasEchor)
|
||||
* Added keys to change image in sequence mode (j: previous, k: next) (#2007 @RammasEchor)
|
||||
* Fixed a bug where the navigator wouldn't pick up opacity/composite changes made while it is loading (#2018 @crydell)
|
||||
* Explicitly set passive:false for wheel event handlers to suppress console warnings. Fixes #1669 (#2043 @msalsbery)
|
||||
* Viewer's canvas-click events now include an originalTarget property so you can know which element received the click (#2037 @iangilman)
|
||||
* Added method for getting the size of an image in window coordinates (#2049 @superbland)
|
||||
* Added a setMaxLevel function to TileSource so you can change its maxLevel if needed (#2059, #2066 @kim-sanghoon)
|
||||
|
||||
2.4.2:
|
||||
|
||||
* Add support for IIIF Image API 3.0 beta (#1764)
|
||||
* You can now crop an image with arbitrary polygons (#1772)
|
||||
* Improved support for using the Reference Strip in an OpenSeadragon Viewer inside a Web Component (#1676)
|
||||
* Added setWidth and setHeight methods to Navigator (#1686)
|
||||
* Improvements to docs (#1696, #1698, #1716, #1719)
|
||||
* Now passing Viewer AJAX configs down to ReferenceStrip thumbnails (#1701)
|
||||
* The ReferenceStrip now honors the useCanvas option from the Viewer (#1742)
|
||||
* Fixed: Navigator was still resizing after you explicitly set its width and height with navigatorWidth and navigatorHeight (#1686)
|
||||
* Fixed issues with touches on iOS 13 and iPad (#1754, #1756)
|
||||
* No longer throwing an exception on pages that have malformed URL parameters (#1758)
|
||||
* Fixed an issue with flipping the viewport on high pixel density screens (#1779)
|
||||
* Removed use of deprecated imageSmoothingEnabled prefixes (#1740)
|
||||
|
||||
2.4.1:
|
||||
|
||||
* You can now turn off the default canvas image smoothing, if you want sharp pixels when zoomed in past 100% (#1507, #1593, #1595, #1647, #1648)
|
||||
* Fixed problem with navigator highlight rectangle when returning from full screen with a custom navigator location (#1515)
|
||||
* Added option to set rotation increment for nav buttons and keyboard (#1524)
|
||||
* Fixed issue with flipping and opacity with multi-image (#1549)
|
||||
* Removed vestigial button group label element that was causing issues for accessibility tools (#1560)
|
||||
* Fixed a bug causing Viewer.areControlsEnabled to throw an exception (#1562)
|
||||
* Added tileFormat option to IIIFTileSource so you can specify the tile format (#1625)
|
||||
* Now using canonical URIs more consistently in IIIF to make caching and processing time easier on the server side (#1625)
|
||||
* Added support for IIIF's new preferredFormats property (#1656)
|
||||
|
||||
2.4.0:
|
||||
|
||||
* BREAKING CHANGE: Viewer's canvas-double-click event is now fired before it initiates the zoom (#1288)
|
||||
* You can now flip the viewport to get a mirror image of the original (#1441)
|
||||
* You can now prevent canvas-double-click events from zooming on a per-event basis (#1288)
|
||||
* Fixed: Opacity 0 images were causing unnecessary redraws (#1319)
|
||||
* The "page" event is now fired after the page index has been updated (#1330)
|
||||
* Added option pixelsPerArrowPress that sets the speed of arrow keys (#1364)
|
||||
* Improved IIIF options.maxLevel calculation (#1401)
|
||||
* Added canvas-key events, along with the ability to cancel key actions (#1414)
|
||||
* Added optional zoom in the middle of the image instead of pointer position (#1423)
|
||||
* Now supporting square edge tiles that are padded rather than cropped (#1426)
|
||||
* Fixed an issue causing the simple image tileSource to sometimes show duplicate copies (#1370)
|
||||
* Fixed an issue causing seams to appear in semi-transparent PNG tiled images (#1470)
|
||||
* Added visual customization options for the navigator (#1480)
|
||||
* You can now prevent canvas-drag events on the navigator (#1484)
|
||||
* You can now prevent canvas-click events on the navigator (#1416)
|
||||
* The navigator can now be restricted to just horizontal or just vertical panning (#1416)
|
||||
* Fixed DziTileSource so it doesn't load levels above maxLevel or below minLevel, if set (#1492)
|
||||
|
||||
2.3.1:
|
||||
|
||||
* Debug mode now uses different colors for different tiled images (customizable via debugGridColor) (#1271)
|
||||
* Fixed a problem with preventDefaultAction for the canvas-drag event (#1278)
|
||||
* Fixed an issue causing double images with certain aspect ratios (#1280)
|
||||
* Fixed: placeholderFillStyle had no effect (#1286)
|
||||
* Fixed seams that appear in wrap mode on Safari and Firefox (#1305)
|
||||
|
||||
2.3.0:
|
||||
|
||||
* BREAKING CHANGE: Tile.distance has been removed (#1027)
|
||||
* BREAKING CHANGE: Viewer's canvas-click event is now fired before it initiates the zoom (#1148)
|
||||
* BREAKING CHANGE: Viewer's canvas-drag event is now fired before it pans (#1149)
|
||||
* Added Zoomify tile source (#863)
|
||||
* You can now set the rotation of individual tiled images (#1006)
|
||||
* Added getFullyLoaded method and "fully-loaded-change" event to TiledImage to know when tiles are fully loaded (#837, #1073)
|
||||
* You can now preload images without drawing them to the screen (#1071)
|
||||
* Added support for commonjs (#984)
|
||||
* Added an option to addTiledImage to change the crossOriginPolicy (#981)
|
||||
* You can now load tiles via AJAX and custom AJAX request headers (#1055)
|
||||
* Added ability to provide thumbnail URLs for reference strip (#1241)
|
||||
* Improved panning constraints for constrainDuringPan (#1133 and #1245)
|
||||
* You can now prevent canvas-click events from zooming on a per-event basis (#1148)
|
||||
* You can now prevent canvas-drag events from panning on a per-event basis (#1149)
|
||||
* The navigationControlAnchor option now works for custom toolbar as well (#1004)
|
||||
* LegacyTileSource now allows any image URLs regardless of type (#1056)
|
||||
* Enabled configuration of ImageLoader timeout (#1192)
|
||||
* Viewer.open() now supports an initialPage argument for sequenceMode (#1196)
|
||||
* New events for opacity and compositeOperation changes (#1203)
|
||||
* Added support for setting debug mode after the Viewer object has been constructed (#1224)
|
||||
* Added functions for dynamically adding and removing the reference strip in sequence mode (#1213)
|
||||
* Better calculation for TileCache release cutoff (#1214)
|
||||
* The navigator now picks up opacity and compositeOperation changes (#1203)
|
||||
* Improved calculation for determining which level to load first (#1198)
|
||||
* Added fix for supporting weird filenames that look like JSONs (#1189)
|
||||
* Improved DziTileSource guessing of tilesUrl (#1074)
|
||||
* The Viewer's tileSources option is now smarter about detecting JSON vs XML vs URL (#999)
|
||||
* Better compression for our UI images (#1134)
|
||||
* Optimization: Use the squared distance when comparing tiles (#1027)
|
||||
* Now clamping pixel ratio density to a minimum of 1, fixing display issues on low density devices (#1200)
|
||||
* More forgiving check for DZI schema (#1249)
|
||||
* ImageTileSource now works in IE8 (#1041)
|
||||
* Fixed CORS bug in IE 10 (#967)
|
||||
* Fixed issue with tiles not appearing with wrapHorizontal/wrapVertical if you pan too far away from the origin (#987, #1066)
|
||||
* Fixed: Initial tile load wasn't happening in parallel (#1014)
|
||||
* Fixed problem with "sparse image" DZI files (#995)
|
||||
* Fix IndexSizeError on IE and Edge that occurred under certain circumstances (e.g. multi-image with transparency) (#1035)
|
||||
* Fixed error in IE8 when zooming in (due to edge smoothing) (#1064)
|
||||
* Fixed issue with OpenSeadragon.version in the minified JavaScript (#1099)
|
||||
* Fixed smoothTileEdgesMinZoom performance degradation on single-tile images (#1101)
|
||||
* Fixed issue with tiles not appearing after rotation (#1102)
|
||||
* Fixed: The navigator wasn't respecting the constrainDuringPan setting (#1104)
|
||||
* Fixed an issue causing overlays to be mis-positioned in some circumstances (#1119)
|
||||
* Fixed: ImageTileSource would sometimes produce a double image (#1123)
|
||||
* Fixed: console.debug caused exceptions on IE10 (#1129)
|
||||
* Fixed: the reference strip would leak memory when opening new sets of images (#1175)
|
||||
* Fixed: zoomTo/zoomBy ignore refPoint if immediately is true (#1184)
|
||||
* Fixed: IIPImageServer didn't work with the latest OSD release (#1199)
|
||||
* Fixed: setItemIndex method not working with navigator inside "open" event (#1201)
|
||||
* Fixed: The reference strip didn't show the initial page if it wasn't the first page (#1208)
|
||||
* Fixed: Sometimes the image would stick to the mouse when right-clicking and left-clicking simultaneously (#1223)
|
||||
* Fixed issue with transparent images sometimes disappearing on Safari (#1222)
|
||||
* Fixed: One image failing to load could cause the others to never load (#1229)
|
||||
* Fixed: Mouse up outside map will cause "canvas-drag" event to stick (#1133)
|
||||
* Fixed more issues with tracking multiple pointers (#1244)
|
||||
|
||||
2.2.1:
|
||||
|
||||
* Fixed problems with zoom/pan constraints with certain extreme settings (#965)
|
||||
* Fixed an issue causing the browser to crash on iOS (#966)
|
||||
|
||||
2.2.0:
|
||||
|
||||
* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and
|
||||
Viewport.contentAspectY have been removed. (#846)
|
||||
* BREAKING CHANGE: The Overlay.getBounds method now takes the viewport as parameter. (#896)
|
||||
* DEPRECATION: Overlay.scales, Overlay.bounds and Overlay.position have been deprecated. (#896)
|
||||
* Overlay.width !== null should be used to test whether the overlay scales horizontally
|
||||
* Overlay.height !== null should be used to test whether the overlay scales vertically
|
||||
* The Overlay.getBounds method should be used to get the bounds of the overlay in viewport coordinates
|
||||
* Overlay.location replaces Overlay.position
|
||||
* DEPRECATION: Viewport.setHomeBounds has been deprecated (#846)
|
||||
* DEPRECATION: the Viewport constructor is now ignoring the contentSize option (#846)
|
||||
* Tile edge smoothing at high zoom (#764)
|
||||
* Fixed issue with reference strip popping up virtual keyboard on mobile devices (#779)
|
||||
* Now supporting rotation in the Rect class (#782)
|
||||
* Drag outside of iframe now works better, as long as both pages are on the same domain (#790)
|
||||
* Coordinate conversion now takes rotation into account (#796)
|
||||
* Support tile-less IIIF as per LegacyTileSource (#816)
|
||||
* You can now give an empty string to the tabIndex option (#805)
|
||||
* Fixed issue with rotation and clicking in the navigator (#807)
|
||||
* Broadened the check for mime type in LegacyTileSource URLs to allow query strings (#819)
|
||||
* Added globalCompositeOperation option for tiledImage, to allow for different transfer modes (#814)
|
||||
* Added Viewer.addSimpleImage method for easily adding non-tiled images (#827)
|
||||
* DziTileSource now works properly with DZI files that have no extension (#835)
|
||||
* Fixed content clipping with rotation (#463, #567 and #833)
|
||||
* Fixed navigator not being rotated when viewport rotation is set in constructor (#840)
|
||||
* Fixed: Viewer.setMouseNavEnabled wasn't affecting all of the viewer's trackers (#845)
|
||||
* Fixed: with scrollToZoom disabled, the viewer caused page scrolling to slow down (#858)
|
||||
* Added Viewer.getOverlayById and Overlay.getBounds functions (#853)
|
||||
* Tiled images with 0 opacity no longer load their tiles or do drawing calculations (#859)
|
||||
* Fixed issue with edge smoothing with PNG tiles at high zoom (#860)
|
||||
* Fixed: Images with transparency were clearing images layered below them (#861)
|
||||
* Fixed issue causing HTML pages to jump unwantedly to the reference strip upon loading (#872)
|
||||
* Added addOnceHandler method to EventSource (#887)
|
||||
* Added TiledImage.fitBounds method (#888)
|
||||
* Overlays can now be scaled in a single dimension by providing a point location and either width or height (#896)
|
||||
* Added full rotation support to overlays (#729, #193)
|
||||
* Viewport.goHome() now takes clipping into account (#910)
|
||||
* Improved zoom to point (#923)
|
||||
* Optimized sketch canvas clearing and blending for images with opacity or transfer modes (#927)
|
||||
* Now taking rotation into account in viewport getBounds and fitBounds methods (#934)
|
||||
* Added option to disable navigator auto-fade (#935)
|
||||
* Fixed issue with maintaining viewport position with full screen (#940)
|
||||
* Fixed an issue with simultaneous touch events (#930)
|
||||
* Avoid loading clipped out tiles (#939)
|
||||
* Improved precision for subtle moves with fitBounds (#939)
|
||||
* Fixed an issue in viewer.addTiledImage with replace:true when viewer has navigator (#948)
|
||||
|
||||
2.1.0:
|
||||
|
||||
* BREAKING CHANGE: the tile does not hold a reference to its image anymore. Only the tile cache keep a reference to images.
|
||||
|
|
BIN
images/button_grouphover.png
Executable file
After Width: | Height: | Size: 1.5 KiB |
BIN
images/button_hover.png
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
images/button_pressed.png
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/button_rest.png
Executable file
After Width: | Height: | Size: 1 KiB |
BIN
images/flip_grouphover.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
images/flip_hover.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
images/flip_pressed.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
images/flip_rest.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 977 B |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -5,7 +5,9 @@ auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
|
|||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
|
||||
auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.continuationIndentSize=4
|
||||
auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.indent-shift-width=4
|
||||
auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.spaceBeforeAnonMethodDeclParen=false
|
||||
auxiliary.org-netbeans-modules-editor-indent.text.x-json.CodeStyle.project.indent-shift-width=2
|
||||
auxiliary.org-netbeans-modules-editor-indent.text.x-json.CodeStyle.project.spaces-per-tab=2
|
||||
auxiliary.org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder=src
|
||||
|
|
5220
package-lock.json
generated
Normal file
71
package.json
|
@ -1,22 +1,53 @@
|
|||
{
|
||||
"name": "OpenSeadragon",
|
||||
"version": "2.1.0",
|
||||
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-contrib-clean": "^0.5.0",
|
||||
"grunt-contrib-compress": "^0.9.1",
|
||||
"grunt-contrib-concat": "^0.4.0",
|
||||
"grunt-contrib-connect": "^0.7.1",
|
||||
"grunt-contrib-jshint": "^0.10.0",
|
||||
"grunt-contrib-uglify": "^0.4.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-git-describe": "^2.3.2",
|
||||
"grunt-qunit-istanbul": "^0.5.0",
|
||||
"grunt-text-replace": "^0.3.11",
|
||||
"qunitjs": "^1.18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
}
|
||||
"name": "openseadragon",
|
||||
"version": "5.0.1",
|
||||
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
|
||||
"keywords": [
|
||||
"image",
|
||||
"zoom",
|
||||
"pan",
|
||||
"openseadragon",
|
||||
"seadragon",
|
||||
"deepzoom",
|
||||
"dzi",
|
||||
"iiif",
|
||||
"osm",
|
||||
"tms"
|
||||
],
|
||||
"homepage": "https://openseadragon.github.io/",
|
||||
"funding": "https://opencollective.com/openseadragon",
|
||||
"bugs": {
|
||||
"url": "https://github.com/openseadragon/openseadragon/issues"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"files": [
|
||||
"build/openseadragon/"
|
||||
],
|
||||
"main": "build/openseadragon/openseadragon.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openseadragon/openseadragon.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-compat": "^4.1.2",
|
||||
"grunt": "^1.6.1",
|
||||
"grunt-contrib-clean": "^2.0.1",
|
||||
"grunt-contrib-compress": "^2.0.0",
|
||||
"grunt-contrib-concat": "^2.1.0",
|
||||
"grunt-contrib-connect": "^3.0.0",
|
||||
"grunt-contrib-qunit": "^7.0.1",
|
||||
"grunt-contrib-uglify": "^5.0.1",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^24.0.1",
|
||||
"grunt-git-describe": "^2.4.4",
|
||||
"grunt-istanbul": "^0.8.0",
|
||||
"grunt-text-replace": "^0.4.0",
|
||||
"qunit": "^2.19.4"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"prepare": "grunt build",
|
||||
"build": "grunt build",
|
||||
"dev": "grunt dev"
|
||||
}
|
||||
}
|
||||
|
|
BIN
psd/button.psd
Executable file
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Button
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -61,7 +61,7 @@ $.ButtonState = {
|
|||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.EventSource
|
||||
* @param {Object} options
|
||||
* @param {Element} [options.element=null] Element to use as the button. If not specified, an HTML <button> element is created.
|
||||
* @param {Element} [options.element=null] Element to use as the button. If not specified, an HTML <div> element is created.
|
||||
* @param {String} [options.tooltip=null] Provides context help for the button when the
|
||||
* user hovers over it.
|
||||
* @param {String} [options.srcRest=null] URL of image to use in 'rest' state.
|
||||
|
@ -77,6 +77,7 @@ $.ButtonState = {
|
|||
* @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.
|
||||
* @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.
|
||||
* @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.
|
||||
* @param {Object} [options.userData=null] Arbitrary object to be passed unchanged to any attached handler methods.
|
||||
*/
|
||||
$.Button = function( options ) {
|
||||
|
||||
|
@ -111,7 +112,8 @@ $.Button = function( options ) {
|
|||
onEnter: null,
|
||||
onExit: null,
|
||||
onFocus: null,
|
||||
onBlur: null
|
||||
onBlur: null,
|
||||
userData: null
|
||||
|
||||
}, options );
|
||||
|
||||
|
@ -120,7 +122,7 @@ $.Button = function( options ) {
|
|||
* @member {Element} element
|
||||
* @memberof OpenSeadragon.Button#
|
||||
*/
|
||||
this.element = options.element || $.makeNeutralElement( "div" );
|
||||
this.element = options.element || $.makeNeutralElement("div");
|
||||
|
||||
//if the user has specified the element to bind the control to explicitly
|
||||
//then do not add the default control images
|
||||
|
@ -136,6 +138,13 @@ $.Button = function( options ) {
|
|||
this.imgDown.alt =
|
||||
this.tooltip;
|
||||
|
||||
// Allow pointer events to pass through the img elements so implicit
|
||||
// pointer capture works on touch devices
|
||||
$.setElementPointerEventsNone( this.imgRest );
|
||||
$.setElementPointerEventsNone( this.imgGroup );
|
||||
$.setElementPointerEventsNone( this.imgHover );
|
||||
$.setElementPointerEventsNone( this.imgDown );
|
||||
|
||||
this.element.style.position = "relative";
|
||||
$.setElementTouchActionNone( this.element );
|
||||
|
||||
|
@ -158,13 +167,6 @@ $.Button = function( options ) {
|
|||
this.imgDown.style.visibility =
|
||||
"hidden";
|
||||
|
||||
if ( $.Browser.vendor == $.BROWSERS.FIREFOX && $.Browser.version < 3 ){
|
||||
this.imgGroup.style.top =
|
||||
this.imgHover.style.top =
|
||||
this.imgDown.style.top =
|
||||
"";
|
||||
}
|
||||
|
||||
this.element.appendChild( this.imgRest );
|
||||
this.element.appendChild( this.imgGroup );
|
||||
this.element.appendChild( this.imgHover );
|
||||
|
@ -172,13 +174,13 @@ $.Button = function( options ) {
|
|||
}
|
||||
|
||||
|
||||
this.addHandler( "press", this.onPress );
|
||||
this.addHandler( "release", this.onRelease );
|
||||
this.addHandler( "click", this.onClick );
|
||||
this.addHandler( "enter", this.onEnter );
|
||||
this.addHandler( "exit", this.onExit );
|
||||
this.addHandler( "focus", this.onFocus );
|
||||
this.addHandler( "blur", this.onBlur );
|
||||
this.addHandler("press", this.onPress);
|
||||
this.addHandler("release", this.onRelease);
|
||||
this.addHandler("click", this.onClick);
|
||||
this.addHandler("enter", this.onEnter);
|
||||
this.addHandler("exit", this.onExit);
|
||||
this.addHandler("focus", this.onFocus);
|
||||
this.addHandler("blur", this.onBlur);
|
||||
|
||||
/**
|
||||
* The button's current state.
|
||||
|
@ -203,6 +205,7 @@ $.Button = function( options ) {
|
|||
*/
|
||||
this.tracker = new $.MouseTracker({
|
||||
|
||||
userData: 'Button.tracker',
|
||||
element: this.element,
|
||||
clickTimeThreshold: this.clickTimeThreshold,
|
||||
clickDistThreshold: this.clickDistThreshold,
|
||||
|
@ -227,7 +230,7 @@ $.Button = function( options ) {
|
|||
},
|
||||
|
||||
focusHandler: function ( event ) {
|
||||
this.enterHandler( event );
|
||||
_this.tracker.enterHandler( event );
|
||||
/**
|
||||
* Raised when the Button element receives focus.
|
||||
*
|
||||
|
@ -241,7 +244,7 @@ $.Button = function( options ) {
|
|||
_this.raiseEvent( "focus", { originalEvent: event.originalEvent } );
|
||||
},
|
||||
|
||||
exitHandler: function( event ) {
|
||||
leaveHandler: function( event ) {
|
||||
outTo( _this, $.ButtonState.GROUP );
|
||||
if ( event.insideElementPressed ) {
|
||||
/**
|
||||
|
@ -259,7 +262,7 @@ $.Button = function( options ) {
|
|||
},
|
||||
|
||||
blurHandler: function ( event ) {
|
||||
this.exitHandler( event );
|
||||
_this.tracker.leaveHandler( event );
|
||||
/**
|
||||
* Raised when the Button element loses focus.
|
||||
*
|
||||
|
@ -350,9 +353,11 @@ $.Button = function( options ) {
|
|||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
_this.raiseEvent( "release", { originalEvent: event.originalEvent } );
|
||||
return false;
|
||||
|
||||
event.preventDefault = true;
|
||||
} else{
|
||||
event.preventDefault = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -363,8 +368,8 @@ $.Button = function( options ) {
|
|||
$.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{
|
||||
|
||||
/**
|
||||
* TODO: Determine what this function is intended to do and if it's actually
|
||||
* useful as an API point.
|
||||
* Used by a button container element (e.g. a ButtonGroup) to transition the button state
|
||||
* to ButtonState.GROUP.
|
||||
* @function
|
||||
*/
|
||||
notifyGroupEnter: function() {
|
||||
|
@ -372,8 +377,8 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
|
|||
},
|
||||
|
||||
/**
|
||||
* TODO: Determine what this function is intended to do and if it's actually
|
||||
* useful as an API point.
|
||||
* Used by a button container element (e.g. a ButtonGroup) to transition the button state
|
||||
* to ButtonState.REST.
|
||||
* @function
|
||||
*/
|
||||
notifyGroupExit: function() {
|
||||
|
@ -386,6 +391,7 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
|
|||
disable: function(){
|
||||
this.notifyGroupExit();
|
||||
this.element.disabled = true;
|
||||
this.tracker.setTracking(false);
|
||||
$.setElementOpacity( this.element, 0.2, true );
|
||||
},
|
||||
|
||||
|
@ -394,8 +400,31 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
|
|||
*/
|
||||
enable: function(){
|
||||
this.element.disabled = false;
|
||||
this.tracker.setTracking(true);
|
||||
$.setElementOpacity( this.element, 1.0, true );
|
||||
this.notifyGroupEnter();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.imgRest) {
|
||||
this.element.removeChild(this.imgRest);
|
||||
this.imgRest = null;
|
||||
}
|
||||
if (this.imgGroup) {
|
||||
this.element.removeChild(this.imgGroup);
|
||||
this.imgGroup = null;
|
||||
}
|
||||
if (this.imgHover) {
|
||||
this.element.removeChild(this.imgHover);
|
||||
this.imgHover = null;
|
||||
}
|
||||
if (this.imgDown) {
|
||||
this.element.removeChild(this.imgDown);
|
||||
this.imgDown = null;
|
||||
}
|
||||
this.removeAllHandlers();
|
||||
this.tracker.destroy();
|
||||
this.element = null;
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -451,13 +480,13 @@ function inTo( button, newState ) {
|
|||
}
|
||||
|
||||
if ( newState >= $.ButtonState.GROUP &&
|
||||
button.currentState == $.ButtonState.REST ) {
|
||||
button.currentState === $.ButtonState.REST ) {
|
||||
stopFading( button );
|
||||
button.currentState = $.ButtonState.GROUP;
|
||||
}
|
||||
|
||||
if ( newState >= $.ButtonState.HOVER &&
|
||||
button.currentState == $.ButtonState.GROUP ) {
|
||||
button.currentState === $.ButtonState.GROUP ) {
|
||||
if( button.imgHover ){
|
||||
button.imgHover.style.visibility = "";
|
||||
}
|
||||
|
@ -465,7 +494,7 @@ function inTo( button, newState ) {
|
|||
}
|
||||
|
||||
if ( newState >= $.ButtonState.DOWN &&
|
||||
button.currentState == $.ButtonState.HOVER ) {
|
||||
button.currentState === $.ButtonState.HOVER ) {
|
||||
if( button.imgDown ){
|
||||
button.imgDown.style.visibility = "";
|
||||
}
|
||||
|
@ -481,7 +510,7 @@ function outTo( button, newState ) {
|
|||
}
|
||||
|
||||
if ( newState <= $.ButtonState.HOVER &&
|
||||
button.currentState == $.ButtonState.DOWN ) {
|
||||
button.currentState === $.ButtonState.DOWN ) {
|
||||
if( button.imgDown ){
|
||||
button.imgDown.style.visibility = "hidden";
|
||||
}
|
||||
|
@ -489,7 +518,7 @@ function outTo( button, newState ) {
|
|||
}
|
||||
|
||||
if ( newState <= $.ButtonState.GROUP &&
|
||||
button.currentState == $.ButtonState.HOVER ) {
|
||||
button.currentState === $.ButtonState.HOVER ) {
|
||||
if( button.imgHover ){
|
||||
button.imgHover.style.visibility = "hidden";
|
||||
}
|
||||
|
@ -497,7 +526,7 @@ function outTo( button, newState ) {
|
|||
}
|
||||
|
||||
if ( newState <= $.ButtonState.REST &&
|
||||
button.currentState == $.ButtonState.GROUP ) {
|
||||
button.currentState === $.ButtonState.GROUP ) {
|
||||
beginFading( button );
|
||||
button.currentState = $.ButtonState.REST;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - ButtonGroup
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -70,11 +70,11 @@ $.ButtonGroup = function( options ) {
|
|||
|
||||
// TODO What if there IS an options.group specified?
|
||||
if( !options.group ){
|
||||
this.label = $.makeNeutralElement( "label" );
|
||||
this.element.style.display = "inline-block";
|
||||
//this.label = $.makeNeutralElement( "label" );
|
||||
//TODO: support labels for ButtonGroups
|
||||
//this.label.innerHTML = this.labelText;
|
||||
this.element.style.display = "inline-block";
|
||||
this.element.appendChild( this.label );
|
||||
//this.element.appendChild( this.label );
|
||||
for ( i = 0; i < buttons.length; i++ ) {
|
||||
this.element.appendChild( buttons[ i ].element );
|
||||
}
|
||||
|
@ -83,11 +83,12 @@ $.ButtonGroup = function( options ) {
|
|||
$.setElementTouchActionNone( this.element );
|
||||
|
||||
/**
|
||||
* Tracks mouse/touch/key events accross the group of buttons.
|
||||
* Tracks mouse/touch/key events across the group of buttons.
|
||||
* @member {OpenSeadragon.MouseTracker} tracker
|
||||
* @memberof OpenSeadragon.ButtonGroup#
|
||||
*/
|
||||
this.tracker = new $.MouseTracker({
|
||||
userData: 'ButtonGroup.tracker',
|
||||
element: this.element,
|
||||
clickTimeThreshold: this.clickTimeThreshold,
|
||||
clickDistThreshold: this.clickDistThreshold,
|
||||
|
@ -97,7 +98,7 @@ $.ButtonGroup = function( options ) {
|
|||
_this.buttons[ i ].notifyGroupEnter();
|
||||
}
|
||||
},
|
||||
exitHandler: function ( event ) {
|
||||
leaveHandler: function ( event ) {
|
||||
var i;
|
||||
if ( !event.insideElementPressed ) {
|
||||
for ( i = 0; i < _this.buttons.length; i++ ) {
|
||||
|
@ -108,7 +109,19 @@ $.ButtonGroup = function( options ) {
|
|||
});
|
||||
};
|
||||
|
||||
$.ButtonGroup.prototype = /** @lends OpenSeadragon.ButtonGroup.prototype */{
|
||||
/** @lends OpenSeadragon.ButtonGroup.prototype */
|
||||
$.ButtonGroup.prototype = {
|
||||
|
||||
/**
|
||||
* Adds the given button to this button group.
|
||||
*
|
||||
* @function
|
||||
* @param {OpenSeadragon.Button} button
|
||||
*/
|
||||
addButton: function( button ){
|
||||
this.buttons.push(button);
|
||||
this.element.appendChild(button.element);
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Figure out why this is used on the public API and if a more useful
|
||||
|
@ -126,8 +139,18 @@ $.ButtonGroup.prototype = /** @lends OpenSeadragon.ButtonGroup.prototype */{
|
|||
* @function
|
||||
* @private
|
||||
*/
|
||||
emulateExit: function() {
|
||||
this.tracker.exitHandler( { eventSource: this.tracker } );
|
||||
emulateLeave: function() {
|
||||
this.tracker.leaveHandler( { eventSource: this.tracker } );
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
while (this.buttons.length) {
|
||||
var button = this.buttons.pop();
|
||||
this.element.removeChild(button.element);
|
||||
button.destroy();
|
||||
}
|
||||
this.tracker.destroy();
|
||||
this.element = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
1054
src/canvasdrawer.js
Normal file
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Control
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -74,6 +74,7 @@ $.ControlAnchor = {
|
|||
* @param {Element} container - the element to control will be anchored too.
|
||||
*/
|
||||
$.Control = function ( element, options, container ) {
|
||||
|
||||
var parent = element.parentNode;
|
||||
if (typeof options === 'number')
|
||||
{
|
||||
|
@ -112,13 +113,13 @@ $.Control = function ( element, options, container ) {
|
|||
* @member {Element} wrapper
|
||||
* @memberof OpenSeadragon.Control#
|
||||
*/
|
||||
if ( this.anchor == $.ControlAnchor.ABSOLUTE ) {
|
||||
if ( this.anchor === $.ControlAnchor.ABSOLUTE ) {
|
||||
this.wrapper = $.makeNeutralElement( "div" );
|
||||
this.wrapper.style.position = "absolute";
|
||||
this.wrapper.style.top = typeof ( options.top ) == "number" ? ( options.top + 'px' ) : options.top;
|
||||
this.wrapper.style.left = typeof ( options.left ) == "number" ? (options.left + 'px' ) : options.left;
|
||||
this.wrapper.style.height = typeof ( options.height ) == "number" ? ( options.height + 'px' ) : options.height;
|
||||
this.wrapper.style.width = typeof ( options.width ) == "number" ? ( options.width + 'px' ) : options.width;
|
||||
this.wrapper.style.top = typeof (options.top) === "number" ? (options.top + 'px') : options.top;
|
||||
this.wrapper.style.left = typeof (options.left) === "number" ? (options.left + 'px') : options.left;
|
||||
this.wrapper.style.height = typeof (options.height) === "number" ? (options.height + 'px') : options.height;
|
||||
this.wrapper.style.width = typeof (options.width) === "number" ? (options.width + 'px') : options.width;
|
||||
this.wrapper.style.margin = "0px";
|
||||
this.wrapper.style.padding = "0px";
|
||||
|
||||
|
@ -130,7 +131,7 @@ $.Control = function ( element, options, container ) {
|
|||
} else {
|
||||
this.wrapper = $.makeNeutralElement( "div" );
|
||||
this.wrapper.style.display = "inline-block";
|
||||
if ( this.anchor == $.ControlAnchor.NONE ) {
|
||||
if ( this.anchor === $.ControlAnchor.NONE ) {
|
||||
// IE6 fix
|
||||
this.wrapper.style.width = this.wrapper.style.height = "100%";
|
||||
}
|
||||
|
@ -138,8 +139,8 @@ $.Control = function ( element, options, container ) {
|
|||
this.wrapper.appendChild( this.element );
|
||||
|
||||
if (options.attachToViewer ) {
|
||||
if ( this.anchor == $.ControlAnchor.TOP_RIGHT ||
|
||||
this.anchor == $.ControlAnchor.BOTTOM_RIGHT ) {
|
||||
if ( this.anchor === $.ControlAnchor.TOP_RIGHT ||
|
||||
this.anchor === $.ControlAnchor.BOTTOM_RIGHT ) {
|
||||
this.container.insertBefore(
|
||||
this.wrapper,
|
||||
this.container.firstChild
|
||||
|
@ -150,9 +151,11 @@ $.Control = function ( element, options, container ) {
|
|||
} else {
|
||||
parent.appendChild( this.wrapper );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$.Control.prototype = /** @lends OpenSeadragon.Control.prototype */{
|
||||
/** @lends OpenSeadragon.Control.prototype */
|
||||
$.Control.prototype = {
|
||||
|
||||
/**
|
||||
* Removes the control from the container.
|
||||
|
@ -160,16 +163,18 @@ $.Control.prototype = /** @lends OpenSeadragon.Control.prototype */{
|
|||
*/
|
||||
destroy: function() {
|
||||
this.wrapper.removeChild( this.element );
|
||||
this.container.removeChild( this.wrapper );
|
||||
if (this.anchor !== $.ControlAnchor.NONE) {
|
||||
this.container.removeChild(this.wrapper);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the control is currently visible.
|
||||
* @function
|
||||
* @return {Boolean} true if currenly visible, false otherwise.
|
||||
* @returns {Boolean} true if currently visible, false otherwise.
|
||||
*/
|
||||
isVisible: function() {
|
||||
return this.wrapper.style.display != "none";
|
||||
return this.wrapper.style.display !== "none";
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -179,7 +184,7 @@ $.Control.prototype = /** @lends OpenSeadragon.Control.prototype */{
|
|||
*/
|
||||
setVisible: function( visible ) {
|
||||
this.wrapper.style.display = visible ?
|
||||
( this.anchor == $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) :
|
||||
( this.anchor === $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) :
|
||||
"none";
|
||||
},
|
||||
|
||||
|
@ -189,11 +194,7 @@ $.Control.prototype = /** @lends OpenSeadragon.Control.prototype */{
|
|||
* @param {Number} opactiy - a value between 1 and 0 inclusively.
|
||||
*/
|
||||
setOpacity: function( opacity ) {
|
||||
if ( this.element[ $.SIGNAL ] && $.Browser.vendor == $.BROWSERS.IE ) {
|
||||
$.setElementOpacity( this.element, opacity, true );
|
||||
} else {
|
||||
$.setElementOpacity( this.wrapper, opacity, true );
|
||||
}
|
||||
$.setElementOpacity( this.wrapper, opacity, true );
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - ControlDock
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -45,7 +45,7 @@
|
|||
i;
|
||||
|
||||
$.extend( true, this, {
|
||||
id: 'controldock-'+$.now()+'-'+Math.floor(Math.random()*1000000),
|
||||
id: 'controldock-' + $.now() + '-' + Math.floor(Math.random() * 1000000),
|
||||
container: $.makeNeutralElement( 'div' ),
|
||||
controls: []
|
||||
}, options );
|
||||
|
@ -59,7 +59,9 @@
|
|||
if( this.element ){
|
||||
this.element = $.getElement( this.element );
|
||||
this.element.appendChild( this.container );
|
||||
this.element.style.position = 'relative';
|
||||
if( $.getElementStyle(this.element).position === 'static' ){
|
||||
this.element.style.position = 'relative';
|
||||
}
|
||||
this.container.style.width = '100%';
|
||||
this.container.style.height = '100%';
|
||||
}
|
||||
|
@ -88,7 +90,8 @@
|
|||
this.container.appendChild( this.controls.bottomleft );
|
||||
};
|
||||
|
||||
$.ControlDock.prototype = /** @lends OpenSeadragon.ControlDock.prototype */{
|
||||
/** @lends OpenSeadragon.ControlDock.prototype */
|
||||
$.ControlDock.prototype = {
|
||||
|
||||
/**
|
||||
* @function
|
||||
|
@ -148,7 +151,7 @@
|
|||
|
||||
/**
|
||||
* @function
|
||||
* @return {OpenSeadragon.ControlDock} Chainable.
|
||||
* @returns {OpenSeadragon.ControlDock} Chainable.
|
||||
*/
|
||||
removeControl: function ( element ) {
|
||||
element = $.getElement( element );
|
||||
|
@ -164,7 +167,7 @@
|
|||
|
||||
/**
|
||||
* @function
|
||||
* @return {OpenSeadragon.ControlDock} Chainable.
|
||||
* @returns {OpenSeadragon.ControlDock} Chainable.
|
||||
*/
|
||||
clearControls: function () {
|
||||
while ( this.controls.length > 0 ) {
|
||||
|
@ -177,7 +180,7 @@
|
|||
|
||||
/**
|
||||
* @function
|
||||
* @return {Boolean}
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
areControlsEnabled: function () {
|
||||
var i;
|
||||
|
@ -194,7 +197,7 @@
|
|||
|
||||
/**
|
||||
* @function
|
||||
* @return {OpenSeadragon.ControlDock} Chainable.
|
||||
* @returns {OpenSeadragon.ControlDock} Chainable.
|
||||
*/
|
||||
setControlsEnabled: function( enabled ) {
|
||||
var i;
|
||||
|
@ -217,7 +220,7 @@
|
|||
i;
|
||||
|
||||
for ( i = controls.length - 1; i >= 0; i-- ) {
|
||||
if ( controls[ i ].element == element ) {
|
||||
if ( controls[ i ].element === element ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
488
src/datatypeconvertor.js
Normal file
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* OpenSeadragon.convertor (static property)
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
/**
|
||||
* modified from https://gist.github.com/Prottoy2938/66849e04b0bac459606059f5f9f3aa1a
|
||||
* @private
|
||||
*/
|
||||
class WeightedGraph {
|
||||
constructor() {
|
||||
this.adjacencyList = {};
|
||||
this.vertices = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add vertex to graph
|
||||
* @param vertex unique vertex ID
|
||||
* @return {boolean} true if inserted, false if exists (no-op)
|
||||
*/
|
||||
addVertex(vertex) {
|
||||
if (!this.vertices[vertex]) {
|
||||
this.vertices[vertex] = new $.PriorityQueue.Node(0, vertex);
|
||||
this.adjacencyList[vertex] = [];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add edge to graph
|
||||
* @param vertex1 id, must exist by calling addVertex()
|
||||
* @param vertex2 id, must exist by calling addVertex()
|
||||
* @param weight
|
||||
* @param transform function that transforms on path vertex1 -> vertex2
|
||||
* @return {boolean} true if new edge, false if replaced existing
|
||||
*/
|
||||
addEdge(vertex1, vertex2, weight, transform) {
|
||||
if (weight < 0) {
|
||||
$.console.error("WeightedGraph: negative weights will make for invalid shortest path computation!");
|
||||
}
|
||||
const outgoingPaths = this.adjacencyList[vertex1],
|
||||
replacedEdgeIndex = outgoingPaths.findIndex(edge => edge.target === this.vertices[vertex2]),
|
||||
newEdge = { target: this.vertices[vertex2], origin: this.vertices[vertex1], weight, transform };
|
||||
if (replacedEdgeIndex < 0) {
|
||||
this.adjacencyList[vertex1].push(newEdge);
|
||||
return true;
|
||||
}
|
||||
this.adjacencyList[vertex1][replacedEdgeIndex] = newEdge;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {{path: ConversionStep[], cost: number}|undefined} cheapest path from start to finish
|
||||
*/
|
||||
dijkstra(start, finish) {
|
||||
let path = []; //to return at end
|
||||
if (start === finish) {
|
||||
return {path: path, cost: 0};
|
||||
}
|
||||
const nodes = new OpenSeadragon.PriorityQueue();
|
||||
let smallestNode;
|
||||
//build up initial state
|
||||
for (let vertex in this.vertices) {
|
||||
vertex = this.vertices[vertex];
|
||||
if (vertex.value === start) {
|
||||
vertex.key = 0; //keys are known distances
|
||||
nodes.insertNode(vertex);
|
||||
} else {
|
||||
vertex.key = Infinity;
|
||||
delete vertex.index;
|
||||
}
|
||||
vertex._previous = null;
|
||||
}
|
||||
// as long as there is something to visit
|
||||
while (nodes.getCount() > 0) {
|
||||
smallestNode = nodes.remove();
|
||||
if (smallestNode.value === finish) {
|
||||
break;
|
||||
}
|
||||
const neighbors = this.adjacencyList[smallestNode.value];
|
||||
for (let neighborKey in neighbors) {
|
||||
let edge = neighbors[neighborKey];
|
||||
//relax node
|
||||
let newCost = smallestNode.key + edge.weight;
|
||||
let nextNeighbor = edge.target;
|
||||
if (newCost < nextNeighbor.key) {
|
||||
nextNeighbor._previous = smallestNode;
|
||||
//key change
|
||||
nodes.decreaseKey(nextNeighbor, newCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!smallestNode || !smallestNode._previous || smallestNode.value !== finish) {
|
||||
return undefined; //no path
|
||||
}
|
||||
|
||||
let finalCost = smallestNode.key; //final weight last node
|
||||
|
||||
// done, build the shortest path
|
||||
while (smallestNode._previous) {
|
||||
//backtrack
|
||||
const to = smallestNode.value,
|
||||
parent = smallestNode._previous,
|
||||
from = parent.value;
|
||||
|
||||
path.push(this.adjacencyList[from].find(x => x.target.value === to));
|
||||
smallestNode = parent;
|
||||
}
|
||||
|
||||
return {
|
||||
path: path.reverse(),
|
||||
cost: finalCost
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edge.transform function on the conversion path in OpenSeadragon.converter.getConversionPath().
|
||||
* It can be also conversion to undefined if used as destructor implementation.
|
||||
*
|
||||
* @callback TypeConvertor
|
||||
* @memberof OpenSeadragon
|
||||
* @param {OpenSeadragon.Tile} tile reference tile that owns the data
|
||||
* @param {any} data data in the input format
|
||||
* @returns {any} data in the output format
|
||||
*/
|
||||
|
||||
/**
|
||||
* Destructor called every time a data type is to be destroyed or converted to another type.
|
||||
*
|
||||
* @callback TypeDestructor
|
||||
* @memberof OpenSeadragon
|
||||
* @param {any} data data in the format the destructor is registered for
|
||||
* @returns {any} can return any value that is carried over to the caller if desirable.
|
||||
* Note: not used by the OSD cache system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Node on the conversion path in OpenSeadragon.converter.getConversionPath().
|
||||
*
|
||||
* @typedef {Object} ConversionStep
|
||||
* @memberof OpenSeadragon
|
||||
* @param {OpenSeadragon.PriorityQueue.Node} target - Target node of the conversion step.
|
||||
* Its value is the target format.
|
||||
* @param {OpenSeadragon.PriorityQueue.Node} origin - Origin node of the conversion step.
|
||||
* Its value is the origin format.
|
||||
* @param {number} weight cost of the conversion
|
||||
* @param {TypeConvertor} transform the conversion itself
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class that orchestrates automated data types conversion. Do not instantiate
|
||||
* this class, use OpenSeadragon.convertor - a global instance, instead.
|
||||
* @class DataTypeConvertor
|
||||
* @memberOf OpenSeadragon
|
||||
*/
|
||||
$.DataTypeConvertor = class {
|
||||
|
||||
constructor() {
|
||||
this.graph = new WeightedGraph();
|
||||
this.destructors = {};
|
||||
this.copyings = {};
|
||||
|
||||
// Teaching OpenSeadragon built-in conversions:
|
||||
const imageCreator = (tile, url) => new $.Promise((resolve, reject) => {
|
||||
if (!$.supportsAsync) {
|
||||
throw "Not supported in sync mode!";
|
||||
}
|
||||
const img = new Image();
|
||||
img.onerror = img.onabort = reject;
|
||||
img.onload = () => resolve(img);
|
||||
img.src = url;
|
||||
});
|
||||
const canvasContextCreator = (tile, imageData) => {
|
||||
const canvas = document.createElement( 'canvas' );
|
||||
canvas.width = imageData.width;
|
||||
canvas.height = imageData.height;
|
||||
const context = canvas.getContext('2d', { willReadFrequently: true });
|
||||
context.drawImage( imageData, 0, 0 );
|
||||
return context;
|
||||
};
|
||||
|
||||
this.learn("context2d", "webImageUrl", (tile, ctx) => ctx.canvas.toDataURL(), 1, 2);
|
||||
this.learn("image", "webImageUrl", (tile, image) => image.url);
|
||||
this.learn("image", "context2d", canvasContextCreator, 1, 1);
|
||||
this.learn("url", "image", imageCreator, 1, 1);
|
||||
|
||||
//Copies
|
||||
this.learn("image", "image", (tile, image) => imageCreator(tile, image.src), 1, 1);
|
||||
this.learn("url", "url", (tile, url) => url, 0, 1); //strings are immutable, no need to copy
|
||||
this.learn("context2d", "context2d", (tile, ctx) => canvasContextCreator(tile, ctx.canvas));
|
||||
|
||||
/**
|
||||
* Free up canvas memory
|
||||
* (iOS 12 or higher on 2GB RAM device has only 224MB canvas memory,
|
||||
* and Safari keeps canvas until its height and width will be set to 0).
|
||||
*/
|
||||
this.learnDestroy("context2d", ctx => {
|
||||
ctx.canvas.width = 0;
|
||||
ctx.canvas.height = 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique identifier (unlike toString.call(x)) to be guessed
|
||||
* from the data value. This type guess is more strict than
|
||||
* OpenSeadragon.type() implementation, but for most type recognition
|
||||
* this test relies on the output of OpenSeadragon.type().
|
||||
*
|
||||
* Note: although we try to implement the type guessing, do
|
||||
* not rely on this functionality! Prefer explicit type declaration.
|
||||
*
|
||||
* @function guessType
|
||||
* @param x object to get unique identifier for
|
||||
* - can be array, in that case, alphabetically-ordered list of inner unique types
|
||||
* is returned (null, undefined are ignored)
|
||||
* - if $.isPlainObject(x) is true, then the object can define
|
||||
* getType function to specify its type
|
||||
* - otherwise, toString.call(x) is applied to get the parameter description
|
||||
* @return {string} unique variable descriptor
|
||||
*/
|
||||
guessType( x ) {
|
||||
if (Array.isArray(x)) {
|
||||
const types = [];
|
||||
for (let item of x) {
|
||||
if (item === undefined || item === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const type = this.guessType(item);
|
||||
if (!types.includes(type)) {
|
||||
types.push(type);
|
||||
}
|
||||
}
|
||||
types.sort();
|
||||
return `Array [${types.join(",")}]`;
|
||||
}
|
||||
|
||||
const guessType = $.type(x);
|
||||
if (guessType === "dom-node") {
|
||||
//distinguish nodes
|
||||
return guessType.nodeName.toLowerCase();
|
||||
}
|
||||
|
||||
if (guessType === "object") {
|
||||
if ($.isFunction(x.getType)) {
|
||||
return x.getType();
|
||||
}
|
||||
}
|
||||
return guessType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teach the system to convert data type 'from' -> 'to'
|
||||
* @param {string} from unique ID of the data item 'from'
|
||||
* @param {string} to unique ID of the data item 'to'
|
||||
* @param {OpenSeadragon.TypeConvertor} callback convertor that takes two arguments: a tile reference, and
|
||||
* a data object of a type 'from'; and converts this data object to type 'to'. It can return also the value
|
||||
* wrapped in a Promise (returned in resolve) or it can be async function.
|
||||
* @param {Number} [costPower=0] positive cost class of the conversion, smaller or equal than 7.
|
||||
* Should reflect the actual cost of the conversion:
|
||||
* - if nothing must be done and only reference is retrieved (or a constant operation done),
|
||||
* return 0 (default)
|
||||
* - if a linear amount of work is necessary,
|
||||
* return 1
|
||||
* ... and so on, basically the number in O() complexity power exponent (for simplification)
|
||||
* @param {Number} [costMultiplier=1] multiplier of the cost class, e.g. O(3n^2) would
|
||||
* use costPower=2, costMultiplier=3; can be between 1 and 10^5
|
||||
*/
|
||||
learn(from, to, callback, costPower = 0, costMultiplier = 1) {
|
||||
$.console.assert(costPower >= 0 && costPower <= 7, "[DataTypeConvertor] Conversion costPower must be between <0, 7>.");
|
||||
$.console.assert($.isFunction(callback), "[DataTypeConvertor:learn] Callback must be a valid function!");
|
||||
|
||||
if (from === to) {
|
||||
this.copyings[to] = callback;
|
||||
} else {
|
||||
//we won't know if somebody added multiple edges, though it will choose some edge anyway
|
||||
costPower++;
|
||||
costMultiplier = Math.min(Math.max(costMultiplier, 1), 10 ^ 5);
|
||||
this.graph.addVertex(from);
|
||||
this.graph.addVertex(to);
|
||||
this.graph.addEdge(from, to, costPower * 10 ^ 5 + costMultiplier, callback);
|
||||
this._known = {}; //invalidate precomputed paths :/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Teach the system to destroy data type 'type'
|
||||
* for example, textures loaded to GPU have to be also manually removed when not needed anymore.
|
||||
* Needs to be defined only when the created object has extra deletion process.
|
||||
* @param {string} type
|
||||
* @param {OpenSeadragon.TypeDestructor} callback destructor, receives the object created,
|
||||
* it is basically a type conversion to 'undefined' - thus the type.
|
||||
*/
|
||||
learnDestroy(type, callback) {
|
||||
this.destructors[type] = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert data item x of type 'from' to any of the 'to' types, chosen is the cheapest known conversion.
|
||||
* Data is destroyed upon conversion. For different behavior, implement your conversion using the
|
||||
* path rules obtained from getConversionPath().
|
||||
* Note: conversion DOES NOT COPY data if [to] contains type 'from' (e.g., the cheapest conversion is no conversion).
|
||||
* It automatically calls destructor on immediate types, but NOT on the x and the result. You should call these
|
||||
* manually if these should be destroyed.
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
* @param {any} data data item to convert
|
||||
* @param {string} from data item type
|
||||
* @param {string} to desired type(s)
|
||||
* @return {OpenSeadragon.Promise<?>} promise resolution with type 'to' or undefined if the conversion failed
|
||||
*/
|
||||
convert(tile, data, from, ...to) {
|
||||
const conversionPath = this.getConversionPath(from, to);
|
||||
if (!conversionPath) {
|
||||
$.console.error(`[OpenSeadragon.convertor.convert] Conversion ${from} ---> ${to} cannot be done!`);
|
||||
return $.Promise.resolve();
|
||||
}
|
||||
|
||||
const stepCount = conversionPath.length,
|
||||
_this = this;
|
||||
const step = (x, i, destroy = true) => {
|
||||
if (i >= stepCount) {
|
||||
return $.Promise.resolve(x);
|
||||
}
|
||||
let edge = conversionPath[i];
|
||||
let y = edge.transform(tile, x);
|
||||
if (y === undefined) {
|
||||
$.console.error(`[OpenSeadragon.convertor.convert] data mid result undefined value (while converting using %s)`, edge);
|
||||
return $.Promise.resolve();
|
||||
}
|
||||
//node.value holds the type string
|
||||
if (destroy) {
|
||||
_this.destroy(x, edge.origin.value);
|
||||
}
|
||||
const result = $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||
return result.then(res => step(res, i + 1));
|
||||
};
|
||||
//destroy only mid-results, but not the original value
|
||||
return step(data, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the data item given.
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
* @param {any} data data item to convert
|
||||
* @param {string} type data type
|
||||
* @return {OpenSeadragon.Promise<?>|undefined} promise resolution with data passed from constructor
|
||||
*/
|
||||
copy(tile, data, type) {
|
||||
const copyTransform = this.copyings[type];
|
||||
if (copyTransform) {
|
||||
const y = copyTransform(tile, data);
|
||||
return $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||
}
|
||||
$.console.warn(`[OpenSeadragon.convertor.copy] is not supported with type %s`, type);
|
||||
return $.Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the data item given.
|
||||
* @param {string} type data type
|
||||
* @param {any} data
|
||||
* @return {OpenSeadragon.Promise<any>|undefined} promise resolution with data passed from constructor, or undefined
|
||||
* if not such conversion exists
|
||||
*/
|
||||
destroy(data, type) {
|
||||
const destructor = this.destructors[type];
|
||||
if (destructor) {
|
||||
const y = destructor(data);
|
||||
return $.type(y) === "promise" ? y : $.Promise.resolve(y);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get possible system type conversions and cache result.
|
||||
* @param {string} from data item type
|
||||
* @param {string|string[]} to array of accepted types
|
||||
* @return {ConversionStep[]|undefined} array of required conversions (returns empty array
|
||||
* for from===to), or undefined if the system cannot convert between given types.
|
||||
* Each object has 'transform' function that converts between neighbouring types, such
|
||||
* that x = arr[i].transform(x) is valid input for convertor arr[i+1].transform(), e.g.
|
||||
* arr[i+1].transform(arr[i].transform( ... )) is a valid conversion procedure.
|
||||
*
|
||||
* Note: if a function is returned, it is a callback called once the data is ready.
|
||||
*/
|
||||
getConversionPath(from, to) {
|
||||
let bestConvertorPath, selectedType;
|
||||
let knownFrom = this._known[from];
|
||||
if (!knownFrom) {
|
||||
this._known[from] = knownFrom = {};
|
||||
}
|
||||
|
||||
if (Array.isArray(to)) {
|
||||
$.console.assert(to.length > 0, "[getConversionPath] conversion 'to' type must be defined.");
|
||||
let bestCost = Infinity;
|
||||
|
||||
//FIXME: pre-compute all paths in 'to' array? could be efficient for multiple
|
||||
// type system, but overhead for simple use cases... now we just use the first type if costs unknown
|
||||
selectedType = to[0];
|
||||
|
||||
for (const outType of to) {
|
||||
const conversion = knownFrom[outType];
|
||||
if (conversion && bestCost > conversion.cost) {
|
||||
bestConvertorPath = conversion;
|
||||
bestCost = conversion.cost;
|
||||
selectedType = outType;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$.console.assert(typeof to === "string", "[getConversionPath] conversion 'to' type must be defined.");
|
||||
bestConvertorPath = knownFrom[to];
|
||||
selectedType = to;
|
||||
}
|
||||
|
||||
if (!bestConvertorPath) {
|
||||
bestConvertorPath = this.graph.dijkstra(from, selectedType);
|
||||
this._known[from][selectedType] = bestConvertorPath;
|
||||
}
|
||||
return bestConvertorPath ? bestConvertorPath.path : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of known conversion types
|
||||
* @return {string[]}
|
||||
*/
|
||||
getKnownTypes() {
|
||||
return Object.keys(this.graph.vertices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether given type is known to the convertor
|
||||
* @param {string} type type to test
|
||||
* @return {boolean}
|
||||
*/
|
||||
existsType(type) {
|
||||
return !!this.graph.vertices[type];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Static convertor available throughout OpenSeadragon.
|
||||
*
|
||||
* Built-in conversions include types:
|
||||
* - context2d canvas 2d context
|
||||
* - image HTMLImage element
|
||||
* - url url string carrying or pointing to 2D raster data
|
||||
* - canvas HTMLCanvas element
|
||||
*
|
||||
* @type OpenSeadragon.DataTypeConvertor
|
||||
* @memberOf OpenSeadragon
|
||||
*/
|
||||
$.convertor = new $.DataTypeConvertor();
|
||||
|
||||
}(OpenSeadragon));
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - DisplayRect
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
|
518
src/drawer.js
|
@ -1,518 +0,0 @@
|
|||
/*
|
||||
* OpenSeadragon - Drawer
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @class Drawer
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Drawer.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||
* @param {Element} options.element - Parent element.
|
||||
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
|
||||
*/
|
||||
$.Drawer = function( options ) {
|
||||
|
||||
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
|
||||
|
||||
//backward compatibility for positional args while prefering more
|
||||
//idiomatic javascript options object as the only argument
|
||||
var args = arguments;
|
||||
|
||||
if( !$.isPlainObject( options ) ){
|
||||
options = {
|
||||
source: args[ 0 ], // Reference to Viewer tile source.
|
||||
viewport: args[ 1 ], // Reference to Viewer viewport.
|
||||
element: args[ 2 ] // Parent element.
|
||||
};
|
||||
}
|
||||
|
||||
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
|
||||
$.console.assert( options.element, "[Drawer] options.element is required" );
|
||||
|
||||
if ( options.source ) {
|
||||
$.console.error( "[Drawer] options.source is no longer accepted; use TiledImage instead" );
|
||||
}
|
||||
|
||||
this.viewer = options.viewer;
|
||||
this.viewport = options.viewport;
|
||||
this.debugGridColor = options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
||||
if (options.opacity) {
|
||||
$.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" );
|
||||
}
|
||||
|
||||
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
|
||||
/**
|
||||
* The parent element of this Drawer instance, passed in when the Drawer was created.
|
||||
* The parent of {@link OpenSeadragon.Drawer#canvas}.
|
||||
* @member {Element} container
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
*/
|
||||
this.container = $.getElement( options.element );
|
||||
/**
|
||||
* A <canvas> element if the browser supports them, otherwise a <div> element.
|
||||
* Child element of {@link OpenSeadragon.Drawer#container}.
|
||||
* @member {Element} canvas
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
*/
|
||||
this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
|
||||
/**
|
||||
* 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.
|
||||
* @member {Object} context
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
*/
|
||||
this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null;
|
||||
|
||||
/**
|
||||
* Sketch canvas used to temporarily draw tiles which cannot be drawn directly
|
||||
* to the main canvas due to opacity. Lazily initialized.
|
||||
*/
|
||||
this.sketchCanvas = null;
|
||||
this.sketchContext = null;
|
||||
|
||||
/**
|
||||
* @member {Element} element
|
||||
* @memberof OpenSeadragon.Drawer#
|
||||
* @deprecated Alias for {@link OpenSeadragon.Drawer#container}.
|
||||
*/
|
||||
this.element = this.container;
|
||||
|
||||
// We force our container to ltr because our drawing math doesn't work in rtl.
|
||||
// This issue only affects our canvas renderer, but we do it always for consistency.
|
||||
// Note that this means overlays you want to be rtl need to be explicitly set to rtl.
|
||||
this.container.dir = 'ltr';
|
||||
|
||||
// check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density
|
||||
if (this.useCanvas) {
|
||||
var viewportSize = this._calculateCanvasSize();
|
||||
this.canvas.width = viewportSize.x;
|
||||
this.canvas.height = viewportSize.y;
|
||||
}
|
||||
|
||||
this.canvas.style.width = "100%";
|
||||
this.canvas.style.height = "100%";
|
||||
this.canvas.style.position = "absolute";
|
||||
$.setElementOpacity( this.canvas, this.opacity, true );
|
||||
|
||||
// explicit left-align
|
||||
this.container.style.textAlign = "left";
|
||||
this.container.appendChild( this.canvas );
|
||||
};
|
||||
|
||||
$.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
|
||||
// deprecated
|
||||
addOverlay: function( element, location, placement, onDraw ) {
|
||||
$.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead.");
|
||||
this.viewer.addOverlay( element, location, placement, onDraw );
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
updateOverlay: function( element, location, placement ) {
|
||||
$.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.");
|
||||
this.viewer.updateOverlay( element, location, placement );
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
removeOverlay: function( element ) {
|
||||
$.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.");
|
||||
this.viewer.removeOverlay( element );
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
clearOverlays: function() {
|
||||
$.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.");
|
||||
this.viewer.clearOverlays();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the opacity of the drawer.
|
||||
* @param {Number} opacity
|
||||
* @return {OpenSeadragon.Drawer} Chainable.
|
||||
*/
|
||||
setOpacity: function( opacity ) {
|
||||
$.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.");
|
||||
var world = this.viewer.world;
|
||||
for (var i = 0; i < world.getItemCount(); i++) {
|
||||
world.getItemAt( i ).setOpacity( opacity );
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the opacity of the drawer.
|
||||
* @returns {Number}
|
||||
*/
|
||||
getOpacity: function() {
|
||||
$.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead.");
|
||||
var world = this.viewer.world;
|
||||
var maxOpacity = 0;
|
||||
for (var i = 0; i < world.getItemCount(); i++) {
|
||||
var opacity = world.getItemAt( i ).getOpacity();
|
||||
if ( opacity > maxOpacity ) {
|
||||
maxOpacity = opacity;
|
||||
}
|
||||
}
|
||||
return maxOpacity;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
needsUpdate: function() {
|
||||
$.console.error( "[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead." );
|
||||
return this.viewer.world.needsDraw();
|
||||
},
|
||||
|
||||
// deprecated
|
||||
numTilesLoaded: function() {
|
||||
$.console.error( "[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead." );
|
||||
return this.viewer.tileCache.numTilesLoaded();
|
||||
},
|
||||
|
||||
// deprecated
|
||||
reset: function() {
|
||||
$.console.error( "[Drawer.reset] this function is deprecated. Use World.resetItems instead." );
|
||||
this.viewer.world.resetItems();
|
||||
return this;
|
||||
},
|
||||
|
||||
// deprecated
|
||||
update: function() {
|
||||
$.console.error( "[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead." );
|
||||
this.clear();
|
||||
this.viewer.world.draw();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Boolean} True if rotation is supported.
|
||||
*/
|
||||
canRotate: function() {
|
||||
return this.useCanvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the drawer (unload current loaded tiles)
|
||||
*/
|
||||
destroy: function() {
|
||||
//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)
|
||||
this.canvas.width = 1;
|
||||
this.canvas.height = 1;
|
||||
this.sketchCanvas = null;
|
||||
this.sketchContext = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the Drawer so it's ready to draw another frame.
|
||||
*/
|
||||
clear: function() {
|
||||
this.canvas.innerHTML = "";
|
||||
if ( this.useCanvas ) {
|
||||
var viewportSize = this._calculateCanvasSize();
|
||||
if( this.canvas.width != viewportSize.x ||
|
||||
this.canvas.height != viewportSize.y ) {
|
||||
this.canvas.width = viewportSize.x;
|
||||
this.canvas.height = viewportSize.y;
|
||||
if ( this.sketchCanvas !== null ) {
|
||||
this.sketchCanvas.width = this.canvas.width;
|
||||
this.sketchCanvas.height = this.canvas.height;
|
||||
}
|
||||
}
|
||||
this._clear();
|
||||
}
|
||||
},
|
||||
|
||||
_clear: function ( useSketch ) {
|
||||
if ( !this.useCanvas ) {
|
||||
return;
|
||||
}
|
||||
var context = this._getContext( useSketch );
|
||||
var canvas = context.canvas;
|
||||
context.clearRect( 0, 0, canvas.width, canvas.height );
|
||||
},
|
||||
|
||||
/**
|
||||
* Translates from OpenSeadragon viewer rectangle to drawer rectangle.
|
||||
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
|
||||
* @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
|
||||
*/
|
||||
viewportToDrawerRectangle: function(rectangle) {
|
||||
var topLeft = this.viewport.pixelFromPoint(rectangle.getTopLeft(), true);
|
||||
var size = this.viewport.deltaPixelsFromPoints(rectangle.getSize(), true);
|
||||
|
||||
return new $.Rect(
|
||||
topLeft.x * $.pixelDensityRatio,
|
||||
topLeft.y * $.pixelDensityRatio,
|
||||
size.x * $.pixelDensityRatio,
|
||||
size.y * $.pixelDensityRatio
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws the given tile.
|
||||
* @param {OpenSeadragon.Tile} tile - The tile to draw.
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
|
||||
* where <code>rendered</code> is the context with the pre-drawn image.
|
||||
*/
|
||||
drawTile: function( tile, drawingHandler, useSketch ) {
|
||||
$.console.assert(tile, '[Drawer.drawTile] tile is required');
|
||||
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
|
||||
|
||||
if ( this.useCanvas ) {
|
||||
var context = this._getContext( useSketch );
|
||||
// TODO do this in a more performant way
|
||||
// specifically, don't save,rotate,restore every time we draw a tile
|
||||
if( this.viewport.degrees !== 0 ) {
|
||||
this._offsetForRotation( tile, this.viewport.degrees, useSketch );
|
||||
tile.drawCanvas( context, drawingHandler );
|
||||
this._restoreRotationChanges( tile, useSketch );
|
||||
} else {
|
||||
tile.drawCanvas( context, drawingHandler );
|
||||
}
|
||||
} else {
|
||||
tile.drawHTML( this.canvas );
|
||||
}
|
||||
},
|
||||
|
||||
_getContext: function( useSketch ) {
|
||||
var context = this.context;
|
||||
if ( useSketch ) {
|
||||
if (this.sketchCanvas === null) {
|
||||
this.sketchCanvas = document.createElement( "canvas" );
|
||||
this.sketchCanvas.width = this.canvas.width;
|
||||
this.sketchCanvas.height = this.canvas.height;
|
||||
this.sketchContext = this.sketchCanvas.getContext( "2d" );
|
||||
}
|
||||
context = this.sketchContext;
|
||||
}
|
||||
return context;
|
||||
},
|
||||
|
||||
// private
|
||||
saveContext: function( useSketch ) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._getContext( useSketch ).save();
|
||||
},
|
||||
|
||||
// private
|
||||
restoreContext: function( useSketch ) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._getContext( useSketch ).restore();
|
||||
},
|
||||
|
||||
// private
|
||||
setClip: function(rect, useSketch) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = this._getContext( useSketch );
|
||||
context.beginPath();
|
||||
context.rect(rect.x, rect.y, rect.width, rect.height);
|
||||
context.clip();
|
||||
},
|
||||
|
||||
// private
|
||||
drawRectangle: function(rect, fillStyle, useSketch) {
|
||||
if (!this.useCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = this._getContext( useSketch );
|
||||
context.save();
|
||||
context.fillStyle = fillStyle;
|
||||
context.fillRect(rect.x, rect.y, rect.width, rect.height);
|
||||
context.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Blends the sketch canvas in the main canvas.
|
||||
* @param {Float} opacity The opacity of the blending.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
blendSketch: function(opacity) {
|
||||
if (!this.useCanvas || !this.sketchCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.save();
|
||||
this.context.globalAlpha = opacity;
|
||||
this.context.drawImage(this.sketchCanvas, 0, 0);
|
||||
this.context.restore();
|
||||
},
|
||||
|
||||
// private
|
||||
drawDebugInfo: function( tile, count, i ){
|
||||
if ( !this.useCanvas ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = this.context;
|
||||
context.save();
|
||||
context.lineWidth = 2 * $.pixelDensityRatio;
|
||||
context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial';
|
||||
context.strokeStyle = this.debugGridColor;
|
||||
context.fillStyle = this.debugGridColor;
|
||||
|
||||
if ( this.viewport.degrees !== 0 ) {
|
||||
this._offsetForRotation( tile, this.viewport.degrees );
|
||||
}
|
||||
|
||||
context.strokeRect(
|
||||
tile.position.x * $.pixelDensityRatio,
|
||||
tile.position.y * $.pixelDensityRatio,
|
||||
tile.size.x * $.pixelDensityRatio,
|
||||
tile.size.y * $.pixelDensityRatio
|
||||
);
|
||||
|
||||
var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio;
|
||||
var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio;
|
||||
|
||||
// Rotate the text the right way around.
|
||||
context.translate( tileCenterX, tileCenterY );
|
||||
context.rotate( Math.PI / 180 * -this.viewport.degrees );
|
||||
context.translate( -tileCenterX, -tileCenterY );
|
||||
|
||||
if( tile.x === 0 && tile.y === 0 ){
|
||||
context.fillText(
|
||||
"Zoom: " + this.viewport.getZoom(),
|
||||
tile.position.x * $.pixelDensityRatio,
|
||||
(tile.position.y - 30) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Pan: " + this.viewport.getBounds().toString(),
|
||||
tile.position.x * $.pixelDensityRatio,
|
||||
(tile.position.y - 20) * $.pixelDensityRatio
|
||||
);
|
||||
}
|
||||
context.fillText(
|
||||
"Level: " + tile.level,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 20) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Column: " + tile.x,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 30) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Row: " + tile.y,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 40) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Order: " + i + " of " + count,
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 50) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Size: " + tile.size.toString(),
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 60) * $.pixelDensityRatio
|
||||
);
|
||||
context.fillText(
|
||||
"Position: " + tile.position.toString(),
|
||||
(tile.position.x + 10) * $.pixelDensityRatio,
|
||||
(tile.position.y + 70) * $.pixelDensityRatio
|
||||
);
|
||||
|
||||
if ( this.viewport.degrees !== 0 ) {
|
||||
this._restoreRotationChanges( tile );
|
||||
}
|
||||
context.restore();
|
||||
},
|
||||
|
||||
// private
|
||||
debugRect: function(rect) {
|
||||
if ( this.useCanvas ) {
|
||||
var context = this.context;
|
||||
context.save();
|
||||
context.lineWidth = 2 * $.pixelDensityRatio;
|
||||
context.strokeStyle = this.debugGridColor;
|
||||
context.fillStyle = this.debugGridColor;
|
||||
|
||||
context.strokeRect(
|
||||
rect.x * $.pixelDensityRatio,
|
||||
rect.y * $.pixelDensityRatio,
|
||||
rect.width * $.pixelDensityRatio,
|
||||
rect.height * $.pixelDensityRatio
|
||||
);
|
||||
|
||||
context.restore();
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
_offsetForRotation: function( tile, degrees, useSketch ){
|
||||
var cx = this.canvas.width / 2,
|
||||
cy = this.canvas.height / 2;
|
||||
|
||||
var context = this._getContext( useSketch );
|
||||
context.save();
|
||||
|
||||
context.translate(cx, cy);
|
||||
context.rotate( Math.PI / 180 * degrees);
|
||||
context.translate(-cx, -cy);
|
||||
},
|
||||
|
||||
// private
|
||||
_restoreRotationChanges: function( tile, useSketch ){
|
||||
var context = this._getContext( useSketch );
|
||||
context.restore();
|
||||
},
|
||||
|
||||
// private
|
||||
_calculateCanvasSize: function() {
|
||||
var pixelDensityRatio = $.pixelDensityRatio;
|
||||
var viewportSize = this.viewport.getContainerSize();
|
||||
return {
|
||||
x: viewportSize.x * pixelDensityRatio,
|
||||
y: viewportSize.y * pixelDensityRatio
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
426
src/drawerbase.js
Normal file
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* OpenSeadragon - DrawerBase
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @typedef BaseDrawerOptions
|
||||
* @memberOf OpenSeadragon
|
||||
* @property {boolean} [usePrivateCache=false] specify whether the drawer should use
|
||||
* detached (=internal) cache object in case it has to perform custom type conversion atop
|
||||
* what cache performs. In that case, drawer must implement internalCacheCreate() which gets data in one
|
||||
* of formats the drawer declares as supported. This method must return object to be used during drawing.
|
||||
* You should probably implement also internalCacheFree() to provide cleanup logics.
|
||||
*
|
||||
* @property {boolean} [preloadCache=true]
|
||||
* When internalCacheCreate is used, it can be applied offline (asynchronously) during data processing = preloading,
|
||||
* or just in time before rendering (if necessary). Preloading supports
|
||||
*/
|
||||
|
||||
const OpenSeadragon = $; // (re)alias back to OpenSeadragon for JSDoc
|
||||
/**
|
||||
* @class OpenSeadragon.DrawerBase
|
||||
* @classdesc Base class for Drawers that handle rendering of tiles for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Drawer.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||
* @param {HTMLElement} options.element - Parent element.
|
||||
* @abstract
|
||||
*/
|
||||
|
||||
OpenSeadragon.DrawerBase = class DrawerBase{
|
||||
constructor(options){
|
||||
$.console.assert( options.viewer, "[Drawer] options.viewer is required" );
|
||||
$.console.assert( options.viewport, "[Drawer] options.viewport is required" );
|
||||
$.console.assert( options.element, "[Drawer] options.element is required" );
|
||||
|
||||
this._id = this.getType() + $.now();
|
||||
this.viewer = options.viewer;
|
||||
this.viewport = options.viewport;
|
||||
this.debugGridColor = typeof options.debugGridColor === 'string' ? [options.debugGridColor] : options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor;
|
||||
this.options = $.extend({}, this.defaultOptions, options.options);
|
||||
|
||||
this.container = $.getElement( options.element );
|
||||
|
||||
this._renderingTarget = this._createDrawingElement();
|
||||
|
||||
|
||||
this.canvas.style.width = "100%";
|
||||
this.canvas.style.height = "100%";
|
||||
this.canvas.style.position = "absolute";
|
||||
// set canvas.style.left = 0 so the canvas is positioned properly in ltr and rtl html
|
||||
this.canvas.style.left = "0";
|
||||
$.setElementOpacity( this.canvas, this.viewer.opacity, true );
|
||||
|
||||
// Allow pointer events to pass through the canvas element so implicit
|
||||
// pointer capture works on touch devices
|
||||
$.setElementPointerEventsNone( this.canvas );
|
||||
$.setElementTouchActionNone( this.canvas );
|
||||
|
||||
// explicit left-align
|
||||
this.container.style.textAlign = "left";
|
||||
this.container.appendChild( this.canvas );
|
||||
|
||||
this._checkInterfaceImplementation();
|
||||
this.setInternalCacheNeedsRefresh(); // initializes stamp
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for the current drawer.
|
||||
* The base implementation provides default shared options.
|
||||
* Overrides should enumerate all defaults or extend from this implementation.
|
||||
* return $.extend({}, super.options, { ... custom drawer instance options ... });
|
||||
* @returns {BaseDrawerOptions} common options
|
||||
*/
|
||||
get defaultOptions() {
|
||||
return {
|
||||
usePrivateCache: false,
|
||||
preloadCache: true,
|
||||
};
|
||||
}
|
||||
|
||||
// protect the canvas member with a getter
|
||||
get canvas(){
|
||||
return this._renderingTarget;
|
||||
}
|
||||
|
||||
get element(){
|
||||
$.console.error('Drawer.element is deprecated. Use Drawer.container instead.');
|
||||
return this.container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique drawer ID
|
||||
* @return {string}
|
||||
*/
|
||||
getId() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {String | undefined} What type of drawer this is. Must be overridden by extending classes.
|
||||
*/
|
||||
getType(){
|
||||
$.console.error('Drawer.getType must be implemented by child class');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve required data formats the data must be converted to.
|
||||
* This list MUST BE A VALID SUBSET OF getSupportedDataFormats()
|
||||
* @abstract
|
||||
* @return {string[]}
|
||||
*/
|
||||
getRequiredDataFormats() {
|
||||
return this.getSupportedDataFormats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve data types
|
||||
* @abstract
|
||||
* @return {string[]}
|
||||
*/
|
||||
getSupportedDataFormats() {
|
||||
throw "Drawer.getSupportedDataFormats must define its supported rendering data types!";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a particular cache record is compatible.
|
||||
* This function _MUST_ be called: if it returns a falsey
|
||||
* value, the rendering _MUST NOT_ proceed. It should
|
||||
* await next animation frames and check again for availability.
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
* @return {any|undefined} undefined if cache not available, compatible data otherwise.
|
||||
*/
|
||||
getDataToDraw(tile) {
|
||||
const cache = tile.getCache(tile.cacheKey);
|
||||
if (!cache) {
|
||||
$.console.warn("Attempt to draw tile %s when not cached!", tile);
|
||||
return undefined;
|
||||
}
|
||||
const dataCache = cache.getDataForRendering(this, tile);
|
||||
return dataCache && dataCache.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {Boolean} Whether the drawer implementation is supported by the browser. Must be overridden by extending classes.
|
||||
*/
|
||||
static isSupported() {
|
||||
$.console.error('Drawer.isSupported must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {Element} the element to draw into
|
||||
* @private
|
||||
*/
|
||||
_createDrawingElement() {
|
||||
$.console.error('Drawer._createDrawingElement must be implemented by child class');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @param {Array} tiledImages - An array of TiledImages that are ready to be drawn.
|
||||
* @private
|
||||
*/
|
||||
draw(tiledImages) {
|
||||
$.console.error('Drawer.draw must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {Boolean} True if rotation is supported.
|
||||
*/
|
||||
canRotate() {
|
||||
$.console.error('Drawer.canRotate must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
*/
|
||||
destroy() {
|
||||
$.console.error('Drawer.destroy must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy internal cache. Should be called within destroy() when
|
||||
* usePrivateCache is set to true. Ensures cleanup of anything created
|
||||
* by internalCacheCreate(...).
|
||||
*/
|
||||
destroyInternalCache() {
|
||||
this.viewer.tileCache.clearDrawerInternalCache(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TiledImage} tiledImage the tiled image that is calling the function
|
||||
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
|
||||
* @private
|
||||
*/
|
||||
minimumOverlapRequired(tiledImage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
* @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
|
||||
* drawn smoothly on the canvas; see imageSmoothingEnabled in
|
||||
* {@link OpenSeadragon.Options} for more explanation.
|
||||
*/
|
||||
setImageSmoothingEnabled(imageSmoothingEnabled){
|
||||
$.console.error('Drawer.setImageSmoothingEnabled must be implemented by child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional public API to draw a rectangle (e.g. for debugging purposes)
|
||||
* Child classes can override this method if they wish to support this
|
||||
* @param {OpenSeadragon.Rect} rect
|
||||
*/
|
||||
drawDebuggingRect(rect) {
|
||||
$.console.warn('[drawer].drawDebuggingRect is not implemented by this drawer');
|
||||
}
|
||||
|
||||
// Deprecated functions
|
||||
clear(){
|
||||
$.console.warn('[drawer].clear() is deprecated. The drawer is responsible for clearing itself as needed before drawing tiles.');
|
||||
}
|
||||
|
||||
/**
|
||||
* If options.usePrivateCache is true, this method MUST RETURN the private cache content
|
||||
* @param {OpenSeadragon.CacheRecord} cache
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
* @return any
|
||||
*/
|
||||
internalCacheCreate(cache, tile) {}
|
||||
|
||||
/**
|
||||
* It is possible to perform any necessary cleanup on internal cache, necessary if you
|
||||
* need to clean up some memory (e.g. destroy canvas by setting with & height to 0).
|
||||
* @param {*} data object returned by internalCacheCreate(...)
|
||||
*/
|
||||
internalCacheFree(data) {}
|
||||
|
||||
/**
|
||||
* Call to invalidate internal cache. It will be rebuilt. With synchronous converions,
|
||||
* it will be rebuilt immediatelly. With asynchronous, it will be rebuilt once invalidation
|
||||
* routine happens, e.g. you should call also requestInvalidate() if you need to happen
|
||||
* it as soon as possible.
|
||||
*/
|
||||
setInternalCacheNeedsRefresh() {
|
||||
this._dataNeedsRefresh = $.now();
|
||||
}
|
||||
|
||||
// Private functions
|
||||
|
||||
/**
|
||||
* Ensures that child classes have provided implementations for public API methods
|
||||
* draw, canRotate, destroy, and setImageSmoothinEnabled. Throws an exception if the original
|
||||
* placeholder methods are still in place.
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
_checkInterfaceImplementation(){
|
||||
if (this._createDrawingElement === $.DrawerBase.prototype._createDrawingElement) {
|
||||
throw(new Error("[drawer]._createDrawingElement must be implemented by child class"));
|
||||
}
|
||||
if (this.draw === $.DrawerBase.prototype.draw) {
|
||||
throw(new Error("[drawer].draw must be implemented by child class"));
|
||||
}
|
||||
if (this.canRotate === $.DrawerBase.prototype.canRotate) {
|
||||
throw(new Error("[drawer].canRotate must be implemented by child class"));
|
||||
}
|
||||
if (this.destroy === $.DrawerBase.prototype.destroy) {
|
||||
throw(new Error("[drawer].destroy must be implemented by child class"));
|
||||
}
|
||||
if (this.setImageSmoothingEnabled === $.DrawerBase.prototype.setImageSmoothingEnabled) {
|
||||
throw(new Error("[drawer].setImageSmoothingEnabled must be implemented by child class"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Utility functions
|
||||
|
||||
/**
|
||||
* Scale from OpenSeadragon viewer rectangle to drawer rectangle
|
||||
* (ignoring rotation)
|
||||
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
|
||||
* @returns {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
|
||||
*/
|
||||
viewportToDrawerRectangle(rectangle) {
|
||||
var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
|
||||
var size = this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(), true);
|
||||
|
||||
return new $.Rect(
|
||||
topLeft.x * $.pixelDensityRatio,
|
||||
topLeft.y * $.pixelDensityRatio,
|
||||
size.x * $.pixelDensityRatio,
|
||||
size.y * $.pixelDensityRatio
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function converts the given point from to the drawer coordinate by
|
||||
* multiplying it with the pixel density.
|
||||
* This function does not take rotation into account, thus assuming provided
|
||||
* point is at 0 degree.
|
||||
* @param {OpenSeadragon.Point} point - the pixel point to convert
|
||||
* @returns {OpenSeadragon.Point} Point in drawer coordinate system.
|
||||
*/
|
||||
viewportCoordToDrawerCoord(point) {
|
||||
var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
|
||||
return new $.Point(
|
||||
vpPoint.x * $.pixelDensityRatio,
|
||||
vpPoint.y * $.pixelDensityRatio
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Internal utility functions
|
||||
|
||||
/**
|
||||
* Calculate width and height of the canvas based on viewport dimensions
|
||||
* and pixelDensityRatio
|
||||
* @private
|
||||
* @returns {OpenSeadragon.Point} {x, y} size of the canvas
|
||||
*/
|
||||
_calculateCanvasSize() {
|
||||
var pixelDensityRatio = $.pixelDensityRatio;
|
||||
var viewportSize = this.viewport.getContainerSize();
|
||||
return new OpenSeadragon.Point( Math.round(viewportSize.x * pixelDensityRatio), Math.round(viewportSize.y * pixelDensityRatio));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by implementations to fire the tiled-image-drawn event (used by tests)
|
||||
* @private
|
||||
*/
|
||||
_raiseTiledImageDrawnEvent(tiledImage, tiles){
|
||||
if(!this.viewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raised when a tiled image is drawn to the canvas. Used internally for testing.
|
||||
* The update-viewport event is preferred if you want to know when a frame has been drawn.
|
||||
*
|
||||
* @event tiled-image-drawn
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
|
||||
* @property {Array} tiles - An array of Tile objects that were drawn.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
* @private
|
||||
*/
|
||||
this.viewer.raiseEvent( 'tiled-image-drawn', {
|
||||
tiledImage: tiledImage,
|
||||
tiles: tiles,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by implementations to fire the drawer-error event
|
||||
* @private
|
||||
*/
|
||||
_raiseDrawerErrorEvent(tiledImage, errorMessage){
|
||||
if(!this.viewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raised when a tiled image is drawn to the canvas. Used internally for testing.
|
||||
* The update-viewport event is preferred if you want to know when a frame has been drawn.
|
||||
*
|
||||
* @event drawer-error
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
|
||||
* @property {OpenSeadragon.DrawerBase} drawer - The drawer that raised the error.
|
||||
* @property {String} error - A message describing the error.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
* @protected
|
||||
*/
|
||||
this.viewer.raiseEvent( 'drawer-error', {
|
||||
tiledImage: tiledImage,
|
||||
drawer: this,
|
||||
error: errorMessage,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - DziTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -108,24 +108,27 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
if ( data.Image ) {
|
||||
ns = data.Image.xmlns;
|
||||
} else if ( data.documentElement) {
|
||||
if ("Image" == data.documentElement.localName || "Image" == data.documentElement.tagName) {
|
||||
if ("Image" === data.documentElement.localName || "Image" === data.documentElement.tagName) {
|
||||
ns = data.documentElement.namespaceURI;
|
||||
}
|
||||
}
|
||||
|
||||
return ( "http://schemas.microsoft.com/deepzoom/2008" == ns ||
|
||||
"http://schemas.microsoft.com/deepzoom/2009" == ns );
|
||||
ns = (ns || '').toLowerCase();
|
||||
|
||||
return (ns.indexOf('schemas.microsoft.com/deepzoom/2008') !== -1 ||
|
||||
ns.indexOf('schemas.microsoft.com/deepzoom/2009') !== -1);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @function
|
||||
* @param {Object|XMLDocument} data - the raw configuration
|
||||
* @param {String} url - the url the data was retreived from if any.
|
||||
* @return {Object} options - A dictionary of keyword arguments sufficient
|
||||
* @param {String} url - the url the data was retrieved from if any.
|
||||
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
|
||||
* @returns {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure this tile sources constructor.
|
||||
*/
|
||||
configure: function( data, url ){
|
||||
configure: function( data, url, postData ){
|
||||
|
||||
var options;
|
||||
|
||||
|
@ -139,9 +142,10 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
}
|
||||
|
||||
if (url && !options.tilesUrl) {
|
||||
options.tilesUrl = url.replace(/([^\/]+)\.(dzi|xml|js)(\?.*|$)/, '$1_files/');
|
||||
options.tilesUrl = url.replace(
|
||||
/([^/]+?)(\.(dzi|xml|js)?(\?[^/]*)?)?\/?$/, '$1_files/');
|
||||
|
||||
if (url.search(/\.(dzi|xml|js)\?/) != -1) {
|
||||
if (url.search(/\.(dzi|xml|js)\?/) !== -1) {
|
||||
options.queryParams = url.match(/\?.*/);
|
||||
}else{
|
||||
options.queryParams = '';
|
||||
|
@ -163,6 +167,14 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* Equality comparator
|
||||
*/
|
||||
equals: function(otherSource) {
|
||||
return this.tilesUrl === otherSource.tilesUrl;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
|
@ -179,6 +191,10 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
yMax,
|
||||
i;
|
||||
|
||||
if ((this.minLevel && level < this.minLevel) || (this.maxLevel && level > this.maxLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !rects || !rects.length ) {
|
||||
return true;
|
||||
}
|
||||
|
@ -196,10 +212,10 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
xMax = xMin + rect.width * scale;
|
||||
yMax = yMin + rect.height * scale;
|
||||
|
||||
xMin = Math.floor( xMin / this.tileSize );
|
||||
yMin = Math.floor( yMin / this.tileSize );
|
||||
xMax = Math.ceil( xMax / this.tileSize );
|
||||
yMax = Math.ceil( yMax / this.tileSize );
|
||||
xMin = Math.floor( xMin / this._tileWidth );
|
||||
yMin = Math.floor( yMin / this._tileWidth ); // DZI tiles are square, so we just use _tileWidth
|
||||
xMax = Math.ceil( xMax / this._tileWidth );
|
||||
yMax = Math.ceil( yMax / this._tileWidth );
|
||||
|
||||
if ( xMin <= x && x < xMax && yMin <= y && y < yMax ) {
|
||||
return true;
|
||||
|
@ -233,7 +249,7 @@ function configureFromXML( tileSource, xmlDoc ){
|
|||
sizeNode,
|
||||
i;
|
||||
|
||||
if ( rootName == "Image" ) {
|
||||
if ( rootName === "Image" ) {
|
||||
|
||||
try {
|
||||
sizeNode = root.getElementsByTagName("Size" )[ 0 ];
|
||||
|
@ -297,10 +313,12 @@ function configureFromXML( tileSource, xmlDoc ){
|
|||
e :
|
||||
new Error( $.getString("Errors.Dzi") );
|
||||
}
|
||||
} else if ( rootName == "Collection" ) {
|
||||
} else if ( rootName === "Collection" ) {
|
||||
throw new Error( $.getString( "Errors.Dzc" ) );
|
||||
} else if ( rootName == "Error" ) {
|
||||
return $._processDZIError( root );
|
||||
} else if ( rootName === "Error" ) {
|
||||
var messageNode = root.getElementsByTagName("Message")[0];
|
||||
var message = messageNode.firstChild.nodeValue;
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
throw new Error( $.getString( "Errors.Dzi" ) );
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - EventSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -37,9 +37,19 @@
|
|||
/**
|
||||
* Event handler method signature used by all OpenSeadragon events.
|
||||
*
|
||||
* @callback EventHandler
|
||||
* @typedef {function(OpenSeadragon.Event): void} OpenSeadragon.EventHandler
|
||||
* @memberof OpenSeadragon
|
||||
* @param {Object} event - See individual events for event-specific properties.
|
||||
* @param {OpenSeadragon.Event} event - The event object containing event-specific properties.
|
||||
* @returns {void} This handler does not return a value.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event handler method signature used by all OpenSeadragon events.
|
||||
*
|
||||
* @typedef {function(OpenSeadragon.Event): Promise<void>} OpenSeadragon.AsyncEventHandler
|
||||
* @memberof OpenSeadragon
|
||||
* @param {OpenSeadragon.Event} event - The event object containing event-specific properties.
|
||||
* @returns {Promise<void>} This handler does not return a value.
|
||||
*/
|
||||
|
||||
|
||||
|
@ -51,44 +61,87 @@
|
|||
*/
|
||||
$.EventSource = function() {
|
||||
this.events = {};
|
||||
this._rejectedEventList = {};
|
||||
};
|
||||
|
||||
$.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
|
||||
/** @lends OpenSeadragon.EventSource.prototype */
|
||||
$.EventSource.prototype = {
|
||||
|
||||
// TODO: Add a method 'one' which automatically unbinds a listener after the first triggered event that matches.
|
||||
/**
|
||||
* Add an event handler to be triggered only once (or a given number of times)
|
||||
* for a given event. It is not removable with removeHandler().
|
||||
* @function
|
||||
* @param {String} eventName - Name of event to register.
|
||||
* @param {OpenSeadragon.EventHandler|OpenSeadragon.AsyncEventHandler} handler - Function to call when event
|
||||
* is triggered.
|
||||
* @param {Object} [userData=null] - Arbitrary object to be passed unchanged
|
||||
* to the handler.
|
||||
* @param {Number} [times=1] - The number of times to handle the event
|
||||
* before removing it.
|
||||
* @param {Number} [priority=0] - Handler priority. By default, all priorities are 0. Higher number = priority.
|
||||
* @returns {Boolean} - True if the handler was added, false if it was rejected
|
||||
*/
|
||||
addOnceHandler: function(eventName, handler, userData, times, priority) {
|
||||
const self = this;
|
||||
times = times || 1;
|
||||
let count = 0;
|
||||
const onceHandler = function(event) {
|
||||
count++;
|
||||
if (count === times) {
|
||||
self.removeHandler(eventName, onceHandler);
|
||||
}
|
||||
return handler(event);
|
||||
};
|
||||
return this.addHandler(eventName, onceHandler, userData, priority);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add an event handler for a given event.
|
||||
* @function
|
||||
* @param {String} eventName - Name of event to register.
|
||||
* @param {OpenSeadragon.EventHandler} handler - Function to call when event is triggered.
|
||||
* @param {OpenSeadragon.EventHandler|OpenSeadragon.AsyncEventHandler} handler - Function to call when event is triggered.
|
||||
* @param {Object} [userData=null] - Arbitrary object to be passed unchanged to the handler.
|
||||
* @param {Number} [priority=0] - Handler priority. By default, all priorities are 0. Higher number = priority.
|
||||
* @returns {Boolean} - True if the handler was added, false if it was rejected
|
||||
*/
|
||||
addHandler: function ( eventName, handler, userData ) {
|
||||
var events = this.events[ eventName ];
|
||||
addHandler: function ( eventName, handler, userData, priority ) {
|
||||
|
||||
if(Object.prototype.hasOwnProperty.call(this._rejectedEventList, eventName)){
|
||||
$.console.error(`Error adding handler for ${eventName}. ${this._rejectedEventList[eventName]}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
let events = this.events[ eventName ];
|
||||
if ( !events ) {
|
||||
this.events[ eventName ] = events = [];
|
||||
}
|
||||
if ( handler && $.isFunction( handler ) ) {
|
||||
events[ events.length ] = { handler: handler, userData: userData || null };
|
||||
let index = events.length,
|
||||
event = { handler: handler, userData: userData || null, priority: priority || 0 };
|
||||
events[ index ] = event;
|
||||
while ( index > 0 && events[ index - 1 ].priority < events[ index ].priority ) {
|
||||
events[ index ] = events[ index - 1 ];
|
||||
events[ index - 1 ] = event;
|
||||
index--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a specific event handler for a given event.
|
||||
* @function
|
||||
* @param {String} eventName - Name of event for which the handler is to be removed.
|
||||
* @param {OpenSeadragon.EventHandler} handler - Function to be removed.
|
||||
* @param {OpenSeadragon.EventHandler|OpenSeadragon.AsyncEventHandler} handler - Function to be removed.
|
||||
*/
|
||||
removeHandler: function ( eventName, handler ) {
|
||||
var events = this.events[ eventName ],
|
||||
handlers = [],
|
||||
i;
|
||||
const events = this.events[ eventName ],
|
||||
handlers = [];
|
||||
if ( !events ) {
|
||||
return;
|
||||
}
|
||||
if ( $.isArray( events ) ) {
|
||||
for ( i = 0; i < events.length; i++ ) {
|
||||
for ( let i = 0; i < events.length; i++ ) {
|
||||
if ( events[i].handler !== handler ) {
|
||||
handlers.push( events[ i ] );
|
||||
}
|
||||
|
@ -97,6 +150,18 @@ $.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the amount of handlers registered for a given event.
|
||||
* @param {String} eventName - Name of event to inspect.
|
||||
* @returns {number} amount of events
|
||||
*/
|
||||
numberOfHandlers: function (eventName) {
|
||||
const events = this.events[ eventName ];
|
||||
if ( !events ) {
|
||||
return 0;
|
||||
}
|
||||
return events.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all event handlers for a given event type. If no type is given all
|
||||
|
@ -108,7 +173,7 @@ $.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
|
|||
if ( eventName ){
|
||||
this.events[ eventName ] = [];
|
||||
} else{
|
||||
for ( var eventType in this.events ) {
|
||||
for ( let eventType in this.events ) {
|
||||
this.events[ eventType ] = [];
|
||||
}
|
||||
}
|
||||
|
@ -119,8 +184,8 @@ $.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
|
|||
* @function
|
||||
* @param {String} eventName - Name of event to get handlers for.
|
||||
*/
|
||||
getHandler: function ( eventName ) {
|
||||
var events = this.events[ eventName ];
|
||||
getHandler: function ( eventName) {
|
||||
let events = this.events[ eventName ];
|
||||
if ( !events || !events.length ) {
|
||||
return null;
|
||||
}
|
||||
|
@ -128,9 +193,8 @@ $.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
|
|||
[ events[ 0 ] ] :
|
||||
Array.apply( null, events );
|
||||
return function ( source, args ) {
|
||||
var i,
|
||||
length = events.length;
|
||||
for ( i = 0; i < length; i++ ) {
|
||||
let length = events.length;
|
||||
for ( let i = 0; i < length; i++ ) {
|
||||
if ( events[ i ] ) {
|
||||
args.eventSource = source;
|
||||
args.userData = events[ i ].userData;
|
||||
|
@ -141,23 +205,106 @@ $.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
|
|||
},
|
||||
|
||||
/**
|
||||
* Trigger an event, optionally passing additional information.
|
||||
* Get a function which iterates the list of all handlers registered for a given event,
|
||||
* calling the handler for each and awaiting async ones.
|
||||
* @function
|
||||
* @param {String} eventName - Name of event to get handlers for.
|
||||
* @param {any} bindTarget - Bound target to return with the promise on finish
|
||||
*/
|
||||
getAwaitingHandler: function ( eventName, bindTarget ) {
|
||||
let events = this.events[ eventName ];
|
||||
if ( !events || !events.length ) {
|
||||
return null;
|
||||
}
|
||||
events = events.length === 1 ?
|
||||
[ events[ 0 ] ] :
|
||||
Array.apply( null, events );
|
||||
|
||||
return function ( source, args ) {
|
||||
// We return a promise that gets resolved after all the events finish.
|
||||
// Returning loop result is not correct, loop promises chain dynamically
|
||||
// and outer code could process finishing logics in the middle of event loop.
|
||||
return new $.Promise(resolve => {
|
||||
const length = events.length;
|
||||
function loop(index) {
|
||||
if ( index >= length || !events[ index ] ) {
|
||||
resolve(bindTarget);
|
||||
return null;
|
||||
}
|
||||
args.eventSource = source;
|
||||
args.userData = events[ index ].userData;
|
||||
let result = events[ index ].handler( args );
|
||||
result = (!result || $.type(result) !== "promise") ? $.Promise.resolve() : result;
|
||||
return result.then(() => loop(index + 1));
|
||||
}
|
||||
loop(0);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger an event, optionally passing additional information. Does not await async handlers, i.e.
|
||||
* OpenSeadragon.AsyncEventHandler.
|
||||
* @function
|
||||
* @param {String} eventName - Name of event to register.
|
||||
* @param {Object} eventArgs - Event-specific data.
|
||||
* @returns {Boolean} True if the event was fired, false if it was rejected because of rejectEventHandler(eventName)
|
||||
*/
|
||||
raiseEvent: function( eventName, eventArgs ) {
|
||||
//uncomment if you want to get a log of all events
|
||||
//$.console.log( eventName );
|
||||
var handler = this.getHandler( eventName );
|
||||
//$.console.log( "Event fired:", eventName );
|
||||
|
||||
if ( handler ) {
|
||||
if ( !eventArgs ) {
|
||||
eventArgs = {};
|
||||
}
|
||||
|
||||
handler( this, eventArgs );
|
||||
if(Object.prototype.hasOwnProperty.call(this._rejectedEventList, eventName)){
|
||||
$.console.error(`Error adding handler for ${eventName}. ${this._rejectedEventList[eventName]}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const handler = this.getHandler( eventName );
|
||||
if ( handler ) {
|
||||
handler( this, eventArgs || {} );
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger an event, optionally passing additional information.
|
||||
* This events awaits every asynchronous or promise-returning function, i.e.
|
||||
* OpenSeadragon.AsyncEventHandler.
|
||||
* @param {String} eventName - Name of event to register.
|
||||
* @param {Object} eventArgs - Event-specific data.
|
||||
* @param {?} [bindTarget = null] - Promise-resolved value on the event finish
|
||||
* @return {OpenSeadragon.Promise|undefined} - Promise resolved upon the event completion.
|
||||
*/
|
||||
raiseEventAwaiting: function ( eventName, eventArgs, bindTarget = null ) {
|
||||
//uncomment if you want to get a log of all events
|
||||
//$.console.log( "Awaiting event fired:", eventName );
|
||||
|
||||
const awaitingHandler = this.getAwaitingHandler(eventName, bindTarget);
|
||||
if (awaitingHandler) {
|
||||
return awaitingHandler(this, eventArgs || {});
|
||||
}
|
||||
return $.Promise.resolve(bindTarget);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set an event name as being disabled, and provide an optional error message
|
||||
* to be printed to the console
|
||||
* @param {String} eventName - Name of the event
|
||||
* @param {String} [errorMessage] - Optional string to print to the console
|
||||
* @private
|
||||
*/
|
||||
rejectEventHandler(eventName, errorMessage = ''){
|
||||
this._rejectedEventList[eventName] = errorMessage;
|
||||
},
|
||||
|
||||
/**
|
||||
* Explicitly allow an event handler to be added for this event type, undoing
|
||||
* the effects of rejectEventHandler
|
||||
* @param {String} eventName - Name of the event
|
||||
* @private
|
||||
*/
|
||||
allowEventHandler(eventName){
|
||||
delete this._rejectedEventList[eventName];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - full-screen support functions
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -67,10 +67,14 @@
|
|||
return document.fullscreenElement;
|
||||
};
|
||||
fullScreenApi.requestFullScreen = function( element ) {
|
||||
return element.requestFullscreen();
|
||||
return element.requestFullscreen().catch(function (msg) {
|
||||
$.console.error('Fullscreen request failed: ', msg);
|
||||
});
|
||||
};
|
||||
fullScreenApi.exitFullScreen = function() {
|
||||
document.exitFullscreen();
|
||||
document.exitFullscreen().catch(function (msg) {
|
||||
$.console.error('Error while exiting fullscreen: ', msg);
|
||||
});
|
||||
};
|
||||
fullScreenApi.fullScreenEventName = "fullscreenchange";
|
||||
fullScreenApi.fullScreenErrorEventName = "fullscreenerror";
|
||||
|
|
288
src/htmldrawer.js
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* OpenSeadragon - HTMLDrawer
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
const OpenSeadragon = $; // alias back for JSDoc
|
||||
|
||||
/**
|
||||
* @class OpenSeadragon.HTMLDrawer
|
||||
* @extends OpenSeadragon.DrawerBase
|
||||
* @classdesc HTML-based implementation of DrawerBase for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Drawer.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.
|
||||
* @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.
|
||||
* @param {Element} options.element - Parent element.
|
||||
* @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.
|
||||
*/
|
||||
|
||||
class HTMLDrawer extends OpenSeadragon.DrawerBase{
|
||||
constructor(options){
|
||||
super(options);
|
||||
|
||||
/**
|
||||
* The HTML element (div) that this drawer uses for drawing
|
||||
* @member {Element} canvas
|
||||
* @memberof OpenSeadragon.HTMLDrawer#
|
||||
*/
|
||||
|
||||
/**
|
||||
* The parent element of this Drawer instance, passed in when the Drawer was created.
|
||||
* The parent of {@link OpenSeadragon.WebGLDrawer#canvas}.
|
||||
* @member {Element} container
|
||||
* @memberof OpenSeadragon.HTMLDrawer#
|
||||
*/
|
||||
|
||||
// Reject listening for the tile-drawing event, which this drawer does not fire
|
||||
this.viewer.rejectEventHandler("tile-drawing", "The HTMLDrawer does not raise the tile-drawing event");
|
||||
// Since the tile-drawn event is fired by this drawer, make sure handlers can be added for it
|
||||
this.viewer.allowEventHandler("tile-drawn");
|
||||
|
||||
// works with canvas & image objects
|
||||
function _prepareTile(tile, data) {
|
||||
const element = $.makeNeutralElement( "div" );
|
||||
const imgElement = data.cloneNode();
|
||||
imgElement.style.msInterpolationMode = "nearest-neighbor";
|
||||
imgElement.style.width = "100%";
|
||||
imgElement.style.height = "100%";
|
||||
|
||||
const style = element.style;
|
||||
style.position = "absolute";
|
||||
|
||||
return {
|
||||
element, imgElement, style, data
|
||||
};
|
||||
}
|
||||
|
||||
// The actual placing logics will not happen at draw event, but when the cache is created:
|
||||
$.convertor.learn("context2d", HTMLDrawer.canvasCacheType, (t, d) => _prepareTile(t, d.canvas), 1, 1);
|
||||
$.convertor.learn("image", HTMLDrawer.imageCacheType, _prepareTile, 1, 1);
|
||||
// Also learn how to move back, since these elements can be just used as-is
|
||||
$.convertor.learn(HTMLDrawer.canvasCacheType, "context2d", (t, d) => d.data.getContext('2d'), 1, 3);
|
||||
$.convertor.learn(HTMLDrawer.imageCacheType, "image", (t, d) => d.data, 1, 3);
|
||||
|
||||
function _freeTile(data) {
|
||||
if ( data.imgElement && data.imgElement.parentNode ) {
|
||||
data.imgElement.parentNode.removeChild( data.imgElement );
|
||||
}
|
||||
if ( data.element && data.element.parentNode ) {
|
||||
data.element.parentNode.removeChild( data.element );
|
||||
}
|
||||
}
|
||||
|
||||
$.convertor.learnDestroy(HTMLDrawer.canvasCacheType, _freeTile);
|
||||
$.convertor.learnDestroy(HTMLDrawer.imageCacheType, _freeTile);
|
||||
}
|
||||
|
||||
static get imageCacheType() {
|
||||
return 'htmlDrawer[image]';
|
||||
}
|
||||
|
||||
static get canvasCacheType() {
|
||||
return 'htmlDrawer[canvas]';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} always true
|
||||
*/
|
||||
static isSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns 'html'
|
||||
*/
|
||||
getType(){
|
||||
return 'html';
|
||||
}
|
||||
|
||||
getSupportedDataFormats() {
|
||||
return [HTMLDrawer.imageCacheType, HTMLDrawer.canvasCacheType];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TiledImage} tiledImage the tiled image that is calling the function
|
||||
* @returns {Boolean} Whether this drawer requires enforcing minimum tile overlap to avoid showing seams.
|
||||
* @private
|
||||
*/
|
||||
minimumOverlapRequired(tiledImage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the HTML element (e.g. canvas, div) that the image will be drawn into
|
||||
* @returns {Element} the div to draw into
|
||||
*/
|
||||
_createDrawingElement(){
|
||||
return $.makeNeutralElement("div");
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the TiledImages
|
||||
*/
|
||||
draw(tiledImages) {
|
||||
var _this = this;
|
||||
this._prepareNewFrame(); // prepare to draw a new frame
|
||||
tiledImages.forEach(function(tiledImage){
|
||||
if (tiledImage.opacity !== 0) {
|
||||
_this._drawTiles(tiledImage);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} False - rotation is not supported.
|
||||
*/
|
||||
canRotate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the drawer (unload current loaded tiles)
|
||||
*/
|
||||
destroy() {
|
||||
this.container.removeChild(this.canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is ignored by the HTML Drawer. Implementing it is required by DrawerBase.
|
||||
* @param {Boolean} [imageSmoothingEnabled] - Whether or not the image is
|
||||
* drawn smoothly on the canvas; see imageSmoothingEnabled in
|
||||
* {@link OpenSeadragon.Options} for more explanation.
|
||||
*/
|
||||
setImageSmoothingEnabled(){
|
||||
// noop - HTML Drawer does not deal with this property
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the Drawer so it's ready to draw another frame.
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
_prepareNewFrame() {
|
||||
this.canvas.innerHTML = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a TiledImage.
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
_drawTiles( tiledImage ) {
|
||||
var lastDrawn = tiledImage.getTilesToDraw().map(info => info.tile);
|
||||
if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over the tiles to draw, and draw them
|
||||
for (var i = lastDrawn.length - 1; i >= 0; i--) {
|
||||
var tile = lastDrawn[ i ];
|
||||
this._drawTile( tile );
|
||||
|
||||
if( this.viewer ){
|
||||
/**
|
||||
* Raised when a tile is drawn to the canvas. Only valid for
|
||||
* context2d and html drawers.
|
||||
*
|
||||
* @event tile-drawn
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
|
||||
* @property {OpenSeadragon.Tile} tile
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.viewer.raiseEvent( 'tile-drawn', {
|
||||
tiledImage: tiledImage,
|
||||
tile: tile
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the given tile.
|
||||
* @private
|
||||
* @param {OpenSeadragon.Tile} tile - The tile to draw.
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event if using canvas.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
*/
|
||||
_drawTile( tile ) {
|
||||
$.console.assert(tile, '[Drawer._drawTile] tile is required');
|
||||
|
||||
let container = this.canvas;
|
||||
|
||||
if ( !tile.loaded ) {
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
tile.toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//EXPERIMENTAL - trying to figure out how to scale the container
|
||||
// content during animation of the container size.
|
||||
|
||||
const dataObject = this.getDataToDraw(tile);
|
||||
if (!dataObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( dataObject.element.parentNode !== container ) {
|
||||
container.appendChild( dataObject.element );
|
||||
}
|
||||
if ( dataObject.imgElement.parentNode !== dataObject.element ) {
|
||||
dataObject.element.appendChild( dataObject.imgElement );
|
||||
}
|
||||
|
||||
dataObject.style.top = tile.position.y + "px";
|
||||
dataObject.style.left = tile.position.x + "px";
|
||||
dataObject.style.height = tile.size.y + "px";
|
||||
dataObject.style.width = tile.size.x + "px";
|
||||
|
||||
if (tile.flipped) {
|
||||
dataObject.style.transform = "scaleX(-1)";
|
||||
}
|
||||
|
||||
$.setElementOpacity( dataObject.element, tile.opacity );
|
||||
}
|
||||
}
|
||||
|
||||
$.HTMLDrawer = HTMLDrawer;
|
||||
|
||||
|
||||
}( OpenSeadragon ));
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - IIIFTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -36,24 +36,34 @@
|
|||
|
||||
/**
|
||||
* @class IIIFTileSource
|
||||
* @classdesc A client implementation of the International Image Interoperability
|
||||
* Format: Image API 1.0 - 2.0
|
||||
* @classdesc A client implementation of the International Image Interoperability Framework
|
||||
* Format: Image API 1.0 - 2.1
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.TileSource
|
||||
* @see http://iiif.io/api/image/
|
||||
* @param {String} [options.tileFormat='jpg']
|
||||
* The extension that will be used when requiring tiles.
|
||||
*/
|
||||
$.IIIFTileSource = function( options ){
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
$.extend( true, this, options );
|
||||
|
||||
if ( !( this.height && this.width && this['@id'] ) ) {
|
||||
throw new Error( 'IIIF required parameters not provided.' );
|
||||
/* Normalizes v3-style 'id' keys to an "_id" internal property */
|
||||
this._id = this["@id"] || this["id"] || this['identifier'] || null;
|
||||
|
||||
if ( !( this.height && this.width && this._id) ) {
|
||||
throw new Error( 'IIIF required parameters (width, height, or id) not provided.' );
|
||||
}
|
||||
|
||||
options.tileSizePerScaleFactor = {};
|
||||
|
||||
this.tileFormat = this.tileFormat || 'jpg';
|
||||
|
||||
this.version = options.version;
|
||||
|
||||
// N.B. 2.0 renamed scale_factors to scaleFactors
|
||||
if ( this.tile_width && this.tile_height ) {
|
||||
options.tileWidth = this.tile_width;
|
||||
|
@ -64,7 +74,7 @@ $.IIIFTileSource = function( options ){
|
|||
options.tileSize = this.tile_height;
|
||||
} else if ( this.tiles ) {
|
||||
// Version 2.0 forwards
|
||||
if ( this.tiles.length == 1 ) {
|
||||
if ( this.tiles.length === 1 ) {
|
||||
options.tileWidth = this.tiles[0].width;
|
||||
// Use height if provided, otherwise assume square tiles and use width.
|
||||
options.tileHeight = this.tiles[0].height || this.tiles[0].width;
|
||||
|
@ -83,10 +93,10 @@ $.IIIFTileSource = function( options ){
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if ( canBeTiled(options) ) {
|
||||
// use the largest of tileOptions that is smaller than the short dimension
|
||||
var shortDim = Math.min( this.height, this.width ),
|
||||
tileOptions = [256,512,1024],
|
||||
tileOptions = [256, 512, 1024],
|
||||
smallerTiles = [];
|
||||
|
||||
for ( var c = 0; c < tileOptions.length; c++ ) {
|
||||
|
@ -101,13 +111,45 @@ $.IIIFTileSource = function( options ){
|
|||
// If we're smaller than 256, just use the short side.
|
||||
options.tileSize = shortDim;
|
||||
}
|
||||
} else if (this.sizes && this.sizes.length > 0) {
|
||||
// This info.json can't be tiled, but we can still construct a legacy pyramid from the sizes array.
|
||||
// In this mode, IIIFTileSource will call functions from the abstract baseTileSource or the
|
||||
// LegacyTileSource instead of performing IIIF tiling.
|
||||
this.emulateLegacyImagePyramid = true;
|
||||
|
||||
options.levels = constructLevels( this );
|
||||
// use the largest available size to define tiles
|
||||
$.extend( true, options, {
|
||||
width: options.levels[ options.levels.length - 1 ].width,
|
||||
height: options.levels[ options.levels.length - 1 ].height,
|
||||
tileSize: Math.max( options.height, options.width ),
|
||||
tileOverlap: 0,
|
||||
minLevel: 0,
|
||||
maxLevel: options.levels.length - 1
|
||||
});
|
||||
this.levels = options.levels;
|
||||
} else {
|
||||
$.console.error("Nothing in the info.json to construct image pyramids from");
|
||||
}
|
||||
|
||||
if ( !options.maxLevel ) {
|
||||
if ( !this.scale_factors ) {
|
||||
options.maxLevel = Number( Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) ) );
|
||||
if (!options.maxLevel && !this.emulateLegacyImagePyramid) {
|
||||
if (!this.scale_factors) {
|
||||
options.maxLevel = Number(Math.round(Math.log(Math.max(this.width, this.height), 2)));
|
||||
} else {
|
||||
options.maxLevel = Math.floor( Math.pow( Math.max.apply(null, this.scale_factors), 0.5) );
|
||||
var maxScaleFactor = Math.max.apply(null, this.scale_factors);
|
||||
options.maxLevel = Math.round(Math.log(maxScaleFactor) * Math.LOG2E);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with our exact resolution sizes if these have been supplied
|
||||
if( this.sizes ) {
|
||||
var sizeLength = this.sizes.length;
|
||||
if ( (sizeLength === options.maxLevel) || (sizeLength === options.maxLevel + 1) ) {
|
||||
this.levelSizes = this.sizes.slice().sort(( size1, size2 ) => size1.width - size2.width);
|
||||
// Need to take into account that the list may or may not include the full resolution size
|
||||
if( sizeLength === options.maxLevel ) {
|
||||
this.levelSizes.push( {width: this.width, height: this.height} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,17 +162,17 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
* this tile source.
|
||||
* @function
|
||||
* @param {Object|Array} data
|
||||
* @param {String} optional - url
|
||||
* @param {String} [url] - url
|
||||
*/
|
||||
|
||||
|
||||
supports: function( data, url ) {
|
||||
// Version 2.0 and forwards
|
||||
if (data.protocol && data.protocol == 'http://iiif.io/api/image') {
|
||||
if (data.protocol && data.protocol === 'http://iiif.io/api/image') {
|
||||
return true;
|
||||
// Version 1.1
|
||||
} else if ( data['@context'] && (
|
||||
data['@context'] == "http://library.stanford.edu/iiif/image-api/1.1/context.json" ||
|
||||
data['@context'] == "http://iiif.io/api/image/1/context.json") ) {
|
||||
data['@context'] === "http://library.stanford.edu/iiif/image-api/1.1/context.json" ||
|
||||
data['@context'] === "http://iiif.io/api/image/1/context.json") ) {
|
||||
// N.B. the iiif.io context is wrong, but where the representation lives so likely to be used
|
||||
return true;
|
||||
|
||||
|
@ -141,8 +183,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
} else if ( data.identifier && data.width && data.height ) {
|
||||
return true;
|
||||
} else if ( data.documentElement &&
|
||||
"info" == data.documentElement.tagName &&
|
||||
"http://library.stanford.edu/iiif/image-api/ns/" ==
|
||||
"info" === data.documentElement.tagName &&
|
||||
"http://library.stanford.edu/iiif/image-api/ns/" ===
|
||||
data.documentElement.namespaceURI) {
|
||||
return true;
|
||||
|
||||
|
@ -153,35 +195,80 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
},
|
||||
|
||||
/**
|
||||
* A static function used to prepare an incoming IIIF Image API info.json
|
||||
* response for processing by the tile handler. Normalizes data for all
|
||||
* versions of IIIF (1.0, 1.1, 2.x, 3.x) and returns a data object that
|
||||
* may be passed to the IIIFTileSource.
|
||||
*
|
||||
* @function
|
||||
* @static
|
||||
* @param {Object} data - the raw configuration
|
||||
* @example <caption>IIIF 1.1 Info Looks like this</caption>
|
||||
* @param {String} url - the url configuration was retrieved from
|
||||
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
|
||||
* @returns {Object} A normalized IIIF data object
|
||||
* @example <caption>IIIF 2.x Info Looks like this</caption>
|
||||
* {
|
||||
* "@context" : "http://library.stanford.edu/iiif/image-api/1.1/context.json",
|
||||
* "@id" : "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C",
|
||||
* "width" : 6000,
|
||||
* "height" : 4000,
|
||||
* "scale_factors" : [ 1, 2, 4 ],
|
||||
* "tile_width" : 1024,
|
||||
* "tile_height" : 1024,
|
||||
* "formats" : [ "jpg", "png" ],
|
||||
* "qualities" : [ "native", "grey" ],
|
||||
* "profile" : "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0"
|
||||
* "@context": "http://iiif.io/api/image/2/context.json",
|
||||
* "@id": "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C",
|
||||
* "protocol": "http://iiif.io/api/image",
|
||||
* "height": 1024,
|
||||
* "width": 775,
|
||||
* "tiles" : [{"width":256, "scaleFactors":[1,2,4,8]}],
|
||||
* "profile": ["http://iiif.io/api/image/2/level1.json", {
|
||||
* "qualities": [ "native", "bitonal", "grey", "color" ],
|
||||
* "formats": [ "jpg", "png", "gif" ]
|
||||
* }]
|
||||
* }
|
||||
*/
|
||||
configure: function( data, url ){
|
||||
configure: function( data, url, postData ){
|
||||
// Try to deduce our version and fake it upwards if needed
|
||||
if ( !$.isPlainObject(data) ) {
|
||||
var options = configureFromXml10( data );
|
||||
options['@context'] = "http://iiif.io/api/image/1.0/context.json";
|
||||
options['@id'] = url.replace('/info.xml', '');
|
||||
options["@id"] = url.replace('/info.xml', '');
|
||||
options.version = 1;
|
||||
return options;
|
||||
} else if ( !data['@context'] ) {
|
||||
data['@context'] = 'http://iiif.io/api/image/1.0/context.json';
|
||||
data['@id'] = url.replace('/info.json', '');
|
||||
return data;
|
||||
} else {
|
||||
if ( !data['@context'] ) {
|
||||
data['@context'] = 'http://iiif.io/api/image/1.0/context.json';
|
||||
data["@id"] = url.replace('/info.json', '');
|
||||
data.version = 1;
|
||||
} else {
|
||||
var context = data['@context'];
|
||||
if (Array.isArray(context)) {
|
||||
for (var i = 0; i < context.length; i++) {
|
||||
if (typeof context[i] === 'string' &&
|
||||
( /^http:\/\/iiif\.io\/api\/image\/[1-3]\/context\.json$/.test(context[i]) ||
|
||||
context[i] === 'http://library.stanford.edu/iiif/image-api/1.1/context.json' ) ) {
|
||||
context = context[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (context) {
|
||||
case 'http://iiif.io/api/image/1/context.json':
|
||||
case 'http://library.stanford.edu/iiif/image-api/1.1/context.json':
|
||||
data.version = 1;
|
||||
break;
|
||||
case 'http://iiif.io/api/image/2/context.json':
|
||||
data.version = 2;
|
||||
break;
|
||||
case 'http://iiif.io/api/image/3/context.json':
|
||||
data.version = 3;
|
||||
break;
|
||||
default:
|
||||
$.console.error('Data has a @context property which contains no known IIIF context URI.');
|
||||
}
|
||||
}
|
||||
|
||||
if (data.preferredFormats) {
|
||||
for (var f = 0; f < data.preferredFormats.length; f++ ) {
|
||||
if ( $.imageFormatSupported(data.preferredFormats[f]) ) {
|
||||
data.tileFormat = data.preferredFormats[f];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
|
@ -192,6 +279,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
* @param {Number} level
|
||||
*/
|
||||
getTileWidth: function( level ) {
|
||||
|
||||
if(this.emulateLegacyImagePyramid) {
|
||||
return $.TileSource.prototype.getTileWidth.call(this, level);
|
||||
}
|
||||
|
||||
var scaleFactor = Math.pow(2, this.maxLevel - level);
|
||||
|
||||
if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) {
|
||||
|
@ -206,6 +298,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
* @param {Number} level
|
||||
*/
|
||||
getTileHeight: function( level ) {
|
||||
|
||||
if(this.emulateLegacyImagePyramid) {
|
||||
return $.TileSource.prototype.getTileHeight.call(this, level);
|
||||
}
|
||||
|
||||
var scaleFactor = Math.pow(2, this.maxLevel - level);
|
||||
|
||||
if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) {
|
||||
|
@ -214,9 +311,99 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
return this._tileHeight;
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getLevelScale: function ( level ) {
|
||||
|
||||
if(this.emulateLegacyImagePyramid) {
|
||||
var levelScale = NaN;
|
||||
if (this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel) {
|
||||
levelScale =
|
||||
this.levels[level].width /
|
||||
this.levels[this.maxLevel].width;
|
||||
}
|
||||
return levelScale;
|
||||
}
|
||||
|
||||
return $.TileSource.prototype.getLevelScale.call(this, level);
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for retreiving the url which will return an image for the
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getNumTiles: function( level ) {
|
||||
|
||||
if(this.emulateLegacyImagePyramid) {
|
||||
var scale = this.getLevelScale(level);
|
||||
if (scale) {
|
||||
return new $.Point(1, 1);
|
||||
} else {
|
||||
return new $.Point(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Use supplied list of scaled resolution sizes if these exist
|
||||
if( this.levelSizes ) {
|
||||
var levelSize = this.levelSizes[level];
|
||||
var x = Math.ceil( levelSize.width / this.getTileWidth(level) ),
|
||||
y = Math.ceil( levelSize.height / this.getTileHeight(level) );
|
||||
return new $.Point( x, y );
|
||||
}
|
||||
// Otherwise call default TileSource->getNumTiles() function
|
||||
else {
|
||||
return $.TileSource.prototype.getNumTiles.call(this, level);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {OpenSeadragon.Point} point
|
||||
*/
|
||||
getTileAtPoint: function( level, point ) {
|
||||
|
||||
if(this.emulateLegacyImagePyramid) {
|
||||
return new $.Point(0, 0);
|
||||
}
|
||||
|
||||
// Use supplied list of scaled resolution sizes if these exist
|
||||
if( this.levelSizes ) {
|
||||
|
||||
var validPoint = point.x >= 0 && point.x <= 1 &&
|
||||
point.y >= 0 && point.y <= 1 / this.aspectRatio;
|
||||
$.console.assert(validPoint, "[TileSource.getTileAtPoint] must be called with a valid point.");
|
||||
|
||||
var widthScaled = this.levelSizes[level].width;
|
||||
var pixelX = point.x * widthScaled;
|
||||
var pixelY = point.y * widthScaled;
|
||||
|
||||
var x = Math.floor(pixelX / this.getTileWidth(level));
|
||||
var y = Math.floor(pixelY / this.getTileHeight(level));
|
||||
|
||||
// When point.x == 1 or point.y == 1 / this.aspectRatio we want to
|
||||
// return the last tile of the row/column
|
||||
if (point.x >= 1) {
|
||||
x = this.getNumTiles(level).x - 1;
|
||||
}
|
||||
var EPSILON = 1e-15;
|
||||
if (point.y >= 1 / this.aspectRatio - EPSILON) {
|
||||
y = this.getNumTiles(level).y - 1;
|
||||
}
|
||||
|
||||
return new $.Point(x, y);
|
||||
}
|
||||
|
||||
// Otherwise call default TileSource->getTileAtPoint() function
|
||||
return $.TileSource.prototype.getTileAtPoint.call(this, level, point);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Responsible for retrieving the url which will return an image for the
|
||||
* region specified by the given x, y, and level components.
|
||||
* @function
|
||||
* @param {Number} level - z index
|
||||
|
@ -226,14 +413,21 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
*/
|
||||
getTileUrl: function( level, x, y ){
|
||||
|
||||
if(this.emulateLegacyImagePyramid) {
|
||||
var url = null;
|
||||
if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
|
||||
url = this.levels[ level ].url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
//# constants
|
||||
var IIIF_ROTATION = '0',
|
||||
//## get the scale (level as a decimal)
|
||||
scale = Math.pow( 0.5, this.maxLevel - level ),
|
||||
|
||||
//# image dimensions at this level
|
||||
levelWidth = Math.ceil( this.width * scale ),
|
||||
levelHeight = Math.ceil( this.height * scale ),
|
||||
levelWidth,
|
||||
levelHeight,
|
||||
|
||||
//## iiif region
|
||||
tileWidth,
|
||||
|
@ -246,40 +440,134 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
iiifTileW,
|
||||
iiifTileH,
|
||||
iiifSize,
|
||||
iiifSizeW,
|
||||
iiifSizeH,
|
||||
iiifQuality,
|
||||
uri;
|
||||
|
||||
tileWidth = this.getTileWidth(level);
|
||||
tileHeight = this.getTileHeight(level);
|
||||
iiifTileSizeWidth = Math.ceil( tileWidth / scale );
|
||||
iiifTileSizeHeight = Math.ceil( tileHeight / scale );
|
||||
|
||||
if ( this['@context'].indexOf('/1.0/context.json') > -1 ||
|
||||
this['@context'].indexOf('/1.1/context.json') > -1 ||
|
||||
this['@context'].indexOf('/1/context.json') > -1 ) {
|
||||
iiifQuality = "native.jpg";
|
||||
} else {
|
||||
iiifQuality = "default.jpg";
|
||||
// Use supplied list of scaled resolution sizes if these exist
|
||||
if( this.levelSizes ) {
|
||||
levelWidth = this.levelSizes[level].width;
|
||||
levelHeight = this.levelSizes[level].height;
|
||||
}
|
||||
// Otherwise calculate the sizes ourselves
|
||||
else {
|
||||
levelWidth = Math.ceil( this.width * scale );
|
||||
levelHeight = Math.ceil( this.height * scale );
|
||||
}
|
||||
|
||||
tileWidth = this.getTileWidth(level);
|
||||
tileHeight = this.getTileHeight(level);
|
||||
iiifTileSizeWidth = Math.round( tileWidth / scale );
|
||||
iiifTileSizeHeight = Math.round( tileHeight / scale );
|
||||
if (this.version === 1) {
|
||||
iiifQuality = "native." + this.tileFormat;
|
||||
} else {
|
||||
iiifQuality = "default." + this.tileFormat;
|
||||
}
|
||||
if ( levelWidth < tileWidth && levelHeight < tileHeight ){
|
||||
iiifSize = levelWidth + ",";
|
||||
if ( this.version === 2 && levelWidth === this.width ) {
|
||||
iiifSize = "full";
|
||||
} else if ( this.version === 3 && levelWidth === this.width && levelHeight === this.height ) {
|
||||
iiifSize = "max";
|
||||
} else if ( this.version === 3 ) {
|
||||
iiifSize = levelWidth + "," + levelHeight;
|
||||
} else {
|
||||
iiifSize = levelWidth + ",";
|
||||
}
|
||||
iiifRegion = 'full';
|
||||
} else {
|
||||
iiifTileX = x * iiifTileSizeWidth;
|
||||
iiifTileY = y * iiifTileSizeHeight;
|
||||
iiifTileW = Math.min( iiifTileSizeWidth, this.width - iiifTileX );
|
||||
iiifTileH = Math.min( iiifTileSizeHeight, this.height - iiifTileY );
|
||||
iiifSize = Math.ceil( iiifTileW * scale ) + ",";
|
||||
iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
|
||||
if ( x === 0 && y === 0 && iiifTileW === this.width && iiifTileH === this.height ) {
|
||||
iiifRegion = "full";
|
||||
} else {
|
||||
iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
|
||||
}
|
||||
iiifSizeW = Math.min( tileWidth, levelWidth - (x * tileWidth) );
|
||||
iiifSizeH = Math.min( tileHeight, levelHeight - (y * tileHeight) );
|
||||
if ( this.version === 2 && iiifSizeW === this.width ) {
|
||||
iiifSize = "full";
|
||||
} else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) {
|
||||
iiifSize = "max";
|
||||
} else if (this.version === 3) {
|
||||
iiifSize = iiifSizeW + "," + iiifSizeH;
|
||||
} else {
|
||||
iiifSize = iiifSizeW + ",";
|
||||
}
|
||||
}
|
||||
uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' );
|
||||
uri = [ this._id, iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' );
|
||||
|
||||
return uri;
|
||||
},
|
||||
|
||||
/**
|
||||
* Equality comparator
|
||||
*/
|
||||
equals: function(otherSource) {
|
||||
return this._id === otherSource._id;
|
||||
},
|
||||
|
||||
__testonly__: {
|
||||
canBeTiled: canBeTiled,
|
||||
constructLevels: constructLevels
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Determine whether arbitrary tile requests can be made against a service with the given profile
|
||||
* @function
|
||||
* @param {Object} options
|
||||
* @param {Array|String} options.profile
|
||||
* @param {Number} options.version
|
||||
* @param {String[]} options.extraFeatures
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function canBeTiled ( options ) {
|
||||
var level0Profiles = [
|
||||
"http://library.stanford.edu/iiif/image-api/compliance.html#level0",
|
||||
"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0",
|
||||
"http://iiif.io/api/image/2/level0.json",
|
||||
"level0",
|
||||
"https://iiif.io/api/image/3/level0.json"
|
||||
];
|
||||
var profileLevel = Array.isArray(options.profile) ? options.profile[0] : options.profile;
|
||||
var isLevel0 = (level0Profiles.indexOf(profileLevel) !== -1);
|
||||
var hasCanoncicalSizeFeature = false;
|
||||
if ( options.version === 2 && options.profile.length > 1 && options.profile[1].supports ) {
|
||||
hasCanoncicalSizeFeature = options.profile[1].supports.indexOf( "sizeByW" ) !== -1;
|
||||
}
|
||||
if ( options.version === 3 && options.extraFeatures ) {
|
||||
hasCanoncicalSizeFeature = options.extraFeatures.indexOf( "sizeByWh" ) !== -1;
|
||||
}
|
||||
return !isLevel0 || hasCanoncicalSizeFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the legacy pyramid URLs (one tile per level)
|
||||
* @function
|
||||
* @param {object} options - infoJson
|
||||
* @throws {Error}
|
||||
*/
|
||||
function constructLevels(options) {
|
||||
var levels = [];
|
||||
for(var i = 0; i < options.sizes.length; i++) {
|
||||
levels.push({
|
||||
url: options._id + '/full/' + options.sizes[i].width + ',' +
|
||||
(options.version === 3 ? options.sizes[i].height : '') +
|
||||
'/0/default.' + options.tileFormat,
|
||||
width: options.sizes[i].width,
|
||||
height: options.sizes[i].height
|
||||
});
|
||||
}
|
||||
return levels.sort(function(a, b) {
|
||||
return a.width - b.width;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function configureFromXml10(xmlDoc) {
|
||||
//parse the xml
|
||||
|
@ -291,7 +579,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
rootName = root.tagName,
|
||||
configuration = null;
|
||||
|
||||
if ( rootName == "info" ) {
|
||||
if ( rootName === "info" ) {
|
||||
try {
|
||||
configuration = {};
|
||||
parseXML10( root, configuration );
|
||||
|
@ -309,7 +597,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
function parseXML10( node, configuration, property ) {
|
||||
var i,
|
||||
value;
|
||||
if ( node.nodeType == 3 && property ) {//text node
|
||||
if ( node.nodeType === 3 && property ) {//text node
|
||||
value = node.nodeValue.trim();
|
||||
if( value.match(/^\d*$/)){
|
||||
value = Number( value );
|
||||
|
@ -322,7 +610,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
}
|
||||
configuration[ property ].push( value );
|
||||
}
|
||||
} else if( node.nodeType == 1 ){
|
||||
} else if( node.nodeType === 1 ){
|
||||
for( i = 0; i < node.childNodes.length; i++ ){
|
||||
parseXML10( node.childNodes[ i ], configuration, node.nodeName );
|
||||
}
|
||||
|
@ -330,4 +618,5 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
|
|||
}
|
||||
|
||||
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - ImageLoader
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -32,64 +32,133 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
(function($){
|
||||
|
||||
// private class
|
||||
function ImageJob ( options ) {
|
||||
/**
|
||||
* @class ImageJob
|
||||
* @classdesc Handles downloading of a single image.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @param {Object} options - Options for this ImageJob.
|
||||
* @param {String} [options.src] - URL of image to download.
|
||||
* @param {Tile} [options.tile] - Tile that belongs the data to.
|
||||
* @param {TileSource} [options.source] - Image loading strategy
|
||||
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
||||
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
|
||||
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
|
||||
* @param {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
||||
* see TileSource::getTilePostData) or null
|
||||
* @param {Function} [options.callback] - Called once image has been downloaded.
|
||||
* @param {Function} [options.abort] - Called when this image job is aborted.
|
||||
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
|
||||
* @param {Number} [options.tries] - Actual number of the current try.
|
||||
*/
|
||||
$.ImageJob = function(options) {
|
||||
|
||||
$.extend( true, this, {
|
||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||
jobId: null
|
||||
}, options );
|
||||
$.extend(true, this, {
|
||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||
jobId: null,
|
||||
tries: 0
|
||||
}, options);
|
||||
|
||||
/**
|
||||
* Image object which will contain downloaded image.
|
||||
* @member {Image} image
|
||||
* Data object which will contain downloaded image data.
|
||||
* @member {Image|*} data data object, by default an Image object (depends on TileSource)
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
this.image = null;
|
||||
}
|
||||
this.data = null;
|
||||
|
||||
ImageJob.prototype = {
|
||||
errorMsg: null,
|
||||
start: function(){
|
||||
var _this = this;
|
||||
/**
|
||||
* User workspace to populate with helper variables
|
||||
* @member {*} userData to append custom data and avoid namespace collision
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
this.userData = {};
|
||||
|
||||
this.image = new Image();
|
||||
/**
|
||||
* Error message holder
|
||||
* @member {string} error message
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
* @private
|
||||
*/
|
||||
this.errorMsg = null;
|
||||
};
|
||||
|
||||
if ( this.crossOriginPolicy !== false ) {
|
||||
this.image.crossOrigin = this.crossOriginPolicy;
|
||||
}
|
||||
$.ImageJob.prototype = {
|
||||
/**
|
||||
* Starts the image job.
|
||||
* @method
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
start: function() {
|
||||
this.tries++;
|
||||
|
||||
this.image.onload = function(){
|
||||
_this.finish( true );
|
||||
};
|
||||
this.image.onabort = this.image.onerror = function(){
|
||||
_this.errorMsg = "Image load aborted";
|
||||
_this.finish( false );
|
||||
};
|
||||
var self = this;
|
||||
var selfAbort = this.abort;
|
||||
|
||||
this.jobId = window.setTimeout( function(){
|
||||
_this.errorMsg = "Image load exceeded timeout";
|
||||
_this.finish( false );
|
||||
this.jobId = window.setTimeout(function () {
|
||||
self.fail("Image load exceeded timeout (" + self.timeout + " ms)", null);
|
||||
}, this.timeout);
|
||||
|
||||
this.image.src = this.src;
|
||||
this.abort = function() {
|
||||
self.source.downloadTileAbort(self);
|
||||
if (typeof selfAbort === "function") {
|
||||
selfAbort();
|
||||
}
|
||||
};
|
||||
|
||||
this.source.downloadTileStart(this);
|
||||
},
|
||||
|
||||
finish: function( successful ) {
|
||||
this.image.onload = this.image.onerror = this.image.onabort = null;
|
||||
if (!successful) {
|
||||
this.image = null;
|
||||
/**
|
||||
* Finish this job.
|
||||
* @param {*} data data that has been downloaded
|
||||
* @param {XMLHttpRequest} request reference to the request if used
|
||||
* @param {string} dataType data type identifier
|
||||
* fallback compatibility behavior: dataType treated as errorMessage if data is falsey value
|
||||
* @memberof OpenSeadragon.ImageJob#
|
||||
*/
|
||||
finish: function(data, request, dataType) {
|
||||
if (!this.jobId) {
|
||||
return;
|
||||
}
|
||||
// old behavior, no deprecation due to possible finish calls with invalid data item (e.g. different error)
|
||||
if (data === null || data === undefined || data === false) {
|
||||
this.fail(dataType || "[downloadTileStart->finish()] Retrieved data is invalid!", request);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this.jobId ) {
|
||||
window.clearTimeout( this.jobId );
|
||||
this.data = data;
|
||||
this.request = request;
|
||||
this.errorMsg = null;
|
||||
this.dataType = dataType;
|
||||
|
||||
if (this.jobId) {
|
||||
window.clearTimeout(this.jobId);
|
||||
}
|
||||
|
||||
this.callback( this );
|
||||
this.callback(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finish this job as a failure.
|
||||
* @param {string} errorMessage description upon failure
|
||||
* @param {XMLHttpRequest} request reference to the request if used
|
||||
*/
|
||||
fail: function(errorMessage, request) {
|
||||
this.data = null;
|
||||
this.request = request;
|
||||
this.errorMsg = errorMessage;
|
||||
this.dataType = null;
|
||||
|
||||
if (this.jobId) {
|
||||
window.clearTimeout(this.jobId);
|
||||
this.jobId = null;
|
||||
}
|
||||
|
||||
this.callback(this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -99,46 +168,83 @@ ImageJob.prototype = {
|
|||
* You generally won't have to interact with the ImageLoader directly.
|
||||
* @param {Object} options - Options for this ImageLoader.
|
||||
* @param {Number} [options.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.
|
||||
* @param {Number} [options.timeout] - The max number of milliseconds that an image job may take to complete.
|
||||
*/
|
||||
$.ImageLoader = function( options ) {
|
||||
$.ImageLoader = function(options) {
|
||||
|
||||
$.extend( true, this, {
|
||||
$.extend(true, this, {
|
||||
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||
jobQueue: [],
|
||||
failedTiles: [],
|
||||
jobsInProgress: 0
|
||||
}, options );
|
||||
}, options);
|
||||
|
||||
};
|
||||
|
||||
$.ImageLoader.prototype = /** @lends OpenSeadragon.ImageLoader.prototype */{
|
||||
/** @lends OpenSeadragon.ImageLoader.prototype */
|
||||
$.ImageLoader.prototype = {
|
||||
|
||||
/**
|
||||
* Add an unloaded image to the loader queue.
|
||||
* @method
|
||||
* @param {String} src - URL of image to download.
|
||||
* @param {String} crossOriginPolicy - CORS policy to use for downloads
|
||||
* @param {Function} callback - Called once image has been downloaded.
|
||||
* @param {Object} options - Options for this job.
|
||||
* @param {String} [options.src] - URL of image to download.
|
||||
* @param {Tile} [options.tile] - Tile that belongs the data to. The tile instance
|
||||
* is not internally used and serves for custom TileSources implementations.
|
||||
* @param {TileSource} [options.source] - Image loading strategy
|
||||
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
||||
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
|
||||
* @param {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form,
|
||||
* see TileSource::getTilePostData) or null
|
||||
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
|
||||
* requests.
|
||||
* @param {Function} [options.callback] - Called once image has been downloaded.
|
||||
* @param {Function} [options.abort] - Called when this image job is aborted.
|
||||
* @returns {boolean} true if job was immediatelly started, false if queued
|
||||
*/
|
||||
addJob: function( options ) {
|
||||
var _this = this,
|
||||
complete = function( job ) {
|
||||
completeJob( _this, job, options.callback );
|
||||
},
|
||||
addJob: function(options) {
|
||||
if (!options.source) {
|
||||
$.console.error('ImageLoader.prototype.addJob() requires [options.source]. ' +
|
||||
'TileSource since new API defines how images are fetched. Creating a dummy TileSource.');
|
||||
var implementation = $.TileSource.prototype;
|
||||
options.source = {
|
||||
downloadTileStart: implementation.downloadTileStart,
|
||||
downloadTileAbort: implementation.downloadTileAbort
|
||||
};
|
||||
}
|
||||
|
||||
const _this = this,
|
||||
jobOptions = {
|
||||
src: options.src,
|
||||
tile: options.tile || {},
|
||||
source: options.source,
|
||||
loadWithAjax: options.loadWithAjax,
|
||||
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
|
||||
crossOriginPolicy: options.crossOriginPolicy,
|
||||
callback: complete,
|
||||
abort: options.abort
|
||||
ajaxWithCredentials: options.ajaxWithCredentials,
|
||||
postData: options.postData,
|
||||
callback: (job) => completeJob(_this, job, options.callback),
|
||||
abort: options.abort,
|
||||
timeout: this.timeout
|
||||
},
|
||||
newJob = new ImageJob( jobOptions );
|
||||
newJob = new $.ImageJob(jobOptions);
|
||||
|
||||
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
|
||||
newJob.start();
|
||||
this.jobsInProgress++;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
this.jobQueue.push( newJob );
|
||||
}
|
||||
this.jobQueue.push( newJob );
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if a job can be submitted
|
||||
*/
|
||||
canAcceptNewJob() {
|
||||
return !this.jobLimit || this.jobsInProgress < this.jobLimit;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -158,25 +264,39 @@ $.ImageLoader.prototype = /** @lends OpenSeadragon.ImageLoader.prototype */{
|
|||
};
|
||||
|
||||
/**
|
||||
* Cleans up ImageJob once completed.
|
||||
* Cleans up ImageJob once completed. Restarts job after tileRetryDelay seconds if failed
|
||||
* but max tileRetryMax times
|
||||
* @method
|
||||
* @private
|
||||
* @param loader - ImageLoader used to start job.
|
||||
* @param job - The ImageJob that has completed.
|
||||
* @param callback - Called once cleanup is finished.
|
||||
*/
|
||||
function completeJob( loader, job, callback ) {
|
||||
var nextJob;
|
||||
function completeJob(loader, job, callback) {
|
||||
if (job.errorMsg && job.data === null && job.tries < 1 + loader.tileRetryMax) {
|
||||
loader.failedTiles.push(job);
|
||||
}
|
||||
let nextJob;
|
||||
|
||||
loader.jobsInProgress--;
|
||||
|
||||
if ( (!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
|
||||
if (loader.canAcceptNewJob() && loader.jobQueue.length > 0) {
|
||||
nextJob = loader.jobQueue.shift();
|
||||
nextJob.start();
|
||||
loader.jobsInProgress++;
|
||||
}
|
||||
|
||||
callback( job.image, job.errorMsg );
|
||||
if (loader.tileRetryMax > 0 && loader.jobQueue.length === 0) {
|
||||
if (loader.canAcceptNewJob() && loader.failedTiles.length > 0) {
|
||||
nextJob = loader.failedTiles.shift();
|
||||
setTimeout(function () {
|
||||
nextJob.start();
|
||||
}, loader.tileRetryDelay);
|
||||
loader.jobsInProgress++;
|
||||
}
|
||||
}
|
||||
|
||||
callback(job.data, job.errorMsg, job.request, job.dataType);
|
||||
}
|
||||
|
||||
}( OpenSeadragon ));
|
||||
}(OpenSeadragon));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - ImageTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -31,257 +31,235 @@
|
|||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
/**
|
||||
* @class ImageTileSource
|
||||
* @classdesc The ImageTileSource allows a simple image to be loaded
|
||||
* into an OpenSeadragon Viewer.
|
||||
* There are 2 ways to open an ImageTileSource:
|
||||
* 1. viewer.open({type: 'image', url: fooUrl});
|
||||
* 2. viewer.open(new OpenSeadragon.ImageTileSource({url: fooUrl}));
|
||||
*
|
||||
* With the first syntax, the crossOriginPolicy, ajaxWithCredentials and
|
||||
* useCanvas options are inherited from the viewer if they are not
|
||||
* specified directly in the options object.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.TileSource
|
||||
* @param {Object} options Options object.
|
||||
* @param {String} options.url URL of the image
|
||||
* @param {Boolean} [options.buildPyramid=true] If set to true (default), a
|
||||
* pyramid will be built internally to provide a better downsampling.
|
||||
* @param {String|Boolean} [options.crossOriginPolicy=false] Valid values are
|
||||
* 'Anonymous', 'use-credentials', and false. If false, image requests will
|
||||
* not use CORS preventing internal pyramid building for images from other
|
||||
* domains.
|
||||
* @param {String|Boolean} [options.ajaxWithCredentials=false] Whether to set
|
||||
* the withCredentials XHR flag for AJAX requests (when loading tile sources).
|
||||
* @param {Boolean} [options.useCanvas=true] Set to false to prevent any use
|
||||
* of the canvas API.
|
||||
*/
|
||||
$.ImageTileSource = class extends $.TileSource {
|
||||
|
||||
/**
|
||||
* @class ImageTileSource
|
||||
* @classdesc The ImageTileSource allows a simple image to be loaded
|
||||
* into an OpenSeadragon Viewer.
|
||||
* There are 2 ways to open an ImageTileSource:
|
||||
* 1. viewer.open({type: 'image', url: fooUrl});
|
||||
* 2. viewer.open(new OpenSeadragon.ImageTileSource({url: fooUrl}));
|
||||
*
|
||||
* With the first syntax, the crossOriginPolicy, ajaxWithCredentials and
|
||||
* useCanvas options are inherited from the viewer if they are not
|
||||
* specified directly in the options object.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.TileSource
|
||||
* @param {Object} options Options object.
|
||||
* @param {String} options.url URL of the image
|
||||
* @param {Boolean} [options.buildPyramid=true] If set to true (default), a
|
||||
* pyramid will be built internally to provide a better downsampling.
|
||||
* @param {String|Boolean} [options.crossOriginPolicy=false] Valid values are
|
||||
* 'Anonymous', 'use-credentials', and false. If false, image requests will
|
||||
* not use CORS preventing internal pyramid building for images from other
|
||||
* domains.
|
||||
* @param {String|Boolean} [options.ajaxWithCredentials=false] Whether to set
|
||||
* the withCredentials XHR flag for AJAX requests (when loading tile sources).
|
||||
* @param {Boolean} [options.useCanvas=true] Set to false to prevent any use
|
||||
* of the canvas API.
|
||||
*/
|
||||
$.ImageTileSource = function (options) {
|
||||
|
||||
options = $.extend({
|
||||
constructor(props) {
|
||||
super($.extend({
|
||||
buildPyramid: true,
|
||||
crossOriginPolicy: false,
|
||||
ajaxWithCredentials: false,
|
||||
useCanvas: true
|
||||
}, options);
|
||||
$.TileSource.apply(this, [options]);
|
||||
}, props));
|
||||
}
|
||||
|
||||
};
|
||||
/**
|
||||
* Determine if the data and/or url imply the image service is supported by
|
||||
* this tile source.
|
||||
* @function
|
||||
* @param {Object|Array} data
|
||||
* @param {String} url - optional
|
||||
*/
|
||||
supports(data, url) {
|
||||
return data.type && data.type === "image";
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @function
|
||||
* @param {Object} options - the options
|
||||
* @param {String} dataUrl - the url the image was retrieved from, if any.
|
||||
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
|
||||
* @returns {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure this tile sources constructor.
|
||||
*/
|
||||
configure(options, dataUrl, postData) {
|
||||
return options;
|
||||
}
|
||||
/**
|
||||
* Responsible for retrieving, and caching the
|
||||
* image metadata pertinent to this TileSources implementation.
|
||||
* @function
|
||||
* @param {String} url
|
||||
* @throws {Error}
|
||||
*/
|
||||
getImageInfo(url) {
|
||||
const image = new Image(),
|
||||
_this = this;
|
||||
|
||||
$.extend($.ImageTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.ImageTileSource.prototype */{
|
||||
/**
|
||||
* Determine if the data and/or url imply the image service is supported by
|
||||
* this tile source.
|
||||
* @function
|
||||
* @param {Object|Array} data
|
||||
* @param {String} optional - url
|
||||
*/
|
||||
supports: function (data, url) {
|
||||
return data.type && data.type === "image";
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @function
|
||||
* @param {Object} options - the options
|
||||
* @param {String} dataUrl - the url the image was retreived from, if any.
|
||||
* @return {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure this tile sources constructor.
|
||||
*/
|
||||
configure: function (options, dataUrl) {
|
||||
return options;
|
||||
},
|
||||
/**
|
||||
* Responsible for retrieving, and caching the
|
||||
* image metadata pertinent to this TileSources implementation.
|
||||
* @function
|
||||
* @param {String} url
|
||||
* @throws {Error}
|
||||
*/
|
||||
getImageInfo: function (url) {
|
||||
var image = this._image = new Image();
|
||||
var _this = this;
|
||||
if (this.crossOriginPolicy) {
|
||||
image.crossOrigin = this.crossOriginPolicy;
|
||||
}
|
||||
if (this.ajaxWithCredentials) {
|
||||
image.useCredentials = this.ajaxWithCredentials;
|
||||
}
|
||||
|
||||
if (this.crossOriginPolicy) {
|
||||
image.crossOrigin = this.crossOriginPolicy;
|
||||
}
|
||||
if (this.ajaxWithCredentials) {
|
||||
image.useCredentials = this.ajaxWithCredentials;
|
||||
}
|
||||
$.addEvent(image, 'load', function () {
|
||||
_this.width = image.naturalWidth;
|
||||
_this.height = image.naturalHeight;
|
||||
_this.aspectRatio = _this.width / _this.height;
|
||||
_this.dimensions = new $.Point(_this.width, _this.height);
|
||||
_this._tileWidth = _this.width;
|
||||
_this._tileHeight = _this.height;
|
||||
_this.tileOverlap = 0;
|
||||
_this.minLevel = 0;
|
||||
_this.image = image;
|
||||
_this.levels = _this._buildLevels(image);
|
||||
_this.maxLevel = _this.levels.length - 1;
|
||||
|
||||
$.addEvent(image, 'load', function () {
|
||||
_this.width = image.naturalWidth;
|
||||
_this.height = image.naturalHeight;
|
||||
_this.aspectRatio = _this.width / _this.height;
|
||||
_this.dimensions = new $.Point(_this.width, _this.height);
|
||||
_this._tileWidth = _this.width;
|
||||
_this._tileHeight = _this.height;
|
||||
_this.tileOverlap = 0;
|
||||
_this.minLevel = 0;
|
||||
_this.levels = _this._buildLevels();
|
||||
_this.maxLevel = _this.levels.length - 1;
|
||||
_this.ready = true;
|
||||
|
||||
_this.ready = true;
|
||||
/**
|
||||
* Raised when a TileSource is opened and initialized.
|
||||
*
|
||||
* @event ready
|
||||
* @memberof OpenSeadragon.TileSource
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.TileSource} eventSource - A reference
|
||||
* to the TileSource which raised the event.
|
||||
* @property {Object} tileSource
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
_this.raiseEvent('ready', {tileSource: _this});
|
||||
// Note: this event is documented elsewhere, in TileSource
|
||||
_this.raiseEvent('ready', {tileSource: _this});
|
||||
});
|
||||
|
||||
$.addEvent(image, 'error', function () {
|
||||
_this.image = null;
|
||||
// Note: this event is documented elsewhere, in TileSource
|
||||
_this.raiseEvent('open-failed', {
|
||||
message: "Error loading image at " + url,
|
||||
source: url
|
||||
});
|
||||
});
|
||||
|
||||
$.addEvent(image, 'error', function () {
|
||||
/***
|
||||
* Raised when an error occurs loading a TileSource.
|
||||
*
|
||||
* @event open-failed
|
||||
* @memberof OpenSeadragon.TileSource
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.TileSource} eventSource - A reference
|
||||
* to the TileSource which raised the event.
|
||||
* @property {String} message
|
||||
* @property {String} source
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
_this.raiseEvent('open-failed', {
|
||||
message: "Error loading image at " + url,
|
||||
source: url
|
||||
});
|
||||
});
|
||||
image.src = url;
|
||||
}
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getLevelScale(level) {
|
||||
let levelScale = NaN;
|
||||
if (level >= this.minLevel && level <= this.maxLevel) {
|
||||
levelScale =
|
||||
this.levels[level].width /
|
||||
this.levels[this.maxLevel].width;
|
||||
}
|
||||
return levelScale;
|
||||
}
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getNumTiles(level) {
|
||||
if (this.getLevelScale(level)) {
|
||||
return new $.Point(1, 1);
|
||||
}
|
||||
return new $.Point(0, 0);
|
||||
}
|
||||
/**
|
||||
* Retrieves a tile url
|
||||
* @function
|
||||
* @param {Number} level Level of the tile
|
||||
* @param {Number} x x coordinate of the tile
|
||||
* @param {Number} y y coordinate of the tile
|
||||
*/
|
||||
getTileUrl(level, x, y) {
|
||||
if (level === this.maxLevel) {
|
||||
return this.url; //for original image, preserve url
|
||||
}
|
||||
//make up url by positional args
|
||||
return `${this.url}?l=${level}&x=${x}&y=${y}`;
|
||||
}
|
||||
|
||||
image.src = url;
|
||||
},
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getLevelScale: function (level) {
|
||||
var levelScale = NaN;
|
||||
if (level >= this.minLevel && level <= this.maxLevel) {
|
||||
levelScale =
|
||||
this.levels[level].width /
|
||||
this.levels[this.maxLevel].width;
|
||||
}
|
||||
return levelScale;
|
||||
},
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getNumTiles: function (level) {
|
||||
var scale = this.getLevelScale(level);
|
||||
if (scale) {
|
||||
return new $.Point(1, 1);
|
||||
} else {
|
||||
return new $.Point(0, 0);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {OpenSeadragon.Point} point
|
||||
*/
|
||||
getTileAtPoint: function (level, point) {
|
||||
return new $.Point(0, 0);
|
||||
},
|
||||
/**
|
||||
* Retrieves a tile url
|
||||
* @function
|
||||
* @param {Number} level Level of the tile
|
||||
* @param {Number} x x coordinate of the tile
|
||||
* @param {Number} y y coordinate of the tile
|
||||
*/
|
||||
getTileUrl: function (level, x, y) {
|
||||
var url = null;
|
||||
if (level >= this.minLevel && level <= this.maxLevel) {
|
||||
url = this.levels[level].url;
|
||||
}
|
||||
return url;
|
||||
},
|
||||
/**
|
||||
* Retrieves a tile context 2D
|
||||
* @function
|
||||
* @param {Number} level Level of the tile
|
||||
* @param {Number} x x coordinate of the tile
|
||||
* @param {Number} y y coordinate of the tile
|
||||
*/
|
||||
getContext2D: function (level, x, y) {
|
||||
var context = null;
|
||||
if (level >= this.minLevel && level <= this.maxLevel) {
|
||||
context = this.levels[level].context2D;
|
||||
}
|
||||
return context;
|
||||
},
|
||||
/**
|
||||
* @private Build the differents levels of the pyramid if possible
|
||||
* (canvas API enabled and no canvas tainting issue)
|
||||
*/
|
||||
_buildLevels: function () {
|
||||
var levels = [{
|
||||
url: this._image.src,
|
||||
width: this._image.naturalWidth,
|
||||
height: this._image.naturalHeight
|
||||
}];
|
||||
/**
|
||||
* Equality comparator
|
||||
*/
|
||||
equals(otherSource) {
|
||||
return this.url === otherSource.url;
|
||||
}
|
||||
|
||||
if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) {
|
||||
// We don't need the image anymore. Allows it to be GC.
|
||||
delete this._image;
|
||||
return levels;
|
||||
}
|
||||
getTilePostData(level, x, y) {
|
||||
return {level: level, x: x, y: y};
|
||||
}
|
||||
|
||||
var currentWidth = this._image.naturalWidth;
|
||||
var currentHeight = this._image.naturalHeight;
|
||||
/**
|
||||
* Retrieves a tile context 2D
|
||||
* @deprecated
|
||||
*/
|
||||
getContext2D(level, x, y) {
|
||||
$.console.error('Using [TiledImage.getContext2D] (for plain images only) is deprecated. ' +
|
||||
'Use overridden downloadTileStart (https://openseadragon.github.io/examples/advanced-data-model/) instead.');
|
||||
return this._createContext2D();
|
||||
}
|
||||
|
||||
var bigCanvas = document.createElement("canvas");
|
||||
var bigContext = bigCanvas.getContext("2d");
|
||||
downloadTileStart(job) {
|
||||
const tileData = job.postData;
|
||||
if (tileData.level === this.maxLevel) {
|
||||
job.finish(this.image, null, "image");
|
||||
return;
|
||||
}
|
||||
|
||||
bigCanvas.width = currentWidth;
|
||||
bigCanvas.height = currentHeight;
|
||||
bigContext.drawImage(this._image, 0, 0, currentWidth, currentHeight);
|
||||
// We cache the context of the highest level because the browser
|
||||
// is a lot faster at downsampling something it already has
|
||||
// downsampled before.
|
||||
levels[0].context2D = bigContext;
|
||||
// We don't need the image anymore. Allows it to be GC.
|
||||
delete this._image;
|
||||
if (tileData.level >= this.minLevel && tileData.level <= this.maxLevel) {
|
||||
const levelData = this.levels[tileData.level];
|
||||
const context = this._createContext2D(this.image, levelData.width, levelData.height);
|
||||
job.finish(context, null, "context2d");
|
||||
return;
|
||||
}
|
||||
job.fail(`Invalid level ${tileData.level} for plain image source. Did you forget to set buildPyramid=true?`);
|
||||
}
|
||||
|
||||
if ($.isCanvasTainted(bigCanvas)) {
|
||||
// If the canvas is tainted, we can't compute the pyramid.
|
||||
return levels;
|
||||
}
|
||||
downloadTileAbort(job) {
|
||||
//no-op
|
||||
}
|
||||
|
||||
// We build smaller levels until either width or height becomes
|
||||
// 1 pixel wide.
|
||||
while (currentWidth >= 2 && currentHeight >= 2) {
|
||||
currentWidth = Math.floor(currentWidth / 2);
|
||||
currentHeight = Math.floor(currentHeight / 2);
|
||||
var smallCanvas = document.createElement("canvas");
|
||||
var smallContext = smallCanvas.getContext("2d");
|
||||
smallCanvas.width = currentWidth;
|
||||
smallCanvas.height = currentHeight;
|
||||
smallContext.drawImage(bigCanvas, 0, 0, currentWidth, currentHeight);
|
||||
// private
|
||||
//
|
||||
// Builds the different levels of the pyramid if possible
|
||||
// (i.e. if canvas API enabled and no canvas tainting issue).
|
||||
_buildLevels(image) {
|
||||
const levels = [{
|
||||
url: image.src,
|
||||
width: image.naturalWidth,
|
||||
height: image.naturalHeight
|
||||
}];
|
||||
|
||||
levels.splice(0, 0, {
|
||||
context2D: smallContext,
|
||||
width: currentWidth,
|
||||
height: currentHeight
|
||||
});
|
||||
|
||||
bigCanvas = smallCanvas;
|
||||
bigContext = smallContext;
|
||||
}
|
||||
if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) {
|
||||
return levels;
|
||||
}
|
||||
});
|
||||
|
||||
let currentWidth = image.naturalWidth,
|
||||
currentHeight = image.naturalHeight;
|
||||
// We build smaller levels until either width or height becomes
|
||||
// 2 pixel wide.
|
||||
while (currentWidth >= 2 && currentHeight >= 2) {
|
||||
currentWidth = Math.floor(currentWidth / 2);
|
||||
currentHeight = Math.floor(currentHeight / 2);
|
||||
|
||||
levels.push({
|
||||
width: currentWidth,
|
||||
height: currentHeight,
|
||||
});
|
||||
}
|
||||
return levels.reverse();
|
||||
}
|
||||
|
||||
|
||||
_createContext2D(data, w, h) {
|
||||
const canvas = document.createElement("canvas"),
|
||||
context = canvas.getContext("2d");
|
||||
|
||||
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
context.drawImage(data, 0, 0, w, h);
|
||||
return context;
|
||||
}
|
||||
};
|
||||
|
||||
}(OpenSeadragon));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - LegacyTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -109,10 +109,10 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
|
|||
supports: function( data, url ){
|
||||
return (
|
||||
data.type &&
|
||||
"legacy-image-pyramid" == data.type
|
||||
"legacy-image-pyramid" === data.type
|
||||
) || (
|
||||
data.documentElement &&
|
||||
"legacy-image-pyramid" == data.documentElement.getAttribute('type')
|
||||
"legacy-image-pyramid" === data.documentElement.getAttribute('type')
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -121,11 +121,12 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
|
|||
*
|
||||
* @function
|
||||
* @param {Object|XMLDocument} configuration - the raw configuration
|
||||
* @param {String} dataUrl - the url the data was retreived from if any.
|
||||
* @return {Object} options - A dictionary of keyword arguments sufficient
|
||||
* @param {String} dataUrl - the url the data was retrieved from if any.
|
||||
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
|
||||
* @returns {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure this tile sources constructor.
|
||||
*/
|
||||
configure: function( configuration, dataUrl ){
|
||||
configure: function( configuration, dataUrl, postData ){
|
||||
|
||||
var options;
|
||||
|
||||
|
@ -169,16 +170,6 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {OpenSeadragon.Point} point
|
||||
*/
|
||||
getTileAtPoint: function( level, point ) {
|
||||
return new $.Point( 0, 0 );
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* This method is not implemented by this class other than to throw an Error
|
||||
* announcing you have to implement it. Because of the variety of tile
|
||||
|
@ -196,11 +187,26 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
|
|||
url = this.levels[ level ].url;
|
||||
}
|
||||
return url;
|
||||
},
|
||||
|
||||
/**
|
||||
* Equality comparator
|
||||
*/
|
||||
equals: function (otherSource) {
|
||||
if (!otherSource.levels || otherSource.levels.length !== this.levels.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = this.minLevel; i <= this.maxLevel; i++) {
|
||||
if (this.levels[i].url !== otherSource.levels[i].url) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* This method removes any files from the Array which dont conform to our
|
||||
* This method removes any files from the Array which don't conform to our
|
||||
* basic requirements for a 'level' in the LegacyTileSource.
|
||||
* @private
|
||||
* @inner
|
||||
|
@ -214,12 +220,7 @@ function filterFiles( files ){
|
|||
file = files[ i ];
|
||||
if( file.height &&
|
||||
file.width &&
|
||||
file.url && (
|
||||
file.url.toLowerCase().match(/^.*\.(png|jpg|jpeg|gif)$/) || (
|
||||
file.mimetype &&
|
||||
file.mimetype.toLowerCase().match(/^.*\/(png|jpg|jpeg|gif)$/)
|
||||
)
|
||||
) ){
|
||||
file.url ){
|
||||
//This is sufficient to serve as a level
|
||||
filtered.push({
|
||||
url: file.url,
|
||||
|
@ -232,7 +233,7 @@ function filterFiles( files ){
|
|||
}
|
||||
}
|
||||
|
||||
return filtered.sort(function(a,b){
|
||||
return filtered.sort(function(a, b) {
|
||||
return a.height - b.height;
|
||||
});
|
||||
|
||||
|
@ -256,7 +257,7 @@ function configureFromXML( tileSource, xmlDoc ){
|
|||
level,
|
||||
i;
|
||||
|
||||
if ( rootName == "image" ) {
|
||||
if ( rootName === "image" ) {
|
||||
|
||||
try {
|
||||
conf = {
|
||||
|
@ -268,7 +269,7 @@ function configureFromXML( tileSource, xmlDoc ){
|
|||
for ( i = 0; i < levels.length; i++ ) {
|
||||
level = levels[ i ];
|
||||
|
||||
conf.levels .push({
|
||||
conf.levels.push({
|
||||
url: level.getAttribute( "url" ),
|
||||
width: parseInt( level.getAttribute( "width" ), 10 ),
|
||||
height: parseInt( level.getAttribute( "height" ), 10 )
|
||||
|
@ -282,9 +283,9 @@ function configureFromXML( tileSource, xmlDoc ){
|
|||
e :
|
||||
new Error( 'Unknown error parsing Legacy Image Pyramid XML.' );
|
||||
}
|
||||
} else if ( rootName == "collection" ) {
|
||||
} else if ( rootName === "collection" ) {
|
||||
throw new Error( 'Legacy Image Pyramid Collections not yet supported.' );
|
||||
} else if ( rootName == "error" ) {
|
||||
} else if ( rootName === "error" ) {
|
||||
throw new Error( 'Error: ' + xmlDoc );
|
||||
}
|
||||
|
||||
|
|
209
src/matrix3.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* OpenSeadragon - Mat3
|
||||
*
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Portions of this source file are taken from WegGL Fundamentals:
|
||||
*
|
||||
* Copyright 2012, Gregg Tavares.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Gregg Tavares. nor the names of his
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
(function( $ ){
|
||||
|
||||
// Modified from https://webglfundamentals.org/webgl/lessons/webgl-2d-matrices.html
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @class Mat3
|
||||
* @classdesc A left-to-right matrix representation, useful for affine transforms for
|
||||
* positioning tiles for drawing
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
*
|
||||
* @param {Array} [values] - Initial values for the matrix
|
||||
*
|
||||
**/
|
||||
class Mat3{
|
||||
constructor(values){
|
||||
if(!values) {
|
||||
values = [
|
||||
0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0
|
||||
];
|
||||
}
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeIdentity
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @returns {OpenSeadragon.Mat3} an identity matrix
|
||||
*/
|
||||
static makeIdentity(){
|
||||
return new Mat3([
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeTranslation
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @param {Number} tx The x value of the translation
|
||||
* @param {Number} ty The y value of the translation
|
||||
* @returns {OpenSeadragon.Mat3} A translation matrix
|
||||
*/
|
||||
static makeTranslation(tx, ty) {
|
||||
return new Mat3([
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
tx, ty, 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeRotation
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @param {Number} angleInRadians The desired rotation angle, in radians
|
||||
* @returns {OpenSeadragon.Mat3} A rotation matrix
|
||||
*/
|
||||
static makeRotation(angleInRadians) {
|
||||
var c = Math.cos(angleInRadians);
|
||||
var s = Math.sin(angleInRadians);
|
||||
return new Mat3([
|
||||
c, -s, 0,
|
||||
s, c, 0,
|
||||
0, 0, 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function makeScaling
|
||||
* @memberof OpenSeadragon.Mat3
|
||||
* @static
|
||||
* @param {Number} sx The x value of the scaling
|
||||
* @param {Number} sy The y value of the scaling
|
||||
* @returns {OpenSeadragon.Mat3} A scaling matrix
|
||||
*/
|
||||
static makeScaling(sx, sy) {
|
||||
return new Mat3([
|
||||
sx, 0, 0,
|
||||
0, sy, 0,
|
||||
0, 0, 1,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @alias multiply
|
||||
* @memberof! OpenSeadragon.Mat3
|
||||
* @param {OpenSeadragon.Mat3} other the matrix to multiply with
|
||||
* @returns {OpenSeadragon.Mat3} The result of matrix multiplication
|
||||
*/
|
||||
multiply(other) {
|
||||
let a = this.values;
|
||||
let b = other.values;
|
||||
|
||||
var a00 = a[0 * 3 + 0];
|
||||
var a01 = a[0 * 3 + 1];
|
||||
var a02 = a[0 * 3 + 2];
|
||||
var a10 = a[1 * 3 + 0];
|
||||
var a11 = a[1 * 3 + 1];
|
||||
var a12 = a[1 * 3 + 2];
|
||||
var a20 = a[2 * 3 + 0];
|
||||
var a21 = a[2 * 3 + 1];
|
||||
var a22 = a[2 * 3 + 2];
|
||||
var b00 = b[0 * 3 + 0];
|
||||
var b01 = b[0 * 3 + 1];
|
||||
var b02 = b[0 * 3 + 2];
|
||||
var b10 = b[1 * 3 + 0];
|
||||
var b11 = b[1 * 3 + 1];
|
||||
var b12 = b[1 * 3 + 2];
|
||||
var b20 = b[2 * 3 + 0];
|
||||
var b21 = b[2 * 3 + 1];
|
||||
var b22 = b[2 * 3 + 2];
|
||||
return new Mat3([
|
||||
b00 * a00 + b01 * a10 + b02 * a20,
|
||||
b00 * a01 + b01 * a11 + b02 * a21,
|
||||
b00 * a02 + b01 * a12 + b02 * a22,
|
||||
b10 * a00 + b11 * a10 + b12 * a20,
|
||||
b10 * a01 + b11 * a11 + b12 * a21,
|
||||
b10 * a02 + b11 * a12 + b12 * a22,
|
||||
b20 * a00 + b21 * a10 + b22 * a20,
|
||||
b20 * a01 + b21 * a11 + b22 * a21,
|
||||
b20 * a02 + b21 * a12 + b22 * a22,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$.Mat3 = Mat3;
|
||||
|
||||
}( OpenSeadragon ));
|
3091
src/mousetracker.js
383
src/navigator.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Navigator
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -45,7 +45,9 @@
|
|||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.Viewer
|
||||
* @extends OpenSeadragon.EventSource
|
||||
* @param {Object} options
|
||||
* @param {Object} options - Navigator options
|
||||
* @param {Element} [options.element] - An element to use for the navigator.
|
||||
* @param {String} [options.id] - Id of the element to use for the navigator. However, this is ignored if {@link options.element} is provided.
|
||||
*/
|
||||
$.Navigator = function( options ){
|
||||
|
||||
|
@ -55,26 +57,49 @@ $.Navigator = function( options ){
|
|||
navigatorSize;
|
||||
|
||||
//We may need to create a new element and id if they did not
|
||||
//provide the id for the existing element
|
||||
if( !options.id ){
|
||||
//provide the id for the existing element or the element itself
|
||||
if( options.element || options.id ){
|
||||
if ( options.element ) {
|
||||
if ( options.id ){
|
||||
$.console.warn("Given option.id for Navigator was ignored since option.element was provided and is being used instead.");
|
||||
}
|
||||
|
||||
// Don't overwrite the element's id if it has one already
|
||||
if ( options.element.id ) {
|
||||
options.id = options.element.id;
|
||||
} else {
|
||||
options.id = 'navigator-' + $.now();
|
||||
}
|
||||
|
||||
this.element = options.element;
|
||||
} else {
|
||||
this.element = document.getElementById( options.id );
|
||||
}
|
||||
|
||||
options.controlOptions = {
|
||||
anchor: $.ControlAnchor.NONE,
|
||||
attachToViewer: false,
|
||||
autoFade: false
|
||||
};
|
||||
} else {
|
||||
options.id = 'navigator-' + $.now();
|
||||
this.element = $.makeNeutralElement( "div" );
|
||||
options.controlOptions = {
|
||||
anchor: $.ControlAnchor.TOP_RIGHT,
|
||||
attachToViewer: true,
|
||||
autoFade: true
|
||||
autoFade: options.autoFade
|
||||
};
|
||||
|
||||
if( options.position ){
|
||||
if( 'BOTTOM_RIGHT' == options.position ){
|
||||
if( 'BOTTOM_RIGHT' === options.position ){
|
||||
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT;
|
||||
} else if( 'BOTTOM_LEFT' == options.position ){
|
||||
} else if( 'BOTTOM_LEFT' === options.position ){
|
||||
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
|
||||
} else if( 'TOP_RIGHT' == options.position ){
|
||||
} else if( 'TOP_RIGHT' === options.position ){
|
||||
options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
|
||||
} else if( 'TOP_LEFT' == options.position ){
|
||||
} else if( 'TOP_LEFT' === options.position ){
|
||||
options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
|
||||
} else if( 'ABSOLUTE' == options.position ){
|
||||
} else if( 'ABSOLUTE' === options.position ){
|
||||
options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
|
||||
options.controlOptions.top = options.top;
|
||||
options.controlOptions.left = options.left;
|
||||
|
@ -82,14 +107,6 @@ $.Navigator = function( options ){
|
|||
options.controlOptions.width = options.width;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
this.element = document.getElementById( options.id );
|
||||
options.controlOptions = {
|
||||
anchor: $.ControlAnchor.NONE,
|
||||
attachToViewer: false,
|
||||
autoFade: false
|
||||
};
|
||||
}
|
||||
this.element.id = options.id;
|
||||
this.element.className += ' navigator';
|
||||
|
@ -107,10 +124,15 @@ $.Navigator = function( options ){
|
|||
showSequenceControl: false,
|
||||
immediateRender: true,
|
||||
blendTime: 0,
|
||||
animationTime: 0,
|
||||
autoResize: options.autoResize,
|
||||
animationTime: options.animationTime,
|
||||
// disable autoResize since resize behavior is implemented differently by the navigator
|
||||
autoResize: false,
|
||||
// prevent resizing the navigator from adding unwanted space around the image
|
||||
minZoomImageRatio: 1.0
|
||||
minZoomImageRatio: 1.0,
|
||||
background: options.background,
|
||||
opacity: options.opacity,
|
||||
borderColor: options.borderColor,
|
||||
displayRegionColor: options.displayRegionColor
|
||||
});
|
||||
|
||||
options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio;
|
||||
|
@ -121,16 +143,16 @@ $.Navigator = function( options ){
|
|||
//At some browser magnification levels the display regions lines up correctly, but at some there appears to
|
||||
//be a one pixel gap.
|
||||
this.fudge = new $.Point(1, 1);
|
||||
this.totalBorderWidths = new $.Point(this.borderWidth*2, this.borderWidth*2).minus(this.fudge);
|
||||
this.totalBorderWidths = new $.Point(this.borderWidth * 2, this.borderWidth * 2).minus(this.fudge);
|
||||
|
||||
|
||||
if ( options.controlOptions.anchor != $.ControlAnchor.NONE ) {
|
||||
if ( options.controlOptions.anchor !== $.ControlAnchor.NONE ) {
|
||||
(function( style, borderWidth ){
|
||||
style.margin = '0px';
|
||||
style.border = borderWidth + 'px solid #555';
|
||||
style.border = borderWidth + 'px solid ' + options.borderColor;
|
||||
style.padding = '0px';
|
||||
style.background = '#000';
|
||||
style.opacity = 0.8;
|
||||
style.background = options.background;
|
||||
style.opacity = options.opacity;
|
||||
style.overflow = 'hidden';
|
||||
}( this.element.style, this.borderWidth));
|
||||
}
|
||||
|
@ -145,12 +167,9 @@ $.Navigator = function( options ){
|
|||
style.left = '0px';
|
||||
style.fontSize = '0px';
|
||||
style.overflow = 'hidden';
|
||||
style.border = borderWidth + 'px solid #900';
|
||||
style.border = borderWidth + 'px solid ' + options.displayRegionColor;
|
||||
style.margin = '0px';
|
||||
style.padding = '0px';
|
||||
//TODO: IE doesnt like this property being set
|
||||
//try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/}
|
||||
|
||||
style.background = 'transparent';
|
||||
|
||||
// We use square bracket notation on the statement below, because float is a keyword.
|
||||
|
@ -159,35 +178,37 @@ $.Navigator = function( options ){
|
|||
style['float'] = 'left'; //Webkit
|
||||
|
||||
style.cssFloat = 'left'; //Firefox
|
||||
style.styleFloat = 'left'; //IE
|
||||
style.zIndex = 999999999;
|
||||
style.cursor = 'default';
|
||||
style.boxSizing = 'content-box';
|
||||
}( this.displayRegion.style, this.borderWidth ));
|
||||
$.setElementPointerEventsNone( this.displayRegion );
|
||||
$.setElementTouchActionNone( this.displayRegion );
|
||||
|
||||
this.displayRegionContainer = $.makeNeutralElement("div");
|
||||
this.displayRegionContainer.id = this.element.id + '-displayregioncontainer';
|
||||
this.displayRegionContainer.className = "displayregioncontainer";
|
||||
this.displayRegionContainer.style.width = "100%";
|
||||
this.displayRegionContainer.style.height = "100%";
|
||||
$.setElementPointerEventsNone( this.displayRegionContainer );
|
||||
$.setElementTouchActionNone( this.displayRegionContainer );
|
||||
|
||||
viewer.addControl(
|
||||
this.element,
|
||||
options.controlOptions
|
||||
);
|
||||
|
||||
this._resizeWithViewer = options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE &&
|
||||
options.controlOptions.anchor != $.ControlAnchor.NONE;
|
||||
this._resizeWithViewer = options.controlOptions.anchor !== $.ControlAnchor.ABSOLUTE &&
|
||||
options.controlOptions.anchor !== $.ControlAnchor.NONE;
|
||||
|
||||
if ( this._resizeWithViewer ) {
|
||||
if ( options.width && options.height ) {
|
||||
this.element.style.height = typeof ( options.height ) == "number" ? ( options.height + 'px' ) : options.height;
|
||||
this.element.style.width = typeof ( options.width ) == "number" ? ( options.width + 'px' ) : options.width;
|
||||
} else {
|
||||
viewerSize = $.getElementSize( viewer.element );
|
||||
this.element.style.height = Math.round( viewerSize.y * options.sizeRatio ) + 'px';
|
||||
this.element.style.width = Math.round( viewerSize.x * options.sizeRatio ) + 'px';
|
||||
this.oldViewerSize = viewerSize;
|
||||
}
|
||||
if (options.width && options.height) {
|
||||
this.setWidth(options.width);
|
||||
this.setHeight(options.height);
|
||||
} else if ( this._resizeWithViewer ) {
|
||||
viewerSize = $.getElementSize( viewer.element );
|
||||
this.element.style.height = Math.round( viewerSize.y * options.sizeRatio ) + 'px';
|
||||
this.element.style.width = Math.round( viewerSize.x * options.sizeRatio ) + 'px';
|
||||
this.oldViewerSize = viewerSize;
|
||||
navigatorSize = $.getElementSize( this.element );
|
||||
this.elementArea = navigatorSize.x * navigatorSize.y;
|
||||
}
|
||||
|
@ -199,29 +220,49 @@ $.Navigator = function( options ){
|
|||
this.displayRegionContainer.appendChild(this.displayRegion);
|
||||
this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
|
||||
|
||||
function rotate(degrees, immediately) {
|
||||
_setTransformRotate(_this.displayRegionContainer, degrees);
|
||||
_setTransformRotate(_this.displayRegion, -degrees);
|
||||
_this.viewport.setRotation(degrees, immediately);
|
||||
}
|
||||
if (options.navigatorRotate) {
|
||||
var degrees = options.viewer.viewport ?
|
||||
options.viewer.viewport.getRotation() :
|
||||
options.viewer.degrees || 0;
|
||||
|
||||
rotate(degrees, true);
|
||||
options.viewer.addHandler("rotate", function (args) {
|
||||
_setTransformRotate(_this.displayRegionContainer, args.degrees);
|
||||
_setTransformRotate(_this.displayRegion, -args.degrees);
|
||||
_this.viewport.setRotation(args.degrees);
|
||||
rotate(args.degrees, args.immediately);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Remove the base class' (Viewer's) innerTracker and replace it with our own
|
||||
this.innerTracker.destroy();
|
||||
this.innerTracker = new $.MouseTracker({
|
||||
element: this.element,
|
||||
userData: 'Navigator.innerTracker',
|
||||
element: this.element, //this.canvas,
|
||||
dragHandler: $.delegate( this, onCanvasDrag ),
|
||||
clickHandler: $.delegate( this, onCanvasClick ),
|
||||
releaseHandler: $.delegate( this, onCanvasRelease ),
|
||||
scrollHandler: $.delegate( this, onCanvasScroll )
|
||||
});
|
||||
|
||||
this.addHandler("reset-size", function() {
|
||||
if (_this.viewport) {
|
||||
_this.viewport.goHome(true);
|
||||
scrollHandler: $.delegate( this, onCanvasScroll ),
|
||||
preProcessEventHandler: function (eventInfo) {
|
||||
if (eventInfo.eventType === 'wheel') {
|
||||
//don't scroll the page up and down if the user is scrolling
|
||||
//in the navigator
|
||||
eventInfo.preventDefault = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.outerTracker.userData = 'Navigator.outerTracker';
|
||||
|
||||
// this.innerTracker is attached to this.element...we need to allow pointer
|
||||
// events to pass through this Viewer's canvas/container elements so implicit
|
||||
// pointer capture works on touch devices
|
||||
//TODO an alternative is to attach the new MouseTracker to this.canvas...not
|
||||
// sure why it isn't already (see MouseTracker constructor call above)
|
||||
$.setElementPointerEventsNone( this.canvas );
|
||||
$.setElementPointerEventsNone( this.container );
|
||||
|
||||
this.addHandler("reset-size", function() {
|
||||
if (_this.viewport) {
|
||||
|
@ -230,8 +271,10 @@ $.Navigator = function( options ){
|
|||
});
|
||||
|
||||
viewer.world.addHandler("item-index-change", function(event) {
|
||||
var item = _this.world.getItemAt(event.previousIndex);
|
||||
_this.world.setItemIndex(item, event.newIndex);
|
||||
window.setTimeout(function(){
|
||||
var item = _this.world.getItemAt(event.previousIndex);
|
||||
_this.world.setItemIndex(item, event.newIndex);
|
||||
}, 1);
|
||||
});
|
||||
|
||||
viewer.world.addHandler("remove-item", function(event) {
|
||||
|
@ -263,16 +306,55 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||
this.viewport.resize( containerSize, true );
|
||||
this.viewport.goHome(true);
|
||||
this.oldContainerSize = containerSize;
|
||||
this.drawer.clear();
|
||||
this.world.update();
|
||||
this.world.draw();
|
||||
this.update(this.viewer.viewport);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Explicitly sets the width of the navigator, in web coordinates. Disables automatic resizing.
|
||||
* @param {Number|String} width - the new width, either a number of pixels or a CSS string, such as "100%"
|
||||
*/
|
||||
setWidth: function(width) {
|
||||
this.width = width;
|
||||
this.element.style.width = typeof (width) === "number" ? (width + 'px') : width;
|
||||
this._resizeWithViewer = false;
|
||||
this.updateSize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Explicitly sets the height of the navigator, in web coordinates. Disables automatic resizing.
|
||||
* @param {Number|String} height - the new height, either a number of pixels or a CSS string, such as "100%"
|
||||
*/
|
||||
setHeight: function(height) {
|
||||
this.height = height;
|
||||
this.element.style.height = typeof (height) === "number" ? (height + 'px') : height;
|
||||
this._resizeWithViewer = false;
|
||||
this.updateSize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Flip navigator element
|
||||
* @param {Boolean} state - Flip state to set.
|
||||
*/
|
||||
setFlip: function(state) {
|
||||
this.viewport.setFlip(state);
|
||||
|
||||
this.setDisplayTransform(this.viewer.viewport.getFlip() ? "scale(-1,1)" : "scale(1,1)");
|
||||
return this;
|
||||
},
|
||||
|
||||
setDisplayTransform: function(rule) {
|
||||
setElementTransform(this.canvas, rule);
|
||||
setElementTransform(this.element, rule);
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Viewport} The viewport this navigator is tracking.
|
||||
* @param {OpenSeadragon.Viewport} [viewport] The viewport to display. Default: the viewport this navigator is tracking.
|
||||
*/
|
||||
update: function( viewport ) {
|
||||
|
||||
|
@ -283,6 +365,10 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||
topleft,
|
||||
bottomright;
|
||||
|
||||
if(!viewport){
|
||||
viewport = this.viewer.viewport;
|
||||
}
|
||||
|
||||
viewerSize = $.getElementSize( this.viewer.element );
|
||||
if ( this._resizeWithViewer && viewerSize.x && viewerSize.y && !viewerSize.equals( this.oldViewerSize ) ) {
|
||||
this.oldViewerSize = viewerSize;
|
||||
|
@ -305,21 +391,26 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||
this.updateSize();
|
||||
}
|
||||
|
||||
if( viewport && this.viewport ) {
|
||||
bounds = viewport.getBounds( true );
|
||||
topleft = this.viewport.pixelFromPoint( bounds.getTopLeft(), false );
|
||||
bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight(), false )
|
||||
if (viewport && this.viewport) {
|
||||
bounds = viewport.getBoundsNoRotate(true);
|
||||
topleft = this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(), false);
|
||||
bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false)
|
||||
.minus( this.totalBorderWidths );
|
||||
|
||||
if (!this.navigatorRotate) {
|
||||
var degrees = viewport.getRotation(true);
|
||||
_setTransformRotate(this.displayRegion, -degrees);
|
||||
}
|
||||
|
||||
//update style for navigator-box
|
||||
var style = this.displayRegion.style;
|
||||
style.display = this.world.getItemCount() ? 'block' : 'none';
|
||||
|
||||
style.top = Math.round( topleft.y ) + 'px';
|
||||
style.left = Math.round( topleft.x ) + 'px';
|
||||
style.top = topleft.y.toFixed(2) + "px";
|
||||
style.left = topleft.x.toFixed(2) + "px";
|
||||
|
||||
var width = Math.abs( topleft.x - bottomright.x );
|
||||
var height = Math.abs( topleft.y - bottomright.y );
|
||||
var width = bottomright.x - topleft.x;
|
||||
var height = bottomright.y - topleft.y;
|
||||
// make sure width and height are non-negative so IE doesn't throw
|
||||
style.width = Math.round( Math.max( width, 0 ) ) + 'px';
|
||||
style.height = Math.round( Math.max( height, 0 ) ) + 'px';
|
||||
|
@ -339,16 +430,35 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||
var myItem = event.item;
|
||||
myItem._originalForNavigator = original;
|
||||
_this._matchBounds(myItem, original, true);
|
||||
_this._matchOpacity(myItem, original);
|
||||
_this._matchCompositeOperation(myItem, original);
|
||||
|
||||
original.addHandler('bounds-change', function() {
|
||||
function matchBounds() {
|
||||
_this._matchBounds(myItem, original);
|
||||
});
|
||||
}
|
||||
|
||||
function matchOpacity() {
|
||||
_this._matchOpacity(myItem, original);
|
||||
}
|
||||
|
||||
function matchCompositeOperation() {
|
||||
_this._matchCompositeOperation(myItem, original);
|
||||
}
|
||||
|
||||
original.addHandler('bounds-change', matchBounds);
|
||||
original.addHandler('clip-change', matchBounds);
|
||||
original.addHandler('opacity-change', matchOpacity);
|
||||
original.addHandler('composite-operation-change', matchCompositeOperation);
|
||||
}
|
||||
});
|
||||
|
||||
return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
return $.Viewer.prototype.destroy.apply(this);
|
||||
},
|
||||
|
||||
// private
|
||||
_getMatchingItem: function(theirItem) {
|
||||
var count = this.world.getItemCount();
|
||||
|
@ -365,22 +475,74 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||
|
||||
// private
|
||||
_matchBounds: function(myItem, theirItem, immediately) {
|
||||
var bounds = theirItem.getBounds();
|
||||
var bounds = theirItem.getBoundsNoRotate();
|
||||
myItem.setPosition(bounds.getTopLeft(), immediately);
|
||||
myItem.setWidth(bounds.width, immediately);
|
||||
myItem.setRotation(theirItem.getRotation(), immediately);
|
||||
myItem.setClip(theirItem.getClip());
|
||||
myItem.setFlip(theirItem.getFlip());
|
||||
},
|
||||
|
||||
// private
|
||||
_matchOpacity: function(myItem, theirItem) {
|
||||
myItem.setOpacity(theirItem.opacity);
|
||||
},
|
||||
|
||||
// private
|
||||
_matchCompositeOperation: function(myItem, theirItem) {
|
||||
myItem.setCompositeOperation(theirItem.compositeOperation);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inner
|
||||
* @function
|
||||
*/
|
||||
function onCanvasClick( event ) {
|
||||
if ( event.quick && this.viewer.viewport ) {
|
||||
this.viewer.viewport.panTo( this.viewport.pointFromPixel( event.position ).rotate( -this.viewer.viewport.degrees, this.viewer.viewport.getHomeBounds().getCenter() ) );
|
||||
this.viewer.viewport.applyConstraints();
|
||||
var canvasClickEventArgs = {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
quick: event.quick,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent,
|
||||
preventDefaultAction: false
|
||||
};
|
||||
/**
|
||||
* Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
|
||||
*
|
||||
* @event navigator-click
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
|
||||
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
|
||||
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
|
||||
* @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
|
||||
* @property {Boolean} shift - True if the shift key was pressed during this event.
|
||||
* @property {Object} originalEvent - The original DOM event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
* @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
|
||||
*/
|
||||
|
||||
this.viewer.raiseEvent('navigator-click', canvasClickEventArgs);
|
||||
|
||||
if ( !canvasClickEventArgs.preventDefaultAction && event.quick && this.viewer.viewport && (this.panVertical || this.panHorizontal)) {
|
||||
if(this.viewer.viewport.flipped) {
|
||||
event.position.x = this.viewport.getContainerSize().x - event.position.x;
|
||||
}
|
||||
var target = this.viewport.pointFromPixel(event.position);
|
||||
if (!this.panVertical) {
|
||||
// perform only horizonal pan
|
||||
target.y = this.viewer.viewport.getCenter(true).y;
|
||||
} else if (!this.panHorizontal) {
|
||||
// perform only vertical pan
|
||||
target.x = this.viewer.viewport.getCenter(true).x;
|
||||
}
|
||||
this.viewer.viewport.panTo(target);
|
||||
this.viewer.viewport.applyConstraints();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -389,18 +551,55 @@ function onCanvasClick( event ) {
|
|||
* @function
|
||||
*/
|
||||
function onCanvasDrag( event ) {
|
||||
if ( this.viewer.viewport ) {
|
||||
if( !this.panHorizontal ){
|
||||
var canvasDragEventArgs = {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
delta: event.delta,
|
||||
speed: event.speed,
|
||||
direction: event.direction,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent,
|
||||
preventDefaultAction: false
|
||||
};
|
||||
/**
|
||||
* Raised when a drag event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
|
||||
*
|
||||
* @event navigator-drag
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
|
||||
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
|
||||
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
|
||||
* @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
|
||||
* @property {Number} speed - Current computed speed, in pixels per second.
|
||||
* @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
|
||||
* @property {Boolean} shift - True if the shift key was pressed during this event.
|
||||
* @property {Object} originalEvent - The original DOM event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
* @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
|
||||
*/
|
||||
this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs);
|
||||
|
||||
if ( !canvasDragEventArgs.preventDefaultAction && this.viewer.viewport ) {
|
||||
if( !this.panHorizontal ){
|
||||
event.delta.x = 0;
|
||||
}
|
||||
if( !this.panVertical ){
|
||||
event.delta.y = 0;
|
||||
}
|
||||
|
||||
if(this.viewer.viewport.flipped){
|
||||
event.delta.x = -event.delta.x;
|
||||
}
|
||||
|
||||
this.viewer.viewport.panBy(
|
||||
this.viewport.deltaPointsFromPixels(
|
||||
event.delta
|
||||
)
|
||||
);
|
||||
if( this.viewer.constrainDuringPan ){
|
||||
this.viewer.viewport.applyConstraints();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -423,6 +622,15 @@ function onCanvasRelease( event ) {
|
|||
* @function
|
||||
*/
|
||||
function onCanvasScroll( event ) {
|
||||
var eventArgs = {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
scroll: event.scroll,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent,
|
||||
preventDefault: event.preventDefault
|
||||
};
|
||||
|
||||
/**
|
||||
* Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.).
|
||||
*
|
||||
|
@ -435,19 +643,12 @@ function onCanvasScroll( event ) {
|
|||
* @property {Number} scroll - The scroll delta for the event.
|
||||
* @property {Boolean} shift - True if the shift key was pressed during this event.
|
||||
* @property {Object} originalEvent - The original DOM event.
|
||||
* @property {Boolean} preventDefault - Set to true to prevent the default user-agent's handling of the wheel event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.viewer.raiseEvent( 'navigator-scroll', {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
scroll: event.scroll,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent
|
||||
});
|
||||
this.viewer.raiseEvent( 'navigator-scroll', eventArgs );
|
||||
|
||||
//dont scroll the page up and down if the user is scrolling
|
||||
//in the navigator
|
||||
return false;
|
||||
event.preventDefault = eventArgs.preventDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -456,12 +657,16 @@ function onCanvasScroll( event ) {
|
|||
* @param {Object} element
|
||||
* @param {Number} degrees
|
||||
*/
|
||||
function _setTransformRotate (element, degrees) {
|
||||
element.style.webkitTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.mozTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.msTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.oTransform = "rotate(" + degrees + "deg)";
|
||||
element.style.transform = "rotate(" + degrees + "deg)";
|
||||
function _setTransformRotate( element, degrees ) {
|
||||
setElementTransform(element, "rotate(" + degrees + "deg)");
|
||||
}
|
||||
|
||||
function setElementTransform( element, rule ) {
|
||||
element.style.webkitTransform = rule;
|
||||
element.style.mozTransform = rule;
|
||||
element.style.msTransform = rule;
|
||||
element.style.oTransform = rule;
|
||||
element.style.transform = rule;
|
||||
}
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
|
1543
src/openseadragon.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - OsmTileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -113,7 +113,7 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
supports: function( data, url ){
|
||||
return (
|
||||
data.type &&
|
||||
"openstreetmaps" == data.type
|
||||
"openstreetmaps" === data.type
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -121,11 +121,12 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
*
|
||||
* @function
|
||||
* @param {Object} data - the raw configuration
|
||||
* @param {String} url - the url the data was retreived from if any.
|
||||
* @return {Object} options - A dictionary of keyword arguments sufficient
|
||||
* @param {String} url - the url the data was retrieved from if any.
|
||||
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
|
||||
* @returns {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure this tile sources constructor.
|
||||
*/
|
||||
configure: function( data, url ){
|
||||
configure: function( data, url, postData ){
|
||||
return data;
|
||||
},
|
||||
|
||||
|
@ -138,6 +139,13 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
|||
*/
|
||||
getTileUrl: function( level, x, y ) {
|
||||
return this.tilesUrl + (level - 8) + "/" + x + "/" + y + ".png";
|
||||
},
|
||||
|
||||
/**
|
||||
* Equality comparator
|
||||
*/
|
||||
equals: function(otherSource) {
|
||||
return this.tilesUrl === otherSource.tilesUrl;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
540
src/overlay.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Overlay
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -32,14 +32,18 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* An enumeration of positions that an overlay may be assigned relative to
|
||||
* the viewport.
|
||||
* It is identical to OpenSeadragon.Placement but is kept for backward
|
||||
* compatibility.
|
||||
* @member OverlayPlacement
|
||||
* @memberof OpenSeadragon
|
||||
* @see OpenSeadragon.Placement
|
||||
* @static
|
||||
* @readonly
|
||||
* @type {Object}
|
||||
* @property {Number} CENTER
|
||||
* @property {Number} TOP_LEFT
|
||||
|
@ -51,17 +55,26 @@
|
|||
* @property {Number} BOTTOM_LEFT
|
||||
* @property {Number} LEFT
|
||||
*/
|
||||
$.OverlayPlacement = {
|
||||
CENTER: 0,
|
||||
TOP_LEFT: 1,
|
||||
TOP: 2,
|
||||
TOP_RIGHT: 3,
|
||||
RIGHT: 4,
|
||||
BOTTOM_RIGHT: 5,
|
||||
BOTTOM: 6,
|
||||
BOTTOM_LEFT: 7,
|
||||
LEFT: 8
|
||||
};
|
||||
$.OverlayPlacement = $.Placement;
|
||||
|
||||
/**
|
||||
* An enumeration of possible ways to handle overlays rotation
|
||||
* @member OverlayRotationMode
|
||||
* @memberOf OpenSeadragon
|
||||
* @static
|
||||
* @readonly
|
||||
* @property {Number} NO_ROTATION The overlay ignore the viewport rotation.
|
||||
* @property {Number} EXACT The overlay use CSS 3 transforms to rotate with
|
||||
* the viewport. If the overlay contains text, it will get rotated as well.
|
||||
* @property {Number} BOUNDING_BOX The overlay adjusts for rotation by
|
||||
* taking the size of the bounding box of the rotated bounds.
|
||||
* Only valid for overlays with Rect location and scalable in both directions.
|
||||
*/
|
||||
$.OverlayRotationMode = $.freezeObject({
|
||||
NO_ROTATION: 1,
|
||||
EXACT: 2,
|
||||
BOUNDING_BOX: 3
|
||||
});
|
||||
|
||||
/**
|
||||
* @class Overlay
|
||||
|
@ -72,19 +85,27 @@
|
|||
* @param {Element} options.element
|
||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The
|
||||
* location of the overlay on the image. If a {@link OpenSeadragon.Point}
|
||||
* is specified, the overlay will keep a constant size independently of the
|
||||
* zoom. If a {@link OpenSeadragon.Rect} is specified, the overlay size will
|
||||
* be adjusted when the zoom changes.
|
||||
* @param {OpenSeadragon.OverlayPlacement} [options.placement=OpenSeadragon.OverlayPlacement.TOP_LEFT]
|
||||
* Relative position to the viewport.
|
||||
* Only used if location is a {@link OpenSeadragon.Point}.
|
||||
* is specified, the overlay will be located at this location with respect
|
||||
* to the placement option. If a {@link OpenSeadragon.Rect} is specified,
|
||||
* the overlay will be placed at this location with the corresponding width
|
||||
* and height and placement TOP_LEFT.
|
||||
* @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT]
|
||||
* Defines what part of the overlay should be at the specified options.location
|
||||
* @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]
|
||||
* @param {Boolean} [options.checkResize=true] Set to false to avoid to
|
||||
* check the size of the overlay everytime it is drawn when using a
|
||||
* {@link OpenSeadragon.Point} as options.location. It will improve
|
||||
* performances but will cause a misalignment if the overlay size changes.
|
||||
* check the size of the overlay every time it is drawn in the directions
|
||||
* which are not scaled. It will improve performances but will cause a
|
||||
* misalignment if the overlay size changes.
|
||||
* @param {Number} [options.width] The width of the overlay in viewport
|
||||
* coordinates. If specified, the width of the overlay will be adjusted when
|
||||
* the zoom changes.
|
||||
* @param {Number} [options.height] The height of the overlay in viewport
|
||||
* coordinates. If specified, the height of the overlay will be adjusted when
|
||||
* the zoom changes.
|
||||
* @param {Boolean} [options.rotationMode=OpenSeadragon.OverlayRotationMode.EXACT]
|
||||
* How to handle the rotation of the viewport.
|
||||
*/
|
||||
$.Overlay = function( element, location, placement ) {
|
||||
$.Overlay = function(element, location, placement) {
|
||||
|
||||
/**
|
||||
* onDraw callback signature used by {@link OpenSeadragon.Overlay}.
|
||||
|
@ -97,7 +118,7 @@
|
|||
*/
|
||||
|
||||
var options;
|
||||
if ( $.isPlainObject( element ) ) {
|
||||
if ($.isPlainObject(element)) {
|
||||
options = element;
|
||||
} else {
|
||||
options = {
|
||||
|
@ -107,72 +128,77 @@
|
|||
};
|
||||
}
|
||||
|
||||
this.element = options.element;
|
||||
this.scales = options.location instanceof $.Rect;
|
||||
this.bounds = new $.Rect(
|
||||
options.location.x,
|
||||
options.location.y,
|
||||
options.location.width,
|
||||
options.location.height
|
||||
);
|
||||
this.position = new $.Point(
|
||||
options.location.x,
|
||||
options.location.y
|
||||
);
|
||||
this.size = new $.Point(
|
||||
options.location.width,
|
||||
options.location.height
|
||||
);
|
||||
this.style = options.element.style;
|
||||
// rects are always top-left
|
||||
this.placement = options.location instanceof $.Point ?
|
||||
options.placement :
|
||||
$.OverlayPlacement.TOP_LEFT;
|
||||
this.onDraw = options.onDraw;
|
||||
this.checkResize = options.checkResize === undefined ?
|
||||
true : options.checkResize;
|
||||
this.elementWrapper = document.createElement('div');
|
||||
this.element = options.element;
|
||||
this.elementWrapper.appendChild(this.element);
|
||||
|
||||
if (this.element.id) {
|
||||
this.elementWrapper.id = "overlay-wrapper-" + this.element.id; // Unique ID if element has one
|
||||
}
|
||||
|
||||
// Always add a class for styling & selection
|
||||
this.elementWrapper.classList.add("openseadragon-overlay-wrapper");
|
||||
|
||||
this.style = this.elementWrapper.style;
|
||||
this._init(options);
|
||||
};
|
||||
|
||||
$.Overlay.prototype = /** @lends OpenSeadragon.Overlay.prototype */{
|
||||
/** @lends OpenSeadragon.Overlay.prototype */
|
||||
$.Overlay.prototype = {
|
||||
|
||||
// private
|
||||
_init: function(options) {
|
||||
this.location = options.location;
|
||||
this.placement = options.placement === undefined ?
|
||||
$.Placement.TOP_LEFT : options.placement;
|
||||
this.onDraw = options.onDraw;
|
||||
this.checkResize = options.checkResize === undefined ?
|
||||
true : options.checkResize;
|
||||
|
||||
// When this.width is not null, the overlay get scaled horizontally
|
||||
this.width = options.width === undefined ? null : options.width;
|
||||
|
||||
// When this.height is not null, the overlay get scaled vertically
|
||||
this.height = options.height === undefined ? null : options.height;
|
||||
|
||||
this.rotationMode = options.rotationMode || $.OverlayRotationMode.EXACT;
|
||||
|
||||
// Having a rect as location is a syntactic sugar
|
||||
if (this.location instanceof $.Rect) {
|
||||
this.width = this.location.width;
|
||||
this.height = this.location.height;
|
||||
this.location = this.location.getTopLeft();
|
||||
this.placement = $.Placement.TOP_LEFT;
|
||||
}
|
||||
|
||||
// Deprecated properties kept for backward compatibility.
|
||||
this.scales = this.width !== null && this.height !== null;
|
||||
this.bounds = new $.Rect(
|
||||
this.location.x, this.location.y, this.width, this.height);
|
||||
this.position = this.location;
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal function to adjust the position of an overlay
|
||||
* depending on it size and placement.
|
||||
* @function
|
||||
* @param {OpenSeadragon.OverlayPlacement} position
|
||||
* @param {OpenSeadragon.Point} position
|
||||
* @param {OpenSeadragon.Point} size
|
||||
*/
|
||||
adjust: function( position, size ) {
|
||||
switch ( this.placement ) {
|
||||
case $.OverlayPlacement.TOP_LEFT:
|
||||
break;
|
||||
case $.OverlayPlacement.TOP:
|
||||
position.x -= size.x / 2;
|
||||
break;
|
||||
case $.OverlayPlacement.TOP_RIGHT:
|
||||
position.x -= size.x;
|
||||
break;
|
||||
case $.OverlayPlacement.RIGHT:
|
||||
position.x -= size.x;
|
||||
position.y -= size.y / 2;
|
||||
break;
|
||||
case $.OverlayPlacement.BOTTOM_RIGHT:
|
||||
position.x -= size.x;
|
||||
position.y -= size.y;
|
||||
break;
|
||||
case $.OverlayPlacement.BOTTOM:
|
||||
position.x -= size.x / 2;
|
||||
position.y -= size.y;
|
||||
break;
|
||||
case $.OverlayPlacement.BOTTOM_LEFT:
|
||||
position.y -= size.y;
|
||||
break;
|
||||
case $.OverlayPlacement.LEFT:
|
||||
position.y -= size.y / 2;
|
||||
break;
|
||||
default:
|
||||
case $.OverlayPlacement.CENTER:
|
||||
position.x -= size.x / 2;
|
||||
position.y -= size.y / 2;
|
||||
break;
|
||||
adjust: function(position, size) {
|
||||
var properties = $.Placement.properties[this.placement];
|
||||
if (!properties) {
|
||||
return;
|
||||
}
|
||||
if (properties.isHorizontallyCentered) {
|
||||
position.x -= size.x / 2;
|
||||
} else if (properties.isRight) {
|
||||
position.x -= size.x;
|
||||
}
|
||||
if (properties.isVerticallyCentered) {
|
||||
position.y -= size.y / 2;
|
||||
} else if (properties.isBottom) {
|
||||
position.y -= size.y;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -180,20 +206,20 @@
|
|||
* @function
|
||||
*/
|
||||
destroy: function() {
|
||||
var element = this.element,
|
||||
style = this.style;
|
||||
var element = this.elementWrapper;
|
||||
var style = this.style;
|
||||
|
||||
if ( element.parentNode ) {
|
||||
element.parentNode.removeChild( element );
|
||||
if (element.parentNode) {
|
||||
element.parentNode.removeChild(element);
|
||||
//this should allow us to preserve overlays when required between
|
||||
//pages
|
||||
if ( element.prevElementParent ) {
|
||||
if (element.prevElementParent) {
|
||||
style.display = 'none';
|
||||
//element.prevElementParent.insertBefore(
|
||||
// element,
|
||||
// element.prevNextSibling
|
||||
//);
|
||||
document.body.appendChild( element );
|
||||
document.body.appendChild(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,115 +230,273 @@
|
|||
style.left = "";
|
||||
style.position = "";
|
||||
|
||||
if ( this.scales ) {
|
||||
if (this.width !== null) {
|
||||
style.width = "";
|
||||
}
|
||||
if (this.height !== null) {
|
||||
style.height = "";
|
||||
}
|
||||
var transformOriginProp = $.getCssPropertyWithVendorPrefix(
|
||||
'transformOrigin');
|
||||
var transformProp = $.getCssPropertyWithVendorPrefix(
|
||||
'transform');
|
||||
if (transformOriginProp && transformProp) {
|
||||
style[transformOriginProp] = "";
|
||||
style[transformProp] = "";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Element} container
|
||||
*/
|
||||
drawHTML: function( container, viewport ) {
|
||||
var element = this.element,
|
||||
style = this.style,
|
||||
scales = this.scales,
|
||||
degrees = viewport.degrees,
|
||||
position = viewport.pixelFromPoint(
|
||||
this.bounds.getTopLeft(),
|
||||
true
|
||||
),
|
||||
size,
|
||||
overlayCenter;
|
||||
|
||||
if ( element.parentNode != container ) {
|
||||
drawHTML: function(container, viewport) {
|
||||
var element = this.elementWrapper;
|
||||
if (element.parentNode !== container) {
|
||||
//save the source parent for later if we need it
|
||||
element.prevElementParent = element.parentNode;
|
||||
element.prevNextSibling = element.nextSibling;
|
||||
container.appendChild( element );
|
||||
this.size = $.getElementSize( element );
|
||||
element.prevElementParent = element.parentNode;
|
||||
element.prevNextSibling = element.nextSibling;
|
||||
container.appendChild(element);
|
||||
|
||||
// have to set position before calculating size, fix #1116
|
||||
this.style.position = "absolute";
|
||||
// this.size is used by overlays which don't get scaled in at
|
||||
// least one direction when this.checkResize is set to false.
|
||||
this.size = $.getElementSize(this.elementWrapper);
|
||||
}
|
||||
|
||||
if ( scales ) {
|
||||
size = viewport.deltaPixelsFromPoints(
|
||||
this.bounds.getSize(),
|
||||
true
|
||||
);
|
||||
} else if ( this.checkResize ) {
|
||||
size = $.getElementSize( element );
|
||||
} else {
|
||||
size = this.size;
|
||||
var positionAndSize = this._getOverlayPositionAndSize(viewport);
|
||||
var position = positionAndSize.position;
|
||||
var size = this.size = positionAndSize.size;
|
||||
var outerScale = "";
|
||||
if (viewport.overlayPreserveContentDirection) {
|
||||
outerScale = viewport.flipped ? " scaleX(-1)" : " scaleX(1)";
|
||||
}
|
||||
|
||||
this.position = position;
|
||||
this.size = size;
|
||||
|
||||
this.adjust( position, size );
|
||||
|
||||
position = position.apply( Math.round );
|
||||
size = size.apply( Math.round );
|
||||
|
||||
// rotate the position of the overlay
|
||||
// TODO only rotate overlays if in canvas mode
|
||||
// TODO replace the size rotation with CSS3 transforms
|
||||
// TODO add an option to overlays to not rotate with the image
|
||||
// Currently only rotates position and size
|
||||
if( degrees !== 0 && this.scales ) {
|
||||
overlayCenter = new $.Point( size.x / 2, size.y / 2 );
|
||||
|
||||
var drawerCenter = new $.Point(
|
||||
viewport.viewer.drawer.canvas.width / 2,
|
||||
viewport.viewer.drawer.canvas.height / 2
|
||||
);
|
||||
position = position.plus( overlayCenter ).rotate(
|
||||
degrees,
|
||||
drawerCenter
|
||||
).minus( overlayCenter );
|
||||
|
||||
size = size.rotate( degrees, new $.Point( 0, 0 ) );
|
||||
size = new $.Point( Math.abs( size.x ), Math.abs( size.y ) );
|
||||
}
|
||||
|
||||
var rotate = viewport.flipped ? -positionAndSize.rotate : positionAndSize.rotate;
|
||||
var scale = viewport.flipped ? " scaleX(-1)" : "";
|
||||
// call the onDraw callback if it exists to allow one to overwrite
|
||||
// the drawing/positioning/sizing of the overlay
|
||||
if ( this.onDraw ) {
|
||||
this.onDraw( position, size, element );
|
||||
if (this.onDraw) {
|
||||
this.onDraw(position, size, this.element);
|
||||
} else {
|
||||
style.left = position.x + "px";
|
||||
style.top = position.y + "px";
|
||||
style.position = "absolute";
|
||||
|
||||
if (style.display != 'none') {
|
||||
style.display = 'block';
|
||||
var style = this.style;
|
||||
var innerStyle = this.element.style;
|
||||
innerStyle.display = "block";
|
||||
style.left = position.x + "px";
|
||||
style.top = position.y + "px";
|
||||
if (this.width !== null) {
|
||||
innerStyle.width = size.x + "px";
|
||||
}
|
||||
|
||||
if ( scales ) {
|
||||
style.width = size.x + "px";
|
||||
style.height = size.y + "px";
|
||||
if (this.height !== null) {
|
||||
innerStyle.height = size.y + "px";
|
||||
}
|
||||
var transformOriginProp = $.getCssPropertyWithVendorPrefix(
|
||||
'transformOrigin');
|
||||
var transformProp = $.getCssPropertyWithVendorPrefix(
|
||||
'transform');
|
||||
if (transformOriginProp && transformProp) {
|
||||
if (rotate && !viewport.flipped) {
|
||||
innerStyle[transformProp] = "";
|
||||
style[transformOriginProp] = this._getTransformOrigin();
|
||||
style[transformProp] = "rotate(" + rotate + "deg)";
|
||||
} else if (!rotate && viewport.flipped) {
|
||||
innerStyle[transformProp] = outerScale;
|
||||
style[transformOriginProp] = this._getTransformOrigin();
|
||||
style[transformProp] = scale;
|
||||
} else if (rotate && viewport.flipped){
|
||||
innerStyle[transformProp] = outerScale;
|
||||
style[transformOriginProp] = this._getTransformOrigin();
|
||||
style[transformProp] = "rotate(" + rotate + "deg)" + scale;
|
||||
} else {
|
||||
innerStyle[transformProp] = "";
|
||||
style[transformOriginProp] = "";
|
||||
style[transformProp] = "";
|
||||
}
|
||||
}
|
||||
style.display = 'flex';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location
|
||||
* @param {OpenSeadragon.OverlayPlacement} position
|
||||
*/
|
||||
update: function( location, placement ) {
|
||||
this.scales = location instanceof $.Rect;
|
||||
this.bounds = new $.Rect(
|
||||
location.x,
|
||||
location.y,
|
||||
location.width,
|
||||
location.height
|
||||
);
|
||||
// rects are always top-left
|
||||
this.placement = location instanceof $.Point ?
|
||||
placement :
|
||||
$.OverlayPlacement.TOP_LEFT;
|
||||
}
|
||||
// private
|
||||
_getOverlayPositionAndSize: function(viewport) {
|
||||
var position = viewport.pixelFromPoint(this.location, true);
|
||||
var size = this._getSizeInPixels(viewport);
|
||||
this.adjust(position, size);
|
||||
|
||||
var rotate = 0;
|
||||
if (viewport.getRotation(true) &&
|
||||
this.rotationMode !== $.OverlayRotationMode.NO_ROTATION) {
|
||||
// BOUNDING_BOX is only valid if both directions get scaled.
|
||||
// Get replaced by EXACT otherwise.
|
||||
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX &&
|
||||
this.width !== null && this.height !== null) {
|
||||
var rect = new $.Rect(position.x, position.y, size.x, size.y);
|
||||
var boundingBox = this._getBoundingBox(rect, viewport.getRotation(true));
|
||||
position = boundingBox.getTopLeft();
|
||||
size = boundingBox.getSize();
|
||||
} else {
|
||||
rotate = viewport.getRotation(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (viewport.flipped) {
|
||||
position.x = (viewport.getContainerSize().x - position.x);
|
||||
}
|
||||
return {
|
||||
position: position,
|
||||
size: size,
|
||||
rotate: rotate
|
||||
};
|
||||
},
|
||||
|
||||
// private
|
||||
_getSizeInPixels: function(viewport) {
|
||||
var width = this.size.x;
|
||||
var height = this.size.y;
|
||||
if (this.width !== null || this.height !== null) {
|
||||
var scaledSize = viewport.deltaPixelsFromPointsNoRotate(
|
||||
new $.Point(this.width || 0, this.height || 0), true);
|
||||
if (this.width !== null) {
|
||||
width = scaledSize.x;
|
||||
}
|
||||
if (this.height !== null) {
|
||||
height = scaledSize.y;
|
||||
}
|
||||
}
|
||||
if (this.checkResize &&
|
||||
(this.width === null || this.height === null)) {
|
||||
var eltSize = this.size = $.getElementSize(this.elementWrapper);
|
||||
if (this.width === null) {
|
||||
width = eltSize.x;
|
||||
}
|
||||
if (this.height === null) {
|
||||
height = eltSize.y;
|
||||
}
|
||||
}
|
||||
return new $.Point(width, height);
|
||||
},
|
||||
|
||||
// private
|
||||
_getBoundingBox: function(rect, degrees) {
|
||||
var refPoint = this._getPlacementPoint(rect);
|
||||
return rect.rotate(degrees, refPoint).getBoundingBox();
|
||||
},
|
||||
|
||||
// private
|
||||
_getPlacementPoint: function(rect) {
|
||||
var result = new $.Point(rect.x, rect.y);
|
||||
var properties = $.Placement.properties[this.placement];
|
||||
if (properties) {
|
||||
if (properties.isHorizontallyCentered) {
|
||||
result.x += rect.width / 2;
|
||||
} else if (properties.isRight) {
|
||||
result.x += rect.width;
|
||||
}
|
||||
if (properties.isVerticallyCentered) {
|
||||
result.y += rect.height / 2;
|
||||
} else if (properties.isBottom) {
|
||||
result.y += rect.height;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
// private
|
||||
_getTransformOrigin: function() {
|
||||
var result = "";
|
||||
var properties = $.Placement.properties[this.placement];
|
||||
if (!properties) {
|
||||
return result;
|
||||
}
|
||||
if (properties.isLeft) {
|
||||
result = "left";
|
||||
} else if (properties.isRight) {
|
||||
result = "right";
|
||||
}
|
||||
if (properties.isTop) {
|
||||
result += " top";
|
||||
} else if (properties.isBottom) {
|
||||
result += " bottom";
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the overlay settings.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect|Object} location
|
||||
* If an object is specified, the options are the same than the constructor
|
||||
* except for the element which can not be changed.
|
||||
* @param {OpenSeadragon.Placement} placement
|
||||
*/
|
||||
update: function(location, placement) {
|
||||
var options = $.isPlainObject(location) ? location : {
|
||||
location: location,
|
||||
placement: placement
|
||||
};
|
||||
this._init({
|
||||
location: options.location || this.location,
|
||||
placement: options.placement !== undefined ?
|
||||
options.placement : this.placement,
|
||||
onDraw: options.onDraw || this.onDraw,
|
||||
checkResize: options.checkResize || this.checkResize,
|
||||
width: options.width !== undefined ? options.width : this.width,
|
||||
height: options.height !== undefined ? options.height : this.height,
|
||||
rotationMode: options.rotationMode || this.rotationMode
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the current bounds of the overlay in viewport coordinates
|
||||
* @function
|
||||
* @param {OpenSeadragon.Viewport} viewport the viewport
|
||||
* @returns {OpenSeadragon.Rect} overlay bounds
|
||||
*/
|
||||
getBounds: function(viewport) {
|
||||
$.console.assert(viewport,
|
||||
'A viewport must now be passed to Overlay.getBounds.');
|
||||
var width = this.width;
|
||||
var height = this.height;
|
||||
if (width === null || height === null) {
|
||||
var size = viewport.deltaPointsFromPixelsNoRotate(this.size, true);
|
||||
if (width === null) {
|
||||
width = size.x;
|
||||
}
|
||||
if (height === null) {
|
||||
height = size.y;
|
||||
}
|
||||
}
|
||||
var location = this.location.clone();
|
||||
this.adjust(location, new $.Point(width, height));
|
||||
return this._adjustBoundsForRotation(
|
||||
viewport, new $.Rect(location.x, location.y, width, height));
|
||||
},
|
||||
|
||||
// private
|
||||
_adjustBoundsForRotation: function(viewport, bounds) {
|
||||
if (!viewport ||
|
||||
viewport.getRotation(true) === 0 ||
|
||||
this.rotationMode === $.OverlayRotationMode.EXACT) {
|
||||
return bounds;
|
||||
}
|
||||
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX) {
|
||||
// If overlay not fully scalable, BOUNDING_BOX falls back to EXACT
|
||||
if (this.width === null || this.height === null) {
|
||||
return bounds;
|
||||
}
|
||||
// It is easier to just compute the position and size and
|
||||
// convert to viewport coordinates.
|
||||
var positionAndSize = this._getOverlayPositionAndSize(viewport);
|
||||
return viewport.viewerElementToViewportRectangle(new $.Rect(
|
||||
positionAndSize.position.x,
|
||||
positionAndSize.position.y,
|
||||
positionAndSize.size.x,
|
||||
positionAndSize.size.y));
|
||||
}
|
||||
|
||||
// NO_ROTATION case
|
||||
return bounds.rotate(-viewport.getRotation(true),
|
||||
this._getPlacementPoint(bounds));
|
||||
}
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
||||
}(OpenSeadragon));
|
||||
|
|
138
src/placement.js
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* OpenSeadragon - Placement
|
||||
*
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* An enumeration of positions to anchor an element.
|
||||
* @member Placement
|
||||
* @memberOf OpenSeadragon
|
||||
* @static
|
||||
* @readonly
|
||||
* @property {OpenSeadragon.Placement} CENTER
|
||||
* @property {OpenSeadragon.Placement} TOP_LEFT
|
||||
* @property {OpenSeadragon.Placement} TOP
|
||||
* @property {OpenSeadragon.Placement} TOP_RIGHT
|
||||
* @property {OpenSeadragon.Placement} RIGHT
|
||||
* @property {OpenSeadragon.Placement} BOTTOM_RIGHT
|
||||
* @property {OpenSeadragon.Placement} BOTTOM
|
||||
* @property {OpenSeadragon.Placement} BOTTOM_LEFT
|
||||
* @property {OpenSeadragon.Placement} LEFT
|
||||
*/
|
||||
$.Placement = $.freezeObject({
|
||||
CENTER: 0,
|
||||
TOP_LEFT: 1,
|
||||
TOP: 2,
|
||||
TOP_RIGHT: 3,
|
||||
RIGHT: 4,
|
||||
BOTTOM_RIGHT: 5,
|
||||
BOTTOM: 6,
|
||||
BOTTOM_LEFT: 7,
|
||||
LEFT: 8,
|
||||
properties: {
|
||||
0: {
|
||||
isLeft: false,
|
||||
isHorizontallyCentered: true,
|
||||
isRight: false,
|
||||
isTop: false,
|
||||
isVerticallyCentered: true,
|
||||
isBottom: false
|
||||
},
|
||||
1: {
|
||||
isLeft: true,
|
||||
isHorizontallyCentered: false,
|
||||
isRight: false,
|
||||
isTop: true,
|
||||
isVerticallyCentered: false,
|
||||
isBottom: false
|
||||
},
|
||||
2: {
|
||||
isLeft: false,
|
||||
isHorizontallyCentered: true,
|
||||
isRight: false,
|
||||
isTop: true,
|
||||
isVerticallyCentered: false,
|
||||
isBottom: false
|
||||
},
|
||||
3: {
|
||||
isLeft: false,
|
||||
isHorizontallyCentered: false,
|
||||
isRight: true,
|
||||
isTop: true,
|
||||
isVerticallyCentered: false,
|
||||
isBottom: false
|
||||
},
|
||||
4: {
|
||||
isLeft: false,
|
||||
isHorizontallyCentered: false,
|
||||
isRight: true,
|
||||
isTop: false,
|
||||
isVerticallyCentered: true,
|
||||
isBottom: false
|
||||
},
|
||||
5: {
|
||||
isLeft: false,
|
||||
isHorizontallyCentered: false,
|
||||
isRight: true,
|
||||
isTop: false,
|
||||
isVerticallyCentered: false,
|
||||
isBottom: true
|
||||
},
|
||||
6: {
|
||||
isLeft: false,
|
||||
isHorizontallyCentered: true,
|
||||
isRight: false,
|
||||
isTop: false,
|
||||
isVerticallyCentered: false,
|
||||
isBottom: true
|
||||
},
|
||||
7: {
|
||||
isLeft: true,
|
||||
isHorizontallyCentered: false,
|
||||
isRight: false,
|
||||
isTop: false,
|
||||
isVerticallyCentered: false,
|
||||
isBottom: true
|
||||
},
|
||||
8: {
|
||||
isLeft: true,
|
||||
isHorizontallyCentered: false,
|
||||
isRight: false,
|
||||
isTop: false,
|
||||
isVerticallyCentered: true,
|
||||
isBottom: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}(OpenSeadragon));
|
68
src/point.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Point
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -50,16 +50,17 @@ $.Point = function( x, y ) {
|
|||
* @member {Number} x
|
||||
* @memberof OpenSeadragon.Point#
|
||||
*/
|
||||
this.x = typeof ( x ) == "number" ? x : 0;
|
||||
this.x = typeof ( x ) === "number" ? x : 0;
|
||||
/**
|
||||
* The vector component 'y'.
|
||||
* @member {Number} y
|
||||
* @memberof OpenSeadragon.Point#
|
||||
*/
|
||||
this.y = typeof ( y ) == "number" ? y : 0;
|
||||
this.y = typeof ( y ) === "number" ? y : 0;
|
||||
};
|
||||
|
||||
$.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
|
||||
/** @lends OpenSeadragon.Point.prototype */
|
||||
$.Point.prototype = {
|
||||
/**
|
||||
* @function
|
||||
* @returns {OpenSeadragon.Point} a duplicate of this Point
|
||||
|
@ -83,10 +84,10 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
|
|||
},
|
||||
|
||||
/**
|
||||
* Substract another Point to this point and return a new Point.
|
||||
* Subtract another Point to this point and return a new Point.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point} point The point to substract vector components.
|
||||
* @returns {OpenSeadragon.Point} A new point representing the substraction of the
|
||||
* @param {OpenSeadragon.Point} point The point to subtract vector components.
|
||||
* @returns {OpenSeadragon.Point} A new point representing the subtraction of the
|
||||
* vector components
|
||||
*/
|
||||
minus: function( point ) {
|
||||
|
@ -147,6 +148,18 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
|
|||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute the squared distance between this point and another point.
|
||||
* Useful for optimizing things like comparing distances.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point} point The point to compute the squared distance with.
|
||||
* @returns {Number} The squared distance between the 2 points
|
||||
*/
|
||||
squaredDistanceTo: function( point ) {
|
||||
return Math.pow( this.x - point.x, 2 ) +
|
||||
Math.pow( this.y - point.y, 2 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply a function to each coordinate of this point and return a new point.
|
||||
* @function
|
||||
|
@ -179,14 +192,43 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
|
|||
* From http://stackoverflow.com/questions/4465931/rotate-rectangle-around-a-point
|
||||
* @function
|
||||
* @param {Number} degress to rotate around the pivot.
|
||||
* @param {OpenSeadragon.Point} pivot Point about which to rotate.
|
||||
* @param {OpenSeadragon.Point} [pivot=(0,0)] Point around which to rotate.
|
||||
* Defaults to the origin.
|
||||
* @returns {OpenSeadragon.Point}. A new point representing the point rotated around the specified pivot
|
||||
*/
|
||||
rotate: function ( degrees, pivot ) {
|
||||
var angle = degrees * Math.PI / 180.0,
|
||||
x = Math.cos( angle ) * ( this.x - pivot.x ) - Math.sin( angle ) * ( this.y - pivot.y ) + pivot.x,
|
||||
y = Math.sin( angle ) * ( this.x - pivot.x ) + Math.cos( angle ) * ( this.y - pivot.y ) + pivot.y;
|
||||
return new $.Point( x, y );
|
||||
rotate: function (degrees, pivot) {
|
||||
pivot = pivot || new $.Point(0, 0);
|
||||
var cos;
|
||||
var sin;
|
||||
// Avoid float computations when possible
|
||||
if (degrees % 90 === 0) {
|
||||
var d = $.positiveModulo(degrees, 360);
|
||||
switch (d) {
|
||||
case 0:
|
||||
cos = 1;
|
||||
sin = 0;
|
||||
break;
|
||||
case 90:
|
||||
cos = 0;
|
||||
sin = 1;
|
||||
break;
|
||||
case 180:
|
||||
cos = -1;
|
||||
sin = 0;
|
||||
break;
|
||||
case 270:
|
||||
cos = 0;
|
||||
sin = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
var angle = degrees * Math.PI / 180.0;
|
||||
cos = Math.cos(angle);
|
||||
sin = Math.sin(angle);
|
||||
}
|
||||
var x = cos * (this.x - pivot.x) - sin * (this.y - pivot.y) + pivot.x;
|
||||
var y = sin * (this.x - pivot.x) + cos * (this.y - pivot.y) + pivot.y;
|
||||
return new $.Point(x, y);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
362
src/priorityqueue.js
Normal file
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* OpenSeadragon - Queue
|
||||
*
|
||||
* Copyright (C) 2024 OpenSeadragon contributors (modified)
|
||||
* Copyright (C) Google Inc., The Closure Library Authors.
|
||||
* https://github.com/google/closure-library
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* @class PriorityQueue
|
||||
* @classdesc Fast priority queue. Implemented as a Heap.
|
||||
*/
|
||||
$.PriorityQueue = class {
|
||||
|
||||
/**
|
||||
* @param {?OpenSeadragon.PriorityQueue} optHeap Optional Heap or
|
||||
* Object to initialize heap with.
|
||||
*/
|
||||
constructor(optHeap = undefined) {
|
||||
/**
|
||||
* The nodes of the heap.
|
||||
*
|
||||
* This is a densely packed array containing all nodes of the heap, using
|
||||
* the standard flat representation of a tree as an array (i.e. element [0]
|
||||
* at the top, with [1] and [2] as the second row, [3] through [6] as the
|
||||
* third, etc). Thus, the children of element `i` are `2i+1` and `2i+2`, and
|
||||
* the parent of element `i` is `⌊(i-1)/2⌋`.
|
||||
*
|
||||
* The only invariant is that children's keys must be greater than parents'.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
this.nodes_ = [];
|
||||
|
||||
if (optHeap) {
|
||||
this.insertAll(optHeap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given value into the heap with the given key.
|
||||
* @param {K} key The key.
|
||||
* @param {V} value The value.
|
||||
*/
|
||||
insert(key, value) {
|
||||
this.insertNode(new Node(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert node item.
|
||||
* @param node
|
||||
*/
|
||||
insertNode(node) {
|
||||
const nodes = this.nodes_;
|
||||
node.index = nodes.length;
|
||||
nodes.push(node);
|
||||
this.moveUp_(node.index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple key-value pairs from another Heap or Object
|
||||
* @param {?OpenSeadragon.PriorityQueue} heap Object containing the data to add.
|
||||
*/
|
||||
insertAll(heap) {
|
||||
let keys, values;
|
||||
if (heap instanceof $.PriorityQueue) {
|
||||
keys = heap.getKeys();
|
||||
values = heap.getValues();
|
||||
|
||||
// If it is a heap and the current heap is empty, I can rely on the fact
|
||||
// that the keys/values are in the correct order to put in the underlying
|
||||
// structure.
|
||||
if (this.getCount() <= 0) {
|
||||
const nodes = this.nodes_;
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const node = new Node(keys[i], values[i]);
|
||||
node.index = nodes.length;
|
||||
nodes.push(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
throw "insertAll supports only OpenSeadragon.PriorityQueue object!";
|
||||
}
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
this.insert(keys[i], values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and removes the root value of this heap.
|
||||
* @return {Node} The root node item removed from the root of the heap. Returns
|
||||
* undefined if the heap is empty.
|
||||
*/
|
||||
remove() {
|
||||
const nodes = this.nodes_;
|
||||
const count = nodes.length;
|
||||
const rootNode = nodes[0];
|
||||
if (count <= 0) {
|
||||
return undefined;
|
||||
} else if (count == 1) { // eslint-disable-line
|
||||
nodes.length = 0;
|
||||
} else {
|
||||
nodes[0] = nodes.pop();
|
||||
if (nodes[0]) {
|
||||
nodes[0].index = 0;
|
||||
}
|
||||
this.moveDown_(0);
|
||||
}
|
||||
if (rootNode) {
|
||||
delete rootNode.index;
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves but does not remove the root value of this heap.
|
||||
* @return {V} The value at the root of the heap. Returns
|
||||
* undefined if the heap is empty.
|
||||
*/
|
||||
peek() {
|
||||
const nodes = this.nodes_;
|
||||
if (nodes.length == 0) { // eslint-disable-line
|
||||
return undefined;
|
||||
}
|
||||
return nodes[0].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves but does not remove the key of the root node of this heap.
|
||||
* @return {string} The key at the root of the heap. Returns undefined if the
|
||||
* heap is empty.
|
||||
*/
|
||||
peekKey() {
|
||||
return this.nodes_[0] && this.nodes_[0].key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the node up in hierarchy
|
||||
* @param {Node} node the node
|
||||
* @param {K} key new ley, must be smaller than current key
|
||||
*/
|
||||
decreaseKey(node, key) {
|
||||
if (node.index === undefined) {
|
||||
node.key = key;
|
||||
this.insertNode(node);
|
||||
} else {
|
||||
node.key = key;
|
||||
this.moveUp_(node.index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the node at the given index down to its proper place in the heap.
|
||||
* @param {number} index The index of the node to move down.
|
||||
* @private
|
||||
*/
|
||||
moveDown_(index) {
|
||||
const nodes = this.nodes_;
|
||||
const count = nodes.length;
|
||||
|
||||
// Save the node being moved down.
|
||||
const node = nodes[index];
|
||||
// While the current node has a child.
|
||||
while (index < (count >> 1)) {
|
||||
const leftChildIndex = this.getLeftChildIndex_(index);
|
||||
const rightChildIndex = this.getRightChildIndex_(index);
|
||||
|
||||
// Determine the index of the smaller child.
|
||||
const smallerChildIndex = rightChildIndex < count &&
|
||||
nodes[rightChildIndex].key < nodes[leftChildIndex].key ?
|
||||
rightChildIndex :
|
||||
leftChildIndex;
|
||||
|
||||
// If the node being moved down is smaller than its children, the node
|
||||
// has found the correct index it should be at.
|
||||
if (nodes[smallerChildIndex].key > node.key) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If not, then take the smaller child as the current node.
|
||||
nodes[index] = nodes[smallerChildIndex];
|
||||
nodes[index].index = index;
|
||||
index = smallerChildIndex;
|
||||
}
|
||||
nodes[index] = node;
|
||||
if (node) {
|
||||
node.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the node at the given index up to its proper place in the heap.
|
||||
* @param {number} index The index of the node to move up.
|
||||
* @private
|
||||
*/
|
||||
moveUp_(index) {
|
||||
const nodes = this.nodes_;
|
||||
const node = nodes[index];
|
||||
|
||||
// While the node being moved up is not at the root.
|
||||
while (index > 0) {
|
||||
// If the parent is greater than the node being moved up, move the parent
|
||||
// down.
|
||||
const parentIndex = this.getParentIndex_(index);
|
||||
if (nodes[parentIndex].key > node.key) {
|
||||
nodes[index] = nodes[parentIndex];
|
||||
nodes[index].index = index;
|
||||
index = parentIndex;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
nodes[index] = node;
|
||||
if (node) {
|
||||
node.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the left child of the node at the given index.
|
||||
* @param {number} index The index of the node to get the left child for.
|
||||
* @return {number} The index of the left child.
|
||||
* @private
|
||||
*/
|
||||
getLeftChildIndex_(index) {
|
||||
return index * 2 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the right child of the node at the given index.
|
||||
* @param {number} index The index of the node to get the right child for.
|
||||
* @return {number} The index of the right child.
|
||||
* @private
|
||||
*/
|
||||
getRightChildIndex_(index) {
|
||||
return index * 2 + 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the parent of the node at the given index.
|
||||
* @param {number} index The index of the node to get the parent for.
|
||||
* @return {number} The index of the parent.
|
||||
* @private
|
||||
*/
|
||||
getParentIndex_(index) {
|
||||
return (index - 1) >> 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values of the heap.
|
||||
* @return {!Array<*>} The values in the heap.
|
||||
*/
|
||||
getValues() {
|
||||
const nodes = this.nodes_;
|
||||
const rv = [];
|
||||
const l = nodes.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
rv.push(nodes[i].value);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the keys of the heap.
|
||||
* @return {!Array<string>} The keys in the heap.
|
||||
*/
|
||||
getKeys() {
|
||||
const nodes = this.nodes_;
|
||||
const rv = [];
|
||||
const l = nodes.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
rv.push(nodes[i].key);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the heap contains the given value.
|
||||
* @param {V} val The value to check for.
|
||||
* @return {boolean} Whether the heap contains the value.
|
||||
*/
|
||||
containsValue(val) {
|
||||
return this.nodes_.some((node) => node.value == val); // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the heap contains the given key.
|
||||
* @param {string} key The key to check for.
|
||||
* @return {boolean} Whether the heap contains the key.
|
||||
*/
|
||||
containsKey(key) {
|
||||
return this.nodes_.some((node) => node.value == key); // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a heap and returns a new heap
|
||||
* @return {!OpenSeadragon.PriorityQueue} A new Heap with the same key-value pairs.
|
||||
*/
|
||||
clone() {
|
||||
return new $.PriorityQueue(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of key-value pairs in the map
|
||||
* @return {number} The number of pairs.
|
||||
*/
|
||||
getCount() {
|
||||
return this.nodes_.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this heap contains no elements.
|
||||
* @return {boolean} Whether this heap contains no elements.
|
||||
*/
|
||||
isEmpty() {
|
||||
return this.nodes_.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the heap.
|
||||
*/
|
||||
clear() {
|
||||
this.nodes_.length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
$.PriorityQueue.Node = class {
|
||||
constructor(key, value) {
|
||||
/**
|
||||
* The key.
|
||||
* @type {K}
|
||||
* @private
|
||||
*/
|
||||
this.key = key;
|
||||
|
||||
/**
|
||||
* The value.
|
||||
* @type {V}
|
||||
* @private
|
||||
*/
|
||||
this.value = value;
|
||||
|
||||
/**
|
||||
* The node index value. Updated in the heap.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new Node(this.key, this.value);
|
||||
}
|
||||
};
|
||||
|
||||
}(OpenSeadragon));
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Profiler
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -68,7 +68,8 @@ $.Profiler = function() {
|
|||
this.maxIdleTime = 0;
|
||||
};
|
||||
|
||||
$.Profiler.prototype = /** @lends OpenSeadragon.Profiler.prototype */{
|
||||
/** @lends OpenSeadragon.Profiler.prototype */
|
||||
$.Profiler.prototype = {
|
||||
|
||||
/**
|
||||
* @function
|
||||
|
|
470
src/rectangle.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Rect
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -32,55 +32,126 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* @class Rect
|
||||
* @classdesc A Rectangle really represents a 2x2 matrix where each row represents a
|
||||
* 2 dimensional vector component, the first is (x,y) and the second is
|
||||
* (width, height). The latter component implies the equation of a simple
|
||||
* plane.
|
||||
* @classdesc A Rectangle is described by it top left coordinates (x, y), width,
|
||||
* height and degrees of rotation around (x, y).
|
||||
* Note that the coordinate system used is the one commonly used with images:
|
||||
* x increases when going to the right
|
||||
* y increases when going to the bottom
|
||||
* degrees increases clockwise with 0 being the horizontal
|
||||
*
|
||||
* The constructor normalizes the rectangle to always have 0 <= degrees < 90
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @param {Number} x The vector component 'x'.
|
||||
* @param {Number} y The vector component 'y'.
|
||||
* @param {Number} width The vector component 'height'.
|
||||
* @param {Number} height The vector component 'width'.
|
||||
* @param {Number} [x=0] The vector component 'x'.
|
||||
* @param {Number} [y=0] The vector component 'y'.
|
||||
* @param {Number} [width=0] The vector component 'width'.
|
||||
* @param {Number} [height=0] The vector component 'height'.
|
||||
* @param {Number} [degrees=0] Rotation of the rectangle around (x,y) in degrees.
|
||||
*/
|
||||
$.Rect = function( x, y, width, height ) {
|
||||
$.Rect = function(x, y, width, height, degrees) {
|
||||
/**
|
||||
* The vector component 'x'.
|
||||
* @member {Number} x
|
||||
* @memberof OpenSeadragon.Rect#
|
||||
*/
|
||||
this.x = typeof ( x ) == "number" ? x : 0;
|
||||
this.x = typeof (x) === "number" ? x : 0;
|
||||
/**
|
||||
* The vector component 'y'.
|
||||
* @member {Number} y
|
||||
* @memberof OpenSeadragon.Rect#
|
||||
*/
|
||||
this.y = typeof ( y ) == "number" ? y : 0;
|
||||
this.y = typeof (y) === "number" ? y : 0;
|
||||
/**
|
||||
* The vector component 'width'.
|
||||
* @member {Number} width
|
||||
* @memberof OpenSeadragon.Rect#
|
||||
*/
|
||||
this.width = typeof ( width ) == "number" ? width : 0;
|
||||
this.width = typeof (width) === "number" ? width : 0;
|
||||
/**
|
||||
* The vector component 'height'.
|
||||
* @member {Number} height
|
||||
* @memberof OpenSeadragon.Rect#
|
||||
*/
|
||||
this.height = typeof ( height ) == "number" ? height : 0;
|
||||
this.height = typeof (height) === "number" ? height : 0;
|
||||
|
||||
/**
|
||||
* The rotation of the rectangle, in degrees.
|
||||
* @member {Number} degrees
|
||||
* @memberof OpenSeadragon.Rect#
|
||||
*/
|
||||
this.degrees = typeof (degrees) === "number" ? degrees : 0;
|
||||
|
||||
// Normalizes the rectangle.
|
||||
this.degrees = $.positiveModulo(this.degrees, 360);
|
||||
var newTopLeft, newWidth;
|
||||
if (this.degrees >= 270) {
|
||||
newTopLeft = this.getTopRight();
|
||||
this.x = newTopLeft.x;
|
||||
this.y = newTopLeft.y;
|
||||
newWidth = this.height;
|
||||
this.height = this.width;
|
||||
this.width = newWidth;
|
||||
this.degrees -= 270;
|
||||
} else if (this.degrees >= 180) {
|
||||
newTopLeft = this.getBottomRight();
|
||||
this.x = newTopLeft.x;
|
||||
this.y = newTopLeft.y;
|
||||
this.degrees -= 180;
|
||||
} else if (this.degrees >= 90) {
|
||||
newTopLeft = this.getBottomLeft();
|
||||
this.x = newTopLeft.x;
|
||||
this.y = newTopLeft.y;
|
||||
newWidth = this.height;
|
||||
this.height = this.width;
|
||||
this.width = newWidth;
|
||||
this.degrees -= 90;
|
||||
}
|
||||
};
|
||||
|
||||
$.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
||||
/**
|
||||
* Builds a rectangle having the 3 specified points as summits.
|
||||
* @static
|
||||
* @memberof OpenSeadragon.Rect
|
||||
* @param {OpenSeadragon.Point} topLeft
|
||||
* @param {OpenSeadragon.Point} topRight
|
||||
* @param {OpenSeadragon.Point} bottomLeft
|
||||
* @returns {OpenSeadragon.Rect}
|
||||
*/
|
||||
$.Rect.fromSummits = function(topLeft, topRight, bottomLeft) {
|
||||
var width = topLeft.distanceTo(topRight);
|
||||
var height = topLeft.distanceTo(bottomLeft);
|
||||
var diff = topRight.minus(topLeft);
|
||||
var radians = Math.atan(diff.y / diff.x);
|
||||
if (diff.x < 0) {
|
||||
radians += Math.PI;
|
||||
} else if (diff.y < 0) {
|
||||
radians += 2 * Math.PI;
|
||||
}
|
||||
return new $.Rect(
|
||||
topLeft.x,
|
||||
topLeft.y,
|
||||
width,
|
||||
height,
|
||||
radians / Math.PI * 180);
|
||||
};
|
||||
|
||||
/** @lends OpenSeadragon.Rect.prototype */
|
||||
$.Rect.prototype = {
|
||||
/**
|
||||
* @function
|
||||
* @returns {OpenSeadragon.Rect} a duplicate of this Rect
|
||||
*/
|
||||
clone: function() {
|
||||
return new $.Rect(this.x, this.y, this.width, this.height);
|
||||
return new $.Rect(
|
||||
this.x,
|
||||
this.y,
|
||||
this.width,
|
||||
this.height,
|
||||
this.degrees);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -114,10 +185,8 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
|||
* the rectangle.
|
||||
*/
|
||||
getBottomRight: function() {
|
||||
return new $.Point(
|
||||
this.x + this.width,
|
||||
this.y + this.height
|
||||
);
|
||||
return new $.Point(this.x + this.width, this.y + this.height)
|
||||
.rotate(this.degrees, this.getTopLeft());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -128,10 +197,8 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
|||
* the rectangle.
|
||||
*/
|
||||
getTopRight: function() {
|
||||
return new $.Point(
|
||||
this.x + this.width,
|
||||
this.y
|
||||
);
|
||||
return new $.Point(this.x + this.width, this.y)
|
||||
.rotate(this.degrees, this.getTopLeft());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -142,10 +209,8 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
|||
* the rectangle.
|
||||
*/
|
||||
getBottomLeft: function() {
|
||||
return new $.Point(
|
||||
this.x,
|
||||
this.y + this.height
|
||||
);
|
||||
return new $.Point(this.x, this.y + this.height)
|
||||
.rotate(this.degrees, this.getTopLeft());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -158,117 +223,323 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
|||
return new $.Point(
|
||||
this.x + this.width / 2.0,
|
||||
this.y + this.height / 2.0
|
||||
);
|
||||
).rotate(this.degrees, this.getTopLeft());
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the width and height component as a vector OpenSeadragon.Point
|
||||
* @function
|
||||
* @returns {OpenSeadragon.Point} The 2 dimensional vector representing the
|
||||
* the width and height of the rectangle.
|
||||
* width and height of the rectangle.
|
||||
*/
|
||||
getSize: function() {
|
||||
return new $.Point( this.width, this.height );
|
||||
return new $.Point(this.width, this.height);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if two Rectangles have equivalent components.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.
|
||||
* @return {Boolean} 'true' if all components are equal, otherwise 'false'.
|
||||
* @returns {Boolean} 'true' if all components are equal, otherwise 'false'.
|
||||
*/
|
||||
equals: function( other ) {
|
||||
return ( other instanceof $.Rect ) &&
|
||||
( this.x === other.x ) &&
|
||||
( this.y === other.y ) &&
|
||||
( this.width === other.width ) &&
|
||||
( this.height === other.height );
|
||||
equals: function(other) {
|
||||
return (other instanceof $.Rect) &&
|
||||
this.x === other.x &&
|
||||
this.y === other.y &&
|
||||
this.width === other.width &&
|
||||
this.height === other.height &&
|
||||
this.degrees === other.degrees;
|
||||
},
|
||||
|
||||
/**
|
||||
* Multiply all dimensions in this Rect by a factor and return a new Rect.
|
||||
* Multiply all dimensions (except degrees) in this Rect by a factor and
|
||||
* return a new Rect.
|
||||
* @function
|
||||
* @param {Number} factor The factor to multiply vector components.
|
||||
* @returns {OpenSeadragon.Rect} A new rect representing the multiplication
|
||||
* of the vector components by the factor
|
||||
*/
|
||||
times: function( factor ) {
|
||||
return new OpenSeadragon.Rect(
|
||||
times: function(factor) {
|
||||
return new $.Rect(
|
||||
this.x * factor,
|
||||
this.y * factor,
|
||||
this.width * factor,
|
||||
this.height * factor
|
||||
);
|
||||
this.height * factor,
|
||||
this.degrees);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the smallest rectangle that will contain this and the given rectangle.
|
||||
* Translate/move this Rect by a vector and return new Rect.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point} delta The translation vector.
|
||||
* @returns {OpenSeadragon.Rect} A new rect with altered position
|
||||
*/
|
||||
translate: function(delta) {
|
||||
return new $.Rect(
|
||||
this.x + delta.x,
|
||||
this.y + delta.y,
|
||||
this.width,
|
||||
this.height,
|
||||
this.degrees);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the smallest rectangle that will contain this and the given
|
||||
* rectangle bounding boxes.
|
||||
* @param {OpenSeadragon.Rect} rect
|
||||
* @return {OpenSeadragon.Rect} The new rectangle.
|
||||
* @returns {OpenSeadragon.Rect} The new rectangle.
|
||||
*/
|
||||
// ----------
|
||||
union: function(rect) {
|
||||
var left = Math.min(this.x, rect.x);
|
||||
var top = Math.min(this.y, rect.y);
|
||||
var right = Math.max(this.x + this.width, rect.x + rect.width);
|
||||
var bottom = Math.max(this.y + this.height, rect.y + rect.height);
|
||||
var thisBoundingBox = this.getBoundingBox();
|
||||
var otherBoundingBox = rect.getBoundingBox();
|
||||
|
||||
return new OpenSeadragon.Rect(left, top, right - left, bottom - top);
|
||||
var left = Math.min(thisBoundingBox.x, otherBoundingBox.x);
|
||||
var top = Math.min(thisBoundingBox.y, otherBoundingBox.y);
|
||||
var right = Math.max(
|
||||
thisBoundingBox.x + thisBoundingBox.width,
|
||||
otherBoundingBox.x + otherBoundingBox.width);
|
||||
var bottom = Math.max(
|
||||
thisBoundingBox.y + thisBoundingBox.height,
|
||||
otherBoundingBox.y + otherBoundingBox.height);
|
||||
|
||||
return new $.Rect(
|
||||
left,
|
||||
top,
|
||||
right - left,
|
||||
bottom - top);
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotates a rectangle around a point. Currently only 90, 180, and 270
|
||||
* degrees are supported.
|
||||
* @function
|
||||
* @param {Number} degrees The angle in degrees to rotate.
|
||||
* @param {OpenSeadragon.Point} pivot The point about which to rotate.
|
||||
* Defaults to the center of the rectangle.
|
||||
* @return {OpenSeadragon.Rect}
|
||||
* Returns the bounding box of the intersection of this rectangle with the
|
||||
* given rectangle.
|
||||
* @param {OpenSeadragon.Rect} rect
|
||||
* @returns {OpenSeadragon.Rect} the bounding box of the intersection
|
||||
* or null if the rectangles don't intersect.
|
||||
*/
|
||||
rotate: function( degrees, pivot ) {
|
||||
// TODO support arbitrary rotation
|
||||
var width = this.width,
|
||||
height = this.height,
|
||||
newTopLeft;
|
||||
intersection: function(rect) {
|
||||
// Simplified version of Weiler Atherton clipping algorithm
|
||||
// https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm
|
||||
// Because we just want the bounding box of the intersection,
|
||||
// we can just compute the bounding box of:
|
||||
// 1. all the summits of this which are inside rect
|
||||
// 2. all the summits of rect which are inside this
|
||||
// 3. all the intersections of rect and this
|
||||
var EPSILON = 0.0000000001;
|
||||
|
||||
degrees = ( degrees + 360 ) % 360;
|
||||
if (degrees % 90 !== 0) {
|
||||
throw new Error('Currently only 0, 90, 180, and 270 degrees are supported.');
|
||||
var intersectionPoints = [];
|
||||
|
||||
var thisTopLeft = this.getTopLeft();
|
||||
if (rect.containsPoint(thisTopLeft, EPSILON)) {
|
||||
intersectionPoints.push(thisTopLeft);
|
||||
}
|
||||
var thisTopRight = this.getTopRight();
|
||||
if (rect.containsPoint(thisTopRight, EPSILON)) {
|
||||
intersectionPoints.push(thisTopRight);
|
||||
}
|
||||
var thisBottomLeft = this.getBottomLeft();
|
||||
if (rect.containsPoint(thisBottomLeft, EPSILON)) {
|
||||
intersectionPoints.push(thisBottomLeft);
|
||||
}
|
||||
var thisBottomRight = this.getBottomRight();
|
||||
if (rect.containsPoint(thisBottomRight, EPSILON)) {
|
||||
intersectionPoints.push(thisBottomRight);
|
||||
}
|
||||
|
||||
if( degrees === 0 ){
|
||||
return new $.Rect(
|
||||
this.x,
|
||||
this.y,
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
var rectTopLeft = rect.getTopLeft();
|
||||
if (this.containsPoint(rectTopLeft, EPSILON)) {
|
||||
intersectionPoints.push(rectTopLeft);
|
||||
}
|
||||
var rectTopRight = rect.getTopRight();
|
||||
if (this.containsPoint(rectTopRight, EPSILON)) {
|
||||
intersectionPoints.push(rectTopRight);
|
||||
}
|
||||
var rectBottomLeft = rect.getBottomLeft();
|
||||
if (this.containsPoint(rectBottomLeft, EPSILON)) {
|
||||
intersectionPoints.push(rectBottomLeft);
|
||||
}
|
||||
var rectBottomRight = rect.getBottomRight();
|
||||
if (this.containsPoint(rectBottomRight, EPSILON)) {
|
||||
intersectionPoints.push(rectBottomRight);
|
||||
}
|
||||
|
||||
var thisSegments = this._getSegments();
|
||||
var rectSegments = rect._getSegments();
|
||||
for (var i = 0; i < thisSegments.length; i++) {
|
||||
var thisSegment = thisSegments[i];
|
||||
for (var j = 0; j < rectSegments.length; j++) {
|
||||
var rectSegment = rectSegments[j];
|
||||
var intersect = getIntersection(thisSegment[0], thisSegment[1],
|
||||
rectSegment[0], rectSegment[1]);
|
||||
if (intersect) {
|
||||
intersectionPoints.push(intersect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get intersection point of segments [a,b] and [c,d]
|
||||
function getIntersection(a, b, c, d) {
|
||||
// http://stackoverflow.com/a/1968345/1440403
|
||||
var abVector = b.minus(a);
|
||||
var cdVector = d.minus(c);
|
||||
|
||||
var denom = -cdVector.x * abVector.y + abVector.x * cdVector.y;
|
||||
if (denom === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var s = (abVector.x * (a.y - c.y) - abVector.y * (a.x - c.x)) / denom;
|
||||
var t = (cdVector.x * (a.y - c.y) - cdVector.y * (a.x - c.x)) / denom;
|
||||
|
||||
if (-EPSILON <= s && s <= 1 - EPSILON &&
|
||||
-EPSILON <= t && t <= 1 - EPSILON) {
|
||||
return new $.Point(a.x + t * abVector.x, a.y + t * abVector.y);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (intersectionPoints.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var minX = intersectionPoints[0].x;
|
||||
var maxX = intersectionPoints[0].x;
|
||||
var minY = intersectionPoints[0].y;
|
||||
var maxY = intersectionPoints[0].y;
|
||||
for (var k = 1; k < intersectionPoints.length; k++) {
|
||||
var point = intersectionPoints[k];
|
||||
if (point.x < minX) {
|
||||
minX = point.x;
|
||||
}
|
||||
if (point.x > maxX) {
|
||||
maxX = point.x;
|
||||
}
|
||||
if (point.y < minY) {
|
||||
minY = point.y;
|
||||
}
|
||||
if (point.y > maxY) {
|
||||
maxY = point.y;
|
||||
}
|
||||
}
|
||||
return new $.Rect(minX, minY, maxX - minX, maxY - minY);
|
||||
},
|
||||
|
||||
// private
|
||||
_getSegments: function() {
|
||||
var topLeft = this.getTopLeft();
|
||||
var topRight = this.getTopRight();
|
||||
var bottomLeft = this.getBottomLeft();
|
||||
var bottomRight = this.getBottomRight();
|
||||
return [[topLeft, topRight],
|
||||
[topRight, bottomRight],
|
||||
[bottomRight, bottomLeft],
|
||||
[bottomLeft, topLeft]];
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotates a rectangle around a point.
|
||||
* @function
|
||||
* @param {Number} degrees The angle in degrees to rotate.
|
||||
* @param {OpenSeadragon.Point} [pivot] The point about which to rotate.
|
||||
* Defaults to the center of the rectangle.
|
||||
* @returns {OpenSeadragon.Rect}
|
||||
*/
|
||||
rotate: function(degrees, pivot) {
|
||||
degrees = $.positiveModulo(degrees, 360);
|
||||
if (degrees === 0) {
|
||||
return this.clone();
|
||||
}
|
||||
|
||||
pivot = pivot || this.getCenter();
|
||||
var newTopLeft = this.getTopLeft().rotate(degrees, pivot);
|
||||
var newTopRight = this.getTopRight().rotate(degrees, pivot);
|
||||
|
||||
switch ( degrees ) {
|
||||
case 90:
|
||||
newTopLeft = this.getBottomLeft();
|
||||
width = this.height;
|
||||
height = this.width;
|
||||
break;
|
||||
case 180:
|
||||
newTopLeft = this.getBottomRight();
|
||||
break;
|
||||
case 270:
|
||||
newTopLeft = this.getTopRight();
|
||||
width = this.height;
|
||||
height = this.width;
|
||||
break;
|
||||
default:
|
||||
newTopLeft = this.getTopLeft();
|
||||
break;
|
||||
var diff = newTopRight.minus(newTopLeft);
|
||||
// Handle floating point error
|
||||
diff = diff.apply(function(x) {
|
||||
var EPSILON = 1e-15;
|
||||
return Math.abs(x) < EPSILON ? 0 : x;
|
||||
});
|
||||
var radians = Math.atan(diff.y / diff.x);
|
||||
if (diff.x < 0) {
|
||||
radians += Math.PI;
|
||||
} else if (diff.y < 0) {
|
||||
radians += 2 * Math.PI;
|
||||
}
|
||||
return new $.Rect(
|
||||
newTopLeft.x,
|
||||
newTopLeft.y,
|
||||
this.width,
|
||||
this.height,
|
||||
radians / Math.PI * 180);
|
||||
},
|
||||
|
||||
newTopLeft = newTopLeft.rotate(degrees, pivot);
|
||||
/**
|
||||
* Retrieves the smallest horizontal (degrees=0) rectangle which contains
|
||||
* this rectangle.
|
||||
* @returns {OpenSeadragon.Rect}
|
||||
*/
|
||||
getBoundingBox: function() {
|
||||
if (this.degrees === 0) {
|
||||
return this.clone();
|
||||
}
|
||||
var topLeft = this.getTopLeft();
|
||||
var topRight = this.getTopRight();
|
||||
var bottomLeft = this.getBottomLeft();
|
||||
var bottomRight = this.getBottomRight();
|
||||
var minX = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
|
||||
var maxX = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
|
||||
var minY = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
|
||||
var maxY = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
|
||||
return new $.Rect(
|
||||
minX,
|
||||
minY,
|
||||
maxX - minX,
|
||||
maxY - minY);
|
||||
},
|
||||
|
||||
return new $.Rect(newTopLeft.x, newTopLeft.y, width, height);
|
||||
/**
|
||||
* Retrieves the smallest horizontal (degrees=0) rectangle which contains
|
||||
* this rectangle and has integers x, y, width and height
|
||||
* @returns {OpenSeadragon.Rect}
|
||||
*/
|
||||
getIntegerBoundingBox: function() {
|
||||
var boundingBox = this.getBoundingBox();
|
||||
var x = Math.floor(boundingBox.x);
|
||||
var y = Math.floor(boundingBox.y);
|
||||
var width = Math.ceil(boundingBox.width + boundingBox.x - x);
|
||||
var height = Math.ceil(boundingBox.height + boundingBox.y - y);
|
||||
return new $.Rect(x, y, width, height);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether a point is inside this rectangle (edge included).
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point} point
|
||||
* @param {Number} [epsilon=0] the margin of error allowed
|
||||
* @returns {Boolean} true if the point is inside this rectangle, false
|
||||
* otherwise.
|
||||
*/
|
||||
containsPoint: function(point, epsilon) {
|
||||
epsilon = epsilon || 0;
|
||||
|
||||
// See http://stackoverflow.com/a/2752754/1440403 for explanation
|
||||
var topLeft = this.getTopLeft();
|
||||
var topRight = this.getTopRight();
|
||||
var bottomLeft = this.getBottomLeft();
|
||||
var topDiff = topRight.minus(topLeft);
|
||||
var leftDiff = bottomLeft.minus(topLeft);
|
||||
|
||||
return ((point.x - topLeft.x) * topDiff.x +
|
||||
(point.y - topLeft.y) * topDiff.y >= -epsilon) &&
|
||||
|
||||
((point.x - topRight.x) * topDiff.x +
|
||||
(point.y - topRight.y) * topDiff.y <= epsilon) &&
|
||||
|
||||
((point.x - topLeft.x) * leftDiff.x +
|
||||
(point.y - topLeft.y) * leftDiff.y >= -epsilon) &&
|
||||
|
||||
((point.x - bottomLeft.x) * leftDiff.x +
|
||||
(point.y - bottomLeft.y) * leftDiff.y <= epsilon);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -279,13 +550,14 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
|||
*/
|
||||
toString: function() {
|
||||
return "[" +
|
||||
(Math.round(this.x*100) / 100) + "," +
|
||||
(Math.round(this.y*100) / 100) + "," +
|
||||
(Math.round(this.width*100) / 100) + "x" +
|
||||
(Math.round(this.height*100) / 100) +
|
||||
"]";
|
||||
(Math.round(this.x * 100) / 100) + ", " +
|
||||
(Math.round(this.y * 100) / 100) + ", " +
|
||||
(Math.round(this.width * 100) / 100) + "x" +
|
||||
(Math.round(this.height * 100) / 100) + ", " +
|
||||
(Math.round(this.degrees * 100) / 100) + "deg" +
|
||||
"]";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}( OpenSeadragon ));
|
||||
}(OpenSeadragon));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - ReferenceStrip
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -46,14 +46,14 @@ var THIS = {};
|
|||
*
|
||||
* This idea is a reexpression of the idea of dzi collections
|
||||
* which allows a clearer algorithm to reuse the tile sources already
|
||||
* supported by OpenSeadragon, in heterogenious or homogenious
|
||||
* supported by OpenSeadragon, in heterogeneous or homogeneous
|
||||
* sequences just like mixed groups already supported by the viewer
|
||||
* for the purpose of image sequnces.
|
||||
*
|
||||
* TODO: The difficult part of this feature is figuring out how to express
|
||||
* this functionality as a combination of the functionality already
|
||||
* provided by Drawer, Viewport, TileSource, and Navigator. It may
|
||||
* require better abstraction at those points in order to effeciently
|
||||
* require better abstraction at those points in order to efficiently
|
||||
* reuse those paradigms.
|
||||
*/
|
||||
/**
|
||||
|
@ -85,24 +85,19 @@ $.ReferenceStrip = function ( options ) {
|
|||
scroll: $.DEFAULT_SETTINGS.referenceStripScroll,
|
||||
clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold
|
||||
}, options, {
|
||||
//required overrides
|
||||
element: this.element,
|
||||
//These need to be overridden to prevent recursion since
|
||||
//the navigator is a viewer and a viewer has a navigator
|
||||
showNavigator: false,
|
||||
mouseNavEnabled: false,
|
||||
showNavigationControl: false,
|
||||
showSequenceControl: false
|
||||
element: this.element
|
||||
} );
|
||||
|
||||
$.extend( this, options );
|
||||
//Private state properties
|
||||
THIS[this.id] = {
|
||||
"animating": false
|
||||
animating: false
|
||||
};
|
||||
|
||||
this.minPixelRatio = this.viewer.minPixelRatio;
|
||||
|
||||
this.element.tabIndex = 0;
|
||||
|
||||
style = this.element.style;
|
||||
style.marginTop = '0px';
|
||||
style.marginRight = '0px';
|
||||
|
@ -119,14 +114,21 @@ $.ReferenceStrip = function ( options ) {
|
|||
$.setElementOpacity( this.element, 0.8 );
|
||||
|
||||
this.viewer = viewer;
|
||||
this.innerTracker = new $.MouseTracker( {
|
||||
this.tracker = new $.MouseTracker( {
|
||||
userData: 'ReferenceStrip.tracker',
|
||||
element: this.element,
|
||||
clickHandler: $.delegate( this, onStripClick ),
|
||||
dragHandler: $.delegate( this, onStripDrag ),
|
||||
scrollHandler: $.delegate( this, onStripScroll ),
|
||||
enterHandler: $.delegate( this, onStripEnter ),
|
||||
exitHandler: $.delegate( this, onStripExit ),
|
||||
leaveHandler: $.delegate( this, onStripLeave ),
|
||||
keyDownHandler: $.delegate( this, onKeyDown ),
|
||||
keyHandler: $.delegate( this, onKeyPress )
|
||||
keyHandler: $.delegate( this, onKeyPress ),
|
||||
preProcessEventHandler: function (eventInfo) {
|
||||
if (eventInfo.eventType === 'wheel') {
|
||||
eventInfo.preventDefault = true;
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
//Controls the position and orientation of the reference strip and sets the
|
||||
|
@ -139,7 +141,7 @@ $.ReferenceStrip = function ( options ) {
|
|||
{ anchor: $.ControlAnchor.BOTTOM_LEFT }
|
||||
);
|
||||
} else {
|
||||
if ( "horizontal" == options.scroll ) {
|
||||
if ( "horizontal" === options.scroll ) {
|
||||
this.element.style.width = (
|
||||
viewerSize.x *
|
||||
options.sizeRatio *
|
||||
|
@ -178,6 +180,7 @@ $.ReferenceStrip = function ( options ) {
|
|||
this.panelWidth = ( viewerSize.x * this.sizeRatio ) + 8;
|
||||
this.panelHeight = ( viewerSize.y * this.sizeRatio ) + 8;
|
||||
this.panels = [];
|
||||
this.miniViewers = {};
|
||||
|
||||
/*jshint loopfunc:true*/
|
||||
for ( i = 0; i < viewer.tileSources.length; i++ ) {
|
||||
|
@ -188,34 +191,11 @@ $.ReferenceStrip = function ( options ) {
|
|||
element.style.width = _this.panelWidth + 'px';
|
||||
element.style.height = _this.panelHeight + 'px';
|
||||
element.style.display = 'inline';
|
||||
element.style.float = 'left'; //Webkit
|
||||
element.style['float'] = 'left'; //Webkit
|
||||
element.style.cssFloat = 'left'; //Firefox
|
||||
element.style.styleFloat = 'left'; //IE
|
||||
element.style.padding = '2px';
|
||||
$.setElementTouchActionNone( element );
|
||||
|
||||
element.innerTracker = new $.MouseTracker( {
|
||||
element: element,
|
||||
clickTimeThreshold: this.clickTimeThreshold,
|
||||
clickDistThreshold: this.clickDistThreshold,
|
||||
pressHandler: function ( event ) {
|
||||
event.eventSource.dragging = $.now();
|
||||
},
|
||||
releaseHandler: function ( event ) {
|
||||
var tracker = event.eventSource,
|
||||
id = tracker.element.id,
|
||||
page = Number( id.split( '-' )[2] ),
|
||||
now = $.now();
|
||||
|
||||
if ( event.insideElementPressed &&
|
||||
event.insideElementReleased &&
|
||||
tracker.dragging &&
|
||||
( now - tracker.dragging ) < tracker.clickTimeThreshold ) {
|
||||
tracker.dragging = null;
|
||||
viewer.goToPage( page );
|
||||
}
|
||||
}
|
||||
} );
|
||||
$.setElementPointerEventsNone( element );
|
||||
|
||||
this.element.appendChild( element );
|
||||
|
||||
|
@ -224,18 +204,19 @@ $.ReferenceStrip = function ( options ) {
|
|||
this.panels.push( element );
|
||||
|
||||
}
|
||||
loadPanels( this, this.scroll == 'vertical' ? viewerSize.y : viewerSize.x, 0 );
|
||||
loadPanels( this, this.scroll === 'vertical' ? viewerSize.y : viewerSize.x, 0 );
|
||||
this.setFocus( 0 );
|
||||
|
||||
};
|
||||
|
||||
$.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.ReferenceStrip.prototype */{
|
||||
/** @lends OpenSeadragon.ReferenceStrip.prototype */
|
||||
$.ReferenceStrip.prototype = {
|
||||
|
||||
/**
|
||||
* @function
|
||||
*/
|
||||
setFocus: function ( page ) {
|
||||
var element = $.getElement( this.element.id + '-' + page ),
|
||||
var element = this.element.querySelector('#' + this.element.id + '-' + page ),
|
||||
viewerSize = $.getElementSize( this.viewer.canvas ),
|
||||
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
|
||||
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
|
||||
|
@ -250,7 +231,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
|||
this.currentSelected = element;
|
||||
this.currentSelected.style.background = '#999';
|
||||
|
||||
if ( 'horizontal' == this.scroll ) {
|
||||
if ( 'horizontal' === this.scroll ) {
|
||||
//right left
|
||||
offset = ( Number( page ) ) * ( this.panelWidth + 3 );
|
||||
if ( offset > offsetLeft + viewerSize.x - this.panelWidth ) {
|
||||
|
@ -276,8 +257,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
|||
}
|
||||
|
||||
this.currentPage = page;
|
||||
$.getElement( element.id + '-displayregion' ).focus();
|
||||
onStripEnter.call( this, { eventSource: this.innerTracker } );
|
||||
onStripEnter.call( this, { eventSource: this.tracker } );
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -286,22 +266,50 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
|||
*/
|
||||
update: function () {
|
||||
if ( THIS[this.id].animating ) {
|
||||
$.console.log( 'image reference strip update' );
|
||||
// $.console.log( 'image reference strip update' );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Overrides Viewer.destroy
|
||||
destroy: function() {
|
||||
if (this.miniViewers) {
|
||||
for (var key in this.miniViewers) {
|
||||
this.miniViewers[key].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this.tracker.destroy();
|
||||
|
||||
if (this.element) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
this.viewer.removeControl( this.element );
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inner
|
||||
* @function
|
||||
*/
|
||||
function onStripClick( event ) {
|
||||
if ( event.quick ) {
|
||||
var page;
|
||||
|
||||
if ( 'horizontal' === this.scroll ) {
|
||||
// +4px fix to solve problem with precision on thumbnail selection if there is a lot of them
|
||||
page = Math.floor(event.position.x / (this.panelWidth + 4));
|
||||
} else {
|
||||
page = Math.floor(event.position.y / this.panelHeight);
|
||||
}
|
||||
|
||||
this.viewer.goToPage( page );
|
||||
}
|
||||
|
||||
this.element.focus();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -311,14 +319,15 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
|||
*/
|
||||
function onStripDrag( event ) {
|
||||
|
||||
var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
|
||||
this.dragging = true;
|
||||
if ( this.element ) {
|
||||
var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
|
||||
offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
|
||||
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
|
||||
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
|
||||
viewerSize = $.getElementSize( this.viewer.canvas );
|
||||
this.dragging = true;
|
||||
if ( this.element ) {
|
||||
if ( 'horizontal' == this.scroll ) {
|
||||
|
||||
if ( 'horizontal' === this.scroll ) {
|
||||
if ( -event.delta.x > 0 ) {
|
||||
//forward
|
||||
if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
|
||||
|
@ -348,7 +357,6 @@ function onStripDrag( event ) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
@ -360,13 +368,14 @@ function onStripDrag( event ) {
|
|||
* @function
|
||||
*/
|
||||
function onStripScroll( event ) {
|
||||
var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
|
||||
if ( this.element ) {
|
||||
var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
|
||||
offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
|
||||
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
|
||||
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
|
||||
viewerSize = $.getElementSize( this.viewer.canvas );
|
||||
if ( this.element ) {
|
||||
if ( 'horizontal' == this.scroll ) {
|
||||
|
||||
if ( 'horizontal' === this.scroll ) {
|
||||
if ( event.scroll > 0 ) {
|
||||
//forward
|
||||
if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
|
||||
|
@ -395,9 +404,9 @@ function onStripScroll( event ) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault = true;
|
||||
}
|
||||
//cancels event
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -406,10 +415,9 @@ function loadPanels( strip, viewerSize, scroll ) {
|
|||
activePanelsStart,
|
||||
activePanelsEnd,
|
||||
miniViewer,
|
||||
style,
|
||||
i,
|
||||
element;
|
||||
if ( 'horizontal' == strip.scroll ) {
|
||||
if ( 'horizontal' === strip.scroll ) {
|
||||
panelSize = strip.panelWidth;
|
||||
} else {
|
||||
panelSize = strip.panelHeight;
|
||||
|
@ -422,9 +430,19 @@ function loadPanels( strip, viewerSize, scroll ) {
|
|||
for ( i = activePanelsStart; i < activePanelsEnd && i < strip.panels.length; i++ ) {
|
||||
element = strip.panels[i];
|
||||
if ( !element.activePanel ) {
|
||||
var miniTileSource;
|
||||
var originalTileSource = strip.viewer.tileSources[i];
|
||||
if (originalTileSource.referenceStripThumbnailUrl) {
|
||||
miniTileSource = {
|
||||
type: 'image',
|
||||
url: originalTileSource.referenceStripThumbnailUrl
|
||||
};
|
||||
} else {
|
||||
miniTileSource = originalTileSource;
|
||||
}
|
||||
miniViewer = new $.Viewer( {
|
||||
id: element.id,
|
||||
tileSources: [strip.viewer.tileSources[i]],
|
||||
tileSources: [miniTileSource],
|
||||
element: element,
|
||||
navigatorSizeRatio: strip.sizeRatio,
|
||||
showNavigator: false,
|
||||
|
@ -433,36 +451,21 @@ function loadPanels( strip, viewerSize, scroll ) {
|
|||
showSequenceControl: false,
|
||||
immediateRender: true,
|
||||
blendTime: 0,
|
||||
animationTime: 0
|
||||
animationTime: 0,
|
||||
loadTilesWithAjax: strip.viewer.loadTilesWithAjax,
|
||||
ajaxHeaders: strip.viewer.ajaxHeaders,
|
||||
drawer: 'canvas', //always use canvas for the reference strip
|
||||
} );
|
||||
// Allow pointer events to pass through miniViewer's canvas/container
|
||||
// elements so implicit pointer capture works on touch devices
|
||||
$.setElementPointerEventsNone( miniViewer.canvas );
|
||||
$.setElementPointerEventsNone( miniViewer.container );
|
||||
// We'll use event delegation from the reference strip element instead of
|
||||
// handling events on every miniViewer
|
||||
miniViewer.innerTracker.setTracking( false );
|
||||
miniViewer.outerTracker.setTracking( false );
|
||||
|
||||
miniViewer.displayRegion = $.makeNeutralElement( "textarea" );
|
||||
miniViewer.displayRegion.id = element.id + '-displayregion';
|
||||
miniViewer.displayRegion.className = 'displayregion';
|
||||
|
||||
style = miniViewer.displayRegion.style;
|
||||
style.position = 'relative';
|
||||
style.top = '0px';
|
||||
style.left = '0px';
|
||||
style.fontSize = '0px';
|
||||
style.overflow = 'hidden';
|
||||
style.float = 'left'; //Webkit
|
||||
style.cssFloat = 'left'; //Firefox
|
||||
style.styleFloat = 'left'; //IE
|
||||
style.zIndex = 999999999;
|
||||
style.cursor = 'default';
|
||||
style.width = ( strip.panelWidth - 4 ) + 'px';
|
||||
style.height = ( strip.panelHeight - 4 ) + 'px';
|
||||
|
||||
// TODO: What is this for? Future keyboard navigation support?
|
||||
miniViewer.displayRegion.innerTracker = new $.MouseTracker( {
|
||||
element: miniViewer.displayRegion,
|
||||
startDisabled: true
|
||||
} );
|
||||
|
||||
element.getElementsByTagName( 'div' )[0].appendChild(
|
||||
miniViewer.displayRegion
|
||||
);
|
||||
strip.miniViewers[element.id] = miniViewer;
|
||||
|
||||
element.activePanel = true;
|
||||
}
|
||||
|
@ -483,7 +486,7 @@ function onStripEnter( event ) {
|
|||
//element.style.border = '1px solid #555';
|
||||
//element.style.background = '#000';
|
||||
|
||||
if ( 'horizontal' == this.scroll ) {
|
||||
if ( 'horizontal' === this.scroll ) {
|
||||
|
||||
//element.style.paddingTop = "0px";
|
||||
element.style.marginBottom = "0px";
|
||||
|
@ -494,7 +497,6 @@ function onStripEnter( event ) {
|
|||
element.style.marginLeft = "0px";
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -503,10 +505,10 @@ function onStripEnter( event ) {
|
|||
* @inner
|
||||
* @function
|
||||
*/
|
||||
function onStripExit( event ) {
|
||||
function onStripLeave( event ) {
|
||||
var element = event.eventSource.element;
|
||||
|
||||
if ( 'horizontal' == this.scroll ) {
|
||||
if ( 'horizontal' === this.scroll ) {
|
||||
|
||||
//element.style.paddingTop = "10px";
|
||||
element.style.marginBottom = "-" + ( $.getElementSize( element ).y / 2 ) + "px";
|
||||
|
@ -517,7 +519,6 @@ function onStripExit( event ) {
|
|||
element.style.marginLeft = "-" + ( $.getElementSize( element ).x / 2 ) + "px";
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -529,26 +530,31 @@ function onStripExit( event ) {
|
|||
function onKeyDown( event ) {
|
||||
//console.log( event.keyCode );
|
||||
|
||||
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
|
||||
if ( !event.ctrl && !event.alt && !event.meta ) {
|
||||
switch ( event.keyCode ) {
|
||||
case 38: //up arrow
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 40: //down arrow
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 37: //left arrow
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 39: //right arrow
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
default:
|
||||
//console.log( 'navigator keycode %s', event.keyCode );
|
||||
return true;
|
||||
event.preventDefault = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
event.preventDefault = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,38 +567,43 @@ function onKeyDown( event ) {
|
|||
function onKeyPress( event ) {
|
||||
//console.log( event.keyCode );
|
||||
|
||||
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
|
||||
if ( !event.ctrl && !event.alt && !event.meta ) {
|
||||
switch ( event.keyCode ) {
|
||||
case 61: //=|+
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 45: //-|_
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 48: //0|)
|
||||
case 119: //w
|
||||
case 87: //W
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 115: //s
|
||||
case 83: //S
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 97: //a
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
case 100: //d
|
||||
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
|
||||
return false;
|
||||
event.preventDefault = true;
|
||||
break;
|
||||
default:
|
||||
//console.log( 'navigator keycode %s', event.keyCode );
|
||||
return true;
|
||||
event.preventDefault = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
event.preventDefault = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} ( OpenSeadragon ) );
|
||||
}(OpenSeadragon));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Spring
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -41,7 +41,7 @@
|
|||
* @param {Number} options.springStiffness - Spring stiffness. Must be greater than zero.
|
||||
* The closer to zero, the closer to linear animation.
|
||||
* @param {Number} options.animationTime - Animation duration per spring, in seconds.
|
||||
* Must be greater than zero.
|
||||
* Must be zero or greater.
|
||||
* @param {Number} [options.initial=0] - Initial value of spring.
|
||||
* @param {Boolean} [options.exponential=false] - Whether this spring represents
|
||||
* an exponential scale (such as zoom) and should be animated accordingly. Note that
|
||||
|
@ -50,11 +50,11 @@
|
|||
$.Spring = function( options ) {
|
||||
var args = arguments;
|
||||
|
||||
if( typeof( options ) != 'object' ){
|
||||
if( typeof ( options ) !== 'object' ){
|
||||
//allows backward compatible use of ( initialValue, config ) as
|
||||
//constructor parameters
|
||||
options = {
|
||||
initial: args.length && typeof ( args[ 0 ] ) == "number" ?
|
||||
initial: args.length && typeof ( args[ 0 ] ) === "number" ?
|
||||
args[ 0 ] :
|
||||
undefined,
|
||||
/**
|
||||
|
@ -79,8 +79,8 @@ $.Spring = function( options ) {
|
|||
$.console.assert(typeof options.springStiffness === "number" && options.springStiffness !== 0,
|
||||
"[OpenSeadragon.Spring] options.springStiffness must be a non-zero number");
|
||||
|
||||
$.console.assert(typeof options.animationTime === "number" && options.springStiffness !== 0,
|
||||
"[OpenSeadragon.Spring] options.animationTime must be a non-zero number");
|
||||
$.console.assert(typeof options.animationTime === "number" && options.animationTime >= 0,
|
||||
"[OpenSeadragon.Spring] options.animationTime must be a number greater than or equal to 0");
|
||||
|
||||
if (options.exponential) {
|
||||
this._exponential = true;
|
||||
|
@ -96,7 +96,7 @@ $.Spring = function( options ) {
|
|||
* @property {Number} time
|
||||
*/
|
||||
this.current = {
|
||||
value: typeof ( this.initial ) == "number" ?
|
||||
value: typeof ( this.initial ) === "number" ?
|
||||
this.initial :
|
||||
(this._exponential ? 0 : 1),
|
||||
time: $.now() // always work in milliseconds
|
||||
|
@ -134,7 +134,8 @@ $.Spring = function( options ) {
|
|||
}
|
||||
};
|
||||
|
||||
$.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
|
||||
/** @lends OpenSeadragon.Spring.prototype */
|
||||
$.Spring.prototype = {
|
||||
|
||||
/**
|
||||
* @function
|
||||
|
@ -205,11 +206,13 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
|
|||
|
||||
/**
|
||||
* @function
|
||||
* @returns true if the spring is still updating its value, false if it is
|
||||
* already at the target value.
|
||||
*/
|
||||
update: function() {
|
||||
this.current.time = $.now();
|
||||
|
||||
var startValue, targetValue;
|
||||
let startValue, targetValue;
|
||||
if (this._exponential) {
|
||||
startValue = this.start._logValue;
|
||||
targetValue = this.target._logValue;
|
||||
|
@ -218,21 +221,34 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
|
|||
targetValue = this.target.value;
|
||||
}
|
||||
|
||||
var currentValue = (this.current.time >= this.target.time) ?
|
||||
targetValue :
|
||||
startValue +
|
||||
( targetValue - startValue ) *
|
||||
transform(
|
||||
this.springStiffness,
|
||||
( this.current.time - this.start.time ) /
|
||||
( this.target.time - this.start.time )
|
||||
);
|
||||
|
||||
if (this._exponential) {
|
||||
this.current.value = Math.exp(currentValue);
|
||||
if(this.current.time >= this.target.time){
|
||||
this.current.value = this.target.value;
|
||||
} else {
|
||||
this.current.value = currentValue;
|
||||
let currentValue = startValue +
|
||||
( targetValue - startValue ) *
|
||||
transform(
|
||||
this.springStiffness,
|
||||
( this.current.time - this.start.time ) /
|
||||
( this.target.time - this.start.time )
|
||||
);
|
||||
|
||||
if (this._exponential) {
|
||||
this.current.value = Math.exp(currentValue);
|
||||
} else {
|
||||
this.current.value = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
return this.current.value !== this.target.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether the spring is at the target value
|
||||
* @function
|
||||
* @returns {Boolean} True if at target value, false otherwise
|
||||
*/
|
||||
isAtTargetValue: function() {
|
||||
return this.current.value === this.target.value;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - getString/setString
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -57,7 +57,8 @@ var I18N = {
|
|||
NextPage: "Next page",
|
||||
PreviousPage: "Previous page",
|
||||
RotateLeft: "Rotate left",
|
||||
RotateRight: "Rotate right"
|
||||
RotateRight: "Rotate right",
|
||||
Flip: "Flip Horizontally"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -75,14 +76,14 @@ $.extend( $, /** @lends OpenSeadragon */{
|
|||
container = I18N,
|
||||
i;
|
||||
|
||||
for ( i = 0; i < props.length-1; i++ ) {
|
||||
for (i = 0; i < props.length - 1; i++) {
|
||||
// in case not a subproperty
|
||||
container = container[ props[ i ] ] || {};
|
||||
}
|
||||
string = container[ props[ i ] ];
|
||||
|
||||
if ( typeof( string ) != "string" ) {
|
||||
$.console.debug( "Untranslated source string:", prop );
|
||||
if ( typeof ( string ) !== "string" ) {
|
||||
$.console.error( "Untranslated source string:", prop );
|
||||
string = ""; // FIXME: this breaks gettext()-style convention, which would return source
|
||||
}
|
||||
|
||||
|
|
771
src/tile.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - Tile
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -40,15 +40,23 @@
|
|||
* @param {Number} level The zoom level this tile belongs to.
|
||||
* @param {Number} x The vector component 'x'.
|
||||
* @param {Number} y The vector component 'y'.
|
||||
* @param {OpenSeadragon.Point} bounds Where this tile fits, in normalized
|
||||
* @param {OpenSeadragon.Rect} bounds Where this tile fits, in normalized
|
||||
* coordinates.
|
||||
* @param {Boolean} exists Is this tile a part of a sparse image? ( Also has
|
||||
* this tile failed to load? )
|
||||
* @param {String} url The URL of this tile's image.
|
||||
* @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
|
||||
* is provided directly by the tile source.
|
||||
* @param {String|Function} url The URL of this tile's image or a function that returns a url.
|
||||
* @param {CanvasRenderingContext2D} [context2D=undefined] The context2D of this tile if it
|
||||
* * is provided directly by the tile source. Deprecated: use Tile::addCache(...) instead.
|
||||
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
|
||||
* @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).
|
||||
* @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the
|
||||
* drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
|
||||
* with HTML the entire tile is always used.
|
||||
* @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
||||
* see TileSource::getTilePostData) or null
|
||||
* @param {String} cacheKey key to act as a tile cache, must be unique for tiles with unique image data
|
||||
*/
|
||||
$.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
||||
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData, cacheKey) {
|
||||
/**
|
||||
* The zoom level this tile belongs to.
|
||||
* @member {Number} level
|
||||
|
@ -73,6 +81,19 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.bounds = bounds;
|
||||
/**
|
||||
* Where this tile fits, in normalized coordinates, after positioning
|
||||
* @member {OpenSeadragon.Rect} positionedBounds
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.positionedBounds = new OpenSeadragon.Rect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
/**
|
||||
* The portion of the tile to use as the source of the drawing operation, in pixels. Note that
|
||||
* this only works when drawing with canvas; when drawing with HTML the entire tile is always used.
|
||||
* @member {OpenSeadragon.Rect} sourceBounds
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.sourceBounds = sourceBounds;
|
||||
/**
|
||||
* Is this tile a part of a sparse image? Also has this tile failed to load?
|
||||
* @member {Boolean} exists
|
||||
|
@ -80,17 +101,52 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||
*/
|
||||
this.exists = exists;
|
||||
/**
|
||||
* The URL of this tile's image.
|
||||
* @member {String} url
|
||||
* Private property to hold string url or url retriever function.
|
||||
* Consumers should access via Tile.getUrl()
|
||||
* @member {String|Function} url
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @private
|
||||
*/
|
||||
this._url = url;
|
||||
/**
|
||||
* Post parameters for this tile. For example, it can be an URL-encoded string
|
||||
* in k1=v1&k2=v2... format, or a JSON, or a FormData instance... or null if no POST request used
|
||||
* @member {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
||||
* see TileSource::getTilePostData) or null
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.url = url;
|
||||
this.postData = postData;
|
||||
/**
|
||||
* The context2D of this tile if it is provided directly by the tile source.
|
||||
* @member {CanvasRenderingContext2D} context2D
|
||||
* @memberOf OpenSeadragon.Tile#
|
||||
*/
|
||||
this.context2D = context2D;
|
||||
if (context2D) {
|
||||
this.context2D = context2D;
|
||||
}
|
||||
/**
|
||||
* Whether to load this tile's image with an AJAX request.
|
||||
* @member {Boolean} loadWithAjax
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.loadWithAjax = loadWithAjax;
|
||||
/**
|
||||
* The headers to be used in requesting this tile's image.
|
||||
* Only used if loadWithAjax is set to true.
|
||||
* @member {Object} ajaxHeaders
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.ajaxHeaders = ajaxHeaders;
|
||||
|
||||
if (cacheKey === undefined) {
|
||||
$.console.warn("Tile constructor needs 'cacheKey' variable: creation tile cache" +
|
||||
" in Tile class is deprecated. TileSource.prototype.getTileHashKey will be used.");
|
||||
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
|
||||
}
|
||||
|
||||
this._cKey = cacheKey || "";
|
||||
this._ocKey = cacheKey || "";
|
||||
|
||||
/**
|
||||
* Is this tile loaded?
|
||||
* @member {Boolean} loaded
|
||||
|
@ -103,32 +159,6 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.loading = false;
|
||||
|
||||
/**
|
||||
* The HTML div element for this tile
|
||||
* @member {Element} element
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.element = null;
|
||||
/**
|
||||
* The HTML img element for this tile.
|
||||
* @member {Element} imgElement
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.imgElement = null;
|
||||
/**
|
||||
* The Image object for this tile.
|
||||
* @member {Object} image
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.image = null;
|
||||
|
||||
/**
|
||||
* The alias of this.element.style.
|
||||
* @member {String} style
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.style = null;
|
||||
/**
|
||||
* This tile's position on screen, in pixels.
|
||||
* @member {OpenSeadragon.Point} position
|
||||
|
@ -141,6 +171,12 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.size = null;
|
||||
/**
|
||||
* Whether to flip the tile when rendering.
|
||||
* @member {Boolean} flipped
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.flipped = false;
|
||||
/**
|
||||
* The start time of this tile's blending.
|
||||
* @member {Number} blendStart
|
||||
|
@ -154,11 +190,13 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||
*/
|
||||
this.opacity = null;
|
||||
/**
|
||||
* The distance of this tile to the viewport center.
|
||||
* @member {Number} distance
|
||||
* The squared distance of this tile to the viewport center.
|
||||
* Use for comparing tiles.
|
||||
* @member {Number} squaredDistance
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @private
|
||||
*/
|
||||
this.distance = null;
|
||||
this.squaredDistance = null;
|
||||
/**
|
||||
* The visibility score of this tile.
|
||||
* @member {Number} visibility
|
||||
|
@ -166,21 +204,70 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||
*/
|
||||
this.visibility = null;
|
||||
|
||||
/**
|
||||
* The transparency indicator of this tile.
|
||||
* @member {Boolean} hasTransparency true if tile contains transparency for correct rendering
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.hasTransparency = false;
|
||||
|
||||
/**
|
||||
* Whether this tile is currently being drawn.
|
||||
* @member {Boolean} beingDrawn
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.beingDrawn = false;
|
||||
|
||||
/**
|
||||
* Timestamp the tile was last touched.
|
||||
* @member {Number} lastTouchTime
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.lastTouchTime = 0;
|
||||
|
||||
/**
|
||||
* Whether this tile is in the right-most column for its level.
|
||||
* @member {Boolean} isRightMost
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.isRightMost = false;
|
||||
|
||||
/**
|
||||
* Whether this tile is in the bottom-most row for its level.
|
||||
* @member {Boolean} isBottomMost
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.isBottomMost = false;
|
||||
|
||||
/**
|
||||
* Owner of this tile. Do not change this property manually.
|
||||
* @member {OpenSeadragon.TiledImage}
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.tiledImage = null;
|
||||
/**
|
||||
* Array of cached tile data associated with the tile.
|
||||
* @member {Object}
|
||||
* @private
|
||||
*/
|
||||
this._caches = {};
|
||||
/**
|
||||
* Processing flag, exempt the tile from removal when there are ongoing updates
|
||||
* @member {Boolean|Number}
|
||||
* @private
|
||||
*/
|
||||
this.processing = false;
|
||||
/**
|
||||
* Processing promise, resolves when the tile exits processing, or
|
||||
* resolves immediatelly if not in the processing state.
|
||||
* @member {OpenSeadragon.Promise}
|
||||
* @private
|
||||
*/
|
||||
this.processingPromise = $.Promise.resolve();
|
||||
};
|
||||
|
||||
$.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
/** @lends OpenSeadragon.Tile.prototype */
|
||||
$.Tile.prototype = {
|
||||
|
||||
/**
|
||||
* Provides a string representation of this tiles level and (x,y)
|
||||
|
@ -193,142 +280,524 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
|||
},
|
||||
|
||||
/**
|
||||
* Renders the tile in an html container.
|
||||
* @function
|
||||
* @param {Element} container
|
||||
* The unique main cache key for this tile. Created automatically
|
||||
* from the given tiledImage.source.getTileHashKey(...) implementation.
|
||||
* @member {String} cacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
drawHTML: function( container ) {
|
||||
if (!this.cacheImageRecord) {
|
||||
$.console.warn(
|
||||
'[Tile.drawHTML] attempting to draw tile %s when it\'s not cached',
|
||||
this.toString());
|
||||
get cacheKey() {
|
||||
return this._cKey;
|
||||
},
|
||||
set cacheKey(value) {
|
||||
if (value === this.cacheKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !this.loaded ) {
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
this.toString()
|
||||
);
|
||||
return;
|
||||
const cache = this.getCache(value);
|
||||
if (!cache) {
|
||||
// It's better to first set cache, then change the key to existing one. Warn if otherwise.
|
||||
$.console.warn("[Tile.cacheKey] should not be set manually. Use addCache() with setAsMain=true.");
|
||||
}
|
||||
|
||||
//EXPERIMENTAL - trying to figure out how to scale the container
|
||||
// content during animation of the container size.
|
||||
|
||||
if ( !this.element ) {
|
||||
this.element = $.makeNeutralElement( "div" );
|
||||
this.imgElement = this.cacheImageRecord.getImage().cloneNode();
|
||||
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
|
||||
this.imgElement.style.width = "100%";
|
||||
this.imgElement.style.height = "100%";
|
||||
|
||||
this.style = this.element.style;
|
||||
this.style.position = "absolute";
|
||||
}
|
||||
if ( this.element.parentNode != container ) {
|
||||
container.appendChild( this.element );
|
||||
}
|
||||
if ( this.imgElement.parentNode != this.element ) {
|
||||
this.element.appendChild( this.imgElement );
|
||||
}
|
||||
|
||||
this.style.top = this.position.y + "px";
|
||||
this.style.left = this.position.x + "px";
|
||||
this.style.height = this.size.y + "px";
|
||||
this.style.width = this.size.x + "px";
|
||||
|
||||
$.setElementOpacity( this.element, this.opacity );
|
||||
this._updateMainCacheKey(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the tile in a canvas-based context.
|
||||
* @function
|
||||
* @param {Canvas} context
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
* where <code>rendered</code> is the context with the pre-drawn image.
|
||||
* By default equal to tile.cacheKey, marks a cache associated with this tile
|
||||
* that holds the cache original data (it was loaded with). In case you
|
||||
* change the tile data, the tile original data should be left with the cache
|
||||
* 'originalCacheKey' and the new, modified data should be stored in cache 'cacheKey'.
|
||||
* This key is used in cache resolution: in case new tile data is requested, if
|
||||
* this cache key exists in the cache it is loaded.
|
||||
* @member {String} originalCacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
drawCanvas: function( context, drawingHandler ) {
|
||||
set originalCacheKey(value) {
|
||||
throw "Original Cache Key cannot be managed manually!";
|
||||
},
|
||||
get originalCacheKey() {
|
||||
return this._ocKey;
|
||||
},
|
||||
|
||||
var position = this.position,
|
||||
size = this.size,
|
||||
rendered;
|
||||
/**
|
||||
* The Image object for this tile.
|
||||
* @member {Object} image
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @deprecated
|
||||
* @returns {Image}
|
||||
*/
|
||||
get image() {
|
||||
$.console.error("[Tile.image] property has been deprecated. Use [Tile.getData] instead.");
|
||||
return this.getImage();
|
||||
},
|
||||
|
||||
if (!this.context2D && !this.cacheImageRecord) {
|
||||
/**
|
||||
* The URL of this tile's image.
|
||||
* @member {String} url
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @deprecated
|
||||
* @returns {String}
|
||||
*/
|
||||
get url() {
|
||||
$.console.error("[Tile.url] property has been deprecated. Use [Tile.getUrl] instead.");
|
||||
return this.getUrl();
|
||||
},
|
||||
|
||||
/**
|
||||
* The HTML div element for this tile
|
||||
* @member {Element} element
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @deprecated
|
||||
*/
|
||||
get element() {
|
||||
$.console.error("Tile::element property is deprecated. Use cache API instead. Moreover, this property might be unstable.");
|
||||
const cache = this.getCache();
|
||||
if (!cache || !cache.loaded) {
|
||||
return null;
|
||||
}
|
||||
if (cache.type !== OpenSeadragon.HTMLDrawer.canvasCacheType || cache.type !== OpenSeadragon.HTMLDrawer.imageCacheType) {
|
||||
$.console.error("Access to HtmlDrawer property via Tile instance: HTMLDrawer must be used!");
|
||||
return null;
|
||||
}
|
||||
return cache.data.element;
|
||||
},
|
||||
|
||||
/**
|
||||
* The HTML img element for this tile.
|
||||
* @member {Element} imgElement
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @deprecated
|
||||
*/
|
||||
get imgElement() {
|
||||
$.console.error("Tile::imgElement property is deprecated. Use cache API instead. Moreover, this property might be unstable.");
|
||||
const cache = this.getCache();
|
||||
if (!cache || !cache.loaded) {
|
||||
return null;
|
||||
}
|
||||
if (cache.type !== OpenSeadragon.HTMLDrawer.canvasCacheType || cache.type !== OpenSeadragon.HTMLDrawer.imageCacheType) {
|
||||
$.console.error("Access to HtmlDrawer property via Tile instance: HTMLDrawer must be used!");
|
||||
return null;
|
||||
}
|
||||
return cache.data.imgElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* The alias of this.element.style.
|
||||
* @member {String} style
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
* @deprecated
|
||||
*/
|
||||
get style() {
|
||||
$.console.error("Tile::style property is deprecated. Use cache API instead. Moreover, this property might be unstable.");
|
||||
const cache = this.getCache();
|
||||
if (!cache || !cache.loaded) {
|
||||
return null;
|
||||
}
|
||||
if (cache.type !== OpenSeadragon.HTMLDrawer.canvasCacheType || cache.type !== OpenSeadragon.HTMLDrawer.imageCacheType) {
|
||||
$.console.error("Access to HtmlDrawer property via Tile instance: HTMLDrawer must be used!");
|
||||
return null;
|
||||
}
|
||||
return cache.data.style;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Image object for this tile.
|
||||
* @returns {?Image}
|
||||
*/
|
||||
getImage: function() {
|
||||
$.console.error("[Tile.getImage] property has been deprecated. Use 'tile-invalidated' routine event instead.");
|
||||
//this method used to ensure the underlying data model conformed to given type - convert instead of getData()
|
||||
const cache = this.getCache(this.cacheKey);
|
||||
if (!cache) {
|
||||
return undefined;
|
||||
}
|
||||
cache.transformTo("image");
|
||||
return cache.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the url string for this tile.
|
||||
* @returns {String}
|
||||
*/
|
||||
getUrl: function() {
|
||||
if (typeof this._url === 'function') {
|
||||
return this._url();
|
||||
}
|
||||
|
||||
return this._url;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the CanvasRenderingContext2D instance for tile image data drawn
|
||||
* onto Canvas if enabled and available
|
||||
* @returns {CanvasRenderingContext2D|undefined}
|
||||
*/
|
||||
getCanvasContext: function() {
|
||||
$.console.error("[Tile.getCanvasContext] property has been deprecated. Use 'tile-invalidated' routine event instead.");
|
||||
//this method used to ensure the underlying data model conformed to given type - convert instead of getData()
|
||||
const cache = this.getCache(this.cacheKey);
|
||||
if (!cache) {
|
||||
return undefined;
|
||||
}
|
||||
cache.transformTo("context2d");
|
||||
return cache.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* The context2D of this tile if it is provided directly by the tile source.
|
||||
* @deprecated
|
||||
* @type {CanvasRenderingContext2D}
|
||||
*/
|
||||
get context2D() {
|
||||
$.console.error("[Tile.context2D] property has been deprecated. Use 'tile-invalidated' routine event instead.");
|
||||
return this.getCanvasContext();
|
||||
},
|
||||
|
||||
/**
|
||||
* The context2D of this tile if it is provided directly by the tile source.
|
||||
* @deprecated
|
||||
*/
|
||||
set context2D(value) {
|
||||
$.console.error("[Tile.context2D] property has been deprecated. Use 'tile-invalidated' routine event instead.");
|
||||
const cache = this._caches[this.cacheKey];
|
||||
if (cache) {
|
||||
this.removeCache(this.cacheKey);
|
||||
}
|
||||
this.addCache(this.cacheKey, value, 'context2d', true, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* The default cache for this tile.
|
||||
* @deprecated
|
||||
* @type OpenSeadragon.CacheRecord
|
||||
*/
|
||||
get cacheImageRecord() {
|
||||
$.console.error("[Tile.cacheImageRecord] property has been deprecated. Use Tile::getCache.");
|
||||
return this.getCache(this.cacheKey);
|
||||
},
|
||||
|
||||
/**
|
||||
* The default cache for this tile.
|
||||
* @deprecated
|
||||
*/
|
||||
set cacheImageRecord(value) {
|
||||
$.console.error("[Tile.cacheImageRecord] property has been deprecated. Use Tile::addCache.");
|
||||
const cache = this._caches[this.cacheKey];
|
||||
|
||||
if (cache) {
|
||||
this.removeCache(this.cacheKey);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (value.loaded) {
|
||||
this.addCache(this.cacheKey, value.data, value.type, true, false);
|
||||
} else {
|
||||
value.await().then(x => this.addCache(this.cacheKey, x, value.type, true, false));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cache key for main cache that is 'cache-equal', but different from original cache key
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
buildDistinctMainCacheKey: function () {
|
||||
return this.cacheKey === this.originalCacheKey ? "mod://" + this.originalCacheKey : this.cacheKey;
|
||||
},
|
||||
|
||||
/**
|
||||
* Read tile cache data object (CacheRecord)
|
||||
* @param {string} [key=this.cacheKey] cache key to read that belongs to this tile
|
||||
* @return {OpenSeadragon.CacheRecord}
|
||||
*/
|
||||
getCache: function(key = this._cKey) {
|
||||
const cache = this._caches[key];
|
||||
if (cache) {
|
||||
cache.withTileReference(this);
|
||||
}
|
||||
return cache;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create tile cache for given data object.
|
||||
*
|
||||
* Using `setAsMain` updates also main tile cache key - the main cache key used to draw this tile.
|
||||
* In that case, the cache should be ready to be rendered immediatelly (converted to one of the supported formats
|
||||
* of the currently employed drawer).
|
||||
*
|
||||
* NOTE: if the existing cache already exists,
|
||||
* data parameter is ignored and inherited from the existing cache object.
|
||||
* WARNING: if you override main tile cache key to point to a different cache, the invalidation routine
|
||||
* will no longer work. If you need to modify tile main data, prefer to use invalidation routine instead.
|
||||
*
|
||||
* @param {string} key cache key, if unique, new cache object is created, else existing cache attached
|
||||
* @param {*} data this data will be IGNORED if cache already exists; therefore if
|
||||
* `typeof data === 'function'` holds (both async and normal functions), the data is called to obtain
|
||||
* the data item: this is an optimization to load data only when necessary.
|
||||
* @param {string} [type=undefined] data type, will be guessed if not provided (not recommended),
|
||||
* if data is a callback the type is a mandatory field, not setting it results in undefined behaviour
|
||||
* @param {boolean} [setAsMain=false] if true, the key will be set as the tile.cacheKey,
|
||||
* no effect if key === this.cacheKey
|
||||
* @param [_safely=true] private
|
||||
* @returns {OpenSeadragon.CacheRecord|null} - The cache record the tile was attached to.
|
||||
*/
|
||||
addCache: function(key, data, type = undefined, setAsMain = false, _safely = true) {
|
||||
const tiledImage = this.tiledImage;
|
||||
if (!tiledImage) {
|
||||
return null; //async can access outside its lifetime
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
if (!this.__typeWarningReported) {
|
||||
$.console.warn(this, "[Tile.addCache] called without type specification. " +
|
||||
"Automated deduction is potentially unsafe: prefer specification of data type explicitly.");
|
||||
this.__typeWarningReported = true;
|
||||
}
|
||||
if (typeof data === 'function') {
|
||||
$.console.error("[TileCache.cacheTile] options.data as a callback requires type argument! Current is " + type);
|
||||
}
|
||||
type = $.convertor.guessType(data);
|
||||
}
|
||||
|
||||
const overwritesMainCache = key === this.cacheKey;
|
||||
if (_safely && (overwritesMainCache || setAsMain)) {
|
||||
// Need to get the supported type for rendering out of the active drawer.
|
||||
const supportedTypes = tiledImage.viewer.drawer.getSupportedDataFormats();
|
||||
const conversion = $.convertor.getConversionPath(type, supportedTypes);
|
||||
$.console.assert(conversion, "[Tile.addCache] data was set for the default tile cache we are unable" +
|
||||
`to render. Make sure OpenSeadragon.convertor was taught to convert ${type} to (one of): ${conversion.toString()}`);
|
||||
}
|
||||
|
||||
const cachedItem = tiledImage._tileCache.cacheTile({
|
||||
data: data,
|
||||
dataType: type,
|
||||
tile: this,
|
||||
cacheKey: key,
|
||||
cutoff: tiledImage.source.getClosestLevel(),
|
||||
});
|
||||
const havingRecord = this._caches[key];
|
||||
if (havingRecord !== cachedItem) {
|
||||
this._caches[key] = cachedItem;
|
||||
if (havingRecord) {
|
||||
havingRecord.removeTile(this);
|
||||
tiledImage._tileCache.safeUnloadCache(havingRecord);
|
||||
}
|
||||
}
|
||||
|
||||
// Update cache key if differs and main requested
|
||||
if (!overwritesMainCache && setAsMain) {
|
||||
this._updateMainCacheKey(key);
|
||||
}
|
||||
return cachedItem;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add cache object to the tile
|
||||
*
|
||||
* @param {string} key cache key, if unique, new cache object is created, else existing cache attached
|
||||
* @param {OpenSeadragon.CacheRecord} cache the cache object to attach to this tile
|
||||
* @param {boolean} [setAsMain=false] if true, the key will be set as the tile.cacheKey,
|
||||
* no effect if key === this.cacheKey
|
||||
* @param [_safely=true] private
|
||||
* @returns {OpenSeadragon.CacheRecord|null} - Returns cache parameter reference if attached.
|
||||
*/
|
||||
setCache(key, cache, setAsMain = false, _safely = true) {
|
||||
const tiledImage = this.tiledImage;
|
||||
if (!tiledImage) {
|
||||
return null; //async can access outside its lifetime
|
||||
}
|
||||
|
||||
const overwritesMainCache = key === this.cacheKey;
|
||||
if (_safely) {
|
||||
$.console.assert(cache instanceof $.CacheRecord, "[Tile.setCache] cache must be a CacheRecord object!");
|
||||
if (overwritesMainCache || setAsMain) {
|
||||
// Need to get the supported type for rendering out of the active drawer.
|
||||
const supportedTypes = tiledImage.viewer.drawer.getSupportedDataFormats();
|
||||
const conversion = $.convertor.getConversionPath(cache.type, supportedTypes);
|
||||
$.console.assert(conversion, "[Tile.setCache] data was set for the default tile cache we are unable" +
|
||||
`to render. Make sure OpenSeadragon.convertor was taught to convert ${cache.type} to (one of): ${conversion.toString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
const havingRecord = this._caches[key];
|
||||
if (havingRecord !== cache) {
|
||||
this._caches[key] = cache;
|
||||
cache.addTile(this); // keep reference bidirectional
|
||||
if (havingRecord) {
|
||||
havingRecord.removeTile(this);
|
||||
tiledImage._tileCache.safeUnloadCache(havingRecord);
|
||||
}
|
||||
}
|
||||
|
||||
// Update cache key if differs and main requested
|
||||
if (!overwritesMainCache && setAsMain) {
|
||||
this._updateMainCacheKey(key);
|
||||
}
|
||||
return cache;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the main cache key for this tile and
|
||||
* performs necessary updates
|
||||
* @param value
|
||||
* @private
|
||||
*/
|
||||
_updateMainCacheKey: function(value) {
|
||||
let ref = this._caches[this._cKey];
|
||||
if (ref) {
|
||||
// make sure we free drawer internal cache if people change cache key externally
|
||||
ref.destroyInternalCache();
|
||||
}
|
||||
this._cKey = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the number of caches available to this tile
|
||||
* @returns {number} number of caches
|
||||
*/
|
||||
getCacheSize: function() {
|
||||
return Object.values(this._caches).length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Free tile cache. Removes by default the cache record if no other tile uses it.
|
||||
* @param {string} key cache key, required
|
||||
* @param {boolean} [freeIfUnused=true] set to false if zombie should be created
|
||||
* @return {OpenSeadragon.CacheRecord|undefined} reference to the cache record if it was removed,
|
||||
* undefined if removal was refused to perform (e.g. does not exist, it is an original data target etc.)
|
||||
*/
|
||||
removeCache: function(key, freeIfUnused = true) {
|
||||
const deleteTarget = this._caches[key];
|
||||
if (!deleteTarget) {
|
||||
// try to erase anyway in case the cache got stuck in memory
|
||||
this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused, true);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const currentMainKey = this.cacheKey,
|
||||
originalDataKey = this.originalCacheKey,
|
||||
sameBuiltinKeys = currentMainKey === originalDataKey;
|
||||
|
||||
if (!sameBuiltinKeys && originalDataKey === key) {
|
||||
$.console.warn("[Tile.removeCache] original data must not be manually deleted: other parts of the code might rely on it!",
|
||||
"If you want the tile not to preserve the original data, toggle of data perseverance in tile.setData().");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (currentMainKey === key) {
|
||||
if (!sameBuiltinKeys && this._caches[originalDataKey]) {
|
||||
// if we have original data let's revert back
|
||||
this._updateMainCacheKey(originalDataKey);
|
||||
} else {
|
||||
$.console.warn("[Tile.removeCache] trying to remove the only cache that can be used to draw the tile!",
|
||||
"If you want to remove the main cache, first set different cache as main with tile.addCache()");
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused, false)) {
|
||||
//if we managed to free tile from record, we are sure we decreased cache count
|
||||
delete this._caches[key];
|
||||
}
|
||||
return deleteTarget;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the ratio between current and original size.
|
||||
* @function
|
||||
* @deprecated
|
||||
* @returns {number}
|
||||
*/
|
||||
getScaleForEdgeSmoothing: function() {
|
||||
// getCanvasContext is deprecated and so should be this method.
|
||||
$.console.warn("[Tile.getScaleForEdgeSmoothing] is deprecated, the following error is the consequence:");
|
||||
const context = this.getCanvasContext();
|
||||
if (!context) {
|
||||
$.console.warn(
|
||||
'[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached',
|
||||
'[Tile.drawCanvas] attempting to get tile scale %s when tile\'s not cached',
|
||||
this.toString());
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
return context.canvas.width / (this.size.x * $.pixelDensityRatio);
|
||||
},
|
||||
|
||||
rendered = this.context2D || this.cacheImageRecord.getRenderedContext();
|
||||
|
||||
if ( !this.loaded || !rendered ){
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
this.toString()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
context.save();
|
||||
|
||||
context.globalAlpha = this.opacity;
|
||||
|
||||
//if we are supposed to be rendering fully opaque rectangle,
|
||||
//ie its done fading or fading is turned off, and if we are drawing
|
||||
//an image with an alpha channel, then the only way
|
||||
//to avoid seeing the tile underneath is to clear the rectangle
|
||||
if (context.globalAlpha === 1 &&
|
||||
(this.context2D || this.url.match('.png'))) {
|
||||
//clearing only the inside of the rectangle occupied
|
||||
//by the png prevents edge flikering
|
||||
context.clearRect(
|
||||
(position.x * $.pixelDensityRatio)+1,
|
||||
(position.y * $.pixelDensityRatio)+1,
|
||||
(size.x * $.pixelDensityRatio)-2,
|
||||
(size.y * $.pixelDensityRatio)-2
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// This gives the application a chance to make image manipulation
|
||||
// changes as we are rendering the image
|
||||
drawingHandler({context: context, tile: this, rendered: rendered});
|
||||
|
||||
context.drawImage(
|
||||
rendered.canvas,
|
||||
0,
|
||||
0,
|
||||
rendered.canvas.width,
|
||||
rendered.canvas.height,
|
||||
position.x * $.pixelDensityRatio,
|
||||
position.y * $.pixelDensityRatio,
|
||||
size.x * $.pixelDensityRatio,
|
||||
size.y * $.pixelDensityRatio
|
||||
/**
|
||||
* Get a translation vector that when applied to the tile position produces integer coordinates.
|
||||
* Needed to avoid swimming and twitching.
|
||||
* @function
|
||||
* @param {Number} [scale=1] - Scale to be applied to position.
|
||||
* @returns {OpenSeadragon.Point}
|
||||
*/
|
||||
getTranslationForEdgeSmoothing: function(scale, canvasSize, sketchCanvasSize) {
|
||||
// The translation vector must have positive values, otherwise the image goes a bit off
|
||||
// the sketch canvas to the top and left and we must use negative coordinates to repaint it
|
||||
// to the main canvas. In that case, some browsers throw:
|
||||
// INDEX_SIZE_ERR: DOM Exception 1: Index or size was negative, or greater than the allowed value.
|
||||
const x = Math.max(1, Math.ceil((sketchCanvasSize.x - canvasSize.x) / 2));
|
||||
const y = Math.max(1, Math.ceil((sketchCanvasSize.y - canvasSize.y) / 2));
|
||||
return new $.Point(x, y).minus(
|
||||
this.position
|
||||
.times($.pixelDensityRatio)
|
||||
.times(scale || 1)
|
||||
.apply(function(x) {
|
||||
return x % 1;
|
||||
})
|
||||
);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes tile from its container.
|
||||
* Reflect that a cache object was renamed. Called internally from TileCache.
|
||||
* Do NOT call manually.
|
||||
* @function
|
||||
* @private
|
||||
*/
|
||||
unload: function() {
|
||||
if ( this.imgElement && this.imgElement.parentNode ) {
|
||||
this.imgElement.parentNode.removeChild( this.imgElement );
|
||||
reflectCacheRenamed: function (oldKey, newKey) {
|
||||
let cache = this._caches[oldKey];
|
||||
if (!cache) {
|
||||
return; // nothing to fix
|
||||
}
|
||||
if ( this.element && this.element.parentNode ) {
|
||||
this.element.parentNode.removeChild( this.element );
|
||||
// Do update via private refs, old key no longer exists in cache
|
||||
if (oldKey === this._ocKey) {
|
||||
this._ocKey = newKey;
|
||||
}
|
||||
if (oldKey === this._cKey) {
|
||||
this._cKey = newKey;
|
||||
}
|
||||
// Working key is never updated, it will be invalidated (but do not dereference cache, just fix the pointers)
|
||||
this._caches[newKey] = cache;
|
||||
delete this._caches[oldKey];
|
||||
},
|
||||
|
||||
this.element = null;
|
||||
this.imgElement = null;
|
||||
/**
|
||||
* Check if two tiles are data-equal
|
||||
* @param {OpenSeadragon.Tile} tile
|
||||
*/
|
||||
equals(tile) {
|
||||
return this._ocKey === tile._ocKey;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes tile from the system: it will still be present in the
|
||||
* OSD memory, but marked as loaded=false, and its data will be erased.
|
||||
* @param {boolean} [erase=false]
|
||||
*/
|
||||
unload: function(erase = false) {
|
||||
if (!this.loaded) {
|
||||
return;
|
||||
}
|
||||
this.tiledImage._tileCache.unloadTile(this, erase);
|
||||
},
|
||||
|
||||
/**
|
||||
* this method shall be called only by cache system when the tile is already empty of data
|
||||
* @private
|
||||
*/
|
||||
_unload: function () {
|
||||
this.tiledImage = null;
|
||||
this._caches = {};
|
||||
this._cacheSize = 0;
|
||||
this.loaded = false;
|
||||
this.loading = false;
|
||||
this._cKey = this._ocKey;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
1539
src/tilecache.js
2696
src/tiledimage.js
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - TileSource
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -55,16 +55,25 @@
|
|||
* @param {Object} options
|
||||
* You can either specify a URL, or literally define the TileSource (by specifying
|
||||
* width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,
|
||||
* the extending class is expected to implement 'getImageInfo' and 'configure'.
|
||||
* the extending class is expected to implement 'supports' and 'configure'.
|
||||
* Note that _in this case, the child class of getImageInfo() is ignored!_
|
||||
* For the latter, the construction is assumed to occur through
|
||||
* the extending classes implementation of 'configure'.
|
||||
* @param {String} [options.url]
|
||||
* The URL for the data necessary for this TileSource.
|
||||
* @param {String} [options.referenceStripThumbnailUrl]
|
||||
* The URL for a thumbnail image to be used by the reference strip
|
||||
* @param {Function} [options.success]
|
||||
* A function to be called upon successful creation.
|
||||
* @param {Boolean} [options.ajaxWithCredentials]
|
||||
* If this TileSource needs to make an AJAX call, this specifies whether to set
|
||||
* the XHR's withCredentials (for accessing secure data).
|
||||
* @param {Object} [options.ajaxHeaders]
|
||||
* A set of headers to include in AJAX requests.
|
||||
* @param {Boolean} [options.splitHashDataForPost]
|
||||
* First occurrence of '#' in the options.url is used to split URL
|
||||
* and the latter part is treated as POST data (applies to getImageInfo(...))
|
||||
* Does not work if getImageInfo() is overridden and used (see the options description)
|
||||
* @param {Number} [options.width]
|
||||
* Width of the source image at max resolution in pixels.
|
||||
* @param {Number} [options.height]
|
||||
|
@ -109,7 +118,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||
//by asynchronously fetching their configuration data.
|
||||
$.EventSource.call( this );
|
||||
|
||||
//we allow options to override anything we dont treat as
|
||||
//we allow options to override anything we don't treat as
|
||||
//required via idiomatic options or which is functionally
|
||||
//set depending on the state of the readiness of this tile
|
||||
//source
|
||||
|
@ -132,6 +141,12 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve context2D of this tile source
|
||||
* @memberOf OpenSeadragon.TileSource
|
||||
* @function getContext2D
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ratio of width to height
|
||||
* @member {Number} aspectRatio
|
||||
|
@ -163,12 +178,14 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||
* @memberof OpenSeadragon.TileSource#
|
||||
*/
|
||||
|
||||
if( 'string' == $.type( arguments[ 0 ] ) ){
|
||||
// TODO potentially buggy behavior: what if .url is used by child class before it calls super constructor?
|
||||
// this can happen if old JS class definition is used
|
||||
if( 'string' === $.type( arguments[ 0 ] ) ){
|
||||
this.url = arguments[0];
|
||||
}
|
||||
|
||||
if (this.url) {
|
||||
//in case the getImageInfo method is overriden and/or implies an
|
||||
//in case the getImageInfo method is overridden and/or implies an
|
||||
//async mechanism set some safe defaults first
|
||||
this.aspectRatio = 1;
|
||||
this.dimensions = new $.Point( 10, 10 );
|
||||
|
@ -187,10 +204,10 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||
//explicit configuration via positional args in constructor
|
||||
//or the more idiomatic 'options' object
|
||||
this.ready = true;
|
||||
this.aspectRatio = ( options.width && options.height ) ?
|
||||
( options.width / options.height ) : 1;
|
||||
this.aspectRatio = (options.width && options.height) ?
|
||||
(options.width / options.height) : 1;
|
||||
this.dimensions = new $.Point( options.width, options.height );
|
||||
|
||||
|
||||
if ( this.tileSize ){
|
||||
this._tileWidth = this._tileHeight = this.tileSize;
|
||||
delete this.tileSize;
|
||||
|
@ -212,7 +229,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||
this._tileHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0;
|
||||
this.minLevel = options.minLevel ? options.minLevel : 0;
|
||||
this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ?
|
||||
|
@ -230,17 +247,17 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||
|
||||
};
|
||||
|
||||
|
||||
$.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
||||
/** @lends OpenSeadragon.TileSource.prototype */
|
||||
$.TileSource.prototype = {
|
||||
|
||||
getTileSize: function( level ) {
|
||||
$.console.error(
|
||||
"[TileSource.getTileSize] is deprecated." +
|
||||
"[TileSource.getTileSize] is deprecated. " +
|
||||
"Use TileSource.getTileWidth() and TileSource.getTileHeight() instead"
|
||||
);
|
||||
return this._tileWidth;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the tileWidth for a given level.
|
||||
* Subclasses should override this if tileWidth can be different at different levels
|
||||
|
@ -271,12 +288,32 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
return this._tileHeight;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the maxLevel to the given level, and perform the memoization of
|
||||
* getLevelScale with the new maxLevel. This function can be useful if the
|
||||
* memoization is required before the first call of getLevelScale, or both
|
||||
* memoized getLevelScale and maxLevel should be changed accordingly.
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
setMaxLevel: function( level ) {
|
||||
this.maxLevel = level;
|
||||
this._memoizeLevelScale();
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
*/
|
||||
getLevelScale: function( level ) {
|
||||
// if getLevelScale is not memoized, we generate the memoized version
|
||||
// at the first call and return the result
|
||||
this._memoizeLevelScale();
|
||||
return this.getLevelScale( level );
|
||||
},
|
||||
|
||||
// private
|
||||
_memoizeLevelScale: function() {
|
||||
// see https://github.com/openseadragon/openseadragon/issues/22
|
||||
// we use the tilesources implementation of getLevelScale to generate
|
||||
// a memoized re-implementation
|
||||
|
@ -288,7 +325,6 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
this.getLevelScale = function( _level ){
|
||||
return levelScaleCache[ _level ];
|
||||
};
|
||||
return this.getLevelScale( level );
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -309,8 +345,8 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
*/
|
||||
getPixelRatio: function( level ) {
|
||||
var imageSizeScaled = this.dimensions.times( this.getLevelScale( level ) ),
|
||||
rx = 1.0 / imageSizeScaled.x,
|
||||
ry = 1.0 / imageSizeScaled.y;
|
||||
rx = 1.0 / imageSizeScaled.x * $.pixelDensityRatio,
|
||||
ry = 1.0 / imageSizeScaled.y * $.pixelDensityRatio;
|
||||
|
||||
return new $.Point(rx, ry);
|
||||
},
|
||||
|
@ -318,25 +354,20 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @returns {Number} The highest level in this tile source that can be contained in a single tile.
|
||||
*/
|
||||
getClosestLevel: function( rect ) {
|
||||
getClosestLevel: function() {
|
||||
var i,
|
||||
tilesPerSide,
|
||||
tiles;
|
||||
|
||||
for( i = this.minLevel; i < this.maxLevel; i++ ){
|
||||
tiles = this.getNumTiles( i );
|
||||
tilesPerSide = new $.Point(
|
||||
Math.floor( rect.x / this.getTileWidth(i) ),
|
||||
Math.floor( rect.y / this.getTileHeight(i) )
|
||||
);
|
||||
|
||||
if( tiles.x + 1 >= tilesPerSide.x && tiles.y + 1 >= tilesPerSide.y ){
|
||||
for (i = this.minLevel + 1; i <= this.maxLevel; i++){
|
||||
tiles = this.getNumTiles(i);
|
||||
if (tiles.x > 1 || tiles.y > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Math.max( 0, i - 1 );
|
||||
|
||||
return i - 1;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -344,12 +375,30 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
* @param {Number} level
|
||||
* @param {OpenSeadragon.Point} point
|
||||
*/
|
||||
getTileAtPoint: function( level, point ) {
|
||||
var pixel = point.times( this.dimensions.x ).times( this.getLevelScale(level) ),
|
||||
tx = Math.floor( pixel.x / this.getTileWidth(level) ),
|
||||
ty = Math.floor( pixel.y / this.getTileHeight(level) );
|
||||
getTileAtPoint: function(level, point) {
|
||||
var validPoint = point.x >= 0 && point.x <= 1 &&
|
||||
point.y >= 0 && point.y <= 1 / this.aspectRatio;
|
||||
$.console.assert(validPoint, "[TileSource.getTileAtPoint] must be called with a valid point.");
|
||||
|
||||
return new $.Point( tx, ty );
|
||||
|
||||
var widthScaled = this.dimensions.x * this.getLevelScale(level);
|
||||
var pixelX = point.x * widthScaled;
|
||||
var pixelY = point.y * widthScaled;
|
||||
|
||||
var x = Math.floor(pixelX / this.getTileWidth(level));
|
||||
var y = Math.floor(pixelY / this.getTileHeight(level));
|
||||
|
||||
// When point.x == 1 or point.y == 1 / this.aspectRatio we want to
|
||||
// return the last tile of the row/column
|
||||
if (point.x >= 1) {
|
||||
x = this.getNumTiles(level).x - 1;
|
||||
}
|
||||
var EPSILON = 1e-15;
|
||||
if (point.y >= 1 / this.aspectRatio - EPSILON) {
|
||||
y = this.getNumTiles(level).y - 1;
|
||||
}
|
||||
|
||||
return new $.Point(x, y);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -357,8 +406,12 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Boolean} [isSource=false] Whether to return the source bounds of the tile.
|
||||
* @returns {OpenSeadragon.Rect} Either where this tile fits (in normalized coordinates) or the
|
||||
* portion of the tile to use as the source of the drawing operation (in pixels), depending on
|
||||
* the isSource parameter.
|
||||
*/
|
||||
getTileBounds: function( level, x, y ) {
|
||||
getTileBounds: function( level, x, y, isSource ) {
|
||||
var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ),
|
||||
tileWidth = this.getTileWidth(level),
|
||||
tileHeight = this.getTileHeight(level),
|
||||
|
@ -371,6 +424,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
sx = Math.min( sx, dimensionsScaled.x - px );
|
||||
sy = Math.min( sy, dimensionsScaled.y - py );
|
||||
|
||||
if (isSource) {
|
||||
return new $.Rect(0, 0, sx, sy);
|
||||
}
|
||||
|
||||
return new $.Rect( px * scale, py * scale, sx * scale, sy * scale );
|
||||
},
|
||||
|
||||
|
@ -378,6 +435,13 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
/**
|
||||
* Responsible for retrieving, and caching the
|
||||
* image metadata pertinent to this TileSources implementation.
|
||||
* There are three scenarios of opening a tile source: providing a parseable string, plain object, or an URL.
|
||||
* This method is only called by OSD if the TileSource configuration is a non-parseable string (~url).
|
||||
*
|
||||
* The string can contain a hash `#` symbol, followed by
|
||||
* key=value arguments. If this is the case, this method sends this
|
||||
* data as a POST body.
|
||||
*
|
||||
* @function
|
||||
* @param {String} url
|
||||
* @throws {Error}
|
||||
|
@ -402,8 +466,17 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
}
|
||||
}
|
||||
|
||||
var postData = null;
|
||||
if (this.splitHashDataForPost) {
|
||||
var hashIdx = url.indexOf("#");
|
||||
if (hashIdx !== -1) {
|
||||
postData = url.substring(hashIdx + 1);
|
||||
url = url.substr(0, hashIdx);
|
||||
}
|
||||
}
|
||||
|
||||
callback = function( data ){
|
||||
if( typeof(data) === "string" ) {
|
||||
if( typeof (data) === "string" ) {
|
||||
data = $.parseXml( data );
|
||||
}
|
||||
var $TileSource = $.TileSource.determineType( _this, data, url );
|
||||
|
@ -423,7 +496,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
return;
|
||||
}
|
||||
|
||||
options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
|
||||
options = $TileSource.prototype.configure.apply( _this, [ data, url, postData ]);
|
||||
if (options.ajaxWithCredentials === undefined) {
|
||||
options.ajaxWithCredentials = _this.ajaxWithCredentials;
|
||||
}
|
||||
|
@ -447,7 +520,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
//TODO: Its not very flexible to require tile sources to end jsonp
|
||||
// request for info with a url that ends with '.js' but for
|
||||
// now it's the only way I see to distinguish uniformly.
|
||||
callbackName = url.split( '/' ).pop().replace('.js','');
|
||||
callbackName = url.split('/').pop().replace('.js', '');
|
||||
$.jsonp({
|
||||
url: url,
|
||||
async: false,
|
||||
|
@ -458,7 +531,9 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
// request info via xhr asynchronously.
|
||||
$.makeAjaxRequest( {
|
||||
url: url,
|
||||
postData: postData,
|
||||
withCredentials: this.ajaxWithCredentials,
|
||||
headers: this.ajaxHeaders,
|
||||
success: function( xhr ) {
|
||||
var data = processResponse( xhr );
|
||||
callback( data );
|
||||
|
@ -472,18 +547,20 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
exception rather than the second one raised when we try to access xhr.status
|
||||
*/
|
||||
try {
|
||||
msg = "HTTP " + xhr.status + " attempting to load TileSource";
|
||||
msg = "HTTP " + xhr.status + " attempting to load TileSource: " + url;
|
||||
} catch ( e ) {
|
||||
var formattedExc;
|
||||
if ( typeof( exc ) == "undefined" || !exc.toString ) {
|
||||
if ( typeof ( exc ) === "undefined" || !exc.toString ) {
|
||||
formattedExc = "Unknown error";
|
||||
} else {
|
||||
formattedExc = exc.toString();
|
||||
}
|
||||
|
||||
msg = formattedExc + " attempting to load TileSource";
|
||||
msg = formattedExc + " attempting to load TileSource: " + url;
|
||||
}
|
||||
|
||||
$.console.error(msg);
|
||||
|
||||
/***
|
||||
* Raised when an error occurs loading a TileSource.
|
||||
*
|
||||
|
@ -493,11 +570,14 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
|
||||
* @property {String} message
|
||||
* @property {String} source
|
||||
* @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
||||
* see TileSource::getTilePostData) or null
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
_this.raiseEvent( 'open-failed', {
|
||||
message: msg,
|
||||
source: url
|
||||
source: url,
|
||||
postData: postData
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -506,23 +586,34 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
},
|
||||
|
||||
/**
|
||||
* Responsible determining if a the particular TileSource supports the
|
||||
* Responsible for determining if the particular TileSource supports the
|
||||
* data format ( and allowed to apply logic against the url the data was
|
||||
* loaded from, if any ). Overriding implementations are expected to do
|
||||
* something smart with data and / or url to determine support. Also
|
||||
* understand that iteration order of TileSources is not guarunteed so
|
||||
* understand that iteration order of TileSources is not guaranteed so
|
||||
* please make sure your data or url is expressive enough to ensure a simple
|
||||
* and sufficient mechanisim for clear determination.
|
||||
* and sufficient mechanism for clear determination.
|
||||
* @function
|
||||
* @param {String|Object|Array|Document} data
|
||||
* @param {String} url - the url the data was loaded
|
||||
* from if any.
|
||||
* @return {Boolean}
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
supports: function( data, url ) {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check whether two tileSources are equal. This is used for example
|
||||
* when replacing tile-sources, which turns on the zombie cache before
|
||||
* old item removal.
|
||||
* @param {OpenSeadragon.TileSource} otherSource
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
equals: function (otherSource) {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for parsing and configuring the
|
||||
* image metadata pertinent to this TileSources implementation.
|
||||
|
@ -534,17 +625,30 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
* @param {String|Object|Array|Document} data
|
||||
* @param {String} url - the url the data was loaded
|
||||
* from if any.
|
||||
* @return {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure this tile sources constructor.
|
||||
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null value obtained from
|
||||
* the protocol URL after '#' sign if flag splitHashDataForPost set to 'true'
|
||||
* @returns {Object} options - A dictionary of keyword arguments sufficient
|
||||
* to configure the tile source constructor (include all values you want to
|
||||
* instantiate the TileSource subclass with - what _options_ object should contain).
|
||||
* @throws {Error}
|
||||
*/
|
||||
configure: function( data, url ) {
|
||||
configure: function( data, url, postData ) {
|
||||
throw new Error( "Method not implemented." );
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for retriving the url which will return an image for the
|
||||
* region speified by the given x, y, and level components.
|
||||
* Shall this source need to free some objects
|
||||
* upon unloading, it must be done here. For example, canvas
|
||||
* size must be set to 0 for safari to free.
|
||||
* @param {OpenSeadragon.Viewer} viewer
|
||||
*/
|
||||
destroy: function ( viewer ) {
|
||||
//no-op
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for retrieving the url which will return an image for the
|
||||
* region specified by the given x, y, and level components.
|
||||
* This method is not implemented by this class other than to throw an Error
|
||||
* announcing you have to implement it. Because of the variety of tile
|
||||
* server technologies, and various specifications for building image
|
||||
|
@ -553,12 +657,98 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @returns {String|Function} url - A string for the url or a function that returns a url string.
|
||||
* @throws {Error}
|
||||
*/
|
||||
getTileUrl: function( level, x, y ) {
|
||||
throw new Error( "Method not implemented." );
|
||||
},
|
||||
|
||||
/**
|
||||
* Must use AJAX in order to work, i.e. loadTilesWithAjax = true is set.
|
||||
* If a value is returned, ajax issues POST request to the tile url.
|
||||
* If null is returned, ajax issues GET request.
|
||||
* The return value must comply to the header 'content type'.
|
||||
*
|
||||
* Examples (USED HEADER --> getTilePostData CODE):
|
||||
* 'Content-type': 'application/x-www-form-urlencoded' -->
|
||||
* return "key1=value=1&key2=value2";
|
||||
*
|
||||
* 'Content-type': 'application/x-www-form-urlencoded' -->
|
||||
* return JSON.stringify({key: "value", number: 5});
|
||||
*
|
||||
* 'Content-type': 'multipart/form-data' -->
|
||||
* let result = new FormData();
|
||||
* result.append("data", myData);
|
||||
* return result;
|
||||
*
|
||||
* IMPORTANT: in case you move all the logic on image fetching
|
||||
* to post data, you must re-define 'getTileHashKey(...)' to
|
||||
* stay unique for different tile images.
|
||||
*
|
||||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @returns {*|null} post data to send with tile configuration request
|
||||
*/
|
||||
getTilePostData: function( level, x, y ) {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for retrieving the headers which will be attached to the image request for the
|
||||
* region specified by the given x, y, and level components.
|
||||
* This option is only relevant if {@link OpenSeadragon.Options}.loadTilesWithAjax is set to true.
|
||||
* The headers returned here will override headers specified at the Viewer or TiledImage level.
|
||||
* Specifying a falsy value for a header will clear its existing value set at the Viewer or
|
||||
* TiledImage level (if any).
|
||||
*
|
||||
* Note that the headers of existing tiles don't automatically change when this function
|
||||
* returns updated headers. To do that, you need to call {@link OpenSeadragon.Viewer#setAjaxHeaders}
|
||||
* and propagate the changes.
|
||||
*
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @returns {Object}
|
||||
*/
|
||||
getTileAjaxHeaders: function( level, x, y ) {
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* The tile cache object is uniquely determined by this key and used to lookup
|
||||
* the image data in cache: keys should be different if images are different.
|
||||
*
|
||||
* You can return falsey tile cache key, in which case the tile will
|
||||
* be created without invoking ImageJob --- but with data=null. Then,
|
||||
* you are responsible for manually creating the cache data. This is useful
|
||||
* particularly if you want to use empty TiledImage with client-side derived data
|
||||
* only. The default tile-cache key is then called "" - an empty string.
|
||||
*
|
||||
* Note: default behaviour does not take into account post data.
|
||||
* @param {Number} level tile level it was fetched with
|
||||
* @param {Number} x x-coordinate in the pyramid level
|
||||
* @param {Number} y y-coordinate in the pyramid level
|
||||
* @param {String} url the tile was fetched with
|
||||
* @param {Object} ajaxHeaders the tile was fetched with
|
||||
* @param {*} postData data the tile was fetched with (type depends on getTilePostData(..) return type)
|
||||
* @return {?String} can return the cache key or null, in that case an empty cache is initialized
|
||||
* without downloading any data for internal use: user has to define the cache contents manually, via
|
||||
* the cache interface of this class.
|
||||
*/
|
||||
getTileHashKey: function(level, x, y, url, ajaxHeaders, postData) {
|
||||
function withHeaders(hash) {
|
||||
return ajaxHeaders ? hash + "+" + JSON.stringify(ajaxHeaders) : hash;
|
||||
}
|
||||
|
||||
if (typeof url !== "string") {
|
||||
return withHeaders(level + "/" + x + "_" + y);
|
||||
}
|
||||
return withHeaders(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
|
@ -567,12 +757,217 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
|||
*/
|
||||
tileExists: function( level, x, y ) {
|
||||
var numTiles = this.getNumTiles( level );
|
||||
return level >= this.minLevel &&
|
||||
level <= this.maxLevel &&
|
||||
x >= 0 &&
|
||||
y >= 0 &&
|
||||
x < numTiles.x &&
|
||||
y < numTiles.y;
|
||||
return level >= this.minLevel &&
|
||||
level <= this.maxLevel &&
|
||||
x >= 0 &&
|
||||
y >= 0 &&
|
||||
x < numTiles.x &&
|
||||
y < numTiles.y;
|
||||
},
|
||||
|
||||
/**
|
||||
* Decide whether tiles have transparency: this is crucial for correct images blending.
|
||||
* Overriden on a tile level by setting tile.hasTransparency = true;
|
||||
* @param context2D unused, deprecated argument
|
||||
* @param url tile.getUrl() value for given tile
|
||||
* @param ajaxHeaders tile.ajaxHeaders value for given tile
|
||||
* @param post tile.post value for given tile
|
||||
* @returns {boolean} true if the image has transparency
|
||||
*/
|
||||
hasTransparency: function(context2D, url, ajaxHeaders, post) {
|
||||
return url.match('.png');
|
||||
},
|
||||
|
||||
/**
|
||||
* Download tile data.
|
||||
* Note that if you override this function, you should override also downloadTileAbort().
|
||||
* @param {ImageJob} context job context that you have to call finish(...) on.
|
||||
* @param {String} [context.src] - URL of image to download.
|
||||
* @param {String} [context.loadWithAjax] - Whether to load this image with AJAX.
|
||||
* @param {String} [context.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||
* @param {Boolean} [context.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
|
||||
* @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads
|
||||
* @param {?String|?Object} [context.postData] - HTTP POST data (usually but not necessarily
|
||||
* in k=v&k2=v2... form, see TileSource::getTilePostData) or null
|
||||
* @param {*} [context.userData] - Empty object to attach your own data and helper variables to.
|
||||
* @param {Function} [context.finish] - Should be called unless abort() was executed upon successful
|
||||
* data retrieval.
|
||||
* Usage: context.finish(data, request, dataType=undefined). Pass the downloaded data object
|
||||
* add also reference to an ajax request if used. Optionally, specify what data type the data is.
|
||||
* @param {Function} [context.fail] - Should be called unless abort() was executed upon unsuccessful request.
|
||||
* Usage: context.fail(errMessage, request). Provide error message in case of failure,
|
||||
* add also reference to an ajax request if used.
|
||||
* @param {Function} [context.abort] - Called automatically when the job times out.
|
||||
* Usage: if you decide to abort the request (no fail/finish will be called), call context.abort().
|
||||
* @param {Function} [context.callback] Private parameter. Called automatically once image has been downloaded
|
||||
* (triggered by finish).
|
||||
* @param {Number} [context.timeout] Private parameter. The max number of milliseconds that
|
||||
* this image job may take to complete.
|
||||
* @param {string} [context.errorMsg] Private parameter. The final error message, default null (set by finish).
|
||||
*/
|
||||
downloadTileStart: function (context) {
|
||||
const dataStore = context.userData,
|
||||
image = new Image();
|
||||
|
||||
dataStore.image = image;
|
||||
dataStore.request = null;
|
||||
|
||||
const finalize = function(error) {
|
||||
if (error || !image) {
|
||||
context.fail(error || "[downloadTileStart] Image load failed: undefined Image instance.",
|
||||
dataStore.request);
|
||||
return;
|
||||
}
|
||||
image.onload = image.onerror = image.onabort = null;
|
||||
context.finish(image, dataStore.request, "image");
|
||||
};
|
||||
image.onload = function () {
|
||||
finalize();
|
||||
};
|
||||
image.onabort = image.onerror = function() {
|
||||
finalize("[downloadTileStart] Image load aborted.");
|
||||
};
|
||||
|
||||
// Load the tile with an AJAX request if the loadWithAjax option is
|
||||
// set. Otherwise load the image by setting the source property of the image object.
|
||||
if (context.loadWithAjax) {
|
||||
dataStore.request = $.makeAjaxRequest({
|
||||
url: context.src,
|
||||
withCredentials: context.ajaxWithCredentials,
|
||||
headers: context.ajaxHeaders,
|
||||
responseType: "arraybuffer",
|
||||
postData: context.postData,
|
||||
success: function(request) {
|
||||
var blb;
|
||||
// Make the raw data into a blob.
|
||||
// BlobBuilder fallback adapted from
|
||||
// http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility
|
||||
try {
|
||||
blb = new window.Blob([request.response]);
|
||||
} catch (e) {
|
||||
const BlobBuilder = (
|
||||
window.BlobBuilder ||
|
||||
window.WebKitBlobBuilder ||
|
||||
window.MozBlobBuilder ||
|
||||
window.MSBlobBuilder
|
||||
);
|
||||
if (e.name === 'TypeError' && BlobBuilder) {
|
||||
const bb = new BlobBuilder();
|
||||
bb.append(request.response);
|
||||
blb = bb.getBlob();
|
||||
}
|
||||
}
|
||||
// If the blob is empty for some reason consider the image load a failure.
|
||||
if (blb.size === 0) {
|
||||
finalize("[downloadTileStart] Empty image response.");
|
||||
} else {
|
||||
// Create a URL for the blob data and make it the source of the image object.
|
||||
// This will still trigger Image.onload to indicate a successful tile load.
|
||||
image.src = (window.URL || window.webkitURL).createObjectURL(blb);
|
||||
}
|
||||
},
|
||||
error: function(request) {
|
||||
finalize("[downloadTileStart] Image load aborted - XHR error");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (context.crossOriginPolicy !== false) {
|
||||
image.crossOrigin = context.crossOriginPolicy;
|
||||
}
|
||||
image.src = context.src;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Provide means of aborting the execution.
|
||||
* Note that if you override this function, you should override also downloadTileStart().
|
||||
* Note that calling job.abort() would create an infinite loop!
|
||||
*
|
||||
* @param {ImageJob} context job, the same object as with downloadTileStart(..)
|
||||
* @param {*} [context.userData] - Empty object to attach (and mainly read) your own data.
|
||||
*/
|
||||
downloadTileAbort: function (context) {
|
||||
if (context.userData.request) {
|
||||
context.userData.request.abort();
|
||||
}
|
||||
var image = context.userData.image;
|
||||
if (context.userData.image) {
|
||||
image.onload = image.onerror = image.onabort = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create cache object from the result of the download process. The
|
||||
* cacheObject parameter should be used to attach the data to, there are no
|
||||
* conventions on how it should be stored - all the logic is implemented within *TileCache() functions.
|
||||
*
|
||||
* Note that
|
||||
* - data is cached automatically as cacheObject.data
|
||||
* - if you override any of *TileCache() functions, you should override all of them.
|
||||
* - these functions might be called over shared cache object managed by other TileSources simultaneously.
|
||||
* @param {OpenSeadragon.CacheRecord} cacheObject context cache object
|
||||
* @param {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
|
||||
* @param {OpenSeadragon.Tile} tile instance the cache was created with
|
||||
* @deprecated
|
||||
*/
|
||||
createTileCache: function(cacheObject, data, tile) {
|
||||
$.console.error("[TileSource.createTileCache] has been deprecated. Use cache API of a tile instead.");
|
||||
//no-op, we create the cache automatically
|
||||
},
|
||||
|
||||
/**
|
||||
* Cache object destructor, unset all properties you created to allow GC collection.
|
||||
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||
* Note that these functions might be called over shared cache object managed by other TileSources simultaneously.
|
||||
* Original cache data is cacheObject.data, but do not delete it manually! It is taken care for,
|
||||
* you might break things.
|
||||
* @param {OpenSeadragon.CacheRecord} cacheObject context cache object
|
||||
* @deprecated
|
||||
*/
|
||||
destroyTileCache: function (cacheObject) {
|
||||
$.console.error("[TileSource.destroyTileCache] has been deprecated. Use cache API of a tile instead.");
|
||||
//no-op, handled internally
|
||||
},
|
||||
|
||||
/**
|
||||
* Raw data getter, should return anything that is compatible with the system, or undefined
|
||||
* if the system can handle it.
|
||||
* @param {OpenSeadragon.CacheRecord} cacheObject context cache object
|
||||
* @returns {OpenSeadragon.Promise<?>} cache data
|
||||
* @deprecated
|
||||
*/
|
||||
getTileCacheData: function(cacheObject) {
|
||||
$.console.error("[TileSource.getTileCacheData] has been deprecated. Use cache API of a tile instead.");
|
||||
return cacheObject.getDataAs(undefined, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Compatibility image element getter
|
||||
* - plugins might need image representation of the data
|
||||
* - div HTML rendering relies on image element presence
|
||||
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||
* Note that these functions might be called over shared cache object managed by other TileSources simultaneously.
|
||||
* @param {OpenSeadragon.CacheRecord} cacheObject context cache object
|
||||
* @returns {Image} cache data as an Image
|
||||
* @deprecated
|
||||
*/
|
||||
getTileCacheDataAsImage: function(cacheObject) {
|
||||
$.console.error("[TileSource.getTileCacheDataAsImage] has been deprecated. Use cache API of a tile instead.");
|
||||
return cacheObject.getImage();
|
||||
},
|
||||
|
||||
/**
|
||||
* Compatibility context 2D getter
|
||||
* - most heavily used rendering method is a canvas-based approach,
|
||||
* convert the data to a canvas and return it's 2D context
|
||||
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||
* @param {OpenSeadragon.CacheRecord} cacheObject context cache object
|
||||
* @returns {CanvasRenderingContext2D} context of the canvas representation of the cache data
|
||||
* @deprecated
|
||||
*/
|
||||
getTileCacheDataAsContext2D: function(cacheObject) {
|
||||
$.console.error("[TileSource.getTileCacheDataAsContext2D] has been deprecated. Use cache API of a tile instead.");
|
||||
return cacheObject.getRenderedContext();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -598,13 +993,13 @@ function processResponse( xhr ){
|
|||
throw new Error( $.getString( "Errors.Security" ) );
|
||||
} else if ( xhr.status !== 200 && xhr.status !== 0 ) {
|
||||
status = xhr.status;
|
||||
statusText = ( status == 404 ) ?
|
||||
statusText = ( status === 404 ) ?
|
||||
"Not Found" :
|
||||
xhr.statusText;
|
||||
throw new Error( $.getString( "Errors.Status", status, statusText ) );
|
||||
}
|
||||
|
||||
if( responseText.match(/\s*<.*/) ){
|
||||
if( responseText.match(/^\s*<.*/) ){
|
||||
try{
|
||||
data = ( xhr.responseXML && xhr.responseXML.documentElement ) ?
|
||||
xhr.responseXML :
|
||||
|
@ -612,8 +1007,12 @@ function processResponse( xhr ){
|
|||
} catch (e){
|
||||
data = xhr.responseText;
|
||||
}
|
||||
}else if( responseText.match(/\s*[\{\[].*/) ){
|
||||
data = $.parseJSON(responseText);
|
||||
}else if( responseText.match(/\s*[{[].*/) ){
|
||||
try{
|
||||
data = $.parseJSON(responseText);
|
||||
} catch(e){
|
||||
data = responseText;
|
||||
}
|
||||
}else{
|
||||
data = responseText;
|
||||
}
|
||||
|
@ -644,6 +1043,8 @@ $.TileSource.determineType = function( tileSource, data, url ){
|
|||
}
|
||||
|
||||
$.console.error( "No TileSource was able to open %s %s", url, data );
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenSeadragon - TileSourceCollection
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
* Copyright (C) 2010-2024 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
|
@ -32,11 +32,11 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
(function($) {
|
||||
|
||||
// deprecated
|
||||
$.TileSourceCollection = function( tileSize, tileSources, rows, layout ) {
|
||||
$.TileSourceCollection = function(tileSize, tileSources, rows, layout) {
|
||||
$.console.error('TileSourceCollection is deprecated; use World instead');
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
||||
}(OpenSeadragon));
|
||||
|
|