gumby 0.0.1
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/.gitignore +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +73 -0
- data/Rakefile +1 -0
- data/gumby.gemspec +20 -0
- data/lib/gumby.rb +8 -0
- data/lib/gumby/version.rb +5 -0
- data/vendor/assets/fonts/icons/entypo.eot +0 -0
- data/vendor/assets/fonts/icons/entypo.ttf +0 -0
- data/vendor/assets/fonts/icons/entypo.woff +0 -0
- data/vendor/assets/javascripts/gumby.js +150 -0
- data/vendor/assets/javascripts/gumby.min.js +1 -0
- data/vendor/assets/javascripts/ui/gumby.checkbox.js +84 -0
- data/vendor/assets/javascripts/ui/gumby.fittext.js +107 -0
- data/vendor/assets/javascripts/ui/gumby.fixed.js +206 -0
- data/vendor/assets/javascripts/ui/gumby.navbar.js +115 -0
- data/vendor/assets/javascripts/ui/gumby.radiobtn.js +74 -0
- data/vendor/assets/javascripts/ui/gumby.retina.js +74 -0
- data/vendor/assets/javascripts/ui/gumby.skiplink.js +145 -0
- data/vendor/assets/javascripts/ui/gumby.tabs.js +71 -0
- data/vendor/assets/javascripts/ui/gumby.toggleswitch.js +203 -0
- data/vendor/assets/javascripts/ui/jquery.validation.js +138 -0
- data/vendor/assets/stylesheets/gumby.css +1876 -0
- metadata +101 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
/**
|
2
|
+
* Gumby Fixed
|
3
|
+
*/
|
4
|
+
!function() {
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
function Fixed($el) {
|
9
|
+
this.$el = $el;
|
10
|
+
|
11
|
+
this.fixedPoint = '';
|
12
|
+
this.pinPoint = false;
|
13
|
+
this.offset = 0;
|
14
|
+
this.pinOffset = 0;
|
15
|
+
this.top = 0;
|
16
|
+
this.constrainEl = true;
|
17
|
+
this.state = false;
|
18
|
+
this.measurements = {
|
19
|
+
left: 0,
|
20
|
+
width: 0
|
21
|
+
};
|
22
|
+
|
23
|
+
// set up module based on attributes
|
24
|
+
this.setup();
|
25
|
+
|
26
|
+
var scope = this;
|
27
|
+
|
28
|
+
// monitor scroll and update fixed elements accordingly
|
29
|
+
$(window).on('scroll load', function() {
|
30
|
+
scope.monitorScroll();
|
31
|
+
});
|
32
|
+
|
33
|
+
// reinitialize event listener
|
34
|
+
this.$el.on('gumby.initialize', function() {
|
35
|
+
scope.setup();
|
36
|
+
});
|
37
|
+
}
|
38
|
+
|
39
|
+
// set up module based on attributes
|
40
|
+
Fixed.prototype.setup = function() {
|
41
|
+
var scope = this;
|
42
|
+
|
43
|
+
this.fixedPoint = this.parseAttrValue(Gumby.selectAttr.apply(this.$el, ['fixed']));
|
44
|
+
|
45
|
+
// pin point is optional
|
46
|
+
this.pinPoint = Gumby.selectAttr.apply(this.$el, ['pin']) || false;
|
47
|
+
|
48
|
+
// offset from fixed point
|
49
|
+
this.offset = Number(Gumby.selectAttr.apply(this.$el, ['offset'])) || 0;
|
50
|
+
|
51
|
+
// offset from pin point
|
52
|
+
this.pinOffset = Number(Gumby.selectAttr.apply(this.$el, ['pinoffset'])) || 0;
|
53
|
+
|
54
|
+
// top position when fixed
|
55
|
+
this.top = Number(Gumby.selectAttr.apply(this.$el, ['top'])) || 0;
|
56
|
+
|
57
|
+
// constrain can be turned off
|
58
|
+
this.constrainEl = Gumby.selectAttr.apply(this.$el, ['constrain']) || true;
|
59
|
+
if(this.constrainEl === 'false') {
|
60
|
+
this.constrainEl = false;
|
61
|
+
}
|
62
|
+
|
63
|
+
// reference to the parent, row/column
|
64
|
+
this.$parent = this.$el.parents('.columns, .column, .row');
|
65
|
+
this.$parent = this.$parent.length ? this.$parent.first() : false;
|
66
|
+
this.parentRow = this.$parent ? !!this.$parent.hasClass('row') : false;
|
67
|
+
|
68
|
+
// if optional pin point set then parse now
|
69
|
+
if(this.pinPoint) {
|
70
|
+
this.pinPoint = this.parseAttrValue(this.pinPoint);
|
71
|
+
}
|
72
|
+
|
73
|
+
// if we have a parent constrain dimenions
|
74
|
+
if(this.$parent && this.constrainEl) {
|
75
|
+
// measure up
|
76
|
+
this.measure();
|
77
|
+
// and on resize reset measurement
|
78
|
+
$(window).resize(function() {
|
79
|
+
if(scope.state) {
|
80
|
+
scope.measure();
|
81
|
+
scope.constrain();
|
82
|
+
}
|
83
|
+
});
|
84
|
+
}
|
85
|
+
};
|
86
|
+
|
87
|
+
// monitor scroll and trigger changes based on position
|
88
|
+
Fixed.prototype.monitorScroll = function() {
|
89
|
+
var scrollAmount = $(window).scrollTop(),
|
90
|
+
// recalculate selector attributes as position may have changed
|
91
|
+
fixedPoint = this.fixedPoint instanceof jQuery ? this.fixedPoint.offset().top : this.fixedPoint,
|
92
|
+
pinPoint = false;
|
93
|
+
|
94
|
+
// if a pin point is set recalculate
|
95
|
+
if(this.pinPoint) {
|
96
|
+
pinPoint = this.pinPoint instanceof jQuery ? this.pinPoint.offset().top : this.pinPoint;
|
97
|
+
}
|
98
|
+
|
99
|
+
// apply offsets
|
100
|
+
if(this.offset) { fixedPoint -= this.offset; }
|
101
|
+
if(this.pinOffset) { pinPoint -= this.pinOffset; }
|
102
|
+
|
103
|
+
// fix it
|
104
|
+
if((scrollAmount >= fixedPoint) && this.state !== 'fixed') {
|
105
|
+
if(!pinPoint || scrollAmount < pinPoint) {
|
106
|
+
this.fix();
|
107
|
+
}
|
108
|
+
// unfix it
|
109
|
+
} else if(scrollAmount < fixedPoint && this.state === 'fixed') {
|
110
|
+
this.unfix();
|
111
|
+
|
112
|
+
// pin it
|
113
|
+
} else if(pinPoint && scrollAmount >= pinPoint && this.state !== 'pinned') {
|
114
|
+
this.pin();
|
115
|
+
}
|
116
|
+
};
|
117
|
+
|
118
|
+
// fix the element and update state
|
119
|
+
Fixed.prototype.fix = function() {
|
120
|
+
this.state = 'fixed';
|
121
|
+
this.$el.css({
|
122
|
+
'top' : 0 + this.top
|
123
|
+
}).addClass('fixed').removeClass('unfixed pinned').trigger('gumby.onFixed');
|
124
|
+
|
125
|
+
// if we have a parent constrain dimenions
|
126
|
+
if(this.$parent) {
|
127
|
+
this.constrain();
|
128
|
+
}
|
129
|
+
};
|
130
|
+
|
131
|
+
// unfix the element and update state
|
132
|
+
Fixed.prototype.unfix = function() {
|
133
|
+
this.state = 'unfixed';
|
134
|
+
this.$el.addClass('unfixed').removeClass('fixed pinned').trigger('gumby.onUnfixed');
|
135
|
+
};
|
136
|
+
|
137
|
+
// pin the element in position
|
138
|
+
Fixed.prototype.pin = function() {
|
139
|
+
this.state = 'pinned';
|
140
|
+
this.$el.css({
|
141
|
+
'top' : this.$el.offset().top
|
142
|
+
}).addClass('pinned fixed').removeClass('unfixed').trigger('gumby.onPinned');
|
143
|
+
};
|
144
|
+
|
145
|
+
// constrain elements dimensions to match width/height
|
146
|
+
Fixed.prototype.constrain = function() {
|
147
|
+
this.$el.css({
|
148
|
+
left: this.measurements.left,
|
149
|
+
width: this.measurements.width
|
150
|
+
});
|
151
|
+
};
|
152
|
+
|
153
|
+
// measure up the parent for constraining
|
154
|
+
Fixed.prototype.measure = function() {
|
155
|
+
var offsets = this.$parent.offset(), parentPadding;
|
156
|
+
|
157
|
+
this.measurements.left = offsets.left;
|
158
|
+
this.measurements.width = this.$parent.width();
|
159
|
+
|
160
|
+
// if element has a parent row then need to consider padding
|
161
|
+
if(this.parentRow) {
|
162
|
+
parentPadding = Number(this.$parent.css('paddingLeft').replace(/px/, ''));
|
163
|
+
if(parentPadding) {
|
164
|
+
this.measurements.left += parentPadding;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
};
|
168
|
+
|
169
|
+
// parse attribute values, could be px, top, selector
|
170
|
+
Fixed.prototype.parseAttrValue = function(attr) {
|
171
|
+
// px value fixed point
|
172
|
+
if($.isNumeric(attr)) {
|
173
|
+
return Number(attr);
|
174
|
+
// 'top' string fixed point
|
175
|
+
} else if(attr === 'top') {
|
176
|
+
return this.$el.offset().top;
|
177
|
+
// selector specified
|
178
|
+
} else {
|
179
|
+
var $el = $(attr);
|
180
|
+
return $el;
|
181
|
+
}
|
182
|
+
};
|
183
|
+
|
184
|
+
// add initialisation
|
185
|
+
Gumby.addInitalisation('fixed', function() {
|
186
|
+
$('[data-fixed],[gumby-fixed],[fixed]').each(function() {
|
187
|
+
var $this = $(this);
|
188
|
+
// this element has already been initialized
|
189
|
+
if($this.data('isFixed')) {
|
190
|
+
return true;
|
191
|
+
}
|
192
|
+
// mark element as initialized
|
193
|
+
$this.data('isFixed', true);
|
194
|
+
new Fixed($this);
|
195
|
+
});
|
196
|
+
});
|
197
|
+
|
198
|
+
// register UI module
|
199
|
+
Gumby.UIModule({
|
200
|
+
module: 'fixed',
|
201
|
+
events: ['onFixed', 'onUnfixed'],
|
202
|
+
init: function() {
|
203
|
+
Gumby.initialize('fixed');
|
204
|
+
}
|
205
|
+
});
|
206
|
+
}();
|
@@ -0,0 +1,115 @@
|
|
1
|
+
/**
|
2
|
+
* Gumby Navbar
|
3
|
+
*/
|
4
|
+
!function() {
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
var $html = Gumby.$dom.find('html');
|
9
|
+
|
10
|
+
// define and init module on touch enabled devices only
|
11
|
+
// when we are at tablet size or smaller
|
12
|
+
if(!Modernizr.touch || $(window).width() > Gumby.breakpoint) {
|
13
|
+
|
14
|
+
// add Gumby no touch class
|
15
|
+
$html.addClass('gumby-no-touch');
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
|
19
|
+
// add Gumby touch class
|
20
|
+
$html.addClass('gumby-touch');
|
21
|
+
|
22
|
+
function Navbar($el) {
|
23
|
+
this.$el = $el;
|
24
|
+
this.$dropDowns = this.$el.find('li:has(.dropdown)');
|
25
|
+
var scope = this;
|
26
|
+
|
27
|
+
// when navbar items
|
28
|
+
this.$dropDowns
|
29
|
+
// are tapped hide/show dropdowns
|
30
|
+
.on('tap', this.toggleDropdown)
|
31
|
+
// are swiped right open link
|
32
|
+
.on('swiperight', this.openLink);
|
33
|
+
|
34
|
+
// if there's a link set
|
35
|
+
if(this.$dropDowns.children('a').attr('href') !== '#') {
|
36
|
+
// append an icon
|
37
|
+
this.$dropDowns.children('a').append('<i class="icon-popup"></i>').children('i')
|
38
|
+
// and bind to click event to open link
|
39
|
+
.on('tap', this.openLink);
|
40
|
+
}
|
41
|
+
|
42
|
+
// on mousemove and touchstart toggle modernizr classes and disable/enable this module
|
43
|
+
// workaround for Pixel and other multi input devices
|
44
|
+
$(window).on('mousemove touchstart', function(e) {
|
45
|
+
e.stopImmediatePropagation();
|
46
|
+
if(e.type === 'mousemove') {
|
47
|
+
scope.$dropDowns.on('mouseover mouseout', scope.toggleDropdown);
|
48
|
+
}
|
49
|
+
});
|
50
|
+
}
|
51
|
+
|
52
|
+
Navbar.prototype.toggleDropdown = function(e) {
|
53
|
+
// prevent click from triggering here too
|
54
|
+
e.stopImmediatePropagation();
|
55
|
+
e.preventDefault();
|
56
|
+
|
57
|
+
var $this = $(this);
|
58
|
+
|
59
|
+
if($this.hasClass('active')) {
|
60
|
+
$this.removeClass('active');
|
61
|
+
} else {
|
62
|
+
$this.addClass('active');
|
63
|
+
}
|
64
|
+
};
|
65
|
+
|
66
|
+
// handle opening list item link
|
67
|
+
Navbar.prototype.openLink = function(e) {
|
68
|
+
e.stopImmediatePropagation();
|
69
|
+
e.preventDefault();
|
70
|
+
|
71
|
+
var $this = $(this),
|
72
|
+
$el, href;
|
73
|
+
|
74
|
+
// tapped icon
|
75
|
+
if($this.is('i')) {
|
76
|
+
$el = $this.parent('a');
|
77
|
+
// swiped li
|
78
|
+
} else if($this.is('li')) {
|
79
|
+
$el = $this.children('a');
|
80
|
+
}
|
81
|
+
|
82
|
+
href = $el.attr('href');
|
83
|
+
|
84
|
+
// open in new window
|
85
|
+
if($el.attr('target') == 'blank') {
|
86
|
+
window.open(href);
|
87
|
+
// regular relocation
|
88
|
+
} else {
|
89
|
+
window.location = href;
|
90
|
+
}
|
91
|
+
};
|
92
|
+
|
93
|
+
// add initialisation
|
94
|
+
Gumby.addInitalisation('navbars', function() {
|
95
|
+
$('.navbar').each(function() {
|
96
|
+
var $this = $(this);
|
97
|
+
// this element has already been initialized
|
98
|
+
if($this.data('isNavbar')) {
|
99
|
+
return true;
|
100
|
+
}
|
101
|
+
// mark element as initialized
|
102
|
+
$this.data('isNavbar', true);
|
103
|
+
new Navbar($this);
|
104
|
+
});
|
105
|
+
});
|
106
|
+
|
107
|
+
// register UI module
|
108
|
+
Gumby.UIModule({
|
109
|
+
module: 'navbar',
|
110
|
+
events: [],
|
111
|
+
init: function() {
|
112
|
+
Gumby.initialize('navbars');
|
113
|
+
}
|
114
|
+
});
|
115
|
+
}();
|
@@ -0,0 +1,74 @@
|
|
1
|
+
/**
|
2
|
+
* Gumby RadioBtn
|
3
|
+
*/
|
4
|
+
!function() {
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
function RadioBtn($el) {
|
9
|
+
|
10
|
+
this.$el = $el;
|
11
|
+
var scope = this;
|
12
|
+
|
13
|
+
// listen for click event and custom gumby check event
|
14
|
+
this.$el.on(Gumby.click, function(e) {
|
15
|
+
// prevent propagation
|
16
|
+
e.stopImmediatePropagation();
|
17
|
+
|
18
|
+
// prevent radio button checking, we'll do that manually
|
19
|
+
e.preventDefault();
|
20
|
+
|
21
|
+
// check radio button
|
22
|
+
scope.update();
|
23
|
+
}).on('gumby.check', function() {
|
24
|
+
scope.update();
|
25
|
+
});
|
26
|
+
|
27
|
+
// update any .checked checkboxes on load
|
28
|
+
if(scope.$el.hasClass('checked')) {
|
29
|
+
scope.update();
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
// check radio button, uncheck all others in name group
|
34
|
+
RadioBtn.prototype.update = function() {
|
35
|
+
var // this specific radio button
|
36
|
+
$input = this.$el.find('input[type=radio]'),
|
37
|
+
$span = this.$el.find('span'),
|
38
|
+
// the group of radio buttons
|
39
|
+
group = 'input[name="'+$input.attr('name')+'"]';
|
40
|
+
|
41
|
+
// uncheck radio buttons in same group - uncheck input, remove checked class, remove <i>
|
42
|
+
$('.radio').has(group).removeClass('checked')
|
43
|
+
.find('input').prop('checked', false).end()
|
44
|
+
.find('i').remove();
|
45
|
+
|
46
|
+
// check this radio button - check input, add checked class, append <i>
|
47
|
+
$input.prop('checked', true);
|
48
|
+
$span.append('<i class="icon-dot" />');
|
49
|
+
this.$el.addClass('checked').trigger('gumby.onChange');
|
50
|
+
};
|
51
|
+
|
52
|
+
// add initialisation
|
53
|
+
Gumby.addInitalisation('radiobtns', function() {
|
54
|
+
$('.radio').each(function() {
|
55
|
+
var $this = $(this);
|
56
|
+
// this element has already been initialized
|
57
|
+
if($this.data('isRadioBtn')) {
|
58
|
+
return true;
|
59
|
+
}
|
60
|
+
// mark element as initialized
|
61
|
+
$this.data('isRadioBtn', true);
|
62
|
+
new RadioBtn($this);
|
63
|
+
});
|
64
|
+
});
|
65
|
+
|
66
|
+
// register UI module
|
67
|
+
Gumby.UIModule({
|
68
|
+
module: 'radiobtn',
|
69
|
+
events: ['onChange', 'check'],
|
70
|
+
init: function() {
|
71
|
+
Gumby.initialize('radiobtns');
|
72
|
+
}
|
73
|
+
});
|
74
|
+
}();
|
@@ -0,0 +1,74 @@
|
|
1
|
+
/**
|
2
|
+
* Gumby Retina
|
3
|
+
*/
|
4
|
+
!function() {
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
function Retina($el) {
|
9
|
+
this.$el = $el;
|
10
|
+
this.imageSrc = this.$el.attr('src');
|
11
|
+
this.retinaSrc = this.fetchRetinaImage();
|
12
|
+
this.$retinaImg = $(new Image());
|
13
|
+
|
14
|
+
var scope = this
|
15
|
+
|
16
|
+
// image src not valid
|
17
|
+
if(!this.retinaSrc) {
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
// load retina image
|
22
|
+
this.$retinaImg.attr('src', this.retinaSrc).load(function() {
|
23
|
+
scope.retinaImageLoaded();
|
24
|
+
});
|
25
|
+
}
|
26
|
+
|
27
|
+
// fetch retina src by appending '@2x' to image string before extension
|
28
|
+
Retina.prototype.fetchRetinaImage = function() {
|
29
|
+
var imgSrc = this.imageSrc,
|
30
|
+
index = this.imageSrc.search(/(\.|\/)(gif|jpe?g|png)$/i);
|
31
|
+
|
32
|
+
// image src is not valid
|
33
|
+
if(index < 0) {
|
34
|
+
return false;
|
35
|
+
}
|
36
|
+
|
37
|
+
// return retina src
|
38
|
+
return imgSrc.substr(0, index) + '@2x' + imgSrc.substr(index, imgSrc.length);
|
39
|
+
};
|
40
|
+
|
41
|
+
// once retina image loaded swap original src
|
42
|
+
Retina.prototype.retinaImageLoaded = function() {
|
43
|
+
this.$el.attr('src', this.$retinaImg.attr('src')).trigger('gumby.onRetina');
|
44
|
+
};
|
45
|
+
|
46
|
+
// add initialisation
|
47
|
+
Gumby.addInitalisation('retina', function() {
|
48
|
+
|
49
|
+
// this module is for retina devices only
|
50
|
+
if(!window.devicePixelRatio || window.devicePixelRatio <= 1) {
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
|
54
|
+
$('img[data-retina],img[gumby-retina],img[retina]').each(function() {
|
55
|
+
var $this = $(this);
|
56
|
+
// this element has already been initialized
|
57
|
+
if($this.data('isRetina')) {
|
58
|
+
return true;
|
59
|
+
}
|
60
|
+
// mark element as initialized
|
61
|
+
$this.data('isRetina', true);
|
62
|
+
new Retina($this);
|
63
|
+
});
|
64
|
+
});
|
65
|
+
|
66
|
+
// register UI module
|
67
|
+
Gumby.UIModule({
|
68
|
+
module: 'retina',
|
69
|
+
events: ['onRetina'],
|
70
|
+
init: function() {
|
71
|
+
Gumby.initialize('retina');
|
72
|
+
}
|
73
|
+
});
|
74
|
+
}();
|