├── test ├── functional │ ├── views │ │ ├── require │ │ │ ├── layout.jade │ │ │ ├── min.jade │ │ │ └── index.jade │ │ └── index.jade │ ├── public │ │ ├── require │ │ │ ├── min.js │ │ │ ├── app.js │ │ │ └── require.js │ │ └── ajax-test.js │ ├── package.json │ ├── require.js │ └── ajax.js ├── index.test.js ├── alias_test.html ├── net_test.js ├── net_test.html ├── alias_test.js ├── plugins_test.html ├── anim_test.html ├── benchmarks │ ├── dom.html │ └── dom.js ├── anim_test_no_riot.html ├── events_test.html ├── touch_test.html ├── anim_test.js ├── min-test.html ├── ready_test.html ├── promise_test.js ├── plugins_test.js ├── modularity_test.html ├── run.html ├── dom_test.html ├── oo_test.js ├── events_test.js ├── enumerable_test.js └── dom_test.js ├── .gitignore ├── .gitmodules ├── turing.js ├── Makefile ├── turing.functional.js ├── package.json ├── turing.html ├── docs └── intro.md ├── lib └── index.js ├── LICENSE.txt ├── turing.plugins.js ├── README.textile ├── turing.promise.js ├── turing.oo.js ├── turing.touch.js ├── turing.core.js ├── turing.require.js ├── turing.net.js ├── turing.events.js ├── turing.enumerable.js ├── turing.anim.js └── turing.dom.js /test/functional/views/require/layout.jade: -------------------------------------------------------------------------------- 1 | != body 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *.DS_Store 4 | *.pid 5 | tmp/ 6 | node_modules/ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/turing-test"] 2 | path = test/turing-test 3 | url = git@github.com:alexyoung/turing-test.js.git 4 | -------------------------------------------------------------------------------- /test/functional/public/require/min.js: -------------------------------------------------------------------------------- 1 | require(['build/turing.min'], function($) { 2 | $('#results').html('Turing has loaded with the DOM module.'); 3 | }); 4 | -------------------------------------------------------------------------------- /test/functional/views/require/min.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html(lang="en") 3 | head 4 | title Turing Test Minified 5 | script(src="/require/require.js") 6 | script(src="/require/min.js") 7 | body 8 | #results Loading... 9 | -------------------------------------------------------------------------------- /test/functional/public/require/app.js: -------------------------------------------------------------------------------- 1 | require(['turing.anim'], function(anim) { 2 | turing('#results').html('Turing has loaded with the DOM module.'); 3 | turing.anim.chain(turing('#animate')[0]).move(1000, { x: '100px', y: '100px', easing: 'ease-in-out' }); 4 | }); 5 | -------------------------------------------------------------------------------- /test/functional/views/require/index.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html(lang="en") 3 | head 4 | title Turing Test 5 | script(src="/require/require.js") 6 | script(src="/require/app.js") 7 | body 8 | #results Loading... 9 | #animate(style="position: absolute") Animate 10 | -------------------------------------------------------------------------------- /test/functional/views/index.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html 3 | link(rel="stylesheet", href="/turing-test/stylesheets/screen.css") 4 | script(src="/turing.js") 5 | script(src="/turing-test.js") 6 | script(src="/ajax-test.js") 7 | title Ajax Tests 8 | body 9 | h1 Ajax Tests 10 | #results 11 | -------------------------------------------------------------------------------- /turing.js: -------------------------------------------------------------------------------- 1 | define('turing', ['turing.core', 'turing.dom', 'turing.anim', 'turing.enumerable', 'turing.events', 'turing.functional', 'turing.net', 'turing.oo', 'turing.plugins', 'turing.promise', 'turing.require', 'turing.touch'], function(core, dom, anim, enumerable, events, functional, net, oo, plugins, promise, require, touch) { 2 | return core; 3 | }); 4 | -------------------------------------------------------------------------------- /test/functional/package.json: -------------------------------------------------------------------------------- 1 | { "name": "turing-net-tests" 2 | , "description": "Turing's net module tests" 3 | , "version": "0.0.1" 4 | , "author": "Alex R. Young " 5 | , "engines": ["node >= 0.4.7"] 6 | , "main": "./ajax.js" 7 | , "dependencies": { 8 | "express": "2.4.5" 9 | , "jade": "0.12.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC = turing.core.js turing.oo.js turing.enumerable.js turing.promise.js turing.functional.js turing.dom.js turing.plugins.js turing.events.js turing.net.js turing.touch.js turing.anim.js turing.require.js 2 | 3 | docs: 4 | ./node_modules/.bin/dox --title Turing turing.*.js --intro docs/intro.md > docs/index.html 5 | 6 | lint: 7 | ./node_modules/.bin/jslint --onevar false *.js 8 | 9 | build: $(SRC) 10 | cat $^ > build/turing.js 11 | 12 | min: build 13 | ./node_modules/.bin/uglifyjs --no-mangle build/turing.js > build/turing.min.js 14 | 15 | .PHONY: lint docs build 16 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | , turing = require(__dirname + '/../lib/index.js'); 3 | 4 | assert.ok(turing.Promise); 5 | 6 | var sum = 0; 7 | turing.enumerable.each([1, 2, 3], function(i) { 8 | sum += i; 9 | }); 10 | assert.equal(6, sum); 11 | 12 | turing.browser('

JSDOM\'s Homepage

', function(turing) { 13 | var triggered = 0; 14 | assert.equal(1, turing('p').length); 15 | turing('a').bind('click', function() { 16 | triggered++; 17 | }); 18 | turing.events.fire(turing('a')[0], 'click'); 19 | assert.equal(1, triggered); 20 | }); 21 | -------------------------------------------------------------------------------- /test/functional/require.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , app = express.createServer(); 3 | 4 | app.configure('development', function() { 5 | app.use(express.static(__dirname + '/../../../turing.js')); 6 | app.use(express.static(__dirname + '/public')); 7 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 8 | }); 9 | 10 | app.get('/', function(req, res) { 11 | res.render('require/index.jade', { layout: 'require/layout' }); 12 | }); 13 | 14 | app.get('/min', function(req, res) { 15 | res.render('require/min.jade', { layout: 'require/layout' }); 16 | }); 17 | 18 | app.listen(3000); 19 | -------------------------------------------------------------------------------- /turing.functional.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Functional 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Turing Functional helpers. 9 | */ 10 | define('turing.functional', ['turing.core'], function(turing) { 11 | turing.functional = { 12 | curry: turing.bind, 13 | 14 | memoize: function(memo, fn) { 15 | var wrapper = function(n) { 16 | var result = memo[n]; 17 | if (typeof result !== 'number') { 18 | result = fn(wrapper, n); 19 | memo[n] = result; 20 | } 21 | return result; 22 | }; 23 | return wrapper; 24 | } 25 | }; 26 | return turing; 27 | }); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name": "turing" 2 | , "description": "A library for enumeration, functional programming, promises, and more" 3 | , "version": "0.0.74" 4 | , "url": "http://turingjs.com/" 5 | , "author": "Alex R. Young " 6 | , "engines": ["node >= 0.4.0"] 7 | , "main": "./lib/index.js" 8 | , "dependencies": { 9 | "jsdom": "0.2.x" 10 | } 11 | , "devDependencies": { 12 | "dox": "latest" 13 | , "jake": "latest" 14 | , "jsmin": "latest" 15 | , "jslint": "latest" 16 | , "uglify-js": "latest" 17 | , "benchmark": "latest" 18 | } 19 | , "repository": { 20 | "type" : "git" 21 | , "url" : "https://github.com/alexyoung/turing.js.git" 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /test/alias_test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Alias Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/net_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | var test = require('test'), 7 | assert = require('assert'); 8 | 9 | require('../turing.net.js'); 10 | 11 | exports.testNet = { 12 | 'test request creation': function() { 13 | var response, 14 | request = turing.net.get('/test', { 15 | 'error': function(r) { response = r; } 16 | }); 17 | 18 | assert.ok(request.readyState); 19 | }, 20 | 21 | 'test jsonp creation': function() { 22 | turing.net.jsonp('http://feeds.delicious.com/v1/json/alex_young/javascript?callback={callback}', { success: function(json) { }}); 23 | } 24 | }; 25 | 26 | test.run(exports); 27 | -------------------------------------------------------------------------------- /test/net_test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Net Test 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /turing.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | DailyJS's Framework: turing.js Playground 7 | 8 | 15 | 16 | 17 |

Turing

18 |

Open your browser's debug console and play around with turing()

19 |

The $t() alias is also available.

20 |

Elements

21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /test/alias_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | t = require('../turing.core.js'); 5 | 6 | /** 7 | * This is used by Node because of the differences in global handling 8 | * between browsers and CommonJS modules. 9 | */ 10 | function correctContext() { 11 | if (typeof turing === 'undefined') { 12 | turing = t.turing; 13 | $t = t.$t; 14 | } 15 | } 16 | 17 | var test = require('test'), 18 | assert = require('assert'); 19 | 20 | exports.testAlias = { 21 | 'test turing is present': function() { 22 | correctContext(); 23 | assert.ok(turing.VERSION, 'turing.core should have loaded'); 24 | }, 25 | 26 | 'test alias exists': function() { 27 | correctContext(); 28 | assert.ok($t.VERSION, 'the $t alias should be available'); 29 | } 30 | }; 31 | 32 | test.run(exports); 33 | -------------------------------------------------------------------------------- /test/plugins_test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Plugins Test 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
turn me red
17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/anim_test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Animation Test 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/intro.md: -------------------------------------------------------------------------------- 1 | Turing is a JavaScript framework. The source is available from GitHub at [alexyoung/turing.js](https://github.com/alexyoung/turing.js/). 2 | 3 | The DOM, Events, and Anim modules are chainable, like this: 4 | 5 | turing('p') 6 | .fadeIn(2000) 7 | .animate(1000, { color: '#ff0000' }) 8 | .click(function() { alert('clicked'); }); 9 | 10 | This is the easiest way to use Turing. 11 | 12 | The DOM results can be manipulated with the `turing.enumerable` module methods: 13 | 14 | turing('p') 15 | .map(function(element) { return element.innerHTML; }) 16 | .values(); 17 | 18 | The `values` method returns the results of the last operation, in this case each paragraph's `innerHTML`. 19 | 20 | Arrays or objects can be iterated using `turing()`, too: 21 | 22 | turing([1, 2, 3]) 23 | .map(function(n) { return n + 1; }) 24 | .values(); 25 | 26 | // => [2, 3, 4] 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/benchmarks/dom.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Turing: DOM Benchmarks 7 | 8 | 9 | 10 | 11 | 12 |

Turing DOM Benchmarks

13 |

Please wait...

14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var turing = require(__dirname + '/../turing.core.js').turing, 2 | jsdom = require('jsdom'), 3 | fs = require('fs'); 4 | 5 | require(__dirname + '/../turing.promise.js')(turing); 6 | require(__dirname + '/../turing.enumerable.js')(turing); 7 | 8 | module.exports = turing; 9 | module.exports.jsdom = jsdom; 10 | module.exports.browser = function(html, fn) { 11 | var js = '', 12 | files = ('turing.core.js turing.oo.js turing.enumerable.js turing.promise.js ' 13 | + 'turing.functional.js turing.dom.js turing.plugins.js turing.events.js ' 14 | + 'turing.net.js turing.touch.js turing.anim.js').split(' '); 15 | 16 | files.forEach(function(file) { 17 | js += fs.readFileSync(__dirname + '/../' + file); 18 | }); 19 | 20 | // The JavaScript in as an array seems to make `done` fire twice 21 | jsdom.env({ 22 | html: html, 23 | src: [ js ], 24 | done: function(err, window) { 25 | if (err) throw(err); 26 | fn(window.turing); 27 | } 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /test/anim_test_no_riot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Riot 5 | 11 | 12 | 27 | 28 | 29 |
30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/events_test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | DOM Test 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

Text

19 |

Example Link

20 |

Example Link 2

21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010-2011 by Alex R. Young 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /test/touch_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Turing Touch 5 | 10 | 11 | 12 | 13 | 14 | 25 | 26 | 27 |
28 |
29 |

TAP ME

30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test/anim_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | var test = require('test'), 7 | assert = require('assert'); 8 | 9 | require('../turing.enumerable.js'); 10 | require('../turing.anim.js'); 11 | 12 | exports.testAnimations = { 13 | 'test hex colour converts to RGB': function() { 14 | assert.equal('rgb(255, 0, 255)', turing.anim.parseColour('#ff00ff').toString()); 15 | }, 16 | 17 | 'test RGB colours are left alone': function() { 18 | assert.equal('rgb(255, 255, 255)', turing.anim.parseColour('rgb(255, 255, 255)').toString()); 19 | }, 20 | 21 | 'test chained animations': function() { 22 | var box = document.getElementById('box'); 23 | turing.anim.chain(box) 24 | .highlight() 25 | .pause(250) 26 | .move(100, { x: '100px', y: '100px', easing: 'ease-in-out' }) 27 | .animate(250, { width: '1000px' }) 28 | .fadeOut(250) 29 | .pause(250) 30 | .fadeIn(250) 31 | .animate(250, { width: '20px' }); 32 | 33 | setTimeout(function() { assert.equal(box.style.width, '20px'); }, 2000); 34 | } 35 | }; 36 | 37 | test.run(exports); 38 | 39 | -------------------------------------------------------------------------------- /test/min-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Turing 5 | 10 | 11 | 28 | 29 | 30 |
31 |
32 |

NOT READY

33 |

NOT READY

34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/ready_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Turing 5 | 10 | 11 | 12 | 13 | 30 | 31 | 32 |
33 |
34 |

NOT READY

35 |

NOT READY

36 |
37 |
38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/benchmarks/dom.js: -------------------------------------------------------------------------------- 1 | var suite = new Benchmark.Suite, 2 | div = $t('#test-div')[0], 3 | cache = {}; 4 | 5 | function log(text) { 6 | $t('#results').append('
  • ' + text + '
  • '); 7 | } 8 | 9 | function hasClassRegExp(element, className) { 10 | if (element.className && element.className.length) { 11 | return new RegExp('(^|\\s)' + className + '($|\\s)').test(element.className); 12 | } else { 13 | return false; 14 | } 15 | }; 16 | 17 | function hasClassCachedRegExp(element, className) { 18 | if (!cache[className]) { 19 | cache[className] = new RegExp('(^|\\s)' + className + '($|\\s)'); 20 | } 21 | if (element.className && element.className.length) { 22 | return cache[className].test(element.className); 23 | } else { 24 | return false; 25 | } 26 | }; 27 | 28 | suite.add('hasClassRegExp', function() { 29 | hasClassRegExp(div, 'example1'); 30 | hasClassRegExp(div, 'unknown'); 31 | }) 32 | .add('hasClassCachedRegExp', function() { 33 | hasClassCachedRegExp(div, 'example1'); 34 | hasClassCachedRegExp(div, 'unknown'); 35 | }) 36 | .add('built-in', function() { 37 | turing.dom.hasClass(div, 'example1'); 38 | turing.dom.hasClass(div, 'unknown'); 39 | }) 40 | .on('cycle', function(event, bench) { 41 | log(String(bench)); 42 | }) 43 | .on('complete', function() { 44 | log('Fastest is ' + this.filter('fastest').pluck('name')); 45 | $t('#notice').text('Done'); 46 | }) 47 | .run(true); 48 | -------------------------------------------------------------------------------- /test/promise_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | require('../turing.promise.js'); 7 | 8 | var test = require('test'), 9 | assert = require('assert'); 10 | 11 | exports.testPromise = { 12 | 'test then': function() { 13 | function delay(ms) { 14 | var p = new turing.Promise(); 15 | setTimeout(p.resolve, ms); 16 | return p; 17 | } 18 | 19 | function timeout(duration, limit) { 20 | var p = new turing.Promise(); 21 | setTimeout(p.resolve, duration); 22 | setTimeout(p.reject, limit); 23 | return p; 24 | } 25 | 26 | delay(1000).then(function() { 27 | assert.ok('Delay completed'); 28 | }); 29 | 30 | timeout(10, 100).then( 31 | function() { 32 | assert.ok('10ms is under 100ms'); 33 | }, 34 | function() { 35 | assert.fail(); 36 | } 37 | ); 38 | 39 | timeout(100, 10).then( 40 | function() { 41 | assert.fail(); 42 | }, 43 | function() { 44 | assert.ok('100ms is over 10ms'); 45 | } 46 | ); 47 | }, 48 | 49 | 'test chain': function() { 50 | var start = (new Date()).valueOf(); 51 | 52 | turing().delay(1010).then(function() { 53 | assert.ok((new Date()).valueOf() - start >= 1000); 54 | }); 55 | } 56 | }; 57 | 58 | test.run(exports); 59 | 60 | -------------------------------------------------------------------------------- /test/plugins_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | var test = require('test'), 7 | assert = require('assert'); 8 | 9 | require('../turing.dom.js'); 10 | require('../turing.plugins.js'); 11 | 12 | function registerExamplePlugin() { 13 | turing.plugins.register('turnRed', { 14 | name: 'Turn Things Red', 15 | version: '1.0.0', 16 | description: 'Turns the background red', 17 | author: 'Alex Young ', 18 | licenses: [ { type: 'MIT' } ], 19 | 20 | turnRed: function() { 21 | this[0].style.backgroundColor = '#ff0000'; 22 | return this; 23 | } 24 | }); 25 | } 26 | 27 | exports.testPlugins = { 28 | 'test plugin registration and removal': function() { 29 | registerExamplePlugin(); 30 | 31 | assert.ok(turing('#example').turnRed()); 32 | 33 | turing.plugins.remove('turnRed'); 34 | assert.ok(!turing.plugins.hasOwnProperty('turnRed')); 35 | }, 36 | 37 | 'test AlreadyRegistered': function() { 38 | registerExamplePlugin(); 39 | 40 | assert.throws(function() { 41 | registerExamplePlugin(); 42 | }, turing.plugins.AlreadyRegistered); 43 | }, 44 | 45 | 'test removing a non-existent plugin': function() { 46 | assert.throws(function() { 47 | turing.plugins.remove('turnBlue'); 48 | }, turing.plugins.NotFound); 49 | } 50 | }; 51 | 52 | test.run(exports); 53 | 54 | -------------------------------------------------------------------------------- /test/modularity_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Let's Make a Framework: Modularity 4 | 5 |

    Test Content

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /test/run.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | All Turing Tests 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 |
    19 |
    20 |

    Text

    21 |

    Example Link

    22 |
    23 |
    24 |

    Text

    25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |

    Text

    32 |

    Example Link

    33 |

    Example Link 2

    34 |
    35 |
    36 |
      37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /turing.plugins.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Plugins 3 | * Copyright (C) 2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing plugin module. 9 | * 10 | * This is an example plugin: 11 | * 12 | * turing.plugins.register('turnRed', { 13 | * name: 'Turn Things Red', 14 | * version: '1.0.0', 15 | * description: 'Turns the background red', 16 | * author: 'Alex Young ', 17 | * licenses: [ { type: 'MIT' } ], 18 | * 19 | * turnRed: function() { 20 | * this[0].style.backgroundColor = '#ff0000'; 21 | * return this; 22 | * } 23 | * }); 24 | * 25 | */ 26 | define('turing.plugins', ['turing.core'], function(turing) { 27 | var plugins = {}; 28 | plugins.registered = {}; 29 | plugins.AlreadyRegistered = Error; 30 | plugins.NotFound = Error; 31 | 32 | /** 33 | * Registers a plugin, making it available for 34 | * chained DOM calls. 35 | * 36 | * Throws turing.plugins.AlreadyRegistered if a 37 | * plugin with the same name has been registered. 38 | * 39 | * @param {String} methodName The name of your plugin method 40 | * @param {Object} metadata Your plugin 41 | */ 42 | plugins.register = function(methodName, metadata) { 43 | if (plugins.registered[methodName]) { 44 | throw new plugins.AlreadyRegistered('Already registered a plugin called: ' + methodName); 45 | } 46 | 47 | plugins.registered[methodName] = metadata; 48 | turing.domChain[methodName] = metadata[methodName]; 49 | }; 50 | 51 | /** 52 | * Removes a plugin. Throws turing.plugins.NotFound if 53 | * the plugin could not be found. 54 | * 55 | * @param {String} methodName The name of the plugin 56 | */ 57 | plugins.remove = function(methodName) { 58 | if (!plugins.registered.hasOwnProperty(methodName)) { 59 | throw new plugins.NotFound('Plugin not found: ' + methodName); 60 | } else { 61 | delete plugins.registered[methodName] 62 | delete turing.domChain[methodName]; 63 | } 64 | }; 65 | 66 | turing.plugins = plugins; 67 | return plugins; 68 | }); 69 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. About Turing 2 | 3 | Turing is an JavaScript framework. 4 | 5 | * Web: "turingjs.com":http://turingjs.com/ 6 | * License: "MIT License":https://github.com/alexyoung/turing.js/blob/master/LICENSE.txt 7 | * Documentation: "API Documentation":http://turingjs.com/ 8 | * Tests: "test/":https://github.com/alexyoung/turing.js/tree/master/test 9 | * Articles: "DailyJS Turing Articles":http://dailyjs.com/tags.html#lmaf 10 | * eBook: "Let's Make a Framework":http://dailyjs.com/2010/12/02/framework-review/ 11 | 12 | Features include: 13 | 14 | * Classes, with extend and mixins 15 | * Functional programming methods: each, map 16 | * Events 17 | * Animation 18 | * Chained API design 19 | * "Turing Test":https://github.com/alexyoung/turing-test.js/ test framework 20 | 21 | h2. Documentation 22 | 23 | Turing's API documentation is here: "turingjs.com":http://turingjs.com. 24 | 25 | Development has been blogged on DailyJS. Each article is tagged with _lmaf_ on DailyJS: "dailyjs.com/tags.html#lmaf":http://dailyjs.com/tags.html#lmaf. 26 | 27 | h2. Running Tests 28 | 29 | To run tests, ensure you've got the required submodules from git: 30 | 31 |
      32 | git submodule init
      33 | git submodule update
      34 | 
      35 | 36 | Remember to run git submodule update to get the latest version referenced by the project. 37 | 38 | h3. Contributions 39 | 40 | * John-David Dalton 41 | * Henrik Lindqvist 42 | * Ryan Cannon (classList indexOf idea) 43 | * Adam Solove (classList bug) 44 | * John Allsopp (classList suggestion) 45 | * Nano Documet (incorrectly named modules) 46 | 47 | h2. TODO 48 | 49 | * [anim] CSS properties should be used instead of DOM properties (margin-left vs. marginLeft) 50 | * [anim] DOM chained animations should be correctly scheduled 51 | * [ajax] The Ajax requests should really return data instead of the request object if possible (json/etc) 52 | * [test] Make turing tests able to work with asynchronous tests (i.e., Ajax) 53 | * [test] Add benchmarks to Turing Test 54 | * [test] Update benchmarks to use Turing Test 55 | * [test] Remove Riot 56 | * [net] Test/fix JSONP json parsing for data responses 57 | -------------------------------------------------------------------------------- /turing.promise.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Promise 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing Promise module. 9 | */ 10 | define('turing.promise', ['turing.core'], function(turing) { 11 | function PromiseModule(global) { 12 | /** 13 | * The Promise class. 14 | */ 15 | function Promise() { 16 | var self = this; 17 | this.pending = []; 18 | 19 | /** 20 | * Resolves a promise. 21 | * 22 | * @param {Object} A value 23 | */ 24 | this.resolve = function(result) { 25 | self.complete('resolve', result); 26 | }, 27 | 28 | /** 29 | * Rejects a promise. 30 | * 31 | * @param {Object} A value 32 | */ 33 | this.reject = function(result) { 34 | self.complete('reject', result); 35 | } 36 | } 37 | 38 | Promise.prototype = { 39 | /** 40 | * Adds a success and failure handler for completion of this Promise object. 41 | * 42 | * @param {Function} success The success handler 43 | * @param {Function} success The failure handler 44 | * @returns {Promise} `this` 45 | */ 46 | then: function(success, failure) { 47 | this.pending.push({ resolve: success, reject: failure }); 48 | return this; 49 | }, 50 | 51 | /** 52 | * Runs through each pending 'thenable' based on type (resolve, reject). 53 | * 54 | * @param {String} type The thenable type 55 | * @param {Object} result A value 56 | */ 57 | complete: function(type, result) { 58 | while (this.pending[0]) { 59 | this.pending.shift()[type](result); 60 | } 61 | } 62 | }; 63 | 64 | /** 65 | * Chained Promises: 66 | * 67 | * global().delay(1000).then(function() { 68 | * assert.ok((new Date()).valueOf() - start >= 1000); 69 | * }); 70 | * 71 | */ 72 | var chain = {}; 73 | 74 | turing.init(function() { 75 | if (arguments.length === 0) 76 | return chain; 77 | }); 78 | 79 | chain.delay = function(ms) { 80 | var p = new turing.Promise(); 81 | setTimeout(p.resolve, ms); 82 | return p; 83 | }; 84 | 85 | global.Promise = Promise; 86 | } 87 | 88 | if (typeof module !== 'undefined') { 89 | module.exports = function(t) { 90 | return PromiseModule(t); 91 | } 92 | } else { 93 | PromiseModule(turing); 94 | } 95 | 96 | return turing.Promise; 97 | }); 98 | 99 | -------------------------------------------------------------------------------- /test/functional/ajax.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Ajax Functional Tests 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * These tests may be run with Node. 9 | */ 10 | 11 | var express = require('express'), 12 | app = express.createServer(), 13 | fs = require('fs'), 14 | path = require('path'), 15 | jade = require('jade'); 16 | 17 | app.configure(function() { 18 | app.use(express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time ms' })); 19 | app.use(express.methodOverride()); 20 | app.use(express.bodyParser()); 21 | app.use(express.static(path.join(__dirname, 'public'))); 22 | app.use(express.static(path.join(__dirname, '..', '..', 'build'))); 23 | app.use(app.router); 24 | app.set('view engine', 'jade'); 25 | }); 26 | 27 | function staticFile(url, path) { 28 | app.get(url, function(req, res) { 29 | fs.readFile(__dirname + path, function(err, data) { 30 | res.contentType(path); 31 | res.send(data); 32 | }); 33 | }); 34 | } 35 | 36 | app.get('/', function(req, res) { 37 | res.render('index', { layout: false }); 38 | }); 39 | 40 | staticFile('/turing-test.js', '/../turing-test/turing-test.js'); 41 | staticFile('/turing-test/lib/test.js', '/../turing-test/lib/test.js'); 42 | staticFile('/turing-test/lib/assert.js', '/../turing-test/lib/assert.js'); 43 | staticFile('/turing-test/stylesheets/screen.css', '/../turing-test/stylesheets/screen.css'); 44 | 45 | /** 46 | * Ajax tests 47 | */ 48 | app.get('/get-test', function(req, res) { 49 | res.header('content-type', 'text/html'); 50 | res.send('Sample text'); 51 | }); 52 | 53 | app.get('/error', function(req, res) { 54 | res.header('content-type', 'text/html'); 55 | res.send('Sample text', 500); 56 | }); 57 | 58 | app.post('/post-test', function(req, res) { 59 | res.send(req.body.key); 60 | }); 61 | 62 | app.post('/post-array', function(req, res) { 63 | res.send(req.body[0]); 64 | }); 65 | 66 | app.post('/give-me-json', function(req, res) { 67 | res.header('content-type', 'application/json'); 68 | res.send({ key: 'value' }); 69 | }); 70 | 71 | app.post('/give-me-xml', function(req, res) { 72 | res.header('content-type', 'application/xml'); 73 | res.send('value'); 74 | }); 75 | 76 | app.get('/load-me.js', function(req, res) { 77 | res.header('content-type', 'text/javascript'); 78 | var js = ''; 79 | Object.keys(req.query).forEach(function(key) { 80 | js = 'window.' + key + ' = ' + req.query[key]; 81 | }); 82 | res.send(js); 83 | }); 84 | 85 | app.listen(3000); 86 | 87 | -------------------------------------------------------------------------------- /test/dom_test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | DOM Test 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
      17 |
      18 |

      Text

      19 |

      Example Link

      20 |
      21 |
      22 |

      Text

      23 |
      24 |
      25 |
      26 |
      27 |

      Example

      28 |
      29 |
      30 |

      Example example

      31 |
      32 |
      33 |

      Example example

      34 |
      35 |
      36 |

      Example

      37 |
      38 |
      39 |

      Test

      40 |

      Test

      41 |

      Test

      42 |

      Test

      43 |
      44 |
      45 |
      46 |
      47 |
      48 |
      49 |
      50 |
      51 | 52 | 53 | 54 | 55 |
      Write to me
      56 | 57 | 58 | 59 | 60 |
      Write to me
      61 | 62 | 63 | 64 | 65 |
      X1
      66 |
      67 |
      68 | link 69 |
      70 | 71 | 72 | 73 |
      74 |
      75 |
      76 |
      77 | link 78 |
      79 | 80 | 81 |
      82 |
      83 |
        84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /turing.oo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing OO 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing Class: 9 | * 10 | * User = turing.Class({ 11 | * initialize: function(name, age) { 12 | * this.name = name; 13 | * this.age = age; 14 | * } 15 | * }); 16 | * 17 | * new User('Alice', '26'); 18 | * 19 | * Inheritance: 20 | * 21 | * Pass an object to `turing.Class` to inherit from it. 22 | * 23 | * SuperUser = turing.Class(User, { 24 | * initialize: function() { 25 | * this.$super('initialize', arguments); 26 | * }, 27 | * 28 | * toString: function() { 29 | * return "SuperUser: " + this.$super('toString'); 30 | * } 31 | * }); 32 | * 33 | * Mixins: 34 | * 35 | * Objects can be embedded within each other: 36 | * 37 | * MixinUser = turing.Class({ 38 | * include: User, 39 | * 40 | * initialize: function(log) { 41 | * this.log = log; 42 | * } 43 | * }); 44 | * 45 | **/ 46 | define('turing.oo', ['turing.core'], function(turing) { 47 | var Class, oo; 48 | 49 | Class = function() { 50 | return oo.create.apply(this, arguments); 51 | } 52 | 53 | oo = { 54 | create: function() { 55 | var methods = null, 56 | parent = undefined, 57 | klass = function() { 58 | this.$super = function(method, args) { return oo.$super(this.$parent, this, method, args); }; 59 | this.initialize.apply(this, arguments); 60 | }; 61 | 62 | if (typeof arguments[0] === 'function') { 63 | parent = arguments[0]; 64 | methods = arguments[1]; 65 | } else { 66 | methods = arguments[0]; 67 | } 68 | 69 | if (typeof parent !== 'undefined') { 70 | oo.extend(klass.prototype, parent.prototype); 71 | klass.prototype.$parent = parent.prototype; 72 | } 73 | 74 | oo.mixin(klass, methods); 75 | oo.extend(klass.prototype, methods); 76 | klass.prototype.constructor = klass; 77 | 78 | if (!klass.prototype.initialize) 79 | klass.prototype.initialize = function(){}; 80 | 81 | return klass; 82 | }, 83 | 84 | mixin: function(klass, methods) { 85 | if (typeof methods.include !== 'undefined') { 86 | if (typeof methods.include === 'function') { 87 | oo.extend(klass.prototype, methods.include.prototype); 88 | } else { 89 | for (var i = 0; i < methods.include.length; i++) { 90 | oo.extend(klass.prototype, methods.include[i].prototype); 91 | } 92 | } 93 | } 94 | }, 95 | 96 | extend: function(destination, source) { 97 | for (var property in source) 98 | destination[property] = source[property]; 99 | return destination; 100 | }, 101 | $super: function(parentClass, instance, method, args) { 102 | return parentClass[method].apply(instance, args); 103 | } 104 | }; 105 | 106 | turing.Class = Class; 107 | turing.oo = oo; 108 | return oo; 109 | }); 110 | -------------------------------------------------------------------------------- /turing.touch.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Touch 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Support for touchscreen devices. Run `turing.touch.register()` to get touch event support. 9 | * 10 | * Tap: 11 | * 12 | * turing.events.add(element, 'tap', function(e) { 13 | * alert('tap'); 14 | * }); 15 | * 16 | * Swipe: 17 | * 18 | * turing.events.add(element, 'swipe', function(e) { 19 | * alert('swipe'); 20 | * }); 21 | * 22 | * Orientation Changes: 23 | * 24 | * Device orientation is available in `turing.touch.orientation()`. 25 | * 26 | * turing.events.add(element, 'orientationchange', function(e) { 27 | * alert('Orientation is now: ' + turing.touch.orientation()); 28 | * }); 29 | */ 30 | define('turing.touch', ['turing.core', 'turing.dom', 'turing.events'], function(turing, dom, events) { 31 | var touch = {}, state = {}; 32 | 33 | touch.swipeThreshold = 50; 34 | 35 | // Returns [orientation angle, orientation string] 36 | touch.orientation = function() { 37 | var orientation = window.orientation, 38 | orientationString = ''; 39 | switch (orientation) { 40 | case 0: 41 | orientationString += 'portrait'; 42 | break; 43 | 44 | case -90: 45 | orientationString += 'landscape right'; 46 | break; 47 | 48 | case 90: 49 | orientationString += 'landscape left'; 50 | break; 51 | 52 | case 180: 53 | orientationString += 'portrait upside-down'; 54 | break; 55 | } 56 | return [orientation, orientationString]; 57 | }; 58 | 59 | function touchStart(e) { 60 | state.touches = e.touches; 61 | state.startTime = (new Date).getTime(); 62 | state.x = e.changedTouches[0].clientX; 63 | state.y = e.changedTouches[0].clientY; 64 | state.startX = state.x; 65 | state.startY = state.y; 66 | state.target = e.target; 67 | state.duration = 0; 68 | } 69 | 70 | function touchEnd(e) { 71 | var x = e.changedTouches[0].clientX, 72 | y = e.changedTouches[0].clientY; 73 | 74 | if (state.x === x && state.y === y && state.touches.length == 1) { 75 | turing.events.fire(e.target, 'tap'); 76 | } 77 | } 78 | 79 | function touchMove(e) { 80 | var moved = 0, touch = e.changedTouches[0]; 81 | state.duration = (new Date).getTime() - state.startTime; 82 | state.x = state.startX - touch.pageX; 83 | state.y = state.startY - touch.pageY; 84 | moved = Math.sqrt(Math.pow(Math.abs(state.x), 2) + Math.pow(Math.abs(state.y), 2)); 85 | 86 | if (state.duration < 1000 && moved > turing.touch.swipeThreshold) { 87 | turing.events.fire(e.target, 'swipe'); 88 | } 89 | } 90 | 91 | // register must be called to register for touch event helpers 92 | touch.register = function() { 93 | turing.events.add(document, 'touchstart', touchStart); 94 | turing.events.add(document, 'touchmove', touchMove); 95 | turing.events.add(document, 'touchend', touchEnd); 96 | turing.touch.swipeThreshold = screen.width / 5; 97 | }; 98 | 99 | turing.touch = touch; 100 | return turing.touch; 101 | }); 102 | -------------------------------------------------------------------------------- /test/oo_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | require('../turing.oo.js'); 7 | require('../turing.functional.js'); 8 | require('../turing.enumerable.js'); 9 | 10 | var test = require('test'), 11 | assert = require('assert'), 12 | NoInitializer, 13 | Admin, 14 | Guest, 15 | User, 16 | MixinUser, 17 | DoubleMixinUser, 18 | SuperUser; 19 | 20 | exports.testOO = { 21 | 'test class setup': function() { 22 | NoInitializer = turing.Class({ 23 | doSomething: function() { return 'something'; } 24 | }); 25 | 26 | User = turing.Class({ 27 | initialize: function(name, age) { 28 | this.name = name; 29 | this.age = age; 30 | }, 31 | 32 | display: function() { 33 | return this.name + ': ' + this.age; 34 | }, 35 | 36 | login: function() { 37 | return true; 38 | }, 39 | 40 | toString: function() { 41 | return "name: " + this.name + ", age: " + this.age; 42 | } 43 | }); 44 | 45 | Admin = turing.Class(User, { 46 | dangerousMethod: function() { 47 | return 'danger!'; 48 | } 49 | }); 50 | 51 | Guest = turing.Class(User, { 52 | initialize: function(state) { 53 | this.name = 'User_' + this.randomId(); 54 | this.age = 0; 55 | this.state = state; 56 | }, 57 | 58 | randomId: function() { 59 | return Math.floor(Math.random() * 100); 60 | } 61 | }); 62 | 63 | MixinUser = turing.Class({ 64 | include: User, 65 | 66 | initialize: function(log) { 67 | this.log = log; 68 | } 69 | }); 70 | 71 | DoubleMixinUser = turing.Class({ 72 | include: [NoInitializer, User], 73 | 74 | initialize: function(log) { 75 | this.log = log; 76 | } 77 | }); 78 | 79 | SuperUser = turing.Class(User, { 80 | initialize: function() { 81 | this.$super('initialize', arguments); 82 | }, 83 | 84 | toString: function() { 85 | return "SuperUser: " + this.$super('toString'); 86 | } 87 | }); 88 | }, 89 | 90 | 'test classes with no initialize': function() { 91 | var noInit = new NoInitializer(); 92 | assert.equal('something', noInit.doSomething()); 93 | }, 94 | 95 | 'test instantiation': function() { 96 | var alex = new User('Alex', 29); 97 | assert.equal('Alex', alex.name); 98 | }, 99 | 100 | 'test inheritance': function() { 101 | var admin = new Admin('Alex', 29), 102 | guest = new Guest('registering'); 103 | 104 | assert.equal('Alex', admin.name); 105 | assert.equal('danger!', admin.dangerousMethod()); 106 | assert.equal('registering', guest.state); 107 | }, 108 | 109 | 'test a class with mixins': function() { 110 | var mixinUser = new MixinUser('log file'), 111 | doubleMixinUser = new DoubleMixinUser('double log file'); 112 | 113 | assert.ok(mixinUser.login()); 114 | assert.equal('log file', mixinUser.log); 115 | assert.equal('something', doubleMixinUser.doSomething()); 116 | }, 117 | 118 | 'an inherited class that uses super': function() { 119 | var superUser = new SuperUser('alex', 104); 120 | assert.equal(104, superUser.age); 121 | assert.equal(104, superUser.age); 122 | assert.ok(superUser.toString().match(/SuperUser:/)); 123 | } 124 | }; 125 | 126 | test.run(exports); 127 | -------------------------------------------------------------------------------- /turing.core.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Core 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The core Turing module. 9 | */ 10 | (function(global) { 11 | var middleware = [], turing, modules = {}; 12 | 13 | /** 14 | * The turing object. Use `turing('selector')` for quick DOM access when built with the DOM module. 15 | * 16 | * @returns {Object} The turing object, run through `init` 17 | */ 18 | turing = function() { 19 | var result, i; 20 | for (i = 0; i < middleware.length; i++) { 21 | result = middleware[i].apply(turing, arguments); 22 | if (result) { 23 | return result; 24 | } 25 | } 26 | } 27 | 28 | turing.VERSION = '0.0.90'; 29 | turing.lesson = 'Part 90: AMD'; 30 | 31 | /** 32 | * This alias will be used as an alternative to `turing()`. 33 | * If `__turing_alias` is present in the global scope this will be used instead. 34 | * 35 | */ 36 | if (typeof window !== 'undefined') { 37 | turing.alias = window.__turing_alias || '$t'; 38 | window[turing.alias] = turing; 39 | } 40 | 41 | /** 42 | * Determine if an object is an `Array`. 43 | * 44 | * @param {Object} object An object that may or may not be an array 45 | * @returns {Boolean} True if the parameter is an array 46 | */ 47 | turing.isArray = Array.isArray || function(object) { 48 | return !!(object && object.concat 49 | && object.unshift && !object.callee); 50 | }; 51 | 52 | /** 53 | * Convert an `Array`-like collection into an `Array`. 54 | * 55 | * @param {Object} collection A collection of items that responds to length 56 | * @returns {Array} An `Array` of items 57 | */ 58 | turing.toArray = function(collection) { 59 | var results = [], i; 60 | for (i = 0; i < collection.length; i++) { 61 | results.push(collection[i]); 62 | } 63 | return results; 64 | }; 65 | 66 | // This can be overriden by libraries that extend turing(...) 67 | turing.init = function(fn) { 68 | middleware.unshift(fn); 69 | }; 70 | 71 | /** 72 | * Determines if an object is a `Number`. 73 | * 74 | * @param {Object} object A value to test 75 | * @returns {Boolean} True if the object is a Number 76 | */ 77 | turing.isNumber = function(object) { 78 | return (object === +object) || (toString.call(object) === '[object Number]'); 79 | }; 80 | 81 | /** 82 | * Binds a function to an object. 83 | * 84 | * @param {Function} fn A function 85 | * @param {Object} object An object to bind to 86 | * @returns {Function} A rebound method 87 | */ 88 | turing.bind = function(fn, object) { 89 | var slice = Array.prototype.slice, 90 | args = slice.apply(arguments, [2]); 91 | return function() { 92 | return fn.apply(object || {}, args.concat(slice.apply(arguments))); 93 | }; 94 | }; 95 | 96 | var testCache = {}, 97 | detectionTests = {}; 98 | 99 | /** 100 | * Used to add feature-detection methods. 101 | * 102 | * @param {String} name The name of the test 103 | * @param {Function} fn The function that performs the test 104 | */ 105 | turing.addDetectionTest = function(name, fn) { 106 | if (!detectionTests[name]) { 107 | detectionTests[name] = fn; 108 | } 109 | }; 110 | 111 | /** 112 | * Run a feature detection name. 113 | * 114 | * @param {String} name The name of the test 115 | * @returns {Boolean} The outcome of the test 116 | */ 117 | turing.detect = function(testName) { 118 | if (typeof testCache[testCache] === 'undefined') { 119 | testCache[testName] = detectionTests[testName](); 120 | } 121 | return testCache[testName]; 122 | }; 123 | 124 | turing.define = function(module, dependencies, fn) { 125 | if (typeof define === 'function' && define.amd) { 126 | define(module, dependencies, fn); 127 | } else { 128 | if (dependencies && dependencies.length) { 129 | for (var i = 0; i < dependencies.length; i++) { 130 | dependencies[i] = modules[dependencies[i]]; 131 | } 132 | } 133 | modules[module] = fn.apply(this, dependencies || []); 134 | } 135 | }; 136 | 137 | /** 138 | * Export `turing` based on environment. 139 | */ 140 | global.turing = turing; 141 | 142 | if (typeof exports !== 'undefined') { 143 | exports.turing = turing; 144 | } 145 | 146 | turing.define('turing.core', [], function() { return turing; } ); 147 | 148 | if (typeof define === 'undefined') { 149 | global.define = turing.define; 150 | } 151 | }(typeof window === 'undefined' ? this : window)); 152 | -------------------------------------------------------------------------------- /test/events_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | var test = require('test'), 7 | assert = require('assert'); 8 | 9 | require('../turing.dom.js'); 10 | require('../turing.events.js'); 11 | 12 | if (typeof document !== 'undefined') { 13 | exports.testEvents = { 14 | 'test delegate handlers': function() { 15 | var clicks = 0; 16 | turing.events.delegate(document, '#events-test a', 'click', function(e) { 17 | clicks++; 18 | }); 19 | 20 | turing.events.fire(turing.dom.get('#events-test a')[0], 'click'); 21 | assert.equal(1, clicks, 'run the handler when the right selector is matched'); 22 | 23 | turing.events.fire(turing.dom.get('p')[0], 'click'); 24 | assert.equal(1, clicks, 'run the handler when the right selector is matched'); 25 | }, 26 | 27 | 'test event lifecycle and chained events': function() { 28 | var element = turing.dom.get('#events-test a')[0], 29 | check = 0, 30 | clicks = 0, 31 | callback = function(e) { check++; return false; }; 32 | 33 | turing.events.add(element, 'click', callback); 34 | turing.events.fire(element, 'click'); 35 | turing.events.remove(element, 'click', callback); 36 | assert.equal(1, check, 'check should have been incremented once'); 37 | 38 | turing('#events-test a').bind('click', function() { clicks++; }); 39 | turing.events.fire(element, 'click'); 40 | assert.equal(1, clicks, 'clicks should have been incremented once'); 41 | 42 | check = 0; 43 | turing.events.add(element, 'click', callback); 44 | turing.events.fire(element, 'click'); 45 | turing.events.remove(element, 'click', callback); 46 | turing.events.fire(element, 'click'); 47 | assert.equal(1, check, 'check should have been increment once because the event handler was removed'); 48 | }, 49 | 50 | 'test event stop and fire': function() { 51 | var lastResult = '', 52 | callback = function(event) { lastResult = event.target.innerHTML; }; 53 | 54 | turing.events.add(document, 'click', callback); 55 | turing.events.fire(turing.dom.get('.clickme')[0], 'click'); 56 | turing.events.remove(document, 'click', callback); 57 | assert.equal('Text', lastResult, 'set the correct element in event.target'); 58 | 59 | var callback = function(event) { event.stop(); }; 60 | turing.events.add(turing.dom.get('#link2')[0], 'click', callback); 61 | turing.events.fire(turing.dom.get('#link2')[0], 'click'); 62 | assert.equal('', window.location.hash, 'event.stop() should have stopped the event'); 63 | } 64 | }; 65 | } 66 | 67 | exports.testEventEmitter = { 68 | 'test addListener': function() { 69 | var Emitter = turing.events.Emitter, 70 | emitter = new Emitter(), 71 | i = 0; 72 | 73 | emitter.on('eventName', function() { 74 | i++; 75 | }); 76 | 77 | emitter.on('testFired', function() { 78 | assert.equal(i, 1); 79 | }); 80 | 81 | assert.ok(emitter.emit('eventName')); 82 | assert.ok(emitter.emit('testFired')); 83 | }, 84 | 85 | 'test removeAllListeners': function() { 86 | var Emitter = turing.events.Emitter, 87 | emitter = new Emitter(); 88 | 89 | emitter.on('event1', function() {}); 90 | emitter.on('event2', function() {}); 91 | 92 | emitter.removeAllListeners('event1'); 93 | 94 | assert.ok(!emitter.emit('event1')); 95 | assert.ok(emitter.emit('event2')); 96 | }, 97 | 98 | 'test removeListener': function() { 99 | var Emitter = turing.events.Emitter, 100 | emitter = new Emitter(), 101 | i = 0; 102 | 103 | function add() { 104 | i++; 105 | } 106 | 107 | function nothing() { 108 | } 109 | 110 | emitter.on('add', add); 111 | emitter.on('add', nothing); 112 | emitter.on('testFired', function() { 113 | assert.equal(i, 1); 114 | }); 115 | 116 | assert.ok(emitter.emit('add')); 117 | assert.ok(emitter.removeListener('add', add)); 118 | assert.ok(emitter.emit('add')); 119 | assert.ok(emitter.removeListener('add', nothing)); 120 | assert.ok(!emitter.removeListener('add', nothing)); 121 | assert.ok(!emitter.emit('add')); 122 | assert.ok(emitter.emit('testFired')); 123 | }, 124 | 125 | 'test listener removal in Emitter': function() { 126 | var Emitter = turing.events.Emitter, 127 | emitter = new Emitter(), 128 | i = 0, 129 | j = 0; 130 | 131 | function add() { 132 | i++; 133 | assert.ok(emitter.removeListener('add', add)); 134 | } 135 | 136 | // This should end up being called twice 137 | function add2() { 138 | j++; 139 | } 140 | 141 | emitter.on('add', add); 142 | emitter.on('add', add2); 143 | 144 | assert.ok(emitter.emit('add')); 145 | assert.ok(!emitter.removeListener('add', add)); 146 | assert.ok(emitter.emit('add')); 147 | assert.equal(2, j); 148 | } 149 | }; 150 | 151 | test.run(exports); 152 | -------------------------------------------------------------------------------- /test/enumerable_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | turing = require('../turing.core.js').turing; 4 | var test = require('test'), 5 | assert = require('assert'); 6 | 7 | require('../turing.oo.js'); 8 | require('../turing.functional.js'); 9 | require('../turing.enumerable.js'); 10 | 11 | exports.testEnumerable = { 12 | 'test array iteration with each': function() { 13 | var a = [1, 2, 3, 4, 5], 14 | count = 0; 15 | turing.enumerable.each(a, function(n) { count += 1; }); 16 | assert.equal(5, count, 'count should have been iterated'); 17 | }, 18 | 19 | 'test array iteration with map': function() { 20 | var a = [1, 2, 3, 4, 5], 21 | b = turing.enumerable.map(a, function(n) { return n + 1; }); 22 | assert.deepEqual([2, 3, 4, 5, 6], b, 'map should iterate'); 23 | }, 24 | 25 | 'test array filtering': function() { 26 | var a = [1, 2, 3, 4, 5]; 27 | assert.deepEqual([2, 4], turing.enumerable.filter(a, function(n) { return n % 2 == 0; }), 'array should be filtered'); 28 | }, 29 | 30 | 'test array reject': function() { 31 | var a = [1, 2, 3, 4, 5]; 32 | assert.deepEqual([1, 3, 5], turing.enumerable.reject(a, function(n) { return n % 2 == 0; }), 'items should have been rejected'); 33 | }, 34 | 35 | 'test finding values with detect': function() { 36 | var a = [1, 2, 3, 4, 5]; 37 | assert.ok(turing.enumerable.detect(a, function(n) { return n == 1; }), 'detect should find the item'); 38 | }, 39 | 40 | 'test detect does not find items that do not exist': function() { 41 | var a = [1, 2, 3, 4, 5]; 42 | assert.equal(null, turing.enumerable.detect(a, function(n) { return n == 1000; })); 43 | }, 44 | 45 | 'test chained method calls': function() { 46 | assert.deepEqual([20, 40], turing.enumerable.chain([1, 2, 3, 4]) 47 | .filter(function(n) { return n % 2 == 0; }) 48 | .map(function(n) { return n * 10; }) 49 | .values()); 50 | }, 51 | 52 | 'test reduce results accumulation': function() { 53 | assert.equal(6, turing.enumerable.reduce([1, 2, 3], 0, function(memo, n) { return memo + n; })); 54 | }, 55 | 56 | 'test flatten': function() { 57 | assert.deepEqual([2, 4, 6, 8], turing.enumerable.flatten([[2, 4], [[6], 8]])); 58 | }, 59 | 60 | 'test method invocation': function() { 61 | assert.deepEqual(['hel', 'wor'], turing.enumerable.invoke(['hello', 'world'], 'substring', 0, 3)); 62 | }, 63 | 64 | 'test pluck': function() { 65 | assert.deepEqual([5, 5, 4, 2, 4], turing.enumerable.pluck(['hello', 'world', 'this', 'is', 'nice'], 'length')); 66 | }, 67 | 68 | 'test tail': function() { 69 | assert.deepEqual([2, 3, 4, 5], turing.enumerable.tail([1, 2, 3, 4, 5])); 70 | }, 71 | 72 | 'test tail skip': function() { 73 | assert.deepEqual([4, 5], turing.enumerable.tail([1, 2, 3, 4, 5], 3)); 74 | }, 75 | 76 | 'test return false from some when an array is empty': function() { 77 | assert.equal(false, turing.enumerable.some([])); 78 | }, 79 | 80 | 'test return true from some when an array has items': function() { 81 | assert.ok(turing.enumerable.some([1, 2, 3])); 82 | }, 83 | 84 | 'test return true from some when an array matches a callback': function() { 85 | assert.ok(turing.enumerable.some([1, 2, 3], function(value) { return value === 3; })); 86 | }, 87 | 88 | 'test check callback returns true for every item with all': function() { 89 | assert.ok(turing.enumerable.all([1, 2, 3], function(value) { return value < 4; })); 90 | }, 91 | 92 | 'test check callback returns false with all when callback returns false': function() { 93 | assert.equal(false, turing.enumerable.all([1, 2, 3], function(value) { return value > 4; })); 94 | }, 95 | 96 | 'test finds values with include': function() { 97 | assert.ok(turing.enumerable.include([1, 2, 3], 3)); 98 | }, 99 | 100 | 'test objects iterate with each': function() { 101 | var obj = { one: '1', two: '2', three: '3' }, 102 | count = 0; 103 | turing.enumerable.each(obj, function(n) { count += 1; }); 104 | assert.equal(3, count); 105 | }, 106 | 107 | 'test iterate objects with map': function() { 108 | var obj = { one: '1', two: '2', three: '3' }; 109 | assert.deepEqual(['11', '21', '31'], turing.enumerable.map(obj, function(n) { return n + 1; })); 110 | }, 111 | 112 | 'test filter objects and return a multi-dimensional array': function() { 113 | var obj = { one: '1', two: '2', three: '3' }; 114 | assert.equal('one', turing.enumerable.filter(obj, function(v, i) { return v < 2; })[0][0]); 115 | }, 116 | 117 | 'test check callback returns true for every item with all': function() { 118 | var obj = { one: '1', two: '2', three: '3' }; 119 | assert.ok(turing.enumerable.all(obj, function(value, key) { return value < 4; })); 120 | }, 121 | 122 | 'test check callback returns false with all when callback returns false': function() { 123 | var obj = { one: '1', two: '2', three: '3' }; 124 | assert.equal(false, turing.enumerable.all(obj, function(value, key) { return value > 4; })); 125 | }, 126 | 127 | 'test finds values with include': function() { 128 | var obj = { one: '1', two: '2', three: '3' }; 129 | assert.ok(turing.enumerable.include(obj, '3')); 130 | } 131 | }; 132 | 133 | test.run(exports); 134 | 135 | -------------------------------------------------------------------------------- /test/functional/public/ajax-test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | var test = require('test'), 4 | assert = require('assert'); 5 | 6 | function mixinExpect(m) { 7 | var m2 = {}, method; 8 | 9 | for (method in m) { 10 | if (m.hasOwnProperty(method)) { 11 | m2['__' + method] = m[method]; 12 | (function(methodName) { 13 | m2[methodName] = function() { 14 | m2.mixinExpectAssertionCount++; 15 | m2['__' + methodName].apply(m2, arguments); 16 | }; 17 | }(method)); 18 | } 19 | } 20 | 21 | m2.expect = function(count) { 22 | m2.mixinExpectAssertionCount = 0; 23 | m2.mixinExpectExpected = count; 24 | }; 25 | 26 | m2.done = function() { 27 | if (m2.mixinExpectAssertionCount !== m2.mixinExpectExpected) { 28 | throw('Expected assertion count was not found, expected: ' + m2.mixinExpectExpected + ', got: ' + m2.mixinExpectAssertionCount); 29 | } 30 | }; 31 | 32 | return m2; 33 | } 34 | 35 | function Expect() { 36 | this.expectations = {}; 37 | } 38 | 39 | Expect.prototype = { 40 | add: function(expectation) { 41 | this.expectations[expectation] = false; 42 | }, 43 | 44 | fulfill: function(expectation) { 45 | this.expectations[expectation] = true; 46 | }, 47 | 48 | done: function() { 49 | for (var expectation in this.expectations) { 50 | if (!this.expectations[expectation]) { 51 | throw('Expected assertion was fulfilled , expected: ' + expectation); 52 | } 53 | } 54 | } 55 | }; 56 | 57 | exports.testAjax = { 58 | 'test ajax get': function() { 59 | $t.get('/get-test') 60 | .set('Accept', 'text/html') 61 | .end(function(res) { 62 | assert.equal('Sample text', res.responseText); 63 | }); 64 | }, 65 | 66 | 'test ajax post': function() { 67 | $t.post('/post-test') 68 | .data('key=value&anotherKey=anotherValue') 69 | .end(function(res) { 70 | assert.equal('value', res.responseText); 71 | }); 72 | }, 73 | 74 | 'test send with data': function() { 75 | $t.post('/post-test') 76 | .send('key=value&anotherKey=anotherValue', function(res) { 77 | assert.equal('value', res.responseText); 78 | }); 79 | }, 80 | 81 | 'test json post object': function() { 82 | $t.post('/post-test') 83 | .data({ key: 'value' }) 84 | .end(function(res) { 85 | assert.equal('value', res.responseText); 86 | }); 87 | }, 88 | 89 | 'test error handling': function() { 90 | $t.get('/error') 91 | .end(function(res) { 92 | assert.equal(500, res.status); 93 | assert.equal(false, res.success); 94 | }); 95 | }, 96 | 97 | 'test json post object with application/json': function() { 98 | $t.post('/post-test') 99 | .data({ 'key': 'value' }) 100 | .set('Content-Type', 'application/json') 101 | .end(function(res) { 102 | assert.equal('value', res.responseText); 103 | }); 104 | }, 105 | 106 | 'test json post array with application/json': function() { 107 | $t.post('/post-array') 108 | .data(['value']) 109 | .set('Content-Type', 'application/json') 110 | .end(function(res) { 111 | assert.equal('value', res.responseText); 112 | }); 113 | }, 114 | 115 | 'test promises': function() { 116 | $t.get('/get-test').then( 117 | function(r) { assert.equal('Sample text', r.responseText); }, 118 | function(r) { assert.ok(false); } 119 | ); 120 | }, 121 | 122 | 'test json parsing': function() { 123 | $t.post('/give-me-json') 124 | .set('Content-Type', 'application/json') 125 | .end(function(res) { 126 | assert.equal('value', res.responseJSON.key); 127 | }); 128 | }, 129 | 130 | 'test xml parsing': function() { 131 | $t.post('/give-me-xml') 132 | .set('Content-Type', 'application/xml') 133 | .end(function(res) { 134 | assert.equal('key', res.responseXML.documentElement.nodeName); 135 | }); 136 | } 137 | }; 138 | 139 | exports.testRequire = { 140 | 'test require': function() { 141 | $t.require('/load-me.js?test0=0', { transport: 'scriptInsertion' }, function() { 142 | assert.equal(window.test0, 0); 143 | }); 144 | }, 145 | 146 | 'test require execution': function() { 147 | $t.require('/load-me.js?test1=1', { async: true, defer: true, transport: 'scriptInsertion' }, function() { 148 | assert.equal(window.test1, 1); 149 | }); 150 | 151 | $t.require('/load-me.js?test2=2', { transport: 'scriptInsertion' }, function() { 152 | assert.equal(window.test2, 2); 153 | }); 154 | }, 155 | 156 | 'test require with XMLHttpRequest': function() { 157 | $t.require('/load-me.js?test3=3', { transport: 'XMLHttpRequest' }, function() { 158 | assert.equal(window.test3, 3); 159 | }); 160 | }, 161 | 162 | 'test turing.require.isSameOrigin': function() { 163 | assert.ok(turing.require.isSameOrigin('/example.js')); 164 | assert.ok(turing.require.isSameOrigin(location.protocol + '//' + location.host + '/example.js')); 165 | assert.ok(!turing.require.isSameOrigin('https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js')); 166 | }, 167 | 168 | 'test async queue loading': function() { 169 | $t.require([ 170 | '/load-me.js?test9=9', 171 | ['/load-me.js?test4=4', '/load-me.js?test5=5'], 172 | ['/load-me.js?test6=6', '/load-me.js?test7=7'], 173 | '/load-me.js?test8=8' 174 | ]).on('complete', function() { 175 | assert.ok(true); 176 | }).on('loaded', function(item) { 177 | if (item.src === '/load-me.js?test9=9') { 178 | assert.equal(typeof test4, 'undefined'); 179 | } 180 | 181 | if (item.src === '/load-me.js?test4=4') { 182 | assert.equal(test9, 9); 183 | assert.equal(test4, 4); 184 | assert.equal(typeof test6, 'undefined'); 185 | assert.equal(typeof test8, 'undefined'); 186 | } 187 | 188 | if (item.src === '/load-me.js?test6=6') { 189 | assert.equal(test9, 9); 190 | assert.equal(test4, 4); 191 | assert.equal(test6, 6); 192 | assert.equal(typeof test8, 'undefined'); 193 | } 194 | }); 195 | }, 196 | 197 | 'test queue loading with remote': function() { 198 | $t.require([ 199 | 'https://ajax.googleapis.com/ajax/libs/webfont/1.0.22/webfont.js', 200 | '/load-me.js?test10=10', 201 | '/load-me.js?test11=11' 202 | ]).on('complete', function() { 203 | assert.ok(true); 204 | }).on('loaded', function(item) { 205 | if (item.src === 'https://ajax.googleapis.com/ajax/libs/webfont/1.0.22/webfont.js') { 206 | assert.equal(typeof test11, 'undefined', 'test11 should not be set when remote is loaded'); 207 | assert.ok(window.WebFont, 'WebFont should be set'); 208 | } 209 | 210 | if (item.src === '/load-me.js?test10=10') { 211 | assert.equal(typeof test11, 'undefined', 'test11 should not be set when test10 is loaded'); 212 | assert.equal(test10, 10, 'test10 should be set when test10 is loaded'); 213 | } 214 | 215 | if (item.src === '/load-me.js?test11=11') { 216 | assert.ok(window.WebFont); 217 | assert.equal(test11, 11); 218 | assert.equal(test10, 10); 219 | } 220 | }); 221 | }, 222 | 223 | 'test queue loading with remote in the middle': function() { 224 | var assertExpect = mixinExpect(assert); 225 | assertExpect.expect(9); 226 | 227 | $t.require([ 228 | '/load-me.js?test12=12', 229 | 'https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js', 230 | '/load-me.js?test13=13' 231 | ]).on('complete', function() { 232 | assertExpect.ok(true); 233 | assertExpect.done(); 234 | }).on('loaded', function(item) { 235 | if (item.src === 'https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js') { 236 | assertExpect.equal(typeof test13, 'undefined', 'test13 should not be set when remote is loaded'); 237 | assertExpect.ok(window.swfobject, 'swfobject should be set'); 238 | } 239 | 240 | if (item.src === '/load-me.js?test12=12') { 241 | assertExpect.ok(!window.swfobject, 'swfobject should not be set when test12 is loaded'); 242 | assertExpect.equal(typeof test13, 'undefined', 'test13 should not be set when test12 is loaded'); 243 | assertExpect.equal(test12, 12, 'test12 should be set when test12 is loaded'); 244 | } 245 | 246 | if (item.src === '/load-me.js?test13=13') { 247 | assertExpect.ok(window.swfobject); 248 | assertExpect.equal(test13, 13); 249 | assertExpect.equal(test12, 12); 250 | } 251 | }); 252 | }, 253 | 254 | 'test queue loading with no local scripts': function() { 255 | var expect = new Expect(); 256 | expect.add('jQuery was set'); 257 | expect.add('loaded fired'); 258 | 259 | $t.require([ 260 | 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js' 261 | ]).on('complete', function() { 262 | assert.ok(true); 263 | expect.fulfill('loaded fired'); 264 | expect.done(); 265 | }).on('loaded', function(item) { 266 | if (item.src === 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js') { 267 | expect.fulfill('jQuery was set'); 268 | assert.ok(jQuery, 'jQuery should be set'); 269 | } 270 | }); 271 | } 272 | }; 273 | 274 | test.run(exports); 275 | -------------------------------------------------------------------------------- /turing.require.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Core 3 | * Copyright (C) 2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Contains everything relating to the `require` module. 9 | */ 10 | define('turing.require', ['turing.core'], function(turing) { 11 | var appendTo = document.head || document.getElementsByTagName('head'), 12 | scriptOptions = ['async', 'defer', 'src', 'text']; 13 | 14 | /** 15 | * Used to determine if a script is from the same origin. 16 | * 17 | * @param {String} Path to a script 18 | * @return {Boolean} True when `src` is from the same origin 19 | */ 20 | function isSameOrigin(src) { 21 | return src.charAt(0) === '/' 22 | || src.indexOf(location.protocol + '//' + location.host) !== -1 23 | || false; 24 | } 25 | 26 | /** 27 | * Creates a script tag from a set of options. 28 | * 29 | * Options may include: `async`, `defer`, `src`, and `text`. 30 | * 31 | * @param {Object} The options 32 | * @return {Object} The script tag's DOM object 33 | */ 34 | function createScript(options) { 35 | var script = document.createElement('script'), 36 | key; 37 | 38 | for (key in scriptOptions) { 39 | key = scriptOptions[key]; 40 | 41 | if (options[key]) { 42 | script[key] = options[key]; 43 | } 44 | } 45 | 46 | return script; 47 | } 48 | 49 | /** 50 | * Inserts a script tag into the document. 51 | * 52 | * @param {String} The script tag 53 | */ 54 | function insertScript(script) { 55 | appendTo.insertBefore(script, appendTo.firstChild); 56 | } 57 | 58 | /** 59 | * Loads scripts using script tag insertion. 60 | * 61 | * @param {String} The script path 62 | * @param {Object} A configuration object 63 | * @param {Function} A callback 64 | */ 65 | function requireWithScriptInsertion(scriptSrc, options, fn) { 66 | options.src = scriptSrc; 67 | var script = createScript(options); 68 | 69 | script.onload = script.onreadystatechange = function() { 70 | if (!script.readyState || (script.readyState === 'complete' || script.readyState === 'loaded')) { 71 | script.onload = script.onreadystatechange = null; 72 | fn(); 73 | appendTo.removeChild(script); 74 | } 75 | }; 76 | 77 | insertScript(script, options, fn); 78 | } 79 | 80 | /** 81 | * Loads scripts using XMLHttpRequest. 82 | * 83 | * @param {String} The script path 84 | * @param {Object} A configuration object 85 | * @param {Function} A callback 86 | */ 87 | function requireWithXMLHttpRequest(scriptSrc, options, fn) { 88 | if (!isSameOrigin(scriptSrc)) { 89 | throw('Scripts loaded with XMLHttpRequest must be from the same origin'); 90 | } 91 | 92 | if (!turing.get) { 93 | throw('Loading scripts with XMLHttpRequest requires turing.net to be loaded'); 94 | } 95 | 96 | turing 97 | .get(scriptSrc) 98 | .end(function(res) { 99 | options.text = res.responseText; 100 | fn(options); 101 | }); 102 | } 103 | 104 | /** 105 | * Parse and run a queue of scripts, preloading where required. 106 | * 107 | * TODO: Handle remote scripts 108 | * 109 | * @param {Array} An array of scripts. 110 | */ 111 | function Queue(sources) { 112 | this.sources = sources; 113 | this.events = new turing.events.Emitter(); 114 | this.queue = []; 115 | this.currentGroup = 0; 116 | this.groups = {}; 117 | this.groupKeys = []; 118 | this.parseQueue(this.sources, false, 0); 119 | 120 | this.installEventHandlers(); 121 | this.pointer = 0; 122 | this.preloadCount = 0; 123 | 124 | var self = this; 125 | runWhenReady(function() { 126 | self.runQueue(); 127 | }); 128 | } 129 | 130 | Queue.prototype = { 131 | on: function() { 132 | this.events.on.apply(this.events, arguments); 133 | return this; 134 | }, 135 | 136 | emit: function() { 137 | this.events.emit.apply(this.events, arguments); 138 | return this; 139 | }, 140 | 141 | installEventHandlers: function() { 142 | var self = this; 143 | 144 | this.on('preloaded', function(groupItem, options) { 145 | var group = self.groups[groupItem.group]; 146 | groupItem.preloaded = true; 147 | groupItem.scriptOptions = options; 148 | self.preloadCount--; 149 | 150 | if (self.preloadCount === 0) { 151 | self.emit('preload-complete'); 152 | } 153 | }); 154 | 155 | this.on('preload-complete', function() { 156 | this.emit('execute-next'); 157 | }); 158 | 159 | this.on('execute-next', function() { 160 | var groupItem = self.nextItem(); 161 | 162 | function completeCallback() { 163 | groupItem.loaded = true; 164 | self.emit('loaded', groupItem); 165 | self.emit('execute-next'); 166 | } 167 | 168 | if (groupItem) { 169 | if (groupItem.preload) { 170 | self.execute(groupItem, completeCallback); 171 | } else { 172 | self.fetchExecute(groupItem, completeCallback); 173 | } 174 | } else { 175 | self.emit('complete'); 176 | } 177 | }); 178 | }, 179 | 180 | nextItem: function() { 181 | var group, i, j, item; 182 | 183 | for (i = 0; i < this.groupKeys.length; i++) { 184 | group = this.groups[this.groupKeys[i]]; 185 | for (j = 0; j < group.length; j++) { 186 | item = group[j]; 187 | if (!item.loaded) { 188 | return item; 189 | } 190 | } 191 | } 192 | }, 193 | 194 | fetchExecute: function(item, fn) { 195 | var self = this; 196 | requireWithScriptInsertion(item.src, { async: true, defer: true }, function() { 197 | fn(); 198 | }); 199 | }, 200 | 201 | execute: function(item, fn) { 202 | if (item && item.scriptOptions) { 203 | script = createScript(item.scriptOptions); 204 | insertScript(script); 205 | appendTo.removeChild(script); 206 | } 207 | 208 | fn(); 209 | }, 210 | 211 | enqueue: function(source, async) { 212 | var preload = isSameOrigin(source), 213 | options; 214 | 215 | options = { 216 | src: source, 217 | preload: preload, 218 | async: async, 219 | group: this.currentGroup 220 | }; 221 | 222 | if (!this.groups[this.currentGroup]) { 223 | this.groups[this.currentGroup] = []; 224 | this.groupKeys.push(this.currentGroup); 225 | } 226 | 227 | this.groups[this.currentGroup].push(options); 228 | }, 229 | 230 | parseQueue: function(sources, async, level) { 231 | var i, source; 232 | for (i = 0; i < sources.length; i++) { 233 | source = sources[i]; 234 | if (turing.isArray(source)) { 235 | this.currentGroup++; 236 | this.parseQueue(source, true, level + 1); 237 | } else { 238 | if (level === 0) { 239 | this.currentGroup++; 240 | } 241 | this.enqueue(source, async); 242 | } 243 | } 244 | }, 245 | 246 | runQueue: function() { 247 | // Preload everything that can be preloaded 248 | this.preloadAll(); 249 | }, 250 | 251 | preloadAll: function() { 252 | var i, g, group, item, self = this; 253 | for (g = 0; g < this.groupKeys.length; g++) { 254 | group = this.groups[this.groupKeys[g]]; 255 | 256 | for (i = 0; i < group.length; i++ ) { 257 | item = group[i]; 258 | 259 | if (item.preload) { 260 | this.preloadCount++; 261 | (function(groupItem) { 262 | requireWithXMLHttpRequest(groupItem.src, {}, function(script) { 263 | self.emit('preloaded', groupItem, script); 264 | }) 265 | }(item)); 266 | } 267 | } 268 | } 269 | 270 | if (this.preloadCount === 0) { 271 | this.emit('execute-next'); 272 | } 273 | } 274 | }; 275 | 276 | function runWhenReady(fn) { 277 | setTimeout(function() { 278 | if ('item' in appendTo) { 279 | if (!appendTo[0]) { 280 | return setTimeout(arguments.callee, 25); 281 | } 282 | 283 | appendTo = appendTo[0]; 284 | } 285 | 286 | fn(); 287 | }); 288 | } 289 | 290 | /** 291 | * Non-blocking script loading. 292 | * 293 | * @param {String} The script path 294 | * @param {Object} A configuration object. Options: {Boolean} `defer`, {Boolean} `async` 295 | * @param {Function} A callback 296 | */ 297 | turing.require = function(scriptSrc, options, fn) { 298 | options = options || {}; 299 | fn = fn || function() {}; 300 | 301 | if (turing.isArray(scriptSrc)) { 302 | return new Queue(scriptSrc); 303 | } 304 | 305 | runWhenReady(function() { 306 | switch (options.transport) { 307 | case 'XMLHttpRequest': 308 | return requireWithXMLHttpRequest(scriptSrc, options, function(options) { 309 | var script = createScript(options); 310 | insertScript(script); 311 | appendTo.removeChild(script); 312 | fn(); 313 | }); 314 | 315 | case 'scriptInsertion': 316 | return requireWithScriptInsertion(scriptSrc, options, fn); 317 | 318 | default: 319 | return requireWithScriptInsertion(scriptSrc, options, fn); 320 | } 321 | }); 322 | }; 323 | 324 | turing.require.isSameOrigin = isSameOrigin; 325 | return turing.require; 326 | }); 327 | -------------------------------------------------------------------------------- /turing.net.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Net 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing Net module (Ajax). 9 | */ 10 | define('turing.net', ['turing.core', 'turing.dom'], function(turing) { 11 | var net = {}; 12 | 13 | /** 14 | * Ajax request options: 15 | * 16 | * - `method`: {String} HTTP method - GET, POST, etc. 17 | * - `success`: {Function} A callback to run when a request is successful 18 | * - `error`: {Function} A callback to run when the request fails 19 | * - `asynchronous`: {Boolean} Defaults to asynchronous 20 | * - `postBody`: {String} The HTTP POST body 21 | * - `contentType`: {String} The content type of the request, default is `application/x-www-form-urlencoded` 22 | * 23 | */ 24 | 25 | /** 26 | * Removes leading and trailing whitespace. 27 | * @param {String} 28 | * @return {String} 29 | */ 30 | var trim = ''.trim 31 | ? function(s) { return s.trim(); } 32 | : function(s) { return s.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }; 33 | 34 | function xhr() { 35 | if (typeof XMLHttpRequest !== 'undefined' && (window.location.protocol !== 'file:' || !window.ActiveXObject)) { 36 | return new XMLHttpRequest(); 37 | } else { 38 | try { 39 | return new ActiveXObject('Msxml2.XMLHTTP.6.0'); 40 | } catch(e) { } 41 | try { 42 | return new ActiveXObject('Msxml2.XMLHTTP.3.0'); 43 | } catch(e) { } 44 | try { 45 | return new ActiveXObject('Msxml2.XMLHTTP'); 46 | } catch(e) { } 47 | } 48 | return false; 49 | } 50 | 51 | function successfulRequest(request) { 52 | return (request.status >= 200 && request.status < 300) || 53 | request.status == 304 || 54 | (request.status == 0 && request.responseText); 55 | } 56 | 57 | /** 58 | * Serialize JavaScript for HTTP requests. 59 | * 60 | * @param {Object} object An Array or Object 61 | * @returns {String} A string suitable for a GET or POST request 62 | */ 63 | net.serialize = function(object) { 64 | if (!object) return; 65 | 66 | if (typeof object === 'string') { 67 | return object; 68 | } 69 | 70 | var results = []; 71 | for (var key in object) { 72 | results.push(encodeURIComponent(key) + '=' + encodeURIComponent(object[key])); 73 | } 74 | return results.join('&'); 75 | }; 76 | 77 | /** 78 | * JSON.parse support can be inferred using `turing.detect('JSON.parse')`. 79 | */ 80 | turing.addDetectionTest('JSON.parse', function() { 81 | return window.JSON && window.JSON.parse; 82 | }); 83 | 84 | /** 85 | * Parses JSON represented as a string. 86 | * 87 | * @param {String} string The original string 88 | * @returns {Object} A JavaScript object 89 | */ 90 | net.parseJSON = function(string) { 91 | if (typeof string !== 'string' || !string) return null; 92 | string = trim(string); 93 | return turing.detect('JSON.parse') ? 94 | window.JSON.parse(string) : 95 | (new Function('return ' + string))(); 96 | }; 97 | 98 | /** 99 | * Parses XML represented as a string. 100 | * 101 | * @param {String} string The original string 102 | * @returns {Object} A JavaScript object 103 | */ 104 | if (window.DOMParser) { 105 | net.parseXML = function(text) { 106 | return new DOMParser().parseFromString(text, 'text/xml'); 107 | }; 108 | } else { 109 | net.parseXML = function(text) { 110 | var xml = new ActiveXObject('Microsoft.XMLDOM'); 111 | xml.async = 'false'; 112 | xml.loadXML(text); 113 | return xml; 114 | }; 115 | } 116 | 117 | /** 118 | * Creates an Ajax request. Returns an object that can be used 119 | * to chain calls. For example: 120 | * 121 | * $t.post('/post-test') 122 | * .data({ key: 'value' }) 123 | * .end(function(res) { 124 | * assert.equal('value', res.responseText); 125 | * }); 126 | * 127 | * $t.get('/get-test') 128 | * .set('Accept', 'text/html') 129 | * .end(function(res) { 130 | * assert.equal('Sample text', res.responseText); 131 | * }); 132 | * 133 | * The available chained methods are: 134 | * 135 | * `set` -- set a HTTP header 136 | * `data` -- the postBody 137 | * `end` -- send the request over the network, and calls your callback with a `res` object 138 | * `send` -- sends the request and calls `data`: `.send({ data: value }, function(res) { });` 139 | * 140 | * @param {String} The URL to call 141 | * @param {Object} Optional settings 142 | * @returns {Object} A chainable object for further configuration 143 | */ 144 | function ajax(url, options) { 145 | var request = xhr(), 146 | promise, 147 | then, 148 | response = {}, 149 | chain; 150 | 151 | if (turing.Promise) { 152 | promise = new turing.Promise(); 153 | } 154 | 155 | function respondToReadyState(readyState) { 156 | if (request.readyState == 4) { 157 | var contentType = request.mimeType || request.getResponseHeader('content-type') || ''; 158 | 159 | response.status = request.status; 160 | response.responseText = request.responseText; 161 | if (/json/.test(contentType)) { 162 | response.responseJSON = net.parseJSON(request.responseText); 163 | } else if (/xml/.test(contentType)) { 164 | response.responseXML = net.parseXML(request.responseText); 165 | } 166 | 167 | response.success = successfulRequest(request); 168 | 169 | if (options.callback) { 170 | return options.callback(response, request); 171 | } 172 | 173 | if (response.success) { 174 | if (options.success) options.success(response, request); 175 | if (promise) promise.resolve(response, request); 176 | } else { 177 | if (options.error) options.error(response, request); 178 | if (promise) promise.reject(response, request); 179 | } 180 | } 181 | } 182 | 183 | // Set the HTTP headers 184 | function setHeaders() { 185 | var defaults = { 186 | 'Accept': 'text/javascript, application/json, text/html, application/xml, text/xml, */*', 187 | 'Content-Type': 'application/x-www-form-urlencoded' 188 | }; 189 | 190 | /** 191 | * Merge headers with defaults. 192 | */ 193 | for (var name in defaults) { 194 | if (!options.headers.hasOwnProperty(name)) 195 | options.headers[name] = defaults[name]; 196 | } 197 | 198 | for (var name in options.headers) { 199 | request.setRequestHeader(name, options.headers[name]); 200 | } 201 | } 202 | 203 | if (typeof options === 'undefined') options = {}; 204 | 205 | options.method = options.method ? options.method.toLowerCase() : 'get'; 206 | options.asynchronous = options.asynchronous || true; 207 | options.postBody = options.postBody || ''; 208 | request.onreadystatechange = respondToReadyState; 209 | request.open(options.method, url, options.asynchronous); 210 | 211 | options.headers = options.headers || {}; 212 | if (options.contentType) { 213 | options.headers['Content-Type'] = options.contentType; 214 | } 215 | 216 | if (typeof options.postBody !== 'string') { 217 | // Serialize JavaScript 218 | options.postBody = net.serialize(options.postBody); 219 | } 220 | 221 | setHeaders(); 222 | 223 | function send() { 224 | try { 225 | request.send(options.postBody); 226 | } catch (e) { 227 | if (options.error) { 228 | options.error(); 229 | } 230 | } 231 | } 232 | 233 | chain = { 234 | set: function(key, value) { 235 | options.headers[key] = value; 236 | return chain; 237 | }, 238 | 239 | send: function(data, callback) { 240 | options.postBody = net.serialize(data); 241 | options.callback = callback; 242 | send(); 243 | return chain; 244 | }, 245 | 246 | end: function(callback) { 247 | options.callback = callback; 248 | send(); 249 | return chain; 250 | }, 251 | 252 | data: function(data) { 253 | options.postBody = net.serialize(data); 254 | return chain; 255 | }, 256 | 257 | then: function() { 258 | chain.end(); 259 | if (promise) promise.then.apply(promise, arguments); 260 | return chain; 261 | } 262 | }; 263 | 264 | return chain; 265 | } 266 | 267 | function JSONPCallback(url, success, failure) { 268 | var self = this; 269 | this.url = url; 270 | this.methodName = '__turing_jsonp_' + parseInt(new Date().getTime()); 271 | this.success = success; 272 | this.failure = failure; 273 | 274 | function runCallback(json) { 275 | self.success(json); 276 | self.teardown(); 277 | } 278 | 279 | window[this.methodName] = runCallback; 280 | } 281 | 282 | JSONPCallback.prototype.run = function() { 283 | this.scriptTag = document.createElement('script'); 284 | this.scriptTag.id = this.methodName; 285 | this.scriptTag.src = this.url.replace('{callback}', this.methodName); 286 | document.body.appendChild(this.scriptTag); 287 | } 288 | 289 | JSONPCallback.prototype.teardown = function() { 290 | window[this.methodName] = null; 291 | delete window[this.methodName]; 292 | if (this.scriptTag) { 293 | document.body.removeChild(this.scriptTag); 294 | } 295 | } 296 | 297 | /** 298 | * An Ajax GET request. 299 | * 300 | * $t.get('/get-test') 301 | * .set('Accept', 'text/html') 302 | * .end(function(res) { 303 | * assert.equal('Sample text', res.responseText); 304 | * }); 305 | * 306 | * @param {String} url The URL to request 307 | * @param {Object} options The Ajax request options 308 | * @returns {Object} A chainable object for further configuration 309 | */ 310 | net.get = function(url, options) { 311 | if (typeof options === 'undefined') options = {}; 312 | options.method = 'get'; 313 | return ajax(url, options); 314 | }; 315 | 316 | /** 317 | * An Ajax POST request. 318 | * 319 | * $t.post('/post-test') 320 | * .data({ key: 'value' }) 321 | * .end(function(res) { 322 | * assert.equal('value', res.responseText); 323 | * }); 324 | * 325 | * @param {String} url The URL to request 326 | * @param {Object} options The Ajax request options (`postBody` may come in handy here) 327 | * @returns {Object} An object for further chaining with promises 328 | */ 329 | net.post = function(url, options) { 330 | if (typeof options === 'undefined') options = {}; 331 | options.method = 'post'; 332 | return ajax(url, options); 333 | }; 334 | 335 | /** 336 | * A jsonp request. Example: 337 | * 338 | * var url = 'http://feeds.delicious.com/v1/json/'; 339 | * url += 'alex_young/javascript?callback={callback}'; 340 | * 341 | * turing.net.jsonp(url, { 342 | * success: function(json) { 343 | * console.log(json); 344 | * } 345 | * }); 346 | * 347 | * @param {String} url The URL to request 348 | */ 349 | net.jsonp = function(url, options) { 350 | if (typeof options === 'undefined') options = {}; 351 | var callback = new JSONPCallback(url, options.success, options.failure); 352 | callback.run(); 353 | }; 354 | 355 | /** 356 | * The Ajax methods are mapped to the `turing` object: 357 | * 358 | * turing.get(); 359 | * turing.post(); 360 | * turing.json(); 361 | * 362 | */ 363 | turing.get = net.get; 364 | turing.post = net.post; 365 | turing.jsonp = net.jsonp; 366 | 367 | net.ajax = ajax; 368 | turing.net = net; 369 | }); 370 | -------------------------------------------------------------------------------- /test/functional/public/require/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS 1.0.3 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | var requirejs,require,define; 7 | (function(){function J(a){return M.call(a)==="[object Function]"}function E(a){return M.call(a)==="[object Array]"}function Z(a,c,h){for(var k in c)if(!(k in K)&&(!(k in a)||h))a[k]=c[k];return d}function N(a,c,d){a=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+a);if(d)a.originalError=d;return a}function $(a,c,d){var k,j,q;for(k=0;q=c[k];k++){q=typeof q==="string"?{name:q}:q;j=q.location;if(d&&(!j||j.indexOf("/")!==0&&j.indexOf(":")===-1))j=d+"/"+(j||q.name);a[q.name]={name:q.name,location:j|| 8 | q.name,main:(q.main||"main").replace(ea,"").replace(aa,"")}}}function U(a,c){a.holdReady?a.holdReady(c):c?a.readyWait+=1:a.ready(!0)}function fa(a){function c(b,l){var f,a;if(b&&b.charAt(0)===".")if(l){p.pkgs[l]?l=[l]:(l=l.split("/"),l=l.slice(0,l.length-1));f=b=l.concat(b.split("/"));var c;for(a=0;c=f[a];a++)if(c===".")f.splice(a,1),a-=1;else if(c==="..")if(a===1&&(f[2]===".."||f[0]===".."))break;else a>0&&(f.splice(a-1,2),a-=2);a=p.pkgs[f=b[0]];b=b.join("/");a&&b===f+"/"+a.main&&(b=f)}else b.indexOf("./")=== 9 | 0&&(b=b.substring(2));return b}function h(b,l){var f=b?b.indexOf("!"):-1,a=null,d=l?l.name:null,i=b,e,h;f!==-1&&(a=b.substring(0,f),b=b.substring(f+1,b.length));a&&(a=c(a,d));b&&(a?e=(f=m[a])&&f.normalize?f.normalize(b,function(b){return c(b,d)}):c(b,d):(e=c(b,d),h=E[e],h||(h=g.nameToUrl(e,null,l),E[e]=h)));return{prefix:a,name:e,parentMap:l,url:h,originalName:i,fullName:a?a+"!"+(e||""):e}}function k(){var b=!0,l=p.priorityWait,f,a;if(l){for(a=0;f=l[a];a++)if(!s[f]){b=!1;break}b&&delete p.priorityWait}return b} 10 | function j(b,l,f){return function(){var a=ga.call(arguments,0),c;if(f&&J(c=a[a.length-1]))c.__requireJsBuild=!0;a.push(l);return b.apply(null,a)}}function q(b,l){var a=j(g.require,b,l);Z(a,{nameToUrl:j(g.nameToUrl,b),toUrl:j(g.toUrl,b),defined:j(g.requireDefined,b),specified:j(g.requireSpecified,b),isBrowser:d.isBrowser});return a}function o(b){var l,a,c,B=b.callback,i=b.map,e=i.fullName,ba=b.deps;c=b.listeners;if(B&&J(B)){if(p.catchError.define)try{a=d.execCb(e,b.callback,ba,m[e])}catch(k){l=k}else a= 11 | d.execCb(e,b.callback,ba,m[e]);if(e)(B=b.cjsModule)&&B.exports!==void 0&&B.exports!==m[e]?a=m[e]=b.cjsModule.exports:a===void 0&&b.usingExports?a=m[e]:(m[e]=a,F[e]&&(Q[e]=!0))}else e&&(a=m[e]=B,F[e]&&(Q[e]=!0));if(C[b.id])delete C[b.id],b.isDone=!0,g.waitCount-=1,g.waitCount===0&&(I=[]);delete R[e];if(d.onResourceLoad&&!b.placeholder)d.onResourceLoad(g,i,b.depArray);if(l)return a=(e?h(e).url:"")||l.fileName||l.sourceURL,c=l.moduleTree,l=N("defineerror",'Error evaluating module "'+e+'" at location "'+ 12 | a+'":\n'+l+"\nfileName:"+a+"\nlineNumber: "+(l.lineNumber||l.line),l),l.moduleName=e,l.moduleTree=c,d.onError(l);for(l=0;B=c[l];l++)B(a)}function r(b,a){return function(f){b.depDone[a]||(b.depDone[a]=!0,b.deps[a]=f,b.depCount-=1,b.depCount||o(b))}}function u(b,a){var f=a.map,c=f.fullName,h=f.name,i=L[b]||(L[b]=m[b]),e;if(!a.loading)a.loading=!0,e=function(b){a.callback=function(){return b};o(a);s[a.id]=!0;w()},e.fromText=function(b,a){var l=O;s[b]=!1;g.scriptCount+=1;g.fake[b]=!0;l&&(O=!1);d.exec(a); 13 | l&&(O=!0);g.completeLoad(b)},c in m?e(m[c]):i.load(h,q(f.parentMap,!0),e,p)}function v(b){C[b.id]||(C[b.id]=b,I.push(b),g.waitCount+=1)}function D(b){this.listeners.push(b)}function t(b,a){var f=b.fullName,c=b.prefix,d=c?L[c]||(L[c]=m[c]):null,i,e;f&&(i=R[f]);if(!i&&(e=!0,i={id:(c&&!d?M++ +"__p@:":"")+(f||"__r@"+M++),map:b,depCount:0,depDone:[],depCallbacks:[],deps:[],listeners:[],add:D},x[i.id]=!0,f&&(!c||L[c])))R[f]=i;c&&!d?(f=t(h(c),!0),f.add(function(){var a=h(b.originalName,b.parentMap),a=t(a, 14 | !0);i.placeholder=!0;a.add(function(b){i.callback=function(){return b};o(i)})})):e&&a&&(s[i.id]=!1,g.paused.push(i),v(i));return i}function A(b,a,f,c){var b=h(b,c),d=b.name,i=b.fullName,e=t(b),k=e.id,j=e.deps,n;if(i){if(i in m||s[k]===!0||i==="jquery"&&p.jQuery&&p.jQuery!==f().fn.jquery)return;x[k]=!0;s[k]=!0;i==="jquery"&&f&&V(f())}e.depArray=a;e.callback=f;for(f=0;f0)){if(p.priorityWait)if(k())w();else return;for(j in s)if(!(j in K)&&(c=!0,!s[j]))if(a)b+=j+" ";else{h=!0;break}if(c||g.waitCount){if(a&&b)return j=N("timeout","Load timeout for modules: "+b),j.requireType="timeout",j.requireModules=b,d.onError(j);if(h||g.scriptCount){if((G||ca)&&!W)W=setTimeout(function(){W=0;z()},50)}else{if(g.waitCount){for(H= 17 | 0;b=I[H];H++)y(b,{});g.paused.length&&w();X<5&&(X+=1,z())}X=0;d.checkReadyState()}}}}var g,w,p={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},catchError:{}},P=[],x={require:!0,exports:!0,module:!0},E={},m={},s={},C={},I=[],S={},M=0,R={},L={},F={},Q={},Y=0;V=function(b){if(!g.jQuery&&(b=b||(typeof jQuery!=="undefined"?jQuery:null))&&!(p.jQuery&&b.fn.jquery!==p.jQuery)&&("holdReady"in b||"readyWait"in b))if(g.jQuery=b,n(["jquery",[],function(){return jQuery}]),g.scriptCount)U(b,!0),g.jQueryIncremented= 18 | !0};w=function(){var b,a,c,h,j,i;Y+=1;if(g.scriptCount<=0)g.scriptCount=0;for(;P.length;)if(b=P.shift(),b[0]===null)return d.onError(N("mismatch","Mismatched anonymous define() module: "+b[b.length-1]));else n(b);if(!p.priorityWait||k())for(;g.paused.length;){j=g.paused;g.pausedCount+=j.length;g.paused=[];for(h=0;b=j[h];h++)a=b.map,c=a.url,i=a.fullName,a.prefix?u(a.prefix,b):!S[c]&&!s[i]&&(d.load(g,i,c),c.indexOf("empty:")!==0&&(S[c]=!0));g.startTime=(new Date).getTime();g.pausedCount-=j.length}Y=== 19 | 1&&z();Y-=1};g={contextName:a,config:p,defQueue:P,waiting:C,waitCount:0,specified:x,loaded:s,urlMap:E,urlFetched:S,scriptCount:0,defined:m,paused:[],pausedCount:0,plugins:L,needFullExec:F,fake:{},fullExec:Q,managerCallbacks:R,makeModuleMap:h,normalize:c,configure:function(b){var a,c,d;b.baseUrl&&b.baseUrl.charAt(b.baseUrl.length-1)!=="/"&&(b.baseUrl+="/");a=p.paths;d=p.pkgs;Z(p,b,!0);if(b.paths){for(c in b.paths)c in K||(a[c]=b.paths[c]);p.paths=a}if((a=b.packagePaths)||b.packages){if(a)for(c in a)c in 20 | K||$(d,a[c],c);b.packages&&$(d,b.packages);p.pkgs=d}if(b.priority)c=g.requireWait,g.requireWait=!1,g.takeGlobalQueue(),w(),g.require(b.priority),w(),g.requireWait=c,p.priorityWait=b.priority;if(b.deps||b.callback)g.require(b.deps||[],b.callback)},requireDefined:function(b,a){return h(b,a).fullName in m},requireSpecified:function(b,a){return h(b,a).fullName in x},require:function(b,c,f){if(typeof b==="string"){if(J(c))return d.onError(N("requireargs","Invalid require call"));if(d.get)return d.get(g, 21 | b,c);c=h(b,c);b=c.fullName;return!(b in m)?d.onError(N("notloaded","Module name '"+c.fullName+"' has not been loaded yet for context: "+a)):m[b]}(b&&b.length||c)&&A(null,b,c,f);if(!g.requireWait)for(;!g.scriptCount&&g.paused.length;)g.takeGlobalQueue(),w();return g.require},takeGlobalQueue:function(){T.length&&(ha.apply(g.defQueue,[g.defQueue.length-1,0].concat(T)),T=[])},completeLoad:function(b){var a;for(g.takeGlobalQueue();P.length;)if(a=P.shift(),a[0]===null){a[0]=b;break}else if(a[0]===b)break; 22 | else n(a),a=null;a?n(a):n([b,[],b==="jquery"&&typeof jQuery!=="undefined"?function(){return jQuery}:null]);d.isAsync&&(g.scriptCount-=1);w();d.isAsync||(g.scriptCount-=1)},toUrl:function(a,c){var d=a.lastIndexOf("."),h=null;d!==-1&&(h=a.substring(d,a.length),a=a.substring(0,d));return g.nameToUrl(a,h,c)},nameToUrl:function(a,h,f){var j,k,i,e,m=g.config,a=c(a,f&&f.fullName);if(d.jsExtRegExp.test(a))h=a+(h?h:"");else{j=m.paths;k=m.pkgs;f=a.split("/");for(e=f.length;e>0;e--)if(i=f.slice(0,e).join("/"), 23 | j[i]){f.splice(0,e,j[i]);break}else if(i=k[i]){a=a===i.name?i.location+"/"+i.main:i.location;f.splice(0,e,a);break}h=f.join("/")+(h||".js");h=(h.charAt(0)==="/"||h.match(/^\w+:/)?"":m.baseUrl)+h}return m.urlArgs?h+((h.indexOf("?")===-1?"?":"&")+m.urlArgs):h}};g.jQueryCheck=V;g.resume=w;return g}function ia(){var a,c,d;if(n&&n.readyState==="interactive")return n;a=document.getElementsByTagName("script");for(c=a.length-1;c>-1&&(d=a[c]);c--)if(d.readyState==="interactive")return n=d;return null}var ja= 24 | /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ka=/require\(\s*["']([^'"\s]+)["']\s*\)/g,ea=/^\.\//,aa=/\.js$/,M=Object.prototype.toString,r=Array.prototype,ga=r.slice,ha=r.splice,G=!!(typeof window!=="undefined"&&navigator&&document),ca=!G&&typeof importScripts!=="undefined",la=G&&navigator.platform==="PLAYSTATION 3"?/^complete$/:/^(complete|loaded)$/,da=typeof opera!=="undefined"&&opera.toString()==="[object Opera]",K={},D={},T=[],n=null,X=0,O=!1,d,r={},I,v,t,x,u,y,z,H,A,V,W;if(typeof define==="undefined"){if(typeof requirejs!== 25 | "undefined")if(J(requirejs))return;else r=requirejs,requirejs=void 0;typeof require!=="undefined"&&!J(require)&&(r=require,require=void 0);d=requirejs=function(a,c,d){var k="_",j;!E(a)&&typeof a!=="string"&&(j=a,E(c)?(a=c,c=d):a=[]);if(j&&j.context)k=j.context;d=D[k]||(D[k]=fa(k));j&&d.configure(j);return d.require(a,c)};d.config=function(a){return d(a)};require||(require=d);d.toUrl=function(a){return D._.toUrl(a)};d.version="1.0.3";d.jsExtRegExp=/^\/|:|\?|\.js$/;v=d.s={contexts:D,skipAsync:{}};if(d.isAsync= 26 | d.isBrowser=G)if(t=v.head=document.getElementsByTagName("head")[0],x=document.getElementsByTagName("base")[0])t=v.head=x.parentNode;d.onError=function(a){throw a;};d.load=function(a,c,h){d.resourcesReady(!1);a.scriptCount+=1;d.attach(h,a,c);if(a.jQuery&&!a.jQueryIncremented)U(a.jQuery,!0),a.jQueryIncremented=!0};define=function(a,c,d){var k,j;typeof a!=="string"&&(d=c,c=a,a=null);E(c)||(d=c,c=[]);!c.length&&J(d)&&d.length&&(d.toString().replace(ja,"").replace(ka,function(a,d){c.push(d)}),c=(d.length=== 27 | 1?["require"]:["require","exports","module"]).concat(c));if(O&&(k=I||ia()))a||(a=k.getAttribute("data-requiremodule")),j=D[k.getAttribute("data-requirecontext")];(j?j.defQueue:T).push([a,c,d])};define.amd={multiversion:!0,plugins:!0,jQuery:!0};d.exec=function(a){return eval(a)};d.execCb=function(a,c,d,k){return c.apply(k,d)};d.addScriptToDom=function(a){I=a;x?t.insertBefore(a,x):t.appendChild(a);I=null};d.onScriptLoad=function(a){var c=a.currentTarget||a.srcElement,h;if(a.type==="load"||c&&la.test(c.readyState))n= 28 | null,a=c.getAttribute("data-requirecontext"),h=c.getAttribute("data-requiremodule"),D[a].completeLoad(h),c.detachEvent&&!da?c.detachEvent("onreadystatechange",d.onScriptLoad):c.removeEventListener("load",d.onScriptLoad,!1)};d.attach=function(a,c,h,k,j,n){var o;if(G)return k=k||d.onScriptLoad,o=c&&c.config&&c.config.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"),o.type=j||"text/javascript",o.charset="utf-8",o.async=!v.skipAsync[a],c&&o.setAttribute("data-requirecontext", 29 | c.contextName),o.setAttribute("data-requiremodule",h),o.attachEvent&&!da?(O=!0,n?o.onreadystatechange=function(){if(o.readyState==="loaded")o.onreadystatechange=null,o.attachEvent("onreadystatechange",k),n(o)}:o.attachEvent("onreadystatechange",k)):o.addEventListener("load",k,!1),o.src=a,n||d.addScriptToDom(o),o;else ca&&(importScripts(a),c.completeLoad(h));return null};if(G){u=document.getElementsByTagName("script");for(H=u.length-1;H>-1&&(y=u[H]);H--){if(!t)t=y.parentNode;if(z=y.getAttribute("data-main")){if(!r.baseUrl)u= 30 | z.split("/"),y=u.pop(),u=u.length?u.join("/")+"/":"./",r.baseUrl=u,z=y.replace(aa,"");r.deps=r.deps?r.deps.concat(z):[z];break}}}d.checkReadyState=function(){var a=v.contexts,c;for(c in a)if(!(c in K)&&a[c].waitCount)return;d.resourcesReady(!0)};d.resourcesReady=function(a){var c,h;d.resourcesDone=a;if(d.resourcesDone)for(h in a=v.contexts,a)if(!(h in K)&&(c=a[h],c.jQueryIncremented))U(c.jQuery,!1),c.jQueryIncremented=!1};d.pageLoaded=function(){if(document.readyState!=="complete")document.readyState= 31 | "complete"};if(G&&document.addEventListener&&!document.readyState)document.readyState="loading",window.addEventListener("load",d.pageLoaded,!1);d(r);if(d.isAsync&&typeof setTimeout!=="undefined")A=v.contexts[r.context||"_"],A.requireWait=!0,setTimeout(function(){A.requireWait=!1;A.takeGlobalQueue();A.scriptCount||A.resume();d.checkReadyState()},0)}})(); 32 | -------------------------------------------------------------------------------- /test/dom_test.js: -------------------------------------------------------------------------------- 1 | require.paths.unshift('./turing-test/lib'); 2 | 3 | if (typeof turing === 'undefined') 4 | turing = require('../turing.core.js').turing; 5 | 6 | var test = require('test'), 7 | assert = require('assert'); 8 | 9 | require('../turing.dom.js'); 10 | 11 | exports.testDOM = { 12 | 'test tokenization': function() { 13 | assert.equal('class', turing.dom.tokenize('.link').finders(), 'return class for .link'); 14 | assert.equal('name and class', turing.dom.tokenize('a.link').finders(), 'return class and name for a.link'); 15 | }, 16 | 17 | 'test selector finders': function() { 18 | assert.equal('dom-test', turing.dom.get('#dom-test')[0].id, 'find with id'); 19 | assert.equal('dom-test', turing.dom.get('div#dom-test')[0].id, 'find with id and name'); 20 | assert.equal('Example Link', turing.dom.get('a')[0].innerHTML, 'find with tag name'); 21 | assert.equal('Text', turing.dom.get('p')[0].innerHTML, 'find with tag name'); 22 | assert.equal('Example Link', turing.dom.get('#dom-test a.link')[0].innerHTML, 'find a link'); 23 | assert.equal('DIV', turing.dom.get('.example1')[0].nodeName, 'find a class name by itself'); 24 | assert.equal('Example Link', turing.dom.get('div#dom-test div p a.link')[0].innerHTML, 'find a nested link'); 25 | assert.equal('Text', turing.dom.get('.example3 p')[0].innerHTML, 'find a nested tag'); 26 | assert.equal(1, turing.dom.get('.example3 p').length, 'find a nested tag'); 27 | }, 28 | 29 | 'test a selector that does not match anything': function() { 30 | assert.equal('', turing.dom.get('div#massive-explosion .failEarly p.lease'), 'not find anything'); 31 | }, 32 | 33 | 'test chained DOM calls': function() { 34 | assert.equal(1, turing('.example3').find('p').length, 'find a nested tag'); 35 | }, 36 | 37 | 'test a nested element': function() { 38 | var element = turing.dom.get('#dom-test a.link')[0]; 39 | 40 | assert.equal(element, turing.dom.findElement(element, '#dom-test a.link', document), 'find elements with the right selector'); 41 | assert.equal(undefined, turing.dom.findElement(turing.dom.get('#dom-test .example1 p')[0], 'a.link', document), 'not find elements with the wrong selector'); 42 | }, 43 | 44 | 'test HTML can be written': function() { 45 | var element = turing.dom.get('#dom-html-tests')[0]; 46 | turing.dom.html(element, '

        This is a link'); 47 | assert.equal(turing.dom.get('#dom-html-tests p').length, 1); 48 | assert.equal(turing.dom.get('#dom-html-tests a').length, 1); 49 | }, 50 | 51 | 'test chained HTML works on multiple elements': function() { 52 | turing('#dom-html-chain-test p').html('Link'); 53 | assert.equal(turing.dom.get('#dom-html-chain-test p a').length, 4); 54 | }, 55 | 56 | 'test manipulating table rows': function() { 57 | turing('#dom-html-table-test').html('12'); 58 | assert.equal(turing.dom.get('#dom-html-table-test tr').length, 2); 59 | }, 60 | 61 | 'test manipulating table rows with tbody': function() { 62 | turing('#dom-html-table-tbody-test').html('34'); 63 | assert.equal(turing.dom.get('#dom-html-table-tbody-test tr').length, 2); 64 | }, 65 | 66 | 'test HTML can be read': function() { 67 | assert.ok(turing('#dom-html-read-test').html().match(/

        Example/i)); 68 | }, 69 | 70 | 'test HTML can be appended': function() { 71 | turing('#dom-html-append').append('

        Example 2

        '); 72 | assert.ok(turing('#dom-html-append').html().match(/Example[^E]*Example 2/)); 73 | }, 74 | 75 | 'test HTML can be appended to tables': function() { 76 | turing('#dom-table-append').append('X2'); 77 | assert.ok(turing('#dom-table-append').html().match(/X1[^X]*X2/)); 78 | }, 79 | 80 | 'test text nodes can be read': function() { 81 | assert.ok(turing('#dom-html-read-test').text().match(/Example/)); 82 | }, 83 | 84 | 'test text nodes can be written': function() { 85 | turing.dom.text(turing.dom.get('#dom-text-write-test p')[0], 'Written'); 86 | assert.ok(turing.dom.get('#dom-text-write-test p')[0].innerHTML.match(/Written/)); 87 | }, 88 | 89 | 'test chained text nodes can be written': function() { 90 | turing('#dom-text-write-test p').text('Written again'); 91 | assert.ok(turing.dom.get('#dom-text-write-test p')[0].innerHTML.match(/Written again/)); 92 | }, 93 | 94 | 'test nodes can be emptied': function() { 95 | turing.dom.empty(turing.dom.get('#dom-html-empty-test')[0]); 96 | assert.equal(turing.dom.get('#dom-html-empty-test')[0].innerHTML, ''); 97 | }, 98 | 99 | 'test reading style properties': function() { 100 | var element = turing.dom.get('#dom-test')[0], 101 | expected = element.currentStyle ? '#f0f0f0' : 'rgb(240, 240, 240)'; 102 | assert.equal(turing.dom.css(element, 'background-color'), expected); 103 | assert.equal(turing.dom.css(element, 'backgroundColor'), expected); 104 | }, 105 | 106 | 'test chained reading style properties': function() { 107 | var element = turing.dom.get('#dom-test')[0], 108 | expected = element.currentStyle ? '#f0f0f0' : 'rgb(240, 240, 240)'; 109 | assert.equal(turing('#dom-test').css('background-color'), expected); 110 | assert.equal(turing('#dom-test').css('backgroundColor'), expected); 111 | }, 112 | 113 | 'test writing style properties': function() { 114 | var element = turing.dom.get('#dom-test')[0], 115 | expected = element.currentStyle ? '#f5f5f5' : 'rgb(240, 240, 240)'; 116 | 117 | turing.dom.css(element, { 'background-color': expected, 'width': 1000 }); 118 | 119 | assert.equal(turing.dom.css(element, 'background-color'), expected); 120 | assert.equal(turing.dom.css(element, 'backgroundColor'), expected); 121 | assert.equal(turing.dom.css(element, 'width'), '1000px'); 122 | }, 123 | 124 | 'test chained writing style properties': function() { 125 | var element = turing.dom.get('#dom-test')[0], 126 | expected = element.currentStyle ? '#f1f1f1' : 'rgb(240, 240, 240)'; 127 | 128 | turing('#dom-test').css({ 'background-color': expected }); 129 | 130 | assert.equal(turing('#dom-test').css('background-color'), expected); 131 | assert.equal(turing('#dom-test').css('backgroundColor'), expected); 132 | }, 133 | 134 | 'test hasClass': function() { 135 | assert.ok(turing('#attr-test').hasClass('example')); 136 | assert.ok(turing('#hasclass-test').hasClass('some'), 'should fine some'); 137 | assert.ok(turing('#hasclass-test').hasClass('something'), 'should find something'); 138 | assert.ok(turing('#attr-test').hasClass('example-2')); 139 | assert.ok(turing('#attr-test').hasClass('example_3')); 140 | assert.ok(!turing('#attr-test').hasClass('example_')); 141 | }, 142 | 143 | 'test nested hasClass': function() { 144 | assert.ok(turing('#nested-hasClass-test div').hasClass('find-me')); 145 | assert.ok(!turing('#nested-hasClass-test div').hasClass('aaa')); 146 | }, 147 | 148 | 'test adding CSS classes': function() { 149 | var element = turing.dom.get('#dom-test')[0]; 150 | 151 | // Invalid values should be ignored 152 | turing.dom.addClass(element, null); 153 | turing.dom.addClass(element, 10); 154 | 155 | // This should change the className 156 | turing.dom.addClass(element, 'newClass'); 157 | assert.equal(element.className, 'newClass'); 158 | 159 | turing.dom.addClass(element, 'class2'); 160 | assert.equal(element.className, 'newClass class2'); 161 | 162 | // Adding the same one twice should be ignored 163 | turing.dom.addClass(element, 'class2'); 164 | assert.equal(element.className, 'newClass class2'); 165 | 166 | // Reset the value 167 | element.className = ''; 168 | }, 169 | 170 | 'test removing CSS classes': function() { 171 | var element = turing.dom.get('#dom-test')[0], 172 | testClasses = 'class1 class2 class3 class4'; 173 | 174 | // Invalid values should be ignored 175 | turing.dom.removeClass(element, null); 176 | turing.dom.removeClass(element, 10); 177 | 178 | // Test a single class 179 | turing.dom.addClass(element, 'newClass'); 180 | assert.equal(element.className, 'newClass'); 181 | turing.dom.removeClass(element, 'newClass'); 182 | assert.equal(element.className, ''); 183 | 184 | // Test multiple, making sure white space is as it should be 185 | element.className = testClasses; 186 | turing.dom.removeClass(element, 'class2'); 187 | assert.equal(element.className, 'class1 class3 class4'); 188 | 189 | element.className = testClasses; 190 | turing.dom.removeClass(element, 'class1'); 191 | assert.equal(element.className, 'class2 class3 class4'); 192 | 193 | element.className = testClasses; 194 | turing.dom.removeClass(element, 'class4'); 195 | assert.equal(element.className, 'class1 class2 class3'); 196 | 197 | // Reset the value 198 | element.className = ''; 199 | }, 200 | 201 | 'test chained class manipulation API': function() { 202 | turing('p').addClass('x1'); 203 | assert.ok(turing('p')[0].className.match(/\bx1\b/)); 204 | turing('p').removeClass('x1'); 205 | assert.ok(!turing('p')[0].className.match(/\bx1\b/)); 206 | }, 207 | 208 | 'test getting attributes': function() { 209 | var element = turing.dom.get('#attr-test')[0], 210 | link = turing.dom.get('#attr-test a')[0], 211 | input = turing.dom.get('#attr-test form input')[0], 212 | checkbox = turing.dom.get('#checkbox')[0], 213 | button = turing.dom.get('#attr-test form button')[0]; 214 | 215 | // A div 216 | assert.equal(turing.dom.attr(element, 'id'), 'attr-test'); 217 | assert.equal(turing.dom.attr(element, 'class'), 'example example-2 example_3'); 218 | assert.equal(turing.dom.attr(element, 'tabindex'), 9); 219 | 220 | // Links 221 | assert.equal(turing.dom.attr(link, 'href'), '/example'); 222 | 223 | // Forms 224 | assert.equal(turing.dom.attr(input, 'value'), 'example'); 225 | assert.equal(turing.dom.attr(input, 'name'), 'e'); 226 | assert.equal(turing.dom.attr(button, 'name'), 'b'); 227 | assert.equal(turing.dom.attr(button, 'value'), 'example'); 228 | 229 | assert.equal(turing.dom.attr(checkbox, 'checked'), 'checked'); 230 | }, 231 | 232 | 'test getting attributes through the chained API': function() { 233 | assert.equal(turing('#attr-test').attr('id'), 'attr-test'); 234 | }, 235 | 236 | 'test setting attributes': function() { 237 | var element = turing.dom.get('#attr-write')[0], 238 | link = turing.dom.get('#attr-write a')[0], 239 | input = turing.dom.get('#attr-write form input')[0], 240 | button = turing.dom.get('#attr-write form button')[0]; 241 | 242 | turing.dom.attr(element, 'id', 'attr-test2'); 243 | assert.equal(turing.dom.attr(element, 'id'), 'attr-test2'); 244 | 245 | turing.dom.attr(element, 'class', 'example2'); 246 | assert.equal(turing.dom.attr(element, 'class'), 'example2'); 247 | 248 | turing.dom.attr(element, 'tabindex', 1); 249 | assert.equal(turing.dom.attr(element, 'tabindex'), 1); 250 | 251 | turing.dom.attr(link, 'href', '/somewhere'); 252 | assert.equal(turing.dom.attr(link, 'href'), '/somewhere'); 253 | 254 | // Forms 255 | turing.dom.attr(input, 'value', 'changed-value'); 256 | assert.equal(turing.dom.attr(input, 'value'), 'changed-value'); 257 | 258 | turing.dom.attr(input, 'name', 'changed-name'); 259 | assert.equal(turing.dom.attr(input, 'name'), 'changed-name'); 260 | 261 | turing.dom.attr(button, 'name', 'changed-button-name'); 262 | assert.equal(turing.dom.attr(button, 'name'), 'changed-button-name'); 263 | 264 | turing.dom.attr(button, 'value', 'changed-button-value'); 265 | assert.equal(turing.dom.attr(button, 'value'), 'changed-button-value'); 266 | }, 267 | 268 | 'test getting properties': function() { 269 | var checkbox = turing.dom.get('#checkbox')[0]; 270 | 271 | assert.equal(turing.dom.prop(checkbox, 'checked'), true); 272 | assert.equal(turing('#checkbox').prop('checked'), true); 273 | }, 274 | 275 | 'test setting properties': function() { 276 | var checkbox = turing.dom.get('#checkbox')[0]; 277 | 278 | turing.dom.prop(checkbox, 'checked', false); 279 | assert.equal(turing.dom.prop(checkbox, 'checked'), false); 280 | }, 281 | 282 | 'test removing properties': function() { 283 | var checkbox = turing.dom.get('#checkbox')[0]; 284 | 285 | turing.dom.removeProp(checkbox, 'checked'); 286 | assert.ok(turing.dom.prop(checkbox, 'checked') == undefined || turing.dom.prop(checkbox, 'checked') == false); 287 | } 288 | }; 289 | 290 | test.run(exports); 291 | -------------------------------------------------------------------------------- /turing.events.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Events 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing Events module. 9 | * 10 | */ 11 | define('turing.events', ['turing.core', 'turing.dom'], function(turing) { 12 | var events = {}, 13 | cache = [], 14 | onReadyBound = false, 15 | isReady = false, 16 | DOMContentLoaded, 17 | readyCallbacks = [], 18 | Emitter; 19 | 20 | function isValidElement(element) { 21 | return element.nodeType !== 3 && element.nodeType !== 8; 22 | } 23 | 24 | function stop(event) { 25 | event.preventDefault(event); 26 | event.stopPropagation(event); 27 | } 28 | 29 | function fix(event, element) { 30 | if (!event) var event = window.event; 31 | 32 | event.stop = function() { stop(event); }; 33 | 34 | if (typeof event.target === 'undefined') 35 | event.target = event.srcElement || element; 36 | 37 | if (!event.preventDefault) 38 | event.preventDefault = function() { event.returnValue = false; }; 39 | 40 | if (!event.stopPropagation) 41 | event.stopPropagation = function() { event.cancelBubble = true; }; 42 | 43 | if (event.target && event.target.nodeType === 3) 44 | event.target = event.target.parentNode; 45 | 46 | if (event.pageX == null && event.clientX != null) { 47 | var doc = document.documentElement, body = document.body; 48 | event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); 49 | event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); 50 | } 51 | 52 | return event; 53 | } 54 | 55 | function createResponder(element, handler) { 56 | return function(event) { 57 | fix(event, element); 58 | return handler(event); 59 | }; 60 | } 61 | 62 | function removeCachedResponder(element, type, handler) { 63 | var i = 0, responder, j = 0; 64 | for (j = 0; j < cache.length; j++) { 65 | if (cache[j].element !== element 66 | && cache[j].type !== type 67 | && cache[j].handler !== handler) { 68 | cache[i++] = cache[j]; 69 | } else { 70 | responder = cache[j].responder; 71 | } 72 | } 73 | cache.length = i; 74 | return responder; 75 | } 76 | 77 | function ready() { 78 | if (!isReady) { 79 | // Make sure body exists 80 | if (!document.body) { 81 | return setTimeout(ready, 13); 82 | } 83 | 84 | isReady = true; 85 | 86 | for (var i in readyCallbacks) { 87 | readyCallbacks[i](); 88 | } 89 | 90 | readyCallbacks = null; 91 | 92 | // TODO: 93 | // When custom events work properly in IE: 94 | // events.fire(document, 'dom:ready'); 95 | } 96 | } 97 | 98 | // This checks if the DOM is ready recursively 99 | function DOMReadyScrollCheck() { 100 | if (isReady) { 101 | return; 102 | } 103 | 104 | try { 105 | document.documentElement.doScroll('left'); 106 | } catch(e) { 107 | setTimeout(DOMReadyScrollCheck, 1); 108 | return; 109 | } 110 | 111 | ready(); 112 | } 113 | 114 | // DOMContentLoaded cleans up listeners 115 | if (typeof document !== 'undefined') { 116 | if (document.addEventListener) { 117 | DOMContentLoaded = function() { 118 | document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false); 119 | ready(); 120 | }; 121 | } else if (document.attachEvent) { 122 | DOMContentLoaded = function() { 123 | if (document.readyState === 'complete') { 124 | document.detachEvent('onreadystatechange', DOMContentLoaded); 125 | ready(); 126 | } 127 | }; 128 | } 129 | } 130 | 131 | function bindOnReady() { 132 | if (onReadyBound) return; 133 | onReadyBound = true; 134 | 135 | if (document.readyState === 'complete') { 136 | ready(); 137 | } else if (document.addEventListener) { 138 | document.addEventListener('DOMContentLoaded', DOMContentLoaded, false ); 139 | window.addEventListener('load', ready, false); 140 | } else if (document.attachEvent) { 141 | document.attachEvent('onreadystatechange', DOMContentLoaded); 142 | 143 | window.attachEvent('onload', ready); 144 | 145 | // Check to see if the document is ready 146 | var toplevel = false; 147 | try { 148 | toplevel = window.frameElement == null; 149 | } catch(e) {} 150 | 151 | if (document.documentElement.doScroll && toplevel) { 152 | DOMReadyScrollCheck(); 153 | } 154 | } 155 | } 156 | 157 | function IEType(type) { 158 | if (type.match(/:/)) { 159 | return type; 160 | } 161 | return 'on' + type; 162 | } 163 | 164 | /** 165 | * Bind an event to an element. 166 | * 167 | * turing.events.add(element, 'click', function() { 168 | * console.log('Clicked'); 169 | * }); 170 | * 171 | * @param {Object} element A DOM element 172 | * @param {String} type The event name 173 | * @param {Function} handler The event handler 174 | */ 175 | events.add = function(element, type, handler) { 176 | if (!isValidElement(element)) return; 177 | 178 | var responder = createResponder(element, handler); 179 | cache.push({ element: element, type: type, handler: handler, responder: responder }); 180 | 181 | if (type.match(/:/) && element.attachEvent) { 182 | element.attachEvent('ondataavailable', responder); 183 | } else { 184 | if (element.addEventListener) { 185 | element.addEventListener(type, responder, false); 186 | } else if (element.attachEvent) { 187 | element.attachEvent(IEType(type), responder); 188 | } 189 | } 190 | }; 191 | 192 | /** 193 | * Remove an event from an element. 194 | * 195 | * turing.events.add(element, 'click', callback); 196 | * 197 | * @param {Object} element A DOM element 198 | * @param {String} type The event name 199 | * @param {Function} handler The event handler 200 | */ 201 | events.remove = function(element, type, handler) { 202 | if (!isValidElement(element)) return; 203 | var responder = removeCachedResponder(element, type, handler); 204 | 205 | if (document.removeEventListener) { 206 | element.removeEventListener(type, responder, false); 207 | } else { 208 | element.detachEvent(IEType(type), responder); 209 | } 210 | }; 211 | 212 | /** 213 | * Fires an event. 214 | * 215 | * turing.events.fire(element, 'click'); 216 | * 217 | * @param {Object} element A DOM element 218 | * @param {String} type The event name 219 | * @returns {Object} The browser's `fireEvent` or `dispatchEvent` result 220 | */ 221 | events.fire = function(element, type) { 222 | var event; 223 | if (document.createEventObject) { 224 | event = document.createEventObject(); 225 | fix(event, element); 226 | 227 | // This isn't quite ready 228 | if (type.match(/:/)) { 229 | event.eventName = type; 230 | event.eventType = 'ondataavailable'; 231 | return element.fireEvent(event.eventType, event) 232 | } else { 233 | return element.fireEvent(IEType(type), event) 234 | } 235 | } else { 236 | event = document.createEvent('HTMLEvents'); 237 | fix(event, element); 238 | event.eventName = type; 239 | event.initEvent(type, true, true); 240 | return !element.dispatchEvent(event); 241 | } 242 | }; 243 | 244 | /** 245 | * Add a 'DOM ready' callback. 246 | * 247 | * turing.events.ready(function() { 248 | * // The DOM is ready 249 | * }); 250 | * 251 | * @param {Function} callback A callback to run 252 | */ 253 | events.ready = function(callback) { 254 | bindOnReady(); 255 | readyCallbacks.push(callback); 256 | }; 257 | 258 | if (turing.dom !== 'undefined') { 259 | events.delegate = function(element, selector, type, handler) { 260 | return events.add(element, type, function(event) { 261 | var matches = turing.dom.findElement(event.target, selector, event.currentTarget); 262 | if (matches) { 263 | handler(event); 264 | } 265 | }); 266 | }; 267 | } 268 | 269 | /** 270 | * Events can be chained with DOM calls: 271 | * 272 | * turing('p').bind('click', function(e) { 273 | * alert('ouch'); 274 | * }); 275 | * 276 | * The event will be bound to each matching element. 277 | * 278 | */ 279 | events.addDOMethods = function() { 280 | if (typeof turing.domChain === 'undefined') return; 281 | 282 | turing.domChain.bind = function(type, handler) { 283 | var element; 284 | for (var i = 0; i < this.length; i++) { 285 | element = this[i]; 286 | if (handler) { 287 | turing.events.add(element, type, handler); 288 | } else { 289 | turing.events.fire(element, type); 290 | } 291 | } 292 | return this; 293 | }; 294 | 295 | var chainedAliases = ('click dblclick mouseover mouseout mousemove ' + 296 | 'mousedowe mouseup blur focus change keydown ' + 297 | 'keypress keyup resize scroll').split(' '); 298 | 299 | for (var i = 0; i < chainedAliases.length; i++) { 300 | (function(name) { 301 | turing.domChain[name] = function(handler) { 302 | return this.bind(name, handler); 303 | }; 304 | })(chainedAliases[i]); 305 | } 306 | }; 307 | 308 | events.addDOMethods(); 309 | 310 | /** 311 | * A generic event manager, based on Node's EventEmitter: 312 | * 313 | * var EventEmitter = turing.events.Emitter, 314 | * emitter = new EventEmitter(); 315 | * 316 | * emitter.on('testFired', function() { 317 | * assert.ok(true); 318 | * }); 319 | * 320 | * emitter.emit('testFired'); 321 | */ 322 | Emitter = function() { 323 | this.events = {}; 324 | }; 325 | 326 | Emitter.prototype = { 327 | /** 328 | * Adds a listener. Multiple can be added per eventName. Aliased as `on`. 329 | * 330 | * @param {String} eventName The name of the event 331 | * @param {Function} handler A callback 332 | */ 333 | addListener: function(eventName, handler) { 334 | if (eventName in this.events === false) 335 | this.events[eventName] = []; 336 | 337 | this.events[eventName].push(handler); 338 | }, 339 | 340 | /** 341 | * Triggers all matching listeners. 342 | * 343 | * @param {String} eventName The name of the event 344 | * @returns {Boolean} `true` if an event fired 345 | */ 346 | emit: function(eventName) { 347 | var fired = false; 348 | if (eventName in this.events === false) return fired; 349 | 350 | var list = this.events[eventName].slice(); 351 | 352 | for (var i = 0; i < list.length; i++) { 353 | list[i].apply(this, Array.prototype.slice.call(arguments, 1)); 354 | fired = true; 355 | } 356 | 357 | return fired; 358 | }, 359 | 360 | /** 361 | * Removes all matching listeners. 362 | * 363 | * @param {String} eventName The name of the event 364 | * @returns {Boolean} `true` if an event was removed 365 | */ 366 | removeAllListeners: function(eventName) { 367 | if (eventName in this.events === false) return false; 368 | 369 | delete this.events[eventName]; 370 | return true; 371 | }, 372 | 373 | removeListenerAt: function(eventName, i) { 374 | this.events[eventName].splice(i, 1); 375 | }, 376 | 377 | /** 378 | * Removes a listener based on the handler function. 379 | * 380 | * @param {String} eventName The name of the event 381 | * @param {Function} handler The handler function to remove 382 | * @returns {Boolean} `true` if an event was removed 383 | */ 384 | removeListener: function(eventName, handler) { 385 | if (eventName in this.events === false) return false; 386 | 387 | for (var i = 0; i < this.events[eventName].length; i++) { 388 | if (this.events[eventName][i] == handler) { 389 | this.removeListenerAt(eventName, i); 390 | return true; 391 | } 392 | } 393 | 394 | return false; 395 | } 396 | }; 397 | 398 | Emitter.prototype.on = Emitter.prototype.addListener; 399 | 400 | events.Emitter = Emitter; 401 | 402 | /** 403 | * DOM ready event handlers can also be set with: 404 | * 405 | * turing.ready(function() { }); 406 | * 407 | * Or just by passing a function to `turing()`: 408 | * 409 | * turing(function() {} ); 410 | * 411 | */ 412 | turing.ready = events.ready; 413 | turing.events = events; 414 | 415 | turing.init(function(arg) { 416 | if (arguments.length === 1 417 | && typeof arguments[0] === 'function') { 418 | turing.events.ready(arguments[0]); 419 | } 420 | }); 421 | 422 | if (typeof window !== 'undefined' && window.attachEvent && !window.addEventListener) { 423 | window.attachEvent('onunload', function() { 424 | for (var i = 0; i < cache.length; i++) { 425 | try { 426 | events.remove(cache[i].element, cache[i].type); 427 | cache[i] = null; 428 | } catch(e) {} 429 | } 430 | }); 431 | } 432 | }); 433 | -------------------------------------------------------------------------------- /turing.enumerable.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Enumerable 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing Enumerable module. 9 | * 10 | * This is bound to DOM objects: 11 | * 12 | * global('p').each(function() { 13 | * // `this` contains a DOM element 14 | * }); 15 | * 16 | */ 17 | define('turing.enumerable', ['turing.core'], function(turing) { 18 | function EnumerableModule(global) { 19 | global.enumerable = { 20 | /** 21 | * Throw to break out of iterators. 22 | */ 23 | Break: {}, 24 | 25 | /** 26 | * Iterates using a function over a set of items. Example: 27 | * 28 | * turing.enumerable.each([1, 2, 3], function(n) { 29 | * console.log(n); 30 | * }); 31 | * 32 | * @param {Object} enumerable A set of items that responds to `length` 33 | * @param {Function} callback The function to run 34 | * @param {Object} [context] An optional parameter to determine `this` in the callback 35 | * @returns {Object} The passed in enumerable object 36 | */ 37 | each: function(enumerable, callback, context) { 38 | try { 39 | if (Array.prototype.forEach && enumerable.forEach === Array.prototype.forEach) { 40 | enumerable.forEach(callback, context); 41 | } else if (global.isNumber(enumerable.length)) { 42 | for (var i = 0, l = enumerable.length; i < l; i++) callback.call(enumerable, enumerable[i], i, enumerable); 43 | } else { 44 | for (var key in enumerable) { 45 | if (hasOwnProperty.call(enumerable, key)) callback.call(context, enumerable[key], key, enumerable); 46 | } 47 | } 48 | } catch(e) { 49 | if (e != global.enumerable.Break) throw e; 50 | } 51 | 52 | return enumerable; 53 | }, 54 | 55 | /** 56 | * Changes a set of item using a function. Example: 57 | * 58 | * turing.enumerable.map([1, 2, 3], function(n) { 59 | * return n + 1; 60 | * }); 61 | * 62 | * @param {Object} enumerable A set of items that responds to `length` 63 | * @param {Function} callback The function to run over each item 64 | * @param {Object} [context] An optional parameter to determine `this` in the callback 65 | * @returns {Array} The changed items 66 | */ 67 | map: function(enumerable, callback, context) { 68 | if (Array.prototype.map && enumerable.map === Array.prototype.map) return enumerable.map(callback, context); 69 | var results = []; 70 | global.enumerable.each(enumerable, function(value, index, list) { 71 | results.push(callback.call(context, value, index, list)); 72 | }); 73 | return results; 74 | }, 75 | 76 | /** 77 | * Removes items based on a callback. For example: 78 | * 79 | * var a = [1, 2, 3, 4, 5, 6, 7, 8]; 80 | * turing.enumerable.filter(a, function(n) { 81 | * return n % 2 === 0; 82 | * }); 83 | * 84 | * => [2, 4, 6, 8] 85 | * 86 | * @param {Object} enumerable A set of items that responds to `length` 87 | * @param {Function} callback The function to run over each item 88 | * @param {Object} [context] An optional parameter to determine `this` in the callback 89 | * @returns {Array} The filtered items 90 | */ 91 | filter: function(enumerable, callback, context) { 92 | if (Array.prototype.filter && enumerable.filter === Array.prototype.filter) 93 | return enumerable.filter(callback, context); 94 | var results = [], 95 | pushIndex = !global.isArray(enumerable); 96 | global.enumerable.each(enumerable, function(value, index, list) { 97 | if (callback.call(context, value, index, list)) { 98 | if (pushIndex) { 99 | results.push([index, value]); 100 | } else { 101 | results.push(value); 102 | } 103 | } 104 | }); 105 | return results; 106 | }, 107 | 108 | /** 109 | * The opposite of filter. For example: 110 | * 111 | * var a = [1, 2, 3, 4, 5, 6, 7, 8]; 112 | * turing.enumerable.reject(a, function(n) { 113 | * return n % 2 === 0; 114 | * }); 115 | * 116 | * => [1, 3, 5, 7] 117 | * 118 | * @param {Object} enumerable A set of items that responds to `length` 119 | * @param {Function} callback The function to run over each item 120 | * @param {Object} [context] An optional parameter to determine `this` in the callback 121 | * @returns {Array} The rejected items 122 | */ 123 | reject: function(enumerable, callback, context) { 124 | return this.filter(enumerable, function() { 125 | return !callback.apply(context, arguments); 126 | }, context); 127 | }, 128 | 129 | /** 130 | * Find a single item. For example: 131 | * 132 | * var a = [1, 2, 3, 4, 5, 6, 7, 8]; 133 | * turing.enumerable.detect(a, function(n) { 134 | * return n === 3; 135 | * }); 136 | * 137 | * => 3 138 | * 139 | * @param {Object} enumerable A set of items that responds to `length` 140 | * @param {Function} callback The function to run over each item 141 | * @param {Object} [context] An optional parameter to determine `this` in the callback 142 | * @returns {Object} The item, if found 143 | */ 144 | detect: function(enumerable, callback, context) { 145 | var result; 146 | global.enumerable.each(enumerable, function(value, index, list) { 147 | if (callback.call(context, value, index, list)) { 148 | result = value; 149 | throw global.enumerable.Break; 150 | } 151 | }); 152 | return result; 153 | }, 154 | 155 | /** 156 | * Runs a function over each item, collecting the results: 157 | * 158 | * var a = [1, 2, 3, 4, 5, 6, 7, 8]; 159 | * turing.enumerable.reduce(a, 0, function(memo, n) { 160 | * return memo + n; 161 | * }); 162 | * 163 | * => 36 164 | * 165 | * @param {Object} enumerable A set of items that responds to `length` 166 | * @param {Object} memo The initial accumulator value 167 | * @param {Function} callback The function to run over each item 168 | * @param {Object} [context] An optional parameter to determine `this` in the callback 169 | * @returns {Object} The accumulated results 170 | */ 171 | reduce: function(enumerable, memo, callback, context) { 172 | if (Array.prototype.reduce && enumerable.reduce === Array.prototype.reduce) 173 | return enumerable.reduce(global.bind(callback, context), memo); 174 | global.enumerable.each(enumerable, function(value, index, list) { 175 | memo = callback.call(context, memo, value, index, list); 176 | }); 177 | return memo; 178 | }, 179 | 180 | /** 181 | * Flattens multidimensional arrays: 182 | * 183 | * turing.enumerable.flatten([[2, 4], [[6], 8]]); 184 | * 185 | * => [2, 4, 6, 8] 186 | * 187 | * @param {Object} enumerable A set of items that responds to `length` 188 | * @returns {Object} The flat array 189 | */ 190 | flatten: function(array) { 191 | return global.enumerable.reduce(array, [], function(memo, value) { 192 | if (global.isArray(value)) return memo.concat(global.enumerable.flatten(value)); 193 | memo.push(value); 194 | return memo; 195 | }); 196 | }, 197 | 198 | /** 199 | * Return the last items from a list: 200 | * 201 | * turing.enumerable.tail([1, 2, 3, 4, 5], 3); 202 | * 203 | * => [4, 5] 204 | * 205 | * @param {Object} enumerable A set of items that responds to `length` 206 | * @param {Number} start The index of the item to 'cut' the array 207 | * @returns {Object} A list of items 208 | */ 209 | tail: function(enumerable, start) { 210 | start = typeof start === 'undefined' ? 1 : start; 211 | return Array.prototype.slice.apply(enumerable, [start]); 212 | }, 213 | 214 | /** 215 | * Invokes `method` on a list of items: 216 | * 217 | * turing.enumerable.invoke(['hello', 'world'], 'substring', 0, 3); 218 | * 219 | * => ['hel', 'wor'] 220 | * 221 | * @param {Object} enumerable A set of items that responds to `length` 222 | * @param {Function} method The method to invoke on each item 223 | * @returns {Object} The changed list 224 | */ 225 | invoke: function(enumerable, method) { 226 | var args = global.enumerable.tail(arguments, 2); 227 | return global.enumerable.map(enumerable, function(value) { 228 | return (method ? value[method] : value).apply(value, args); 229 | }); 230 | }, 231 | 232 | /** 233 | * Pluck a property from each item of a list: 234 | * 235 | * turing.enumerable.pluck(['hello', 'world'], 'length'); 236 | * 237 | * => [5, 5] 238 | * 239 | * @param {Object} enumerable A set of items that responds to `length` 240 | * @param {String} key The property to pluck 241 | * @returns {Object} The plucked properties 242 | */ 243 | pluck: function(enumerable, key) { 244 | return global.enumerable.map(enumerable, function(value) { 245 | return value[key]; 246 | }); 247 | }, 248 | 249 | /** 250 | * Determines if a list matches some items based on a callback: 251 | * 252 | * turing.enumerable.some([1, 2, 3], function(value) { 253 | * return value === 3; 254 | * }); 255 | * 256 | * => true 257 | * 258 | * @param {Object} enumerable A set of items that responds to `length` 259 | * @param {Function} callback A function to run against each item 260 | * @param {Object} [context] An optional parameter to determine `this` in the callback 261 | * @returns {Boolean} True if an item was matched 262 | */ 263 | some: function(enumerable, callback, context) { 264 | callback = callback || global.enumerable.identity; 265 | if (Array.prototype.some && enumerable.some === Array.prototype.some) 266 | return enumerable.some(callback, context); 267 | var result = false; 268 | global.enumerable.each(enumerable, function(value, index, list) { 269 | if (result = callback.call(context, value, index, list)) { 270 | throw global.enumerable.Break; 271 | } 272 | }); 273 | return result; 274 | }, 275 | 276 | /** 277 | * Checks if all items match the callback: 278 | * 279 | * turing.enumerable.all([1, 2, 3], function(value) { 280 | * return value < 4; 281 | * }) 282 | * 283 | * => true 284 | * 285 | * @param {Object} enumerable A set of items that responds to `length` 286 | * @param {Function} callback A function to run against each item 287 | * @param {Object} [context] An optional parameter to determine `this` in the callback 288 | * @returns {Boolean} True if all items match 289 | */ 290 | all: function(enumerable, callback, context) { 291 | callback = callback || global.enumerable.identity; 292 | if (Array.prototype.every && enumerable.every === Array.prototype.every) 293 | return enumerable.every(callback, context); 294 | var result = true; 295 | global.enumerable.each(enumerable, function(value, index, list) { 296 | if (!(result = result && callback.call(context, value, index, list))) { 297 | throw global.enumerable.Break; 298 | } 299 | }); 300 | return result; 301 | }, 302 | 303 | /** 304 | * Checks if one item matches a value: 305 | * 306 | * turing.enumerable.include([1, 2, 3], 3); 307 | * 308 | * => true 309 | * 310 | * @param {Object} enumerable A set of items that responds to `length` 311 | * @param {Object} target A value to find 312 | * @returns {Boolean} True if an item was found 313 | */ 314 | include: function(enumerable, target) { 315 | if (Array.prototype.indexOf && enumerable.indexOf === Array.prototype.indexOf) 316 | return enumerable.indexOf(target) != -1; 317 | var found = false; 318 | global.enumerable.each(enumerable, function(value, key) { 319 | if (found = value === target) { 320 | throw global.enumerable.Break; 321 | } 322 | }); 323 | return found; 324 | }, 325 | 326 | /** 327 | * Chain enumerable calls: 328 | * 329 | * turing.enumerable.chain([1, 2, 3, 4]) 330 | * .filter(function(n) { return n % 2 == 0; }) 331 | * .map(function(n) { return n * 10; }) 332 | * .values(); 333 | * 334 | * => [20, 40] 335 | * 336 | * @param {Object} enumerable A set of items that responds to `length` 337 | * @returns {Object} The chained enumerable API 338 | */ 339 | chain: function(enumerable) { 340 | return new global.enumerable.Chainer(enumerable); 341 | }, 342 | 343 | identity: function(value) { 344 | return value; 345 | } 346 | }; 347 | 348 | // Aliases 349 | global.enumerable.select = global.enumerable.filter; 350 | global.enumerable.collect = global.enumerable.map; 351 | global.enumerable.inject = global.enumerable.reduce; 352 | global.enumerable.rest = global.enumerable.tail; 353 | global.enumerable.any = global.enumerable.some; 354 | global.enumerable.every = global.enumerable.all; 355 | global.chainableMethods = ['map', 'collect', 'detect', 'filter', 'reduce', 'each', 356 | 'tail', 'rest', 'reject', 'pluck', 'any', 'some', 'all']; 357 | 358 | // Chainer class 359 | global.enumerable.Chainer = function(values) { 360 | this.results = values; 361 | }; 362 | 363 | global.enumerable.Chainer.prototype.values = function() { 364 | return this.results; 365 | }; 366 | 367 | global.enumerable.each(global.chainableMethods, function(methodName) { 368 | var method = global.enumerable[methodName]; 369 | global.enumerable.Chainer.prototype[methodName] = function() { 370 | var args = Array.prototype.slice.call(arguments); 371 | args.unshift(this.results); 372 | this.results = method.apply(this, args); 373 | return this; 374 | } 375 | }); 376 | 377 | global.init(function(arg) { 378 | if (arg.hasOwnProperty.length && typeof arg !== 'string') { 379 | return global.enumerable.chain(arg); 380 | } 381 | }); 382 | } 383 | 384 | if (typeof module !== 'undefined') { 385 | module.exports = function(t) { 386 | return EnumerableModule(t); 387 | } 388 | } else { 389 | EnumerableModule(turing); 390 | } 391 | }); 392 | -------------------------------------------------------------------------------- /turing.anim.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing Anim 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The main animation method is `turing.anim.animate`. The animate method animates CSS properties. 9 | * 10 | * There are also animation helper methods, like `turing.anim.fadeIn` and `turing.anim.move`. 11 | * 12 | * Animation Examples: 13 | * 14 | * Turn a paragraph red: 15 | * 16 | * turing.anim.animate($t('p')[0], 2000, { 17 | * 'color': '#ff0000' 18 | * }); 19 | * 20 | * Move a paragraph: 21 | * 22 | * turing.anim.animate($t('p')[0], 2000, { 23 | * 'marginLeft': '400px' 24 | * }); 25 | * 26 | * It's possible to chain animation module calls with `turing.anim.chain`, but it's easier to use the DOM chained methods: 27 | * 28 | * turing('p').fadeIn(2000).animate(1000, { 29 | * 'marginLeft': '200px' 30 | * }) 31 | * 32 | * Or: 33 | * 34 | * $t('p').fadeIn(2000).animate(1000, { 35 | * 'marginLeft': '200px' 36 | * }) 37 | * 38 | */ 39 | 40 | define('turing.anim', ['turing.core', 'turing.dom'], function(turing, dom) { 41 | var anim = {}, 42 | easing = {}, 43 | Chainer, 44 | opacityType, 45 | methodName, 46 | CSSTransitions = {}; 47 | 48 | // These CSS related functions should be moved into turing.css 49 | function camelize(property) { 50 | return property.replace(/-+(.)?/g, function(match, chr) { 51 | return chr ? chr.toUpperCase() : ''; 52 | }); 53 | } 54 | 55 | function getOpacityType() { 56 | return (typeof document.body.style.opacity !== 'undefined') ? 'opacity' : 'filter'; 57 | } 58 | 59 | function Colour(value) { 60 | this.r = 0; 61 | this.g = 0; 62 | this.b = 0; 63 | this.value = this.normalise(value); 64 | this.parse(); 65 | } 66 | 67 | // Based on: http://www.phpied.com/rgb-color-parser-in-javascript/ 68 | Colour.matchers = [ 69 | { 70 | re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, 71 | example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], 72 | process: function (bits){ 73 | return [ 74 | parseInt(bits[1], 10), 75 | parseInt(bits[2], 10), 76 | parseInt(bits[3], 10) 77 | ]; 78 | } 79 | }, 80 | { 81 | re: /^(\w{2})(\w{2})(\w{2})$/, 82 | example: ['#00ff00', '336699'], 83 | process: function (bits){ 84 | return [ 85 | parseInt(bits[1], 16), 86 | parseInt(bits[2], 16), 87 | parseInt(bits[3], 16) 88 | ]; 89 | } 90 | }, 91 | { 92 | re: /^(\w{1})(\w{1})(\w{1})$/, 93 | example: ['#fb0', 'f0f'], 94 | process: function (bits) { 95 | return [ 96 | parseInt(bits[1] + bits[1], 16), 97 | parseInt(bits[2] + bits[2], 16), 98 | parseInt(bits[3] + bits[3], 16) 99 | ]; 100 | } 101 | } 102 | ]; 103 | 104 | Colour.prototype.normalise = function(value) { 105 | value.replace(/ /g, ''); 106 | if (value.charAt(0) === '#') { 107 | value = value.substr(1, 6); 108 | } 109 | return value; 110 | }; 111 | 112 | Colour.prototype.parse = function() { 113 | var channels = [], i; 114 | for (i = 0; i < Colour.matchers.length; i++) { 115 | channels = this.value.match(Colour.matchers[i].re); 116 | if (channels) { 117 | channels = Colour.matchers[i].process(channels); 118 | this.r = channels[0]; 119 | this.g = channels[1]; 120 | this.b = channels[2]; 121 | break; 122 | } 123 | } 124 | this.validate(); 125 | }; 126 | 127 | Colour.prototype.validate = function() { 128 | this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r); 129 | this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g); 130 | this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b); 131 | }; 132 | 133 | Colour.prototype.sum = function() { 134 | return this.r + this.g + this.b; 135 | }; 136 | 137 | Colour.prototype.toString = function() { 138 | return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')'; 139 | }; 140 | 141 | function isColour(value) { 142 | return typeof value === 'string' && value.match(/(#[a-f|A-F|0-9]|rgb)/); 143 | } 144 | 145 | function parseColour(value) { 146 | return { value: new Colour(value), units: '', transform: colourTransform }; 147 | } 148 | 149 | function numericalTransform(parsedValue, position, easingFunction) { 150 | return (easingFunction(position) * parsedValue.value); 151 | } 152 | 153 | function colourTransform(v, position, easingFunction) { 154 | var colours = []; 155 | colours[0] = Math.round(v.base.r + (v.direction[0] * (Math.abs(v.base.r - v.value.r) * easingFunction(position)))); 156 | colours[1] = Math.round(v.base.g + (v.direction[1] * (Math.abs(v.base.g - v.value.g) * easingFunction(position)))); 157 | colours[2] = Math.round(v.base.b + (v.direction[2] * (Math.abs(v.base.b - v.value.b) * easingFunction(position)))); 158 | return 'rgb(' + colours.join(', ') + ')'; 159 | } 160 | 161 | function parseNumericalValue(value) { 162 | var n = (typeof value === 'string') ? parseFloat(value) : value, 163 | units = (typeof value === 'string') ? value.replace(n, '') : ''; 164 | return { value: n, units: units, transform: numericalTransform }; 165 | } 166 | 167 | function parseCSSValue(value, element, property) { 168 | if (isColour(value)) { 169 | var colour = parseColour(value), i; 170 | colour.base = new Colour(element.style[property]); 171 | colour.direction = [colour.base.r < colour.value.r ? 1 : -1, 172 | colour.base.g < colour.value.g ? 1 : -1, 173 | colour.base.b < colour.value.b ? 1 : -1]; 174 | return colour; 175 | } else if (typeof value !== 'object') { 176 | return parseNumericalValue(value); 177 | } else { 178 | return value; 179 | } 180 | } 181 | 182 | function setCSSProperty(element, property, value) { 183 | if (property === 'opacity' && opacityType === 'filter') { 184 | element.style[opacityType] = 'alpha(opacity=' + Math.round(value * 100) + ')'; 185 | return element; 186 | } 187 | element.style[property] = value; 188 | return element; 189 | } 190 | 191 | easing.linear = function(position) { 192 | return position; 193 | }; 194 | 195 | easing.sine = function(position) { 196 | return (-Math.cos(position * Math.PI) / 2) + 0.5; 197 | }; 198 | 199 | easing.reverse = function(position) { 200 | return 1.0 - position; 201 | }; 202 | 203 | easing.spring = function(position) { 204 | return 1 - (Math.cos(position * Math.PI * 4) * Math.exp(-position * 6)); 205 | }; 206 | 207 | easing.bounce = function(position) { 208 | if (position < (1 / 2.75)) { 209 | return 7.6 * position * position; 210 | } else if (position < (2 /2.75)) { 211 | return 7.6 * (position -= (1.5 / 2.75)) * position + 0.74; 212 | } else if (position < (2.5 / 2.75)) { 213 | return 7.6 * (position -= (2.25 / 2.75)) * position + 0.91; 214 | } else { 215 | return 7.6 * (position -= (2.625 / 2.75)) * position + 0.98; 216 | } 217 | }; 218 | 219 | /** 220 | * Animates an element using CSS properties. 221 | * 222 | * @param {Object} element A DOM element 223 | * @param {Number} duration Duration in milliseconds 224 | * @param {Object} properties CSS properties to animate, for example: `{ width: '20px' }` 225 | * @param {Object} options Currently accepts an easing function or built-in easing method name (linear, sine, reverse, spring, bounce) 226 | */ 227 | anim.animate = function(element, duration, properties, options) { 228 | var start = new Date().valueOf(), 229 | finish = start + duration, 230 | easingFunction = easing.linear, 231 | interval, 232 | p; 233 | 234 | if (!opacityType) { 235 | opacityType = getOpacityType(); 236 | } 237 | 238 | options = options || {}; 239 | if (options.hasOwnProperty('easing')) { 240 | if (typeof options.easing === 'string') { 241 | easingFunction = easing[options.easing]; 242 | } else if (options.easing) { 243 | easingFunction = options.easing; 244 | } 245 | } 246 | 247 | for (p in properties) { 248 | if (properties.hasOwnProperty(p)) { 249 | properties[p] = parseCSSValue(properties[p], element, p); 250 | if (p === 'opacity' && opacityType === 'filter') { 251 | element.style.zoom = 1; 252 | } else if (CSSTransitions.vendorPrefix && (p === 'left' || p === 'top')) { 253 | CSSTransitions.start(element, duration, p, properties[p].value + properties[p].units, options.easing); 254 | return setTimeout(function() { 255 | CSSTransitions.end(element, p); 256 | }, duration); 257 | } 258 | } 259 | } 260 | 261 | interval = setInterval(function() { 262 | var time = new Date().valueOf(), position = time > finish ? 1 : (time - start) / duration, 263 | property; 264 | 265 | for (property in properties) { 266 | if (properties.hasOwnProperty(property)) { 267 | setCSSProperty( 268 | element, 269 | property, 270 | properties[property].transform(properties[property], position, easingFunction) + properties[property].units); 271 | } 272 | } 273 | 274 | if (time > finish) { 275 | clearInterval(interval); 276 | } 277 | }, 10); 278 | }; 279 | 280 | CSSTransitions = { 281 | // CSS3 vendor detection 282 | vendors: { 283 | // Opera Presto 2.3 284 | 'opera': { 285 | 'prefix': '-o-', 286 | 'detector': function() { 287 | try { 288 | document.createEvent('OTransitionEvent'); 289 | return true; 290 | } catch(e) { 291 | return false; 292 | } 293 | } 294 | }, 295 | 296 | // Chrome 5, Safari 4 297 | 'webkit': { 298 | 'prefix': '-webkit-', 299 | 'detector': function() { 300 | try { 301 | document.createEvent('WebKitTransitionEvent'); 302 | return true; 303 | } catch(e) { 304 | return false; 305 | } 306 | } 307 | }, 308 | 309 | // Firefox 4 310 | 'firefox': { 311 | 'prefix': '-moz-', 312 | 'detector': function() { 313 | var div = document.createElement('div'), 314 | supported = false; 315 | if (typeof div.style.MozTransition !== 'undefined') { 316 | supported = true; 317 | } 318 | div = null; 319 | return supported; 320 | } 321 | } 322 | }, 323 | 324 | findCSS3VendorPrefix: function() { 325 | var detector; 326 | for (detector in CSSTransitions.vendors) { 327 | if (this.vendors.hasOwnProperty(detector)) { 328 | detector = this.vendors[detector]; 329 | if (detector.detector()) { 330 | return detector.prefix; 331 | } 332 | } 333 | } 334 | }, 335 | 336 | vendorPrefix: null, 337 | 338 | // CSS3 Transitions 339 | start: function(element, duration, property, value, easing) { 340 | element.style[camelize(this.vendorPrefix + 'transition')] = property + ' ' + duration + 'ms ' + (easing || 'linear'); 341 | element.style[property] = value; 342 | }, 343 | 344 | end: function(element, property) { 345 | element.style[camelize(this.vendorPrefix + 'transition')] = null; 346 | } 347 | }; 348 | 349 | CSSTransitions.vendorPrefix = CSSTransitions.findCSS3VendorPrefix(); 350 | 351 | /** 352 | * Fade an element. 353 | * 354 | * @param {Object} element A DOM element 355 | * @param {Number} duration Duration in milliseconds 356 | * @param {Object} options to, from, easing function: `{ to: 1, from: 0, easing: 'bounce' }` 357 | */ 358 | anim.fade = function(element, duration, options) { 359 | element.style.opacity = options.from; 360 | return anim.animate(element, duration, { 'opacity': options.to }, { 'easing': options.easing }); 361 | }; 362 | 363 | /** 364 | * Fade in an element. 365 | * 366 | * @param {Object} element A DOM element 367 | * @param {Number} duration Duration in milliseconds 368 | * @param {Object} options May include an easing function: `{ to: 1, from: 0, easing: 'bounce' }` 369 | */ 370 | anim.fadeIn = function(element, duration, options) { 371 | options = options || {}; 372 | options.from = options.from || 0.0; 373 | options.to = options.to || 1.0; 374 | return anim.fade(element, duration, options); 375 | }; 376 | 377 | /** 378 | * Fade out an element. 379 | * 380 | * @param {Object} element A DOM element 381 | * @param {Number} duration Duration in milliseconds 382 | * @param {Object} options May include an easing function: `{ to: 1, from: 0, easing: 'bounce' }` 383 | */ 384 | anim.fadeOut = function(element, duration, options) { 385 | var from; 386 | options = options || {}; 387 | options.from = options.from || 1.0; 388 | options.to = options.to || 0.0; 389 | 390 | // Swap from and to 391 | from = options.from; 392 | options.from = options.to; 393 | options.to = from; 394 | 395 | // This easing function reverses the position value and adds from 396 | options.easing = function(p) { return (1.0 - p) + options.from; }; 397 | 398 | return anim.fade(element, duration, options); 399 | }; 400 | 401 | /** 402 | * Highlight an element. 403 | * 404 | * @param {Object} element A DOM element 405 | * @param {Number} duration Duration in milliseconds 406 | * @param {Object} options May include an easing function: `{ to: 1, from: 0, easing: 'bounce' }` 407 | */ 408 | anim.highlight = function(element, duration, options) { 409 | var style = element.currentStyle ? element.currentStyle : getComputedStyle(element, null); 410 | options = options || {}; 411 | options.from = options.from || '#ff9'; 412 | options.to = options.to || style.backgroundColor; 413 | options.easing = options.easing || easing.sine; 414 | duration = duration || 500; 415 | element.style.backgroundColor = options.from; 416 | return setTimeout(function() { 417 | anim.animate(element, duration, { 'backgroundColor': options.to, 'easing': options.easing }); 418 | }, 200); 419 | }; 420 | 421 | /** 422 | * Move an element. 423 | * 424 | * @param {Object} element A DOM element 425 | * @param {Number} duration Duration in milliseconds 426 | * @param {Object} options Position and easing, for example: `{ left: 100, top: 50, easing: 'sine' }` 427 | */ 428 | anim.move = function(element, duration, options) { 429 | return anim.animate(element, duration, { 'left': options.x, 'top': options.y }, { 'easing': options.easing || easing.sine }); 430 | }; 431 | 432 | /** 433 | * Parse colour strings. For example: 434 | * 435 | * assert.equal('rgb(255, 0, 255)', 436 | * turing.anim.parseColour('#ff00ff').toString()); 437 | * 438 | * @param {String} colourString A hex colour string 439 | * @returns {String} RGB string 440 | */ 441 | anim.parseColour = function(colourString) { return new Colour(colourString); }; 442 | anim.pause = function(element, duration, options) {}; 443 | 444 | /** 445 | * Easing functions: linear, sine, reverse, spring, bounce. 446 | */ 447 | anim.easing = easing; 448 | 449 | Chainer = function(element) { 450 | this.element = element; 451 | this.position = 0; 452 | }; 453 | 454 | function makeChain(m) { 455 | var method = anim[m]; 456 | Chainer.prototype[m] = function() { 457 | var args = Array.prototype.slice.call(arguments); 458 | args.unshift(this.element); 459 | // Note: the duration needs to be communicated another way 460 | // because of defaults (like highlight()) 461 | this.position += args[1] || 0; 462 | setTimeout(function() { 463 | method.apply(null, args); 464 | }, this.position); 465 | return this; 466 | }; 467 | } 468 | 469 | for (methodName in anim) { 470 | if (anim.hasOwnProperty(methodName)) { 471 | makeChain(methodName); 472 | } 473 | } 474 | 475 | /** 476 | * Chain animation module calls, for example: 477 | * 478 | * turing.anim.chain(element) 479 | * .highlight() 480 | * .pause(250) 481 | * .move(100, { x: '100px', y: '100px', easing: 'ease-in-out' }) 482 | * .animate(250, { width: '1000px' }) 483 | * .fadeOut(250) 484 | * .pause(250) 485 | * .fadeIn(250) 486 | * .animate(250, { width: '20px' }); 487 | * 488 | * @param {Object} element A DOM element 489 | * @returns {Chainer} Chained API object 490 | */ 491 | anim.chain = function(element) { 492 | return new Chainer(element); 493 | }; 494 | 495 | /** 496 | * Animations can be chained with DOM calls: 497 | * 498 | * turing('p').animate(2000, { 499 | * color: '#ff0000' 500 | * }); 501 | * 502 | */ 503 | anim.addDOMethods = function() { 504 | if (typeof turing.domChain === 'undefined') { 505 | return; 506 | } 507 | 508 | var chainedAliases = ('animate fade fadeIn fadeOut highlight ' + 509 | 'move parseColour pause easing').split(' '), 510 | i; 511 | 512 | function makeChainedAlias(name) { 513 | turing.domChain[name] = function(handler) { 514 | var j, args = turing.toArray(arguments); 515 | args.unshift(null); 516 | 517 | for (j = 0; j < this.length; j++) { 518 | args[0] = this[j]; 519 | anim[name].apply(this, args); 520 | } 521 | return this; 522 | }; 523 | } 524 | 525 | for (i = 0; i < chainedAliases.length; i++) { 526 | makeChainedAlias(chainedAliases[i]); 527 | } 528 | }; 529 | anim.addDOMethods(); 530 | 531 | turing.anim = anim; 532 | return anim; 533 | }); 534 | -------------------------------------------------------------------------------- /turing.dom.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Turing DOM 3 | * Copyright (C) 2010-2011 Alex R. Young 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * The Turing DOM module. 9 | */ 10 | define('turing.dom', ['turing.core'], function(turing) { 11 | var dom = {}, InvalidFinder = Error, macros, rules, tokenMap, 12 | find, matchMap, findMap, filter, scannerRegExp, nodeTypes, 13 | getStyle, setStyle, cssNumericalProperty, propertyFix, 14 | getAttributeParamFix, booleanAttributes; 15 | 16 | macros = { 17 | 'nl': '\n|\r\n|\r|\f', 18 | 'w': '[\s\r\n\f]*', 19 | 'nonascii': '[^\0-\177]', 20 | 'num': '-?([0-9]+|[0-9]*\.[0-9]+)', 21 | 'unicode': '\\[0-9A-Fa-f]{1,6}(\r\n|[\s\n\r\t\f])?', 22 | 'escape': '#{unicode}|\\[^\n\r\f0-9A-Fa-f]', 23 | 'nmchar': '[_A-Za-z0-9-]|#{nonascii}|#{escape}', 24 | 'nmstart': '[_A-Za-z]|#{nonascii}|#{escape}', 25 | 'ident': '[-@]?(#{nmstart})(#{nmchar})*', 26 | 'name': '(#{nmchar})+', 27 | 'string1': '"([^\n\r\f"]|#{nl}|#{nonascii}|#{escape})*"', 28 | 'string2': "'([^\n\r\f']|#{nl}|#{nonascii}|#{escape})*'", 29 | 'string': '#{string1}|#{string2}' 30 | }; 31 | 32 | nodeTypes = { 33 | ELEMENT_NODE: 1, 34 | ATTRIBUTE_NODE: 2, 35 | TEXT_NODE: 3, 36 | CDATA_SECTION_NODE: 4, 37 | ENTITY_REFERENCE_NODE: 5, 38 | ENTITY_NODE: 6, 39 | PROCESSING_INSTRUCTION_NODE: 7, 40 | COMMENT_NODE: 8, 41 | DOCUMENT_NODE: 9, 42 | DOCUMENT_TYPE_NODE: 10, 43 | DOCUMENT_FRAGMENT_NODE: 11, 44 | NOTATION_NODE: 12 45 | }; 46 | 47 | cssNumericalProperty = { 48 | 'zIndex': true, 49 | 'fontWeight': true, 50 | 'opacity': true, 51 | 'zoom': true, 52 | 'lineHeight': true 53 | }; 54 | 55 | booleanAttributes = { 56 | 'selected': true, 57 | 'readonly': true, 58 | 'checked': true 59 | }; 60 | 61 | rules = { 62 | 'name and id': '(#{ident}##{ident})', 63 | 'id': '(##{ident})', 64 | 'class': '(\\.#{ident})', 65 | 'name and class': '(#{ident}\\.#{ident})', 66 | 'element': '(#{ident})', 67 | 'pseudo class': '(:#{ident})' 68 | }; 69 | 70 | propertyFix = { 71 | tabindex: 'tabIndex', 72 | readonly: 'readOnly', 73 | 'for': 'htmlFor', 74 | 'class': 'className', 75 | maxlength: 'maxLength', 76 | cellspacing: 'cellSpacing', 77 | cellpadding: 'cellPadding', 78 | rowspan: 'rowSpan', 79 | colspan: 'colSpan', 80 | usemap: 'useMap', 81 | frameborder: 'frameBorder', 82 | contenteditable: 'contentEditable' 83 | }; 84 | 85 | getAttributeParamFix = { 86 | width: true, 87 | height: true, 88 | src: true, 89 | href: true 90 | }; 91 | 92 | turing.addDetectionTest('classList', function() { 93 | var div = document.createElement('div'); 94 | 95 | if (div.classList) { 96 | return true; 97 | } 98 | 99 | div = null; 100 | return false; 101 | }); 102 | 103 | function scanner() { 104 | function replacePattern(pattern, patterns) { 105 | var matched = true, match; 106 | while (matched) { 107 | match = pattern.match(/#\{([^}]+)\}/); 108 | if (match && match[1]) { 109 | pattern = pattern.replace(new RegExp('#\{' + match[1] + '\}', 'g'), patterns[match[1]]); 110 | matched = true; 111 | } else { 112 | matched = false; 113 | } 114 | } 115 | return pattern; 116 | } 117 | 118 | function escapePattern(text) { 119 | return text.replace(/\//g, '//'); 120 | } 121 | 122 | function convertPatterns() { 123 | var key, pattern, results = {}, patterns, source; 124 | 125 | if (arguments.length === 2) { 126 | source = arguments[0]; 127 | patterns = arguments[1]; 128 | } else { 129 | source = arguments[0]; 130 | patterns = arguments[0]; 131 | } 132 | 133 | for (key in patterns) { 134 | pattern = escapePattern(replacePattern(patterns[key], source)); 135 | results[key] = pattern; 136 | } 137 | 138 | return results; 139 | } 140 | 141 | function joinPatterns(regexps) { 142 | var results = [], key; 143 | for (key in regexps) { 144 | results.push(regexps[key]); 145 | } 146 | return new RegExp(results.join('|'), 'g'); 147 | } 148 | 149 | return joinPatterns( 150 | convertPatterns(convertPatterns(macros), rules) 151 | ); 152 | } 153 | 154 | scannerRegExp = scanner(); 155 | 156 | find = { 157 | byId: function(root, id) { 158 | if (root === null) return []; 159 | return [root.getElementById(id)]; 160 | }, 161 | 162 | byNodeName: function(root, tagName) { 163 | if (root === null) return []; 164 | var i, results = [], nodes = root.getElementsByTagName(tagName); 165 | for (i = 0; i < nodes.length; i++) { 166 | results.push(nodes[i]); 167 | } 168 | return results; 169 | }, 170 | 171 | byClassName: function(root, className) { 172 | if (root === null) return []; 173 | var i, results = [], nodes = root.getElementsByTagName('*'); 174 | for (i = 0; i < nodes.length; i++) { 175 | if (nodes[i].className.match('\\b' + className + '\\b')) { 176 | results.push(nodes[i]); 177 | } 178 | } 179 | return results; 180 | } 181 | }; 182 | 183 | findMap = { 184 | 'id': function(root, selector) { 185 | selector = selector.split('#')[1]; 186 | return find.byId(root, selector); 187 | }, 188 | 189 | 'name and id': function(root, selector) { 190 | var matches = selector.split('#'), name, id; 191 | name = matches[0]; 192 | id = matches[1]; 193 | return filter.byAttr(find.byId(root, id), 'nodeName', name.toUpperCase()); 194 | }, 195 | 196 | 'name': function(root, selector) { 197 | return find.byNodeName(root, selector); 198 | }, 199 | 200 | 'class': function(root, selector) { 201 | selector = selector.split('\.')[1]; 202 | return find.byClassName(root, selector); 203 | }, 204 | 205 | 'name and class': function(root, selector) { 206 | var matches = selector.split('\.'), name, className; 207 | name = matches[0]; 208 | className = matches[1]; 209 | return filter.byAttr(find.byClassName(root, className), 'nodeName', name.toUpperCase()); 210 | } 211 | }; 212 | 213 | if (typeof document !== 'undefined' && typeof document.getElementsByClassName !== 'undefined') { 214 | find.byClassName = function(root, className) { 215 | return root.getElementsByClassName(className); 216 | }; 217 | } 218 | 219 | filter = { 220 | byAttr: function(elements, attribute, value) { 221 | var key, results = []; 222 | for (key in elements) { 223 | if (elements[key] && elements[key][attribute] === value) { 224 | results.push(elements[key]); 225 | } 226 | } 227 | return results; 228 | } 229 | }; 230 | 231 | matchMap = { 232 | 'id': function(element, selector) { 233 | selector = selector.split('#')[1]; 234 | return element && element.id === selector; 235 | }, 236 | 237 | 'name': function(element, nodeName) { 238 | return element.nodeName === nodeName.toUpperCase(); 239 | }, 240 | 241 | 'name and id': function(element, selector) { 242 | return matchMap.id(element, selector) && matchMap.name(element, selector.split('#')[0]); 243 | }, 244 | 245 | 'class': function(element, selector) { 246 | if (element && element.className) { 247 | selector = selector.split('\.')[1]; 248 | return element.className.match('\\b' + selector + '\\b'); 249 | } 250 | }, 251 | 252 | 'name and class': function(element, selector) { 253 | return matchMap['class'](element, selector) && matchMap.name(element, selector.split('\.')[0]); 254 | } 255 | }; 256 | 257 | function Searcher(root, tokens) { 258 | this.root = root; 259 | this.key_selector = tokens.pop(); 260 | this.tokens = tokens; 261 | this.results = []; 262 | } 263 | 264 | Searcher.prototype.matchesToken = function(element, token) { 265 | if (!matchMap[token.finder]) { 266 | throw new InvalidFinder('Invalid matcher: ' + token.finder); 267 | } 268 | return matchMap[token.finder](element, token.identity); 269 | }; 270 | 271 | Searcher.prototype.find = function(token) { 272 | if (!findMap[token.finder]) { 273 | throw new InvalidFinder('Invalid finder: ' + token.finder); 274 | } 275 | return findMap[token.finder](this.root, token.identity); 276 | }; 277 | 278 | Searcher.prototype.matchesAllRules = function(element) { 279 | if (this.tokens.length === 0) return; 280 | 281 | var i = this.tokens.length - 1, 282 | token = this.tokens[i], 283 | matchFound = false; 284 | 285 | while (i >= 0 && element) { 286 | if (this.matchesToken(element, token)) { 287 | matchFound = true; 288 | i--; 289 | token = this.tokens[i]; 290 | } 291 | element = element.parentNode; 292 | } 293 | 294 | return matchFound && i < 0; 295 | }; 296 | 297 | Searcher.prototype.parse = function() { 298 | // Find all elements with the key selector 299 | var i, element, elements = this.find(this.key_selector), results = []; 300 | 301 | // Traverse upwards from each element to see if it matches all of the rules 302 | for (i = 0; i < elements.length; i++) { 303 | element = elements[i]; 304 | if (this.tokens.length > 0) { 305 | if (this.matchesAllRules(element.parentNode)) { 306 | results.push(element); 307 | } 308 | } else { 309 | if (this.matchesToken(element, this.key_selector)) { 310 | results.push(element); 311 | } 312 | } 313 | } 314 | return results; 315 | }; 316 | 317 | Searcher.prototype.values = function() { 318 | return this.results; 319 | }; 320 | 321 | function normalize(text) { 322 | return text.replace(/^\s+|\s+$/g, '').replace(/[ \t\r\n\f]+/g, ' '); 323 | } 324 | 325 | // Tokens are used by the Tokenizer 326 | function Token(identity, finder) { 327 | this.identity = identity; 328 | this.finder = finder; 329 | } 330 | 331 | Token.prototype.toString = function() { 332 | return 'identity: ' + this.identity + ', finder: ' + this.finder; 333 | }; 334 | 335 | // Tokenizer: classify sections of the scanner output 336 | function Tokenizer(selector) { 337 | this.selector = normalize(selector); 338 | this.tokens = []; 339 | this.tokenize(); 340 | } 341 | 342 | Tokenizer.prototype.tokenize = function() { 343 | var match, r, finder; 344 | 345 | r = scannerRegExp; 346 | r.lastIndex = 0; 347 | 348 | while (match = r.exec(this.selector)) { 349 | finder = null; 350 | 351 | if (match[10]) { 352 | finder = 'id'; 353 | } else if (match[1]) { 354 | finder = 'name and id'; 355 | } else if (match[29]) { 356 | finder = 'name'; 357 | } else if (match[15]) { 358 | finder = 'class'; 359 | } else if (match[20]) { 360 | finder = 'name and class'; 361 | } 362 | this.tokens.push(new Token(match[0], finder)); 363 | } 364 | return this.tokens; 365 | }; 366 | 367 | Tokenizer.prototype.finders = function() { 368 | var i, results = []; 369 | for (i in this.tokens) { 370 | results.push(this.tokens[i].finder); 371 | } 372 | return results; 373 | }; 374 | 375 | dom.tokenize = function(selector) { 376 | var tokenizer = new Tokenizer(selector); 377 | return tokenizer; 378 | }; 379 | 380 | function get(selector, root) { 381 | var tokens = dom.tokenize(selector).tokens, 382 | searcher = new Searcher(root, tokens); 383 | return searcher.parse(); 384 | } 385 | 386 | turing.addDetectionTest('querySelectorAll', function() { 387 | var div = document.createElement('div'); 388 | div.innerHTML = '

        '; 389 | 390 | // Some versions of Safari can't handle uppercase in quirks mode 391 | if (div.querySelectorAll) { 392 | if (div.querySelectorAll('.TEST').length === 0) return false; 393 | return true; 394 | } 395 | 396 | // Helps IE release memory associated with the div 397 | div = null; 398 | return false; 399 | }); 400 | 401 | /** 402 | * Converts property names with hyphens to camelCase. 403 | * 404 | * @param {String} text A property name 405 | * @returns {String} text A camelCase property name 406 | */ 407 | function camelCase(text) { 408 | if (typeof text !== 'string') return; 409 | return text.replace(/-([a-z])/ig, function(all, letter) { return letter.toUpperCase(); }); 410 | }; 411 | 412 | /** 413 | * Converts property names in camelCase to ones with hyphens. 414 | * 415 | * @param {String} text A property name 416 | * @returns {String} text A camelCase property name 417 | */ 418 | function uncamel(text) { 419 | if (typeof text !== 'string') return; 420 | return text.replace(/([A-Z])/g, '-$1').toLowerCase(); 421 | }; 422 | 423 | function invalidCSSNode(element) { 424 | return !element || element.nodeType === nodeTypes.TEXT_NODE || element.nodeType === nodeTypes.COMMENT_NODE || !element.style; 425 | } 426 | 427 | function setStyleProperty(element, property, value) { 428 | if (invalidCSSNode(element)) { 429 | return; 430 | } 431 | 432 | if (typeof value === 'number' && !cssNumericalProperty[property]) { 433 | value += 'px'; 434 | } 435 | 436 | element.style[property] = value; 437 | } 438 | 439 | if (typeof document !== 'undefined') { 440 | if (document.documentElement.currentStyle) { 441 | getStyle = function(element, property) { 442 | return element.currentStyle[camelCase(property)]; 443 | }; 444 | 445 | setStyle = function(element, property, value) { 446 | return setStyleProperty(element, camelCase(property), value); 447 | }; 448 | } else if (document.defaultView.getComputedStyle) { 449 | getStyle = function(element, property) { 450 | return element.ownerDocument.defaultView.getComputedStyle(element, null).getPropertyValue(uncamel(property)); 451 | }; 452 | 453 | setStyle = function(element, property, value) { 454 | return setStyleProperty(element, property, value); 455 | }; 456 | } 457 | } 458 | 459 | /** 460 | * Gets or sets style values. 461 | * 462 | * @param {Object} element A DOM element 463 | * @returns {Object} The style value 464 | */ 465 | dom.css = function(element, options) { 466 | if (typeof options === 'string') { 467 | return getStyle(element, options); 468 | } else { 469 | for (var property in options) { 470 | if (options.hasOwnProperty(property)) { 471 | setStyle(element, property, options[property]); 472 | } 473 | } 474 | } 475 | }; 476 | 477 | /** 478 | * Finds DOM elements based on a CSS selector. 479 | * 480 | * @param {String} selector A CSS selector 481 | * @returns {Array} The elements 482 | */ 483 | dom.get = function(selector) { 484 | var root = typeof arguments[1] === 'undefined' ? document : arguments[1]; 485 | return turing.toArray(turing.detect('querySelectorAll') ? 486 | root.querySelectorAll(selector) : get(selector, root)); 487 | }; 488 | 489 | /** 490 | * Does an element satify a selector, based on root element? 491 | * 492 | * @param {Object} element A DOM element 493 | * @param {String} selector A CSS selector 494 | * @param {Object} root The root DOM element 495 | * @returns {Object} The matching DOM element 496 | */ 497 | dom.findElement = function(element, selector, root) { 498 | var tokens = dom.tokenize(selector).tokens, 499 | searcher = new Searcher(root, []); 500 | searcher.tokens = tokens; 501 | while (element) { 502 | if (searcher.matchesAllRules(element)) { 503 | return element; 504 | } 505 | element = element.parentNode; 506 | } 507 | }; 508 | 509 | function manipulateDOM(element, html, callback) { 510 | var context = document, 511 | isTable = element.nodeName === 'TABLE', 512 | shim, 513 | div; 514 | 515 | div = context.createElement('div'); 516 | div.innerHTML = '<' + element.nodeName + '>' + html + ''; 517 | shim = isTable ? div.lastChild.lastChild : div.lastChild; 518 | callback(isTable ? element.lastChild : element, shim); 519 | div = null; 520 | }; 521 | 522 | function getText(elements) { 523 | var results = '', element, i; 524 | 525 | for (i = 0; elements[i]; i++) { 526 | element = elements[i]; 527 | if (element.nodeType === nodeTypes.TEXT_NODE 528 | || element.nodeType === nodeTypes.CDATA_SECTION_NODE) { 529 | results += element.nodeValue; 530 | } else if (element.nodeType !== nodeTypes.COMMENT_NODE) { 531 | results += getText(element.childNodes); 532 | } 533 | } 534 | 535 | return results; 536 | }; 537 | 538 | /** 539 | * Replaces the content of an element. 540 | * 541 | * @param {Object} element A DOM element 542 | * @param {String} html A string containing HTML 543 | */ 544 | dom.replace = function(element, html) { 545 | manipulateDOM(element, html, function(insert, shim) { 546 | element.replaceChild(shim, insert); 547 | }); 548 | }; 549 | 550 | /** 551 | * Appends an element to the end of an element. 552 | * 553 | * @param {Object} element A DOM element 554 | * @param {String} html A string containing HTML 555 | */ 556 | dom.append = function(element, html) { 557 | manipulateDOM(element, html, function(insertTo, shim) { 558 | insertTo.appendChild(shim.firstChild); 559 | }); 560 | }; 561 | 562 | /** 563 | * Set or get innerHTML. 564 | * 565 | * @param {Object} element A DOM element 566 | * @param {String} html A string containing HTML 567 | */ 568 | dom.html = function(element, html) { 569 | if (arguments.length === 1) { 570 | return element.innerHTML; 571 | } 572 | 573 | try { 574 | element.innerHTML = html; 575 | } catch (e) { 576 | dom.replace(element, html); 577 | } 578 | }; 579 | 580 | /** 581 | * Set or get text nodes. 582 | * 583 | * @param {Object} element A DOM element 584 | * @param {String} text A string containing text 585 | */ 586 | dom.text = function(element, text) { 587 | if (arguments.length === 1) { 588 | return getText(element); 589 | } else { 590 | dom.empty(element); 591 | element.appendChild(document.createTextNode(text)); 592 | } 593 | }; 594 | 595 | /** 596 | * Empty nodes. 597 | * 598 | * @param {Object} element A DOM element 599 | */ 600 | dom.empty = function(element) { 601 | while (element.firstChild) { 602 | element.removeChild(element.firstChild); 603 | } 604 | }; 605 | 606 | /** 607 | * Detects if a class is present, optimised by Henrik Lindqvist 608 | * and Ryan Cannon. 609 | * 610 | * @param {Object} element A DOM element 611 | * @param {String} className The class name 612 | * @return {Boolean} 613 | */ 614 | if (turing.detect('classList')) { 615 | dom.hasClass = function(element, className) { 616 | return element.classList.contains(className); 617 | }; 618 | } else { 619 | dom.hasClass = function(element, className) { 620 | return (' ' + element.className + ' ').indexOf(' ' + className + ' ') !== -1; 621 | }; 622 | } 623 | 624 | /** 625 | * Append CSS classes. 626 | * 627 | * @param {Object} element A DOM element 628 | * @param {String} className The class name 629 | */ 630 | dom.addClass = function(element, className) { 631 | if (!className || typeof className !== 'string') return; 632 | if (element.nodeType !== nodeTypes.ELEMENT_NODE) return; 633 | if (element.classList) return element.classList.add(className); 634 | 635 | if (element.className && element.className.length) { 636 | if (!element.className.match('\\b' + className + '\\b')) { 637 | element.className += ' ' + className; 638 | } 639 | } else { 640 | element.className = className; 641 | } 642 | }; 643 | 644 | /** 645 | * Remove CSS classes. 646 | * 647 | * @param {Object} element A DOM element 648 | * @param {String} className The class name 649 | */ 650 | dom.removeClass = function(element, className) { 651 | if (!className || typeof className !== 'string') return; 652 | if (element.nodeType !== nodeTypes.ELEMENT_NODE) return; 653 | if (element.classList) return element.classList.remove(className); 654 | 655 | if (element.className) { 656 | element.className = element.className. 657 | replace(new RegExp('\\s?\\b' + className + '\\b'), ''). 658 | replace(/^\s+/, ''); 659 | } 660 | }; 661 | 662 | turing.addDetectionTest('getAttribute', function() { 663 | var div = document.createElement('div'); 664 | div.innerHTML = ''; 665 | 666 | if (div.childNodes[0].getAttribute('href') === '/example') { 667 | return true; 668 | } 669 | 670 | // Helps IE release memory associated with the div 671 | div = null; 672 | return false; 673 | }); 674 | 675 | function getAttribute(element, name) { 676 | if (propertyFix[name]) { 677 | name = propertyFix[name]; 678 | } 679 | 680 | if (getAttributeParamFix[name]) { 681 | return element.getAttribute(name, 2); 682 | } 683 | 684 | if (name === 'value' && element.nodeName === 'BUTTON') { 685 | return element.getAttributeNode(name).nodeValue; 686 | } else if (booleanAttributes[name]) { 687 | return element[name] ? name : undefined; 688 | } 689 | 690 | return element.getAttribute(name); 691 | } 692 | 693 | function setAttribute(element, name, value) { 694 | if (propertyFix[name]) { 695 | name = propertyFix[name]; 696 | } 697 | 698 | if (name === 'value' && element.nodeName === 'BUTTON') { 699 | return element.getAttributeNode(name).nodeValue = value; 700 | } 701 | 702 | return element.setAttribute(name, value); 703 | } 704 | 705 | function removeAttribute(element, name) { 706 | if (element.nodeType !== nodeTypes.ELEMENT_NODE) return; 707 | if (propertyFix[name]) name = propertyFix[name]; 708 | setAttribute(element, name, ''); 709 | element.removeAttributeNode(element.getAttributeNode(name)); 710 | } 711 | 712 | /** 713 | * Removes an attribute from a node. 714 | * 715 | * @param {Object} element A DOM element 716 | * @param {String} attribute The attribute name 717 | */ 718 | dom.removeAttr = function(element, attribute) { 719 | turing.detect('getAttribute') ? 720 | element.removeAttribute(attribute) : removeAttribute(element, attribute); 721 | }; 722 | 723 | /** 724 | * Get or set attributes. 725 | * 726 | * @param {Object} element A DOM element 727 | * @param {String} attribute The attribute name 728 | * @param {String} value The attribute value 729 | */ 730 | dom.attr = function(element, attribute, value) { 731 | if (typeof value === 'undefined') { 732 | return turing.detect('getAttribute') ? 733 | element.getAttribute(attribute) : getAttribute(element, attribute); 734 | } else { 735 | if (value === null) { 736 | return dom.removeAttr(element, attribute); 737 | } else { 738 | return turing.detect('getAttribute') ? 739 | element.setAttribute(attribute, value) : setAttribute(element, attribute, value); 740 | } 741 | } 742 | }; 743 | 744 | /** 745 | * Get or set properties. 746 | * 747 | * @param {Object} element A DOM element 748 | * @param {String} attribute The property name 749 | * @param {String|Number|Boolean} value The property value 750 | */ 751 | dom.prop = function(element, property, value) { 752 | if (propertyFix[property]) 753 | property = propertyFix[property]; 754 | if (typeof value === 'undefined') { 755 | return element[property]; 756 | } else { 757 | if (value === null) { 758 | return dom.removeProperty(element, property); 759 | } else { 760 | return element[property] = value; 761 | } 762 | } 763 | }; 764 | 765 | /** 766 | * Removes properties. 767 | * 768 | * @param {Object} element A DOM element 769 | * @param {String} attribute The property name 770 | */ 771 | dom.removeProp = function(element, property) { 772 | if (propertyFix[property]) 773 | property = propertyFix[property]; 774 | try { 775 | element[property] = undefined; 776 | delete element[property]; 777 | } catch (e) { 778 | } 779 | }; 780 | 781 | // Chained API 782 | turing.init(function(arg) { 783 | if (typeof arg === 'string' || typeof arg === 'undefined') { 784 | // CSS selector 785 | return turing.domChain.init(arg); 786 | } 787 | }); 788 | 789 | turing.domChain = { 790 | init: function(selector) { 791 | this.selector = selector; 792 | this.length = 0; 793 | this.prevObject = null; 794 | this.elements = []; 795 | 796 | if (!selector) { 797 | return this; 798 | } else { 799 | return this.find(selector); 800 | } 801 | }, 802 | 803 | writeElements: function() { 804 | for (var i = 0; i < this.elements.length; i++) { 805 | this[i] = this.elements[i]; 806 | } 807 | }, 808 | 809 | /** 810 | * `first` will return a domChain with a length of 1 or 0. 811 | */ 812 | first: function() { 813 | var elements = [], 814 | ret = turing.domChain; 815 | ret.elements = this.elements.length === 0 ? [] : [this.elements[0]]; 816 | ret.selector = this.selector; 817 | ret.length = ret.elements.length; 818 | ret.prevObject = this; 819 | ret.writeElements(); 820 | return ret; 821 | }, 822 | 823 | /** 824 | * Get or set innerHTML. Applied to every element. 825 | * 826 | * @param {String} html A string containing HTML 827 | * @returns {Object} `this` or the innerHTML 828 | */ 829 | html: function(html) { 830 | if (arguments.length === 0) { 831 | return this.elements.length === 0 ? null : dom.html(this[0]); 832 | } else { 833 | for (var i = 0; i < this.elements.length; i++) { 834 | dom.html(this[i], html); 835 | } 836 | } 837 | return this; 838 | }, 839 | 840 | /** 841 | * Get or set text nodes. Applied to every element. 842 | * 843 | * @param {String} text A string containing text to set 844 | * @returns {Object} `this` or the text content 845 | */ 846 | text: function(text) { 847 | if (arguments.length === 0) { 848 | return this.elements.length === 0 ? null : getText(this.elements); 849 | } else { 850 | for (var i = 0; i < this.elements.length; i++) { 851 | dom.text(this.elements[i], text); 852 | } 853 | } 854 | return this; 855 | }, 856 | 857 | /** 858 | * Get or set styles. 859 | * 860 | * @param {Objects} options Either options for a style to set or a property name 861 | * @returns {Object} `this` or the style property 862 | */ 863 | css: function(options) { 864 | if (typeof options === 'string') { 865 | return this.elements.length > 0 ? getStyle(this.elements[0], options) : null; 866 | } else { 867 | for (var i = 0; i < this.elements.length; i++) { 868 | dom.css(this[i], options); 869 | } 870 | } 871 | return this; 872 | }, 873 | 874 | /** 875 | * Add class names. 876 | * 877 | * @param {String} className A class name 878 | * @returns {Object} `this` 879 | */ 880 | addClass: function(className) { 881 | for (var i = 0; i < this.elements.length; i++) { 882 | dom.addClass(this[i], className); 883 | } 884 | return this; 885 | }, 886 | 887 | /** 888 | * Detects if a class is present. 889 | * 890 | * @param {String} className A class name 891 | * @returns {Boolean} 892 | */ 893 | hasClass: function(className) { 894 | for (var i = 0; i < this.length; i++) { 895 | if (dom.hasClass(this[i], className)) { 896 | return true; 897 | } 898 | } 899 | return false; 900 | }, 901 | 902 | /** 903 | * Remove class names. 904 | * 905 | * @param {String} className A class name 906 | * @returns {Object} `this` 907 | */ 908 | removeClass: function(className) { 909 | for (var i = 0; i < this.elements.length; i++) { 910 | dom.removeClass(this[i], className); 911 | } 912 | return this; 913 | }, 914 | 915 | /** 916 | * Get or set an attribute. 917 | * 918 | * @param {String} attribute The attribute name 919 | * @param {String} value The attribute value 920 | * @returns {String} The attribute value 921 | */ 922 | attr: function(attribute, value) { 923 | if (this.elements.length > 0) { 924 | return dom.attr(this[0], attribute, value); 925 | } 926 | }, 927 | 928 | /** 929 | * Remove an attribute. 930 | * 931 | * @param {String} attribute The attribute name 932 | * @returns {Object} `this` 933 | */ 934 | removeAttr: function(attribute) { 935 | if (this.elements.length > 0) { 936 | dom.removeAttr(this[0], attribute); 937 | } 938 | return this; 939 | }, 940 | 941 | /** 942 | * Get or set a property. 943 | * 944 | * @param {String} property The property name 945 | * @param {String} value The property value 946 | * @returns {String} The property value 947 | */ 948 | prop: function(property, value) { 949 | if (this.elements.length > 0) { 950 | return dom.prop(this[0], property, value); 951 | } 952 | }, 953 | 954 | /** 955 | * Removes properties. 956 | * 957 | * @param {String} attribute The property name 958 | */ 959 | removeProp: function(property) { 960 | if (this.elements.length > 0) { 961 | return dom.removeProp(this[0], property, value); 962 | } 963 | }, 964 | 965 | /** 966 | * Append HTML to an element. Applied to every element. 967 | * 968 | * @param {String} html A string containing HTML 969 | * @returns {Object} `this` 970 | */ 971 | append: function(html) { 972 | for (var i = 0; i < this.elements.length; i++) { 973 | dom.append(this[i], html); 974 | } 975 | return this; 976 | }, 977 | 978 | find: function(selector) { 979 | var elements = [], 980 | ret = turing.domChain, 981 | root = document; 982 | 983 | if (this.prevObject) { 984 | if (this.prevObject.elements.length > 0) { 985 | root = this.prevObject.elements[0]; 986 | } else { 987 | root = null; 988 | } 989 | } 990 | 991 | elements = dom.get(selector, root); 992 | this.elements = elements; 993 | ret.elements = elements; 994 | ret.selector = selector; 995 | ret.length = elements.length; 996 | ret.prevObject = this; 997 | ret.writeElements(); 998 | return ret; 999 | } 1000 | }; 1001 | 1002 | turing.domChain.init.prototype = turing.domChain; 1003 | 1004 | /** 1005 | * Enumerable methods can be chained with DOM calls: 1006 | * 1007 | * turing('p').each(function(element) { 1008 | * console.log(element); 1009 | * }); 1010 | * 1011 | */ 1012 | if (typeof turing.enumerable !== 'undefined') { 1013 | turing.domChain['values'] = function() { 1014 | return this.elements; 1015 | }; 1016 | 1017 | turing.enumerable.each(turing.chainableMethods, function(methodName) { 1018 | turing.domChain[methodName] = function(fn) { 1019 | var elements = turing.enumerable[methodName](this, fn), 1020 | ret = turing.domChain; 1021 | this.elements = elements; 1022 | ret.elements = elements; 1023 | ret.selector = this.selector; 1024 | ret.length = elements.length; 1025 | ret.prevObject = this; 1026 | ret.writeElements(); 1027 | return ret; 1028 | }; 1029 | }); 1030 | } 1031 | 1032 | dom.nodeTypes = nodeTypes; 1033 | turing.dom = dom; 1034 | return dom; 1035 | }); 1036 | --------------------------------------------------------------------------------