/**
* Copyright Franson Technology AB, Sweden, 2009
* http://gpsgate.com, http://franson.com
*
* author Fredrik Blomqvist
*
* @module DOM
*
*/
var Franson = Franson || {};
/**
* namespace
* @class Franson.DOM
* @static
*/
Franson.DOM = Franson.DOM || {};
/**
* @method isBlockVisible
* @param {DOM|string} elem
* @return {boolean}
*/
Franson.DOM.isBlockVisible = function(elem)
{
elem = MochiKit.DOM.getElement(elem);
return elem !== null && elem.style.display != 'none'; // ok?
};
/**
* @method toggleEnabled
* @param {DOM|string|Array[DOM|string]} elem
*/
Franson.DOM.toggleEnabled = function(elem)
{
forEach(arguments, function(elem)
{
elem = MochiKit.DOM.getElement(elem);
if (elem)
elem.disabled = !elem.disabled;
});
};
/**
* @metod toggleBlockVisible
* @param {DOM|string|Array[DOM|string]} elem
*/
Franson.DOM.toggleBlockVisible = function(elem)
{
forEach(arguments, function(elem)
{
if (Franson.DOM.isBlockVisible(elem))
{
MochiKit.Style.hideElement(elem);
}
else
{
MochiKit.Style.showElement(elem);
}
});
};
/**
* @method translateElement
* @param {HTMLElement|string} element
* @param {Vec2} dv. (x:dx, y:dy) vector, can also just specify only one value (default to 0)
* @param {string} [unit='px']
*/
Franson.DOM.translateElement = function(element, dv, unit)
{
element = MochiKit.DOM.getElement(element);
unit = unit || 'px';
var dx = dv.x || 0;
var dy = dv.y || 0;
// var pos = MochiKit.Style.getElementPosition(element);
// MochiKit.Style.setElementPosition(element, {x: pos.x + dx, y: pos.y + dy}, unit);
// todo: this works, the above doesn't!? (bug in MochiKit?)
element.style.left = parseFloat(element.style.left) + dx + unit;
element.style.top = parseFloat(element.style.top) + dy + unit;
};
/**
* sets both position and dimension for a DOM element
* @method setElementBounds
* @param {Element|string} element
* @param {Bounds|Pos} boundsOrPos (x, y, w, h)
* @param {Size} [size] if not present boundsOrPos is assumed to be bounds.
*/
Franson.DOM.setElementBounds = function(element, boundsOrPos, size)
{
var pos = boundsOrPos;
var dim = arguments.length == 3 ? size : boundsOrPos;
MochiKit.Style.setElementPosition(element, pos);
MochiKit.Style.setElementDimensions(element, dim);
};
/**
* extracts both the element's position and size
* @method getElementBounds
* @param {Element|string} element
* @return {(x: number, y: number, w: number, h: number)} bounds
*/
Franson.DOM.getElementBounds = function(element)
{
var pos = MochiKit.Style.getElementPosition(element);
var dim = MochiKit.Style.getElementDimensions(element);
return {
x: pos.x, y: pos.y,
w: dim.w, h: dim.h
};
};
/**
* todo: deprecate. (only used in SOAP XML-parsing & same as MochiKit.DOM.getElementsByTagAndClassName(tagName, null, document))
* ! <i>except</i> for one particular sub version of IE6, sigh..
* @deprecated use <a href="http://mochikit.com/doc/html/MochiKit/DOM.html#fn-getelementsbytagandclassname">MochiKit.DOM.getElementsByTagAndClassName()</a> instead
* @method getElementsByTagName
* @param {DOCUMENT} document
* @param {string} tagName
* @return DOM[]
*/
Franson.DOM.getElementsByTagName = function(document, tagName)
{
try
{
// trying to get node omitting any namespaces (latest versions of MSXML.XMLDocument)
return document.selectNodes(".//*[local-name()=\""+ tagName +"\"]");
}
catch (ex) {}
// old XML parser support
return document.getElementsByTagName(tagName);
};
/**
* @method appendToSelectList
* @param {DOM.SELECT|string} select
* @param {DOM.OPTION} option (todo: support multiple options? or array? (or just plain text also?))
*/
Franson.DOM.appendToSelectList = function(select, option)
{
select = MochiKit.DOM.getElement(select);
select.options[select.options.length] = option;
};
/**
* todo: create a enableContextMenu also?
* todo: support multiple elements?
* @method disableContextMenu
* @param {DOM|string} element
*/
Franson.DOM.disableContextMenu = function(element)
{
MochiKit.DOM.getElement(element).oncontextmenu = Franson.Util.returnFalse;
};
/**
* @method getScrollOffset
* @param {DOM|string} node (todo: default to window?)
* @return { x: number, y: number }
*/
Franson.DOM.getScrollOffset = function(node)
{
node = MochiKit.DOM.getElement(node);
return {
x: Math.max(node.scrollWidth, node.offsetWidth),
y: Math.max(node.scrollHeight, node.offsetHeight)
};
};
/**
* adjusts the mouse position to be relative the div/element that triggered the event.
* (also adjust for window scrolling.)
* todo: Why doesn't MochiKit handle this?
* @method getMousePosition
* @param {Event} mouseEvent (MochiKit Event)
* @return {(x,y)}
*/
Franson.DOM.getMousePosition = function(mouseEvent) // absoluteMousePos?
{
var offset = MochiKit.Style.getElementPosition(mouseEvent.src());
var page = mouseEvent.mouse().page;
var x = page.x - offset.x;
var y = page.y - offset.y;
return { x: x, y: y };
};
/**
* moves the element to the end of it's sibling chain (<i>without disconnecting it</i>)
* Useful to bring objects to the front for example (could be called moveToFront?)
* (but not guaranteed to be visible, this doesn't care about z-index etc)
* @method makeLastSibling
* @param {DOM|string} element
*/
Franson.DOM.makeLastSibling = function(element)
{
element = MochiKit.DOM.getElement(element);
var parent = element.parentNode;
if (parent)
parent.appendChild(element);
};
/**
* @method body
* @return {BODY} document.body
*/
Franson.DOM.body = function()
{
return document.body || document.getElementsByTagName('body')[0];
};
/**
* Disables text selection for the element
* todo: create a enableTextSelection also?
* @method disableTextSelection
* @param {DOM|string} element
*/
Franson.DOM.disableTextSelection = function(element)
{
element = MochiKit.DOM.getElement(element);
if (typeof(element.onselectstart) != 'undefined') // IE
{
element.onselectstart = Franson.Util.returnFalse; // doesn't work in IE7..
element.unselectable = 'on'; //? GMap does this also but it still doesn't work in IE7 (GMap fails on IE7 also, so...)
}
else if (typeof(element.style.MozUserSelect) != 'undefined') // FireFox
{
element.style.MozUserSelect = 'none'; // (can be done in CSS also)
}
else if (typeof(element.style.KhtmlUserSelect) != 'undefined') // Safari
{
element.style.KhtmlUserSelect = 'none';
}
else // all other browsers (i.e: Opera)
{
element.onmousedown = Franson.Util.returnFalse; // ok? clash with dragging stuff? just skip?
element.style.cursor = 'default'; // ok?
}
/*
if (typeof(element.ondragstart) != 'undefined')
{
element.ondragstart = Franson.Util.returnFalse; // change to Franson.Event.stop();
}
if (typeof(element.ondrag) != 'undefined')
{
element.ondrag = Franson.Util.returnFalse; // todo: test if this works in IE
}
*/
/*
// this seems to work in IE! (just need to avoid disturbing other browsers)
Franson.Event.connectOnce(element, 'ondragstart', Franson.Event.stop);
Franson.Event.connectOnce(element, 'onselectstart', Franson.Event.stop);
Franson.Event.connectOnce(element, 'ondrag', Franson.Event.stop); // necessary?
// oncontextmenu also?
*/
};
/**
* todo: change to use Async.Deferred interface?
* todo: see http://trac.mochikit.com/ticket/258
* @method loadScript
* @param {string} uri
* @param {string|function} [bodyOrHead='head'] (hmm, necessary?)
* @param {function} [onLoaded]
*/
Franson.DOM.loadScript = function(uri, bodyOrHead, onloaded)
{
if (typeof(arguments[1]) == 'function')
{
onloaded = arguments[1];
bodyOrHead = 'head';
}
else
{
bodyOrHead = bodyOrHead || 'head';
}
var scriptNode = document.createElement('script');
scriptNode.type = 'text/javascript';
if (typeof(onloaded) != 'undefined')
{
var done = false;
// todo: this is not a 100% portable way.. (see http://remysharp.com/2007/04/12/how-to-detect-when-an-external-library-has-loaded/)
// (though this is the way jQuery does it)
scriptNode.onload = scriptNode.onreadystatechange = function() // handle both IE and FF etc (Safari needs a poll instead?)
{
if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete'))
{
done = true; // necessary since the onreadystatechange may fire multiple times
onloaded(scriptNode);
}
};
}
scriptNode.src = uri;
document.getElementsByTagName(bodyOrHead)[0].appendChild(scriptNode);
};
if (typeof(Franson.Iter) != 'undefined') // necessary? (will not fail until used anyway..)
{
/**
* @method levelOrderIter
* @param {DOM} node
* @return {Iterable[DOM]}
*/
Franson.DOM.levelOrderIter = function(node)
{
return Franson.Iter.treeLevelOrderIter(node, MochiKit.Base.itemgetter('childNodes'));
};
/**
* @method postOrderIter
* @param {DOM} node
* @return {Iterable[DOM]}
*/
Franson.DOM.postOrderIter = function(node)
{
return Franson.Iter.treePostOrderIter(node, MochiKit.Base.itemgetter('childNodes'));
};
/**
* @method preOrderIter
* @param {DOM} node
* @return {Iterable[DOM]}
*/
Franson.DOM.preOrderIter = function(node)
{
return Franson.Iter.treePreOrderIter(node, MochiKit.Base.itemgetter('childNodes'));
};
/**
* @method parentIter
* @param {DOM} node
* @return {Iterable[DOM]}
*/
Franson.DOM.parentIter = function(node)
{
return Franson.Iter.leafParentIter(node, MochiKit.Base.itemgetter('parentNode'));
};
}