hoarder-js 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/CHANGELOG +2 -0
  2. data/Gemfile.lock +2 -2
  3. data/Manifest +14 -3
  4. data/assets/scripts/coffee/hoarder/form/form.coffee +47 -44
  5. data/assets/scripts/coffee/hoarder/form/form_serializer.coffee +23 -0
  6. data/assets/scripts/coffee/hoarder/form_manager.coffee +35 -19
  7. data/assets/scripts/coffee/hoarder/submitter/form_submitter.coffee +10 -15
  8. data/assets/scripts/coffee/hoarder/submitter/submitters/base_submitter.coffee +11 -0
  9. data/assets/scripts/coffee/hoarder/submitter/submitters/polling_submitter.coffee +30 -27
  10. data/assets/scripts/coffee/hoarder/submitter/submitters/simple_submitter.coffee +11 -21
  11. data/assets/scripts/coffee/hoarder/validator/constraints/alpha_constraint.coffee +8 -14
  12. data/assets/scripts/coffee/hoarder/validator/constraints/alphanumeric_constraint.coffee +8 -14
  13. data/assets/scripts/coffee/hoarder/validator/constraints/base_constraint.coffee +10 -0
  14. data/assets/scripts/coffee/hoarder/validator/constraints/credit_card_constraint.coffee +8 -14
  15. data/assets/scripts/coffee/hoarder/validator/constraints/email_constraint.coffee +8 -14
  16. data/assets/scripts/coffee/hoarder/validator/constraints/max_length_constraint.coffee +8 -14
  17. data/assets/scripts/coffee/hoarder/validator/constraints/min_length_constraint.coffee +8 -14
  18. data/assets/scripts/coffee/hoarder/validator/constraints/numeric_constraint.coffee +8 -14
  19. data/assets/scripts/coffee/hoarder/validator/constraints/phone_constraint.coffee +8 -14
  20. data/assets/scripts/coffee/hoarder/validator/constraints/required_constraint.coffee +8 -14
  21. data/assets/scripts/coffee/hoarder/validator/form_validator.coffee +30 -25
  22. data/assets/scripts/coffee/hoarder/validator/rules/validation_rule.coffee +10 -0
  23. data/assets/scripts/js/lib/bonzo.js +1151 -0
  24. data/assets/scripts/js/lib/qwery.js +369 -0
  25. data/assets/scripts/js/lib/reqwest.js +565 -0
  26. data/assets/scripts/js/patches/event_listeners.js +73 -0
  27. data/bin/hoarder.js +2807 -369
  28. data/hoarder-js.gemspec +3 -3
  29. data/spec/jasmine.yml +3 -0
  30. data/spec/runner.html +44 -34
  31. data/spec/support/fixtures.coffee +19 -0
  32. data/spec/support/lib/jasmine-fixture.js +506 -0
  33. data/spec/support/lib/jquery-1.7.1.min.js +4 -0
  34. data/spec/support/mocks.coffee +12 -37
  35. data/spec/support/requirements.coffee +0 -1
  36. data/spec/tests/form/form_serializer_spec.coffee +78 -0
  37. data/spec/tests/form/form_spec.coffee +127 -0
  38. data/spec/tests/form_manager_spec.coffee +128 -40
  39. data/spec/tests/submitter/form_submitter_spec.coffee +59 -42
  40. data/spec/tests/submitter/submitters/polling_submitter_spec.coffee +102 -57
  41. data/spec/tests/submitter/submitters/simple_submitter_spec.coffee +42 -16
  42. data/spec/tests/validator/constraints_spec.coffee +65 -57
  43. data/spec/tests/validator/form_validator_spec.coffee +39 -23
  44. data/spec/tests/validator/validation_rule_spec.coffee +14 -0
  45. metadata +16 -5
  46. data/assets/scripts/coffee/hoarder/form/form_element.coffee +0 -22
  47. data/assets/scripts/coffee/hoarder/validator/error/validation_error.coffee +0 -9
  48. data/assets/scripts/js/lib/jquery.js +0 -5
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require 'hoarder/validator/constraints/base_constraint'
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class AlphanumericConstraint
9
- canHandle: (type)->
10
- type is "alphanumeric"
3
+ class AlphanumericConstraint extends BaseConstraint
11
4
 
12
- handle: (element)->
13
- if element.value.match(/^[A-Za-z0-9\s]*$/)
14
- return []
15
- else
16
- return [ new ValidationError "This field only accepts numbers and characters (0-9, A-Z, a-z)." ]
5
+ constructor: ->
6
+ @type = "alphanumeric"
7
+
8
+ rulePasses: (element)-> element.value.match(/^[A-Za-z0-9\s]*$/)
9
+
10
+ errorMessage: -> "This field only accepts numbers and characters (0-9, A-Z, a-z)."
17
11
 
18
12
  return AlphanumericConstraint
@@ -0,0 +1,10 @@
1
+ class BaseConstraint
2
+
3
+ constructor: ->
4
+ @type = null
5
+
6
+ canHandle: (rule)-> rule.type is @type
7
+
8
+ handle: (element, rule)-> element.setCustomValidity @errorMessage(rule, element) unless @rulePasses element, rule
9
+
10
+ return BaseConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require 'hoarder/validator/constraints/base_constraint'
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class CreditCardConstraint
9
- canHandle: (type)->
10
- type is "creditCard"
3
+ class CreditCardConstraint extends BaseConstraint
11
4
 
12
- handle: (element)->
13
- if element.value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)
14
- return []
15
- else
16
- return [ new ValidationError "Please enter a valid credit card number." ]
5
+ constructor: ->
6
+ @type = "creditCard"
7
+
8
+ rulePasses: (element)-> element.value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)
9
+
10
+ errorMessage: -> "Please enter a valid credit card number."
17
11
 
18
12
  return CreditCardConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require 'hoarder/validator/constraints/base_constraint'
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class EmailConstraint
9
- canHandle: (type)->
10
- type is "email"
3
+ class EmailConstraint extends BaseConstraint
4
+
5
+ constructor: ->
6
+ @type = "email"
11
7
 
12
- handle: (element)->
13
- if element.value.match(/^([a-zA-Z0-9_-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,4})$/i)
14
- return []
15
- else
16
- return [ new ValidationError "Please enter a valid email address." ]
8
+ rulePasses: (element)-> element.value.match(/^([a-zA-Z0-9_-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,4})$/i)
9
+
10
+ errorMessage: -> "Please enter a valid email address."
17
11
 
18
12
  return EmailConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require "hoarder/validator/constraints/base_constraint"
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class MaxLengthConstraint
9
- canHandle: (type)->
10
- type is "maxLength"
3
+ class MaxLengthConstraint extends BaseConstraint
4
+
5
+ constructor: ->
6
+ @type = "maxLength"
11
7
 
12
- handle: (element, context)->
13
- if element.value.length <= context.value
14
- return []
15
- else
16
- return [ new ValidationError "The maximum length of this field is #{context.value}." ]
8
+ rulePasses: (element, rule)-> element.value.length <= rule.context
9
+
10
+ errorMessage: (rule)-> "The maximum length of this field is #{rule.context}."
17
11
 
18
12
  return MaxLengthConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require "hoarder/validator/constraints/base_constraint"
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class MinLengthConstraint
9
- canHandle: (type)->
10
- type is "minLength"
3
+ class MinLengthConstraint extends BaseConstraint
4
+
5
+ constructor: ->
6
+ @type = "minLength"
11
7
 
12
- handle: (element, context)->
13
- if element.value.length >= context.value
14
- return []
15
- else
16
- return [ new ValidationError "The minimum length of this field is #{context.value}." ]
8
+ rulePasses: (element, rule)-> element.value.length >= rule.context
9
+
10
+ errorMessage: (rule)-> "The minimum length of this field is #{rule.context}."
17
11
 
18
12
  return MinLengthConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require "hoarder/validator/constraints/base_constraint"
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class NumericConstraint
9
- canHandle: (type)->
10
- type is "numeric"
3
+ class NumericConstraint extends BaseConstraint
11
4
 
12
- handle: (element)->
13
- if element.value.match(/^[0-9]*$/)
14
- return []
15
- else
16
- return [ new ValidationError "This field only accepts numbers (0-9)." ]
5
+ constructor: ->
6
+ @type = "numeric"
7
+
8
+ rulePasses: (element)-> element.value.match(/^[0-9]*$/)
9
+
10
+ errorMessage: -> "This field only accepts numbers (0-9)."
17
11
 
18
12
  return NumericConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require 'hoarder/validator/constraints/base_constraint'
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class PhoneConstraint
9
- canHandle: (type)->
10
- type is "phone"
3
+ class PhoneConstraint extends BaseConstraint
11
4
 
12
- handle: (element)->
13
- if element.value.match(/^\d?[.(\-]?\d\d\d[.)\-]?\d\d\d[.\-]?\d\d\d\d$/)
14
- return []
15
- else
16
- return [ new ValidationError "Please enter a valid phone number." ]
5
+ constructor: ->
6
+ @type = "phone"
7
+
8
+ rulePasses: (element)-> element.value.match(/^\d?[.(\-]?\d\d\d[.)\-]?\d\d\d[.\-]?\d\d\d\d$/)
9
+
10
+ errorMessage: -> "Please enter a valid phone number."
17
11
 
18
12
  return PhoneConstraint
@@ -1,18 +1,12 @@
1
- ValidationError = require "hoarder/validator/error/validation_error"
1
+ BaseConstraint = require 'hoarder/validator/constraints/base_constraint'
2
2
 
3
- #
4
- # @author - Tim Shelburne <tim@musiconelive.com>
5
- #
6
- #
7
- #
8
- class RequiredConstraint
9
- canHandle: (type)->
10
- type is "required"
3
+ class RequiredConstraint extends BaseConstraint
11
4
 
12
- handle: (element)->
13
- if element.value? and element.value isnt ""
14
- return []
15
- else
16
- return [ new ValidationError "This field is required." ]
5
+ constructor: ->
6
+ @type = "required"
7
+
8
+ rulePasses: (element)-> element.value? and element.value isnt ""
9
+
10
+ errorMessage: -> "This field is required."
17
11
 
18
12
  return RequiredConstraint
@@ -1,4 +1,6 @@
1
- #AlphaConstraint = require "/hoarder/validator/constraints/alpha_constraint"
1
+ ValidationRule = require 'hoarder/validator/rules/validation_rule'
2
+
3
+ AlphaConstraint = require "hoarder/validator/constraints/alpha_constraint"
2
4
  AlphanumericConstraint = require "hoarder/validator/constraints/alphanumeric_constraint"
3
5
  CreditCardConstraint = require "hoarder/validator/constraints/credit_card_constraint"
4
6
  EmailConstraint = require "hoarder/validator/constraints/email_constraint"
@@ -8,16 +10,10 @@ NumericConstraint = require "hoarder/validator/constraints/numeric_constraint"
8
10
  PhoneConstraint = require "hoarder/validator/constraints/phone_constraint"
9
11
  RequiredConstraint = require "hoarder/validator/constraints/required_constraint"
10
12
 
11
- #
12
- # @author - Tim Shelburne <tim@musiconelive.com>
13
- #
14
- # a class to validate forms and form elements
15
- #
16
13
  class FormValidator
17
- constructor: (@constraints)->
18
-
19
- @default: ->
20
- new @([
14
+
15
+ @libraryConstraints = [
16
+ new AlphaConstraint()
21
17
  new AlphanumericConstraint()
22
18
  new CreditCardConstraint()
23
19
  new EmailConstraint()
@@ -26,28 +22,37 @@ class FormValidator
26
22
  new NumericConstraint()
27
23
  new PhoneConstraint()
28
24
  new RequiredConstraint()
29
- ])
25
+ ]
26
+
27
+ @create: -> new @(FormValidator.libraryConstraints)
28
+
29
+ constructor: (@constraints)->
30
30
 
31
31
  validateForm: (form)->
32
- errors = []
32
+ isValid = true
33
33
  for element in form.elements()
34
- errors = errors.concat @validateElement(element)
35
- errors
34
+ isValid = false unless @validateElement(element)
35
+ isValid
36
36
 
37
37
  validateElement: (element)->
38
- for rule in element.validationRules
39
- ruleParts = rule.split('=')
40
- type = ruleParts[0]
41
- context = {}
42
- context.value = ruleParts[1] if ruleParts.length > 1
38
+ for ruleString in validationStringsFrom element
39
+ rule = ValidationRule.fromString ruleString
43
40
 
44
41
  for constraint in @constraints
45
- if constraint.canHandle type
46
- errors = constraint.handle element, context
47
- if errors.length > 0
48
- element.addError(errors[0])
49
- return errors
42
+ constraint.handle(element, rule) if constraint.canHandle rule
43
+ break unless isValid element
44
+
45
+ break unless isValid element
46
+
47
+ isValid element
48
+
49
+ # private
50
+
51
+ validationStringsFrom = (element)->
52
+ validationAttribute = element.getAttribute("data-validation")
53
+ return [] unless validationAttribute?
54
+ (ruleString.trim() for ruleString in validationAttribute.split(','))
50
55
 
51
- return []
56
+ isValid = (element)-> not element.validity.customError
52
57
 
53
58
  return FormValidator
@@ -0,0 +1,10 @@
1
+ class ValidationRule
2
+
3
+ constructor: (@type, @context=null)->
4
+
5
+ @fromString: (ruleString)->
6
+ ruleParts = ruleString.split('=')
7
+ context = if ruleParts[1]? then ruleParts[1] else null
8
+ new @(ruleParts[0], context)
9
+
10
+ return ValidationRule
@@ -0,0 +1,1151 @@
1
+ /*!
2
+ * Bonzo: DOM Utility (c) Dustin Diaz 2012
3
+ * https://github.com/ded/bonzo
4
+ * License MIT
5
+ */
6
+ (function (name, context, definition) {
7
+ if (typeof module != 'undefined' && module.exports) module.exports = definition()
8
+ else if (typeof define == 'function' && define.amd) define(definition)
9
+ else context[name] = definition()
10
+ })('bonzo', this, function() {
11
+ var win = window
12
+ , doc = win.document
13
+ , html = doc.documentElement
14
+ , parentNode = 'parentNode'
15
+ , specialAttributes = /^(checked|value|selected|disabled)$/i
16
+ // tags that we have trouble inserting *into*
17
+ , specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i
18
+ , simpleScriptTagRe = /\s*<script +src=['"]([^'"]+)['"]>/
19
+ , table = ['<table>', '</table>', 1]
20
+ , td = ['<table><tbody><tr>', '</tr></tbody></table>', 3]
21
+ , option = ['<select>', '</select>', 1]
22
+ , noscope = ['_', '', 0, 1]
23
+ , tagMap = { // tags that we have trouble *inserting*
24
+ thead: table, tbody: table, tfoot: table, colgroup: table, caption: table
25
+ , tr: ['<table><tbody>', '</tbody></table>', 2]
26
+ , th: td , td: td
27
+ , col: ['<table><colgroup>', '</colgroup></table>', 2]
28
+ , fieldset: ['<form>', '</form>', 1]
29
+ , legend: ['<form><fieldset>', '</fieldset></form>', 2]
30
+ , option: option, optgroup: option
31
+ , script: noscope, style: noscope, link: noscope, param: noscope, base: noscope
32
+ }
33
+ , stateAttributes = /^(checked|selected|disabled)$/
34
+ , ie = /msie/i.test(navigator.userAgent)
35
+ , hasClass, addClass, removeClass
36
+ , uidMap = {}
37
+ , uuids = 0
38
+ , digit = /^-?[\d\.]+$/
39
+ , dattr = /^data-(.+)$/
40
+ , px = 'px'
41
+ , setAttribute = 'setAttribute'
42
+ , getAttribute = 'getAttribute'
43
+ , byTag = 'getElementsByTagName'
44
+ , features = function() {
45
+ var e = doc.createElement('p')
46
+ e.innerHTML = '<a href="#x">x</a><table style="float:left;"></table>'
47
+ return {
48
+ hrefExtended: e[byTag]('a')[0][getAttribute]('href') != '#x' // IE < 8
49
+ , autoTbody: e[byTag]('tbody').length !== 0 // IE < 8
50
+ , computedStyle: doc.defaultView && doc.defaultView.getComputedStyle
51
+ , cssFloat: e[byTag]('table')[0].style.styleFloat ? 'styleFloat' : 'cssFloat'
52
+ , transform: function () {
53
+ var props = ['transform', 'webkitTransform', 'MozTransform', 'OTransform', 'msTransform'], i
54
+ for (i = 0; i < props.length; i++) {
55
+ if (props[i] in e.style) return props[i]
56
+ }
57
+ }()
58
+ , classList: 'classList' in e
59
+ , opasity: function () {
60
+ return typeof doc.createElement('a').style.opacity !== 'undefined'
61
+ }()
62
+ }
63
+ }()
64
+ , trimReplace = /(^\s*|\s*$)/g
65
+ , whitespaceRegex = /\s+/
66
+ , toString = String.prototype.toString
67
+ , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, boxFlex: 1, WebkitBoxFlex: 1, MozBoxFlex: 1 }
68
+ , query = doc.querySelectorAll && function (selector) { return doc.querySelectorAll(selector) }
69
+ , trim = String.prototype.trim ?
70
+ function (s) {
71
+ return s.trim()
72
+ } :
73
+ function (s) {
74
+ return s.replace(trimReplace, '')
75
+ }
76
+
77
+ , getStyle = features.computedStyle
78
+ ? function (el, property) {
79
+ var value = null
80
+ , computed = doc.defaultView.getComputedStyle(el, '')
81
+ computed && (value = computed[property])
82
+ return el.style[property] || value
83
+ }
84
+ : !(ie && html.currentStyle)
85
+ ? function (el, property) {
86
+ return el.style[property]
87
+ }
88
+ :
89
+ /**
90
+ * @param {Element} el
91
+ * @param {string} property
92
+ * @return {string|number}
93
+ */
94
+ function (el, property) {
95
+ var val, value
96
+ if (property == 'opacity' && !features.opasity) {
97
+ val = 100
98
+ try {
99
+ val = el['filters']['DXImageTransform.Microsoft.Alpha'].opacity
100
+ } catch (e1) {
101
+ try {
102
+ val = el['filters']('alpha').opacity
103
+ } catch (e2) {}
104
+ }
105
+ return val / 100
106
+ }
107
+ value = el.currentStyle ? el.currentStyle[property] : null
108
+ return el.style[property] || value
109
+ }
110
+
111
+ function isNode(node) {
112
+ return node && node.nodeName && (node.nodeType == 1 || node.nodeType == 11)
113
+ }
114
+
115
+
116
+ function normalize(node, host, clone) {
117
+ var i, l, ret
118
+ if (typeof node == 'string') return bonzo.create(node)
119
+ if (isNode(node)) node = [ node ]
120
+ if (clone) {
121
+ ret = [] // don't change original array
122
+ for (i = 0, l = node.length; i < l; i++) ret[i] = cloneNode(host, node[i])
123
+ return ret
124
+ }
125
+ return node
126
+ }
127
+
128
+ /**
129
+ * @param {string} c a class name to test
130
+ * @return {boolean}
131
+ */
132
+ function classReg(c) {
133
+ return new RegExp('(^|\\s+)' + c + '(\\s+|$)')
134
+ }
135
+
136
+
137
+ /**
138
+ * @param {Bonzo|Array} ar
139
+ * @param {function(Object, number, (Bonzo|Array))} fn
140
+ * @param {Object=} opt_scope
141
+ * @param {boolean=} opt_rev
142
+ * @return {Bonzo|Array}
143
+ */
144
+ function each(ar, fn, opt_scope, opt_rev) {
145
+ var ind, i = 0, l = ar.length
146
+ for (; i < l; i++) {
147
+ ind = opt_rev ? ar.length - i - 1 : i
148
+ fn.call(opt_scope || ar[ind], ar[ind], ind, ar)
149
+ }
150
+ return ar
151
+ }
152
+
153
+
154
+ /**
155
+ * @param {Bonzo|Array} ar
156
+ * @param {function(Object, number, (Bonzo|Array))} fn
157
+ * @param {Object=} opt_scope
158
+ * @return {Bonzo|Array}
159
+ */
160
+ function deepEach(ar, fn, opt_scope) {
161
+ for (var i = 0, l = ar.length; i < l; i++) {
162
+ if (isNode(ar[i])) {
163
+ deepEach(ar[i].childNodes, fn, opt_scope)
164
+ fn.call(opt_scope || ar[i], ar[i], i, ar)
165
+ }
166
+ }
167
+ return ar
168
+ }
169
+
170
+
171
+ /**
172
+ * @param {string} s
173
+ * @return {string}
174
+ */
175
+ function camelize(s) {
176
+ return s.replace(/-(.)/g, function (m, m1) {
177
+ return m1.toUpperCase()
178
+ })
179
+ }
180
+
181
+
182
+ /**
183
+ * @param {string} s
184
+ * @return {string}
185
+ */
186
+ function decamelize(s) {
187
+ return s ? s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : s
188
+ }
189
+
190
+
191
+ /**
192
+ * @param {Element} el
193
+ * @return {*}
194
+ */
195
+ function data(el) {
196
+ el[getAttribute]('data-node-uid') || el[setAttribute]('data-node-uid', ++uuids)
197
+ var uid = el[getAttribute]('data-node-uid')
198
+ return uidMap[uid] || (uidMap[uid] = {})
199
+ }
200
+
201
+
202
+ /**
203
+ * removes the data associated with an element
204
+ * @param {Element} el
205
+ */
206
+ function clearData(el) {
207
+ var uid = el[getAttribute]('data-node-uid')
208
+ if (uid) delete uidMap[uid]
209
+ }
210
+
211
+
212
+ function dataValue(d) {
213
+ var f
214
+ try {
215
+ return (d === null || d === undefined) ? undefined :
216
+ d === 'true' ? true :
217
+ d === 'false' ? false :
218
+ d === 'null' ? null :
219
+ (f = parseFloat(d)) == d ? f : d;
220
+ } catch(e) {}
221
+ return undefined
222
+ }
223
+
224
+
225
+ /**
226
+ * @param {Bonzo|Array} ar
227
+ * @param {function(Object, number, (Bonzo|Array))} fn
228
+ * @param {Object=} opt_scope
229
+ * @return {boolean} whether `some`thing was found
230
+ */
231
+ function some(ar, fn, opt_scope) {
232
+ for (var i = 0, j = ar.length; i < j; ++i) if (fn.call(opt_scope || null, ar[i], i, ar)) return true
233
+ return false
234
+ }
235
+
236
+
237
+ /**
238
+ * this could be a giant enum of CSS properties
239
+ * but in favor of file size sans-closure deadcode optimizations
240
+ * we're just asking for any ol string
241
+ * then it gets transformed into the appropriate style property for JS access
242
+ * @param {string} p
243
+ * @return {string}
244
+ */
245
+ function styleProperty(p) {
246
+ (p == 'transform' && (p = features.transform)) ||
247
+ (/^transform-?[Oo]rigin$/.test(p) && (p = features.transform + 'Origin')) ||
248
+ (p == 'float' && (p = features.cssFloat))
249
+ return p ? camelize(p) : null
250
+ }
251
+
252
+ // this insert method is intense
253
+ function insert(target, host, fn, rev) {
254
+ var i = 0, self = host || this, r = []
255
+ // target nodes could be a css selector if it's a string and a selector engine is present
256
+ // otherwise, just use target
257
+ , nodes = query && typeof target == 'string' && target.charAt(0) != '<' ? query(target) : target
258
+ // normalize each node in case it's still a string and we need to create nodes on the fly
259
+ each(normalize(nodes), function (t, j) {
260
+ each(self, function (el) {
261
+ fn(t, r[i++] = j > 0 ? cloneNode(self, el) : el)
262
+ }, null, rev)
263
+ }, this, rev)
264
+ self.length = i
265
+ each(r, function (e) {
266
+ self[--i] = e
267
+ }, null, !rev)
268
+ return self
269
+ }
270
+
271
+
272
+ /**
273
+ * sets an element to an explicit x/y position on the page
274
+ * @param {Element} el
275
+ * @param {?number} x
276
+ * @param {?number} y
277
+ */
278
+ function xy(el, x, y) {
279
+ var $el = bonzo(el)
280
+ , style = $el.css('position')
281
+ , offset = $el.offset()
282
+ , rel = 'relative'
283
+ , isRel = style == rel
284
+ , delta = [parseInt($el.css('left'), 10), parseInt($el.css('top'), 10)]
285
+
286
+ if (style == 'static') {
287
+ $el.css('position', rel)
288
+ style = rel
289
+ }
290
+
291
+ isNaN(delta[0]) && (delta[0] = isRel ? 0 : el.offsetLeft)
292
+ isNaN(delta[1]) && (delta[1] = isRel ? 0 : el.offsetTop)
293
+
294
+ x != null && (el.style.left = x - offset.left + delta[0] + px)
295
+ y != null && (el.style.top = y - offset.top + delta[1] + px)
296
+
297
+ }
298
+
299
+ // classList support for class management
300
+ // altho to be fair, the api sucks because it won't accept multiple classes at once
301
+ if (features.classList) {
302
+ hasClass = function (el, c) {
303
+ return el.classList.contains(c)
304
+ }
305
+ addClass = function (el, c) {
306
+ el.classList.add(c)
307
+ }
308
+ removeClass = function (el, c) {
309
+ el.classList.remove(c)
310
+ }
311
+ }
312
+ else {
313
+ hasClass = function (el, c) {
314
+ return classReg(c).test(el.className)
315
+ }
316
+ addClass = function (el, c) {
317
+ el.className = trim(el.className + ' ' + c)
318
+ }
319
+ removeClass = function (el, c) {
320
+ el.className = trim(el.className.replace(classReg(c), ' '))
321
+ }
322
+ }
323
+
324
+
325
+ /**
326
+ * this allows method calling for setting values
327
+ *
328
+ * @example
329
+ * bonzo(elements).css('color', function (el) {
330
+ * return el.getAttribute('data-original-color')
331
+ * })
332
+ *
333
+ * @param {Element} el
334
+ * @param {function (Element)|string}
335
+ * @return {string}
336
+ */
337
+ function setter(el, v) {
338
+ return typeof v == 'function' ? v(el) : v
339
+ }
340
+
341
+ function scroll(x, y, type) {
342
+ var el = this[0]
343
+ if (!el) return this
344
+ if (x == null && y == null) {
345
+ return (isBody(el) ? getWindowScroll() : { x: el.scrollLeft, y: el.scrollTop })[type]
346
+ }
347
+ if (isBody(el)) {
348
+ win.scrollTo(x, y)
349
+ } else {
350
+ x != null && (el.scrollLeft = x)
351
+ y != null && (el.scrollTop = y)
352
+ }
353
+ return this
354
+ }
355
+
356
+ /**
357
+ * @constructor
358
+ * @param {Array.<Element>|Element|Node|string} elements
359
+ */
360
+ function Bonzo(elements) {
361
+ this.length = 0
362
+ if (elements) {
363
+ elements = typeof elements !== 'string' &&
364
+ !elements.nodeType &&
365
+ typeof elements.length !== 'undefined' ?
366
+ elements :
367
+ [elements]
368
+ this.length = elements.length
369
+ for (var i = 0; i < elements.length; i++) this[i] = elements[i]
370
+ }
371
+ }
372
+
373
+ Bonzo.prototype = {
374
+
375
+ /**
376
+ * @param {number} index
377
+ * @return {Element|Node}
378
+ */
379
+ get: function (index) {
380
+ return this[index] || null
381
+ }
382
+
383
+ // itetators
384
+ /**
385
+ * @param {function(Element|Node)} fn
386
+ * @param {Object=} opt_scope
387
+ * @return {Bonzo}
388
+ */
389
+ , each: function (fn, opt_scope) {
390
+ return each(this, fn, opt_scope)
391
+ }
392
+
393
+ /**
394
+ * @param {Function} fn
395
+ * @param {Object=} opt_scope
396
+ * @return {Bonzo}
397
+ */
398
+ , deepEach: function (fn, opt_scope) {
399
+ return deepEach(this, fn, opt_scope)
400
+ }
401
+
402
+
403
+ /**
404
+ * @param {Function} fn
405
+ * @param {Function=} opt_reject
406
+ * @return {Array}
407
+ */
408
+ , map: function (fn, opt_reject) {
409
+ var m = [], n, i
410
+ for (i = 0; i < this.length; i++) {
411
+ n = fn.call(this, this[i], i)
412
+ opt_reject ? (opt_reject(n) && m.push(n)) : m.push(n)
413
+ }
414
+ return m
415
+ }
416
+
417
+ // text and html inserters!
418
+
419
+ /**
420
+ * @param {string} h the HTML to insert
421
+ * @param {boolean=} opt_text whether to set or get text content
422
+ * @return {Bonzo|string}
423
+ */
424
+ , html: function (h, opt_text) {
425
+ var method = opt_text
426
+ ? html.textContent === undefined ? 'innerText' : 'textContent'
427
+ : 'innerHTML'
428
+ , that = this
429
+ , append = function (el, i) {
430
+ each(normalize(h, that, i), function (node) {
431
+ el.appendChild(node)
432
+ })
433
+ }
434
+ , updateElement = function (el, i) {
435
+ try {
436
+ if (opt_text || (typeof h == 'string' && !specialTags.test(el.tagName))) {
437
+ return el[method] = h
438
+ }
439
+ } catch (e) {}
440
+ append(el, i)
441
+ }
442
+ return typeof h != 'undefined'
443
+ ? this.empty().each(updateElement)
444
+ : this[0] ? this[0][method] : ''
445
+ }
446
+
447
+ /**
448
+ * @param {string=} opt_text the text to set, otherwise this is a getter
449
+ * @return {Bonzo|string}
450
+ */
451
+ , text: function (opt_text) {
452
+ return this.html(opt_text, true)
453
+ }
454
+
455
+ // more related insertion methods
456
+
457
+ /**
458
+ * @param {Bonzo|string|Element|Array} node
459
+ * @return {Bonzo}
460
+ */
461
+ , append: function (node) {
462
+ var that = this
463
+ return this.each(function (el, i) {
464
+ each(normalize(node, that, i), function (i) {
465
+ el.appendChild(i)
466
+ })
467
+ })
468
+ }
469
+
470
+
471
+ /**
472
+ * @param {Bonzo|string|Element|Array} node
473
+ * @return {Bonzo}
474
+ */
475
+ , prepend: function (node) {
476
+ var that = this
477
+ return this.each(function (el, i) {
478
+ var first = el.firstChild
479
+ each(normalize(node, that, i), function (i) {
480
+ el.insertBefore(i, first)
481
+ })
482
+ })
483
+ }
484
+
485
+
486
+ /**
487
+ * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
488
+ * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
489
+ * @return {Bonzo}
490
+ */
491
+ , appendTo: function (target, opt_host) {
492
+ return insert.call(this, target, opt_host, function (t, el) {
493
+ t.appendChild(el)
494
+ })
495
+ }
496
+
497
+
498
+ /**
499
+ * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
500
+ * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
501
+ * @return {Bonzo}
502
+ */
503
+ , prependTo: function (target, opt_host) {
504
+ return insert.call(this, target, opt_host, function (t, el) {
505
+ t.insertBefore(el, t.firstChild)
506
+ }, 1)
507
+ }
508
+
509
+
510
+ /**
511
+ * @param {Bonzo|string|Element|Array} node
512
+ * @return {Bonzo}
513
+ */
514
+ , before: function (node) {
515
+ var that = this
516
+ return this.each(function (el, i) {
517
+ each(normalize(node, that, i), function (i) {
518
+ el[parentNode].insertBefore(i, el)
519
+ })
520
+ })
521
+ }
522
+
523
+
524
+ /**
525
+ * @param {Bonzo|string|Element|Array} node
526
+ * @return {Bonzo}
527
+ */
528
+ , after: function (node) {
529
+ var that = this
530
+ return this.each(function (el, i) {
531
+ each(normalize(node, that, i), function (i) {
532
+ el[parentNode].insertBefore(i, el.nextSibling)
533
+ }, null, 1)
534
+ })
535
+ }
536
+
537
+
538
+ /**
539
+ * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
540
+ * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
541
+ * @return {Bonzo}
542
+ */
543
+ , insertBefore: function (target, opt_host) {
544
+ return insert.call(this, target, opt_host, function (t, el) {
545
+ t[parentNode].insertBefore(el, t)
546
+ })
547
+ }
548
+
549
+
550
+ /**
551
+ * @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
552
+ * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
553
+ * @return {Bonzo}
554
+ */
555
+ , insertAfter: function (target, opt_host) {
556
+ return insert.call(this, target, opt_host, function (t, el) {
557
+ var sibling = t.nextSibling
558
+ sibling ?
559
+ t[parentNode].insertBefore(el, sibling) :
560
+ t[parentNode].appendChild(el)
561
+ }, 1)
562
+ }
563
+
564
+
565
+ /**
566
+ * @param {Bonzo|string|Element|Array} node
567
+ * @return {Bonzo}
568
+ */
569
+ , replaceWith: function (node) {
570
+ bonzo(normalize(node)).insertAfter(this)
571
+ return this.remove()
572
+ }
573
+
574
+ /**
575
+ * @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
576
+ * @return {Bonzo}
577
+ */
578
+ , clone: function (opt_host) {
579
+ var ret = [] // don't change original array
580
+ , l, i
581
+ for (i = 0, l = this.length; i < l; i++) ret[i] = cloneNode(opt_host || this, this[i])
582
+ return bonzo(ret)
583
+ }
584
+
585
+ // class management
586
+
587
+ /**
588
+ * @param {string} c
589
+ * @return {Bonzo}
590
+ */
591
+ , addClass: function (c) {
592
+ c = toString.call(c).split(whitespaceRegex)
593
+ return this.each(function (el) {
594
+ // we `each` here so you can do $el.addClass('foo bar')
595
+ each(c, function (c) {
596
+ if (c && !hasClass(el, setter(el, c)))
597
+ addClass(el, setter(el, c))
598
+ })
599
+ })
600
+ }
601
+
602
+
603
+ /**
604
+ * @param {string} c
605
+ * @return {Bonzo}
606
+ */
607
+ , removeClass: function (c) {
608
+ c = toString.call(c).split(whitespaceRegex)
609
+ return this.each(function (el) {
610
+ each(c, function (c) {
611
+ if (c && hasClass(el, setter(el, c)))
612
+ removeClass(el, setter(el, c))
613
+ })
614
+ })
615
+ }
616
+
617
+
618
+ /**
619
+ * @param {string} c
620
+ * @return {boolean}
621
+ */
622
+ , hasClass: function (c) {
623
+ c = toString.call(c).split(whitespaceRegex)
624
+ return some(this, function (el) {
625
+ return some(c, function (c) {
626
+ return c && hasClass(el, c)
627
+ })
628
+ })
629
+ }
630
+
631
+
632
+ /**
633
+ * @param {string} c classname to toggle
634
+ * @param {boolean=} opt_condition whether to add or remove the class straight away
635
+ * @return {Bonzo}
636
+ */
637
+ , toggleClass: function (c, opt_condition) {
638
+ c = toString.call(c).split(whitespaceRegex)
639
+ return this.each(function (el) {
640
+ each(c, function (c) {
641
+ if (c) {
642
+ typeof opt_condition !== 'undefined' ?
643
+ opt_condition ? !hasClass(el, c) && addClass(el, c) : removeClass(el, c) :
644
+ hasClass(el, c) ? removeClass(el, c) : addClass(el, c)
645
+ }
646
+ })
647
+ })
648
+ }
649
+
650
+ // display togglers
651
+
652
+ /**
653
+ * @param {string=} opt_type useful to set back to anything other than an empty string
654
+ * @return {Bonzo}
655
+ */
656
+ , show: function (opt_type) {
657
+ opt_type = typeof opt_type == 'string' ? opt_type : ''
658
+ return this.each(function (el) {
659
+ el.style.display = opt_type
660
+ })
661
+ }
662
+
663
+
664
+ /**
665
+ * @return {Bonzo}
666
+ */
667
+ , hide: function () {
668
+ return this.each(function (el) {
669
+ el.style.display = 'none'
670
+ })
671
+ }
672
+
673
+
674
+ /**
675
+ * @param {Function=} opt_callback
676
+ * @param {string=} opt_type
677
+ * @return {Bonzo}
678
+ */
679
+ , toggle: function (opt_callback, opt_type) {
680
+ opt_type = typeof opt_type == 'string' ? opt_type : '';
681
+ typeof opt_callback != 'function' && (opt_callback = null)
682
+ return this.each(function (el) {
683
+ el.style.display = (el.offsetWidth || el.offsetHeight) ? 'none' : opt_type;
684
+ opt_callback && opt_callback.call(el)
685
+ })
686
+ }
687
+
688
+
689
+ // DOM Walkers & getters
690
+
691
+ /**
692
+ * @return {Element|Node}
693
+ */
694
+ , first: function () {
695
+ return bonzo(this.length ? this[0] : [])
696
+ }
697
+
698
+
699
+ /**
700
+ * @return {Element|Node}
701
+ */
702
+ , last: function () {
703
+ return bonzo(this.length ? this[this.length - 1] : [])
704
+ }
705
+
706
+
707
+ /**
708
+ * @return {Element|Node}
709
+ */
710
+ , next: function () {
711
+ return this.related('nextSibling')
712
+ }
713
+
714
+
715
+ /**
716
+ * @return {Element|Node}
717
+ */
718
+ , previous: function () {
719
+ return this.related('previousSibling')
720
+ }
721
+
722
+
723
+ /**
724
+ * @return {Element|Node}
725
+ */
726
+ , parent: function() {
727
+ return this.related(parentNode)
728
+ }
729
+
730
+
731
+ /**
732
+ * @private
733
+ * @param {string} method the directional DOM method
734
+ * @return {Element|Node}
735
+ */
736
+ , related: function (method) {
737
+ return bonzo(this.map(
738
+ function (el) {
739
+ el = el[method]
740
+ while (el && el.nodeType !== 1) {
741
+ el = el[method]
742
+ }
743
+ return el || 0
744
+ },
745
+ function (el) {
746
+ return el
747
+ }
748
+ ))
749
+ }
750
+
751
+
752
+ /**
753
+ * @return {Bonzo}
754
+ */
755
+ , focus: function () {
756
+ this.length && this[0].focus()
757
+ return this
758
+ }
759
+
760
+
761
+ /**
762
+ * @return {Bonzo}
763
+ */
764
+ , blur: function () {
765
+ this.length && this[0].blur()
766
+ return this
767
+ }
768
+
769
+ // style getter setter & related methods
770
+
771
+ /**
772
+ * @param {Object|string} o
773
+ * @param {string=} opt_v
774
+ * @return {Bonzo|string}
775
+ */
776
+ , css: function (o, opt_v) {
777
+ var p, iter = o
778
+ // is this a request for just getting a style?
779
+ if (opt_v === undefined && typeof o == 'string') {
780
+ // repurpose 'v'
781
+ opt_v = this[0]
782
+ if (!opt_v) return null
783
+ if (opt_v === doc || opt_v === win) {
784
+ p = (opt_v === doc) ? bonzo.doc() : bonzo.viewport()
785
+ return o == 'width' ? p.width : o == 'height' ? p.height : ''
786
+ }
787
+ return (o = styleProperty(o)) ? getStyle(opt_v, o) : null
788
+ }
789
+
790
+ if (typeof o == 'string') {
791
+ iter = {}
792
+ iter[o] = opt_v
793
+ }
794
+
795
+ if (ie && iter.opacity) {
796
+ // oh this 'ol gamut
797
+ iter.filter = 'alpha(opacity=' + (iter.opacity * 100) + ')'
798
+ // give it layout
799
+ iter.zoom = o.zoom || 1;
800
+ delete iter.opacity;
801
+ }
802
+
803
+ function fn(el, p, v) {
804
+ for (var k in iter) {
805
+ if (iter.hasOwnProperty(k)) {
806
+ v = iter[k];
807
+ // change "5" to "5px" - unless you're line-height, which is allowed
808
+ (p = styleProperty(k)) && digit.test(v) && !(p in unitless) && (v += px)
809
+ try { el.style[p] = setter(el, v) } catch(e) {}
810
+ }
811
+ }
812
+ }
813
+ return this.each(fn)
814
+ }
815
+
816
+
817
+ /**
818
+ * @param {number=} opt_x
819
+ * @param {number=} opt_y
820
+ * @return {Bonzo|number}
821
+ */
822
+ , offset: function (opt_x, opt_y) {
823
+ if (opt_x && typeof opt_x == 'object' && (typeof opt_x.top == 'number' || typeof opt_x.left == 'number')) {
824
+ return this.each(function (el) {
825
+ xy(el, opt_x.left, opt_x.top)
826
+ })
827
+ } else if (typeof opt_x == 'number' || typeof opt_y == 'number') {
828
+ return this.each(function (el) {
829
+ xy(el, opt_x, opt_y)
830
+ })
831
+ }
832
+ if (!this[0]) return {
833
+ top: 0
834
+ , left: 0
835
+ , height: 0
836
+ , width: 0
837
+ }
838
+ var el = this[0]
839
+ , de = el.ownerDocument.documentElement
840
+ , bcr = el.getBoundingClientRect()
841
+ , scroll = getWindowScroll()
842
+ , width = el.offsetWidth
843
+ , height = el.offsetHeight
844
+ , top = bcr.top + scroll.y - Math.max(0, de && de.clientTop, doc.body.clientTop)
845
+ , left = bcr.left + scroll.x - Math.max(0, de && de.clientLeft, doc.body.clientLeft)
846
+
847
+ return {
848
+ top: top
849
+ , left: left
850
+ , height: height
851
+ , width: width
852
+ }
853
+ }
854
+
855
+
856
+ /**
857
+ * @return {number}
858
+ */
859
+ , dim: function () {
860
+ if (!this.length) return { height: 0, width: 0 }
861
+ var el = this[0]
862
+ , de = el.nodeType == 9 && el.documentElement // document
863
+ , orig = !de && !!el.style && !el.offsetWidth && !el.offsetHeight ?
864
+ // el isn't visible, can't be measured properly, so fix that
865
+ function (t) {
866
+ var s = {
867
+ position: el.style.position || ''
868
+ , visibility: el.style.visibility || ''
869
+ , display: el.style.display || ''
870
+ }
871
+ t.first().css({
872
+ position: 'absolute'
873
+ , visibility: 'hidden'
874
+ , display: 'block'
875
+ })
876
+ return s
877
+ }(this) : null
878
+ , width = de
879
+ ? Math.max(el.body.scrollWidth, el.body.offsetWidth, de.scrollWidth, de.offsetWidth, de.clientWidth)
880
+ : el.offsetWidth
881
+ , height = de
882
+ ? Math.max(el.body.scrollHeight, el.body.offsetHeight, de.scrollHeight, de.offsetHeight, de.clientHeight)
883
+ : el.offsetHeight
884
+
885
+ orig && this.first().css(orig)
886
+ return {
887
+ height: height
888
+ , width: width
889
+ }
890
+ }
891
+
892
+ // attributes are hard. go shopping
893
+
894
+ /**
895
+ * @param {string} k an attribute to get or set
896
+ * @param {string=} opt_v the value to set
897
+ * @return {Bonzo|string}
898
+ */
899
+ , attr: function (k, opt_v) {
900
+ var el = this[0]
901
+ , n
902
+
903
+ if (typeof k != 'string' && !(k instanceof String)) {
904
+ for (n in k) {
905
+ k.hasOwnProperty(n) && this.attr(n, k[n])
906
+ }
907
+ return this
908
+ }
909
+
910
+ return typeof opt_v == 'undefined' ?
911
+ !el ? null : specialAttributes.test(k) ?
912
+ stateAttributes.test(k) && typeof el[k] == 'string' ?
913
+ true : el[k] : (k == 'href' || k =='src') && features.hrefExtended ?
914
+ el[getAttribute](k, 2) : el[getAttribute](k) :
915
+ this.each(function (el) {
916
+ specialAttributes.test(k) ? (el[k] = setter(el, opt_v)) : el[setAttribute](k, setter(el, opt_v))
917
+ })
918
+ }
919
+
920
+
921
+ /**
922
+ * @param {string} k
923
+ * @return {Bonzo}
924
+ */
925
+ , removeAttr: function (k) {
926
+ return this.each(function (el) {
927
+ stateAttributes.test(k) ? (el[k] = false) : el.removeAttribute(k)
928
+ })
929
+ }
930
+
931
+
932
+ /**
933
+ * @param {string=} opt_s
934
+ * @return {Bonzo|string}
935
+ */
936
+ , val: function (s) {
937
+ return (typeof s == 'string') ?
938
+ this.attr('value', s) :
939
+ this.length ? this[0].value : null
940
+ }
941
+
942
+ // use with care and knowledge. this data() method uses data attributes on the DOM nodes
943
+ // to do this differently costs a lot more code. c'est la vie
944
+ /**
945
+ * @param {string|Object=} opt_k the key for which to get or set data
946
+ * @param {Object=} opt_v
947
+ * @return {Bonzo|Object}
948
+ */
949
+ , data: function (opt_k, opt_v) {
950
+ var el = this[0], o, m
951
+ if (typeof opt_v === 'undefined') {
952
+ if (!el) return null
953
+ o = data(el)
954
+ if (typeof opt_k === 'undefined') {
955
+ each(el.attributes, function (a) {
956
+ (m = ('' + a.name).match(dattr)) && (o[camelize(m[1])] = dataValue(a.value))
957
+ })
958
+ return o
959
+ } else {
960
+ if (typeof o[opt_k] === 'undefined')
961
+ o[opt_k] = dataValue(this.attr('data-' + decamelize(opt_k)))
962
+ return o[opt_k]
963
+ }
964
+ } else {
965
+ return this.each(function (el) { data(el)[opt_k] = opt_v })
966
+ }
967
+ }
968
+
969
+ // DOM detachment & related
970
+
971
+ /**
972
+ * @return {Bonzo}
973
+ */
974
+ , remove: function () {
975
+ this.deepEach(clearData)
976
+ return this.detach()
977
+ }
978
+
979
+
980
+ /**
981
+ * @return {Bonzo}
982
+ */
983
+ , empty: function () {
984
+ return this.each(function (el) {
985
+ deepEach(el.childNodes, clearData)
986
+
987
+ while (el.firstChild) {
988
+ el.removeChild(el.firstChild)
989
+ }
990
+ })
991
+ }
992
+
993
+
994
+ /**
995
+ * @return {Bonzo}
996
+ */
997
+ , detach: function () {
998
+ return this.each(function (el) {
999
+ el[parentNode] && el[parentNode].removeChild(el)
1000
+ })
1001
+ }
1002
+
1003
+ // who uses a mouse anyway? oh right.
1004
+
1005
+ /**
1006
+ * @param {number} y
1007
+ */
1008
+ , scrollTop: function (y) {
1009
+ return scroll.call(this, null, y, 'y')
1010
+ }
1011
+
1012
+
1013
+ /**
1014
+ * @param {number} x
1015
+ */
1016
+ , scrollLeft: function (x) {
1017
+ return scroll.call(this, x, null, 'x')
1018
+ }
1019
+
1020
+ }
1021
+
1022
+
1023
+ function cloneNode(host, el) {
1024
+ var c = el.cloneNode(true)
1025
+ , cloneElems
1026
+ , elElems
1027
+ , i
1028
+
1029
+ // check for existence of an event cloner
1030
+ // preferably https://github.com/fat/bean
1031
+ // otherwise Bonzo won't do this for you
1032
+ if (host.$ && typeof host.cloneEvents == 'function') {
1033
+ host.$(c).cloneEvents(el)
1034
+
1035
+ // clone events from every child node
1036
+ cloneElems = host.$(c).find('*')
1037
+ elElems = host.$(el).find('*')
1038
+
1039
+ for (i = 0; i < elElems.length; i++)
1040
+ host.$(cloneElems[i]).cloneEvents(elElems[i])
1041
+ }
1042
+ return c
1043
+ }
1044
+
1045
+ function isBody(element) {
1046
+ return element === win || (/^(?:body|html)$/i).test(element.tagName)
1047
+ }
1048
+
1049
+ function getWindowScroll() {
1050
+ return { x: win.pageXOffset || html.scrollLeft, y: win.pageYOffset || html.scrollTop }
1051
+ }
1052
+
1053
+ function createScriptFromHtml(html) {
1054
+ var scriptEl = document.createElement('script')
1055
+ , matches = html.match(simpleScriptTagRe)
1056
+ scriptEl.src = matches[1]
1057
+ return scriptEl
1058
+ }
1059
+
1060
+ /**
1061
+ * @param {Array.<Element>|Element|Node|string} els
1062
+ * @return {Bonzo}
1063
+ */
1064
+ function bonzo(els) {
1065
+ return new Bonzo(els)
1066
+ }
1067
+
1068
+ bonzo.setQueryEngine = function (q) {
1069
+ query = q;
1070
+ delete bonzo.setQueryEngine
1071
+ }
1072
+
1073
+ bonzo.aug = function (o, target) {
1074
+ // for those standalone bonzo users. this love is for you.
1075
+ for (var k in o) {
1076
+ o.hasOwnProperty(k) && ((target || Bonzo.prototype)[k] = o[k])
1077
+ }
1078
+ }
1079
+
1080
+ bonzo.create = function (node) {
1081
+ // hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
1082
+ return typeof node == 'string' && node !== '' ?
1083
+ function () {
1084
+ if (simpleScriptTagRe.test(node)) return [createScriptFromHtml(node)]
1085
+ var tag = node.match(/^\s*<([^\s>]+)/)
1086
+ , el = doc.createElement('div')
1087
+ , els = []
1088
+ , p = tag ? tagMap[tag[1].toLowerCase()] : null
1089
+ , dep = p ? p[2] + 1 : 1
1090
+ , ns = p && p[3]
1091
+ , pn = parentNode
1092
+ , tb = features.autoTbody && p && p[0] == '<table>' && !(/<tbody/i).test(node)
1093
+
1094
+ el.innerHTML = p ? (p[0] + node + p[1]) : node
1095
+ while (dep--) el = el.firstChild
1096
+ // for IE NoScope, we may insert cruft at the begining just to get it to work
1097
+ if (ns && el && el.nodeType !== 1) el = el.nextSibling
1098
+ do {
1099
+ // tbody special case for IE<8, creates tbody on any empty table
1100
+ // we don't want it if we're just after a <thead>, <caption>, etc.
1101
+ if ((!tag || el.nodeType == 1) && (!tb || (el.tagName && el.tagName != 'TBODY'))) {
1102
+ els.push(el)
1103
+ }
1104
+ } while (el = el.nextSibling)
1105
+ // IE < 9 gives us a parentNode which messes up insert() check for cloning
1106
+ // `dep` > 1 can also cause problems with the insert() check (must do this last)
1107
+ each(els, function(el) { el[pn] && el[pn].removeChild(el) })
1108
+ return els
1109
+ }() : isNode(node) ? [node.cloneNode(true)] : []
1110
+ }
1111
+
1112
+ bonzo.doc = function () {
1113
+ var vp = bonzo.viewport()
1114
+ return {
1115
+ width: Math.max(doc.body.scrollWidth, html.scrollWidth, vp.width)
1116
+ , height: Math.max(doc.body.scrollHeight, html.scrollHeight, vp.height)
1117
+ }
1118
+ }
1119
+
1120
+ bonzo.firstChild = function (el) {
1121
+ for (var c = el.childNodes, i = 0, j = (c && c.length) || 0, e; i < j; i++) {
1122
+ if (c[i].nodeType === 1) e = c[j = i]
1123
+ }
1124
+ return e
1125
+ }
1126
+
1127
+ bonzo.viewport = function () {
1128
+ return {
1129
+ width: ie ? html.clientWidth : self.innerWidth
1130
+ , height: ie ? html.clientHeight : self.innerHeight
1131
+ }
1132
+ }
1133
+
1134
+ bonzo.isAncestor = 'compareDocumentPosition' in html ?
1135
+ function (container, element) {
1136
+ return (container.compareDocumentPosition(element) & 16) == 16
1137
+ } : 'contains' in html ?
1138
+ function (container, element) {
1139
+ return container !== element && container.contains(element);
1140
+ } :
1141
+ function (container, element) {
1142
+ while (element = element[parentNode]) {
1143
+ if (element === container) {
1144
+ return true
1145
+ }
1146
+ }
1147
+ return false
1148
+ }
1149
+
1150
+ return bonzo
1151
+ }); // the only line we care about using a semi-colon. placed here for concatenation tools