Sencha touch 2: Set button handler parameter to lunch javascript function - extjs

I am using the sencha touch o'really example. Is a conference that have sessions and speakers associated. Imagine that I have a session list that when is tapped the view change to the session details.
I have a view like this:
Ext.define('MyApp.view.session.Detail', {
extend: 'Ext.Container',
xtype: 'session',
config: {
layout: 'vbox',
title: '',
items: [
{
flex: 1,
layout: 'fit',
scrollable: 'vertical',
xtype: 'sessionInfo'
},
{
flex: 2,
xtype: 'speakers',
store: 'SessionSpeakers',
items: [
{
xtype: 'listitemheader',
cls: 'dark',
html: 'Speakers'
}
]
},
{
flex: 1,
xtype: 'button',
text: "Decline Button" ,
handler: function () {
/*HERE I WANT TO PUT SOME DATA FROM THE SESSION OBJECT. FOR EXAMPLE */
alert({title});
alert({start_date});
alert({location});
}
}
]
}
});
So, I want to replace the {title} {start_date} {location} for the corresponding values of the current session.
By the way in the controller I have this:
onSessionTap: function(list, idx, el, record) {
var speakerStore = Ext.getStore('SessionSpeakers'),
speakerIds = record.get('speakerIds');
speakerStore.clearFilter();
speakerStore.filterBy(function(speaker) {
return Ext.Array.contains(speakerIds, speaker.get('id'));
});
if (!this.session) {
this.session = Ext.widget('session');
}
this.session.setTitle(record.get('title'));
this.getSessionContainer().push(this.session);
this.getSessionInfo().setRecord(record);
},
Thanks!!

I could resolve it.
I had to create the button on runtime.
:
In Session view:
Ext.define('MyApp.view.session.Detail', {
extend: 'Ext.Container',
xtype: 'session',
config: {
layout: 'vbox',
title: '',
items: [
{
flex: 1,
layout: 'fit',
scrollable: 'vertical',
xtype: 'sessionInfo'
},
{
flex: 2,
xtype: 'speakers',
store: 'SessionSpeakers',
items: [
{
xtype: 'listitemheader',
cls: 'dark',
html: 'Speakers'
}
]
}
]
}
});
In Session controller:
onSessionTap: function(list, idx, el, record) {
var speakerStore = Ext.getStore('SessionSpeakers'),
speakerIds = record.get('speakerIds');
speakerStore.clearFilter();
speakerStore.filterBy(function(speaker) {
return Ext.Array.contains(speakerIds, speaker.get('id'));
});
if (!this.session) {
this.session = Ext.widget('session');
}
else{
//To avoid multiple buttons
this.session.removeAt(2);
}
this.session.setTitle(record.get('title'));
this.session.add(
Ext.Button.create({
flex: 1,
xtype: 'button',
text:'My button' ,
handler: function () {
alert(record.get('title'));
alert(record.get('start_date'));
} })
);
this.getSessionContainer().push(this.session);
this.getSessionInfo().setRecord(record);
},

Related

extjs grid column renderer not calling

cannot find answer (
Ext.define('Cabinet.view.main.Main', {
extend: 'Ext.Container',
...
items: [
{
xtype: 'toolbar',
docked: 'top',
title: 'Sliding Menu',
items: [
{
xtype: 'button',
id: 'listButton',
iconCls: 'x-fa fa-bars',//list //x-fa fa-mortar-board
ui: 'plain',
listeners : {
tap: 'OnToggle'
}
}
]
},
{
xtype: 'container',
flex: 1,
itemId: 'contentPanel',
reference: 'mainCardPanel',
scrollable: 'y',
layoutConfig:{layoutOnCardChange:true},
layout: {
type: 'card',
anchor: '100%'
}
}
],
,
initialize: function(){
Ext.Viewport.setMenu(this.createMenu(),{
side: 'left',
reveal: true
});
this.callParent();
},
createMenu: function(){
var menu = Ext.create('Ext.Menu', {
width: 250,
defaultListenerScope: true,
scrollable: 'vertical',
controller: 'main',
ViewModel :
{
},
items: [
{
xtype: 'button',
text: 'asd',
viewName: 'acadPerf',
controller: 'main',
listeners : {
tap: function ()
{
if (Ext.Viewport.down('#contentPanel').getActiveItem() != 0) {
Ext.Viewport.down('#contentPanel').getActiveItem().destroy();
}
newView = Ext.create({xtype: this.viewName});
Ext.suspendLayouts();
Ext.Viewport.down('#contentPanel').setActiveItem(newView);
Ext.resumeLayouts(true);
load store
Ext.define('Cabinet.view.charts.acadPerf', {
extend: 'Ext.container.Container',
xtype: 'acadPerf',
viewModel: {
// type: 'charts'
},
layout: 'fit', //responsivecolumn
defaults: {
defaults: {
animation : !Ext.isIE9m && Ext.os.is.Desktop
}
},
listeners: {
render: function () {
this.down('grid').getStore().reload();
}
},
items: [
{
xtype: 'panel',
cls: 'faq-left-sidebar shadow',
header: false,
ui: 'light',
layout: 'fit',
responsiveConfig: {
'width < 1000': {
width: 0,
visible: false
},
'width >= 1000 && width < 1600': {
width: 230,
visible: true
},
'width >= 1600': {
width: 300,
visible: true
}
},
items: [
{
xtype:'grid',
reference: 'mainCardPanel',
store: 'acadPerfStore',
hideHeaders: true,
columns: [
{
text: 'asd',
xtype: 'gridcolumn',
flex: 1,
dataIndex: 'CONTROL_FORM',
cell: {
encodeHtml: false
},
renderer: 'renderTitleColumnAcademPerf'
},
]
}
]
}
],
...
when i at first time click to show menu and then tap on first button - all ok.
renderer work. I see all records.
but when i click once again to show menu and click on first button - i see only content of one record from store. but the size of verical scroll of grid is like all records rendered. in html i see only one record.
What's wrong?

Routing Extjs deeper navigation

I'm writing a Extjs app in the 6.2.0 version, I’ve got a routing situation.
My problem is when we enter on the NavigateDeep if I enter the Url ok it catches but it doesn’t render.
I define the routes on the main Controller like:
Ext.define('App.view.main.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
listen : {
controller : {
'*' : {
unmatchedroute : 'onRouteChange1',
changeroute: 'changeRoute'
}
}
},
routes: {
':node': 'onNavigate',
':node/:id' : 'onNavigateDeep',
':node/:id/:tabid' : 'onNavigateDeepTab'
},
lastView: null,
onRouteChange1: function(){
console.log("hier unmatched route");
},
setCurrentView: function(hashTag) {
hashTag = (hashTag || '').toLowerCase();
console.log("hash:" + hashTag);
var me = this,
refs = me.getReferences(),
mainCard = refs.mainCardPanel,
mainLayout = mainCard.getLayout(),
navigationList = refs.navigationTreeList,
store = navigationList.getStore(),
node = store.findNode('routeId', hashTag) ||
store.findNode('viewType', hashTag),
view = (node && node.get('viewType')) || 'page404',
lastView = me.lastView,
existingItem = mainCard.child('component[routeId=' + hashTag + ']'),
newView;
// Kill any previously routed window
if (lastView && lastView.isWindow) {
lastView.destroy();
}
lastView = mainLayout.getActiveItem();
if (!existingItem) {
newView = Ext.create({
xtype: view,
routeId: hashTag, // for existingItem search later
hideMode: 'offsets'
});
}
if (!newView || !newView.isWindow) {
// !newView means we have an existing view, but if the newView isWindow
// we don't add it to the card layout.
if (existingItem) {
// We don't have a newView, so activate the existing view.
if (existingItem !== lastView) {
mainLayout.setActiveItem(existingItem);
}
newView = existingItem;
}
else {
// newView is set (did not exist already), so add it and make it the
// activeItem.
Ext.suspendLayouts();
mainLayout.setActiveItem(mainCard.add(newView));
Ext.resumeLayouts(true);
}
}
navigationList.setSelection(node);
if (newView.isFocusable(true)) {
newView.focus();
}
me.lastView = newView;
},
onNavigationTreeSelectionChange: function (tree, node) {
var to = node && (node.get('routeId') || node.get('viewType'));
if (to) {
console.log("to;:" + to);
this.redirectTo(to);
}
},
onToggleNavigationSize: function () {
var me = this,
refs = me.getReferences(),
navigationList = refs.navigationTreeList,
wrapContainer = refs.mainContainerWrap,
collapsing = !navigationList.getMicro(),
new_width = collapsing ? 64 : 250;
if (Ext.isIE9m || !Ext.os.is.Desktop) {
Ext.suspendLayouts();
refs.logo.setWidth(new_width);
navigationList.setWidth(new_width);
navigationList.setMicro(collapsing);
Ext.resumeLayouts(); // do not flush the layout here...
// No animation for IE9 or lower...
wrapContainer.layout.animatePolicy = wrapContainer.layout.animate = null;
wrapContainer.updateLayout(); // ... since this will flush them
}
else {
if (!collapsing) {
// If we are leaving micro mode (expanding), we do that first so that the
// text of the items in the navlist will be revealed by the animation.
navigationList.setMicro(false);
}
// Start this layout first since it does not require a layout
refs.logo.animate({dynamic: true, to: {width: new_width}});
// Directly adjust the width config and then run the main wrap container layout
// as the root layout (it and its chidren). This will cause the adjusted size to
// be flushed to the element and animate to that new size.
navigationList.width = new_width;
wrapContainer.updateLayout({isRoot: true});
navigationList.el.addCls('nav-tree-animating');
// We need to switch to micro mode on the navlist *after* the animation (this
// allows the "sweep" to leave the item text in place until it is no longer
// visible.
if (collapsing) {
navigationList.on({
afterlayoutanimation: function () {
navigationList.setMicro(true);
navigationList.el.removeCls('nav-tree-animating');
},
single: true
});
}
}
},
onMainViewRender:function() {
if (!window.location.hash) {
this.redirectTo("dashboard");
}
},
changeRoute: function(controller,route){
this.redirectTo(route,true);
console.log("change route fired");
},
onClickLogoutButton: function () {
// Remove the localStorage key/value
localStorage.removeItem('LoggedIn');
// Remove Main View
this.getView().destroy();
// Add the Login Window
Ext.create({
xtype: 'login'
});
},
onClickShareButton: function(){
var text = window.location;
window.prompt("Copy to clipboard:", text);
},
onNavigate:function(node){
console.log("on route change");
this.setCurrentView(node);
},
onNavigateDeep: function (node, id) {
console.log("Tab");
console.log(node + '/' + id);
var route = node+'/'+id;
this.setCurrentView(route);
},
onNavigateDeepTab: function (node, id, tabid) {
console.log("navigate Tab");
}
});
My main view is:
Ext.define('App.view.main.Main', {
extend: 'Ext.container.Viewport',
xtype: 'app-main',
requires: [
'App.view.Dashboard',
'App.view.immo.List',
'App.view.settings.Settings',
'App.view.news.News',
'App.view.help.Help'
],
controller: 'main',
viewModel: 'main',
cls: 'sencha-dash-viewport',
itemId: 'mainView',
layout: {
type: 'vbox',
align: 'stretch'
},
listeners: {
render: 'onMainViewRender'
},
items: [{
xtype: 'toolbar',
cls: 'dash-dash-headerbar shadow',
height: 64,
itemId: 'headerBar',
items: [{
xtype: 'component',
reference: 'logo',
cls: 'logo',
html: '<div class="main-logo"><img src="resources/images/logo.png">App</div>',
width: 250
}, {
margin: '0 0 0 8',
ui: 'header',
iconCls:'x-fa fa-navicon',
id: 'main-navigation-btn',
handler: 'onToggleNavigationSize',
tooltip: 'Navigation umschalten'
},
'->',
{
xtype: 'button',
ui: 'header',
iconCls: 'x-fa fa-share-alt',
handler: 'onClickShareButton',
tooltip: 'Share'
},
{
iconCls:'x-fa fa-question',
ui: 'header',
href: '#help',
hrefTarget: '_self',
tooltip: 'Hilfe'
}, {
iconCls:'x-fa fa-th-large',
ui: 'header',
href: '#Dashboard',
hrefTarget: '_self',
tooltip: 'Zum Dashboard'
}, {
xtype: 'button',
ui: 'header',
iconCls: 'x-fa fa-power-off',
handler: 'onClickLogoutButton',
tooltip: 'Logout'
}]
}, {
xtype: 'maincontainerwrap',
id: 'main-view-detail-wrap',
reference: 'mainContainerWrap',
flex: 1,
items: [{
xtype: 'treelist',
reference: 'navigationTreeList',
itemId: 'navigationTreeList',
ui: 'navigation',
store: 'NavigationTree',
width: 250,
expanderFirst: false,
expanderOnly: false,
listeners: {
selectionchange: 'onNavigationTreeSelectionChange'
}
}, {
xtype: 'container',
flex: 1,
reference: 'mainCardPanel',
cls: 'sencha-dash-right-main-container',
itemId: 'contentPanel',
layout: {
type: 'card',
anchor: '100%'
}
}]
}]
});
When we click on the tree I change the container but one of them has the route if the :id where I have a Tab:
Ext.define('App.view.settings.Settings',{
extend: 'Ext.tab.Panel',
xtype: 'settings',
itemId:'settings',
requires: [
'App.view.settings.SettingsController',
'App.view.settings.SettingsModel',
'App.view.settings.property.Property',
'App.view.settings.user.User'
],
controller: 'settings-settings',
viewModel: {
type: 'settings-settings'
},
reference: 'tab',
tabPosition: 'left',
tabBar:{
flex: 1,
tabRotation: 0,
tabStretchMax: true,
cls: 'immoNavi'
},
layout: 'Container',
defaults: {
padding: 0,
textAlign: 'left'
},
listeners: {
tabchange: 'onTabChange'
},
items: [{
xtype: 'component',
tabConfig:{
hidden: true
}
},{
title: 'Property',
itemId: 'property',
xtype: 'property',
cls: 'container'
},{
title: 'User',
itemId: 'user',
xtype: 'user'
}]
});
I went through the documentation but didn't find any tip that might help me with it. What am I missing here? Should I take care the rendering on the tab controller? Or How?
Thanks in advance for the help

ExtJS pass variable from controller to view

I have a base controller that has a user object that is bind with the user data after login success. At this point the view that I need to pass the user object is already instantiated. Here is my base controller:
Ext.define('App.abstract.app.Controller', {
extend: 'Ext.app.Controller',
user: {} // user object that can be accessed by any controller
});
Now, here is my Main.js controller that extends from my base controller and that implements the login function:
Ext.define('App.controller.Main', {
extend: 'App.abstract.app.Controller',
refs: [{
ref: 'main',
selector: 'app-main'
},
{
ref: 'header',
selector: 'appheader'
}],
init: function (application) {
this.control({
"loginform": {
loguser: this.onLoginUser
},
'app-main #logoutBtn': {
//click: this.onLogoutUser
}
});
},
onLoginUser: function (form, values) {
var me = this;
values.password = App.util.MD5.encode(values.password);
App.util.Ajax.request({
url: '/api/Security/ValidateUser',
jsonData: values,
success: function (response, opts) {
var obj = Ext.decode(response.responseText);
if (obj.success) {
// sets the base controller user object so it can be available in all app
Ext.apply(me.user, obj.data);
me.getMain().layout.setActiveItem(1);
// HERE I WANT TO PASS MY me.user TO MY MAIN.js VIEW
}
}
});
},
}
});
Here is my Main.js view where I need to get the user object to I can set the User name in my footer (south panel).
Ext.define('App.view.Main', {
extend: 'Ext.container.Container',
requires: [
'Ext.layout.container.Card',
'Ext.layout.container.Border',
'Ext.layout.container.Accordion',
'Ext.form.Label',
'App.view.login.LoginForm',
'App.view.Dashboard',
'App.view.MainPanel',
'App.view.Header',
'App.view.menu.Accordion'
],
xtype: 'app-main',
layout: {
type: 'card'
},
items: [{
xtype: 'container',
layout: {
type: 'hbox',
align: 'stretch'
},
defaults: { flex: 1 },
items: [{
xtype: 'component'
}, {
xtype: 'container',
layout: {
type: 'vbox',
align: 'stretch',
},
defaults: { flex: 1 },
items: [{ xtype: 'component' }, { xtype: 'loginform' }, { xtype: 'component' }]
}, {
xtype: 'component'
}]
},
{
xtype: 'panel',
layout: {
type: 'border'
},
items: [
{
xtype: 'mainmenu',
width: 250,
region: 'west'
},
{
xtype: 'appheader',
region: 'north'
},
{
xtype: 'mainpanel',
region: 'center'
},
{
xtype: 'container',
region: 'south',
items:
[
{
xtype: 'toolbar',
margin: '0',
style: 'background-color:#3892D3;',
items: [
{
xtype: 'displayfield',
id: 'username',
fieldLabel: 'User Name',
width: 100,
labelWidth: 70,
style: 'color:#FFFFFF;',
value: '<span style="color:#FFFFFF;font-size:12px">HERE I NEED THE USER NAME</span>'
}
]
}
]
}
]
}
]
});
Any clue?

Sencha Touch 2: Trying to add a list to a panel

I have a panel which consists of a toolbar and tabs. On each tab there should be a list and a button. I believe I have this set up correctly except for the list, which I am trying to add as follows:
Ext.define('Myapp.view.Search', {
xtype: 'search',
extend: 'Ext.tab.Panel',
config: {
activeItem: 0,
tabBar: {
docked: 'top',
autoScroll: 'auto',
ui: 'light',
layout: {
pack: 'center'
}
},
items: [
{
xtype:'toolbar',
docked:'top',
ui: 'light',
title: 'Search'
},
{
title: 'Tab 1',
xtype: 'formpanel',
items: [
{
xtype: 'Mylist' //DOES NOT WORK
},
{
xtype: 'panel',
defaults: {
xtype: 'button',
style: 'margin: 0.1em',
flex : 1
},
layout: {
type: 'hbox'
},
items: [
{
text: 'Button 1',
}
]
}
]
},
{
title: 'Tab 2',
xtype: 'formpanel',
items: [
{
xtype: 'panel',
defaults: {
xtype: 'button',
style: 'margin: 0.1em',
flex : 1
},
layout: {
type: 'hbox'
},
items: [
{
text: 'Button 1',
}
]
}
]
}
]
}
});
Please let me know what I am doing wrong and thanks for you help!
EDIT: added Mylist below:
Ext.define('Myapp.view.Mylist', {
extend: 'Ext.dataview.NestedList',
xtype: 'Mylist',
config: {
store: 'Sections'
},
getTitleTextTpl: function() {
return '{name}';
},
getItemTextTpl: function(node) {
return '<strong>{name}</strong>';
},
});
Try these
in View:
{
xtype: 'panel',
flex: 4,
width: '400px',
height:"700px",
layout: {
type: 'fit'
},
items: [
{
xtype: 'list',
// loadingText:"Loading Category",
styleHtmlContent: true,
// id:"mylist2",
width:"300px",
itemTpl:
'<div class="mycon">'+
'<input type="image" id="click" img src="{coupon_image}" style="max-width:130%;border:6px double #000000;" width="200" height="200"' +'style="padding:3px;">' +
'</div>'+
'<div><font size="2" color="red"><b>Coupon Name:</b></font></div>'+
'<div><font size="2" color="green"><b>{coupon_name}</b></font></div>'+
'</div>',
store : 'ViewCategoryStore',
},
]
}
in controller:
Ext.define('Expressdeal.controller.ViewCategoryController', {
extend: 'Ext.app.Controller',
config: {
refs: {
viewcat : 'viewcategory' // xtype of the view
},
control: {
'viewcategory list: {
activate: 'onActivate',
itemtap: 'onItemTap',
},
Where you define your list in your List view you should say
alias:'widget.Mylist'
instead of
xtype:'Mylist'
also your list needs an itemTpl config im pretty sure.
config: {
store: 'Sections',
itemTpl:'{example}'
}
And as long as the store that drives your list is in working shape the list should appear correctly

show window in tabpanel

I am working on extjs4, my case is:
Extjs mvc is used to build my application, viewport is the toppest container of my application, west region is a tree, center region is a tabpage container, when click the tree item, a new page with certain content will be created. then in this page, I popup a model window, this model window just mask the page, not the whole viewport, so that I can still click the tree item to open another new page.
I have achieved this, but there is a problem, if I have already open a model window in a tab, and I switch to a another tab then return back, the model window is hidden, but I still want that window to show if I haven't closed it. Can anyone help me, is there a better way except using ifram in tabpage?
app.js:
Ext.application({
name: 'SysOpv',
appFolder: '/Js/AppSysOpv/app',
autoCreateViewport: true,
controllers: [
'Category',
'Band'
]
});
Viewport:
Ext.define('SysOpv.view.Viewport', {
extend: 'Ext.container.Viewport',
layout: 'fit',
initComponent: function() {
this.items = {
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
height: 80,
items: [
{ xtype: 'component', html: 'setup' }
]
}],
layout: {
type: 'hbox',
align: 'stretch'
},
items: [{
width: 250,
xtype: 'categorytree'
}, {
id: 'maintabpanel',
flex: 1,
xtype: 'tabpanel'
}]
};
this.callParent(arguments);
}
});
Tree View:
Ext.define('SysOpv.view.category.Tree', {
extend: 'Ext.tree.Panel',
alias: 'widget.categorytree',
title: 'setup',
rootVisible: false,
useArrows: true,
hideHeaders: true,
columns: [{
flex: 1,
xtype: 'treecolumn',
text: 'Name',
dataIndex: 'name'
}],
store: 'Category',
initComponent: function() {
this.callParent(arguments);
}
});
Window View:
Ext.define('SysOpv.view.edit.Band', {
extend: 'Ext.window.Window',
alias: 'widget.editband',
title: 'Setup',
layout: 'fit',
constrain: true,
modal: true,
initComponent: function() {
this.items = [{
xtype: 'form',
bodyPadding: 10,
items: [{
xtype: 'textfield',
name: 'name',
fieldLabel: 'Name'
}]
}];
this.buttons = [{
text: 'Save',
action: 'save'
}, {
text: 'Cancel',
scope: this,
handler: this.close
}];
this.callParent(arguments);
}
});
Tree Controller:
Ext.define('SysOpv.controller.Category', {
extend: 'Ext.app.Controller',
models: [ 'Category' ],
stores: [ 'Category' ],
views: [ 'category.Tree' ],
init: function() {
this.control({
'categorytree': {
itemdblclick: this.onTreeItemdblclick
}
});
},
onTreeItemdblclick: function (tree, record, item, index, e, eOpts) {
var mainTabs = Ext.getCmp('maintabpanel');
var tabId = record.get('id');
if (mainTabs) {
var checkTab = mainTabs.getComponent(tabId);
if (checkTab) {
mainTabs.setActiveTab(checkTab);
} else {
var controller;
var list;
switch (tabId) {
case '0101':
list = Ext.widget('listband');
break;
}
if (list)
{
var tabPage = mainTabs.add({
id: record.get('id'),
title: record.get('name'),
closable: true,
layout: 'fit',
items: [ list ]
});
mainTabs.setActiveTab(tabPage);
}
}
}
}
});
Module Controller:
Ext.define('SysOpv.controller.Band', {
extend: 'Ext.app.Controller',
models: [ 'Band' ],
stores: [ 'Band' ],
views: [ 'list.Band', 'edit.Band' ],
init: function() {
this.control({
'listband button[action=edit]': {
click: this.onEdit
}
});
},
onEdit: function(button, e, eOpts) {
var edit = Ext.widget('editband');
var list = button.up('gridpanel');
if (list.getSelectionModel().hasSelection()) {
var record = list.getSelectionModel().getLastSelected();
// I use renderTo here but have no effect,
// so I search in the google find a way to show the window in tab,
// and not mask all the viewport.
button.up('#0101').add(edit);
edit.down('form').loadRecord(record);
edit.show();
} else {
console.log('Not selected');
}
}
});
Below is example solution:
Ext.create('Ext.TabPanel', {
renderTo: 'container',
items: [
{
title: 'Tab 1',
itemId: 'tab1',
items: [
{ xtype: 'button', text: 'Show window', handler: function(){
var tab = this.up('#tab1'); // Find tab
var win = Ext.widget('editband'); // Find window
this.up('tabpanel').showWindow(tab, win);
} }
]
},
],
showWindow: function(tab, w){
tab.add(w);
tab.popup = w;
w.on('close', function() { // clean up after window close
delete this.popup;
}, tab, { single: true });
w.show();
},
listeners: {
tabchange: function(panel, tab) {
if (tab.popup !== undefined) { // show window after tab change
tab.popup.show();
}
}
}
});
Basically I've created event handler for tabchange event in which I re-show window.
Working sample: http://jsfiddle.net/aCxYU/1/

Resources