Advertisement
Alhadis

JavaScript Utility Functions

Oct 1st, 2014
554
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**============
  2. EDIT 2014-12-01: As of this writing, this file's now being curated on GitHub, where any and all future updates will be managed: https://github.com/Alhadis/Snippets
  3. ======= */
  4.  
  5.  
  6. function random(min, max){
  7.     return Math.floor(Math.random() * max - min + 1) + min;
  8. }
  9.  
  10. function percent(value, outOf, startAt){
  11.     var startAt =   (startAt == undefined) ? 0 : startAt;
  12.     return ((value-startAt) / (outOf-startAt)) * 100;
  13. }
  14.  
  15. function percentOf(percentage, outOf, startAt){
  16.     return ((percentage / 100) * (outOf - ((startAt == undefined) ? 0 : startAt)));
  17. }
  18.  
  19. /** Measures the arctangent between two points (the angle required for one point to face another). */
  20. function angleTo(a, b){
  21.     return (Math.atan2(b[1] - a[1], a[0] - b[0])) * 180 / Math.PI;
  22. }
  23.  
  24. /** Measures the distance between two points. */
  25. function distance(a, b){
  26.     return Math.sqrt(Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2));
  27. }
  28.  
  29. /** Converts radians to degrees. */
  30. function radToDeg(value){
  31.     return value * 180 / Math.PI;
  32. }
  33.  
  34. /** Converts degrees to radians. */
  35. function degToRad(value){
  36.     return value * Math.PI / 180;
  37. }
  38.  
  39.  
  40. /** Applies De Casteljau's algorithm to an array of points to ascertain the final midpoint. */
  41. function deCasteljau(points, p){
  42.     var a, b, i,=   p || .5,
  43.         midpoints   =   [];
  44.  
  45.     while(points.length > 1){
  46.  
  47.         for(i = 0; i < points.length - 1; ++i){
  48.             a   =   points[i];
  49.             b   =   points[i+1];
  50.  
  51.             midpoints.push([
  52.                 a[0] + ((b[0] - a[0]) * p),
  53.                 a[1] + ((b[1] - a[1]) * p)
  54.             ]);
  55.         }
  56.  
  57.         points      =   midpoints;
  58.         midpoints   =   [];
  59.     }
  60.  
  61.     return [points[0], a, b];
  62. }
  63.  
  64.  
  65.  
  66. /**
  67.  * Add leading zeros when necessary.
  68.  *
  69.  * @param {Number} value - The number being formatted.
  70.  * @param {Number} min - The minimum required length of the formatted number.
  71.  */
  72. function zeroise(value, min){
  73.     var val =   value.toString();
  74.     if(val.length < min)
  75.         val =   Array(min - val.length + 1).join("0") + val;
  76.     return val;
  77. }
  78.  
  79.  
  80.  
  81.  
  82. /**
  83.  * Formats a number of bytes for human-readable output.
  84.  * @param {Number} input - Number of bytes.
  85.  * @return {String} A reader-friendly representation of filesize.
  86.  */
  87. function formatBytes(input){
  88.     var bytes = new Array("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
  89.     for(val in bytes) if(input >= 1024) input = input / 1024; else break;
  90.     return Math.round(input*100)/100+" "+bytes[val];
  91. }
  92.  
  93.  
  94.  
  95. /**
  96.  * Parses a well-formed URL query string into an associative array.
  97.  *
  98.  * @param {String} q - If supplied, will be used instead of the current document's own URL.
  99.  * @return {Object} A hash enumerated with key/value pairs found in the parsed string.
  100.  */
  101. function unserialiseQuery(q){
  102.     var q   =   q || document.location.search;
  103.     if(!q) return {};
  104.     q   =   q.replace(/^\?/, "").split(/&/g);
  105.     for(var output = {}, i = 0; i < q.length; ++i){
  106.         if(!i) continue;
  107.         q[i]    =   q[i].split(/=/);
  108.         output[q[i][0]] =   q[i].slice(1).join("=");
  109.     }
  110.     return output;
  111. }
  112.  
  113.  
  114. /**
  115.  * Returns the subproperty located on an object at the designated path.
  116.  *
  117.  * For instance, to access a nested value of "App.config.pages[0].dialogue.title.innerText", simply pass that
  118.  * exact string sequence (minus "App.") to the function's second argument, with App passed to the first.
  119.  * Both dot and array-like accessor notation are supported.
  120.  *
  121.  * @param {Object} object - The subject to scan the properties of
  122.  * @param {String} path - An accessor string pointing to the desired property.
  123.  * @param {Boolean} useLast - If TRUE, will return the last valid value in an accessor chain if an invalid property was encountered.
  124.  * @return {Mixed} The referenced value, or undefined if the path pointed to an invalid property.
  125.  */
  126. function resolveProperty(object, path, useLast){
  127.     var path    =   path.replace(/\[(['"])?([^\]]+)\1\]/g, ".$2").split(/\./g),
  128.         prev    =   object, p, i = 0, l = path.length;
  129.     for(; i < l; ++i){
  130.         p   =   path[i];
  131.         if(prev === undefined || !(p in prev)) return useLast ? prev : undefined;
  132.         prev    =   prev[p];
  133.         if(i >= l-1) return prev;
  134.     }
  135.     return undefined;
  136. }
  137.  
  138.  
  139.  
  140. /**
  141.  * Returns the English ordinal suffix for a number (-st, -nd, -rd, -th)
  142.  *
  143.  * @param {Number} n - A number (preferably an integer) to return the suffix for.
  144.  * @return {String}
  145.  */
  146. function ordinalSuffix(n){
  147.     return [,"st", "nd", "rd"][((n %= 100) > 10 && n < 20) ? 0 : (n % 10)] || "th";
  148. }
  149.  
  150.  
  151. /**
  152.  * Returns a number of milliseconds from a string representing a time value in CSS.
  153.  *
  154.  * @param {String} t - A CSS time value such as "3200ms" or "4s".
  155.  * @return {Number}
  156.  */
  157. function parseDuration(t){
  158.     if(typeof t != "string")    return t;
  159.     if(/\ds\s*$/i.test(t))      return parseFloat(t) * 1000;
  160.     else                        return parseFloat(t);
  161. }
  162.  
  163.  
  164.  
  165. /**
  166.  * Ascertains a browser's support for a CSS property.
  167.  *
  168.  * @param {String} n - CSS property name, supplied in sentence case (e.g., "Transition")
  169.  * @return {Boolean} TRUE if the browser supports the property in either prefixed or unprefixed form.
  170.  */
  171. function cssSupport(n){
  172.     var s   =   document.documentElement.style;
  173.     if(n.toLowerCase() in s) return true;
  174.     for(var p = "Webkit Moz Ms O Khtml", p = (p.toLowerCase() + p).split(" "), i = 0; i < 10; ++i)
  175.         if(p[i]+n in s) return true;
  176.     return false;
  177. }
  178.  
  179.  
  180.  
  181.  
  182. /**
  183.  * Returns TRUE if a browser appears to support a given CSS unit.
  184.  *
  185.  * @param {String} unit - Name of a CSS unit (e.g., em, rem, vmax)
  186.  * @return {Boolean}
  187.  */
  188. function cssUnitSupport(unit){
  189.     try{
  190.         var d           =   document.createElement("div");
  191.         d.style.width   =   "32"+unit;
  192.         return d.style.width == "32"+unit;
  193.     }   catch(e){return false;}
  194. }
  195.  
  196.  
  197.  
  198. /**
  199.  * Returns the width of the scrollbars being displayed by this user's OS/device.
  200.  * @return {Number}
  201.  */
  202. function getScrollbarWidth(){
  203.     var el  =   document.createElement("div");
  204.     el.style.width      =   "120px";
  205.     el.style.height     =   "60px";
  206.     el.style.overflow   =   "auto";
  207.     el.innerHTML        =   Array(150).join(" W ");
  208.     (document.body || document.documentElement).appendChild(el);
  209.  
  210.     var output  =   el.offsetWidth - el.scrollWidth;
  211.     el.parentNode.removeChild(el);
  212.     return output;
  213. }
  214.  
  215.  
  216.  
  217.  
  218. /**
  219.  * Inclusive string splitting method. Similar to String.prototype.split, except
  220.  * matching results are always included as part of the returned array.
  221.  *
  222.  * @param {RegExp} pattern - The pattern to split the string by.
  223.  * @this {String}
  224.  * @return {Array}
  225.  */
  226. String.prototype.isplit =   function(pattern){
  227.     var output          =   [],
  228.         startFrom       =   0,
  229.         match;
  230.     while(match = pattern.exec(this)){
  231.         output.push(this.substring(startFrom, pattern.lastIndex - match[0].length), match[0]);
  232.         startFrom   =   pattern.lastIndex;
  233.     }
  234.     if(startFrom < this.length)
  235.         output.push(this.substring(startFrom, this.length));
  236.     return output;
  237. };
  238.  
  239.  
  240.  
  241. /**
  242.  * Converts a string to title case using crude/basic English capitalisation rules.
  243.  *
  244.  * @this {String}
  245.  * @return {String}
  246.  */
  247. String.prototype.toTitleCase    =   function(){
  248.     var ignore  =   (function(o){
  249.         var h   =   {};
  250.         for(var i in o) h[o[i]] = true;
  251.         return h;
  252.     }("the a an and but or nor of to in on for with to".split(" "))),
  253.  
  254.     o   =   this.toLowerCase().replace(/\b(\w)(\w+)?/gi, function(word, firstLetter, remainder, index, input){
  255.        
  256.         /** Matching a single letter. */
  257.         if(remainder === undefined){
  258.             return firstLetter.toUpperCase();
  259.         }
  260.         if( /** Ignore certain words that're supposed to be left lowercase between words. */
  261.             ignore[word] ||
  262.  
  263.             /** Beware of contractions. */
  264.             ("'" === input[index-1] && /\w'$/.test(input.substring(index, 0)))
  265.         )   return word;
  266.  
  267.         return firstLetter.toUpperCase() + remainder;
  268.     })
  269.  
  270.     /** Make sure "I" is always capitalised! */
  271.     .replace(/\bi\b/g, "I");
  272.  
  273.     return o[0].toUpperCase() + o.slice(1);
  274. };
  275.  
  276.  
  277.  
  278. /**
  279.  * Truncates a block of text to a designated number of characters or words, returning the split result as an array.
  280.  * Default behaviour is to split the text at 25 characters, breaking words if need be.
  281.  *
  282.  * @param {String} Text to operate on.
  283.  * @param {Object} Options for fine-tuning the truncation result. Possible options are:
  284.  *  -   by:         {Enum}      Whether to measure text based on word or character limit (Values: "char" or "word").
  285.  *  -   limit:      {Number}    Maximum number of words/characters permitted before breaking.
  286.  *  -   cutoff:     {Mixed}     Decides where and if to break a word to meet the character limit.
  287.  *  -   I'll finish this off later, probably...
  288.  */
  289. function truncate(string){
  290.     if(arguments.length < 2 || !string) return [string || ""];
  291.  
  292.     /** Default arguments. */
  293.     var args    =   {
  294.         limit:      25,
  295.         by:         "char",
  296.         cutoff:     "break",
  297.         trim:       true
  298.     };
  299.  
  300.  
  301.     /** If passed a number as our second argument, simply set the limit parameter. */
  302.     if("number" === typeof arguments[1])
  303.         args.limit  =   arguments[1];
  304.  
  305.  
  306.     /** Otherwise, simply merge our supplied arguments into our defaults. */
  307.     else for(var i in arguments[1])
  308.         args[i] =   arguments[1][i];
  309.    
  310.  
  311.     /** Lowercase our string-typed arguments for easier comparison. */
  312.     args.by         =   args.by.toLowerCase();
  313.     args.cutoff     =   "string" === typeof args.cutoff ? args.cutoff.toLowerCase() : +(args.cutoff);
  314.  
  315.  
  316.  
  317.     /** Trim leading/trailing whitespace from our string */
  318.     if(args.trim)
  319.         string  =   string.replace(/(^\s+|\s+$)/g, "");
  320.  
  321.  
  322.  
  323.     /** Truncating based on word count. */
  324.     if("word" === args.by){
  325.         var words   =   string.split(/\s+/);
  326.  
  327.         if(words.length <= args.limit) return [string];
  328.  
  329.         return [
  330.             words.slice(0, args.limit).join(" "),
  331.             words.slice(args.limit).join(" ")
  332.         ];
  333.     }
  334.  
  335.  
  336.  
  337.     /** Truncating based on character count (default behaviour). */
  338.     else{
  339.         if(string.length < args.limit) return [string];
  340.  
  341.  
  342.         /** Break text mid-word; or, the character at the cutoff point is whitespace anyway. */
  343.         if(!args.cutoff || "break" === args.cutoff || /\s/.test(string[args.limit])) return [
  344.             string.substring(0, args.limit),
  345.             string.substring(args.limit)
  346.         ];
  347.  
  348.  
  349.         /** Some word-preservation behaviour is in order, so let's dig a little closer into the string's contents. */
  350.         var before      =   string.substring(0, args.limit),
  351.             after       =   string.substring(args.limit),
  352.             lastStart   =   before.match(/(\s*)(\S+)$/),
  353.             lastEnd     =   after.match(/^\S+/);
  354.  
  355.  
  356.  
  357.         /** Always include the last word in the untruncated half of the string. */
  358.         if("after" === args.cutoff) return [
  359.             string.substring(0, before.length + lastEnd[0].length),
  360.             string.substring(   before.length + lastEnd[0].length)
  361.         ];
  362.  
  363.  
  364.         /** Never include the final word in the untruncated result. */
  365.         else if("before" === args.cutoff) return [
  366.             string.substring(0, before.length - lastStart[0].length),
  367.             string.substring(   before.length - lastStart[0].length)
  368.         ];
  369.  
  370.  
  371.         /** Otherwise, use an arbitrary threshold point to determine where the threshold should lie. */
  372.         else{
  373.             var lastWord    =   lastStart[2] + lastEnd;
  374.  
  375.             /** If supplied a floating point number, interpret it as a percentage of the affected word's length. */
  376.             if(args.cutoff > 0 && args.cutoff < 1)
  377.                 args.cutoff =   Math.round(lastWord.length * args.cutoff);
  378.  
  379.  
  380.             /** Word's cutoff length is still less than the desired truncation limit. Include the word in the first half. */
  381.             if(args.limit > (before.length - lastStart[2].length)+args.cutoff) return [
  382.                 string.substring(0, before.length - lastStart[0].length),
  383.                 string.substring(   before.length - lastStart[0].length)
  384.             ];
  385.  
  386.             /** Otherwise, do the opposite of what the above comment just said. */
  387.             return [
  388.                 string.substring(0, before.length + lastEnd[0].length),
  389.                 string.substring(   before.length + lastEnd[0].length)
  390.             ];
  391.         }
  392.     }
  393.  
  394.     return [string];
  395. }
  396.  
  397.  
  398.  
  399.  
  400. /**
  401.  * Executes a callback function on every text node found within an element's descendants.
  402.  *
  403.  * @param {Element} el - Element to parse the contents of.
  404.  * @param {Function} fn - Callback executed on each text node. Passed two args: the text node itself, and the currentl depth level.
  405.  * @param {Number} depth - Internal use only. Current number of recursion levels.
  406.  *
  407.  * @return {Element} The HTML element originally passed to the function.
  408.  */
  409. function walkTextNodes(el, fn, depth){
  410.     depth   =   depth || 0;
  411.  
  412.     var children    =   Array.prototype.slice.call(el.childNodes, 0);
  413.     for(var n, l = children.length, i = 0; i < l; ++i){
  414.         n   =   children[i];
  415.         if(n.nodeType === Node.TEXT_NODE)
  416.             fn.call(this, n, depth);
  417.         else if(n.nodeType === Node.ELEMENT_NODE)
  418.             walkTextNodes(n, fn, depth+1);
  419.     }
  420.     return el;
  421. };
  422.  
  423.  
  424. /**
  425.  * Injects <wbr /> elements into any lengthy words found in each text node found within an element's descendants.
  426.  *
  427.  * @uses walkTextNodes
  428.  * @param {Element} element - DOM element to operate on.
  429.  * @param {Number} limit - Number of characters to traverse in a single word before inserting a breakpoint.
  430.  */
  431. function injectWordBreaks(element, limit){
  432.  
  433.     walkTextNodes(element, function(node){
  434.         var original    =   node;
  435.         var terminators =   '.,+*?$|#{}()\\^\\-\\[\\]\\\\\/!%\'"~=<>_:;\\s';
  436.         var splitAt     =   new RegExp("([^" + terminators + "]{" + limit + "})", "g");
  437.        
  438.         /** Collect a list of insertion points. */
  439.         var breakPoints =   [];
  440.         while(splitAt.exec(node.data))
  441.             breakPoints.push(splitAt.lastIndex);
  442.    
  443.         for(var otherHalf, i = breakPoints.length - 1; i >= 0; --i){
  444.             otherHalf   =   node.splitText(breakPoints[i]);
  445.             node.parentNode.insertBefore(document.createElement("wbr"), otherHalf);
  446.         }
  447.     });
  448. }
  449.  
  450.  
  451. /**
  452.  * Checks if the user agent is a particular version of Internet Explorer.
  453.  *
  454.  * @param {String} version - The version to check against.
  455.  * @param {String} operand - Type of comparison to perform. Use basic JavaScript operators: <, >=, !=, etc.
  456.  * @return {Boolean}
  457.  */
  458. function isIE(version, operand){
  459.     var operands    =   {
  460.         "<":    "lt ",
  461.         "<=":   "lte ",
  462.         ">":    "gt ",
  463.         ">=":   "gte ",
  464.         "!=":   "!"
  465.     };
  466.  
  467.     var div         =   document.createElement("div");
  468.     div.innerHTML   =   "<!--[if "+(operands[operand] || "")+"IE "+version+"]><i></i><![endif]-->";
  469.     return div.getElementsByTagName("i").length;
  470. }
  471.  
  472.  
  473.  
  474.  
  475. /**
  476.  * Converts a camelCased string to its kebab-cased equivalent.
  477.  * Hilariously-named function entirely coincidental.
  478.  *
  479.  * @param {String} string - camelCasedStringToConvert
  480.  * @return {String} input-string-served-in-kebab-form
  481.  */
  482. function camelToKebabCase(string){
  483.  
  484.     /** Don't bother trying to transform a string that isn't well-formed camelCase. */
  485.     if(!/^([a-z]+[A-Z])+[a-z]+$/.test(string)) return string;
  486.  
  487.     return string.replace(/([a-z]+)([A-Z])/g, function(match, before, after){return before + "-" + after;}).toLowerCase();
  488. }
  489.  
  490.  
  491.  
  492. /**
  493.  * Allow an easy way of triggering JavaScript callbacks based on a hash an anchor tag points to.
  494.  *
  495.  * This also allows "hotlinking" to said actions by including the hash as part of the requested URL.
  496.  * For instance, the following would allow a gallery to be opened on page load:
  497.  *
  498.  *  <a href="#open-gallery">Browse gallery</a>
  499.  *  hashActions({ openGallery: function(){ galleryNode.classList.add("open"); } });
  500.  *
  501.  * @param {Object} actions - An object map of callbacks assigned by key.
  502.  */
  503. function hashActions(actions){
  504.     var addEvent    =   document.addEventListener || function(e,f){this.attachEvent("on"+e,f);};
  505.     for(var id in actions) (function(id, callback){
  506.         for(var id = camelToKebabCase(id), links = document.querySelectorAll('a[href="#' + id + '"]'), l = links.length, i = 0; i < l; ++i)
  507.             addEvent.call(links[i], "click", function(e){
  508.                 e.preventDefault ? e.preventDefault() : e.returnValue = false;
  509.                 callback.call(this, e);
  510.                 return false;
  511.             });
  512.  
  513.         /** Trigger the action's callback if its ID is in the document's hash. */
  514.         if(document.location.hash === "#"+id) callback();
  515.     }(id, actions[id]));
  516. }
  517.  
  518.  
  519.  
  520. /**
  521.  * Get or set the value of a cookie with the designated name.
  522.  *
  523.  * @param {String} name - Cookie's name
  524.  * @param {String} value - Value to assign to cookie. Passing NULL deletes the cookie.
  525.  * @param {Object} options - An object literal containing optional parameters to use when setting the cookie (expires/path/domain/secure).
  526.  * @return {String} The cookie's existing value if a value wasn't passed to the function.
  527.  */
  528. function cookie(name, value, options){
  529.  
  530.     /** If called without any arguments, or if an empty value's passed as our name parameter, return a hash of EVERY available cookie. */
  531.     if(!name){
  532.         var cookies =   document.cookie.split(/;\s*/g),
  533.             output  =   {},
  534.             cutoff, i, len;
  535.         for(i = 0, len = cookies.length; i < len; ++i)
  536.             if(cutoff = cookies[i].indexOf("="))
  537.                 output[cookies[i].substr(0, cutoff)] = decodeURIComponent(cookies[i].substr(cutoff+1));
  538.         return output;
  539.     }
  540.  
  541.  
  542.     /** Getter */
  543.     if(undefined === value){
  544.         cookies =   document.cookie.split(/;\s*/g),
  545.         cutoff  =   name.length + 1;
  546.  
  547.         for(var i = 0, len = cookies.length; i < len; ++i)
  548.             if(name+"=" === cookies[i].substr(0, cutoff))
  549.                 return decodeURIComponent(cookies[i].substr(cutoff));
  550.         return null;
  551.     }
  552.  
  553.  
  554.     /** Setter */
  555.     else{
  556.         options =   options || {};
  557.  
  558.         /** Delete a cookie */
  559.         if(null === value){
  560.             value           =   "";
  561.             options.expires =   -1;
  562.         }
  563.  
  564.         /** Expiry date */
  565.         if(options.expires){
  566.             var expiry  =   options.expires,
  567.  
  568.             /** If we weren't passed a Date instance as our expiry point, typecast the expiry option to an integer and use as a number of days from now. */
  569.             expiry  =   (!expiry.toUTCString ? new Date(Date.now() + (86400000 * expiry)) : expiry).toUTCString();
  570.         }
  571.  
  572.         document.cookie =   name+"="+encodeURIComponent(value) + (expiry ? "; expires="+expiry : "")
  573.             +   (options.path ? "; path="+options.path : "")
  574.             +   (options.domain ? "; domain="+options.domain : "")
  575.             +   (options.secure ? "; secure" : "");
  576.     }
  577. }
  578.  
  579.  
  580.  
  581. /**
  582.  * Wrapper for creating a new DOM element, optionally assigning it a hash of properties upon construction.
  583.  *
  584.  * @param {String} nodeType - Element type to create.
  585.  * @param {Object} obj - An optional hash of properties to assign the newly-created object.
  586.  * @return {Element}
  587.  */
  588. function New(nodeType, obj){
  589.     var node    =   document.createElement(nodeType),
  590.         absorb  =   function(a, b){
  591.             for(i in b)
  592.                 if(Object(a[i]) === a[i] && Object(b[i]) === b[i])
  593.                     absorb(a[i], b[i]);
  594.                 else a[i] = b[i];
  595.         };
  596.     if(obj) absorb(node, obj);
  597.     return node;
  598. }
  599.  
  600.  
  601.  
  602. /**
  603.  * Generates a base64-encoded 4x4-size PNG image of a designated RGBA value.
  604.  *
  605.  * @param {Number} r - Red component (0-255)
  606.  * @param {Number} g - Green component (0-255)
  607.  * @param {Number} b - Blue component (0-255)
  608.  * @param {Number} a - Alpha value (0-255: transparent to opaque)
  609.  *
  610.  * @return {String} A base64-encoded PNG image without a leading data URI prefix (no "data:image/png;base64,"...)
  611.  */
  612. function rgba(r, g, b, a){
  613.     var chr     =   String.fromCharCode,
  614.         fill    =   function(mult, str){ return Array(mult+1).join(str || "\0"); },
  615.         hton    =   function(i){ return String.fromCharCode(i >>> 24, i >>> 16 & 255, i >>> 8 & 255, i & 255); },
  616.  
  617.  
  618.         /** Binary output */
  619.         img     =   "\x89PNG\15\12\32\12\0\0\0\15IHDR\0\0\0\4\0\0\0\4\10\6\0\0\0\xA9\xF1\x9E~\0\0\0O",
  620.  
  621.         /** IDAT (Image Data) chunk. */
  622.         idat    =   "IDAT\10\35\1D\0\xBB\xFF",
  623.         data    =   "\1" + chr(r) + chr(g) + chr(b) + chr(a) + fill(12) + "\2" + fill(2, fill(16) + "\2") + fill(16),
  624.  
  625.         crc1    =   hton(function(data){
  626.             /** Addler's algorithm */
  627.             for(var a = 1, b = i = 0, l = data.length, k = 65521; i < l; ++i)
  628.                 a   =   (a + data.charCodeAt(i)) % k,
  629.                 b   =   (b + a) % k;
  630.             return b << 16 | a;
  631.         }(data)),
  632.  
  633.         crc2    =   hton(function(data){
  634.             /** CRC32 */
  635.             for(var c = ~0, i = 0; i < data.length; ++i)
  636.                 for(var b = data.charCodeAt(i) | 0x100; b != 1; b >>>= 1)
  637.                     c = (c >>> 1) ^ ((c ^ b) & 1 ? 0xEDB88320 : 0);
  638.             return ~c;
  639.         }(idat + data + crc1));
  640.  
  641.  
  642.         /** Stitch the IDAT chunk together and write the IEND chunk to wrap it up. */
  643.         return (function(data){
  644.  
  645.             /** Base64-encode that bitch. */
  646.             for(var enc = "", c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", i = 5, n = data.length * 8 + 5; i < n; i += 6)
  647.                 enc += c[(data.charCodeAt(~~(i/8)-1) << 8 | data.charCodeAt(~~(i/8))) >> 7 - i%8 & 63];
  648.             for(; enc.length % 4; enc += "=");
  649.             return enc;
  650.  
  651.         }(  img + idat+data+crc1+crc2 + fill(4)+"IEND\xAEB`\x82" ));
  652. }
  653.  
  654.  
  655.  
  656. /** Compressed version for use in production sites. */
  657. function rgba(r,n,t,o){var x=String.fromCharCode,e=function(r,n){return Array(r+1).join(n||"\0")},a=function(r){return String.fromCharCode(r>>>24,r>>>16&255,r>>>8&255,255&r)},f="\x89PNG\15\12\32\12\0\0\0\15IHDR\0\0\0\4\0\0\0\4\10\6\0\0\0\xA9\xF1\x9E~\0\0\0O",u="IDAT\10\35\1D\0\xBB\xFF",c="\1"+x(r)+x(n)+x(t)+x(o)+e(12)+"\2"+e(2,e(16)+"\2")+e(16),h=a(function(r){for(var n=1,t=i=0,o=r.length,x=65521;o>i;++i)n=(n+r.charCodeAt(i))%x,t=(t+n)%x;return t<<16|n}(c)),C=a(function(r){for(var n=-1,t=0;t<r.length;++t)for(var o=256|r.charCodeAt(t);1!=o;o>>>=1)n=n>>>1^(1&(n^o)?3988292384:0);return~n}(u+c+h));return function(r){for(var n="",t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=5,x=8*r.length+5;x>o;o+=6)n+=t[(r.charCodeAt(~~(o/8)-1)<<8|r.charCodeAt(~~(o/8)))>>7-o%8&63];for(;n.length%4;n+="=");return n}(f+u+c+h+C+e(4)+"IEND\xAEB`\x82")}
  658.  
  659.  
  660.  
  661.  
  662. /** Decodes a UTF-8 string into a stream of single-byte sequences. */
  663. function UTF8Decode(data){
  664.     for(var char = String.fromCharCode, data = data.replace(/\r\n/g, "\n"), s = "", c, i = 0, l = data.length; i < l; ++i){
  665.         c   =   data.charCodeAt(i);
  666.         if(c < 128)                         s   +=  char(c);
  667.         else if((c > 127) && (c < 2048))    s   +=  char((c >> 6)   | 192) + char((c & 63)          | 128);
  668.         else                                s   +=  char((c >> 12)  | 224) + char(((c >> 6) & 63)   | 128) + char((c & 63) | 128);
  669.     }
  670.     return s;
  671. };
  672.  
  673. /** Encodes a sequence of single-byte characters as a UTF-8 string. */
  674. function UTF8Encode(data){
  675.     var s   =
  676.         c   =   "",
  677.         i   =   0,
  678.  
  679.         length  =   data.length,
  680.         at      =   "charCodeAt",
  681.         char    =   String.fromCharCode;
  682.  
  683.     while(i < length){
  684.         c   =   data[at](i);
  685.         if(c < 128){                        s   +=  char(c); ++i;   }
  686.         else if((c > 191) && (c < 224)){    s   +=  char(((c & 31) << 6)    | (data[at](i+1) & 63));    i += 2; }
  687.         else{                               s   +=  char(((c & 15) << 12)   | ((data[at](i+1) & 63) << 6) | (data[at](i+2) & 63)); i += 3; }
  688.     }
  689.  
  690.     return s;
  691. };
  692.  
  693.  
  694.  
  695. /** Encodes a string using MIME Base64 */
  696. function base64Encode(data){
  697.  
  698.     /** Convert UTF-8 strings to whatever "normal" encoding is needed for JavaScript to safely manipulate at binary-level. */
  699.     data    =   (function(data){
  700.         for(var char = String.fromCharCode, data = data.replace(/\r\n/g, "\n"), s = "", c, i = 0, l = data.length; i < l; ++i){
  701.             c   =   data.charCodeAt(i);
  702.             if(c < 128)                         s   +=  char(c);
  703.             else if((c > 127) && (c < 2048))    s   +=  char((c >> 6)   | 192) + char((c & 63)          | 128);
  704.             else                                s   +=  char((c >> 12)  | 224) + char(((c >> 6) & 63)   | 128) + char((c & 63) | 128);
  705.         }
  706.         return s;
  707.     }(data));
  708.  
  709.  
  710.     /** Apply the actual base64 encoding */
  711.     for(var enc = "", c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", i = 5, n = data.length * 8 + 5; i < n; i += 6)
  712.         enc += c[(data.charCodeAt(~~(i/8)-1) << 8 | data.charCodeAt(~~(i/8))) >> 7 - i%8 & 63];
  713.     for(; enc.length % 4; enc += "=");
  714.     return enc;
  715. }
  716.  
  717.  
  718.  
  719. /** Decodes a base64-encoded string */
  720. function base64Decode(data){
  721.     var a = b = c = d = s = "",
  722.  
  723.     char    =   String.fromCharCode,
  724.     codex   =   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
  725.     data    =   data.replace(/[^A-Za-z0-9\+\/=]/g, ""),
  726.     i       =   0,
  727.     l       =   data.length;
  728.  
  729.     while(i < l){
  730.         a   =   codex.indexOf(data[i++]),
  731.         b   =   codex.indexOf(data[i++]),
  732.         c   =   codex.indexOf(data[i++]),
  733.         d   =   codex.indexOf(data[i++]);
  734.                         s   +=  char((a << 2) | (b >> 4));
  735.         if(64 !== c)    s   +=  char(((b & 15) << 4) | (c >> 2));
  736.         if(64 !== d)    s   +=  char(((c & 3) << 6) | d);
  737.     }
  738.  
  739.     /** Re-encode the data as UTF-8 */
  740.     s   =   (function(data){
  741.         var s = c = "",
  742.             i       =   0,
  743.             length  =   data.length,
  744.             at      =   "charCodeAt",
  745.             char    =   String.fromCharCode;
  746.  
  747.         while(i < length){
  748.             c   =   data[at](i);
  749.             if(c < 128){                        s   +=  char(c); ++i;   }
  750.             else if((c > 191) && (c < 224)){    s   +=  char(((c & 31) << 6)    | (data[at](i+1) & 63));    i += 2; }
  751.             else{                               s   +=  char(((c & 15) << 12)   | ((data[at](i+1) & 63) << 6) | (data[at](i+2) & 63)); i += 3; }
  752.         }
  753.  
  754.         return s;
  755.     }(s));
  756.  
  757.     return s;
  758. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement