Imgpop

| Comments

Now I will going into technical details inside imgpop.

Like I said in the previous post. I extends Brian’s imgpopup and do some makeup to things. At the backend, I use mini_magick to actually save the resized image, and use that as preview/thumbnail. Here’s the snippet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#...#
# Open the source image, and scale it accordingly.
image = MiniMagick::Image.open(image_path)
vars['full_width'] = image[:width]
vars['full_height'] = image[:height]
image.resize "#{@percent}%"

rpath = source+ resized_image
if not File.exists? rpath
# Actually save the image
  image.format "jpg"
  image.quality "92"
  image.write rpath

  # This is the tricky part
  # we should register the new created file
  # since Jekyll already indexed all files before
  context.registers[:site].static_files << StaticFile.new(
          context.registers[:site], source, File.dirname(@path.sub(%r{^/}, '')),
          "resized_#{File.basename(@path)}")
  print "image saved to #{rpath}\n"
end
#...#

I also change the tag into imgpop, add new parameter for the <img> class, and made all the parameters mandatory.

I moved all the logic into separate file other than the template. So now we have the original template img_popup.html.erb and javascript file called imgpop.js.

The template now looks like this.

(img_popup.html.erb) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- -*- html -*- -->
<a href='<%= image %>' style="text-decoration: none" id="image-<%= id %>"><img src="<%= scaled_image %>" alt="Click me." class="<%= klass %>"/></a>
  <div id="image-dialog-<%= id %>" class="noTitle">
    <div class="image-caption" align="center">
      <div class="capt-left"><%= title %></div>
      <div class="capt-right">
        <a href="<%= image %>">permalink</a>
      </div>
    </div>
    <div id="proxy<%= id %>" align="center" class="proxy-container">
      <img class="image-drag" src="/images/1px.png" id="full_image-<%= id %>" />
    </div>
  </div>
  <script type="text/javascript">
    jQuery(document).ready(function() {
        jQuery.imgpop({
          id: "<%= id %>",
          full_image: "<%= image %>",
          full_width: <%= full_width %>,
          full_height: <%= full_height %>,
          title: "<%= title %>"
        });
    });
  </script>

Above, I only have to call jQuery.imgpop, and pass full image data, id and the title. It’s pretty neat.

All the logic happen in imgpop.js. You can load this file after jQuery UI. For octopress, you can put the script tag at /source/_include/custom/head.html file.

(imgpop.js) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
if (jQuery && typeof jQuery == "function") {
  jQuery(document).ready(function () {

    // Watch for click event at overlay screen
    jQuery(".ui-widget-overlay").live("click", function () {
      jQuery(".noTitle").dialog("close");
    });

    jQuery.imgpop = function (opts) {
      var id = opts.id,
          full_image = opts.full_image,
          fw = opts.full_width,
          fh = opts.full_height,
          title = opts.title,
          tw = jQuery(window).width() - 40,
          th = jQuery(window).height() - 40,
          loaded = false,
          image_dialog = "#image-dialog-" + id,
          proxy_div = "#proxy" + id,
          fimgdiv = "#full_image-" + id,
          imgdiv = "#image-" + id;

      // FIXME: firefox has bug that will download image
      // multiple times when you change <img> `src` from
      // javascript. However, this is only happened if the
      // `src` is set to another image (1x1 px transparent
      // png in this case). This workaround will empty `src`
      // attribute before it's changed to the full-size image.
      jQuery.each(jQuery(".image-drag"), function (i, v) {
        v.src = "";
      });

      jQuery(image_dialog).hide();

      jQuery(image_dialog).dialog({
        dialogClass: 'noTitle',
        autoOpen:  false,
        modal:     true,
        resizable: false,
        minWidth:  (fw >= tw) ? tw : fw,
        height:    (fh >= th) ? th : fh,
        "title":   title,
        show:      'scale',
        hide:      'scale'
      });

      // calculate proxy size for draggable containment
      var dw = (fw < tw) ? 0 : (fw - tw),
      dh = (fh < th) ? 0 : (fh - th),
      w = jQuery(image_dialog).dialog("option", "minWidth") + dw * 2,
      h = jQuery(image_dialog).dialog("option", "height") + dh * 2;
      jQuery(proxy_div).width(w);
      jQuery(proxy_div).height(h);

      // put proxy at the center of viewport
      var l = 0 - dw, t = 0 - dh;
      jQuery(proxy_div).css('left', l + 'px');
      jQuery(proxy_div).css('top', t + 'px');

      // put image at this specific position,
      // so th top-left area of the image is showed.
      jQuery(fimgdiv).css('left', dw + 'px');
      jQuery(fimgdiv).css('top', dh + 'px');

      // Make image draggable
      jQuery(fimgdiv).draggable({
        containment: 'parent',
        cursor: "move",
        scroll: false
      });

      jQuery(imgdiv).click(function() {
        jQuery(image_dialog).dialog('open');
        if (!loaded) { // poorman's caching system.
          jQuery(fimgdiv)[0].src=full_image;
          loaded = true;
        }
        return false;
      });

      jQuery(fimgdiv).click(function () {
        jQuery(image_dialog).dialog('close');
      });
    }
  });
}

I should highlight the FIXME part above. The workaround emptied the src, since if I leave it to /images/1px.png firefox will do this: For some reason firefox download the same image multiple times.

The last one is its stylesheet.

(_styles.scss) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
.image-drag {
    position: absolute !important;
}

.image-caption {
    z-index: 2000;
    position: absolute;
    left: 0px;
    right: 0px;
    height: 24px;
    padding: 10px;

}

.image-caption:hover > div {
    display: block;
}

.image-caption:hover {
    background: rgba(0, 0, 0, 0.6);
}

.capt-left {
    display: none;
    float: left;
    color: white;
}

.capt-right {
    display: none;
    float: right;
}

.ui-widget-content .ui-dialog-content {
    padding: 0px;
}

.ui-widget-content a {
    color: white;
}

.noTitle .ui-dialog-titlebar { display: none; }

.noTitle {
    position: fixed;
};

.ui-widget-overlay {
    position: fixed;
}

.proxy-container {
    position: absolute;
}

There’s nothing special I guess, except that I should remind myself about html layouting system. For position property, absolute value means that the element positioned relative to its non static parent. While fixed value would put the element relative to the viewport.

The full repo can be accessed here.
It is released under BSD license. Please take a look, all suggestions are welcomed!

Comments