You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

777 lines
22 KiB

/* Copyright (c) Business Objects 2006. All rights reserved. */
if (typeof bobj == 'undefined') {
bobj = {};
}
if (typeof bobj.constants == 'undefined') {
bobj.constants = {
modalLayerIndex:1000
};
}
/**
* @return [String] Returns a different id each time it's called
*/
bobj.uniqueId = function() {
return 'bobjid_' + (++bobj.uniqueId._count);
};
if (typeof bobj.uniqueId._count == 'undefined') {
bobj.uniqueId._count = new Date().getTime();
}
/**
* Like MochiKit.Base.update except that it checks each item in obj against
* a test function before adding it to self.
*
* @param test [Function] function that returns a boolean when passed (self, obj, key)
* @param self [Object|null] object to be updated
* @param obj [Object] object to copy properties from
*/
bobj.updateIf = function (test, self, obj/*, ... */) {
if (self === null) {
self = {};
}
for (var i = 1, len = arguments.length; i < len; i++) {
var o = arguments[i];
if (typeof(o) != 'undefined' && o !== null) {
for (var k in o) {
if (test(self, obj, k)) {
self[k] = o[k];
}
}
}
}
return self;
};
/**
* Copy properties from obj to self if the properties are undefined in self
*/
bobj.fillIn = function (self, obj) {
var test = function(self, obj, k) {
return (typeof(self[k]) == 'undefined');
}
bobj.updateIf(test, self, obj);
};
bobj.isObject = function(obj) {
return (obj && typeof obj == 'object');
};
bobj.isArray = function(obj) {
if(bobj.isObject(obj)) {
try {
return obj.constructor == Array;
}
catch(e) {
return false;
}
}
return false;
};
bobj.isString = function(obj) {
return (typeof(obj) == 'string');
};
bobj.isNumber = function(obj) {
return typeof(obj) == 'number' && isFinite(obj);
};
bobj.isBoolean = function(obj) {
return typeof obj == 'boolean';
};
bobj.isFunction = function(obj) {
return typeof(obj) == 'function';
};
/**
* Checks for the border box model, where css width includes padding and borders.
* IE uses this box model when a strict dtd is not specified.
*
* @return [boolean] Returns true if the border box model is being used
*/
bobj.isBorderBoxModel = function() {
if (typeof bobj.isBorderBoxModel._cachedValue == 'undefined') {
/*
* TODO: It is unnecessary to create DIV to check border box model. All we need to do is check _ie && quirksMode
* I didn't remove it for sake of not breaking different scenarios (requires alot of testing)
*/
if(document.body) {
var box = document.createElement('div');
box.style.width = '10px';
box.style.padding = '1px';
box.style.position = 'absolute';
box.style.visibility = 'hidden';
document.body.appendChild(box);
bobj.isBorderBoxModel._cachedValue = (box.offsetWidth == 10);
document.body.removeChild(box);
}
else {
return _ie && bobj.isQuirksMode();
}
}
return bobj.isBorderBoxModel._cachedValue;
};
/**
* @return [boolean] True if the document is rendering in quirks mode
*/
bobj.isQuirksMode = function() {
return (document.compatMode != 'CSS1Compat');
};
/* Sets the visual style of the specified element
*
* @param element [DOM node]
* @param visualStyle {} object containing visual styles
*/
bobj.setVisualStyle =function(element,visualStyle) {
if(element === null || visualStyle === null) {
return;
}
var elemStyle = element.style;
if(visualStyle.className)
element.className = visualStyle.className;
MochiKit.Iter.forEach ( [ "background", "borderWidth", "borderStyle", "borderColor", "fontFamily", "fontStyle", "fontSize",
"fontWeight", "textDecoration", "color", "width", "height", "left", "top" ], function(styleName) {
if (visualStyle[styleName])
elemStyle[styleName] = visualStyle[styleName];
});
};
/**
* Sets the outer size of an element, including padding, borders and margins.
*
* Note: Non-pixel units are ignored
*
* @param node [DOM node]
* @param w [Int - optional] Width in pixels
* @param h [Int - optional] Height in pixels
* @param excludeMargins [bool - optional] When true, margins are not included
* in the box size.
*/
bobj.setOuterSize = function(node, w, h, excludeMargins) {
var origStyle = null;
var nodeStyle = node.style;
if (nodeStyle.display == 'none') {
// Nodes have to be displayed to get their calculated styles.
// We display them but make them invisible and absolutely positioned
// so they don't affect the layout.
origStyle = {
visibility: nodeStyle.visibility,
position: nodeStyle.position,
display: 'none'
};
nodeStyle.visibility = 'hidden';
nodeStyle.position = 'absolute';
nodeStyle.display = '';
}
function pixels (selector) {
var value = MochiKit.DOM.getStyle(node, selector);
if (bobj.isString(value) && value.substring(value.length - 2 == 'px')) {
return (parseInt(value, 10) || 0);
}
return 0;
}
if (bobj.isNumber(w)) {
if (!bobj.isBorderBoxModel()) {
w -= pixels('border-left-width');
w -= pixels('border-right-width');
w -= pixels('padding-left');
w -= pixels('padding-right');
if(excludeMargins) {
w -= pixels('margin-left');
w -= pixels('margin-right');
}
}
nodeStyle.width = Math.max(0, w) + 'px';
}
if (bobj.isNumber(h)) {
if (!bobj.isBorderBoxModel()) {
if(excludeMargins) {
h -= pixels('margin-top');
h -= pixels('margin-bottom');
}
h -= pixels('border-top-width');
h -= pixels('border-bottom-width');
h -= pixels('padding-top');
h -= pixels('padding-bottom');
}
nodeStyle.height = Math.max(0, h) + 'px';
}
if (origStyle) {
nodeStyle.display = origStyle.display;
nodeStyle.position = origStyle.position;
nodeStyle.visibility = origStyle.visibility;
}
};
/**
* Get the node that contains a child widget.
*
* @param child [object] the widget whose parent node to be looked for
*/
bobj.getContainer = function(child) {
if (child && child.layer) {
return child.layer.parentNode;
}
return null;
};
/**
* Checks whether elem has a parent with the tag name equivalent to parentTagName
*
* @param elem [HTML node] the element whose parent is checked against parentTagName
* @param parentTagName [String] Parent's tagName ie) TABLE
*
*
* @return [boolean] True if elem has a parent with tagName equivalent to parentTagName
*/
bobj.checkParent = function(elem,parentTagName) {
var foundParent = false;
if(elem && parentTagName) {
parentTagName = parentTagName.toUpperCase();
var parent = elem.parentNode;
while(parent) {
if(parent.tagName == parentTagName) {
foundParent = true;
break;
}
parent = parent.parentNode;
}
}
return foundParent;
};
/**
* Implements Array.slice for array-like objects. For example, the special
* "arguments" variable within function calls is array-like but doesn't have
* a slice method.
*
* @param arrayLike [Object] An array-like object as defined by MochiKit.Base.isArrayLike.
* @param begin [Number] Zero-based index at which to begin extraction.
* @param end [Number] Zero-based index at which to end extraction.
* Extracts up to but not including end.
*
* @return [Array] A shallow copy of the portion of the array specified or null if invalid argument.
*/
bobj.slice = function(arrayLike, begin, end) {
if (bobj.isArray(arrayLike)) {
return arrayLike.slice(begin, end);
}
else if (MochiKit.Base.isArrayLike(arrayLike)) {
var retArray = [];
var endIdx = arrayLike.length;
if (bobj.isNumber(end) && end < endIdx) {
endIdx = end;
}
begin = Math.max(begin, 0);
for (var i = begin; i < endIdx; ++i) {
retArray.push(arrayLike[i]);
}
return retArray;
}
return null;
};
/**
* Extract a range of elements from a string or array-like list (non-destructive)
*
* @param list [String | Array-Like]
* @param start [Int] Index of start, inclusive
* @param end [Int] Index of end, exclusive
*
* @return Array of extracted elements or Null
*/
bobj.extractRange = function(list, start, end) {
if (list && bobj.isNumber(start)) {
if (!bobj.isNumber(end) || end > list.length) {
end = list.length;
}
start = Math.max(0, start);
if (start < end) {
var s1 = 0, e1 = start;
var s2 = end, e2 = list.length;
if (list.substring) {
return (list.substring(s1, e1) + list.substring(s2, e2));
}
else {
return bobj.slice(list, s1, e1).concat(bobj.slice(list, s2, e2));
}
}
}
return list;
};
/**
* Returns a value with a unit appended
*
* @param val [int or string]
* @param unit [sring - optional] Defaults to 'px'
*
* @return [string] Returns val as a string with unit appended if val is a
* number. Returns val without modification if val is not a number.
*/
bobj.unitValue = function(val, unit) {
if (bobj.isNumber(val)) {
return val + (unit || 'px');
}
return val;
};
/**
* Evaluate an expression in the window (global) scope
*
* @param expression [String] Expression to evaluate
*
* @return Returns the result of the evaluation
*/
bobj.evalInWindow = function(expression) {
if (window.execScript) { // Internet Explorer
return window.execScript(expression);
}
else {
return MochiKit.Base.bind(eval, window, expression).call();
}
};
/**
* Loads specified resource and executes callback
*/
bobj.loadJSResourceAndExecCallBack = function(resource, callback)
{
if(!resource || !callback)
return; // if arguments are not defined, just return
/*
* If bobj.crv.config.useCompressedScripts is true, then the resource is already loaded and we can skip loading
*/
if(!resource.isLoaded) {
var onLoad = function(resource, callback, response) {
resource.isLoaded = true;
bobj.evalInWindow(response.responseText);
callback.apply();
};
var req = MochiKit.Async.getXMLHttpRequest();
req.open("GET", bobj.crvUri(resource.path), true);
req.setRequestHeader('Accept','application/x-javascript,application/javascript, text/javascript');
var deferred = MochiKit.Async.sendXMLHttpRequest(req);
deferred.addCallback(MochiKit.Base.bind(onLoad, this, resource, callback));
}
else {
setTimeout(function(){callback.apply()},0);
}
};
/**
* Remove whitespace from the left end of a string.
*
* @param str [String]
*
* @return [String] Returns a string with no leading whitespace
*/
bobj.trimLeft = function(str) {
str = str || '';
return str.replace(/^\s+/g, '');
};
/**
* Remove whitespace from the right end of a string.
*
* @param str [String]
*
* @return [String] Returns a string with no trailing whitespace
*/
bobj.trimRight = function(str) {
str = str || '';
return str.replace(/\s+$/g, '');
};
/**
* Remove whitespace from both ends of a string.
*
* @param str [String]
*
* @return [String] Returns a string with no leading or trailing whitespace
*/
bobj.trim = function(str) {
return bobj.trimLeft(bobj.trimRight(str));
};
/**
* Check if the two inputs (and their contents) are equal.
*
* @param obj1 [Any]
* @param obj2 [Any]
*
* @return [boolean] Returns true if the two inputs are equal.
*/
bobj.equals = function (obj1, obj2) {
if (typeof(obj1) != typeof(obj2)) {
return false;
}
if (bobj.isObject(obj1)) {
var same = true;
for (var prop in obj1) {
same = same && bobj.equals(obj1[prop], obj2[prop]);
}
return same;
}
else {
return obj1 == obj2;
}
};
/**
* Creates a stylesheet link and adds it to document body
* @param1 href [String] location of css file
*
*/
bobj.includeLink = function(href) {
var head = document.getElementsByTagName("head")[0];
var body = document.body;
var link = document.createElement("link");
link.setAttribute("rel","stylesheet");
link.setAttribute("type","text/css");
link.setAttribute("href",href);
if(head) {
head.appendChild(link);
}
else if(body) {
body.appendChild(link);
}
};
/**
* @param hrefArray Array of url of css files
* @param callback function that gets executed once all css files are loaded
*/
bobj.includeCSSLinksAndExecuteCallback = function (hrefArray, callback) {
if(hrefArray == null || hrefArray.length < 1) {
callback.apply();
return;
}
var cb = function () {
var me = arguments.callee;
var callback = me.callback;
me.hrefCount--;
if(me.hrefCount == 0)
callback.apply();
}
cb.hrefCount = hrefArray.length;
cb.callback = callback;
for(var i = 0, len = hrefArray.length; i < len; i++) {
bobj.includeCSSLinkAndExecuteCallback(hrefArray[i], cb);
}
};
bobj.includeCSSLinkAndExecuteCallback = function(href, callback) {
var cssLinkId = encodeURIComponent(href);
/*if a css file with same href is already added, execute cb and continue */
if(getLayer(cssLinkId)) {
callback.apply();
return;
}
/*if css file successfully loads, add css text and call callback*/
var onLoad = function(callback, linkId, response) {
bobj.addStyleSheet(response.responseText, linkId);
callback.apply();
};
/*if css file fails to load, continue with callback */
var onError = function(callback) {
callback.apply();
};
var req = MochiKit.Async.getXMLHttpRequest();
req.open("GET", href, true);
req.setRequestHeader('Accept','text/css');
var deferred = MochiKit.Async.sendXMLHttpRequest(req);
var cb = MochiKit.Base.bind(onLoad, this, callback, cssLinkId)
var eb = MochiKit.Base.bind(onError, this, callback)
deferred.addCallbacks(cb, eb);
};
bobj.addStyleSheet = function(stylesheet,id) {
var style = document.createElement('style');
style.setAttribute("type", "text/css");
if(id) {
style.setAttribute("id", id);
}
if (style.styleSheet) {
style.styleSheet.cssText = stylesheet;
}
else {
style.appendChild(document.createTextNode(stylesheet));
}
var head = document.getElementsByTagName('head');
var body = document.getElementsByTagName('body');
if(head && head[0]) {
head[0].appendChild(style);
}
else if(body && body[0]) {
body[0].appendChild(style);
}
};
bobj.removeAllChildElements = function(elem) {
if(elem) {
while(elem.lastChild) {
elem.removeChild(elem.lastChild);
}
}
};
bobj.getValueHashCode = function(valueType, value) {
var Types = bobj.crv.params.DataTypes;
switch(valueType) {
case Types.BOOLEAN :
case Types.CURRENCY:
case Types.NUMBER:
case Types.STRING:
return '' + value;
case Types.TIME:
return '' + value.h + ',' + value.min + ',' + value.s + ',' + value.ms;
case Types.DATE:
return '' + value.y + ',' + value.m + ',' + value.d;
case Types.DATE_TIME:
return '' + value.y + ',' +value.m + ',' + value.d + ',' + value.h + ',' + value.min + ',' + value.s + ',' + value.ms;
}
};
/**
* Checks if there's a DOM element whose ID attribute matches the given string. If not, continue to search for a DOM element
* whose Name attribute matches the given string.
*
* @param idOrName [string] The ID or Name of the element to search for.
* @return [DOM element] Returns a DOM element, or null.
*/
bobj.getElementByIdOrName = function (idOrName) {
if (!idOrName) {
return null;
}
var elem = document.getElementById(idOrName);
if (elem) {
return elem;
}
var elems = document.getElementsByName(idOrName);
if (elems && elems.length > 0) {
return elems[0];
}
return null;
};
/*
* Returns a rectangle that can be used for css clip property
* @param top, right, bottom, left [Int]
* @return [String] returns rect(top,right,bottom,left) in pixel unit
*/
bobj.getRect = function(top, right, bottom, left) {
return "rect(" + top + "px, "+ right + "px," + bottom + "px," + left + "px)";
}
bobj.getBodyScrollDimension = function() {
var w = 0;
var h = 0;
var bodyTags = document.getElementsByTagName("Body");
if(bodyTags && bodyTags[0]) {
w = bodyTags[0].scrollWidth;
h = bodyTags[0].scrollHeight;
}
return {w : w, h : h};
}
/**
* Disables tabbing on element specified
* @param layer, The layer that will be modified
* @param dis [boolean], true would disable tabbing on element
*/
bobj.disableTabbingKey = function(layer, dis) {
if(layer) {
//Setting tabIndex value to (-1) would prevent tabbing to the DOM layer
layer.tabIndex = dis ? -1 : 0;
}
}
bobj.getStringWidth = function(string, fontFamily, fontSize) {
if(document.body) {
var span = document.createElement('span');
span.appendChild(document.createTextNode(string));
span.style.position = 'absolute';
span.style.visibility = 'hidden';
if(fontFamily)
span.style.fontFamily = fontFamily;
if(fontSize)
span.style.fontSize = fontSize;
document.body.appendChild(span);
var width = span.offsetWidth;
document.body.removeChild(span);
return width;
}
return 0;
}
bobj.deleteWidget = function(widget) {
if(widget && widget.widx) {
if(widget.layer) {
//Helps GC reclaim memory. Essential to IE memory leak
widget.layer.click = null;
widget.layer.onmouseup = null;
widget.layer.onmousedown = null;
widget.layer.onmouseover = null;
widget.layer.onmousemove = null;
widget.layer.onmouseout = null;
widget.layer.onchange = null;
widget.layer.onfocus = null;
widget.layer.onkeydown = null;
widget.layer.onkeyup = null;
widget.layer.onkeypress = null;
var parent = widget.layer.parentNode;
if(parent) {
parent.removeChild(widget.layer); //removing child from parent
}
delete widget.layer;
}
delete widget.css;
delete _widgets[widget.widx]; // cleans DHTML_Lib collection of widgets
_widgets[widget.widx] = null; /* for debugging purpose only */
delete widget;
}
};
bobj.cloneArray = function(array) {
return array.slice();
};
/**
* returns a function that would execute obj.func with obj as the context
* Similar to MochiKit.Base.bind, but this is a lot faster
*/
bobj.bindFunctionToObject = function(func, obj) {
return function () {
return func.apply(obj, arguments);
};
};
/**
* Object.superClass will be populated with all functions defined in superClass
*/
bobj.extendClass = function(object, ObjectClassDefinition, superClass) {
MochiKit.Base.update(object, ObjectClassDefinition);
object.superClass = {};
for ( var funcName in superClass) {
object.superClass[funcName] = bobj.bindFunctionToObject(superClass[funcName], object);;
}
};
bobj.displayElementWithAnimation = function (element) {
if(element != null) {
MochiKit.DOM.setOpacity(element, 0); //to reserve the element's space
MochiKit.Style.setDisplayForElement("block", element);
new MochiKit.Visual.appear(element, {duration: 0.5});
}
};
/**
* Returns dimension of element when it is invisible.DOM.offsetHeight should be used when
* element is visible to avoid execution cost of this method
*/
bobj.getHiddenElementDimensions = function (element) {
var size = { w : 0, h : 0};
if(element) {
var body = document.body;
var clonedNode = element.cloneNode(true); //clone and append the node to body as
var nodeStyle = clonedNode.style;
nodeStyle.display = "";
nodeStyle.visibility = "hidden";
nodeStyle.width = "";
nodeStyle.height = "";
nodeStyle.position = "absolute";
nodeStyle.left = "-1000px";
nodeStyle.top = "-1000px";
body.appendChild(clonedNode);
size = { w : clonedNode.offsetWidth, h : clonedNode.offsetHeight};
body.removeChild(clonedNode);
}
return size;
}
/**
* Checks for PDF reader that has the ability to process JavaScript.
* Currently, it checks for Adobe Acrobat Reader version >= 7 on IE and Version >= 8 on other browsers.
*/
bobj.hasPDFReaderWithJSFunctionality = function() {
if (navigator.plugins) {
var plugins = navigator.plugins;
for (var i=0, len=plugins.length; i < len; i++) {
if (plugins[i].description.indexOf('Adobe PDF Plug-In') != -1) {
return true;
}
}
}
try {
var pdfReader = new ActiveXObject('AcroPDF.PDF.1');
if (pdfReader) {
return true;
}
} catch (e) {}
return false;
};