image_layout.js 5.4 KB

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