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 &lt; 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 
     <b>dddd</b>";
    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

    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 &gt; 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.

    Tree with Nodes that Have Multiple Parents

    Most tree structures are built as hierarchies: the number of nodes increases at each level. In our sample we will look at a tree, where not all branches have a higher number of nodes than their predecessors. In our tree some nodes will have multiple parents e.g. there are nodes that have several ancestors.

    You can try the sample online:

    In order to build this application we use MindFusion Diagramming for JavaScript library.

    I. General Settings

    In a web page we add the code that initializes a Canvas. We give the Canvas an id:

    <div style="overflow: visible; height:100%; margin: 1px; padding: 0px;">
                    <canvas id="diagram" width="2100" height="2500">
                        This page requires a browser that supports HTML 5 Canvas element.
                    </canvas>
    </div>

    We add references to the two JavaScipt files that provide the diagramming functionality: MindFusion.Diagramming and MindFusion.Common. We also add a reference to a code-behind file that contains the JavaScript code for our application:

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

    We have placed those files in a subfolder called Scripts, which is located in our main application folder.

    II. Diagram Settings

    We create the diagram in the window.onload event handler. We want to be sure that all scripts are loaded:

    window.onload = function(e){ 
    
    	var diagramEl = document.getElementById('diagram');
    	// create a Diagram component that wraps the "diagram" canvas
    	diagram = AbstractionLayer.createControl(Diagram, null, null, null, diagramEl);
    	diagram.setAllowInplaceEdit(true);
    	diagram.setRouteLinks(true);
    	diagram.setShowGrid(true);
    	diagram.setRoundedLinks(true);
    	diagram.setBounds(new Rect(0, 0, 2000,2500));
    }

    We create the Diagram using a reference to the DOM element of the Canvas from the web page. We set its allowInplaceEdit property to true, which lets users edit interactively nodes and links. We use showGrid to render a background grid that helps to align nodes and links. We setRoundedLinks and give the diagram a big work are with the setBounds method.

    II. Creating the Diagram Nodes

    We create the DiagramNode -s in a separate method, which we call after the control is created and all settings are made:

    function onLoaded()
    {
    	var nodes = {};
    	
    	for(var i = 0; i &lt; 5; i++)
    	{
    		nodes[i] = diagram.getFactory().createShapeNode(new Rect(20, 20, 20, 12));
    		nodes[i].setShape('Rectangle');
    	    nodes[i].setBrush({ type: 'SolidBrush', color: '#567939' });
    	};

    We initialize a list, where we will store dynamically those nodes that we want to have reference to. At first we create 5 ShapeNode -s that are the first level of the tree. We use the createShapeNode method of Factory to create the ShapeNode -s and DiagramLink -s. Note that we will create all nodes with equal bounds. We don’t have to worry about their location because we will apply an automatic layout at the end.

    Factory is available through the getFactory method of Diagram You do not usually create the class but get an instance of it through the Diagram instance.

    We use setShape to provide the id af the diagram shape that we want the node to take. A list with the diagram shapes available, together with their id-s can be found in the online help.

    We also use setBrush to specify the fill of the ShapeNode . In our case we use a SolidBrush ,but there are other options to choose from.

    We create then a single ShapeNode that will be the next level:

    var node5 = diagram.getFactory().createShapeNode(new Rect(20, 20, 20, 12 ));
    	node5.setShape('Rectangle');
    	node5.setBrush({ type: 'SolidBrush', color: '#6f9c49' });

    We color it in a slightly lighter shade of green than the nodes at the first level. We then use the Factory class once again to create the DiagramLink -s between the 5 nodes at the first level and this one single node at the second level:

    	for(var i = 0; i &lt; 5; i++)
    	{ 
            var link = diagram.getFactory().createDiagramLink(nodes[i], node5);	
    		link.setHeadShape("Triangle");
    		link.setText("20%");
    	    link.setHeadShapeSize(3.0);
    	    link.setHeadBrush({ type: 'SolidBrush', color: '#7F7F7F' });
    	};

    The setText and setHeadShape methods of the DiagramLink class allow us to specify the label of the link and its shape at the end. There is also setBaseShape that allows us the specify the shape at the start of the DiagramLink.

    The lovely thing about the Factory class is that it adds automatically the newly created DiagramItem -s, such as nodes and links, to the items collection of the diagram. You can find the newly created DiagramNode -s and DiagramLink -s also as members of the nodes and links collections respectively.

    Now we have 5 links coming from all 5 nodes from the first level that point to the second level:

    Node with Multiple Parents

    We go on doing the rest of the diagram in the same way. We create ShapeNode -s with Factory and then bind the nodes with Factory .

    III. Layout

    We use the LayeredLayout to arrange all nodes of the diagram. Since the diagram is not a typical tree, we prefer the LayeredLayout to the TreeLayout

    var lLayout = new MindFusion.Graphs.LayeredLayout();
    	diagram.arrange(lLayout);

    It is really easy to apply any other algorithm on the diagram – you just need to create an instance of it and call the arrange method of your diagram to apply this instance. You can quickly change layouts and experiment to see which one provides the best result.

    In our case the LayeredLayout looks fine and with this we are done building the tree.

    You can download the sample together with all libraries used from the following link:

    A JavaScript Graph with Nodes that Have Multiple Parents

    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.

    Ski School Scheduler in JavaScript

    In this blog post we will create the following timetable that shows the registered ski classes for a ski school week per week:

    Ski School Scheduler

    We use MindFusion JavaScript Scheduler to create the timetable. We use the BaseForm class to create the custom form that allows us to choose the level of the skier.

    This is not possible in the standard appointment form that comes with the scheduling library.

    I. General Settings

    We need to add a DIV element with an id at the location where we want the timetable to appear on the page. Note that the size of the DIV determines the size of the calendar:

    <div style="position: absolute; width: 100%; height: 97%;">
    
    
    
    <div id="calendar" style="height: 97%; width: 100%;">
    </div>
    
    
    </div>

    MindFusion.Scheduling requires a reference to MindFusion.Scheduling:

    <script src="MindFusion.Scheduling.js" type="text/javascript"></script>
    <script src="SkiSchoolSchedule.js" type="text/javascript"></script>
    <script src="SkiStudentForm.js" type="text/javascript"></script>

    We also add a reference to two JavaScript files that will contain the source code for our application: SkiSchoolSchedule and SkiStudentForm. Finally we add a reference to the CSS file that defines the theme used by the timetable – business.css:

     	<link rel="stylesheet" type="text/css" href="themes/business.css">

    The Scheduling library offers a variety of CSS themes, which you can customize or use as a template to create new ones.

    II. The Timetable

    In the code-behind file we add a mapping to MindFusion.Scheduling namespace and then create the Calendar instance using the id of the DIV element that will render it:

    var p = MindFusion.Scheduling;
    
    // create a new instance of the calendar
    calendar = new p.Calendar(document.getElementById("calendar"));
    
    // set the view to Timetable, which displays the allotment of resources to distinct hours of a day
    calendar.currentView = p.CalendarView.Timetable;
    //set the theme to business as referenced
    calendar.theme = "business";

    We also set the currentView property to CalendarView and specify the theme – business as the name of the CSS file that we referenced.

    The timetableSettings class exposes a dates property where we add the initial dates that will appear in the view:

    //get the current date
    var currDay = p.DateTime.today();
    calendar.timetableSettings.dates.clear();
    
    /* add dates to the timetable in such manner that always a full week
    rom Mo to Su will be visible */
    for(var i = 1; i &lt; 8; i++)
    {
    	calendar.timetableSettings.dates.add(currDay.addDays(-1 * currDay.dayOfWeek + i));
    }

    We get the current date and add in a cycle all 7 days of the week that contains it. We set the scrollStep property to 7, which indicates the number of days that will appear by initial click on one of the navigation arrows in the header:

    // set the number of days to scroll with when a navigation button is clicked
    calendar.timetableSettings.scrollStep = 7;

    We will also use the startTime and endTime properties to indicate the start and and time of the timetable for each day. Those properties show the time as interval added to the start of the current day, in minutes. Thus a startTime value of 300 means the timetable starts 5 hours (5*60 min.) after midnight of the respective day:

    // set the start time to 8:00 AM
    calendar.timetableSettings.startTime = 480;
    // set the end time to 18:00 PM
    calendar.timetableSettings.endTime = 1020;

    III. The Custom Form

    We don’t want to use the standard form for creating appointments. We will create a custom one, you can see the difference between them at this image:

    Ski School Scheduler

    The left one is the custom form while to the right you can see the standard New Appointment form for calendar events.

    The custom form for creating and editing ski lessons derives from the BaseForm class. Here is its constructor:

    var SkiStudentForm = function (calendar, item, type)
    {
            p.BaseForm.call(this, calendar, item);
    
    	this._id = "SkiStudentForm";
    	this._type = type;
    	
    	if(type == "new")
    	this.headerText = "New Skiing Class";
    else
    	this.headerText = "Edit Skiing Class";
    
    	this.levels = [{ value: 0, text: "beginner" }, { value: 1, text: "intermediate" }, { value: 2, text: "advanced" }];
    }

    We want each form to have a reference to the Calendar, to the Item that was created and a type. The type is simply a string and we recognize two types: new and edit e.g. whether we create a new class or edit an existing one.

    Note that in the constructor we initialize a new variable called levels, which will provide data for the combo box with options for the skiing level of the student.

    Then we call the prototype methods of the class and the constructor so we can initialize instances of SkiStudentForm:

    SkiStudentForm.prototype = Object.create(p.BaseForm.prototype);
    SkiStudentForm.prototype.constructor = SkiStudentForm;

    The two methods that are responsible for drawing the contents of a custom BaseForm and its buttons are drawContent and drawButtons

    We start with the drawContent method, where we create the first row:

    SkiStudentForm.prototype.drawContent = function ()
    {
    	p.BaseForm.prototype.drawContent.call(this);
    
    	var content = this.content;
    
    	var row = this.row();
    	row.className = "header-row";
    	row.innerText = "From";
    	content.appendChild(row);
    
    	row = this.row();
    	row.className = "data-row";
    	row.innerHTML = this.item.startTime.toString("dddd, MMMM d,  HH:00", this.formatInfo);
    	content.appendChild(row);
            ........................
    }

    We call the prototype of the drawContent method and we add a new row element. The row is an empty DIV. Then we add another row, this one contains a string. The string represents the start time of the Item .

    We create the combo box using the createDropDownList method. Before we create the combo box we add a new DIV with the label. Then we add another DIV and the dropDownList element to it:

    // create a drop-down list for status
    row = this.row();
    row.className = "header-row";
    row.innerHTML = "Level";
    content.appendChild(row);
    
    var control = this.createDropDownList({ id: "level", items: this.levels, initValue: this.item.tag, addEmptyValue: false });
    control.element.style.width = "200px";
    this.addControl(control);
    
    row = this.row();
    row.className = "input-row";
    row.appendChild(control.element);
    content.appendChild(row);

    The drawButtons method that we implement overrides the buttons of the BaseForm with new ones, styled as we want. Here the code for the Save button:

    // override BaseForm's drawButtons method to create form buttons
    SkiStudentForm.prototype.drawButtons = function ()
    {
           var thisObj = this;
    
    	var btnSave = this.createButton({
    		id: "btnSave",
    		text: "&#x2714;",
    		events: { "click": function click(e)
    		{
    			return thisObj.onSaveButtonClick(e);
    		}
    		}
    	});
    btnSave.element.className = "form-button-save";

    We use the createButton method to create the button, give it an id and assign a text to it. We also indicate the the click event will be handled by an implementation of the default onSaveButtonClick event for BaseForm

    The Cancel button is the same, just the CSS styling for it is different. Both buttons use custom CSS class, which are assigned to them through the className property of HTML Dom elements. Here is the code for the appearance of the two buttons:

    .form-buttons
    {
    	color: #fff;
    	font-size: x-large;
    	text-align: center;
    	margin-top: 20px !important;
    }
    .form-button-save
    {
    	width: 80%;
    	padding: 10px;
    	background-color: #31bd41 !important;
    }
    .form-button-cancel
    {
    	width: 20%;
    	padding: 10px;
    	background-color: #ce0000 !important;
    }

    We create and render an instance of the form with the following lines of code:

    var form = new SkiStudentForm(sender, item, "new");
    form.showForm();

    Here we create the form from an event handler for the Calendar class – we will look at that in the next section.

    IV. Events

    We show the student appointment form when a selection of cells is make. In order to do this we handle the selectionEnd method of the Calendar class:

    // handle the selectionEnd event to show the custom form for item creation
    calendar.selectionEnd.addEventListener(handleSelectionEnd);
    function handleSelectionEnd(sender, args)
    {	
    	/* we create a new item with the selected start and end time
    	var item = new p.Item();
    	item.startTime = args.startTime;
    	item.endTime = args.endTime;	
    	item.tag = 0;	
    
    	// create and show the custom form
    	var form = new SkiStudentForm(sender, item, "new");
    	form.showForm();
    }

    We create a new Item and we set its startTime and endTime to the start and end of the selected cell range. Then we use the current Calendar, which is provided as a sender, the newly created Item and “new” as type to create an instance of the SkiStudentForm and render it.

    What shall we do if we want to use the custom form to edin an existing appointment? We will handle the itemDoubleClick event of the Calendar and show the SkiStudentForm. In this case we will get the Item that we want to edit and provide it as a parameter to the SkiStudentForm instance. We also change the type to be “edit”:

    function handleItemDoubleClick(sender, args)
    {	
    	// show the custom form with data from the clicked item
    	var form = new SkiStudentForm(sender, args.item, "edit");
    	form.showForm();
    }

    These were the most important parts of the ski school scheduler application. The application has a few more, which we did not mention here but you can check them in code. The complete code, together with the themes and libraries used is available at:

    Download the Ski School Scheduler Application

    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

    Organization Services Chart with the JavaScript Diagram Library

    In this blog post we will use MindFusion JavaScript Diagram library to build an application that allows the users to create a diagram with nodes that hold icons and text. We have used this online diagram application to build an infogram for the services portfolio of an imaginary company. However, you can replace the icons and use it as a tool to build diagrams of whatever domain is of interest to you.

    In the sample diagram we use SVG images from Google’s material design icons set. You can download them from https://github.com/google/material-design-icons.


    Our sample application uses a Diagram and a NodeListView controls – both of them part of JsDiagram.

    I. General Settings

    We need to add two HTML Canvas elements to the web page where the sample application will be. One is for the NodeListView and the other is for the Diagram

    <div style="width: 70px; height: 100%; overflow-y: auto; overflow-x: hidden; position: absolute;
    				top: 5px; left: 0px; right: 0px; bottom: 0px;">
    				<canvas id="nodeList" width="200">
    				</canvas>
    </div>
    
    ...........................
    
    <div style="position: absolute; left: 70px; top: 5px; 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>

    We place the two Canvas elements using CSS next to each other. The NodeListView is in a long and narrow Canvas and next to it is the Diagram Canvas, which has a big size of 211 pixels. It is important that we provide the two Canvases with id-s: we will refer to them later in the JavaScript code using these id-s.

    Then we add references to the two JavaScript files that we use from the Diagram library – MindFusion.Diagramming and MindFusion.Common:

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

    The last JavaScript file that we reference is OrgInfoDiagram and there we keep the code for our application.

    II. The Diagram

    We create the Diagram object by using the reference to the Canvas that will render it:

    // create a Diagram component that wraps the "diagram" canvas
    diagram = Diagram.create(document.getElementById("diagram"));

    Then we use an instance of the Style class to create a style that will determine how newly created –DiagramNode -s are styled:

    //styling
    var shapeNodeStyle = new Style();
    shapeNodeStyle.setBrush({ type: 'SolidBrush', color: '#e0e9e9' });
    shapeNodeStyle.setStroke("Orange");
    shapeNodeStyle.setFontName("Verdana");
    shapeNodeStyle.setFontSize(9);   
    shapeNodeStyle.setNodeEffects([new GlassEffect()]);

    The Style instance applies GlassEffect on the new nodes and places a thick orange border around them. We also allow users to edit diagram nodes by calling setAllowInplaceEdit .

    diagram.setStyle(shapeNodeStyle);
    diagram.setAllowInplaceEdit(true);

    III. The NodeListView

    The NodeListView control can hold both text and image nodes. We create it the same way we created the Diagram : by using the id of its Canvas element:

    // create a NodeListView component that wraps the "nodeList" canvas
    var nodeList = MindFusion.Diagramming.NodeListView.create(document.getElementById("nodeList"));
    nodeList.setTargetView(document.getElementById("diagram"));
    nodeList.setIconSize(new Size(36, 36));
    nodeList.setDefaultNodeSize (new Size(18, 18));

    It is important that we set the target of the NodeListView – this is the Canvas, where the newly created DiagramNode will be rendered. We also set the size of the icons in the NodeListView and the size that newly created nodes will have. For this we use the setIconSize and setDefaultNodeSize methods respectively.

    We create the nodes for the NodeListView in a special method called initNodeList:

    function initNodeList(nodeList, diagram)
    {
    	
    	var node = new ShapeNode(diagram);
    	node.setText("text");
    	node.setShape("Ellipse");
    	node.setPen("Gray");		
    	node.setBrush("White");
    	node.setBounds(new Rect(0, 0, 48, 48));
    	node.setEnableStyledText(true);
    	node.setFont(new Font("sans-serif", 12, true));
    	node.setStrokeThickness(2);
    	nodeList.addNode(node);
    .......................................
    .......................................

    The first ShapeNode that we create is a standard text node with the difference that we enable styled text for it: that means the user can enter bold, italic or underline text using the standard HTML tags <b>, <i>, <u>. We specify this with the method setEnableStyledText . We also add the node to the NodeListView that we have provided to the method.

    In order to create nodes with SVG images, we need to create instances of the SvgNode class:

    // add some nodes to the NodeListView
    	var svgImages = ["call", "reader", "computer", "data", "car", "text", "music", "movie", "nature", "calendar", "chart", "router",  "account", "alarm", "announcement", "book", "calls", "copyright", "event", "grade", "group_work", "info", "key", "list", "payment", "phone", "shop", "sign", "textsms", "theaters", "work"];
    	for (var i = 1; i &lt;= svgImages.length; ++i)
    	{
    		node = new MindFusion.Diagramming.SvgNode(new Rect(0, i * 48, 48, 48));
    		
    	    node.setTransparent(true);
    	    var content = new SvgContent();
    	    content.parse(svgImages[i] + ".svg");
    	    node.setContent(content);
    	    nodeList.addNode(node);
    	}

    We have copied the desired icons in the directory of the application and we have listed their names in a list. Then we cycle through all members of the list and create instances of the SvgNode class. We set their background to be transparent and we render the images thanks to the SvgContent class. Finally we also add the newly created SvgNode -s to the node list.

    We call the initNodeList method providing the nodeList and diagram instances:

    initNodeList(nodeList, diagram);

    IV. Events

    Though the code so far is enough to provide the desired functionality of our application, we want to customize the newly created diagram nodes and links. This can be achieved through events. We handle two of them onNodeCreated and LinkCreated.

    diagram.addEventListener(Events.nodeCreated, onNodeCreated);
    diagram.addEventListener(Events.linkCreated, onLinkCreated);

    In the NodeCreated event handler we get the ShapeNode that is being created and make its shape to a round one with the setShape method. We assign to it a thick orange border and add some padding to the images – in case it is an SvgNode:

    function onNodeCreated(sender, args)
    {
    	var node = args.getNode();
    	node.setTransparent(false);
    	node.setShape("Ellipse");
    	node.setStroke("orange");
    	node.setStrokeThickness(3);
    	node.setFont(new Font("sans-serif", 4));
    	node.setImagePadding(new Thickness(1.5, 1.5, 1.5, 1.5));
    }

    The linkCreated event handler applies some styling, but to newly created DiagramLink instances”

    function onLinkCreated(sender, e)
    {
    	var link = e.getLink();
    	link.setBaseShape(null);
    	link.setHeadShape(null);
    	link.setStrokeThickness(3.0);
    }

    We remove the default base and head shapes of the diagram links and leave the connectors to be just straight lines. We also draw them with a thick orange pen.

    And these were the last lines of code that we had to add to make the application complete. You can download the source code, together with all all Diagramming libraries used from this link:

    Download Organigram Application in JavaScript Source Code

    You can use MindFusion Diagramming for JavaScript forum to post your questions, comments and recommendations about this sample or the Js Diagram library.

    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 and 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.

    Different-size Icons in the Js Diagram NodeListView

    In this blog post we will learn how to add a NodeListView control to a Diagram and how to set its ShapeNode -s to a different size. When ShapeNode -s are dragged from the NodeListView the instances that will be created are proprtional in size to the size of the ShapeNode that was dragged. Here is a screenshot of the final application:

    I. General Settings

    We create an HTML page and add to it references to the MindFusion JavaScript files that represent the diagramming library:

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

    The last reference is to the Controls JavaScript file, where the code for the application is located.

    Then we create two Canvas-es: one is for the NodeListView and the other is for the Diagram The NodeListView component is bound to the canvas element below:

    <div style="width: 200px; height: 100%; overflow-y: auto; overflow-x: hidden; position: absolute; top: 201px; left: 0px; right: 0px; bottom: 0px;">
    <canvas id="nodeList" width="200"></canvas></div>
    
    
    ......
    <!-- The Diagram component is bound to the canvas element below -->
    
    
    <div style="position: absolute; left: 0px; 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>

    Both the Diagram and NodeListView controls require Canvas elements to render themselves onto.

    II. Initializing the Controls

    We create the Diagram using the id that we’ve provided to its Canvas in the web page:

    // create a Diagram component that wraps the "diagram" canvas
    diagram = Diagram.create(document.getElementById("diagram"));
    diagram.setBounds(new MindFusion.Drawing.Rect(0, 0, 500, 500));

    We set a bigger size to the diagram in order to make it fill the web page.

    We create the NodeListView instance the same way we created the diagram:

    // create a NodeListView component that wraps the "nodeList" canvas
    var nodeList = MindFusion.Diagramming.NodeListView.create(document.getElementById("nodeList"));
    nodeList.setTargetView(document.getElementById("diagram"));

    Now we need to add the settings that will make the ShapeNode -s different in size when rendered onto the list:

    nodeList.setIconSize(null);

    The setIconSize method is used to specify the default size of nodes in the NodeListView When we set the size to null, the control draws each node in the NodeListView with the size that was assigned to it:

    function initNodeList(nodeList, diagram)
    {
    	// add some nodes to the NodeListView
    	var shapes = ["Actor", "RoundRect", "Triangle", "Decision"];
    	for (var i = 0; i &lt; shapes.length; ++i)
    	{
    		var node = new MindFusion.Diagramming.ShapeNode(diagram);
    		node.setText(shapes[i]);
    		node.setShape(shapes[i]);
            node.setBounds(new MindFusion.Drawing.Rect(0, 0, (i+1)*10, (i+1)*10));
    		nodeList.addNode(node, shapes[i]);
    	}
    }

    Here we increase the size of wach ShapeNode with 10 points on each itereation. This makes the icons with various size but does not create them with different size when dropped on the diagram. In order to do this we must set:

    nodeList.setDefaultNodeSize (null);

    setDefaultNodeSize specifies the size of those nodes that are created when a ShapeNode is dropped on the Diagram area. By setting this size to null we tell the control to read the size of the new ShapeNode from the instance in the NodeListView control.

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

    JavaScript NodeListView with Various Size Nodes: Download Sample

    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 and 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.

    JavaScript Diagram with Chart Nodes

    In this blog post we will create DiagramNode instances that render a chart. The charts get resized according to the size of the node. We will use two MindFusion libraries for JavaScript – Charting and Diagramming. You can run the sample online from this link:

    I. Project Setup

    We will use a web page that will hold the HTML Canvas element that we need in order to render the diagram:

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

    We give the diagram’s Canavs big width and height and we also provide an id. The id is important because we will need to access the Canvas from code.

    We add the scripts that we need in order to use the Charting and Diagramming libraries:

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

    We add the references at the end of the web page, just before the closing BODY tag. We also need to add a reference to a JavaScript file that will hold the code for our sample. We name it “PieNode.js”.

    II. The Diagram

    In the code-behind file we create a diagram instance. We use the DOMContentLoaded event to initialize our diagram:

    document.addEventListener("DOMContentLoaded", function ()
    {
    // create a Diagram component that wraps the "diagram" canvas
    diagram = MindFusion.Diagramming.Diagram.create(document.getElementById("diagram"));
    diagram.setBehavior(MindFusion.Diagramming.Behavior.LinkShapes);
    diagram.setLinkHeadShapeSize(2);
    diagram.setBounds(new Rect(0, 0, 2000, 2000));
    
    // draw a pie when the user creates a node
    diagram.addEventListener(Events.nodeCreated, onNodeCreated);
    
    });

    The Behavior enumeration lists various modes of behavior for the diagram. We choose Behavior.LinkShapes, which creates nodes, when the mouse is dragged
    over an empty area and connects the nodes if the mouse is dragged from an existing node. We set tbe bounds of the diagram to a big Rect which guarantees that the user can draw nodes anywhere on the visible area. When the user draws towards the edge of the browser, the diagram control expands automatically.

    Finally, we add an event handler for the nodeCreated event.

    //nodeCreated event handler
    function onNodeCreated(sender, args)
    {
    	var node = args.getNode();
    	
    	var nBounds = node.getBounds ();
    	var topLeftCoord = diagram.docToClient(nBounds.topLeft());
    	var bottomRightCoord = diagram.docToClient(nBounds.bottomRight());
    	.......................................................
    }

    At first we get the node that was created. Then we need to get its actual size, for which we use the docToClient method that converts between diagram and browser measure units. We get the two coordinates of the node’s bounding rectangle.

    We create then a Canvas, which takes the size of the node’s rectangle:

    ............................................
    .....................................................
    var pieChartCanvas = document.createElement('canvas');
    pieChartCanvas.width = bottomRightCoord.x - topLeftCoord.x;
    pieChartCanvas.height = bottomRightCoord.y - topLeftCoord.y;

    We then add this temp canvas to the body of the web page and call a method “createPie” where we draw the pie chart. Once the chart is drawn we get the URL of the image and set is to the node with the setImageLocation method. We remove the Canvas from the tree with the elements because we want to use for the next node.

    //create a temporary Canvas for the pie chart
    //to draw itself upon
    document.body.appendChild(pieChartCanvas);
    createPie(pieChartCanvas);
    var pieImageLocation = pieChartCanvas.toDataURL();
    node.setImageLocation(pieImageLocation);
    document.body.removeChild(pieChartCanvas);

    III. The Chart

    We create a pie chart in the Canvas provided to the createPie method:

    //draw a pie chart on the provided canvas
    function createPie(pieChartCanvas)
    {
    	var pieChart = new Controls.PieChart(pieChartCanvas);
    	pieChart.startAngle = 45;			
    	pieChart.showLegend = false;
    	pieChart.title = "Sales";
    	pieChart.titleMargin = new Charting.Margins(0, 10, 0, 0);
    	pieChart.chartPadding = 3;
    ...........................

    We set some appearance properties to make the chart look the way we want – change the start angle of the pie, add a title and title margin, hide the legend. Then we create a PieSeries which holds the data and the labels of the pie. We assign the PieSeries to the series property of the pie chart:

    // create sample data
    var values = new Collections.List([20.00, 30.00, 15.00, 40.00]);
    pieChart.series = new Charting.PieSeries(
    	values,
    	new Collections.List(["20", "30", "15", "40"]),
    null);

    We style the chart with an instance of the PerElementSeriesStyle class, which colors all elements of a Series with the consequtive Brush and stroke from its brushes and strokes collections. Then we adjust the dataLabelsFontSize to match the size of the canvas. We make the labels be drawn with a white brush using the dataLabelsBrush property. Finally we call draw to render the pie.

    var brushes = new Collections.List(
    		[
    			new Drawing.Brush("#0455BF"),
    			new Drawing.Brush("#033E8C"),
    			new Drawing.Brush("#F24405"),
    			new Drawing.Brush("#F21905")
    		]);
    
    	var seriesBrushes = new Collections.List();
    	seriesBrushes.add(brushes);
    
    	var strokes = new Collections.List(
    		[
    			new Drawing.Brush("#c0c0c0")
    		]);
    
    	var seriesStrokes = new Collections.List();
    	seriesStrokes.add(strokes);
    
    	pieChart.plot.seriesStyle = new Charting.PerElementSeriesStyle(seriesBrushes, seriesStrokes);
    	pieChart.theme.highlightStroke = new Drawing.Brush("#000063");
    	pieChart.theme.dataLabelsFontSize = pieChartCanvas.height/20;
    	pieChart.theme.dataLabelsBrush = new Drawing.Brush("#FFFFFF");
    
    	pieChart.draw();

    And with that the sample is ready. You can download the source code together with all MindFusion JavaScript libraries used from:

    http://mindfusion.eu/samples/javascript/diagram/PieNodes.zip

    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 and 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.

    About Charting for JavaScript: MindFusion library for interactive charts and gauges. It supports all common chart types including 3D bar charts. Charts can have a grid, a legend, unlimited number of axes and series. Scroll, zoom and pan are supported out of the box. You can easily create your own chart series by implementing the Series interface.
    The gauges library is part of Charting for JavaScript. It supports oval and linear gauge with several types of labels and ticks. Various samples show you how the implement the gauges to create and customize all popular gauge types: car dashboard, clock, thermometer, compass etc. Learn more about Charting and Gauges for JavaScript at https://mindfusion.eu/javascript-chart.html.

    Multi-tabs Form in JavaScript with Validation

    In this blog post we will create a form that spans over several tabs. Each tab contains input fields for a specific type of information. There are placeholders and restrictions for each input field. If the provided data is not correct we display a guiding message and select the first tab that has wrong data. All incorrect fields are outlined with red.

    I. General Settings

    We will use two files for our form – one is a web page and the other a JavaScript file that will hold all JavaScript code for the sample. They both are names TabForm.

    In the web page we add references to the css file that holds the theme we’ve chosen:

     	<link rel="stylesheet" type="text/css" href="themes/light.css">

    This is the CSS for the light theme of the MindFusion JavaScript UI controls. There are various themes provided out-of-the-box with the library and you can choose another one.

    Then, at the bottom of the web page, before the closing BODY tag we add references to the three JavaScript files that we want to use:

    <script src="../Scripts/MindFusion.Common.js" type="text/javascript"></script>
    <script src="MindFusion.UI.js" type="text/javascript"></script>
    <script src="TabForm.js" type="text/javascript"></script>

    The first two of them point to the libraries of the UI controls: MindFusion.Common and MindFusion.UI. The other is a reference to the JS code-behind file.

    In order to render the TabControl we need a DIV element. So, we create one and give it an id – that is important:

    <div style="position: absolute; top: 0px; left: 0; right: 0; bottom: 0;">
            
    
    
    
    <div id="host">
    </div>
    
    
    </div>

    We also add a paragraph with an id and red stroke – it will render text, that describes the error fields and the validation message for each one – if the user has provided wrong data.

    II. Creating the Tab Control and the Tab Pages

    In the code-behind file we first add mappings to the two namespaces we want to use:

    var ui = MindFusion.UI;
    var d = MindFusion.Drawing

    Then we create an instance of the TabControl in code this way:

    // Create a new TabControl.
    var host = new ui.TabControl(document.getElementById("host"));
    host.width = new ui.Unit(70, ui.UnitType.Percent);
    host.height = new ui.Unit(70, ui.UnitType.Percent);
    host.theme = "light";

    We use the id of the DIV element in the constructor of the TabControl Then we use the theme property to refer to the theme that we want to use. The value of the theme property should be the same as the name of the CSS ile that we referenced in the web page. It can point to a custom theme that you have created as long as the names of the file and the property value match.

    We create the first TabPage as an instance of the TabPage class:

    // Create four templated tab pages and add them to the host's tabs collection.
    var tab1 = new ui.TabPage("Owner Details");
    // The HTML of the specified page will be set as the innerHTML of a scrollable div inside the tab content element.
    tab1.templateUrl = "page1.html";
    host.tabs.add(tab1);

    We provide the string that will render in the title of the TabPage in the constructor. Then we set the content that the TabPage will have as a url to the web page that contains it e.g. the TabPage loads a content from a page specified with templateUrl Here is the code for the first page:

    <div style="padding: 10px;">
            <label for="fname">First Name: </label>
            <input type="text" id="fname" name="First name" placeholder="John" title="Your first name" required"="">
    </div>
    
    
    
    
    <div style="padding: 10px;">
            <label for="lname">Last Name: </label>
            <input type="text" name="Last name" id="lname" placeholder="Smith" title="Your family name" required="">
    </div>
    
    
    
    
    <div style="padding: 10px;">
            <label for="citizen_id">Citizen Id:</label><input id="citizen_id" type="number" name="Citizen number" size="10" pattern="[0-9]{10}" min="1000000000" max="9000000000" placeholder="0123456789" title="10 digits" required="">
    </div>
    
    
    

    In terms of HTML, we have provided each input element with an id, a placeholder value and the necessary restrictions that will validate its content. We strictly use and follow the Validation API of JavaScript, which you can check here: https://www.w3schools.com/js/js_validation_api.asp and here https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation.

    III. Data Submission and Validation

    On the last tab of the form, we have placed a submit button:

    <div style="padding: 10px;">
            <button id="submit">Send Data</button>
    </div>

    We wire the event handler of the click action for this button in the contentLoad event of the fourth tab, where the button is:

    tab4.contentLoad.addEventListener(tabLoad);
    ..........................
    ..........................
    function tabLoad(sender, args) {
    
        let current_datetime = new Date();
        let formatted_date = current_datetime.getFullYear() + "-" + (current_datetime.getMonth() + 1) + "-" + current_datetime.getDate();
        sender.element.querySelector("#start").value = formatted_date;
        sender.element.querySelector("#submit").addEventListener("click", function () {
        submitData(sender);
        });
    }

    In the event handler we get the current date and format it the way the default DateTime picker HTML control expects to get it. We get each input control through its id and the querySelector of the HTML Document object. The sender in this case is the fourth tab or tab4.

    The method that validates the content is submitData:

    function submitData(sender) {
        var txt = "";
    
        var inputObj = tab4.element.querySelector("#start");
    
        if (!inputObj.checkValidity()) {
            txt += inputObj.name + ": ";
            txt += inputObj.validationMessage + "
    ";
            inputObj.style["border-color"] = "red";
            host.selectedItem = tab4;
            dataIsCorrect = false;
        } else
            inputObj.style["border-color"] = "gray";
            ....................................

    We use querySelector once again to get the input fields on each page one by one. For each one we see if the validity check has failed. If yes, we outline this field in red and append the validation message to a text variable.

    We walk through all tabs and all input fields in this same manner and in reverse order. Our aim is that the first tab with error gets selected, even if there are errors in fields further in the form.

    Note that if the field is OK we set its border to the default color. This way we reset the appearance of fields that were previously wrong but the user has corrected.

    Finally, we assign the text to the content of the paragraph that renders text in red:

    ...............................
    document.getElementById("error").innerHTML = txt;  
    
        if (txt.length === 0)
            confirmData();

    If no errors have been detected – that means the error text is an empty string – we submit the data. The data submission is handled by the confirmData method:

    function confirmData() {
    
        //first tab
        tab1.element.querySelector("#fname").value = "";
        tab1.element.querySelector("#lname").value = "";
        tab1.element.querySelector("#citizen_id").value = "";
        ........................................
        ........................................
         //fourth tab
        tab4.element.querySelector("#duration").value = "";
        let current_datetime = new Date();
        let formatted_date = current_datetime.getFullYear() + "-" + (current_datetime.getMonth() + 1) + "-" + current_datetime.getDate();
        tab4.element.querySelector("#start").value = formatted_date;
    
        ui.Dialogs.showInfoDialog("Confirmation", "Your info has been submitted!", null, host.element, host.theme);
    
    }

    We reset the values of all input fields and we show an instance of MindFusion InfoDialog to inform the user that their data has been successfully collected.

    You can download the source code of the sample and all MindFusion themes and libraries used from this link:

    http://mindfusion.eu/samples/javascript/ui/TabForm.zip

    You can ask technical question about MindFusion JavaScript developer tools at the online forum at https://mindfusion.eu/Forum/YaBB.pl.

    About MindFusion JavaScript UI Tools: MindFusion UI libraries are a set of smart, easy to use and customize JavaScript UI components. Each control boasts an intuitive API, detailed documentation and various samples that demonstrate its use. The rich feature set, multiple appearance options and numerous events make the UI controls powerful tools that greatly facilitate the programmers when building interactive JavaScript applications. MindFusion UI for JavaScript is part of MindFusion JavaScript Pack. You can read details at https://mindfusion.eu/javascript-pack.html.

    Cost Meter Gauge in JavaScript

    In this post we will look at the steps we need to make if we want to create this beautiful gauge below:

    The gauge is done with MindFusion Charts and Gauges for JavaScript library. You can download the sample together with all needed libraries from this link.

    I. Project Setup

    We will build the gauge using the OvalGauge library from MindFusion JS Charts and Gauges control. We add two references, needed for the control to work properly:

    <script type="text/javascript" src="Scripts/MindFusion.Common.js"></script>
    <script type="text/javascript" src="Scripts/MindFusion.Gauges.js"></script>

    We have placed those files in a Scripts folder. We will write the JavaScript code for the gauge in a separate file, which we call ValueGauge.js. This file is at the same directory where the web page is. We add a reference to it as well:

    <script type="text/javascript" src="ValueGauge.js"></script>

    The web page with our sample contains a table. We use the table to place the control together with a text box. The text box is not needed, but we will use it to give the user the option to set the value of the gauge by typing it not only by dragging the pointer.

    <table cellpadding="10">
    <tbody>
    <tr>
    <td>Project Cost (in thousands)</td>
    </tr>
    <tr>
    <td><canvas id="value_meter" width="300" height="300"></canvas></td>
    </tr>
    <tr>
    <td>Cost <input id="cost" style="width: 80px"></td>
    </tr>
    </tbody>
    </table>
    

    The gauge will be rendered through an HTML Canvas element. The location and size of the Canvas determine the location and the size of the gauge. It is important that we add an id the Canvas – this way we can reference it in the JavaScript code page, which will be necessary.

    II. The Control

    Now we start editing the ValueGauge.js file and there we first add mappings to the namespaces of Mindusion.Gauges.js that we will use:

    /// <reference path="Scripts/MindFusion.Charting-vsdoc.js">
    var Gauges = MindFusion.Gauges;
    
    var d = MindFusion.Drawing;
    var OvalScale = Gauges.OvalScale;
    var Length = Gauges.Length;
    var LengthType = Gauges.LengthType;
    var Thickness = Gauges.Thickness;
    var Alignment = Gauges.Alignment;
    var LabelRotation = Gauges.LabelRotation;
    var TickShape = Gauges.TickShape;
    var PointerShape = Gauges.PointerShape;</reference>

    The first line is a reference to the Intellisense file that allows us to use code completion of the API members, if supported by our JavaScript IDE.

    Now we need to get the DOM Element that corresponds to the gauge Canvas and use it to create an instance of the OvalGauge class:

    var value_meter = Gauges.OvalGauge.create(document.getElementById('value_meter'), false);

    III. Gauge Scales

    Gauge scales depend on the type of the gauge. For oval gauges we use OvalScale The OvalScale needs to be associated with a gauge and here is how we create it:

    var valueScale = new Gauges.OvalScale(value_meter);
    valueScale.setMinValue(0);
    valueScale.setMaxValue(100);
    valueScale.setStartAngle(120);
    valueScale.setEndAngle(420);

    The OvalScale class offers the full set of properties needed to customize the scale. We use the setMinValue and setMaxValue methods to specify the value range o the gauge. The setStartAngle and setEndAngle specify the arc of the gauge and we set them to 120 and 420 respectively. You see that the arc is 300 degrees, which is less than a full circle – exactly how we want it to be.

    We continue our customization by setting the fill and stroke of the scale. We actually do not want the default scale to be rendered at all, so we use setFill and setStroke to specify transparent colors:

    valueScale.setFill('Transparent');
    valueScale.setStroke('Transparent');
    valueScale.setMargin(new Gauges.Thickness(0.075, 0.075, 0.075, 0.075, true));

    Now we can continue with the ticks. Each gauge can have major, middle and minor ticks. Those ticks are not rendered by default.

    var majorSettings = valueScale.majorTickSettings;
    majorSettings.setTickShape(Gauges.TickShape.Line);
    majorSettings.setTickWidth(new Gauges.Length(10, Gauges.LengthType.Relative));
    majorSettings.setTickHeight(new Gauges.Length(10, Gauges.LengthType.Relative));
    majorSettings.setFontSize(new Length(14, LengthType.Relative));
    majorSettings.setNumberPrecision(0);
    majorSettings.setFill('rgb(46, 52, 66)');
    majorSettings.setStroke('rgb(46, 52, 66)');
    majorSettings.setLabelForeground('rgb(175, 175, 175)');
    majorSettings.setLabelAlignment(Alignment.InnerCenter);
    majorSettings.setLabelRotation(LabelRotation.Auto);
    majorSettings.setLabelOffset(new Length(6, LengthType.Relative));
    majorSettings.setStep(20);
    majorSettings.setTickAlignment (Alignment.OuterOutside);

    We start the customization with the majorTickSettings They will render labels and want to have one tick with a tep of 20. So, we use setStep to specify 20 as an interval and use setTickWidth and setTickHeight to set the size of the ticks. Those properties can be set to an absolute or relative value – see the LengthType enumeration. We also need to change the default shape of the pointer – we use TickShape rest of the settings are intuitive – setFill and setStroke specify how the ticks are colored. We also use setLabelAlignment to position the labels outside the ticks. setTickAlignment is also an important property -it allows us to change the alignment of the ticks, so they can be drawn inside the scale.

    The TickSettings object is similar to MajorTickSettings

    var middleSettings = valueScale.middleTickSettings;
    middleSettings.setTickShape(TickShape.Line);
    middleSettings.setTickWidth(new Gauges.Length(10, Gauges.LengthType.Relative));
    middleSettings.setTickHeight(new Gauges.Length(10, Gauges.LengthType.Relative));
    middleSettings.setTickAlignment (Alignment.OuterOutside);
    middleSettings.setShowTicks(true);
    middleSettings.setShowLabels(false);
    middleSettings.setFill('rgb(46, 52, 66)');
    middleSettings.setStroke('rgb(46, 52, 66)');
    middleSettings.setCount(5);

    We should note here that setShowLabels is false because we want the labels to appear only at intervals of 20. We also use setCount to specify how many ticks we want between each two adjacent major ticks. The rest of the settings are the same as for MajorTickSettings.

    IV. Custom Painting

    The painting of the colored sections at the edge of the gauge is custom code. The gauges library provides various events that allow the developer to replace the default gauge drawing with custom drawing – see the Events section of the OvalGauge class.

    In our sample we will handle two events:

    value_meter.addEventListener(Gauges.Events.prepaintBackground, onPrepaintBackground.bind(this));
    value_meter.addEventListener(Gauges.Events.prepaintForeground, onPrepaintForeground.bind(this));

    prepaintBackground is raised before the background is painted. We can cancel the default painting or add some custom drawing to it. The same is true for prepaintForeground

    function onPrepaintBackground(sender, args)
    {
    	args.setCancelDefaultPainting(true);
    
    	var context = args.getContext();
    	var element = args.getElement();
    	var bounds = new d.Rect(0, 0, element.getRenderSize().width, element.getRenderSize().height);
            ..................................
    }

    In the prepaintBackground event handler we first get the handler to the CanvasRenderingContext2D instance. Then we get the bounds of the painted element. This is the inside of the gauge. Each o the colored segments is pained as an arc. We do not create a path figure to fill – instead we set a very thick lineWidth of the stroke:

    context.lineWidth = 25;
    var correction = context.lineWidth / 2;
    	
    //light green segment
    context.beginPath();
    context.strokeStyle = 'rgb(0, 205, 154)';
    context.arc(bounds.center().x, bounds.center().y, bounds.width / 2-correction, 0.665*Math.PI, 1*Math.PI, false);	
    context.stroke();

    We go on painting this way all colored sections of the gauge, only changing the start and end angles. When we are ready we paint the inside of the gauge. We do it with a full arc:

    context.beginPath();
    bounds.x += 25;
    bounds.y += 25;
    bounds.width -= 50;
    bounds.height -= 50;
    context.fillStyle = '#2e3442';
    
    context.arc(bounds.center().x, bounds.center().y, bounds.width / 2, 0*Math.PI, 2*Math.PI, false);
    context.fill();

    The complete drawing is done inside the prepaintBackground event handler. So, in the prepaintForeground handler we only need to cancel the default painting:

    function onPrepaintForeground(sender, args)
    {
        args.setCancelDefaultPainting(true);
    
    };

    V. The Gauge Pointer

    We need to add a Pointer to the OvalScale of the gauge instance if we want to show one:

    var pointer = new Gauges.Pointer();
    pointer.setFill('white');
    pointer.setStroke("#333333");
    
    pointer.setPointerWidth(new Gauges.Length(90, Gauges.LengthType.Relative));
    pointer.setPointerHeight(new Gauges.Length(20, Gauges.LengthType.Relative));
    
    pointer.setShape(Gauges.PointerShape.Needle2);
    pointer.setIsInteractive(true);
    
    valueScale.addPointer(pointer);

    The size of the pointer is also set in LengthType units. This allows us to have the same pointer size relative to the size of the gauge even if we change the size of the Canvas. We use the PointerShape enumeration to specify the type of pointer we want and then we make it interactive with setIsInteractive As an addition to the default needle of the pointer we want to render a circle at the base of the pointer. We do it with custom drawing:

    value_meter.addEventListener(Gauges.Events.prepaintPointer, onPrepaintPointer.bind(this));

    First we need to handle the prepaintPointer event. In the event handling code we do the drawing:

    function onPrepaintPointer(sender, args)
    {	
    	//args.setCancelDefaultPainting(true);
    
    	var context = args.getContext();
    	var element = args.getElement();
    	var size = element.getRenderSize();
    	var psize = new d.Size(0.2 * size.width, size.height);
    
    	context.save();
    	context.transform.apply(context, element.transform.matrix());
    
    	context.beginPath();
    	context.arc(psize.width / 2, psize.height / 2, psize.height*0.75, 0, 2 * Math.PI, false);
    	var fill = element.getFill();
    	context.fillStyle = Gauges.Utils.getBrush(context, fill, new d.Rect(0, 0, size.width, size.height), false);
    	context.fill();
    	context.strokeStyle = '#333333';
    	context.stroke();
    
    	context.restore();
    };

    Note that in this case we do not cancel the default painting – we will add to it, rather than replace it. Then we get the CanvasRenderingContext2D and size of the rendered element. What is new here is the transform of the CanvasRenderingContext2D object to the center of the gauge. Then we get the Brush that is used to paint the rest of the pointer and use it to fill the custom part as well. We can set the brush directly, but we prefer to take it from the base element – the Pointer This way if we change settings of the Pointer the color of the custom drawn circle will change automatically as well.

    VI. Data Binding

    What we would like to do now is bind a change in the text field to the value of the gauge scale. We add a method that does it:

    function valueChanged(id)
    {
    	if (isNaN(this.value)) return;
    	var gauge = Gauges.OvalGauge.find(id);
    	var pointer = gauge.scales[0].pointers[0];
    	pointer.setValue(+this.value);
    };

    When we call the valueChanged method with the instance of the OvalGauge as an argument, we can get its pointer and set its value to the value of ‘this’. We call the valueChanged in such way, that the ‘this’ reference will be the text field:

    var cost = document.getElementById('cost');
    cost.onchange = valueChanged.bind(cost, ['value_meter']);

    Now when the value changes, the event handler takes the pointer and set its value to the value the user has types.

    That is the end of this tutorial. You can download the source code of the sample, together with all MindFusion libraries used from the following link:

    Download Value Gauge in JavaScript Source Code

    You can use the discussion forum to post your questions, comments and recommendations about the sample or MindFusion charts and gauges.

    About MindFusion JavaScript Gauges: A set of two gauge controls: oval and rectangular, with the option to add unlimited nuber of scales and gauges. All gauge elements support complete customization of their appearance. Custom drawing is also possible, where you can replace the default rendering of the gauge element or add to it. The gauge controls include a variety of samples that offer beautiful implementations of the most popular applications of gauges: thermometer, car dashboard, functions, compass, clock, cost meter and more.
    Gauges for JavaScript is part of MindFusion charts and Dashboards for JavaScript. Details at https://mindfusion.eu/javascript-chart.html.

    Horizontal Full Bar Chart in JavaScript

    We use here MindFusion JavaScript library for Charts and Gauges to build this horizontal stacked bar chart that renders custom tooltips:

    Run the sample from this link.

    You can download the source code together with the libraries used from the link at the bottom of the post.

    I. General Setup

    We split our chart in two files – one is the web page that hosts an HTML Canvas element that will render the chart. The other file is a JavaScript code-behind file that contains the code for the chart.

    We need to add reference to two JavaScript library files that provide the charting and drawing functionality that we need:

    MindFusion.Common.js
    MindFusion.Charting.js

    We place them in a Scripts folder at the same level as our web page and JavaScript code behind file.

    <script type="text/javascript" src="Scripts/MindFusion.Common.js"></script>
    <script type="text/javascript" src="Scripts/MindFusion.Charting.js"></script>
    

    We also add a reference to the code-behind file that we call StackedBarChart.js:

    <script type="text/javascript" src="StackedBarChart.js"></script>

    Now we need to create an HTML Canvas element and we must provide it with an id so we can reference it in our JS code:

    <canvas id="barChart" width="600px" height="400px"></canvas>

    The size of the Canvas determines the size of the chart.

    II. Chart Instance and General Settings

    We add some namespace mappings that allow us to reference classes from the Chart library in a more consice manner:

    var Charting = MindFusion.Charting;
    var Controls = MindFusion.Charting.Controls;
    var Collections = MindFusion.Charting.Collections;
    var Drawing = MindFusion.Charting.Drawing;
    var GridType = MindFusion.Charting.GridType;
    var ToolTip = Charting.ToolTip;

    Then we create an instance of the BarChart control. We need to get the Dom Element that corresponds to the Canvas that we’ve prepared for the chart:

    var chartEl = document.getElementById('barChart');
    chartEl.width = chartEl.offsetParent.clientWidth;
    chartEl.height = chartEl.offsetParent.clientHeight;
    var chart = new Controls.BarChart(chartEl, Charting.BarLayout.Stack);

    The BarChart constructor supports a second argument that indicates the type of the bar chart to render.

    We set the bar chart to horizontal with the horizontalBars property. We also make the bars thicker than normal – the property for this is barSpacingRatio It measures the thickness of the bars as a percente of the bar width.

    chart.horizontalBars = true;
    chart.barSpacingRatio = 0.2;

    III. The Data Series

    We want our chart to render labels as tooltips, inside the bars and also we want custom labels at the Y-axis. The predefined BarSeries class accepts 4 lists with data: one for bar data and three with labels inside the bars, at the top of the bars and at the X-axis. So, it is not an exact match for what we want to do and we need to customize it.

    We will create our own custom BarSeries that we will call SeriesWithLabels. We will inherit the BarSeries class and override its constructor and getLabel members to provide the desired data for the desired type of labels.

    We override the constructor by creating three new variables, which receive the data for the bars and the labels:

    var SeriesWithLabels = function (barValues, innerLabels, yLabels) {
        Charting.BarSeries.apply(this, [barValues, innerLabels, yLabels]);
    	
    	this.yLabels = yLabels;
    	this.innerLabels = innerLabels;
    	this.values = barValues;
        
    };
    
    SeriesWithLabels.prototype = Object.create(Charting.BarSeries.prototype);

    Note that before we do anything else in the new constructor we need to call the apply method of the BarSeries class to transfer the provided data to the base class. We also need to create a prototype of the new series and also define its constructor:

     Object.defineProperty(SeriesWithLabels.prototype, 'constructor', {
       	value: SeriesWithLabels,
       	enumerable: false,
       	writable: true
       });

    Next we will override the getLabel method. This is the method that returns the correct label according to the requested label kind and the index of the label. We said we want to support inner labels, tooltips and Y-axis labels. So, we make sure our implementation of getLabel returns exactly those labels:

    SeriesWithLabels.prototype.getLabel = function (index, kind) {
        if ((kind &amp; Charting.LabelKinds.YAxisLabel) != 0 &amp;&amp; this.yLabels)
            return this.yLabels.items()[index];
    
        if ((kind &amp; Charting.LabelKinds.InnerLabel) != 0 &amp;&amp; this.innerLabels)
            return this.innerLabels.items()[index];
    	
    	if ((kind &amp; Charting.LabelKinds.ToolTip) != 0)
            return getPercentLabel(index, this);
       
        return "";
    };

    Getting the correct inner and top label is easy – we just return the label at the requested position. What is more work is building the tooltip. We want our tooltip to calculate the portion of the part in the stacked bar the mouse currently is over, to the entire bar. This means we need to calculate the data of all bar portions, which is a combination of the values at the requested position in all three bar series. We do this calculation in a separate method called getPercentLabel.

    Before we get to the getPercentLabel method let’s create 3 instances of our custom SeriesWithLabels class:

    var labels = new Collections.List([
    	"POSITION", "SALARY", "LOCATION", "COLLEAGUES", "WORKTIME"
    ]);
    
    // create sample data series
    var series1 = new SeriesWithLabels(new Collections.List([123, 212, 220, 115, 0.01]), new Collections.List([123, 212, 220, 115, 0]), labels);
    var series2 = new SeriesWithLabels(new Collections.List([53, 132, 42, 105, 80]), new Collections.List([53, 132, 42, 105, 80]), null);
    var series3 = new SeriesWithLabels(new Collections.List([224, 56, 138, 180, 320]), new Collections.List([224, 56, 138, 180, 320]), null);

    The third argument in the SeriesWithLabels constructor is the lists with labels at the Y-axis. We need just one list with labels and we set it with the first series. The other series take null as their third argument.

    We need to create a collection with the series and assign it to the series property of the chart:

    var series = new Collections.ObservableCollection(new Array(series1, series2, series3));
    chart.series = series;

    There is a special property called supportedLabels that is member of Series and tells the chart, what type of labels this Series needs to draw. In our case we need to indicate that the first series renders labels at the Y-axis, the inner labels and tooltips. The other two series render inner labels and tooltips:

    series1.supportedLabels = Charting.LabelKinds.YAxisLabel | Charting.LabelKinds.InnerLabel | Charting.LabelKinds.ToolTip;
    series2.supportedLabels = Charting.LabelKinds.InnerLabel | Charting.LabelKinds.ToolTip;
    series3.supportedLabels = Charting.LabelKinds.InnerLabel | Charting.LabelKinds.ToolTip;

    Now let’s get back to the method that calculates the tooltip:

    function getPercentLabel(index, series)
    {
    	var value1 = series1.values.items()[index];
    	var value2 = series2.values.items()[index];
    	var value3 = series3.values.items()[index];
    	
    	var theValue = series.values.items()[index];	
    	var result = theValue/(value1+value2+value3) * 100;
    	
    	return Number(result).toFixed(0) + "%";	
    };

    In it we calculate the sum of all data that is rendered by the stacked bar at the specified index. Then we convert the data to percent and format it to have no numbers after the decimal point. That gives us a little inacurracy sometimes, when the value gets rounded to the next number and the sum of all percents actually is 101. You might want to change the formatting to toFixed(2) if you want to see the exact number rendered.

    IV. Axes and Tooltip

    By default the X-axis shows a title and both axes render the auto scale for the data of the chart. We need to hide the scale and we also hide the ticks that are rendered at the interval values:

    chart.xAxis.title = "";
    chart.yAxis.title = "";
    chart.showXCoordinates = false;
    chart.showYCoordinates = false;
    chart.showXTicks = false;
    chart.showYTicks = false;

    We don’t want our chart to render axes at all, so we will draw them with the color of the chart background. You can also draw them with a transparent brush:

    chart.theme.axisStroke = new Drawing.Brush(Drawing.Color.knownColors.White);

    The tooltip renders automatically when the user hovers a bar. We can customize it with the properties of the static Tooltip class:

    ToolTip.brush = new Drawing.Brush("#fafafa");
    ToolTip.pen = new Drawing.Pen("#9caac6");
    ToolTip.textBrush = new Drawing.Brush("#5050c0");
    ToolTip.horizontalPadding = 6;
    ToolTip.verticalPadding = 4;
    ToolTip.horizontalOffset = 76;
    ToolTip.verticalOffset = 34;
    ToolTip.font = new Charting.Drawing.Font("Verdana", 12);

    We add some padding to the tooltip text and increase its font size. We also render the tooltip with a little offset that will place it inside the bar, ater the inner label.

    V. Styling and Legend

    Styling o the charts is done through instances of SeriesStyle derived classes. The instance is assigned to the seriesStyle property of the Chart In our case we want to color each bar in three sections. That means the portion of the bar that corresponds to the same series is colored in the same color for all its members. That kind of styling is supported by the PerSeriesStyle class. It accepts a list with brushes and strokes and paints all elements of the series corresponding to the index of the brush in the list with this brush:

    // create bar brushes
    var thirdBrush = new Drawing.Brush("#97b5b5");
    var secondBrush = new Drawing.Brush("#5a79a5");
    var firstBrush = new Drawing.Brush("#003466");
    
    // assign one brush per series
    var brushes = new Collections.List([firstBrush, secondBrush, thirdBrush]);
    chart.plot.seriesStyle = new Charting.PerSeriesStyle(brushes, brushes);

    The theme property is the main property for styling the chart. The Theme class exposes fields for customizing the appearance of all chart elements. We first adjust the font and size of the axis labels – remember we have labels only at the Y-axis:

    chart.theme.axisTitleFontSize = 14;
    chart.theme.axisLabelsFontSize = 11;
    chart.theme.axisTitleFontName = "Verdana";
    chart.theme.axisLabelsFontName = "Verdana";
    chart.theme.axisLabelsFontSize = 14;
    chart.theme.axisStroke = new Drawing.Brush(Drawing.Color.knownColors.White);

    The labels inside the bars are called data labels and there are dataLabels*** properties that regulate their appearance:

    chart.theme.dataLabelsFontName = "Verdana";
    chart.theme.dataLabelsFontSize = 14;
    chart.theme.dataLabelsBrush = new Drawing.Brush("#ffffff");

    The dataLabelsBrush is also used when the legend labels are rendered. In order to make them visible we need to set a darker background for the legend:

    chart.theme.legendBackground = new Drawing.Brush("#cccccc");
    chart.theme.legendBorderStroke = new Drawing.Brush("#cecece");

    The labels inside the legend are taken from the title property of the Series instances:

    series.item(0).title = "CAREER START";
    series.item(1).title = "MIDDLE OF CAREER";
    series.item(2).title = "CAREER END";

    Finally we should not forget to call the draw method that actually renders the chart:

    chart.draw();

    With this our costimization of the chart is done. You can download the source code of the sample and the MindFusion JavaScript libraries used from this link:

    Download the Horizontal Stacked Bar Chart Sample: Source Code and Libraries

    About Charting for JavaScript: MindFusion library for interactive charts and gauges. It supports all common chart types including 3D bar charts. Charts can have a grid, a legend, unlimitd number of axes and series. Scroll, zoom and pan are supported out of the box. You can easily create your own chart series by implementing the Series interface.
    The gauges library is part of Charting for JavaScript. It supports oval and linear gauge with several types of labels and ticks. Various samples show you how the implement the gauges to create and customize all popular gauge types: car dashboard, clock, thermometer, compass etc. Learn more about Charting and Gauges for JavaScript at https://mindfusion.eu/javascript-chart.html.