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