Basic Introduction
Ext JS stand for Extended JavaScript, is a JavaScript framework and product of sencha which is based on YUI (Yahoo user interface).It is basically a desktop application development platform with modern UI. This tutorial gives a complete understanding of Ext JS. This reference will take you through simple and practical approach while learning Ext JS.Audience
This tutorial has been prepared for the beginners to help them understand the concepts of Ext JS to build dynamic web UI.Prerequisites
For this tutorial, the reader should have a prior knowledge of HTML, CSS and JavaScript coding. It would be helpful if the reader knows concepts of object-oriented programming and have general idea on creating web applications.What is Ext JS
Ext JS is a popular JavaScript framework which provide rich UI for building web applications with cross browser functionality. Ext JS is basically used for creating desktop applications It supports all the modern browsers as IE6+, FF, Chrome, safari 6+, opera 12+ etc. Whereas another product of sencha, sencha touch is used for mobile applications.Ext JS is based on MVC/ MVVM architecture. The latest version of Ext JS 6 is a single platform which can be used for both desktop and mobile application without having different code for different platform.
History
Ext JS 1.1
The first version of Ext JS was developed by Jack Slocum in 2006. It was a set of utility classes which is an extension of YUI. He named the library as YUI-ext.Ext JS 2.0
Ext JS version 2.0 was released in 2007. This version had new API documentation for desktop Application with limited features. This version doesn’t had backward compatibility with previous version of Ext JS.Ext JS 3.0
Ext JS version 3.0 was released in 2009. This version added new features as chart and list view but at the cost of speed. It had backwards compatible with version 2.0.Ext JS 4.0
After the release of Ext JS 3 the developers of Ext JS had the major challenge of ramping up the speed. Ext JS version 4.0 was released in 2011. It had the complete revised structure which followed by MVC architecture and a speedy application.Ext JS 5.0
Ext JS version 5.0 was released in 2014. The major change in this release was to change the MVC architecture to MVVM architecture. It includes the ability to build desktop apps on touch-enabled devices, two-way data binding, responsive layouts and many more features.Ext JS 6.0
Ext JS 6 merges the Ext JS (for desktop application) and sencha touch (for mobile application) framework.Features
These are the highlighted features of Ext JS- Customizable UI widgets with collection of rich UI such as Grids, pivot grids, forms, charts, trees.
- Code compatibility of new versions with the older one.
- A flexible layout manager helps to organize the display of data and content across multiple browsers, devices, and screen sizes.
- Advance data package decouples the UI widgets from the data layer. The data package allows client-side collection of data using highly functional models that enable features such as sorting and filtering.
- It is protocol agnostic, and can access data from any back-end source.
- Customizable Themes Ext JS widgets are available in multiple out-of-the-box themes that are consistent across platforms.
Benefits
Sencha Ext JS is the leading standard for business-grade web application development. Ext JS provides the tools necessary to build robust applications for the desktop and tablets.- Streamlines cross-platform development across desktops, tablets, and smartphones — for both modern and legacy browsers.
- Increases the productivity of development teams by integrating into enterprise development environments via IDE plugins.
- Reduces the cost of web application development.
- Empowers teams to create apps with a compelling user experience.
- It has set of widgets for making UI powerful and easy.
- It follows MVC architecture so highly readable code.
Limitations
- The size of library is large around 500 KB which makes initial loading time more and makes application slow.
- HTML is full of tags makes it complex and difficult to debug.
- According to general public license policy it is free for open source applications but paid for commercial applications.
- Some times for loading even simple things requires few lines of coding which is simpler in plain html or Jquery.
- Need quite experienced developer for developing Ext JS applications.
Tools
These are the tools provided by sencha used for Ext JS application development mainly for production level.Sencha Cmd
Sencha CMD is a tool which provides the features of Ext JS code minification, scaffolding, production build generation.Sencha IDE Plugins
Sencha IDE plugins which are integrates Sencha frameworks into IntelliJ, WebStorm IDEs. Which helps in improving developer’s productivity by providing features such as code completion, code inspection, code navigation, code generation, code refactoring, template creation, and spell-checking etc.Sencha Inspector
Sencha Inspector is a debugging tool which helps debugger to debug any issue while development.Try it Option Online
We have set up ExtJS Programming environment online, so that you can compile and execute all the available examples online. It gives you confidence in what you are reading and enables you to verify the programs with different options. Feel free to modify any example and execute it online.
Try the following example using Try it option available at the top right corner of the below sample code box:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.Panel', { renderTo: 'helloWorldPanel', height: 100, width: 200, title: 'Hello world', html: 'First Ext JS Hello World Program' }); }); </script> </head> <body> <div id="helloWorldPanel" /> </body> </html>For most of the examples given in this tutorial, you will find a Try it option in our website code sections at the top right corner that will take you to the online compiler. So just make use of it and enjoy your learning.
Local Environment Setup
This section guides you on how to download and set up Ext JS on your machine. Please follow the steps to set up the environment.Downloading library files
Download a trial version of Ext JS library files from sencha https://www.sencha.com. You will get the trial version from the site on your registered mail id which will be a zipped folder named ext-6.0.1-trial.Unzip the folder and you will find various JavaScript and CSS files which you will include in our application. We will mostly include following files:
(1) Javascript Files JS file which you can find under folder \ext-6.0.1-trial\ext-6.0.1\build are :
File and Description |
---|
ext.js This is the core file which contains all functionality to run the application. |
ext-all.js This file contains all the code minified with no comments in the file |
ext-all-debug.js This is the unminified version of ext-all.js for debugging purpose. |
ext-all-dev.js This file is also unminified and is used for development purpose as it contains all the comments and console logs also to check any errors/issue |
ext-all.js This file is used for production purpose mostly as it is much smaller than any other. |
(2) CSS Files There are number of theme based files which you can find under folder \ext-6.0.1-trial\ext-6.0.1\build\classic\theme-classic\resources\theme-classic-all.css
- If we are going to use desktop application then we can use classic themes under folder \ext-6.0.1-trial\ext-6.0.1\build\classic
- If we are going to use mobile application then we will use modern themes which can be found under folder \ext-6.0.1-trial\ext-6.0.1\build\modern
<html> <head> <link rel = "stylesheet" type ="text/css" href= "..\ext-6.0.1-trial\ext-6.0.1\build\classic\theme-classic\resources\theme-classic-all.css" /> <script type ="text/javascript" src = "..\ext-6.0.1-trial\ext-6.0.1\build\ext-all.js" > </script> <script type ="text/javascript" src = "app.js" > </script> </head> </html>You will keep ExtJS application code in app.js file.
CDN setup
CDN is content delivery network with which you do not need to download the Ext JS library files instead you can directly add CDN link for ExtJS to your program as follows:<html> <head> <link rel = "stylesheet" type ="text/css" href= "https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-crisp/resources/theme-crisp-all.css" / > <script type ="text/javascript" src = "https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js" > </script> <script type ="text/javascript" src = "app.js" > </script> </head> </html>
Popular Editors
As it is a JavaScript framework which is used for developing web applications, in our project we will have HTML, JS files and to write your Ext JS programs, you will need a text editor. There are even multiple IDEs available in the market. But for now, you can consider one of the following:- Notepad: On Windows machine you can use any simple text editor like Notepad (Recommended for this tutorial), Notepad++, sublime.
- Eclipse: is an IDE developed by the eclipse open-source community and can be downloaded from http://www.eclipse.org/ .
Browser
Ext JS supports cross browser compatibility, it supports all major browsers as:- IE 6 and above
- Firefox 3.6 and above
- Chrome10 and above
- Safari 4 and above
- Opera 11 and above
Naming convention is a set of rule to be followed for identifiers.
It makes code more readable and understandable to the other programmers as well.
Naming convention in Ext JS follows the standard JavaScript convention which is not mandatory but a good practice to follow.
It should follows camel case syntax for naming the class, method, variable and properties.
If name is combined with two words, second word will start with uppercase letter always e.g. doLayout(), StudentForm, firstName etc.
Name | Convention |
---|---|
Class Name |
It should start with uppercase letter and followed by camel case E.g. StudentClass |
Method Name |
It should start with lowercase letter and followed by camel case E.g. doLayout() |
Variable Name |
It should start with lowercase letter and followed by camel case E.g. firstName |
Constant Name |
It should be in uppercase only E.g. COUNT, MAX_VALUE |
Property Name |
It should start with lowercase letter and followed by camel case e.g. enableColumnResize = true |
MVC – Model View Controller architecture (version 4)
MVVM – Model View Viewmodel (version 5)
This architecture is not mandatory for the program but it is best practice to follow this structure to make your code highly maintainable and organized.
Project structure With Ext JS App
----------src ----------resources -------------------CSS files -------------------Images ----------JavaScript --------------------App Folder -------------------------------Controller ------------------------------------Contoller.js -------------------------------Model ------------------------------------Model.js -------------------------------Store ------------------------------------Store.js -------------------------------View ------------------------------------View.js -------------------------------Utils ------------------------------------Utils.js --------------------------------app.js -----------HTML filesExt JS app folder will reside in JavaScript folder of your project.
The App will contain controller, view, model, store, utility files with app.js.
app.js: the main file from where the flow of program will start, which should be included in main HTML file using <script> tag. App calls the controller of application for rest of the functionality.
Controller.js: It is the controller file of Ext JS MVC architecture. This contains all the control of application, the events listeners the most functionality of code. It has the path defined for all the other files used in that application such as store, view, model, require, mixins.
View.js: It contains the interface part of the application which shows up to user. Ext JS uses various UI rich views which can be extended and customized here according to the requirement.
Store.js: It contains the data locally cached which is to be rendered on view with the help of model objects. Store fetches the data using proxies which has the path defined for services to fetch the backend data.
Model.js: It contains the objects which binds the store data to view. It has mapping of backend data objects to the view dataIndex. The data is fetched with the help of store.
Utils.js: It is not included in MVC architecture but a best practice to use to make the code clean, less complex more readable. We can write methods in this file and call them in controller or view renderer where ever required. It is helpful for code reusability purpose as well.
In MVVM architecture controller is replaced by ViewModel.
ViewModel: It is basically medicates the changes between view and model. It binds the data from model to view. At the same time it does not have any direct interaction with view it has only knowledge of model.
How it works
For example if we are using one model object at two - three places in UI, if we change the value at one place of UI we can see without even saving that change the value of model changes and so gets reflected in all the places in UI where ever the model is used.It makes developers effort much lesser and easier as no extra coding is required for binding data.
This chapter list down the steps to write first Hello World program in Ext JS:
Step 1
Create index.htm page in an editor of our choice. Include the required library files in head section of html page as mentioned below:index.htm
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.Panel', { renderTo: 'helloWorldPanel', height: 200, width: 600, title: 'Hello world', html: 'First Ext JS Hello World Program' }); }); </script> </head> <body> <div id="helloWorldPanel" /> </body> </html>
Explanation
- Ext.onReady() method will be called once the Ext JS is ready to render the Ext JS elements.
- Ext.create() method is used to create object in Ext JS here we are creating an object of simple panel class Ext.Panel.
- Ext.Panel is the predefined class in Ext JS for creating a panel.
- Every Ext JS class has different properties to perform some basic functionalities.
- renderTo is the element where this panel has to be render. 'helloWorldPanel' is the div id in Index.html file.
- Height and width properties are for giving custom size of the panel.
- Title property is to provide the title to the panel.
- Html property is the html content to be shown in the panel.
Step 2
Open index.htm file in a standard browser and you will get the following output on browser.Ext JS is a JavaScript framework which has functionalities of object oriented programming. Ext is the namespace which encapsulates all the classes in Ext JS.
Defining a class in Ext JS
Ext provides more than 300 classes which we can use for various functionalities.
Ext.define() is used for defining classes in Ext JS.
Syntax:
Ext.define(class name, class members/properties, callback function);Class name is the name of class according to app structure e.g. appName.folderName.ClassName studentApp.view.StudentView.
Class properties/members - which define the behavior of class.
Callback function is optional. It is called when the class has loaded properly.
Example of Ext JS class definition
Ext.define(studentApp.view.StudentDeatilsGrid, { extend : 'Ext.grid.GridPanel', id : 'studentsDetailsGrid', store : 'StudentsDetailsGridStore', renderTo : 'studentsDetailsRenderDiv', layout : 'fit', columns : [{ text : 'Student Name', dataIndex : 'studentName' },{ text : 'ID', dataIndex : 'studentId' },{ text : 'Department', dataIndex : 'department' }] });Creating Objects
As like other OOPS based languages we can create objects in Ext JS as well.
Different ways of creating objects in Ext JS-
Using new keyword:
var studentObject = new student(); studentObject.getStudentName();Using Ext.create():
Ext.create('Ext.Panel', { renderTo : 'helloWorldPanel', height : 100, width : 100, title : 'Hello world', html : 'First Ext JS Hello World Program' });Inheritance in Ext JS
Inheritance is the principle of using functionality defined in class A into class B.
In Ext JS inheritance can be done using two methods-
Ext.extend:
Ext.define(studentApp.view.StudentDetailsGrid, { extend : 'Ext.grid.GridPanel', ... });Here our custom class StudentDetailsGrid is using basic features of Ext JS class GridPanel.
Using Mixins:
Mixins is the different way of using class A in class B without extend.
mixins : { commons : 'DepartmentApp.utils.DepartmentUtils' },Mixins we add in controller where we declare all the other classes such as store, view etc. In this way we can call DepartmentUtils class and use its functions in controller or in this application.
Containers
Container in Ext JS is the component where we can add other container or child components. These containers can have multiple layout to arrange the components in the containers. We can add or remove components from container and from its child elements. Ext.container.Container is the base class for all the containers in Ext JS.S.N. | Description |
---|---|
1 | Components inside Container This example shown how to define components inside container |
2 | Container inside container This example shown how to define container inside container with other components |
S.N. | Type of Containers & Description |
---|---|
1 | Ext.panel.Panel This example shows a Ext.panel.Panel container |
2 | Ext.form.Panel This example shows a Ext.form.Panel container |
3 | Ext.tab.Panel This example shows a Ext.tab.Panel container |
4 | Ext.container.Viewport This example shows a Ext.container.Viewport container |
S.N. | Layout & Description |
---|---|
1 | Absolute This layout allows to position the items using XY coordinates in the container. |
2 | Accordion This layout allows to place all the items in stack fashion (one on top of other) inside container. |
3 | Anchor This layout gives the privilege to the user to give the size of each element with respect to the container size. |
4 | Border In this layout various panels are nested and separated by borders. |
5 | Auto This is the default layout decides the layout of the elements based on the number of elements. |
6 |
Card(TabPanel)
This layout arranges different components in tab fashion. Tabs will
be displayed on top of the container.Every time only one tab is visible
and each tab is considered as different component. |
7 | Card(Wizard) In this layout every time the elements comes for full container space. There would be a bottom tool bar in the wizard for navigation. |
8 |
Column
This layout is to show multiple columns in the container. We can
define fixed or percentage width to the columns. The percentage width
will be calculated based on the full size of the container. |
9 |
Fit
In this layout the container is filled with a single panel and when
there is no specific requirement related to the layout then this layout
is used. |
10 | Table As name implies this layout arranges the components in container in the HTML table format. |
11 | vBox This layout allows the element to be distributed in the vertical manner. This is one of the mostly used layout. |
12 | hBox This layout allows the element to be distributed in the horizontal manner. |
S.N. | Methods & Description |
---|---|
1 | GridGrid component can be used to show the data in the tabular format. |
2 | FormForm widget is to get the data from the user. |
3 | Message BoxMessage box is basically used to show data in the form of alert box. |
4 | ChartCharts are used to reprent data in pictorial format. |
5 | Tool tipTool tip is used to show some basic information when any event occurs. |
6 | WindowThis UI widget is to create a window which should pop up when any event occurs. |
7 | HTML editorHTML Editor is one of the very useful UI component which is used for styling the data which user enters in terms of fonts, color, size etc. |
8 | Progress barTo show the progress of the backend work. |
Description
Drag and drop feature is one of the powerful feature added for making developers task easy.A drag operation, essentially, is a click gesture on some UI element while the mouse button is held down and the mouse is moved. A drop operation occurs when the mouse button is released after a drag operation.Syntax
Adding drag and drop class to the draggable targets.var dd = Ext.create('Ext.dd.DD', el, 'imagesDDGroup', { isTarget: false });Adding drag and drop target class to drappable target
var mainTarget = Ext.create('Ext.dd.DDTarget', 'mainRoom', 'imagesDDGroup', { ignoreSelf: false });
Example
Following is a simple example<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.application({ launch: function() { var images = Ext.get('images').select('img'); Ext.each(images.elements, function(el) { var dd = Ext.create('Ext.dd.DD', el, 'imagesDDGroup', { isTarget: false }); }); } }); var mainTarget = Ext.create('Ext.dd.DDTarget', 'mainRoom', 'imagesDDGroup', { ignoreSelf: false }); </script> <style> #content{ width:600px; height:400px; padding:10px; border:1px solid #000; } #images{ float:left; width:40%; height:100%; border:1px solid Black; background-color:rgba(222, 222, 222, 1.0); } #mainRoom{ float:left; width:55%; height:100%; margin-left:15px; border:1px solid Black; background-color:rgba(222, 222, 222, 1.0); } .image{ width:64px; height:64px; margin:10px; cursor:pointer; border:1px solid Black; display: inline-block; } </style> </head> <body> <div id="content"> <div id="images"> <img src = "/extjs/images/1.jpg" class = "image" /> <img src = "/extjs/images/2.jpg" class = "image" /> <img src = "/extjs/images/3.jpg" class = "image" /> <img src = "/extjs/images/4.jpg" class = "image" /> <img src = "/extjs/images/5.jpg" class = "image" /> <img src = "/extjs/images/6.jpg" class = "image" /> <img src = "/extjs/images/7.jpg" class = "image" /> <img src = "/extjs/images/8.jpg" class = "image" /> </div> <div id="mainRoom"></div> </div> </body> </html>This will produce following result −
With the help of drag and drop in Extjs we can move data from grid to grid and grid to form.Below are the example of moving data between grids and forms.
Drag and drop - Grid to Grid.
drag and drop - Grid to Form
Ext.js provides a number of themes to be used in your applications. You can add different theme inplace of classic theme and see the difference in output, this is done simply by replacing theme CSS file as explained below.
Neptune Theme
Consider your very first Hello World application. Remove the following CSS from the application:https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.cssAdd the following CSS to use Neptune theme:
https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.cssTo see the effect, try the following program:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.Panel', { renderTo: 'helloWorldPanel', height: 200, width: 600, title: 'Hello world', html: 'First Ext JS Hello World Program' }); }); </script> </head> <body> <div id="helloWorldPanel" /> </body> </html>This will produce following result:
Crisp Theme
Consider your very first Hello World application. Remove the following CSS from the application:https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.cssAdd the following CSS to use Neptune theme:
https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-crisp/resources/theme-crisp-all.cssTo see the effect, try the following program:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-crisp/resources/theme-crisp-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.Panel', { renderTo: 'helloWorldPanel', height: 200, width: 600, title: 'Hello world', html: 'First Ext JS Hello World Program' }); }); </script> </head> <body> <div id="helloWorldPanel" /> </body> </html>This will produce following result:
Triton Theme
Consider your very first Hello World application. Remove the following CSS from the application:https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.cssAdd the following CSS to use Triton theme:
https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.cssTo see the effect, try the following program:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.Panel', { renderTo: 'helloWorldPanel', height: 200, width: 600, title: 'Hello world', html: 'First Ext JS Hello World Program' }); }); </script> </head> <body> <div id="helloWorldPanel" /> </body> </html>This will produce following result:
Gray Theme
Consider your very first Hello World application. Remove the following CSS from the application:https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.cssAdd the following CSS to use Gray theme:
https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-gray/resources/theme-gray-all.cssTo see the effect, try the following program:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-gray/resources/theme-gray-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.Panel', { renderTo: 'helloWorldPanel', height: 200, width: 600, title: 'Hello world', html: 'First Ext JS Hello World Program' }); }); </script> </head> <body> <div id="helloWorldPanel" /> </body> </html>This will produce following result:
Events are something which get fired when something happens to the class. For example when a button is getting clicked or before/ after element is rendered.
Methods of writing events:
- Built in events using listeners
- Attaching events later
- Custom events
Built in events using listeners
Ext JS provides listener property for writing events and custom events in Ext JS files.Writing listener in Ext JS
We will add the listener in the previous program itself by adding listen property to the panel as below:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function(){ Ext.create('Ext.Button', { renderTo: Ext.getElementById('helloWorldPanel'), text: 'My Button', listeners: { click: function() { Ext.MessageBox.alert('Alert box', 'Button is clicked'); } } }); }); </script> </head> <body> <p> Please click the button to see event listener </p> <div id = 'helloWorldPanel' /> <!-- panel will be rendered here-- > </body> </html>This will produce following result:
This way we can write multiple events also in listeners property.
Multiple events in same listener
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function(){ Ext.get('tag2').hide() Ext.create('Ext.Button', { renderTo: Ext.getElementById('helloWorldPanel'), text: 'My Button', listeners: { click: function() { this.hide(); }, hide: function() { Ext.get('tag1').hide(); Ext.get('tag2').show(); } } }); }); </script> </head> <body> <div id = "tag1">Please click the button to see event listener.</div> <div id = "tag2">The button was clicked and now it is hidden.</div> <div id = 'helloWorldPanel' /> <!-- panel will be rendered here-- > </body> </html>
Attaching event later
In the previous method of writing events we have written events in listeners at the time of creating elements.Other way is to attach events in the as:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function(){ var button = Ext.create('Ext.Button', { renderTo: Ext.getElementById('helloWorldPanel'), text: 'My Button' }); // This way we can attach event to the button after the button is created. button.on('click', function() { Ext.MessageBox.alert('Alert box', 'Button is clicked'); }); }); </script> </head> <body> <p> Please click the button to see event listener </p> <div id = 'helloWorldPanel' /> <!-- panel will be rendered here-- > </body> </html>
Custom events
We can write custom events in ext JS and fire the events with fireEvent method, below example explains how to write a custom events.<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function(){ var button = Ext.create('Ext.Button', { renderTo: Ext.getElementById('helloWorldPanel'), text: 'My Button', listeners: { myEvent: function(button) { Ext.MessageBox.alert('Alert box', 'My custom event is called'); } } }); Ext.defer(function() { button.fireEvent('myEvent'); }, 5000); }); </script> </head> <body> <p> The event will be called after 5 seconds when the page is loaded. </p> <div id = 'helloWorldPanel' /> <!-- panel will be rendered here-- > </body> </html>Once the page is loaded and document is ready the UI page with button will appear and as we are firing an event after 5 sec the document is ready the alert box will appear after 5 seconds.
Here we have written the custom event 'myEvent' and we are firing events as button.fireEvent(eventName);
These are the three ways of writing events in Ext JS.
Data package is used for loading and saving all the data in the application.
The data package has numerous number of classes but the most important classes are:
- Modal
- Store
- Proxy
Model:
The base class for modal is Ext.data.Model.It represents an entity in an application. It binds the store data to view. It has mapping of backend data objects to the view dataIndex. The data is fetched with the help of store.Creating a Model:
For creating a model we need to extend Ext.data.Model class and we need to define fields their name and mapping.Ext.define('StudentDataModel', { extend: 'Ext.data.Model', fields: [ {name: 'name', mapping : 'name'}, {name: 'age', mapping : 'age'}, {name: 'marks', mapping : 'marks'} ] });Here the name should be same as the dataIndex which we declare in the view and the mapping should match the data either static or dynamic from database which is to be fetched using store.
Store:
The base class for store is Ext.data.Store. It contains the data locally cached which is to be rendered on view with the help of model objects. Store fetches the data using proxies which has the path defined for services to fetch the backend data.Store data can be fetched from two ways static or dynamic.
Static store:
For static store we will have all the data present in the store as below:Ext.create('Ext.data.Store', { model: 'StudentDataModel', data: [ { name : "Asha", age : "16", marks : "90" }, { name : "Vinit", age : "18", marks : "95" }, { name : "Anand", age : "20", marks : "68" }, { name : "Niharika", age : "21", marks : "86" }, { name : "Manali", age : "22", marks : "57" } ]; });
Dynamic store:
The dynamic data can be fetched using proxy. we can have proxy can fetched data from Ajax, Rest and Json.Proxy:
The base class for proxy is Ext.data.proxy.Proxy. Proxy is used by Models and Stores to handle the loading and saving of Model data.There are two types of proxies:
- Client Proxy
- Server Proxy
Client Proxy
Client proxies include Memory and Local Storage using HTML5 local storage.Server Proxy
Server proxies handles data from remote server using Ajax, Json data and Rest service.Defining proxies in the server:
Ext.create('Ext.data.Store', { model: 'StudentDataModel', proxy : { type : 'rest', actionMethods : { read : 'POST' // Get or Post type based on requirement }, url : 'restUrlPathOrJsonFilePath', // here we have to include the rest URL path which fetches data from database or Json file path where the data is stored reader: { type : 'json', // the type of data which is fetched is of JSON type root : 'data' }, } });
Description
Extjs provides the facility to use different font packages. Font packages are used to add different classes for icons available in package.1. Font-Awesome
2. Font-Pictos
Font-Awesome
ExtJS new theme Triton has the inbuilt font family font-awesome included in the framework inself so we do not need any explicit require for the font-awesome stylesheet.Below is the example of using Font-Awesome classes in Triton theme.
Font-Awesome with Triton theme
When we are using any other theme other than Triton we need to require or add stylesheet for font-awesome explicitly.
Below is the example of using Font-Awesome classes without Triton theme.
Font-Awesome with normal theme(Any theme other then Triton theme)
Font-Pictos
Font-pictos is not include in framework for EXTJS so we have to require it first and only licenced user of sencha will have the benefit to use font-pictos.Steps to add font-pictos
1. Require font-pictos class like :
"requires": ["font-pictos"]2. Now add pictos classes as:
iconCls: 'pictos pictos-home'Application Styling refers to user adjustment of the look and feel of components. These adjustments may include: color, color gradients, font, margins/padding, etc. Ext JS 6 has a new way of styling the application.
It uses SCSS for styling. SCSS is the more dynamic way of writing CSS code. We can write the variables in our style sheet with the help of this. But a browser cannot understand SCSS it can only understand CSS, so all the SCSS file should get compiled into CSS to a production ready code.
That why SCSS file is called preprocessor files. In Extjs compilation is done through Sencha CMD tool.
Sencha CMD compiles it manually only once using command as below:
sencha app build [development]
Global_CSS is the main CSS file which has all SCSS variable associted with it in ExtJS which we can use in our application for customizing our theme by providing different value based on our need.
Below are some of the CSS variable available in the Global_CSS in Extjs:
S.N. | Variable & Description |
---|---|
1 | $base-color $base-color : color (e.g. $base-color : #808080) This base color to be used throughout the theme. |
2 | $base-gradient $base-gradient : string (e.g. $base-gradient : 'matte') The base gradient to be used throughout the theme. |
3 | $body-background-color $body-background-color : color (e.g. $body-background-color : #808080) Background color to apply to the body element. If set to transparent or 'none' no background-color style will be set on the body element |
4 | $color $color : color (e.g. $color : #808080) The default text color to be used throughout the theme |
5 | $font-family $font-family : string (e.g. $font-family : arial) The default font-family to be used throughout the theme. |
6 | $font-size $font-size : number (e.g. $font-size : 9px ) The default font-size to be used throughout the theme. |
7 | $font-weight $font-weight : string/number (e.g. $font-weight : normal ) The default font-weight to be used throughout the theme |
8 | $font-weight-bold $font-weight-bold : string/number (e.g. $font-weight-bold : bold ) The default font-weight for bold font to be used throughout the theme |
9 | $include-chrome $include-chrome : boolean (e.g. $include-chrome : true) True to include Chrome specific rules |
10 | $include-ff $include-ff : boolean (e.g. $include-ff : true) True to include Firefox specific rules |
11 | $include-ie $include-ie : boolean (e.g. $include-ie : true) True to include Internet Explorer specific rules for IE9 and lower |
12 | $include-opera $include-opera : boolean (e.g. $include-opera : true) True to include Opera specific rules |
13 | $include-safari $include-safari : boolean (e.g. $include-safari : true) True to include Opera specific rules |
14 | $include-webkit $include-webkit : boolean (e.g. $include-webkit : true) True to include Webkit specific rules |
S.N. | Drawing |
---|---|
1 | CircleThis graphics is used to create circulare shape. |
2 | RectangleThis graphics is used to create rectanglar shape. |
3 | ArcThis graphics is used to create an arc shape. |
4 | EllipseThis graphics is used to create ellipse shape. |
5 | EllipticalArcThis graphics is used to create elliptical arc shape. |
6 | ImageThis graphics is used to add image to your application. |
7 | PathThis graphics is used to create free path. |
8 | TextThis graphics is used to add any text to your appication. |
9 | Translate after renderThis property is used to move your starting point in your container after the graphic is rendered. It can be used with any graphics. |
10 | RotationThis property is used to add rotation to the drawing added. It can be used with any graphics. |
11 | SquareThis graphic is used to create a square. |
This program is to show month in different locale to see the effect, try the following program:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-fr.js"></script> <script type="text/javascript"> Ext.onReady(function() { var monthArray = Ext.Array.map(Ext.Date.monthNames, function (e) { return [e]; }); var ds = Ext.create('Ext.data.Store', { fields: ['month'], remoteSort: true, pageSize: 6, proxy: { type: 'memory', enablePaging: true, data: monthArray, reader: {type: 'array'} } }); Ext.create('Ext.grid.Panel', { renderTo: 'grid', id : 'gridId', width: 600, height: 200, title:'Month Browser', columns:[{ text: 'Month of the year', dataIndex: 'month', width: 300 }], store: ds, bbar: Ext.create('Ext.toolbar.Paging', { pageSize: 6, store: ds, displayInfo: true }) }); Ext.getCmp('gridId').getStore().load(); }); </script> </head> <body> <div id="grid" /> </body> </html>This will produce following result:
Description:
For Using different locale other than English we would need to add the locale specific file in our program here we are using https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-fr.js for French. You can use different locale for different language such as https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-ko.js for korean etc.
This program is to show date picker in Korean locale to see the effect, try the following program:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-classic/resources/theme-classic-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-ko.js"></script> <script type="text/javascript"> Ext.onReady(function() { Ext.create('Ext.picker.Date', { renderTo: 'datePicker' }); }); </script> </head> <body> <div id="datePicker" /> </body> </html>This will produce following result:
Below are the few locales available in ExtJS and the main file locale URL to be changed.
Locale | Language | Locale URL |
---|---|---|
ko | Korean | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-ko.js |
fr | French | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-fa.js |
es | Spanish | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-es.js |
ja | Japanese | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-ja.js |
it | Italian | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-it.js |
ru | Russian | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-ru.js |
zh_CN | Simplifies Chinese | https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/locale/locale-zh_CN.js |
What is accessibility?
In general accessibility means availability, the content is accessible means the content is available.In software terms the application is accessible means the application is available for all. Here all means the persons with disabilities, the visually impaired once or those who use screen readers, to use a computer or those who prefers all the navigation with keyboard instead of using a mouse.
The applications which are accessible are called ARIA (Accessible Rich Internet Applications).
Accessibility in Ext JS:
Ext JS is designed to keep this in mind that it should work with all keyboard navigations.It has built in tab indexing and focus-ability, and it is always on by default so we do not need to add any property to enable this functionality.This functionality allows all keyboard enabled components to interact with the user when tabbed into. Like we can use tab for moving on to next component instead of a mouse to move on that component. Same way we can shift+tab for moving backward and enter keyboard for clicking etc.
Focus styling and tabs:
The Focus is inbuilt in Extjs when using keystroke for tabbing.Below example shows how to the style changes when the focus changes with the tabs.
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-crisp/resources/theme-crisp-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.onReady(function(){ Ext.create('Ext.Button', { renderTo: Ext.getElementById('button1'), text: 'Button1', listeners: { click: function() { Ext.MessageBox.alert('Alert box', 'Button 1 is clicked'); } } }); Ext.create('Ext.Button', { renderTo: Ext.getElementById('button2'), text: 'Button2', listeners: { click: function() { Ext.MessageBox.alert('Alert box', 'Button 2 is clicked'); } } }); Ext.create('Ext.Button', { renderTo: Ext.getElementById('button3'), text: 'Button3', listeners: { click: function() { Ext.MessageBox.alert('Alert box', 'Button 3 is clicked'); } } }); }); </script> </head> <body> <p>Please click the button to see event listener:</p> <span id="button3"/> <span id="button2"/> <span id="button1"/> </body> </html>To see the effect use tab for moving from next button and shft+tab for focusing backward use enter and see the focused button's related alert would pop up
ARIA theme:
ExtJS provides the theme aria for the visually impaired person.Here the example to show the aria theme which is more accessible for visually impaired.
Below example shows the Aria theme:
<!DOCTYPE html> <html> <head> <link href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-aria/resources/theme-aria-all.css" rel="stylesheet" /> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script type="text/javascript"> Ext.require([ 'Ext.grid.*', 'Ext.data.*' ]); // Creation of data model Ext.define('StudentDataModel', { extend: 'Ext.data.Model', fields: [ {name: 'name', mapping : 'name'}, {name: 'age', mapping : 'age'}, {name: 'marks', mapping : 'marks'} ] }); Ext.onReady(function(){ // Store data var myData = [ { name : "Asha", age : "16", marks : "90" }, { name : "Vinit", age : "18", marks : "95" }, { name : "Anand", age : "20", marks : "68" }, { name : "Niharika", age : "21", marks : "86" }, { name : "Manali", age : "22", marks : "57" } ]; // Creation of first grid store var firstGridStore = Ext.create('Ext.data.Store', { model: 'StudentDataModel', data: myData }); // Creation of first grid var firstGrid = Ext.create('Ext.grid.Panel', { store : firstGridStore, columns : [{ header: "Student Name", dataIndex: 'name', id : 'name', flex: 1, sortable: true },{ header: "Age", dataIndex: 'age', flex: .5, sortable: true },{ header: "Marks", dataIndex: 'marks', flex: .5, sortable: true }], stripeRows : true, title : 'First Grid', margins : '0 2 0 0' }); // Creation of a panel to show both the grids. var displayPanel = Ext.create('Ext.Panel', { width : 600, height : 200, layout : { type: 'hbox', align: 'stretch', padding: 5 }, renderTo : 'panel', defaults : { flex : 1 }, items : [ firstGrid ] }); }); </script> </head> <body> <div id = "panel" > </div> </body> </html>This will produce following result and you can use tab and mouse up and down keys for moving the focus across the grid and the theme is basically for the visually impaired people.
This is how ExtJS inbuilt theme and focusing makes it easy to accessible for anyone.
Any JavaScript code can be debug using alert() box or console.log() or with the debug pointer in a debugger.
- Alert box:
place an alert box in the code where you want to check the flow or any variable value. e.g alert('message to show' + variable); - Development/debugging Tool:
Debugger is the most important tool for any developer to check the issue and error in the code while developing. Ext JS is a JavaScript framework so it can be easily debugged using developer tools provided by or specific to different browsers.
All the major browser have their developer tools available to test and debug JavaScript code.
Popular debuggers are IE development tool for IE, firebug for firefox, chrome development tool for Chrome browser.
Chrome debugger comes with Chrome browser but firebug has to get installed specifically as it doesn’t come as a package with firefox.
Here is a link to install firebug for firefox browser http://getfirebug.com
Shortcut to open development tool in windows OS is F12 keyboard key.
How to debug JS code in debugger:
There are two ways to debug JavaScript code- Placing console.log() in the code and see the value of the log which will be printed in the console of development tool.
- Using breakpoints in development tool:
- Open the file in among all the available scripts under script tag
- Now place a breakpoint to the line you want to debug
- Run the application in browser
- Now whenever the code flow will reach to this line it will break the code and stay there until user run the code by keys F6(go to next line of code), F7(go inside the function) or F8(go to the next breakpoint or run the code if there is no more breakpoints) based on flow you want to debug.
- You can select the variable or the function you want to see the value of.
- You can use console to check the value or to check some changes in browser itself.
Ext.is class:
This class checks the platform you are using, whether it is a phone or desktop, a mac or windows operating system. These are the following methods related to Ext.is class
S.N. | Methods & Description |
---|---|
1 | Ext.is.Platforms This function returns the platform available for this version. E.g. When you run following function it returns something like this: [Object { property="platform", regex=RegExp, identity="iPhone"}, Object { property="platform", regex=RegExp, identity="iPod"}, Object { property="userAgent", regex=RegExp, identity="iPad"}, Object { property="userAgent", regex=RegExp, identity="Blackberry"}, Object { property="userAgent", regex=RegExp, identity="Android"}, Object { property="platform", regex=RegExp, identity="Mac"}, Object { property="platform", regex=RegExp, identity="Windows"}, Object { property="platform", regex=RegExp, identity="Linux"}] |
2 | Ext.is.Android This function will return true if you are using Android operating system else it returns false. |
3 | Ext.is.Desktop This function will return true if you are using a desktop for the application else it returns false. |
4 | Ext.is.Phone This function will return true if you are using a mobile else it returns false. |
5 | Ext.is.iPhone This function will return true if you are using iPhone else it returns false. |
6 | Ext.is.iPod This function will return true if you are using iPod else it returns false. |
7 | Ext.is.iPad This function will return true if you are using iPad else it returns false. |
8 | Ext.is.Windows This function will return true if you are using windows operating system else it returns false. |
9 | Ext.is.Linux This function will return true if you are using linux operating system else it returns false. |
10 | Ext.is.Blackberry This function will return true if you are using Blackberry else it returns false. |
11 | Ext.is.Mac This function will return true if you are using mac operating system else it returns false. |
As the name indicates this class gives information about the feature is supported by the browser/ device basically current environment or not.
S.N. | Methods & Description |
---|---|
1 | Ext.supports.History This return the Boolean value based in the device supports HTML 5 history as window.history or not. If the device supports history then it returns true else false. |
2 | Ext.supports.GeoLocation This return the Boolean value based in the device supports geolocation method or not. Internally it checks for navigator.geolocation method. |
3 | Ext.supports.Svg This return the Boolean value based in the device supports HTML 5 feature scalable vector graphics (svg) method or not. Internally it checks for doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect. |
4 | Ext.supports.Canvas This return the Boolean value based in the device supports HTML 5 feature canvas to draw method or not. Internally it checks for doc.createElement('canvas').getContext and return value based on the output of this method. |
5 | Ext.supports.Range This return the Boolean value based in the browser supports document.createRange method or not. |
Ext.String class has various methods to work with string data, most used method are encoding decoding, trim, toggle, urlAppend eyc.
Encoding Decoding function: These are the function available in Ext.String class to encode and decode HTML values.
S.N. | Methods & Description |
---|---|
1 | Ext.String.htmlEncode This function is used to encode html value to make it parsable. E.g. Ext.String.htmlEncode("< p > Hello World < /p >"); Output - "< p > Hello World < /p >". |
2 | Ext.String.htmlDecode This function is used to decode the encoded html value E.g. Ext.String.htmlDecode("< p > Hello World < /p >"); Output - "< p > Hello World < /p >" |
3 | Ext.String.trim This function is to trim unwanted space in the string. E.g. Ext.String.trim(' hello '); Output – "hello" |
4 | Ext.String.urlAppend This method is used to append value in URL string E.g. Ext.String.urlAppend('https://www.google.com' , 'hello'); Output - "https://www.google.com?hello" Ext.String.urlAppend('https://www.google.com?index=1' , 'hello'); Output – "https://www.google.com?index=1&hello" |
5 | Ext.String.toggle This function is to toggle the value between two different values. E.g. var toggleString = 'ASC' toggleString = Ext.String.toggle(a, 'ASC', 'DESC'); Output – DESC as toggleString had value ASC. Now again if we print the same we will get toggleString = “ASC” this time as it had value 'DESC'. It is similar to ternary operator toggleString = ((toggleString =='ASC')? 'DESC' : 'ASC' ); |
S.N. | Methods & Description | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Ext.userAgent() This function gives information about browser userAgent. UserAgent is to identify the browser and operating system to the web server. E.g. If you are working in Mozilla it returns some thing like : "mozilla/5.0 (windows nt 6.1; wow64; rv:43.0) gecko/20100101 firefox/43.0" |
||||||||||||||
2 | Version related function These function returns the version of the browser currently in use if the function is called related to IE in firefox browser it return 0. These functions are Ext.firefoxVersion, Ext.ieVersion etc. E.g. if we are using firefox browser and we call method Ext.ieVersion for fetching version of IE then it returns 0 or if we are using the same method in IE browser then it will return the version we are using such as 8,9 etc. |
||||||||||||||
3 | Ext.getVersion() This function return the current Ext JS version in use. E.g. If we call Ext.getVersion() it return an array of values such as version , short version etc. Ext.getVersion().version return the current version of Ext JS used in program such as “4.2.2". |
||||||||||||||
4 | Browser related functions These function returns Boolean values based on the browser in use. This method are Ext.isIE, Ext.isIE6, Ext.isFF06, Ext.isChrome. E.g. If we are using Chrome browser then function Ext.isChrome will return true all other will return false. |
||||||||||||||
5 | Ext.typeOf() This function return the datatype of the variable such as E.g. var a = 5; var b = 'hello'; Ext.typeOf(a); Output – Number Ext.typeOf(b); Output - String |
||||||||||||||
6 | DataType related methods: These function returns boolean value based on the datatype of variable.E.g. var a = ['a', 'bc']; var b = 'hello'; var c = 123; var emptyVariable; var definedVariable; function extraFunction(){return true;}
|
Advertisements
No comments:
Post a Comment