Template:Harvard BioDesign/JS Scroll5

(function($) {

 /**
  * Generate an indented list of links from a nav. Meant for use with panel().
  * @return {jQuery} jQuery object.
  */
 $.fn.navList = function() {
   var  $this = $(this);
     $a = $this.find('a'),
     b = [];
   $a.each(function() {
     var  $this = $(this),
       indent = Math.max(0, $this.parents('li').length - 1),
       href = $this.attr('href'),
       target = $this.attr('target');
     b.push(
       '<a ' +
         'class="link depth-' + indent + '"' +
         ( (typeof target !== 'undefined' && target != ) ? ' target="' + target + '"' : ) +
         ( (typeof href !== 'undefined' && href != ) ? ' href="' + href + '"' : ) +
       '>' +
         '' +
         $this.text() +
       '</a>'
     );
   });
   return b.join();
 };
 /**
  * Panel-ify an element.
  * @param {object} userConfig User config.
  * @return {jQuery} jQuery object.
  */
 $.fn.panel = function(userConfig) {
   // No elements?
     if (this.length == 0)
       return $this;
   // Multiple elements?
     if (this.length > 1) {
       for (var i=0; i < this.length; i++)
         $(this[i]).panel(userConfig);
       return $this;
     }
   // Vars.
     var  $this = $(this),
       $body = $('body'),
       $window = $(window),
       id = $this.attr('id'),
       config;
   // Config.
     config = $.extend({
       // Delay.
         delay: 0,
       // Hide panel on link click.
         hideOnClick: false,
       // Hide panel on escape keypress.
         hideOnEscape: false,
       // Hide panel on swipe.
         hideOnSwipe: false,
       // Reset scroll position on hide.
         resetScroll: false,
       // Reset forms on hide.
         resetForms: false,
       // Side of viewport the panel will appear.
         side: null,
       // Target element for "class".
         target: $this,
       // Class to toggle.
         visibleClass: 'visible'
     }, userConfig);
     // Expand "target" if it's not a jQuery object already.
       if (typeof config.target != 'jQuery')
         config.target = $(config.target);
   // Panel.
     // Methods.
       $this._hide = function(event) {
         // Already hidden? Bail.
           if (!config.target.hasClass(config.visibleClass))
             return;
         // If an event was provided, cancel it.
           if (event) {
             event.preventDefault();
             event.stopPropagation();
           }
         // Hide.
           config.target.removeClass(config.visibleClass);
         // Post-hide stuff.
           window.setTimeout(function() {
             // Reset scroll position.
               if (config.resetScroll)
                 $this.scrollTop(0);
             // Reset forms.
               if (config.resetForms)
                 $this.find('form').each(function() {
                   this.reset();
                 });
           }, config.delay);
       };
     // Vendor fixes.
       $this
         .css('-ms-overflow-style', '-ms-autohiding-scrollbar')
         .css('-webkit-overflow-scrolling', 'touch');
     // Hide on click.
       /*if (config.hideOnClick) {
         $this.find('a')
           .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
         $this
           .on('click', 'a', function(event) {
             var $a = $(this),
               href = $a.attr('href'),
               target = $a.attr('target');
             if (!href || href == '#' || href ==  || href == '#' + id)
               return;
             // Cancel original event.
               event.preventDefault();
               event.stopPropagation();
             // Hide panel.
               $this._hide();
             // Redirect to href.
               window.setTimeout(function() {
                 if (target == '_blank')
                   window.open(href);
                 else
                   window.location.href = href;
               }, config.delay + 10);
           });
       }*/
     // Event: Touch stuff.
       $this.on('touchstart', function(event) {
         $this.touchPosX = event.originalEvent.touches[0].pageX;
         $this.touchPosY = event.originalEvent.touches[0].pageY;
       })
       $this.on('touchmove', function(event) {
         if ($this.touchPosX === null
         ||  $this.touchPosY === null)
           return;
         var  diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
           diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
           th = $this.outerHeight(),
           ts = ($this.get(0).scrollHeight - $this.scrollTop());
         // Hide on swipe?
           if (config.hideOnSwipe) {
             var result = false,
               boundary = 20,
               delta = 50;
             switch (config.side) {
               case 'left':
                 result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
                 break;
               case 'right':
                 result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
                 break;
               case 'top':
                 result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
                 break;
               case 'bottom':
                 result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
                 break;
               default:
                 break;
             }
             if (result) {
               $this.touchPosX = null;
               $this.touchPosY = null;
               $this._hide();
               return false;
             }
           }
         // Prevent vertical scrolling past the top or bottom.
           if (($this.scrollTop() < 0 && diffY < 0)
           || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
             event.preventDefault();
             event.stopPropagation();
           }
       });
     // Event: Prevent certain events inside the panel from bubbling.
       $this.on('click touchend touchstart touchmove', function(event) {
         event.stopPropagation();
       });
     // Event: Hide panel if a child anchor tag pointing to its ID is clicked.
       $this.on('click', 'a[href="#' + id + '"]', function(event) {
         event.preventDefault();
         event.stopPropagation();
         config.target.removeClass(config.visibleClass);
       });
   // Body.
     // Event: Hide panel on body click/tap.
       $body.on('click touchend', function(event) {
         $this._hide(event);
       });
     // Event: Toggle.
       $body.on('click', 'a[href="#' + id + '"]', function(event) {
         event.preventDefault();
         event.stopPropagation();
         config.target.toggleClass(config.visibleClass);
       });
   // Window.
     // Event: Hide on ESC.
       if (config.hideOnEscape)
         $window.on('keydown', function(event) {
           if (event.keyCode == 27)
             $this._hide(event);
         });
   return $this;
 };
 /**
  * Apply "placeholder" attribute polyfill to one or more forms.
  * @return {jQuery} jQuery object.
  */
 $.fn.placeholder = function() {
   // Browser natively supports placeholders? Bail.
     if (typeof (document.createElement('input')).placeholder != 'undefined')
       return $(this);
   // No elements?
     if (this.length == 0)
       return $this;
   // Multiple elements?
     if (this.length > 1) {
       for (var i=0; i < this.length; i++)
         $(this[i]).placeholder();
       return $this;
     }
   // Vars.
     var $this = $(this);
   // Text, TextArea.
     $this.find('input[type=text],textarea')
       .each(function() {
         var i = $(this);
         if (i.val() == 
         ||  i.val() == i.attr('placeholder'))
           i
             .addClass('polyfill-placeholder')
             .val(i.attr('placeholder'));
       })
       .on('blur', function() {
         var i = $(this);
         if (i.attr('name').match(/-polyfill-field$/))
           return;
         if (i.val() == )
           i
             .addClass('polyfill-placeholder')
             .val(i.attr('placeholder'));
       })
       .on('focus', function() {
         var i = $(this);
         if (i.attr('name').match(/-polyfill-field$/))
           return;
         if (i.val() == i.attr('placeholder'))
           i
             .removeClass('polyfill-placeholder')
             .val();
       });
   /*// Password.
     $this.find('input[type=password]')
       .each(function() {
         var i = $(this);
         var x = $(
$('
')
                 .append(i.clone())
                 .remove()
                 .html()
                 .replace(/type="password"/i, 'type="text"')
                 .replace(/type=password/i, 'type=text')
         );
         if (i.attr('id') != )
           x.attr('id', i.attr('id') + '-polyfill-field');
         if (i.attr('name') != )
           x.attr('name', i.attr('name') + '-polyfill-field');
         x.addClass('polyfill-placeholder')
           .val(x.attr('placeholder')).insertAfter(i);
         if (i.val() == )
           i.hide();
         else
           x.hide();
         i
           .on('blur', function(event) {
             event.preventDefault();
             var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
             if (i.val() == ) {
               i.hide();
               x.show();
             }
           });
         x
           .on('focus', function(event) {
             event.preventDefault();
             var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', ) + ']');
             x.hide();
             i
               .show()
               .focus();
           })
           .on('keypress', function(event) {
             event.preventDefault();
             x.val();
           });
       });*/
   // Events.
     $this
       .on('submit', function() {
         $this.find('input[type=text],input[type=password],textarea')
           .each(function(event) {
             var i = $(this);
             if (i.attr('name').match(/-polyfill-field$/))
               i.attr('name', );
             if (i.val() == i.attr('placeholder')) {
               i.removeClass('polyfill-placeholder');
               i.val();
             }
           });
       })
       .on('reset', function(event) {
         event.preventDefault();
         $this.find('select')
           .val($('option:first').val());
         $this.find('input,textarea')
           .each(function() {
             var i = $(this),
               x;
             i.removeClass('polyfill-placeholder');
             switch (this.type) {
               case 'submit':
               case 'reset':
                 break;
               case 'password':
                 i.val(i.attr('defaultValue'));
                 x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
                 if (i.val() == ) {
                   i.hide();
                   x.show();
                 }
                 else {
                   i.show();
                   x.hide();
                 }
                 break;
               case 'checkbox':
               case 'radio':
                 i.attr('checked', i.attr('defaultValue'));
                 break;
               case 'text':
               case 'textarea':
                 i.val(i.attr('defaultValue'));
                 if (i.val() == ) {
                   i.addClass('polyfill-placeholder');
                   i.val(i.attr('placeholder'));
                 }
                 break;
               default:
                 i.val(i.attr('defaultValue'));
                 break;
             }
           });
       });
   return $this;
 };
 /**
  * Moves elements to/from the first positions of their respective parents.
  * @param {jQuery} $elements Elements (or selector) to move.
  * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
  */
 $.prioritize = function($elements, condition) {
   var key = '__prioritize';
   // Expand $elements if it's not already a jQuery object.
     if (typeof $elements != 'jQuery')
       $elements = $($elements);
   // Step through elements.
     $elements.each(function() {
       var  $e = $(this), $p,
         $parent = $e.parent();
       // No parent? Bail.
         if ($parent.length == 0)
           return;
       // Not moved? Move it.
         if (!$e.data(key)) {
           // Condition is false? Bail.
             if (!condition)
               return;
           // Get placeholder (which will serve as our point of reference for when this element needs to move back).
             $p = $e.prev();
             // Couldn't find anything? Means this element's already at the top, so bail.
               if ($p.length == 0)
                 return;
           // Move element to top of parent.
             $e.prependTo($parent);
           // Mark element as moved.
             $e.data(key, $p);
         }
       // Moved already?
         else {
           // Condition is true? Bail.
             if (condition)
               return;
           $p = $e.data(key);
           // Move element back to its original location (using our placeholder).
             $e.insertAfter($p);
           // Unmark element as moved.
             $e.removeData(key);
         }
     });
 };
})(jQuery);