hoarder-js 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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