
 /******************************************************
  * Useful methods and utilities for
  * commonly needed tasks
  ******************************************************/


    var Common = new Object();


   /**
    * Set a new event handler for specified element
    * @param object element reference to node element
    * @param string event name of the target event
    * @param mixed callback anonymous function or function name
    */
    Common.addEventListener = function(element, event, callback, useCapture)
    {
        useCapture = useCapture != false ? true : false;

        if (Common.isIE)
            element.attachEvent('on' + event, callback, useCapture);
        else
            element.addEventListener(event, callback, useCapture);
    };


   /**
    * Remove children from given DOM-element
    * @param object element reference to node element
    */
    Common.clearElement = function(element)
    {
        while (element.childNodes.length > 0)
            element.removeChild(element.lastChild);
    };


   /**
    * Create CSS rule
    * @param object page parent document to append to
    * @param string ruleName CSS selector name
    * @param string ruleText CSS selector content
    */
    Common.createCssRule = function(page, ruleName, ruleText)
    {
        // Note: IE-method is not implemented
        var heads = page.getElementsByTagName('HEAD');

        $('STYLE').
            setAttributes({
                'type':'text/css',
                'media':'screen'
            }).
            addText(ruleName + ' {' + ruleText + '}').
            injectInside(heads[0]);
    };


   /**
    * Create a new DOM-element
    * @param string nodeName name of node to create
    * @param object nodeParams optional node attributes
    * @param string nodeText option string to insert as textnode
    * @return object newly created node element
    */
    Common.createElement = function(nodeName, nodeParams, nodeText)
    {
        var result = document.createElement(nodeName);

        if (nodeText)
            result.appendChild(document.createTextNode(nodeText));

        // No attributes
        if (!nodeParams)
            return result;

        // Set attributes
        for (var i in nodeParams)
        {
            if (typeof (nodeParams[i]) != 'object')
                result.setAttribute(i, nodeParams[i]);
        }

        // Set styles
        if (nodeParams.style)
        {
            for (var i in nodeParams.style)
                result.style[i] = nodeParams.style[i];
        }

        // Set classname for IE
        if (nodeParams['class'])
            result.className = nodeParams['class'];

        return result;
    };


   /**
    * Turn variable into an evaluatable string of javascript
    * @param mixed param variable to turn into a string
    * @return string given variable as a string
    */
    Common.getAsString = function(param)
    {
        // Create recursive handler
        var encoder = function(param)
        {
            if (param == null)
                return 'null';

            // Get type
            var type = typeof param;

            // Number
            if (type == 'number')
                return param;

            // String
            if (type == 'string')
                return '"' + param.split('"').join('\\"').split("\n").join('\\n') + '"';

            // Array
            if (type == 'object' && param.constructor == Array)
            {
                var item = new Array();

                for (var i = 0; i < param.length; ++i)
                    item.push(encoder(param[i]));

                return '[' + item.join(', ') + ']';
            }

            // Object
            if (type == 'object')
            {
                var item = new Array();

                for (var i in param)
                    item.push('"' + i + '":' + encoder(param[i]));

                return '{' + item.join(', ') + '}';
            }

            // Undentified (function etc.)
            return 'null';
        };

        return encoder(param);
    };
    
    
   /**
    * Get string as URL, empty string if not valid
    * @return string url
    * @static
    */
    Common.getAsURL = function(url)
    {
        if (url.indexOf('http') != 0)
            url = 'http://' + url;

        return Common.isURL(url) ? url : '';
    };


   /**
    * Get browser height in pixels
    * @return int browser height
    */
    Common.getBrowserHeight = function()
    {
        if (self.innerHeight)
            return self.innerHeight;

        if (document.documentElement && document.documentElement.clientHeight)
            return document.documentElement.clientHeight;

        if (document.body)
            return document.body.clientHeight;

        return screen.height;
    };


   /**
    * Get browser name
    * @return mixed lowercase browser name, null on failure
    */
    Common.getBrowserName = function()
    {
        // Just the essential browser detection as
        // we do not need to know the details

        if (window.opera)
            return 'opera';

        if (navigator.userAgent.indexOf('MSIE') > -1)
            return 'ie';

        if (navigator.userAgent.indexOf('Gecko') > -1)
            return 'mozilla';

        return null;
    };


   /**
    * Get browser width in pixels
    * @return int browser width
    */
    Common.getBrowserWidth = function()
    {
        if (self.innerWidth)
            return self.innerWidth;

        if (document.documentElement && document.documentElement.clientWidth)
            return document.documentElement.clientWidth;

        if (document.body)
            return document.body.clientWidth;

        return screen.width;
    };


   /**
    * Get cookie value
    * @param string name cookie name
    * @return mixed cookie contents or null on failure
    */
    Common.getCookie = function(name)
    {
        var start = document.cookie.indexOf(name + '=');

        if (start == -1)
            return null;

        var cookie = document.cookie.substring(start);
        cookie = cookie.split(';').shift().split(name + '=').join('');

        return cookie;
    };


   /**
    * Turn CSS style into style-property in Javascript
    * @param string name original css-style's name
    * @return string javascript style name in element.style.*
    */
    Common.getCssRuleName = function(name)
    {
        name = name.split('-');

        for (var i = 1; i < name.length; ++i)
            name[i] = name[i].charAt(0).toUpperCase() + name[i].substring(1);

        return name.join('');
    };
    
    
   /**
    ' Get keycode from event object
    * @param object event reference to event object
    * @return int keycode
    */
    Common.getEventKeycode = function(event, frame)
    {
        if (Common.isIE)
        {
            if (typeof (frame) != 'undefined')
                return frame.contentWindow.event.keyCode;
            else
                return window.event.keyCode;
        }
        else
            return event.keyCode;
    };


   /**
    ' Get target element from event object
    * @param object event reference to event object
    * @return object reference to target node
    */
    Common.getEventTarget = function(event, frame)
    {
        if (Common.isIE)
        {
            if (typeof (frame) != 'undefined')
                return frame.contentWindow.event.srcElement;
            else
                return window.event.srcElement;
        }
        else
            return event.target;
    };


   /**
    * Get given number as a float string with minimum amount of decimals
    * @param mixed value original float or integer value
    * @param int amount minimum amount of decimals
    * @return string float value with minimum amount of decimals
    */
    Common.getFloatString = function(value, amount)
    {
        value = new String(value);
        value = value.split('.');

        if (value.length < 2)
            value.push('');

        while (value[1].length < amount)
            value[1] += '0';

        return value[0] + '.' + value[1];
    };


   /**
    * Get human readable filesize
    * @param int size original filesize in bytes
    * @return object size and unit of human readable filesize
    */
    Common.getHumanFilesize = function(size)
    {
        var units = ['b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'];

        for (var i = 0; size >= 1024 && i < units.length; ++i)
            size = (size / 1024);

        size += '';

        if (size.indexOf('.') > -1)
            size = size.substring(0, size.indexOf('.') + 2);

        return {size:parseFloat(size), unit:units[i]};
    };


   /**
    * Get horizontal mouse position in the browser
    * @param object event original event object
    * @return int mouse x-position
    */
    Common.getMouseX = function(event)
    {
        return Common.isIE ? window.event.pageX : event.pageX;
    };


   /**
    * Get vertical mouse position in the browser
    * @param object event original event object
    * @return int mouse y-position
    */
    Common.getMouseY = function(event)
    {
        return Common.isIE ? window.event.pageY : event.pageY;
    };


   /**
    * Get element's horizontal position on page
    * @param object element reference to node element
    * @return int pixels from left
    */
    Common.getOffsetLeft = function(element)
    {
        var result = 0;
        var parent;

        // Add offsets
        parent = element;

        while (parent.offsetParent)
        {
            result += parent.offsetLeft;
            parent = parent.offsetParent;
        }

        // Remove scrolling
        parent = element;

        while (parent.parentNode)
        {
            result -= parent.scrollLeft;
            parent = parent.parentNode;
        }

        return result;
    };


   /**
    * Get element's vertical position on page
    * @param object element reference to node element
    * @return int pixels from top
    */
    Common.getOffsetTop = function(element)
    {
        var result = 0;
        var parent;

        // Add offsets
        parent = element;

        while (parent.offsetParent)
        {
            result += parent.offsetTop;
            parent = parent.offsetParent;
        }

        // Remove scrolling
        parent = element;

        while (parent.parentNode)
        {
            result -= parent.scrollTop;
            parent = parent.parentNode;
        }

        return Common.getPageOffsetY() + result;
    };
    
    
   /**
    * Get horizontal center position for element
    * @param int width element width
    * @return int horizontal position
    * @static
    */
    Common.getPageCenterX = function(width)
    {
        return ((Common.getBrowserWidth() - width) / 2);
    };
    
    
   /**
    * Get vertical center position for element
    * @param int height element height
    * @return int vertical position
    * @static
    */
    Common.getPageCenterY = function(height)
    {
        return Common.getPageOffsetY() + ((Common.getBrowserHeight() - height) / 2);
    };


   /**
    * Get page height in pixels
    * @return int page height
    */
    Common.getPageHeight = function()
    {
        if (window.innerHeight && window.scrollMaxY)
            return window.innerHeight + window.scrollMaxY;

        if (document.body.scrollHeight > document.body.offsetHeight)
            return document.body.scrollHeight;

        return document.body.offsetHeight;
    };


   /**
    * Get vertical page scrolling offset in pixels
    * @return int vertical page offset
    */
    Common.getPageOffsetY = function()
    {
        if (window.innerHeight)
            return window.pageYOffset;

        if (document.documentElement && document.documentElement.scrollTop)
            return document.documentElement.scrollTop

        if (document.body)
            return document.body.scrollTop

        return 0;
    };


   /**
    * Get maximum thumbnail size from given values
    * @param int origW original image width
    * @param int origH original image height
    * @param int maxW thumbnail maximum width
    * @param int maxH thumbnail maximum height
    * @return array new size insided indexes width and height
    */
    Common.getThumbSize = function(origW, origH, maxW, maxH)
    {
        // Original size by default
        var result = {width:origW, height:origH};

        // Calculate scale
        var scale = Math.min(maxW/origW, maxH/origH);

        // Must be resized
        if (scale > 0 && scale < 1)
        {
            result.width = Math.ceil(scale * origW);
            result.height = Math.ceil(scale * origH);
        }

        return result;
    };


   /**
    * Get requested runtime style from specified element
    * @param object root reference to document element, e.g. iframe document
    * @param object element reference to node element
    * @param string rule css-attribute value to get, e.g. 'font-family'
    * @return mixed css attribute value string or null on failure
    */
    Common.getStyle = function(root, element, rule)
    {
        if (root.defaultView && root.defaultView.getComputedStyle)
            return root.defaultView.getComputedStyle(element, '').getPropertyValue(rule);

        if (element.currentStyle)
            return element.currentStyle[Common.getCssRuleName(rule)];

        return null;
    };


   /**
    * Insert node at active selection
    * @param object parent reference to target window node
    * @param object element reference to node element to insert
    */
    Common.insertElementAtSelection = function(parent, element)
    {
        var range;

        if (Common.isIE)
        {
            range = parent.selection.createRange();
            range.appendChild(element);
        }
        else
        {
     // get current selection
      var sel = parent.getSelection();

      // get the first range of the selection
      // (there's almost always only one range)
      var range = sel.getRangeAt(0);

      // deselect everything
      sel.removeAllRanges();

      // remove content of current selection from document
      range.deleteContents();

      // get location of current selection
      var container = range.startContainer;
      var pos = range.startOffset;

      // make a new range for the new selection
      range=document.createRange();

      if (container.nodeType==3 && element.nodeType==3) {

        // if we insert text in a textnode, do optimized insertion
        container.insertData(pos, element.nodeValue);

        // put cursor after inserted text
        range.setEnd(container, pos+element.length);
        range.setStart(container, pos+element.length);

      } else {


        var afterNode;
        if (container.nodeType==3) {

          // when inserting into a textnode
          // we create 2 new textnodes
          // and put the element in between

          var textNode = container;
          container = textNode.parentNode;
          var text = textNode.nodeValue;

          // text before the split
          var textBefore = text.substr(0,pos);
          // text after the split
          var textAfter = text.substr(pos);

          var beforeNode = document.createTextNode(textBefore);
          afterNode = document.createTextNode(textAfter);

          // insert the 3 new nodes before the old one
          container.insertBefore(afterNode, textNode);
          container.insertBefore(element, afterNode);
          container.insertBefore(beforeNode, element);

          // remove the old node
          container.removeChild(textNode);

        } else {

          // else simply insert the node
          afterNode = container.childNodes[pos];
          container.insertBefore(element, afterNode);
        }

        range.setEnd(afterNode, 0);
        range.setStart(afterNode, 0);
      }

      sel.addRange(range);
        }
    };
    
    
   /**
    * Check if e-mail address is valid
    * @param string email e-mail address
    * @return bool true if e-mail is valid, otherwise false
    */
    Common.isEmail = function(email)
    {
        // Don't even bother regexping if there's no @
        if (email.indexOf('@') === -1)
            return false;
        
        // Test against pattern and return result
        var regexp = new RegExp(/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+([\.][a-z0-9-]+)+$/i);
        return regexp.test(email);
    }


   /**
    * Check if string is a URL
    * @param string url URL
    * @return bool true if string is a URL, otherwise false
    */
    Common.isURL = function(url)
    {
        // Test against pattern and return result
        var regexp = new RegExp(/^(((ht|f)tp(s?))\:\/\/)([0-9a-z\-\_]+\.)+[a-z]{2,6}(\:[0-9]+)?(\/\S*)?$/i);
        return regexp.test(url);
    }
    
    
   /**
    * Check if string is part of a URL, e.g. www.example.com/something
    * @param string urlPart URL
    * @return bool true if string is part of a URL, otherwise false
    */
    Common.isURLPart = function(urlPart)
    {
        return urlPart.indexOf('www') == 0 ? true : false;
    }


   /**
    * Perform a call to remote service using JSON
    * @param string method name of remote service method
    * @param array params list of parameters to pass
    * @param mixed callback function or name of callback function
    */
    Common.Json = function(method, params, callback)
    {
        var service;

        // Create native request object
        if (window.XMLHttpRequest)
            service = new XMLHttpRequest();

        // Create ActiveX request object
        else if (window.ActiveXObject)
        {
            try
            {
                service = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch(e)
            {
                service = new ActiveXObject('Microsoft.XMLHTTP');
            }
        }

        // Could not create request object
        if (!service)
            return;

        // Create event handler to wait for a response
        service.onreadystatechange = function()
        {
            // Parse response when ready
            if (callback && service.readyState == 4)
            {
                var result = '';

                if (service.responseText.length)
                    eval('result = ' + service.responseText + ';');

                callback(result);
            }
        };

        params.unshift(Application.Session);

        service.open('POST', Application.JsonUrl, true);
        service.send(Common.getAsString({'methodName':method, 'params':params}));
    };


   /**
    * Prevent default event behavior in a cross-browser-friendly way
    * @param object event original event object
    */
    Common.preventDefault = function(event, frame)
    {
        if (!Common.isIE)
            event.preventDefault();
        else
        {
            if (typeof (frame) == 'undefined')
            {
                window.event.cancelBubble = true;
                window.event.returnValue = false;
            }
            else
            {
//                frame.contentWindow.event.cancelBubble = true;
//                frame.contentWindow.event.returnValue = false;
            }
        }
    };


   /**
    * Preload image
    * @param string path URL or relative path to image
    */
    Common.preloadImage = function(path)
    {
        var image = new Image();
        image.src = path;
    };


    // Deprecated, see String.ucFirst
    Common.getUcFirstString = function(source)
    {
        return source.ucFirst();
    };


   /**
    * Remove existing event handler from specified element
    * @param object element reference to node element
    * @param string event name of the target event
    * @param mixed callback anonymous function or function name
    */
    Common.removeEventListener = function(element, event, callback)
    {
        if (Common.isIE)
            element.detachEvent('on' + event, callback, true);
        else
            element.removeEventListener(event, callback, true);
    };


   /**
    * Remove HTML tags from given source string
    * @param string source original string
    * @return string source string stripped from tags
    */
    Common.removeHtmlTags = function(source)
    {
        var result = '';

        for (var i = 0; i < source.length; ++i)
        {
            if (source.charAt(i) != '<')
            {
                result += source.charAt(i);
                continue;
            }

            while (source.charAt(i) != '>' && i < source.length)
                ++i;
        }

        return result;
    };
    
    
   /**
    * Round number to nearest integer/half-integer
    * @param double number
    * @return double rounded number
    */
    Common.roundHalfway = function(number)
    {
        var floored = Math.floor(number);
        var ceiled  = Math.ceil(number);
        var rounded = Math.round(number);
        
        // Between .0 and 0.49
        if (floored == rounded)
            return (number < floored + 0.25) ? floored : floored + 0.5;
        // Between .5 and 0.99
        else
            return (number < ceiled - 0.25) ? ceiled - 0.5 : ceiled;
    };


   /**
    * Set a cookie
    * @param string name cookie name
    * @param mixed value cookie value
    */
    Common.setCookie = function(name, value)
    {
        document.cookie = name + '=' + value + ';';
    };


   /**
    * Set styles for element
    * @param object element reference to node element
    * @param object styles CSS-styles in key-value format
    */
    Common.setElementStyles = function(element, styles)
    {
        for (var s in styles)
            element.style[Common.getCssRuleName(s)] = styles[s];
    };


   /**
    * Set given element's opacity level
    * @param object element reference to node element
    * @param int percent new opacity percentage
    */
    Common.setOpacity = function(element, percent)
    {
        var alpha = percent / 100;

        element.style.filter       = 'alpha(opacity:' + percent + ')';
        element.style.KHTMLOpacity = alpha;
        element.style.MozOpacity   = alpha;
        element.style.opacity      = alpha;
    };


   /**
    * Set elements png-background
    * @param object element reference to node element
    * @param string path path or full url to png-image
    * @param bool repeat optional parameter to say if the image should be repeated
    */
    Common.setPngBackground = function(element, png, repeat)
    {
        if (!Common.isIE)
            element.style.backgroundImage = 'url(' + png + ')';
    };


   /**
    * Trigger given function only once when specified event is
    * launched anywhere, even inside some other IFRAME document
    * @param string event name of the target event
    * @param mixed callback anonymous function or funciton name
    */
    Common.triggerOnEvent = function(event, callback)
    {
        // Response handler
        var handler = function()
        {
            callback(this);

            // Remove event listeners
            for (var i = 0; i < targets.length; ++i)
                Common.removeEventListener(targets[i], event, handler);
        };

        // Collect possible targets recursively
        var collect = function(parent)
        {
            var items = targets[parent].getElementsByTagName('IFRAME');

            for (var i = 0; i < items.length; ++i)
            {
                targets.push(items[i].contentWindow.document);
                collect(targets.length - 1);
            }
        };

        // Find targets from current document
        var targets = new Array(document);
        collect(0);

        // Assign event listeners
        for (var i = 0; i < targets.length; ++i)
            Common.addEventListener(targets[i], event, callback);
    };

    // Quick browser detection
    Common.isIE  = (Common.getBrowserName() == 'ie' || Common.getBrowserName() == 'opera');
    Common.isIE6 = (navigator.userAgent.indexOf('MSIE 6.0') > -1) ? true : false ;

