retina.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /*!
  2. * Retina.js v1.3.0
  3. *
  4. * Copyright 2014 Imulus, LLC
  5. * Released under the MIT license
  6. *
  7. * Retina.js is an open source script that makes it easy to serve
  8. * high-resolution images to devices with retina displays.
  9. */
  10. (function() {
  11. var root = (typeof exports === 'undefined' ? window : exports);
  12. var config = {
  13. // An option to choose a suffix for 2x images
  14. retinaImageSuffix : '@2x',
  15. // Ensure Content-Type is an image before trying to load @2x image
  16. // https://github.com/imulus/retinajs/pull/45)
  17. check_mime_type: true,
  18. // Resize high-resolution images to original image's pixel dimensions
  19. // https://github.com/imulus/retinajs/issues/8
  20. force_original_dimensions: true
  21. };
  22. function Retina() {}
  23. root.Retina = Retina;
  24. Retina.configure = function(options) {
  25. if (options === null) {
  26. options = {};
  27. }
  28. for (var prop in options) {
  29. if (options.hasOwnProperty(prop)) {
  30. config[prop] = options[prop];
  31. }
  32. }
  33. };
  34. Retina.init = function(context) {
  35. if (context === null) {
  36. context = root;
  37. }
  38. var existing_onload = context.onload || function(){};
  39. context.onload = function() {
  40. var images = document.getElementsByTagName('img'), retinaImages = [], i, image;
  41. for (i = 0; i < images.length; i += 1) {
  42. image = images[i];
  43. if (!!!image.getAttributeNode('data-no-retina')) {
  44. retinaImages.push(new RetinaImage(image));
  45. }
  46. }
  47. existing_onload();
  48. };
  49. };
  50. Retina.isRetina = function(){
  51. var mediaQuery = '(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-resolution: 1.5dppx)';
  52. if (root.devicePixelRatio > 1) {
  53. return true;
  54. }
  55. if (root.matchMedia && root.matchMedia(mediaQuery).matches) {
  56. return true;
  57. }
  58. return false;
  59. };
  60. var regexMatch = /\.\w+$/;
  61. function suffixReplace (match) {
  62. return config.retinaImageSuffix + match;
  63. }
  64. function RetinaImagePath(path, at_2x_path) {
  65. this.path = path || '';
  66. if (typeof at_2x_path !== 'undefined' && at_2x_path !== null) {
  67. this.at_2x_path = at_2x_path;
  68. this.perform_check = false;
  69. } else {
  70. if (undefined !== document.createElement) {
  71. var locationObject = document.createElement('a');
  72. locationObject.href = this.path;
  73. locationObject.pathname = locationObject.pathname.replace(regexMatch, suffixReplace);
  74. this.at_2x_path = locationObject.href;
  75. } else {
  76. var parts = this.path.split('?');
  77. parts[0] = parts[0].replace(regexMatch, suffixReplace);
  78. this.at_2x_path = parts.join('?');
  79. }
  80. this.perform_check = true;
  81. }
  82. }
  83. root.RetinaImagePath = RetinaImagePath;
  84. RetinaImagePath.confirmed_paths = [];
  85. RetinaImagePath.prototype.is_external = function() {
  86. return !!(this.path.match(/^https?\:/i) && !this.path.match('//' + document.domain) );
  87. };
  88. RetinaImagePath.prototype.check_2x_variant = function(callback) {
  89. var http, that = this;
  90. if (this.is_external()) {
  91. return callback(false);
  92. } else if (!this.perform_check && typeof this.at_2x_path !== 'undefined' && this.at_2x_path !== null) {
  93. return callback(true);
  94. } else if (this.at_2x_path in RetinaImagePath.confirmed_paths) {
  95. return callback(true);
  96. } else {
  97. http = new XMLHttpRequest();
  98. http.open('HEAD', this.at_2x_path);
  99. http.onreadystatechange = function() {
  100. if (http.readyState !== 4) {
  101. return callback(false);
  102. }
  103. if (http.status >= 200 && http.status <= 399) {
  104. if (config.check_mime_type) {
  105. var type = http.getResponseHeader('Content-Type');
  106. if (type === null || !type.match(/^image/i)) {
  107. return callback(false);
  108. }
  109. }
  110. RetinaImagePath.confirmed_paths.push(that.at_2x_path);
  111. return callback(true);
  112. } else {
  113. return callback(false);
  114. }
  115. };
  116. http.send();
  117. }
  118. };
  119. function RetinaImage(el) {
  120. this.el = el;
  121. this.path = new RetinaImagePath(this.el.getAttribute('src'), this.el.getAttribute('data-at2x'));
  122. var that = this;
  123. this.path.check_2x_variant(function(hasVariant) {
  124. if (hasVariant) {
  125. that.swap();
  126. }
  127. });
  128. }
  129. root.RetinaImage = RetinaImage;
  130. RetinaImage.prototype.swap = function(path) {
  131. if (typeof path === 'undefined') {
  132. path = this.path.at_2x_path;
  133. }
  134. var that = this;
  135. function load() {
  136. if (! that.el.complete) {
  137. setTimeout(load, 5);
  138. } else {
  139. if (config.force_original_dimensions) {
  140. that.el.setAttribute('width', that.el.offsetWidth);
  141. that.el.setAttribute('height', that.el.offsetHeight);
  142. }
  143. that.el.setAttribute('src', path);
  144. }
  145. }
  146. load();
  147. };
  148. if (Retina.isRetina()) {
  149. Retina.init(root);
  150. }
  151. })();