RSS

String representation of path in RaphaelJS

raphaeljs paths

Simple thing but took me a few hours to find a solution. If you are using RaphaelJs and have performed some modifications on the path (like translation etc.) and then you would like to get a string representation of that modified path there is a very simple way to do it:

[path object].attr('path');

As always it’s all in the documentation if you know how to read it…

Quick working example:

var paper = Raphael(10, 50, 320, 200);
var p = paper.path("M10 10L90 90").attr({stroke: 'black'});

/* original path string */				
alert(p.attr('path')); //M10,10L90,90
				
p.translate(30,30);

/* modified path string */
alert(p.attr('path')); //M40,40L120,120
 
2 Comments

Posted by on May 26, 2011 in RaphaelJS

 

Tags: ,

EditorGridPanel drag and drop

sm.isSelected is not a function error message

If you are wondering why you are getting “sm.isSelected is not a function” error when trying to drag and drop records from EditorGridPanel then the answer is… CellSelectionModel.

Drag and drop in ExtJs works with RowSelectionModel but EditorGridPanel – with CellSelectionModel by default (which doesn’t implement isSelected method).

The quick and easy solution is to use RowSelectionModel instead (if it’s acceptable for you of course).

sm: new Ext.grid.RowSelectionModel()

Otherwise you most probably will have to override ExtJs implementation of drag and drop for EditorGridPanel.

 
1 Comment

Posted by on May 4, 2011 in Sencha

 

Tags: , ,

Add icons to NestedList in Sencha Touch

NestedList with icons

Unlike most other components NestedLists is missing itemTpl property.

According to Sencha documentation we should use the method called getItemTextTpl instead.

The quick example. Assuming your store looks something like this:

var eventsListStore = new Ext.data.TreeStore({
    model: 'EventItem',
    root: {
        items: [
            {leaf: true, eventName: 'Event 1', eventImage: 'http://url to image'},
            {leaf: true, eventName: 'Event 2', eventImage: 'http://url to image 2'},
            ...
       ]
    },
    proxy: {
        type: 'ajax',
        reader: {
            type: 'tree',
            root: 'items'
        }
    }
});

Then NestedList configuration would be:

...
store: eventsListStore,
getItemTextTpl: function() {
    return '<img class="eventIcon" src="{eventImage}"> {eventName}';
}
...
 
2 Comments

Posted by on April 29, 2011 in misc

 

Auto height and window shadow

Incorrect window shadow

The issue seems to appear when window autoHeight property is set to true. Which means that window height will be adjusted according to window contents height.But it starts misbehaving if you dynamically change the window contents – resize or add/delete new items.

Looks like the easiest solution is to call syncShadow() method on your window object.

    Ext.getCmp('myWindow').syncShadow();

The best place to stick it is after you modified the child controls or on window resize event.

* I haven’t tried replicating this issue in ExtJS 4.0.0 but it appeared on my 3.3.0 and prior.

 
Leave a comment

Posted by on April 28, 2011 in Sencha

 

Tags: , , ,

EditorGridPanel not firing afteredit event if value didn’t change

EditorGridPanel

If you are using EditorGridPanel plugin for ExtJS grids chances are you want to know when user starts and finishes editing a cell.

There is no problem to find out when edit starts with beforeedit event. But afteredit event ExtJS provides for capturing edit completion only fires if the content of the cell has changed. But if the user leaves the cell without modifying it there is no way of capturing this with afteredit.

But we can always override ExtJS internal methods to achieve required functionality ;). In this case we extend EditorGridPanel’s onEditComplete function:

Ext.grid.EditorGridPanel.prototype.onEditComplete = function(ed, value, startValue)
{
        this.editing = false;
        this.lastActiveEditor = this.activeEditor;
        this.activeEditor = null;

        var r = ed.record,
            field = this.colModel.getDataIndex(ed.col);
        value = this.postEditValue(value, startValue, r, field);
        if(this.forceValidation === true || String(value) !== String(startValue)){
            var e = {
                grid: this,
                record: r,
                field: field,
                originalValue: startValue,
                value: value,
                row: ed.row,
                column: ed.col,
                cancel:false
            };
            if(this.fireEvent("validateedit", e) !== false && !e.cancel /* && String(value) !== String(startValue) */){
                r.set(field, e.value);
                delete e.cancel;
                this.fireEvent("afteredit", e);
            }
        }
        this.view.focusCell(ed.row, ed.col);
};  
 
Leave a comment

Posted by on April 15, 2011 in misc, Sencha

 

Tags: , ,

SVG image zooming and panning with RaphaelJs


raphaeljs image zoom

View the demo here.

Easy way to do image zooming and panning without sending images to the server to be processed and trimmed with RaphaelJS and a few simple steps.

1. Set initial zoom, image window width and height:

var mapZoom = 1;

var mapWidth = 400;
var mapHeight = 400;
$('#mapHolder').css('width', mapWidth);
$('#mapHolder').css('height', mapHeight);

2. Create a RaphaelJS object and add a full-size image to it.

var paper = Raphael("mapHolder", mapWidth, mapHeight);
var mapImage = paper.image("https://dashasalo.files.wordpress.com/2011/09/figure1-worldatlas-large.jpg", 0, 0, mapWidth, mapHeight);

You should see your image resized to specified mapWidth and mapHeight.

3. Next step is to draw a menu of controls. I used RaphaelJS to draw the controls but you can also use images if you like.

var mapMenuHolderWidth = 40;
var mapMenuHolderHeight = 216;
var mapMenuHolderX = mapWidth - mapMenuHolderWidth - 6; // menu right margin
var mapMenuHolderY = 5; // menu top margin

// draw menu panel
var mapMenuHolder = paper.rect(mapMenuHolderX, mapMenuHolderY, mapMenuHolderWidth, mapMenuHolderHeight, 12).attr({fill: 'black', stroke: '#aaa', 'stroke-width': 2, opacity: 0.7});

Now controls themselves. You can find very nice icons and paths used to draw them on RaphaelJs website. SVG paths for icons were quite long and made code unreadable so I removed them from this code snippet. If you need paths have a look at the demo source code.

var maxIcon = paper.path("M22...051z").attr({fill: "#bbb", stroke: "#000", translation: (mapMenuHolderX+5) + ', '+(mapMenuHolderY + 10)});

var minIcon = paper.path("M22...884z").attr({fill: "#bbb", stroke: "#000", translation: (mapMenuHolderX+5) + ', '+(mapMenuHolderY + 7+ 34)});
  		
var arrowTop = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, -25', rotation: 180});
  		
var arrowLeft = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 4', rotation: 90});
  		
var arrowRight = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 33', rotation: -90});
  		
var arrowBottom = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 63'});

I used one path for all the arrows and just rotated it to make it point different direction.

4. Now we add a bunch of handlers to the controls to catch the click event.

// maximize icon clicked 
maxIcon.click(function(e){
  	mapZoom++;
  	mapImage.scale(mapZoom, mapZoom);
  	e.stopPropagation();
  	e.preventDefault();
  }); 

// minimize icon clicked 
  minIcon.click(function(e){
  	if (mapZoom == 1) return;
  	mapZoom--;
  	mapImage.scale(mapZoom, mapZoom);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
  }); 
  
// top arrow clicked		
arrowTop.click(function(e){
  	mapImage.translate(0, 20);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
}); 
  
// bottom arrow clicked		
arrowBottom.click(function(e){
  	mapImage.translate(0, -20);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
}); 
  
// left arrow clicked		
arrowLeft.click(function(e){
  	mapImage.translate(20, 0);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
}); 
  		
// right arrow clicked
arrowRight.click(function(e){
  	mapImage.translate(-20, 0);
  	adjustMapEdge();
  	e.stopPropagation();
  	e.preventDefault();
});  	

We will have a look at adjustMapEdge function in a second.

You can also add nice hover effects to the controls with mouseover and mousedown events (look at my demo).

5. AdjustMapEdge function makes sure that image doesn’t move out of its window boundaries. For instance, if user moves up for long enough eventually he/she will hit the image top edge. When this happens we shouldn’t allow the image move any further up and just use its current location even if the user drags the image up again. This way from this position the user can only move down, left or right.

var adjustMapEdge = function()
{
    if (mapImage.attr('x') > 0) mapImage.attr({'x': 0});
    if (mapImage.attr('x') < (mapWidth - mapImage.attr('width'))) mapImage.attr({'x': (mapWidth - mapImage.attr('width'))});
    		
    if (mapImage.attr('y') > 0) mapImage.attr({'y': 0});
    if (mapImage.attr('y') < (mapHeight - mapImage.attr('height') )) mapImage.attr({'y': (mapHeight - mapImage.attr('height') )});
};

6. Panning the image. Panning the image can be split into 3 basic steps – grab the image, hold the mouse button and drag the image, release the mouse button and drop the image.

As there is no reliable way to say whether mouse button is being hold when the mouse moves, we use a javascript variable to store the click state. In the code snippet below when mouse button is clicked I set clicking variable to true. Once the mouse button is released set clicking to false. And while mouse moves check if clicking is still true (hasn’t been released).

// coordinates of the last drag position
var lastX = 0, lastY = 0;

// record the click event on the image for drag and drop
var clicking = false;

// set clicking to true to record start of drag and drop
// and update the last coordinates
$('#mapHolder').mousedown(function(e){
    	clicking = true;
    	lastX = e.pageX;
    	lastY = e.pageY;
    	$('#mapHolder').css('cursor', 'move');
    	e.stopPropagation();
    	e.preventDefault();
});

// when the mouse button is released set clicking to false
// to stop drag and drop		
$(document).mouseup(function(e){
    	clicking = false;
    	$('#mapHolder').css('cursor', 'default');
    	e.stopPropagation();
    	e.preventDefault();
});

$('#mapHolder').mousemove(function(e){
    	// when mouse if moved check if user is also holding a mouse button
    	if (clicking == false) return;
    		
    	var currentMapPosX = 0, currentMapPosY = 0;
    	
    	// get difference between current and previous mouse position	
    	if ((mapImage.attr('x') <= 0) && (mapImage.attr('x') >= (mapWidth - mapImage.attr('width'))))
    	{
    		currentMapPosX = e.pageX - lastX;
    	}
    		
    	if ((mapImage.attr('y') <= 0) && (mapImage.attr('y') >= (mapHeight - mapImage.attr('height'))))
    	{
    		currentMapPosY = e.pageY - lastY;
    	}
    	
    	// move the image	
    	mapImage.translate(currentMapPosX, currentMapPosY);
    	
    	// record previous position
    	lastX = e.pageX;
    	lastY = e.pageY;
    	
    	// check for image edge	
    	adjustMapEdge();
});		
 
39 Comments

Posted by on April 13, 2011 in JavaScript, RaphaelJS

 

Tags: , , , ,

Hide page content accessibly

Method offered by Jeff Burnz in his AdaptiveThemes article and then improved by community now evolved into a reliable cross-browser technique.

.hidden 
{
    position: absolute !important;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
    clip: rect(1px, 1px, 1px, 1px);
    padding: 0 !important;
    border: 0 !important;
    height: 1px !important;
    width: 1px !important;
    overflow: hidden;
}

This method takes the element out of flow so it doesn’t affect the layout around it. By using height of 1px it fixes the issue with VoiceOver screen reader (Apple) that will not read content of an element that has 0 height.

Though if your gole is to hide content from all users you should use visibility and display of none instead.

.hidden 
{
  visibility: hidden;
  display: none;
}
 
Leave a comment

Posted by on April 11, 2011 in CSS

 
 
%d bloggers like this: