-
Notifications
You must be signed in to change notification settings - Fork 24
user ui 3.2 code
This document provides the code overview of the user console in eucalyptus.
Before delving into the user console code, we recommend people to visit jquery sites to review their documentation (http://api.jquery.com/). It would be critical to understand how jquery performs DOM navigation, manipulation, ajax, event handling, etc. In addition, as we use jquery-ui components across pages (e.g., dialog, table, flip animation), the documentation for each component can be useful resources. Finally, jquery UI widget factory has been the fundamental building blocks of the user console. Thus, we recommend reviewing the materials, such as:
The user-console is designed to be rich ajax client(i.e., rendering is 100% the browser-side). The set of javascript files run the user-console in client's browser and they interact with proxy, which is a thin layer that transforms data between the user-console clients and the Eucalyptus CLC (The proxy also implements authentication and concurrency, which is not the scope of this doc).
We use jquery (1.8.2) as the most fundamental javascript library. In addition, jquery widget factory has been applied as the fundamental building blocks for most JS codes (http://wiki.jqueryui.com/w/page/12138135/Widget%20factory) The primary motivation behind the widget factory is to give the codes a structure that is easy to understand and extend. In particular, widget factory is useful because:
- It gives a sense of OOP to the javascript language (so widget=class in rough sense). In particular,
- It allows us to map a end-user function (such as dashboard) to a set of JS variables and methods, which would be considered as an "object", and
- Such "object" can have a lifecycle, meaning that there is a initialization (_init and _create methods) and destruction (_destroy).
- We can define methods within a widget's namespace (e.g., instance, image, dashboard, etc), which would access and change the states of the widget. So they can be considered as methods of a class in OOP.
- Widget factory gives a sense of encapsulation in the virtue of "public/private" methods using the naming convention. If the method name starts with '_', it's considered a private method, which is not callable via string invocation of the method name.
- Widget factory even provides a form of inheritence. In our implementation, we implemented the "flip to help" in the base widget(eucawidget.js), which is inherited by different landing pages (instance.js, dashboard.js, etc).
The bootstrap of the user-console happens with 'index.html', which is the only static HTML file in the code. The 'index.html' serves these purposes:
- List javascript files that should be downloaded upon loading the html in the client browsers (in the \<head\></head\>)
- Gives the broad HTML layout with nested div tags. For instance, there's a 'euca-container' in the outer-most area of the body, which contains 'euca-header-container', 'euca-explorer(menu area)', 'euca-notification-container', 'euca-main-outercontainer (main area such as dashboard)', and 'euca-footer-container'.
- Defines templates that is later copied and manipulated by JS codes. For instance, there is a 'footerTmpl' script, in which the copyright text is later copied from MESSAGE.properties file. The template is critical in implementing internationalization and the clean separation between the UI structure (the business of css/html people) and the JS codes (the application behavior).
- To the last point of clean separation, it is often the job of CSS/HTML person to change the templates in the index.html, along with the CSS files that decorates what's defined in the templates. Once the template is defined, JS guys can manipulate it with the data from the proxy and i18n messages.
We use template for supporting i18n of the console texts. Messages{LANG_CODE}.properties files in static/custom defines variable=texts in different languages. 'i18n.js' downloads the messages file appropriate for the configured langauge, and loads the variables=texts pairs into the JS application. The templates in the index.html contains variables (with the enclosing marker, ""), which is later substituted with texts by calling .render() method.
Many JS codes in the static/js directory has the following format:
(function($, eucalyptus) {
$.widget("eucalyptus.FEATURE", { var_X : 'true', var_Y: 'any string', options: { variable_Z : 'some string' } , _init: function () { },
_create: function () { },
_destroy: function () { this.element.hide(); } });
}(jQuery,
window.eucalyptus ? window.eucalyptus : window.eucalyptus = {}));
- The name given to the widget method, "eucalyptus.FEATURE", is the name by which other JS files will refer to this widget (eucalytpus part won't be needed if the calling widget lives within that namespace). For instance, calling 'element.instance()' will replace the current div (e.g., main area), represented by 'element', to be reloaded with the HTML contents that 'instance' widget will produce.
- _init method is called upon calling the widget from JS files. Typically the init method is reponsible for formatting and producing HTML snippets, that is appended to the DOM element, that the widget is attached to. In our case, we often substitute templates with texts, decorates the HTML, and setup dialogs that is to be invoked with menu actions.
- while '_init' is called everytime widget is being called from JS files, '_create' is called only once when the application is loaded in the browser. So the initialization that should happen only once must be defined in 'create'.
- '_destroy' method defines any clean-up procedures necessary for the widget (externally, it's invoked by calling close() method on the widget). For instance, we want to hide all HTML DOMs that the widget produced while cleaning up.
- the variables defined in the widget (var_X and var_Y in the sample) are the variables only accessible within the widget (so private variables).
- the variables defined in the 'options' property is the variable set by users of the widget, so that parameters can be set at widget's construction time.
'eucalyptus.js' is the starting point. Upon loading the index.html, eucalyptus.js makes ajax call to the proxy to get the initial parameters, such as the language code. After checking the browser version, it makes sure if session-id is found in the cookie. If not, it calls login widget, to display the login window. If the sesion-id is found(i.e., logged in), it asks for a set of session-specific data from the proxy, which includes user_name, account, instance types, etc. Then it calls 'main' function defined in main.js (explained later).
when the session-id is not found, login widget is always invoked. The login widget constructs the login window using the template defined in index.html. It then makes ajax call to the proxy (the actual ajax call definition is in eucalytpus.js, as a callback, for unknown reason)
When the console is already logged-in, or after login attempts succeed, main function, defined in main.js, is called. In the initial step, the main wait for the 'eucadata' which handles client-side caching of server data, to become "online". The 'eucadata' becomes "online" if the proxy responds to eucadata.js with resource state (emi, instances, etc). Note that 'when', 'done', and 'fail' are the jquery methods that allows a set of asynchrous routines to be invoked in order (i.e., calling ajax 1 and 2 in sequence, not in parallel). So the body of done() method is not invoked until the body of when releases its contol.
Upon the cache becoming "online", the main calls the widgets -- explorer, maincontainer, notification, and footer, which are the widgets managing the top-menu, main, notification, and the footer areas of the layout. explorer.js defines the widget that displays the top menus and triggers events according to user's menu selection.
maincontainer is the "dispatcher" widget that handles reloading pages, triggered by menu selections. In the 'updateSelected' method of main_container.js, the widgets representing different landing pages (e.g., dashboard, instance, image, etc), are invoked depending on the chosen menu text. Note that the code this.element.keypair()
works, because 'this.element' represent the DOM binding of the calling widget (maincontainer) and the maincontainer widget is bound to div ID='euca-main-container'.
Since almost every landing page is based on table, we made 'eucatable' as the basic abstraction of landing pages. 'eucatable.js' in template/ directory defines a widget, that implements basically a table abstraction for the user console. The widget uses the external libarary 'jquery ui table' and decorate the table with our own features, such as search, menu actions, table events, etc. The landing page widgets such as instance.js and image.js, CONTAINS the eucatable widget, with differing arguments given to the eucatable at the construction time.
Another fundamental abstraction in the console is the 'eucadialog', which implments add-ons to jquery ui dialog. A landing page CONTAINS one or many 'eucadialog', which is customized to perform the intended actions (e.g., run instances). 'eucadialog.js' defines a set of methods that is to customize a dialog for the caller's intent. Typically, the caller, for example instance.js, calls eucadialog, with callbacks attached to the dialog's event. For instance, instance.js will invoke 'terminate-instance-dialog', which is an instance of eucadialog widget, with euca-terminate-instances as a callback attached to 'OK' button.
eucadata.js is the widget that pulls resource state from the proxy and make them accessible to widgets across the console. For example, dashboard.js retrieves instance, volume, snapshot data from the eucadata, instead of pulling them directly from the proxy. The primary purpose of the caching layer is the conding convenience, although it incurs the problem of the data not up-to-date. eucadata.js defines the frequency by which resource states are pulled from the proxy (10 seconds).
support.js contains variables and methods, whose purpose doesn't fall into a single feature, but the usage is shared by multiple pages. It includes constants, utility methods, and the variables that are set/get by multiple widgets.
tag:rls-3.2