diff options
author | Petr Vobornik <pvoborni@redhat.com> | 2012-12-14 16:39:20 +0100 |
---|---|---|
committer | Petr Vobornik <pvoborni@redhat.com> | 2013-05-06 16:22:17 +0200 |
commit | 693dc560620d52dc24a0ab89e20147b10ed4f469 (patch) | |
tree | a748bc7a89f76d8ea4cdf1dc1a91895192a7ea56 /install/ui/src/freeipa/widgets | |
parent | a4d9e19c79b60b8f7269141374b2e3b6c0d66c45 (diff) | |
download | freeipa-693dc560620d52dc24a0ab89e20147b10ed4f469.tar.gz freeipa-693dc560620d52dc24a0ab89e20147b10ed4f469.tar.xz freeipa-693dc560620d52dc24a0ab89e20147b10ed4f469.zip |
Menu and application controller refactoring
https://fedorahosted.org/freeipa/ticket/3235
https://fedorahosted.org/freeipa/ticket/3236
Diffstat (limited to 'install/ui/src/freeipa/widgets')
-rw-r--r-- | install/ui/src/freeipa/widgets/App.js | 193 | ||||
-rw-r--r-- | install/ui/src/freeipa/widgets/Menu.js | 271 |
2 files changed, 464 insertions, 0 deletions
diff --git a/install/ui/src/freeipa/widgets/App.js b/install/ui/src/freeipa/widgets/App.js new file mode 100644 index 000000000..662d0ee0b --- /dev/null +++ b/install/ui/src/freeipa/widgets/App.js @@ -0,0 +1,193 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2012 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +define(['dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/_base/array', + 'dojo/dom', + 'dojo/dom-construct', + 'dojo/dom-prop', + 'dojo/dom-class', + 'dojo/dom-style', + 'dojo/query', + 'dojo/on', + 'dojo/Evented', + 'dojo/Stateful', + './Menu', + 'dojo/NodeList-dom' + ], + function(declare, lang, array, dom, construct, prop, dom_class, + dom_style, query, on, Stateful, Evented, Menu) { + + /** + * Main application widget + * + * This class serves as top level widget. It creates basic UI: controls + * rendering of header, footer and placeholder for facets. + * + * @name freeipa.widgets.app + * @class + */ + var app = declare([Stateful, Evented], { + + //widgets + menu_widget: null, + + //nodes: + + domNode: null, + + container_node: null, + + background_node: null, + + header_node: null, + + password_expires_node: null, + + logged_nodes: null, + + logged_user_node: null, + + logged_user_link_node: null, + + logout_link_node: null, + + menu_node: null, + + content_node: null, + + app_id: 'container', + + logged: false, + + _loggedSetter: function(value) { + this.logged = value; + if (this.logged_nodes) { + this.logged_nodes.style('visibility', value ? 'visible' : 'hidden'); + } + }, + + fullname: '', + + _fullnameSetter: function(value) { + this.fullname = value; + if (this.logged_user_node) { + prop.set(this.logged_user_node, 'textContent', value); + } + }, + + render: function() { + // TODO: this method may be split into several components + + + this.domNode = construct.create('div', { + id: this.app_id + }); + + if (this.container_node) { + construct.place(this.domNode, this.container_node); + } + + this._render_background(); + this._render_header(); + + this.menu_node = this.menu_widget.render(); + construct.place(this.menu_node, this.domNode); + + this.content_node = construct.create('div', { + id: 'content' + }, this.domNode); + }, + + _render_background: function() { + var inner_html = ''+ + '<div id="background-header"></div>'+ + '<div id="background-navigation"></div>'+ + '<div id="background-left"></div>'+ + '<div id="background-center"></div>'+ + '<div id="background-right"></div>'; + + this.background_node = construct.create('div', { + id: 'background', + innerHTML: inner_html + }, this.domNode); + }, + + _render_header: function() { + this.header_node = construct.create('div', { + id: 'header' + }, this.domNode); + + // logo + construct.place(''+ + '<span class="header-logo">'+ + '<a href="#"><img src="images/ipa-logo.png" />'+ + '<img src="images/ipa-banner.png" /></a>'+ + '</span>', this.header_node); + + // right part + construct.place(''+ + '<span class="header-right">'+ + '<span class="header-passwordexpires"></span>'+ + '<span id="loggedinas" class="header-loggedinas" style="visibility:hidden;">'+ + '<a href="#"><span id="login_header">Logged in as</span>: <span class="login"></span></a>'+ + '</span>'+ + '<span class="header-loggedinas" style="visibility:hidden;">'+ + ' | <a href="#logout" id="logout">Logout</a>'+ + '</span>'+ + '<span id="header-network-activity-indicator" class="network-activity-indicator">'+ + '<img src="images/spinner-header.gif" />'+ + '</span>'+ + '</span>', this.header_node); + + + this.password_expires_node = query('.header-passwordexpires', this.header_node)[0]; + this.logged_nodes = query('.header-loggedinas', this.header_node); + this.logged_header_node = dom.byId('login_header');// maybe ditch the id? + this.logged_user_node = query('#loggedinas .login', this.header_node)[0]; + this.logged_user_link_node = query('#loggedinas a', this.header_node)[0]; + this.logout_link_node = dom.byId('logout'); + + on(this.logout_link_node, 'click', lang.hitch(this,this.on_logout)); + on(this.logged_user_link_node, 'click', lang.hitch(this,this.on_profile)); + + construct.place(this.header_node, this.domNode); + }, + + on_profile: function(event) { + event.preventDefault(); + this.emit('profile-click'); + }, + + on_logout: function(event) { + event.preventDefault(); + this.emit('logout-click'); + }, + + constructor: function(spec) { + spec = spec || {}; + this.menu_widget = new Menu(); + } + + }); + + return app; +});
\ No newline at end of file diff --git a/install/ui/src/freeipa/widgets/Menu.js b/install/ui/src/freeipa/widgets/Menu.js new file mode 100644 index 000000000..0f69efa9d --- /dev/null +++ b/install/ui/src/freeipa/widgets/Menu.js @@ -0,0 +1,271 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2012 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +define(['dojo/_base/declare', + 'dojo/_base/array', + 'dojo/_base/lang', + 'dojo/dom', + 'dojo/dom-construct', + 'dojo/dom-prop', + 'dojo/dom-class', + 'dojo/dom-style', + 'dojo/dom-attr', + 'dojo/query', + 'dojo/Evented', + 'dojo/on', + '../ipa'], function(declare, array, lang, dom, construct, prop, dom_class, + dom_style, attr, query, Evented, on, IPA) { + + return declare([Evented], { + /** + * @name freeipa.widget.menu + * @class + * + * Creates UI for freeipa.navigation.menu. Provides an event when + * a menu items is selected. + * + * event: item-select(menu_item) + */ + + + /** + * Object store of menu items + * @protected + * @type freeipa.navigation.menu + */ + menu: null, + + /** + * domNode of this widget. FIXME: move to superclass (none yet) + * @type Node + */ + domNode: null, + + /** + * Turns off update on data change + * @type Boolen + */ + ignore_changes: false, + + /** + * Css class for nodes containing a submenu of certain level_class + * @type String + */ + level_class: 'menu-level', + + /** + * Renders widget's elements + */ + render: function() { + if (this.domNode) { + construct.empty(this.domNode); + } else { + this.domNode = construct.create('div', { + 'class': 'navigation' + }); + } + if (this.menu) { + this._render_children(null, this.domNode, 1); + } + return this.domNode; + }, + + /** + * Render children of menu_item + * Top level items are rendered if menu_items is null + * + * @protected + * @param {menu_item|null} menu_item + * @param {Node} node + * @param {Number} level + */ + _render_children: function (menu_item, node, level) { + + var self = this; + var name = menu_item ? menu_item.name : null; + var children = this.menu.items.query({ parent: name }, + { sort: [{attribute:'position'}]}); + + var lvl_class = this._get_lvl_class(level); + + if (children.total > 0) { + var menu_node = construct.create('div', { + 'class': 'submenu ' + lvl_class + //style: { display: 'none' } + }); + + if (menu_item) { + attr.set(menu_node, 'data-item', menu_item.name); + } + + var ul_node = construct.create('ul', null, menu_node); + + array.forEach(children, function(menu_item) { + + var click_handler = function(event) { + self.item_clicked(menu_item, event); + event.preventDefault(); + }; + + var li_node = construct.create('li', { + 'data-name': menu_item.name, + click: click_handler + }, ul_node); + + var a_node = construct.create('a', { + click: click_handler + }, li_node); + + this._update_item(menu_item, li_node); + + // create submenu + this._render_children(menu_item, menu_node, level + 1); + }, this); + + construct.place(menu_node, node); + } + }, + + _get_lvl_class: function(level) { + return this.level_class + '-' + level; + }, + + /** + * Updates content of li_node associated with menu_item base on + * menu_item's state. + * + * @protected + * @param {menu_item|string} menu_item + * @param {Node} [li_node] + */ + _update_item: function(menu_item, li_node) { + + if (typeof menu_item === 'string') { + menu_item = this.menu.items.get(menu_item); + } + + if (!li_node) { + li_node = query('li[data-name=\''+menu_item.name+'\']')[0]; + + // Quit for non-existing nodes. + // FIXME: maybe change to exception + if (!li_node) return; + } + + dom_class.toggle(li_node, 'disabled', !menu_item.disabled); + dom_class.toggle(li_node, 'selected', menu_item.selected); + dom_style.set(li_node, { + display: menu_item.hidden ? 'none': 'default' + }); + + var a_node = query('a', li_node)[0]; + + prop.set(a_node, 'href', '#' + menu_item.name); + prop.set(a_node, 'textContent', menu_item.label); + prop.set(a_node, 'title', menu_item.title || menu_item.label); + }, + + /** + * Displays only supplied menu items. + * @param {menu_item[]} menu_items Items to show + */ + select: function(menu_items) { + + // hide all except top level + var exception = this._get_lvl_class(1); + query('div.submenu', this.domNode).forEach(function(submenu_node) { + + if (dom_class.contains(submenu_node, exception)) return; + + dom_style.set(submenu_node, { + display: 'none' + }); + }, this); + + // show and update selected + array.forEach(menu_items, function(item) { + this._update_item(item); + + // show submenu + var item_div = query('div[data-item=\''+item.name+'\']', this.domNode)[0]; + if (item_div) { + dom_style.set(item_div, { + display: 'block' + }); + } + }, this); + }, + + /** + * Handles changes in this.menu object. + * + * @protected + * @param {menu_item} object + * @param {Number} removedFrom + * @param {Number} insertedInto + */ + _items_changed: function(object, removedFrom, insertedInto) { + + if (this.ignore_changes) return; + + if (removedFrom === -1 && insertedInto === -1) { + this._update_item(object); + } else { + // on add or removal, replace whole menu + this.render(); + this.select(this.menu.selected); + } + }, + + /** + * Sets this.menu and starts to watch its changes + * @param {freeipa.navigation.menu} menu + */ + set_menu: function(menu) { + this.menu = menu; + //get all items + var q = menu.items.query(); + q.observe(lang.hitch(this, this._items_changed), true); + on(this.menu, 'selected', lang.hitch(this, function(event) { + this.select(event.new_selection); + })); + }, + + /** + * Internal handler for clicking on menu item. + * Raises item-select event. + */ + _item_clicked: function(menu_item) { + this.emit('item-select', menu_item); + }, + + /** + * Handles click on menu item. + * + * Intended for overriding. + * + * @param {menu_item} menu_item + * @param {Event} event + */ + item_clicked: function(menu_item/*, event*/) { + this._item_clicked(menu_item); + } + }); +});
\ No newline at end of file |