summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI-extensions/widgets
Side-by-side diff
Diffstat (limited to 'frontend/beta/js/YUI-extensions/widgets') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/BasicDialog.js1046
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/Button.js185
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/DatePicker.js344
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/InlineEditor.js216
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/MessageBox.js230
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/QuickTips.js311
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/Resizable.js586
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/SplitBar.js468
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TabPanel.js756
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TaskPanel.js0
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/TemplateView.js766
-rw-r--r--frontend/beta/js/YUI-extensions/widgets/Toolbar.js296
12 files changed, 5204 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI-extensions/widgets/BasicDialog.js b/frontend/beta/js/YUI-extensions/widgets/BasicDialog.js
new file mode 100644
index 0000000..3912568
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/BasicDialog.js
@@ -0,0 +1,1046 @@
+/**
+ * @class YAHOO.ext.BasicDialog
+ * @extends YAHOO.ext.util.Observable
+ * Lightweight Dialog Class.
+ *
+ * The code below lists all configuration options along with the default value.
+ * If the default value is what you want you can leave it out:
+ * <pre><code>
+ var dlg = new YAHOO.ext.BasicDialog('element-id', {
+ autoCreate: false, (true to auto create from scratch, or DomHelper Object)
+ title: null, (title to set at config time)
+ width: (css),
+ height: (css),
+ x: 200, //(defaults to center screen if blank)
+ y: 500, //(defaults to center screen if blank)
+ animateTarget: null,// (no animation) This is the id or element to animate from
+ resizable: true,
+ minHeight: 80,
+ minWidth: 200,
+ modal: false,
+ autoScroll: true,
+ closable: true,
+ constraintoviewport: true,
+ draggable: true,
+ autoTabs: false, (if true searches child nodes for elements with class ydlg-tab and converts them to tabs)
+ tabTag: 'div', // the tag name of tab elements
+ proxyDrag: false, (drag a proxy element rather than the dialog itself)
+ fixedcenter: false,
+ shadow: false,
+ buttonAlign: 'right',
+ minButtonWidth: 75,
+ shim: false // true to create an iframe shim to
+ // keep selects from showing through
+ });
+ </code></pre>
+ * @constructor
+ * Create a new BasicDialog.
+ * @param {String/HTMLElement/YAHOO.ext.Element} el The id of or container element
+ * @param {Object} config configuration options
+ */
+YAHOO.ext.BasicDialog = function(el, config){
+ this.el = getEl(el);
+ var dh = YAHOO.ext.DomHelper;
+ if(!this.el && config && config.autoCreate){
+ if(typeof config.autoCreate == 'object'){
+ if(!config.autoCreate.id){
+ config.autoCreate.id = el;
+ }
+ this.el = dh.append(document.body,
+ config.autoCreate, true);
+ }else{
+ this.el = dh.append(document.body,
+ {tag: 'div', id: el}, true);
+ }
+ }
+ el = this.el;
+ el.setDisplayed(true);
+ el.hide = this.hideAction;
+ this.id = el.id;
+ el.addClass('ydlg');
+
+ YAHOO.ext.util.Config.apply(this, config);
+
+ this.proxy = el.createProxy('ydlg-proxy');
+ this.proxy.hide = this.hideAction;
+ this.proxy.setOpacity(.5);
+ this.proxy.hide();
+
+ if(config.width){
+ el.setWidth(config.width);
+ }
+ if(config.height){
+ el.setHeight(config.height);
+ }
+ this.size = el.getSize();
+ if(typeof config.x != 'undefined' && typeof config.y != 'undefined'){
+ this.xy = [config.x,config.y];
+ }else{
+ this.xy = el.getCenterXY(true);
+ }
+ // find the header, body and footer
+ var cn = el.dom.childNodes;
+ for(var i = 0, len = cn.length; i < len; i++) {
+ var node = cn[i];
+ if(node && node.nodeType == 1){
+ if(YAHOO.util.Dom.hasClass(node, 'ydlg-hd')){
+ this.header = getEl(node, true);
+ }else if(YAHOO.util.Dom.hasClass(node, 'ydlg-bd')){
+ this.body = getEl(node, true);
+ }else if(YAHOO.util.Dom.hasClass(node, 'ydlg-ft')){
+ /**
+ * The footer element
+ * @type YAHOO.ext.Element
+ */
+ this.footer = getEl(node, true);
+ }
+ }
+ }
+
+ if(!this.header){
+ /**
+ * The header element
+ * @type YAHOO.ext.Element
+ */
+ this.header = this.body ?
+ dh.insertBefore(this.body.dom, {tag: 'div', cls:'ydlg-hd'}, true) :
+ dh.append(el.dom, {tag: 'div', cls:'ydlg-hd'}, true);
+ }
+ if(this.title){
+ this.header.update(this.title);
+ }
+ // this element allows the dialog to be focused for keyboard event
+ this.focusEl = dh.append(el.dom, {tag: 'a', href:'#', cls:'ydlg-focus', tabIndex:'-1'}, true);
+ this.focusEl.swallowEvent('click', true);
+ if(!this.body){
+ /**
+ * The body element
+ * @type YAHOO.ext.Element
+ */
+ this.body = dh.append(el.dom, {tag: 'div', cls:'ydlg-bd'}, true);
+ }
+ // wrap the header for special rendering
+ var hl = dh.insertBefore(this.header.dom, {tag: 'div', cls:'ydlg-hd-left'});
+ var hr = dh.append(hl, {tag: 'div', cls:'ydlg-hd-right'});
+ hr.appendChild(this.header.dom);
+
+ // wrap the body and footer for special rendering
+ this.bwrap = dh.insertBefore(this.body.dom, {tag: 'div', cls:'ydlg-dlg-body'}, true);
+ this.bwrap.dom.appendChild(this.body.dom);
+ if(this.footer) this.bwrap.dom.appendChild(this.footer.dom);
+
+ this.bg = this.el.createChild({
+ tag: 'div', cls:'ydlg-bg',
+ html: '<div class="ydlg-bg-left"><div class="ydlg-bg-right"><div class="ydlg-bg-center">&#160;</div></div></div>'
+ });
+ this.centerBg = getEl(this.bg.dom.firstChild.firstChild.firstChild);
+
+
+ if(this.autoScroll !== false && !this.autoTabs){
+ this.body.setStyle('overflow', 'auto');
+ }
+ if(this.closable !== false){
+ this.el.addClass('ydlg-closable');
+ this.close = dh.append(el.dom, {tag: 'div', cls:'ydlg-close'}, true);
+ this.close.mon('click', this.closeClick, this, true);
+ this.close.addClassOnOver('ydlg-close-over');
+ }
+ if(this.resizable !== false){
+ this.el.addClass('ydlg-resizable');
+ this.resizer = new YAHOO.ext.Resizable(el, {
+ minWidth: this.minWidth || 80,
+ minHeight:this.minHeight || 80,
+ handles: 'all',
+ pinned: true
+ });
+ this.resizer.on('beforeresize', this.beforeResize, this, true);
+ this.resizer.on('resize', this.onResize, this, true);
+ }
+ if(this.draggable !== false){
+ el.addClass('ydlg-draggable');
+ if (!this.proxyDrag) {
+ var dd = new YAHOO.util.DD(el.dom.id, 'WindowDrag');
+ }
+ else {
+ var dd = new YAHOO.util.DDProxy(el.dom.id, 'WindowDrag', {dragElId: this.proxy.id});
+ }
+ dd.setHandleElId(this.header.id);
+ dd.endDrag = this.endMove.createDelegate(this);
+ dd.startDrag = this.startMove.createDelegate(this);
+ dd.onDrag = this.onDrag.createDelegate(this);
+ this.dd = dd;
+ }
+ if(this.modal){
+ this.mask = dh.append(document.body, {tag: 'div', cls:'ydlg-mask'}, true);
+ this.mask.enableDisplayMode('block');
+ this.mask.hide();
+ this.el.addClass('ydlg-modal');
+ }
+ if(this.shadow){
+ this.shadow = el.createProxy({tag: 'div', cls:'ydlg-shadow'});
+ this.shadow.setOpacity(.3);
+ this.shadow.setVisibilityMode(YAHOO.ext.Element.VISIBILITY);
+ this.shadow.setDisplayed('block');
+ this.shadow.hide = this.hideAction;
+ this.shadow.hide();
+ }else{
+ this.shadowOffset = 0;
+ }
+ // adding an iframe shim to FF kills the cursor on the PC, but is needed on the Mac
+ // where it (luckily) does not kill the cursor
+ if(!YAHOO.ext.util.Browser.isGecko || YAHOO.ext.util.Browser.isMac){
+ if(this.shim){
+ this.shim = this.el.createShim();
+ this.shim.hide = this.hideAction;
+ this.shim.hide();
+ }
+ }else{
+ this.shim = false;
+ }
+ if(this.autoTabs){
+ this.initTabs();
+ }
+ this.syncBodyHeight();
+ this.events = {
+ /**
+ * @event keydown
+ * Fires when a key is pressed
+ * @param {YAHOO.ext.BasicDialog} this
+ * @param {YAHOO.ext.EventObject} e
+ */
+ 'keydown' : true,
+ /**
+ * @event move
+ * Fires when this dialog is moved by the user.
+ * @param {YAHOO.ext.BasicDialog} this
+ * @param {Number} x The new page X
+ * @param {Number} y The new page Y
+ */
+ 'move' : true,
+ /**
+ * @event resize
+ * Fires when this dialog is resized by the user.
+ * @param {YAHOO.ext.BasicDialog} this
+ * @param {Number} width The new width
+ * @param {Number} height The new height
+ */
+ 'resize' : true,
+ /**
+ * @event beforehide
+ * Fires before this dialog is hidden.
+ * @param {YAHOO.ext.BasicDialog} this
+ */
+ 'beforehide' : true,
+ /**
+ * @event hide
+ * Fires when this dialog is hidden.
+ * @param {YAHOO.ext.BasicDialog} this
+ */
+ 'hide' : true,
+ /**
+ * @event beforeshow
+ * Fires before this dialog is shown.
+ * @param {YAHOO.ext.BasicDialog} this
+ */
+ 'beforeshow' : true,
+ /**
+ * @event show
+ * Fires when this dialog is shown.
+ * @param {YAHOO.ext.BasicDialog} this
+ */
+ 'show' : true
+ };
+ el.mon('keydown', this.onKeyDown, this, true);
+ el.mon("mousedown", this.toFront, this, true);
+
+ YAHOO.ext.EventManager.onWindowResize(this.adjustViewport, this, true);
+ this.el.hide();
+ YAHOO.ext.DialogManager.register(this);
+};
+
+YAHOO.extendX(YAHOO.ext.BasicDialog, YAHOO.ext.util.Observable, {
+ shadowOffset: 3,
+ minHeight: 80,
+ minWidth: 200,
+ minButtonWidth: 75,
+ defaultButton: null,
+ buttonAlign: 'right',
+ /**
+ * Sets the dialog title.
+ * @param {String} text
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ setTitle : function(text){
+ this.header.update(text);
+ return this;
+ },
+
+ closeClick : function(){
+ this.hide();
+ },
+
+ /**
+ * Reinitializes the tabs component, clearing out old tabs and finding new ones.
+ * @return {YAHOO.ext.TabPanel} tabs The tabs component
+ */
+ initTabs : function(){
+ var tabs = this.getTabs();
+ while(tabs.getTab(0)){
+ tabs.removeTab(0);
+ }
+ var tabEls = YAHOO.util.Dom.getElementsByClassName('ydlg-tab', this.tabTag || 'div', this.el.dom);
+ if(tabEls.length > 0){
+ for(var i = 0, len = tabEls.length; i < len; i++) {
+ var tabEl = tabEls[i];
+ tabs.addTab(YAHOO.util.Dom.generateId(tabEl), tabEl.title);
+ tabEl.title = '';
+ }
+ tabs.activate(0);
+ }
+ return tabs;
+ },
+
+ beforeResize : function(){
+ this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
+ },
+
+ onResize : function(){
+ this.refreshSize();
+ this.syncBodyHeight();
+ this.adjustAssets();
+ this.fireEvent('resize', this, this.size.width, this.size.height);
+ },
+
+ onKeyDown : function(e){
+ if(this.isVisible()){
+ this.fireEvent('keydown', this, e);
+ }
+ },
+
+ /**
+ * Resizes the dialog.
+ * @param {Number} width
+ * @param {Number} height
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ resizeTo : function(width, height){
+ this.el.setSize(width, height);
+ this.size = {width: width, height: height};
+ this.syncBodyHeight();
+ if(this.fixedcenter){
+ this.center();
+ }
+ if(this.isVisible()){
+ this.constrainXY();
+ this.adjustAssets();
+ }
+ this.fireEvent('resize', this, width, height);
+ return this;
+ },
+
+
+ /**
+ * Resizes the dialog to fit the specified content size.
+ * @param {Number} width
+ * @param {Number} height
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ setContentSize : function(w, h){
+ h += this.getHeaderFooterHeight() + this.body.getMargins('tb');
+ w += this.body.getMargins('lr') + this.bwrap.getMargins('lr') + this.centerBg.getPadding('lr');
+ //if(!this.el.isBorderBox()){
+ h += this.body.getPadding('tb') + this.bwrap.getBorderWidth('tb') + this.body.getBorderWidth('tb') + this.el.getBorderWidth('tb');
+ w += this.body.getPadding('lr') + this.bwrap.getBorderWidth('lr') + this.body.getBorderWidth('lr') + this.bwrap.getPadding('lr') + this.el.getBorderWidth('lr');
+ //}
+ if(this.tabs){
+ h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins('tb') + this.tabs.bodyEl.getPadding('tb');
+ w += this.tabs.bodyEl.getMargins('lr') + this.tabs.bodyEl.getPadding('lr');
+ }
+ this.resizeTo(w, h);
+ return this;
+ },
+
+ /**
+ * Adds a key listener for when this dialog is displayed
+ * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
+ * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
+ * @param {Function} fn The function to call
+ * @param {Object} scope (optional) The scope of the function
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ addKeyListener : function(key, fn, scope){
+ var keyCode, shift, ctrl, alt;
+ if(typeof key == 'object' && !(key instanceof Array)){
+ keyCode = key['key'];
+ shift = key['shift'];
+ ctrl = key['ctrl'];
+ alt = key['alt'];
+ }else{
+ keyCode = key;
+ }
+ var handler = function(dlg, e){
+ if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
+ var k = e.getKey();
+ if(keyCode instanceof Array){
+ for(var i = 0, len = keyCode.length; i < len; i++){
+ if(keyCode[i] == k){
+ fn.call(scope || window, dlg, k, e);
+ return;
+ }
+ }
+ }else{
+ if(k == keyCode){
+ fn.call(scope || window, dlg, k, e);
+ }
+ }
+ }
+ };
+ this.on('keydown', handler);
+ return this;
+ },
+
+ /**
+ * Returns the TabPanel component (if autoTabs)
+ * @return {YAHOO.ext.TabPanel}
+ */
+ getTabs : function(){
+ if(!this.tabs){
+ this.el.addClass('ydlg-auto-tabs');
+ this.body.addClass(this.tabPosition == 'bottom' ? 'ytabs-bottom' : 'ytabs-top');
+ this.tabs = new YAHOO.ext.TabPanel(this.body.dom, this.tabPosition == 'bottom');
+ }
+ return this.tabs;
+ },
+
+ /**
+ * Adds a button.
+ * @param {String/Object} config A string becomes the button text, an object is expected to be a valid YAHOO.ext.DomHelper element config
+ * @param {Function} handler The function called when the button is clicked
+ * @param {Object} scope (optional) The scope of the handler function
+ * @return {YAHOO.ext.Button}
+ */
+ addButton : function(config, handler, scope){
+ var dh = YAHOO.ext.DomHelper;
+ if(!this.footer){
+ this.footer = dh.append(this.bwrap.dom, {tag: 'div', cls:'ydlg-ft'}, true);
+ }
+ if(!this.btnContainer){
+ var tb = this.footer.createChild({
+ tag:'div',
+ cls:'ydlg-btns ydlg-btns-'+this.buttonAlign,
+ html:'<table cellspacing="0"><tbody><tr></tr></tbody></table>'
+ });
+ this.btnContainer = tb.dom.firstChild.firstChild.firstChild;
+ }
+ var bconfig = {
+ handler: handler,
+ scope: scope,
+ minWidth: this.minButtonWidth
+ };
+ if(typeof config == 'string'){
+ bconfig.text = config;
+ }else{
+ bconfig.dhconfig = config;
+ }
+ var btn = new YAHOO.ext.Button(
+ this.btnContainer.appendChild(document.createElement('td')),
+ bconfig
+ );
+ this.syncBodyHeight();
+ if(!this.buttons){
+ this.buttons = [];
+ }
+ this.buttons.push(btn);
+ return btn;
+ },
+
+ /**
+ * Sets the default button to be focused when the dialog is displayed
+ * @param {YAHOO.ext.BasicDialog.Button} btn The button object returned by addButton
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ setDefaultButton : function(btn){
+ this.defaultButton = btn;
+ return this;
+ },
+
+ getHeaderFooterHeight : function(safe){
+ var height = 0;
+ if(this.header){
+ height += this.header.getHeight();
+ }
+ if(this.footer){
+ var fm = this.footer.getMargins();
+ height += (this.footer.getHeight()+fm.top+fm.bottom);
+ }
+ height += this.bwrap.getPadding('tb')+this.bwrap.getBorderWidth('tb');
+ height += this.centerBg.getPadding('tb');
+ return height;
+ },
+
+ syncBodyHeight : function(){
+ var height = this.size.height - this.getHeaderFooterHeight(false);
+ this.body.setHeight(height-this.body.getMargins('tb'));
+ if(this.tabs){
+ this.tabs.syncHeight();
+ }
+ var hh = this.header.getHeight();
+ var h = this.size.height-hh;
+ this.centerBg.setHeight(h);
+ this.bwrap.setLeftTop(this.centerBg.getPadding('l'), hh+this.centerBg.getPadding('t'));
+ this.bwrap.setHeight(h-this.centerBg.getPadding('tb'));
+ this.bwrap.setWidth(this.el.getWidth(true)-this.centerBg.getPadding('lr'));
+ this.body.setWidth(this.bwrap.getWidth(true));
+ },
+
+ /**
+ * Restores the previous state of the dialog if YAHOO.ext.state is configured
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ restoreState : function(){
+ var box = YAHOO.ext.state.Manager.get(this.stateId || (this.el.id + '-state'));
+ if(box && box.width){
+ this.xy = [box.x, box.y];
+ this.resizeTo(box.width, box.height);
+ }
+ return this;
+ },
+
+ beforeShow : function(){
+ if(this.fixedcenter) {
+ this.xy = this.el.getCenterXY(true);
+ }
+ if(this.modal){
+ YAHOO.util.Dom.addClass(document.body, 'masked');
+ this.mask.setSize(YAHOO.util.Dom.getDocumentWidth(), YAHOO.util.Dom.getDocumentHeight());
+ this.mask.show();
+ }
+ this.constrainXY();
+ },
+
+ animShow : function(){
+ var b = getEl(this.animateTarget, true).getBox();
+ this.proxy.setSize(b.width, b.height);
+ this.proxy.setLocation(b.x, b.y);
+ this.proxy.show();
+ this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
+ true, .35, this.showEl.createDelegate(this));
+ },
+
+ /**
+ * Shows the dialog.
+ * @param {String/HTMLElement/YAHOO.ext.Element} animateTarget (optional) Reset the animation target
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ show : function(animateTarget){
+ if (this.fireEvent('beforeshow', this) === false){
+ return;
+ }
+ if(this.syncHeightBeforeShow){
+ this.syncBodyHeight();
+ }
+ this.animateTarget = animateTarget || this.animateTarget;
+ if(!this.el.isVisible()){
+ this.beforeShow();
+ if(this.animateTarget){
+ this.animShow();
+ }else{
+ this.showEl();
+ }
+ }
+ return this;
+ },
+
+ showEl : function(){
+ this.proxy.hide();
+ this.el.setXY(this.xy);
+ this.el.show();
+ this.adjustAssets(true);
+ this.toFront();
+ this.focus();
+ this.fireEvent('show', this);
+ },
+
+ focus : function(){
+ if(this.defaultButton){
+ this.defaultButton.focus();
+ }else{
+ this.focusEl.focus();
+ }
+ },
+
+ constrainXY : function(){
+ if(this.constraintoviewport !== false){
+ if(!this.viewSize){
+ if(this.container){
+ var s = this.container.getSize();
+ this.viewSize = [s.width, s.height];
+ }else{
+ this.viewSize = [YAHOO.util.Dom.getViewportWidth(),
+ YAHOO.util.Dom.getViewportHeight()];
+ }
+ }
+ var x = this.xy[0], y = this.xy[1];
+ var w = this.size.width, h = this.size.height;
+ var vw = this.viewSize[0], vh = this.viewSize[1];
+ // only move it if it needs it
+ var moved = false;
+ // first validate right/bottom
+ if(x + w > vw){
+ x = vw - w;
+ moved = true;
+ }
+ if(y + h > vh){
+ y = vh - h;
+ moved = true;
+ }
+ // then make sure top/left isn't negative
+ if(x < 0){
+ x = 0;
+ moved = true;
+ }
+ if(y < 0){
+ y = 0;
+ moved = true;
+ }
+ if(moved){
+ // cache xy
+ this.xy = [x, y];
+ if(this.isVisible()){
+ this.el.setLocation(x, y);
+ this.adjustAssets();
+ }
+ }
+ }
+ },
+
+ onDrag : function(){
+ if(!this.proxyDrag){
+ this.xy = this.el.getXY();
+ this.adjustAssets();
+ }
+ },
+
+ adjustAssets : function(doShow){
+ var x = this.xy[0], y = this.xy[1];
+ var w = this.size.width, h = this.size.height;
+ if(doShow === true){
+ if(this.shadow){
+ this.shadow.show();
+ }
+ if(this.shim){
+ this.shim.show();
+ }
+ }
+ if(this.shadow && this.shadow.isVisible()){
+ this.shadow.setBounds(x + this.shadowOffset, y + this.shadowOffset, w, h);
+ }
+ if(this.shim && this.shim.isVisible()){
+ this.shim.setBounds(x, y, w, h);
+ }
+ },
+
+
+ adjustViewport : function(w, h){
+ if(!w || !h){
+ w = YAHOO.util.Dom.getViewportWidth();
+ h = YAHOO.util.Dom.getViewportHeight();
+ }
+ // cache the size
+ this.viewSize = [w, h];
+ if(this.modal && this.mask.isVisible()){
+ this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
+ this.mask.setSize(YAHOO.util.Dom.getDocumentWidth(), YAHOO.util.Dom.getDocumentHeight());
+ }
+ if(this.isVisible()){
+ this.constrainXY();
+ }
+ },
+
+ /**
+ * Destroys this dialog
+ * @param {Boolean} removeEl (optional) true to remove the element from the DOM
+ */
+ destroy : function(removeEl){
+ YAHOO.ext.EventManager.removeResizeListener(this.adjustViewport, this);
+ if(this.tabs){
+ this.tabs.destroy(removeEl);
+ }
+ if(this.shim){
+ this.shim.remove();
+ }
+ if(this.shadow){
+ this.shadow.remove();
+ }
+ if(this.proxy){
+ this.proxy.remove();
+ }
+ if(this.resizer){
+ this.resizer.destroy();
+ }
+ if(this.close){
+ this.close.removeAllListeners();
+ this.close.remove();
+ }
+ if(this.mask){
+ this.mask.remove();
+ }
+ if(this.dd){
+ this.dd.unreg();
+ }
+ if(this.buttons){
+ for(var i = 0, len = this.buttons.length; i < len; i++){
+ this.buttons[i].destroy();
+ }
+ }
+ this.el.removeAllListeners();
+ if(removeEl === true){
+ this.el.update('');
+ this.el.remove();
+ }
+ YAHOO.ext.DialogManager.unregister(this);
+ },
+
+ startMove : function(){
+ if(this.proxyDrag){
+ this.proxy.show();
+ }
+ if(this.constraintoviewport !== false){
+ this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
+ }
+ },
+
+ endMove : function(){
+ if(!this.proxyDrag){
+ YAHOO.util.DD.prototype.endDrag.apply(this.dd, arguments);
+ }else{
+ YAHOO.util.DDProxy.prototype.endDrag.apply(this.dd, arguments);
+ this.proxy.hide();
+ }
+ this.refreshSize();
+ this.adjustAssets();
+ this.fireEvent('move', this, this.xy[0], this.xy[1])
+ },
+
+ /**
+ * Brings this dialog to the front of any other visible dialogs
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ toFront : function(){
+ YAHOO.ext.DialogManager.bringToFront(this);
+ return this;
+ },
+
+ /**
+ * Sends this dialog to the back (under) of any other visible dialogs
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ toBack : function(){
+ YAHOO.ext.DialogManager.sendToBack(this);
+ return this;
+ },
+
+ /**
+ * Centers this dialog
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ center : function(){
+ var xy = this.el.getCenterXY(true);
+ this.moveTo(xy[0], xy[1]);
+ return this;
+ },
+
+ /**
+ * Moves the dialog to the specified point
+ * @param {Number} x
+ * @param {Number} y
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ moveTo : function(x, y){
+ this.xy = [x,y];
+ if(this.isVisible()){
+ this.el.setXY(this.xy);
+ this.adjustAssets();
+ }
+ return this;
+ },
+
+ /**
+ * Returns true if the dialog is visible
+ * @return {Boolean}
+ */
+ isVisible : function(){
+ return this.el.isVisible();
+ },
+
+ animHide : function(callback){
+ var b = getEl(this.animateTarget, true).getBox();
+ this.proxy.show();
+ this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
+ this.el.hide();
+ this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
+ this.hideEl.createDelegate(this, [callback]));
+ },
+
+ /**
+ * Hides the dialog.
+ * @param {Function} callback (optional) Function to call when the dialog is hidden
+ * @return {YAHOO.ext.BasicDialog} this
+ */
+ hide : function(callback){
+ if (this.fireEvent('beforehide', this) === false)
+ return;
+
+ if(this.shadow){
+ this.shadow.hide();
+ }
+ if(this.shim) {
+ this.shim.hide();
+ }
+ if(this.animateTarget){
+ this.animHide(callback);
+ }else{
+ this.el.hide();
+ this.hideEl(callback);
+ }
+ return this;
+ },
+
+ hideEl : function(callback){
+ this.proxy.hide();
+ if(this.modal){
+ this.mask.hide();
+ YAHOO.util.Dom.removeClass(document.body, 'masked');
+ }
+ this.fireEvent('hide', this);
+ if(typeof callback == 'function'){
+ callback();
+ }
+ },
+
+ hideAction : function(){
+ this.setLeft('-10000px');
+ this.setTop('-10000px');
+ this.setStyle('visibility', 'hidden');
+ },
+
+ refreshSize : function(){
+ this.size = this.el.getSize();
+ this.xy = this.el.getXY();
+ YAHOO.ext.state.Manager.set(this.stateId || this.el.id + '-state', this.el.getBox());
+ },
+
+ setZIndex : function(index){
+ if(this.modal){
+ this.mask.setStyle('z-index', index);
+ }
+ if(this.shim){
+ this.shim.setStyle('z-index', ++index);
+ }
+ if(this.shadow){
+ this.shadow.setStyle('z-index', ++index);
+ }
+ this.el.setStyle('z-index', ++index);
+ if(this.proxy){
+ this.proxy.setStyle('z-index', ++index);
+ }
+ if(this.resizer){
+ this.resizer.proxy.setStyle('z-index', ++index);
+ }
+
+ this.lastZIndex = index;
+ },
+
+ /**
+ * Returns the element for this dialog
+ * @return {YAHOO.ext.Element}
+ */
+ getEl : function(){
+ return this.el;
+ }
+});
+
+/**
+ * @class YAHOO.ext.DialogManager
+ * Provides global access to BasicDialogs that have been created and
+ * support for z-indexing (layering) multiple open dialogs.
+ */
+YAHOO.ext.DialogManager = function(){
+ var list = {};
+ var accessList = [];
+ var front = null;
+
+ var sortDialogs = function(d1, d2){
+ return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
+ };
+
+ var orderDialogs = function(){
+ accessList.sort(sortDialogs);
+ var seed = YAHOO.ext.DialogManager.zseed;
+ for(var i = 0, len = accessList.length; i < len; i++){
+ if(accessList[i]){
+ accessList[i].setZIndex(seed + (i*10));
+ }
+ }
+ };
+
+ return {
+ /**
+ * The starting z-index for BasicDialogs - defaults to 10000
+ * @type Number
+ */
+ zseed : 10000,
+
+
+ register : function(dlg){
+ list[dlg.id] = dlg;
+ accessList.push(dlg);
+ },
+
+ unregister : function(dlg){
+ delete list[dlg.id];
+ if(!accessList.indexOf){
+ for(var i = 0, len = accessList.length; i < len; i++){
+ accessList.splice(i, 1);
+ return;
+ }
+ }else{
+ var i = accessList.indexOf(dlg);
+ if(i != -1){
+ accessList.splice(i, 1);
+ }
+ }
+ },
+
+ /**
+ * Gets a registered dialog by id
+ * @param {String/Object} id The id of the dialog or a dialog
+ * @return {YAHOO.ext.BasicDialog}
+ */
+ get : function(id){
+ return typeof id == 'object' ? id : list[id];
+ },
+
+ /**
+ * Brings the specified dialog to the front
+ * @param {String/Object} dlg The id of the dialog or a dialog
+ * @return {YAHOO.ext.BasicDialog}
+ */
+ bringToFront : function(dlg){
+ dlg = this.get(dlg);
+ if(dlg != front){
+ front = dlg;
+ dlg._lastAccess = new Date().getTime();
+ orderDialogs();
+ }
+ return dlg;
+ },
+
+ /**
+ * Sends the specified dialog to the back
+ * @param {String/Object} dlg The id of the dialog or a dialog
+ * @return {YAHOO.ext.BasicDialog}
+ */
+ sendToBack : function(dlg){
+ dlg = this.get(dlg);
+ dlg._lastAccess = -(new Date().getTime());
+ orderDialogs();
+ return dlg;
+ }
+ };
+}();
+
+/**
+ * @class YAHOO.ext.LayoutDialog
+ * @extends YAHOO.ext.BasicDialog
+ * Dialog which provides adjustments for working with a layout in a Dialog.
+ * Add your neccessary layout config options to the dialogs config.<br>
+ * Example Usage (including a nested layout):
+ * <pre><code> if(!dialog){
+ dialog = new YAHOO.ext.LayoutDialog("download-dlg", {
+ modal: true,
+ width:600,
+ height:450,
+ shadow:true,
+ minWidth:500,
+ minHeight:350,
+ autoTabs:true,
+ proxyDrag:true,
+ // layout config merges with the dialog config
+ center:{
+ tabPosition: 'top',
+ alwaysShowTabs: true
+ }
+ });
+ dialog.addKeyListener(27, dialog.hide, dialog);
+ dialog.setDefaultButton(dialog.addButton('Close', dialog.hide, dialog));
+ dialog.addButton('Build It!', this.getDownload, this);
+
+ // we can even add nested layouts
+ var innerLayout = new YAHOO.ext.BorderLayout('dl-inner', {
+ east: {
+ initialSize: 200,
+ autoScroll:true,
+ split:true
+ },
+ center: {
+ autoScroll:true
+ }
+ });
+ innerLayout.beginUpdate();
+ innerLayout.add('east', new YAHOO.ext.ContentPanel('dl-details'));
+ innerLayout.add('center', new YAHOO.ext.ContentPanel('selection-panel'));
+ innerLayout.endUpdate(true);
+
+ // when doing updates to the top level layout in a dialog, you need to
+ // use dialog.beginUpdate()/endUpdate() instead of layout.beginUpdate()/endUpdate()
+ var layout = dialog.getLayout();
+ dialog.beginUpdate();
+ layout.add('center', new YAHOO.ext.ContentPanel('standard-panel',
+ {title: 'Download the Source', fitToFrame:true}));
+ layout.add('center', new YAHOO.ext.NestedLayoutPanel(innerLayout,
+ {title: 'Build your own yui-ext.js'}));
+ layout.getRegion('center').showPanel(sp);
+ dialog.endUpdate();</code></pre>
+ * @constructor
+ * @param {String/HTMLElement/YAHOO.ext.Element} el The id of or container element
+ * @param {Object} config configuration options
+ */
+YAHOO.ext.LayoutDialog = function(el, config){
+ config.autoTabs = false;
+ YAHOO.ext.LayoutDialog.superclass.constructor.call(this, el, config);
+ this.body.setStyle({overflow:'hidden', position:'relative'});
+ this.layout = new YAHOO.ext.BorderLayout(this.body.dom, config);
+ this.layout.monitorWindowResize = false;
+ this.el.addClass('ydlg-auto-layout');
+ // fix case when center region overwrites center function
+ this.center = YAHOO.ext.BasicDialog.prototype.center;
+ this.on('show', this.layout.layout, this.layout, true);
+};
+YAHOO.extendX(YAHOO.ext.LayoutDialog, YAHOO.ext.BasicDialog, {
+ /**
+ * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
+ * @deprecated
+ */
+ endUpdate : function(){
+ this.layout.endUpdate();
+ },
+ /**
+ * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
+ * @deprecated
+ */
+ beginUpdate : function(){
+ this.layout.beginUpdate();
+ },
+ /**
+ * Get the BorderLayout for this dialog
+ * @return {YAHOO.ext.BorderLayout}
+ */
+ getLayout : function(){
+ return this.layout;
+ },
+ syncBodyHeight : function(){
+ YAHOO.ext.LayoutDialog.superclass.syncBodyHeight.call(this);
+ if(this.layout)this.layout.layout();
+ }
+});
diff --git a/frontend/beta/js/YUI-extensions/widgets/Button.js b/frontend/beta/js/YUI-extensions/widgets/Button.js
new file mode 100644
index 0000000..5bf3dc3
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/Button.js
@@ -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');
+ }
+});
diff --git a/frontend/beta/js/YUI-extensions/widgets/DatePicker.js b/frontend/beta/js/YUI-extensions/widgets/DatePicker.js
new file mode 100644
index 0000000..eb1e06f
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/DatePicker.js
@@ -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;
diff --git a/frontend/beta/js/YUI-extensions/widgets/InlineEditor.js b/frontend/beta/js/YUI-extensions/widgets/InlineEditor.js
new file mode 100644
index 0000000..a498faa
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/InlineEditor.js
@@ -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);
+ }
+});
diff --git a/frontend/beta/js/YUI-extensions/widgets/MessageBox.js b/frontend/beta/js/YUI-extensions/widgets/MessageBox.js
new file mode 100644
index 0000000..01b168d
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/MessageBox.js
@@ -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;
diff --git a/frontend/beta/js/YUI-extensions/widgets/QuickTips.js b/frontend/beta/js/YUI-extensions/widgets/QuickTips.js
new file mode 100644
index 0000000..6658574
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/QuickTips.js
@@ -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
+ }
+}();
diff --git a/frontend/beta/js/YUI-extensions/widgets/Resizable.js b/frontend/beta/js/YUI-extensions/widgets/Resizable.js
new file mode 100644
index 0000000..6944683
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/Resizable.js
@@ -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);
+ }
+};
+
+
+
diff --git a/frontend/beta/js/YUI-extensions/widgets/SplitBar.js b/frontend/beta/js/YUI-extensions/widgets/SplitBar.js
new file mode 100644
index 0000000..855d138
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/SplitBar.js
@@ -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;
diff --git a/frontend/beta/js/YUI-extensions/widgets/TabPanel.js b/frontend/beta/js/YUI-extensions/widgets/TabPanel.js
new file mode 100644
index 0000000..25fd142
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/TabPanel.js
@@ -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};
+ }
+};
diff --git a/frontend/beta/js/YUI-extensions/widgets/TaskPanel.js b/frontend/beta/js/YUI-extensions/widgets/TaskPanel.js
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/TaskPanel.js
diff --git a/frontend/beta/js/YUI-extensions/widgets/TemplateView.js b/frontend/beta/js/YUI-extensions/widgets/TemplateView.js
new file mode 100644
index 0000000..2100205
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/TemplateView.js
@@ -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);
+ }
+ }
+ }
+});
diff --git a/frontend/beta/js/YUI-extensions/widgets/Toolbar.js b/frontend/beta/js/YUI-extensions/widgets/Toolbar.js
new file mode 100644
index 0000000..7c14753
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/widgets/Toolbar.js
@@ -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);
+ }
+ }
+ }
+};