$(document).ready(function()
{
    if (typeof(yapyap) == 'undefined')
    {
        if (typeof(console) != 'undefined' && typeof(console.log) == 'function')
        {
            console.log("Warning: yapyap.utils.js was included without or before yapyap.js");
        }
        yapyap = {};
    }

    if (typeof(yapyap.utils) == 'undefined')
    {
        yapyap.utils =
        {
            generateUniqID: function ()
            {
		        var d = new Date();
		        return ''+d.getDate()+d.getHours()+d.getMinutes()+d.getSeconds()+d.getMilliseconds()+'';
	        },

            array:
            {
                find: function (array, value)
                // Returns true if the passed value is found in the
                // array. Returns false if it is not.
                {
                    var i;
                    for (i=0; i < array.length; i++)
                    {
                        if (array[i] === value)
                        {
                            return i;
                        }
                    }
                    return -1;
                },

                inside: function (array, value)
                // Checks element presence inside array
                {
                    return yapyap.utils.array.find(array, value) != -1;
                },

                remove: function (array, value)
                // Remove element from array if present
                {
                    var i;
                    for (i=0; i < array.length; i++)
                    {
                        if (array[i] === value)
                        {
                            array.splice(i, 1);
                            return true;
                        }
                    }
                    return false;
                }
            },

            string:
            {
                ltrim: function (value)
                {
                    var re = /\s*((\S+\s*)*)/;
                    return value.replace(re, "$1");
                },

                rtrim: function (value)
                {
                    var re = /((\s*\S+)*)\s*/;
                    return value.replace(re, "$1");
                },

                trim: function (value)
                {
                    return yapyap.utils.string.ltrim(yapyap.utils.string.rtrim(value));
                },

                htmlen: function (text)
                {
                    if (typeof(text) != 'string')
                    {
                        return text;
                    }
                    return text
                            .replace(/"/g, '&quote;')
                            .replace(/&/g, '&amp;')
                            .replace(/</g, '&lt;')
                            .replace(/>/g, '&gt;');
                },

                htmlen_noQuote: function (text)
                {
		            if (typeof(text) != 'string')
                    {
                        return text;
                    }
                    return text
                            .replace(/&/g, '&amp;')
                            .replace(/</g, '&lt;')
                            .replace(/>/g, '&gt;');
	            },

                transliterate: function (text)
                {
                    var transliterationTables =
                    {
                        rus2eng: {
                            'Ё': "YO", 'Й': "I", 'Ц': "TS", 'У': "U", 'К': "K", 'Е': "E", 'Н': "N", 'Г': "G", 'Ш': "SH",
                            'Щ': "SCH", 'З': "Z", 'Х': "H", 'Ъ': "'",
                            'ё': "yo", 'й': "i", 'ц': "ts", 'у': "u", 'к': "k", 'е': "e", 'н': "n", 'г': "g", 'ш': "sh",
                            'щ': "sch", 'з': "z", 'х': "h", 'ъ': "'",
                            'Ф': "F", 'Ы': "I", 'В': "V", 'А': "A", 'П': "P", 'Р': "R", 'О': "O", 'Л': "L", 'Д': "D", 'Ж': "ZH", 'Э': "E",
                            'ф': "f", 'ы': "i", 'в': "v", 'а': "a", 'п': "p", 'р': "r", 'о': "o", 'л': "l", 'д': "d", 'ж': "zh", 'э': "e",
                            'Я': "YA", 'Ч': "CH", 'С': "S", 'М': "M", 'И': "I", 'Т': "T", 'Ь': "'", 'Б': "B", 'Ю': "YU",
                            'я': "ya", 'ч': "ch", 'с': "s", 'м': "m", 'и': "i", 'т': "t", 'ь': "'", 'б': "b", 'ю': "yu"
                        }
                    };

                    var transliterationTable = transliterationTables.rus2eng;

                    var result = '';
                    for (var i = 0; i < text.length; ++i)
                    {
                    	var c = text.charAt(i);
                        result += (c in transliterationTable) ? transliterationTable[c] : c;
                    }

                    return result;
                }
            },

            functors:
            {
                map: function (array, func)
                {
                    if (typeof(func) != 'function')
                    {
                        yapyap.log('wrong map functor', func);
                        return;
                    }
                    for (var i=0; i<array.length; ++i)
                    {
                        array[i] = func(array[i]);
                    }
                    return array;
                },

                stepmap: function (func, iterable, stop)
                {
                    // if 'stop' argument is not a function, default function is being created
                    // in this case 'stop' argument can be interpreted as a 'continueValue',
                    // and iterations break should occur only if 'func' returned value not equal
                    // to a 'continueValue'. By default 'continueValue' is set to 0, so first
                    // function call resulted not with zero shoud break iterations.
                    if (typeof(func) != 'function')
                    {
                        yapyap.log('wrong stepmap functor', func);
                        return;
                    }
                    if (typeof(stop) !== 'function')
                    {
                        var continueValue = (typeof(stop) !== 'undefined') ? stop : 0;
                        stop = function (value)
                        {
                            return value != continueValue;
                        };
                    }
                    var result;
                    for (var i=0; i < iterable.length; ++i)
                    {
                        if (stop(result = func(iterable[i])))
                        {
                            break;
                        }
                    }
                    return result;
                }
            },

            time:
            {
                get: function ()
                {
                    var dateObj = new Date();
                    return dateObj.getTime();
                },

                convertDate: function (date, format)
                {
                    try
                    {
                        var d = date.split(' ')[0].split('-');
                        var t = date.split(' ')[1].split(':');
                        return format.replace('Y', d[0]).replace('m', d[1]).replace('d', d[2])
                                     .replace('H', t[0]).replace('i', d[1]).replace('s', d[2]);
                    }
                    catch (e) {}
                    return null;
                }
            },

            benchmark:
            {
                _data: {},
                
                _lastKey: null,

                clear: function (key)
                {
                    if (typeof(key) == 'undefined')
                    {
                        yapyap.utils.benchmark._data = {};
                    }
                    else
                    {
                        delete yapyap.utils.benchmark._data[key];
                    }
                },

                start: function (key)
                {
                    if (typeof(key) == 'undefined')
                    {
                        key = yapyap.utils.generateUniqID();
                    }
                    yapyap.utils.benchmark._lastKey = key;
                    yapyap.utils.benchmark._data[key] =
                    {
                        start: yapyap.utils.time.get()
                    };
                },

                finish: function (key)
                {
                    if (typeof(key) == 'undefined')
                    {
                        key = yapyap.utils.time.benchmark._lastKey;
                    }
                    yapyap.utils.benchmark._data[key].end = yapyap.utils.time.get();
                    yapyap.utils.benchmark._data[key].diff =
                        yapyap.utils.benchmark._data[key].end
                        - yapyap.utils.benchmark._data[key].start;
                    return yapyap.utils.benchmark._data[key];
                },

                stop: function ()
                {
                    return yapyap.utils.benchmark.finish.apply(null, arguments);
                },

                get: function (key)
                {
                    if (typeof(key) == 'undefined')
                    {
                        key = yapyap.utils.benchmark._lastKey;
                    }
                    return yapyap.utils.benchmark._data[key];
                },

                getAll: function ()
                {
                    return yapyap.utils.benchmark._data;
                }
            },

            geometry:
            {
                line:
                {
                    getLength: function (point1, point2)
                    {
                        return Math.sqrt((point2.y - point1.y) * (point2.y - point1.y)
                                + (point2.x - point1.x) * (point2.x - point1.x));
                    }
                },

                region:
                {
                    getCenterPoint: function (region)
                    {
		                return {
			                x: parseInt((region[2] - region[0])/2 + region[0]),
			                y: parseInt((region[3] - region[1])/2 + region[1])
                        };
		            },

                    containsPoint: function (region, point)
                    {
                        return (point.x >= region[0] && point.x <= region[2]
                            &&  point.y >= region[1] && point.y <= region[3]);
                    },

                    getSquare: function (region)
                    {
		                return (region[2] - region[0]) * (region[3] - region[1]);
	                },

                    equals: function (region1, region2)
                    {
                        return (   region1[0] == region2[0] && region1[1] == region2[1]
                                && region1[2] == region2[2] && region1[3] == region2[3]);
                    }
                },

                getElementRegion: function (elem)
                {
                    var elemOffset = elem.offset({scroll: false});
                    return [
                        elemOffset.left,
                        elemOffset.top,
                        elemOffset.left + elem.outerWidth(),
                        elemOffset.top  + elem.outerHeight()
                    ];
                },

                getElementSize: function (elem)
                {
		            var elemOffset = elem.offset({scroll: false});
		            return {
			            l: elemOffset.left,
			            t: elemOffset.top,
                        w: elem.outerWidth(),
			            h: elem.outerHeight()
		            };
	            },

                getClientSize: function ()
                {
		            var docObj = $(document);
		            return {
                        sx: docObj.scrollLeft(),
                        sy: docObj.scrollTop(),
                        w:  docObj.width(),
                        h:  docObj.height()
                    };
		        }
	        }
        };
    }
});