Advertisement
Alhadis

DOMTokenList Polyfill

Oct 1st, 2014
521
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * DOMTokenList polyfill
  3.  * https://github.com/Alhadis/DOMTokenList-Polyfill
  4.  *
  5.  * Tested with IE8 & IE9.
  6.  * Naturally, this script does absolutely nothing if the browser already supports DOMTokenList (which is virtually every modern browser, it's just versions of Explorer between 8-9 that can't hack it).
  7.  */
  8.  
  9. /** COMPRESSED: */
  10. window.DOMTokenList||function(){var t="defineProperty"in Object||"__defineGetter__"in Object.prototype||null,e=function(e,n,i,r){Object.defineProperty?Object.defineProperty(e,n,{configurable:!1===t?!0:!!r,get:i}):e.__defineGetter__(n,i)};if(t)try{e({},"support")}catch(n){t=!1}var i=function(t,n){var i,r=[],l={},s=0,o=0,a=function(){if(s>=o)for(;s>o;++o)(function(t){e(this,t,function(){return c.call(this),r[t]},!1)}).call(this,o)},c=function(){var e;if(arguments.length)for(e=0;e<arguments.length;++e)if(/\s/.test(arguments[e])){var o=new SyntaxError('String "'+arguments[e]+'" contains an invalid character');throw o.code=5,o.name="InvalidCharacterError",o}if(i!==t[n]){r=(""+t[n]).replace(/^\s+|\s+$/g,"").split(/\s+/),l={};for(var e=0;e<r.length;++e)l[r[e]]=!0;s=r.length,a.call(this)}};return c.call(this),e(this,"length",function(){return c.call(this),s}),this.toLocaleString=this.toString=function(){return c.call(this),r.join(" ")},this.item=function(t){return c.call(this),r[t]},this.contains=function(t){return c.call(this),!!l[t]},this.add=function(){c.apply(this,arguments);for(var e,i=0;i<arguments.length;++i)e=arguments[i],l[e]||(r.push(e),l[e]=!0);s!==r.length&&(s=r.length>>>0,t[n]=r.join(" "),a.call(this))},this.remove=function(){c.apply(this,arguments);for(var e={},i=0;i<arguments.length;++i)e[arguments[i]]=!0,delete l[arguments[i]];for(var o=[],i=0;i<r.length;++i)e[r[i]]||o.push(r[i]);r=o,s=o.length>>>0,t[n]=r.join(" "),a.call(this)},this.toggle=function(t,e){return c.apply(this,[t]),void 0!==e?e?(this.add(t),!0):(this.remove(t),!1):l[t]?(this.remove(t),!1):(this.add(t),!0)},function(t,e){if(e)for(var n="item contains add remove toggle toString toLocaleString".split(" "),i=0;7>i;++i)e(t,n[i],{enumerable:!1})}(this,Object.defineProperty),this};i.polyfill=!0,window.DOMTokenList=i;var r=function(n,l,s){e(n.prototype,l,function(){var n,o="__defining_"+l+"__";if(this[o])return n;if(this[o]=!0,!1===t){for(var a,c=r.mirror=r.mirror||document.createElement("div"),h=c.childNodes,u=h.length,f=0;u>f;++f)if(h[f].reflectedElement===this){a=h[f];break}a||(a=document.createElement("div"),c.appendChild(a)),n=i.call(a,this,s)}else n=new i(this,s);return e(this,l,function(){return n}),delete this[o],n},!0)};r(Element,"classList","className"),r(HTMLLinkElement,"relList","rel"),r(HTMLAnchorElement,"relList","rel"),r(HTMLAreaElement,"relList","rel")}();
  11.  
  12.  
  13. /** UNCOMPRESSED: */
  14. if(!window.DOMTokenList)
  15. (function(){
  16.  
  17.  
  18.     /** Ascertain browser support for Object.defineProperty */
  19.     var dpSupport   =   "defineProperty" in Object || "__defineGetter__" in Object.prototype || null;
  20.  
  21.  
  22.     /** Wrapper for Object.defineProperty that falls back to using the legacy __defineGetter__ method if available. */
  23.     var defineGetter    =   function(object, name, fn, configurable){
  24.         if(Object.defineProperty)
  25.             Object.defineProperty(object, name, {
  26.                 configurable:   false === dpSupport ? true : !!configurable,
  27.                 get:            fn
  28.             });
  29.  
  30.         else object.__defineGetter__(name, fn);
  31.     };
  32.  
  33.  
  34.     /** Ensure the browser allows Object.defineProperty to be used on native JavaScript objects. */
  35.     if(dpSupport)
  36.         try{ defineGetter({}, "support"); }
  37.         catch(e){ dpSupport = false; }
  38.  
  39.  
  40.  
  41.     var DOMTokenList    =   function(el, prop){
  42.  
  43.         /** Private variables */
  44.         var tokens      =   [],
  45.             tokenMap    =   {},
  46.             length      =   0,
  47.             maxLength   =   0,
  48.             lastValue,
  49.  
  50.  
  51.         reindex     =   function(){
  52.  
  53.             /** Define getter functions for array-like access to the tokenList's contents. */
  54.             if(length >= maxLength){
  55.                 for(; maxLength < length; ++maxLength){
  56.  
  57.                     (function(i){
  58.                         defineGetter(this, i, function(){ preop.call(this); return tokens[i]; }, false);
  59.                     }).call(this, maxLength);
  60.                 }
  61.             }
  62.         },
  63.  
  64.        
  65.         /** Helper function called at the start of each class method. Internal use only. */
  66.         preop   =   function(){
  67.             var i;
  68.  
  69.             /** Validate the token/s passed to an instance method, if any. */
  70.             if(arguments.length){
  71.                 for(i = 0; i < arguments.length; ++i)
  72.                     if(/\s/.test(arguments[i])){
  73.                         var error   =   new SyntaxError('String "' + arguments[i] + '" contains an invalid character');
  74.                         error.code  =   5;
  75.                         error.name  =   "InvalidCharacterError";
  76.                         throw error;
  77.                     }
  78.             }
  79.  
  80.  
  81.             /** Check if the subject attribute of the target element has changed since the tokenList was last used. If so, repopulate the internal token lists. */
  82.             if(lastValue !== el[prop]){
  83.                 tokens      =   ("" + el[prop]).replace(/^\s+|\s+$/g, "").split(/\s+/);
  84.                 tokenMap    =   {};
  85.                 for(var i = 0; i < tokens.length; ++i)
  86.                     tokenMap[tokens[i]] =   true;
  87.                 length  =   tokens.length;
  88.                 reindex.call(this);
  89.             }
  90.         };
  91.  
  92.  
  93.  
  94.         /** Populate our internal token lists if the targeted attribute of the subject element isn't empty. */
  95.         preop.call(this);
  96.  
  97.  
  98.  
  99.         /** Returns the number of tokens in the underlying string. Read-only. */
  100.         defineGetter(this, "length", function(){ preop.call(this); return length; });
  101.  
  102.  
  103.         /** Override the default toString method to return a space-delimited list of tokens when typecast. */
  104.         this.toLocaleString =
  105.         this.toString       =   function(){
  106.             preop.call(this);
  107.             return tokens.join(" ");
  108.         };
  109.  
  110.  
  111.  
  112.         /** Returns an item in the list by its index (or undefined if the number is greater than or equal to the length of the list) */
  113.         this.item   =   function(idx){
  114.             preop.call(this);
  115.             return tokens[idx];
  116.         };
  117.  
  118.  
  119.         /** Returns TRUE if the underlying string contains `token`; otherwise, FALSE. */
  120.         this.contains   =   function(token){
  121.             preop.call(this);
  122.             return !!tokenMap[token];
  123.         };
  124.  
  125.  
  126.  
  127.         /** Adds one or more tokens to the underlying string. */
  128.         this.add    =   function(){
  129.             preop.apply(this, arguments);
  130.            
  131.             for(var token, i = 0; i < arguments.length; ++i){
  132.                 token   =   arguments[i];
  133.                 if(!tokenMap[token]){
  134.                     tokens.push(token);
  135.                     tokenMap[token] =   true;
  136.                 }
  137.             }
  138.  
  139.             /** Update the targeted attribute of the attached element if the token list's changed. */
  140.             if(length !== tokens.length){
  141.                 length      =   tokens.length >>> 0;
  142.                 el[prop]    =   tokens.join(" ");
  143.                 reindex.call(this);
  144.             }
  145.         };
  146.  
  147.  
  148.         /** Removes one or more tokens from the underlying string. */
  149.         this.remove =   function(){
  150.             preop.apply(this, arguments);
  151.  
  152.             /** Build a hash of token names to compare against when recollecting our token list., */
  153.             for(var ignore = {}, i = 0; i < arguments.length; ++i){
  154.                 ignore[arguments[i]]    =   true;
  155.                 delete tokenMap[arguments[i]];
  156.             }
  157.  
  158.             /** Run through our tokens list and reassign only those that aren't defined in the hash declared above. */
  159.             for(var t = [], i = 0; i < tokens.length; ++i)
  160.                 if(!ignore[tokens[i]]) t.push(tokens[i]);
  161.  
  162.             tokens  =   t;
  163.             length  =   t.length >>> 0;
  164.  
  165.             /** Update the targeted attribute of the attached element. */
  166.             el[prop]    =   tokens.join(" ");
  167.             reindex.call(this);
  168.         };
  169.  
  170.  
  171.         /** Adds or removes a token depending on whether it's already contained within the token list. */
  172.         this.toggle =   function(token, force){
  173.             preop.apply(this, [token]);
  174.  
  175.             /** Token state's being forced. */
  176.             if(undefined !== force){
  177.                 if(force)   {   this.add(token);    return true;    }
  178.                 else        {   this.remove(token); return false;   }
  179.             }
  180.  
  181.             /** Token already exists in tokenList. Remove it, and return FALSE. */
  182.             if(tokenMap[token]){
  183.                 this.remove(token);
  184.                 return false;
  185.             }
  186.  
  187.             /** Otherwise, add the token and return TRUE. */
  188.             this.add(token);
  189.             return true;
  190.         };
  191.  
  192.  
  193.         /** Mark our newly-assigned methods as non-enumerable. */
  194.         (function(o, defineProperty){
  195.             if(defineProperty)
  196.                 for(var methods = "item contains add remove toggle toString toLocaleString".split(" "), i = 0; i < 7; ++i)
  197.                     defineProperty(o, methods[i], {enumerable: false});
  198.         }(this, Object.defineProperty));
  199.         return this;
  200.     };
  201.  
  202.     DOMTokenList.polyfill   =   true;
  203.     window.DOMTokenList     =   DOMTokenList;
  204.  
  205.  
  206.  
  207.     /** Polyfill our properties */
  208.     var addProp =   function(o, name, attr){
  209.  
  210.         defineGetter(o.prototype, name, function(){
  211.             var tokenList, cork = "__defining_" + name + "__";
  212.             if(this[cork]) return tokenList;
  213.  
  214.             this[cork]  =   true;
  215.  
  216.  
  217.             /** IE8 can't define properties on native JavaScript objects, so we'll use a retarded hack instead. */
  218.             if(false === dpSupport){
  219.  
  220.                 var mirror = addProp.mirror     =   addProp.mirror || document.createElement("div"),
  221.                     reflections                 =   mirror.childNodes,
  222.                    
  223.                     /** Iterator variables */
  224.                     l   = reflections.length,
  225.                     i   = 0,
  226.                     visage;
  227.  
  228.                 for(; i < l; ++i)
  229.                     if(reflections[i].reflectedElement === this){
  230.                         visage  =   reflections[i];
  231.                         break;
  232.                     }
  233.  
  234.                 /** Couldn't find an element's reflection inside the mirror. Materialise one. */
  235.                 if(!visage){
  236.                     visage  =   document.createElement("div");
  237.                     /*visage.IE8_GETTER_REFLECTION  =   this;*/
  238.                     mirror.appendChild(visage);
  239.                 }
  240.  
  241.                 tokenList   =   DOMTokenList.call(visage, this, attr);
  242.             }
  243.  
  244.             else tokenList  =   new DOMTokenList(this, attr);
  245.  
  246.  
  247.             defineGetter(this, name, function(){ return tokenList; });
  248.             delete this[cork];
  249.  
  250.             return tokenList;
  251.         }, true);
  252.     };
  253.  
  254.     addProp( Element,           "classList",    "className");
  255.     addProp( HTMLLinkElement,   "relList",      "rel");
  256.     addProp( HTMLAnchorElement, "relList",      "rel");
  257.     addProp( HTMLAreaElement,   "relList",      "rel");
  258. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement