Initial commit
[editorial.git] / assets / main / js / util.js
diff --git a/assets/main/js/util.js b/assets/main/js/util.js
new file mode 100644 (file)
index 0000000..bdb8e9f
--- /dev/null
@@ -0,0 +1,587 @@
+(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