120 |
121 | ## 匹配器
122 |
123 | 匹配器用来实现断言功能。在前面的例子中,我们只使用了 `toBe()` 匹配器:
124 |
125 | ```
126 | test('Adding 1 + 1 equals 2', () => {
127 | expect(sum(1, 1)).toBe(2)
128 | })
129 | 复制代码
130 | ```
131 |
132 | 在此代码中,`expect(sum(1, 1))` 返回一个“期望”对象,`.toBe(2)` 是匹配器。匹配器将 `expect()` 的结果(**实际值**)与自己的参数(**期望值**)进行比较。当 Jest 运行时,它会跟踪所有失败的匹配器,并打印出错误信息。
133 |
134 | 常用的匹配器如下:
135 |
136 | - `toBe` 使用 [Object.is](https://link.juejin.im/?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FJavaScript%2FReference%2FGlobal_Objects%2FObject%2Fis) 判断是否严格相等。
137 | - `toEqual` 递归检查对象或数组的每个字段。
138 | - `toBeNull` 只匹配 `null`。
139 | - `toBeUndefined` 只匹配 `undefined`。
140 | - `toBeDefined` 只匹配非 `undefined`。
141 | - `toBeTruthy` 只匹配真。
142 | - `toBeFalsy` 只匹配假。
143 | - `toBeGreaterThan` 实际值大于期望。
144 | - `toBeGreaterThanOrEqual` 实际值大于或等于期望值
145 | - `toBeLessThan` 实际值小于期望值。
146 | - `toBeLessThanOrEqual` 实际值小于或等于期望值。
147 | - `toBeCloseTo` 比较浮点数的值,避免误差。
148 | - `toMatch` 正则匹配。
149 | - `toContain` 判断数组中是否包含指定项。
150 | - `.toHaveProperty(keyPath, value)` 判断对象中是否包含指定属性。
151 | - `toThrow` 判断是否抛出指定的异常。
152 | - `toBeInstanceOf` 判断对象是否是某个类的实例,底层使用 `instanceof`。
153 |
154 | ## 异步测试
155 | 当执行到测试代码的尾部时,Jest 即认为测试完成。因此,如果存在异步代码,Jest 不会等待回调函数执行。要解决这个问题,在测试函数中我们接受一个参数叫做 done,Jest 将会一直等待,直到我们调用 done()。如果一直不调用 done(),则此测试不通过。
156 | ```javaScript
157 | // async/fetch.js
158 | export const fetchApple = (callback) => {
159 | setTimeout(() => callback('apple'), 300)
160 | }
161 |
162 | // async/fetch.test.js
163 | import { fetchApple } from './fetch'
164 |
165 | test('the data is apple', (done) => {
166 | expect.assertions(1)
167 | const callback = data => {
168 | expect(data).toBe('apple')
169 | done()
170 | }
171 |
172 | fetchApple(callback)
173 | })
174 | ```
175 | 复制代码expect.assertions(1) 验证当前测试中有 1 处断言会被执行,在测试异步代码时,能确保回调中的断言被执行。
176 |
--------------------------------------------------------------------------------
/小程序/index.js:
--------------------------------------------------------------------------------
1 | import { Toast } from 'live/common/toast';
2 |
3 | Component({
4 | data: {
5 |
6 | },
7 |
8 | options: {
9 | multipleSlots: true, // 在组件定义时的选项中启用多slot支持
10 | },
11 |
12 | properties: {
13 | show: Boolean,
14 | shareIcon: {
15 | type: Boolean,
16 | value: true,
17 | },
18 | posterIcon: {
19 | type: Boolean,
20 | value: true,
21 | },
22 | },
23 |
24 | methods: {
25 | onHide() {
26 | if (this.data.posterLoading) Toast.clear();
27 | this.triggerEvent('close');
28 | },
29 | resetPoster() {
30 | this.setData({ posterUrl: '' });
31 | },
32 | noop() {},
33 | },
34 |
35 | observers: {
36 | show(showable) {
37 |
38 | },
39 | },
40 | });
41 |
--------------------------------------------------------------------------------
/小程序/login.js:
--------------------------------------------------------------------------------
1 | let isSilentLoginIng = false;
2 | let subscribers = [];
3 | let code = ""; // 模拟 code, 真实场景这个存放在本地内存中
4 | let MAX_TIME = 3;
5 | let currentLoginTime = 1;
6 |
7 | function onAccessTokenFetched() {
8 | subscribers.forEach((request) => {
9 | request();
10 | });
11 | subscribers = [];
12 | }
13 |
14 | function addSubscriber(callback) {
15 | subscribers.push(callback);
16 | }
17 |
18 | // 静默登录 用户无感知,获取 token
19 | const silentLogin = (option = {}) => {
20 | const { firstResolve } = option;
21 | return new Promise((resolve, reject) => {
22 | setTimeout(() => {
23 | const isBig = Math.random() > 0.5;
24 | console.log(isBig, "isBig>>>>>>");
25 | if (isBig) {
26 | code = "xxx1";
27 | console.log("code 获取成功>>>>>>");
28 | if (firstResolve) {
29 | return firstResolve();
30 | }
31 | resolve();
32 | } else {
33 | if (currentLoginTime++ < MAX_TIME) {
34 | console.log(`code 获取失败, 准备第${currentLoginTime}次调用`);
35 | silentLogin({ firstResolve: firstResolve || resolve });
36 | } else {
37 | console.log("code 最终还是失败");
38 | reject();
39 | }
40 | }
41 | }, 1000);
42 | });
43 | };
44 |
45 | const request = (params = {}) => {
46 | const { callback } = params;
47 | return new Promise((resolve, reject) => {
48 | if (isSilentLoginIng) {
49 | return addSubscriber(() => {
50 | request({
51 | ...params,
52 | temp: "这是队列里面的接口,我只调用过一次",
53 | callback: resolve,
54 | });
55 | });
56 | }
57 |
58 | // 模拟 request
59 | setTimeout(() => {
60 | console.log(params.name, "执行第一次");
61 | if (!code) {
62 | addSubscriber(() => {
63 | request({
64 | ...params,
65 | temp: `${params.name}第二次执行`,
66 | callback: resolve,
67 | });
68 | });
69 |
70 | if (!isSilentLoginIng) {
71 | isSilentLoginIng = true;
72 | silentLogin()
73 | .then(() => {
74 | isSilentLoginIng = false;
75 | console.log("code 成功获取了,开始执行队列请求>>>>>>");
76 | // 依次去执行缓存的接口
77 | onAccessTokenFetched();
78 | })
79 | .catch(() => {
80 | console.log(11212, "11212>>>>>>");
81 | });
82 | }
83 | return;
84 | } else {
85 | // 有 callback 说明是队列里面的请求, 直接 callback 返回
86 | if (callback) {
87 | return callback({ ...params });
88 | }
89 | resolve(params);
90 | }
91 | }, 100);
92 | });
93 | };
94 |
95 | // 1 2 模拟小程序上来就调用的接口
96 | request({ name: "接口1" }).then((res) => {
97 | console.log(res, ">>>>>>");
98 | });
99 |
100 | request({ name: "接口2" }).then((res) => {
101 | console.log(res, ">>>>>>");
102 | });
103 |
104 | // 模拟用户 操作调用的接口,但是这个时候 code 身份验证 接口还没有响应完成
105 | setTimeout(() => {
106 | request({ name: "接口3" }).then((res) => {
107 | console.log(res, ">>>>>>");
108 | });
109 | }, 400);
110 |
111 | // 模拟 code 身份校验完成后调用的接口
112 | setTimeout(() => {
113 | request({ name: "接口4" }).then((res) => {
114 | console.log(res, ">>>>>>");
115 | });
116 | }, 2000);
117 |
--------------------------------------------------------------------------------
/小程序/uniapp.json:
--------------------------------------------------------------------------------
1 | uniapp XBuilder 的数据配置
2 | {
3 | "editor.codeassist.px2upx.proportion": "0.5",
4 | "editor.colorScheme": "Monokai",
5 | "editor.tabSize": 2,
6 | "editor.tokenColorCustomizations": {
7 | "[Monokai]": {
8 | "rules": [
9 | {
10 | "scope": ["variable"],
11 | "settings": {
12 | "foreground": "#9cdcfe"
13 | }
14 | },
15 | {
16 | "scope": ["storage.type"],
17 | "settings": {
18 | "fontStyle": "italic ",
19 | "foreground": "#569cd6"
20 | }
21 | },
22 | {
23 | "scope": ["keyword"],
24 | "settings": {
25 | "foreground": "#c586c0"
26 | }
27 | },
28 | {
29 | "scope": ["entity.name.function"],
30 | "settings": {
31 | "foreground": "#dcdcaa"
32 | }
33 | },
34 | {
35 | "scope": ["variable.parameter"],
36 | "settings": {
37 | "fontStyle": "italic ",
38 | "foreground": "#9cdcfe"
39 | }
40 | },
41 | {
42 | "scope": ["string"],
43 | "settings": {
44 | "foreground": "#ce9178"
45 | }
46 | },
47 | {
48 | "scope": ["constant.numeric"],
49 | "settings": {
50 | "foreground": "#b5cea8"
51 | }
52 | },
53 | {
54 | "scope": ["comment"],
55 | "settings": {
56 | "foreground": "#6a9955"
57 | }
58 | },
59 | {
60 | "scope": [
61 | "punctuation.definition.template-expression",
62 | "punctuation.section.embedded"
63 | ],
64 | "settings": {
65 | "foreground": "#569cd6"
66 | }
67 | },
68 | {
69 | "scope": ["entity.other.attribute-name"],
70 | "settings": {
71 | "foreground": "#d7ba7d"
72 | }
73 | },
74 | {
75 | "scope": ["entity.name.tag"],
76 | "settings": {
77 | "foreground": "#569cde"
78 | }
79 | }
80 | ]
81 | }
82 | },
83 | "explorer.iconTheme": "vs-seti",
84 | "weApp.devTools.path": "D:/微信web开发者工具",
85 | "workbench.colorCustomizations": {
86 | "[Default]": {}
87 | },
88 | "editor.contentAssistSelectionMode": "Alt+数字模式"
89 | }
90 |
--------------------------------------------------------------------------------
/算法/DFC-BFC.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
98 |
99 |
--------------------------------------------------------------------------------
/算法/heapSort.md:
--------------------------------------------------------------------------------
1 | **堆排序**
2 |
3 | 堆排序是利用**堆**这种数据结构而设计的一种排序算法,堆排序是一种**选择排序,**它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
4 |
5 | **堆**
6 |
7 | **堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:**
8 |
9 | 
10 |
11 | 同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
12 |
13 | 
14 |
15 | 该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:
16 |
17 | **大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]**
18 |
19 | **小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]**
20 |
21 | ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤:
22 |
23 | # 堆排序基本思想及步骤
24 |
25 | > **堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了**
26 |
27 | **步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。**
28 |
29 | a.假设给定无序序列结构如下
30 |
31 | 
32 |
33 | 2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。
34 |
35 | 
36 |
37 | 4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。
38 |
39 | 
40 |
41 | 这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
42 |
43 | 
44 |
45 | 此时,我们就将一个无需序列构造成了一个大顶堆。
46 |
47 | **步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。**
48 |
49 | a.将堆顶元素9和末尾元素4进行交换
50 |
51 | 
52 |
53 | b.重新调整结构,使其继续满足堆定义
54 |
55 | 
56 |
57 | c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.
58 |
59 | 
60 |
61 | 后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
62 |
63 | 
64 |
65 | 再简单总结下堆排序的基本思路:
66 |
67 | **a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;**
68 |
69 | **b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;**
70 |
71 | **c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。**
72 |
73 | # 代码实现
74 |
75 | ```javascript
76 | var len; // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量
77 |
78 | function buildMaxHeap(arr) { // 建立大顶堆
79 | len = arr.length;
80 | for (var i = Math.floor(len/2); i >= 0; i--) {
81 | heapify(arr, i);
82 | }
83 | }
84 |
85 | function heapify(arr, i) { // 堆调整
86 | var left = 2 * i + 1,
87 | right = 2 * i + 2,
88 | largest = i;
89 |
90 | if (left < len && arr[left] > arr[largest]) {
91 | largest = left;
92 | }
93 |
94 | if (right < len && arr[right] > arr[largest]) {
95 | largest = right;
96 | }
97 |
98 | if (largest != i) {
99 | swap(arr, i, largest);
100 | heapify(arr, largest);
101 | }
102 | }
103 |
104 | function swap(arr, i, j) {
105 | var temp = arr[i];
106 | arr[i] = arr[j];
107 | arr[j] = temp;
108 | }
109 |
110 | function heapSort(arr) {
111 | buildMaxHeap(arr);
112 |
113 | for (var i = arr.length-1; i > 0; i--) {
114 | swap(arr, 0, i);
115 | len--;
116 | heapify(arr, 0);
117 | }
118 | return arr;
119 | }
120 | ```
121 | [图解排序算法](https://www.cnblogs.com/chengxiao/p/6129630.html)
122 | [github 各类语言算法](https://github.com/hustcc/JS-Sorting-Algorithm/blob/master/7.heapSort.md)
--------------------------------------------------------------------------------
/算法/js实现超长文字省略.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
20 |
21 |
22 |
23 | 零一二三四五六七八九以而山斯吴留起吧就
24 | 零一二三四五六七八九以而山斯吴留起吧就
25 | 零一二三四五六七八九以而山斯吴留起吧就
26 | 零一二三四五六七八九以而山斯吴留起吧就
27 | 零一二三四五六七八九以而山斯吴留起吧就
28 |
29 |
30 |
31 |
32 |
96 |
97 |
--------------------------------------------------------------------------------
/算法/函数柯里化.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 | 请实现一个 add 函数,满足以下功能
13 | add(1); // 1
14 | add(1)(2); // 3
15 | add(1)(2)(3); // 6
16 | add(1)(2, 3); // 6
17 | add(1, 2)(3); // 6
18 | add(1, 2, 3); // 6
19 |
20 |
37 |
38 |
--------------------------------------------------------------------------------
/算法/堆排序.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
57 |
58 |
--------------------------------------------------------------------------------
/算法/快排.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
32 |
33 |
--------------------------------------------------------------------------------
/算法/语法高亮.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | auto complete
8 |
17 |
18 |
19 |
20 |
23 |
24 |
74 |
--------------------------------------------------------------------------------
/算法/黄金问题.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 | 题目
13 | 有一个国家发现了5座金矿,每座金矿的黄金储量不同,
14 | 需要参与挖掘的工人数也不同。参与挖矿工人的总数是10人。
15 | 每座金矿要么全挖,要么不挖(不能派出一半人挖取一半金矿)。
16 |
要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?
17 | 金矿1 400金 / 5人
18 | 金矿2 500金 / 5人
19 | 金矿3 200金 / 3人
20 | 金矿4 300金 / 4人
21 | 金矿5 350金 / 3人
22 |
23 | 解
24 | 我们设:金矿数 n,工人数 w, 黄金量为数组 g[], 金矿用工量为数组 P[],
25 |
26 |
53 |
54 |
--------------------------------------------------------------------------------
/输入URL后发生了什么.md:
--------------------------------------------------------------------------------
1 | 第一步:浏览器输入域名
2 |
3 | 例如输入:www.baidu.com
4 |
5 | 第二步:浏览器查找域名的IP地址
6 |
7 | 浏览器会把输入的域名解析成对应的IP,其过程如下:
8 |
9 | 1.查找浏览器缓存:因为浏览器一般会缓存DNS记录一段时间,不同浏览器的时间可能不一样,一般2-30分钟不等,浏览器去查找这些缓存,如果有缓存,直接返回IP,否则下一步。
10 |
11 | 2.查找系统缓存:浏览器缓存中找不到IP之后,浏览器会进行系统调用(windows中是gethostbyname),查找本机的hosts文件,如果找到,直接返回IP,否则下一步。
12 |
13 | 3.查找路由器缓存:如果1,2步都查询无果,则需要借助网络,路由器一般都有自己的DNS缓存,将前面的请求发给路由器,查找ISP 服务商缓存 DNS的服务器,如果查找到IP则直接返回,没有的话继续查找。
14 |
15 | 4.递归查询:如果以上步骤还找不到,则ISP的DNS服务器就会进行递归查询,所谓递归查询就是如果主机所询问的本地域名服务器不知道被查询域名的IP地址,那么本地域名服务器就以DNS客户的身份,向其他根域名服务器继续发出查询请求报文,而不是让该主机自己进行下一步查询。(本地域名服务器地址是通过DHPC协议获取地址,DHPC是负责分配IP地址的)
16 |
17 | 5.迭代查询:本地域名服务器采用迭代查询,它先向一个根域名服务器查询。本地域名服务器向根域名服务器的查询一般都是采用迭代查询。所谓迭代查询就是当根域名服务器收到本地域名服务器发出的查询请求报文后,要么告诉本地域名服务器下一步应该查询哪一个域名服务器,然后本地域名服务器自己进行后续的查询。(而不是替代本地域名服务器进行后续查询)。
18 |
19 | 本例子中:根域名服务器告诉本地域名服务器,下一次应查询的顶级域名服务器dns.net的IP地址。本地域名服务器向顶级域名服务器dns.net进行查询。顶级域名服务器dns.net告诉本地域名服务器,下一次应查询的权限域名服务器dns.csdn.net的IP地址。本地域名服务器向权限域名服务器dns.csdn.net进行查询。权限域名服务器dns.csdn.net告诉本地域名服务器,所查询的主机www.csdn.net的IP地址。本地域名服务器最后把结果告诉主机。
20 |
21 | 第三步 :浏览器与目标服务器建立TCP连接
22 |
23 | 1. 主机浏览器通过DNS解析得到了目标服务器的IP地址后,与服务器建立TCP连接。
24 |
25 | 2. TCP3次握手连接:浏览器所在的客户机向服务器发出连接请求报文(SYN标志为1);服务器接收报文后,同意建立连接,向客户机发出确认报文(SYN,ACK标志位均为1);客户机接收到确认报文后,再次向服务器发出报文,确认已接收到确认报文;此处客户机与服务器之间的TCP连接建立完成,开始通信。
26 |
27 | 第四步:浏览器通过http协议发送请求
28 |
29 | 浏览器向主机发起一个HTTP-GET方法报文请求。请求中包含访问的URL,也就是http://www.csdn.com/ ,KeepAlive,长连接,还有User-Agent用户浏览器操作系统信息,编码等。值得一提的是Accep-Encoding和Cookies项。Accept-Encoding一般采用gzip,压缩之后传输html文件。Cookies如果是首次访问,会提示服务器建立用户缓存信息,如果不是,可以利用Cookies对应键值,找到相应缓存,缓存里面存放着用户名,密码和一些用户设置项。
30 |
31 | 第五步:某些服务会做永久重定向响应
32 |
33 | 对于大型网站存在多个主机站点,了负载均衡或者导入流量,提高SEO排名,往往不会直接返回请求页面,而是重定向。返回的状态码就不是200OK,而是301,302以3开头的重定向码,浏览器在获取了重定向响应后,在响应报文中Location项找到重定向地址,浏览器重新第一步访问即可。
34 |
35 | 重定向的作用:重定向是为了负载均衡或者导入流量,提高SEO排名。利用一个前端服务器接受请求,然后负载到不同的主机上,可以大大提高站点的业务并发处理能力;重定向也可将多个域名的访问,集中到一个站点;由于baidu.com,www.baidu.com会被搜索引擎认为是两个网站,照成每个的链接数都会减少从而降低排名,永久重定向会将两个地址关联起来,搜索引擎会认为是同一个网站,从而提高排名。
36 |
37 | 第六步:浏览器跟踪重定向地址
38 |
39 | 当浏览器知道了重定向后最终的访问地址之后,重新发送一个http请求,发送内容同上。
40 |
41 | 第七步:服务器处理请求
42 |
43 | 服务器接收到获取请求,然后处理并返回一个响应。
44 |
45 | 第八步:服务器发出一个HTML响应
46 |
47 | 返回状态码200 OK,表示服务器可以响应请求,返回报文,由于在报头中Content-type为“text/html”,浏览器以HTML形式呈现,而不是下载文件。
48 |
49 | 第九步:释放TCP连接
50 |
51 | 1. 浏览器所在主机向服务器发出连接释放报文,然后停止发送数据;
52 |
53 | 2. 服务器接收到释放报文后发出确认报文,然后将服务器上未传送完的数据发送完;
54 |
55 | 3. 服务器数据传输完毕后,向客户机发送连接释放报文;
56 |
57 | 4. 客户机接收到报文后,发出确认,然后等待一段时间后,释放TCP连接;
58 |
59 | 第十步:浏览器显示页面
60 |
61 | 在浏览器没有完整接受全部HTML文档时,它就已经开始显示这个页面了,浏览器接收到返回的数据包,根据浏览器的渲染机制对相应的数据进行渲染。渲染后的数据,进行相应的页面呈现和脚步的交互。
62 |
63 | 第十一步:浏览器发送获取嵌入在HTML中的其他内容
64 |
65 | 比如一些样式文件,图片url,js文件url等,浏览器会通过这些url重新发送请求,请求过程依然是HTML读取类似的过程,查询域名,发送请求,重定向等。不过这些静态文件是可以缓存到浏览器中的,有时访问这些文件不需要通过服务器,直接从缓存中取。某些网站也会使用第三方CDN进行托管这些静态文件。
66 | 原文链接:https://blog.csdn.net/jiao_0509/article/details/82491299
--------------------------------------------------------------------------------
/链表.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
77 |
78 |
--------------------------------------------------------------------------------