//COMP.LoadLibraries(['SlideZone']);
COMP.LoadStylesheet('rws_controls');

Ext.ns('RWS');

var lastTrekkerRequest=null;

// A grid column that has a checkbox
Ext.grid.CheckColumn = function(config){
	Ext.apply(this, config);
		if(!this.id){
			this.id = Ext.id();
		}
		this.renderer = this.renderer.createDelegate(this);
	};
Ext.grid.CheckColumn.prototype ={
	init : function(grid){
		this.grid = grid;
		this.grid.on('render', function(){
			var view = this.grid.getView();
			view.mainBody.on('mousedown', this.onMouseDown, this);
		}, this);
	}
	,onMouseDown : function(e, t){
		if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
			e.stopEvent();
			var index = this.grid.getView().findRowIndex(t);
			var record = this.grid.store.getAt(index);
			record.set(this.dataIndex, !record.data[this.dataIndex]);
		}
	}
	,renderer : function(v, p, record){
		p.css += ' x-grid3-check-col-td';
		return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
	}
};

Ext.ux.advancedSearchRow = Ext.extend(Ext.Container, {
	constructor:function(config){
		Ext.apply(this, config);
		Ext.apply(this, {
			defaults: { border: false }
			,height:38   
			,layout:'hbox'
			,items: [
				this.firstRow?{xtype:'spacer', width:100}:{
					xtype: 'container'
					,bodyStyle: 'padding-right: 10px'
					,width: 100
					,items: [{
						xtype: 'comboMenu'
						,id: this.id+'_operator'
						,showDefaultOption: false
						,width:90
						,menuItems:[
							{value: 'And', display:L(5853)}
							,{value: 'Or', display:L(3857)}
							,{value: 'And_Not', display:L(5854)}
							,{value: 'Through', display:L(5855)}
						]
						,defaultID: 'And'
						,defaultText: L(5853)
						,displayField: 'display'
						,valueField:'value'
					}]
				},{ 
					xtype:'container'
					,width:140
					,items:[{
						xtype:'comboMenu'
						,id: this.id+'_category'
						,width: 130
						,defaultID:'Keyword'
						,menuItems: [
							{value:'Keyword', display:L(4404)}
			 				,{value:'title_words', display:L(27391)} // ls_Title_Keywords
			 				,{value:'author_words', display:L(27392)} // ls_Author_Keywords
			 				,{value:'Accession', display:L(5849)}
			 				,{value:'Authors', display:L(3690)}
			 				,{value:'Awards', display:L(12500)}
			 				,{value:'Barcodes', display:L(3697)}
			 				,{value:'Bibliographies', display:L(5850)}
			 				,{value:'Callnum', display:L(3689)}
			 				,{value:'Curriculum', display:L(25939)}
			 				,{value:'Full', display:L(7147)}
			 				,{value:'Subjects', display:L(7152)}
			 				,{value:'Interest', display:L(25940)}
			 				,{value:'ISBN', display:L(5852)}
			 				,{value:'Location', display:L(4080)}
			 				,{value:'Lexile', display:L(18919)}
			 				,{value:'Medium', display:L(4090)}
			 				,{value:'Publication', display:L(3691)}
			 				,{value:'Year', display:L(4094)}
			 				,{value:'Series', display:L(3692)}
			 				,{value:'Shelving', display:L(11112)}
			 				,{value:'Titles', display:L(3688)}
						] 
						,showDefaultOption: false
						,selectOnRender: 'Keyword'
						,displayField: 'display'
						,valueField: 'value'
					}]
				},{ 
					xtype: 'textfield'
					,id: this.id+'_textfield'
					,width: 375
					,cls: 'browseField'
                          ,listeners: {
                              'specialkey': {
                                  fn: function (f,e) {
                                      if(e.getKey() == e.ENTER){
                                          InitAdvancedSearch(e);
                                      }
                                  }
                              ,scope: this
                              }
                          }
				},{
					xtype: 'button'
					,id: this.id+'browseIcon'
					,width: 25
					,cls:'clearButton'
					,height: 22
					,style: 'margin-left: -' + (COMP.IE ? '2' : '1') + '0px; margin-top: 3px; background-image: url(/images/small_icon_sprite_map.png); background-position: 0 -800'
					,handler: function(){COMP.LoadLibrariesSerial(['rws_browse'], function(){ 
						Browse(this.id); 
					}, this.findParentByType('advancedSearchRow'))}
				}
			]
		});
		Ext.ux.advancedSearchRow.superclass.constructor.call(this);
	}
	,GetSearchText : function() { return Ext.getCmp(this.id+'_textfield').getValue(); }
	,GetSearchType : function() { return Ext.getCmp(this.id+'_category').getValue(); }
	,GetSearchOper : function() { return Ext.getCmp(this.id+'_operator').getValue(); }
	,GetBrowseDialog: function() { 
		this.browseText = Ext.getCmp(this.id+'_textfield').getValue();
		this.category = Ext.getCmp(this.id+'_category').getValue();
		if(!this.firstRow) {
			this.operator = Ext.getCmp(this.id+'_operator').getValue();
		}
	}
	,SetSearchText : function(text) { Ext.getCmp(this.id+'_textfield').setValue(text); }
});
Ext.reg('advancedSearchRow', Ext.ux.advancedSearchRow);


RWS.PreFilterControl = Ext.extend(Ext.Container,{
	layout:'vbox'
	,layoutConfig:{align:'stretch'}
	,width:225
	,height:'100%'
	,border:false
	,cls:'prefilterItem'
	,constructor:function(config){
		RWS.PreFilterControl.superclass.constructor.call(this, config);
		this.sectionTitle = config.sectionTitle;
		this.filters = config.filters;
	}
	,initComponent:function(){
		RWS.PreFilterControl.superclass.initComponent.call(this);
		this.add(this.createTitleBox())
		for(var i = 0; i < this.filters.length; i ++){
			var filter = this.filters[i];
			this.add(this.createFilterBox(filter.title, filter.comp));
		}
	}
	,createFilterBox:function(title, comp){
		Ext.applyIf(comp, {width:155, maxWidth:155, style:'margin: 0 20;'});
		if(!title) return comp;
		var box = {
			xtype:'container'
			,layout:'vbox'
			,layoutConfig:{align:'stretch'}
			,height:60
			,width:'100%'
			,items:[{
				xtype:'label'
				,cls:'filter_sub_title'
				,text: title
			}
			,comp]
		};
		return box;
	}
	,clear:function(){
	}
	,createTitleBox:function(){
		return {
			xtype:'container'
			,layout:'hbox'
			,height:30
			,items:[{
				xtype:'label'
				,cls:'filter_title'
				,text:this.sectionTitle
			},{
				xtype:'spacer'
				,flex:1
			},{
				xtype:'button'
				,text:L(5859)
				,ref:'../../clearButton'
				,clear:this.initialConfig.clear
				,handler:function(){this.clear()}
			}]
		};
	}
});

RWS.StarSelector = {};
RWS.StarSelector.Stars = 3;

RWS.StarSelector.Setstars = function(val) {
	RWS.StarSelector.Stars = val;
}

RWS.StarSelector.Fillstars = function(val) {
	if(val == undefined || val == 0)
		val = RWS.StarSelector.Stars;
	for(var i = 1; i <= val; i++)
	{
		var tmp = Ext.getCmp('star' + i);
		if(tmp)
		{
			tmp.el.dom.src = '/images/rws_star_full.png';
		}
	}
	for(var i = val + 1; i <= 5; i++)
	{
		var tmp = Ext.getCmp('star' + i);
		if(tmp)
		{
			tmp.el.dom.src = '/images/rws_star_empty.png';
		}
	}
}

// Turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';

// Create a password validation type
Ext.apply(Ext.form.VTypes, {
    password : function(val, field) {
        if (field.initialPassField) {
            var pwd = Ext.getCmp(field.initialPassField);
			if (!pwd)
				return true;

            return (val == pwd.getValue());
        }
        return true;
    },
    passwordText : L(6359) // 'Passwords do not match'
});

lookAheadStore = new Ext.data.SimpleStore({
	url: '/ajax/rws_search_autocomplete'
	,baseParams: { sessionid : COMP.V.sessionid }
	,fields: [ 'name' ]
	,id:'name'
	,listeners:{
		beforeload:function(me, opt){
			if(RWS.Layout.Results)
				return(!RWS.Layout.Results.isVisible())
		}
		,load:function(me, opt){
			if(RWS.Layout.Results)
				return(!RWS.Layout.Results.isVisible())
		}
	}
});

Ext.ux.searchBar = Ext.extend( Ext.Panel, {
	layout: 'column'
	,border: false
	,cls:'searchBar'
	,initComponent: function(){
		this.barId = this.initialConfig.barId || this.id+'_textfield';
		Ext.apply(this, {
			items: [
			{
				width: 500
				,id:this.barId
				,ref:'../textField'
				,xtype: 'combo'
				,store: lookAheadStore
				,displayField: 'name'
				,valueField: 'name'
				,typeAhead: false
				,hideTrigger: true
				,minChars: 3
				,editable: true
				,mode: 'remote'
				,enableKeyEvents:true
				,onKeypress:function(a,b) {alert(1); }
				,listeners:{
					expand:function(combo){
						if(RWS.Layout.Results && RWS.Layout.Results.isVisible()) {
                            this.collapse();
							return false;
                        }
					}
				}
			},{
				xtype: 'button'
				,id:'simpleGoButton'
				,cls:'searchButton'
				,text:L(3638)
				,height: 22
				,width: 72
				,handler: this.initialConfig.handler || function(e) { InitSearch(null,e); } 
			}
		]
		});
		Ext.ux.searchBar.superclass.initComponent.apply(this, arguments);
	}
	,afterRender: function() {
		Ext.getCmp(this.barId).on('specialkey', function(f, e){
			if(e.getKey() == e.ENTER){
				this.initialConfig.handler ? this.initialConfig.handler.call() : InitSearch(null,e);
			}}, this);  
		Ext.getCmp(this.barId).focus(true,2);
		Ext.ux.searchBar.superclass.afterRender.apply(this, arguments);
	}
	,GetSearchText:function() {
		return Ext.get(this.barId).getValue();
	}
	,clear:function(){
		Ext.getCmp(this.barId).setValue('');
	}
});
Ext.reg('searchBar', Ext.ux.searchBar);


// nettrekkerLink - A panel that contains the nettrekker icon and text and its searching functionality.
Ext.ux.nettrekkerLink = Ext.extend(Ext.Container, {
	initComponent:function(){
		Ext.apply(this, {
			layout:'column'
			,cls:'nettrekkerLink'
			,items:[{
				xtype:'icon'
				,id:this.id+'_nettrekkerResIcon'
				,ground:'fore'
				,style:'background: url(/images/small_icon_sprite_map.png); background-position: 0 -1095'
				,height:24
				,width:24 
			},{
				xtype:'button'
				,id:this.id+'_nettrekkerResText'
				,text:L(27393)+'...' //ls_Searching_netTrekker
				,cls:'fakeButton'
				,scale:'medium'     
			}]
		});
		Ext.ux.nettrekkerLink.superclass.initComponent.apply(this, arguments);
	}
	,requestNetTrekkerCount:function(searchText){
		//COMP.Send/AjaxCommand('rws_request_nettrekker_count',{id:this.id, search:searchText});
		COMP.LoadLibrary('netTrekkerTool');
		var me = this;
		var c = this.findById(this.id+'_nettrekkerResText');
		c.addClass('fakeButton');
		c.removeClass('textLink');
		c.setText(L(27393)+'...');  //ls_Searching_netTrekker
		c.setHandler(function(){});
		Ext.Ajax.abort(lastTrekkerRequest);
		lastTrekkerRequest = Ext.Ajax.request({
			url:'/ajax/rws_request_nettrekker_count'
			,success:function(o)
			{
				var num = o.responseText;
				me.showNetTrekkerCount(searchText,num);
			}
			,failure:function()
			{
				me.showNetTrekkerCount(searchText,0);
			}
			,params:{
				search:searchText
				,id:this.id
				,sessionid:COMP.V.sessionid
				,collectioninfo:COMP.V.collectioninfo || ''
			}
		});
	}
	,showNetTrekker:function(text){
		if (!COMP.Config.GetDefault('showNetTrekker'))	return;
		var c = this.findById(this.id+'_nettrekkerResText');
		c.addClass('textLink');
		c.removeClass('fakeButton');
		c.setText(text);
		c.ownerCt.ownerCt.ownerCt.ownerCt.doLayout();
		c.setHandler(this.searchNetTrekker);
	}
	,showNetTrekkerCount:function(search, count){
		if (!COMP.Config.GetDefault('showNetTrekker'))	return;
		COMP.V.netTrekkerSearch = search;
		this.showNetTrekker(count + ' '+L(27394)+' \'' +  COMP.fitStringToWidth(search, 350, 'font14') + '\''); //ls_netTrekker_results_for
	}
	,searchNetTrekker:function(){
		if (!COMP.Config.GetDefault('showNetTrekker'))	return;
		//COMP.OpenWindow(COMP.Prefs.netTrekkerURL + COMP.V.netTrekkerSearch);
		netTrekkerLinkFunction({searchTerm:COMP.V.netTrekkerSearch, searchType:'general',schoolType:COMP.Prefs.ntSchoolType});
	}
});
Ext.reg('nettrekkerLink', Ext.ux.nettrekkerLink);

RWS.Dialog = Ext.extend(Ext.Window, {
	constructor:function(config){
		var buttons = [];
		var yesButton =  {text:'Yes', btn:'yes'};
		var noButton =  {text:'No', btn:'no'};
		var okayButton =  {text:'Okay', btn:'okay'};
		var cancelButton =  {text:'Cancel', btn:'cancel'};
		switch(config.buttons){
			case Ext.Msg.YESNO:{
				buttons.add(yesButton);
				buttons.add(noButton);
				break;
			}
			case Ext.Msg.YESNOCANCEL:{
				buttons.add(yesButton);
				buttons.add(noButton);
				buttons.add(cancelButton);
				break;
			}
			case Ext.Msg.OKAY:{
				buttons.add(okayButton);
				break;
			}
			case Ext.Msg.OKAYCANCEL:{
				buttons.add(okayButton);
				buttons.add(cancelButton);
				break;
			}
		}
		config.buttons = null;
		var closeFunc = function(){
			if(config.fn)
				config.fn(this.btn, this.text);
		};
		for(var i = 0; i < buttons.length; i ++){
			Ext.apply(buttons[i], {scope:buttons[i], handler:closeFunc.createSequence(this.close, this)});
		}
		var bar = new Ext.Toolbar(['->',buttons]);
		var content = new Ext.Panel({
			html:(config.message || config.msg) + '',
			border:false,
			frame:false,
			layout:'fit'
		});
		Ext.apply(config, {layout:'fit', items:content, bbar:bar, closable:false, resizable:false});
		RWS.Dialog.superclass.constructor.call(this, config);
		this.on('afterRender', function(){
			var nav = new Ext.KeyNav(this.getEl(), {
			  'enter': function(e) {
			    this.getBottomToolbar().items.items[this.getBottomToolbar().items.items.length - 1].handler();
			  },
			  'scope': this
			}); 
		})
	}
});
Ext.reg('dialog', RWS.Dialog);

// hoverPanel - A panel that detects and publishes the mouse over event
Ext.ux.hoverPanel = Ext.extend( Ext.Panel, {
	initComponent: function(){
		Ext.ux.hoverPanel.superclass.initComponent.call(this);
		this.addEvents('mouseover');
		this.addEvents('mouseout');
	}
	,afterRender: function(){
		Ext.ux.hoverPanel.superclass.afterRender.call(this);
		this.el.on('mouseover', this.onMouseOver, this);
		this.el.on('mouseout', this.onMouseOut, this);
	}
	,onMouseOver: function(e){
		this.fireEvent('mouseover', this, e);
	}
	,onMouseOut: function(e){
		this.fireEvent('mouseout', this, e);
	}	
});
Ext.reg('hoverPanel', Ext.ux.hoverPanel);

// trackOverView - A DataView that tracks and publishes the mouse over and out events
//I believe we'll be able to remove this when we update ExtJs because they added this feature in a newer version.
Ext.ux.trackOverView = Ext.extend(Ext.DataView, {
	initComponent:function(){
		this.addEvents('mouseover', 'mouseout');
		Ext.ux.trackOverView.superclass.initComponent.apply(this, arguments);
	}
	,onMouseOver:function(e){
		var item = e.getTarget(this.itemSelector, this.el);
        if(item && item !== this.lastItem){
            this.lastItem = item;
            Ext.fly(item).addClass(this.overClass);
			this.fireEvent('mouseover', e, item);
		}
	}
	,onMouseOut:function(e){		
		this.fireEvent('mouseout', e, this.lastItem);
		Ext.ux.trackOverView.superclass.onMouseOut.apply(this, arguments);
	}
	
});
Ext.reg('trackOverView', Ext.ux.trackOverView);

// textLink - Text that caputres and publishes an onClick event
//@text - The text to display
//@handlerStr - The function to call when the text is clicked. Must be a string.
Ext.ux.textLink = Ext.extend(Ext.BoxComponent, {
	text:'none'
	,handlerStr:'ShowUCMsg(\''+this.id+'\')'
	,initComponent:function(){
		Ext.apply(this, {
			autoEl:{
				tag:'div'
				,cls:'textlink'
				,onclick:this.handlerStr
				,html:this.text
			}
		});
		Ext.ux.textLink.superclass.initComponent.apply(this, arguments);
	}
});
Ext.reg('textLink', Ext.ux.textLink);

// imageButton - A large picture with some text beneath it.
Ext.ux.imageButton = Ext.extend( Ext.Panel, {
	initComponent: function(){
		Ext.apply(this, {
			border: false
			,center:true
			,imgWidth: 130
			,imgHeight: 130
			,cls:'imageButton'
			,bodyStyle:this.center?'text-align:"center"':''
			,items: [{
				xtype:'panel'
				,border: false
				,items: [{
					width: this.imgWidth
					,height: this.imgHeight
					,ground:this.ground
					,xtype:'icon'
					,id:this.id+"_image"
					,image:this.image
					,border:false
					,style:'margin-left: auto; margin-right: auto; display: block;'
					,cls: this.cls + ' ' + 'image'
				},{
					xtype: 'label'
					,cls: 'text'
					,text: this.labelText
				}]
			}]
		});
		Ext.ux.imageButton.superclass.initComponent.apply(this, arguments);
	}
	,afterRender: function() {
		Ext.getCmp(this.id + "_image").setPosition(-30, 0);
		Ext.ux.imageButton.superclass.afterRender.apply(this, arguments);
	}
});
Ext.reg('imageButton', Ext.ux.imageButton);

// icon - Just a clickable image. Doesn't have to really be an icon.
Ext.ux.icon = Ext.extend( Ext.BoxComponent, {
	ground: 'back' // or 'fore'
	,center:true //center the image
	,initComponent: function(){
		Ext.apply(this, {
			border: false
			,autoEl: this.ground=="fore"?{tag:'img',src:'/images/blank.gif'}:'div'
			,cls:this.cls?'icon '+this.cls:'icon'
			,items: [
				{
					width: this.width
					,height: this.height
					,border:true
				}
			]
		});
		this.addEvents('onclick')
		Ext.ux.icon.superclass.initComponent.apply(this, arguments);
	}
	,afterRender: function() {
		COMP.SetControlImage(this);
		this.el.on('click', this.onClick, this);
		Ext.ux.icon.superclass.afterRender.apply(this, arguments);
	}
	,setImage: function(t){
        this.image = t;
        if(this.rendered){
            COMP.SetControlImage(this);
        }
    }
	,onClick:function(e){
		this.fireEvent('onclick', this, e);
	}
});
Ext.reg('icon', Ext.ux.icon);

Ext.ux.comboMenu = Ext.extend(Ext.Button, {
	defaultText: 'None'
	,defaultID: 'id'
	,cls: 'comboMenu'
	,selectedID: -1
	,valueField:'id'
	,displayField:'text'
	,showDefaultOption:true
	,isRendered:false
	,initComponent: function(){
		this.titles = [];
		Ext.apply(this, {
			menu: this.menuItems ? this.createMenu(this.menuItems) : ''
			,maxWidth:this.width
		});
		Ext.ux.comboMenu.superclass.initComponent.apply(this, arguments);
	}
	,onRender:function(){
		this.isRendered = true;
		if(this.selectOnRender){
			this.setSelectedID(this.selectOnRender);
		}
		Ext.ux.comboMenu.superclass.onRender.apply(this, arguments);
	}
	,createMenu:function(data){
		var menu = new Ext.menu.Menu({id:this.id + 'Menu'});
		if(!data)
			return menu;
		if(this.showDefaultOption){
			var defaultItem = menu.addMenuItem({id:this.defaultID, text: this.defaultText});
			defaultItem.on('click', function(c){
				this.clickedItem(c);
			}, this);
			defaultItem[this.valueField] = this.defaultID;
			this.titles[this.defaultID] = this.defaultText;
		}
		this.setSelectedID(this.defaultID);
		for(var i = 0; i < data.length; i++){
			var item = data[i];
			var menuItem;
			if(item.menuItems)
				item.items = item.menuItems;
			if(item.items){
				var menuItem = menu.addMenuItem({text: item[this.displayField], menu: item.items});
				for(var j = 0; j < menuItem.menu.items.items.length; j ++){
					var currentItem = menuItem.menu.items.items[j]
					this.titles[currentItem[this.valueField]] = currentItem[this.displayField];
					currentItem.on('click', function(c){
						this.clickedItem(c);
					}, this);
				};
			}
			else{
				var menuItem = menu.addMenuItem({text: item[this.displayField], menu: item.items});
				menuItem[this.valueField] = item[this.valueField];
				this.titles[item[this.valueField]] = item[this.displayField];
				menuItem.on('click', function(c){
					this.clickedItem(c);
				}, this);
			}
		}
		return menu
	}
	,clickedItem:function(c){
		this.setSelectedID(c[this.valueField]);
		this.fireEvent('select', this);
	}
	,setSelectedID:function(id){
		if(!this.isRendered){
			this.selectOnRender = id;
			return;
		}
		this.selectedID = id;
		var title = this.titles[id];
		if(!title) title = this.defaultText;
		this.setText(COMP.fitStringToWidth(title, this.maxWidth - 22, 'size12Font'));
	}
	,getValue:function(){
		var val = this.titles[this.selectedID];
		if (!val)
			return this.defaultID;
		else
			return this.selectedID;
	}
	,setValue:function(val){
		this.setSelectedID(val);
	}
	,replaceMenu:function(data)
	{
		if (!this.menu)	return;
		else if (!data) return;
		else if ((!this.menuItems || this.menuItems.length == 0) && !this.store){
			this.replaceOnRender = data;
			return;
		}	
		// Clear any existing titles and menu items.
		this.titles = [];
		this.menu.removeAll();
		this.menuItems = data;
		
		// Add the items to the existing menu.
		this.menu = this.createMenu(data);
	}
	,setSelectedIndex:function(index){
		this.setSelectedID(this.menuItems[index][this.valueField]);
	}
});
Ext.reg('comboMenu', Ext.ux.comboMenu);

// serverMenuCombo - Combobox with full menu support.
Ext.ux.serverMenuCombo = Ext.extend(Ext.form.TriggerField,
{
	constructor:function(config)
	{
		this.menuMap=config.staticMap || {};
 		this.menuID=config.staticMenuID || { menuID:'' };
		this.menu={};
		this.menuConfig =
			{
				id: config.id
				,onClick:function(x,menuItem,e,y) 
				{
					var c=Ext.getCmp(this.id); 
					var t=menuItem.innerText; 
					if(!t) t=menuItem.text
					var value=c.valueFromString(t);
					if (value != undefined)
						c.setValue(value);
					c.select();			
				}
				,width:100
				,items: []
			}
		this.populated = false;
		this.border = false;
		this.cls = 'serverMenuCombo';
		
		Ext.ux.serverMenuCombo.superclass.constructor.apply(this, arguments);
	}
	,initComponent:function(){
		Ext.ux.serverMenuCombo.superclass.initComponent.apply(this, arguments);
		this.addEvents('select');
		this.onTriggerClick(false);
	}
	,select:function(){
		this.DestroyMenu();
		this.fireEvent('select', this);
	}
	,Reset:function()
	{
		this.populated=false;
		this.setValue(null);
		this.onTriggerClick(false);
	}
	,CreateMenu:function()  
	{ 
		this.menu = new Ext.menu.Menu(this.menuConfig); 
		this.menu.show(this.id);   
	} 
	,DestroyMenu:function() { 
		var c=Ext.getCmp(this.id); 
		if (c.menu.destroy != undefined) 
			c.menu.destroy(); 
		c.menu = {};
	}
	,DetermineValue:function()
	{
		if (this.setValue(this.menuID.menuID)) return;
		if (this.initialConfig.autoDetermineValue==false)
			return;
			
		// This is called after we've received new menu data.
		// Try to determine what we should initialize this field with.
		// 1. Try using menuID since it's the last known set value
		// 2. Try using initialConfig.value
		// 3. Default to whatever is first in the menu.
		if (this.setValue(this.initialConfig.value)) return;
		this.PickFirstOption();
	}
	,PickFirstOption:function()
	{
		var item = this.menuConfig.items[0];
		if (item)
			this.setValue(item[this.valueField]);
		else
			this.setValue(null);
	}
	,ReceivedMenu:function(menu,showMenu) 
	{ 
		this.menuConfig.items=menu.items;
		this.MapMenu(this.menuMap,this.menuConfig.items);
		if (showMenu)
			this.CreateMenu(); 
		this.DetermineValue(); 
	}
	,onTriggerClick:function(showMenu){
		if (showMenu != false) showMenu = true;
		this.menuConfig.items=this.data;
		this.MapMenu(this.menuMap,this.menuConfig.items);
		if (showMenu)
			this.CreateMenu(); 
		this.DetermineValue();
		// if (this.populated)
		// {
		// 	if (this.menu.items == undefined)	this.CreateMenu();
		// 	else								this.DestroyMenu();
		// }
		// else
		// {
		// 	// Request the menu
		// 	this.populated = true; // So we don't keep requesting. Really should show some kind of 'waiting' message.
		// 	var o = {menuControl:this.id, showMenu:showMenu};
		// 	if (this.extraData)
		// 		o = COMP.CombineObjects(o,this.extraData);
		// 	if (this.ajaxObjectFunction)
		// 		o = COMP.CombineObjects(o,this.ajaxObjectFunction());
		// 		
		// 	COMP.Send/AjaxCommand(COMP.AssembleURL(this.ajaxURL, o));
		// }
	}
	,afterRender:function() {
		Ext.ux.serverMenuCombo.superclass.afterRender.apply(this, arguments);
	}
	,isExpanded:function() {
		if(this.menu.isVisible)
			if(this.menu.isVisible())
				return true;
		return false;
	}
	,getValue:function() { 
		return this.menuID.menuID; 
	} 
	,setValue:function(value){
		var success=false;
		if (value == null)
		{
			this.menuID.menuID='';
			arguments[0]=' ';
		}
		else
		{
			for (var x in this.menuMap)
				if (this.menuMap[x] == value)
				{
					this.menuID.menuID = value;
					this.setString(x);
					return true;
				}
		}
		
		// Dunno if this is a good idea...
		this.setString(value);
		
		return false;
	}
	,setString : function(str)
	{
		// Make sure the string matches the value. If there isn't one, return false.
		if (str.length && this.menuMap[str] == undefined)
			return false;
		
		Ext.ux.serverMenuCombo.superclass.setValue.apply(this, arguments);
		return true;
	}
	,clear : function()
	{
		this.menuID.menuID='';
		this.setString('');
	}
	,valueFromString : function(str)
	{
		return this.menuMap[str];
	}
	,MapMenu:function(menuMap,items)
	{
		for (var i=0; i<items.length; i++) 
			menuMap[items[i][this.displayField]]=items[i][this.valueField];
	}
});
Ext.reg('serverMenuCombo',Ext.ux.serverMenuCombo);



// This is code found online to fix a problem where the
// radio button group getValue function doesn't work.
Ext.override(Ext.form.RadioGroup, {
  getName: function() {
    return this.items.first().getName();
  },

  getValue: function() {
    var v;

    this.items.each(function(item) {
      v = item.getRawValue();
      return !item.getValue();
    });

    return v;
  },

  setValue: function(v) {
    if(this.rendered)
		this.items.each(function(item) {
	      item.setValue(item.getRawValue() == v);
	    });
	else
		for(k in this.items) this.items[k].checked = this.items[k].inputValue == v;
  }
});

Ext.ux.exploreButton = Ext.extend( Ext.Container, {
	cls:'exploreButton'
	,width:152
	,height:170
	,Update : function() {
		if(!this.rendered) return;
		var t=Ext.get(this.id+'_text');
		if(t !== null)
			t.dom.innerHTML=this.labelText; // textContent?
		
		var i=Ext.get(this.id+'_image');
		i.image=this.image;
		i.ground="fore"; // Why isn't this working anywhere else?
		COMP.SetControlImage(i);
	}
	,initComponent: function(){
		Ext.apply(this, {
			items: [
				{
					xtype:'icon'
					,ground:'fore'
					,id:this.id+"_image"
					,image:this.image
					,cls: 'exploreImage'
				},
				{
					xtype: 'label'
					,cls: 'text'
					,id:this.id+'_text'
					,text: this.labelText
				}
			]
		});
		Ext.ux.exploreButton.superclass.initComponent.apply(this, arguments);
	}
	,afterRender: function() {
		//Ext.get(this.id+"_image").setHeight(70);
		this.el.on('click', function() {
			var c=Ext.getCmp(this.id);
			var url;
			switch (c.buttonAction)
			{
				case RWS.Explore.xVars.exploreNone:
					break;

				case RWS.Explore.xVars.exploreMessage: 
					alert(c.buttonData2,"Explore");
					break;

				case RWS.Explore.xVars.exploreSearch:
					RWS.Search.SendRequest({Interface:'explore',paneID:c.paneID,curButton:c.curButton,SearchField0:c.labelText}); // SearchField0 is being used (int this case) as a search title. It's used for bread crumb titles.
					break;

				case RWS.Explore.xVars.explorePane:
					RWS.Explore.RequestExplore(c.buttonData1);
					/*Ext.getCmp('comboSearch').el.ghost('l', {callback:function(){*/Ext.getCmp('comboSearch').hide()//}});
					/*Ext.getCmp('addonSearch').el.ghost('l', {callback:function(){*/Ext.getCmp('addonSearch').hide()//}});
					break;

				case RWS.Explore.xVars.exploreLink:
				{
					url=c.buttonData1;
					if (url.toLowerCase().startsWith("mailto:"))
					{
						// Don't need SetWindowLocation here.
						window.location = url;
						break;
					}

					if (url.toLowerCase().indexOf("://") == -1)
						url = "http://" + url;

					window.open(url)
				}	break;

				case RWS.Explore.xVars.exploreHelp:
					oldAlert('exploreHelp. data1 or 2?')
					oldAlert(c.buttonData1)
					oldAlert(c.buttonData2)
					break;

				case RWS.Explore.xVars.exploreResource:
					oldAlert('exploreResource. data1 or 2?')
					oldAlert(c.buttonData1)
					oldAlert(c.buttonData2)
					break;

				default:
					oldAlert(c.buttonAction)
			}
		});                                               		Ext.ux.exploreButton.superclass.afterRender.apply(this, arguments);
	}
});
Ext.reg('exploreButton', Ext.ux.exploreButton);


//-------------New controls for RWS revamp go here-----------------
Ext.ns('RWS.Controls');

RWS.Controls.TipManager = [];//Track open tooltips, only want one at a time

RWS.Controls.TipManager.hideAll = function(){
	for(var i = 0; i < RWS.Controls.TipManager.length; i++){
		if(RWS.Controls.TipManager[i].hide)
			RWS.Controls.TipManager[i].hide(null, true);
	}
}

document.onmousemove = function(e) {
    var event = e || window.event;
    window.mouseX = event.clientX;
    window.mouseY = event.clientY;
};

RWS.Controls.ToolTip = Ext.extend(Ext.ToolTip, {
	 constructor:function(config){
		 Ext.apply(this, {
			showDelay:200
			,dismissDelay: 300
		});
		RWS.Controls.ToolTip.superclass.constructor.call(this, config);
	}
	,mouseInBox:function(box){
		var xMin = box.x;
		var xMax = xMin + box.width;
		var yMin = box.y;
		var yMax = yMin + box.height;
		return(window.mouseX >= xMin && window.mouseX <= xMax 
			&& window.mouseY >= yMin && window.mouseY <= yMax)
	}
	,getBox:function(){
		var box = RWS.Controls.ToolTip.superclass.getBox.call(this);
		box.x -= 10;
		box.y -= 10;
		box.width += 20;
		box.height += 20;
		return box;
	}
	,hide: function(x, hide){
		if(this.isHidden) return;
		if(hide)
			RWS.Controls.ToolTip.superclass.hide.call(this);
		if(!this.hidden && this.anchorTarget && this.anchorEl && (this.mouseInBox(this.getBox()) || this.mouseInBox(this.anchorTarget.getBox()) || this.mouseInBox(this.anchorEl.getBox()))) 
			this.hide.defer(this.dismissDelay, this);
		else if (!this.el.dom)
			return;
		else
		 	RWS.Controls.ToolTip.superclass.hide.call(this);
	}
	,show: function(){
		if(RWS.SavedLists.dragging || !this.anchorTarget || !(this.mouseInBox(this.anchorTarget.getBox()))){
			this.show.defer(250, this);
			return;
		}
		for(var i = 0; i < RWS.Controls.TipManager.length; i ++){
			if(RWS.Controls.TipManager[i].id != this.id && RWS.Controls.TipManager[i].isVisible()) 
				RWS.Controls.TipManager[i].hide();
		}
		for(var i = 0; i < RWS.Controls.TipManager.length; i ++){
			RWS.Controls.TipManager.remove(i);
		}
		RWS.Controls.TipManager.add(this);
		this.setWidth(this.initialConfig.width);
		if(this.anchorTarget.getBox().y + this.initialConfig.height > Ext.lib.Dom.getViewHeight() - 10){
			var prevHeight =  Ext.lib.Dom.getViewHeight() - this.anchorTarget.getBox().y - 20;
			this.setSize(this.initialConfig.width, prevHeight);
			if(this.initialConfig.dataView){
				var diff = 300 - prevHeight;
				this.initialConfig.dataView.setHeight(261 - diff);
			}
		}
		else {
			this.setSize(this.initialConfig.width, 300);
			if(this.initialConfig.dataView)
				this.initialConfig.dataView.setHeight(261);
		}
		this.doLayout();
		RWS.Controls.ToolTip.superclass.show.call(this);
	}
});

RWS.Controls.TitledTip = Ext.extend(RWS.Controls.ToolTip, {
	constructor:function(config){
		this.title =  {
			xtype:'container'
			,width:'100%'
			,cls:'tipTitle'
			,items:{
				text:config.title
				,xtype:'label'
			}
		};
		config.items = config.items ? [this.title].concat(config.items) : this.title;
		this.autoHeight = false;
		this.autoScroll = true;
		config.padding = '5 10 10 10';
		config.cls = config.cls ? config.cls + 'borderedPanel' : 'borderedPanel';
		RWS.Controls.TitledTip.superclass.constructor.call(this, config);
	}
});

RWS.Controls.SavedListTip = Ext.extend(RWS.Controls.ToolTip, {
	constructor:function(config){
		this.layout = 'vbox';
		this.layoutConfig = {align:'stretch'};
		var tbar = {
	   	     cls:'tipbar'
			,items:[{
				text:COMP.fitStringToWidth(config.title, config.width - 165, 'tipTitle', config.textTitle)
				,cls:'clearButton'
				,overCls:config.textTitle ? '' : 'textLink'
				,style:config.textTitle ? 'cursor:default !important;' : ''
				,handler:function(){
					RWS.SavedLists.ShowCurrentSavedListResults(this);
					RWS.Controls.TipManager.hideAll();
				}
				,scope:config.owner
				,xtype: config.textTitle ? 'label' : 'button'
			},'->'
			,{
				text:L(4653)//ls_Delete
				,cls:'clearButton'
				,overCls:'textLink'
				,hidden:!config.removeable
				,handler:function(){
					DeleteSavedList(this.name, this.key);
					RWS.Controls.TipManager.hideAll();
				}
				,scope:config.owner
			},{
				text:L(3625)//ls_Print
				,cls:'clearButton'
				,hidden:config.textTitle ? true : false
				,overCls:'textLink'
				,handler:function(){
					RWS.SavedLists.PrintCurrentSavedList(this.ownerCt.ownerCt.ownerCt.owner);
					RWS.Controls.TipManager.hideAll();
				}
			},{    
				text:L(5859)//ls_Clear
				,cls:'clearButton'
				,overCls:'textLink'
				,handler:this.clearList
				,scope:this
			}]          
		};
		this.topPanel = new Ext.Panel({
			tbar: tbar
			,cls:'tipTopPanel'
			,border:false
		});
		this.items = [this.topPanel, config.dataView];
		this.autoHeight = false;
		config.cls = config.cls ? config.cls + 'borderedPanel' : 'borderedPanel';
		RWS.Controls.SavedListTip.superclass.constructor.call(this, config);
	}
	,clearList:function(){
		var o = this.owner;
		if(this.initialConfig.textTitle){
			o.store.removeAll();
			o.el.last().last().update(o.store.getCount() + '');
		}else
			clearList(o.key, o.text.toString());
	}
});

RWS.Controls.FilterTip = Ext.extend(RWS.Controls.ToolTip, {
	constructor:function(config){
		this.layout = 'vbox';
		this.layoutConfig = {align:'stretch'};
		var tbar = {
	   	     cls:'tipbar'
			,items:[{
				text:COMP.fitStringToWidth(config.title, config.width - 120, "font14", true)
				,xtype:'label'
			}]          
		};
		this.topPanel = new Ext.Panel({
			tbar: tbar
			,cls:'tipTopPanel'
			,border:false
		});
		this.items = [this.topPanel, config.dataView];
		this.autoHeight = false;
		config.cls = config.cls ? config.cls + 'borderedPanel' : 'borderedPanel';
		RWS.Controls.FilterTip.superclass.constructor.call(this, config);
	}
	,clearList:function(){
		var o = this.owner;
		clearList(o.key, o.text.toString());
	}
});


RWS.Controls.ToolBtn = Ext.extend(Ext.BoxComponent, {
	constructor:function(config){
		Ext.apply(config, {
			autoEl:{
				cls:'toolBtn'
				,tag:'div'
				,cn:[{
					tag: 'div'
					,style:'margin-right:10px; height: 18px;'//bug 25723	
					,cn:[{
						tag:'div'
						,style:'float:left;'
					}]
				}]
			}
		});
		this.click = config.handler;          
		RWS.Controls.ToolBtn.superclass.constructor.call(this, config);
		this.textWidth = 140;
	}
	,onRender:function(){ 
		RWS.Controls.ToolBtn.superclass.onRender.apply(this, arguments);
		if(!this.initialConfig.text) this.initialConfig.text = '';
		var text = typeof(this.initialConfig.text) == "string" ? this.initialConfig.text : this.initialConfig.text.toString();
		this.el.first().first().update(COMP.fitStringToWidth(text, this.textWidth, "toolBtn"));
		if(this.click){
			this.el.on('click', this.click, this);        
			this.el.addClass('pointer');
		}
	}
    ,setText:function(txt){
        this.text = txt;
        this.el.first().first().update(txt);
    }
}); 
Ext.reg('toolbtn', RWS.Controls.ToolBtn);

RWS.Controls.ListBtn = Ext.extend(RWS.Controls.ToolBtn, {
	constructor:function(config){
		RWS.Controls.ListBtn.superclass.constructor.call(this,config);
		this.autoEl.cn[0].cn.add({tag:'div', cls: 'arrowPointer'});
		this.autoEl.cn[0].cn.add({tag:'div', cls: 'toolBtn-count', html:'0'});
		this.autoEl.cls += ' pointer';
		this.textWidth = 130;
	}
	,add:function(){}
	,afterRender:function(){
		this.el.on('mouseenter', function(evnt, html, object){
			 this.child(".arrowPointer").show();
		});
		this.el.on('mouseleave', function(evnt, html, object){
			 this.child(".arrowPointer").hide();
		});
	}
});
Ext.reg('listbtn', RWS.Controls.ListBtn);

RWS.Controls.FilterBtn = Ext.extend(RWS.Controls.ListBtn,{
	constructor:function(config){
		config.store = new Ext.data.Store({
			recordType:RWS.Search.PostFilters.recType
		});
		this.dataView = new Ext.DataView({
			tpl:RWS.Search.PostFilters.menuTpl
			,store:config.store
			,itemSelector:'div.postfilterItem'
			,overClass:'textLink'
			,owner:this
			,style:'padding:0 10;'
			,autoScroll:true
			,height: 261
			,listeners:{
				'click':{fn:function(view, index, node, e){
					var selFilter = view.getRecord(node).data.name;
					RWS.Search.PostFilters.Add(this.owner.filter, selFilter, false);
				}}
			}
		});
		RWS.Controls.FilterBtn.superclass.constructor.call(this, config);
	}
	,onRender:function(){
		RWS.Controls.FilterBtn.superclass.onRender.apply(this, arguments);
		this.store.on('add', this.updateCount, this);
		this.store.on('remove', this.resetCount, this);
		this.tooltip = new RWS.Controls.FilterTip({
			target: this.el.dom.firstChild
			,owner:this
			,anchor:'left'
			,title:this.initialConfig.text
			,height:300
			,autoScroll:true
			,width: 300
			,dataView:this.dataView
		})
	}
	,updateCount:function(store, records, index){
		this.count += records.length;
		this.el.child('.toolBtn-count').update(this.count + '');
	}
	,resetCount:function(){
		this.count = 0;
		this.el.child('.toolBtn-count').update(this.count + '');
	}   
	,loadData:function(){
		this.count = 0;
		RWS.Search.PostFilters.LoadFilterList(this.filter,this.store);
	}
});
Ext.reg('filterbtn', RWS.Controls.FilterBtn);

RWS.Controls.SavedListBtn = Ext.extend(RWS.Controls.ListBtn, {
	constructor:function(config){
		this.id = 'savedList_' + config.text;
		Ext.apply(config, {tooltipWidth:300});
		config.handler = config.textTitle ? function(){} : function(){
			RWS.SavedLists.ShowCurrentSavedListResults(this);
			this.tooltip.hide();
		}
		this.style = config.textTitle ? 'cursor:default !important;' : ''
		this.store = new Ext.data.Store({
			storeId: this.id + '_store'
			,autoLoad:config.textTitle ? false : true
			,list:this
			,sortInfo:{field:'title',direction:'ASC'}
			,baseParams: {sessionid:COMP.V.sessionid, slName:config.name, slKey:config.key}
			,recordType:RWS.SavedLists.slResultRecord
			,url: config.textTitle ? null : '/ajax/rws_request_saved_list_items'
			,reader:new Ext.data.JsonReader({
				root:'items'
				,idProperty:'rsn'
			}, RWS.SavedLists.slResultRecord)
			,listeners:{
				'load':this.storeLoaded
				,'add':config.textTitle ? this.itemAdded : function(){}
				,'remove':config.textTitle ? this.itemRemoved : function(){}
			}
		});
		this.dataView = new Ext.DataView({
			id: this.id + config.key + '_dataView'
			,tpl:RWS.SavedLists.tplSingleItemWithDeleteOpt
			,store:this.store
			,itemSelector:'div.slItem'
			,overClass:'textLink'
			,owner:this
			,style:'padding:0 0 10 10;'
			,autoScroll:true
			,height: 261
		});
		RWS.Controls.SavedListBtn.superclass.constructor.call(this, config);
	}
	,onRender:function(me){
		RWS.Controls.SavedListBtn.superclass.onRender.apply(this, arguments);
		initializeSavedListDropZone(this.id);
		this.tooltip = new RWS.Controls.SavedListTip({
			target: this.el.dom.firstChild
			,anchor:'left'
			,owner:this
			,title:this.initialConfig.text
			,height:300
			,width: this.initialConfig.tooltipWidth || 200
			,dataView: this.dataView
			,removeable:this.removeable
			,textTitle:this.initialConfig.textTitle
		});          
	}
	,storeLoaded:function(store, records, opt){
		if(this.list.el)
			if(this.list.el.child('.toolBtn-count'))
				this.list.el.child('.toolBtn-count').update(records.length + '');
	}
	,itemAdded:function(store, records, opt){
		 if(this.list.el)
			if(this.list.el.child('.toolBtn-count'))
				this.list.el.child('.toolBtn-count').update(store.getCount() + '');
		
	}
	,itemRemoved:function(store, record, opt){
		if(this.list.el)
			if(this.list.el.child('.toolBtn-count'))
				this.list.el.child('.toolBtn-count').update(store.getCount() + '');
	} 
});
Ext.reg('savedlistbtn', RWS.Controls.SavedListBtn);

RWS.Controls.PanelHeader = Ext.extend(Ext.Container, {
	constructor: function(config){
		config.height = 40;
		config.layout = 'hbox';
		var closeArea = config.closable ? 
			{xtype:'container', width: 90, layout:'hbox', items:[
				{xtype:'spacer', flex:3}
				,{xtype:'icon',style:'background:url(/images/small_icon_sprite_map.png) 0 -2900px;', height:29, width:29, onClick:function(){
					RWS.Bread.RemoveLastCrumb();
					resetFunction();
				}}
				,{xtype:'spacer', flex:1}]
			}
			:
			{xtype:'spacer', width: 90};
		config.items = [{xtype:'spacer', width: 40},{xtype:'spacer', width: 50, height: 1, style:'margin-top: 22px; border-top-style:solid; border-top-color:black; border-top-width: 1px;'},{xtype:'spacer', width: 15},{
				xtype:'label'
				,cls:'panelTitle'
				,text:config.title || 'Panel'
			},{xtype:'spacer', width: 15},{xtype:'spacer', flex: 100, height: 1, style:'margin-top: 22px; border-top-style:solid; border-top-color:black; border-top-width: 1px;'}, closeArea];
		RWS.Controls.PanelHeader.superclass.constructor.call(this, config);
	}
})
Ext.reg('panelHeader', RWS.Controls.PanelHeader);

//Panel with button in header
RWS.Controls.HeaderButtonPanel = Ext.extend(Ext.Panel, {
	onRender : function(ct, position){
		RWS.Controls.HeaderButtonPanel.superclass.onRender.call(this, ct, position);
		this.addButton();
	}
	,addButton : function(){
		this.button = new Ext.Button({
			id: this.id + '_button'
			,width: 16
			,height: 16
			,style:'float: left;'
			,iconCls:'add'
			,hidden:true
			,onClick: ShowAddSavedList
		});
		this.button.render(this.header, 0);
	}
	,showButton: function(){
		this.button.show();
	}
	,hideButton:function(){
		this.button.hide();
	}
});
Ext.reg('HeaderButtonPanel', RWS.Controls.HeaderButtonPanel);

// linebreak - A simple line break
Ext.ux.linebreak = Ext.extend(Ext.BoxComponent, {
	autoEl:{
		tag:'div'
		,html:'&nbsp;'
	}
});
Ext.reg('linebreak', Ext.ux.linebreak);

RWS.Controls.PatronChangePassword = Ext.extend(Ext.Window, {
	initComponent:function(){
		Ext.apply(this, {
			title:L(6498)	//Change Password
			,layout:'form'
			,width:500
			,resizable:false
			,plain:true
			,border:false
			,modal:true
			,cls:'resetDialog'
			,buttonAlign:'center'
			,defaults:{inputType:'password'}
			,defaultType:'textfield'
			,items:[{
				xtype:'label'
				,html:L(27395)+'</br></br>'	//ls_You_can_change_your_password
			},{
				fieldLabel:L(6499)	//Old Password
				,id:'oldPassword'
			},{
				fieldLabel:L(6500)	//New Password
				,id:'newPassword'
				,name:'newPassword'
				,allowBlank:false
			},{
				fieldLabel:L(6501)	//Verify Password
				,id:'verifyPassword'
				,name:'verifyPassword'
				,initialPassField:'newPassword'
				,allowBlank:false
				,validationEvent:'keyup'
				,validateOnBlur:false
			}]
			,bbar:['->',{
				text:'Cancel'
				,handler:function(){this.ownerCt.ownerCt.close()}
			},{
				text:L(6498)	//Change Password
				,formBind:true
				,handler:function(){
					var oldPass = Ext.getCmp('oldPassword').getValue();
					var newPass = Ext.getCmp('newPassword').getValue();
					var newConfirm = Ext.getCmp('verifyPassword').getValue();

					COMP.SendAjaxCommand('all_patron_change_password',
					{
						rsn:RWS.Patron.current.data.rsn
						,old:myUrlEncode(oldPass,oldPass)
						,'new':myUrlEncode(newPass, newPass)
						,newConfirm:myUrlEncode(newConfirm, newConfirm)
					}, RWS.Patron.PasswordChanged, RWS.Patron);
					this.ownerCt.ownerCt.close();
				}
			}]
			
		});
		RWS.Controls.PatronChangePassword.superclass.initComponent.call(this);
	}
});
Ext.reg('patronchangepassword', RWS.Controls.PatronChangePassword);

if (window.COMP) COMP.Broadcast('scriptLoaded_rws_controls');