Dienstag, 26. November 2013

jQuery sticky sidebar plugin

For a small project I had to search for a new sticky sidebar plugin which would meet my needs. Previously I used the plugin made by p-m-p but it had some issues with not dealing with a position to stop and if the element was not fitting to screen it moved it below the site bottom edge, so the new edge was prolonged. A user could almost scroll into infinity.

Some days ago I found another sticky sidebar plugin made by Spoiled Milk which seems to have almost perfect functionality for my purposes, it can be configured pretty good. The only small issue I had I could circumvent by adding one line and modifying another line of code to be perfect for me. My problem was that the top edge of the element I wanted to be sticky wasn't just below a generic horizontal element but one of several in a vertical row with my element being the last, so it wasn't sufficient to just get the element above my sticky element as header and getting it's height like the plugin does it as of the time of this post being written, I have to add the top offset relative to the top edge of the document.

Search for the follwing line:
var breakingPoint1 = headerHeight + navHeight;

Replace it with the following two lines:
var headerTop = $(config.headerSelector).offset().top;
var breakingPoint1 = headerTop + headerHeight + navHeight;

Et voilà, it works as intended!

Freitag, 22. November 2013

Magento, Plesk, nginx and PHP-FPM

Since v11.5 Plesk has the ability to manage PHP websites without Apache by using the ability of nginx to run PHP directly via PHP-FPM, this is considered to be a faster way to run PHP scripts with generally lower memory footprint as nginx is really small.

Magento needs rewrite feature for internal SEO, while Apache has his mod_rewrite and the ability to use .htaccess files to configure its features, nginx doesn't offer such dynamic configuration, it's done statically so it doesn't have to search the path for .htaccess files and try to parse it, which happens on every webserver access and the price of slowing down the webservice while keeping the flexibility.

There are several tutorials across the web (e.g. nginx wiki) which show how to configure nginx, so Magento will run without .htaccess which are quite good but will not work with Plesk, which of course adds some own nginx configuration that collides with the nginx configuration in the tutorials.

The general problem is that Plesk defines its own "location /" and while you can work it around by using "location ~ (.*)" for the most important instruction "try_files $uri $uri/ @handler;" to run "location @handler { rewrite / /index.php; }", this will not work for urls with trailing slash which Magento generates for example for CMS sites, these will deliver 404 because Plesk adds following to every nginx config:
    location ~ /$ {
        index index.html index.cgi index.pl index.php index.xhtml index.htm index.shtml;
    }

The Plesk per-site nginx custom config is included inside the server scope, so a workaround using a combination of some ifs and rewrites is possible to circumvent this.

The following nginx config pasted into the Plesk custom nginx seems to do the magic properly:

## Taken from http://www.linux-magazin.de/Ausgaben/2012/01/Magento-Hosting/%28offset%29/4
# gzip compression
gzip on;
gzip_min_length 1100;
gzip_buffers 4 8k;
gzip_proxied any;
gzip_types text/plain text/css application/x-javascript text/javascript application/json;

############################################
## uncomment next line to enable light API calls processing
#rewrite ^/api/([a-z][0-9a-z_]+)/?$ /api.php?type=$1 break;

############################################
## rewrite API2 calls to api.php (by now it is REST only)
rewrite ^/api/rest /api.php?type=rest last;

############################################
## TRACE and TRACK HTTP methods disabled to prevent XSS attacks
if ($request_method ~ "^TRAC[EK]") {
 return 405;
}

############################################
## always send 404 on missing files in these folders
if ($uri !~ "^/(media|skin|js)/") {
 set $rewrite_to_index 1;
}

###########################################
## Deny access to release notes to prevent disclosure of the installed Magento version
if ($uri ~* "/RELEASE_NOTES.txt") {
 return 404;
}

# Don't rewrite if file exists
if (-e $request_filename) {
 set $rewrite_to_index 0;
}

############################################
## rewrite everything else to index.php
if ($rewrite_to_index = "1") {
 rewrite / /index.php;
}

############################################
## Prevent character encoding issues from server overrides
## If you still have problems, use the second line instead
charset off;
#charset utf-8;

############################################
## Add default Expires header
## http://developer.yahoo.com/performance/rules.html#expires
expires 365d;

##Taken from http://wiki.nginx.org/Magento
# Hide the system directories
location ~ ^/(app|includes|lib|media/downloadable|pkginfo|report/config.xml|var)/ {
 internal;
}

# Hide the hidden files
location /. {
 return 404;
}

Donnerstag, 21. November 2013

Delphi, ScrollBox and mouse wheel

I had a problem using Delphi 2010 that wasn't too easy to solve. Having a ScrollBox on a Frame or a Form and reacting to OnMouseWheel event isn't trivial. The problem seems to be in the VCL implementation of the TScrollBox control.

In my case I have several panels (TPanel) inside the scrollbox, testing it's OnMouseWheel event revealed that it was never fired. Probably this is because the mouse is above one of the panels, it gets the OnMouseWheel event, says that it handled it because it hasn't anything to scroll, and I have the current situation.

After trying different solutions I found the WinAPI style by intercepting and resending the WM_MOUSEWHEEL event to the scrollbox too complicated and finally found a working though a little bit hacky solution.

To solve it you need to use the OnMouseWheel event of the frame or form, where the scrollbox is.


procedure TMyFrame.FrameMouseWheel(Sender: TObject;
  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  var Handled: Boolean);
var
  LTopLeft, LTopRight, LBottomLeft, LBottomRight: Integer;
  LPoint: TPoint;
begin
  inherited;

  // First you have to get the position of the control on screen
  // as MousePos coordinates are based on the screen positions.
  LPoint := ScrollBox1.ClientToScreen(Point(0,0));
  LTopLeft := LPoint.X;
  LTopRight := LTopLeft + ScrollBox1.Width;
  LBottomLeft := LPoint.Y;
  LBottomRight := LBottomLeft + ScrollBox1.Width;

  if (MousePos.X >= LTopLeft) and
    (MousePos.X <= LTopRight) and
    (MousePos.Y >= LBottomLeft)and
    (MousePos.Y <= LBottomRight) then
  begin
    // If the mouse is inside the scrollbox coordinates,
    // scroll it by setting .VertScrollBar.Position.
    ScrollBox1.VertScrollBar.Position :=
      ScrollBox1.VertScrollBar.Position - WheelDelta;
    Handled := True;
  end;
end;

Funny part of it, you have to set .VertScrollBar.Position and mustn't use the ScrollBy function of the scrollbox as the latter doesn#t look for valid values and you can scroll out of screen beyond the actual content of the scrollbox.