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.
- data/CHANGELOG +2 -0
- data/Gemfile.lock +2 -2
- data/Manifest +14 -3
- data/assets/scripts/coffee/hoarder/form/form.coffee +47 -44
- data/assets/scripts/coffee/hoarder/form/form_serializer.coffee +23 -0
- data/assets/scripts/coffee/hoarder/form_manager.coffee +35 -19
- data/assets/scripts/coffee/hoarder/submitter/form_submitter.coffee +10 -15
- data/assets/scripts/coffee/hoarder/submitter/submitters/base_submitter.coffee +11 -0
- data/assets/scripts/coffee/hoarder/submitter/submitters/polling_submitter.coffee +30 -27
- data/assets/scripts/coffee/hoarder/submitter/submitters/simple_submitter.coffee +11 -21
- data/assets/scripts/coffee/hoarder/validator/constraints/alpha_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/alphanumeric_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/base_constraint.coffee +10 -0
- data/assets/scripts/coffee/hoarder/validator/constraints/credit_card_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/email_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/max_length_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/min_length_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/numeric_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/phone_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/constraints/required_constraint.coffee +8 -14
- data/assets/scripts/coffee/hoarder/validator/form_validator.coffee +30 -25
- data/assets/scripts/coffee/hoarder/validator/rules/validation_rule.coffee +10 -0
- data/assets/scripts/js/lib/bonzo.js +1151 -0
- data/assets/scripts/js/lib/qwery.js +369 -0
- data/assets/scripts/js/lib/reqwest.js +565 -0
- data/assets/scripts/js/patches/event_listeners.js +73 -0
- data/bin/hoarder.js +2807 -369
- data/hoarder-js.gemspec +3 -3
- data/spec/jasmine.yml +3 -0
- data/spec/runner.html +44 -34
- data/spec/support/fixtures.coffee +19 -0
- data/spec/support/lib/jasmine-fixture.js +506 -0
- data/spec/support/lib/jquery-1.7.1.min.js +4 -0
- data/spec/support/mocks.coffee +12 -37
- data/spec/support/requirements.coffee +0 -1
- data/spec/tests/form/form_serializer_spec.coffee +78 -0
- data/spec/tests/form/form_spec.coffee +127 -0
- data/spec/tests/form_manager_spec.coffee +128 -40
- data/spec/tests/submitter/form_submitter_spec.coffee +59 -42
- data/spec/tests/submitter/submitters/polling_submitter_spec.coffee +102 -57
- data/spec/tests/submitter/submitters/simple_submitter_spec.coffee +42 -16
- data/spec/tests/validator/constraints_spec.coffee +65 -57
- data/spec/tests/validator/form_validator_spec.coffee +39 -23
- data/spec/tests/validator/validation_rule_spec.coffee +14 -0
- metadata +16 -5
- data/assets/scripts/coffee/hoarder/form/form_element.coffee +0 -22
- data/assets/scripts/coffee/hoarder/validator/error/validation_error.coffee +0 -9
- data/assets/scripts/js/lib/jquery.js +0 -5
@@ -1,18 +1,12 @@
|
|
1
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
@@ -1,18 +1,12 @@
|
|
1
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
1
|
+
BaseConstraint = require 'hoarder/validator/constraints/base_constraint'
|
2
2
|
|
3
|
-
|
4
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
1
|
+
BaseConstraint = require "hoarder/validator/constraints/base_constraint"
|
2
2
|
|
3
|
-
|
4
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
1
|
+
BaseConstraint = require "hoarder/validator/constraints/base_constraint"
|
2
2
|
|
3
|
-
|
4
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
32
|
+
isValid = true
|
33
33
|
for element in form.elements()
|
34
|
-
|
35
|
-
|
34
|
+
isValid = false unless @validateElement(element)
|
35
|
+
isValid
|
36
36
|
|
37
37
|
validateElement: (element)->
|
38
|
-
for
|
39
|
-
|
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
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
56
|
+
isValid = (element)-> not element.validity.customError
|
52
57
|
|
53
58
|
return FormValidator
|
@@ -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
|