bdb8e9f0b26baa090d77bab66d759d75413a4f01
4 * Generate an indented list of links from a nav. Meant for use with panel().
5 * @return {jQuery} jQuery object.
7 $.fn
.navList = function() {
16 indent
= Math
.max(0, $this.parents('li').length
- 1),
17 href
= $this.attr('href'),
18 target
= $this.attr('target');
22 'class="link depth-' + indent
+ '"' +
23 ( (typeof target
!== 'undefined' && target
!= '') ? ' target="' + target
+ '"' : '') +
24 ( (typeof href
!== 'undefined' && href
!= '') ? ' href="' + href
+ '"' : '') +
26 '<span class="indent-' + indent
+ '"></span>' +
38 * Panel-ify an element.
39 * @param {object} userConfig User config.
40 * @return {jQuery} jQuery object.
42 $.fn
.panel = function(userConfig
) {
49 if (this.length
> 1) {
51 for (var i
=0; i
< this.length
; i
++)
52 $(this[i
]).panel(userConfig
);
62 id
= $this.attr('id'),
71 // Hide panel on link click.
74 // Hide panel on escape keypress.
77 // Hide panel on swipe.
80 // Reset scroll position on hide.
83 // Reset forms on hide.
86 // Side of viewport the panel will appear.
89 // Target element for "class".
93 visibleClass
: 'visible'
97 // Expand "target" if it's not a jQuery object already.
98 if (typeof config
.target
!= 'jQuery')
99 config
.target
= $(config
.target
);
104 $this._hide = function(event
) {
106 // Already hidden? Bail.
107 if (!config
.target
.hasClass(config
.visibleClass
))
110 // If an event was provided, cancel it.
113 event
.preventDefault();
114 event
.stopPropagation();
119 config
.target
.removeClass(config
.visibleClass
);
122 window
.setTimeout(function() {
124 // Reset scroll position.
125 if (config
.resetScroll
)
129 if (config
.resetForms
)
130 $this.find('form').each(function() {
140 .css('-ms-overflow-style', '-ms-autohiding-scrollbar')
141 .css('-webkit-overflow-scrolling', 'touch');
144 if (config
.hideOnClick
) {
147 .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
150 .on('click', 'a', function(event
) {
153 href
= $a
.attr('href'),
154 target
= $a
.attr('target');
156 if (!href
|| href
== '#' || href
== '' || href
== '#' + id
)
159 // Cancel original event.
160 event
.preventDefault();
161 event
.stopPropagation();
167 window
.setTimeout(function() {
169 if (target
== '_blank')
172 window
.location
.href
= href
;
174 }, config
.delay
+ 10);
180 // Event: Touch stuff.
181 $this.on('touchstart', function(event
) {
183 $this.touchPosX
= event
.originalEvent
.touches
[0].pageX
;
184 $this.touchPosY
= event
.originalEvent
.touches
[0].pageY
;
188 $this.on('touchmove', function(event
) {
190 if ($this.touchPosX
=== null
191 || $this.touchPosY
=== null)
194 var diffX
= $this.touchPosX
- event
.originalEvent
.touches
[0].pageX
,
195 diffY
= $this.touchPosY
- event
.originalEvent
.touches
[0].pageY
,
196 th
= $this.outerHeight(),
197 ts
= ($this.get(0).scrollHeight
- $this.scrollTop());
200 if (config
.hideOnSwipe
) {
206 switch (config
.side
) {
209 result
= (diffY
< boundary
&& diffY
> (-1 * boundary
)) && (diffX
> delta
);
213 result
= (diffY
< boundary
&& diffY
> (-1 * boundary
)) && (diffX
< (-1 * delta
));
217 result
= (diffX
< boundary
&& diffX
> (-1 * boundary
)) && (diffY
> delta
);
221 result
= (diffX
< boundary
&& diffX
> (-1 * boundary
)) && (diffY
< (-1 * delta
));
231 $this.touchPosX
= null;
232 $this.touchPosY
= null;
241 // Prevent vertical scrolling past the top or bottom.
242 if (($this.scrollTop() < 0 && diffY
< 0)
243 || (ts
> (th
- 2) && ts
< (th
+ 2) && diffY
> 0)) {
245 event
.preventDefault();
246 event
.stopPropagation();
252 // Event: Prevent certain events inside the panel from bubbling.
253 $this.on('click touchend touchstart touchmove', function(event
) {
254 event
.stopPropagation();
257 // Event: Hide panel if a child anchor tag pointing to its ID is clicked.
258 $this.on('click', 'a[href="#' + id
+ '"]', function(event
) {
260 event
.preventDefault();
261 event
.stopPropagation();
263 config
.target
.removeClass(config
.visibleClass
);
269 // Event: Hide panel on body click/tap.
270 $body
.on('click touchend', function(event
) {
275 $body
.on('click', 'a[href="#' + id
+ '"]', function(event
) {
277 event
.preventDefault();
278 event
.stopPropagation();
280 config
.target
.toggleClass(config
.visibleClass
);
286 // Event: Hide on ESC.
287 if (config
.hideOnEscape
)
288 $window
.on('keydown', function(event
) {
290 if (event
.keyCode
== 27)
300 * Apply "placeholder" attribute polyfill to one or more forms.
301 * @return {jQuery} jQuery object.
303 $.fn
.placeholder = function() {
305 // Browser natively supports placeholders? Bail.
306 if (typeof (document
.createElement('input')).placeholder
!= 'undefined')
310 if (this.length
== 0)
313 // Multiple elements?
314 if (this.length
> 1) {
316 for (var i
=0; i
< this.length
; i
++)
317 $(this[i
]).placeholder();
327 $this.find('input[type=text],textarea')
333 || i
.val() == i
.attr('placeholder'))
335 .addClass('polyfill-placeholder')
336 .val(i
.attr('placeholder'));
339 .on('blur', function() {
343 if (i
.attr('name').match(/-polyfill-field$/))
348 .addClass('polyfill-placeholder')
349 .val(i
.attr('placeholder'));
352 .on('focus', function() {
356 if (i
.attr('name').match(/-polyfill-field$/))
359 if (i
.val() == i
.attr('placeholder'))
361 .removeClass('polyfill-placeholder')
367 $this.find('input[type=password]')
376 .replace(/type="password"/i, 'type="text"')
377 .replace(/type=password/i, 'type=text')
380 if (i
.attr('id') != '')
381 x
.attr('id', i
.attr('id') + '-polyfill-field');
383 if (i
.attr('name') != '')
384 x
.attr('name', i
.attr('name') + '-polyfill-field');
386 x
.addClass('polyfill-placeholder')
387 .val(x
.attr('placeholder')).insertAfter(i
);
395 .on('blur', function(event
) {
397 event
.preventDefault();
399 var x
= i
.parent().find('input[name=' + i
.attr('name') + '-polyfill-field]');
411 .on('focus', function(event
) {
413 event
.preventDefault();
415 var i
= x
.parent().find('input[name=' + x
.attr('name').replace('-polyfill-field', '') + ']');
424 .on('keypress', function(event
) {
426 event
.preventDefault();
435 .on('submit', function() {
437 $this.find('input[type=text],input[type=password],textarea')
438 .each(function(event
) {
442 if (i
.attr('name').match(/-polyfill-field$/))
445 if (i
.val() == i
.attr('placeholder')) {
447 i
.removeClass('polyfill-placeholder');
455 .on('reset', function(event
) {
457 event
.preventDefault();
460 .val($('option:first').val());
462 $this.find('input,textarea')
468 i
.removeClass('polyfill-placeholder');
477 i
.val(i
.attr('defaultValue'));
479 x
= i
.parent().find('input[name=' + i
.attr('name') + '-polyfill-field]');
494 i
.attr('checked', i
.attr('defaultValue'));
499 i
.val(i
.attr('defaultValue'));
502 i
.addClass('polyfill-placeholder');
503 i
.val(i
.attr('placeholder'));
509 i
.val(i
.attr('defaultValue'));
522 * Moves elements to/from the first positions of their respective parents.
523 * @param {jQuery} $elements Elements (or selector) to move.
524 * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
526 $.prioritize = function($elements
, condition
) {
528 var key
= '__prioritize';
530 // Expand $elements if it's not already a jQuery object.
531 if (typeof $elements
!= 'jQuery')
532 $elements
= $($elements
);
534 // Step through elements.
535 $elements
.each(function() {
537 var $e
= $(this), $p
,
538 $parent
= $e
.parent();
541 if ($parent
.length
== 0)
544 // Not moved? Move it.
547 // Condition is false? Bail.
551 // Get placeholder (which will serve as our point of reference for when this element needs to move back).
554 // Couldn't find anything? Means this element's already at the top, so bail.
558 // Move element to top of parent.
559 $e
.prependTo($parent
);
561 // Mark element as moved.
569 // Condition is true? Bail.
575 // Move element back to its original location (using our placeholder).
578 // Unmark element as moved.