Using ControlNode-s in the JavaScript Diagram

Featured

In this blog post we will look on how to build ControlNode -s with Mindusion Diagramming library for JavaScript. We will create a web page that creates a template for cooking recipes. Each recipe is a DiagramNode . Rows in the table with ingredients can be added or removed dynamically with buttons. When you click on the recipe image, a browse dialog appears and you can point to another one.

You can test the page yourself:

I. General Settings

We need a blank HTML page and a blank JavaScript file, which will hold the code for the application.

We add links to three JavaScript files. We add them at the end of the web page, right before the closing BODY tag:

<script src="MindFusion.Common.js" type="text/javascript"></script>
<script src="MindFusion.Diagramming.js" type="text/javascript"></script>
<script src="RecipeNodes.js" type="text/javascript"></script>

In the web page we need two Canvas elements: one for the diagram and one for an overview control. The Overview control shows a mini-version of the diagram. It is convinient but not necessary in order to render a flowchart.

<div id="content" style="top: 60px; bottom: 24px;">
        <div style="position: absolute; left: 0px; top: 0px; bottom: 0px; width: 200px; border-right: 1px solid #e2e4e7;
			overflow: hidden; vertical-align: top;">
            <!-- The Overview component is bound to the canvas element below -->
            <div style="position: absolute; top: 0px; bottom: 0px; right: 0px; width: 200px;
				height: 200px; border-bottom: 1px solid #e2e4e7; background-color: #c0c0c0;">
                <canvas id="overview" width="200" height="200">
                </canvas>
            </div>            
        </div>
        <!-- The Diagram component is bound to the canvas element below -->
        <div style="position: absolute; left: 200px; top: 0px; right: 0px; bottom: 0px; overflow: auto;">
            <canvas id="diagram" width="2100" height="2100">
                This page requires a browser that supports HTML 5 Canvas element.
            </canvas>
        </div>
        <!-- The ZoomControl component is bound to the canvas element below -->
        <div style="width: 50px; height: 300px; position: absolute; top: 20px; right: 35px;
			width: 50px; height: 300px;z-index:3;">
            <canvas id="zoomer" width="50" height="300">
            </canvas>
        </div>
    </div>

We also add a ZoomControl control and we provide all HTML controls with an id. This is important because we need this id to refer to the Canvas elements from JavaScript code.

We’ve also added some CSS styling options to the web page. They are meant to make the HTML controls that we will embed in our
control node prettier.

II. The Controls

Now that we have set up all code in the web page, we start coding the JavaScript code-behind file. We add some namespace mappings, to make the code shorter. If you are using Visual Studio Code, you can add the TypeScript definitions of the Diagram library. This will enable IntelliSense support while you code.

We handle the DOMCOntentLoaded event of the initial HTML document to initialize the MindFusion controls.

document.addEventListener("DOMContentLoaded", function ()
{
	 // create a Diagram component that wraps the "diagram" canvas
    var diagram = Diagram.create(document.getElementById("diagram"));
    diagram.setVirtualScroll(true);
    diagram.setBehavior(Behavior.LinkControls);    

    // create an Overview component that wraps the "overview" canvas
    var overview = MindFusion.Diagramming.Overview.create(document.getElementById("overview"));
    overview.setDiagram(diagram);

    // create an ZoomControl component that wraps the "zoomer" canvas
    var zoomer = MindFusion.Controls.ZoomControl.create(document.getElementById("zoomer"));
    zoomer.setTarget(diagram);
}});

We get the HTML element that corresponds to each of the three Canvas elements that we have created in the web page. We use the document.getElementById of the Web API. You see now that we query the document using the id-s of the Canvas elements, which we have previously specified.

The Overview and ZoomControl instances use the setDiagram and setTarget methods prespectively, to specify the Diagram instance, to which they should be bound.

III. The Node Template

The ControlNode class allows us to use plain HTML in order to initialize the look of a ControlNode This is done with the setTemplate method. You can specify different templates for each instance of ControlNode that you create.

var node1 = new MindFusion.Diagramming.ControlNode(diagram);
        node1.setTemplate(`<input value="Cupcakes" data-interactive="true"></input>
	<div style="padding:10px"><img width="70" height="70" title="Click to choose picture" style="float:left" src="data:image/png;base64,${placeholderBase64}" data-interactive="true" data-event-click="placeholderClick" /><table width="50%"><tr><td contenteditable='true' data-interactive="true">softened butter</td><td contenteditable='true' data-interactive="true">110g</td></tr><tr><td contenteditable='true' data-interactive="true">golden caster sugar</td><td contenteditable='true' data-interactive="true">110g</td></tr><tr><td contenteditable='true' data-interactive="true">large eggs</td><td contenteditable='true' data-interactive="true">2</td></tr><tr><td contenteditable='true' data-interactive="true">vanilla extract</td><td contenteditable='true' data-interactive="true">½ tsp</td></tr>
<tr><td contenteditable='true' data-interactive="true">self-raising flour</td><td contenteditable='true' data-interactive="true">110g</td></tr></table></div>
	<button data-interactive="true" data-event-click="addRow">Add Ingredient</button>
	<button data-interactive="true" data-event-click="removeRow">Remove Ingredient</button>
	<div style="width: 100%;"><textarea id="w3review" name="w3review"  rows="4" columns="50" data-interactive="true">
  Using an electric whisk beat 110g softened butter and 110g golden caster sugar together until pale and fluffy then whisk in 2 large eggs, one at a time, scraping down the sides of the bowl after each addition.
Add ½ tsp vanilla extract, 110g self-raising flour and a pinch of salt, whisk until just combined then spoon the mixture into the cupcake cases.
  </textarea></div>`);
        node1.setBounds(new Rect(40, 20, 100, 100))
        diagram.addItem(node1);

We also use setBounds to specify the location of the new DiagramNode on the diagram’s work area. It is also important to add the new node to the collection of DiagramItem -s with addItem if we open the web page in browser, we will see the recipe node for cupcakes. If we create new nodes, however, we will see that they render as default white rectangles:

In order to change that we need to replace the default templates for nodes of the Diagram with a custom one: our recipe node. Here is the new template:

 var defaultTemplate = `
<input placeholder="Recipe Title" data-interactive="true"></input>
	<div style="padding:10px"><img width="70" height="70" title="Click to choose picture" style="float:left" src="data:image/png;base64,${placeholderBase64}" /><table width="50%"><tr><td contenteditable='true' data-interactive="true">Milk</td><td contenteditable='true' data-interactive="true">1 cup</td></tr></table></div>

	<div><button>Add Ingredient</button>
	<button>Remove Ingredient</button>
	<div><textarea id="w3review" name="w3review"  rows="4" columns="50" data-interactive="true">
  Preparation
  </textarea></div>`;

Now we can call the setDefaultControlTemplate method to tell the Diagram that from now on, when new DiagramNode -s are created, they will have this template:

diagram.setDefaultControlTemplate(defaultTemplate);

IV. Interactivity

When we create a new node, the table renders with a sample row, which you cannot edit. The same is with the title. If we want to make the controls editable we need to set the data-interractive atribute:

<div><button data-interactive="true" data-event-click="addRow">Add Ingredient</button>

As a rule each ControlNode exposes various attributes, which are accessible through the following syntax:

 data-attributeName="value"

As you see from the code above, there is data-event-click attribute. This attribute specifies the event handler of the click event for the button that is rendered in the node. Here it is:

function addRow(e, sender)
{
	var table = sender.getContent().getElementsByTagName("table")[0];
	
	
	  var rows = table.getElementsByTagName('tr');
	  if(rows.length > 0)
      {
		   var clone = rows[rows.length - 1].cloneNode(true); 
           table.appendChild(clone);
	  }
	  else
	  {
		  var tr = document.createElement('tr');
           
          var td = document.createElement('td');
         td.appendChild(document.createTextNode('Milk'))
         td.setAttribute('contenteditable', 'true');
		 td.setAttribute('data-interactive', 'true');
         tr.appendChild(td);
		 
		 td = document.createElement('td');
         td.appendChild(document.createTextNode('1 cup'))
         td.setAttribute('contenteditable', 'true');
		 td.setAttribute('data-interactive', 'true');
         tr.appendChild(td);
		 
		 table.appendChild(tr);
      }
}

In the event handler we check if there are rows in the table, and if yes – we copy the first one. If there are no rows, we add a new row with default data. The data-event-eventName is the syntax to handle any event that is available on the HTML control that you have included in the ControlNode is how we specify that the image of a cupcake is clickable:

<img width="70" height="70" title="Click to choose picture" style="float:left" src="data:image/png;base64,${placeholderBase64}" data-interactive="true" data-event-click="placeholderClick" />

As you see, it is interactive and we handle the click event on it. Once we click on the image, a dialog renders and we can choose a new image:

function placeholderClick(e, sender)
{
    var input = document.createElement("input");
    input.type = "file";
    var img = e.target;
    input.addEventListener('change', e =>
    {
        Utils.toDataUrl(URL.createObjectURL(e.target.files[0]), function (base64str)
        {
            img.src = base64str;
            sender.setTag(base64str);
            sender.createImage();
        });
    })
    input.click();
}

When a new image is chosen, we assign its Base64 represenation as source to the image element that is in our ControlNode template, which represents cooking recipes.

And these were all the steps you need to make in order to get interactive, recipe nodes with customizable count of ingredients, title and image.

You can download the source code from:

Download Recipe Nodes JavaScript Diagram Sample

Technical support is available through MindFusion forum here.

About Diagramming for JavaScript: This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree, BPMN diagrams and much more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.

Styling The Timetable View in JavaScript Scheduler

In this blog post we are going to look at the most common adjustments in the appearance of a timetable that developers want to make based on the questions we have received regarding MindFusion Scheduler library for Java Script.

Here is how our timetable will look at the end:

Styling the Timetable View in JS Scheduler

I. General Looks

The overall appearance of the scheduling library is controlled by themes. MindFusion Scheduler comes with a collection of 9 predefined themes. In order to apply a theme you need to:

  • add a reference to the CSS file in the web page where you want the calendar to appear:
    <link rel="stylesheet" type="text/css" href="themes/gray.css">
  • assign the name of the theme to the theme property of the Calendar class:
    calendar.theme = "gray";
  • Themes can change dramatically the color scheme of the control including the forms that appear for item creation. We recommend that you choose the theme that is most closely related to the colors you want to see and customize it, if necessary.

    Themes in the JavaScript Scheduler

    Each of the themes is in a CSS file and you can search by the color code and replace the occurrences of a given color with another one to fine-tune the color scheme.

    II. Timetable Settings

    Initially, a timetable renders only one day – today. The timetableSettings property, just like the *settings properties for all views supported by the Calendar (—SingleMonth, RespurceView, MonthRange etc.) expose the properties that let you customize the look of the calendar.

    In the case with the timetableView, the number of columns that are rendered depends on the number of dates added to the dates propеrty of timetableSettings In our case we want to show the week, so we add 7 dates:

    //get the current date
    var currDay = schedule.DateTime.today();
    calendar.timetableSettings.dates.clear();
    
    for (var i = 1; i < 8; i++) {
    	calendar.timetableSettings.dates.add(currDay.addDays(-1 * currDay.dayOfWeek + i));	
    	}
    }

    Setting the Number Of Dates in a Timetable in the JavaScript Scheduler

    We also want to scroll week by week. By default the timetable scrolls with one day. In order to scroll more you need to set the scrollStep property to a bigger number:

    calendar.timetableSettings.scrollStep = 7;

    The next thing that we need is to change the time scale. We want the scales to be per 20 minutes and we want to cover the time interval from 10 to 16 o’clock. These is regulated with the startTime and endTime fields of timetableSettings These properties take as an argument the number of minutes. So, if you want your day to start at 8 o’clock your start time needs to be 8×60=480 minutes or you need to assign 480 as value to the startTime property.

    // set the start time to 10:00 AM
    calendar.timetableSettings.  = 600;
    // set the end time to 16:00 PM
    calendar.timetableSettings.endTime = 1020;

    What we want to specify is the format of the header. The default format is based on the locale settings of the user that runs the application. In our application the date in the timetable header renders as DD/MM/YYYY. We will use the titleFormat property. We also use cellTime to change the time scales between each two hours. The default value is 30 minutes. We change it to 20 with the cellTime property.

    calendar.timetableSettings.titleFormat = "d MMMM 
     dddd";
    calendar.timetableSettings.cellTime = schedule.TimeSpan.fromMinutes(20);
    calendar.timetableSettings.cellSize = 20;

    Adjusting the Timetable Settings in the JavaScript Scheduler

    We also increase the cell size – this is the height of rows that are defined by each 20-minute interval. The calendar also shows just one header – that with the dates. We want to render the days header, which shows the day of the week. The property for that is showDayHeader

    calendar.timetableSettings.showDayHeader = true;

    III. CSS Styling

    We’ve customized our timetable as much as we could through the properties and fields of the Calendar control. We would like to add some additional styling, which can be done through css. We use the style inspector of the browser to identify the styles that are applied on the elements that interest us. We would like to show the lines that separate 20 minute cells in yellow and the lines that separate hours in red. Let’s start with the hour lines. The css to render them in red is this one:

    .mfp-timetable-view .mfp-content .mfp-column .mfp-cell-wrap:nth-child(3n+1) .mfp-cell { 
    			border-top: 1px solid red; 
    }

    The class that styles cells is called mfo-cell-wrap. This class regulates the styling for all cells, so we need to apply red border only on the cells that interest us, and they are the 1st, 4th, 9th etc. cell. We want the rest of the cells to be yellow. This is done with the CSS “not” keyword:

    .mfp-timetable-view .mfp-content .mfp-column .mfp-cell-wrap:not(:nth-child(3n+1)) .mfp-cell {  
    			border-top: 1px solid yellow; 
    	}

    This colors the rows of the timetable red/yellow but does not color the delimeters between the time scales. They are regulated by another CSS class and are div elements:

    .mfp-timetable-view.gray .mfp-header-timeline .mfp-group-time div:not(:first-child)
    	{
    
    		border-top: solid 1px yellow;
    	}
    		
    	.mfp-timetable-view.gray .mfp-header-timeline .mfp-hour
    	{
    		border-top: solid 1px red;
    	}

    Note that the CSS style names are with the suffix “gray”. This is the name of the theme. In many cases the class that needs to be changed is bound to a certain theme.

    The last thing that we want to add as styling is a background for the weekend days. We use again the nth-child CSS property. This time the “children” are the 6th and 7th element, so we define styles for them:

    .mfp-timetable-view .mfp-content .mfp-column:nth-child(5n + 6), .mfp-column:nth-child(5n + 7) {
    			background-color: rgba(145, 179, 188, 0.4);
    		}

    We note one more thing. When we create an appointment, the text of the subject is not visible because the line height is too small for the item styling. We have two options: either to increase the cell height, which is set with cellSize and is 20 or to style the item, so that the subject is visible. We choose the latter. We will make the resize line-s smaller because that’s what hiding the subject: the big resize lines:

    .mfp-item-vertical-detail .mfp-subject {
    	flex-shrink: 0 !important;
    }
    
    .mfp-item-vertical-detail .mfp-resize-start,
    .mfp-item-vertical-detail .mfp-resize-end {
    	flex-shrink: 1 !important;
    }

    Here is the final result:

    Styling the Timetable Vew in JS Scheduler

    You can download the sample with all libraries ued and the full source code from this link:

    Styling a Timetable in the JavaScript Scheduler

    You can post technical questions, comments and recommendations about MindFusion Scheduling for JavaScript at the library online forum.

    About Scheduling for JavaScript: MindFusion Js Scheduler is the right solution for all applications that need to render interactive timetables, rich event calendars, lists with appointments or resources. Fully responsive, highly customizable and easy to integrate, you can quickly program the JavaScript scheduling library according to your needs. The library supports a variety of export options, styling through themes, 6 calendar views and much more. Find out more at https://mindfusion.eu/javascript-scheduler.html

    A Monthly Calendar in Java Swing that Ends at a Given Date

    In this blog post we will build a monthly calendar in Java Swing using the scheduler library. We will use the monthly view of the calendar but we will make it render only 3 months after the current month. By default there are no limits how far users can scroll the months in Single month view both back- and forth-wards. We will let our users scroll as many months they want in the past but only scroll 3 months ahead.

    I. General Settings

    We create an empty project in Eclipse and add the JPlanner.jar as an external Jar library as shows in this picture:

    Then we create a Java class that extends JFrame and there, in the constructor we create a new instance of the Calendar class:

    calendar = new Calendar();
    getContentPane().add(calendar, BorderLayout.CENTER);

    Then we set the current view to be SingleMonth using the setCurrentView method and we set the theme to be silver with setTheme calendar supports a variety ot views and themes, which are members of the CalendarView and ThemeType enumerations.

    II. Handling Events

    We will use the addCalendarListener method to add an instance of the CalendarAdapter class that is used to handle events in the Calendar

    calendar.addCalendarListener(new CalendarAdapter(){		
    		
    		@Override()
    		public void visibleDateChanged(DateChangedEvent e) {
    			onVisibleDateChanged(e);
    		}
    		
    	});

    We will handle the visibleDateChanged event and check when the user is about to scroll to a month that we do not want to show. In our sample we want the user to be able to scroll only three months in advance.

    The Calendar initializes by default with the current date being visible. For a CalendarView this means the current month is rendered. We will keep this date in a global variable for the class because we want to be able to use it in the event handler method:

    protected MainWindow()
    {
    	setDefaultCloseOperation(EXIT_ON_CLOSE);
    	setSize(400, 400);
    	setTitle("Tutorial 1");
    	initialDate = DateTime.now();
    	...........................
    	...........................
    }
    
    private Calendar calendar;
    private DateTime initialDate;

    We will use our initialDate variable to reset the calendar to a data three months after it. Whenever we detect that the user is about to scroll to the 4th month, we reset the date to be 3 months after initialDate’s month. Here is how:

    //make sure that dates are rendered till the end of May
    public void onVisibleDateChanged(DateChangedEvent e)
    {
    					
    	if(e.getNewDate().getMonth() == initialDate.getMonth() + 4)
    	{		
    		calendar.setDate(new DateTime(initialDate.getYear(), 
    				initialDate.getMonth() + 3, initialDate.getDay()));
    	}
    }

    Now if the user want to go to the 4th month, the view will always bring the 3rd month and will not allow switiching to the month ahead.

    With that our tutorial is finished. You can download the sample code from this link:

    Monthly Calendar with Fixed End Date

    Technical support is available at the Java Swing Online Discussion Forum.

    About MindFusion Scheduling for Java Swing: The library provides extensive feature-set for creating and customizing all sorts of calendars, task lists, time-management tables, resource allocation tables and other. It boasts various options for customizing appearance and numerous events for handling user actions. The distribution archive includes a lot of samples and detailed documentation. Learn more at https://mindfusion.eu/java-scheduler.html

    Custom Painting of Resources in Java Scheduler

    In this blog post we will explain how to color cells and resources in the Resource view of the calendar based on a certain criteria. In our case we take the “Resource Table” sample from the Samples for the Java Swing Scheduler and we will edit its code to color the header and background of cells that correspond to given resources, in our sample it is an employee:

    We will also add tooltips that show when the mouse is over cells that correspond to this employee:

    I. General Settings

    The code that we will demonstrate and explain is an extension to the “Resource Table” sample, which you can download from:

    Download Resource Table Java Scheduler Sample

    The sample uses the Scheduling Library for Java Swing, which is included as a Jar reference to the project.

    We create an instance of the Calendar class and set its current time, date and the end date – this is the final date that will be visible in the resource view:

    calendar = new Calendar();
    calendar.setCurrentTime(DateTime.now());
    calendar.setDate(new DateTime(2020, 6, 8));
    calendar.setEndDate(new DateTime(2020, 7, 7));
    

    The Calendar class exposes many methods for customizing the schedule. We first set the view to be CalendarView itemSettings and resourceViewSettings classes provide us with lots of options to customize the calendar look. We use them to twist the appearance of our resource table:

    calendar.getItemSettings().getSelectedItemStyle().setHeaderFont(new Font("Verdana", Font.PLAIN, 9));
    calendar.getItemSettings().getSelectedItemStyle().setHeaderTextAlignment(EnumSet.of(TextAlignment.MiddleLeft));
    calendar.getItemSettings().getSelectedItemStyle().setHeaderTextShadowStyle(ShadowStyle.None);
    ...........................
    ...........................
    calendar.getItemSettings().getStyle().setHeaderFont(new Font("Verdana", Font.PLAIN, 9));
    calendar.getItemSettings().getStyle().setHeaderTextAlignment(EnumSet.of(TextAlignment.MiddleLeft));
    calendar.getItemSettings().getStyle().setHeaderTextShadowStyle(ShadowStyle.None);
    calendar.getItemSettings().getStyle().setHeaderTextShadowOffset(0);
    ...........................
    ...........................
    calendar.getResourceViewSettings().getBottomTimelineSettings().setFormat("EEE (MM/dd)");
    calendar.getResourceViewSettings().getBottomTimelineSettings().setSize(15);
    calendar.getResourceViewSettings().getBottomTimelineSettings().getStyle().setHeaderBrush(Brushes.White);
    calendar.getResourceViewSettings().getBottomTimelineSettings().getStyle().setHeaderFont(new Font("Verdana", Font.BOLD, 10));

    The employees are instances of the Contact class. We add them to the contacts collection of the Calendar, once we’ve created them. It is important that we provide an id to each Contact, because that’s how we will identify them later in code:

    Contact contact = new Contact();
    contact.setFirstName("Mike");
    contact.setId("IdMike");
    contact.setName("Mike");
    calendar.getContacts().add(contact);

    The items that represent tasks for the resources are Appointment instances. We create them in code and add them to the items collection of the Calendar app;

    app = new Appointment();
    app.setStartTime(new DateTime(2006, 3, 27));
    app.setEndTime(new DateTime(2006, 3, 28));
    app.getContacts().add(calendar.getSchedule().getContacts().get("IdMike"));
    app.setHeaderText("21965 Carbon Mesa Rd (1)");
    app.setPriority(0);
    calendar.getSchedule().getItems().add(app);

    The resource view renders rows of cells that correspond to a given Resource, Location, Item, Task etc. The options available are members of the GroupType enumeration. In our sample we group the view by resources e.g. employees:

    calendar.setGroupType(GroupType.GroupByContacts);

    And with that we’ve finished with the general settings and we continue writing the code that will customize our application.

    II. Custom Drawing

    What we want to achieve as appearance of our resource table – selective coloring of the background of cells – can be done through custom drawing. Custom drawing provides us with means to color most elements of the calendar, depending on the view. The “Custom Draw Elements” sample gives us visual representation of the elements that correspond to the CustomDrawElements enumeration, which determines what is to be custom drawn in a schedule:

    The sample is available from this link:

    Java Swing Scheduler: Sample that Demonstrates the Custom Draw Elements according to the Calendar View

    We want to color the resource header and the cells that correspond to this resource. So we use the setCustomDraw method to achieve that:

    calendar.setCustomDraw(EnumSet.of(CustomDrawElements.ResourceViewRowHeader, CustomDrawElements.ResourceViewCellComplete));

    The members of the CustomDrawElements enumeration allow bitwise combining. The drawing is done in an event handler for the draw event:

    calendar.addCalendarListener(new CalendarAdapter() {
    	public void draw(CalendarDrawEvent e) {
    		onCalendarDraw(e);
    	}
    });

    We use a CalendarAdapter to subscribe to the draw event, which we handle with the onCalendarDraw method. The CalendarDrawEvent class exposes many properties that give us information about the element that is being drawn. We use the getElement method to check, which element is being drawn – the cell or the header. If it is the cell, we get the resource that correspond to it and if it is the right one, we paint a rectangle, which represents the whole area of the element that is painted at the moment. We get it with the getBounds method:

    if (e.getElement() == CustomDrawElements.ResourceViewCellComplete)
    {
    	Rectangle bounds = new Rectangle(e.getBounds());
    	bounds.x += 1;
    
    if (e.getResource().getId().equals("IdMike") ||
        e.getResource().getId().equals("IdChuck") ||
        e.getResource().getId().equals("IdTom") ||
        e.getResource().getId().equals("IdAlfredo"))
    				
    {
    	g.fillRectangle(_brush3, bounds);
    	g.drawString("Office", _font, _textBrush, bounds, f);
    }

    We will draw an outline to the resource header that correspond to the same resource, whose rows we colored with _brush3:

    else if (e.getElement() == CustomDrawElements.ResourceViewRowHeader)
    	{
    		if (e.getResource().getId().equals("IdMike") ||
    			e.getResource().getId().equals("IdChuck") ||
    			e.getResource().getId().equals("IdTom") ||
    			e.getResource().getId().equals("IdAlfredo"))
    					
    		{
    			Brush _brush3 = new SolidBrush(new Color(254, 249, 207, 100));
    			g.fillRectangle(_brush3, e.getBounds());
    			g.drawRectangle(new Pen(new Color(163, 198, 134, 255), 2),
    				e.getBounds().getMinX() + 1,
    				e.getBounds().getMinY() + 1,
    				e.getBounds().getMaxX() - 2,
    				e.getBounds().getMaxY() - 2);
    		}
    			
    	}

    Here we check if the custom draw element is CustomDrawElements The CalendarDrawEvent class, which provides data for the event gives us sufficient information to recognize the exact element that is being painted.

    III. Tooltips Over Selected Resources

    By default the Calendar provides tooltips for items. The ItemTooltipEvent provides more information about this. However, we want to show tooltips when the user hovers over cells that correspond to the elements that we’ve painted in section II. We can do that by using a MouseMotionListener and subscribing to the mouseMove event. Note that these are standard Java Swing events:

    calendar.addMouseMotionListener(new MouseAdapter() {
    	public void mouseMoved(MouseEvent e) {
    		onCalendarMouseMoved(e);
         }
    		
    });

    In the event handler method we use the getResourceAt method of the Calendar to learn the resource, over which the mouse is hovering. Then we use the id-s that we’ve assigned to our resources and check if the mouse is over the resources that we want to render tooltips:

    private void onCalendarMouseMoved(MouseEvent e) {
    		
    		Resource res = calendar.getResourceAt(e.getX(), e.getY());
    		
    		if (res.getId().equals("IdMike") ||
    		    res.getId().equals("IdChuck") ||
    		    res.getId().equals("IdTom") ||
    		    res.getId().equals("IdAlfredo"))
    		{				
    			   calendar.setToolTipText(res.getId());				
    				
    		}
    		else
    		{
    		     calendar.setToolTipText("");		    
    		}			
    		
    }

    When we detect that the mouse is over a resource that does not need to render tooltip, we set the tolltip text to be an empty string.

    With that we’ve finished customizing the resource sample. You can download the extended version from this link:

    Resource Table in Java Swing with Tooltips and Resource Coloring: Download

    You are welcome to post your questions and comments at the Online Forum for Scheduling for Java Swing.

    About MindFusion Scheduling for Java Swing: The library provides extensive feature-set for creating and customizing all sorts of calendars, task lists, time-management tables, resource allocation tables and other. It boasts various options for customizing appearance and numerous events for handling user actions. The distribution archive includes a lot of samples and detailed documentation. Learn more at https://mindfusion.eu/java-scheduler.html

    Pan and Zoom Programmatically in a JavaScript Diagram

    We will build a diagram with 50 random nodes and we will zoom and pan this diagram programmatically. Here is a screenshot of the final diagram, which is a link to the sample:

    We will use the MindFusion Diagramming library for JavaScript.

    I. Project Setup

    We add a reference to the MindFusion.Diagramming.js and MindFusion.Common.js files. We also add a reference to another file called MouseEvents.js. This is our code-behind file.

    <script src="MindFusion.Common.js" type="text/javascript"></script>
    <script src="MindFusion.Diagramming.js" type="text/javascript"></script>
    <script src="MouseEvents.js" type="text/javascript"></script>

    In the BODY of the web page we create a Canvas element, to which we assign an id. This is important, because we will refer to the Canvas in code:

    <div style="width: 100%; height: 100%; overflow: auto;">
        <canvas id="diagram_canvas" width="2100" height="2100">
            This page requires a browser that supports HTML 5 Canvas element.
        </canvas>
    </div>

    II. Diagram Settings

    In the code-behind file that we called MouseEvents.js we use the DOMContentLoaded event to initialize the diagram.

    document.addEventListener("DOMContentLoaded", function ()
    {
        // create a Diagram component that wraps the "diagram_canvas" canvas
        diagram = MindFusion.AbstractionLayer.createControl(Diagram, null, null, null, document.getElementById("diagram_canvas"));
        diagram.setBounds(new Rect(5, 5, 2000, 1000));

    We use the createControl method of the AbstractionLayer class to create an instance of the Diagram class. The setBounds method determines the size of the diagram’s drawing area. If this size is bigger than the size of the Canvas, the diagram automatically shows scrollbars. Note that only if the diagram’s area is larger than the canvas we can use panning.

    We use some settings of the Diagram class to customize the application:

    diagram.setDefaultShape("Rectangle");
    diagram.setRouteLinks(true);
    diagram.setRoundedLinks(true);
    diagram.setShowGrid(false);

    The links will be routed and rounded and no grid will be rendered.

    III. Diagram Items

    We create the diagram nodes with the createShapeNode method of the Factory class. The Factory class as an instance is available through the getFactory() method:
    for(var i = 0; i < 50; i++)

        {
            var colorIndex = Math.floor(Math.random() * 3);  
            var shape = diagram.getFactory().createShapeNode(new Rect(136, 36, 20, 10));
            shape.setBrush({ type: 'SolidBrush', color: colors[colorIndex] });
            if(i % 3   == 0)
                shape.setShape('Ellipse');
            else 
                shape.setShape('Rectangle');
            if( i % 7 == 0)
            {
                shape.setBounds(new Rect(136, 36, 16, 8));	
            }
    		
            shape.setText("Node " + (i + 1).toString());
            shape.setTextColor("white");
        }

    We make each third shape Ellipse and we choose the brush on a random principle out of three brushes, that we initialized in an array. Each seventh shape is slightly smaller – that is set with the setBounds method, which takes as an argument a Rect, that is slightly smaller than the Rect instance that we use when we create the shape nodes.

    The connectors among the nodes are created with the createDiagramLink method of Factory . We cycle through all 50 nodes and connect each one of them with a randomly taken node from the diagram nodes collection. This collection is available through the nodes proeprty of the Diagram class:

    diagram.nodes.forEach(function(node)
    {
        var nodeIndex = Math.floor(Math.random() * 50);  
    
        var node2 = diagram.nodes[nodeIndex];
        var link = diagram.getFactory().createDiagramLink(node, node2);
        link.setHeadShape("Circle");
    })

    We customize the appearance of the link through the setHeadShape method. We choose the ‘Circle’ shape as a head to each link.

    We have created the diagram items with the same bounds, which means they are on top of each other. The best way to arrange them is with one of the automatic layout algorithms, available with the JsDiagram. They are members of the MindFusion.Graphs namespace – you can check the rest. In our sample we’ve chosen the LayeredLayout ,which provides quite nice result. We set its direction to LayoutDirection .There a few other properties that we’ve set that regulate the node distance, the layer distance and more:

    var layout = new MindFusion.Graphs.LayeredLayout();
    layout.direction = MindFusion.Graphs.LayoutDirection.LeftToRight;
    layout.siftingRounds = 0;
    layout.nodeDistance = 8;
    layout.layerDistance = 8;
    diagram.arrange(layout);

    All layouts are applies through the arrange method of the Diagram that takes an instance of the layout as an argument.

    IV. Pan and Zoom

    We will implement pan and zoom by handling standard DOM events. The first one is the “wheel” event, which we attach to the diagram canvas element:

    var dgrm = document.getElementById('diagram_canvas');
    
    dgrm.addEventListener('wheel', function(e)
    {
        var zoom = diagram.getZoomFactor();
        zoom -= e.deltaY / 10;
        if (zoom > 10)
            diagram.setZoomFactor(zoom);
    
        e.preventDefault(); // do not scroll
    });

    We use the getZoomFactor and setZoomFactor methods of the Diagram , to manipulate the zoom ratio. The zoom step is calculated based on the deltaY value of the event args. You can command the amount of zoom by dividing by a smaller or a larger number. It is important that we call preventDefault() on the event arguments, to surpass the default response of the canvas to the wheel event.

    The panning is implemented by handling the mousedown and mouseup DOM events of the Canvas.

    /* events fired on the draggable target */
    dgrm.addEventListener('mousedown', function(e)
    {
     if( e.ctrlKey)
    	diagram.setBehavior(MindFusion.Diagramming.Behavior.Pan);
     
    }, false);
    
    dgrm.addEventListener('mouseup', function(e)
    {
     if( e.ctrlKey)
    	diagram.setBehavior(MindFusion.Diagramming.Behavior.LinkShapes);
     
    }, false);

    If we want to make the Diagram pan we need simply to change the diagram’s behavior with the setBehavior method. The options are members of the Behavior enumeration. When the user clicks on the Diagram and the Ctrl key is pressed, we change the diagram’s behavior to “Pan”. When the mouse is up, but the Ctrl key is pressed, we rest the behavior back to LinkShapes. This is the default behavior, where dragging with the mouse creates new shapes, while dragging between existing DiagramShape -s, creates DiagramLink -s.

    With that our sample is ready. You can download the source code from this link:

    Download the Mouse Events Sample with JavaScript Diagram

    Technical support is available through MindFusion forum here.

    About Diagramming for JavaScript: This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree, BPMN diagrams and much more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.