├── .gitignore ├── example4 ├── fake-bonzo │ ├── main.js │ ├── package.json │ └── bonzo-ender-bridge.js ├── example4.html └── bonzo.js ├── example2 ├── example2.html └── bonzo.js ├── example1 ├── example1.html └── bonzo.js ├── example3 ├── example3.html ├── bonzo-ender-bridge.js ├── bonzo.js └── ender.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.min.js 3 | -------------------------------------------------------------------------------- /example4/fake-bonzo/main.js: -------------------------------------------------------------------------------- 1 | provide('bonzo', bonzo) 2 | -------------------------------------------------------------------------------- /example4/fake-bonzo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fake-bonzo", 3 | "version": "0.0.0", 4 | "description": "Fake Bonzo", 5 | "main": "main.js", 6 | "ender": "bonzo-ender-bridge.js" 7 | } -------------------------------------------------------------------------------- /example2/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 2 6 | 7 | 8 | 9 | 24 | 25 | -------------------------------------------------------------------------------- /example4/example4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 4 6 | 7 | 8 | 9 | 10 | 25 | 26 | -------------------------------------------------------------------------------- /example1/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 1 6 | 7 | 8 | 9 | 28 | 29 | -------------------------------------------------------------------------------- /example3/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 32 | 33 | -------------------------------------------------------------------------------- /example3/bonzo-ender-bridge.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | var b = require('bonzo') 4 | b.setQueryEngine($) 5 | $.ender(b) 6 | $.ender(b(), true) 7 | $.ender({ 8 | create: function (node) { 9 | return $(b.create(node)) 10 | } 11 | }) 12 | 13 | $.id = function (id) { 14 | return $([document.getElementById(id)]) 15 | } 16 | 17 | function indexOf(ar, val) { 18 | for (var i = 0; i < ar.length; i++) if (ar[i] === val) return i 19 | return -1 20 | } 21 | 22 | function uniq(ar) { 23 | var r = [], i = 0, j = 0, k, item, inIt 24 | for (; item = ar[i]; ++i) { 25 | inIt = false 26 | for (k = 0; k < r.length; ++k) { 27 | if (r[k] === item) { 28 | inIt = true; break 29 | } 30 | } 31 | if (!inIt) r[j++] = item 32 | } 33 | return r 34 | } 35 | 36 | $.ender({ 37 | parents: function (selector, closest) { 38 | if (!this.length) return this 39 | ;!selector && (selector = '*') 40 | var collection = $(selector), j, k, p, r = [] 41 | for (j = 0, k = this.length; j < k; j++) { 42 | p = this[j] 43 | while (p = p.parentNode) { 44 | if (~indexOf(collection, p)) { 45 | r.push(p) 46 | if (closest) break; 47 | } 48 | } 49 | } 50 | return $(uniq(r)) 51 | } 52 | 53 | , parent: function() { 54 | return $(uniq(b(this).parent())) 55 | } 56 | 57 | , closest: function (selector) { 58 | return this.parents(selector, true) 59 | } 60 | 61 | , first: function () { 62 | return $(this.length ? this[0] : this) 63 | } 64 | 65 | , last: function () { 66 | return $(this.length ? this[this.length - 1] : []) 67 | } 68 | 69 | , next: function () { 70 | return $(b(this).next()) 71 | } 72 | 73 | , previous: function () { 74 | return $(b(this).previous()) 75 | } 76 | 77 | , appendTo: function (t) { 78 | return b(this.selector).appendTo(t, this) 79 | } 80 | 81 | , prependTo: function (t) { 82 | return b(this.selector).prependTo(t, this) 83 | } 84 | 85 | , insertAfter: function (t) { 86 | return b(this.selector).insertAfter(t, this) 87 | } 88 | 89 | , insertBefore: function (t) { 90 | return b(this.selector).insertBefore(t, this) 91 | } 92 | 93 | , replaceWith: function (t) { 94 | return b(this.selector).replaceWith(t, this) 95 | } 96 | 97 | , siblings: function () { 98 | var i, l, p, r = [] 99 | for (i = 0, l = this.length; i < l; i++) { 100 | p = this[i] 101 | while (p = p.previousSibling) p.nodeType == 1 && r.push(p) 102 | p = this[i] 103 | while (p = p.nextSibling) p.nodeType == 1 && r.push(p) 104 | } 105 | return $(r) 106 | } 107 | 108 | , children: function () { 109 | var i, l, el, r = [] 110 | for (i = 0, l = this.length; i < l; i++) { 111 | if (!(el = b.firstChild(this[i]))) continue; 112 | r.push(el) 113 | while (el = el.nextSibling) el.nodeType == 1 && r.push(el) 114 | } 115 | return $(uniq(r)) 116 | } 117 | 118 | , height: function (v) { 119 | return dimension.call(this, 'height', v) 120 | } 121 | 122 | , width: function (v) { 123 | return dimension.call(this, 'width', v) 124 | } 125 | }, true) 126 | 127 | /** 128 | * @param {string} type either width or height 129 | * @param {number=} opt_v becomes a setter instead of a getter 130 | * @return {number} 131 | */ 132 | function dimension(type, opt_v) { 133 | return typeof opt_v == 'undefined' 134 | ? b(this).dim()[type] 135 | : this.css(type, opt_v) 136 | } 137 | }(ender)); -------------------------------------------------------------------------------- /example4/fake-bonzo/bonzo-ender-bridge.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | var b = require('bonzo') 4 | b.setQueryEngine($) 5 | $.ender(b) 6 | $.ender(b(), true) 7 | $.ender({ 8 | create: function (node) { 9 | return $(b.create(node)) 10 | } 11 | }) 12 | 13 | $.id = function (id) { 14 | return $([document.getElementById(id)]) 15 | } 16 | 17 | function indexOf(ar, val) { 18 | for (var i = 0; i < ar.length; i++) if (ar[i] === val) return i 19 | return -1 20 | } 21 | 22 | function uniq(ar) { 23 | var r = [], i = 0, j = 0, k, item, inIt 24 | for (; item = ar[i]; ++i) { 25 | inIt = false 26 | for (k = 0; k < r.length; ++k) { 27 | if (r[k] === item) { 28 | inIt = true; break 29 | } 30 | } 31 | if (!inIt) r[j++] = item 32 | } 33 | return r 34 | } 35 | 36 | $.ender({ 37 | parents: function (selector, closest) { 38 | if (!this.length) return this 39 | ;!selector && (selector = '*') 40 | var collection = $(selector), j, k, p, r = [] 41 | for (j = 0, k = this.length; j < k; j++) { 42 | p = this[j] 43 | while (p = p.parentNode) { 44 | if (~indexOf(collection, p)) { 45 | r.push(p) 46 | if (closest) break; 47 | } 48 | } 49 | } 50 | return $(uniq(r)) 51 | } 52 | 53 | , parent: function() { 54 | return $(uniq(b(this).parent())) 55 | } 56 | 57 | , closest: function (selector) { 58 | return this.parents(selector, true) 59 | } 60 | 61 | , first: function () { 62 | return $(this.length ? this[0] : this) 63 | } 64 | 65 | , last: function () { 66 | return $(this.length ? this[this.length - 1] : []) 67 | } 68 | 69 | , next: function () { 70 | return $(b(this).next()) 71 | } 72 | 73 | , previous: function () { 74 | return $(b(this).previous()) 75 | } 76 | 77 | , appendTo: function (t) { 78 | return b(this.selector).appendTo(t, this) 79 | } 80 | 81 | , prependTo: function (t) { 82 | return b(this.selector).prependTo(t, this) 83 | } 84 | 85 | , insertAfter: function (t) { 86 | return b(this.selector).insertAfter(t, this) 87 | } 88 | 89 | , insertBefore: function (t) { 90 | return b(this.selector).insertBefore(t, this) 91 | } 92 | 93 | , replaceWith: function (t) { 94 | return b(this.selector).replaceWith(t, this) 95 | } 96 | 97 | , siblings: function () { 98 | var i, l, p, r = [] 99 | for (i = 0, l = this.length; i < l; i++) { 100 | p = this[i] 101 | while (p = p.previousSibling) p.nodeType == 1 && r.push(p) 102 | p = this[i] 103 | while (p = p.nextSibling) p.nodeType == 1 && r.push(p) 104 | } 105 | return $(r) 106 | } 107 | 108 | , children: function () { 109 | var i, l, el, r = [] 110 | for (i = 0, l = this.length; i < l; i++) { 111 | if (!(el = b.firstChild(this[i]))) continue; 112 | r.push(el) 113 | while (el = el.nextSibling) el.nodeType == 1 && r.push(el) 114 | } 115 | return $(uniq(r)) 116 | } 117 | 118 | , height: function (v) { 119 | return dimension.call(this, 'height', v) 120 | } 121 | 122 | , width: function (v) { 123 | return dimension.call(this, 'width', v) 124 | } 125 | }, true) 126 | 127 | /** 128 | * @param {string} type either width or height 129 | * @param {number=} opt_v becomes a setter instead of a getter 130 | * @return {number} 131 | */ 132 | function dimension(type, opt_v) { 133 | return typeof opt_v == 'undefined' 134 | ? b(this).dim()[type] 135 | : this.css(type, opt_v) 136 | } 137 | }(ender)); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How Ender bundles libraries for the browser 2 | ***By [Rod Vagg](https://twitter.com/rvagg)*** 3 | 4 | I was asked an interesting Ender question on IRC (#enderjs on Freenode) and as I was answering it, it occurred to me that the subject would be an ideal way to explain how Ender's multi-library bundling works. So here is that explanation! 5 | 6 | The original question went something like this: 7 | 8 | > When a browser first visits my page, they only get served Bonzo (a DOM manipulation library) as a stand-alone library, but on returning visits they are also served Qwery (a selector engine), Bean (an event manager) and a few other modules in an Ender build. Can I integrate Bonzo into the Ender build on the browser for repeat visitors? 9 | 10 | ## Wait, what's Ender? 11 | 12 | Let's step back a bit and start with some basics. The way I generally explain Ender to people is that it's two different things: 13 | 14 | 1. It's a build tool, for bundling JavaScript libraries together into a single file. The resulting file constitutes a new "framework" based around the jQuery-style DOM element collection pattern: `$('selector').method()`. The constituent libraries provide the functionality for the *methods* and may also provide the selector engine functionality. 15 | 2. It's an *ecosystem* of JavaScript libraries. Ender promotes a small collection of libraries as a base, called **The Jeesh**, which together provide a large portion of the functionality normally required of a JavaScript framework, but there are many more libraries compatible with Ender that add extra functionality. Many of the libraries available for Ender are also usable outside of Ender as stand-alone libraries. 16 | 17 | The Jeesh is made up of the following libraries, each of these also works as a stand-alone library: 18 | 19 | * **[domReady](https://github.com/ded/domready)**: detects when the DOM is ready for manipulation. Provides `$.domReady(callback)` and `$.ready(callback)` methods. 20 | * **[Qwery](https://github.com/ded/qwery)**: a small and fast CSS3-compatible selector engine. Does the work of looking up DOM elements when you call `$('selector')` and also provides `$(elements).find('selector')`, `$(elements).and(elements)` and `$(elements).is('selector')`. 21 | * **[Bonzo](https://github.com/ded/bonzo)**: a DOM manipulation library, providing some of the most commonly used methods, such as `$(elements).css('property', 'value')`, `$(elements).empty()`, `$(elements).after(elements||html)`, and many more. 22 | * **[Bean](https://github.com/fat/bean)**: an event manager, provides jQuery-style `$(elements).bind('event', callback)` and others. 23 | 24 | The Jeesh gives you the features of these four libraries bundled into a neat package for only *11.7 kB* minified and gzipped. 25 | 26 | ## Starting with the basics, Bonzo stand-alone 27 | 28 | Bonzo is a great way to start getting your head around Ender because it's so useful by itself. Let's include it in a page and do some really simple DOM manipulation with it. 29 | 30 | ```html 31 | 32 | 33 | 34 | 35 | Example 1 36 | 37 | 38 | 39 | 58 | 59 | 60 | ``` 61 | 62 | *You can run this as [example1](http://rvagg.github.com/ender-tutorial-guts/example1/example1.html), also available in my GitHub [repository](https://github.com/rvagg/ender-tutorial-guts) for this article.* 63 | 64 | This should look relatively familiar to a jQuery user -- you can see that Bonzo is providing some of the important utilities you need for modifying the DOM. 65 | 66 | ## Jumping straight in, Bonzo inside Ender 67 | 68 | Let's see what happens when we use a simple Ender build that includes Bonzo. We'll also include Qwery so we can skip the `document.getElementById()` noise, and we'll also use Bean to demonstrate how neatly the libraries can mesh together. 69 | 70 | This is done on the command line with: `ender build qwery bean bonzo`. A file named *ender.js* will be created that can be loaded on a suitable HTML page. 71 | 72 | Our script becomes: 73 | 74 | ```js 75 | $('
')
 76 |   .text($('#scr').text())
 77 |   .css({
 78 |     fontWeight: 'bold',
 79 |     border: 'solid 1px red',
 80 |     margin: 10,
 81 |     padding: 10
 82 |   })
 83 |   .bind('click', function () {
 84 |     alert('Clickety clack');
 85 |   })
 86 |   .appendTo('body');
 87 | ```
 88 | 
 89 | *You can run this as [example2](http://rvagg.github.com/ender-tutorial-guts/example2/example2.html), also available in my GitHub [repository](https://github.com/rvagg/ender-tutorial-guts) for this article.*
 90 | 
 91 | Bonzo performs most of the work here but it's bundled up nicely into the `$` object (also available as `ender`). The previous example can be summarised as follows:
 92 | 
 93 | * `bonzo.create()` is now working when HTML is passed to `$()`.
 94 | * Qwery does the work when `$()` is called with anything else, in this case `$('#scr')` is used as a selector for the script element.
 95 | * We're using the no-argument variant of `bonzo.text()` to fetch the `innerHTML` of the script element.
 96 | * Bean makes a showing with the `.bind()` call, but the important point is that it's integrated into our call-chain even though it's a separate library. This is where Ender's bundling magic shines.
 97 | * `bonzo.appendTo()` takes the selector argument which is in turn passed to Qwery to fetch the selected element from the DOM (`document.body`).
 98 | 
 99 | Also important here, which we haven't demonstrated, is we can do all of this on multiple elements in the same collection. The first line could be changed to `$('
')` and we'd end up with two blocks, both responding to the click event.
100 | 
101 | ## Getting educational, pull it apart again!
102 | 
103 | It's possible to pull Bonzo out of the Ender build and manually stitch it back together again. Just like we used to do with our toys when we were children! (Or was that just me?)
104 | 
105 | First, our Ender build is now created with: `ender build qwery bean` (or we could run `ender remove bonzo` to remove Bonzo from the previous example's `ender.js` file).  The new `ender.js` file will contain the selector engine goodness from Qwery, and event management from Bean, but not much else.
106 | 
107 | Bonzo can be loaded separately, but we'll need some special glue to do this. In Ender parlance, this glue is called an Ender **Bridge**.
108 | 
109 | ### The Ender Bridge
110 | 
111 | Ender follows the basic CommonJS Module pattern -- it sets up a simple module registry and gives each module a `module.exports` object and a `require()` method that can be used to fetch any other modules in the build. It also uses a `provide('name', module.exports)` method to insert exports into the registry with the name of your module. The exact details here aren't important and I'll cover how you can build your own Ender module in a later article, for now we just need a basic understanding of the module registry system.
112 | 
113 | Using our Qwery, Bean and Bonzo build, the file looks something like this:
114 | 
115 | ```
116 | |========================================|
117 | | Ender initialisation & module registry |
118 | | (we call this the 'client library')    |
119 | |========================================|
120 | | 'module.exports' setup                 |
121 | |----------------------------------------|
122 | | Qwery source                           |
123 | |----------------------------------------|
124 | | provide('qwery', module.exports)       |
125 | |----------------------------------------|
126 | | Qwery bridge                           |
127 | ==========================================
128 | | 'module.exports' setup                 |
129 | |----------------------------------------|
130 | | Bean source                            |
131 | |----------------------------------------|
132 | | provide('bean', module.exports)        |
133 | |----------------------------------------|
134 | | Bean bridge                            |
135 | ==========================================
136 | | 'module.exports' setup                 |
137 | |----------------------------------------|
138 | | Bonzo source                           |
139 | |----------------------------------------|
140 | | provide('bonzo', module.exports)       |
141 | |----------------------------------------|
142 | | Bonzo bridge                           |
143 | ==========================================
144 | ```
145 | 
146 | To be a useful Ender library, the code should be able to adhere to the CommonJS Module pattern if a `module.exports` or `exports` object exists. Many libraries already do this so they can operate both in the browser and in a CommonJS environment such as Node. Consider Underscore.js for example, it [detects the existence of `exports`](https://github.com/documentcloud/underscore/blob/ca0df9076079a3b2c45475ddb2299fb901a29989/underscore.js#L56-63) and inserts itself onto that object if it exists, otherwise it inserts itself into the global (i.e. `window`) object. This is how Ender compatible libraries that can also be used as stand-alone libraries work too.
147 | 
148 | So, skipping over the complexities here, our libraries are registered within Ender and then we encounter the **Bridge**. Technically the bridge is just an arbitrary piece of code that Ender-compatible libraries are allowed to provide the Ender CLI tool; it could be anything. The intention, though, is to use it as a glue to bind the library into the core `ender` / `$` object. A bridge isn't necessary and can be omitted -- in this case everything found on `module.exports` is automatically bound to the `ender` / `$` object. Underscore.js doesn't need a bridge because it conforms to the standard CommonJS pattern and its methods are utilities that logically belong on `$` -- for example, `$.each(list, callback)`. If a module needs to operate on `$('selector')` collections then it needs a special binding for its methods. Many modules also require quite complex bindings to make them work nicely inside the Ender environment.
149 | 
150 | Bonzo has one of the most complex bridges that you'll find in the Endersphere, so we won't be looking into it here. If you're interested in digging deeper, a simpler bridge with some interesting features can be found in [Morpheus](https://github.com/ded/morpheus/blob/master/src/ender.js), an animation framework for Ender. Morpheus adds a `$.tween()` method and also an `$('selector').animate()` and some additional helper methods.
151 | 
152 | The simplest form of Ender bridge is one that lifts the `module.exports` methods to a new *namespace*. Consider [Moment.js](http://momentjs.com/), the popular date and time library. When used in a CommonJS environment it adds all of its methods to `module.exports`. Without a bridge, when added to an Ender build you'd end up with `$.utc()`, `$.unix()`, `$.add()`, `$.subtract()` and other methods that don't have very meaningful names outside of Moment.js.  They are also likely to conflict with other libraries that you may want to add to your Ender build. The logical solution is to lift them up to `$.moment.utc()` etc., then you also get to use the exported main function as `$.moment(Date|String|Number)`. To achieve this, Moment.js' [bridge](https://github.com/timrwood/moment/blob/master/ender.js) looks like this:
153 | 
154 | ```js
155 | $.ender({ moment: require('moment') })
156 | ```
157 | 
158 | The `$.ender()` method is the way that a bridge can add methods to the global `ender` / `$` object, it takes an optional boolean argument to indicate whether the methods can operate on DOM element collections, i.e. `$('selector').method()`.
159 | 
160 | ### Bonzo in parts
161 | 
162 | Back to what we were originally trying to achieve: we're loading Bonzo as a stand-alone library and we want to integrate it into an Ender build in the browser. There are two important things we need to do to achieve this: (1) load Bonzo's bridge so it can wire Bonzo into Ender, and (2) make Ender aware of Bonzo so a `require('bonzo')` will do the right thing because this is how the bridge fetches Bonzo.
163 | 
164 | Let's first do this the easy way. With an Ender build that just contains Qwery and Bean and Bonzo's bridge in a separate file named *bonzo-ender-bridge.js*, we can do the following:
165 | 
166 | ```html
167 | 
168 | 
169 | 
170 | 
173 | 
174 | ```
175 | 
176 | If you look at the diagram of the Ender file structure above you'll see that we're replicating it with our `
228 |   
229 |   
244 | 
245 | 
246 | ```
247 | 
248 | You can dig further into this and run it as [example4](http://rvagg.github.com/ender-tutorial-guts/example4/example4.html), also available in my GitHub [repository](https://github.com/rvagg/ender-tutorial-guts) for this article.
249 | 
250 | ## That's all folks!
251 | 
252 | Hopefully this has helped demystify the way that Ender packages libraries together; it's really not magic. If you want to dig deeper then a good place to start would be to examine the [client library](https://github.com/ender-js/ender-js/blob/master/ender.js) that appears at the top of each Ender build—it's relatively straightforward and fairly short.
253 | 
254 | This work is licensed under a Creative Commons Attribution 3.0 Unported License.


--------------------------------------------------------------------------------
/example1/bonzo.js:
--------------------------------------------------------------------------------
   1 | /*!
   2 |   * Bonzo: DOM Utility (c) Dustin Diaz 2012
   3 |   * https://github.com/ded/bonzo
   4 |   * License MIT
   5 |   */
   6 | (function (name, definition, context) {
   7 |   if (typeof module != 'undefined' && module.exports) module.exports = definition()
   8 |   else if (typeof context['define'] == 'function' && context['define']['amd']) define(name, definition)
   9 |   else context[name] = definition()
  10 | })('bonzo', function() {
  11 |   var win = window
  12 |     , doc = win.document
  13 |     , html = doc.documentElement
  14 |     , parentNode = 'parentNode'
  15 |     , query = null // used for setting a selector engine host
  16 |     , specialAttributes = /^(checked|value|selected|disabled)$/i
  17 |     , specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i // tags that we have trouble inserting *into*
  18 |     , table = ['', '
', 1] 19 | , td = ['', '
', 3] 20 | , option = ['', 1] 21 | , noscope = ['_', '', 0, 1] 22 | , tagMap = { // tags that we have trouble *inserting* 23 | thead: table, tbody: table, tfoot: table, colgroup: table, caption: table 24 | , tr: ['', '
', 2] 25 | , th: td , td: td 26 | , col: ['', '
', 2] 27 | , fieldset: ['
', '
', 1] 28 | , legend: ['
', '
', 2] 29 | , option: option, optgroup: option 30 | , script: noscope, style: noscope, link: noscope, param: noscope, base: noscope 31 | } 32 | , stateAttributes = /^(checked|selected|disabled)$/ 33 | , ie = /msie/i.test(navigator.userAgent) 34 | , hasClass, addClass, removeClass 35 | , uidMap = {} 36 | , uuids = 0 37 | , digit = /^-?[\d\.]+$/ 38 | , dattr = /^data-(.+)$/ 39 | , px = 'px' 40 | , setAttribute = 'setAttribute' 41 | , getAttribute = 'getAttribute' 42 | , byTag = 'getElementsByTagName' 43 | , features = function() { 44 | var e = doc.createElement('p') 45 | e.innerHTML = 'x
' 46 | return { 47 | hrefExtended: e[byTag]('a')[0][getAttribute]('href') != '#x' // IE < 8 48 | , autoTbody: e[byTag]('tbody').length !== 0 // IE < 8 49 | , computedStyle: doc.defaultView && doc.defaultView.getComputedStyle 50 | , cssFloat: e[byTag]('table')[0].style.styleFloat ? 'styleFloat' : 'cssFloat' 51 | , transform: function () { 52 | var props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'], i 53 | for (i = 0; i < props.length; i++) { 54 | if (props[i] in e.style) return props[i] 55 | } 56 | }() 57 | , classList: 'classList' in e 58 | , opasity: function () { 59 | return typeof doc.createElement('a').style.opacity !== 'undefined' 60 | }() 61 | } 62 | }() 63 | , trimReplace = /(^\s*|\s*$)/g 64 | , whitespaceRegex = /\s+/ 65 | , toString = String.prototype.toString 66 | , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, boxFlex: 1, WebkitBoxFlex: 1, MozBoxFlex: 1 } 67 | , trim = String.prototype.trim ? 68 | function (s) { 69 | return s.trim() 70 | } : 71 | function (s) { 72 | return s.replace(trimReplace, '') 73 | } 74 | 75 | 76 | /** 77 | * @param {string} c a class name to test 78 | * @return {boolean} 79 | */ 80 | function classReg(c) { 81 | return new RegExp("(^|\\s+)" + c + "(\\s+|$)") 82 | } 83 | 84 | 85 | /** 86 | * @param {Bonzo|Array} ar 87 | * @param {function(Object, number, (Bonzo|Array))} fn 88 | * @param {Object=} opt_scope 89 | * @param {boolean=} opt_rev 90 | * @return {Bonzo|Array} 91 | */ 92 | function each(ar, fn, opt_scope, opt_rev) { 93 | var ind, i = 0, l = ar.length 94 | for (; i < l; i++) { 95 | ind = opt_rev ? ar.length - i - 1 : i 96 | fn.call(opt_scope || ar[ind], ar[ind], ind, ar) 97 | } 98 | return ar 99 | } 100 | 101 | 102 | /** 103 | * @param {Bonzo|Array} ar 104 | * @param {function(Object, number, (Bonzo|Array))} fn 105 | * @param {Object=} opt_scope 106 | * @return {Bonzo|Array} 107 | */ 108 | function deepEach(ar, fn, opt_scope) { 109 | for (var i = 0, l = ar.length; i < l; i++) { 110 | if (isNode(ar[i])) { 111 | deepEach(ar[i].childNodes, fn, opt_scope) 112 | fn.call(opt_scope || ar[i], ar[i], i, ar) 113 | } 114 | } 115 | return ar 116 | } 117 | 118 | 119 | /** 120 | * @param {string} s 121 | * @return {string} 122 | */ 123 | function camelize(s) { 124 | return s.replace(/-(.)/g, function (m, m1) { 125 | return m1.toUpperCase() 126 | }) 127 | } 128 | 129 | 130 | /** 131 | * @param {string} s 132 | * @return {string} 133 | */ 134 | function decamelize(s) { 135 | return s ? s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : s 136 | } 137 | 138 | 139 | /** 140 | * @param {Element} el 141 | * @return {*} 142 | */ 143 | function data(el) { 144 | el[getAttribute]('data-node-uid') || el[setAttribute]('data-node-uid', ++uuids) 145 | var uid = el[getAttribute]('data-node-uid') 146 | return uidMap[uid] || (uidMap[uid] = {}) 147 | } 148 | 149 | 150 | /** 151 | * removes the data associated with an element 152 | * @param {Element} el 153 | */ 154 | function clearData(el) { 155 | var uid = el[getAttribute]('data-node-uid') 156 | if (uid) delete uidMap[uid] 157 | } 158 | 159 | 160 | function dataValue(d) { 161 | var f 162 | try { 163 | return (d === null || d === undefined) ? undefined : 164 | d === 'true' ? true : 165 | d === 'false' ? false : 166 | d === 'null' ? null : 167 | (f = parseFloat(d)) == d ? f : d; 168 | } catch(e) {} 169 | return undefined 170 | } 171 | 172 | function isNode(node) { 173 | return node && node.nodeName && (node.nodeType == 1 || node.nodeType == 11) 174 | } 175 | 176 | 177 | /** 178 | * @param {Bonzo|Array} ar 179 | * @param {function(Object, number, (Bonzo|Array))} fn 180 | * @param {Object=} opt_scope 181 | * @return {boolean} whether `some`thing was found 182 | */ 183 | function some(ar, fn, opt_scope) { 184 | for (var i = 0, j = ar.length; i < j; ++i) if (fn.call(opt_scope || null, ar[i], i, ar)) return true 185 | return false 186 | } 187 | 188 | 189 | /** 190 | * this could be a giant enum of CSS properties 191 | * but in favor of file size sans-closure deadcode optimizations 192 | * we're just asking for any ol string 193 | * then it gets transformed into the appropriate style property for JS access 194 | * @param {string} p 195 | * @return {string} 196 | */ 197 | function styleProperty(p) { 198 | (p == 'transform' && (p = features.transform)) || 199 | (/^transform-?[Oo]rigin$/.test(p) && (p = features.transform + "Origin")) || 200 | (p == 'float' && (p = features.cssFloat)) 201 | return p ? camelize(p) : null 202 | } 203 | 204 | var getStyle = features.computedStyle ? 205 | function (el, property) { 206 | var value = null 207 | , computed = doc.defaultView.getComputedStyle(el, '') 208 | computed && (value = computed[property]) 209 | return el.style[property] || value 210 | } : 211 | 212 | (ie && html.currentStyle) ? 213 | 214 | /** 215 | * @param {Element} el 216 | * @param {string} property 217 | * @return {string|number} 218 | */ 219 | function (el, property) { 220 | if (property == 'opacity' && !features.opasity) { 221 | var val = 100 222 | try { 223 | val = el['filters']['DXImageTransform.Microsoft.Alpha'].opacity 224 | } catch (e1) { 225 | try { 226 | val = el['filters']('alpha').opacity 227 | } catch (e2) {} 228 | } 229 | return val / 100 230 | } 231 | var value = el.currentStyle ? el.currentStyle[property] : null 232 | return el.style[property] || value 233 | } : 234 | 235 | function (el, property) { 236 | return el.style[property] 237 | } 238 | 239 | // this insert method is intense 240 | function insert(target, host, fn, rev) { 241 | var i = 0, self = host || this, r = [] 242 | // target nodes could be a css selector if it's a string and a selector engine is present 243 | // otherwise, just use target 244 | , nodes = query && typeof target == 'string' && target.charAt(0) != '<' ? query(target) : target 245 | // normalize each node in case it's still a string and we need to create nodes on the fly 246 | each(normalize(nodes), function (t, j) { 247 | each(self, function (el) { 248 | fn(t, r[i++] = j > 0 ? cloneNode(self, el) : el) 249 | }, null, rev) 250 | }, this, rev) 251 | self.length = i 252 | each(r, function (e) { 253 | self[--i] = e 254 | }, null, !rev) 255 | return self 256 | } 257 | 258 | 259 | /** 260 | * sets an element to an explicit x/y position on the page 261 | * @param {Element} el 262 | * @param {?number} x 263 | * @param {?number} y 264 | */ 265 | function xy(el, x, y) { 266 | var $el = bonzo(el) 267 | , style = $el.css('position') 268 | , offset = $el.offset() 269 | , rel = 'relative' 270 | , isRel = style == rel 271 | , delta = [parseInt($el.css('left'), 10), parseInt($el.css('top'), 10)] 272 | 273 | if (style == 'static') { 274 | $el.css('position', rel) 275 | style = rel 276 | } 277 | 278 | isNaN(delta[0]) && (delta[0] = isRel ? 0 : el.offsetLeft) 279 | isNaN(delta[1]) && (delta[1] = isRel ? 0 : el.offsetTop) 280 | 281 | x != null && (el.style.left = x - offset.left + delta[0] + px) 282 | y != null && (el.style.top = y - offset.top + delta[1] + px) 283 | 284 | } 285 | 286 | // classList support for class management 287 | // altho to be fair, the api sucks because it won't accept multiple classes at once 288 | if (features.classList) { 289 | hasClass = function (el, c) { 290 | return el.classList.contains(c) 291 | } 292 | addClass = function (el, c) { 293 | el.classList.add(c) 294 | } 295 | removeClass = function (el, c) { 296 | el.classList.remove(c) 297 | } 298 | } 299 | else { 300 | hasClass = function (el, c) { 301 | return classReg(c).test(el.className) 302 | } 303 | addClass = function (el, c) { 304 | el.className = trim(el.className + ' ' + c) 305 | } 306 | removeClass = function (el, c) { 307 | el.className = trim(el.className.replace(classReg(c), ' ')) 308 | } 309 | } 310 | 311 | 312 | /** 313 | * this allows method calling for setting values 314 | * 315 | * @example 316 | * bonzo(elements).css('color', function (el) { 317 | * return el.getAttribute('data-original-color') 318 | * }) 319 | * 320 | * @param {Element} el 321 | * @param {function (Element)|string} 322 | * @return {string} 323 | */ 324 | function setter(el, v) { 325 | return typeof v == 'function' ? v(el) : v 326 | } 327 | 328 | /** 329 | * @constructor 330 | * @param {Array.|Element|Node|string} elements 331 | */ 332 | function Bonzo(elements) { 333 | this.length = 0 334 | if (elements) { 335 | elements = typeof elements !== 'string' && 336 | !elements.nodeType && 337 | typeof elements.length !== 'undefined' ? 338 | elements : 339 | [elements] 340 | this.length = elements.length 341 | for (var i = 0; i < elements.length; i++) this[i] = elements[i] 342 | } 343 | } 344 | 345 | Bonzo.prototype = { 346 | 347 | /** 348 | * @param {number} index 349 | * @return {Element|Node} 350 | */ 351 | get: function (index) { 352 | return this[index] || null 353 | } 354 | 355 | // itetators 356 | /** 357 | * @param {function(Element|Node)} fn 358 | * @param {Object=} opt_scope 359 | * @return {Bonzo} 360 | */ 361 | , each: function (fn, opt_scope) { 362 | return each(this, fn, opt_scope) 363 | } 364 | 365 | /** 366 | * @param {Function} fn 367 | * @param {Object=} opt_scope 368 | * @return {Bonzo} 369 | */ 370 | , deepEach: function (fn, opt_scope) { 371 | return deepEach(this, fn, opt_scope) 372 | } 373 | 374 | 375 | /** 376 | * @param {Function} fn 377 | * @param {Function=} opt_reject 378 | * @return {Array} 379 | */ 380 | , map: function (fn, opt_reject) { 381 | var m = [], n, i 382 | for (i = 0; i < this.length; i++) { 383 | n = fn.call(this, this[i], i) 384 | opt_reject ? (opt_reject(n) && m.push(n)) : m.push(n) 385 | } 386 | return m 387 | } 388 | 389 | // text and html inserters! 390 | 391 | /** 392 | * @param {string} h the HTML to insert 393 | * @param {boolean=} opt_text whether to set or get text content 394 | * @return {Bonzo|string} 395 | */ 396 | , html: function (h, opt_text) { 397 | var method = opt_text 398 | ? html.textContent === undefined ? 'innerText' : 'textContent' 399 | : 'innerHTML' 400 | , that = this 401 | , append = function (el, i) { 402 | each(normalize(h, that, i), function (node) { 403 | el.appendChild(node) 404 | }) 405 | } 406 | , updateElement = function (el, i) { 407 | try { 408 | if (opt_text || (typeof h == 'string' && !specialTags.test(el.tagName))) { 409 | return el[method] = h 410 | } 411 | } catch (e) {} 412 | append(el, i) 413 | } 414 | return typeof h != 'undefined' 415 | ? this.empty().each(updateElement) 416 | : this[0] ? this[0][method] : '' 417 | } 418 | 419 | /** 420 | * @param {string=} opt_text the text to set, otherwise this is a getter 421 | * @return {Bonzo|string} 422 | */ 423 | , text: function (opt_text) { 424 | return this.html(opt_text, true) 425 | } 426 | 427 | // more related insertion methods 428 | 429 | /** 430 | * @param {Bonzo|string|Element|Array} node 431 | * @return {Bonzo} 432 | */ 433 | , append: function (node) { 434 | var that = this 435 | return this.each(function (el, i) { 436 | each(normalize(node, that, i), function (i) { 437 | el.appendChild(i) 438 | }) 439 | }) 440 | } 441 | 442 | 443 | /** 444 | * @param {Bonzo|string|Element|Array} node 445 | * @return {Bonzo} 446 | */ 447 | , prepend: function (node) { 448 | var that = this 449 | return this.each(function (el, i) { 450 | var first = el.firstChild 451 | each(normalize(node, that, i), function (i) { 452 | el.insertBefore(i, first) 453 | }) 454 | }) 455 | } 456 | 457 | 458 | /** 459 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 460 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 461 | * @return {Bonzo} 462 | */ 463 | , appendTo: function (target, opt_host) { 464 | return insert.call(this, target, opt_host, function (t, el) { 465 | t.appendChild(el) 466 | }) 467 | } 468 | 469 | 470 | /** 471 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 472 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 473 | * @return {Bonzo} 474 | */ 475 | , prependTo: function (target, opt_host) { 476 | return insert.call(this, target, opt_host, function (t, el) { 477 | t.insertBefore(el, t.firstChild) 478 | }, 1) 479 | } 480 | 481 | 482 | /** 483 | * @param {Bonzo|string|Element|Array} node 484 | * @return {Bonzo} 485 | */ 486 | , before: function (node) { 487 | var that = this 488 | return this.each(function (el, i) { 489 | each(normalize(node, that, i), function (i) { 490 | el[parentNode].insertBefore(i, el) 491 | }) 492 | }) 493 | } 494 | 495 | 496 | /** 497 | * @param {Bonzo|string|Element|Array} node 498 | * @return {Bonzo} 499 | */ 500 | , after: function (node) { 501 | var that = this 502 | return this.each(function (el, i) { 503 | each(normalize(node, that, i), function (i) { 504 | el[parentNode].insertBefore(i, el.nextSibling) 505 | }, null, 1) 506 | }) 507 | } 508 | 509 | 510 | /** 511 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 512 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 513 | * @return {Bonzo} 514 | */ 515 | , insertBefore: function (target, opt_host) { 516 | return insert.call(this, target, opt_host, function (t, el) { 517 | t[parentNode].insertBefore(el, t) 518 | }) 519 | } 520 | 521 | 522 | /** 523 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 524 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 525 | * @return {Bonzo} 526 | */ 527 | , insertAfter: function (target, opt_host) { 528 | return insert.call(this, target, opt_host, function (t, el) { 529 | var sibling = t.nextSibling 530 | sibling ? 531 | t[parentNode].insertBefore(el, sibling) : 532 | t[parentNode].appendChild(el) 533 | }, 1) 534 | } 535 | 536 | 537 | /** 538 | * @param {Bonzo|string|Element|Array} node 539 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 540 | * @return {Bonzo} 541 | */ 542 | , replaceWith: function (node, opt_host) { 543 | var ret = bonzo(normalize(node)).insertAfter(this, opt_host) 544 | this.remove() 545 | Bonzo.call(opt_host || this, ret) 546 | return opt_host || this 547 | } 548 | 549 | // class management 550 | 551 | /** 552 | * @param {string} c 553 | * @return {Bonzo} 554 | */ 555 | , addClass: function (c) { 556 | c = toString.call(c).split(whitespaceRegex) 557 | return this.each(function (el) { 558 | // we `each` here so you can do $el.addClass('foo bar') 559 | each(c, function (c) { 560 | if (c && !hasClass(el, setter(el, c))) 561 | addClass(el, setter(el, c)) 562 | }) 563 | }) 564 | } 565 | 566 | 567 | /** 568 | * @param {string} c 569 | * @return {Bonzo} 570 | */ 571 | , removeClass: function (c) { 572 | c = toString.call(c).split(whitespaceRegex) 573 | return this.each(function (el) { 574 | each(c, function (c) { 575 | if (c && hasClass(el, setter(el, c))) 576 | removeClass(el, setter(el, c)) 577 | }) 578 | }) 579 | } 580 | 581 | 582 | /** 583 | * @param {string} c 584 | * @return {boolean} 585 | */ 586 | , hasClass: function (c) { 587 | c = toString.call(c).split(whitespaceRegex) 588 | return some(this, function (el) { 589 | return some(c, function (c) { 590 | return c && hasClass(el, c) 591 | }) 592 | }) 593 | } 594 | 595 | 596 | /** 597 | * @param {string} c classname to toggle 598 | * @param {boolean=} opt_condition whether to add or remove the class straight away 599 | * @return {Bonzo} 600 | */ 601 | , toggleClass: function (c, opt_condition) { 602 | c = toString.call(c).split(whitespaceRegex) 603 | return this.each(function (el) { 604 | each(c, function (c) { 605 | if (c) { 606 | typeof opt_condition !== 'undefined' ? 607 | opt_condition ? addClass(el, c) : removeClass(el, c) : 608 | hasClass(el, c) ? removeClass(el, c) : addClass(el, c) 609 | } 610 | }) 611 | }) 612 | } 613 | 614 | // display togglers 615 | 616 | /** 617 | * @param {string=} opt_type useful to set back to anything other than an empty string 618 | * @return {Bonzo} 619 | */ 620 | , show: function (opt_type) { 621 | opt_type = typeof opt_type == 'string' ? opt_type : '' 622 | return this.each(function (el) { 623 | el.style.display = opt_type 624 | }) 625 | } 626 | 627 | 628 | /** 629 | * @return {Bonzo} 630 | */ 631 | , hide: function () { 632 | return this.each(function (el) { 633 | el.style.display = 'none' 634 | }) 635 | } 636 | 637 | 638 | /** 639 | * @param {Function=} opt_callback 640 | * @param {string=} opt_type 641 | * @return {Bonzo} 642 | */ 643 | , toggle: function (opt_callback, opt_type) { 644 | opt_type = typeof opt_type == 'string' ? opt_type : ''; 645 | typeof opt_callback != 'function' && (opt_callback = null) 646 | return this.each(function (el) { 647 | el.style.display = (el.offsetWidth || el.offsetHeight) ? 'none' : opt_type; 648 | opt_callback && opt_callback.call(el) 649 | }) 650 | } 651 | 652 | 653 | // DOM Walkers & getters 654 | 655 | /** 656 | * @return {Element|Node} 657 | */ 658 | , first: function () { 659 | return bonzo(this.length ? this[0] : []) 660 | } 661 | 662 | 663 | /** 664 | * @return {Element|Node} 665 | */ 666 | , last: function () { 667 | return bonzo(this.length ? this[this.length - 1] : []) 668 | } 669 | 670 | 671 | /** 672 | * @return {Element|Node} 673 | */ 674 | , next: function () { 675 | return this.related('nextSibling') 676 | } 677 | 678 | 679 | /** 680 | * @return {Element|Node} 681 | */ 682 | , previous: function () { 683 | return this.related('previousSibling') 684 | } 685 | 686 | 687 | /** 688 | * @return {Element|Node} 689 | */ 690 | , parent: function() { 691 | return this.related(parentNode) 692 | } 693 | 694 | 695 | /** 696 | * @private 697 | * @param {string} method the directional DOM method 698 | * @return {Element|Node} 699 | */ 700 | , related: function (method) { 701 | return this.map( 702 | function (el) { 703 | el = el[method] 704 | while (el && el.nodeType !== 1) { 705 | el = el[method] 706 | } 707 | return el || 0 708 | }, 709 | function (el) { 710 | return el 711 | } 712 | ) 713 | } 714 | 715 | 716 | /** 717 | * @return {Bonzo} 718 | */ 719 | , focus: function () { 720 | this.length && this[0].focus() 721 | return this 722 | } 723 | 724 | 725 | /** 726 | * @return {Bonzo} 727 | */ 728 | , blur: function () { 729 | this.length && this[0].blur() 730 | return this 731 | } 732 | 733 | // style getter setter & related methods 734 | 735 | /** 736 | * @param {Object|string} o 737 | * @param {string=} opt_v 738 | * @return {Bonzo|string} 739 | */ 740 | , css: function (o, opt_v) { 741 | var p, iter = o 742 | // is this a request for just getting a style? 743 | if (opt_v === undefined && typeof o == 'string') { 744 | // repurpose 'v' 745 | opt_v = this[0] 746 | if (!opt_v) return null 747 | if (opt_v === doc || opt_v === win) { 748 | p = (opt_v === doc) ? bonzo.doc() : bonzo.viewport() 749 | return o == 'width' ? p.width : o == 'height' ? p.height : '' 750 | } 751 | return (o = styleProperty(o)) ? getStyle(opt_v, o) : null 752 | } 753 | 754 | if (typeof o == 'string') { 755 | iter = {} 756 | iter[o] = opt_v 757 | } 758 | 759 | if (ie && iter.opacity) { 760 | // oh this 'ol gamut 761 | iter.filter = 'alpha(opacity=' + (iter.opacity * 100) + ')' 762 | // give it layout 763 | iter.zoom = o.zoom || 1; 764 | delete iter.opacity; 765 | } 766 | 767 | function fn(el, p, v) { 768 | for (var k in iter) { 769 | if (iter.hasOwnProperty(k)) { 770 | v = iter[k]; 771 | // change "5" to "5px" - unless you're line-height, which is allowed 772 | (p = styleProperty(k)) && digit.test(v) && !(p in unitless) && (v += px) 773 | try { el.style[p] = setter(el, v) } catch(e) {} 774 | } 775 | } 776 | } 777 | return this.each(fn) 778 | } 779 | 780 | 781 | /** 782 | * @param {number=} opt_x 783 | * @param {number=} opt_y 784 | * @return {Bonzo|number} 785 | */ 786 | , offset: function (opt_x, opt_y) { 787 | if (typeof opt_x == 'number' || typeof opt_y == 'number') { 788 | return this.each(function (el) { 789 | xy(el, opt_x, opt_y) 790 | }) 791 | } 792 | if (!this[0]) return { 793 | top: 0 794 | , left: 0 795 | , height: 0 796 | , width: 0 797 | } 798 | var el = this[0] 799 | , width = el.offsetWidth 800 | , height = el.offsetHeight 801 | , top = el.offsetTop 802 | , left = el.offsetLeft 803 | while (el = el.offsetParent) { 804 | top = top + el.offsetTop 805 | left = left + el.offsetLeft 806 | 807 | if (el != doc.body) { 808 | top -= el.scrollTop 809 | left -= el.scrollLeft 810 | } 811 | } 812 | 813 | return { 814 | top: top 815 | , left: left 816 | , height: height 817 | , width: width 818 | } 819 | } 820 | 821 | 822 | /** 823 | * @return {number} 824 | */ 825 | , dim: function () { 826 | if (!this.length) return { height: 0, width: 0 } 827 | var el = this[0] 828 | , orig = !el.offsetWidth && !el.offsetHeight ? 829 | // el isn't visible, can't be measured properly, so fix that 830 | function (t) { 831 | var s = { 832 | position: el.style.position || '' 833 | , visibility: el.style.visibility || '' 834 | , display: el.style.display || '' 835 | } 836 | t.first().css({ 837 | position: 'absolute' 838 | , visibility: 'hidden' 839 | , display: 'block' 840 | }) 841 | return s 842 | }(this) : null 843 | , width = el.offsetWidth 844 | , height = el.offsetHeight 845 | 846 | orig && this.first().css(orig) 847 | return { 848 | height: height 849 | , width: width 850 | } 851 | } 852 | 853 | // attributes are hard. go shopping 854 | 855 | /** 856 | * @param {string} k an attribute to get or set 857 | * @param {string=} opt_v the value to set 858 | * @return {Bonzo|string} 859 | */ 860 | , attr: function (k, opt_v) { 861 | var el = this[0] 862 | if (typeof k != 'string' && !(k instanceof String)) { 863 | for (var n in k) { 864 | k.hasOwnProperty(n) && this.attr(n, k[n]) 865 | } 866 | return this 867 | } 868 | return typeof opt_v == 'undefined' ? 869 | !el ? null : specialAttributes.test(k) ? 870 | stateAttributes.test(k) && typeof el[k] == 'string' ? 871 | true : el[k] : (k == 'href' || k =='src') && features.hrefExtended ? 872 | el[getAttribute](k, 2) : el[getAttribute](k) : 873 | this.each(function (el) { 874 | specialAttributes.test(k) ? (el[k] = setter(el, opt_v)) : el[setAttribute](k, setter(el, opt_v)) 875 | }) 876 | } 877 | 878 | 879 | /** 880 | * @param {string} k 881 | * @return {Bonzo} 882 | */ 883 | , removeAttr: function (k) { 884 | return this.each(function (el) { 885 | stateAttributes.test(k) ? (el[k] = false) : el.removeAttribute(k) 886 | }) 887 | } 888 | 889 | 890 | /** 891 | * @param {string=} opt_s 892 | * @return {Bonzo|string} 893 | */ 894 | , val: function (s) { 895 | return (typeof s == 'string') ? 896 | this.attr('value', s) : 897 | this.length ? this[0].value : null 898 | } 899 | 900 | // use with care and knowledge. this data() method uses data attributes on the DOM nodes 901 | // to do this differently costs a lot more code. c'est la vie 902 | /** 903 | * @param {string|Object=} opt_k the key for which to get or set data 904 | * @param {Object=} opt_v 905 | * @return {Bonzo|Object} 906 | */ 907 | , data: function (opt_k, opt_v) { 908 | var el = this[0], o, m 909 | if (typeof opt_v === 'undefined') { 910 | if (!el) return null 911 | o = data(el) 912 | if (typeof opt_k === 'undefined') { 913 | each(el.attributes, function (a) { 914 | (m = ('' + a.name).match(dattr)) && (o[camelize(m[1])] = dataValue(a.value)) 915 | }) 916 | return o 917 | } else { 918 | if (typeof o[opt_k] === 'undefined') 919 | o[opt_k] = dataValue(this.attr('data-' + decamelize(opt_k))) 920 | return o[opt_k] 921 | } 922 | } else { 923 | return this.each(function (el) { data(el)[opt_k] = opt_v }) 924 | } 925 | } 926 | 927 | // DOM detachment & related 928 | 929 | /** 930 | * @return {Bonzo} 931 | */ 932 | , remove: function () { 933 | this.deepEach(clearData) 934 | 935 | return this.each(function (el) { 936 | el[parentNode] && el[parentNode].removeChild(el) 937 | }) 938 | } 939 | 940 | 941 | /** 942 | * @return {Bonzo} 943 | */ 944 | , empty: function () { 945 | return this.each(function (el) { 946 | deepEach(el.childNodes, clearData) 947 | 948 | while (el.firstChild) { 949 | el.removeChild(el.firstChild) 950 | } 951 | }) 952 | } 953 | 954 | 955 | /** 956 | * @return {Bonzo} 957 | */ 958 | , detach: function () { 959 | return this.each(function (el) { 960 | el[parentNode].removeChild(el) 961 | }) 962 | } 963 | 964 | // who uses a mouse anyway? oh right. 965 | 966 | /** 967 | * @param {number} y 968 | */ 969 | , scrollTop: function (y) { 970 | return scroll.call(this, null, y, 'y') 971 | } 972 | 973 | 974 | /** 975 | * @param {number} x 976 | */ 977 | , scrollLeft: function (x) { 978 | return scroll.call(this, x, null, 'x') 979 | } 980 | 981 | } 982 | 983 | function normalize(node, host, clone) { 984 | var i, l, ret 985 | if (typeof node == 'string') return bonzo.create(node) 986 | if (isNode(node)) node = [ node ] 987 | if (clone) { 988 | ret = [] // don't change original array 989 | for (i = 0, l = node.length; i < l; i++) ret[i] = cloneNode(host, node[i]) 990 | return ret 991 | } 992 | return node 993 | } 994 | 995 | function cloneNode(host, el) { 996 | var c = el.cloneNode(true) 997 | , cloneElems 998 | , elElems 999 | 1000 | // check for existence of an event cloner 1001 | // preferably https://github.com/fat/bean 1002 | // otherwise Bonzo won't do this for you 1003 | if (host.$ && typeof host.cloneEvents == 'function') { 1004 | host.$(c).cloneEvents(el) 1005 | 1006 | // clone events from every child node 1007 | cloneElems = host.$(c).find('*') 1008 | elElems = host.$(el).find('*') 1009 | 1010 | for (var i = 0; i < elElems.length; i++) 1011 | host.$(cloneElems[i]).cloneEvents(elElems[i]) 1012 | } 1013 | return c 1014 | } 1015 | 1016 | function scroll(x, y, type) { 1017 | var el = this[0] 1018 | if (!el) return this 1019 | if (x == null && y == null) { 1020 | return (isBody(el) ? getWindowScroll() : { x: el.scrollLeft, y: el.scrollTop })[type] 1021 | } 1022 | if (isBody(el)) { 1023 | win.scrollTo(x, y) 1024 | } else { 1025 | x != null && (el.scrollLeft = x) 1026 | y != null && (el.scrollTop = y) 1027 | } 1028 | return this 1029 | } 1030 | 1031 | function isBody(element) { 1032 | return element === win || (/^(?:body|html)$/i).test(element.tagName) 1033 | } 1034 | 1035 | function getWindowScroll() { 1036 | return { x: win.pageXOffset || html.scrollLeft, y: win.pageYOffset || html.scrollTop } 1037 | } 1038 | 1039 | /** 1040 | * @param {Array.|Element|Node|string} els 1041 | * @return {Bonzo} 1042 | */ 1043 | function bonzo(els) { 1044 | return new Bonzo(els) 1045 | } 1046 | 1047 | bonzo.setQueryEngine = function (q) { 1048 | query = q; 1049 | delete bonzo.setQueryEngine 1050 | } 1051 | 1052 | bonzo.aug = function (o, target) { 1053 | // for those standalone bonzo users. this love is for you. 1054 | for (var k in o) { 1055 | o.hasOwnProperty(k) && ((target || Bonzo.prototype)[k] = o[k]) 1056 | } 1057 | } 1058 | 1059 | bonzo.create = function (node) { 1060 | // hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh 1061 | return typeof node == 'string' && node !== '' ? 1062 | function () { 1063 | var tag = /^\s*<([^\s>]+)/.exec(node) 1064 | , el = doc.createElement('div') 1065 | , els = [] 1066 | , p = tag ? tagMap[tag[1].toLowerCase()] : null 1067 | , dep = p ? p[2] + 1 : 1 1068 | , ns = p && p[3] 1069 | , pn = parentNode 1070 | , tb = features.autoTbody && p && p[0] == '' && !(/,
, etc. 1079 | if ((!tag || el.nodeType == 1) && (!tb || el.tagName.toLowerCase() != 'tbody')) { 1080 | els.push(el) 1081 | } 1082 | } while (el = el.nextSibling) 1083 | // IE < 9 gives us a parentNode which messes up insert() check for cloning 1084 | // `dep` > 1 can also cause problems with the insert() check (must do this last) 1085 | each(els, function(el) { el[pn] && el[pn].removeChild(el) }) 1086 | return els 1087 | }() : isNode(node) ? [node.cloneNode(true)] : [] 1088 | } 1089 | 1090 | bonzo.doc = function () { 1091 | var vp = bonzo.viewport() 1092 | return { 1093 | width: Math.max(doc.body.scrollWidth, html.scrollWidth, vp.width) 1094 | , height: Math.max(doc.body.scrollHeight, html.scrollHeight, vp.height) 1095 | } 1096 | } 1097 | 1098 | bonzo.firstChild = function (el) { 1099 | for (var c = el.childNodes, i = 0, j = (c && c.length) || 0, e; i < j; i++) { 1100 | if (c[i].nodeType === 1) e = c[j = i] 1101 | } 1102 | return e 1103 | } 1104 | 1105 | bonzo.viewport = function () { 1106 | return { 1107 | width: ie ? html.clientWidth : self.innerWidth 1108 | , height: ie ? html.clientHeight : self.innerHeight 1109 | } 1110 | } 1111 | 1112 | bonzo.isAncestor = 'compareDocumentPosition' in html ? 1113 | function (container, element) { 1114 | return (container.compareDocumentPosition(element) & 16) == 16 1115 | } : 'contains' in html ? 1116 | function (container, element) { 1117 | return container !== element && container.contains(element); 1118 | } : 1119 | function (container, element) { 1120 | while (element = element[parentNode]) { 1121 | if (element === container) { 1122 | return true 1123 | } 1124 | } 1125 | return false 1126 | } 1127 | 1128 | return bonzo 1129 | }, this); // the only line we care about using a semi-colon. placed here for concatenation tools 1130 | -------------------------------------------------------------------------------- /example2/bonzo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bonzo: DOM Utility (c) Dustin Diaz 2012 3 | * https://github.com/ded/bonzo 4 | * License MIT 5 | */ 6 | (function (name, definition, context) { 7 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 8 | else if (typeof context['define'] == 'function' && context['define']['amd']) define(name, definition) 9 | else context[name] = definition() 10 | })('bonzo', function() { 11 | var win = window 12 | , doc = win.document 13 | , html = doc.documentElement 14 | , parentNode = 'parentNode' 15 | , query = null // used for setting a selector engine host 16 | , specialAttributes = /^(checked|value|selected|disabled)$/i 17 | , specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i // tags that we have trouble inserting *into* 18 | , table = ['', '
', 1] 19 | , td = ['', '
', 3] 20 | , option = ['', 1] 21 | , noscope = ['_', '', 0, 1] 22 | , tagMap = { // tags that we have trouble *inserting* 23 | thead: table, tbody: table, tfoot: table, colgroup: table, caption: table 24 | , tr: ['', '
', 2] 25 | , th: td , td: td 26 | , col: ['', '
', 2] 27 | , fieldset: ['
', '
', 1] 28 | , legend: ['
', '
', 2] 29 | , option: option, optgroup: option 30 | , script: noscope, style: noscope, link: noscope, param: noscope, base: noscope 31 | } 32 | , stateAttributes = /^(checked|selected|disabled)$/ 33 | , ie = /msie/i.test(navigator.userAgent) 34 | , hasClass, addClass, removeClass 35 | , uidMap = {} 36 | , uuids = 0 37 | , digit = /^-?[\d\.]+$/ 38 | , dattr = /^data-(.+)$/ 39 | , px = 'px' 40 | , setAttribute = 'setAttribute' 41 | , getAttribute = 'getAttribute' 42 | , byTag = 'getElementsByTagName' 43 | , features = function() { 44 | var e = doc.createElement('p') 45 | e.innerHTML = 'x
' 46 | return { 47 | hrefExtended: e[byTag]('a')[0][getAttribute]('href') != '#x' // IE < 8 48 | , autoTbody: e[byTag]('tbody').length !== 0 // IE < 8 49 | , computedStyle: doc.defaultView && doc.defaultView.getComputedStyle 50 | , cssFloat: e[byTag]('table')[0].style.styleFloat ? 'styleFloat' : 'cssFloat' 51 | , transform: function () { 52 | var props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'], i 53 | for (i = 0; i < props.length; i++) { 54 | if (props[i] in e.style) return props[i] 55 | } 56 | }() 57 | , classList: 'classList' in e 58 | , opasity: function () { 59 | return typeof doc.createElement('a').style.opacity !== 'undefined' 60 | }() 61 | } 62 | }() 63 | , trimReplace = /(^\s*|\s*$)/g 64 | , whitespaceRegex = /\s+/ 65 | , toString = String.prototype.toString 66 | , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, boxFlex: 1, WebkitBoxFlex: 1, MozBoxFlex: 1 } 67 | , trim = String.prototype.trim ? 68 | function (s) { 69 | return s.trim() 70 | } : 71 | function (s) { 72 | return s.replace(trimReplace, '') 73 | } 74 | 75 | 76 | /** 77 | * @param {string} c a class name to test 78 | * @return {boolean} 79 | */ 80 | function classReg(c) { 81 | return new RegExp("(^|\\s+)" + c + "(\\s+|$)") 82 | } 83 | 84 | 85 | /** 86 | * @param {Bonzo|Array} ar 87 | * @param {function(Object, number, (Bonzo|Array))} fn 88 | * @param {Object=} opt_scope 89 | * @param {boolean=} opt_rev 90 | * @return {Bonzo|Array} 91 | */ 92 | function each(ar, fn, opt_scope, opt_rev) { 93 | var ind, i = 0, l = ar.length 94 | for (; i < l; i++) { 95 | ind = opt_rev ? ar.length - i - 1 : i 96 | fn.call(opt_scope || ar[ind], ar[ind], ind, ar) 97 | } 98 | return ar 99 | } 100 | 101 | 102 | /** 103 | * @param {Bonzo|Array} ar 104 | * @param {function(Object, number, (Bonzo|Array))} fn 105 | * @param {Object=} opt_scope 106 | * @return {Bonzo|Array} 107 | */ 108 | function deepEach(ar, fn, opt_scope) { 109 | for (var i = 0, l = ar.length; i < l; i++) { 110 | if (isNode(ar[i])) { 111 | deepEach(ar[i].childNodes, fn, opt_scope) 112 | fn.call(opt_scope || ar[i], ar[i], i, ar) 113 | } 114 | } 115 | return ar 116 | } 117 | 118 | 119 | /** 120 | * @param {string} s 121 | * @return {string} 122 | */ 123 | function camelize(s) { 124 | return s.replace(/-(.)/g, function (m, m1) { 125 | return m1.toUpperCase() 126 | }) 127 | } 128 | 129 | 130 | /** 131 | * @param {string} s 132 | * @return {string} 133 | */ 134 | function decamelize(s) { 135 | return s ? s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : s 136 | } 137 | 138 | 139 | /** 140 | * @param {Element} el 141 | * @return {*} 142 | */ 143 | function data(el) { 144 | el[getAttribute]('data-node-uid') || el[setAttribute]('data-node-uid', ++uuids) 145 | var uid = el[getAttribute]('data-node-uid') 146 | return uidMap[uid] || (uidMap[uid] = {}) 147 | } 148 | 149 | 150 | /** 151 | * removes the data associated with an element 152 | * @param {Element} el 153 | */ 154 | function clearData(el) { 155 | var uid = el[getAttribute]('data-node-uid') 156 | if (uid) delete uidMap[uid] 157 | } 158 | 159 | 160 | function dataValue(d) { 161 | var f 162 | try { 163 | return (d === null || d === undefined) ? undefined : 164 | d === 'true' ? true : 165 | d === 'false' ? false : 166 | d === 'null' ? null : 167 | (f = parseFloat(d)) == d ? f : d; 168 | } catch(e) {} 169 | return undefined 170 | } 171 | 172 | function isNode(node) { 173 | return node && node.nodeName && (node.nodeType == 1 || node.nodeType == 11) 174 | } 175 | 176 | 177 | /** 178 | * @param {Bonzo|Array} ar 179 | * @param {function(Object, number, (Bonzo|Array))} fn 180 | * @param {Object=} opt_scope 181 | * @return {boolean} whether `some`thing was found 182 | */ 183 | function some(ar, fn, opt_scope) { 184 | for (var i = 0, j = ar.length; i < j; ++i) if (fn.call(opt_scope || null, ar[i], i, ar)) return true 185 | return false 186 | } 187 | 188 | 189 | /** 190 | * this could be a giant enum of CSS properties 191 | * but in favor of file size sans-closure deadcode optimizations 192 | * we're just asking for any ol string 193 | * then it gets transformed into the appropriate style property for JS access 194 | * @param {string} p 195 | * @return {string} 196 | */ 197 | function styleProperty(p) { 198 | (p == 'transform' && (p = features.transform)) || 199 | (/^transform-?[Oo]rigin$/.test(p) && (p = features.transform + "Origin")) || 200 | (p == 'float' && (p = features.cssFloat)) 201 | return p ? camelize(p) : null 202 | } 203 | 204 | var getStyle = features.computedStyle ? 205 | function (el, property) { 206 | var value = null 207 | , computed = doc.defaultView.getComputedStyle(el, '') 208 | computed && (value = computed[property]) 209 | return el.style[property] || value 210 | } : 211 | 212 | (ie && html.currentStyle) ? 213 | 214 | /** 215 | * @param {Element} el 216 | * @param {string} property 217 | * @return {string|number} 218 | */ 219 | function (el, property) { 220 | if (property == 'opacity' && !features.opasity) { 221 | var val = 100 222 | try { 223 | val = el['filters']['DXImageTransform.Microsoft.Alpha'].opacity 224 | } catch (e1) { 225 | try { 226 | val = el['filters']('alpha').opacity 227 | } catch (e2) {} 228 | } 229 | return val / 100 230 | } 231 | var value = el.currentStyle ? el.currentStyle[property] : null 232 | return el.style[property] || value 233 | } : 234 | 235 | function (el, property) { 236 | return el.style[property] 237 | } 238 | 239 | // this insert method is intense 240 | function insert(target, host, fn, rev) { 241 | var i = 0, self = host || this, r = [] 242 | // target nodes could be a css selector if it's a string and a selector engine is present 243 | // otherwise, just use target 244 | , nodes = query && typeof target == 'string' && target.charAt(0) != '<' ? query(target) : target 245 | // normalize each node in case it's still a string and we need to create nodes on the fly 246 | each(normalize(nodes), function (t, j) { 247 | each(self, function (el) { 248 | fn(t, r[i++] = j > 0 ? cloneNode(self, el) : el) 249 | }, null, rev) 250 | }, this, rev) 251 | self.length = i 252 | each(r, function (e) { 253 | self[--i] = e 254 | }, null, !rev) 255 | return self 256 | } 257 | 258 | 259 | /** 260 | * sets an element to an explicit x/y position on the page 261 | * @param {Element} el 262 | * @param {?number} x 263 | * @param {?number} y 264 | */ 265 | function xy(el, x, y) { 266 | var $el = bonzo(el) 267 | , style = $el.css('position') 268 | , offset = $el.offset() 269 | , rel = 'relative' 270 | , isRel = style == rel 271 | , delta = [parseInt($el.css('left'), 10), parseInt($el.css('top'), 10)] 272 | 273 | if (style == 'static') { 274 | $el.css('position', rel) 275 | style = rel 276 | } 277 | 278 | isNaN(delta[0]) && (delta[0] = isRel ? 0 : el.offsetLeft) 279 | isNaN(delta[1]) && (delta[1] = isRel ? 0 : el.offsetTop) 280 | 281 | x != null && (el.style.left = x - offset.left + delta[0] + px) 282 | y != null && (el.style.top = y - offset.top + delta[1] + px) 283 | 284 | } 285 | 286 | // classList support for class management 287 | // altho to be fair, the api sucks because it won't accept multiple classes at once 288 | if (features.classList) { 289 | hasClass = function (el, c) { 290 | return el.classList.contains(c) 291 | } 292 | addClass = function (el, c) { 293 | el.classList.add(c) 294 | } 295 | removeClass = function (el, c) { 296 | el.classList.remove(c) 297 | } 298 | } 299 | else { 300 | hasClass = function (el, c) { 301 | return classReg(c).test(el.className) 302 | } 303 | addClass = function (el, c) { 304 | el.className = trim(el.className + ' ' + c) 305 | } 306 | removeClass = function (el, c) { 307 | el.className = trim(el.className.replace(classReg(c), ' ')) 308 | } 309 | } 310 | 311 | 312 | /** 313 | * this allows method calling for setting values 314 | * 315 | * @example 316 | * bonzo(elements).css('color', function (el) { 317 | * return el.getAttribute('data-original-color') 318 | * }) 319 | * 320 | * @param {Element} el 321 | * @param {function (Element)|string} 322 | * @return {string} 323 | */ 324 | function setter(el, v) { 325 | return typeof v == 'function' ? v(el) : v 326 | } 327 | 328 | /** 329 | * @constructor 330 | * @param {Array.|Element|Node|string} elements 331 | */ 332 | function Bonzo(elements) { 333 | this.length = 0 334 | if (elements) { 335 | elements = typeof elements !== 'string' && 336 | !elements.nodeType && 337 | typeof elements.length !== 'undefined' ? 338 | elements : 339 | [elements] 340 | this.length = elements.length 341 | for (var i = 0; i < elements.length; i++) this[i] = elements[i] 342 | } 343 | } 344 | 345 | Bonzo.prototype = { 346 | 347 | /** 348 | * @param {number} index 349 | * @return {Element|Node} 350 | */ 351 | get: function (index) { 352 | return this[index] || null 353 | } 354 | 355 | // itetators 356 | /** 357 | * @param {function(Element|Node)} fn 358 | * @param {Object=} opt_scope 359 | * @return {Bonzo} 360 | */ 361 | , each: function (fn, opt_scope) { 362 | return each(this, fn, opt_scope) 363 | } 364 | 365 | /** 366 | * @param {Function} fn 367 | * @param {Object=} opt_scope 368 | * @return {Bonzo} 369 | */ 370 | , deepEach: function (fn, opt_scope) { 371 | return deepEach(this, fn, opt_scope) 372 | } 373 | 374 | 375 | /** 376 | * @param {Function} fn 377 | * @param {Function=} opt_reject 378 | * @return {Array} 379 | */ 380 | , map: function (fn, opt_reject) { 381 | var m = [], n, i 382 | for (i = 0; i < this.length; i++) { 383 | n = fn.call(this, this[i], i) 384 | opt_reject ? (opt_reject(n) && m.push(n)) : m.push(n) 385 | } 386 | return m 387 | } 388 | 389 | // text and html inserters! 390 | 391 | /** 392 | * @param {string} h the HTML to insert 393 | * @param {boolean=} opt_text whether to set or get text content 394 | * @return {Bonzo|string} 395 | */ 396 | , html: function (h, opt_text) { 397 | var method = opt_text 398 | ? html.textContent === undefined ? 'innerText' : 'textContent' 399 | : 'innerHTML' 400 | , that = this 401 | , append = function (el, i) { 402 | each(normalize(h, that, i), function (node) { 403 | el.appendChild(node) 404 | }) 405 | } 406 | , updateElement = function (el, i) { 407 | try { 408 | if (opt_text || (typeof h == 'string' && !specialTags.test(el.tagName))) { 409 | return el[method] = h 410 | } 411 | } catch (e) {} 412 | append(el, i) 413 | } 414 | return typeof h != 'undefined' 415 | ? this.empty().each(updateElement) 416 | : this[0] ? this[0][method] : '' 417 | } 418 | 419 | /** 420 | * @param {string=} opt_text the text to set, otherwise this is a getter 421 | * @return {Bonzo|string} 422 | */ 423 | , text: function (opt_text) { 424 | return this.html(opt_text, true) 425 | } 426 | 427 | // more related insertion methods 428 | 429 | /** 430 | * @param {Bonzo|string|Element|Array} node 431 | * @return {Bonzo} 432 | */ 433 | , append: function (node) { 434 | var that = this 435 | return this.each(function (el, i) { 436 | each(normalize(node, that, i), function (i) { 437 | el.appendChild(i) 438 | }) 439 | }) 440 | } 441 | 442 | 443 | /** 444 | * @param {Bonzo|string|Element|Array} node 445 | * @return {Bonzo} 446 | */ 447 | , prepend: function (node) { 448 | var that = this 449 | return this.each(function (el, i) { 450 | var first = el.firstChild 451 | each(normalize(node, that, i), function (i) { 452 | el.insertBefore(i, first) 453 | }) 454 | }) 455 | } 456 | 457 | 458 | /** 459 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 460 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 461 | * @return {Bonzo} 462 | */ 463 | , appendTo: function (target, opt_host) { 464 | return insert.call(this, target, opt_host, function (t, el) { 465 | t.appendChild(el) 466 | }) 467 | } 468 | 469 | 470 | /** 471 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 472 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 473 | * @return {Bonzo} 474 | */ 475 | , prependTo: function (target, opt_host) { 476 | return insert.call(this, target, opt_host, function (t, el) { 477 | t.insertBefore(el, t.firstChild) 478 | }, 1) 479 | } 480 | 481 | 482 | /** 483 | * @param {Bonzo|string|Element|Array} node 484 | * @return {Bonzo} 485 | */ 486 | , before: function (node) { 487 | var that = this 488 | return this.each(function (el, i) { 489 | each(normalize(node, that, i), function (i) { 490 | el[parentNode].insertBefore(i, el) 491 | }) 492 | }) 493 | } 494 | 495 | 496 | /** 497 | * @param {Bonzo|string|Element|Array} node 498 | * @return {Bonzo} 499 | */ 500 | , after: function (node) { 501 | var that = this 502 | return this.each(function (el, i) { 503 | each(normalize(node, that, i), function (i) { 504 | el[parentNode].insertBefore(i, el.nextSibling) 505 | }, null, 1) 506 | }) 507 | } 508 | 509 | 510 | /** 511 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 512 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 513 | * @return {Bonzo} 514 | */ 515 | , insertBefore: function (target, opt_host) { 516 | return insert.call(this, target, opt_host, function (t, el) { 517 | t[parentNode].insertBefore(el, t) 518 | }) 519 | } 520 | 521 | 522 | /** 523 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 524 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 525 | * @return {Bonzo} 526 | */ 527 | , insertAfter: function (target, opt_host) { 528 | return insert.call(this, target, opt_host, function (t, el) { 529 | var sibling = t.nextSibling 530 | sibling ? 531 | t[parentNode].insertBefore(el, sibling) : 532 | t[parentNode].appendChild(el) 533 | }, 1) 534 | } 535 | 536 | 537 | /** 538 | * @param {Bonzo|string|Element|Array} node 539 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 540 | * @return {Bonzo} 541 | */ 542 | , replaceWith: function (node, opt_host) { 543 | var ret = bonzo(normalize(node)).insertAfter(this, opt_host) 544 | this.remove() 545 | Bonzo.call(opt_host || this, ret) 546 | return opt_host || this 547 | } 548 | 549 | // class management 550 | 551 | /** 552 | * @param {string} c 553 | * @return {Bonzo} 554 | */ 555 | , addClass: function (c) { 556 | c = toString.call(c).split(whitespaceRegex) 557 | return this.each(function (el) { 558 | // we `each` here so you can do $el.addClass('foo bar') 559 | each(c, function (c) { 560 | if (c && !hasClass(el, setter(el, c))) 561 | addClass(el, setter(el, c)) 562 | }) 563 | }) 564 | } 565 | 566 | 567 | /** 568 | * @param {string} c 569 | * @return {Bonzo} 570 | */ 571 | , removeClass: function (c) { 572 | c = toString.call(c).split(whitespaceRegex) 573 | return this.each(function (el) { 574 | each(c, function (c) { 575 | if (c && hasClass(el, setter(el, c))) 576 | removeClass(el, setter(el, c)) 577 | }) 578 | }) 579 | } 580 | 581 | 582 | /** 583 | * @param {string} c 584 | * @return {boolean} 585 | */ 586 | , hasClass: function (c) { 587 | c = toString.call(c).split(whitespaceRegex) 588 | return some(this, function (el) { 589 | return some(c, function (c) { 590 | return c && hasClass(el, c) 591 | }) 592 | }) 593 | } 594 | 595 | 596 | /** 597 | * @param {string} c classname to toggle 598 | * @param {boolean=} opt_condition whether to add or remove the class straight away 599 | * @return {Bonzo} 600 | */ 601 | , toggleClass: function (c, opt_condition) { 602 | c = toString.call(c).split(whitespaceRegex) 603 | return this.each(function (el) { 604 | each(c, function (c) { 605 | if (c) { 606 | typeof opt_condition !== 'undefined' ? 607 | opt_condition ? addClass(el, c) : removeClass(el, c) : 608 | hasClass(el, c) ? removeClass(el, c) : addClass(el, c) 609 | } 610 | }) 611 | }) 612 | } 613 | 614 | // display togglers 615 | 616 | /** 617 | * @param {string=} opt_type useful to set back to anything other than an empty string 618 | * @return {Bonzo} 619 | */ 620 | , show: function (opt_type) { 621 | opt_type = typeof opt_type == 'string' ? opt_type : '' 622 | return this.each(function (el) { 623 | el.style.display = opt_type 624 | }) 625 | } 626 | 627 | 628 | /** 629 | * @return {Bonzo} 630 | */ 631 | , hide: function () { 632 | return this.each(function (el) { 633 | el.style.display = 'none' 634 | }) 635 | } 636 | 637 | 638 | /** 639 | * @param {Function=} opt_callback 640 | * @param {string=} opt_type 641 | * @return {Bonzo} 642 | */ 643 | , toggle: function (opt_callback, opt_type) { 644 | opt_type = typeof opt_type == 'string' ? opt_type : ''; 645 | typeof opt_callback != 'function' && (opt_callback = null) 646 | return this.each(function (el) { 647 | el.style.display = (el.offsetWidth || el.offsetHeight) ? 'none' : opt_type; 648 | opt_callback && opt_callback.call(el) 649 | }) 650 | } 651 | 652 | 653 | // DOM Walkers & getters 654 | 655 | /** 656 | * @return {Element|Node} 657 | */ 658 | , first: function () { 659 | return bonzo(this.length ? this[0] : []) 660 | } 661 | 662 | 663 | /** 664 | * @return {Element|Node} 665 | */ 666 | , last: function () { 667 | return bonzo(this.length ? this[this.length - 1] : []) 668 | } 669 | 670 | 671 | /** 672 | * @return {Element|Node} 673 | */ 674 | , next: function () { 675 | return this.related('nextSibling') 676 | } 677 | 678 | 679 | /** 680 | * @return {Element|Node} 681 | */ 682 | , previous: function () { 683 | return this.related('previousSibling') 684 | } 685 | 686 | 687 | /** 688 | * @return {Element|Node} 689 | */ 690 | , parent: function() { 691 | return this.related(parentNode) 692 | } 693 | 694 | 695 | /** 696 | * @private 697 | * @param {string} method the directional DOM method 698 | * @return {Element|Node} 699 | */ 700 | , related: function (method) { 701 | return this.map( 702 | function (el) { 703 | el = el[method] 704 | while (el && el.nodeType !== 1) { 705 | el = el[method] 706 | } 707 | return el || 0 708 | }, 709 | function (el) { 710 | return el 711 | } 712 | ) 713 | } 714 | 715 | 716 | /** 717 | * @return {Bonzo} 718 | */ 719 | , focus: function () { 720 | this.length && this[0].focus() 721 | return this 722 | } 723 | 724 | 725 | /** 726 | * @return {Bonzo} 727 | */ 728 | , blur: function () { 729 | this.length && this[0].blur() 730 | return this 731 | } 732 | 733 | // style getter setter & related methods 734 | 735 | /** 736 | * @param {Object|string} o 737 | * @param {string=} opt_v 738 | * @return {Bonzo|string} 739 | */ 740 | , css: function (o, opt_v) { 741 | var p, iter = o 742 | // is this a request for just getting a style? 743 | if (opt_v === undefined && typeof o == 'string') { 744 | // repurpose 'v' 745 | opt_v = this[0] 746 | if (!opt_v) return null 747 | if (opt_v === doc || opt_v === win) { 748 | p = (opt_v === doc) ? bonzo.doc() : bonzo.viewport() 749 | return o == 'width' ? p.width : o == 'height' ? p.height : '' 750 | } 751 | return (o = styleProperty(o)) ? getStyle(opt_v, o) : null 752 | } 753 | 754 | if (typeof o == 'string') { 755 | iter = {} 756 | iter[o] = opt_v 757 | } 758 | 759 | if (ie && iter.opacity) { 760 | // oh this 'ol gamut 761 | iter.filter = 'alpha(opacity=' + (iter.opacity * 100) + ')' 762 | // give it layout 763 | iter.zoom = o.zoom || 1; 764 | delete iter.opacity; 765 | } 766 | 767 | function fn(el, p, v) { 768 | for (var k in iter) { 769 | if (iter.hasOwnProperty(k)) { 770 | v = iter[k]; 771 | // change "5" to "5px" - unless you're line-height, which is allowed 772 | (p = styleProperty(k)) && digit.test(v) && !(p in unitless) && (v += px) 773 | try { el.style[p] = setter(el, v) } catch(e) {} 774 | } 775 | } 776 | } 777 | return this.each(fn) 778 | } 779 | 780 | 781 | /** 782 | * @param {number=} opt_x 783 | * @param {number=} opt_y 784 | * @return {Bonzo|number} 785 | */ 786 | , offset: function (opt_x, opt_y) { 787 | if (typeof opt_x == 'number' || typeof opt_y == 'number') { 788 | return this.each(function (el) { 789 | xy(el, opt_x, opt_y) 790 | }) 791 | } 792 | if (!this[0]) return { 793 | top: 0 794 | , left: 0 795 | , height: 0 796 | , width: 0 797 | } 798 | var el = this[0] 799 | , width = el.offsetWidth 800 | , height = el.offsetHeight 801 | , top = el.offsetTop 802 | , left = el.offsetLeft 803 | while (el = el.offsetParent) { 804 | top = top + el.offsetTop 805 | left = left + el.offsetLeft 806 | 807 | if (el != doc.body) { 808 | top -= el.scrollTop 809 | left -= el.scrollLeft 810 | } 811 | } 812 | 813 | return { 814 | top: top 815 | , left: left 816 | , height: height 817 | , width: width 818 | } 819 | } 820 | 821 | 822 | /** 823 | * @return {number} 824 | */ 825 | , dim: function () { 826 | if (!this.length) return { height: 0, width: 0 } 827 | var el = this[0] 828 | , orig = !el.offsetWidth && !el.offsetHeight ? 829 | // el isn't visible, can't be measured properly, so fix that 830 | function (t) { 831 | var s = { 832 | position: el.style.position || '' 833 | , visibility: el.style.visibility || '' 834 | , display: el.style.display || '' 835 | } 836 | t.first().css({ 837 | position: 'absolute' 838 | , visibility: 'hidden' 839 | , display: 'block' 840 | }) 841 | return s 842 | }(this) : null 843 | , width = el.offsetWidth 844 | , height = el.offsetHeight 845 | 846 | orig && this.first().css(orig) 847 | return { 848 | height: height 849 | , width: width 850 | } 851 | } 852 | 853 | // attributes are hard. go shopping 854 | 855 | /** 856 | * @param {string} k an attribute to get or set 857 | * @param {string=} opt_v the value to set 858 | * @return {Bonzo|string} 859 | */ 860 | , attr: function (k, opt_v) { 861 | var el = this[0] 862 | if (typeof k != 'string' && !(k instanceof String)) { 863 | for (var n in k) { 864 | k.hasOwnProperty(n) && this.attr(n, k[n]) 865 | } 866 | return this 867 | } 868 | return typeof opt_v == 'undefined' ? 869 | !el ? null : specialAttributes.test(k) ? 870 | stateAttributes.test(k) && typeof el[k] == 'string' ? 871 | true : el[k] : (k == 'href' || k =='src') && features.hrefExtended ? 872 | el[getAttribute](k, 2) : el[getAttribute](k) : 873 | this.each(function (el) { 874 | specialAttributes.test(k) ? (el[k] = setter(el, opt_v)) : el[setAttribute](k, setter(el, opt_v)) 875 | }) 876 | } 877 | 878 | 879 | /** 880 | * @param {string} k 881 | * @return {Bonzo} 882 | */ 883 | , removeAttr: function (k) { 884 | return this.each(function (el) { 885 | stateAttributes.test(k) ? (el[k] = false) : el.removeAttribute(k) 886 | }) 887 | } 888 | 889 | 890 | /** 891 | * @param {string=} opt_s 892 | * @return {Bonzo|string} 893 | */ 894 | , val: function (s) { 895 | return (typeof s == 'string') ? 896 | this.attr('value', s) : 897 | this.length ? this[0].value : null 898 | } 899 | 900 | // use with care and knowledge. this data() method uses data attributes on the DOM nodes 901 | // to do this differently costs a lot more code. c'est la vie 902 | /** 903 | * @param {string|Object=} opt_k the key for which to get or set data 904 | * @param {Object=} opt_v 905 | * @return {Bonzo|Object} 906 | */ 907 | , data: function (opt_k, opt_v) { 908 | var el = this[0], o, m 909 | if (typeof opt_v === 'undefined') { 910 | if (!el) return null 911 | o = data(el) 912 | if (typeof opt_k === 'undefined') { 913 | each(el.attributes, function (a) { 914 | (m = ('' + a.name).match(dattr)) && (o[camelize(m[1])] = dataValue(a.value)) 915 | }) 916 | return o 917 | } else { 918 | if (typeof o[opt_k] === 'undefined') 919 | o[opt_k] = dataValue(this.attr('data-' + decamelize(opt_k))) 920 | return o[opt_k] 921 | } 922 | } else { 923 | return this.each(function (el) { data(el)[opt_k] = opt_v }) 924 | } 925 | } 926 | 927 | // DOM detachment & related 928 | 929 | /** 930 | * @return {Bonzo} 931 | */ 932 | , remove: function () { 933 | this.deepEach(clearData) 934 | 935 | return this.each(function (el) { 936 | el[parentNode] && el[parentNode].removeChild(el) 937 | }) 938 | } 939 | 940 | 941 | /** 942 | * @return {Bonzo} 943 | */ 944 | , empty: function () { 945 | return this.each(function (el) { 946 | deepEach(el.childNodes, clearData) 947 | 948 | while (el.firstChild) { 949 | el.removeChild(el.firstChild) 950 | } 951 | }) 952 | } 953 | 954 | 955 | /** 956 | * @return {Bonzo} 957 | */ 958 | , detach: function () { 959 | return this.each(function (el) { 960 | el[parentNode].removeChild(el) 961 | }) 962 | } 963 | 964 | // who uses a mouse anyway? oh right. 965 | 966 | /** 967 | * @param {number} y 968 | */ 969 | , scrollTop: function (y) { 970 | return scroll.call(this, null, y, 'y') 971 | } 972 | 973 | 974 | /** 975 | * @param {number} x 976 | */ 977 | , scrollLeft: function (x) { 978 | return scroll.call(this, x, null, 'x') 979 | } 980 | 981 | } 982 | 983 | function normalize(node, host, clone) { 984 | var i, l, ret 985 | if (typeof node == 'string') return bonzo.create(node) 986 | if (isNode(node)) node = [ node ] 987 | if (clone) { 988 | ret = [] // don't change original array 989 | for (i = 0, l = node.length; i < l; i++) ret[i] = cloneNode(host, node[i]) 990 | return ret 991 | } 992 | return node 993 | } 994 | 995 | function cloneNode(host, el) { 996 | var c = el.cloneNode(true) 997 | , cloneElems 998 | , elElems 999 | 1000 | // check for existence of an event cloner 1001 | // preferably https://github.com/fat/bean 1002 | // otherwise Bonzo won't do this for you 1003 | if (host.$ && typeof host.cloneEvents == 'function') { 1004 | host.$(c).cloneEvents(el) 1005 | 1006 | // clone events from every child node 1007 | cloneElems = host.$(c).find('*') 1008 | elElems = host.$(el).find('*') 1009 | 1010 | for (var i = 0; i < elElems.length; i++) 1011 | host.$(cloneElems[i]).cloneEvents(elElems[i]) 1012 | } 1013 | return c 1014 | } 1015 | 1016 | function scroll(x, y, type) { 1017 | var el = this[0] 1018 | if (!el) return this 1019 | if (x == null && y == null) { 1020 | return (isBody(el) ? getWindowScroll() : { x: el.scrollLeft, y: el.scrollTop })[type] 1021 | } 1022 | if (isBody(el)) { 1023 | win.scrollTo(x, y) 1024 | } else { 1025 | x != null && (el.scrollLeft = x) 1026 | y != null && (el.scrollTop = y) 1027 | } 1028 | return this 1029 | } 1030 | 1031 | function isBody(element) { 1032 | return element === win || (/^(?:body|html)$/i).test(element.tagName) 1033 | } 1034 | 1035 | function getWindowScroll() { 1036 | return { x: win.pageXOffset || html.scrollLeft, y: win.pageYOffset || html.scrollTop } 1037 | } 1038 | 1039 | /** 1040 | * @param {Array.|Element|Node|string} els 1041 | * @return {Bonzo} 1042 | */ 1043 | function bonzo(els) { 1044 | return new Bonzo(els) 1045 | } 1046 | 1047 | bonzo.setQueryEngine = function (q) { 1048 | query = q; 1049 | delete bonzo.setQueryEngine 1050 | } 1051 | 1052 | bonzo.aug = function (o, target) { 1053 | // for those standalone bonzo users. this love is for you. 1054 | for (var k in o) { 1055 | o.hasOwnProperty(k) && ((target || Bonzo.prototype)[k] = o[k]) 1056 | } 1057 | } 1058 | 1059 | bonzo.create = function (node) { 1060 | // hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh 1061 | return typeof node == 'string' && node !== '' ? 1062 | function () { 1063 | var tag = /^\s*<([^\s>]+)/.exec(node) 1064 | , el = doc.createElement('div') 1065 | , els = [] 1066 | , p = tag ? tagMap[tag[1].toLowerCase()] : null 1067 | , dep = p ? p[2] + 1 : 1 1068 | , ns = p && p[3] 1069 | , pn = parentNode 1070 | , tb = features.autoTbody && p && p[0] == '' && !(/,
, etc. 1079 | if ((!tag || el.nodeType == 1) && (!tb || el.tagName.toLowerCase() != 'tbody')) { 1080 | els.push(el) 1081 | } 1082 | } while (el = el.nextSibling) 1083 | // IE < 9 gives us a parentNode which messes up insert() check for cloning 1084 | // `dep` > 1 can also cause problems with the insert() check (must do this last) 1085 | each(els, function(el) { el[pn] && el[pn].removeChild(el) }) 1086 | return els 1087 | }() : isNode(node) ? [node.cloneNode(true)] : [] 1088 | } 1089 | 1090 | bonzo.doc = function () { 1091 | var vp = bonzo.viewport() 1092 | return { 1093 | width: Math.max(doc.body.scrollWidth, html.scrollWidth, vp.width) 1094 | , height: Math.max(doc.body.scrollHeight, html.scrollHeight, vp.height) 1095 | } 1096 | } 1097 | 1098 | bonzo.firstChild = function (el) { 1099 | for (var c = el.childNodes, i = 0, j = (c && c.length) || 0, e; i < j; i++) { 1100 | if (c[i].nodeType === 1) e = c[j = i] 1101 | } 1102 | return e 1103 | } 1104 | 1105 | bonzo.viewport = function () { 1106 | return { 1107 | width: ie ? html.clientWidth : self.innerWidth 1108 | , height: ie ? html.clientHeight : self.innerHeight 1109 | } 1110 | } 1111 | 1112 | bonzo.isAncestor = 'compareDocumentPosition' in html ? 1113 | function (container, element) { 1114 | return (container.compareDocumentPosition(element) & 16) == 16 1115 | } : 'contains' in html ? 1116 | function (container, element) { 1117 | return container !== element && container.contains(element); 1118 | } : 1119 | function (container, element) { 1120 | while (element = element[parentNode]) { 1121 | if (element === container) { 1122 | return true 1123 | } 1124 | } 1125 | return false 1126 | } 1127 | 1128 | return bonzo 1129 | }, this); // the only line we care about using a semi-colon. placed here for concatenation tools 1130 | -------------------------------------------------------------------------------- /example3/bonzo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bonzo: DOM Utility (c) Dustin Diaz 2012 3 | * https://github.com/ded/bonzo 4 | * License MIT 5 | */ 6 | (function (name, definition, context) { 7 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 8 | else if (typeof context['define'] == 'function' && context['define']['amd']) define(name, definition) 9 | else context[name] = definition() 10 | })('bonzo', function() { 11 | var win = window 12 | , doc = win.document 13 | , html = doc.documentElement 14 | , parentNode = 'parentNode' 15 | , query = null // used for setting a selector engine host 16 | , specialAttributes = /^(checked|value|selected|disabled)$/i 17 | , specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i // tags that we have trouble inserting *into* 18 | , table = ['', '
', 1] 19 | , td = ['', '
', 3] 20 | , option = ['', 1] 21 | , noscope = ['_', '', 0, 1] 22 | , tagMap = { // tags that we have trouble *inserting* 23 | thead: table, tbody: table, tfoot: table, colgroup: table, caption: table 24 | , tr: ['', '
', 2] 25 | , th: td , td: td 26 | , col: ['', '
', 2] 27 | , fieldset: ['
', '
', 1] 28 | , legend: ['
', '
', 2] 29 | , option: option, optgroup: option 30 | , script: noscope, style: noscope, link: noscope, param: noscope, base: noscope 31 | } 32 | , stateAttributes = /^(checked|selected|disabled)$/ 33 | , ie = /msie/i.test(navigator.userAgent) 34 | , hasClass, addClass, removeClass 35 | , uidMap = {} 36 | , uuids = 0 37 | , digit = /^-?[\d\.]+$/ 38 | , dattr = /^data-(.+)$/ 39 | , px = 'px' 40 | , setAttribute = 'setAttribute' 41 | , getAttribute = 'getAttribute' 42 | , byTag = 'getElementsByTagName' 43 | , features = function() { 44 | var e = doc.createElement('p') 45 | e.innerHTML = 'x
' 46 | return { 47 | hrefExtended: e[byTag]('a')[0][getAttribute]('href') != '#x' // IE < 8 48 | , autoTbody: e[byTag]('tbody').length !== 0 // IE < 8 49 | , computedStyle: doc.defaultView && doc.defaultView.getComputedStyle 50 | , cssFloat: e[byTag]('table')[0].style.styleFloat ? 'styleFloat' : 'cssFloat' 51 | , transform: function () { 52 | var props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'], i 53 | for (i = 0; i < props.length; i++) { 54 | if (props[i] in e.style) return props[i] 55 | } 56 | }() 57 | , classList: 'classList' in e 58 | , opasity: function () { 59 | return typeof doc.createElement('a').style.opacity !== 'undefined' 60 | }() 61 | } 62 | }() 63 | , trimReplace = /(^\s*|\s*$)/g 64 | , whitespaceRegex = /\s+/ 65 | , toString = String.prototype.toString 66 | , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, boxFlex: 1, WebkitBoxFlex: 1, MozBoxFlex: 1 } 67 | , trim = String.prototype.trim ? 68 | function (s) { 69 | return s.trim() 70 | } : 71 | function (s) { 72 | return s.replace(trimReplace, '') 73 | } 74 | 75 | 76 | /** 77 | * @param {string} c a class name to test 78 | * @return {boolean} 79 | */ 80 | function classReg(c) { 81 | return new RegExp("(^|\\s+)" + c + "(\\s+|$)") 82 | } 83 | 84 | 85 | /** 86 | * @param {Bonzo|Array} ar 87 | * @param {function(Object, number, (Bonzo|Array))} fn 88 | * @param {Object=} opt_scope 89 | * @param {boolean=} opt_rev 90 | * @return {Bonzo|Array} 91 | */ 92 | function each(ar, fn, opt_scope, opt_rev) { 93 | var ind, i = 0, l = ar.length 94 | for (; i < l; i++) { 95 | ind = opt_rev ? ar.length - i - 1 : i 96 | fn.call(opt_scope || ar[ind], ar[ind], ind, ar) 97 | } 98 | return ar 99 | } 100 | 101 | 102 | /** 103 | * @param {Bonzo|Array} ar 104 | * @param {function(Object, number, (Bonzo|Array))} fn 105 | * @param {Object=} opt_scope 106 | * @return {Bonzo|Array} 107 | */ 108 | function deepEach(ar, fn, opt_scope) { 109 | for (var i = 0, l = ar.length; i < l; i++) { 110 | if (isNode(ar[i])) { 111 | deepEach(ar[i].childNodes, fn, opt_scope) 112 | fn.call(opt_scope || ar[i], ar[i], i, ar) 113 | } 114 | } 115 | return ar 116 | } 117 | 118 | 119 | /** 120 | * @param {string} s 121 | * @return {string} 122 | */ 123 | function camelize(s) { 124 | return s.replace(/-(.)/g, function (m, m1) { 125 | return m1.toUpperCase() 126 | }) 127 | } 128 | 129 | 130 | /** 131 | * @param {string} s 132 | * @return {string} 133 | */ 134 | function decamelize(s) { 135 | return s ? s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : s 136 | } 137 | 138 | 139 | /** 140 | * @param {Element} el 141 | * @return {*} 142 | */ 143 | function data(el) { 144 | el[getAttribute]('data-node-uid') || el[setAttribute]('data-node-uid', ++uuids) 145 | var uid = el[getAttribute]('data-node-uid') 146 | return uidMap[uid] || (uidMap[uid] = {}) 147 | } 148 | 149 | 150 | /** 151 | * removes the data associated with an element 152 | * @param {Element} el 153 | */ 154 | function clearData(el) { 155 | var uid = el[getAttribute]('data-node-uid') 156 | if (uid) delete uidMap[uid] 157 | } 158 | 159 | 160 | function dataValue(d) { 161 | var f 162 | try { 163 | return (d === null || d === undefined) ? undefined : 164 | d === 'true' ? true : 165 | d === 'false' ? false : 166 | d === 'null' ? null : 167 | (f = parseFloat(d)) == d ? f : d; 168 | } catch(e) {} 169 | return undefined 170 | } 171 | 172 | function isNode(node) { 173 | return node && node.nodeName && (node.nodeType == 1 || node.nodeType == 11) 174 | } 175 | 176 | 177 | /** 178 | * @param {Bonzo|Array} ar 179 | * @param {function(Object, number, (Bonzo|Array))} fn 180 | * @param {Object=} opt_scope 181 | * @return {boolean} whether `some`thing was found 182 | */ 183 | function some(ar, fn, opt_scope) { 184 | for (var i = 0, j = ar.length; i < j; ++i) if (fn.call(opt_scope || null, ar[i], i, ar)) return true 185 | return false 186 | } 187 | 188 | 189 | /** 190 | * this could be a giant enum of CSS properties 191 | * but in favor of file size sans-closure deadcode optimizations 192 | * we're just asking for any ol string 193 | * then it gets transformed into the appropriate style property for JS access 194 | * @param {string} p 195 | * @return {string} 196 | */ 197 | function styleProperty(p) { 198 | (p == 'transform' && (p = features.transform)) || 199 | (/^transform-?[Oo]rigin$/.test(p) && (p = features.transform + "Origin")) || 200 | (p == 'float' && (p = features.cssFloat)) 201 | return p ? camelize(p) : null 202 | } 203 | 204 | var getStyle = features.computedStyle ? 205 | function (el, property) { 206 | var value = null 207 | , computed = doc.defaultView.getComputedStyle(el, '') 208 | computed && (value = computed[property]) 209 | return el.style[property] || value 210 | } : 211 | 212 | (ie && html.currentStyle) ? 213 | 214 | /** 215 | * @param {Element} el 216 | * @param {string} property 217 | * @return {string|number} 218 | */ 219 | function (el, property) { 220 | if (property == 'opacity' && !features.opasity) { 221 | var val = 100 222 | try { 223 | val = el['filters']['DXImageTransform.Microsoft.Alpha'].opacity 224 | } catch (e1) { 225 | try { 226 | val = el['filters']('alpha').opacity 227 | } catch (e2) {} 228 | } 229 | return val / 100 230 | } 231 | var value = el.currentStyle ? el.currentStyle[property] : null 232 | return el.style[property] || value 233 | } : 234 | 235 | function (el, property) { 236 | return el.style[property] 237 | } 238 | 239 | // this insert method is intense 240 | function insert(target, host, fn, rev) { 241 | var i = 0, self = host || this, r = [] 242 | // target nodes could be a css selector if it's a string and a selector engine is present 243 | // otherwise, just use target 244 | , nodes = query && typeof target == 'string' && target.charAt(0) != '<' ? query(target) : target 245 | // normalize each node in case it's still a string and we need to create nodes on the fly 246 | each(normalize(nodes), function (t, j) { 247 | each(self, function (el) { 248 | fn(t, r[i++] = j > 0 ? cloneNode(self, el) : el) 249 | }, null, rev) 250 | }, this, rev) 251 | self.length = i 252 | each(r, function (e) { 253 | self[--i] = e 254 | }, null, !rev) 255 | return self 256 | } 257 | 258 | 259 | /** 260 | * sets an element to an explicit x/y position on the page 261 | * @param {Element} el 262 | * @param {?number} x 263 | * @param {?number} y 264 | */ 265 | function xy(el, x, y) { 266 | var $el = bonzo(el) 267 | , style = $el.css('position') 268 | , offset = $el.offset() 269 | , rel = 'relative' 270 | , isRel = style == rel 271 | , delta = [parseInt($el.css('left'), 10), parseInt($el.css('top'), 10)] 272 | 273 | if (style == 'static') { 274 | $el.css('position', rel) 275 | style = rel 276 | } 277 | 278 | isNaN(delta[0]) && (delta[0] = isRel ? 0 : el.offsetLeft) 279 | isNaN(delta[1]) && (delta[1] = isRel ? 0 : el.offsetTop) 280 | 281 | x != null && (el.style.left = x - offset.left + delta[0] + px) 282 | y != null && (el.style.top = y - offset.top + delta[1] + px) 283 | 284 | } 285 | 286 | // classList support for class management 287 | // altho to be fair, the api sucks because it won't accept multiple classes at once 288 | if (features.classList) { 289 | hasClass = function (el, c) { 290 | return el.classList.contains(c) 291 | } 292 | addClass = function (el, c) { 293 | el.classList.add(c) 294 | } 295 | removeClass = function (el, c) { 296 | el.classList.remove(c) 297 | } 298 | } 299 | else { 300 | hasClass = function (el, c) { 301 | return classReg(c).test(el.className) 302 | } 303 | addClass = function (el, c) { 304 | el.className = trim(el.className + ' ' + c) 305 | } 306 | removeClass = function (el, c) { 307 | el.className = trim(el.className.replace(classReg(c), ' ')) 308 | } 309 | } 310 | 311 | 312 | /** 313 | * this allows method calling for setting values 314 | * 315 | * @example 316 | * bonzo(elements).css('color', function (el) { 317 | * return el.getAttribute('data-original-color') 318 | * }) 319 | * 320 | * @param {Element} el 321 | * @param {function (Element)|string} 322 | * @return {string} 323 | */ 324 | function setter(el, v) { 325 | return typeof v == 'function' ? v(el) : v 326 | } 327 | 328 | /** 329 | * @constructor 330 | * @param {Array.|Element|Node|string} elements 331 | */ 332 | function Bonzo(elements) { 333 | this.length = 0 334 | if (elements) { 335 | elements = typeof elements !== 'string' && 336 | !elements.nodeType && 337 | typeof elements.length !== 'undefined' ? 338 | elements : 339 | [elements] 340 | this.length = elements.length 341 | for (var i = 0; i < elements.length; i++) this[i] = elements[i] 342 | } 343 | } 344 | 345 | Bonzo.prototype = { 346 | 347 | /** 348 | * @param {number} index 349 | * @return {Element|Node} 350 | */ 351 | get: function (index) { 352 | return this[index] || null 353 | } 354 | 355 | // itetators 356 | /** 357 | * @param {function(Element|Node)} fn 358 | * @param {Object=} opt_scope 359 | * @return {Bonzo} 360 | */ 361 | , each: function (fn, opt_scope) { 362 | return each(this, fn, opt_scope) 363 | } 364 | 365 | /** 366 | * @param {Function} fn 367 | * @param {Object=} opt_scope 368 | * @return {Bonzo} 369 | */ 370 | , deepEach: function (fn, opt_scope) { 371 | return deepEach(this, fn, opt_scope) 372 | } 373 | 374 | 375 | /** 376 | * @param {Function} fn 377 | * @param {Function=} opt_reject 378 | * @return {Array} 379 | */ 380 | , map: function (fn, opt_reject) { 381 | var m = [], n, i 382 | for (i = 0; i < this.length; i++) { 383 | n = fn.call(this, this[i], i) 384 | opt_reject ? (opt_reject(n) && m.push(n)) : m.push(n) 385 | } 386 | return m 387 | } 388 | 389 | // text and html inserters! 390 | 391 | /** 392 | * @param {string} h the HTML to insert 393 | * @param {boolean=} opt_text whether to set or get text content 394 | * @return {Bonzo|string} 395 | */ 396 | , html: function (h, opt_text) { 397 | var method = opt_text 398 | ? html.textContent === undefined ? 'innerText' : 'textContent' 399 | : 'innerHTML' 400 | , that = this 401 | , append = function (el, i) { 402 | each(normalize(h, that, i), function (node) { 403 | el.appendChild(node) 404 | }) 405 | } 406 | , updateElement = function (el, i) { 407 | try { 408 | if (opt_text || (typeof h == 'string' && !specialTags.test(el.tagName))) { 409 | return el[method] = h 410 | } 411 | } catch (e) {} 412 | append(el, i) 413 | } 414 | return typeof h != 'undefined' 415 | ? this.empty().each(updateElement) 416 | : this[0] ? this[0][method] : '' 417 | } 418 | 419 | /** 420 | * @param {string=} opt_text the text to set, otherwise this is a getter 421 | * @return {Bonzo|string} 422 | */ 423 | , text: function (opt_text) { 424 | return this.html(opt_text, true) 425 | } 426 | 427 | // more related insertion methods 428 | 429 | /** 430 | * @param {Bonzo|string|Element|Array} node 431 | * @return {Bonzo} 432 | */ 433 | , append: function (node) { 434 | var that = this 435 | return this.each(function (el, i) { 436 | each(normalize(node, that, i), function (i) { 437 | el.appendChild(i) 438 | }) 439 | }) 440 | } 441 | 442 | 443 | /** 444 | * @param {Bonzo|string|Element|Array} node 445 | * @return {Bonzo} 446 | */ 447 | , prepend: function (node) { 448 | var that = this 449 | return this.each(function (el, i) { 450 | var first = el.firstChild 451 | each(normalize(node, that, i), function (i) { 452 | el.insertBefore(i, first) 453 | }) 454 | }) 455 | } 456 | 457 | 458 | /** 459 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 460 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 461 | * @return {Bonzo} 462 | */ 463 | , appendTo: function (target, opt_host) { 464 | return insert.call(this, target, opt_host, function (t, el) { 465 | t.appendChild(el) 466 | }) 467 | } 468 | 469 | 470 | /** 471 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 472 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 473 | * @return {Bonzo} 474 | */ 475 | , prependTo: function (target, opt_host) { 476 | return insert.call(this, target, opt_host, function (t, el) { 477 | t.insertBefore(el, t.firstChild) 478 | }, 1) 479 | } 480 | 481 | 482 | /** 483 | * @param {Bonzo|string|Element|Array} node 484 | * @return {Bonzo} 485 | */ 486 | , before: function (node) { 487 | var that = this 488 | return this.each(function (el, i) { 489 | each(normalize(node, that, i), function (i) { 490 | el[parentNode].insertBefore(i, el) 491 | }) 492 | }) 493 | } 494 | 495 | 496 | /** 497 | * @param {Bonzo|string|Element|Array} node 498 | * @return {Bonzo} 499 | */ 500 | , after: function (node) { 501 | var that = this 502 | return this.each(function (el, i) { 503 | each(normalize(node, that, i), function (i) { 504 | el[parentNode].insertBefore(i, el.nextSibling) 505 | }, null, 1) 506 | }) 507 | } 508 | 509 | 510 | /** 511 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 512 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 513 | * @return {Bonzo} 514 | */ 515 | , insertBefore: function (target, opt_host) { 516 | return insert.call(this, target, opt_host, function (t, el) { 517 | t[parentNode].insertBefore(el, t) 518 | }) 519 | } 520 | 521 | 522 | /** 523 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 524 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 525 | * @return {Bonzo} 526 | */ 527 | , insertAfter: function (target, opt_host) { 528 | return insert.call(this, target, opt_host, function (t, el) { 529 | var sibling = t.nextSibling 530 | sibling ? 531 | t[parentNode].insertBefore(el, sibling) : 532 | t[parentNode].appendChild(el) 533 | }, 1) 534 | } 535 | 536 | 537 | /** 538 | * @param {Bonzo|string|Element|Array} node 539 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 540 | * @return {Bonzo} 541 | */ 542 | , replaceWith: function (node, opt_host) { 543 | var ret = bonzo(normalize(node)).insertAfter(this, opt_host) 544 | this.remove() 545 | Bonzo.call(opt_host || this, ret) 546 | return opt_host || this 547 | } 548 | 549 | // class management 550 | 551 | /** 552 | * @param {string} c 553 | * @return {Bonzo} 554 | */ 555 | , addClass: function (c) { 556 | c = toString.call(c).split(whitespaceRegex) 557 | return this.each(function (el) { 558 | // we `each` here so you can do $el.addClass('foo bar') 559 | each(c, function (c) { 560 | if (c && !hasClass(el, setter(el, c))) 561 | addClass(el, setter(el, c)) 562 | }) 563 | }) 564 | } 565 | 566 | 567 | /** 568 | * @param {string} c 569 | * @return {Bonzo} 570 | */ 571 | , removeClass: function (c) { 572 | c = toString.call(c).split(whitespaceRegex) 573 | return this.each(function (el) { 574 | each(c, function (c) { 575 | if (c && hasClass(el, setter(el, c))) 576 | removeClass(el, setter(el, c)) 577 | }) 578 | }) 579 | } 580 | 581 | 582 | /** 583 | * @param {string} c 584 | * @return {boolean} 585 | */ 586 | , hasClass: function (c) { 587 | c = toString.call(c).split(whitespaceRegex) 588 | return some(this, function (el) { 589 | return some(c, function (c) { 590 | return c && hasClass(el, c) 591 | }) 592 | }) 593 | } 594 | 595 | 596 | /** 597 | * @param {string} c classname to toggle 598 | * @param {boolean=} opt_condition whether to add or remove the class straight away 599 | * @return {Bonzo} 600 | */ 601 | , toggleClass: function (c, opt_condition) { 602 | c = toString.call(c).split(whitespaceRegex) 603 | return this.each(function (el) { 604 | each(c, function (c) { 605 | if (c) { 606 | typeof opt_condition !== 'undefined' ? 607 | opt_condition ? addClass(el, c) : removeClass(el, c) : 608 | hasClass(el, c) ? removeClass(el, c) : addClass(el, c) 609 | } 610 | }) 611 | }) 612 | } 613 | 614 | // display togglers 615 | 616 | /** 617 | * @param {string=} opt_type useful to set back to anything other than an empty string 618 | * @return {Bonzo} 619 | */ 620 | , show: function (opt_type) { 621 | opt_type = typeof opt_type == 'string' ? opt_type : '' 622 | return this.each(function (el) { 623 | el.style.display = opt_type 624 | }) 625 | } 626 | 627 | 628 | /** 629 | * @return {Bonzo} 630 | */ 631 | , hide: function () { 632 | return this.each(function (el) { 633 | el.style.display = 'none' 634 | }) 635 | } 636 | 637 | 638 | /** 639 | * @param {Function=} opt_callback 640 | * @param {string=} opt_type 641 | * @return {Bonzo} 642 | */ 643 | , toggle: function (opt_callback, opt_type) { 644 | opt_type = typeof opt_type == 'string' ? opt_type : ''; 645 | typeof opt_callback != 'function' && (opt_callback = null) 646 | return this.each(function (el) { 647 | el.style.display = (el.offsetWidth || el.offsetHeight) ? 'none' : opt_type; 648 | opt_callback && opt_callback.call(el) 649 | }) 650 | } 651 | 652 | 653 | // DOM Walkers & getters 654 | 655 | /** 656 | * @return {Element|Node} 657 | */ 658 | , first: function () { 659 | return bonzo(this.length ? this[0] : []) 660 | } 661 | 662 | 663 | /** 664 | * @return {Element|Node} 665 | */ 666 | , last: function () { 667 | return bonzo(this.length ? this[this.length - 1] : []) 668 | } 669 | 670 | 671 | /** 672 | * @return {Element|Node} 673 | */ 674 | , next: function () { 675 | return this.related('nextSibling') 676 | } 677 | 678 | 679 | /** 680 | * @return {Element|Node} 681 | */ 682 | , previous: function () { 683 | return this.related('previousSibling') 684 | } 685 | 686 | 687 | /** 688 | * @return {Element|Node} 689 | */ 690 | , parent: function() { 691 | return this.related(parentNode) 692 | } 693 | 694 | 695 | /** 696 | * @private 697 | * @param {string} method the directional DOM method 698 | * @return {Element|Node} 699 | */ 700 | , related: function (method) { 701 | return this.map( 702 | function (el) { 703 | el = el[method] 704 | while (el && el.nodeType !== 1) { 705 | el = el[method] 706 | } 707 | return el || 0 708 | }, 709 | function (el) { 710 | return el 711 | } 712 | ) 713 | } 714 | 715 | 716 | /** 717 | * @return {Bonzo} 718 | */ 719 | , focus: function () { 720 | this.length && this[0].focus() 721 | return this 722 | } 723 | 724 | 725 | /** 726 | * @return {Bonzo} 727 | */ 728 | , blur: function () { 729 | this.length && this[0].blur() 730 | return this 731 | } 732 | 733 | // style getter setter & related methods 734 | 735 | /** 736 | * @param {Object|string} o 737 | * @param {string=} opt_v 738 | * @return {Bonzo|string} 739 | */ 740 | , css: function (o, opt_v) { 741 | var p, iter = o 742 | // is this a request for just getting a style? 743 | if (opt_v === undefined && typeof o == 'string') { 744 | // repurpose 'v' 745 | opt_v = this[0] 746 | if (!opt_v) return null 747 | if (opt_v === doc || opt_v === win) { 748 | p = (opt_v === doc) ? bonzo.doc() : bonzo.viewport() 749 | return o == 'width' ? p.width : o == 'height' ? p.height : '' 750 | } 751 | return (o = styleProperty(o)) ? getStyle(opt_v, o) : null 752 | } 753 | 754 | if (typeof o == 'string') { 755 | iter = {} 756 | iter[o] = opt_v 757 | } 758 | 759 | if (ie && iter.opacity) { 760 | // oh this 'ol gamut 761 | iter.filter = 'alpha(opacity=' + (iter.opacity * 100) + ')' 762 | // give it layout 763 | iter.zoom = o.zoom || 1; 764 | delete iter.opacity; 765 | } 766 | 767 | function fn(el, p, v) { 768 | for (var k in iter) { 769 | if (iter.hasOwnProperty(k)) { 770 | v = iter[k]; 771 | // change "5" to "5px" - unless you're line-height, which is allowed 772 | (p = styleProperty(k)) && digit.test(v) && !(p in unitless) && (v += px) 773 | try { el.style[p] = setter(el, v) } catch(e) {} 774 | } 775 | } 776 | } 777 | return this.each(fn) 778 | } 779 | 780 | 781 | /** 782 | * @param {number=} opt_x 783 | * @param {number=} opt_y 784 | * @return {Bonzo|number} 785 | */ 786 | , offset: function (opt_x, opt_y) { 787 | if (typeof opt_x == 'number' || typeof opt_y == 'number') { 788 | return this.each(function (el) { 789 | xy(el, opt_x, opt_y) 790 | }) 791 | } 792 | if (!this[0]) return { 793 | top: 0 794 | , left: 0 795 | , height: 0 796 | , width: 0 797 | } 798 | var el = this[0] 799 | , width = el.offsetWidth 800 | , height = el.offsetHeight 801 | , top = el.offsetTop 802 | , left = el.offsetLeft 803 | while (el = el.offsetParent) { 804 | top = top + el.offsetTop 805 | left = left + el.offsetLeft 806 | 807 | if (el != doc.body) { 808 | top -= el.scrollTop 809 | left -= el.scrollLeft 810 | } 811 | } 812 | 813 | return { 814 | top: top 815 | , left: left 816 | , height: height 817 | , width: width 818 | } 819 | } 820 | 821 | 822 | /** 823 | * @return {number} 824 | */ 825 | , dim: function () { 826 | if (!this.length) return { height: 0, width: 0 } 827 | var el = this[0] 828 | , orig = !el.offsetWidth && !el.offsetHeight ? 829 | // el isn't visible, can't be measured properly, so fix that 830 | function (t) { 831 | var s = { 832 | position: el.style.position || '' 833 | , visibility: el.style.visibility || '' 834 | , display: el.style.display || '' 835 | } 836 | t.first().css({ 837 | position: 'absolute' 838 | , visibility: 'hidden' 839 | , display: 'block' 840 | }) 841 | return s 842 | }(this) : null 843 | , width = el.offsetWidth 844 | , height = el.offsetHeight 845 | 846 | orig && this.first().css(orig) 847 | return { 848 | height: height 849 | , width: width 850 | } 851 | } 852 | 853 | // attributes are hard. go shopping 854 | 855 | /** 856 | * @param {string} k an attribute to get or set 857 | * @param {string=} opt_v the value to set 858 | * @return {Bonzo|string} 859 | */ 860 | , attr: function (k, opt_v) { 861 | var el = this[0] 862 | if (typeof k != 'string' && !(k instanceof String)) { 863 | for (var n in k) { 864 | k.hasOwnProperty(n) && this.attr(n, k[n]) 865 | } 866 | return this 867 | } 868 | return typeof opt_v == 'undefined' ? 869 | !el ? null : specialAttributes.test(k) ? 870 | stateAttributes.test(k) && typeof el[k] == 'string' ? 871 | true : el[k] : (k == 'href' || k =='src') && features.hrefExtended ? 872 | el[getAttribute](k, 2) : el[getAttribute](k) : 873 | this.each(function (el) { 874 | specialAttributes.test(k) ? (el[k] = setter(el, opt_v)) : el[setAttribute](k, setter(el, opt_v)) 875 | }) 876 | } 877 | 878 | 879 | /** 880 | * @param {string} k 881 | * @return {Bonzo} 882 | */ 883 | , removeAttr: function (k) { 884 | return this.each(function (el) { 885 | stateAttributes.test(k) ? (el[k] = false) : el.removeAttribute(k) 886 | }) 887 | } 888 | 889 | 890 | /** 891 | * @param {string=} opt_s 892 | * @return {Bonzo|string} 893 | */ 894 | , val: function (s) { 895 | return (typeof s == 'string') ? 896 | this.attr('value', s) : 897 | this.length ? this[0].value : null 898 | } 899 | 900 | // use with care and knowledge. this data() method uses data attributes on the DOM nodes 901 | // to do this differently costs a lot more code. c'est la vie 902 | /** 903 | * @param {string|Object=} opt_k the key for which to get or set data 904 | * @param {Object=} opt_v 905 | * @return {Bonzo|Object} 906 | */ 907 | , data: function (opt_k, opt_v) { 908 | var el = this[0], o, m 909 | if (typeof opt_v === 'undefined') { 910 | if (!el) return null 911 | o = data(el) 912 | if (typeof opt_k === 'undefined') { 913 | each(el.attributes, function (a) { 914 | (m = ('' + a.name).match(dattr)) && (o[camelize(m[1])] = dataValue(a.value)) 915 | }) 916 | return o 917 | } else { 918 | if (typeof o[opt_k] === 'undefined') 919 | o[opt_k] = dataValue(this.attr('data-' + decamelize(opt_k))) 920 | return o[opt_k] 921 | } 922 | } else { 923 | return this.each(function (el) { data(el)[opt_k] = opt_v }) 924 | } 925 | } 926 | 927 | // DOM detachment & related 928 | 929 | /** 930 | * @return {Bonzo} 931 | */ 932 | , remove: function () { 933 | this.deepEach(clearData) 934 | 935 | return this.each(function (el) { 936 | el[parentNode] && el[parentNode].removeChild(el) 937 | }) 938 | } 939 | 940 | 941 | /** 942 | * @return {Bonzo} 943 | */ 944 | , empty: function () { 945 | return this.each(function (el) { 946 | deepEach(el.childNodes, clearData) 947 | 948 | while (el.firstChild) { 949 | el.removeChild(el.firstChild) 950 | } 951 | }) 952 | } 953 | 954 | 955 | /** 956 | * @return {Bonzo} 957 | */ 958 | , detach: function () { 959 | return this.each(function (el) { 960 | el[parentNode].removeChild(el) 961 | }) 962 | } 963 | 964 | // who uses a mouse anyway? oh right. 965 | 966 | /** 967 | * @param {number} y 968 | */ 969 | , scrollTop: function (y) { 970 | return scroll.call(this, null, y, 'y') 971 | } 972 | 973 | 974 | /** 975 | * @param {number} x 976 | */ 977 | , scrollLeft: function (x) { 978 | return scroll.call(this, x, null, 'x') 979 | } 980 | 981 | } 982 | 983 | function normalize(node, host, clone) { 984 | var i, l, ret 985 | if (typeof node == 'string') return bonzo.create(node) 986 | if (isNode(node)) node = [ node ] 987 | if (clone) { 988 | ret = [] // don't change original array 989 | for (i = 0, l = node.length; i < l; i++) ret[i] = cloneNode(host, node[i]) 990 | return ret 991 | } 992 | return node 993 | } 994 | 995 | function cloneNode(host, el) { 996 | var c = el.cloneNode(true) 997 | , cloneElems 998 | , elElems 999 | 1000 | // check for existence of an event cloner 1001 | // preferably https://github.com/fat/bean 1002 | // otherwise Bonzo won't do this for you 1003 | if (host.$ && typeof host.cloneEvents == 'function') { 1004 | host.$(c).cloneEvents(el) 1005 | 1006 | // clone events from every child node 1007 | cloneElems = host.$(c).find('*') 1008 | elElems = host.$(el).find('*') 1009 | 1010 | for (var i = 0; i < elElems.length; i++) 1011 | host.$(cloneElems[i]).cloneEvents(elElems[i]) 1012 | } 1013 | return c 1014 | } 1015 | 1016 | function scroll(x, y, type) { 1017 | var el = this[0] 1018 | if (!el) return this 1019 | if (x == null && y == null) { 1020 | return (isBody(el) ? getWindowScroll() : { x: el.scrollLeft, y: el.scrollTop })[type] 1021 | } 1022 | if (isBody(el)) { 1023 | win.scrollTo(x, y) 1024 | } else { 1025 | x != null && (el.scrollLeft = x) 1026 | y != null && (el.scrollTop = y) 1027 | } 1028 | return this 1029 | } 1030 | 1031 | function isBody(element) { 1032 | return element === win || (/^(?:body|html)$/i).test(element.tagName) 1033 | } 1034 | 1035 | function getWindowScroll() { 1036 | return { x: win.pageXOffset || html.scrollLeft, y: win.pageYOffset || html.scrollTop } 1037 | } 1038 | 1039 | /** 1040 | * @param {Array.|Element|Node|string} els 1041 | * @return {Bonzo} 1042 | */ 1043 | function bonzo(els) { 1044 | return new Bonzo(els) 1045 | } 1046 | 1047 | bonzo.setQueryEngine = function (q) { 1048 | query = q; 1049 | delete bonzo.setQueryEngine 1050 | } 1051 | 1052 | bonzo.aug = function (o, target) { 1053 | // for those standalone bonzo users. this love is for you. 1054 | for (var k in o) { 1055 | o.hasOwnProperty(k) && ((target || Bonzo.prototype)[k] = o[k]) 1056 | } 1057 | } 1058 | 1059 | bonzo.create = function (node) { 1060 | // hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh 1061 | return typeof node == 'string' && node !== '' ? 1062 | function () { 1063 | var tag = /^\s*<([^\s>]+)/.exec(node) 1064 | , el = doc.createElement('div') 1065 | , els = [] 1066 | , p = tag ? tagMap[tag[1].toLowerCase()] : null 1067 | , dep = p ? p[2] + 1 : 1 1068 | , ns = p && p[3] 1069 | , pn = parentNode 1070 | , tb = features.autoTbody && p && p[0] == '' && !(/,
, etc. 1079 | if ((!tag || el.nodeType == 1) && (!tb || el.tagName.toLowerCase() != 'tbody')) { 1080 | els.push(el) 1081 | } 1082 | } while (el = el.nextSibling) 1083 | // IE < 9 gives us a parentNode which messes up insert() check for cloning 1084 | // `dep` > 1 can also cause problems with the insert() check (must do this last) 1085 | each(els, function(el) { el[pn] && el[pn].removeChild(el) }) 1086 | return els 1087 | }() : isNode(node) ? [node.cloneNode(true)] : [] 1088 | } 1089 | 1090 | bonzo.doc = function () { 1091 | var vp = bonzo.viewport() 1092 | return { 1093 | width: Math.max(doc.body.scrollWidth, html.scrollWidth, vp.width) 1094 | , height: Math.max(doc.body.scrollHeight, html.scrollHeight, vp.height) 1095 | } 1096 | } 1097 | 1098 | bonzo.firstChild = function (el) { 1099 | for (var c = el.childNodes, i = 0, j = (c && c.length) || 0, e; i < j; i++) { 1100 | if (c[i].nodeType === 1) e = c[j = i] 1101 | } 1102 | return e 1103 | } 1104 | 1105 | bonzo.viewport = function () { 1106 | return { 1107 | width: ie ? html.clientWidth : self.innerWidth 1108 | , height: ie ? html.clientHeight : self.innerHeight 1109 | } 1110 | } 1111 | 1112 | bonzo.isAncestor = 'compareDocumentPosition' in html ? 1113 | function (container, element) { 1114 | return (container.compareDocumentPosition(element) & 16) == 16 1115 | } : 'contains' in html ? 1116 | function (container, element) { 1117 | return container !== element && container.contains(element); 1118 | } : 1119 | function (container, element) { 1120 | while (element = element[parentNode]) { 1121 | if (element === container) { 1122 | return true 1123 | } 1124 | } 1125 | return false 1126 | } 1127 | 1128 | return bonzo 1129 | }, this); // the only line we care about using a semi-colon. placed here for concatenation tools 1130 | -------------------------------------------------------------------------------- /example4/bonzo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bonzo: DOM Utility (c) Dustin Diaz 2012 3 | * https://github.com/ded/bonzo 4 | * License MIT 5 | */ 6 | (function (name, definition, context) { 7 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 8 | else if (typeof context['define'] == 'function' && context['define']['amd']) define(name, definition) 9 | else context[name] = definition() 10 | })('bonzo', function() { 11 | var win = window 12 | , doc = win.document 13 | , html = doc.documentElement 14 | , parentNode = 'parentNode' 15 | , query = null // used for setting a selector engine host 16 | , specialAttributes = /^(checked|value|selected|disabled)$/i 17 | , specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i // tags that we have trouble inserting *into* 18 | , table = ['', '
', 1] 19 | , td = ['', '
', 3] 20 | , option = ['', 1] 21 | , noscope = ['_', '', 0, 1] 22 | , tagMap = { // tags that we have trouble *inserting* 23 | thead: table, tbody: table, tfoot: table, colgroup: table, caption: table 24 | , tr: ['', '
', 2] 25 | , th: td , td: td 26 | , col: ['', '
', 2] 27 | , fieldset: ['
', '
', 1] 28 | , legend: ['
', '
', 2] 29 | , option: option, optgroup: option 30 | , script: noscope, style: noscope, link: noscope, param: noscope, base: noscope 31 | } 32 | , stateAttributes = /^(checked|selected|disabled)$/ 33 | , ie = /msie/i.test(navigator.userAgent) 34 | , hasClass, addClass, removeClass 35 | , uidMap = {} 36 | , uuids = 0 37 | , digit = /^-?[\d\.]+$/ 38 | , dattr = /^data-(.+)$/ 39 | , px = 'px' 40 | , setAttribute = 'setAttribute' 41 | , getAttribute = 'getAttribute' 42 | , byTag = 'getElementsByTagName' 43 | , features = function() { 44 | var e = doc.createElement('p') 45 | e.innerHTML = 'x
' 46 | return { 47 | hrefExtended: e[byTag]('a')[0][getAttribute]('href') != '#x' // IE < 8 48 | , autoTbody: e[byTag]('tbody').length !== 0 // IE < 8 49 | , computedStyle: doc.defaultView && doc.defaultView.getComputedStyle 50 | , cssFloat: e[byTag]('table')[0].style.styleFloat ? 'styleFloat' : 'cssFloat' 51 | , transform: function () { 52 | var props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'], i 53 | for (i = 0; i < props.length; i++) { 54 | if (props[i] in e.style) return props[i] 55 | } 56 | }() 57 | , classList: 'classList' in e 58 | , opasity: function () { 59 | return typeof doc.createElement('a').style.opacity !== 'undefined' 60 | }() 61 | } 62 | }() 63 | , trimReplace = /(^\s*|\s*$)/g 64 | , whitespaceRegex = /\s+/ 65 | , toString = String.prototype.toString 66 | , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, boxFlex: 1, WebkitBoxFlex: 1, MozBoxFlex: 1 } 67 | , trim = String.prototype.trim ? 68 | function (s) { 69 | return s.trim() 70 | } : 71 | function (s) { 72 | return s.replace(trimReplace, '') 73 | } 74 | 75 | 76 | /** 77 | * @param {string} c a class name to test 78 | * @return {boolean} 79 | */ 80 | function classReg(c) { 81 | return new RegExp("(^|\\s+)" + c + "(\\s+|$)") 82 | } 83 | 84 | 85 | /** 86 | * @param {Bonzo|Array} ar 87 | * @param {function(Object, number, (Bonzo|Array))} fn 88 | * @param {Object=} opt_scope 89 | * @param {boolean=} opt_rev 90 | * @return {Bonzo|Array} 91 | */ 92 | function each(ar, fn, opt_scope, opt_rev) { 93 | var ind, i = 0, l = ar.length 94 | for (; i < l; i++) { 95 | ind = opt_rev ? ar.length - i - 1 : i 96 | fn.call(opt_scope || ar[ind], ar[ind], ind, ar) 97 | } 98 | return ar 99 | } 100 | 101 | 102 | /** 103 | * @param {Bonzo|Array} ar 104 | * @param {function(Object, number, (Bonzo|Array))} fn 105 | * @param {Object=} opt_scope 106 | * @return {Bonzo|Array} 107 | */ 108 | function deepEach(ar, fn, opt_scope) { 109 | for (var i = 0, l = ar.length; i < l; i++) { 110 | if (isNode(ar[i])) { 111 | deepEach(ar[i].childNodes, fn, opt_scope) 112 | fn.call(opt_scope || ar[i], ar[i], i, ar) 113 | } 114 | } 115 | return ar 116 | } 117 | 118 | 119 | /** 120 | * @param {string} s 121 | * @return {string} 122 | */ 123 | function camelize(s) { 124 | return s.replace(/-(.)/g, function (m, m1) { 125 | return m1.toUpperCase() 126 | }) 127 | } 128 | 129 | 130 | /** 131 | * @param {string} s 132 | * @return {string} 133 | */ 134 | function decamelize(s) { 135 | return s ? s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : s 136 | } 137 | 138 | 139 | /** 140 | * @param {Element} el 141 | * @return {*} 142 | */ 143 | function data(el) { 144 | el[getAttribute]('data-node-uid') || el[setAttribute]('data-node-uid', ++uuids) 145 | var uid = el[getAttribute]('data-node-uid') 146 | return uidMap[uid] || (uidMap[uid] = {}) 147 | } 148 | 149 | 150 | /** 151 | * removes the data associated with an element 152 | * @param {Element} el 153 | */ 154 | function clearData(el) { 155 | var uid = el[getAttribute]('data-node-uid') 156 | if (uid) delete uidMap[uid] 157 | } 158 | 159 | 160 | function dataValue(d) { 161 | var f 162 | try { 163 | return (d === null || d === undefined) ? undefined : 164 | d === 'true' ? true : 165 | d === 'false' ? false : 166 | d === 'null' ? null : 167 | (f = parseFloat(d)) == d ? f : d; 168 | } catch(e) {} 169 | return undefined 170 | } 171 | 172 | function isNode(node) { 173 | return node && node.nodeName && (node.nodeType == 1 || node.nodeType == 11) 174 | } 175 | 176 | 177 | /** 178 | * @param {Bonzo|Array} ar 179 | * @param {function(Object, number, (Bonzo|Array))} fn 180 | * @param {Object=} opt_scope 181 | * @return {boolean} whether `some`thing was found 182 | */ 183 | function some(ar, fn, opt_scope) { 184 | for (var i = 0, j = ar.length; i < j; ++i) if (fn.call(opt_scope || null, ar[i], i, ar)) return true 185 | return false 186 | } 187 | 188 | 189 | /** 190 | * this could be a giant enum of CSS properties 191 | * but in favor of file size sans-closure deadcode optimizations 192 | * we're just asking for any ol string 193 | * then it gets transformed into the appropriate style property for JS access 194 | * @param {string} p 195 | * @return {string} 196 | */ 197 | function styleProperty(p) { 198 | (p == 'transform' && (p = features.transform)) || 199 | (/^transform-?[Oo]rigin$/.test(p) && (p = features.transform + "Origin")) || 200 | (p == 'float' && (p = features.cssFloat)) 201 | return p ? camelize(p) : null 202 | } 203 | 204 | var getStyle = features.computedStyle ? 205 | function (el, property) { 206 | var value = null 207 | , computed = doc.defaultView.getComputedStyle(el, '') 208 | computed && (value = computed[property]) 209 | return el.style[property] || value 210 | } : 211 | 212 | (ie && html.currentStyle) ? 213 | 214 | /** 215 | * @param {Element} el 216 | * @param {string} property 217 | * @return {string|number} 218 | */ 219 | function (el, property) { 220 | if (property == 'opacity' && !features.opasity) { 221 | var val = 100 222 | try { 223 | val = el['filters']['DXImageTransform.Microsoft.Alpha'].opacity 224 | } catch (e1) { 225 | try { 226 | val = el['filters']('alpha').opacity 227 | } catch (e2) {} 228 | } 229 | return val / 100 230 | } 231 | var value = el.currentStyle ? el.currentStyle[property] : null 232 | return el.style[property] || value 233 | } : 234 | 235 | function (el, property) { 236 | return el.style[property] 237 | } 238 | 239 | // this insert method is intense 240 | function insert(target, host, fn, rev) { 241 | var i = 0, self = host || this, r = [] 242 | // target nodes could be a css selector if it's a string and a selector engine is present 243 | // otherwise, just use target 244 | , nodes = query && typeof target == 'string' && target.charAt(0) != '<' ? query(target) : target 245 | // normalize each node in case it's still a string and we need to create nodes on the fly 246 | each(normalize(nodes), function (t, j) { 247 | each(self, function (el) { 248 | fn(t, r[i++] = j > 0 ? cloneNode(self, el) : el) 249 | }, null, rev) 250 | }, this, rev) 251 | self.length = i 252 | each(r, function (e) { 253 | self[--i] = e 254 | }, null, !rev) 255 | return self 256 | } 257 | 258 | 259 | /** 260 | * sets an element to an explicit x/y position on the page 261 | * @param {Element} el 262 | * @param {?number} x 263 | * @param {?number} y 264 | */ 265 | function xy(el, x, y) { 266 | var $el = bonzo(el) 267 | , style = $el.css('position') 268 | , offset = $el.offset() 269 | , rel = 'relative' 270 | , isRel = style == rel 271 | , delta = [parseInt($el.css('left'), 10), parseInt($el.css('top'), 10)] 272 | 273 | if (style == 'static') { 274 | $el.css('position', rel) 275 | style = rel 276 | } 277 | 278 | isNaN(delta[0]) && (delta[0] = isRel ? 0 : el.offsetLeft) 279 | isNaN(delta[1]) && (delta[1] = isRel ? 0 : el.offsetTop) 280 | 281 | x != null && (el.style.left = x - offset.left + delta[0] + px) 282 | y != null && (el.style.top = y - offset.top + delta[1] + px) 283 | 284 | } 285 | 286 | // classList support for class management 287 | // altho to be fair, the api sucks because it won't accept multiple classes at once 288 | if (features.classList) { 289 | hasClass = function (el, c) { 290 | return el.classList.contains(c) 291 | } 292 | addClass = function (el, c) { 293 | el.classList.add(c) 294 | } 295 | removeClass = function (el, c) { 296 | el.classList.remove(c) 297 | } 298 | } 299 | else { 300 | hasClass = function (el, c) { 301 | return classReg(c).test(el.className) 302 | } 303 | addClass = function (el, c) { 304 | el.className = trim(el.className + ' ' + c) 305 | } 306 | removeClass = function (el, c) { 307 | el.className = trim(el.className.replace(classReg(c), ' ')) 308 | } 309 | } 310 | 311 | 312 | /** 313 | * this allows method calling for setting values 314 | * 315 | * @example 316 | * bonzo(elements).css('color', function (el) { 317 | * return el.getAttribute('data-original-color') 318 | * }) 319 | * 320 | * @param {Element} el 321 | * @param {function (Element)|string} 322 | * @return {string} 323 | */ 324 | function setter(el, v) { 325 | return typeof v == 'function' ? v(el) : v 326 | } 327 | 328 | /** 329 | * @constructor 330 | * @param {Array.|Element|Node|string} elements 331 | */ 332 | function Bonzo(elements) { 333 | this.length = 0 334 | if (elements) { 335 | elements = typeof elements !== 'string' && 336 | !elements.nodeType && 337 | typeof elements.length !== 'undefined' ? 338 | elements : 339 | [elements] 340 | this.length = elements.length 341 | for (var i = 0; i < elements.length; i++) this[i] = elements[i] 342 | } 343 | } 344 | 345 | Bonzo.prototype = { 346 | 347 | /** 348 | * @param {number} index 349 | * @return {Element|Node} 350 | */ 351 | get: function (index) { 352 | return this[index] || null 353 | } 354 | 355 | // itetators 356 | /** 357 | * @param {function(Element|Node)} fn 358 | * @param {Object=} opt_scope 359 | * @return {Bonzo} 360 | */ 361 | , each: function (fn, opt_scope) { 362 | return each(this, fn, opt_scope) 363 | } 364 | 365 | /** 366 | * @param {Function} fn 367 | * @param {Object=} opt_scope 368 | * @return {Bonzo} 369 | */ 370 | , deepEach: function (fn, opt_scope) { 371 | return deepEach(this, fn, opt_scope) 372 | } 373 | 374 | 375 | /** 376 | * @param {Function} fn 377 | * @param {Function=} opt_reject 378 | * @return {Array} 379 | */ 380 | , map: function (fn, opt_reject) { 381 | var m = [], n, i 382 | for (i = 0; i < this.length; i++) { 383 | n = fn.call(this, this[i], i) 384 | opt_reject ? (opt_reject(n) && m.push(n)) : m.push(n) 385 | } 386 | return m 387 | } 388 | 389 | // text and html inserters! 390 | 391 | /** 392 | * @param {string} h the HTML to insert 393 | * @param {boolean=} opt_text whether to set or get text content 394 | * @return {Bonzo|string} 395 | */ 396 | , html: function (h, opt_text) { 397 | var method = opt_text 398 | ? html.textContent === undefined ? 'innerText' : 'textContent' 399 | : 'innerHTML' 400 | , that = this 401 | , append = function (el, i) { 402 | each(normalize(h, that, i), function (node) { 403 | el.appendChild(node) 404 | }) 405 | } 406 | , updateElement = function (el, i) { 407 | try { 408 | if (opt_text || (typeof h == 'string' && !specialTags.test(el.tagName))) { 409 | return el[method] = h 410 | } 411 | } catch (e) {} 412 | append(el, i) 413 | } 414 | return typeof h != 'undefined' 415 | ? this.empty().each(updateElement) 416 | : this[0] ? this[0][method] : '' 417 | } 418 | 419 | /** 420 | * @param {string=} opt_text the text to set, otherwise this is a getter 421 | * @return {Bonzo|string} 422 | */ 423 | , text: function (opt_text) { 424 | return this.html(opt_text, true) 425 | } 426 | 427 | // more related insertion methods 428 | 429 | /** 430 | * @param {Bonzo|string|Element|Array} node 431 | * @return {Bonzo} 432 | */ 433 | , append: function (node) { 434 | var that = this 435 | return this.each(function (el, i) { 436 | each(normalize(node, that, i), function (i) { 437 | el.appendChild(i) 438 | }) 439 | }) 440 | } 441 | 442 | 443 | /** 444 | * @param {Bonzo|string|Element|Array} node 445 | * @return {Bonzo} 446 | */ 447 | , prepend: function (node) { 448 | var that = this 449 | return this.each(function (el, i) { 450 | var first = el.firstChild 451 | each(normalize(node, that, i), function (i) { 452 | el.insertBefore(i, first) 453 | }) 454 | }) 455 | } 456 | 457 | 458 | /** 459 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 460 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 461 | * @return {Bonzo} 462 | */ 463 | , appendTo: function (target, opt_host) { 464 | return insert.call(this, target, opt_host, function (t, el) { 465 | t.appendChild(el) 466 | }) 467 | } 468 | 469 | 470 | /** 471 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 472 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 473 | * @return {Bonzo} 474 | */ 475 | , prependTo: function (target, opt_host) { 476 | return insert.call(this, target, opt_host, function (t, el) { 477 | t.insertBefore(el, t.firstChild) 478 | }, 1) 479 | } 480 | 481 | 482 | /** 483 | * @param {Bonzo|string|Element|Array} node 484 | * @return {Bonzo} 485 | */ 486 | , before: function (node) { 487 | var that = this 488 | return this.each(function (el, i) { 489 | each(normalize(node, that, i), function (i) { 490 | el[parentNode].insertBefore(i, el) 491 | }) 492 | }) 493 | } 494 | 495 | 496 | /** 497 | * @param {Bonzo|string|Element|Array} node 498 | * @return {Bonzo} 499 | */ 500 | , after: function (node) { 501 | var that = this 502 | return this.each(function (el, i) { 503 | each(normalize(node, that, i), function (i) { 504 | el[parentNode].insertBefore(i, el.nextSibling) 505 | }, null, 1) 506 | }) 507 | } 508 | 509 | 510 | /** 511 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 512 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 513 | * @return {Bonzo} 514 | */ 515 | , insertBefore: function (target, opt_host) { 516 | return insert.call(this, target, opt_host, function (t, el) { 517 | t[parentNode].insertBefore(el, t) 518 | }) 519 | } 520 | 521 | 522 | /** 523 | * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content 524 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 525 | * @return {Bonzo} 526 | */ 527 | , insertAfter: function (target, opt_host) { 528 | return insert.call(this, target, opt_host, function (t, el) { 529 | var sibling = t.nextSibling 530 | sibling ? 531 | t[parentNode].insertBefore(el, sibling) : 532 | t[parentNode].appendChild(el) 533 | }, 1) 534 | } 535 | 536 | 537 | /** 538 | * @param {Bonzo|string|Element|Array} node 539 | * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender) 540 | * @return {Bonzo} 541 | */ 542 | , replaceWith: function (node, opt_host) { 543 | var ret = bonzo(normalize(node)).insertAfter(this, opt_host) 544 | this.remove() 545 | Bonzo.call(opt_host || this, ret) 546 | return opt_host || this 547 | } 548 | 549 | // class management 550 | 551 | /** 552 | * @param {string} c 553 | * @return {Bonzo} 554 | */ 555 | , addClass: function (c) { 556 | c = toString.call(c).split(whitespaceRegex) 557 | return this.each(function (el) { 558 | // we `each` here so you can do $el.addClass('foo bar') 559 | each(c, function (c) { 560 | if (c && !hasClass(el, setter(el, c))) 561 | addClass(el, setter(el, c)) 562 | }) 563 | }) 564 | } 565 | 566 | 567 | /** 568 | * @param {string} c 569 | * @return {Bonzo} 570 | */ 571 | , removeClass: function (c) { 572 | c = toString.call(c).split(whitespaceRegex) 573 | return this.each(function (el) { 574 | each(c, function (c) { 575 | if (c && hasClass(el, setter(el, c))) 576 | removeClass(el, setter(el, c)) 577 | }) 578 | }) 579 | } 580 | 581 | 582 | /** 583 | * @param {string} c 584 | * @return {boolean} 585 | */ 586 | , hasClass: function (c) { 587 | c = toString.call(c).split(whitespaceRegex) 588 | return some(this, function (el) { 589 | return some(c, function (c) { 590 | return c && hasClass(el, c) 591 | }) 592 | }) 593 | } 594 | 595 | 596 | /** 597 | * @param {string} c classname to toggle 598 | * @param {boolean=} opt_condition whether to add or remove the class straight away 599 | * @return {Bonzo} 600 | */ 601 | , toggleClass: function (c, opt_condition) { 602 | c = toString.call(c).split(whitespaceRegex) 603 | return this.each(function (el) { 604 | each(c, function (c) { 605 | if (c) { 606 | typeof opt_condition !== 'undefined' ? 607 | opt_condition ? addClass(el, c) : removeClass(el, c) : 608 | hasClass(el, c) ? removeClass(el, c) : addClass(el, c) 609 | } 610 | }) 611 | }) 612 | } 613 | 614 | // display togglers 615 | 616 | /** 617 | * @param {string=} opt_type useful to set back to anything other than an empty string 618 | * @return {Bonzo} 619 | */ 620 | , show: function (opt_type) { 621 | opt_type = typeof opt_type == 'string' ? opt_type : '' 622 | return this.each(function (el) { 623 | el.style.display = opt_type 624 | }) 625 | } 626 | 627 | 628 | /** 629 | * @return {Bonzo} 630 | */ 631 | , hide: function () { 632 | return this.each(function (el) { 633 | el.style.display = 'none' 634 | }) 635 | } 636 | 637 | 638 | /** 639 | * @param {Function=} opt_callback 640 | * @param {string=} opt_type 641 | * @return {Bonzo} 642 | */ 643 | , toggle: function (opt_callback, opt_type) { 644 | opt_type = typeof opt_type == 'string' ? opt_type : ''; 645 | typeof opt_callback != 'function' && (opt_callback = null) 646 | return this.each(function (el) { 647 | el.style.display = (el.offsetWidth || el.offsetHeight) ? 'none' : opt_type; 648 | opt_callback && opt_callback.call(el) 649 | }) 650 | } 651 | 652 | 653 | // DOM Walkers & getters 654 | 655 | /** 656 | * @return {Element|Node} 657 | */ 658 | , first: function () { 659 | return bonzo(this.length ? this[0] : []) 660 | } 661 | 662 | 663 | /** 664 | * @return {Element|Node} 665 | */ 666 | , last: function () { 667 | return bonzo(this.length ? this[this.length - 1] : []) 668 | } 669 | 670 | 671 | /** 672 | * @return {Element|Node} 673 | */ 674 | , next: function () { 675 | return this.related('nextSibling') 676 | } 677 | 678 | 679 | /** 680 | * @return {Element|Node} 681 | */ 682 | , previous: function () { 683 | return this.related('previousSibling') 684 | } 685 | 686 | 687 | /** 688 | * @return {Element|Node} 689 | */ 690 | , parent: function() { 691 | return this.related(parentNode) 692 | } 693 | 694 | 695 | /** 696 | * @private 697 | * @param {string} method the directional DOM method 698 | * @return {Element|Node} 699 | */ 700 | , related: function (method) { 701 | return this.map( 702 | function (el) { 703 | el = el[method] 704 | while (el && el.nodeType !== 1) { 705 | el = el[method] 706 | } 707 | return el || 0 708 | }, 709 | function (el) { 710 | return el 711 | } 712 | ) 713 | } 714 | 715 | 716 | /** 717 | * @return {Bonzo} 718 | */ 719 | , focus: function () { 720 | this.length && this[0].focus() 721 | return this 722 | } 723 | 724 | 725 | /** 726 | * @return {Bonzo} 727 | */ 728 | , blur: function () { 729 | this.length && this[0].blur() 730 | return this 731 | } 732 | 733 | // style getter setter & related methods 734 | 735 | /** 736 | * @param {Object|string} o 737 | * @param {string=} opt_v 738 | * @return {Bonzo|string} 739 | */ 740 | , css: function (o, opt_v) { 741 | var p, iter = o 742 | // is this a request for just getting a style? 743 | if (opt_v === undefined && typeof o == 'string') { 744 | // repurpose 'v' 745 | opt_v = this[0] 746 | if (!opt_v) return null 747 | if (opt_v === doc || opt_v === win) { 748 | p = (opt_v === doc) ? bonzo.doc() : bonzo.viewport() 749 | return o == 'width' ? p.width : o == 'height' ? p.height : '' 750 | } 751 | return (o = styleProperty(o)) ? getStyle(opt_v, o) : null 752 | } 753 | 754 | if (typeof o == 'string') { 755 | iter = {} 756 | iter[o] = opt_v 757 | } 758 | 759 | if (ie && iter.opacity) { 760 | // oh this 'ol gamut 761 | iter.filter = 'alpha(opacity=' + (iter.opacity * 100) + ')' 762 | // give it layout 763 | iter.zoom = o.zoom || 1; 764 | delete iter.opacity; 765 | } 766 | 767 | function fn(el, p, v) { 768 | for (var k in iter) { 769 | if (iter.hasOwnProperty(k)) { 770 | v = iter[k]; 771 | // change "5" to "5px" - unless you're line-height, which is allowed 772 | (p = styleProperty(k)) && digit.test(v) && !(p in unitless) && (v += px) 773 | try { el.style[p] = setter(el, v) } catch(e) {} 774 | } 775 | } 776 | } 777 | return this.each(fn) 778 | } 779 | 780 | 781 | /** 782 | * @param {number=} opt_x 783 | * @param {number=} opt_y 784 | * @return {Bonzo|number} 785 | */ 786 | , offset: function (opt_x, opt_y) { 787 | if (typeof opt_x == 'number' || typeof opt_y == 'number') { 788 | return this.each(function (el) { 789 | xy(el, opt_x, opt_y) 790 | }) 791 | } 792 | if (!this[0]) return { 793 | top: 0 794 | , left: 0 795 | , height: 0 796 | , width: 0 797 | } 798 | var el = this[0] 799 | , width = el.offsetWidth 800 | , height = el.offsetHeight 801 | , top = el.offsetTop 802 | , left = el.offsetLeft 803 | while (el = el.offsetParent) { 804 | top = top + el.offsetTop 805 | left = left + el.offsetLeft 806 | 807 | if (el != doc.body) { 808 | top -= el.scrollTop 809 | left -= el.scrollLeft 810 | } 811 | } 812 | 813 | return { 814 | top: top 815 | , left: left 816 | , height: height 817 | , width: width 818 | } 819 | } 820 | 821 | 822 | /** 823 | * @return {number} 824 | */ 825 | , dim: function () { 826 | if (!this.length) return { height: 0, width: 0 } 827 | var el = this[0] 828 | , orig = !el.offsetWidth && !el.offsetHeight ? 829 | // el isn't visible, can't be measured properly, so fix that 830 | function (t) { 831 | var s = { 832 | position: el.style.position || '' 833 | , visibility: el.style.visibility || '' 834 | , display: el.style.display || '' 835 | } 836 | t.first().css({ 837 | position: 'absolute' 838 | , visibility: 'hidden' 839 | , display: 'block' 840 | }) 841 | return s 842 | }(this) : null 843 | , width = el.offsetWidth 844 | , height = el.offsetHeight 845 | 846 | orig && this.first().css(orig) 847 | return { 848 | height: height 849 | , width: width 850 | } 851 | } 852 | 853 | // attributes are hard. go shopping 854 | 855 | /** 856 | * @param {string} k an attribute to get or set 857 | * @param {string=} opt_v the value to set 858 | * @return {Bonzo|string} 859 | */ 860 | , attr: function (k, opt_v) { 861 | var el = this[0] 862 | if (typeof k != 'string' && !(k instanceof String)) { 863 | for (var n in k) { 864 | k.hasOwnProperty(n) && this.attr(n, k[n]) 865 | } 866 | return this 867 | } 868 | return typeof opt_v == 'undefined' ? 869 | !el ? null : specialAttributes.test(k) ? 870 | stateAttributes.test(k) && typeof el[k] == 'string' ? 871 | true : el[k] : (k == 'href' || k =='src') && features.hrefExtended ? 872 | el[getAttribute](k, 2) : el[getAttribute](k) : 873 | this.each(function (el) { 874 | specialAttributes.test(k) ? (el[k] = setter(el, opt_v)) : el[setAttribute](k, setter(el, opt_v)) 875 | }) 876 | } 877 | 878 | 879 | /** 880 | * @param {string} k 881 | * @return {Bonzo} 882 | */ 883 | , removeAttr: function (k) { 884 | return this.each(function (el) { 885 | stateAttributes.test(k) ? (el[k] = false) : el.removeAttribute(k) 886 | }) 887 | } 888 | 889 | 890 | /** 891 | * @param {string=} opt_s 892 | * @return {Bonzo|string} 893 | */ 894 | , val: function (s) { 895 | return (typeof s == 'string') ? 896 | this.attr('value', s) : 897 | this.length ? this[0].value : null 898 | } 899 | 900 | // use with care and knowledge. this data() method uses data attributes on the DOM nodes 901 | // to do this differently costs a lot more code. c'est la vie 902 | /** 903 | * @param {string|Object=} opt_k the key for which to get or set data 904 | * @param {Object=} opt_v 905 | * @return {Bonzo|Object} 906 | */ 907 | , data: function (opt_k, opt_v) { 908 | var el = this[0], o, m 909 | if (typeof opt_v === 'undefined') { 910 | if (!el) return null 911 | o = data(el) 912 | if (typeof opt_k === 'undefined') { 913 | each(el.attributes, function (a) { 914 | (m = ('' + a.name).match(dattr)) && (o[camelize(m[1])] = dataValue(a.value)) 915 | }) 916 | return o 917 | } else { 918 | if (typeof o[opt_k] === 'undefined') 919 | o[opt_k] = dataValue(this.attr('data-' + decamelize(opt_k))) 920 | return o[opt_k] 921 | } 922 | } else { 923 | return this.each(function (el) { data(el)[opt_k] = opt_v }) 924 | } 925 | } 926 | 927 | // DOM detachment & related 928 | 929 | /** 930 | * @return {Bonzo} 931 | */ 932 | , remove: function () { 933 | this.deepEach(clearData) 934 | 935 | return this.each(function (el) { 936 | el[parentNode] && el[parentNode].removeChild(el) 937 | }) 938 | } 939 | 940 | 941 | /** 942 | * @return {Bonzo} 943 | */ 944 | , empty: function () { 945 | return this.each(function (el) { 946 | deepEach(el.childNodes, clearData) 947 | 948 | while (el.firstChild) { 949 | el.removeChild(el.firstChild) 950 | } 951 | }) 952 | } 953 | 954 | 955 | /** 956 | * @return {Bonzo} 957 | */ 958 | , detach: function () { 959 | return this.each(function (el) { 960 | el[parentNode].removeChild(el) 961 | }) 962 | } 963 | 964 | // who uses a mouse anyway? oh right. 965 | 966 | /** 967 | * @param {number} y 968 | */ 969 | , scrollTop: function (y) { 970 | return scroll.call(this, null, y, 'y') 971 | } 972 | 973 | 974 | /** 975 | * @param {number} x 976 | */ 977 | , scrollLeft: function (x) { 978 | return scroll.call(this, x, null, 'x') 979 | } 980 | 981 | } 982 | 983 | function normalize(node, host, clone) { 984 | var i, l, ret 985 | if (typeof node == 'string') return bonzo.create(node) 986 | if (isNode(node)) node = [ node ] 987 | if (clone) { 988 | ret = [] // don't change original array 989 | for (i = 0, l = node.length; i < l; i++) ret[i] = cloneNode(host, node[i]) 990 | return ret 991 | } 992 | return node 993 | } 994 | 995 | function cloneNode(host, el) { 996 | var c = el.cloneNode(true) 997 | , cloneElems 998 | , elElems 999 | 1000 | // check for existence of an event cloner 1001 | // preferably https://github.com/fat/bean 1002 | // otherwise Bonzo won't do this for you 1003 | if (host.$ && typeof host.cloneEvents == 'function') { 1004 | host.$(c).cloneEvents(el) 1005 | 1006 | // clone events from every child node 1007 | cloneElems = host.$(c).find('*') 1008 | elElems = host.$(el).find('*') 1009 | 1010 | for (var i = 0; i < elElems.length; i++) 1011 | host.$(cloneElems[i]).cloneEvents(elElems[i]) 1012 | } 1013 | return c 1014 | } 1015 | 1016 | function scroll(x, y, type) { 1017 | var el = this[0] 1018 | if (!el) return this 1019 | if (x == null && y == null) { 1020 | return (isBody(el) ? getWindowScroll() : { x: el.scrollLeft, y: el.scrollTop })[type] 1021 | } 1022 | if (isBody(el)) { 1023 | win.scrollTo(x, y) 1024 | } else { 1025 | x != null && (el.scrollLeft = x) 1026 | y != null && (el.scrollTop = y) 1027 | } 1028 | return this 1029 | } 1030 | 1031 | function isBody(element) { 1032 | return element === win || (/^(?:body|html)$/i).test(element.tagName) 1033 | } 1034 | 1035 | function getWindowScroll() { 1036 | return { x: win.pageXOffset || html.scrollLeft, y: win.pageYOffset || html.scrollTop } 1037 | } 1038 | 1039 | /** 1040 | * @param {Array.|Element|Node|string} els 1041 | * @return {Bonzo} 1042 | */ 1043 | function bonzo(els) { 1044 | return new Bonzo(els) 1045 | } 1046 | 1047 | bonzo.setQueryEngine = function (q) { 1048 | query = q; 1049 | delete bonzo.setQueryEngine 1050 | } 1051 | 1052 | bonzo.aug = function (o, target) { 1053 | // for those standalone bonzo users. this love is for you. 1054 | for (var k in o) { 1055 | o.hasOwnProperty(k) && ((target || Bonzo.prototype)[k] = o[k]) 1056 | } 1057 | } 1058 | 1059 | bonzo.create = function (node) { 1060 | // hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh 1061 | return typeof node == 'string' && node !== '' ? 1062 | function () { 1063 | var tag = /^\s*<([^\s>]+)/.exec(node) 1064 | , el = doc.createElement('div') 1065 | , els = [] 1066 | , p = tag ? tagMap[tag[1].toLowerCase()] : null 1067 | , dep = p ? p[2] + 1 : 1 1068 | , ns = p && p[3] 1069 | , pn = parentNode 1070 | , tb = features.autoTbody && p && p[0] == '' && !(/,
, etc. 1079 | if ((!tag || el.nodeType == 1) && (!tb || el.tagName.toLowerCase() != 'tbody')) { 1080 | els.push(el) 1081 | } 1082 | } while (el = el.nextSibling) 1083 | // IE < 9 gives us a parentNode which messes up insert() check for cloning 1084 | // `dep` > 1 can also cause problems with the insert() check (must do this last) 1085 | each(els, function(el) { el[pn] && el[pn].removeChild(el) }) 1086 | return els 1087 | }() : isNode(node) ? [node.cloneNode(true)] : [] 1088 | } 1089 | 1090 | bonzo.doc = function () { 1091 | var vp = bonzo.viewport() 1092 | return { 1093 | width: Math.max(doc.body.scrollWidth, html.scrollWidth, vp.width) 1094 | , height: Math.max(doc.body.scrollHeight, html.scrollHeight, vp.height) 1095 | } 1096 | } 1097 | 1098 | bonzo.firstChild = function (el) { 1099 | for (var c = el.childNodes, i = 0, j = (c && c.length) || 0, e; i < j; i++) { 1100 | if (c[i].nodeType === 1) e = c[j = i] 1101 | } 1102 | return e 1103 | } 1104 | 1105 | bonzo.viewport = function () { 1106 | return { 1107 | width: ie ? html.clientWidth : self.innerWidth 1108 | , height: ie ? html.clientHeight : self.innerHeight 1109 | } 1110 | } 1111 | 1112 | bonzo.isAncestor = 'compareDocumentPosition' in html ? 1113 | function (container, element) { 1114 | return (container.compareDocumentPosition(element) & 16) == 16 1115 | } : 'contains' in html ? 1116 | function (container, element) { 1117 | return container !== element && container.contains(element); 1118 | } : 1119 | function (container, element) { 1120 | while (element = element[parentNode]) { 1121 | if (element === container) { 1122 | return true 1123 | } 1124 | } 1125 | return false 1126 | } 1127 | 1128 | return bonzo 1129 | }, this); // the only line we care about using a semi-colon. placed here for concatenation tools 1130 | -------------------------------------------------------------------------------- /example3/ender.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ============================================================= 3 | * Ender: open module JavaScript framework (https://ender.no.de) 4 | * Build: ender build qwery bean 5 | * Packages: ender-js@0.4.4 qwery@3.3.11 bean@0.4.11-1 6 | * ============================================================= 7 | */ 8 | 9 | /*! 10 | * Ender: open module JavaScript framework (client-lib) 11 | * copyright Dustin Diaz & Jacob Thornton 2011-2012 (@ded @fat) 12 | * http://ender.no.de 13 | * License MIT 14 | */ 15 | (function (context) { 16 | 17 | // a global object for node.js module compatiblity 18 | // ============================================ 19 | 20 | context['global'] = context 21 | 22 | // Implements simple module system 23 | // losely based on CommonJS Modules spec v1.1.1 24 | // ============================================ 25 | 26 | var modules = {} 27 | , old = context['$'] 28 | , oldEnder = context['ender'] 29 | , oldRequire = context['require'] 30 | , oldProvide = context['provide'] 31 | 32 | function require (identifier) { 33 | // modules can be required from ender's build system, or found on the window 34 | var module = modules['$' + identifier] || window[identifier] 35 | if (!module) throw new Error("Ender Error: Requested module '" + identifier + "' has not been defined.") 36 | return module 37 | } 38 | 39 | function provide (name, what) { 40 | return (modules['$' + name] = what) 41 | } 42 | 43 | context['provide'] = provide 44 | context['require'] = require 45 | 46 | function aug(o, o2) { 47 | for (var k in o2) k != 'noConflict' && k != '_VERSION' && (o[k] = o2[k]) 48 | return o 49 | } 50 | 51 | /** 52 | * main Ender return object 53 | * @constructor 54 | * @param {Array|Node|string} s a CSS selector or DOM node(s) 55 | * @param {Array.|Node} r a root node(s) 56 | */ 57 | function Ender(s, r) { 58 | var elements 59 | , i 60 | 61 | this.selector = s 62 | // string || node || nodelist || window 63 | if (typeof s == 'undefined') { 64 | elements = [] 65 | this.selector = '' 66 | } else if (typeof s == 'string' || s.nodeName || (s.length && 'item' in s) || s == window) { 67 | elements = ender._select(s, r) 68 | } else { 69 | elements = isFinite(s.length) ? s : [s] 70 | } 71 | this.length = elements.length 72 | for (i = this.length; i--;) this[i] = elements[i] 73 | } 74 | 75 | /** 76 | * @param {function(el, i, inst)} fn 77 | * @param {Object} opt_scope 78 | * @returns {Ender} 79 | */ 80 | Ender.prototype['forEach'] = function (fn, opt_scope) { 81 | var i, l 82 | // opt out of native forEach so we can intentionally call our own scope 83 | // defaulting to the current item and be able to return self 84 | for (i = 0, l = this.length; i < l; ++i) i in this && fn.call(opt_scope || this[i], this[i], i, this) 85 | // return self for chaining 86 | return this 87 | } 88 | 89 | Ender.prototype.$ = ender // handy reference to self 90 | 91 | 92 | function ender(s, r) { 93 | return new Ender(s, r) 94 | } 95 | 96 | ender['_VERSION'] = '0.4.3-dev' 97 | 98 | ender.fn = Ender.prototype // for easy compat to jQuery plugins 99 | 100 | ender.ender = function (o, chain) { 101 | aug(chain ? Ender.prototype : ender, o) 102 | } 103 | 104 | ender._select = function (s, r) { 105 | if (typeof s == 'string') return (r || document).querySelectorAll(s) 106 | if (s.nodeName) return [s] 107 | return s 108 | } 109 | 110 | 111 | // use callback to receive Ender's require & provide and remove them from global 112 | ender.noConflict = function (callback) { 113 | context['$'] = old 114 | if (callback) { 115 | context['provide'] = oldProvide 116 | context['require'] = oldRequire 117 | context['ender'] = oldEnder 118 | if (typeof callback == 'function') callback(require, provide, this) 119 | } 120 | return this 121 | } 122 | 123 | if (typeof module !== 'undefined' && module.exports) module.exports = ender 124 | // use subscript notation as extern for Closure compilation 125 | context['ender'] = context['$'] = ender 126 | 127 | }(this)); 128 | 129 | (function () { 130 | 131 | var module = { exports: {} }, exports = module.exports; 132 | 133 | /*! 134 | * @preserve Qwery - A Blazing Fast query selector engine 135 | * https://github.com/ded/qwery 136 | * copyright Dustin Diaz & Jacob Thornton 2012 137 | * MIT License 138 | */ 139 | 140 | (function (name, definition, context) { 141 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 142 | else if (typeof context['define'] == 'function' && context['define']['amd']) define(name, definition) 143 | else context[name] = definition() 144 | })('qwery', function () { 145 | var doc = document 146 | , html = doc.documentElement 147 | , byClass = 'getElementsByClassName' 148 | , byTag = 'getElementsByTagName' 149 | , qSA = 'querySelectorAll' 150 | , useNativeQSA = 'useNativeQSA' 151 | , tagName = 'tagName' 152 | , nodeType = 'nodeType' 153 | , select // main select() method, assign later 154 | 155 | , id = /#([\w\-]+)/ 156 | , clas = /\.[\w\-]+/g 157 | , idOnly = /^#([\w\-]+)$/ 158 | , classOnly = /^\.([\w\-]+)$/ 159 | , tagOnly = /^([\w\-]+)$/ 160 | , tagAndOrClass = /^([\w]+)?\.([\w\-]+)$/ 161 | , splittable = /(^|,)\s*[>~+]/ 162 | , normalizr = /^\s+|\s*([,\s\+\~>]|$)\s*/g 163 | , splitters = /[\s\>\+\~]/ 164 | , splittersMore = /(?![\s\w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^'"]*\]|[\s\w\+\-]*\))/ 165 | , specialChars = /([.*+?\^=!:${}()|\[\]\/\\])/g 166 | , simple = /^(\*|[a-z0-9]+)?(?:([\.\#]+[\w\-\.#]+)?)/ 167 | , attr = /\[([\w\-]+)(?:([\|\^\$\*\~]?\=)['"]?([ \w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^]+)["']?)?\]/ 168 | , pseudo = /:([\w\-]+)(\(['"]?([^()]+)['"]?\))?/ 169 | , easy = new RegExp(idOnly.source + '|' + tagOnly.source + '|' + classOnly.source) 170 | , dividers = new RegExp('(' + splitters.source + ')' + splittersMore.source, 'g') 171 | , tokenizr = new RegExp(splitters.source + splittersMore.source) 172 | , chunker = new RegExp(simple.source + '(' + attr.source + ')?' + '(' + pseudo.source + ')?') 173 | , walker = { 174 | ' ': function (node) { 175 | return node && node !== html && node.parentNode 176 | } 177 | , '>': function (node, contestant) { 178 | return node && node.parentNode == contestant.parentNode && node.parentNode 179 | } 180 | , '~': function (node) { 181 | return node && node.previousSibling 182 | } 183 | , '+': function (node, contestant, p1, p2) { 184 | if (!node) return false 185 | return (p1 = previous(node)) && (p2 = previous(contestant)) && p1 == p2 && p1 186 | } 187 | } 188 | 189 | function cache() { 190 | this.c = {} 191 | } 192 | cache.prototype = { 193 | g: function (k) { 194 | return this.c[k] || undefined 195 | } 196 | , s: function (k, v, r) { 197 | v = r ? new RegExp(v) : v 198 | return (this.c[k] = v) 199 | } 200 | } 201 | 202 | var classCache = new cache() 203 | , cleanCache = new cache() 204 | , attrCache = new cache() 205 | , tokenCache = new cache() 206 | 207 | function classRegex(c) { 208 | return classCache.g(c) || classCache.s(c, '(^|\\s+)' + c + '(\\s+|$)', 1) 209 | } 210 | 211 | // not quite as fast as inline loops in older browsers so don't use liberally 212 | function each(a, fn) { 213 | var i = 0, l = a.length 214 | for (; i < l; i++) fn(a[i]) 215 | } 216 | 217 | function flatten(ar) { 218 | for (var r = [], i = 0, l = ar.length; i < l; ++i) arrayLike(ar[i]) ? (r = r.concat(ar[i])) : (r[r.length] = ar[i]) 219 | return r 220 | } 221 | 222 | function arrayify(ar) { 223 | var i = 0, l = ar.length, r = [] 224 | for (; i < l; i++) r[i] = ar[i] 225 | return r 226 | } 227 | 228 | function previous(n) { 229 | while (n = n.previousSibling) if (n[nodeType] == 1) break; 230 | return n 231 | } 232 | 233 | function q(query) { 234 | return query.match(chunker) 235 | } 236 | 237 | // called using `this` as element and arguments from regex group results. 238 | // given => div.hello[title="world"]:foo('bar') 239 | // div.hello[title="world"]:foo('bar'), div, .hello, [title="world"], title, =, world, :foo('bar'), foo, ('bar'), bar] 240 | function interpret(whole, tag, idsAndClasses, wholeAttribute, attribute, qualifier, value, wholePseudo, pseudo, wholePseudoVal, pseudoVal) { 241 | var i, m, k, o, classes 242 | if (this[nodeType] !== 1) return false 243 | if (tag && tag !== '*' && this[tagName] && this[tagName].toLowerCase() !== tag) return false 244 | if (idsAndClasses && (m = idsAndClasses.match(id)) && m[1] !== this.id) return false 245 | if (idsAndClasses && (classes = idsAndClasses.match(clas))) { 246 | for (i = classes.length; i--;) if (!classRegex(classes[i].slice(1)).test(this.className)) return false 247 | } 248 | if (pseudo && qwery.pseudos[pseudo] && !qwery.pseudos[pseudo](this, pseudoVal)) return false 249 | if (wholeAttribute && !value) { // select is just for existance of attrib 250 | o = this.attributes 251 | for (k in o) { 252 | if (Object.prototype.hasOwnProperty.call(o, k) && (o[k].name || k) == attribute) { 253 | return this 254 | } 255 | } 256 | } 257 | if (wholeAttribute && !checkAttr(qualifier, getAttr(this, attribute) || '', value)) { 258 | // select is for attrib equality 259 | return false 260 | } 261 | return this 262 | } 263 | 264 | function clean(s) { 265 | return cleanCache.g(s) || cleanCache.s(s, s.replace(specialChars, '\\$1')) 266 | } 267 | 268 | function checkAttr(qualify, actual, val) { 269 | switch (qualify) { 270 | case '=': 271 | return actual == val 272 | case '^=': 273 | return actual.match(attrCache.g('^=' + val) || attrCache.s('^=' + val, '^' + clean(val), 1)) 274 | case '$=': 275 | return actual.match(attrCache.g('$=' + val) || attrCache.s('$=' + val, clean(val) + '$', 1)) 276 | case '*=': 277 | return actual.match(attrCache.g(val) || attrCache.s(val, clean(val), 1)) 278 | case '~=': 279 | return actual.match(attrCache.g('~=' + val) || attrCache.s('~=' + val, '(?:^|\\s+)' + clean(val) + '(?:\\s+|$)', 1)) 280 | case '|=': 281 | return actual.match(attrCache.g('|=' + val) || attrCache.s('|=' + val, '^' + clean(val) + '(-|$)', 1)) 282 | } 283 | return 0 284 | } 285 | 286 | // given a selector, first check for simple cases then collect all base candidate matches and filter 287 | function _qwery(selector, _root) { 288 | var r = [], ret = [], i, l, m, token, tag, els, intr, item, root = _root 289 | , tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr)) 290 | , dividedTokens = selector.match(dividers) 291 | 292 | if (!tokens.length) return r 293 | 294 | token = (tokens = tokens.slice(0)).pop() // copy cached tokens, take the last one 295 | if (tokens.length && (m = tokens[tokens.length - 1].match(idOnly))) root = byId(_root, m[1]) 296 | if (!root) return r 297 | 298 | intr = q(token) 299 | // collect base candidates to filter 300 | els = root !== _root && root[nodeType] !== 9 && dividedTokens && /^[+~]$/.test(dividedTokens[dividedTokens.length - 1]) ? 301 | function (r) { 302 | while (root = root.nextSibling) { 303 | root[nodeType] == 1 && (intr[1] ? intr[1] == root[tagName].toLowerCase() : 1) && (r[r.length] = root) 304 | } 305 | return r 306 | }([]) : 307 | root[byTag](intr[1] || '*') 308 | // filter elements according to the right-most part of the selector 309 | for (i = 0, l = els.length; i < l; i++) { 310 | if (item = interpret.apply(els[i], intr)) r[r.length] = item 311 | } 312 | if (!tokens.length) return r 313 | 314 | // filter further according to the rest of the selector (the left side) 315 | each(r, function(e) { if (ancestorMatch(e, tokens, dividedTokens)) ret[ret.length] = e }) 316 | return ret 317 | } 318 | 319 | // compare element to a selector 320 | function is(el, selector, root) { 321 | if (isNode(selector)) return el == selector 322 | if (arrayLike(selector)) return !!~flatten(selector).indexOf(el) // if selector is an array, is el a member? 323 | 324 | var selectors = selector.split(','), tokens, dividedTokens 325 | while (selector = selectors.pop()) { 326 | tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr)) 327 | dividedTokens = selector.match(dividers) 328 | tokens = tokens.slice(0) // copy array 329 | if (interpret.apply(el, q(tokens.pop())) && (!tokens.length || ancestorMatch(el, tokens, dividedTokens, root))) { 330 | return true 331 | } 332 | } 333 | return false 334 | } 335 | 336 | // given elements matching the right-most part of a selector, filter out any that don't match the rest 337 | function ancestorMatch(el, tokens, dividedTokens, root) { 338 | var cand 339 | // recursively work backwards through the tokens and up the dom, covering all options 340 | function crawl(e, i, p) { 341 | while (p = walker[dividedTokens[i]](p, e)) { 342 | if (isNode(p) && (interpret.apply(p, q(tokens[i])))) { 343 | if (i) { 344 | if (cand = crawl(p, i - 1, p)) return cand 345 | } else return p 346 | } 347 | } 348 | } 349 | return (cand = crawl(el, tokens.length - 1, el)) && (!root || isAncestor(cand, root)) 350 | } 351 | 352 | function isNode(el, t) { 353 | return el && typeof el === 'object' && (t = el[nodeType]) && (t == 1 || t == 9) 354 | } 355 | 356 | function uniq(ar) { 357 | var a = [], i, j 358 | o: for (i = 0; i < ar.length; ++i) { 359 | for (j = 0; j < a.length; ++j) if (a[j] == ar[i]) continue o 360 | a[a.length] = ar[i] 361 | } 362 | return a 363 | } 364 | 365 | function arrayLike(o) { 366 | return (typeof o === 'object' && isFinite(o.length)) 367 | } 368 | 369 | function normalizeRoot(root) { 370 | if (!root) return doc 371 | if (typeof root == 'string') return qwery(root)[0] 372 | if (!root[nodeType] && arrayLike(root)) return root[0] 373 | return root 374 | } 375 | 376 | function byId(root, id, el) { 377 | // if doc, query on it, else query the parent doc or if a detached fragment rewrite the query and run on the fragment 378 | return root[nodeType] === 9 ? root.getElementById(id) : 379 | root.ownerDocument && 380 | (((el = root.ownerDocument.getElementById(id)) && isAncestor(el, root) && el) || 381 | (!isAncestor(root, root.ownerDocument) && select('[id="' + id + '"]', root)[0])) 382 | } 383 | 384 | function qwery(selector, _root) { 385 | var m, el, root = normalizeRoot(_root) 386 | 387 | // easy, fast cases that we can dispatch with simple DOM calls 388 | if (!root || !selector) return [] 389 | if (selector === window || isNode(selector)) { 390 | return !_root || (selector !== window && isNode(root) && isAncestor(selector, root)) ? [selector] : [] 391 | } 392 | if (selector && arrayLike(selector)) return flatten(selector) 393 | if (m = selector.match(easy)) { 394 | if (m[1]) return (el = byId(root, m[1])) ? [el] : [] 395 | if (m[2]) return arrayify(root[byTag](m[2])) 396 | if (hasByClass && m[3]) return arrayify(root[byClass](m[3])) 397 | } 398 | 399 | return select(selector, root) 400 | } 401 | 402 | // where the root is not document and a relationship selector is first we have to 403 | // do some awkward adjustments to get it to work, even with qSA 404 | function collectSelector(root, collector) { 405 | return function(s) { 406 | var oid, nid 407 | if (splittable.test(s)) { 408 | if (root[nodeType] !== 9) { 409 | // make sure the el has an id, rewrite the query, set root to doc and run it 410 | if (!(nid = oid = root.getAttribute('id'))) root.setAttribute('id', nid = '__qwerymeupscotty') 411 | s = '[id="' + nid + '"]' + s // avoid byId and allow us to match context element 412 | collector(root.parentNode || root, s, true) 413 | oid || root.removeAttribute('id') 414 | } 415 | return; 416 | } 417 | s.length && collector(root, s, false) 418 | } 419 | } 420 | 421 | var isAncestor = 'compareDocumentPosition' in html ? 422 | function (element, container) { 423 | return (container.compareDocumentPosition(element) & 16) == 16 424 | } : 'contains' in html ? 425 | function (element, container) { 426 | container = container[nodeType] === 9 || container == window ? html : container 427 | return container !== element && container.contains(element) 428 | } : 429 | function (element, container) { 430 | while (element = element.parentNode) if (element === container) return 1 431 | return 0 432 | } 433 | , getAttr = function() { 434 | // detect buggy IE src/href getAttribute() call 435 | var e = doc.createElement('p') 436 | return ((e.innerHTML = 'x') && e.firstChild.getAttribute('href') != '#x') ? 437 | function(e, a) { 438 | return a === 'class' ? e.className : (a === 'href' || a === 'src') ? 439 | e.getAttribute(a, 2) : e.getAttribute(a) 440 | } : 441 | function(e, a) { return e.getAttribute(a) } 442 | }() 443 | , hasByClass = !!doc[byClass] 444 | // has native qSA support 445 | , hasQSA = doc.querySelector && doc[qSA] 446 | // use native qSA 447 | , selectQSA = function (selector, root) { 448 | var result = [], ss, e 449 | try { 450 | if (root[nodeType] === 9 || !splittable.test(selector)) { 451 | // most work is done right here, defer to qSA 452 | return arrayify(root[qSA](selector)) 453 | } 454 | // special case where we need the services of `collectSelector()` 455 | each(ss = selector.split(','), collectSelector(root, function(ctx, s) { 456 | e = ctx[qSA](s) 457 | if (e.length == 1) result[result.length] = e.item(0) 458 | else if (e.length) result = result.concat(arrayify(e)) 459 | })) 460 | return ss.length > 1 && result.length > 1 ? uniq(result) : result 461 | } catch(ex) { } 462 | return selectNonNative(selector, root) 463 | } 464 | // no native selector support 465 | , selectNonNative = function (selector, root) { 466 | var result = [], items, m, i, l, r, ss 467 | selector = selector.replace(normalizr, '$1') 468 | if (m = selector.match(tagAndOrClass)) { 469 | r = classRegex(m[2]) 470 | items = root[byTag](m[1] || '*') 471 | for (i = 0, l = items.length; i < l; i++) { 472 | if (r.test(items[i].className)) result[result.length] = items[i] 473 | } 474 | return result 475 | } 476 | // more complex selector, get `_qwery()` to do the work for us 477 | each(ss = selector.split(','), collectSelector(root, function(ctx, s, rewrite) { 478 | r = _qwery(s, ctx) 479 | for (i = 0, l = r.length; i < l; i++) { 480 | if (ctx[nodeType] === 9 || rewrite || isAncestor(r[i], root)) result[result.length] = r[i] 481 | } 482 | })) 483 | return ss.length > 1 && result.length > 1 ? uniq(result) : result 484 | } 485 | , configure = function (options) { 486 | // configNativeQSA: use fully-internal selector or native qSA where present 487 | if (typeof options[useNativeQSA] !== 'undefined') 488 | select = !options[useNativeQSA] ? selectNonNative : hasQSA ? selectQSA : selectNonNative 489 | } 490 | 491 | configure({ useNativeQSA: true }) 492 | 493 | qwery.configure = configure 494 | qwery.uniq = uniq 495 | qwery.is = is 496 | qwery.pseudos = {} 497 | 498 | return qwery 499 | }, this); 500 | 501 | provide("qwery", module.exports); 502 | 503 | (function ($) { 504 | var q = function () { 505 | var r 506 | try { 507 | r = require('qwery') 508 | } catch (ex) { 509 | r = require('qwery-mobile') 510 | } finally { 511 | return r 512 | } 513 | }() 514 | 515 | $.pseudos = q.pseudos 516 | 517 | $._select = function (s, r) { 518 | // detect if sibling module 'bonzo' is available at run-time 519 | // rather than load-time since technically it's not a dependency and 520 | // can be loaded in any order 521 | // hence the lazy function re-definition 522 | return ($._select = (function () { 523 | var b 524 | if (typeof $.create == 'function') return function (s, r) { 525 | return /^\s* 0) { 1009 | // remove(el, 't1 t2 t3', fn) or remove(el, 't1 t2 t3') 1010 | typeSpec = typeSpec.split(' ') 1011 | for (i = typeSpec.length; i--;) 1012 | remove(element, typeSpec[i], fn) 1013 | return element 1014 | } 1015 | type = isString && typeSpec.replace(nameRegex, '') 1016 | if (type && customEvents[type]) 1017 | type = customEvents[type].type 1018 | if (!typeSpec || isString) { 1019 | // remove(el) or remove(el, t1.ns) or remove(el, .ns) or remove(el, .ns1.ns2.ns3) 1020 | if (namespaces = isString && typeSpec.replace(namespaceRegex, '')) 1021 | namespaces = namespaces.split('.') 1022 | rm(element, type, fn, namespaces) 1023 | } else if (typeof typeSpec === 'function') { 1024 | // remove(el, fn) 1025 | rm(element, null, typeSpec) 1026 | } else { 1027 | // remove(el, { t1: fn1, t2, fn2 }) 1028 | for (k in typeSpec) { 1029 | if (typeSpec.hasOwnProperty(k)) 1030 | remove(element, k, typeSpec[k]) 1031 | } 1032 | } 1033 | return element 1034 | } 1035 | 1036 | // 5th argument, $=selector engine, is deprecated and will be removed 1037 | , add = function (element, events, fn, delfn, $) { 1038 | var type, types, i, args 1039 | , originalFn = fn 1040 | , isDel = fn && typeof fn === 'string' 1041 | 1042 | if (events && !fn && typeof events === 'object') { 1043 | for (type in events) { 1044 | if (events.hasOwnProperty(type)) 1045 | add.apply(this, [ element, type, events[type] ]) 1046 | } 1047 | } else { 1048 | args = arguments.length > 3 ? slice.call(arguments, 3) : [] 1049 | types = (isDel ? fn : events).split(' ') 1050 | isDel && (fn = del(events, (originalFn = delfn), $ || selectorEngine)) && (args = slice.call(args, 1)) 1051 | // special case for one() 1052 | this === ONE && (fn = once(remove, element, events, fn, originalFn)) 1053 | for (i = types.length; i--;) addListener(element, types[i], fn, originalFn, args) 1054 | } 1055 | return element 1056 | } 1057 | 1058 | , one = function () { 1059 | return add.apply(ONE, arguments) 1060 | } 1061 | 1062 | , fireListener = W3C_MODEL ? function (isNative, type, element) { 1063 | var evt = doc.createEvent(isNative ? 'HTMLEvents' : 'UIEvents') 1064 | evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1) 1065 | element.dispatchEvent(evt) 1066 | } : function (isNative, type, element) { 1067 | element = targetElement(element, isNative) 1068 | // if not-native then we're using onpropertychange so we just increment a custom property 1069 | isNative ? element.fireEvent('on' + type, doc.createEventObject()) : element['_on' + type]++ 1070 | } 1071 | 1072 | , fire = function (element, type, args) { 1073 | var i, j, l, names, handlers 1074 | , types = type.split(' ') 1075 | 1076 | for (i = types.length; i--;) { 1077 | type = types[i].replace(nameRegex, '') 1078 | if (names = types[i].replace(namespaceRegex, '')) 1079 | names = names.split('.') 1080 | if (!names && !args && element[eventSupport]) { 1081 | fireListener(nativeEvents[type], type, element) 1082 | } else { 1083 | // non-native event, either because of a namespace, arguments or a non DOM element 1084 | // iterate over all listeners and manually 'fire' 1085 | handlers = registry.get(element, type) 1086 | args = [false].concat(args) 1087 | for (j = 0, l = handlers.length; j < l; j++) { 1088 | if (handlers[j].inNamespaces(names)) 1089 | handlers[j].handler.apply(element, args) 1090 | } 1091 | } 1092 | } 1093 | return element 1094 | } 1095 | 1096 | , clone = function (element, from, type) { 1097 | var i = 0 1098 | , handlers = registry.get(from, type) 1099 | , l = handlers.length 1100 | , args, beanDel 1101 | 1102 | for (;i < l; i++) { 1103 | if (handlers[i].original) { 1104 | beanDel = handlers[i].handler.__beanDel 1105 | if (beanDel) { 1106 | args = [ element, beanDel.selector, handlers[i].type, handlers[i].original, beanDel.$] 1107 | } else 1108 | args = [ element, handlers[i].type, handlers[i].original ] 1109 | add.apply(null, args) 1110 | } 1111 | } 1112 | return element 1113 | } 1114 | 1115 | , bean = { 1116 | add: add 1117 | , one: one 1118 | , remove: remove 1119 | , clone: clone 1120 | , fire: fire 1121 | , setSelectorEngine: setSelectorEngine 1122 | , noConflict: function () { 1123 | context[name] = old 1124 | return this 1125 | } 1126 | } 1127 | 1128 | if (win[attachEvent]) { 1129 | // for IE, clean up on unload to avoid leaks 1130 | var cleanup = function () { 1131 | var i, entries = registry.entries() 1132 | for (i in entries) { 1133 | if (entries[i].type && entries[i].type !== 'unload') 1134 | remove(entries[i].element, entries[i].type) 1135 | } 1136 | win[detachEvent]('onunload', cleanup) 1137 | win.CollectGarbage && win.CollectGarbage() 1138 | } 1139 | win[attachEvent]('onunload', cleanup) 1140 | } 1141 | 1142 | return bean 1143 | }) 1144 | 1145 | provide("bean", module.exports); 1146 | 1147 | !function ($) { 1148 | var b = require('bean') 1149 | , integrate = function (method, type, method2) { 1150 | var _args = type ? [type] : [] 1151 | return function () { 1152 | for (var i = 0, l = this.length; i < l; i++) { 1153 | if (!arguments.length && method == 'add' && type) method = 'fire' 1154 | b[method].apply(this, [this[i]].concat(_args, Array.prototype.slice.call(arguments, 0))) 1155 | } 1156 | return this 1157 | } 1158 | } 1159 | , add = integrate('add') 1160 | , remove = integrate('remove') 1161 | , fire = integrate('fire') 1162 | 1163 | , methods = { 1164 | on: add // NOTE: .on() is likely to change in the near future, don't rely on this as-is see https://github.com/fat/bean/issues/55 1165 | , addListener: add 1166 | , bind: add 1167 | , listen: add 1168 | , delegate: add 1169 | 1170 | , one: integrate('one') 1171 | 1172 | , off: remove 1173 | , unbind: remove 1174 | , unlisten: remove 1175 | , removeListener: remove 1176 | , undelegate: remove 1177 | 1178 | , emit: fire 1179 | , trigger: fire 1180 | 1181 | , cloneEvents: integrate('clone') 1182 | 1183 | , hover: function (enter, leave, i) { // i for internal 1184 | for (i = this.length; i--;) { 1185 | b.add.call(this, this[i], 'mouseenter', enter) 1186 | b.add.call(this, this[i], 'mouseleave', leave) 1187 | } 1188 | return this 1189 | } 1190 | } 1191 | 1192 | , shortcuts = 1193 | ('blur change click dblclick error focus focusin focusout keydown keypress ' 1194 | + 'keyup load mousedown mouseenter mouseleave mouseout mouseover mouseup ' 1195 | + 'mousemove resize scroll select submit unload').split(' ') 1196 | 1197 | for (var i = shortcuts.length; i--;) { 1198 | methods[shortcuts[i]] = integrate('add', shortcuts[i]) 1199 | } 1200 | 1201 | b.setSelectorEngine($) 1202 | 1203 | $.ender(methods, true) 1204 | }(ender) 1205 | 1206 | }()); --------------------------------------------------------------------------------