has.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. define(["require", "module"], function(require, module){
  2. // module:
  3. // dojo/has
  4. // summary:
  5. // Defines the has.js API and several feature tests used by dojo.
  6. // description:
  7. // This module defines the has API as described by the project has.js with the following additional features:
  8. //
  9. // - the has test cache is exposed at has.cache.
  10. // - the method has.add includes a forth parameter that controls whether or not existing tests are replaced
  11. // - the loader's has cache may be optionally copied into this module's has cahce.
  12. //
  13. // This module adopted from https://github.com/phiggins42/has.js; thanks has.js team!
  14. // try to pull the has implementation from the loader; both the dojo loader and bdLoad provide one
  15. // if using a foreign loader, then the has cache may be initialized via the config object for this module
  16. // WARNING: if a foreign loader defines require.has to be something other than the has.js API, then this implementation fail
  17. var has = require.has || function(){};
  18. if(!has("dojo-has-api")){
  19. var
  20. isBrowser =
  21. // the most fundamental decision: are we in the browser?
  22. typeof window != "undefined" &&
  23. typeof location != "undefined" &&
  24. typeof document != "undefined" &&
  25. window.location == location && window.document == document,
  26. // has API variables
  27. global = (function () { return this; })(),
  28. doc = isBrowser && document,
  29. element = doc && doc.createElement("DiV"),
  30. cache = (module.config && module.config()) || {};
  31. has = function(name){
  32. // summary:
  33. // Return the current value of the named feature.
  34. //
  35. // name: String|Integer
  36. // The name (if a string) or identifier (if an integer) of the feature to test.
  37. //
  38. // description:
  39. // Returns the value of the feature named by name. The feature must have been
  40. // previously added to the cache by has.add.
  41. return typeof cache[name] == "function" ? (cache[name] = cache[name](global, doc, element)) : cache[name]; // Boolean
  42. };
  43. has.cache = cache;
  44. has.add = function(name, test, now, force){
  45. // summary:
  46. // Register a new feature test for some named feature.
  47. // name: String|Integer
  48. // The name (if a string) or identifier (if an integer) of the feature to test.
  49. // test: Function
  50. // A test function to register. If a function, queued for testing until actually
  51. // needed. The test function should return a boolean indicating
  52. // the presence of a feature or bug.
  53. // now: Boolean?
  54. // Optional. Omit if `test` is not a function. Provides a way to immediately
  55. // run the test and cache the result.
  56. // force: Boolean?
  57. // Optional. If the test already exists and force is truthy, then the existing
  58. // test will be replaced; otherwise, add does not replace an existing test (that
  59. // is, by default, the first test advice wins).
  60. // example:
  61. // A redundant test, testFn with immediate execution:
  62. // | has.add("javascript", function(){ return true; }, true);
  63. //
  64. // example:
  65. // Again with the redundantness. You can do this in your tests, but we should
  66. // not be doing this in any internal has.js tests
  67. // | has.add("javascript", true);
  68. //
  69. // example:
  70. // Three things are passed to the testFunction. `global`, `document`, and a generic element
  71. // from which to work your test should the need arise.
  72. // | has.add("bug-byid", function(g, d, el){
  73. // | // g == global, typically window, yadda yadda
  74. // | // d == document object
  75. // | // el == the generic element. a `has` element.
  76. // | return false; // fake test, byid-when-form-has-name-matching-an-id is slightly longer
  77. // | });
  78. (typeof cache[name]=="undefined" || force) && (cache[name]= test);
  79. return now && has(name);
  80. };
  81. // since we're operating under a loader that doesn't provide a has API, we must explicitly initialize
  82. // has as it would have otherwise been initialized by the dojo loader; use has.add to the builder
  83. // can optimize these away iff desired
  84. has.add("host-browser", isBrowser);
  85. has.add("host-node", (typeof process == "object" && process.versions && process.versions.node && process.versions.v8));
  86. has.add("host-rhino", (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object")));
  87. has.add("dom", isBrowser);
  88. has.add("dojo-dom-ready-api", 1);
  89. has.add("dojo-sniff", 1);
  90. }
  91. if(has("host-browser")){
  92. // Common application level tests
  93. has.add("dom-addeventlistener", !!document.addEventListener);
  94. // Do the device and browser have touch capability?
  95. has.add("touch", "ontouchstart" in document
  96. || ("onpointerdown" in document && navigator.maxTouchPoints > 0)
  97. || window.navigator.msMaxTouchPoints);
  98. // Touch events support
  99. has.add("touch-events", "ontouchstart" in document);
  100. // Pointer Events support
  101. has.add("pointer-events", "onpointerdown" in document);
  102. has.add("MSPointer", "msMaxTouchPoints" in navigator); //IE10 (+IE11 preview)
  103. // I don't know if any of these tests are really correct, just a rough guess
  104. has.add("device-width", screen.availWidth || innerWidth);
  105. // Tests for DOMNode.attributes[] behavior:
  106. // - dom-attributes-explicit - attributes[] only lists explicitly user specified attributes
  107. // - dom-attributes-specified-flag (IE8) - need to check attr.specified flag to skip attributes user didn't specify
  108. // - Otherwise, in IE6-7. attributes[] will list hundreds of values, so need to do outerHTML to get attrs instead.
  109. var form = document.createElement("form");
  110. has.add("dom-attributes-explicit", form.attributes.length == 0); // W3C
  111. has.add("dom-attributes-specified-flag", form.attributes.length > 0 && form.attributes.length < 40); // IE8
  112. }
  113. has.clearElement = function(element){
  114. // summary:
  115. // Deletes the contents of the element passed to test functions.
  116. element.innerHTML= "";
  117. return element;
  118. };
  119. has.normalize = function(id, toAbsMid){
  120. // summary:
  121. // Resolves id into a module id based on possibly-nested tenary expression that branches on has feature test value(s).
  122. //
  123. // toAbsMid: Function
  124. // Resolves a relative module id into an absolute module id
  125. var
  126. tokens = id.match(/[\?:]|[^:\?]*/g), i = 0,
  127. get = function(skip){
  128. var term = tokens[i++];
  129. if(term == ":"){
  130. // empty string module name, resolves to 0
  131. return 0;
  132. }else{
  133. // postfixed with a ? means it is a feature to branch on, the term is the name of the feature
  134. if(tokens[i++] == "?"){
  135. if(!skip && has(term)){
  136. // matched the feature, get the first value from the options
  137. return get();
  138. }else{
  139. // did not match, get the second value, passing over the first
  140. get(true);
  141. return get(skip);
  142. }
  143. }
  144. // a module
  145. return term || 0;
  146. }
  147. };
  148. id = get();
  149. return id && toAbsMid(id);
  150. };
  151. has.load = function(id, parentRequire, loaded){
  152. // summary:
  153. // Conditional loading of AMD modules based on a has feature test value.
  154. // id: String
  155. // Gives the resolved module id to load.
  156. // parentRequire: Function
  157. // The loader require function with respect to the module that contained the plugin resource in it's
  158. // dependency list.
  159. // loaded: Function
  160. // Callback to loader that consumes result of plugin demand.
  161. if(id){
  162. parentRequire([id], loaded);
  163. }else{
  164. loaded();
  165. }
  166. };
  167. return has;
  168. });