13 |
14 |
#1
15 |
#2
16 |
#3
17 |
#4
18 |
#5
19 |
#6
20 |
#7
21 |
#8
22 |
#9
23 |
#10
24 |
#11
25 |
#12
26 |
#13
27 |
#14
28 |
#15
29 |
#16
30 |
#17
31 |
#18
32 |
#19
33 |
#20
34 |
#21
35 |
#22
36 |
#23
37 |
#24
38 |
#25
39 |
#26
40 |
#27
41 |
#28
42 |
#29
43 |
#30
44 |
#31
45 |
#32
46 |
#33
47 |
#34
48 |
#35
49 |
#36
50 |
#37
51 |
#38
52 |
#39
53 |
#40
54 |
#41
55 |
#42
56 |
#43
57 |
#44
58 |
#45
59 |
#46
60 |
#47
61 |
#48
62 |
#49
63 |
#50
64 |
#51
65 |
#52
66 |
#53
67 |
#54
68 |
#55
69 |
#56
70 |
#57
71 |
#58
72 |
#59
73 |
#60
74 |
#61
75 |
#62
76 |
#63
77 |
#64
78 |
#65
79 |
#66
80 |
#67
81 |
#68
82 |
#69
83 |
#70
84 |
#71
85 |
#72
86 |
#73
87 |
#74
88 |
#75
89 |
#76
90 |
#77
91 |
#78
92 |
#79
93 |
#80
94 |
#81
95 |
#82
96 |
#83
97 |
#84
98 |
#85
99 |
#86
100 |
#87
101 |
#88
102 |
#89
103 |
#90
104 |
#91
105 |
#92
106 |
#93
107 |
#94
108 |
#95
109 |
#96
110 |
#97
111 |
#98
112 |
#99
113 |
#100
114 |
115 |
--------------------------------------------------------------------------------
/config/changelog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Generate changelog file from commit log
3 | *
4 | * - USAGE:
5 | * // will get logs from: a month ago from today ~ today
6 | * npm run changelog
7 | *
8 | * // will get logs from: 2017-06-08 ~ 2017-07-09
9 | * npm run changelog -- 2017-06-08:2017-07-09
10 | */
11 | const pkg = require("../package.json");
12 | const exec = require("child_process").exec;
13 | const fs = require("fs");
14 | const xml2js = require("xml2js");
15 |
16 | // retrieve parameter
17 | const period = process.argv[2] ? process.argv[2].split(":") : [,];
18 |
19 | const changelog = {
20 | // Commit log tag filtering types for changelog
21 | filterType: {
22 | feat: "Features",
23 | fix: "Bug Fixes",
24 | docs: "Documents",
25 | style: "Code Styles",
26 | refactor: "Refactorings",
27 | test: "Test Codes",
28 | chore: "Chore tasks"
29 | },
30 |
31 | // git info holder
32 | gitinfo: {},
33 |
34 | // convert to 2 digit
35 | get2digit(val) {
36 | return String(val).length === 1 ? `0${val}` : val;
37 | },
38 |
39 | // get 'YYYY-MM-DD' formatted value
40 | getFormattedDate(dateValue) {
41 | const delimiter = "-";
42 | const date = (dateValue && new Date(dateValue)) || new Date();
43 |
44 | return [
45 | date.getFullYear(),
46 | this.get2digit(date.getMonth() + 1),
47 | this.get2digit(date.getDate())
48 | ].join(delimiter);
49 | },
50 |
51 | // get git log command string
52 | getLogCmd(afterValue, beforeValue) {
53 | const date = new Date();
54 |
55 | const after = afterValue; // default: latest tag is created.
56 | const before = beforeValue || this.getFormattedDate(); // default: today
57 |
58 | console.log("---------------------------------------------------------------");
59 | console.log(" [CHANGELOG] Creating for period of =>", after, "~", before);
60 | console.log("---------------------------------------------------------------");
61 |
62 | return [
63 | "git log",
64 | [,].concat(Object.keys(this.filterType)).join(" --grep="),
65 | "-i",
66 | `--after={${after}}`,
67 | `--before={${before}`,
68 | "--pretty=\"
- %h
\"",
69 | "--no-merges"
70 | ].join(" ");
71 | },
72 |
73 | // get git info cmd string
74 | getGitInfo() {
75 | return "git rev-parse --abbrev-ref HEAD && git log -1 --pretty=\"format:%h %ci\" --no-merges";
76 | },
77 |
78 | getLatestTagDate() {
79 | return "git log --tags --simplify-by-decoration --pretty='format:%ad' -1 --date=short";
80 | },
81 |
82 | // generate changelog from the git log result
83 | generate(error, stdout, stderr) {
84 | const parser = new xml2js.Parser();
85 |
86 | // define log data structure
87 | const logdata = {};
88 |
89 | Object.keys(this.filterType).forEach(v => {
90 | logdata[v] = {};
91 | });
92 |
93 | // check for duplication
94 | const isDuplicated = (data, value) => {
95 | const val = value.toLowerCase();
96 |
97 | for (let i = 0, el; (el = data[i]); i++) {
98 | if (el.subject.toLowerCase() === val) {
99 | return true;
100 | }
101 | }
102 |
103 | return false;
104 | };
105 |
106 | // capitalize
107 | const capitalize = val => val.charAt(0).toUpperCase() + val.substr(1);
108 |
109 | // get sorted module name
110 | const getModuleName = function(value) {
111 | let val = value;
112 |
113 | if (val.indexOf(",") === -1) {
114 | return capitalize(val);
115 | }
116 |
117 | val = val.trim()
118 | .replace(/\s*,\s*/g, ",")
119 | .split(",");
120 |
121 | val.forEach((v, i) => {
122 | val[i] = capitalize(v);
123 | });
124 |
125 | return val.sort().join(", ");
126 | };
127 |
128 | parser.parseString(`
${stdout}`, (function(err, result) {
129 | if (!result || !result.logs) {
130 | return;
131 | }
132 |
133 | const rxNewline = /\r?\n/g;
134 | const rxBody = /(?:ref|fix|close)\s([g#]|gh)-?([0-9]+)/i;
135 | const rxSubject = new RegExp(`^(${Object.keys(this.filterType).join("|")})\\s?\\(([\\w-_,\\.\\s]+)\\)\\s*:\\s*(.*)`, "i");
136 | let issue;
137 | let subject;
138 | let category;
139 | let module;
140 |
141 | for (let i = 0, el; (el = result.logs.item[i]); i++) {
142 | // filter logs which has issue reference on commit body message.
143 | issue = el.body[0].replace(rxNewline, "").match(rxBody);
144 |
145 | if (issue) {
146 | subject = el.subject[0].match(rxSubject);
147 |
148 | // filter subject which matches with fix or feat format
149 | if (subject) {
150 | category = logdata[subject[1]];
151 | module = getModuleName(subject[2]);
152 |
153 | if (!category[module]) {
154 | category[module] = [];
155 | }
156 |
157 | // filter duplicated subject
158 | if (!isDuplicated(category[module], subject[3])) {
159 | category[module].push({
160 | subject: capitalize(subject[3]),
161 | issueType: issue[1],
162 | issueNo: issue[2],
163 | hash: el.hash[0]
164 | });
165 | }
166 | }
167 | }
168 | }
169 | }).bind(this));
170 |
171 | // template for content of CHANGELOG.md
172 | const template = {
173 | header: "# {=VERSION} release ({=DATE})\r\n",
174 | category: "\r\n## {=CATEGORY} :\r\n",
175 | module: "\r\n- **{=MODULE}**\r\n",
176 | item: "\t- {=SUBJECT} ([#{=ISSUE-NO}]({=URL}/{=ISSUE-NO}))\r\n"
177 | };
178 |
179 | let markdown = template.header
180 | .replace(/{=VERSION}/g, pkg.version)
181 | .replace(/{=DATE}/g, this.gitinfo.lastCommitTime);
182 |
183 | for (const x in logdata) {
184 | if (Object.keys(logdata[x]).length === 0) {
185 | continue;
186 | }
187 |
188 | markdown += template.category
189 | .replace(/{=CATEGORY}/g, this.filterType[x] || "");
190 |
191 | for (const z in logdata[x]) {
192 | markdown += template.module
193 | .replace(/{=MODULE}/g, z);
194 |
195 | for (let i = 0, el; (el = logdata[x][z][i]); i++) {
196 | markdown += template.item
197 | .replace(/{=SUBJECT}/g, el.subject)
198 | .replace(/{=ISSUE-NO}/g, el.issueNo)
199 | .replace(/{=URL}/g, pkg.homepage + "/issues")
200 | .replace(/{=SUBJECT}/g, el.subject);
201 | }
202 | }
203 | }
204 |
205 | fs.writeFile("CHANGELOG.md", markdown, err => {
206 | err ?
207 | console.log(err) :
208 | console.log("Done, check out 'CHANGELOG.md' file.");
209 | });
210 | },
211 |
212 | // initialization
213 | init() {
214 | exec(this.getLatestTagDate(), (function(error, stdout, stderr) {
215 | const cmd = this.getLogCmd(period[0] || stdout, period[1]);
216 |
217 | exec(this.getGitInfo(), (function(error, stdout, stderr) {
218 | const info = stdout.replace(/\r?\n/," ").split(" ");
219 |
220 | this.gitinfo = {
221 | branchName: info[0],
222 | shortSHA: info[1],
223 | lastCommitTime: info[2]
224 | };
225 |
226 | exec(cmd, this.generate.bind(this));
227 | }).bind(this));
228 | }).bind(this));
229 | }
230 | };
231 |
232 | changelog.init();
233 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # egjs-visible [](https://badge.fury.io/js/%40egjs%2Fvisible) [](https://travis-ci.org/naver/egjs-visible) [](https://coveralls.io/github/naver/egjs-visible?branch=master)
2 |
3 | A class that checks if an element is visible in the base element or viewport.
4 |
5 | ## Documents
6 | - [Get Started and Demos](https://naver.github.io/egjs-visible/)
7 | - [API documentation](https://naver.github.io/egjs-visible/release/latest/doc/)
8 |
9 | ## Download and Installation
10 |
11 | Download dist files from repo directly or install it via npm.
12 |
13 | ### For development (Uncompressed)
14 |
15 | You can download the uncompressed files for development
16 |
17 | - Latest : https://naver.github.io/egjs-visible/release/latest/dist/visible.js
18 | - Specific version : https://naver.github.io/egjs-visible/release/[VERSION]/dist/visible.js
19 |
20 | ### For production (Compressed)
21 |
22 | You can download the compressed files for production
23 |
24 | - Latest : https://naver.github.io/egjs-visible/release/latest/dist/visible.min.js
25 | - Specific version : https://naver.github.io/egjs-visible/release/[VERSION]/dist/visible.min.js
26 |
27 | ### Packaged version (with Dependencies)
28 | > Packaged version is not an official distribution.
29 | > Is just to provide for ease use of 'egjs-visible' with dependency.
30 |
31 | - **Latest**
32 | - https://naver.github.io/egjs-visible/release/latest/dist/visible.pkgd.js
33 | - https://naver.github.io/egjs-visible/release/latest/dist/visible.pkgd.min.js
34 |
35 | - **Specific version**
36 | - https://naver.github.io/egjs-visible/release/[VERSION]/dist/visible.pkgd.js
37 | - https://naver.github.io/egjs-visible/release/[VERSION]/dist/visible.pkgd.min.js
38 |
39 | ### Installation with npm
40 |
41 | The following command shows how to install egjs-visible using npm.
42 |
43 | ```bash
44 | $ npm install @egjs/visible
45 | ```
46 |
47 | ## How to use
48 | Set up your HTML
49 | ```html
50 |
51 | -
52 |
test1
53 |
54 | -
55 |
test2
56 |
57 | -
58 |
test3
59 |
60 | -
61 |
test4
62 |
63 | -
64 |
test5
65 |
66 | -
67 |
test6
68 |
69 |
70 | ```
71 | ```js
72 | //
73 | import Visible from "@egjs/visible";
74 |
75 | // Create eg.Visible instance
76 | var visible = new eg.Visible(document,{
77 | // You can find targets through the targetSelector or targetClass option.
78 | targetSelector : ".check_visible",
79 | // or targetClass: "check_visible",
80 | expandSize : 0
81 | });
82 |
83 | // Add change event handler
84 | visible.on("change", function (e) {
85 | // e.visible;
86 | // e.invisible;
87 | });
88 |
89 | // Call "check" method whenever you want to check visibility change of the elements compared with last time you call "check" method.
90 | // When you call "check" mehtod and if there is a change, "change" event will trigger.
91 | visible.check();
92 | ```
93 | ## Supported Browsers
94 | The following are the supported browsers.
95 |
96 | |Internet Explorer|Chrome|Firefox|Safari|iOS|Android|
97 | |---|---|---|---|---|---|
98 | |8+|Latest|Latest|Latest|7+|2.1+(except 3.x)|
99 |
100 | ## Dependency
101 |
102 | egjs-visible has the dependencies for the following libraries:
103 |
104 | |[egjs-component](http://github.com/naver/egjs/egjs-component)|
105 | |----|
106 | |2.0.0+|
107 |
108 |
109 | ## How to start developing egjs-visible?
110 |
111 | For anyone interested to develop egjs-visible, follow the instructions below.
112 |
113 | ### Development Environment
114 |
115 | #### 1. Clone the repository
116 |
117 | Clone the egjs-visible repository and install the dependency modules.
118 |
119 | ```bash
120 | # Clone the repository.
121 | $ git clone https://github.com/naver/egjs-visible.git
122 | ```
123 |
124 | #### 2. Install dependencies
125 | `npm` is supported.
126 |
127 | ```
128 | # Install the dependency modules.
129 | $ npm install
130 | ```
131 |
132 | #### 3. Build
133 |
134 | Use npm script to build eg.Visible
135 |
136 | ```bash
137 | # Run webpack-dev-server for development
138 | $ npm start
139 |
140 | # Build
141 | $ npm run build
142 |
143 | # Generate jsdoc
144 | $ npm run jsdoc
145 | ```
146 |
147 | Two folders will be created after complete build is completed.
148 |
149 | - **dist** folder: Includes the **visible.js** and **visible.min.js** files.
150 | - **doc** folder: Includes API documentation. The home page for the documentation is **doc/index.html**.
151 |
152 | ### Linting
153 |
154 | To keep the same code style, we adopted [ESLint](http://eslint.org/) to maintain our code quality. The [rules](https://github.com/naver/eslint-config-naver/tree/master/rules) are modified version based on [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript).
155 | Setup your editor for check or run below command for linting.
156 |
157 | ```bash
158 | $ npm run lint
159 | ```
160 |
161 | ### Test
162 |
163 | Once you created a branch and done with development, you must perform a test running `npm run test` command before you push code to a remote repository.
164 |
165 | ```bash
166 | $ npm run test
167 | ```
168 | Running a `npm run test` command will start [Mocha](https://mochajs.org/) tests via [Karma-runner](https://karma-runner.github.io/).
169 |
170 |
171 | ## Bug Report
172 |
173 | If you find a bug, please report it to us using the [Issues](https://github.com/naver/egjs-visible/issues) page on GitHub.
174 |
175 |
176 | ## License
177 | egjs-visible is released under the [MIT license](http://naver.github.io/egjs/license.txt).
178 |
179 |
180 | ```
181 | Copyright (c) 2015 NAVER Corp.
182 |
183 | Permission is hereby granted, free of charge, to any person obtaining a copy
184 | of this software and associated documentation files (the "Software"), to deal
185 | in the Software without restriction, including without limitation the rights
186 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
187 | copies of the Software, and to permit persons to whom the Software is
188 | furnished to do so, subject to the following conditions:
189 |
190 | The above copyright notice and this permission notice shall be included in
191 | all copies or substantial portions of the Software.
192 |
193 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
194 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
195 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
196 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
198 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
199 | THE SOFTWARE.
200 | ```
201 |
--------------------------------------------------------------------------------
/test/unit/pre.tmpl.html:
--------------------------------------------------------------------------------
1 |
5 |
6 | GNU GENERAL PUBLIC LICENSE
7 | Version 3, 29 June 2007
8 |
9 | Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
10 | Everyone is2 permitted to copy and disdtribute verbatim copies
11 | of this license document, but changing it is not allowed.
12 |
13 | Preamble
14 |
15 | The GNU General Public License is a free, copyleft license for
16 | software and other kinds of works.
17 |
18 | The licenses for most software and other practical works are designed
19 | to take away your freedom to share and change the works. By contrast,
20 | the GNU General Public License is intended to guarantee your freedom to
21 | share and change all versions of a program--to make sure it remains free
22 | software for all its users. We, the Free Software Foundation, use the
23 | GNU General Public License for most of our software; it applies also to
24 | any other work released this way by its authors. You can apply it to
25 | your programs, too.
26 |
27 | When we speak of free software, we are referring to freedom, not
28 | price. Our General Public Licenses are designed to make sure that you
29 | have the freedom to distribute copies of free software (and charge for
30 | them if you wish), that you receive source code or can get it if you
31 | want it, that you can change the software or use pieces of it in new
32 | free programs, and that you know you can do these things.
33 |
34 | To protect your rights, we need to prevent others from denying you
35 | these rights or asking you to surrender the rights. Therefore, you have
36 | certain responsibilities if you distribute copies of the software, or if
37 | you modify it: responsibilities to respect the freedom of others.
38 |
39 | For example, if you distribute copies of such a program, whether
40 | gratis or for a fee, you must pass on to the recipients the same
41 | freedoms that you received. You must make sure that they, too, receive
42 | or can get the source code. And you must show them these terms so they
43 | know their rights.
44 |
45 | Developers that use the GNU GPL protect your rights with two steps:
46 | (1) assert copyright on the software, and (2) offer you this License
47 | giving you legal permission to copy, distribute and/or modify it.
48 |
49 | For the developers' and authors' protection, the GPL clearly explains
50 | that there is no warranty for this free software. For both users' and
51 | authors' sake, the GPL requires that modified versions be marked as
52 | changed, so that their problems will not be attributed erroneously to
53 | authors of previous versions.
54 |
55 | Some devices are designed to deny users access to install or run
56 | modified versions of the software inside them, although the manufacturer
57 | can do so. This is fundamentally incompatible with the aim of
58 | protecting users' freedom to change the software. The systematic
59 | pattern of such abuse occurs in the area of products for individuals to
60 | use, which is precisely where it is most unacceptable. Therefore, we
61 | have designed this version of the GPL to prohibit the practice for those
62 | products. If such problems arise substantially in other domains, we
63 | stand ready to extend this provision to those domains in future versions
64 | of the GPL, as needed to protect the freedom of users.
65 |
66 | Finally, every program is threatened constantly by software patents.
67 | States should not allow patents to restrict development and use of
68 | software on general-purpose computers, but in those that do, we wish to
69 | avoid the special danger that patents applied to a free program could
70 | make it effectively proprietary. To prevent this, the GPL assures that
71 | patents cannot be used to render the program non-free.
72 |
73 | The precise terms and conditions for copying, distribution and
74 | modification follow.
75 |
76 | TERMS AND CONDITIONS
77 |
78 | 0. Definitions.
79 |
80 | "This License" refers to version 3 of the GNU General Public License.
81 |
82 | "Copyright" also means copyright-like laws that apply to other kinds of
83 | works, such as semiconductor masks.
84 |
85 | "The Program" refers to any copyrightable work licensed under this
86 | License. Each licensee is addressed as "you". "Licensees" and
87 | "recipients" may be individuals or organizations.
88 |
89 | To "modify" a work means to copy from or adapt all or part of the work
90 | in a fashion requiring copyright permission, other than the making of an
91 | exact copy. The resulting work is called a "modified version" of the
92 | earlier work or a work "based on" the earlier work.
93 |
94 | A "covered work" means either the unmodified Program or a work based
95 | on the Program.
96 |
97 | To "propagate" a work means to do anything with it that, without
98 | permission, would make you directly or secondarily liable for
99 | infringement under applicable copyright law, except executing it on a
100 | computer or modifying a private copy. Propagation includes copying,
101 | distribution (with or without modification), making available to the
102 | public, and in some countries other activities as well.
103 |
104 | To "convey" a work means any kind of propagation that enables other
105 | parties to make or receive copies. Mere interaction with a user through
106 | a computer network, with no transfer of a copy, is not conveying.
107 |
108 | An interactive user interface displays "Appropriate Legal Notices"
109 | to the extent that it includes a convenient and prominently visible
110 | feature that (1) displays an appropriate copyright notice, and (2)
111 | tells the user that there is no warranty for the work (except to the
112 | extent that warranties are provided), that licensees may convey the
113 | work under this License, and how to view a copy of this License. If
114 | the interface presents a list of user commands or options, such as a
115 | menu, a prominent item in the list meets this criterion.
116 |
117 | 1. Source Code.
118 |
119 | The "source code" for a work means the preferred form of the work
120 | for making modifications to it. "Object code" means any non-source
121 | form of a work.
122 |
123 | A "Standard Interface" means an interface that either is an official
124 | standard defined by a recognized standards body, or, in the case of
125 | interfaces specified for a particular programming language, one that
126 | is widely used among developers working in that language.
127 |
128 | The "System Libraries" of an executable work include anything, other
129 | than the work as a whole, that (a) is included in the normal form of
130 | packaging a Major Component, but which is not part of that Major
131 | Component, and (b) serves only to enable use of the work with that
132 | Major Component, or to implement a Standard Interface for which an
133 | implementation is available to the public in source code form. A
134 | "Major Component", in this context, means a major essential component
135 | (kernel, window system, and so on) of the specific operating system
136 | (if any) on which the executable work runs, or a compiler used to
137 | produce the work, or an object code interpreter used to run it.
138 |
139 | The "Corresponding Source" for a work in object code form means all
140 | the source code needed to generate, install, and (for an executable
141 | work) run the object code and to modify the work, including scripts to
142 | control those activities. However, it does not include the work's
143 | System Libraries, or general-purpose tools or generally available free
144 | programs which are used unmodified in performing those activities but
145 | which are not part of the work. For example, Corresponding Source
146 | includes interface definition files associated with source files for
147 | the work, and the source code for shared libraries and dynamically
148 | linked subprograms that the work is specifically designed to require,
149 | such as by intimate data communication or control flow between those
150 | subprograms and other parts of the work.
151 |
152 | The Corresponding Source need not include anything that users
153 | can regenerate automatically from other parts of the Corresponding
154 | Source.
155 |
156 | The Corresponding Source for a work in source code form is that
157 | same work.
158 |
--------------------------------------------------------------------------------
/test/manual/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
egjs-visible
6 |
7 |
8 |
105 |
106 |
107 |
108 |
109 |
110 | GNU GENERAL PUBLIC LICENSE
111 | Version 3, 29 June 2007
112 |
113 | Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
114 | Everyone is permitted to copy and distribute verbatim copies
115 | of this license document, but changing it is not allowed.
116 |
117 | Preamble
118 |
119 | The GNU General Public License is a free, copyleft license for
120 | software and other kinds of works.
121 |
122 | The licenses for most software and other practical works are designed
123 | to take away your freedom to share and change the works. By contrast,
124 | the GNU General Public License is intended to guarantee your freedom to
125 | share and change all versions of a program--to make sure it remains free
126 | software for all its users. We, the Free Software Foundation, use the
127 | GNU General Public License for most of our software; it applies also to
128 | any other work released this way by its authors. You can apply it to
129 | your programs, too.
130 |
131 | When we speak of free software, we are referring to freedom, not
132 | price. Our General Public Licenses are designed to make sure that you
133 | have the freedom to distribute copies of free software (and charge for
134 | them if you wish), that you receive source code or can get it if you
135 | want it, that you can change the software or use pieces of it in new
136 | free programs, and that you know you can do these things.
137 |
138 | To protect your rights, we need to prevent others from denying you
139 | these rights or asking you to surrender the rights. Therefore, you have
140 | certain responsibilities if you distribute copies of the software, or if
141 | you modify it: responsibilities to respect the freedom of others.
142 |
143 | For example, if you distribute copies of such a program, whether
144 | gratis or for a fee, you must pass on to the recipients the same
145 | freedoms that you received. You must make sure that they, too, receive
146 | or can get the source code. And you must show them these terms so they
147 | know their rights.
148 |
149 | Developers that use the GNU GPL protect your rights with two steps:
150 | (1) assert copyright on the software, and (2) offer you this License
151 | giving you legal permission to copy, distribute and/or modify it.
152 |
153 | For the developers' and authors' protection, the GPL clearly explains
154 | that there is no warranty for this free software. For both users' and
155 | authors' sake, the GPL requires that modified versions be marked as
156 | changed, so that their problems will not be attributed erroneously to
157 | authors of previous versions.
158 |
159 | Some devices are designed to deny users access to install or run
160 | modified versions of the software inside them, although the manufacturer
161 | can do so. This is fundamentally incompatible with the aim of
162 | protecting users' freedom to change the software. The systematic
163 | pattern of such abuse occurs in the area of products for individuals to
164 | use, which is precisely where it is most unacceptable. Therefore, we
165 | have designed this version of the GPL to prohibit the practice for those
166 | products. If such problems arise substantially in other domains, we
167 | stand ready to extend this provision to those domains in future versions
168 | of the GPL, as needed to protect the freedom of users.
169 |
170 | Finally, every program is threatened constantly by software patents.
171 | States should not allow patents to restrict development and use of
172 | software on general-purpose computers, but in those that do, we wish to
173 | avoid the special danger that patents applied to a free program could
174 | make it effectively proprietary. To prevent this, the GPL assures that
175 | patents cannot be used to render the program non-free.
176 |
177 | The precise terms and conditions for copying, distribution and
178 | modification follow.
179 |
180 | TERMS AND CONDITIONS
181 |
182 | 0. Definitions.
183 |
184 | "This License" refers to version 3 of the GNU General Public License.
185 |
186 | "Copyright" also means copyright-like laws that apply to other kinds of
187 | works, such as semiconductor masks.
188 |
189 | "The Program" refers to any copyrightable work licensed under this
190 | License. Each licensee is addressed as "you". "Licensees" and
191 | "recipients" may be individuals or organizations.
192 |
193 | To "modify" a work means to copy from or adapt all or part of the work
194 | in a fashion requiring copyright permission, other than the making of an
195 | exact copy. The resulting work is called a "modified version" of the
196 | earlier work or a work "based on" the earlier work.
197 |
198 | A "covered work" means either the unmodified Program or a work based
199 | on the Program.
200 |
201 | To "propagate" a work means to do anything with it that, without
202 | permission, would make you directly or secondarily liable for
203 | infringement under applicable copyright law, except executing it on a
204 | computer or modifying a private copy. Propagation includes copying,
205 | distribution (with or without modification), making available to the
206 | public, and in some countries other activities as well.
207 |
208 | To "convey" a work means any kind of propagation that enables other
209 | parties to make or receive copies. Mere interaction with a user through
210 | a computer network, with no transfer of a copy, is not conveying.
211 |
212 | An interactive user interface displays "Appropriate Legal Notices"
213 | to the extent that it includes a convenient and prominently visible
214 | feature that (1) displays an appropriate copyright notice, and (2)
215 | tells the user that there is no warranty for the work (except to the
216 | extent that warranties are provided), that licensees may convey the
217 | work under this License, and how to view a copy of this License. If
218 | the interface presents a list of user commands or options, such as a
219 | menu, a prominent item in the list meets this criterion.
220 |
221 | 1. Source Code.
222 |
223 | The "source code" for a work means the preferred form of the work
224 | for making modifications to it. "Object code" means any non-source
225 | form of a work.
226 |
227 | A "Standard Interface" means an interface that either is an official
228 | standard defined by a recognized standards body, or, in the case of
229 | interfaces specified for a particular programming language, one that
230 | is widely used among developers working in that language.
231 |
232 | The "System Libraries" of an executable work include anything, other
233 | than the work as a whole, that (a) is included in the normal form of
234 | packaging a Major Component, but which is not part of that Major
235 | Component, and (b) serves only to enable use of the work with that
236 | Major Component, or to implement a Standard Interface for which an
237 | implementation is available to the public in source code form. A
238 | "Major Component", in this context, means a major essential component
239 | (kernel, window system, and so on) of the specific operating system
240 | (if any) on which the executable work runs, or a compiler used to
241 | produce the work, or an object code interpreter used to run it.
242 |
243 | The "Corresponding Source" for a work in object code form means all
244 | the source code needed to generate, install, and (for an executable
245 | work) run the object code and to modify the work, including scripts to
246 | control those activities. However, it does not include the work's
247 | System Libraries, or general-purpose tools or generally available free
248 | programs which are used unmodified in performing those activities but
249 | which are not part of the work. For example, Corresponding Source
250 | includes interface definition files associated with source files for
251 | the work, and the source code for shared libraries and dynamically
252 | linked subprograms that the work is specifically designed to require,
253 | such as by intimate data communication or control flow between those
254 | subprograms and other parts of the work.
255 |
256 | The Corresponding Source need not include anything that users
257 | can regenerate automatically from other parts of the Corresponding
258 | Source.
259 |
260 | The Corresponding Source for a work in source code form is that
261 | same work.
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
--------------------------------------------------------------------------------
/test/manual/observer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
egjs-visible
6 |
7 |
8 |
105 |
106 |
107 |
108 |
109 |
110 | GNU GENERAL PUBLIC LICENSE
111 | Version 3, 29 June 2007
112 |
113 | Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
114 | Everyone is permitted to copy and distribute verbatim copies
115 | of this license document, but changing it is not allowed.
116 |
117 | Preamble
118 |
119 | The GNU General Public License is a free, copyleft license for
120 | software and other kinds of works.
121 |
122 | The licenses for most software and other practical works are designed
123 | to take away your freedom to share and change the works. By contrast,
124 | the GNU General Public License is intended to guarantee your freedom to
125 | share and change all versions of a program--to make sure it remains free
126 | software for all its users. We, the Free Software Foundation, use the
127 | GNU General Public License for most of our software; it applies also to
128 | any other work released this way by its authors. You can apply it to
129 | your programs, too.
130 |
131 | When we speak of free software, we are referring to freedom, not
132 | price. Our General Public Licenses are designed to make sure that you
133 | have the freedom to distribute copies of free software (and charge for
134 | them if you wish), that you receive source code or can get it if you
135 | want it, that you can change the software or use pieces of it in new
136 | free programs, and that you know you can do these things.
137 |
138 | To protect your rights, we need to prevent others from denying you
139 | these rights or asking you to surrender the rights. Therefore, you have
140 | certain responsibilities if you distribute copies of the software, or if
141 | you modify it: responsibilities to respect the freedom of others.
142 |
143 | For example, if you distribute copies of such a program, whether
144 | gratis or for a fee, you must pass on to the recipients the same
145 | freedoms that you received. You must make sure that they, too, receive
146 | or can get the source code. And you must show them these terms so they
147 | know their rights.
148 |
149 | Developers that use the GNU GPL protect your rights with two steps:
150 | (1) assert copyright on the software, and (2) offer you this License
151 | giving you legal permission to copy, distribute and/or modify it.
152 |
153 | For the developers' and authors' protection, the GPL clearly explains
154 | that there is no warranty for this free software. For both users' and
155 | authors' sake, the GPL requires that modified versions be marked as
156 | changed, so that their problems will not be attributed erroneously to
157 | authors of previous versions.
158 |
159 | Some devices are designed to deny users access to install or run
160 | modified versions of the software inside them, although the manufacturer
161 | can do so. This is fundamentally incompatible with the aim of
162 | protecting users' freedom to change the software. The systematic
163 | pattern of such abuse occurs in the area of products for individuals to
164 | use, which is precisely where it is most unacceptable. Therefore, we
165 | have designed this version of the GPL to prohibit the practice for those
166 | products. If such problems arise substantially in other domains, we
167 | stand ready to extend this provision to those domains in future versions
168 | of the GPL, as needed to protect the freedom of users.
169 |
170 | Finally, every program is threatened constantly by software patents.
171 | States should not allow patents to restrict development and use of
172 | software on general-purpose computers, but in those that do, we wish to
173 | avoid the special danger that patents applied to a free program could
174 | make it effectively proprietary. To prevent this, the GPL assures that
175 | patents cannot be used to render the program non-free.
176 |
177 | The precise terms and conditions for copying, distribution and
178 | modification follow.
179 |
180 | TERMS AND CONDITIONS
181 |
182 | 0. Definitions.
183 |
184 | "This License" refers to version 3 of the GNU General Public License.
185 |
186 | "Copyright" also means copyright-like laws that apply to other kinds of
187 | works, such as semiconductor masks.
188 |
189 | "The Program" refers to any copyrightable work licensed under this
190 | License. Each licensee is addressed as "you". "Licensees" and
191 | "recipients" may be individuals or organizations.
192 |
193 | To "modify" a work means to copy from or adapt all or part of the work
194 | in a fashion requiring copyright permission, other than the making of an
195 | exact copy. The resulting work is called a "modified version" of the
196 | earlier work or a work "based on" the earlier work.
197 |
198 | A "covered work" means either the unmodified Program or a work based
199 | on the Program.
200 |
201 | To "propagate" a work means to do anything with it that, without
202 | permission, would make you directly or secondarily liable for
203 | infringement under applicable copyright law, except executing it on a
204 | computer or modifying a private copy. Propagation includes copying,
205 | distribution (with or without modification), making available to the
206 | public, and in some countries other activities as well.
207 |
208 | To "convey" a work means any kind of propagation that enables other
209 | parties to make or receive copies. Mere interaction with a user through
210 | a computer network, with no transfer of a copy, is not conveying.
211 |
212 | An interactive user interface displays "Appropriate Legal Notices"
213 | to the extent that it includes a convenient and prominently visible
214 | feature that (1) displays an appropriate copyright notice, and (2)
215 | tells the user that there is no warranty for the work (except to the
216 | extent that warranties are provided), that licensees may convey the
217 | work under this License, and how to view a copy of this License. If
218 | the interface presents a list of user commands or options, such as a
219 | menu, a prominent item in the list meets this criterion.
220 |
221 | 1. Source Code.
222 |
223 | The "source code" for a work means the preferred form of the work
224 | for making modifications to it. "Object code" means any non-source
225 | form of a work.
226 |
227 | A "Standard Interface" means an interface that either is an official
228 | standard defined by a recognized standards body, or, in the case of
229 | interfaces specified for a particular programming language, one that
230 | is widely used among developers working in that language.
231 |
232 | The "System Libraries" of an executable work include anything, other
233 | than the work as a whole, that (a) is included in the normal form of
234 | packaging a Major Component, but which is not part of that Major
235 | Component, and (b) serves only to enable use of the work with that
236 | Major Component, or to implement a Standard Interface for which an
237 | implementation is available to the public in source code form. A
238 | "Major Component", in this context, means a major essential component
239 | (kernel, window system, and so on) of the specific operating system
240 | (if any) on which the executable work runs, or a compiler used to
241 | produce the work, or an object code interpreter used to run it.
242 |
243 | The "Corresponding Source" for a work in object code form means all
244 | the source code needed to generate, install, and (for an executable
245 | work) run the object code and to modify the work, including scripts to
246 | control those activities. However, it does not include the work's
247 | System Libraries, or general-purpose tools or generally available free
248 | programs which are used unmodified in performing those activities but
249 | which are not part of the work. For example, Corresponding Source
250 | includes interface definition files associated with source files for
251 | the work, and the source code for shared libraries and dynamically
252 | linked subprograms that the work is specifically designed to require,
253 | such as by intimate data communication or control flow between those
254 | subprograms and other parts of the work.
255 |
256 | The Corresponding Source need not include anything that users
257 | can regenerate automatically from other parts of the Corresponding
258 | Source.
259 |
260 | The Corresponding Source for a work in source code form is that
261 | same work.
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
--------------------------------------------------------------------------------
/src/Visible.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) NAVER Corp.
3 | * egjs-visible projects are licensed under the MIT license
4 | */
5 | import Component from "@egjs/component";
6 | import {$, addEvent, removeEvent, getWindowRect, hasClass} from "./utils";
7 |
8 | // IE8
9 | // https://stackoverflow.com/questions/43216659/babel-ie8-inherit-issue-with-object-create
10 | /* eslint-disable */
11 | if (typeof Object.create !== "function") {
12 | Object.create = function (o, properties) {
13 | if (typeof o !== "object" && typeof o !== "function") {
14 | throw new TypeError("Object prototype may only be an Object: " + o);
15 | } else if (o === null) {
16 | throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
17 | }
18 | function F() {}
19 | F.prototype = o;
20 | return new F();
21 | };
22 | }
23 | const SUPPORT_OBSERVER = !!window.IntersectionObserver;
24 | const SUPPORT_ELEMENTS_BY_CLASSNAME = (() => {
25 | const dummy = document.createElement("div");
26 |
27 | if (!dummy.getElementsByClassName) {
28 | return false;
29 | }
30 |
31 | const dummies = dummy.getElementsByClassName("dummy");
32 |
33 | dummy.innerHTML = "
";
34 | return dummies.length === 1;
35 | })();
36 |
37 | /* eslint-enable */
38 |
39 | /**
40 | * A Class used to check whether an element is visible in the base element or viewport.
41 | * @ko 엘리먼트가 기준 엘리먼트나 뷰포트 안에 보이는지 확인하는 클래스
42 | * @alias eg.Visible
43 | * @extends eg.Component
44 | *
45 | * @support {"ie": "8+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.1+ (except 3.x)"}
46 | */
47 | class Visible extends Component {
48 | /**
49 | * @param {HTMLElement|String|jQuery} [baseElement=document] A base element that detects if target elements are visible
타겟 엘리먼트들이 보이는 기준 엘리먼트
50 | * @param {Object} options The option object of the Visible module
Visible 모듈의 옵션 객체
51 | * @param {String} [options.targetContainer=baseElement] A target container element that contains the target elements. (baseElement >= targetContainer > target elements)
타겟 엘리먼트들을 포함하고 있는 타겟 컨테이너 엘리먼트 (baseElement >= targetContainer > target elements)
52 | * @param {String} [options.targetClass="check_visible"] The class name of the element to be checked. It has slightly better performance than the targetSelector option.
보이는지 확인할 엘리먼트의 클래스 이름. targetSelector 옵션보다 성능이 조금 좋다.
53 | * @param {String} [options.targetSelector=""] The selector of the element to be checked. You can use targetClass instead.
보이는지 확인할 엘리먼트의 셀렉터. targetClass 대신 사용할 수 있다.
54 | * @param {Number} [options.expandSize=0] The size of the expanded area to be checked whether an element is visible. If this value is less than zero, the size of the area is smaller than that of the base element.
기준 엘리먼트의 경계를 넘어 엘리먼트가 보이는지 확인할 영역의 크기. 값이 0보다 작으면 엘리먼트가 보이는지 확인할 영역의 크기가 기준 엘리먼트보다 작아진다
55 | */
56 | constructor(baseElement, options) {
57 | super();
58 | this.options = {
59 | targetContainer: null,
60 | targetClass: "check_visible",
61 | targetSelector: "",
62 | expandSize: 0,
63 | };
64 | Object.assign(this.options, options);
65 | const targetContainer = this.options.targetContainer || baseElement;
66 |
67 | this._base = $(baseElement) || document;
68 | this._wrapper = $(targetContainer) || this._base;
69 |
70 | // this._base is Element, or may be Window
71 | if (this._base.nodeType && this._base.nodeType === 1) {
72 | this._getAreaRect = this._getWrapperRect;
73 | } else {
74 | this._getAreaRect = getWindowRect;
75 | }
76 |
77 | this._targets = [];
78 | this._timer = null;
79 | this.refresh();
80 | }
81 | /**
82 | * Updates the list of elements where the visibility property is to be checked
83 | * @ko visibility 속성을 검사할 엘리먼트의 목록을 갱신한다
84 | * @return {eg.Visible} An instance of a module itself
모듈 자신의 인스턴스
85 | *
86 | * @remark
87 | * If targets was added or removed from DOM tree, must call refresh method to update internal target list.
88 | *
확인 대상이 영역 안에 추가되거나 삭제된 경우, 모듈내부에서 사용하는 확인 대상 목록을 이 메소드를 호출하여 갱신해야한다.
89 | */
90 | refresh() {
91 | const options = this.options;
92 | const targetClass = options.targetClass;
93 | const targetSelector = options.targetSelector;
94 |
95 | if (SUPPORT_ELEMENTS_BY_CLASSNAME && !targetSelector) {
96 | this._targets = this._wrapper.getElementsByClassName(targetClass);
97 | this.refresh = function() {
98 | this._refreshObserver();
99 | return this;
100 | };
101 | } else {
102 | this.refresh = function() {
103 | const selector = targetSelector || `.${targetClass}`;
104 | const targets = this._wrapper.querySelectorAll(selector);
105 |
106 | this._targets = [];
107 |
108 | for (let i = 0; i < targets.length; i++) {
109 | this._targets.push(targets[i]);
110 | }
111 | this._refreshObserver();
112 | return this;
113 | };
114 | }
115 | return this.refresh();
116 | }
117 |
118 | /**
119 | * Checks whether the visible of the target elements has changed. It trigger that change event on a component.
120 | * @ko 대상 엘리먼트의 가시성이 변경됐는지 체크한다. change 이벤트를 발생한다.
121 | * @param {Number} [delay=-1] Delay time. It delay that change event trigger.지연시간. change 이벤트 발생을 지연한다.
122 | * @param {Boolean} [containment=false] Whether to check only elements that are completely contained within the reference area.기준 영역 안에 완전히 포함된 엘리먼트만 체크할지 여부.
123 | * @return {eg.Visible} An instance of a module itself모듈 자신의 인스턴스
124 | */
125 | check(...params) {
126 | let delay = params[0];
127 | let containment = params[1];
128 |
129 | if (typeof delay !== "number") {
130 | containment = delay;
131 | delay = -1;
132 | }
133 |
134 | if (typeof delay === "undefined") {
135 | delay = -1;
136 | }
137 |
138 | if (typeof containment === "undefined") {
139 | containment = false;
140 | }
141 |
142 | clearTimeout(this._timer);
143 | if (delay < 0) {
144 | this._check(containment);
145 | this._checkAfter();
146 | } else {
147 | this._timer = setTimeout(() => {
148 | this._check(containment);
149 | this._checkAfter();
150 | this._timer = null;
151 | }, delay);
152 | }
153 | return this;
154 | }
155 |
156 | /**
157 | * Get current visible elements.
158 | * @ko 현재 보이는 엘리먼트들을 반환한다.
159 | * @return {HTMLElement[]} An array of visible elements.현재 보이는 엘리먼트들의 배열
160 | */
161 | getVisibleElements() {
162 | const targets = this._targets;
163 | const result = [];
164 |
165 | for (let i = 0; i < targets.length; i++) {
166 | const el = targets[i];
167 |
168 | if (el && el.__VISIBLE__ === true) {
169 | result.push(el);
170 | }
171 | }
172 | return result;
173 | }
174 |
175 | _getWrapperRect() {
176 | return this._base.getBoundingClientRect();
177 | }
178 |
179 | _reviseElements(...params) {
180 | const options = this.options;
181 |
182 | if (SUPPORT_ELEMENTS_BY_CLASSNAME && !options.targetSelector) {
183 | this._reviseElements = () => true;
184 | } else {
185 | this._reviseElements = (target, i) => {
186 | if (!hasClass(target, options.targetClass)) {
187 | target.__VISIBLE__ = null;
188 | this._targets.splice(i, 1);
189 | return false;
190 | }
191 | return true;
192 | };
193 | }
194 | return this._reviseElements(...params);
195 | }
196 |
197 | _check(containment) {
198 | const expandSize = parseInt(this.options.expandSize, 10);
199 |
200 | let i;
201 | let target;
202 | let targetArea;
203 | let after;
204 |
205 | // Error Fix: Cannot set property top of # which has only a getter
206 | const rect = this._getAreaRect();
207 | const area = {
208 | top: rect.top - expandSize,
209 | left: rect.left - expandSize,
210 | bottom: rect.bottom + expandSize,
211 | right: rect.right + expandSize,
212 | };
213 |
214 | for (i = this._targets.length - 1;
215 | (target = this._targets[i]); i--) {
216 | targetArea = target.getBoundingClientRect();
217 |
218 | if (targetArea.width === 0 && targetArea.height === 0) {
219 | continue;
220 | }
221 |
222 | if (!this._reviseElements(target, i)) {
223 | continue;
224 | }
225 | if (containment) {
226 | after = !(
227 | targetArea.top < area.top ||
228 | targetArea.bottom > area.bottom ||
229 | targetArea.right > area.right ||
230 | targetArea.left < area.left
231 | );
232 | } else {
233 | after = !(
234 | targetArea.bottom < area.top ||
235 | area.bottom < targetArea.top ||
236 | targetArea.right < area.left ||
237 | area.right < targetArea.left
238 | );
239 | }
240 | target.__AFTER__ = after;
241 | }
242 | }
243 | checkObserve(delay = -1) {
244 | if (this._timer) {
245 | clearTimeout(this._timer);
246 | this._timer = 0;
247 | }
248 | if (delay < 0) {
249 | this._checkAfter();
250 | } else {
251 | this._timer = setTimeout(() => {
252 | this._checkAfter();
253 | this._timer = null;
254 | }, delay);
255 | }
256 | return this;
257 | }
258 | _checkAfter() {
259 | const targets = this._targets;
260 | const length = targets.length;
261 | const visibles = [];
262 | const invisibles = [];
263 |
264 | for (let i = 0; i < length; ++i) {
265 | const target = targets[i];
266 | const prev = target.__VISIBLE__;
267 | const after = target.__AFTER__;
268 |
269 | if (prev === after) {
270 | continue;
271 | }
272 | if (after) {
273 | visibles.push(target);
274 | } else {
275 | invisibles.push(target);
276 | }
277 | target.__VISIBLE__ = after;
278 | }
279 | if (visibles.length === 0 && invisibles.length === 0) {
280 | return;
281 | }
282 | /**
283 | * This event is fired when the event is compared with the last verified one and there is an element of which the visibility property has changed.
284 | * @ko 마지막으로 확인한 결과와 비교해 visibility 속성이 변경된 엘리먼트가 있을 때 발생하는 이벤트
285 | * @name eg.Visible#change
286 | * @event
287 | * @type {obejct} The object of data to be sent when the event is fired이벤트가 발생할 때 전달되는 데이터 객체
288 | * @property {Array} visible Visible elements (the element type is `HTMLElement`) 보이게 된 엘리먼트들
289 | * @property {Array} invisible Invisible elements (the element type is `HTMLElement`) 안 보이게 된 엘리먼트들
290 | * @property {Boolean} isTrusted Returns true if an event was generated by the user action, or false if it was caused by a script or API call 사용자의 액션에 의해 이벤트가 발생하였으면 true, 스크립트나 API호출에 의해 발생하였을 경우에는 false를 반환한다.
291 | */
292 | this.trigger("change", {
293 | visible: visibles,
294 | invisible: invisibles,
295 | isTrusted: true, // This event is called by 'check' method.
296 | });
297 | }
298 | /**
299 | * Observe whether the visible of the target elements has changed. It trigger that change event on a component.
300 | * @ko 대상 엘리먼트의 가시성이 변경됐는지 관찰한다. change 이벤트를 발생한다.
301 | * @param {Object} [options={}] Options to observe the target elements. 대상 엘리먼트를 관찰하기 위한 옵션들.
302 | * @param {Number} [options.delay=-1] Delay time. It delay that change event trigger.지연시간. change 이벤트 발생을 지연한다.
303 | * @param {Boolean} [options.containment=false] Whether to check only elements that are completely contained within the reference area.기준 영역 안에 완전히 포함된 엘리먼트만 체크할지 여부.
304 | * @return {eg.Visible} An instance of a module itself모듈 자신의 인스턴스
305 | */
306 | observe(options = {}) {
307 | if (!SUPPORT_OBSERVER) {
308 | this._addObserveEvent(options);
309 | return this;
310 | }
311 | const delay = typeof options.delay === "undefined" ? -1 : options.delay;
312 | const containment = !!options.containment;
313 |
314 | this._observeCallback = entries => {
315 | entries.forEach(entry => {
316 | entry.target.__AFTER__ = containment ? entry.intersectionRatio >= 1 : entry.isIntersecting;
317 | });
318 | this._checkAfter(delay);
319 | };
320 | if (this._observer) {
321 | this.unobserve();
322 | }
323 | this._observer = new IntersectionObserver(this._observeCallback, {
324 | root: this._base.nodeType === 1 ? this._base : null,
325 | rootMargin: `${this.options.expandSize}px`,
326 | threshold: containment ? [0, 1] : [0],
327 | });
328 | const observer = this._observer;
329 | const targets = this._targets;
330 | const length = targets.length;
331 |
332 | for (let i = 0; i < length; ++i) {
333 | observer.observe(targets[i]);
334 | }
335 | return this;
336 | }
337 | unobserve() {
338 | if (!this._observeCallback) {
339 | return this;
340 | }
341 | if (SUPPORT_OBSERVER) {
342 | this._observer && this._observer.disconnect();
343 | } else {
344 | removeEvent(this._base, "scroll", this._observeCallback);
345 | removeEvent(this._base, "resize", this._observeCallback);
346 | }
347 | this._observer = null;
348 | this._observeCallback = null;
349 | return this;
350 | }
351 | _refreshObserver() {
352 | if (!this._observer) {
353 | return;
354 | }
355 | this._observer.disconnect();
356 | const targets = this._targets;
357 | const length = targets.length;
358 |
359 | for (let i = 0; i < length; ++i) {
360 | this._observer.observe(targets[i]);
361 | }
362 | }
363 | _addObserveEvent(options = {}) {
364 | if (this._observeCallback) {
365 | return;
366 | }
367 | const delay = typeof options.delay === "undefined" ? -1 : options.delay;
368 | const containment = !!options.containment;
369 |
370 | this._observeCallback = e => {
371 | this._check(containment);
372 | this.checkObserve(delay);
373 | };
374 | addEvent(this._base, "scroll", this._observeCallback);
375 | addEvent(this._base, "resize", this._observeCallback);
376 |
377 | this._observeCallback();
378 | }
379 | destroy() {
380 | this.off();
381 | this.unobserve();
382 | this._targets = [];
383 | this._base = null;
384 | this._timer = null;
385 | }
386 | }
387 |
388 | Visible.VERSION = "#__VERSION__#";
389 | export default Visible;
390 |
--------------------------------------------------------------------------------
/demo/common/css/page.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Template Name: devAid - Responsive Website Template for developers
3 | * Version: 1.0
4 | * Author: Xiaoying Riley at 3rd Wave Media
5 | * Website: http://themes.3rdwavemedia.com/
6 | * Twitter: @3rdwave_themes
7 | */
8 | /* Default */
9 | /* ======= Base ======= */
10 | body {
11 | font-family: 'Open Sans', sans-serif;
12 | color: #2B2B2B;
13 | font-size: 16px;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 | h1,
18 | h2,
19 | h3,
20 | h4,
21 | h5,
22 | h6 {
23 | font-family: 'Zilla Slab', serif;
24 | font-weight: 700;
25 | letter-spacing: -0.8px;
26 | color: #2B2B2B;
27 | font-weight: bold;
28 | }
29 |
30 | #index {
31 | padding-top: 30px;
32 | color: #2B2B2B;
33 | }
34 |
35 | #index a {
36 |
37 | color: #2B2B2B;
38 | }
39 |
40 | .btn .fa,
41 | a.btn .fa {
42 | margin-right: 5px;
43 | }
44 | .btn:focus,
45 | a.btn:focus {
46 | color: #fff;
47 | }
48 | a.btn-cta-primary,
49 | .btn-cta-primary {
50 | background: #333;
51 | border: 1px solid #dff0d8;
52 | color: #fff;
53 | font-weight: 600;
54 | }
55 | a.btn-cta-primary:hover,
56 | .btn-cta-primary:hover {
57 | background: rgb(60, 118, 61);
58 | border: 1px solid #000;
59 | color: #fff;
60 | }
61 | a.btn-cta-secondary,
62 | .btn-cta-secondary {
63 | background: #333;
64 | border: 1px solid #dff0d8;
65 | color: #fff;
66 | font-weight: 600;
67 | }
68 | a.btn-cta-secondary:hover,
69 | .btn-cta-secondary:hover {
70 | background: rgb(60, 118, 61);
71 | border: 1px solid #000;
72 | color: #fff;
73 | }
74 | .offset-header {
75 | padding-top: 90px;
76 | }
77 | pre code {
78 | font-size: 16px;
79 | }
80 | /* ======= Header ======= */
81 | .header {
82 | position: absolute;
83 | padding: 10px 0;
84 | color: #fff;
85 | width: 100%;
86 | z-index: 999999;
87 | }
88 | .header .main-nav {
89 | height: 50px;
90 | }
91 |
92 | .header.navbar-fixed-top {
93 | background: rgba(255, 255, 255, 0.7);
94 | z-index: 9999999999;
95 | -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.4);
96 | -moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.4);
97 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.4);
98 | }
99 |
100 | .header .logo {
101 | margin: 0;
102 | font-size: 26px;
103 | padding-top: 10px;
104 | }
105 | .header .logo a {
106 | color: #fff;
107 | }
108 | .header .logo a:hover {
109 | text-decoration: none;
110 | }
111 |
112 | .header .logo {
113 | background: url("../image/type_black.svg") no-repeat;
114 | width: 80px;
115 | height: 27px;
116 | margin-top: 5px;
117 | position: absolute;
118 | left: 25px;
119 | }
120 |
121 | .header.navbar-fixed-top .logo {
122 | background: url("../image/type_black.svg") no-repeat;
123 | }
124 |
125 | .header.navbar-fixed-top .navbar-nav li a {
126 | color: #000;
127 | }
128 |
129 | .header.navbar-fixed-top .navbar-nav li a:hover {
130 | color: #eebf3f;
131 | }
132 |
133 | .navbar-nav li a:focus {
134 | background: transparent;
135 | color: #eebf3f;
136 | }
137 |
138 | .header .main-nav button {
139 | width: 55px;
140 | color: #fff !important;
141 | -moz-background-clip: padding;
142 | -webkit-background-clip: padding-box;
143 | background-clip: padding-box;
144 | }
145 | .header .main-nav button:focus {
146 | outline: none;
147 | }
148 | .header .main-nav button .icon-bar {
149 | background-color: #fff;
150 | }
151 | .header .main-nav .navbar-collapse {
152 | padding: 0;
153 | /*background-color: #000;*/
154 | overflow: hidden;
155 | }
156 |
157 | .header.navbar-fixed-top .main-nav .navbar-collapse {
158 | padding: 0;
159 | /*background: rgba(255, 255, 255, 0.7);*/
160 | /*background-color: #fff;*/
161 | }
162 |
163 | .header .main-nav .nav .nav-item {
164 | font-weight: normal;
165 | margin-right: 30px;
166 | font-family: 'Montserrat', sans-serif;
167 | }
168 | .header .main-nav .nav .nav-item.active a {
169 | color: #17baef;
170 | background: none;
171 | }
172 | .header .main-nav .nav .nav-item a {
173 | color: #0a7396;
174 | -webkit-transition: none;
175 | -moz-transition: none;
176 | -ms-transition: none;
177 | -o-transition: none;
178 | font-size: 14px;
179 | padding: 15px 10px;
180 | }
181 | .header .main-nav .nav .nav-item a:hover {
182 | color: #eebf3f;
183 | background: none;
184 | }
185 | .header .main-nav .nav .nav-item a:focus {
186 | outline: none;
187 | background: none;
188 | }
189 | .header .main-nav .nav .nav-item a:active {
190 | outline: none;
191 | background: none;
192 | }
193 | .header .main-nav .nav .nav-item.active {
194 | color: #17baef;
195 | }
196 | .header .main-nav .nav .nav-item.last {
197 | margin-right: 0;
198 | }
199 | /* ======= Promo Section ======= */
200 | .promo {
201 | /*background: #ffce00;*/
202 | /*color: #8c532e;*/
203 | background: #f3f3f3;
204 | padding-top: 0px;
205 | }
206 | .promo .container {
207 | max-width: 800px;
208 | }
209 |
210 | .promo .title {
211 | font-size: 100px;
212 | margin-top: -104px;
213 | }
214 | .promo .title .highlight {
215 | font-family: 'Zilla Slab', serif;
216 | font-size: 56px;
217 | line-height: 40px;
218 | letter-spacing: -0.8px;
219 | color: #2B2B2B;
220 | font-weight: bold;
221 | }
222 | .promo .intro {
223 | max-width: 680px;
224 | margin-bottom: 30px;
225 | text-align: left;
226 | font-size: 16px;
227 | }
228 | .promo .btns .btn {
229 | margin-right: 15px;
230 | font-size: 18px;
231 | padding: 8px 30px;
232 | }
233 | .promo .meta {
234 | margin-top: 120px;
235 | margin-bottom: 30px;
236 | color: #0a7396;
237 | }
238 | .promo .meta li {
239 | margin-right: 15px;
240 | }
241 |
242 | .container.text-center {
243 | padding: 0px 30px;
244 | }
245 | /*.promo .meta a {
246 | color: #0a7396;
247 | }
248 | .promo .meta a:hover {
249 | color: #074f66;
250 | }*/
251 | .promo .social-media {
252 | /*background: #eebf3f;*/
253 | padding: 10px 0;
254 | margin: 0 auto;
255 | }
256 | .promo .social-media li {
257 | margin-top: 15px;
258 | }
259 | .promo .social-media li.facebook-like {
260 | margin-top: 0;
261 | position: relative;
262 | top: -5px;
263 | }
264 | /* ======= Docs Section ======= */
265 | .docs {
266 | padding: 50px 0;
267 | /*background: #f5f5f5;*/
268 | /* background: #eebf3f; */
269 | }
270 | .docs.demos {
271 | background: #f5f5f5;
272 | }
273 | .docs .title {
274 | /*color: #8c532e;*/
275 | /* color: #eebf3f; */
276 | margin-top: 0;
277 | margin-bottom: 30px;
278 | /* text-shadow: 1px 1px #8c532e; */
279 | }
280 | .docs .docs-inner {
281 | max-width: 800px;
282 | background: #fff;
283 | padding: 30px;
284 | -webkit-border-radius: 4px;
285 | -moz-border-radius: 4px;
286 | -ms-border-radius: 4px;
287 | -o-border-radius: 4px;
288 | border-radius: 4px;
289 | -moz-background-clip: padding;
290 | -webkit-background-clip: padding-box;
291 | background-clip: padding-box;
292 | margin: 0 auto;
293 | }
294 | .docs .docs-inner a {
295 | color: #2b2b2b;
296 | }
297 | .docs .block {
298 | margin-bottom: 60px;
299 | }
300 | .docs .code-block {
301 | margin: 30px inherit;
302 | }
303 | .docs .code-block pre[class*="language-"] {
304 | -webkit-border-radius: 4px;
305 | -moz-border-radius: 4px;
306 | -ms-border-radius: 4px;
307 | -o-border-radius: 4px;
308 | border-radius: 4px;
309 | -moz-background-clip: padding;
310 | -webkit-background-clip: padding-box;
311 | background-clip: padding-box;
312 | }
313 | /* ======= License Section ======= */
314 | .license {
315 | padding: 80px 0;
316 | background: #f5f5f5;
317 | }
318 | .license .title {
319 | margin-top: 0;
320 | margin-bottom: 60px;
321 | color: #074f66;
322 | }
323 | .license .license-inner {
324 | max-width: 800px;
325 | background: #fff;
326 | padding: 30px;
327 | -webkit-border-radius: 4px;
328 | -moz-border-radius: 4px;
329 | -ms-border-radius: 4px;
330 | -o-border-radius: 4px;
331 | border-radius: 4px;
332 | -moz-background-clip: padding;
333 | -webkit-background-clip: padding-box;
334 | background-clip: padding-box;
335 | margin: 0 auto;
336 | }
337 | .license .info {
338 | max-width: 760px;
339 | margin: 0 auto;
340 | }
341 | .license .cta-container {
342 | max-width: 540px;
343 | margin: 0 auto;
344 | margin-top: 60px;
345 | -webkit-border-radius: 4px;
346 | -moz-border-radius: 4px;
347 | -ms-border-radius: 4px;
348 | -o-border-radius: 4px;
349 | border-radius: 4px;
350 | -moz-background-clip: padding;
351 | -webkit-background-clip: padding-box;
352 | background-clip: padding-box;
353 | }
354 | .license .cta-container .speech-bubble {
355 | background: #d6f3fc;
356 | color: #074f66;
357 | padding: 30px;
358 | margin-bottom: 30px;
359 | position: relative;
360 | -webkit-border-radius: 4px;
361 | -moz-border-radius: 4px;
362 | -ms-border-radius: 4px;
363 | -o-border-radius: 4px;
364 | border-radius: 4px;
365 | -moz-background-clip: padding;
366 | -webkit-background-clip: padding-box;
367 | background-clip: padding-box;
368 | }
369 | .license .cta-container .speech-bubble:after {
370 | position: absolute;
371 | left: 50%;
372 | bottom: -10px;
373 | margin-left: -10px;
374 | content: "";
375 | display: inline-block;
376 | width: 0;
377 | height: 0;
378 | border-left: 10px solid transparent;
379 | border-right: 10px solid transparent;
380 | border-top: 10px solid #d6f3fc;
381 | }
382 | .license .cta-container .icon-holder {
383 | margin-bottom: 15px;
384 | }
385 | .license .cta-container .icon-holder .fa {
386 | font-size: 56px;
387 | }
388 | .license .cta-container .intro {
389 | margin-bottom: 30px;
390 | }
391 | /* ======= Footer ======= */
392 | .footer {
393 | padding: 15px 0;
394 | background: #000;
395 | color: #fff;
396 | }
397 | .footer .copyright {
398 | -webkit-opacity: 0.8;
399 | -moz-opacity: 0.8;
400 | opacity: 0.8;
401 | }
402 | .footer .fa-heart {
403 | color: #fb866a;
404 | }
405 |
406 |
407 | .promo .btns {
408 | margin-bottom: 30px;
409 | }
410 |
411 | .nav li a:hover {
412 | background-color: transparent;
413 | }
414 |
415 | .hashtag {
416 | color: #003569;
417 | }
418 | span.version {
419 | font-size: 16px;
420 | margin: 8px;
421 | color: #2B2B2B;
422 | font-weight: 500;
423 | font-family: 'Open Sans', sans-serif;
424 | }
425 | span.version:hover {
426 | opacity: .6;
427 | }
428 |
429 | .hero-image {
430 | display: block;
431 | width: 100%;
432 | background-repeat: no-repeat;
433 | background-position: center center;
434 | background-size: cover;
435 | background-image: url(./img/thumbnail_flicking.png);
436 | height: 347px;
437 | }
438 | /* Extra small devices (phones, less than 768px) */
439 | @media (max-width: 767px) {
440 | .hero-image {
441 | margin-top: -30px;
442 | height: 270px;
443 | margin-bottom: 40px;
444 | }
445 | .docs .docs-inner {
446 | padding: 15px;
447 | }
448 | .header .main-nav button {
449 | margin-right: 0;
450 | }
451 |
452 | .header .main-nav .navbar-collapse {
453 | background: #000;
454 | /*background-color: #fff;*/
455 | }
456 |
457 | .header.navbar-fixed-top .main-nav .navbar-collapse {
458 | background: rgba(255, 255, 255, 0.7);
459 | /*background-color: #fff;*/
460 | }
461 |
462 | .header .main-nav .navbar-collapse {
463 | padding-left: 15px;
464 | padding-right: 15px;
465 | margin-left: -15px;
466 | margin-right: -15px;
467 | margin-top: 15px;
468 | text-align: right;
469 | }
470 |
471 | .header.navbar-fixed-top .main-nav .navbar-collapse {
472 | margin-top: 15px;
473 | padding-left: 15px;
474 | padding-right: 15px;
475 | margin-left: -15px;
476 | margin-right: -15px;
477 | text-align: right;
478 | }
479 |
480 | .promo {
481 | padding-top: 50px;
482 | }
483 | .promo .btns {
484 | margin-bottom: 0px;
485 | }
486 |
487 | .promo .btns .btn {
488 | margin-right: 0;
489 | clear: both;
490 | display: block;
491 | margin-bottom: 30px;
492 | }
493 | .promo .title {
494 | font-size: 45px;
495 | margin-top: -60px;
496 | }
497 |
498 | .promo .intro {
499 | 1font-size: 25px;
500 | }
501 | .promo .meta {
502 | margin-top: 60px;
503 | }
504 | .promo .meta li {
505 | float: none;
506 | display: block;
507 | margin-bottom: 5px;
508 | }
509 | .contact .author-message {
510 | text-align: center;
511 | }
512 | .contact .author-message .profile {
513 | position: static;
514 | margin: 0 auto;
515 | margin-bottom: 30px;
516 | }
517 | .contact .author-message .speech-bubble {
518 | margin-left: 0;
519 | }
520 | .contact .author-message .speech-bubble:after {
521 | display: none;
522 | }
523 | .contact .social-icons a {
524 | width: 36px;
525 | height: 36px;
526 | padding-top: 7px;
527 | margin-right: 2px;
528 | }
529 | .contact .social-icons a .fa:before {
530 | font-size: 18px;
531 | }
532 | }
533 | /* Small devices (tablets, 768px and up) */
534 | /* Medium devices (desktops, 992px and up) */
535 | /* Large devices (large desktops, 1200px and up) */
536 |
537 | pre {
538 | background-color: #333;
539 | border: 1px solid #333;
540 | }
541 |
542 | pre code {
543 | font-size: 13px;
544 | color: #f8f8f2;
545 | }
546 |
547 | .codepenform {
548 | float: right;
549 | }
550 |
551 | .navbar-toggle {
552 | margin-top:0px;
553 | margin-bottom :0px;
554 | padding: 0 10px;
555 | }
556 |
557 | h3 {
558 | margin-top: 50px;
559 | margin-bottom: 20px;
560 | }
561 |
562 | h4 {
563 | margin-top: 30px;
564 | margin-bottom: 10px;
565 | }
566 |
567 | .docs-inner a:hover h3 {
568 | text-decoration: none;
569 | /* color: #2b2b2b; */
570 | }
571 |
572 | .demobox {
573 | margin-bottom: 20px;
574 | border-radius: 4px;
575 | }
576 |
577 |
578 | footer {
579 |
580 | clear: both;
581 | display: block;
582 | /* margin-top: 50px; */
583 | min-height: 114px;
584 | text-align: center;
585 | padding-top: 40px;
586 | border-top: 1px solid #DFDFDF;
587 | }
588 | footer #footerContent ul li {
589 |
590 | display: inline-block;
591 | position: relative;
592 | list-style: none;
593 | line-height: 30px;
594 | vertical-align: middle;
595 | }
596 | footer #footerContent ul li:not(:first-child) {
597 | margin-left: 28px;
598 | }
599 | footer #footerContent ul li:not(:first-child):before {
600 | position: absolute;
601 | left: -17px;
602 | top: 8px;
603 | height: 14px;
604 | border-left: 1px solid #aaa;
605 | content: "";
606 | }footer #footerContent ul li a {
607 | 1color: #01ABF7;
608 | }footer #footerContent a {
609 | color: #888;
610 | }a {
611 | text-decoration: none;
612 | }
613 | .octicon-mark-github:hover {
614 | fill: #444;
615 | }
616 | .octicon-mark-github {
617 | /* position: absolute; */
618 | top: 37px;
619 | /* left: 50%; */
620 | vertical-align: middle;
621 | margin-left: 7px;
622 | font-size: 24px;
623 | color: #c6cbd1;
624 | fill: #888;
625 | }svg:not(:root) {
626 | overflow: hidden;
627 | }
628 | footer #footerContent .right {
629 | text-align: right;
630 | padding-top: 10px;
631 | }
632 |
633 | .naver_opensource {
634 | height: 27px;
635 | vertical-align: middle;
636 | }
637 | span.size {
638 | opacity: .6;
639 | }
640 |
--------------------------------------------------------------------------------
/test/unit/visible.spec.js:
--------------------------------------------------------------------------------
1 | import Visible from "../../src/Visible";
2 |
3 | function getWindowInnerHeight() {
4 | return window.innerHeight
5 | || document.documentElement.clientHeight
6 | || document.body.clientHeight;
7 | }
8 |
9 | describe("visible", () => {
10 | let visible,
11 | windowInnerHeight,
12 | itemOffsetHeight;
13 |
14 | beforeEach(() => {
15 | document.body.innerHTML = __html__["test/unit/list.tmpl.html"];
16 | windowInnerHeight = getWindowInnerHeight();
17 | itemOffsetHeight = document.getElementsByClassName("check_visible").item(0).offsetHeight;
18 | visible = new Visible();
19 | });
20 |
21 | afterEach(() => {
22 | visible.destroy();
23 | visible = null;
24 |
25 | document.body.innerHTML = "";
26 | });
27 |
28 | describe("#check", () => {
29 | it("should have correct number of visible item", done => {
30 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
31 |
32 | if (windowInnerHeight % itemOffsetHeight === 0) {
33 | visibleItemLength++;
34 | }
35 |
36 | visible.on("change", e => {
37 | e.visible.should.have.lengthOf(visibleItemLength);
38 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
39 | done();
40 | });
41 |
42 | visible.check();
43 | });
44 |
45 | it("should have correct number of visible item with expanded size", done => {
46 | let visibleItemLength = Math.ceil((windowInnerHeight + (2 * itemOffsetHeight)) / itemOffsetHeight);
47 |
48 | if (windowInnerHeight % itemOffsetHeight === 0) {
49 | visibleItemLength++;
50 | }
51 |
52 | visible.options.expandSize = itemOffsetHeight * 2;
53 |
54 | visible.on("change", e => {
55 | e.visible.should.have.lengthOf(visibleItemLength);
56 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
57 | done();
58 | });
59 |
60 | visible.check();
61 | });
62 |
63 | it("should have correct number of visible item when scroll", done => {
64 | let visibleItems, invisibleItems;
65 |
66 | visible.check();
67 |
68 | visible.on("change", e => {
69 | visibleItems = e.visible;
70 | invisibleItems = e.invisible;
71 | });
72 |
73 | window.scrollTo(0, 250);
74 |
75 | setTimeout(() => {
76 | visible.check();
77 | visibleItems.should.have.length.above(0);
78 | invisibleItems.should.have.length.above(0);
79 | done();
80 | }, 200);
81 | });
82 | });
83 |
84 | describe("#refresh", () => {
85 | it("should have correct number of target item when element appended", () => {
86 | var targetLength = visible._targets.length;
87 |
88 | document.getElementById("contents").insertAdjacentHTML(
89 | "beforeend",
90 | `APPEND
91 | APPEND
92 | APPEND`);
93 |
94 | visible._targets.should.to.have.lengthOf(targetLength + 3);
95 | });
96 | it("should have length that is not updated when element appended with targetSelector", () => {
97 | visible.destroy();
98 | visible = new Visible(document, {
99 | targetSelector: ".check_visible",
100 | });
101 |
102 | const targetLength = visible._targets.length;
103 |
104 | document.getElementById("contents").insertAdjacentHTML(
105 | "beforeend",
106 | `APPEND
107 | APPEND
108 | APPEND`);
109 |
110 | visible._targets.should.to.have.lengthOf(targetLength);
111 | });
112 | });
113 | });
114 |
115 | describe("visible observe in targetContainer", () => {
116 | let visible;
117 |
118 | beforeEach(() => {
119 | document.body.innerHTML = __html__["test/unit/targetContainer.tmpl.html"];
120 | visible = new Visible(document.querySelector("#contents"), {
121 | targetContainer: document.querySelector(".container"),
122 | });
123 | window.scrollTo(0, 0);
124 | });
125 |
126 | afterEach(() => {
127 | visible.destroy();
128 | visible = null;
129 |
130 | document.body.innerHTML = "";
131 | });
132 | it("should have correct number of visible item in targetContainer", done => {
133 | visible.on("change", e => {
134 | // total: 100, document.querySelectorAll(".wrapper").children.length: 97
135 | expect(e.visible.length + e.invisible.length).to.be.equals(97);
136 | done();
137 | });
138 |
139 | visible.observe();
140 | });
141 | });
142 | describe("visible observe", () => {
143 | let visible,
144 | windowInnerHeight,
145 | itemOffsetHeight;
146 |
147 | beforeEach(() => {
148 | document.body.innerHTML = __html__["test/unit/observe.tmpl.html"];
149 | windowInnerHeight = getWindowInnerHeight();
150 | itemOffsetHeight = document.getElementsByClassName("check_visible").item(0).offsetHeight;
151 | visible = new Visible(document.querySelector("#contents"));
152 | window.scrollTo(0, 0);
153 | });
154 |
155 | afterEach(() => {
156 | visible.destroy();
157 | visible = null;
158 |
159 | document.body.innerHTML = "";
160 | });
161 |
162 | describe("#check", () => {
163 | it("should have correct number of visible item", done => {
164 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
165 | if (windowInnerHeight % itemOffsetHeight === 0) {
166 | visibleItemLength++;
167 | }
168 |
169 | visible.on("change", e => {
170 | e.visible.should.have.lengthOf(visibleItemLength);
171 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
172 | e.isTrusted.should.to.be.true;
173 | done();
174 | });
175 |
176 | visible.observe();
177 | expect(visible._observeCallback).to.be.ok;
178 |
179 | });
180 | it("should have correct number of visible item with delay", done => {
181 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
182 | let visibles = [];
183 | let invisibles = [];
184 |
185 | if (windowInnerHeight % itemOffsetHeight === 0) {
186 | visibleItemLength++;
187 | }
188 | visible.observe({delay: 50});
189 | visibles.should.have.lengthOf(0);
190 | invisibles.should.have.lengthOf(0);
191 |
192 | visible.on("change", e => {
193 | visibles = e.visible;
194 | invisibles = e.invisible;
195 |
196 | e.visible.should.have.lengthOf(visibleItemLength);
197 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
198 | done();
199 | });
200 | setTimeout(e => {
201 | visibles.should.have.lengthOf(visibleItemLength);
202 | invisibles.should.have.lengthOf(100 - visibleItemLength);
203 | }, 200);
204 | });
205 | it("should have correct number of visible item with containment", done => {
206 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
207 | visible.on("change", e => {
208 | e.visible.should.have.lengthOf(visibleItemLength);
209 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
210 | done();
211 | });
212 | setTimeout(e => {
213 | visible.observe({containment: true});
214 | });
215 | });
216 | it("should have correct number of visible item with containment2", done => {
217 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
218 |
219 | visibleItemLength--;
220 |
221 | visible.on("change", e => {
222 | e.visible.should.have.lengthOf(visibleItemLength);
223 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
224 | done();
225 | });
226 | document.querySelector("#contents").scrollTop = 10;
227 | setTimeout(e => {
228 | visible.observe({containment: true});
229 | });
230 | });
231 | it("should have correct number of visible item with expanded size", done => {
232 | let visibleItemLength = Math.ceil((windowInnerHeight + (2 * itemOffsetHeight)) / itemOffsetHeight);
233 |
234 | if (windowInnerHeight % itemOffsetHeight === 0) {
235 | visibleItemLength++;
236 | }
237 |
238 | visible.options.expandSize = itemOffsetHeight * 2;
239 |
240 | visible.on("change", e => {
241 | e.visible.should.have.lengthOf(visibleItemLength);
242 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
243 | done();
244 | });
245 |
246 | visible.observe();
247 | });
248 |
249 | it("should have correct number of visible item when scroll", done => {
250 | let visibleItems, invisibleItems;
251 |
252 | visible.observe();
253 | expect(visible._observer).to.be.ok;
254 |
255 | visible.on("change", e => {
256 | e.visible.should.have.length.above(0);
257 | e.invisible.should.have.length.above(0);
258 | e.isTrusted.should.to.be.true;
259 | done();
260 | });
261 |
262 | visible._wrapper.scrollTop = 250;
263 | });
264 | });
265 | });
266 |
267 |
268 | describe("visible observe#event", () => {
269 | let visible,
270 | windowInnerHeight,
271 | itemOffsetHeight;
272 |
273 | beforeEach(() => {
274 | document.body.innerHTML = __html__["test/unit/observe.tmpl.html"];
275 | windowInnerHeight = getWindowInnerHeight();
276 | itemOffsetHeight = document.getElementsByClassName("check_visible").item(0).offsetHeight;
277 | visible = new Visible(document.querySelector("#contents"));
278 | window.scrollTo(0, 0);
279 | });
280 |
281 | afterEach(() => {
282 | visible.destroy();
283 | visible = null;
284 |
285 | document.body.innerHTML = "";
286 | });
287 |
288 | describe("#check", () => {
289 | it("should have correct number of visible item", done => {
290 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
291 | if (windowInnerHeight % itemOffsetHeight === 0) {
292 | visibleItemLength++;
293 | }
294 |
295 | visible.on("change", e => {
296 | e.visible.should.have.lengthOf(visibleItemLength);
297 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
298 | done();
299 | });
300 | setTimeout(e => {
301 | visible._addObserveEvent();
302 | })
303 | });
304 | it("should have correct number of visible item with delay", done => {
305 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
306 | let visibles = [];
307 | let invisibles = [];
308 |
309 | if (windowInnerHeight % itemOffsetHeight === 0) {
310 | visibleItemLength++;
311 | }
312 | visible._addObserveEvent({delay: 50});
313 | visibles.should.have.lengthOf(0);
314 | invisibles.should.have.lengthOf(0);
315 |
316 | visible.on("change", e => {
317 | visibles = e.visible;
318 | invisibles = e.invisible;
319 |
320 | e.visible.should.have.lengthOf(visibleItemLength);
321 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
322 | done();
323 | });
324 | setTimeout(e => {
325 | visibles.should.have.lengthOf(visibleItemLength);
326 | invisibles.should.have.lengthOf(100 - visibleItemLength);
327 | }, 200);
328 | });
329 | it("should have correct number of visible item with containment", done => {
330 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
331 |
332 |
333 | visible.on("change", e => {
334 | e.visible.should.have.lengthOf(visibleItemLength);
335 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
336 | done();
337 | });
338 | setTimeout(e => {
339 | visible._addObserveEvent({containment: true});
340 | });
341 | });
342 | it("should have correct number of visible item with containment2", done => {
343 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
344 |
345 | visibleItemLength--;
346 |
347 | visible.on("change", e => {
348 | e.visible.should.have.lengthOf(visibleItemLength);
349 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
350 | done();
351 | });
352 | document.querySelector("#contents").scrollTop = 10;
353 | setTimeout(e => {
354 | visible._addObserveEvent({ containment: true });
355 | });
356 | });
357 | it("should have correct number of visible item with expanded size", done => {
358 | let visibleItemLength = Math.ceil((windowInnerHeight + (2 * itemOffsetHeight)) / itemOffsetHeight);
359 |
360 | if (windowInnerHeight % itemOffsetHeight === 0) {
361 | visibleItemLength++;
362 | }
363 |
364 | visible.options.expandSize = itemOffsetHeight * 2;
365 |
366 | visible.on("change", e => {
367 | e.visible.should.have.lengthOf(visibleItemLength);
368 | e.invisible.should.have.lengthOf(100 - visibleItemLength);
369 | done();
370 | });
371 |
372 | visible._addObserveEvent();
373 | });
374 |
375 | it("should have correct number of visible item when scroll", done => {
376 | let visibleItems, invisibleItems;
377 |
378 | visible._addObserveEvent();
379 |
380 | visible.on("change", e => {
381 | e.visible.should.have.length.above(0);
382 | e.invisible.should.have.length.above(0);
383 | e.isTrusted.should.to.be.true;
384 | done();
385 | });
386 |
387 | visible._wrapper.scrollTop = 250;
388 |
389 | });
390 | });
391 | });
392 |
393 | describe("iScroll", () => {
394 | let visible,
395 | iScroll;
396 |
397 | beforeEach(() => {
398 | document.body.innerHTML = __html__["test/unit/fixed.tmpl.html"];
399 | visible = new Visible();
400 | iScroll = new IScroll("#contents");
401 | });
402 |
403 | afterEach(() => {
404 | visible.destroy();
405 | visible = null;
406 |
407 | iScroll.destroy();
408 | iScroll = null;
409 |
410 | document.body.innerHTML = "";
411 | });
412 | it("should have correct number of visible item after iscroll", done => {
413 | window.scrollTo(0, 0);
414 | iScroll.scrollTo(0, 0);
415 | visible.check();
416 | visible.on("change", e => {
417 | e.visible.should.to.have.lengthOf(5);
418 | e.invisible.should.to.have.lengthOf(5);
419 | e.isTrusted.should.to.be.true;
420 | done();
421 | });
422 | iScroll.scrollTo(0, -501);
423 |
424 | setTimeout(() => {
425 | visible.check();
426 | }, 200);
427 | });
428 | });
429 |
430 | describe("contaiment visible", () => {
431 | let visible,
432 | contentsEl;
433 |
434 | beforeEach(() => {
435 | document.body.innerHTML = __html__["test/unit/list.tmpl.html"];
436 | contentsEl = document.getElementById("contents");
437 | contentsEl.classList.add("scrollable");
438 |
439 | visible = new Visible(contentsEl);
440 | });
441 |
442 | afterEach(() => {
443 | contentsEl = null;
444 |
445 | visible.destroy();
446 | visible = null;
447 |
448 | document.body.innerHTML = "";
449 | });
450 |
451 | it("should have correct number of visible item", done => {
452 | visible.on("change", e => {
453 | e.visible.should.to.have.lengthOf(5);
454 | e.visible[4].innerText.should.to.equal("#5");
455 | done();
456 | });
457 |
458 | visible.check(true);
459 | });
460 |
461 | it("should have correct number of visible item when scroll", done => {
462 | visible.check(true);
463 |
464 | visible.on("change", e => {
465 | e.visible.should.to.have.lengthOf(2);
466 | e.invisible.should.to.have.lengthOf(2);
467 | done();
468 | });
469 |
470 | contentsEl.scrollTop = 40;
471 |
472 | setTimeout(() => {
473 | visible.check(true);
474 | }, 200);
475 | });
476 | });
477 |
478 | describe("getVisibleElements", () => {
479 | let visible,
480 | windowInnerHeight,
481 | itemOffsetHeight;
482 |
483 | beforeEach(() => {
484 | document.body.innerHTML = __html__["test/unit/list.tmpl.html"];
485 | windowInnerHeight = getWindowInnerHeight();
486 | itemOffsetHeight = document.getElementsByClassName("check_visible").item(0).offsetHeight;
487 |
488 | visible = new Visible();
489 | });
490 |
491 | afterEach(() => {
492 | visible.destroy();
493 | visible = null;
494 |
495 | document.body.innerHTML = "";
496 | });
497 |
498 | it("should have 0 visible elements on init", () => {
499 | expect(visible.getVisibleElements()).to.be.empty;
500 | });
501 |
502 | it("should have same amount of visible elements as 'visible' from the change event", done => {
503 | let visibleItemLength = Math.ceil(windowInnerHeight / itemOffsetHeight);
504 |
505 | if (windowInnerHeight % itemOffsetHeight === 0) {
506 | visibleItemLength++;
507 | }
508 |
509 | visible.on("change", e => {
510 | expect(visible.getVisibleElements().length).to.equal(visibleItemLength);
511 | expect(visible.getVisibleElements().length).to.equal(e.visible.length);
512 | done();
513 | });
514 |
515 | visible.check();
516 | });
517 | });
518 |
--------------------------------------------------------------------------------