diff --git a/admin-root/hdr.html b/admin-root/hdr.html index f87f6883..a6545846 100644 --- a/admin-root/hdr.html +++ b/admin-root/hdr.html @@ -8,6 +8,7 @@ @ispage smart.html::@ @ispage playlist.html::@ @ispage playlist.html::@ + @ispage playlist.html::@ @ispage playlist.html::@ diff --git a/admin-root/lib-js/Makefile.am b/admin-root/lib-js/Makefile.am index 619c2016..dc3c9442 100644 --- a/admin-root/lib-js/Makefile.am +++ b/admin-root/lib-js/Makefile.am @@ -3,6 +3,6 @@ SUBDIRS=script.aculo.us jslibdir = ${pkgdatadir}/admin-root/lib-js -jslib_DATA = prototype.js -EXTRA_DIST = prototype.js +jslib_DATA = prototype.js rico.js +EXTRA_DIST = prototype.js rico.js diff --git a/admin-root/lib-js/rico.js b/admin-root/lib-js/rico.js new file mode 100644 index 00000000..3400b9f0 --- /dev/null +++ b/admin-root/lib-js/rico.js @@ -0,0 +1,2820 @@ +/** + * + * Copyright 2005 Sabre Airline Solutions + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + **/ + + +//-------------------- rico.js +var Rico = { + Version: '1.1.2', + prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1]) +} + +if((typeof Prototype=='undefined') || Rico.prototypeVersion < 1.3) + throw("Rico requires the Prototype JavaScript framework >= 1.3"); + +Rico.ArrayExtensions = new Array(); + +if (Object.prototype.extend) { + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend; +}else{ + Object.prototype.extend = function(object) { + return Object.extend.apply(this, [this, object]); + } + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend; +} + +if (Array.prototype.push) { + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push; +} + +if (!Array.prototype.remove) { + Array.prototype.remove = function(dx) { + if( isNaN(dx) || dx > this.length ) + return false; + for( var i=0,n=0; i= this.accordionTabs.length) + this.options.onLoadShowTab = 0; + + // set the initial visual state... + for ( var i=0 ; i < this.accordionTabs.length ; i++ ) + { + if (i != this.options.onLoadShowTab){ + this.accordionTabs[i].collapse(); + this.accordionTabs[i].content.style.display = 'none'; + } + } + this.lastExpandedTab = this.accordionTabs[this.options.onLoadShowTab]; + if (this.options.panelHeight == 'auto'){ + var tabToCheck = (this.options.onloadShowTab === 0)? 1 : 0; + var titleBarSize = parseInt(RicoUtil.getElementsComputedStyle(this.accordionTabs[tabToCheck].titleBar, 'height')); + if (isNaN(titleBarSize)) + titleBarSize = this.accordionTabs[tabToCheck].titleBar.offsetHeight; + + var totalTitleBarSize = this.accordionTabs.length * titleBarSize; + var parentHeight = parseInt(RicoUtil.getElementsComputedStyle(this.container.parentNode, 'height')); + if (isNaN(parentHeight)) + parentHeight = this.container.parentNode.offsetHeight; + + this.options.panelHeight = parentHeight - totalTitleBarSize-2; + } + + this.lastExpandedTab.content.style.height = this.options.panelHeight + "px"; + this.lastExpandedTab.showExpanded(); + this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight; + + }, + + setOptions: function(options) { + this.options = { + expandedBg : '#63699c', + hoverBg : '#63699c', + collapsedBg : '#6b79a5', + expandedTextColor : '#ffffff', + expandedFontWeight : 'bold', + hoverTextColor : '#ffffff', + collapsedTextColor : '#ced7ef', + collapsedFontWeight : 'normal', + hoverTextColor : '#ffffff', + borderColor : '#1f669b', + panelHeight : 200, + onHideTab : null, + onShowTab : null, + onLoadShowTab : 0 + } + Object.extend(this.options, options || {}); + }, + + showTabByIndex: function( anIndex, animate ) { + var doAnimate = arguments.length == 1 ? true : animate; + this.showTab( this.accordionTabs[anIndex], doAnimate ); + }, + + showTab: function( accordionTab, animate ) { + if ( this.lastExpandedTab == accordionTab ) + return; + + var doAnimate = arguments.length == 1 ? true : animate; + + if ( this.options.onHideTab ) + this.options.onHideTab(this.lastExpandedTab); + + this.lastExpandedTab.showCollapsed(); + var accordion = this; + var lastExpandedTab = this.lastExpandedTab; + + this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px'; + accordionTab.content.style.display = ''; + + accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight; + + if ( doAnimate ) { + new Rico.Effect.AccordionSize( this.lastExpandedTab.content, + accordionTab.content, + 1, + this.options.panelHeight, + 50, 7, + { complete: function() {accordion.showTabDone(lastExpandedTab)} } ); + this.lastExpandedTab = accordionTab; + } + else { + this.lastExpandedTab.content.style.height = "1px"; + accordionTab.content.style.height = this.options.panelHeight + "px"; + this.lastExpandedTab = accordionTab; + this.showTabDone(lastExpandedTab); + } + }, + + showTabDone: function(collapsedTab) { + collapsedTab.content.style.display = 'none'; + this.lastExpandedTab.showExpanded(); + if ( this.options.onShowTab ) + this.options.onShowTab(this.lastExpandedTab); + }, + + _attachBehaviors: function() { + var panels = this._getDirectChildrenByTag(this.container, 'DIV'); + for ( var i = 0 ; i < panels.length ; i++ ) { + + var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV'); + if ( tabChildren.length != 2 ) + continue; // unexpected + + var tabTitleBar = tabChildren[0]; + var tabContentBox = tabChildren[1]; + this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) ); + } + }, + + _getDirectChildrenByTag: function(e, tagName) { + var kids = new Array(); + var allKids = e.childNodes; + for( var i = 0 ; i < allKids.length ; i++ ) + if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName ) + kids.push(allKids[i]); + return kids; + } + +}; + +Rico.Accordion.Tab = Class.create(); + +Rico.Accordion.Tab.prototype = { + + initialize: function(accordion, titleBar, content) { + this.accordion = accordion; + this.titleBar = titleBar; + this.content = content; + this._attachBehaviors(); + }, + + collapse: function() { + this.showCollapsed(); + this.content.style.height = "1px"; + }, + + showCollapsed: function() { + this.expanded = false; + this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg; + this.titleBar.style.color = this.accordion.options.collapsedTextColor; + this.titleBar.style.fontWeight = this.accordion.options.collapsedFontWeight; + this.content.style.overflow = "hidden"; + }, + + showExpanded: function() { + this.expanded = true; + this.titleBar.style.backgroundColor = this.accordion.options.expandedBg; + this.titleBar.style.color = this.accordion.options.expandedTextColor; + this.content.style.overflow = "auto"; + }, + + titleBarClicked: function(e) { + if ( this.accordion.lastExpandedTab == this ) + return; + this.accordion.showTab(this); + }, + + hover: function(e) { + this.titleBar.style.backgroundColor = this.accordion.options.hoverBg; + this.titleBar.style.color = this.accordion.options.hoverTextColor; + this.titleBar.style.cursor = 'pointer'; + }, + + unhover: function(e) { + if ( this.expanded ) { + this.titleBar.style.backgroundColor = this.accordion.options.expandedBg; + this.titleBar.style.color = this.accordion.options.expandedTextColor; + } + else { + this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg; + this.titleBar.style.color = this.accordion.options.collapsedTextColor; + } + this.titleBar.style.cursor = ''; + }, + + _attachBehaviors: function() { + this.content.style.border = "1px solid " + this.accordion.options.borderColor; + this.content.style.borderTopWidth = "0px"; + this.content.style.borderBottomWidth = "0px"; + this.content.style.margin = "0px"; + + this.titleBar.onclick = this.titleBarClicked.bindAsEventListener(this); + this.titleBar.onmouseover = this.hover.bindAsEventListener(this); + this.titleBar.onmouseout = this.unhover.bindAsEventListener(this); + } + +}; + + +//-------------------- ricoAjaxEngine.js +Rico.AjaxEngine = Class.create(); + +Rico.AjaxEngine.prototype = { + + initialize: function() { + this.ajaxElements = new Array(); + this.ajaxObjects = new Array(); + this.requestURLS = new Array(); + this.options = {}; + }, + + registerAjaxElement: function( anId, anElement ) { + if ( !anElement ) + anElement = $(anId); + this.ajaxElements[anId] = anElement; + }, + + registerAjaxObject: function( anId, anObject ) { + this.ajaxObjects[anId] = anObject; + }, + + registerRequest: function (requestLogicalName, requestURL) { + this.requestURLS[requestLogicalName] = requestURL; + }, + + sendRequest: function(requestName, options) { + // Allow for backwards Compatibility + if ( arguments.length >= 2 ) + if (typeof arguments[1] == 'string') + options = {parameters: this._createQueryString(arguments, 1)}; + this.sendRequestWithData(requestName, null, options); + }, + + sendRequestWithData: function(requestName, xmlDocument, options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + // Allow for backwards Compatibility + if ( arguments.length >= 3 ) + if (typeof arguments[2] == 'string') + options.parameters = this._createQueryString(arguments, 2); + + new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument)); + }, + + sendRequestAndUpdate: function(requestName,container,options) { + // Allow for backwards Compatibility + if ( arguments.length >= 3 ) + if (typeof arguments[2] == 'string') + options.parameters = this._createQueryString(arguments, 2); + + this.sendRequestWithDataAndUpdate(requestName, null, container, options); + }, + + sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + // Allow for backwards Compatibility + if ( arguments.length >= 4 ) + if (typeof arguments[3] == 'string') + options.parameters = this._createQueryString(arguments, 3); + + var updaterOptions = this._requestOptions(options,xmlDocument); + + new Ajax.Updater(container, requestURL, updaterOptions); + }, + + // Private -- not part of intended engine API -------------------------------------------------------------------- + + _requestOptions: function(options,xmlDoc) { + var requestHeaders = ['X-Rico-Version', Rico.Version ]; + var sendMethod = 'post'; + if ( xmlDoc == null ) + if (Rico.prototypeVersion < 1.4) + requestHeaders.push( 'Content-type', 'text/xml' ); + else + sendMethod = 'get'; + (!options) ? options = {} : ''; + + if (!options._RicoOptionsProcessed){ + // Check and keep any user onComplete functions + if (options.onComplete) + options.onRicoComplete = options.onComplete; + // Fix onComplete + if (options.overrideOnComplete) + options.onComplete = options.overrideOnComplete; + else + options.onComplete = this._onRequestComplete.bind(this); + options._RicoOptionsProcessed = true; + } + + // Set the default options and extend with any user options + this.options = { + requestHeaders: requestHeaders, + parameters: options.parameters, + postBody: xmlDoc, + method: sendMethod, + onComplete: options.onComplete + }; + // Set any user options: + Object.extend(this.options, options); + return this.options; + }, + + _createQueryString: function( theArgs, offset ) { + var queryString = "" + for ( var i = offset ; i < theArgs.length ; i++ ) { + if ( i != offset ) + queryString += "&"; + + var anArg = theArgs[i]; + + if ( anArg.name != undefined && anArg.value != undefined ) { + queryString += anArg.name + "=" + escape(anArg.value); + } + else { + var ePos = anArg.indexOf('='); + var argName = anArg.substring( 0, ePos ); + var argValue = anArg.substring( ePos + 1 ); + queryString += argName + "=" + escape(argValue); + } + } + return queryString; + }, + + _onRequestComplete : function(request) { + if(!request) + return; + // User can set an onFailure option - which will be called by prototype + if (request.status != 200) + return; + + var response = request.responseXML.getElementsByTagName("ajax-response"); + if (response == null || response.length != 1) + return; + this._processAjaxResponse( response[0].childNodes ); + + // Check if user has set a onComplete function + var onRicoComplete = this.options.onRicoComplete; + if (onRicoComplete != null) + onRicoComplete(); + }, + + _processAjaxResponse: function( xmlResponseElements ) { + for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) { + var responseElement = xmlResponseElements[i]; + + // only process nodes of type element..... + if ( responseElement.nodeType != 1 ) + continue; + + var responseType = responseElement.getAttribute("type"); + var responseId = responseElement.getAttribute("id"); + + if ( responseType == "object" ) + this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement ); + else if ( responseType == "element" ) + this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement ); + else + alert('unrecognized AjaxResponse type : ' + responseType ); + } + }, + + _processAjaxObjectUpdate: function( ajaxObject, responseElement ) { + ajaxObject.ajaxUpdate( responseElement ); + }, + + _processAjaxElementUpdate: function( ajaxElement, responseElement ) { + ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement); + } + +} + +var ajaxEngine = new Rico.AjaxEngine(); + + +//-------------------- ricoColor.js +Rico.Color = Class.create(); + +Rico.Color.prototype = { + + initialize: function(red, green, blue) { + this.rgb = { r: red, g : green, b : blue }; + }, + + setRed: function(r) { + this.rgb.r = r; + }, + + setGreen: function(g) { + this.rgb.g = g; + }, + + setBlue: function(b) { + this.rgb.b = b; + }, + + setHue: function(h) { + + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.h = h; + + // convert back to RGB... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setSaturation: function(s) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.s = s; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setBrightness: function(b) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.b = b; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); + }, + + darken: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); + }, + + brighten: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); + }, + + blend: function(other) { + this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); + this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); + this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); + }, + + isBright: function() { + var hsb = this.asHSB(); + return this.asHSB().b > 0.5; + }, + + isDark: function() { + return ! this.isBright(); + }, + + asRGB: function() { + return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; + }, + + asHex: function() { + return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); + }, + + asHSB: function() { + return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); + }, + + toString: function() { + return this.asHex(); + } + +}; + +Rico.Color.createFromHex = function(hexCode) { + if(hexCode.length==4) { + var shortHexCode = hexCode; + var hexCode = '#'; + for(var i=1;i<4;i++) hexCode += (shortHexCode.charAt(i) + +shortHexCode.charAt(i)); + } + if ( hexCode.indexOf('#') == 0 ) + hexCode = hexCode.substring(1); + var red = hexCode.substring(0,2); + var green = hexCode.substring(2,4); + var blue = hexCode.substring(4,6); + return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); +} + +/** + * Factory method for creating a color from the background of + * an HTML element. + */ +Rico.Color.createColorFromBackground = function(elem) { + + var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color"); + + if ( actualColor == "transparent" && elem.parentNode ) + return Rico.Color.createColorFromBackground(elem.parentNode); + + if ( actualColor == null ) + return new Rico.Color(255,255,255); + + if ( actualColor.indexOf("rgb(") == 0 ) { + var colors = actualColor.substring(4, actualColor.length - 1 ); + var colorArray = colors.split(","); + return new Rico.Color( parseInt( colorArray[0] ), + parseInt( colorArray[1] ), + parseInt( colorArray[2] ) ); + + } + else if ( actualColor.indexOf("#") == 0 ) { + return Rico.Color.createFromHex(actualColor); + } + else + return new Rico.Color(255,255,255); +} + +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { + + var red = 0; + var green = 0; + var blue = 0; + + if (saturation == 0) { + red = parseInt(brightness * 255.0 + 0.5); + green = red; + blue = red; + } + else { + var h = (hue - Math.floor(hue)) * 6.0; + var f = h - Math.floor(h); + var p = brightness * (1.0 - saturation); + var q = brightness * (1.0 - saturation * f); + var t = brightness * (1.0 - (saturation * (1.0 - f))); + + switch (parseInt(h)) { + case 0: + red = (brightness * 255.0 + 0.5); + green = (t * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 1: + red = (q * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 2: + red = (p * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (t * 255.0 + 0.5); + break; + case 3: + red = (p * 255.0 + 0.5); + green = (q * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 4: + red = (t * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 5: + red = (brightness * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (q * 255.0 + 0.5); + break; + } + } + + return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; +} + +Rico.Color.RGBtoHSB = function(r, g, b) { + + var hue; + var saturation; + var brightness; + + var cmax = (r > g) ? r : g; + if (b > cmax) + cmax = b; + + var cmin = (r < g) ? r : g; + if (b < cmin) + cmin = b; + + brightness = cmax / 255.0; + if (cmax != 0) + saturation = (cmax - cmin)/cmax; + else + saturation = 0; + + if (saturation == 0) + hue = 0; + else { + var redc = (cmax - r)/(cmax - cmin); + var greenc = (cmax - g)/(cmax - cmin); + var bluec = (cmax - b)/(cmax - cmin); + + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + + hue = hue / 6.0; + if (hue < 0) + hue = hue + 1.0; + } + + return { h : hue, s : saturation, b : brightness }; +} + + +//-------------------- ricoCorner.js +Rico.Corner = { + + round: function(e, options) { + var e = $(e); + this._setOptions(options); + + var color = this.options.color; + if ( this.options.color == "fromElement" ) + color = this._background(e); + + var bgColor = this.options.bgColor; + if ( this.options.bgColor == "fromParent" ) + bgColor = this._background(e.offsetParent); + + this._roundCornersImpl(e, color, bgColor); + }, + + _roundCornersImpl: function(e, color, bgColor) { + if(this.options.border) + this._renderBorder(e,bgColor); + if(this._isTopRounded()) + this._roundTopCorners(e,color,bgColor); + if(this._isBottomRounded()) + this._roundBottomCorners(e,color,bgColor); + }, + + _renderBorder: function(el,bgColor) { + var borderValue = "1px solid " + this._borderColor(bgColor); + var borderL = "border-left: " + borderValue; + var borderR = "border-right: " + borderValue; + var style = "style='" + borderL + ";" + borderR + "'"; + el.innerHTML = "
" + el.innerHTML + "
" + }, + + _roundTopCorners: function(el, color, bgColor) { + var corner = this._createCorner(bgColor); + for(var i=0 ; i < this.options.numSlices ; i++ ) + corner.appendChild(this._createCornerSlice(color,bgColor,i,"top")); + el.style.paddingTop = 0; + el.insertBefore(corner,el.firstChild); + }, + + _roundBottomCorners: function(el, color, bgColor) { + var corner = this._createCorner(bgColor); + for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) + corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom")); + el.style.paddingBottom = 0; + el.appendChild(corner); + }, + + _createCorner: function(bgColor) { + var corner = document.createElement("div"); + corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor); + return corner; + }, + + _createCornerSlice: function(color,bgColor, n, position) { + var slice = document.createElement("span"); + + var inStyle = slice.style; + inStyle.backgroundColor = color; + inStyle.display = "block"; + inStyle.height = "1px"; + inStyle.overflow = "hidden"; + inStyle.fontSize = "1px"; + + var borderColor = this._borderColor(color,bgColor); + if ( this.options.border && n == 0 ) { + inStyle.borderTopStyle = "solid"; + inStyle.borderTopWidth = "1px"; + inStyle.borderLeftWidth = "0px"; + inStyle.borderRightWidth = "0px"; + inStyle.borderBottomWidth = "0px"; + inStyle.height = "0px"; // assumes css compliant box model + inStyle.borderColor = borderColor; + } + else if(borderColor) { + inStyle.borderColor = borderColor; + inStyle.borderStyle = "solid"; + inStyle.borderWidth = "0px 1px"; + } + + if ( !this.options.compact && (n == (this.options.numSlices-1)) ) + inStyle.height = "2px"; + + this._setMargin(slice, n, position); + this._setBorder(slice, n, position); + return slice; + }, + + _setOptions: function(options) { + this.options = { + corners : "all", + color : "fromElement", + bgColor : "fromParent", + blend : true, + border : false, + compact : false + } + Object.extend(this.options, options || {}); + + this.options.numSlices = this.options.compact ? 2 : 4; + if ( this._isTransparent() ) + this.options.blend = false; + }, + + _whichSideTop: function() { + if ( this._hasString(this.options.corners, "all", "top") ) + return ""; + + if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) + return ""; + + if (this.options.corners.indexOf("tl") >= 0) + return "left"; + else if (this.options.corners.indexOf("tr") >= 0) + return "right"; + return ""; + }, + + _whichSideBottom: function() { + if ( this._hasString(this.options.corners, "all", "bottom") ) + return ""; + + if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) + return ""; + + if(this.options.corners.indexOf("bl") >=0) + return "left"; + else if(this.options.corners.indexOf("br")>=0) + return "right"; + return ""; + }, + + _borderColor : function(color,bgColor) { + if ( color == "transparent" ) + return bgColor; + else if ( this.options.border ) + return this.options.border; + else if ( this.options.blend ) + return this._blend( bgColor, color ); + else + return ""; + }, + + + _setMargin: function(el, n, corners) { + var marginSize = this._marginSize(n); + var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); + + if ( whichSide == "left" ) { + el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px"; + } + else if ( whichSide == "right" ) { + el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px"; + } + else { + el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px"; + } + }, + + _setBorder: function(el,n,corners) { + var borderSize = this._borderSize(n); + var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); + if ( whichSide == "left" ) { + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px"; + } + else if ( whichSide == "right" ) { + el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px"; + } + else { + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px"; + } + if (this.options.border != false) + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px"; + }, + + _marginSize: function(n) { + if ( this._isTransparent() ) + return 0; + + var marginSizes = [ 5, 3, 2, 1 ]; + var blendedMarginSizes = [ 3, 2, 1, 0 ]; + var compactMarginSizes = [ 2, 1 ]; + var smBlendedMarginSizes = [ 1, 0 ]; + + if ( this.options.compact && this.options.blend ) + return smBlendedMarginSizes[n]; + else if ( this.options.compact ) + return compactMarginSizes[n]; + else if ( this.options.blend ) + return blendedMarginSizes[n]; + else + return marginSizes[n]; + }, + + _borderSize: function(n) { + var transparentBorderSizes = [ 5, 3, 2, 1 ]; + var blendedBorderSizes = [ 2, 1, 1, 1 ]; + var compactBorderSizes = [ 1, 0 ]; + var actualBorderSizes = [ 0, 2, 0, 0 ]; + + if ( this.options.compact && (this.options.blend || this._isTransparent()) ) + return 1; + else if ( this.options.compact ) + return compactBorderSizes[n]; + else if ( this.options.blend ) + return blendedBorderSizes[n]; + else if ( this.options.border ) + return actualBorderSizes[n]; + else if ( this._isTransparent() ) + return transparentBorderSizes[n]; + return 0; + }, + + _hasString: function(str) { for(var i=1 ; i= 0) return true; return false; }, + _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; }, + _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } }, + _isTransparent: function() { return this.options.color == "transparent"; }, + _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); }, + _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); }, + _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; } +} + + +//-------------------- ricoDragAndDrop.js +Rico.DragAndDrop = Class.create(); + +Rico.DragAndDrop.prototype = { + + initialize: function() { + this.dropZones = new Array(); + this.draggables = new Array(); + this.currentDragObjects = new Array(); + this.dragElement = null; + this.lastSelectedDraggable = null; + this.currentDragObjectVisible = false; + this.interestedInMotionEvents = false; + this._mouseDown = this._mouseDownHandler.bindAsEventListener(this); + this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this); + this._mouseUp = this._mouseUpHandler.bindAsEventListener(this); + }, + + registerDropZone: function(aDropZone) { + this.dropZones[ this.dropZones.length ] = aDropZone; + }, + + deregisterDropZone: function(aDropZone) { + var newDropZones = new Array(); + var j = 0; + for ( var i = 0 ; i < this.dropZones.length ; i++ ) { + if ( this.dropZones[i] != aDropZone ) + newDropZones[j++] = this.dropZones[i]; + } + + this.dropZones = newDropZones; + }, + + clearDropZones: function() { + this.dropZones = new Array(); + }, + + registerDraggable: function( aDraggable ) { + this.draggables[ this.draggables.length ] = aDraggable; + this._addMouseDownHandler( aDraggable ); + }, + + clearSelection: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].deselect(); + this.currentDragObjects = new Array(); + this.lastSelectedDraggable = null; + }, + + hasSelection: function() { + return this.currentDragObjects.length > 0; + }, + + setStartDragFromElement: function( e, mouseDownElement ) { + this.origPos = RicoUtil.toDocumentPosition(mouseDownElement); + this.startx = e.screenX - this.origPos.x + this.starty = e.screenY - this.origPos.y + //this.startComponentX = e.layerX ? e.layerX : e.offsetX; + //this.startComponentY = e.layerY ? e.layerY : e.offsetY; + //this.adjustedForDraggableSize = false; + + this.interestedInMotionEvents = this.hasSelection(); + this._terminateEvent(e); + }, + + updateSelection: function( draggable, extendSelection ) { + if ( ! extendSelection ) + this.clearSelection(); + + if ( draggable.isSelected() ) { + this.currentDragObjects.removeItem(draggable); + draggable.deselect(); + if ( draggable == this.lastSelectedDraggable ) + this.lastSelectedDraggable = null; + } + else { + this.currentDragObjects[ this.currentDragObjects.length ] = draggable; + draggable.select(); + this.lastSelectedDraggable = draggable; + } + }, + + _mouseDownHandler: function(e) { + if ( arguments.length == 0 ) + e = event; + + // if not button 1 ignore it... + var nsEvent = e.which != undefined; + if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1)) + return; + + var eventTarget = e.target ? e.target : e.srcElement; + var draggableObject = eventTarget.draggable; + + var candidate = eventTarget; + while (draggableObject == null && candidate.parentNode) { + candidate = candidate.parentNode; + draggableObject = candidate.draggable; + } + + if ( draggableObject == null ) + return; + + this.updateSelection( draggableObject, e.ctrlKey ); + + // clear the drop zones postion cache... + if ( this.hasSelection() ) + for ( var i = 0 ; i < this.dropZones.length ; i++ ) + this.dropZones[i].clearPositionCache(); + + this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() ); + }, + + + _mouseMoveHandler: function(e) { + var nsEvent = e.which != undefined; + if ( !this.interestedInMotionEvents ) { + //this._terminateEvent(e); + return; + } + + if ( ! this.hasSelection() ) + return; + + if ( ! this.currentDragObjectVisible ) + this._startDrag(e); + + if ( !this.activatedDropZones ) + this._activateRegisteredDropZones(); + + //if ( !this.adjustedForDraggableSize ) + // this._adjustForDraggableSize(e); + + this._updateDraggableLocation(e); + this._updateDropZonesHover(e); + + this._terminateEvent(e); + }, + + _makeDraggableObjectVisible: function(e) + { + if ( !this.hasSelection() ) + return; + + var dragElement; + if ( this.currentDragObjects.length > 1 ) + dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects); + else + dragElement = this.currentDragObjects[0].getSingleObjectDragGUI(); + + // go ahead and absolute position it... + if ( RicoUtil.getElementsComputedStyle(dragElement, "position") != "absolute" ) + dragElement.style.position = "absolute"; + + // need to parent him into the document... + if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 ) + document.body.appendChild(dragElement); + + this.dragElement = dragElement; + this._updateDraggableLocation(e); + + this.currentDragObjectVisible = true; + }, + + /** + _adjustForDraggableSize: function(e) { + var dragElementWidth = this.dragElement.offsetWidth; + var dragElementHeight = this.dragElement.offsetHeight; + if ( this.startComponentX > dragElementWidth ) + this.startx -= this.startComponentX - dragElementWidth + 2; + if ( e.offsetY ) { + if ( this.startComponentY > dragElementHeight ) + this.starty -= this.startComponentY - dragElementHeight + 2; + } + this.adjustedForDraggableSize = true; + }, + **/ + + _leftOffset: function(e) { + return e.offsetX ? document.body.scrollLeft : 0 + }, + + _topOffset: function(e) { + return e.offsetY ? document.body.scrollTop:0 + }, + + + _updateDraggableLocation: function(e) { + var dragObjectStyle = this.dragElement.style; + dragObjectStyle.left = (e.screenX + this._leftOffset(e) - this.startx) + "px" + dragObjectStyle.top = (e.screenY + this._topOffset(e) - this.starty) + "px"; + }, + + _updateDropZonesHover: function(e) { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) ) + this.dropZones[i].hideHover(); + } + + for ( var i = 0 ; i < n ; i++ ) { + if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) { + if ( this.dropZones[i].canAccept(this.currentDragObjects) ) + this.dropZones[i].showHover(); + } + } + }, + + _startDrag: function(e) { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].startDrag(); + + this._makeDraggableObjectVisible(e); + }, + + _mouseUpHandler: function(e) { + if ( ! this.hasSelection() ) + return; + + var nsEvent = e.which != undefined; + if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1)) + return; + + this.interestedInMotionEvents = false; + + if ( this.dragElement == null ) { + this._terminateEvent(e); + return; + } + + if ( this._placeDraggableInDropZone(e) ) + this._completeDropOperation(e); + else { + this._terminateEvent(e); + new Rico.Effect.Position( this.dragElement, + this.origPos.x, + this.origPos.y, + 200, + 20, + { complete : this._doCancelDragProcessing.bind(this) } ); + } + + Event.stopObserving(document.body, "mousemove", this._mouseMove); + Event.stopObserving(document.body, "mouseup", this._mouseUp); + }, + + _retTrue: function () { + return true; + }, + + _completeDropOperation: function(e) { + if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) { + if ( this.dragElement.parentNode != null ) + this.dragElement.parentNode.removeChild(this.dragElement); + } + + this._deactivateRegisteredDropZones(); + this._endDrag(); + this.clearSelection(); + this.dragElement = null; + this.currentDragObjectVisible = false; + this._terminateEvent(e); + }, + + _doCancelDragProcessing: function() { + this._cancelDrag(); + + if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement) + if ( this.dragElement.parentNode != null ) + this.dragElement.parentNode.removeChild(this.dragElement); + + + this._deactivateRegisteredDropZones(); + this.dragElement = null; + this.currentDragObjectVisible = false; + }, + + _placeDraggableInDropZone: function(e) { + var foundDropZone = false; + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) { + if ( this.dropZones[i].canAccept(this.currentDragObjects) ) { + this.dropZones[i].hideHover(); + this.dropZones[i].accept(this.currentDragObjects); + foundDropZone = true; + break; + } + } + } + + return foundDropZone; + }, + + _cancelDrag: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].cancelDrag(); + }, + + _endDrag: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].endDrag(); + }, + + _mousePointInDropZone: function( e, dropZone ) { + + var absoluteRect = dropZone.getAbsoluteRect(); + + return e.clientX > absoluteRect.left + this._leftOffset(e) && + e.clientX < absoluteRect.right + this._leftOffset(e) && + e.clientY > absoluteRect.top + this._topOffset(e) && + e.clientY < absoluteRect.bottom + this._topOffset(e); + }, + + _addMouseDownHandler: function( aDraggable ) + { + htmlElement = aDraggable.getMouseDownHTMLElement(); + if ( htmlElement != null ) { + htmlElement.draggable = aDraggable; + Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this)); + Event.observe(htmlElement, "mousedown", this._mouseDown); + } + }, + + _activateRegisteredDropZones: function() { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + var dropZone = this.dropZones[i]; + if ( dropZone.canAccept(this.currentDragObjects) ) + dropZone.activate(); + } + + this.activatedDropZones = true; + }, + + _deactivateRegisteredDropZones: function() { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) + this.dropZones[i].deactivate(); + this.activatedDropZones = false; + }, + + _onmousedown: function () { + Event.observe(document.body, "mousemove", this._mouseMove); + Event.observe(document.body, "mouseup", this._mouseUp); + }, + + _terminateEvent: function(e) { + if ( e.stopPropagation != undefined ) + e.stopPropagation(); + else if ( e.cancelBubble != undefined ) + e.cancelBubble = true; + + if ( e.preventDefault != undefined ) + e.preventDefault(); + else + e.returnValue = false; + }, + + + initializeEventHandlers: function() { + if ( typeof document.implementation != "undefined" && + document.implementation.hasFeature("HTML", "1.0") && + document.implementation.hasFeature("Events", "2.0") && + document.implementation.hasFeature("CSS", "2.0") ) { + document.addEventListener("mouseup", this._mouseUpHandler.bindAsEventListener(this), false); + document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false); + } + else { + document.attachEvent( "onmouseup", this._mouseUpHandler.bindAsEventListener(this) ); + document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) ); + } + } + } + + var dndMgr = new Rico.DragAndDrop(); + dndMgr.initializeEventHandlers(); + + +//-------------------- ricoDraggable.js +Rico.Draggable = Class.create(); + +Rico.Draggable.prototype = { + + initialize: function( type, htmlElement ) { + this.type = type; + this.htmlElement = $(htmlElement); + this.selected = false; + }, + + /** + * Returns the HTML element that should have a mouse down event + * added to it in order to initiate a drag operation + * + **/ + getMouseDownHTMLElement: function() { + return this.htmlElement; + }, + + select: function() { + this.selected = true; + + if ( this.showingSelected ) + return; + + var htmlElement = this.getMouseDownHTMLElement(); + + var color = Rico.Color.createColorFromBackground(htmlElement); + color.isBright() ? color.darken(0.033) : color.brighten(0.033); + + this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color"); + htmlElement.style.backgroundColor = color.asHex(); + this.showingSelected = true; + }, + + deselect: function() { + this.selected = false; + if ( !this.showingSelected ) + return; + + var htmlElement = this.getMouseDownHTMLElement(); + + htmlElement.style.backgroundColor = this.saveBackground; + this.showingSelected = false; + }, + + isSelected: function() { + return this.selected; + }, + + startDrag: function() { + }, + + cancelDrag: function() { + }, + + endDrag: function() { + }, + + getSingleObjectDragGUI: function() { + return this.htmlElement; + }, + + getMultiObjectDragGUI: function( draggables ) { + return this.htmlElement; + }, + + getDroppedGUI: function() { + return this.htmlElement; + }, + + toString: function() { + return this.type + ":" + this.htmlElement + ":"; + } + +} + + +//-------------------- ricoDropzone.js +Rico.Dropzone = Class.create(); + +Rico.Dropzone.prototype = { + + initialize: function( htmlElement ) { + this.htmlElement = $(htmlElement); + this.absoluteRect = null; + }, + + getHTMLElement: function() { + return this.htmlElement; + }, + + clearPositionCache: function() { + this.absoluteRect = null; + }, + + getAbsoluteRect: function() { + if ( this.absoluteRect == null ) { + var htmlElement = this.getHTMLElement(); + var pos = RicoUtil.toViewportPosition(htmlElement); + + this.absoluteRect = { + top: pos.y, + left: pos.x, + bottom: pos.y + htmlElement.offsetHeight, + right: pos.x + htmlElement.offsetWidth + }; + } + return this.absoluteRect; + }, + + activate: function() { + var htmlElement = this.getHTMLElement(); + if (htmlElement == null || this.showingActive) + return; + + this.showingActive = true; + this.saveBackgroundColor = htmlElement.style.backgroundColor; + + var fallbackColor = "#ffea84"; + var currentColor = Rico.Color.createColorFromBackground(htmlElement); + if ( currentColor == null ) + htmlElement.style.backgroundColor = fallbackColor; + else { + currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2); + htmlElement.style.backgroundColor = currentColor.asHex(); + } + }, + + deactivate: function() { + var htmlElement = this.getHTMLElement(); + if (htmlElement == null || !this.showingActive) + return; + + htmlElement.style.backgroundColor = this.saveBackgroundColor; + this.showingActive = false; + this.saveBackgroundColor = null; + }, + + showHover: function() { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null || this.showingHover ) + return; + + this.saveBorderWidth = htmlElement.style.borderWidth; + this.saveBorderStyle = htmlElement.style.borderStyle; + this.saveBorderColor = htmlElement.style.borderColor; + + this.showingHover = true; + htmlElement.style.borderWidth = "1px"; + htmlElement.style.borderStyle = "solid"; + //htmlElement.style.borderColor = "#ff9900"; + htmlElement.style.borderColor = "#ffff00"; + }, + + hideHover: function() { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null || !this.showingHover ) + return; + + htmlElement.style.borderWidth = this.saveBorderWidth; + htmlElement.style.borderStyle = this.saveBorderStyle; + htmlElement.style.borderColor = this.saveBorderColor; + this.showingHover = false; + }, + + canAccept: function(draggableObjects) { + return true; + }, + + accept: function(draggableObjects) { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null ) + return; + + n = draggableObjects.length; + for ( var i = 0 ; i < n ; i++ ) + { + var theGUI = draggableObjects[i].getDroppedGUI(); + if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" ) + { + theGUI.style.position = "static"; + theGUI.style.top = ""; + theGUI.style.top = ""; + } + htmlElement.appendChild(theGUI); + } + } +} + + +//-------------------- ricoEffects.js + +Rico.Effect = {}; + +Rico.Effect.SizeAndPosition = Class.create(); +Rico.Effect.SizeAndPosition.prototype = { + + initialize: function(element, x, y, w, h, duration, steps, options) { + this.element = $(element); + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.duration = duration; + this.steps = steps; + this.options = arguments[7] || {}; + + this.sizeAndPosition(); + }, + + sizeAndPosition: function() { + if (this.isFinished()) { + if(this.options.complete) this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + + // Get original values: x,y = top left corner; w,h = width height + var currentX = this.element.offsetLeft; + var currentY = this.element.offsetTop; + var currentW = this.element.offsetWidth; + var currentH = this.element.offsetHeight; + + // If values not set, or zero, we do not modify them, and take original as final as well + this.x = (this.x) ? this.x : currentX; + this.y = (this.y) ? this.y : currentY; + this.w = (this.w) ? this.w : currentW; + this.h = (this.h) ? this.h : currentH; + + // how much do we need to modify our values for each step? + var difX = this.steps > 0 ? (this.x - currentX)/this.steps : 0; + var difY = this.steps > 0 ? (this.y - currentY)/this.steps : 0; + var difW = this.steps > 0 ? (this.w - currentW)/this.steps : 0; + var difH = this.steps > 0 ? (this.h - currentH)/this.steps : 0; + + this.moveBy(difX, difY); + this.resizeBy(difW, difH); + + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration); + }, + + isFinished: function() { + return this.steps <= 0; + }, + + moveBy: function( difX, difY ) { + var currentLeft = this.element.offsetLeft; + var currentTop = this.element.offsetTop; + var intDifX = parseInt(difX); + var intDifY = parseInt(difY); + + var style = this.element.style; + if ( intDifX != 0 ) + style.left = (currentLeft + intDifX) + "px"; + if ( intDifY != 0 ) + style.top = (currentTop + intDifY) + "px"; + }, + + resizeBy: function( difW, difH ) { + var currentWidth = this.element.offsetWidth; + var currentHeight = this.element.offsetHeight; + var intDifW = parseInt(difW); + var intDifH = parseInt(difH); + + var style = this.element.style; + if ( intDifW != 0 ) + style.width = (currentWidth + intDifW) + "px"; + if ( intDifH != 0 ) + style.height = (currentHeight + intDifH) + "px"; + } +} + +Rico.Effect.Size = Class.create(); +Rico.Effect.Size.prototype = { + + initialize: function(element, w, h, duration, steps, options) { + new Rico.Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options); + } +} + +Rico.Effect.Position = Class.create(); +Rico.Effect.Position.prototype = { + + initialize: function(element, x, y, duration, steps, options) { + new Rico.Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options); + } +} + +Rico.Effect.Round = Class.create(); +Rico.Effect.Round.prototype = { + + initialize: function(tagName, className, options) { + var elements = document.getElementsByTagAndClassName(tagName,className); + for ( var i = 0 ; i < elements.length ; i++ ) + Rico.Corner.round( elements[i], options ); + } +}; + +Rico.Effect.FadeTo = Class.create(); +Rico.Effect.FadeTo.prototype = { + + initialize: function( element, opacity, duration, steps, options) { + this.element = $(element); + this.opacity = opacity; + this.duration = duration; + this.steps = steps; + this.options = arguments[4] || {}; + this.fadeTo(); + }, + + fadeTo: function() { + if (this.isFinished()) { + if(this.options.complete) this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + var currentOpacity = this.getElementOpacity(); + var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0; + + this.changeOpacityBy(delta); + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.fadeTo.bind(this), stepDuration); + }, + + changeOpacityBy: function(v) { + var currentOpacity = this.getElementOpacity(); + var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1)); + this.element.ricoOpacity = newOpacity; + + this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")"; + this.element.style.opacity = newOpacity; /*//*/; + }, + + isFinished: function() { + return this.steps <= 0; + }, + + getElementOpacity: function() { + if ( this.element.ricoOpacity == undefined ) { + var opacity = RicoUtil.getElementsComputedStyle(this.element, 'opacity'); + this.element.ricoOpacity = opacity != undefined ? opacity : 1.0; + } + return parseFloat(this.element.ricoOpacity); + } +} + +Rico.Effect.AccordionSize = Class.create(); + +Rico.Effect.AccordionSize.prototype = { + + initialize: function(e1, e2, start, end, duration, steps, options) { + this.e1 = $(e1); + this.e2 = $(e2); + this.start = start; + this.end = end; + this.duration = duration; + this.steps = steps; + this.options = arguments[6] || {}; + + this.accordionSize(); + }, + + accordionSize: function() { + + if (this.isFinished()) { + // just in case there are round errors or such... + this.e1.style.height = this.start + "px"; + this.e2.style.height = this.end + "px"; + + if(this.options.complete) + this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + + var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0; + this.resizeBy(diff); + + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.accordionSize.bind(this), stepDuration); + }, + + isFinished: function() { + return this.steps <= 0; + }, + + resizeBy: function(diff) { + var h1Height = this.e1.offsetHeight; + var h2Height = this.e2.offsetHeight; + var intDiff = parseInt(diff); + if ( diff != 0 ) { + this.e1.style.height = (h1Height - intDiff) + "px"; + this.e2.style.height = (h2Height + intDiff) + "px"; + } + } + +}; + + +//-------------------- ricoLiveGrid.js +// Rico.LiveGridMetaData ----------------------------------------------------- + +Rico.LiveGridMetaData = Class.create(); + +Rico.LiveGridMetaData.prototype = { + + initialize: function( pageSize, totalRows, columnCount, options ) { + this.pageSize = pageSize; + this.totalRows = totalRows; + this.setOptions(options); + this.ArrowHeight = 16; + this.columnCount = columnCount; + }, + + setOptions: function(options) { + this.options = { + largeBufferSize : 7.0, // 7 pages + nearLimitFactor : 0.2 // 20% of buffer + }; + Object.extend(this.options, options || {}); + }, + + getPageSize: function() { + return this.pageSize; + }, + + getTotalRows: function() { + return this.totalRows; + }, + + setTotalRows: function(n) { + this.totalRows = n; + }, + + getLargeBufferSize: function() { + return parseInt(this.options.largeBufferSize * this.pageSize); + }, + + getLimitTolerance: function() { + return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor); + } +}; + +// Rico.LiveGridScroller ----------------------------------------------------- + +Rico.LiveGridScroller = Class.create(); + +Rico.LiveGridScroller.prototype = { + + initialize: function(liveGrid, viewPort) { + this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0; + this.liveGrid = liveGrid; + this.metaData = liveGrid.metaData; + this.createScrollBar(); + this.scrollTimeout = null; + this.lastScrollPos = 0; + this.viewPort = viewPort; + this.rows = new Array(); + }, + + isUnPlugged: function() { + return this.scrollerDiv.onscroll == null; + }, + + plugin: function() { + this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this); + }, + + unplug: function() { + this.scrollerDiv.onscroll = null; + }, + + sizeIEHeaderHack: function() { + if ( !this.isIE ) return; + var headerTable = $(this.liveGrid.tableId + "_header"); + if ( headerTable ) + headerTable.rows[0].cells[0].style.width = + (headerTable.rows[0].cells[0].offsetWidth + 1) + "px"; + }, + + createScrollBar: function() { + var visibleHeight = this.liveGrid.viewPort.visibleHeight(); + // create the outer div... + this.scrollerDiv = document.createElement("div"); + var scrollerStyle = this.scrollerDiv.style; + scrollerStyle.borderRight = this.liveGrid.options.scrollerBorderRight; + scrollerStyle.position = "relative"; + scrollerStyle.left = this.isIE ? "-6px" : "-3px"; + scrollerStyle.width = "19px"; + scrollerStyle.height = visibleHeight + "px"; + scrollerStyle.overflow = "auto"; + + // create the inner div... + this.heightDiv = document.createElement("div"); + this.heightDiv.style.width = "1px"; + + this.heightDiv.style.height = parseInt(visibleHeight * + this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ; + this.scrollerDiv.appendChild(this.heightDiv); + this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this); + + var table = this.liveGrid.table; + table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling ); + var eventName = this.isIE ? "mousewheel" : "DOMMouseScroll"; + Event.observe(table, eventName, + function(evt) { + if (evt.wheelDelta>=0 || evt.detail < 0) //wheel-up + this.scrollerDiv.scrollTop -= (2*this.viewPort.rowHeight); + else + this.scrollerDiv.scrollTop += (2*this.viewPort.rowHeight); + this.handleScroll(false); + }.bindAsEventListener(this), + false); + }, + + updateSize: function() { + var table = this.liveGrid.table; + var visibleHeight = this.viewPort.visibleHeight(); + this.heightDiv.style.height = parseInt(visibleHeight * + this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px"; + }, + + rowToPixel: function(rowOffset) { + return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight + }, + + moveScroll: function(rowOffset) { + this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset); + if ( this.metaData.options.onscroll ) + this.metaData.options.onscroll( this.liveGrid, rowOffset ); + }, + + handleScroll: function() { + if ( this.scrollTimeout ) + clearTimeout( this.scrollTimeout ); + + var scrollDiff = this.lastScrollPos-this.scrollerDiv.scrollTop; + if (scrollDiff != 0.00) { + var r = this.scrollerDiv.scrollTop % this.viewPort.rowHeight; + if (r != 0) { + this.unplug(); + if ( scrollDiff < 0 ) { + this.scrollerDiv.scrollTop += (this.viewPort.rowHeight-r); + } else { + this.scrollerDiv.scrollTop -= r; + } + this.plugin(); + } + } + var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight); + this.liveGrid.requestContentRefresh(contentOffset); + this.viewPort.scrollTo(this.scrollerDiv.scrollTop); + + if ( this.metaData.options.onscroll ) + this.metaData.options.onscroll( this.liveGrid, contentOffset ); + + this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 ); + this.lastScrollPos = this.scrollerDiv.scrollTop; + + }, + + scrollIdle: function() { + if ( this.metaData.options.onscrollidle ) + this.metaData.options.onscrollidle(); + } +}; + +// Rico.LiveGridBuffer ----------------------------------------------------- + +Rico.LiveGridBuffer = Class.create(); + +Rico.LiveGridBuffer.prototype = { + + initialize: function(metaData, viewPort) { + this.startPos = 0; + this.size = 0; + this.metaData = metaData; + this.rows = new Array(); + this.updateInProgress = false; + this.viewPort = viewPort; + this.maxBufferSize = metaData.getLargeBufferSize() * 2; + this.maxFetchSize = metaData.getLargeBufferSize(); + this.lastOffset = 0; + }, + + getBlankRow: function() { + if (!this.blankRow ) { + this.blankRow = new Array(); + for ( var i=0; i < this.metaData.columnCount ; i++ ) + this.blankRow[i] = " "; + } + return this.blankRow; + }, + + loadRows: function(ajaxResponse) { + var rowsElement = ajaxResponse.getElementsByTagName('rows')[0]; + this.updateUI = rowsElement.getAttribute("update_ui") == "true" + var newRows = new Array() + var trs = rowsElement.getElementsByTagName("tr"); + for ( var i=0 ; i < trs.length; i++ ) { + var row = newRows[i] = new Array(); + var cells = trs[i].getElementsByTagName("td"); + for ( var j=0; j < cells.length ; j++ ) { + var cell = cells[j]; + var convertSpaces = cell.getAttribute("convert_spaces") == "true"; + var cellContent = RicoUtil.getContentAsString(cell); + row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent; + if (!row[j]) + row[j] = ' '; + } + } + return newRows; + }, + + update: function(ajaxResponse, start) { + var newRows = this.loadRows(ajaxResponse); + if (this.rows.length == 0) { // initial load + this.rows = newRows; + this.size = this.rows.length; + this.startPos = start; + return; + } + if (start > this.startPos) { //appending + if (this.startPos + this.rows.length < start) { + this.rows = newRows; + this.startPos = start;// + } else { + this.rows = this.rows.concat( newRows.slice(0, newRows.length)); + if (this.rows.length > this.maxBufferSize) { + var fullSize = this.rows.length; + this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length) + this.startPos = this.startPos + (fullSize - this.rows.length); + } + } + } else { //prepending + if (start + newRows.length < this.startPos) { + this.rows = newRows; + } else { + this.rows = newRows.slice(0, this.startPos).concat(this.rows); + if (this.rows.length > this.maxBufferSize) + this.rows = this.rows.slice(0, this.maxBufferSize) + } + this.startPos = start; + } + this.size = this.rows.length; + }, + + clear: function() { + this.rows = new Array(); + this.startPos = 0; + this.size = 0; + }, + + isOverlapping: function(start, size) { + return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0) + }, + + isInRange: function(position) { + return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos()); + //&& this.size() != 0; + }, + + isNearingTopLimit: function(position) { + return position - this.startPos < this.metaData.getLimitTolerance(); + }, + + endPos: function() { + return this.startPos + this.rows.length; + }, + + isNearingBottomLimit: function(position) { + return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance(); + }, + + isAtTop: function() { + return this.startPos == 0; + }, + + isAtBottom: function() { + return this.endPos() == this.metaData.getTotalRows(); + }, + + isNearingLimit: function(position) { + return ( !this.isAtTop() && this.isNearingTopLimit(position)) || + ( !this.isAtBottom() && this.isNearingBottomLimit(position) ) + }, + + getFetchSize: function(offset) { + var adjustedOffset = this.getFetchOffset(offset); + var adjustedSize = 0; + if (adjustedOffset >= this.startPos) { //apending + var endFetchOffset = this.maxFetchSize + adjustedOffset; + if (endFetchOffset > this.metaData.totalRows) + endFetchOffset = this.metaData.totalRows; + adjustedSize = endFetchOffset - adjustedOffset; + if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize){ + adjustedSize = this.maxFetchSize; + } + } else {//prepending + var adjustedSize = this.startPos - adjustedOffset; + if (adjustedSize > this.maxFetchSize) + adjustedSize = this.maxFetchSize; + } + return adjustedSize; + }, + + getFetchOffset: function(offset) { + var adjustedOffset = offset; + if (offset > this.startPos) //apending + adjustedOffset = (offset > this.endPos()) ? offset : this.endPos(); + else { //prepending + if (offset + this.maxFetchSize >= this.startPos) { + var adjustedOffset = this.startPos - this.maxFetchSize; + if (adjustedOffset < 0) + adjustedOffset = 0; + } + } + this.lastOffset = adjustedOffset; + return adjustedOffset; + }, + + getRows: function(start, count) { + var begPos = start - this.startPos + var endPos = begPos + count + + // er? need more data... + if ( endPos > this.size ) + endPos = this.size + + var results = new Array() + var index = 0; + for ( var i=begPos ; i < endPos; i++ ) { + results[index++] = this.rows[i] + } + return results + }, + + convertSpaces: function(s) { + return s.split(" ").join(" "); + } + +}; + + +//Rico.GridViewPort -------------------------------------------------- +Rico.GridViewPort = Class.create(); + +Rico.GridViewPort.prototype = { + + initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) { + this.lastDisplayedStartPos = 0; + this.div = table.parentNode; + this.table = table + this.rowHeight = rowHeight; + this.div.style.height = (this.rowHeight * visibleRows) + "px"; + this.div.style.overflow = "hidden"; + this.buffer = buffer; + this.liveGrid = liveGrid; + this.visibleRows = visibleRows + 1; + this.lastPixelOffset = 0; + this.startPos = 0; + }, + + populateRow: function(htmlRow, row) { + for (var j=0; j < row.length; j++) { + htmlRow.cells[j].innerHTML = row[j] + } + }, + + bufferChanged: function() { + this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight)); + }, + + clearRows: function() { + if (!this.isBlank) { + this.liveGrid.table.className = this.liveGrid.options.loadingClass; + for (var i=0; i < this.visibleRows; i++) + this.populateRow(this.table.rows[i], this.buffer.getBlankRow()); + this.isBlank = true; + } + }, + + clearContents: function() { + this.clearRows(); + this.scrollTo(0); + this.startPos = 0; + this.lastStartPos = -1; + }, + + refreshContents: function(startPos) { + if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) { + return; + } + if ((startPos + this.visibleRows < this.buffer.startPos) + || (this.buffer.startPos + this.buffer.size < startPos) + || (this.buffer.size == 0)) { + this.clearRows(); + return; + } + this.isBlank = false; + var viewPrecedesBuffer = this.buffer.startPos > startPos + var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos; + var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows) + ? this.buffer.startPos + this.buffer.size + : startPos + this.visibleRows; + var rowSize = contentEndPos - contentStartPos; + var rows = this.buffer.getRows(contentStartPos, rowSize ); + var blankSize = this.visibleRows - rowSize; + var blankOffset = viewPrecedesBuffer ? 0: rowSize; + var contentOffset = viewPrecedesBuffer ? blankSize: 0; + + for (var i=0; i < rows.length; i++) {//initialize what we have + this.populateRow(this.table.rows[i + contentOffset], rows[i]); + } + for (var i=0; i < blankSize; i++) {// blank out the rest + this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow()); + } + this.isPartialBlank = blankSize > 0; + this.lastRowPos = startPos; + + this.liveGrid.table.className = this.liveGrid.options.tableClass; + // Check if user has set a onRefreshComplete function + var onRefreshComplete = this.liveGrid.options.onRefreshComplete; + if (onRefreshComplete != null) + onRefreshComplete(); + }, + + scrollTo: function(pixelOffset) { + if (this.lastPixelOffset == pixelOffset) + return; + + this.refreshContents(parseInt(pixelOffset / this.rowHeight)) + this.div.scrollTop = pixelOffset % this.rowHeight + + this.lastPixelOffset = pixelOffset; + }, + + visibleHeight: function() { + return parseInt(RicoUtil.getElementsComputedStyle(this.div, 'height')); + } + +}; + + +Rico.LiveGridRequest = Class.create(); +Rico.LiveGridRequest.prototype = { + initialize: function( requestOffset, options ) { + this.requestOffset = requestOffset; + } +}; + +// Rico.LiveGrid ----------------------------------------------------- + +Rico.LiveGrid = Class.create(); + +Rico.LiveGrid.prototype = { + + initialize: function( tableId, visibleRows, totalRows, url, options, ajaxOptions ) { + + this.options = { + tableClass: $(tableId).className, + loadingClass: $(tableId).className, + scrollerBorderRight: '1px solid #ababab', + bufferTimeout: 20000, + sortAscendImg: 'images/sort_asc.gif', + sortDescendImg: 'images/sort_desc.gif', + sortImageWidth: 9, + sortImageHeight: 5, + ajaxSortURLParms: [], + onRefreshComplete: null, + requestParameters: null, + inlineStyles: true + }; + Object.extend(this.options, options || {}); + + this.ajaxOptions = {parameters: null}; + Object.extend(this.ajaxOptions, ajaxOptions || {}); + + this.tableId = tableId; + this.table = $(tableId); + + this.addLiveGridHtml(); + + var columnCount = this.table.rows[0].cells.length; + this.metaData = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options); + this.buffer = new Rico.LiveGridBuffer(this.metaData); + + var rowCount = this.table.rows.length; + this.viewPort = new Rico.GridViewPort(this.table, + this.table.offsetHeight/rowCount, + visibleRows, + this.buffer, this); + this.scroller = new Rico.LiveGridScroller(this,this.viewPort); + this.options.sortHandler = this.sortHandler.bind(this); + + if ( $(tableId + '_header') ) + this.sort = new Rico.LiveGridSort(tableId + '_header', this.options) + + this.processingRequest = null; + this.unprocessedRequest = null; + + this.initAjax(url); + if ( this.options.prefetchBuffer || this.options.prefetchOffset > 0) { + var offset = 0; + if (this.options.offset ) { + offset = this.options.offset; + this.scroller.moveScroll(offset); + this.viewPort.scrollTo(this.scroller.rowToPixel(offset)); + } + if (this.options.sortCol) { + this.sortCol = options.sortCol; + this.sortDir = options.sortDir; + } + this.requestContentRefresh(offset); + } + }, + + addLiveGridHtml: function() { + // Check to see if need to create a header table. + if (this.table.getElementsByTagName("thead").length > 0){ + // Create Table this.tableId+'_header' + var tableHeader = this.table.cloneNode(true); + tableHeader.setAttribute('id', this.tableId+'_header'); + tableHeader.setAttribute('class', this.table.className+'_header'); + + // Clean up and insert + for( var i = 0; i < tableHeader.tBodies.length; i++ ) + tableHeader.removeChild(tableHeader.tBodies[i]); + this.table.deleteTHead(); + this.table.parentNode.insertBefore(tableHeader,this.table); + } + + new Insertion.Before(this.table, "
"); + this.table.previousSibling.appendChild(this.table); + new Insertion.Before(this.table,"
"); + this.table.previousSibling.appendChild(this.table); + }, + + + resetContents: function() { + this.scroller.moveScroll(0); + this.buffer.clear(); + this.viewPort.clearContents(); + }, + + sortHandler: function(column) { + if(!column) return ; + this.sortCol = column.name; + this.sortDir = column.currentSort; + + this.resetContents(); + this.requestContentRefresh(0) + }, + + adjustRowSize: function() { + + }, + + setTotalRows: function( newTotalRows ) { + this.resetContents(); + this.metaData.setTotalRows(newTotalRows); + this.scroller.updateSize(); + }, + + initAjax: function(url) { + ajaxEngine.registerRequest( this.tableId + '_request', url ); + ajaxEngine.registerAjaxObject( this.tableId + '_updater', this ); + }, + + invokeAjax: function() { + }, + + handleTimedOut: function() { + //server did not respond in 4 seconds... assume that there could have been + //an error or something, and allow requests to be processed again... + this.processingRequest = null; + this.processQueuedRequest(); + }, + + fetchBuffer: function(offset) { + if ( this.buffer.isInRange(offset) && + !this.buffer.isNearingLimit(offset)) { + return; + } + if (this.processingRequest) { + this.unprocessedRequest = new Rico.LiveGridRequest(offset); + return; + } + var bufferStartPos = this.buffer.getFetchOffset(offset); + this.processingRequest = new Rico.LiveGridRequest(offset); + this.processingRequest.bufferOffset = bufferStartPos; + var fetchSize = this.buffer.getFetchSize(offset); + var partialLoaded = false; + + var queryString + if (this.options.requestParameters) + queryString = this._createQueryString(this.options.requestParameters, 0); + + queryString = (queryString == null) ? '' : queryString+'&'; + queryString = queryString+'id='+this.tableId+'&page_size='+fetchSize+'&offset='+bufferStartPos; + if (this.sortCol) + queryString = queryString+'&sort_col='+escape(this.sortCol)+'&sort_dir='+this.sortDir; + + this.ajaxOptions.parameters = queryString; + + ajaxEngine.sendRequest( this.tableId + '_request', this.ajaxOptions ); + + this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout); + + }, + + setRequestParams: function() { + this.options.requestParameters = []; + for ( var i=0 ; i < arguments.length ; i++ ) + this.options.requestParameters[i] = arguments[i]; + }, + + requestContentRefresh: function(contentOffset) { + this.fetchBuffer(contentOffset); + }, + + ajaxUpdate: function(ajaxResponse) { + try { + clearTimeout( this.timeoutHandler ); + this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset); + this.viewPort.bufferChanged(); + } + catch(err) {} + finally {this.processingRequest = null; } + this.processQueuedRequest(); + }, + + _createQueryString: function( theArgs, offset ) { + var queryString = "" + if (!theArgs) + return queryString; + + for ( var i = offset ; i < theArgs.length ; i++ ) { + if ( i != offset ) + queryString += "&"; + + var anArg = theArgs[i]; + + if ( anArg.name != undefined && anArg.value != undefined ) { + queryString += anArg.name + "=" + escape(anArg.value); + } + else { + var ePos = anArg.indexOf('='); + var argName = anArg.substring( 0, ePos ); + var argValue = anArg.substring( ePos + 1 ); + queryString += argName + "=" + escape(argValue); + } + } + return queryString; + }, + + processQueuedRequest: function() { + if (this.unprocessedRequest != null) { + this.requestContentRefresh(this.unprocessedRequest.requestOffset); + this.unprocessedRequest = null + } + } +}; + + +//-------------------- ricoLiveGridSort.js +Rico.LiveGridSort = Class.create(); + +Rico.LiveGridSort.prototype = { + + initialize: function(headerTableId, options) { + this.headerTableId = headerTableId; + this.headerTable = $(headerTableId); + this.options = options; + this.setOptions(); + this.applySortBehavior(); + + if ( this.options.sortCol ) { + this.setSortUI( this.options.sortCol, this.options.sortDir ); + } + }, + + setSortUI: function( columnName, sortDirection ) { + var cols = this.options.columns; + for ( var i = 0 ; i < cols.length ; i++ ) { + if ( cols[i].name == columnName ) { + this.setColumnSort(i, sortDirection); + break; + } + } + }, + + setOptions: function() { + // preload the images... + new Image().src = this.options.sortAscendImg; + new Image().src = this.options.sortDescendImg; + + this.sort = this.options.sortHandler; + if ( !this.options.columns ) + this.options.columns = this.introspectForColumnInfo(); + else { + // allow client to pass { columns: [ ["a", true], ["b", false] ] } + // and convert to an array of Rico.TableColumn objs... + this.options.columns = this.convertToTableColumns(this.options.columns); + } + }, + + applySortBehavior: function() { + var headerRow = this.headerTable.rows[0]; + var headerCells = headerRow.cells; + for ( var i = 0 ; i < headerCells.length ; i++ ) { + this.addSortBehaviorToColumn( i, headerCells[i] ); + } + }, + + addSortBehaviorToColumn: function( n, cell ) { + if ( this.options.columns[n].isSortable() ) { + cell.id = this.headerTableId + '_' + n; + cell.style.cursor = 'pointer'; + cell.onclick = this.headerCellClicked.bindAsEventListener(this); + cell.innerHTML = cell.innerHTML + '' + + '   '; + } + }, + + // event handler.... + headerCellClicked: function(evt) { + var eventTarget = evt.target ? evt.target : evt.srcElement; + var cellId = eventTarget.id; + var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 )); + var sortedColumnIndex = this.getSortedColumnIndex(); + if ( sortedColumnIndex != -1 ) { + if ( sortedColumnIndex != columnNumber ) { + this.removeColumnSort(sortedColumnIndex); + this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC); + } + else + this.toggleColumnSort(sortedColumnIndex); + } + else + this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC); + + if (this.options.sortHandler) { + this.options.sortHandler(this.options.columns[columnNumber]); + } + }, + + removeColumnSort: function(n) { + this.options.columns[n].setUnsorted(); + this.setSortImage(n); + }, + + setColumnSort: function(n, direction) { + if(isNaN(n)) return ; + this.options.columns[n].setSorted(direction); + this.setSortImage(n); + }, + + toggleColumnSort: function(n) { + this.options.columns[n].toggleSort(); + this.setSortImage(n); + }, + + setSortImage: function(n) { + var sortDirection = this.options.columns[n].getSortDirection(); + + var sortImageSpan = $( this.headerTableId + '_img_' + n ); + if ( sortDirection == Rico.TableColumn.UNSORTED ) + sortImageSpan.innerHTML = '  '; + else if ( sortDirection == Rico.TableColumn.SORT_ASC ) + sortImageSpan.innerHTML = '  '; + else if ( sortDirection == Rico.TableColumn.SORT_DESC ) + sortImageSpan.innerHTML = '  '; + }, + + getSortedColumnIndex: function() { + var cols = this.options.columns; + for ( var i = 0 ; i < cols.length ; i++ ) { + if ( cols[i].isSorted() ) + return i; + } + + return -1; + }, + + introspectForColumnInfo: function() { + var columns = new Array(); + var headerRow = this.headerTable.rows[0]; + var headerCells = headerRow.cells; + for ( var i = 0 ; i < headerCells.length ; i++ ) + columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) ); + return columns; + }, + + convertToTableColumns: function(cols) { + var columns = new Array(); + for ( var i = 0 ; i < cols.length ; i++ ) + columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) ); + return columns; + }, + + deriveColumnNameFromCell: function(cell,columnNumber) { + var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent; + return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber; + } +}; + +Rico.TableColumn = Class.create(); + +Rico.TableColumn.UNSORTED = 0; +Rico.TableColumn.SORT_ASC = "ASC"; +Rico.TableColumn.SORT_DESC = "DESC"; + +Rico.TableColumn.prototype = { + initialize: function(name, sortable) { + this.name = name; + this.sortable = sortable; + this.currentSort = Rico.TableColumn.UNSORTED; + }, + + isSortable: function() { + return this.sortable; + }, + + isSorted: function() { + return this.currentSort != Rico.TableColumn.UNSORTED; + }, + + getSortDirection: function() { + return this.currentSort; + }, + + toggleSort: function() { + if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC ) + this.currentSort = Rico.TableColumn.SORT_ASC; + else if ( this.currentSort == Rico.TableColumn.SORT_ASC ) + this.currentSort = Rico.TableColumn.SORT_DESC; + }, + + setUnsorted: function(direction) { + this.setSorted(Rico.TableColumn.UNSORTED); + }, + + setSorted: function(direction) { + // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC... + this.currentSort = direction; + } + +}; + + +//-------------------- ricoUtil.js +var RicoUtil = { + + getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) { + if ( arguments.length == 2 ) + mozillaEquivalentCSS = cssProperty; + + var el = $(htmlElement); + if ( el.currentStyle ) + return el.currentStyle[cssProperty]; + else + return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS); + }, + + createXmlDocument : function() { + if (document.implementation && document.implementation.createDocument) { + var doc = document.implementation.createDocument("", "", null); + + if (doc.readyState == null) { + doc.readyState = 1; + doc.addEventListener("load", function () { + doc.readyState = 4; + if (typeof doc.onreadystatechange == "function") + doc.onreadystatechange(); + }, false); + } + + return doc; + } + + if (window.ActiveXObject) + return Try.these( + function() { return new ActiveXObject('MSXML2.DomDocument') }, + function() { return new ActiveXObject('Microsoft.DomDocument')}, + function() { return new ActiveXObject('MSXML.DomDocument') }, + function() { return new ActiveXObject('MSXML3.DomDocument') } + ) || false; + + return null; + }, + + getContentAsString: function( parentNode ) { + return parentNode.xml != undefined ? + this._getContentAsStringIE(parentNode) : + this._getContentAsStringMozilla(parentNode); + }, + + _getContentAsStringIE: function(parentNode) { + var contentStr = ""; + for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) { + var n = parentNode.childNodes[i]; + if (n.nodeType == 4) { + contentStr += n.nodeValue; + } + else { + contentStr += n.xml; + } + } + return contentStr; + }, + + _getContentAsStringMozilla: function(parentNode) { + var xmlSerializer = new XMLSerializer(); + var contentStr = ""; + for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) { + var n = parentNode.childNodes[i]; + if (n.nodeType == 4) { // CDATA node + contentStr += n.nodeValue; + } + else { + contentStr += xmlSerializer.serializeToString(n); + } + } + return contentStr; + }, + + toViewportPosition: function(element) { + return this._toAbsolute(element,true); + }, + + toDocumentPosition: function(element) { + return this._toAbsolute(element,false); + }, + + /** + * Compute the elements position in terms of the window viewport + * so that it can be compared to the position of the mouse (dnd) + * This is additions of all the offsetTop,offsetLeft values up the + * offsetParent hierarchy, ...taking into account any scrollTop, + * scrollLeft values along the way... + * + * IE has a bug reporting a correct offsetLeft of elements within a + * a relatively positioned parent!!! + **/ + _toAbsolute: function(element,accountForDocScroll) { + + if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 ) + return this._toAbsoluteMozilla(element,accountForDocScroll); + + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + + var borderXOffset = 0; + var borderYOffset = 0; + if ( parent != element ) { + var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" )); + var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" )); + borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset; + borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset; + } + + x += parent.offsetLeft - parent.scrollLeft + borderXOffset; + y += parent.offsetTop - parent.scrollTop + borderYOffset; + parent = parent.offsetParent; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + /** + * Mozilla did not report all of the parents up the hierarchy via the + * offsetParent property that IE did. So for the calculation of the + * offsets we use the offsetParent property, but for the calculation of + * the scrollTop/scrollLeft adjustments we navigate up via the parentNode + * property instead so as to get the scroll offsets... + * + **/ + _toAbsoluteMozilla: function(element,accountForDocScroll) { + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + x += parent.offsetLeft; + y += parent.offsetTop; + parent = parent.offsetParent; + } + + parent = element; + while ( parent && + parent != document.body && + parent != document.documentElement ) { + if ( parent.scrollLeft ) + x -= parent.scrollLeft; + if ( parent.scrollTop ) + y -= parent.scrollTop; + parent = parent.parentNode; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + docScrollLeft: function() { + if ( window.pageXOffset ) + return window.pageXOffset; + else if ( document.documentElement && document.documentElement.scrollLeft ) + return document.documentElement.scrollLeft; + else if ( document.body ) + return document.body.scrollLeft; + else + return 0; + }, + + docScrollTop: function() { + if ( window.pageYOffset ) + return window.pageYOffset; + else if ( document.documentElement && document.documentElement.scrollTop ) + return document.documentElement.scrollTop; + else if ( document.body ) + return document.body.scrollTop; + else + return 0; + } + +}; diff --git a/admin-root/playlist.html b/admin-root/playlist.html index 41304978..7a26cd5d 100644 --- a/admin-root/playlist.html +++ b/admin-root/playlist.html @@ -30,6 +30,12 @@ padding-left: 2ex; padding-top: 1ex; } +#edit_playlist_name { + position: absolute; + font: icon; + width: 25ex; + display: none; +}
@@ -41,7 +47,8 @@ +
+ Add static playlist
diff --git a/admin-root/playlist.js b/admin-root/playlist.js index 848d8794..a033b654 100644 --- a/admin-root/playlist.js +++ b/admin-root/playlist.js @@ -9,12 +9,32 @@ function initPlaylist() { Query.send('genres'); Event.observe('search','keypress',Search.keyPress); Event.observe('source','change',EventHandler.sourceChange); + Event.observe('source','click',EventHandler.sourceClick); Event.observe('genres','change',EventHandler.genresChange); Event.observe('artists','change',EventHandler.artistsChange); Event.observe('albums','change',EventHandler.albumsChange); + + Event.observe(document,'click',GlobalEvents.click); + Event.observe('edit_playlist_name','keypress',EditPlaylistName.keyPress); // Firefox remebers the search box value on page reload Field.clear('search'); } +var GlobalEvents = { + _listeners: [], + click: function (e) { + GlobalEvents._listeners.each(function (name) { + name.click(e); + }); + }, + addListener: function (el) { + this._listeners.push(el); + }, + removeListener: function (el) { + this._listeners = this._listeners.findAll(function (element) { + return (element != el); + }); + } +} // TODO // Find a decent spinner instad of the busy text // Handle 'all' in select boxes @@ -34,8 +54,81 @@ var Search = { } } }; +var EditPlaylistName = { + playlistId: '', + playlistName: '', + _getOptionElement: function () { + return option = $A($('source').getElementsByTagName('option')).find(function (el) { + return (el.value == EditPlaylistName.playlistId); + }); + }, + keyPress: function (e) { + input = $('edit_playlist_name'); + if (e.keyCode == Event.KEY_ESC) { + EditPlaylistName.hide(); + } + if (e.keyCode == Event.KEY_RETURN) { + EditPlaylistName.save(); + } + }, + save: function () { + input = $('edit_playlist_name'); + var url = '/databases/1/containers/edit?output=xml'; + url += '&dmap.itemid=' + EditPlaylistName.playlistId; + url += '&dmap.itemname=' + input.value.encode(); + new Ajax.Request(url ,{method: 'get',onComplete:EditPlaylistName.response}); + var option = EditPlaylistName._getOptionElement(); + option.text = input.value; + EditPlaylistName.hide(); + }, + show: function () { + input = $('edit_playlist_name'); + input.style.top = RicoUtil.toViewportPosition(el).y+ 'px'; + input.value = this.playlistName; + input.style.display = 'block'; + Field.activate(input); + GlobalEvents.addListener(this); + }, + hide: function () { + $('edit_playlist_name').style.display = 'none'; + EditPlaylistName.playlistId = ''; + GlobalEvents.removeListener(this); + }, + response: function (request) { + }, + click: function (e) { + if (EditPlaylistName.playlistId) { +// EditPlaylistName.save(); + } + } +} var EventHandler = { + sourceClickCount: [], + sourceClick: function (e) { + var playlistId = Event.element(e).value; + if (1 == playlistId) { + // do nothing for Library + return; + } + if (EventHandler.sourceClickCount[playlistId]) { + EventHandler.sourceClickCount[playlistId]++ + } else { + EventHandler.sourceClickCount[playlistId] = 1; + } + if (EventHandler.sourceClickCount[playlistId] > 1) { + el = Event.element(e); + if (!el.text) { + // Firefox generates and event when clicking in and empty area + // of the select box + return; + } + EditPlaylistName.playlistId = el.value; + EditPlaylistName.playlistName = el.text; + EditPlaylistName.show(); + } + }, sourceChange: function (e) { + EventHandler.sourceClickCount = []; Query.clearSelection('genres'); Query.clearSelection('artists'); Query.clearSelection('albums'); diff --git a/win32/nsi/mt-daapd.nsi b/win32/nsi/mt-daapd.nsi index 8887f71d..706a1a8d 100644 --- a/win32/nsi/mt-daapd.nsi +++ b/win32/nsi/mt-daapd.nsi @@ -87,6 +87,7 @@ Section "MainSection" SEC01 File "..\..\admin-root\applet.html" SetOutPath "$INSTDIR\admin-root\lib-js" File "..\..\admin-root\lib-js\prototype.js" + File "..\..\admin-root\lib-js\rico.js" SetOutPath "$INSTDIR\admin-root\lib-js\script.aculo.us" File "..\..\admin-root\lib-js\script.aculo.us\builder.js" File "..\..\admin-root\lib-js\script.aculo.us\controls.js"