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