image_layout.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /**
  2. *
  3. * Google Image Layout v0.0.1
  4. * Description, by Anh Trinh.
  5. * Heavily modified for searx
  6. * https://ptgamr.github.io/2014-09-12-google-image-layout/
  7. * https://ptgamr.github.io/google-image-layout/src/google-image-layout.js
  8. *
  9. * @license Free to use under the MIT License.
  10. *
  11. */
  12. (function (w, d) {
  13. function ImageLayout(container_selector, results_selector, img_selector, margin, maxHeight) {
  14. this.container_selector = container_selector;
  15. this.results_selector = results_selector;
  16. this.img_selector = img_selector;
  17. this.margin = margin;
  18. this.maxHeight = maxHeight;
  19. this.isAlignDone = true;
  20. }
  21. /**
  22. * Get the height that make all images fit the container
  23. *
  24. * width = w1 + w2 + w3 + ... = r1*h + r2*h + r3*h + ...
  25. *
  26. * @param {[type]} images the images to be calculated
  27. * @param {[type]} width the container witdth
  28. * @param {[type]} margin the margin between each image
  29. *
  30. * @return {[type]} the height
  31. */
  32. ImageLayout.prototype._getHeigth = function (images, width) {
  33. var i, img;
  34. var r = 0;
  35. for (i = 0; i < images.length; i++) {
  36. img = images[i];
  37. if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
  38. r += img.naturalWidth / img.naturalHeight;
  39. } else {
  40. // assume that not loaded images are square
  41. r += 1;
  42. }
  43. }
  44. return (width - images.length * this.margin) / r; //have to round down because Firefox will automatically roundup value with number of decimals > 3
  45. };
  46. ImageLayout.prototype._setSize = function (images, height) {
  47. var i, img, imgWidth;
  48. var imagesLength = images.length, resultNode;
  49. for (i = 0; i < imagesLength; i++) {
  50. img = images[i];
  51. if ((img.naturalWidth > 0) && (img.naturalHeight > 0)) {
  52. imgWidth = height * img.naturalWidth / img.naturalHeight;
  53. } else {
  54. // not loaded image : make it square as _getHeigth said it
  55. imgWidth = height;
  56. }
  57. img.style.width = imgWidth + 'px';
  58. img.style.height = height + 'px';
  59. img.style.marginLeft = '3px';
  60. img.style.marginTop = '3px';
  61. img.style.marginRight = this.margin - 7 + 'px'; // -4 is the negative margin of the inline element
  62. img.style.marginBottom = this.margin - 7 + 'px';
  63. resultNode = img.parentNode.parentNode;
  64. if (!resultNode.classList.contains('js')) {
  65. resultNode.classList.add('js');
  66. }
  67. }
  68. };
  69. ImageLayout.prototype._alignImgs = function (imgGroup) {
  70. var isSearching, slice, i, h;
  71. var containerElement = d.querySelector(this.container_selector);
  72. var containerCompStyles = window.getComputedStyle(containerElement);
  73. var containerPaddingLeft = parseInt(containerCompStyles.getPropertyValue('padding-left'), 10);
  74. var containerPaddingRight = parseInt(containerCompStyles.getPropertyValue('padding-right'), 10);
  75. var containerWidth = containerElement.clientWidth - containerPaddingLeft - containerPaddingRight;
  76. while (imgGroup.length > 0) {
  77. isSearching = true;
  78. for (i = 1; i <= imgGroup.length && isSearching; i++) {
  79. slice = imgGroup.slice(0, i);
  80. h = this._getHeigth(slice, containerWidth);
  81. if (h < this.maxHeight) {
  82. this._setSize(slice, h);
  83. // continue with the remaining images
  84. imgGroup = imgGroup.slice(i);
  85. isSearching = false;
  86. }
  87. }
  88. if (isSearching) {
  89. this._setSize(slice, Math.min(this.maxHeight, h));
  90. break;
  91. }
  92. }
  93. };
  94. ImageLayout.prototype.align = function () {
  95. var i;
  96. var results_selectorNode = d.querySelectorAll(this.results_selector);
  97. var results_length = results_selectorNode.length;
  98. var previous = null;
  99. var current = null;
  100. var imgGroup = [];
  101. for (i = 0; i < results_length; i++) {
  102. current = results_selectorNode[i];
  103. if (current.previousElementSibling !== previous && imgGroup.length > 0) {
  104. // the current image is not connected to previous one
  105. // so the current image is the start of a new group of images.
  106. // so call _alignImgs to align the current group
  107. this._alignImgs(imgGroup);
  108. // and start a new empty group of images
  109. imgGroup = [];
  110. }
  111. // add the current image to the group (only the img tag)
  112. imgGroup.push(current.querySelector(this.img_selector));
  113. // update the previous variable
  114. previous = current;
  115. }
  116. // align the remaining images
  117. if (imgGroup.length > 0) {
  118. this._alignImgs(imgGroup);
  119. }
  120. };
  121. ImageLayout.prototype.watch = function () {
  122. var i, img;
  123. var obj = this;
  124. var results_nodes = d.querySelectorAll(this.results_selector);
  125. var results_length = results_nodes.length;
  126. function throttleAlign() {
  127. if (obj.isAlignDone) {
  128. obj.isAlignDone = false;
  129. setTimeout(function () {
  130. obj.align();
  131. obj.isAlignDone = true;
  132. }, 100);
  133. }
  134. }
  135. w.addEventListener('pageshow', throttleAlign);
  136. w.addEventListener('load', throttleAlign);
  137. w.addEventListener('resize', throttleAlign);
  138. for (i = 0; i < results_length; i++) {
  139. img = results_nodes[i].querySelector(this.img_selector);
  140. if (img !== null && img !== undefined) {
  141. img.addEventListener('load', throttleAlign);
  142. img.addEventListener('error', throttleAlign);
  143. }
  144. }
  145. };
  146. w.searx.ImageLayout = ImageLayout;
  147. }(window, document));