UNPKG

cocholate

Version:

Small and fast DOM manipulation library.

477 lines (386 loc) 26.9 kB
<!DOCTYPE HTML> <html> <head> <meta charset="utf8"> <title>Cocholate!</title> </head> <body> <script src="https://cdn.jsdelivr.net/gh/douglascrockford/JSON-js@aef828bfcd7d5efaa41270f831f8d27d5eef3845/json2.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/fpereiro/dale@3199cebc19ec639abf242fd8788481b65c7dc3a3/dale.js"></script> <script src="https://cdn.jsdelivr.net/gh/fpereiro/teishi@31a9cf552dbaee79fb1c2b7d12c6fad20f987983/teishi.js"></script> <script src="https://cdn.jsdelivr.net/gh/fpereiro/lith@206ca67469ff0a8d6dcbc28593b3978e908c6cca/lith.js"></script> <script src="cocholate.js"></script> <script> window.onerror = function () { alert (dale.go (arguments, function (v) {return v + ''}).join (' ')); } window.c.ready (function () { var dale = window.dale; var teishi = window.teishi; var lith = window.lith; var c = window.c; // We override dale.clog to avoid seeing a ton of alerts on old browsers. try { dale.clog = console.log.bind (console); } catch (error) { dale.clog = function () { var output = dale.go (arguments, function (v) {return v === undefined ? 'undefined' : v}).join (' '); if (window.console) window.console.log (output); } } var type = teishi.type, clog = teishi.clog, inc = teishi.inc; var done; var Test = function (fun) { var error = fun (); if (error) { alert (error); throw new Error (error); } setInterval (function () { if (! done) return; done = false; var perf = {dev: 0, prod: 0}; dale.go (dale.times (5), function () { dale.go (['dev', 'prod'], function (mode) { c.prod = mode === 'prod'; var time = teishi.time (); fun (true); perf [mode] += teishi.time () - time; }); }); c.fill ('body', lith.g (['All tests were successful!', ['br'], 'Performance: ', perf.dev, 'ms (dev), ', perf.prod, 'ms (prod)'])); }, 50); }; Test (function (perf) { var counter = {}; window.increment = function (ev) { if (! counter [ev]) counter [ev] = 0; counter [ev]++; } c.fill ('body', lith.g ([ ['input', {id: 'button', type: 'button', onclick: 'increment (event.type)'}], ['input', {id: 'text', type: 'text', onchange: 'increment (event.type)'}], ])); if (c.fire ('#button', /notastring/) !== false) return 'c.fire didn\'t validate its input #1.'; if (c.fire ('#button', 1337) !== false) return 'c.fire didn\'t validate its input #2.'; if (c.fire ('#button') !== false) return 'c.fire didn\'t validate its input #3.'; c.fire ('#button', 'click'); c.fire ('#text', 'change'); if (! teishi.eq (counter, {click: 1, change: 1})) return 'c.fire didn\'t work.'; if (c ('selector', /invalidfun/) !== false) return 'Invalid function was considered valid #1.'; if (c (document.body, /invalidfun/) !== false) return 'Invalid function was considered valid #2.'; if (c ('body') !== document.body) return 'Find could not locate body #1.'; if (c (document.body) !== document.body) return 'When passing DOM element, c is not returning it.'; if (! document.querySelectorAll) { if (c ('#a-b') !== undefined) return 'Valid selector with special characters wasn\'t accepted #1.'; if (c ('#в') !== undefined) return 'Valid selector with special characters wasn\'t accepted #2.'; if (c ('a>b') !== false) return 'Invalid selector with special characters was accepted #1.'; if (c ('a,b') !== false) return 'Invalid selector with special characters was accepted #2.'; if (c ('div[data=b]') !== false) return 'Invalid selector with special characters was accepted #3.'; } c.fill ('body', lith.g ([ ['div', {"class": 'hola'}, 'Content'], ['div', {id: 'hola'}, 'Content'] ])); if (c ('.hola') [0].innerHTML !== 'Content') return 'Class selector didn\'t work.'; if (c ('#hola').innerHTML !== 'Content') return 'Id selector didn\'t work.'; if (c ('div#hola').innerHTML !== 'Content') return 'Id selector didn\'t work #1.'; if (c (c ('div#hola')).innerHTML !== 'Content') return 'Id selector didn\'t work #2.'; if (document.querySelectorAll) { if (type (c ('#hola[name="name"]')) !== 'array') return 'Non id selector starting with # didn\'t work #1.'; if (type (c ('#hola>div')) !== 'array') return 'Non id selector starting with # didn\'t work #2.'; } else { if (c ('#hola[name="name"]') !== false) return 'Non id selector starting with # wasn\'t rejected #1.'; if (c ('#hola>div') !== false) return 'Non id selector starting with # wasn\'t rejected #2.'; } c.fill ('body', lith.g ([ ['div', {id: 'hola'}, [ ['p', {"class": 'a'}, 'Yes.'], ['p', {"class": 'b'}, 'No.'], ]] ])); if (c ('div').length !== 1) return 'c.empty didn\'t work.'; if (c ('p').length !== 2) return 'c.fill didn\'t work.'; if (document.querySelectorAll) { if (c ('#hola p.a').length !== 1) return 'Non-id selector with id first element didn\'t work.'; } else { if (c ({from: c ('#hola'), selector: 'p.a'}).length !== 1) return 'Non-id selector with id first element didn\'t work.'; } if (c.fill ('body', /notastring/) !== false) return 'c.fill accepted invalid HTML #1.'; if (c.fill (document.body, /notastring/) !== false) return 'c.fill accepted invalid HTML #2.'; if (c (['invalid', 'div']) !== false) return 'invalid array selector was accepted.'; if (c ([':or', 'div', 'p']).length !== 3) return 'or selector didn\'t work.'; if (c ([':or', 'div', [':or', 'div', 'p']]).length !== 3) return 'nested or selector didn\'t work.'; if (document.querySelectorAll) { if (c ([':and', 'div *', 'p']).length !== 2) return 'and selector didn\'t work.'; } if (c ([':and', 'div', 'p']).length !== 0) return 'and selector didn\'t work.'; if (c ([':and', [':or', 'div', 'p']]).length !== 3) return 'and selector didn\'t work.'; var all = c ([':not', 'h5']).length; if (c ([':not', 'div#hola']).length !== all - 1) return 'not selector didn\'t work #1.'; if (c ([':not', 'body']).length !== all - 1) return 'not selector didn\'t work #2.'; if (c ([':not', 'div#hola', 'body']).length !== all - 2) return 'not selector didn\'t work #3.'; if (document.querySelectorAll) { if (c ([':not', 'div#hola *']).length !== all - 2) return 'not selector didn\'t work #4.'; } if (c ([':not', 'div', 'p']).length !== all - 3) return 'not selector didn\'t work #5.'; if (c ([':not', [':or', 'div', 'p']]).length !== all - 3) return 'not selector didn\'t work #6.'; if (c ([':and', [':not', 'div#hola'], [':not', 'p']]).length !== all - 3) return 'not selector didn\'t work #7.'; if (document.querySelectorAll) { if (c ([':and', 'body *', [':or', 'div', 'p']]).length !== 3) return 'nested selector didn\'t work #8.'; } else { if (c ([':and', {from: c ('body'), selector: '*'}, [':or', 'div', 'p']]).length !== 3) return 'nested selector didn\'t work #8.'; } c.fill ('body', lith.g ('a')); c.empty ('body'); if (document.body.innerHTML !== '') return 'c.empty didn\'t work.'; c.fill ('body', lith.g ([ ['div', {"class": 'inner'}], ['div', {id: 'container'}, [ ['div', {"class": 'inner foobar'}], ['div', {"class": 'innerTree'}], ['div', {"class": 'foobar inner'}], ]] ])); if (c ({selector: 'div.inner'}) !== false) return 'undefined from selector accepted.'; if (c ({selector: 'div.inner', from: [c ('body')]}) !== false) return 'array from selector accepted.'; if (c ({selector: 'div.inner', from: c ('#container')}).length !== 2) return 'from selector didn\'t work.'; c.fill ('body', lith.g (['div', {id: 'container', style: 'border: solid 1px lime'}, [['div', {id: 'inner', style: 'border: solid 1px blue'}, 'inner'], 'container']])); c.place ('#container', 'beforeBegin', lith.g (['div', {id: 'beforecontainer'}, 'beforecontainer'])); if (c ('div') [0].getAttribute ('id') !== 'beforecontainer') return 'c.place beforeBegin didn\'t work'; c.place ('#container', 'afterEnd', lith.g (['div', {id: 'aftercontainer'}, 'aftercontainer'])); if (c ('div') [c ('div').length - 1].getAttribute ('id') !== 'aftercontainer') return 'c.place afterEnd didn\'t work'; c.place ('#container', 'afterBegin', lith.g (['div', {id: 'beforeinner'}, 'beforeinner'])); if (c ({from: c ('#container'), selector: 'div'}) [0].getAttribute ('id') !== 'beforeinner') return 'c.place afterBegin didn\'t work'; c.place ('#container', 'beforeEnd', lith.g ([['div', {id: 'afterinner'}, 'afterinner'], ['div', {id: 'afterinner2'}, 'afterinner2']])); if (c ({from: c ('#container'), selector: 'div'}) [2].getAttribute ('id') !== 'afterinner') return 'c.place beforeEnd didn\'t work'; if (c ({from: c ('#container'), selector: 'div'}) [3].getAttribute ('id') !== 'afterinner2') return 'c.place beforeEnd didn\'t work'; var cdiv = document.querySelectorAll ? '#container div' : {from: c ('#container'), selector: 'div'}; if (c.set (cdiv, {'^': 'someclass'}) !== false) return 'c.set accepted invalid input'; c.set (cdiv, {"class": 'someclass'}); if (c (cdiv).length !== 3) 'c.set didn\'t work.'; if (c.get (cdiv, 'class') [0] ['class'] !== 'someclass') return 'c.get didn\'t work #1.'; var allAttrs = c.get (cdiv) [0]; // We delete the `contentEditable` property because IE6-7 will add this property that we haven't set ourselves. delete allAttrs.contentEditable; if (! teishi.eq (allAttrs, {id: 'beforeinner', 'class': 'someclass'})) return 'c.get didn\'t bring all attributes.'; var allAttrsNoClass = c.get ('div') [0]; delete allAttrsNoClass.contentEditable; if (! teishi.eq (allAttrsNoClass, {id: 'beforecontainer'})) return 'c.get didn\'t bring all attributes on element with no class.'; c.set (cdiv, {width: '200px', opacity: 0.9}, true); if (c.get (cdiv, ['height', 'width'], true) [0].height !== null) return 'multiple c.get didn\'t work #1.'; if (c.get (cdiv, ['height', 'width'], true) [0].width !== '200px') return 'multiple c.get didn\'t work #2.'; var allcssattrs = c.get (cdiv, undefined, true) [0]; // All major browsers except for Internet Explorer stringify the opacity, so we do that too. if (! teishi.eq ({width: allcssattrs.width, opacity: allcssattrs.opacity + ''}, {width: '200px', opacity: '0.9'})) return 'c.get didn\'t bring all CSS attributes.'; c.fill ('body', lith.g (['div', {id: 'test'}])); // Test that we can pass a DOM element as the target. c.set (c ('#test'), {'class': 'parte'}); if (! teishi.eq (c.get ('#test', 'class'), {'class': 'parte'})) return 'Could not pass a DOM element as target.'; c.fill ('body', lith.g (dale.go (['red', 'blue', 'green'], function (v, k) { return ['p', {id: 'a' + k, "class": v}]; }))); if (document.querySelectorAll) { if (JSON.stringify (c.get ('p', ['id', 'class'])) !== '[{"id":"a0","class":"red"},{"id":"a1","class":"blue"},{"id":"a2","class":"green"}]') return 'c.get didn\'t work #3.'; if (JSON.stringify (c.get ('#a0', ['id', 'class'])) !== '{"id":"a0","class":"red"}') return 'c.get didn\'t work #4.'; if (JSON.stringify (c.get ('p#a0', ['id', 'class'])) !== '{"id":"a0","class":"red"}') return 'c.get didn\'t work #5.'; } else { // IE6-7 use className, FF3 uses class if (JSON.stringify (c.get ('p', ['id', 'className'])) !== '[{"id":"a0","className":"red"},{"id":"a1","className":"blue"},{"id":"a2","className":"green"}]' && JSON.stringify (c.get ('p', ['id', 'class'])) !== '[{"id":"a0","class":"red"},{"id":"a1","class":"blue"},{"id":"a2","class":"green"}]') return 'c.get didn\'t work #3.'; if (JSON.stringify (c.get ('#a0', ['id', 'className'])) !== '{"id":"a0","className":"red"}' && JSON.stringify (c.get ('#a0', ['id', 'class'])) !== '{"id":"a0","class":"red"}') return 'c.get didn\'t work #4.'; if (JSON.stringify (c.get ('p#a0', ['id', 'className'])) !== '{"id":"a0","className":"red"}' && JSON.stringify (c.get ('p#a0', ['id', 'class'])) !== '{"id":"a0","class":"red"}') return 'c.get didn\'t work #5.'; } c.fill ('body', lith.g (dale.go (['red', 'blue', 'green'], function (v) { return ['p', {style: 'color: ' + v}]; }))); // Interestingly enough, Opera 11.1 and below convert CSS colors to hexadecimal, hence the need for the second comparison. if (JSON.stringify (c.get ('p', 'color', true)) !== '[{"color":"red"},{"color":"blue"},{"color":"green"}]' && JSON.stringify (c.get ('p', 'color', true)) !== '[{"color":"#ff0000"},{"color":"#0000ff"},{"color":"#008000"}]') return 'c.get didn\'t work #6.'; c.fill ('body', lith.g (['p'])); var bodyp = document.querySelectorAll ? 'body p' : {from: document.body, selector: 'p'}; c.set (bodyp, {"class": 'someclass'}); if (JSON.stringify (c.get (bodyp, 'class')) !== '[{"class":"someclass"}]') return 'c.set didn\'t work #1.'; c.set (bodyp, {"class": null}); if (JSON.stringify (c.get (bodyp, ['class', 'another'])) !== '[{"class":null,"another":null}]') return 'c.set didn\'t work #2.'; c.set (bodyp, {color: 'red'}, true); if (JSON.stringify (c.get (bodyp, 'color', true)) !== '[{"color":"red"}]' && JSON.stringify (c.get (bodyp, 'color', true)) !== '[{"color":"#ff0000"}]') return 'c.set didn\'t work #3.'; c.set (bodyp, {color: null}, true); if (JSON.stringify (c.get (bodyp, 'color', true)) !== '[{"color":null}]') return 'c.set didn\'t work #4.'; c.fill ('body', lith.g ([ ['input', {'class': 'noclass'}] ])); var result = c.get ('input') [0]; if (result ['class'] !== 'noclass') return 'c.get didn\'t return class properly.'; c.fill ('body', lith.g ([ ['input', {id: 'wha', value: 1}], ['input', {id: 'other'}] ])); // Check that DOM functions can take an invalid selector without throwing an exception. c.empty (/abc/); c.fill (/abc/, 'abc'); c.place (/abc/, 'beforeBegin', 'abc'); c.get (/abc/, 'class'); c.set (/abc/, {'class': null}); var wha, styl, evType; c ('#wha').onchange = function (ev) { wha = this.value; styl = this.style.color; // IE<=8 doesn't seem to pass arguments to event handlers, even when passed explicitly. evType = arguments.length ? ev.type : 'change'; } c.set ('#wha', {value: 2}); if (wha !== '2') return 'onchange wasn\'t fired by c.set #1.'; if (evType !== 'change') return 'onchange event wasn\'t passed by c.set #1.'; c.set ('#wha', {color: 'lime'}, true); if (styl !== 'lime' && styl !== '#00ff00') return 'onchange wasn\'t fired by c.set #2.'; if (evType !== 'change') return 'onchange event wasn\'t passed by c.set #2.'; c.set ('#wha', {value: 3}, false, true); c.set ('#wha', {color: 'magenta'}, true, true); if (wha !== '2') return 'onchange wasn\'t detriggered by c.set.'; if (styl !== 'lime' && styl !== '#00ff00') return 'onchange wasn\'t fired by c.set #3.'; if (evType !== 'change') return 'onchange event wasn\'t passed by c.set #3.'; // Check insertAdjacentElement against empty div. c.fill ('body', lith.g (['div', {id: 'reference'}, ['a']])); c.place ('#reference', 'beforeBegin', 'a'); if (! inc ([ 'a<div id="reference"><a></a></div>', // Compatibility: IE<=8 removes the quotes of id, uppercases the tags and adds \r\n 'a\r\n<DIV id=reference><A></A></DIV>' ], document.body.innerHTML)) return 'insertAdjacentElement beforeBegin generated incorrect HTML: ' + document.body.innerHTML; c.fill ('body', lith.g (['div', {id: 'reference'}, ['a']])); c.place ('#reference', 'afterBegin', 'b'); if (! inc ([ '<div id="reference">b<a></a></div>', // Compatibility: IE<=8 removes the quotes of id and uppercases the tags '<DIV id=reference>b<A></A></DIV>' ], document.body.innerHTML)) return 'insertAdjacentElement afterBegin generated incorrect HTML: ' + document.body.innerHTML; c.fill ('body', lith.g (['div', {id: 'reference'}, ['a']])); c.place ('#reference', 'beforeEnd', 'c'); if (! inc ([ '<div id="reference"><a></a>c</div>', // Compatibility: IE<=8 removes the quotes of id and uppercases the tags '<DIV id=reference><A></A>c</DIV>' ], document.body.innerHTML)) return 'insertAdjacentElement beforeEnd generated incorrect HTML: ' + document.body.innerHTML; c.fill ('body', lith.g (['div', {id: 'reference'}, ['a']])); c.place ('#reference', 'afterEnd', 'd'); if (! inc ([ '<div id="reference"><a></a></div>d', // Compatibility: IE<=8 removes the quotes of id and uppercases the tags '<DIV id=reference><A></A></DIV>d' ], document.body.innerHTML)) return 'insertAdjacentElement afterEnd generated incorrect HTML: ' + document.body.innerHTML; c.fill ('body', ''); c.fill ('body', 'text'); if (c.cookie ('username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC').username !== 'John Doe') return 'c.cookie didn\'t work.'; if (dale.stopNot ([ undefined, {}, 'a', /a/, ['a', function () {}, function () {}], [[]], [['a', function () {}, function () {}], []], [['a', function () {}, function () {}, function () {}]], ], false, c.test) !== false) return 'c.test didn\'t refuse invalid `tests` array.'; var errorThrown; try { c.test ([['a', function (wait) {wait (-1)}, function () {}]]); } catch (err) { errorThrown = true; } if (! errorThrown) return 'c.test didn\'t validate `wait` parameter passed to `next` function #1.'; errorThrown = false; try { c.test ([['b', function (wait) {wait ('foo')}, function () {}]]); } catch (err) { errorThrown = true; } if (! errorThrown) return 'c.test didn\'t validate `wait` parameter passed to `next` function #2.'; if (c.test ([]) !== undefined) return 'c.test didn\'t accept empty `tests` array.'; c.test ([['unfinished', function () {}, function () {return 'Check function was executed synchronously.'}]]); try { c.test ([['Invalid `wait` passed to `next`', function (wait) { wait (null); }, function () {}]]); return 'Invalid `wait` parameter accepted'; } catch (error) {} try { c.test ([['Invalid `ms` passed to `next`', function (wait) { wait (10, -5); }, function () {}]]); return 'Invalid `ms` parameter accepted'; } catch (error) {} if (perf) return; var testObject = {}; c.test ([ ['a1', function () {testObject.a = 0; return true}, function () { return testObject.a === 0; }], ['a2', function () {testObject.b = testObject.a; return true}, function () { return testObject.b === 0; }], ['a3', function () { testObject.c = 0; return testObject.a === testObject.b; }], ['a4', function (next) {testObject.d = 0; next ()}, function () { testObject.t = teishi.time (); return testObject.d === 0; }], ['a5', function (next) {next (10)}, function () { if ((teishi.time () - testObject.t) < 10) return 'c.test didn\'t specify timeout properly.'; return true; }], // On a modern browser, 11ms should be enough to do the check five times; but good-ol' IE6 needs closer to 150ms. ['a6', function (next) {next (150, 2)}, function () { if (! testObject.times) testObject.times = 0; testObject.times++; return testObject.times === 5; }], ['a7', function () { if (testObject.times > 5) return 'next with `ms` did not stop at the first successful check.'; if (c.ajax (/invalidmethod/, /invalidpath/) !== false) return 'c.ajax didn\'t validate its input.'; var jsonajax = c.ajax ('post', 'notaroute', {}, {a: 'b'}, function () {}); if (type (jsonajax) !== 'object' || ! teishi.eq ({headers: jsonajax.headers, body: jsonajax.body}, {headers: {'content-type': 'application/json'}, body: '{"a":"b"}'})) return 'Error when sending a JSON request through c.ajax.'; // We place an `error` variable here so that if the synchronous assertions below the `ajax` call fail, the asynchronous ajax block won't give a thumbs up if it itself ran properly but the synchronous assertions failed. var error; var ajax = c.ajax ('get', 'notaroute', null, null, function () { if (error) return; // From now on, we need to throw since the `check` function of `c.test` must be synchronous and we're in an asynchronous function. c.loadScript ('cocholate.js', function () { if (arguments [1] && type (arguments [1].headers) !== 'object') throw new Error ('Empty headers are not processed correctly'); // We ignore CORS errors when running the test locally. if (! (arguments [0] && arguments [0].status === 0)) { if (arguments.length !== 2) throw new Error ('Script was not loaded.'); } c.loadScript ('nosuchscript.js', function () { if (arguments.length !== 1) throw new Error ('Invalid script was loaded.'); if (c.loadScript (false, function () {}) !== false) throw new Error ('Invalid invocation to c.ajax wasn\'t returned by c.loadScript.'); // In IE6, repeating the call to `nosuchscript.js` generates an alert with a warning about a missing semicolon (perhaps because the script is attempted to be loaded twice) - by changing the name to something else, the warning is avoided. var result = c.loadScript ('nosuchscript2.js'); if (type (result) !== 'object') throw new Error ('c.loadScript didn\'t return the request object returned by c.ajax.'); setTimeout (function () { if (! error) done = true; }, 200); }); }); }); if (type (ajax) !== 'object' || type (ajax.headers) !== 'object' || type (ajax.body) !== 'string' || type (ajax.xhr) !== 'object') { error = true; return 'c.ajax didn\'t return a valid object.'; } return true; }] ]); }); }); </script> </body> </html>