Source: core/shims.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This shims class adds extra functionality to the core browser objects. It's used for
 * things like adding ES5 functionality where it makes sense to do so rather than using
 * Dojo to provide the cross-browser functionality.
 *
 * @module alfresco/core/shims
 * @author Martin Doyle
 */
define(["es6Promise"], function() {

   // Only apply the shims once!
   var _applied = false;

   // Pass back the shims object (not a class!)
   return {

      /**
       * Apply the current shims.
       *
       * @instance
       */
      apply: function alfresco_core_shim__apply() {
         if (_applied) {
            return;
         }
         this._addArrayIsArray();
         this._addArrayReduce();
         this._addDateNow();
         this._addObjectKeys();
         this._addRequestAnimationFrame();
         this._addTextContent();
         this._addES6Promise();
         _applied = true;
      },

      /**
       * Add an isArray() function to the global Array object.
       * From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Polyfill
       *
       * @protected
       * @instance
       * @since 1.0.56
       */
      _addArrayIsArray: function alfresco_core_shim___addArrayIsArray() {
         if (!Array.isArray) {
           Array.isArray = function(arg) {
             return Object.prototype.toString.call(arg) === "[object Array]";
           };
         }
      },

      /**
       * Add a reduce() function to the global Array object, because dojo/_base/array does not support it.
       * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Polyfill
       *
       * @protected
       * @instance
       * @since 1.0.52
       */
      _addArrayReduce: function alfresco_core_shim___addArrayReduce() {
         /*jshint bitwise:false,freeze:false,eqnull:true*/

         // Production steps of ECMA-262, Edition 5, 15.4.4.21
         // Reference: http://es5.github.io/#x15.4.4.21
         if (!Array.prototype.reduce) {
            Array.prototype.reduce = function(callback /*, initialValue*/ ) {
               "use strict";
               if (this == null) {
                  throw new TypeError("Array.prototype.reduce called on null or undefined");
               }
               if (typeof callback !== "function") {
                  throw new TypeError(callback + " is not a function");
               }
               var t = Object(this),
                  len = t.length >>> 0,
                  k = 0,
                  value;
               if (arguments.length === 2) {
                  value = arguments[1];
               } else {
                  while (k < len && !(k in t)) {
                     k++;
                  }
                  if (k >= len) {
                     throw new TypeError("Reduce of empty array with no initial value");
                  }
                  value = t[k++];
               }
               for (; k < len; k++) {
                  if (k in t) {
                     value = callback(value, t[k], k, t);
                  }
               }
               return value;
            };
         }
      },

      /**
       * Add a now() function to the global Date object.
       * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now
       *
       * @protected
       * @instance
       */
      _addDateNow: function alfresco_core_shim___addDateNow() {
         if (!Date.now) {
            Date.now = function now() {
               return new Date().getTime();
            };
         }
      },

      /**
       * Add a keys() function to the global Object object.
       * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
       *
       * @protected
       * @instance
       */
      _addObjectKeys: function alfresco_core_shim___addObjectKeys() {
         if (!Object.keys) {
            Object.keys = (function() {
               "use strict";
               var hasOwnProperty = Object.prototype.hasOwnProperty,
                  hasDontEnumBug = !({
                     toString: null
                  }).propertyIsEnumerable("toString"),
                  dontEnums = [
                     "toString",
                     "toLocaleString",
                     "valueOf",
                     "hasOwnProperty",
                     "isPrototypeOf",
                     "propertyIsEnumerable",
                     "constructor"
                  ],
                  dontEnumsLength = dontEnums.length;

               return function(obj) {
                  if (typeof obj !== "object" && (typeof obj !== "function" || obj === null)) {
                     throw new TypeError("Object.keys called on non-object");
                  }

                  var result = [],
                     prop, i;

                  for (prop in obj) {
                     if (hasOwnProperty.call(obj, prop)) {
                        result.push(prop);
                     }
                  }

                  if (hasDontEnumBug) {
                     for (i = 0; i < dontEnumsLength; i++) {
                        if (hasOwnProperty.call(obj, dontEnums[i])) {
                           result.push(dontEnums[i]);
                        }
                     }
                  }
                  return result;
               };
            }());
         }
      },

      /**
       * Shim for <=IE9 to add ES6 Promises. Uses code from
       * https://github.com/stefanpenner/es6-promise
       * 
       * @protected
       * @instance
       * @since 1.0.100
       */
      _addES6Promise: function alfresco_core_shim___addES6Promise() {
         require(["es6Promise"], function(es6shim) { 
            es6shim.polyfill();
         });
      },

      /**
       * Shim for <=IE9 to add requestAnimationFrame to the global object. Uses code from
       * http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
       *
       * @protected
       * @instance
       * @since 1.0.57
       */
      _addRequestAnimationFrame: function alfresco_core_shim___addRequestAnimationFrame() {
         var lastTime = 0;
         var vendors = ["webkit", "moz"];
         for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
            window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
            window.cancelAnimationFrame =
               window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"];
         }

         if (!window.requestAnimationFrame) {
            window.requestAnimationFrame = function(callback, /*jshint unused:false*/ element) {
               var currTime = new Date().getTime();
               var timeToCall = Math.max(0, 16 - (currTime - lastTime));
               var id = window.setTimeout(function() {
                     callback(currTime + timeToCall);
                  },
                  timeToCall);
               lastTime = currTime + timeToCall;
               return id;
            };
         }

         if (!window.cancelAnimationFrame) {
            window.cancelAnimationFrame = function(id) {
               clearTimeout(id);
            };
         }
      },

      /**
       * Allow setting of textContent on elements in IE8
       * From https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
       *
       * @protected
       * @instance
       * @since 1.0.49
       */
      _addTextContent: function alfresco_core_shim___addTextContent() {
         if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(Element.prototype, "textContent") && !Object.getOwnPropertyDescriptor(Element.prototype, "textContent").get) {
            (function() {
               var innerText = Object.getOwnPropertyDescriptor(Element.prototype, "innerText");
               Object.defineProperty(Element.prototype, "textContent", {
                  get: function() {
                     return innerText.get.call(this);
                  },
                  set: function(s) {
                     return innerText.set.call(this, s);
                  }
               });
            })();
         }
      }
   };
});