├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bower.json
├── package.json
├── script
└── test
├── src
└── index.js
└── tests
├── qunit.html
├── remove.js
├── runner.coffee
└── tests.js
/.travis.yml:
--------------------------------------------------------------------------------
1 | script: script/test
2 | language: node_js
3 | node_js:
4 | - '0.10'
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.2.0
4 |
5 | * Rewrite history for [yola/classlist-polyfill][], branching and rebasing
6 | from [eligrey/classList][].
7 | * Update fork
8 | * Update package.json to use Unlicense [eligrey#56][]
9 | * Fixes add/remove/toggle in IE10 and IE11 [eligrey#57][]
10 | * IE8 fixes [eligrey#43][]
11 |
12 | [yola/classlist-polyfill]: https://github.com/yola/classlist-polyfill
13 | [eligrey/classList]: https://github.com/eligrey/classList.js
14 | [eligrey#57]: https://github.com/eligrey/classList.js/pull/57
15 | [eligrey#56]: https://github.com/eligrey/classList.js/pull/56
16 | [eligrey#43]: https://github.com/eligrey/classList.js/pull/43
17 |
18 |
19 | ## 1.0.3
20 |
21 | * Add support for missing SVGElement.classList in IE
22 |
23 |
24 | ## 1.0.2
25 |
26 | * Fix issue with `self` not being defined in CommonJS
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # classlist-polyfill
2 |
3 | Polyfill for [`element.classList`][docs].
4 |
5 | This is a published fork of [classList.js][].
6 |
7 | [classList.js]:https://github.com/eligrey/classList.js
8 | [docs]: https://developer.mozilla.org/en/DOM/element.classList
9 |
10 |
11 | ## Installation
12 |
13 | Download using [NPM](https://www.npmjs.com/package/classlist-polyfill):
14 |
15 | ```shell
16 | npm install classlist-polyfill
17 | ```
18 |
19 | Download using [Bower](http://bower.io/):
20 |
21 | ```shell
22 | bower install classlist-polyfill
23 | ```
24 |
25 |
26 | ## What is the purpose of this repo?
27 |
28 | The upstream maintainer has decided [not to publish][comment].
29 |
30 | [comment]: https://github.com/eligrey/classList.js/pull/46#issuecomment-189782600
31 |
32 |
33 | ## Contributing
34 |
35 | Preferably all changes are made upstream.
36 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "classlist-polyfill",
3 | "description": "MDN's ClassList Polyfill",
4 | "main": "src/index.js",
5 | "authors": [
6 | "Eli Grey ",
7 | "Yola Engineering (https://www.yola.com/)"
8 | ],
9 | "license": "Unlicense",
10 | "keywords": [
11 | "classList",
12 | "polyfill",
13 | "shim",
14 | "cross-browser"
15 | ],
16 | "homepage": "https://github.com/yola/classlist-polyfill",
17 | "ignore": [
18 | "**/.*",
19 | "node_modules",
20 | "bower_components",
21 | "test",
22 | "tests"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "classlist-polyfill",
3 | "version": "1.2.0",
4 | "description": "Cross-browser JavaScript shim that fully implements element.classList (referenced on MDN)",
5 | "main": "src/index.js",
6 | "directories": {
7 | "test": "tests"
8 | },
9 | "scripts": {
10 | "test": "bash ./script/test"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/yola/classlist-polyfill.git"
15 | },
16 | "keywords": [
17 | "classList",
18 | "polyfill",
19 | "shim",
20 | "cross-browser"
21 | ],
22 | "author": "Eli Grey ",
23 | "contributors": [
24 | "Eli Grey ",
25 | "Yola Engineering (https://www.yola.com/)"
26 | ],
27 | "license": "Unlicense",
28 | "bugs": {
29 | "url": "https://github.com/eligrey/classList.js/issues"
30 | },
31 | "homepage": "https://github.com/yola/classlist-polyfill"
32 | }
33 |
--------------------------------------------------------------------------------
/script/test:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | if [ -t 1 ]; then
5 | red="$(printf "\033[31m")"
6 | brightred="$(printf "\033[31;1m")"
7 | green="$(printf "\033[32m")"
8 | reset="$(printf "\033[m")"
9 | else
10 | red=
11 | brightred=
12 | green=
13 | reset=
14 | fi
15 |
16 | phantomjs tests/runner.coffee tests/qunit.html | sed -E "
17 | # failure line:
18 | s/^(✘.+)/${red}\\1${reset}/
19 | # failure details:
20 | s/^( .+)/${brightred}\\1${reset}/
21 | # success marker:
22 | s/(✔︎)/${green}\\1${reset}/
23 | "
24 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * classList.js: Cross-browser full element.classList implementation.
3 | * 1.1.20170427
4 | *
5 | * By Eli Grey, http://eligrey.com
6 | * License: Dedicated to the public domain.
7 | * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
8 | */
9 |
10 | /*global self, document, DOMException */
11 |
12 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
13 |
14 | if ("document" in window.self) {
15 |
16 | // Full polyfill for browsers with no classList support
17 | // Including IE < Edge missing SVGElement.classList
18 | if (!("classList" in document.createElement("_"))
19 | || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))) {
20 |
21 | (function (view) {
22 |
23 | "use strict";
24 |
25 | if (!('Element' in view)) return;
26 |
27 | var
28 | classListProp = "classList"
29 | , protoProp = "prototype"
30 | , elemCtrProto = view.Element[protoProp]
31 | , objCtr = Object
32 | , strTrim = String[protoProp].trim || function () {
33 | return this.replace(/^\s+|\s+$/g, "");
34 | }
35 | , arrIndexOf = Array[protoProp].indexOf || function (item) {
36 | var
37 | i = 0
38 | , len = this.length
39 | ;
40 | for (; i < len; i++) {
41 | if (i in this && this[i] === item) {
42 | return i;
43 | }
44 | }
45 | return -1;
46 | }
47 | // Vendors: please allow content code to instantiate DOMExceptions
48 | , DOMEx = function (type, message) {
49 | this.name = type;
50 | this.code = DOMException[type];
51 | this.message = message;
52 | }
53 | , checkTokenAndGetIndex = function (classList, token) {
54 | if (token === "") {
55 | throw new DOMEx(
56 | "SYNTAX_ERR"
57 | , "An invalid or illegal string was specified"
58 | );
59 | }
60 | if (/\s/.test(token)) {
61 | throw new DOMEx(
62 | "INVALID_CHARACTER_ERR"
63 | , "String contains an invalid character"
64 | );
65 | }
66 | return arrIndexOf.call(classList, token);
67 | }
68 | , ClassList = function (elem) {
69 | var
70 | trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
71 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
72 | , i = 0
73 | , len = classes.length
74 | ;
75 | for (; i < len; i++) {
76 | this.push(classes[i]);
77 | }
78 | this._updateClassName = function () {
79 | elem.setAttribute("class", this.toString());
80 | };
81 | }
82 | , classListProto = ClassList[protoProp] = []
83 | , classListGetter = function () {
84 | return new ClassList(this);
85 | }
86 | ;
87 | // Most DOMException implementations don't allow calling DOMException's toString()
88 | // on non-DOMExceptions. Error's toString() is sufficient here.
89 | DOMEx[protoProp] = Error[protoProp];
90 | classListProto.item = function (i) {
91 | return this[i] || null;
92 | };
93 | classListProto.contains = function (token) {
94 | token += "";
95 | return checkTokenAndGetIndex(this, token) !== -1;
96 | };
97 | classListProto.add = function () {
98 | var
99 | tokens = arguments
100 | , i = 0
101 | , l = tokens.length
102 | , token
103 | , updated = false
104 | ;
105 | do {
106 | token = tokens[i] + "";
107 | if (checkTokenAndGetIndex(this, token) === -1) {
108 | this.push(token);
109 | updated = true;
110 | }
111 | }
112 | while (++i < l);
113 |
114 | if (updated) {
115 | this._updateClassName();
116 | }
117 | };
118 | classListProto.remove = function () {
119 | var
120 | tokens = arguments
121 | , i = 0
122 | , l = tokens.length
123 | , token
124 | , updated = false
125 | , index
126 | ;
127 | do {
128 | token = tokens[i] + "";
129 | index = checkTokenAndGetIndex(this, token);
130 | while (index !== -1) {
131 | this.splice(index, 1);
132 | updated = true;
133 | index = checkTokenAndGetIndex(this, token);
134 | }
135 | }
136 | while (++i < l);
137 |
138 | if (updated) {
139 | this._updateClassName();
140 | }
141 | };
142 | classListProto.toggle = function (token, force) {
143 | token += "";
144 |
145 | var
146 | result = this.contains(token)
147 | , method = result ?
148 | force !== true && "remove"
149 | :
150 | force !== false && "add"
151 | ;
152 |
153 | if (method) {
154 | this[method](token);
155 | }
156 |
157 | if (force === true || force === false) {
158 | return force;
159 | } else {
160 | return !result;
161 | }
162 | };
163 | classListProto.toString = function () {
164 | return this.join(" ");
165 | };
166 |
167 | if (objCtr.defineProperty) {
168 | var classListPropDesc = {
169 | get: classListGetter
170 | , enumerable: true
171 | , configurable: true
172 | };
173 | try {
174 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
175 | } catch (ex) { // IE 8 doesn't support enumerable:true
176 | // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36
177 | // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected
178 | if (ex.number === undefined || ex.number === -0x7FF5EC54) {
179 | classListPropDesc.enumerable = false;
180 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
181 | }
182 | }
183 | } else if (objCtr[protoProp].__defineGetter__) {
184 | elemCtrProto.__defineGetter__(classListProp, classListGetter);
185 | }
186 |
187 | }(window.self));
188 |
189 | }
190 |
191 | // There is full or partial native classList support, so just check if we need
192 | // to normalize the add/remove and toggle APIs.
193 |
194 | (function () {
195 | "use strict";
196 |
197 | var testElement = document.createElement("_");
198 |
199 | testElement.classList.add("c1", "c2");
200 |
201 | // Polyfill for IE 10/11 and Firefox <26, where classList.add and
202 | // classList.remove exist but support only one argument at a time.
203 | if (!testElement.classList.contains("c2")) {
204 | var createMethod = function(method) {
205 | var original = DOMTokenList.prototype[method];
206 |
207 | DOMTokenList.prototype[method] = function(token) {
208 | var i, len = arguments.length;
209 |
210 | for (i = 0; i < len; i++) {
211 | token = arguments[i];
212 | original.call(this, token);
213 | }
214 | };
215 | };
216 | createMethod('add');
217 | createMethod('remove');
218 | }
219 |
220 | testElement.classList.toggle("c3", false);
221 |
222 | // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
223 | // support the second argument.
224 | if (testElement.classList.contains("c3")) {
225 | var _toggle = DOMTokenList.prototype.toggle;
226 |
227 | DOMTokenList.prototype.toggle = function(token, force) {
228 | if (1 in arguments && !this.contains(token) === !force) {
229 | return force;
230 | } else {
231 | return _toggle.call(this, token);
232 | }
233 | };
234 |
235 | }
236 |
237 | testElement = null;
238 | }());
239 |
240 | }
241 |
--------------------------------------------------------------------------------
/tests/qunit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | QUnit Tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/tests/remove.js:
--------------------------------------------------------------------------------
1 | QUnit.module("classList.remove");
2 |
3 | QUnit.test("Removes duplicated instances of class", function(assert) {
4 | var el = document.createElement("p"), cList = el.classList;
5 | el.className = "ho ho ho"
6 |
7 | cList.remove("ho");
8 | assert.ok(!cList.contains("ho"), "Should remove all instances of 'ho'");
9 | assert.strictEqual(el.className, "")
10 | });
11 |
--------------------------------------------------------------------------------
/tests/runner.coffee:
--------------------------------------------------------------------------------
1 | urls = require('system').args.slice(1)
2 | page = require('webpage').create()
3 | timeout = 3000
4 |
5 | qunitHooks = ->
6 | window.document.addEventListener 'DOMContentLoaded', ->
7 | for callback in ['log', 'testDone', 'done']
8 | do (callback) ->
9 | QUnit[callback] (result) ->
10 | window.callPhantom
11 | name: "QUnit.#{callback}"
12 | data: result
13 |
14 | page.onInitialized = -> page.evaluate qunitHooks
15 |
16 | page.onConsoleMessage = (msg) -> console.log msg
17 |
18 | page.onCallback = (event) ->
19 | if event.name is 'QUnit.log'
20 | details = event.data
21 | if details.result is false
22 | console.log "✘ #{details.module}: #{details.name}"
23 | if details.message and details.message isnt "failed"
24 | console.log " #{details.message}"
25 | if "actual" of details
26 | console.log " expected: #{details.expected}"
27 | console.log " actual: #{details.actual}"
28 | else if event.name is 'QUnit.testDone'
29 | result = event.data
30 | unless result.failed
31 | console.log "✔︎ #{result.module}: #{result.name}"
32 | else if event.name is 'QUnit.done'
33 | res = event.data
34 | console.log "#{res.total} tests, #{res.failed} failed. Done in #{res.runtime} ms"
35 | phantom.exit if !res.total or res.failed then 1 else 0
36 |
37 | for url in urls
38 | page.open url, (status) ->
39 | if status isnt 'success'
40 | console.error "failed opening #{url}: #{status}"
41 | phantom.exit 1
42 | else
43 | setTimeout ->
44 | console.error "ERROR: Test execution has timed out"
45 | phantom.exit 1
46 | , timeout
47 |
--------------------------------------------------------------------------------
/tests/tests.js:
--------------------------------------------------------------------------------
1 | QUnit.module("classList.toggle");
2 |
3 | QUnit.test("Adds a class", function(assert) {
4 | var cList = document.createElement("p").classList;
5 |
6 | cList.toggle("c1");
7 | assert.ok(cList.contains("c1"), "Adds a class that is not present");
8 |
9 | assert.strictEqual(
10 | cList.toggle("c2"),
11 | true,
12 | "Returns true when class is added"
13 | );
14 | });
15 |
16 | QUnit.test("Removes a class", function(assert) {
17 | var cList = document.createElement("p").classList;
18 |
19 | cList.add("c1");
20 | cList.toggle("c1");
21 | assert.ok(!cList.contains("c1"), "Removes a class that is present");
22 |
23 | cList.add("c2");
24 | assert.strictEqual(
25 | cList.toggle("c2"),
26 | false,
27 | "Return false when class is removed"
28 | );
29 | });
30 |
31 | QUnit.test("Adds class with second argument", function(assert) {
32 | var cList = document.createElement("p").classList;
33 |
34 | cList.toggle("c1", true);
35 | assert.ok(cList.contains("c1"), "Adds a class");
36 |
37 | assert.strictEqual(
38 | cList.toggle("c2", true),
39 | true,
40 | "Returns true when class is added"
41 | );
42 |
43 | cList.add("c3");
44 | cList.toggle("c3", true);
45 | assert.ok(
46 | cList.contains("c3"),
47 | "Does not remove a class that is already present"
48 | );
49 |
50 | cList.add("c4");
51 | assert.strictEqual(
52 | cList.toggle("c4", true),
53 | true,
54 | "Returns true when class is already present"
55 | );
56 | });
57 |
58 | QUnit.test("Removes class with second argument", function(assert) {
59 | var cList = document.createElement("p").classList;
60 |
61 | cList.add("c1");
62 | cList.toggle("c1", false);
63 | assert.ok(!cList.contains("c1"), "Removes a class");
64 |
65 | assert.strictEqual(
66 | cList.toggle("c2", false),
67 | false,
68 | "Returns false when class is removed"
69 | );
70 |
71 | cList.toggle("c3", false);
72 | assert.ok(
73 | !cList.contains("c3"),
74 | "Does not add a class that is not present"
75 | );
76 |
77 | assert.strictEqual(
78 | cList.toggle("c4", false),
79 | false,
80 | "Returns false when class was not present"
81 | );
82 | });
83 |
--------------------------------------------------------------------------------