| ofs | hex dump | ascii |
|---|
| 0000 | 47 49 46 38 39 61 cc 01 98 01 f7 fe 00 3e 3d 40 40 3f 43 78 56 11 79 5a 20 6f 63 39 4d 4d 4f 5e | GIF89a.......>=@@?CxV.yZ.oc9MMO^ |
| 0020 | 5e 60 5e 69 7c 60 5f 62 74 6e 49 6f 6f 70 70 78 86 72 7d a9 7a 85 90 7b 88 ab 70 b0 88 77 b0 a1 | ^`^i|`_btnIooppx.r}.z..{..p..w.. |
| 0040 | 78 c3 8c 7c d4 a0 95 1b 1b b9 0d 0d ac 2c 2c 82 5d 12 82 5e 21 96 6d 15 89 65 24 82 69 35 8c 71 | x..|.........,,.]..^!.m..e$.i5.q |
| 0060 | 37 94 6b 23 9d 66 3b 99 72 27 96 78 39 a6 78 19 a0 68 37 a3 7a 26 a1 7d 33 b0 6a 2d b8 70 32 9e | 7.k#.f;.r'.x9.x..h7.z&.}3.j-.p2. |
| 0080 | 58 58 90 75 4d 8a 7a 69 b3 52 52 aa 79 4e a6 71 71 d8 1b 1b cb 33 33 eb 01 02 fd 1b 20 ef 26 2c | XX.uM.zi.RR.yN.qq....33.......&, |
| 00a0 | cb 4c 4f d4 63 6a 80 7f 81 9e 80 3f b4 83 19 ab 82 2b a5 81 38 be 8e 2b b7 8d 35 bd 93 36 86 80 | .LO.cj.....?.....+..8..+..5..6.. |
| 00c0 | 4e 8c 85 55 9d 82 49 94 8c 59 9a 92 5d 8b 85 67 8a 87 75 94 88 65 93 8c 75 9d 95 61 98 93 78 a7 | N..U..I..Y..]..g..u..e..u..a..x. |
| 00e0 | 88 46 a4 8a 56 a5 96 5a b0 8e 46 b5 93 4a b6 93 52 a3 89 65 a1 87 70 a2 98 63 a3 99 76 b1 97 64 | .F..V..Z..F..J..R..e..p..c..v..d |
| 0100 | b1 9c 71 a7 a0 5e bd a5 5d aa a2 67 a9 a3 78 b2 a9 69 b4 aa 75 bb b2 6a ba b3 75 c3 8e 1c cb 93 | ..q..^..]..g..x..i..u..j..u..... |
| 0120 | 1d d5 9b 1e c1 8f 23 c6 94 26 c0 93 32 d3 9d 28 dd a0 1f dc a1 21 d9 a4 33 e0 a2 1f e7 a8 21 e1 | ......#..&..2..(.....!..3.....!. |
| 0140 | ac 3b e7 b3 3a f0 ae 22 f7 b6 26 f1 b7 35 c2 9c 4c c8 a0 4c ca a4 51 d4 ab 54 db b1 57 c9 a9 65 | .;..:.."..&..5..L..L..Q..T..W..e |
| 0160 | c4 ac 75 c1 b9 6d c4 ba 75 d1 ae 64 d9 b4 65 d4 b7 75 e6 b9 5b e2 bd 6d fd c4 2c fd cb 30 fe d7 | ..u..m..u..d..e..u..[..m..,..0.. |
| 0180 | 34 ff e6 3b ff f0 3f d7 c1 58 d3 ca 77 e7 c0 47 ee c0 5e e5 d2 4e f0 c1 5f f4 d9 41 fa cf 67 e7 | 4..;..?..X..w..G..^..N.._..A..g. |
| 01a0 | e4 56 ff ee 40 fe f9 45 f2 f0 50 85 87 89 88 8c 93 8f 90 8e 8c 92 98 97 8a 84 98 8d 92 97 94 86 | .V..@..E..P..................... |
| 01c0 | 94 95 97 86 8c a4 81 8d ba 89 93 a8 8c 9b bc 90 8f a2 96 9b a4 93 9c b5 88 af 96 89 a4 a6 8f a0 | ................................ |
| 01e0 | b8 89 b6 a3 88 b4 b1 9c a2 a9 9a a4 b4 97 b2 aa 99 b9 b7 a8 83 87 a1 9c 86 a1 9b 96 a0 9e ab a8 | ................................ |
| 0200 | a4 87 a7 a4 97 b2 ac 87 b1 ab 97 b8 b3 87 b9 b5 96 a5 a6 a9 a4 ab b5 aa b1 ae ac b3 b9 b2 ae a3 | ................................ |
| 0220 | b0 ae b3 b6 b4 a7 b7 b8 b9 8c 99 c1 92 9e c4 94 a2 c6 9b ab d3 9d b1 c8 9f b0 d9 a6 ac c3 a2 ae | ................................ |
| 0240 | d1 a8 b4 c6 a8 ba d3 b5 ae c0 b5 ba c5 b6 bb d4 b4 bb e3 87 c7 9a 88 c8 a4 b9 c1 be b0 d1 bd 99 | ................................ |
| 0260 | ce c8 ac c0 da ba c3 c9 b8 c3 d5 b7 d0 c9 b7 cd e9 c2 bb 87 c1 bb 96 c2 bb a6 c1 bc b4 c0 be c4 | ................................ |
| 0280 | c9 c2 85 c8 c4 96 d3 cb 84 d2 cc 94 db d2 85 d9 d3 95 c9 c8 a3 c5 c3 ba d0 cc a8 d1 ce b3 d6 d6 | ................................ |
| 02a0 | a4 d2 d1 b2 e3 da 83 e2 de a7 f2 eb 90 fa f9 ac c6 c7 c9 c4 ca d5 c8 d0 ce ca d3 d9 d0 ce ca d0 | ................................ |
| 02c0 | cf d0 d3 d2 cc d5 d6 d7 c9 c9 e5 cb d5 e2 c3 d9 f8 d0 cf e7 d3 d7 e8 dc dc f0 d8 e0 de ce e1 e9 | ................................ |
| 02e0 | cb e2 fc d9 e2 e9 d9 e6 f4 db f2 fd e0 de e9 e4 e3 db e6 eb ea e6 e7 f5 e8 f0 ef e9 f5 fa f0 ef | ................................ |
| 0300 | fd f0 f0 ee fe fe fe 00 00 00 00 00 00 21 ff 0b 4e 45 54 53 43 41 50 45 32 2e 30 03 01 02 00 00 | .............!..NETSCAPE2.0..... |
| 0320 | 21 f9 04 05 05 00 ff 00 2c 00 00 00 00 cc 01 98 01 00 08 fe 00 fb 09 1c 48 b0 a0 c1 83 08 13 2a | !.......,...............H......* |
| 0340 | 5c c8 b0 a1 c3 87 10 23 4a 9c 48 b1 a2 c5 8b 18 33 6a dc c8 b1 a3 c7 8f 20 43 8a 1c 49 b2 a4 c9 | \......#J.H.....3j.......C..I... |
| 0360 | 93 28 53 aa 5c c9 b2 a5 cb 97 30 63 ca 9c 49 b3 a6 cd 9b 38 73 ea dc c9 b3 a7 cf 9f 40 83 0a 1d | .(S.\.....0c..I....8s.......@... |
| 0380 | 4a b4 a8 d1 a3 48 93 2a 5d ca b4 a9 d3 a7 50 a3 4a 9d 4a b5 aa d5 ab 58 b3 6a dd ca b5 ab d7 af | J....H.*].....P.J.J....X.j...... |
| 03a0 | 60 c3 8a 1d 4b b6 ac d9 b3 68 d3 aa 5d cb b6 ad db b7 70 e3 ca 9d 4b b7 ae dd bb 78 f3 ea dd cb | `...K....h..].....p...K....x.... |
| 03c0 | b7 af df bf 80 03 0b 1e 4c b8 b0 e1 c3 88 13 2b 5e cc b8 b1 e3 c7 90 23 4b 9e 4c b9 b2 e5 cb 98 | ........L......+^......#K.L..... |
| 03e0 | 33 6b de cc b9 b3 e7 cf a0 43 8b 1e 4d ba b4 e9 d3 a8 53 ab 5e cd ba b5 eb d7 b0 63 d7 bc c7 ea | 3k.......C..M.....S.^......c.... |
| 0400 | d4 a9 67 f9 64 eb 06 1b 2e 52 11 48 41 1c 5d ba b7 bb b8 d6 59 a6 ae 0c 41 42 84 48 91 59 c6 a3 | ..g.d....R.HA.].....Y...AB.H.Y.. |
| 0420 | 57 c5 16 a9 ca 93 e6 4e 9a 47 92 ce 1d 2a 36 55 a8 9e fe fc c8 4e 24 3b a4 ee e8 97 ce 62 a6 aa | W......N.G...*6U.....N$;.....b.. |
| 0440 | 48 f3 f7 cd 8b a4 9f 7f d4 14 2a 53 ef b3 eb 2f 12 8e be ff a0 e8 a4 62 4a 11 e4 c1 e7 1c 31 ff | H.........*S.../.......bJ.....1. |
| 0460 | 25 d8 93 2c ab e0 87 5d 79 d8 3d a7 e0 84 38 bd a3 ca 80 44 30 97 5f 7c 08 52 e8 e1 4c b2 98 c2 | %..,...]y.=...8....D0._|.R..L... |
| 0480 | 4c 81 10 46 08 dd 87 28 ba 94 0f 29 cc b8 f7 e0 8b 45 3c 93 e2 8c 2b a1 63 ca 2a 2e c2 47 1e 73 | L..F...(...).....E<...+.c.*..G.s |
| 04a0 | 1d d2 e8 63 49 e8 40 e2 e0 8e 1b 12 21 e3 8f 48 8a f4 8c 29 a9 bc a7 61 89 e5 45 42 5c 92 54 76 | ...cI.@.....!..H...)...a..EB\.Tv |
| 04c0 | c4 e4 2a 06 ea a8 dd 94 55 76 89 11 24 aa 54 61 20 89 5b 7a 69 66 45 c4 8c d2 e2 0f 63 ea 28 e5 | ..*.....Uv..$.Ta..[zifE.....c.(. |
| 04e0 | 99 70 46 f4 0c 2a d7 0c 41 66 86 0f be 19 e7 9e 0c 21 33 ca 29 4f 66 f9 5e 11 5c f2 69 68 41 f7 | .pF..*..Af.......!3.)Of.^.\.ihA. |
| 0500 | 54 81 ca 28 82 8e 49 e8 a1 90 12 74 cf 15 0d 36 6a 60 11 ef 44 aa e9 3d a4 ac 72 9d a5 83 b2 a2 | T..(..I....t...6j`..D..=..r..... |
| 0520 | 69 a4 b0 40 c2 0c a8 f0 3d 21 ea a8 87 c2 62 8a 34 fe a8 86 ca 6a ab 91 50 13 6b 7c ab ce 1a 67 | i..@....=!....b.4....j..P.k|...g |
| 0540 | 3e a7 60 78 6b 11 b9 ea 7a 26 3a 55 30 03 09 9b b1 aa 2a 6c 9c e8 8c a2 cd 10 b7 36 77 ca b2 70 | >.`xk...z&:U0.....*l.......6w..p |
| 0560 | ce 12 49 24 d1 4a 4b ed 99 a3 98 62 0a b2 b7 9e a2 cf b6 5e da d7 64 b6 90 a0 43 6e 97 91 9c b2 | ..I$.JK....b.......^..d...Cn.... |
| 0580 | ca 9d 96 a6 bb 2e 95 e1 8c 42 ca a7 d1 ca 3b 2f 92 cf 54 d1 5e b6 44 e8 bb af 8f 69 b6 08 b0 c0 | .........B....;/..T.^.D....i.... |
| 05a0 | 03 cf f8 4c 24 cf 22 01 2e aa 08 27 fc a1 3e a9 44 e2 20 ba ea 4a 9c 62 a2 90 40 e2 c4 c3 a8 16 | ...L$."....'..>.D....J.b..@..... |
| 05c0 | 91 b1 c6 1f de 63 1f a3 00 13 31 32 c9 14 de f3 48 83 20 c7 ba 32 cb 0a 8e 13 49 88 29 ab 4c b3 | .....c....12....H....2....I.).L. |
| 05e0 | 87 b0 fc 49 45 cc a8 7a b2 33 85 3d 63 4b 04 d0 96 6e 37 b4 82 a5 aa f2 1e d2 82 2a bd f4 7f ac | ...IE..z.3.=cK...n7........*.... |
| 0600 | 44 a2 0a b4 29 4b 3d 35 7d 55 4b 43 1e d4 59 6a bd 35 7a e8 5c 9b 0d 7c 60 c3 27 f6 d8 dc a1 f3 | D...)K=5}UKC..Yj.5z.\..|`.'..... |
| 0620 | 44 24 28 67 cd f6 7c 6e fb 76 b4 dc 73 93 5d 85 fe 6f 40 b3 89 f4 da 79 17 17 24 2a ee f9 dd 1c | D$(g..|n.v..s.]..o@....y..$*.... |
| 0640 | b2 0f 1b 4e 04 e0 81 eb 56 36 e1 87 23 7e 37 b8 0f 9f d7 b8 74 8f 17 61 f8 e6 92 df 1d df e5 98 | ...N....V6..#~7.....t..a........ |
| 0660 | 5b ad f9 d1 9b 4f 4e ba e7 f2 81 6e 9c 2b 8f 34 a3 b9 df a5 73 3e a8 ea c6 41 12 89 34 85 c3 6e | [....ON....n.+.4....s>...A..4..n |
| 0680 | fa 0f 8a 1f 9d 3a ed ba fd 96 cd e8 bc 9f 2e 3b ef 6c fe 0e 7c 6c a3 40 72 4d e1 c6 c7 be b9 f2 | .....:.........;.l..|l.@rM...... |
| 06a0 | cb bf f6 9b 36 af ef 2e fd dd d4 57 df 5a f3 dc 10 af fd f8 3f 74 ef fd 6a cd 6b 13 fb e4 db 8b | ....6......W.Z......?t..j.k..... |
| 06c0 | 7c be f5 90 68 c3 3e ec c5 4b 2f e1 fb df 43 32 3c f2 c8 93 ae 7b ff e5 eb 11 fe 54 f3 9b e7 f1 | |...h.>..K/...C2<....{.....T.... |
| 06e0 | ef 7f ff e3 de 89 06 a8 9a e6 19 f0 80 fe 8b 1e f7 90 c1 c0 d5 fc 06 77 07 cc a0 ff 60 77 bf 0a | .......................w....`w.. |
| 0700 | a2 e6 37 cd 38 56 06 47 58 bf f2 51 d0 83 1f 04 d3 eb 48 c8 c2 0e a2 b0 34 bf 21 1c 0b 67 08 89 | ..7.8V.GX..Q......H.....4.!..g.. |
| 0720 | 23 bd 10 86 90 90 e1 0c 49 58 84 5f dc d0 34 fe bf 19 d0 0e 79 b8 c0 1f 8a 26 12 90 18 c5 0a 87 | #.......IX._..4.....y....&...... |
| 0740 | c8 bf 22 08 d0 88 a0 99 45 12 97 c8 44 de b9 10 8a 9f 09 d2 28 b2 50 c5 03 16 e1 84 58 04 8d 16 | ..".....E...D.......(.P.....X... |
| 0760 | a5 21 84 2e 22 ef 8a 61 e4 4c 90 8a e0 34 33 5a b1 88 69 54 63 0c cb e8 46 f3 c5 31 33 e8 18 82 | .!.."..a.L...43Z..iTc...F..13... |
| 0780 | 90 36 58 45 3b de f1 32 41 7a 82 c7 48 e8 83 16 fe f1 33 ac 78 82 80 7e 50 48 e4 35 d2 90 87 ec | .6XE;..2Az..H.....3.x..~PH.5.... |
| 07a0 | 4c 22 4d 31 0a de 3d 92 91 8e b4 e4 19 23 29 c9 27 8c 62 14 8f bc e4 25 9b c8 49 ce 9c a2 08 45 | L"M1..=......#).'.b....%..I....E |
| 07c0 | a8 24 26 33 a9 c9 55 96 af 94 9b 91 05 2a 99 31 84 56 ba f2 96 af 6c ca b8 04 f2 0e 56 b0 62 16 | .$&3..U......*.1.V....l.....V.b. |
| 07e0 | f7 c8 0d 36 5e e1 cb 70 dc 83 4b c7 dc 25 2c 65 72 0f df a0 a2 96 a3 14 a5 23 fd 38 94 7c 3c 23 | ...6^..p..K..%,er........#.8.|<# |
| 0800 | 16 c4 b9 87 2f 3c d1 8b 57 44 e2 15 b3 f8 c4 32 7a c1 8b 6b e9 62 16 f9 28 db 23 5c f1 8c 59 c0 | ..../<..WD.....2z..k.b..(.#\..Y. |
| 0820 | 71 99 2b 69 a6 13 46 51 cb 55 36 52 9a 56 fe 44 0a 3a c2 31 0b 74 b0 e2 12 98 20 ce 2c 78 01 8c | q.+i..FQ.U6R.V.D.:.1.t......,x.. |
| 0840 | 75 2c e3 18 97 68 c5 25 3e c1 8b 64 2c 63 19 ad 50 28 2b 78 d1 8b 83 b6 c2 13 8f 88 44 3f 67 06 | u,...h.%>..d,c..P(+x........D?g. |
| 0860 | cf 92 dc 03 12 44 18 45 15 ee d9 4a 92 5a 92 9a 3b 39 26 36 5a d1 8b 4b e8 e2 12 30 25 4e 24 78 | .....D.E...J.Z..;9&6Z..K...0%N$x |
| 0880 | 71 0c 83 1e 83 a5 37 f5 c4 27 3e d1 0b 87 f6 a2 15 9f b8 e8 31 8e 71 d0 aa f9 32 16 c4 c8 4d 47 | q.....7..'>.........1.q...2...MG |
| 08a0 | 3d ea 88 54 7e cb a4 a1 6c 25 e3 76 62 cd 6b 79 a2 15 44 e5 05 2f 2e 11 09 6c 46 74 1d 36 bd 04 | =..T~...l%.vb.ky..D../...lFt.6.. |
| 08c0 | 2b 88 ba 0c 5e fc 53 17 ad 78 e8 31 78 f1 d2 57 a4 75 19 14 8c c7 32 80 f1 4b 8e 2e d5 23 c4 48 | +...^.S..x.1x..W.u....2..K...#.H |
| 08e0 | a5 2a 49 0a 55 de 09 61 aa 3a 41 07 51 d1 fa 52 5d f0 e2 a1 c9 50 e8 b5 70 fa 09 97 5e c2 13 34 | .*I.U..a.:A.Q..R]....P..p...^..4 |
| 0900 | 7d 68 44 1f 4b d1 65 24 a3 b1 07 55 57 3c e2 61 d3 4f b0 c2 ae 77 d5 c8 33 3c f6 54 4c f6 15 93 | }hD.K.e$...UW<.a.O...w..3<.TL... |
| 0920 | 44 68 c4 50 5e f1 50 b5 f6 c2 13 ba b8 fe ea 50 cb 9a 50 4f 78 22 12 c7 48 c6 4d 81 7a 89 9e 1e | Dh.P^.P........P..POx"..H.M.z... |
| 0940 | e3 a7 41 f5 44 6e d3 7a 0c 74 e8 83 1e 06 3d 28 42 21 c1 8a 77 28 35 b4 1b 41 c6 10 9e d0 9e be | ..A.Dn.z.t....=(B!..w(5..A...... |
| 0960 | 9a f4 07 7f 1d ca 2c 5a 9b dc b2 f2 c2 b6 11 55 eb 77 83 8a d5 71 96 f3 5a 97 60 ed 5a 0b 7b 09 | ......,Z.......U.w...q..Z.`.Z.{. |
| 0980 | 5e fc 22 1f fa e0 ac 41 29 0a d9 56 44 c2 15 a0 85 ee 44 ca 56 84 2a 14 a1 90 00 b6 a7 69 d9 a4 | ^."....A)..VD.....D.V.*......i.. |
| 09a0 | 5a 00 5d 62 19 dd 6d ad 5a 5b f1 d2 56 44 96 17 0c 4e 28 2f 5e b1 02 17 b8 20 05 8e 60 68 45 13 | pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Tango.Scripting.Editors.Document;
using Tango.Scripting.Editors.Editing;
using Tango.Scripting.Editors.Folding;
using Tango.Scripting.Editors.Rendering;
namespace Tango.Scripting.Editors.Search
{
/// <summary>
/// Provides search functionality for AvalonEdit. It is displayed in the top-right corner of the TextArea.
/// </summary>
public class SearchPanel : Control
{
TextArea textArea;
TextDocument currentDocument;
SearchResultBackgroundRenderer renderer;
TextBox searchTextBox;
SearchPanelAdorner adorner;
#region DependencyProperties
/// <summary>
/// Dependency property for <see cref="UseRegex"/>.
/// </summary>
public static readonly DependencyProperty UseRegexProperty =
DependencyProperty.Register("UseRegex", typeof(bool), typeof(SearchPanel),
new FrameworkPropertyMetadata(false, SearchPatternChangedCallback));
/// <summary>
/// Gets/sets whether the search pattern should be interpreted as regular expression.
/// </summary>
public bool UseRegex {
get { return (bool)GetValue(UseRegexProperty); }
set { SetValue(UseRegexProperty, value); }
}
/// <summary>
/// Dependency property for <see cref="MatchCase"/>.
/// </summary>
public static readonly DependencyProperty MatchCaseProperty =
DependencyProperty.Register("MatchCase", typeof(bool), typeof(SearchPanel),
new FrameworkPropertyMetadata(false, SearchPatternChangedCallback));
/// <summary>
/// Gets/sets whether the search pattern should be interpreted case-sensitive.
/// </summary>
public bool MatchCase {
get { return (bool)GetValue(MatchCaseProperty); }
set { SetValue(MatchCaseProperty, value); }
}
/// <summary>
/// Dependency property for <see cref="WholeWords"/>.
/// </summary>
public static readonly DependencyProperty WholeWordsProperty =
DependencyProperty.Register("WholeWords", typeof(bool), typeof(SearchPanel),
new FrameworkPropertyMetadata(false, SearchPatternChangedCallback));
/// <summary>
/// Gets/sets whether the search pattern should only match whole words.
/// </summary>
public bool WholeWords {
get { return (bool)GetValue(WholeWordsProperty); }
set { SetValue(WholeWordsProperty, value); }
}
/// <summary>
/// Dependency property for <see cref="SearchPattern"/>.
/// </summary>
public static readonly DependencyProperty SearchPatternProperty =
DependencyProperty.Register("SearchPattern", typeof(string), typeof(SearchPanel),
new FrameworkPropertyMetadata("", SearchPatternChangedCallback));
/// <summary>
/// Gets/sets the search pattern.
/// </summary>
public string SearchPattern {
get { return (string)GetValue(SearchPatternProperty); }
set { SetValue(SearchPatternProperty, value); }
}
/// <summary>
/// Dependency property for <see cref="MarkerBrush"/>.
/// </summary>
public static readonly DependencyProperty MarkerBrushProperty =
DependencyProperty.Register("MarkerBrush", typeof(Brush), typeof(SearchPanel),
new FrameworkPropertyMetadata(Brushes.LightGreen, MarkerBrushChangedCallback));
/// <summary>
/// Gets/sets the Brush used for marking search results in the TextView.
/// </summary>
public Brush MarkerBrush {
get { return (Brush)GetValue(MarkerBrushProperty); }
set { SetValue(MarkerBrushProperty, value); }
}
/// <summary>
/// Dependency property for <see cref="Localization"/>.
/// </summary>
public static readonly DependencyProperty LocalizationProperty =
DependencyProperty.Register("Localization", typeof(Localization), typeof(SearchPanel),
new FrameworkPropertyMetadata(new Localization()));
/// <summary>
/// Gets/sets the localization for the SearchPanel.
/// </summary>
public Localization Localization {
get { return (Localization)GetValue(LocalizationProperty); }
set { SetValue(LocalizationProperty, value); }
}
#endregion
static void MarkerBrushChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SearchPanel panel = d as SearchPanel;
if (panel != null) {
panel.renderer.MarkerBrush = (Brush)e.NewValue;
}
}
static SearchPanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchPanel), new FrameworkPropertyMetadata(typeof(SearchPanel)));
}
ISearchStrategy strategy;
static void SearchPatternChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SearchPanel panel = d as SearchPanel;
if (panel != null) {
panel.ValidateSearchText();
panel.UpdateSearch();
}
}
void UpdateSearch()
{
// only reset as long as there are results
// if no results are found, the "no matches found" message should not flicker.
// if results are found by the next run, the message will be hidden inside DoSearch ...
if (renderer.CurrentResults.Any())
messageView.IsOpen = false;
strategy = SearchStrategyFactory.Create(SearchPattern ?? "", !MatchCase, WholeWords, UseRegex ? SearchMode.RegEx : SearchMode.Normal);
OnSearchOptionsChanged(new SearchOptionsChangedEventArgs(SearchPattern, MatchCase, UseRegex, WholeWords));
DoSearch(true);
}
/// <summary>
/// Creates a new SearchPanel.
/// </summary>
public SearchPanel()
{
}
/// <summary>
/// Attaches this SearchPanel to a TextArea instance.
/// </summary>
public void Attach(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
adorner = new SearchPanelAdorner(textArea, this);
DataContext = this;
renderer = new SearchResultBackgroundRenderer();
currentDocument = textArea.Document;
if (currentDocument != null)
currentDocument.TextChanged += textArea_Document_TextChanged;
textArea.DocumentChanged += textArea_DocumentChanged;
KeyDown += SearchLayerKeyDown;
this.CommandBindings.Add(new CommandBinding(SearchCommands.FindNext, (sender, e) => FindNext()));
this.CommandBindings.Add(new CommandBinding(SearchCommands.FindPrevious, (sender, e) => FindPrevious()));
this.CommandBindings.Add(new CommandBinding(SearchCommands.CloseSearchPanel, (sender, e) => Close()));
IsClosed = true;
}
void textArea_DocumentChanged(object sender, EventArgs e)
{
if (currentDocument != null)
currentDocument.TextChanged -= textArea_Document_TextChanged;
currentDocument = textArea.Document;
if (currentDocument != null) {
currentDocument.TextChanged += textArea_Document_TextChanged;
DoSearch(false);
}
}
void textArea_Document_TextChanged(object sender, EventArgs e)
{
DoSearch(false);
}
/// <inheritdoc/>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
searchTextBox = Template.FindName("PART_searchTextBox", this) as TextBox;
}
void ValidateSearchText()
{
if (searchTextBox == null)
return;
var be = searchTextBox.GetBindingExpression(TextBox.TextProperty);
try {
Validation.ClearInvalid(be);
UpdateSearch();
} catch (SearchPatternException ex) {
var ve = new ValidationError(be.ParentBinding.ValidationRules[0], be, ex.Message, ex);
Validation.MarkInvalid(be, ve);
}
}
/// <summary>
/// Reactivates the SearchPanel by setting the focus on the search box and selecting all text.
/// </summary>
public void Reactivate()
{
if (searchTextBox == null)
return;
searchTextBox.Focus();
searchTextBox.SelectAll();
}
/// <summary>
/// Moves to the next occurrence in the file.
/// </summary>
public void FindNext()
{
SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset + 1);
if (result == null)
result = renderer.CurrentResults.FirstSegment;
if (result != null) {
SelectResult(result);
}
}
/// <summary>
/// Moves to the previous occurrence in the file.
/// </summary>
public void FindPrevious()
{
SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset);
if (result != null)
result = renderer.CurrentResults.GetPreviousSegment(result);
if (result == null)
result = renderer.CurrentResults.LastSegment;
if (result != null) {
SelectResult(result);
}
}
ToolTip messageView = new ToolTip { Placement = PlacementMode.Bottom, StaysOpen = false };
void DoSearch(bool changeSelection)
{
if (IsClosed)
return;
renderer.CurrentResults.Clear();
if (!string.IsNullOrEmpty(SearchPattern)) {
int offset = textArea.Caret.Offset;
if (changeSelection) {
textArea.ClearSelection();
}
// We cast from ISearchResult to SearchResult; this is safe because we always use the built-in strategy
foreach (SearchResult result in strategy.FindAll(textArea.Document, 0, textArea.Document.TextLength)) {
if (changeSelection && result.StartOffset >= offset) {
SelectResult(result);
changeSelection = false;
}
renderer.CurrentResults.Add(result);
}
if (!renderer.CurrentResults.Any()) {
messageView.IsOpen = true;
messageView.Content = Localization.NoMatchesFoundText;
messageView.PlacementTarget = searchTextBox;
} else
messageView.IsOpen = false;
}
textArea.TextView.InvalidateLayer(KnownLayer.Selection);
}
void SelectResult(SearchResult result)
{
textArea.Caret.Offset = result.StartOffset;
textArea.Selection = Selection.Create(textArea, result.StartOffset, result.EndOffset);
textArea.Caret.BringCaretToView();
// show caret even if the editor does not have the Keyboard Focus
textArea.Caret.Show();
}
void SearchLayerKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key) {
case Key.Enter:
e.Handled = true;
messageView.IsOpen = false;
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
FindPrevious();
else
FindNext();
if (searchTextBox != null) {
var error = Validation.GetErrors(searchTextBox).FirstOrDefault();
if (error != null) {
messageView.Content = Localization.ErrorText + " " + error.ErrorContent;
messageView.PlacementTarget = searchTextBox;
messageView.IsOpen = true;
}
}
break;
case Key.Escape:
e.Handled = true;
Close();
break;
}
}
/// <summary>
/// Gets whether the Panel is already closed.
/// </summary>
public bool IsClosed { get; private set; }
/// <summary>
/// Closes the SearchPanel.
/// </summary>
public void Close()
{
bool hasFocus = this.IsKeyboardFocusWithin;
var layer = AdornerLayer.GetAdornerLayer(textArea);
if (layer != null)
layer.Remove(adorner);
messageView.IsOpen = false;
textArea.TextView.BackgroundRenderers.Remove(renderer);
if (hasFocus)
textArea.Focus();
IsClosed = true;
// Clear existing search results so that the segments don't have to be maintained
renderer.CurrentResults.Clear();
}
/// <summary>
/// Closes the SearchPanel and removes it.
/// </summary>
public void CloseAndRemove()
{
Close();
textArea.DocumentChanged -= textArea_DocumentChanged;
if (currentDocument != null)
currentDocument.TextChanged -= textArea_Document_TextChanged;
}
/// <summary>
/// Opens the an existing search panel.
/// </summary>
public void Open()
{
if (!IsClosed) return;
var layer = AdornerLayer.GetAdornerLayer(textArea);
if (layer != null)
layer.Add(adorner);
textArea.TextView.BackgroundRenderers.Add(renderer);
IsClosed = false;
DoSearch(false);
}
/// <summary>
/// Fired when SearchOptions are changed inside the SearchPanel.
/// </summary>
public event EventHandler<SearchOptionsChangedEventArgs> SearchOptionsChanged;
/// <summary>
/// Raises the <see cref="SearchPanel.SearchOptionsChanged" /> event.
/// </summary>
protected virtual void OnSearchOptionsChanged(SearchOptionsChangedEventArgs e)
{
if (SearchOptionsChanged != null) {
SearchOptionsChanged(this, e);
}
}
}
/// <summary>
/// EventArgs for <see cref="SearchPanel.SearchOptionsChanged"/> event.
/// </summary>
public class SearchOptionsChangedEventArgs : EventArgs
{
/// <summary>
/// Gets the search pattern.
/// </summary>
public string SearchPattern { get; private set; }
/// <summary>
/// Gets whether the search pattern should be interpreted case-sensitive.
/// </summary>
public bool MatchCase { get; private set; }
/// <summary>
/// Gets whether the search pattern should be interpreted as regular expression.
/// </summary>
public bool UseRegex { get; private set; }
/// <summary>
/// Gets whether the search pattern should only match whole words.
/// </summary>
public bool WholeWords { get; private set; }
/// <summary>
/// Creates a new SearchOptionsChangedEventArgs instance.
/// </summary>
public SearchOptionsChangedEventArgs(string searchPattern, bool matchCase, bool useRegex, bool wholeWords)
{
this.SearchPattern = searchPattern;
this.MatchCase = matchCase;
this.UseRegex = useRegex;
this.WholeWords = wholeWords;
}
}
class SearchPanelAdorner : Adorner
{
SearchPanel panel;
public SearchPanelAdorner(TextArea textArea, SearchPanel panel)
: base(textArea)
{
this.panel = panel;
AddVisualChild(panel);
}
protected override int VisualChildrenCount {
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
if (index != 0)
throw new ArgumentOutOfRangeException();
return panel;
}
protected override Size ArrangeOverride(Size finalSize)
{
panel.Arrange(new Rect(new Point(0, 0), finalSize));
return new Size(panel.ActualWidth, panel.ActualHeight);
}
}
}
|