1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-25 01:37:34 +02:00

First version of the newly restructured repository

This commit is contained in:
Giulio Cesare Solaroli
2011-10-03 00:56:18 +01:00
parent 597ecfbc02
commit ef68436ac0
729 changed files with 232898 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
/**
* @class YAHOO.ext.Button
* @extends YAHOO.ext.util.Observable
* Simple Button class
* @cfg {String} text The button text
* @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
* @cfg {Object} scope The scope of the handler
* @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
* @constructor
* Create a new button
* @param {String/HTMLElement/Element} renderTo The element to append the button to
* @param {Object} config The config object
*/
YAHOO.ext.Button = function(renderTo, config){
YAHOO.ext.util.Config.apply(this, config);
this.events = {
/**
* @event click
* Fires when this button is clicked
* @param {Button} this
* @param {EventObject} e The click event
*/
'click' : true
};
if(renderTo){
this.render(renderTo);
}
};
YAHOO.extendX(YAHOO.ext.Button, YAHOO.ext.util.Observable, {
render : function(renderTo){
var btn;
if(!this.dhconfig){
if(!YAHOO.ext.Button.buttonTemplate){
// hideous table template
YAHOO.ext.Button.buttonTemplate = new YAHOO.ext.DomHelper.Template('<a href="#" class="ybtn-focus"><table border="0" cellpadding="0" cellspacing="0" class="ybtn-wrap"><tbody><tr><td class="ybtn-left">&#160;</td><td class="ybtn-center" unselectable="on">{0}</td><td class="ybtn-right">&#160;</td></tr></tbody></table></a>');
}
btn = YAHOO.ext.Button.buttonTemplate.append(
getEl(renderTo).dom, [this.text], true);
this.tbl = getEl(btn.dom.firstChild, true);
}else{
btn = YAHOO.ext.DomHelper.append(this.footer.dom, this.dhconfig, true);
}
this.el = btn;
this.autoWidth();
btn.addClass('ybtn');
btn.mon('click', this.onClick, this, true);
btn.on('mouseover', this.onMouseOver, this, true);
btn.on('mouseout', this.onMouseOut, this, true);
btn.on('mousedown', this.onMouseDown, this, true);
btn.on('mouseup', this.onMouseUp, this, true);
},
/**
* Returns the buttons element
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
},
/**
* Destroys this Button.
*/
destroy : function(){
this.el.removeAllListeners();
this.purgeListeners();
this.el.update('');
this.el.remove();
},
autoWidth : function(){
if(this.tbl){
this.el.setWidth('auto');
this.tbl.setWidth('auto');
if(this.minWidth){
if(this.tbl.getWidth() < this.minWidth){
this.tbl.setWidth(this.minWidth);
}
}
this.el.setWidth(this.tbl.getWidth());
}
},
/**
* Sets this buttons click handler
* @param {Function} handler The function to call when the button is clicked
* @param {Object} scope (optional) Scope for the function passed above
*/
setHandler : function(handler, scope){
this.handler = handler;
this.scope = scope;
},
/**
* Set this buttons text
* @param {String} text
*/
setText : function(text){
this.text = text;
this.el.dom.firstChild.firstChild.firstChild.childNodes[1].innerHTML = text;
this.autoWidth();
},
/**
* Get the text for this button
* @return {String}
*/
getText : function(){
return this.text;
},
/**
* Show this button
*/
show: function(){
this.el.setStyle('display', '');
},
/**
* Hide this button
*/
hide: function(){
this.el.setStyle('display', 'none');
},
/**
* Convenience function for boolean show/hide
* @param {Boolean} visible true to show/false to hide
*/
setVisible: function(visible){
if(visible) {
this.show();
}else{
this.hide();
}
},
/**
* Focus the button
*/
focus : function(){
this.el.focus();
},
/**
* Disable this button
*/
disable : function(){
this.el.addClass('ybtn-disabled');
this.disabled = true;
},
/**
* Enable this button
*/
enable : function(){
this.el.removeClass('ybtn-disabled');
this.disabled = false;
},
onClick : function(e){
e.preventDefault();
if(!this.disabled){
this.fireEvent('click', this, e);
if(this.handler){
this.handler.call(this.scope || this, this, e);
}
}
},
onMouseOver : function(e){
if(!this.disabled){
this.el.addClass('ybtn-over');
}
},
onMouseOut : function(e){
this.el.removeClass('ybtn-over');
},
onMouseDown : function(){
if(!this.disabled){
this.el.addClass('ybtn-click');
}
},
onMouseUp : function(){
this.el.removeClass('ybtn-click');
}
});

View File

@@ -0,0 +1,344 @@
YAHOO.ext.DatePicker = function(id, parentElement){
this.id = id;
this.selectedDate = new Date();
this.visibleDate = new Date();
this.element = null;
this.shadow = null;
this.callback = null;
this.buildControl(parentElement || document.body);
this.mouseDownHandler = YAHOO.ext.EventManager.wrap(this.handleMouseDown, this, true);
this.keyDownHandler = YAHOO.ext.EventManager.wrap(this.handleKeyDown, this, true);
this.wheelHandler = YAHOO.ext.EventManager.wrap(this.handleMouseWheel, this, true);
};
YAHOO.ext.DatePicker.prototype = {
show : function(x, y, value, callback){
this.hide();
this.selectedDate = value;
this.visibleDate = value;
this.callback = callback;
this.refresh();
this.element.show();
this.element.setXY(this.constrainToViewport ? this.constrainXY(x, y) : [x, y]);
this.shadow.show();
this.shadow.setRegion(this.element.getRegion());
this.element.dom.tabIndex = 1;
this.element.focus();
YAHOO.util.Event.on(document, "mousedown", this.mouseDownHandler);
YAHOO.util.Event.on(document, "keydown", this.keyDownHandler);
YAHOO.util.Event.on(document, "mousewheel", this.wheelHandler);
YAHOO.util.Event.on(document, "DOMMouseScroll", this.wheelHandler);
},
constrainXY : function(x, y){
var w = YAHOO.util.Dom.getViewportWidth();
var h = YAHOO.util.Dom.getViewportHeight();
var size = this.element.getSize();
return [
Math.min(w-size.width, x),
Math.min(h-size.height, y)
];
},
hide : function(){
this.shadow.hide();
this.element.hide();
YAHOO.util.Event.removeListener(document, "mousedown", this.mouseDownHandler);
YAHOO.util.Event.removeListener(document, "keydown", this.keyDownHandler);
YAHOO.util.Event.removeListener(document, "mousewheel", this.wheelHandler);
YAHOO.util.Event.removeListener(document, "DOMMouseScroll", this.wheelHandler);
},
setSelectedDate : function(date){
this.selectedDate = date;
},
getSelectedDate : function(){
return this.selectedDate;
},
showPrevMonth : function(){
this.visibleDate = this.getPrevMonth(this.visibleDate);
this.refresh();
},
showNextMonth : function(){
this.visibleDate = this.getNextMonth(this.visibleDate);
this.refresh();
},
showPrevYear : function(){
var d = this.visibleDate;
this.visibleDate = new Date(d.getFullYear()-1, d.getMonth(), d.getDate());
this.refresh();
},
showNextYear : function(){
var d = this.visibleDate;
this.visibleDate = new Date(d.getFullYear()+1, d.getMonth(), d.getDate());
this.refresh();
},
handleMouseDown : function(e){
var target = e.getTarget();
if(target != this.element.dom && !YAHOO.util.Dom.isAncestor(this.element.dom, target)){
this.hide();
}
},
handleKeyDown : function(e){
switch(e.browserEvent.keyCode){
case e.LEFT:
this.showPrevMonth();
e.stopEvent();
break;
case e.RIGHT:
this.showNextMonth();
e.stopEvent();
break;
case e.DOWN:
this.showPrevYear();
e.stopEvent();
break;
case e.UP:
this.showNextYear();
e.stopEvent();
break;
}
},
handleMouseWheel : function(e){
var delta = e.getWheelDelta();
if(delta > 0){
this.showPrevMonth();
e.stopEvent();
} else if(delta < 0){
this.showNextMonth();
e.stopEvent();
}
},
handleClick : function(e){
var d = this.visibleDate;
var t = e.getTarget();
if(t && t.className){
var cls = t.className.split(' ')[0];
switch(cls){
case 'active':
this.handleSelection(new Date(d.getFullYear(), d.getMonth(), parseInt(t.innerHTML)));
break;
case 'prevday':
var p = this.getPrevMonth(d);
this.handleSelection(new Date(p.getFullYear(), p.getMonth(), parseInt(t.innerHTML)));
break;
case 'nextday':
var n = this.getNextMonth(d);
this.handleSelection(new Date(n.getFullYear(), n.getMonth(), parseInt(t.innerHTML)));
break;
case 'ypopcal-today':
this.handleSelection(new Date());
break;
case 'next-month':
this.showNextMonth();
break;
case 'prev-month':
this.showPrevMonth();
break;
}
}
e.stopEvent();
},
selectToday : function(){
this.handleSelection(new Date());
},
handleSelection: function(date){
this.selectedDate = date;
this.callback(date);
this.hide();
},
getPrevMonth : function(d){
var m = d.getMonth();var y = d.getFullYear();
return (m == 0 ? new Date(--y, 11, 1) : new Date(y, --m, 1));
},
getNextMonth : function(d){
var m = d.getMonth();var y = d.getFullYear();
return (m == 11 ? new Date(++y, 0, 1) : new Date(y, ++m, 1));
},
getDaysInMonth : function(m, y){
return (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) ? 31 : (m == 4 || m == 6 || m == 9 || m == 11) ? 30 : this.isLeapYear(y) ? 29 : 28;
},
isLeapYear : function(y){
return (((y % 4) == 0) && ((y % 100) != 0) || ((y % 400) == 0));
},
clearTime : function(date){
if(date){
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
}
return date;
},
refresh : function(){
var d = this.visibleDate;
this.buildInnerCal(d);
this.calHead.update(this.monthNames[d.getMonth()] + ' ' + d.getFullYear());
if(this.element.isVisible()){
this.shadow.setRegion(this.element.getRegion());
}
}
};
/**
* This code is not pretty, but it is fast!
* @ignore
*/
YAHOO.ext.DatePicker.prototype.buildControl = function(parentElement){
var c = document.createElement('div');
c.style.position = 'absolute';
c.style.visibility = 'hidden';
document.body.appendChild(c);
var html = '<iframe id="'+this.id+'_shdw" frameborder="0" class="ypopcal-shadow" src="'+YAHOO.ext.SSL_SECURE_URL+'"></iframe>' +
'<div hidefocus="true" class="ypopcal" id="'+this.id+'">' +
'<table class="ypopcal-head" border=0 cellpadding=0 cellspacing=0><tbody><tr><td class="ypopcal-arrow"><div class="prev-month">&#160;</div></td><td class="ypopcal-month">&#160;</td><td class="ypopcal-arrow"><div class="next-month">&#160;</div></td></tr></tbody></table>' +
'<center><div class="ypopcal-inner">';
html += "<table border=0 cellspacing=0 class=\"ypopcal-table\"><thead><tr class=\"ypopcal-daynames\">";
var names = this.dayNames;
for(var i = 0; i < names.length; i++){
html += '<td>' + names[i].substr(0, 1) + '</td>';
}
html+= "</tr></thead><tbody><tr>";
for(var i = 0; i < 42; i++) {
if(i % 7 == 0 && i != 0){
html += '</tr><tr>';
}
html += "<td>&nbsp;</td>";
}
html += "</tr></tbody></table>";
html += '</div><button class="ypopcal-today">'+this.todayText+'</button></center></div>';
c.innerHTML = html;
this.shadow = getEl(c.childNodes[0], true);
this.shadow.enableDisplayMode('block');
this.element = getEl(c.childNodes[1], true);
this.element.enableDisplayMode('block');
document.body.appendChild(this.shadow.dom);
document.body.appendChild(this.element.dom);
document.body.removeChild(c);
this.element.on("selectstart", function(){return false;});
var tbody = this.element.dom.getElementsByTagName('tbody')[1];
this.cells = tbody.getElementsByTagName('td');
this.calHead = this.element.getChildrenByClassName('ypopcal-month', 'td')[0];
this.element.mon('mousedown', this.handleClick, this, true);
};
YAHOO.ext.DatePicker.prototype.buildInnerCal = function(dateVal){
var days = this.getDaysInMonth(dateVal.getMonth() + 1, dateVal.getFullYear());
var firstOfMonth = new Date(dateVal.getFullYear(), dateVal.getMonth(), 1);
var startingPos = firstOfMonth.getDay();
if(startingPos == 0) startingPos = 7;
var pm = this.getPrevMonth(dateVal);
var prevStart = this.getDaysInMonth(pm.getMonth()+1, pm.getFullYear())-startingPos;
var cells = this.cells;
days += startingPos;
// convert everything to numbers so it's fast
var day = 86400000;
var date = this.clearTime(new Date(pm.getFullYear(), pm.getMonth(), prevStart));
var today = this.clearTime(new Date()).getTime();
var sel = this.selectedDate ? this.clearTime(this.selectedDate).getTime() : today + 1; //today +1 will never match anything
var min = this.minDate ? this.clearTime(this.minDate).getTime() : Number.NEGATIVE_INFINITY;
var max = this.maxDate ? this.clearTime(this.maxDate).getTime() : Number.POSITIVE_INFINITY;
var ddMatch = this.disabledDatesRE;
var ddText = this.disabledDatesText;
var ddays = this.disabledDays;
var ddaysText = this.disabledDaysText;
var format = this.format;
var setCellClass = function(cal, cell, d){
cell.title = '';
var t = d.getTime();
if(t == today){
cell.className += ' today';
cell.title = cal.todayText;
}
if(t == sel){
cell.className += ' selected';
}
// disabling
if(t < min) {
cell.className = ' ypopcal-disabled';
cell.title = cal.minText;
return;
}
if(t > max) {
cell.className = ' ypopcal-disabled';
cell.title = cal.maxText;
return;
}
if(ddays){
var day = d.getDay();
for(var i = 0; i < ddays.length; i++) {
if(day === ddays[i]){
cell.title = ddaysText;
cell.className = ' ypopcal-disabled';
return;
}
}
}
if(ddMatch && format){
var fvalue = d.format(format);
if(ddMatch.test(fvalue)){
cell.title = ddText.replace('%0', fvalue);
cell.className = ' ypopcal-disabled';
return;
}
}
};
var i = 0;
for(; i < startingPos; i++) {
cells[i].innerHTML = (++prevStart);
date.setDate(date.getDate()+1);
cells[i].className = 'prevday';
setCellClass(this, cells[i], date);
}
for(; i < days; i++){
intDay = i - startingPos + 1;
cells[i].innerHTML = (intDay);
date.setDate(date.getDate()+1);
cells[i].className = 'active';
setCellClass(this, cells[i], date);
}
var extraDays = 0;
for(; i < 42; i++) {
cells[i].innerHTML = (++extraDays);
date.setDate(date.getDate()+1);
cells[i].className = 'nextday';
setCellClass(this, cells[i], date);
}
};
YAHOO.ext.DatePicker.prototype.todayText = "Today";
YAHOO.ext.DatePicker.prototype.minDate = null;
YAHOO.ext.DatePicker.prototype.maxDate = null;
YAHOO.ext.DatePicker.prototype.minText = "This date is before the minimum date";
YAHOO.ext.DatePicker.prototype.maxText = "This date is after the maximum date";
YAHOO.ext.DatePicker.prototype.format = 'm/d/y';
YAHOO.ext.DatePicker.prototype.disabledDays = null;
YAHOO.ext.DatePicker.prototype.disabledDaysText = '';
YAHOO.ext.DatePicker.prototype.disabledDatesRE = null;
YAHOO.ext.DatePicker.prototype.disabledDatesText = '';
YAHOO.ext.DatePicker.prototype.constrainToViewport = true;
YAHOO.ext.DatePicker.prototype.monthNames = Date.monthNames;
YAHOO.ext.DatePicker.prototype.dayNames = Date.dayNames;

View File

@@ -0,0 +1,216 @@
YAHOO.ext.InlineEditor = function(config, existingEl){
YAHOO.ext.util.Config.apply(this, config);
var dh = YAHOO.ext.DomHelper;
this.wrap = dh.append(this.container || document.body, {
tag:'div',
cls:'yinline-editor-wrap'
}, true);
this.textSizeEl = dh.append(document.body, {
tag: 'div',
cls: 'yinline-editor-sizer ' + (this.cls || '')
});
if(YAHOO.ext.util.Browser.isSafari){ // extra padding for safari's textboxes
this.textSizeEl.style.padding = '4px';
YAHOO.util.Dom.setStyle(this.textSizeEl, 'padding-right', '10px');
}
if(!YAHOO.ext.util.Browser.isGecko){ // no one else needs FireFox cursor fix
this.wrap.setStyle('overflow', 'hidden');
}
if(existingEl){
this.el = getEl(existingEl);
}
if(!this.el){
this.id = this.id || YAHOO.util.Dom.generateId();
if(!this.multiline){
this.el = this.wrap.createChild({
tag: 'input',
name: this.name || this.id,
id: this.id,
type: this.type || 'text',
autocomplete: 'off',
value: this.value || '',
cls: 'yinline-editor ' + (this.cls || ''),
maxlength: this.maxLength || ''
});
}else{
this.el = this.wrap.createChild({
tag: 'textarea',
name: this.name || this.id,
id: this.id,
html: this.value || '',
cls: 'yinline-editor yinline-editor-multiline ' + (this.cls || ''),
wrap: 'none'
});
}
}else{
this.wrap.dom.appendChild(this.el.dom);
}
this.el.addKeyMap([{
key: [10, 13],
fn: this.onEnter,
scope: this
},{
key: 27,
fn: this.onEsc,
scope: this
}]);
this.el.mon('keyup', this.onKeyUp, this, true);
this.el.on('blur', this.onBlur, this, true);
this.el.swallowEvent('keydown');
this.events = {
'startedit' : true,
'beforecomplete' : true,
'complete' : true
};
this.editing = false;
this.autoSizeTask = new YAHOO.ext.util.DelayedTask(this.autoSize, this);
};
YAHOO.extendX(YAHOO.ext.InlineEditor, YAHOO.ext.util.Observable, {
onEnter : function(k, e){
if(this.multiline && (e.ctrlKey || e.shiftKey)){
return;
}else{
this.completeEdit();
e.stopEvent();
}
},
onEsc : function(){
if(this.ignoreNoChange){
this.revert(true);
}else{
this.revert(false);
this.completeEdit();
}
},
onBlur : function(){
if(this.editing && this.completeOnBlur !== false){
this.completeEdit();
}
},
startEdit : function(el, value){
this.boundEl = YAHOO.util.Dom.get(el);
if(this.hideEl !== false){
this.boundEl.style.visibility = 'hidden';
}
var v = value || this.boundEl.innerHTML;
this.startValue = v;
this.setValue(v);
this.moveTo(YAHOO.util.Dom.getXY(this.boundEl));
this.editing = true;
if(YAHOO.ext.QuickTips){
YAHOO.ext.QuickTips.disable();
}
this.show.defer(10, this);
},
onKeyUp : function(e){
var k = e.getKey();
if(this.editing && (k < 33 || k > 40) && k != 27){
this.autoSizeTask.delay(50);
}
},
completeEdit : function(){
var v = this.getValue();
if(this.revertBlank !== false && v.length < 1){
v = this.startValue;
this.revert();
}
if(v == this.startValue && this.ignoreNoChange){
this.hide();
}
if(this.fireEvent('beforecomplete', this, v, this.startValue) !== false){
if(this.updateEl !== false && this.boundEl){
this.boundEl.innerHTML = v;
}
this.hide();
this.fireEvent('complete', this, v, this.startValue);
}
},
revert : function(hide){
this.setValue(this.startValue);
if(hide){
this.hide();
}
},
show : function(){
this.autoSize();
this.wrap.show();
this.el.focus();
if(this.selectOnEdit !== false){
this.el.dom.select();
}
},
hide : function(){
this.editing = false;
this.wrap.hide();
this.wrap.setLeftTop(-10000,-10000);
this.el.blur();
if(this.hideEl !== false){
this.boundEl.style.visibility = 'visible';
}
if(YAHOO.ext.QuickTips){
YAHOO.ext.QuickTips.enable();
}
},
setValue : function(v){
this.el.dom.value = v;
},
getValue : function(){
return this.el.dom.value;
},
autoSize : function(){
var el = this.el;
var wrap = this.wrap;
var v = el.dom.value;
var ts = this.textSizeEl;
if(v.length < 1){
ts.innerHTML = "&#160;&#160;";
}else{
v = v.replace(/[<> ]/g, '&#160;');
if(this.multiline){
v = v.replace(/\n/g, '<br />&#160;');
}
ts.innerHTML = v;
}
var ww = wrap.dom.offsetWidth;
var wh = wrap.dom.offsetHeight;
var w = ts.offsetWidth;
var h = ts.offsetHeight;
// lots of magic numbers in this block - wtf?
// the logic is to prevent the scrollbars from flashing
// in firefox. Updates the right element first
// so there's never overflow.
if(ww > w+4){
el.setWidth(w+4);
wrap.setWidth(w+8);
}else{
wrap.setWidth(w+8);
el.setWidth(w+4);
}
if(wh > h+4){
el.setHeight(h);
wrap.setHeight(h+4);
}else{
wrap.setHeight(h+4);
el.setHeight(h);
}
},
moveTo : function(xy){
this.wrap.setXY(xy);
}
});

View File

@@ -0,0 +1,230 @@
YAHOO.ext.MessageBox = function(){
var dlg, opt, mask;
var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
var buttons, activeTextEl, bwidth;
var handleButton = function(button){
if(typeof opt.fn == 'function'){
if(opt.fn.call(opt.scope||window, button, activeTextEl.dom.value) !== false){
dlg.hide();
}
}else{
dlg.hide();
}
};
var updateButtons = function(b){
var width = 0;
if(!b){
buttons['ok'].hide();
buttons['cancel'].hide();
buttons['yes'].hide();
buttons['no'].hide();
return width;
}
for(var k in buttons){
if(typeof buttons[k] != 'function'){
if(b[k]){
buttons[k].show();
buttons[k].setText(typeof b[k] == 'string' ? b[k] : YAHOO.ext.MessageBox.buttonText[k]);
width += buttons[k].el.getWidth()+15;
}else{
buttons[k].hide();
}
}
}
return width;
};
return {
getDialog : function(){
if(!dlg){
dlg = new YAHOO.ext.BasicDialog('mb-dlg', {
autoCreate : true,
shadow: true,
draggable: true,
resizable:false,
constraintoviewport:true,
fixedcenter:true,
shim:true,
modal: true,
width:400, height:100,
buttonAlign:'center',
closeClick : function(){
if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
handleButton('no');
}else{
handleButton('cancel');
}
}
});
dlg.closeClick = function(){
alert('wtf');
};
mask = dlg.mask;
dlg.addKeyListener(27, dlg.hide, dlg);
buttons = {};
buttons['ok'] = dlg.addButton(this.buttonText['ok'], handleButton.createCallback('ok'));
buttons['yes'] = dlg.addButton(this.buttonText['yes'], handleButton.createCallback('yes'));
buttons['no'] = dlg.addButton(this.buttonText['no'], handleButton.createCallback('no'));
buttons['cancel'] = dlg.addButton(this.buttonText['cancel'], handleButton.createCallback('cancel'));
bodyEl = dlg.body.createChild({
tag:'div',
html:'<span class="ext-mb-text"></span><br /><input type="text" class="ext-mb-input"><textarea class="ext-mb-textarea"></textarea><div class="ext-mb-progress-wrap"><div class="ext-mb-progress"><div class="ext-mb-progress-bar">&#160;</div></div></div>'
});
msgEl = bodyEl.dom.firstChild;
textboxEl = getEl(bodyEl.dom.childNodes[2]);
textboxEl.enableDisplayMode();
textboxEl.addKeyListener([10,13], function(){
if(dlg.isVisible() && opt && opt.buttons){
if(opt.buttons.ok){
handleButton('ok');
}else if(opt.buttons.yes){
handleButton('yes');
}
}
});
textareaEl = getEl(bodyEl.dom.childNodes[3]);
textareaEl.enableDisplayMode();
progressEl = getEl(bodyEl.dom.childNodes[4]);
progressEl.enableDisplayMode();
pp = getEl(progressEl.dom.firstChild.firstChild);
}
return dlg;
},
updateText : function(text){
if(!dlg.isVisible() && !opt.width){
dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
}
msgEl.innerHTML = text;
var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth),
Math.max(opt.minWidth || this.minWidth, bwidth));
if(opt.prompt){
activeTextEl.setWidth(w);
}
dlg.setContentSize(w, bodyEl.getHeight());
},
updateProgress : function(value, text){
if(text){
this.updateText(text);
}
pp.setWidth(value*progressEl.dom.firstChild.offsetWidth);
},
isVisible : function(){
return dlg && dlg.isVisible();
},
hide : function(){
if(this.isVisible()){
dlg.hide();
}
},
show : function(options){
var d = this.getDialog();
opt = options;
d.setTitle(opt.title || '&#160;');
d.close.setDisplayed(opt.closable !== false);
activeTextEl = textboxEl;
opt.prompt = opt.prompt || (opt.multiline ? true : false)
if(opt.prompt){
if(opt.multiline){
textboxEl.hide();
textareaEl.show();
textareaEl.setHeight(typeof opt.multiline == 'number' ?
opt.multiline : this.defaultTextHeight);
activeTextEl = textareaEl;
}else{
textboxEl.show();
textareaEl.hide();
}
}else{
textboxEl.hide();
textareaEl.hide();
}
progressEl.setDisplayed(opt.progress === true);
this.updateProgress(0);
activeTextEl.dom.value = opt.value || '';
if(opt.prompt){
dlg.setDefaultButton(activeTextEl);
}else{
var bs = opt.buttons;
var db = null;
if(bs && bs.ok){
db = buttons['ok'];
}else if(bs && bs.yes){
db = buttons['yes'];
}
dlg.setDefaultButton(db);
}
bwidth = updateButtons(opt.buttons);
this.updateText(opt.msg);
d.modal = opt.modal !== false;
d.mask = opt.modal !== false ? mask : false;
d.animateTarget = null;
d.show(options.animEl);
},
progress : function(title, msg){
this.show({
title : title,
msg : msg,
buttons: false,
progress:true,
closable:false
});
},
alert : function(title, msg, fn, scope){
this.show({
title : title,
msg : msg,
buttons: this.OK,
fn: fn,
scope : scope
});
},
confirm : function(title, msg, fn, scope){
this.show({
title : title,
msg : msg,
buttons: this.YESNO,
fn: fn,
scope : scope
});
},
prompt : function(title, msg, fn, scope, multiline){
this.show({
title : title,
msg : msg,
buttons: this.OKCANCEL,
fn: fn,
minWidth:250,
scope : scope,
prompt:true,
multiline: multiline
});
},
OK : {ok:true},
YESNO : {yes:true, no:true},
OKCANCEL : {ok:true, cancel:true},
YESNOCANCEL : {yes:true, no:true, cancel:true},
defaultTextHeight:75,
maxWidth : 500,
minWidth : 100,
buttonText : {
ok : 'OK',
cancel : 'Cancel',
yes : 'Yes',
no : 'No'
}
};
}();
YAHOO.ext.Msg = YAHOO.ext.MessageBox;

View File

@@ -0,0 +1,311 @@
/**
* @class YAHOO.ext.QuickTips
* @singleton
*/
YAHOO.ext.QuickTips = function(){
var el, tipBody, tipTitle, tm, cfg, close, tagEls = {}, reader, esc, anim, removeCls = null;
var ce, bd, xy;
var visible = false, disabled = true, inited = false;
var showProc = hideProc = dismissProc = 1, locks = [];
var E = YAHOO.util.Event, dd;
var onOver = function(e){
if(disabled){
return;
}
var t = E.getTarget(e);
if(!t){
return;
}
if(ce && t == ce.el){
clearTimeout(hideProc);
return;
}
if(t && tagEls[t.id]){
tagEls[t.id].el = t;
showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
return;
}
var ttp = reader.getAttribute(t, cfg.attribute);
if(!ttp && tm.interceptTitles && t.title){
ttp = t.title;
t.title = '';
if(reader.useNS){
t.setAttributeNS('y', 'qtip', ttp);
}else{
t.setAttribute('qtip', ttp);
}
}
if(ttp){
xy = E.getXY(e);
xy[0] += 12; xy[1] += 20;
showProc = show.defer(tm.showDelay, tm, [{
el: t,
text: ttp,
width: reader.getAttribute(t, cfg.width),
autoHide: reader.getAttribute(t, cfg.hide) != 'user',
title: reader.getAttribute(t, cfg.title),
cls: reader.getAttribute(t, cfg.cls)
}]);
}
};
var onOut = function(e){
clearTimeout(showProc);
var t = E.getTarget(e);
if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
hideProc = setTimeout(hide, tm.hideDelay);
}
};
var onMove = function(e){
if(disabled){
return;
}
xy = E.getXY(e);
xy[0] += 12; xy[1] += 20;
if(tm.trackMouse && ce){
el.setXY(xy);
}
};
var onDown = function(e){
clearTimeout(showProc);
clearTimeout(hideProc);
if(!e.within(el)){
if(tm.hideOnClick && ce && ce.autoHide !== false){
hide();
tm.disable();
}
}
};
var onUp = function(e){
tm.enable();
}
var show = function(o){
if(disabled){
return;
}
clearTimeout(dismissProc);
stopAnim();
ce = o;
if(removeCls){ // in case manually hidden
el.removeClass(removeCls);
removeCls = null;
}
if(ce.cls){
el.addClass(ce.cls);
removeCls = ce.cls;
}
if(ce.title){
tipTitleText.update(ce.title);
tipTitle.show();
}else{
tipTitle.hide();
}
tipBody.update(o.text);
if(!ce.width){
if(tipBody.dom.style.width){
tipBody.dom.style.width = '';
}
if(tipBody.dom.offsetWidth > tm.maxWidth){
tipBody.setWidth(tm.maxWidth);
}
}else{
tipBody.setWidth(ce.width);
}
if(!ce.autoHide){
close.setDisplayed(true);
if(dd){
dd.unlock();
}
}else{
close.setDisplayed(false);
if(dd){
dd.lock();
}
}
if(xy){
el.setXY(xy);
}
if(tm.animate){
anim.attributes = {opacity:{to:1}};
el.setOpacity(.1);
el.setStyle('visibility', 'visible');
anim.animateX(afterShow);
}else{
afterShow();
}
};
var afterShow = function(){
if(ce){
el.show();
esc.enable();
if(tm.autoDismiss && ce.autoHide !== false){
dismissProc = setTimeout(hide, tm.autoDismissDelay);
}
}
}
var hide = function(noanim){
clearTimeout(dismissProc);
clearTimeout(hideProc);
ce = null;
if(el.isVisible()){
esc.disable();
stopAnim();
if(noanim !== true && tm.animate){
anim.attributes = {opacity:{to:.1}};
el.beforeAction();
anim.animateX(afterHide);
}else{
afterHide();
}
}
};
var afterHide = function(){
el.hide();
if(removeCls){
el.removeClass(removeCls);
removeCls = null;
}
}
var stopAnim = function(){
if(anim && anim.isAnimated()){
anim.stop();
}
}
return {
init : function(){
if(YAHOO.ext.util.Browser.isIE && !YAHOO.ext.util.Browser.isIE7){
return;
}
tm = YAHOO.ext.QuickTips;
cfg = tm.tagConfig;
reader = new YAHOO.ext.CustomTagReader(cfg.namespace);
if(!inited){
el = new YAHOO.ext.Layer({cls:'ytip', shadow:true, useDisplay: false});
el.update('<div class="ytip-hd-left"><div class="ytip-hd-right"><div class="ytip-hd"></div></div></div>');
tipTitle = getEl(el.dom.firstChild);
tipTitleText = getEl(el.dom.firstChild.firstChild.firstChild);
tipTitle.enableDisplayMode('block');
tipBody = el.createChild({tag:'div', cls:'ytip-bd'});
close = el.createChild({tag:'div', cls:'ytip-close'});
close.on('click', hide);
d = getEl(document);
d.mon('mousedown', onDown);
d.on('mouseup', onUp);
d.on('mouseover', onOver);
d.on('mouseout', onOut);
d.on('mousemove', onMove);
esc = d.addKeyListener(27, hide);
esc.disable();
if(tm.animate){
anim = new YAHOO.util.Anim(el.dom, {}, .1);
}
if(YAHOO.util.DD){
dd = el.initDD('default', null, {
onDrag : function(){
el.sync();
}
});
dd.setHandleElId(tipTitleText.id);
dd.lock();
}
inited = true;
}
this.scan(document.body);
this.enable();
},
tips : function(config){
var cs = config instanceof Array ? config : arguments;
for(var i = 0, len = cs.length; i < len; i++) {
var c = cs[i];
var target = c.target;
if(target){
if(target instanceof Array){
for(var j = 0, jlen = target.length; j < jlen; j++){
tagEls[target[j]] = c;
}
}else{
tagEls[target] = c;
}
}
}
},
enable : function(){
if(inited){
locks.pop();
if(locks.length < 1){
disabled = false;
}
}
},
disable : function(){
disabled = true;
clearTimeout(showProc);
clearTimeout(hideProc);
clearTimeout(dismissProc);
if(ce){
hide(true);
}
locks.push(1);
},
scan : function(toScan){
toScan = toScan.dom ? toScan.dom : YAHOO.util.Dom.get(toScan);
var found = [];
reader.eachElement(cfg.tag, toScan, function(el){
var t = reader.getAttribute(el, cfg.target);
if(t){
found.push({
target: t.indexOf(',') != -1 ? t.split(',') : t,
text: el.innerHTML,
autoHide: reader.getAttribute(el, cfg.hide) != 'user',
width: reader.getAttribute(el, cfg.width),
title: reader.getAttribute(el, cfg.title),
cls: reader.getAttribute(el, cfg.cls)
});
}
el.parentNode.removeChild(el);
});
this.tips(found);
},
tagConfig : {
namespace : 'y',
tag : 'qtip',
attribute : 'qtip',
width : 'width',
target : 'target',
title : 'qtitle',
hide : 'hide',
cls : 'qclass'
},
maxWidth : 300,
interceptTitles : true,
trackMouse : false,
hideOnClick : true,
showDelay : 500,
hideDelay : 200,
autoHide : true,
autoDismiss : true,
autoDismissDelay : 5000,
/**
* True to turn on fade animation. Defaults to true
* except in IE7 (ClearType/scrollbar flicker issues in IE7 with filters).
* @type Boolean
*/
animate : YAHOO.util.Anim && !YAHOO.ext.util.Browser.isIE7
}
}();

View File

@@ -0,0 +1,586 @@
/**
* @class YAHOO.ext.Resizable
* @extends YAHOO.ext.util.Observable
* <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
* and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
* the textarea in a div and set "resizeChild" to true (or the id of the textarea), <b>or</b> set wrap:true in your config and
* the element will be wrapped for you automatically.</p><br/>
* Here's a Resizable with every possible config option and it's default value:
<pre><code>
var resizer = new YAHOO.ext.Resizable('element-id', {
resizeChild : false,
adjustments : [0, 0],
minWidth : 5,
minHeight : 5,
maxWidth : 10000,
maxHeight : 10000,
enabled : true,
wrap: false, // true to wrap the element
width: null, // initial size
height: null, // initial size
animate : false,
duration : .35,
dynamic : false,
handles : false,
multiDirectional : false,
disableTrackOver : false,
easing : YAHOO.util.Easing ? YAHOO.util.Easing.easeOutStrong : null,
widthIncrement : 0,
heightIncrement : 0,
pinned : false,
width : null,
height : null,
preserveRatio : false,
transparent: false,
minX: 0,
minY: 0,
draggable: false
});
resizer.on('resize', myHandler);
</code></pre>
* <p>
* To hide a particular handle, set it's display to none in CSS, or through script:<br>
* resizer.east.setDisplayed(false);
* </p>
* @constructor
* Create a new resizable component
* @param {String/HTMLElement/YAHOO.ext.Element} el The id or element to resize
* @param {Object} config configuration options
*/
YAHOO.ext.Resizable = function(el, config){
this.el = getEl(el);
if(config && config.wrap){
config.resizeChild = this.el;
this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : null);
this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';
this.el.setStyle('overflow', 'hidden');
this.el.setPositioning(config.resizeChild.getPositioning());
config.resizeChild.clearPositioning();
if(!config.width || !config.height){
var csize = config.resizeChild.getSize();
//csize.width -= config.adjustments[0];
//csize.height -= config.adjustments[1];
this.el.setSize(csize.width, csize.height);
}
if(config.pinned && !config.adjustments){
config.adjustments = 'auto';
}
}
this.proxy = this.el.createProxy({tag: 'div', cls: 'yresizable-proxy', id: this.el.id + '-rzproxy'})
this.proxy.unselectable();
// the overlay traps mouse events while dragging and fixes iframe issue
this.overlay = this.el.createProxy({tag: 'div', cls: 'yresizable-overlay', html: '&#160;'});
this.overlay.unselectable();
this.overlay.enableDisplayMode('block');
this.overlay.mon('mousemove', this.onMouseMove, this, true);
this.overlay.mon('mouseup', this.onMouseUp, this, true);
YAHOO.ext.util.Config.apply(this, config, {
/** True to resizeSize the first child or id/element to resize @type YAHOO.ext.Element */
resizeChild : false,
/** String "auto" or an array [width, height] with values to be <b>added</b> to the resize operation's new size. @type Array/String */
adjustments : [0, 0],
/** The minimum width for the element @type Number */
minWidth : 5,
/** The minimum height for the element @type Number */
minHeight : 5,
/** The maximum width for the element @type Number */
maxWidth : 10000,
/** The maximum height for the element @type Number */
maxHeight : 10000,
/** false to disable resizing @type Boolean */
enabled : true,
/** True to animate the resize (not compatible with dynamic sizing) @type Boolean */
animate : false,
/** Animation duration @type Float */
duration : .35,
/** True to resize the element while dragging instead of using a proxy @type Boolean */
dynamic : false,
// these 3 are only available at config time
/** String consisting of the resize handles to display. Valid handles are
* n (north), s (south) e (east), w (west), ne (northeast), nw (northwest), se (southeast), sw (southwest)
* and all (which applies them all). If this is blank it defaults to "e,s,se". Handles can be delimited using
* a space, comma or semi-colon. This is only applied at config time. @type String*/
handles : false,
multiDirectional : false,
/** true to disable mouse tracking. This is only applied at config time. @type Boolean*/
disableTrackOver : false,
/** Animation easing @type YAHOO.util.Easing */
easing : YAHOO.util.Easing ? YAHOO.util.Easing.easeOutStrong : null,
/** The increment to snap the width resize in pixels (dynamic must be true) @type Number */
widthIncrement : 0,
/** The increment to snap the height resize in pixels (dynamic must be true) @type Number */
heightIncrement : 0,
/** true to pin the resize handles. This is only applied at config time. @type Boolean*/
pinned : false,
/** The initial width for the element @type Number */
width : null,
/** The initial height for the element @type Number */
height : null,
/** true to preserve the initial size ratio. @type Boolean*/
preserveRatio : false,
/** true for transparent handles. This is only applied at config time. @type Boolean*/
transparent: false,
/** The minimum allowed page X for the element (only used for west resizing, defaults to 0) @type Number */
minX: 0,
/** The minimum allowed page Y for the element (only used for north resizing, defaults to 0) @type Number */
minY: 0,
/** convenience to initialize drag drop. @type Boolean*/
draggable: false
});
if(this.pinned){
this.disableTrackOver = true;
this.el.addClass('yresizable-pinned');
}
// if the element isn't positioned, make it relative
var position = this.el.getStyle('position');
if(position != 'absolute' && position != 'fixed'){
this.el.setStyle('position', 'relative');
}
if(!this.handles){ // no handles passed, must be legacy style
this.handles = 's,e,se';
if(this.multiDirectional){
this.handles += ',n,w';
}
}
if(this.handles == 'all'){
this.handles = 'n s e w ne nw se sw';
}
var hs = this.handles.split(/\s*?[,;]\s*?| /);
var ps = YAHOO.ext.Resizable.positions;
for(var i = 0, len = hs.length; i < len; i++){
if(hs[i] && ps[hs[i]]){
var pos = ps[hs[i]];
this[pos] = new YAHOO.ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
}
}
// legacy
this.corner = this.southeast;
this.activeHandle = null;
if(this.resizeChild){
if(typeof this.resizeChild == 'boolean'){
this.resizeChild = YAHOO.ext.Element.get(this.el.dom.firstChild, true);
}else{
this.resizeChild = YAHOO.ext.Element.get(this.resizeChild, true);
}
}
if(this.adjustments == 'auto'){
var rc = this.resizeChild;
var hw = this.west, he = this.east, hn = this.north, hs = this.south;
if(rc && (hw || hn)){
rc.setRelativePositioned();
rc.setLeft(hw ? hw.el.getWidth() : 0);
rc.setTop(hn ? hn.el.getHeight() : 0);
}
this.adjustments = [
(he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
(hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
];
}
if(this.draggable){
this.dd = this.dynamic ?
this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
}
// public events
this.events = {
/**
* @event beforeresize
* Fired before resize is allowed. Set enabled to false to cancel resize.
* @param {YAHOO.ext.Resizable} this
* @param {YAHOO.ext.EventObject} e The mousedown event
*/
'beforeresize' : new YAHOO.util.CustomEvent(),
/**
* @event resize
* Fired after a resize.
* @param {YAHOO.ext.Resizable} this
* @param {Number} width The new width
* @param {Number} height The new height
* @param {YAHOO.ext.EventObject} e The mouseup event
*/
'resize' : new YAHOO.util.CustomEvent()
};
if(this.width !== null && this.height !== null){
this.resizeTo(this.width, this.height);
}else{
this.updateChildSize();
}
};
YAHOO.extendX(YAHOO.ext.Resizable, YAHOO.ext.util.Observable, {
/**
* Perform a manual resize
* @param {Number} width
* @param {Number} height
*/
resizeTo : function(width, height){
this.el.setSize(width, height);
this.updateChildSize();
this.fireEvent('resize', this, width, height, null);
},
startSizing : function(e){
this.fireEvent('beforeresize', this, e);
if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
this.resizing = true;
this.startBox = this.el.getBox();
this.startPoint = e.getXY();
this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
(this.startBox.y + this.startBox.height) - this.startPoint[1]];
this.proxy.setBox(this.startBox);
this.overlay.setSize(YAHOO.util.Dom.getDocumentWidth(), YAHOO.util.Dom.getDocumentHeight());
this.overlay.show();
if(!this.dynamic){
this.proxy.show();
}
}
},
onMouseDown : function(handle, e){
if(this.enabled){
e.stopEvent();
this.activeHandle = handle;
this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));
this.startSizing(e);
}
},
onMouseUp : function(e){
var size = this.resizeElement();
this.resizing = false;
this.handleOut();
this.overlay.hide();
this.fireEvent('resize', this, size.width, size.height, e);
},
updateChildSize : function(){
if(this.resizeChild){
var el = this.el;
var child = this.resizeChild;
var adj = this.adjustments;
if(el.dom.offsetWidth){
var b = el.getSize(true);
child.setSize(b.width+adj[0], b.height+adj[1]);
}
// Second call here for IE
// The first call enables instant resizing and
// the second call corrects scroll bars if they
// exist
if(YAHOO.ext.util.Browser.isIE){
setTimeout(function(){
if(el.dom.offsetWidth){
var b = el.getSize(true);
child.setSize(b.width+adj[0], b.height+adj[1]);
}
}, 10);
}
}
},
snap : function(value, inc, min){
if(!inc || !value) return value;
var newValue = value;
var m = value % inc;
if(m > 0){
if(m > (inc/2)){
newValue = value + (inc-m);
}else{
newValue = value - m;
}
}
return Math.max(min, newValue);
},
resizeElement : function(){
var box = this.proxy.getBox();
//box.width = this.snap(box.width, this.widthIncrement);
//box.height = this.snap(box.height, this.heightIncrement);
//if(this.multiDirectional){
this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
//}else{
// this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
//}
this.updateChildSize();
this.proxy.hide();
return box;
},
constrain : function(v, diff, m, mx){
if(v - diff < m){
diff = v - m;
}else if(v - diff > mx){
diff = mx - v;
}
return diff;
},
onMouseMove : function(e){
if(this.enabled){
try{// try catch so if something goes wrong the user doesn't get hung
//var curXY = this.startPoint;
var curSize = this.curSize || this.startBox;
var x = this.startBox.x, y = this.startBox.y;
var ox = x, oy = y;
var w = curSize.width, h = curSize.height;
var ow = w, oh = h;
var mw = this.minWidth, mh = this.minHeight;
var mxw = this.maxWidth, mxh = this.maxHeight;
var wi = this.widthIncrement;
var hi = this.heightIncrement;
var eventXY = e.getXY();
var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
var pos = this.activeHandle.position;
switch(pos){
case 'east':
w += diffX;
w = Math.min(Math.max(mw, w), mxw);
break;
case 'south':
h += diffY;
h = Math.min(Math.max(mh, h), mxh);
break;
case 'southeast':
w += diffX;
h += diffY;
w = Math.min(Math.max(mw, w), mxw);
h = Math.min(Math.max(mh, h), mxh);
break;
case 'north':
diffY = this.constrain(h, diffY, mh, mxh);
y += diffY;
h -= diffY;
break;
case 'west':
diffX = this.constrain(w, diffX, mw, mxw);
x += diffX;
w -= diffX;
break;
case 'northeast':
w += diffX;
w = Math.min(Math.max(mw, w), mxw);
diffY = this.constrain(h, diffY, mh, mxh);
y += diffY;
h -= diffY;
break;
case 'northwest':
diffX = this.constrain(w, diffX, mw, mxw);
diffY = this.constrain(h, diffY, mh, mxh);
y += diffY;
h -= diffY;
x += diffX;
w -= diffX;
break;
case 'southwest':
diffX = this.constrain(w, diffX, mw, mxw);
h += diffY;
h = Math.min(Math.max(mh, h), mxh);
x += diffX;
w -= diffX;
break;
}
var sw = this.snap(w, wi, mw);
var sh = this.snap(h, hi, mh);
if(sw != w || sh != h){
switch(pos){
case 'northeast':
y -= sh - h;
break;
case 'north':
y -= sh - h;
break;
case 'southwest':
x -= sw - w;
break;
case 'west':
x -= sw - w;
break;
case 'northwest':
x -= sw - w;
y -= sh - h;
break;
}
w = sw;
h = sh;
}
if(this.preserveRatio){
switch(pos){
case 'southeast':
case 'east':
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
w = ow * (h/oh);
break;
case 'south':
w = ow * (h/oh);
w = Math.min(Math.max(mw, w), mxw);
h = oh * (w/ow);
break;
case 'northeast':
w = ow * (h/oh);
w = Math.min(Math.max(mw, w), mxw);
h = oh * (w/ow);
break;
case 'north':
var tw = w;
w = ow * (h/oh);
w = Math.min(Math.max(mw, w), mxw);
h = oh * (w/ow);
x += (tw - w) / 2;
break;
case 'southwest':
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
var tw = w;
w = ow * (h/oh);
x += tw - w;
break;
case 'west':
var th = h;
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
y += (th - h) / 2;
var tw = w;
w = ow * (h/oh);
x += tw - w;
break;
case 'northwest':
var tw = w;
var th = h;
h = oh * (w/ow);
h = Math.min(Math.max(mh, h), mxh);
w = ow * (h/oh);
y += th - h;
x += tw - w;
break;
}
}
this.proxy.setBounds(x, y, w, h);
if(this.dynamic){
this.resizeElement();
}
}catch(e){}
}
},
handleOver : function(){
if(this.enabled){
this.el.addClass('yresizable-over');
}
},
handleOut : function(){
if(!this.resizing){
this.el.removeClass('yresizable-over');
}
},
/**
* Returns the element this component is bound to.
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
},
/**
* Returns the resizeChild element (or null).
* @return {YAHOO.ext.Element}
*/
getResizeChild : function(){
return this.resizeChild;
},
/**
* Destroys this resizable. If the element was wrapped and
* removeEl is not true then the wrap remains.
* @param {Boolean} removeEl (optional) true to remove the element from the DOM
*/
destroy : function(removeEl){
this.proxy.remove();
this.overlay.removeAllListeners();
this.overlay.remove();
var ps = YAHOO.ext.Resizable.positions;
for(var k in ps){
if(typeof ps[k] != 'function' && this[ps[k]]){
var h = this[ps[k]];
h.el.removeAllListeners();
h.el.remove();
}
}
if(removeEl){
this.el.update('');
this.el.remove();
}
}
});
// hash to map config positions to true positions
YAHOO.ext.Resizable.positions = {
n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'
};
YAHOO.ext.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
if(!this.tpl){
// only initialize the template if resizable is used
var tpl = YAHOO.ext.DomHelper.createTemplate(
{tag: 'div', cls: 'yresizable-handle yresizable-handle-{0}', html: '&#160;'}
);
tpl.compile();
YAHOO.ext.Resizable.Handle.prototype.tpl = tpl;
}
this.position = pos;
this.rz = rz;
this.el = this.tpl.append(rz.el.dom, [this.position], true);
this.el.unselectable();
if(transparent){
this.el.setOpacity(0);
}
this.el.mon('mousedown', this.onMouseDown, this, true);
if(!disableTrackOver){
this.el.mon('mouseover', this.onMouseOver, this, true);
this.el.mon('mouseout', this.onMouseOut, this, true);
}
};
YAHOO.ext.Resizable.Handle.prototype = {
afterResize : function(rz){
// do nothing
},
onMouseDown : function(e){
this.rz.onMouseDown(this, e);
},
onMouseOver : function(e){
this.rz.handleOver(this, e);
},
onMouseOut : function(e){
this.rz.handleOut(this, e);
}
};

View File

@@ -0,0 +1,468 @@
/*
* splitbar.js, version .7
* Copyright(c) 2006, Jack Slocum.
* Code licensed under the BSD License
*/
if(YAHOO.util.DragDropMgr){
YAHOO.util.DragDropMgr.clickTimeThresh = 350;
}
/**
* @class YAHOO.ext.SplitBar
* @extends YAHOO.ext.util.Observable
* Creates draggable splitter bar functionality from two elements.
* <br><br>
* Usage:
* <pre><code>
var split = new YAHOO.ext.SplitBar('elementToDrag', 'elementToSize',
YAHOO.ext.SplitBar.HORIZONTAL, YAHOO.ext.SplitBar.LEFT);
split.setAdapter(new YAHOO.ext.SplitBar.AbsoluteLayoutAdapter("container"));
split.minSize = 100;
split.maxSize = 600;
split.animate = true;
split.onMoved.subscribe(splitterMoved);
</code></pre>
* @requires YAHOO.ext.Element
* @requires YAHOO.util.Dom
* @requires YAHOO.util.Event
* @requires YAHOO.util.CustomEvent
* @requires YAHOO.util.DDProxy
* @requires YAHOO.util.Anim (optional) to support animation
* @requires YAHOO.util.Easing (optional) to support animation
* @constructor
* Create a new SplitBar
* @param {String/HTMLElement/Element} dragElement The element to be dragged and act as the SplitBar.
* @param {String/HTMLElement/Element} resizingElement The element to be resized based on where the SplitBar element is dragged
* @param {Number} orientation (optional) Either YAHOO.ext.SplitBar.HORIZONTAL or YAHOO.ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
* @param {Number} placement (optional) Either YAHOO.ext.SplitBar.LEFT or YAHOO.ext.SplitBar.RIGHT for horizontal or
YAHOO.ext.SplitBar.TOP or YAHOO.ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the intial position
position of the SplitBar).
*/
YAHOO.ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
/** @private */
this.el = YAHOO.ext.Element.get(dragElement, true);
this.el.dom.unselectable = 'on';
/** @private */
this.resizingEl = YAHOO.ext.Element.get(resizingElement, true);
/**
* @private
* The orientation of the split. Either YAHOO.ext.SplitBar.HORIZONTAL or YAHOO.ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
* Note: If this is changed after creating the SplitBar, the placement property must be manually updated
* @type Number
*/
this.orientation = orientation || YAHOO.ext.SplitBar.HORIZONTAL;
/**
* The minimum size of the resizing element. (Defaults to 0)
* @type Number
*/
this.minSize = 0;
/**
* The maximum size of the resizing element. (Defaults to 2000)
* @type Number
*/
this.maxSize = 2000;
this.onMoved = new YAHOO.util.CustomEvent("SplitBarMoved", this);
/**
* Whether to animate the transition to the new size
* @type Boolean
*/
this.animate = false;
/**
* Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
* @type Boolean
*/
this.useShim = false;
/** @private */
this.shim = null;
if(!existingProxy){
/** @private */
this.proxy = YAHOO.ext.SplitBar.createProxy(this.orientation);
}else{
this.proxy = getEl(existingProxy).dom;
}
/** @private */
this.dd = new YAHOO.util.DDProxy(this.el.dom.id, "SplitBars", {dragElId : this.proxy.id});
/** @private */
this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
/** @private */
this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
/** @private */
this.dragSpecs = {};
/**
* @private The adapter to use to positon and resize elements
*/
this.adapter = new YAHOO.ext.SplitBar.BasicLayoutAdapter();
this.adapter.init(this);
if(this.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
/** @private */
this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? YAHOO.ext.SplitBar.LEFT : YAHOO.ext.SplitBar.RIGHT);
this.el.setStyle('cursor', 'e-resize');
}else{
/** @private */
this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? YAHOO.ext.SplitBar.TOP : YAHOO.ext.SplitBar.BOTTOM);
this.el.setStyle('cursor', 'n-resize');
}
this.events = {
/**
* @event resize
* Fires when the splitter is moved (alias for moved)
* @param {YAHOO.ext.SplitBar} this
* @param {Number} newSize the new width or height
*/
'resize' : this.onMoved,
/**
* @event moved
* Fires when the splitter is moved
* @param {YAHOO.ext.SplitBar} this
* @param {Number} newSize the new width or height
*/
'moved' : this.onMoved,
/**
* @event beforeresize
* Fires before the splitter is dragged
* @param {YAHOO.ext.SplitBar} this
*/
'beforeresize' : new YAHOO.util.CustomEvent('beforeresize')
}
}
YAHOO.extendX(YAHOO.ext.SplitBar, YAHOO.ext.util.Observable, {
onStartProxyDrag : function(x, y){
this.fireEvent('beforeresize', this);
if(this.useShim){
if(!this.shim){
this.shim = YAHOO.ext.SplitBar.createShim();
}
this.shim.setVisible(true);
}
YAHOO.util.Dom.setStyle(this.proxy, 'display', 'block');
var size = this.adapter.getElementSize(this);
this.activeMinSize = this.getMinimumSize();;
this.activeMaxSize = this.getMaximumSize();;
var c1 = size - this.activeMinSize;
var c2 = Math.max(this.activeMaxSize - size, 0);
if(this.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
this.dd.resetConstraints();
this.dd.setXConstraint(
this.placement == YAHOO.ext.SplitBar.LEFT ? c1 : c2,
this.placement == YAHOO.ext.SplitBar.LEFT ? c2 : c1
);
this.dd.setYConstraint(0, 0);
}else{
this.dd.resetConstraints();
this.dd.setXConstraint(0, 0);
this.dd.setYConstraint(
this.placement == YAHOO.ext.SplitBar.TOP ? c1 : c2,
this.placement == YAHOO.ext.SplitBar.TOP ? c2 : c1
);
}
this.dragSpecs.startSize = size;
this.dragSpecs.startPoint = [x, y];
YAHOO.util.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
},
/**
* @private Called after the drag operation by the DDProxy
*/
onEndProxyDrag : function(e){
YAHOO.util.Dom.setStyle(this.proxy, 'display', 'none');
var endPoint = YAHOO.util.Event.getXY(e);
if(this.useShim){
this.shim.setVisible(false);
}
var newSize;
if(this.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
newSize = this.dragSpecs.startSize +
(this.placement == YAHOO.ext.SplitBar.LEFT ?
endPoint[0] - this.dragSpecs.startPoint[0] :
this.dragSpecs.startPoint[0] - endPoint[0]
);
}else{
newSize = this.dragSpecs.startSize +
(this.placement == YAHOO.ext.SplitBar.TOP ?
endPoint[1] - this.dragSpecs.startPoint[1] :
this.dragSpecs.startPoint[1] - endPoint[1]
);
}
newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
if(newSize != this.dragSpecs.startSize){
this.adapter.setElementSize(this, newSize);
this.onMoved.fireDirect(this, newSize);
}
},
/**
* Get the adapter this SplitBar uses
* @return The adapter object
*/
getAdapter : function(){
return this.adapter;
},
/**
* Set the adapter this SplitBar uses
* @param {Object} adapter A SplitBar adapter object
*/
setAdapter : function(adapter){
this.adapter = adapter;
this.adapter.init(this);
},
/**
* Gets the minimum size for the resizing element
* @return {Number} The minimum size
*/
getMinimumSize : function(){
return this.minSize;
},
/**
* Sets the minimum size for the resizing element
* @param {Number} minSize The minimum size
*/
setMinimumSize : function(minSize){
this.minSize = minSize;
},
/**
* Gets the maximum size for the resizing element
* @return {Number} The maximum size
*/
getMaximumSize : function(){
return this.maxSize;
},
/**
* Sets the maximum size for the resizing element
* @param {Number} maxSize The maximum size
*/
setMaximumSize : function(maxSize){
this.maxSize = maxSize;
},
/**
* Sets the initialize size for the resizing element
* @param {Number} size The initial size
*/
setCurrentSize : function(size){
var oldAnimate = this.animate;
this.animate = false;
this.adapter.setElementSize(this, size);
this.animate = oldAnimate;
},
/**
* Destroy this splitbar.
* @param {Boolean} removeEl True to remove the element
*/
destroy : function(removeEl){
if(this.shim){
this.shim.remove();
}
this.dd.unreg();
this.proxy.parentNode.removeChild(this.proxy);
if(removeEl){
this.el.remove();
}
}
});
/**
* @private static Create the shim to drag over iframes
*/
YAHOO.ext.SplitBar.createShim = function(){
var shim = document.createElement('div');
shim.unselectable = 'on';
YAHOO.util.Dom.generateId(shim, 'split-shim');
YAHOO.util.Dom.setStyle(shim, 'width', '100%');
YAHOO.util.Dom.setStyle(shim, 'height', '100%');
YAHOO.util.Dom.setStyle(shim, 'position', 'absolute');
YAHOO.util.Dom.setStyle(shim, 'background', 'white');
YAHOO.util.Dom.setStyle(shim, 'z-index', 11000);
window.document.body.appendChild(shim);
var shimEl = YAHOO.ext.Element.get(shim);
shimEl.setOpacity(.01);
shimEl.setXY([0, 0]);
return shimEl;
};
/**
* @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
*/
YAHOO.ext.SplitBar.createProxy = function(orientation){
var proxy = document.createElement('div');
proxy.unselectable = 'on';
YAHOO.util.Dom.generateId(proxy, 'split-proxy');
YAHOO.util.Dom.setStyle(proxy, 'position', 'absolute');
YAHOO.util.Dom.setStyle(proxy, 'visibility', 'hidden');
YAHOO.util.Dom.setStyle(proxy, 'z-index', 11001);
YAHOO.util.Dom.setStyle(proxy, 'background-color', "#aaa");
if(orientation == YAHOO.ext.SplitBar.HORIZONTAL){
YAHOO.util.Dom.setStyle(proxy, 'cursor', 'e-resize');
}else{
YAHOO.util.Dom.setStyle(proxy, 'cursor', 'n-resize');
}
// the next 2 fix IE abs position div height problem
YAHOO.util.Dom.setStyle(proxy, 'line-height', '0px');
YAHOO.util.Dom.setStyle(proxy, 'font-size', '0px');
window.document.body.appendChild(proxy);
return proxy;
};
/**
* @class YAHOO.ext.SplitBar.BasicLayoutAdapter
* Default Adapter. It assumes the splitter and resizing element are not positioned
* elements and only gets/sets the width of the element. Generally used for table based layouts.
*/
YAHOO.ext.SplitBar.BasicLayoutAdapter = function(){
};
YAHOO.ext.SplitBar.BasicLayoutAdapter.prototype = {
// do nothing for now
init : function(s){
},
/**
* Called before drag operations to get the current size of the resizing element.
* @param {YAHOO.ext.SplitBar} s The SplitBar using this adapter
*/
getElementSize : function(s){
if(s.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
return s.resizingEl.getWidth();
}else{
return s.resizingEl.getHeight();
}
},
/**
* Called after drag operations to set the size of the resizing element.
* @param {YAHOO.ext.SplitBar} s The SplitBar using this adapter
* @param {Number} newSize The new size to set
* @param {Function} onComplete A function to be invoke when resizing is complete
*/
setElementSize : function(s, newSize, onComplete){
if(s.orientation == YAHOO.ext.SplitBar.HORIZONTAL){
if(!YAHOO.util.Anim || !s.animate){
s.resizingEl.setWidth(newSize);
if(onComplete){
onComplete(s, newSize);
}
}else{
s.resizingEl.setWidth(newSize, true, .1, onComplete, YAHOO.util.Easing.easeOut);
}
}else{
if(!YAHOO.util.Anim || !s.animate){
s.resizingEl.setHeight(newSize);
if(onComplete){
onComplete(s, newSize);
}
}else{
s.resizingEl.setHeight(newSize, true, .1, onComplete, YAHOO.util.Easing.easeOut);
}
}
}
};
/**
*@class YAHOO.ext.SplitBar.AbsoluteLayoutAdapter
* @extends YAHOO.ext.SplitBar.BasicLayoutAdapter
* Adapter that moves the splitter element to align with the resized sizing element.
* Used with an absolute positioned SplitBar.
* @param {String/HTMLElement/Element} container The container that wraps around the absolute positioned content. If it's
* document.body, make sure you assign an id to the body element.
*/
YAHOO.ext.SplitBar.AbsoluteLayoutAdapter = function(container){
this.basic = new YAHOO.ext.SplitBar.BasicLayoutAdapter();
this.container = getEl(container);
}
YAHOO.ext.SplitBar.AbsoluteLayoutAdapter.prototype = {
init : function(s){
this.basic.init(s);
//YAHOO.util.Event.on(window, 'resize', this.moveSplitter.createDelegate(this, [s]));
},
getElementSize : function(s){
return this.basic.getElementSize(s);
},
setElementSize : function(s, newSize, onComplete){
this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
},
moveSplitter : function(s){
var yes = YAHOO.ext.SplitBar;
switch(s.placement){
case yes.LEFT:
s.el.setX(s.resizingEl.getRight());
break;
case yes.RIGHT:
s.el.setStyle('right', (this.container.getWidth() - s.resizingEl.getLeft()) + 'px');
break;
case yes.TOP:
s.el.setY(s.resizingEl.getBottom());
break;
case yes.BOTTOM:
s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
break;
}
}
};
/**
* Orientation constant - Create a vertical SplitBar
* @static
* @type Number
*/
YAHOO.ext.SplitBar.VERTICAL = 1;
/**
* Orientation constant - Create a horizontal SplitBar
* @static
* @type Number
*/
YAHOO.ext.SplitBar.HORIZONTAL = 2;
/**
* Placement constant - The resizing element is to the left of the splitter element
* @static
* @type Number
*/
YAHOO.ext.SplitBar.LEFT = 1;
/**
* Placement constant - The resizing element is to the right of the splitter element
* @static
* @type Number
*/
YAHOO.ext.SplitBar.RIGHT = 2;
/**
* Placement constant - The resizing element is positioned above the splitter element
* @static
* @type Number
*/
YAHOO.ext.SplitBar.TOP = 3;
/**
* Placement constant - The resizing element is positioned under splitter element
* @static
* @type Number
*/
YAHOO.ext.SplitBar.BOTTOM = 4;

View File

@@ -0,0 +1,756 @@
/**
* @class YAHOO.ext.TabPanel
* @extends YAHOO.ext.util.Observable
* Creates a lightweight TabPanel component using Yahoo! UI.
* <br><br>
* Usage:
* <pre><code>
<font color="#008000">// basic tabs 1, built from existing content</font>
var tabs = new YAHOO.ext.TabPanel('tabs1');
tabs.addTab('script', "View Script");
tabs.addTab('markup', "View Markup");
tabs.activate('script');
<font color="#008000">// more advanced tabs, built from javascript</font>
var jtabs = new YAHOO.ext.TabPanel('jtabs');
jtabs.addTab('jtabs-1', "Normal Tab", "My content was added during construction.");
<font color="#008000">// set up the UpdateManager</font>
var tab2 = jtabs.addTab('jtabs-2', "Ajax Tab 1");
var updater = tab2.getUpdateManager();
updater.setDefaultUrl('ajax1.htm');
tab2.onActivate.subscribe(updater.refresh, updater, true);
<font color="#008000">// Use setUrl for Ajax loading</font>
var tab3 = jtabs.addTab('jtabs-3', "Ajax Tab 2");
tab3.setUrl('ajax2.htm', null, true);
<font color="#008000">// Disabled tab</font>
var tab4 = jtabs.addTab('tabs1-5', "Disabled Tab", "Can't see me cause I'm disabled");
tab4.disable();
jtabs.activate('jtabs-1');
}
* </code></pre>
* @requires YAHOO.ext.Element
* @requires YAHOO.ext.UpdateManager
* @requires YAHOO.util.Dom
* @requires YAHOO.util.Event
* @requires YAHOO.util.CustomEvent
* @requires YAHOO.util.Connect (optional)
* @constructor
* Create new TabPanel.
* @param {String/HTMLElement/Element} container The id, DOM element or YAHOO.ext.Element container where this TabPanel is to be rendered.
* @param {Boolean} config Config object to set any properties for this TabPanel or true to render the tabs on the bottom.
*/
YAHOO.ext.TabPanel = function(container, config){
/**
* The container element for this TabPanel.
* @type YAHOO.ext.Element
*/
this.el = getEl(container, true);
/** The position of the tabs. Can be 'top' or 'bottom' @type String */
this.tabPosition = 'top';
this.currentTabWidth = 0;
/** The minimum width of a tab (ignored if resizeTabs is not true). @type Number */
this.minTabWidth = 40;
/** The maximum width of a tab (ignored if resizeTabs is not true). @type Number */
this.maxTabWidth = 250;
/** The preferred (default) width of a tab (ignored if resizeTabs is not true). @type Number */
this.preferredTabWidth = 175;
/** Set this to true to enable dynamic tab resizing. @type Boolean */
this.resizeTabs = false;
/** Set this to true to turn on window resizing monitoring (ignored if resizeTabs is not true). @type Boolean */
this.monitorResize = true;
if(config){
if(typeof config == 'boolean'){
this.tabPosition = config ? 'bottom' : 'top';
}else{
YAHOO.ext.util.Config.apply(this, config);
}
}
if(this.tabPosition == 'bottom'){
this.bodyEl = getEl(this.createBody(this.el.dom));
this.el.addClass('ytabs-bottom');
}
this.stripWrap = getEl(this.createStrip(this.el.dom), true);
this.stripEl = getEl(this.createStripList(this.stripWrap.dom), true);
this.stripBody = getEl(this.stripWrap.dom.firstChild.firstChild, true);
if(YAHOO.ext.util.Browser.isIE){
YAHOO.util.Dom.setStyle(this.stripWrap.dom.firstChild, 'overflow-x', 'hidden');
}
if(this.tabPosition != 'bottom'){
/** The body element that contains TabPaneItem bodies.
* @type YAHOO.ext.Element
*/
this.bodyEl = getEl(this.createBody(this.el.dom));
this.el.addClass('ytabs-top');
}
this.items = [];
this.bodyEl.setStyle('position', 'relative');
// add indexOf to array if it isn't present
if(!this.items.indexOf){
this.items.indexOf = function(o){
for(var i = 0, len = this.length; i < len; i++){
if(this[i] == o) return i;
}
return -1;
}
}
this.active = null;
this.onTabChange = new YAHOO.util.CustomEvent('TabItem.onTabChange');
this.activateDelegate = this.activate.createDelegate(this);
this.events = {
/**
* @event tabchange
* Fires when the active tab changes
* @param {YAHOO.ext.TabPanel} this
* @param {YAHOO.ext.TabPanelItem} activePanel The new active tab
*/
'tabchange': this.onTabChange,
/**
* @event beforetabchange
* Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
* @param {YAHOO.ext.TabPanel} this
* @param {Object} e Set cancel to true on this object to cancel the tab change
* @param {YAHOO.ext.TabPanelItem} tab The tab being changed to
*/
'beforetabchange' : new YAHOO.util.CustomEvent('beforechange')
};
YAHOO.ext.EventManager.onWindowResize(this.onResize, this, true);
this.cpad = this.el.getPadding('lr');
this.hiddenCount = 0;
}
YAHOO.ext.TabPanel.prototype = {
fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
on : YAHOO.ext.util.Observable.prototype.on,
addListener : YAHOO.ext.util.Observable.prototype.addListener,
delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners,
/**
* Creates a new TabPanelItem by looking for an existing element with the provided id - if it's not found it creates one.
* @param {String} id The id of the div to use <b>or create</b>
* @param {String} text The text for the tab
* @param {<i>String</i>} content (optional) Content to put in the TabPanelItem body
* @param {<i>Boolean</i>} closable (optional) True to create a close icon on the tab
* @return {YAHOO.ext.TabPanelItem} The created TabPanelItem
*/
addTab : function(id, text, content, closable){
var item = new YAHOO.ext.TabPanelItem(this, id, text, closable);
this.addTabItem(item);
if(content){
item.setContent(content);
}
return item;
},
/**
* Returns the TabPanelItem with the specified id/index
* @param {String/Number} id The id or index of the TabPanelItem to fetch.
* @return {YAHOO.ext.TabPanelItem}
*/
getTab : function(id){
return this.items[id];
},
/**
* Hides the TabPanelItem with the specified id/index
* @param {String/Number} id The id or index of the TabPanelItem to hide.
*/
hideTab : function(id){
var t = this.items[id];
if(!t.isHidden()){
t.setHidden(true);
this.hiddenCount++;
this.autoSizeTabs();
}
},
/**
* "Unhides" the TabPanelItem with the specified id/index
* @param {String/Number} id The id or index of the TabPanelItem to unhide.
*/
unhideTab : function(id){
var t = this.items[id];
if(t.isHidden()){
t.setHidden(false);
this.hiddenCount--;
this.autoSizeTabs();
}
},
/**
* Add an existing TabPanelItem.
* @param {YAHOO.ext.TabPanelItem} item The TabPanelItem to add
*/
addTabItem : function(item){
this.items[item.id] = item;
this.items.push(item);
if(this.resizeTabs){
item.setWidth(this.currentTabWidth || this.preferredTabWidth)
this.autoSizeTabs();
}else{
item.autoSize();
}
},
/**
* Remove a TabPanelItem.
* @param {String/Number} id The id or index of the TabPanelItem to remove.
*/
removeTab : function(id){
var items = this.items;
var tab = items[id];
if(!tab) return;
var index = items.indexOf(tab);
if(this.active == tab && items.length > 1){
var newTab = this.getNextAvailable(index);
if(newTab)newTab.activate();
}
this.stripEl.dom.removeChild(tab.pnode.dom);
if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
this.bodyEl.dom.removeChild(tab.bodyEl.dom);
}
items.splice(index, 1);
delete this.items[tab.id];
tab.fireEvent('close', tab);
tab.purgeListeners();
this.autoSizeTabs();
},
getNextAvailable : function(start){
var items = this.items;
var index = start;
// look for a next tab that will slide over to
// replace the one being removed
while(index < items.length){
var item = items[++index];
if(item && !item.isHidden()){
return item;
}
}
// if one isn't found select the previous tab (on the left)
var index = start;
while(index >= 0){
var item = items[--index];
if(item && !item.isHidden()){
return item;
}
}
return null;
},
/**
* Disable a TabPanelItem. <b>It cannot be the active tab, if it is this call is ignored.</b>.
* @param {String/Number} id The id or index of the TabPanelItem to disable.
*/
disableTab : function(id){
var tab = this.items[id];
if(tab && this.active != tab){
tab.disable();
}
},
/**
* Enable a TabPanelItem that is disabled.
* @param {String/Number} id The id or index of the TabPanelItem to enable.
*/
enableTab : function(id){
var tab = this.items[id];
tab.enable();
},
/**
* Activate a TabPanelItem. The currently active will be deactivated.
* @param {String/Number} id The id or index of the TabPanelItem to activate.
*/
activate : function(id){
var tab = this.items[id];
if(tab == this.active){
return tab;
}
var e = {};
this.fireEvent('beforetabchange', this, e, tab);
if(e.cancel !== true && !tab.disabled){
if(this.active){
this.active.hide();
}
this.active = this.items[id];
this.active.show();
this.onTabChange.fireDirect(this, this.active);
}
return tab;
},
/**
* Get the active TabPanelItem
* @return {YAHOO.ext.TabPanelItem} The active TabPanelItem or null if none are active.
*/
getActiveTab : function(){
return this.active;
},
/**
* Updates the tab body element to fit the height of the container element
* for overflow scrolling
* @param {Number} targetHeight (optional) Override the starting height from the elements height
*/
syncHeight : function(targetHeight){
var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth('tb')-this.el.getPadding('tb');
var bm = this.bodyEl.getMargins();
var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
this.bodyEl.setHeight(newHeight);
return newHeight;
},
onResize : function(){
if(this.monitorResize){
this.autoSizeTabs();
}
},
/**
* Disables tab resizing while tabs are being added (if resizeTabs is false this does nothing)
*/
beginUpdate : function(){
this.updating = true;
},
/**
* Stops an update and resizes the tabs (if resizeTabs is false this does nothing)
*/
endUpdate : function(){
this.updating = false;
this.autoSizeTabs();
},
/**
* Manual call to resize the tabs (if resizeTabs is false this does nothing)
*/
autoSizeTabs : function(){
var count = this.items.length;
var vcount = count - this.hiddenCount;
if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
var w = Math.max(this.el.getWidth() - this.cpad, 10);
var availWidth = Math.floor(w / vcount);
var b = this.stripBody;
if(b.getWidth() > w){
var tabs = this.items;
this.setTabWidth(Math.max(availWidth, this.minTabWidth));
if(availWidth < this.minTabWidth){
/*if(!this.sleft){ // incomplete scrolling code
this.createScrollButtons();
}
this.showScroll();
this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
}
}else{
if(this.currentTabWidth < this.preferredTabWidth){
this.setTabWidth(Math.min(availWidth, this.preferredTabWidth));
}
}
},
/**
* Returns the number of tabs
* @return {Number}
*/
getCount : function(){
return this.items.length;
},
/**
* Resizes all the tabs to the passed width
* @param {Number} The new width
*/
setTabWidth : function(width){
this.currentTabWidth = width;
for(var i = 0, len = this.items.length; i < len; i++) {
if(!this.items[i].isHidden())this.items[i].setWidth(width);
}
},
/**
* Destroys this TabPanel
* @param {Boolean} removeEl (optional) True to remove the element from the DOM as well
*/
destroy : function(removeEl){
YAHOO.ext.EventManager.removeResizeListener(this.onResize, this);
for(var i = 0, len = this.items.length; i < len; i++){
this.items[i].purgeListeners();
}
if(removeEl === true){
this.el.update('');
this.el.remove();
}
}
};
/**
* @class YAHOO.ext.TabPanelItem
* @extends YAHOO.ext.util.Observable
*/
YAHOO.ext.TabPanelItem = function(tabPanel, id, text, closable){
/**
* The TabPanel this TabPanelItem belongs to
* @type YAHOO.ext.TabPanel
*/
this.tabPanel = tabPanel;
/**
* The id for this TabPanelItem
* @type String
*/
this.id = id;
/** @private */
this.disabled = false;
/** @private */
this.text = text;
/** @private */
this.loaded = false;
this.closable = closable;
/**
* The body element for this TabPanelItem
* @type YAHOO.ext.Element
*/
this.bodyEl = getEl(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
this.bodyEl.setVisibilityMode(YAHOO.ext.Element.VISIBILITY);
this.bodyEl.setStyle('display', 'block');
this.bodyEl.setStyle('zoom', '1');
this.hideAction();
var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
/** @private */
this.el = getEl(els.el, true);
this.inner = getEl(els.inner, true);
this.textEl = getEl(this.el.dom.firstChild.firstChild.firstChild, true);
this.pnode = getEl(els.el.parentNode, true);
this.el.mon('click', this.onTabClick, this, true);
/** @private */
if(closable){
var c = getEl(els.close, true);
c.dom.title = this.closeText;
c.addClassOnOver('close-over');
c.mon('click', this.closeClick, this, true);
}
// these two are now private and deprecated
this.onActivate = new YAHOO.util.CustomEvent('TabItem.onActivate');
this.onDeactivate = new YAHOO.util.CustomEvent('TabItem.onDeactivate');
this.events = {
/**
* @event activate
* Fires when this tab becomes the active tab
* @param {YAHOO.ext.TabPanel} tabPanel
* @param {YAHOO.ext.TabPanelItem} this
*/
'activate': this.onActivate,
/**
* @event beforeclose
* Fires before this tab is closed. To cancal the close, set cancel to true on e. (e.cancel = true)
* @param {YAHOO.ext.TabPanelItem} this
* @param {Object} e Set cancel to true on this object to cancel the close.
*/
'beforeclose': new YAHOO.util.CustomEvent('beforeclose'),
/**
* @event close
* Fires when this tab is closed
* @param {YAHOO.ext.TabPanelItem} this
*/
'close': new YAHOO.util.CustomEvent('close'),
/**
* @event deactivate
* Fires when this tab is no longer the active tab
* @param {YAHOO.ext.TabPanel} tabPanel
* @param {YAHOO.ext.TabPanelItem} this
*/
'deactivate' : this.onDeactivate
};
this.hidden = false;
};
YAHOO.ext.TabPanelItem.prototype = {
fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent,
on : YAHOO.ext.util.Observable.prototype.on,
addListener : YAHOO.ext.util.Observable.prototype.addListener,
delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener,
removeListener : YAHOO.ext.util.Observable.prototype.removeListener,
purgeListeners : function(){
YAHOO.ext.util.Observable.prototype.purgeListeners.call(this);
this.el.removeAllListeners();
},
/**
* Show this TabPanelItem - this <b>does not</b> deactivate the currently active TabPanelItem.
*/
show : function(){
this.pnode.addClass('on');
this.showAction();
if(YAHOO.ext.util.Browser.isOpera){
this.tabPanel.stripWrap.repaint();
}
this.onActivate.fireDirect(this.tabPanel, this);
},
/**
* Returns true if this tab is the active tab
* @return {Boolean}
*/
isActive : function(){
return this.tabPanel.getActiveTab() == this;
},
/**
* Hide this TabPanelItem - if you don't activate another TabPanelItem this could look odd.
*/
hide : function(){
this.pnode.removeClass('on');
this.hideAction();
this.onDeactivate.fireDirect(this.tabPanel, this);
},
hideAction : function(){
this.bodyEl.setStyle('position', 'absolute');
this.bodyEl.setLeft('-20000px');
this.bodyEl.setTop('-20000px');
this.bodyEl.hide();
},
showAction : function(){
this.bodyEl.setStyle('position', 'relative');
this.bodyEl.setTop('');
this.bodyEl.setLeft('');
this.bodyEl.show();
this.tabPanel.el.repaint.defer(1);
},
/**
* Set the tooltip (title attribute) for the tab
* @param {String} tooltip
*/
setTooltip : function(text){
this.textEl.dom.title = text;
},
onTabClick : function(e){
e.preventDefault();
this.tabPanel.activate(this.id);
},
getWidth : function(){
return this.inner.getWidth();
},
setWidth : function(width){
var iwidth = width - this.pnode.getPadding("lr");
this.inner.setWidth(iwidth);
this.textEl.setWidth(iwidth-this.inner.getPadding('lr'));
this.pnode.setWidth(width);
},
setHidden : function(hidden){
this.hidden = hidden;
this.pnode.setStyle('display', hidden ? 'none' : '');
},
/**
* Returns true if this tab is "hidden"
* @return {Boolean}
*/
isHidden : function(){
return this.hidden;
},
/**
* Returns the text for this tab
* @return {String}
*/
getText : function(){
return this.text;
},
autoSize : function(){
this.el.beginMeasure();
this.textEl.setWidth(1);
this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding('lr'));
this.el.endMeasure();
},
/**
* Sets the text for the tab (Note: this also sets the tooltip)
* @param {String} text
*/
setText : function(text){
this.text = text;
this.textEl.update(text);
this.textEl.dom.title = text;
if(!this.tabPanel.resizeTabs){
this.autoSize();
}
},
/**
* Activate this TabPanelItem - this <b>does</b> deactivate the currently active TabPanelItem.
*/
activate : function(){
this.tabPanel.activate(this.id);
},
/**
* Disable this TabPanelItem - this call is ignore if this is the active TabPanelItem.
*/
disable : function(){
if(this.tabPanel.active != this){
this.disabled = true;
this.pnode.addClass('disabled');
}
},
/**
* Enable this TabPanelItem if it was previously disabled.
*/
enable : function(){
this.disabled = false;
this.pnode.removeClass('disabled');
},
/**
* Set the content for this TabPanelItem.
* @param {String} content The content
* @param {Boolean} loadScripts true to look for and load scripts
*/
setContent : function(content, loadScripts){
this.bodyEl.update(content, loadScripts);
},
/**
* Get the {@link YAHOO.ext.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
* @return {YAHOO.ext.UpdateManager} The UpdateManager
*/
getUpdateManager : function(){
return this.bodyEl.getUpdateManager();
},
/**
* Set a URL to be used to load the content for this TabPanelItem.
* @param {String/Function} url The url to load the content from or a function to call to get the url
* @param {<i>String/Object</i>} params (optional) The string params for the update call or an object of the params. See {@link YAHOO.ext.UpdateManager#update} for more details. (Defaults to null)
* @param {<i>Boolean</i>} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
* @return {YAHOO.ext.UpdateManager} The UpdateManager
*/
setUrl : function(url, params, loadOnce){
if(this.refreshDelegate){
this.onActivate.unsubscribe(this.refreshDelegate);
}
this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
this.onActivate.subscribe(this.refreshDelegate);
return this.bodyEl.getUpdateManager();
},
/** @private */
_handleRefresh : function(url, params, loadOnce){
if(!loadOnce || !this.loaded){
var updater = this.bodyEl.getUpdateManager();
updater.update(url, params, this._setLoaded.createDelegate(this));
}
},
/**
* Force a content refresh from the URL specified in the setUrl() method.
* Will fail silently if the setUrl method has not been called.
* This does not activate the panel, just updates its content.
*/
refresh : function(){
if(this.refreshDelegate){
this.loaded = false;
this.refreshDelegate();
}
},
/** @private */
_setLoaded : function(){
this.loaded = true;
},
/** @private */
closeClick : function(e){
var e = {};
this.fireEvent('beforeclose', this, e);
if(e.cancel !== true){
this.tabPanel.removeTab(this.id);
}
},
/**
* The text displayed in the tooltip for the close icon.
* @type String
*/
closeText : 'Close this tab'
};
/** @private */
YAHOO.ext.TabPanel.prototype.createStrip = function(container){
var strip = document.createElement('div');
strip.className = 'ytab-wrap';
container.appendChild(strip);
return strip;
};
/** @private */
YAHOO.ext.TabPanel.prototype.createStripList = function(strip){
// div wrapper for retard IE
strip.innerHTML = '<div class="ytab-strip-wrap"><table class="ytab-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
return strip.firstChild.firstChild.firstChild.firstChild;
};
/** @private */
YAHOO.ext.TabPanel.prototype.createBody = function(container){
var body = document.createElement('div');
YAHOO.util.Dom.generateId(body, 'tab-body');
YAHOO.util.Dom.addClass(body, 'yui-ext-tabbody');
container.appendChild(body);
return body;
};
/** @private */
YAHOO.ext.TabPanel.prototype.createItemBody = function(bodyEl, id){
var body = YAHOO.util.Dom.get(id);
if(!body){
body = document.createElement('div');
body.id = id;
}
YAHOO.util.Dom.addClass(body, 'yui-ext-tabitembody');
bodyEl.insertBefore(body, bodyEl.firstChild);
return body;
};
/** @private */
YAHOO.ext.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
var td = document.createElement('td');
stripEl.appendChild(td);
if(closable){
td.className = "ytab-closable";
if(!this.closeTpl){
this.closeTpl = new YAHOO.ext.Template(
'<a href="#" class="ytab-right"><span class="ytab-left"><em class="ytab-inner">' +
'<span unselectable="on" title="{text}" class="ytab-text">{text}</span>' +
'<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
);
}
var el = this.closeTpl.overwrite(td, {'text': text});
var close = el.getElementsByTagName('div')[0];
var inner = el.getElementsByTagName('em')[0];
return {'el': el, 'close': close, 'inner': inner};
} else {
if(!this.tabTpl){
this.tabTpl = new YAHOO.ext.Template(
'<a href="#" class="ytab-right"><span class="ytab-left"><em class="ytab-inner">' +
'<span unselectable="on" title="{text}" class="ytab-text">{text}</span></em></span></a>'
);
}
var el = this.tabTpl.overwrite(td, {'text': text});
var inner = el.getElementsByTagName('em')[0];
return {'el': el, 'inner': inner};
}
};

View File

@@ -0,0 +1,766 @@
/**
* @class YAHOO.ext.View
* @extends YAHOO.ext.util.Observable
* Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
* This class also supports single and multi selection modes. <br>
* Create a data model bound view:
<pre><code>
var dataModel = new YAHOO.ext.grid.XMLDataModel(...);
var view = new YAHOO.ext.View('my-element',
'&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
dataModel, {
singleSelect: true,
selectedClass: 'ydataview-selected'
});
// listen for node click?
view.on('click', function(vw, index, node, e){
alert('Node "' + node.id + '" at index: ' + index + ' was clicked.');
});
// load XML data
dataModel.load('foobar.xml');
</code></pre>
For an example of creating a JSON/UpdateManager view, see {@link YAHOO.ext.JsonView}.
* <br><br>
* <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
* IE's limited insertion support with tables and Opera's faulty event bubbling.</b>
* @constructor
* Create a new View
* @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
* @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
* @param {DataModel} dataModel The bound data model
* @param {Object} config The config object
*/
YAHOO.ext.View = function(container, tpl, dataModel, config){
this.el = getEl(container, true);
this.nodes = this.el.dom.childNodes;
if(typeof tpl == 'string'){
tpl = new YAHOO.ext.Template(tpl);
}
tpl.compile();
/**
* The template used by this View
* @type {YAHOO.ext.DomHelper.Template}
*/
this.tpl = tpl;
this.setDataModel(dataModel);
var CE = YAHOO.util.CustomEvent;
/** @private */
this.events = {
/**
* @event beforeclick
* Fires before a click is processed. Returns false to cancel the default action.
* @param {YAHOO.ext.View} this
* @param {Number} index The index of the target node
* @param {HTMLElement} node The target node
* @param {YAHOO.ext.EventObject} e The raw event object
*/
'beforeclick' : true,
/**
* @event click
* Fires when a template node is clicked.
* @param {YAHOO.ext.View} this
* @param {Number} index The index of the target node
* @param {HTMLElement} node The target node
* @param {YAHOO.ext.EventObject} e The raw event object
*/
'click' : true,
/**
* @event dblclick
* Fires when a template node is double clicked.
* @param {YAHOO.ext.View} this
* @param {Number} index The index of the target node
* @param {HTMLElement} node The target node
* @param {YAHOO.ext.EventObject} e The raw event object
*/
'dblclick' : true,
/**
* @event contextmenu
* Fires when a template node is right clicked.
* @param {YAHOO.ext.View} this
* @param {Number} index The index of the target node
* @param {HTMLElement} node The target node
* @param {YAHOO.ext.EventObject} e The raw event object
*/
'contextmenu' : true,
/**
* @event selectionchange
* Fires when the selected nodes change.
* @param {YAHOO.ext.View} this
* @param {Array} selections Array of the selected nodes
*/
'selectionchange' : true,
/**
* @event beforeselect
* Fires before a selection is made. If any handlers return false, the selection is cancelled.
* @param {YAHOO.ext.View} this
* @param {HTMLElement} node The node to be selected
* @param {Array} selections Array of currently selected nodes
*/
'beforeselect' : true
};
this.el.mon("click", this.onClick, this, true);
this.el.mon("dblclick", this.onDblClick, this, true);
this.el.mon("contextmenu", this.onContextMenu, this, true);
/**
* The css class to add to selected nodes
* @type {YAHOO.ext.DomHelper.Template}
*/
this.selectedClass = 'ydataview-selected';
this.emptyText = '';
this.selections = [];
this.lastSelection = null;
/**
* The root property in the loaded json object that contains the data
* @type {String}
*/
this.jsonRoot = null;
YAHOO.ext.util.Config.apply(this, config);
if(this.renderUpdates || this.jsonRoot){
var um = this.el.getUpdateManager();
um.setRenderer(this);
}
};
YAHOO.extendX(YAHOO.ext.View, YAHOO.ext.util.Observable, {
/**
* Returns the element this view is bound to.
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
},
render : function(el, response){
this.clearSelections();
this.el.update('');
var o;
try{
o = YAHOO.ext.util.JSON.decode(response.responseText);
if(this.jsonRoot){
o = eval('o.' + this.jsonRoot);
}
}catch(e){}
/**
* The current json data or null
*/
this.jsonData = o;
this.beforeRender();
this.refresh();
},
beforeRender : function(){
},
/**
* Refreshes the view.
*/
refresh : function(){
this.clearSelections();
this.el.update('');
this.html = [];
if(this.renderUpdates || this.jsonRoot){
var o = this.jsonData;
if(o){
for(var i = 0, len = o.length; i < len; i++) {
this.renderEach(o[i]);
}
}
}else{
this.dataModel.each(this.renderEach, this);
}
var strHtml;
if(this.html.length > 0){
strHtml = this.html.join('');
}else{
strHtml = this.emptyText;
}
this.el.update(strHtml);
this.html = null;
this.nodes = this.el.dom.childNodes;
this.updateIndexes(0);
},
/**
* Function to override to reformat the data that is sent to
* the template for each node.
* @param {Array/Object} data The raw data (array of colData for a data model bound view or
* a JSON object for an UpdateManager bound view).
* @param {Number} index The index of the data within the data model
*/
prepareData : function(data, index){
return data;
},
renderEach : function(data){
this.html[this.html.length] = this.tpl.applyTemplate(this.prepareData(data));
},
/**
* Refresh an individual node.
* @param {Number} index
*/
refreshNode : function(index){
this.refreshNodes(index, index);
},
refreshNodes : function(dm, startIndex, endIndex){
this.clearSelections();
var dm = this.dataModel;
var ns = this.nodes;
for(var i = startIndex; i <= endIndex; i++){
var d = this.prepareData(dm.getRow(i), i);
if(i < ns.length-1){
var old = ns[i];
this.tpl.insertBefore(old, d);
this.el.dom.removeChild(old);
}else{
this.tpl.append(this.el.dom, d);
}
}
this.updateIndexes(startIndex, endIndex);
},
deleteNodes : function(dm, startIndex, endIndex){
this.clearSelections();
if(startIndex == 0 && endIndex >= this.nodes.length-1){
this.el.update('');
}else{
var el = this.el.dom;
for(var i = startIndex; i <= endIndex; i++){
el.removeChild(this.nodes[startIndex]);
}
this.updateIndexes(startIndex);
}
},
insertNodes : function(dm, startIndex, endIndex){
if(this.nodes.length == 0){
this.refresh();
}else{
this.clearSelections();
var t = this.tpl;
var before = this.nodes[startIndex];
var dm = this.dataModel;
if(before){
for(var i = startIndex; i <= endIndex; i++){
t.insertBefore(before, this.prepareData(dm.getRow(i), i));
}
}else{
var el = this.el.dom;
for(var i = startIndex; i <= endIndex; i++){
t.append(el, this.prepareData(dm.getRow(i), i));
}
}
this.updateIndexes(startIndex);
}
},
updateIndexes : function(dm, startIndex, endIndex){
var ns = this.nodes;
startIndex = startIndex || 0;
endIndex = endIndex || ns.length-1;
for(var i = startIndex; i <= endIndex; i++){
ns[i].nodeIndex = i;
}
},
/**
* Changes the data model this view uses and refresh the view.
* @param {DataModel} dataModel
*/
setDataModel : function(dm){
if(!dm) return;
this.unplugDataModel(this.dataModel);
this.dataModel = dm;
dm.on('cellupdated', this.refreshNode, this, true);
dm.on('datachanged', this.refresh, this, true);
dm.on('rowsdeleted', this.deleteNodes, this, true);
dm.on('rowsinserted', this.insertNodes, this, true);
dm.on('rowsupdated', this.refreshNodes, this, true);
dm.on('rowssorted', this.refresh, this, true);
this.refresh();
},
/**
* Unplug the data model and stop updates.
* @param {DataModel} dataModel
*/
unplugDataModel : function(dm){
if(!dm) return;
dm.removeListener('cellupdated', this.refreshNode, this);
dm.removeListener('datachanged', this.refresh, this);
dm.removeListener('rowsdeleted', this.deleteNodes, this);
dm.removeListener('rowsinserted', this.insertNodes, this);
dm.removeListener('rowsupdated', this.refreshNodes, this);
dm.removeListener('rowssorted', this.refresh, this);
this.dataModel = null;
},
/**
* Returns the template node the passed child belongs to or null if it doesn't belong to one.
* @param {HTMLElement} node
* @return {HTMLElement} The template node
*/
findItemFromChild : function(node){
var el = this.el.dom;
if(!node || node.parentNode == el){
return node;
}
var p = node.parentNode;
while(p && p != el){
if(p.parentNode == el){
return p;
}
p = p.parentNode;
}
return null;
},
/** @ignore */
onClick : function(e){
var item = this.findItemFromChild(e.getTarget());
if(item){
var index = this.indexOf(item);
if(this.onItemClick(item, index, e) !== false){
this.fireEvent('click', this, index, item, e);
}
}else{
this.clearSelections();
}
},
/** @ignore */
onContextMenu : function(e){
var item = this.findItemFromChild(e.getTarget());
if(item){
this.fireEvent('contextmenu', this, this.indexOf(item), item, e);
}
},
/** @ignore */
onDblClick : function(e){
var item = this.findItemFromChild(e.getTarget());
if(item){
this.fireEvent('dblclick', this, this.indexOf(item), item, e);
}
},
onItemClick : function(item, index, e){
if(this.fireEvent('beforeclick', this, index, item, e) !== false){
if(this.multiSelect || this.singleSelect){
if(this.multiSelect && e.shiftKey && this.lastSelection){
this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
}else{
this.select(item, this.multiSelect && e.ctrlKey);
this.lastSelection = item;
}
e.preventDefault();
}
return true;
}else{
return false;
}
},
/**
* Get the number of selected nodes.
* @return {Number}
*/
getSelectionCount : function(){
return this.selections.length;
},
/**
* Get the currently selected nodes.
* @return {Array} An array of HTMLElements
*/
getSelectedNodes : function(){
return this.selections;
},
/**
* Get the indexes of the selected nodes.
* @return {Array}
*/
getSelectedIndexes : function(){
var indexes = [];
for(var i = 0, len = this.selections.length; i < len; i++) {
indexes.push(this.selections[i].nodeIndex);
}
return indexes;
},
/**
* Clear all selections
* @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
*/
clearSelections : function(suppressEvent){
if(this.multiSelect || this.singleSelect){
YAHOO.util.Dom.removeClass(this.selections, this.selectedClass);
this.selections = [];
if(!suppressEvent){
this.fireEvent('selectionchange', this, this.selections);
}
}
},
/**
* Returns true if the passed node is selected
* @param {HTMLElement/Number} node The node or node index
* @return {Boolean}
*/
isSelected : function(node){
node = this.getNode(node);
var s = this.selections;
if(s.length < 1){
return false;
}
if(s.indexOf){
return s.indexOf(node) !== -1;
}else{
for(var i = 0, len = s.length; i < len; i++){
if (s[i] == node){
return true;
}
}
return false;
}
},
/**
* Selects nodes.
* @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
* @param {Boolean} keepExisting (optional) true to keep existing selections
* @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
*/
select : function(nodeInfo, keepExisting, suppressEvent){
if(!keepExisting){
this.clearSelections(true);
}
if(nodeInfo instanceof Array){
for(var i = 0, len = nodeInfo.length; i < len; i++) {
this.select(nodeInfo[i], true, true);
}
}else{
var node = this.getNode(nodeInfo);
if(node && !this.isSelected(node)){
if(this.fireEvent('beforeselect', this, node, this.selections) !== false){
YAHOO.util.Dom.addClass(node, this.selectedClass);
this.selections.push(node);
}
}
}
if(!suppressEvent){
this.fireEvent('selectionchange', this, this.selections);
}
},
/**
* Gets a template node.
* @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
* @return {HTMLElement} The node or null if it wasn't found
*/
getNode : function(nodeInfo){
if(typeof nodeInfo == 'object'){
return nodeInfo;
}else if(typeof nodeInfo == 'string'){
return document.getElementById(nodeInfo);
}else if(typeof nodeInfo == 'number'){
return this.nodes[nodeInfo];
}
return null;
},
/**
* Gets a range template nodes.
* @param {Number} startIndex
* @param {Number} endIndex
* @return {Array} An array of nodes
*/
getNodes : function(start, end){
var ns = this.nodes;
start = start || 0;
end = typeof end == 'undefined' ? ns.length-1 : end;
var nodes = [];
if(start <= end){
for(var i = start; i <= end; i++) {
nodes.push(ns[i]);
}
}else{
for(var i = start; i >= end; i--) {
nodes.push(ns[i]);
}
}
return nodes;
},
/**
* Finds the index of the passed node
* @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
* @return {Number} The index of the node or -1
*/
indexOf : function(node){
node = this.getNode(node);
if(typeof node.nodeIndex == 'number'){
return node.nodeIndex;
}
var ns = this.nodes;
for(var i = 0, len = ns.length; i < len; i++) {
if(ns[i] == node){
return i;
}
}
return -1;
}
});
/**
* @class YAHOO.ext.JsonView
* @extends YAHOO.ext.View
* Shortcut class to create a JSON + UpdateManager template view. Usage:
<pre><code>
var view = new YAHOO.ext.JsonView('my-element',
'&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
{ multiSelect: true, jsonRoot: 'data' });
// listen for node click?
view.on('click', function(vw, index, node, e){
alert('Node "' + node.id + '" at index: ' + index + ' was clicked.');
});
// direct load of JSON data
view.load('foobar.php');
// Example from my blog list
var tpl = new YAHOO.ext.Template(
'&lt;div class="entry"&gt;' +
'&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
'&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}' +
'&lt;/div&gt;&lt;hr /&gt;'
);
var moreView = new YAHOO.ext.JsonView('entry-list', tpl, {
jsonRoot: 'posts'
});
moreView.on('beforerender', this.sortEntries, this, true);
moreView.load({
url:'/blog/get-posts.php',
params: 'allposts=true',
text:'Loading Blog Entries...'
});
</code></pre>
* @constructor
* Create a new JsonView
* @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
* @param {DomHelper.Template} tpl The rendering template
* @param {Object} config The config object
*/
YAHOO.ext.JsonView = function(container, tpl, config){
var cfg = config || {};
cfg.renderUpdates = true;
YAHOO.ext.JsonView.superclass.constructor.call(this, container, tpl, null, cfg);
/**
* @event beforerender
* Fires before rendering of the downloaded json data.
* @param {YAHOO.ext.View} this
* @param {Object} data The json data loaded
*/
this.events['beforerender'] = true;
/**
* @event load
* Fires when data is loaded.
* @param {YAHOO.ext.View} this
* @param {Object} data The json data loaded
* @param {Object} response The raw Connect response object
*/
this.events['load'] = true;
/**
* @event loadexception
* Fires when loading fails.
* @param {YAHOO.ext.View} this
* @param {Object} response The raw Connect response object
*/
this.events['loadexception'] = true;
this.el.getUpdateManager().on('update', this.onLoad, this, true);
this.el.getUpdateManager().on('failure', this.onLoadException, this, true);
};
YAHOO.extendX(YAHOO.ext.JsonView, YAHOO.ext.View, {
/**
* Performs an async request, loading the JSON from the response. If params are specified it uses POST, otherwise it uses GET.
* @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
<pre><code>
view.load({
url: 'your-url.php',<br/>
params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string<br/>
callback: yourFunction,<br/>
scope: yourObject, //(optional scope) <br/>
discardUrl: false, <br/>
nocache: false,<br/>
text: 'Loading...',<br/>
timeout: 30,<br/>
scripts: false<br/>
});
</code></pre>
* The only required property is url. The optional properties nocache, text and scripts
* are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
* @param {<i>String/Object</i>} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
* @param {<i>Function</i>} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
* @param {<i>Boolean</i>} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
*/
load : function(){
var um = this.el.getUpdateManager();
um.update.apply(um, arguments);
},
/**
* Get the number of records in the current JSON dataset
* @return {Number}
*/
getCount : function(){
return this.jsonData ? this.jsonData.length : 0;
},
/**
* Returns the JSON object for the specified node(s)
* @param {HTMLElement/Array} node The node or an array of nodes
* @return {Object/Array} If you pass in an array, you get an array back, otherwise
* you get the JSON object for the node
*/
getNodeData : function(node){
if(node instanceof Array){
var data = [];
for(var i = 0, len = node.length; i < len; i++){
data.push(this.getNodeData(node[i]));
}
return data;
}
return this.jsonData[this.indexOf(node)] || null;
},
beforeRender : function(){
this.snapshot = this.jsonData;
if(this.sortInfo){
this.sort.apply(this, this.sortInfo);
}
this.fireEvent('beforerender', this, this.jsonData);
},
onLoad : function(el, o){
this.fireEvent('load', this, this.jsonData, o);
},
onLoadException : function(el, o){
this.fireEvent('loadexception', this, o);
},
/**
* Filter the data by a specific property.
* @param {String} property A property on your JSON objects
* @param {String/RegExp} value Either string that the property values
* should start with or a RegExp to test against the property
*/
filter : function(property, value){
if(this.jsonData){
var data = [];
var ss = this.snapshot;
if(typeof value == 'string'){
var vlen = value.length;
if(vlen == 0){
this.clearFilter();
return;
}
value = value.toLowerCase();
for(var i = 0, len = ss.length; i < len; i++){
var o = ss[i];
if(o[property].substr(0, vlen).toLowerCase() == value){
data.push(o);
}
}
}else if(value.exec){ // regex?
for(var i = 0, len = ss.length; i < len; i++){
var o = ss[i];
if(value.test(o[property])){
data.push(o);
}
}
}else{
return;
}
this.jsonData = data;
this.refresh();
}
},
/**
* Filter by a function. The passed function will be called with each
* object in the current dataset. If the function returns true, the value is kept
* otherwise it is filtered.
* @param {Function} fn
* @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
*/
filterBy : function(fn, scope){
if(this.jsonData){
var data = [];
var ss = this.snapshot;
for(var i = 0, len = ss.length; i < len; i++){
var o = ss[i];
if(fn.call(scope|| this, o)){
data.push(o);
}
}
this.jsonData = data;
this.refresh();
}
},
/**
* Clears the current filter.
*/
clearFilter : function(){
if(this.snapshot && this.jsonData != this.snapshot){
this.jsonData = this.snapshot;
this.refresh();
}
},
/**
* Sorts the data for this view and refreshes it.
* @param {String} property A property on your JSON objects to sort on
* @param {String} direction (optional) desc or asc (defaults to asc)
* @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
*/
sort : function(property, dir, sortType){
this.sortInfo = Array.prototype.slice.call(arguments, 0);
if(this.jsonData){
var p = property;
var dsc = dir && dir.toLowerCase() == 'desc';
var f = function(o1, o2){
var v1 = sortType ? sortType(o1[p]) : o1[p];
var v2 = sortType ? sortType(o2[p]) : o2[p];;
if(v1 < v2){
return dsc ? +1 : -1;
}else if(v1 > v2){
return dsc ? -1 : +1;
}else{
return 0;
}
};
this.jsonData.sort(f);
this.refresh();
if(this.jsonData != this.snapshot){
this.snapshot.sort(f);
}
}
}
});

View File

@@ -0,0 +1,296 @@
/**
* @class YAHOO.ext.Toolbar
* Basic Toolbar used by the Grid to create the paging toolbar. This class is reusable but functionality
* is limited. Look for more functionality in a future version.
* @constructor
* @param {String/HTMLElement/Element} container
* @param {Array} buttons (optional) array of button configs or elements to add
*/
YAHOO.ext.Toolbar = function(container, buttons){
this.el = getEl(container, true);
var div = document.createElement('div');
div.className = 'ytoolbar';
var tb = document.createElement('table');
tb.border = 0;
tb.cellPadding = 0;
tb.cellSpacing = 0;
div.appendChild(tb);
var tbody = document.createElement('tbody');
tb.appendChild(tbody);
var tr = document.createElement('tr');
tbody.appendChild(tr);
this.el.dom.appendChild(div);
this.tr = tr;
if(buttons){
this.add.apply(this, buttons);
}
};
YAHOO.ext.Toolbar.prototype = {
/**
* Adds element(s) to the toolbar - this function takes a variable number of
* arguments of mixed type and adds them to the toolbar...
*
* @param {Mixed} arg If arg is a ToolbarButton, it is added. If arg is a string, it is wrapped
* in a ytb-text element and added unless the text is "separator" in which case a separator
* is added. Otherwise, it is assumed the element is an HTMLElement and it is added directly.
*/
add : function(){
for(var i = 0; i < arguments.length; i++){
var el = arguments[i];
var td = document.createElement('td');
this.tr.appendChild(td);
if(el instanceof YAHOO.ext.ToolbarButton){
el.init(td);
}else if(el instanceof Array){
this.addButton(el);
}else if(typeof el == 'string'){
var span = document.createElement('span');
if(el == 'separator'){
span.className = 'ytb-sep';
}else{
span.innerHTML = el;
span.className = 'ytb-text';
}
td.appendChild(span);
}else if(typeof el == 'object' && el.nodeType){ // must be element?
td.appendChild(el);
}else if(typeof el == 'object'){ // must be button config?
this.addButton(el);
}
}
},
/**
* Returns the element for this toolbar
* @return {YAHOO.ext.Element}
*/
getEl : function(){
return this.el;
},
/**
* Adds a separator
*/
addSeparator : function(){
var td = document.createElement('td');
this.tr.appendChild(td);
var span = document.createElement('span');
span.className = 'ytb-sep';
td.appendChild(span);
},
/**
* Add a button (or buttons), see {@link YAHOO.ext.ToolbarButton} for more info on the config
* @param {Object/Array} config A button config or array of configs
* @return {YAHOO.ext.ToolbarButton/Array}
*/
addButton : function(config){
if(config instanceof Array){
var buttons = [];
for(var i = 0, len = config.length; i < len; i++) {
buttons.push(this.addButton(config[i]));
}
return buttons;
}
var b = config;
if(!(config instanceof YAHOO.ext.ToolbarButton)){
b = new YAHOO.ext.ToolbarButton(config);
}
this.add(b);
return b;
},
/**
* Adds text to the toolbar
* @param {String} text The text to add
* @return {HTMLElement} The span element created which you can use to update the text.
*/
addText : function(text){
var td = document.createElement('td');
this.tr.appendChild(td);
var span = document.createElement('span');
span.className = 'ytb-text';
span.innerHTML = text;
td.appendChild(span);
return span;
},
/**
* Inserts a button (or buttons) at the specified index
* @param {Number} index The index where the buttons are to be inserted
* @param {Object/Array} config A button config or array of configs
* @return {YAHOO.ext.ToolbarButton/Array}
*/
insertButton : function(index, config){
if(config instanceof Array){
var buttons = [];
for(var i = 0, len = config.length; i < len; i++) {
buttons.push(this.insertButton(index + i, config[i]));
}
return buttons;
}
var b = new YAHOO.ext.ToolbarButton(config);
var td = document.createElement('td');
var nextSibling = this.tr.childNodes[index];
if (nextSibling)
this.tr.insertBefore(td, nextSibling);
else
this.tr.appendChild(td);
b.init(td);
return b;
}
};
/**
* @class YAHOO.ext.ToolbarButton
* A toolbar button. The config has the following options:
* <ul>
* <li>className - The CSS class for the button. Use this to attach a background image for an icon.</li>
* <li>text - The button's text</li>
* <li>tooltip - The buttons tooltip text</li>
* <li>click - function to call when the button is clicked</li>
* <li>mouseover - function to call when the mouse moves over the button</li>
* <li>mouseout - function to call when the mouse moves off the button</li>
* <li>scope - The scope of the above event handlers</li>
* <li></li>
* <li></li>
* @constructor
* @param {Object} config
*/
YAHOO.ext.ToolbarButton = function(config){
YAHOO.ext.util.Config.apply(this, config);
};
YAHOO.ext.ToolbarButton.prototype = {
/** @private */
init : function(appendTo){
var element = document.createElement('span');
element.className = 'ytb-button';
if(this.id){
element.id = this.id;
}
this.setDisabled(this.disabled === true);
var inner = document.createElement('span');
inner.className = 'ytb-button-inner ' + (this.className || this.cls);
inner.unselectable = 'on';
if(this.tooltip){
element.setAttribute('title', this.tooltip);
}
if(this.style){
YAHOO.ext.DomHelper.applyStyles(inner, this.style);
}
element.appendChild(inner);
appendTo.appendChild(element);
this.el = getEl(element, true);
this.el.unselectable();
inner.innerHTML = (this.text ? this.text : '&#160;');
this.inner = inner;
this.el.mon('click', this.onClick, this, true);
this.el.mon('mouseover', this.onMouseOver, this, true);
this.el.mon('mouseout', this.onMouseOut, this, true);
},
/**
* Sets this buttons click handler
* @param {Function} click The function to call when the button is clicked
* @param {Object} scope (optional) Scope for the function passed above
*/
setHandler : function(click, scope){
this.click = click;
this.scope = scope;
},
/**
* Set this buttons text
* @param {String} text
*/
setText : function(text){
this.inner.innerHTML = text;
},
/**
* Set this buttons tooltip text
* @param {String} text
*/
setTooltip : function(text){
this.el.dom.title = text;
},
/**
* Show this button
*/
show: function(){
this.el.dom.parentNode.style.display = '';
},
/**
* Hide this button
*/
hide: function(){
this.el.dom.parentNode.style.display = 'none';
},
/**
* Disable this button
*/
disable : function(){
this.disabled = true;
if(this.el){
this.el.addClass('ytb-button-disabled');
}
},
/**
* Enable this button
*/
enable : function(){
this.disabled = false;
if(this.el){
this.el.removeClass('ytb-button-disabled');
}
},
/**
* Returns true if this button is disabled.
* @return {Boolean}
*/
isDisabled : function(){
return this.disabled === true;
},
setDisabled : function(disabled){
if(disabled){
this.disable();
}else{
this.enable();
}
},
/** @private */
onClick : function(){
if(!this.disabled && this.click){
this.click.call(this.scope || window, this);
}
},
/** @private */
onMouseOver : function(){
if(!this.disabled){
this.el.addClass('ytb-button-over');
if(this.mouseover){
this.mouseover.call(this.scope || window, this);
}
}
},
/** @private */
onMouseOut : function(){
this.el.removeClass('ytb-button-over');
if(!this.disabled){
if(this.mouseout){
this.mouseout.call(this.scope || window, this);
}
}
}
};