(function() {
    // private variables
    var
    /**
     * @var string JSON-RPC server address
     */
    apiUrl = '/app.php?service=pt/jsonrpc/exec',
    /**
     * @var string API namespace
     */
    apiNamespace = 'drobee',
    /**
     * @var function Callback to handle exceptions
     */
    defaultExceptionsHandler = null;
    /**
     * @var object Some default exceptions
     */
    var exceptions = {
        BadResponseId: {
            name: 'BadResponseId',
            message: 'A response ID seems to be wrong'
        },
        TypeMismatch: {
            name: 'TypeMismatch',
            message: 'Bad query params'
        },
        BadParameters: {
            name: 'BadParameters',
            message: 'One or more of function\'s params are wrong'
        }
    };
    
    DrobeeApi = window.DrobeeApi = function() {
        return new DrobeeApi.fn.init();
    };
    
    /**
     * Main JSON-RPC Query
     *
     * @param string functionName
     * @param array params
     * @param function callback
     * @return XMLHttpRequest
     */
    var query = function(functionName, params, callback) {
        // if params are ok, then...
        if ('object' == typeof params && 'string' == typeof functionName) {
            // cast it to string, because on x86 OS this value will be converted to float on client side
            var id = String(new Date().getTime());
            // create query object in json-rpc format
            var query = {
                jsonrpc: '2.0',
                method: apiNamespace + '.' + functionName,
                id: id,
                params: params
            };
            
            // send query
            return $.post(apiUrl, $.toJSON(query), function (data) {
                // i jeśli callback jest funkcją
                if ('function' == typeof callback) {
                    // to wywołaj ją z parametrami (dane przychodzące, błąd),
                    // przy czym zawsze jeden z parametrów będzie nullem
                    if (id != data.id) {
                        handleExceptions(exceptions.BadResponseId);
                    }
                    callback.apply(this, [data.result, data.error]);
                } else {
                    // jeśli nie ma callback (lub błędny), wykorzystaj standardową
                    // metodę obsługi błędów
                    if (null != data.error) {
                        handleExceptions(data.error);
                    }
                }
            }, 'json');
        } else {
            handleExceptions(exceptions.TypeMismatch);
        }
    }

    /**
     * Funkcja domyślnej obsługi błędów
     *
     * @param object exception
     */
    var handleExceptionsDefault = function(exception) {
        throw exception.name;
    }

    /**
     * Funkcja obsługi błędów - wywołuje domyślną, lub ustawioną przez użytkownika,
     * jeśli jest poprawna
     *
     * @param object exception
     */
    var handleExceptions = function(exception) {
        if (null == defaultExceptionsHandler || 'function' != typeof defaultExceptionsHandler) {
            defaultExceptionsHandler = handleExceptionsDefault;
        }
        defaultExceptionsHandler.call(this, exception);
    }

    // public methods
    DrobeeApi.fn = DrobeeApi.prototype = {
        /**
         * @var string Numer wersji
         */
        version: '0.2',

        /**
         * Prosty konstruktor
         */
        init: function() {
            return this;
        },

        /**
         * Funkcja pozwalająca na zmianę domyślnej funkcji obsługi błędów
         *
         * @param function callback
         * @return bool
         */
        setDefaultExceptionsHandler: function(callback) {
            if ('function' == typeof callback) {
                defaultExceptionsHandler = callback;
                return true;
            }
            return false;
        },

        /**
         * Obsługa błędów
         *
         * @param object exception Obiekt błędu
         * @return mixed
         */
        handleExceptions: function(exception) {
            return handleExceptions(exception);
        },

        // namespaces
        user: {
            /**
             * @var string Nazwa aktualnej przestrzeni nazw
             */
            namespace: 'user',

            /**
             * Logowanie
             *
             * @param string email
             * @param string password
             * @param function callback
             * @return XMLHttpRequest
             */
            login: function(email, password, callback) {
                if ('string' != typeof email || 'string' != typeof password || 6 >= email.length || 3 >= password.length || (callback && 'function' != typeof callback)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.login', {email: email, password: password}, callback);
            },
        	saveUserData: function(){},
        	/**
        	 * Zmiana hasła 
        	 * 
        	 * @param string oldPswd
        	 * @param string newPswd
        	 * @param string retypePswd
        	 * @param function callback
        	 * @return XMLHttpRequest
        	 */
        	changePassword: function(oldPswd,newPswd,retypePswd,callback){
        		if ('string' != typeof oldPswd || 'string' != typeof oldPswd || 'string' != typeof oldPswd ){
        			handleExceptions(exceptions.BadParameters);
                    return false;
        		}	
        		return query(this.namespace + '.changePassword', {oldPswd: oldPswd, newPswd: newPswd, retypePswd: retypePswd}, callback);
        	},
        	/**
        	 * Zmiana hasła 
        	 * 
        	 * @param int warehouse
        	 * @param int lang 0-auto 1-pl 2-ang
        	 * @patam string separator
        	 * @parem string dateFormat
        	 * @parem string currency
        	 * @parem int timezone
        	 * @param function callback
        	 * @return XMLHttpRequest
        	 */
        	saveSettings: function(warehouse,lang,separator,dateFormat,currency,timezone,callback) {
        		if ('number' != typeof lang || 'string' != typeof separator || 'string' != typeof dateFormat){
        			handleExceptions(exceptions.BadParameters);
                	return false;
        		}
        	
        		return query(this.namespace + '.saveSettings', {warehouse: warehouse, lang: lang, separator: separator, dateFormat: dateFormat, currency: currency, timezone: timezone }, callback);
        	}	
        },
        warehouse: {
            /**
             * @var string Nazwa aktualnej przestrzeni nazw
             */
            namespace: 'warehouse',
                        
            /**
             * Zwraca listę magazynów aktualnego użytkownika
             *
             * @param function callback
             * @return XMLHttpRequest
             */
            getList: function(callback) {
                if ((callback && 'function' != typeof callback)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.getList', {}, callback);
            },
            /**
             * Backup
             * 
             * @param warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            backup: function(warehouse,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.backup', {warehouse: warehouse },callback);
            },
            /**
             * Importt
             * 
             * @param warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            importt: function(warehouse,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.import', {warehouse: warehouse },callback);
            },
            /**
             * Pobiera sciezke do logo
             * 
             * @param warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            getLogo: function(warehouse,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.getLogo', {warehouse: warehouse },callback);
            },
            
            /**
             * Zapisuje dane nagłówka dokumentów
             * @param int warehouse
             * @param string firstLine
             * @param string secondLine
             * @param string thirdtLine
             * @param function callback
             * @return XMLHttpRequest
             */
            saveHeader: function(warehouse,firstLine,secondLine,thirdLine,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.saveHeader', {warehouse: warehouse,fL: firstLine, sL: secondLine, tL: thirdLine }, callback);
            },
            /**
             * Usuwa logo
             * @param int warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            deleteLogo: function(warehouse,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	
            	return query(this.namespace + '.deleteLogo', {warehouse: warehouse }, callback);
            },
            /**
             * Pobiera dane nagłówka dokumentów
             * @param int warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            getHeaders: function(warehouse,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	
            	return query(this.namespace + '.getHeaders', {warehouse: warehouse }, callback);
            },
            /**
             * Zapisuje ustawienia raportów
             * @param int warehouse
             * @param array reports
             * @param function callback
             * @return XMLHttpRequest
             */
            saveReports: function(warehouse,reports,callback){
            	if ('number' != typeof warehouse){
           			handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.saveReports', {warehouse: warehouse, reports: reports}, callback);
            },
            /**
             * Pobiera infotmacje o formie płatności
             * @param int warehouse
             * @param int paymentType //0-free 1-standard, 2-premium
             * @param function callback
             * @return XMLHttpRequest
             */
            getPaymentData: function(warehouse,paymentType,callback){
            	if ('number' != typeof warehouse || 'number' != typeof paymentType){
            		handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.getPaymentData', {warehouse: warehouse, paymentType: paymentType}, callback);
            },
            /**
             * Blokuje nadwyzke przedmiotów
             * @param int warehouse
             * @param array items
             * @param function callback
             * @return XMLHttpRequest
             */
            blockItems: function(warehouse,items,callback){
            	if ('number' != typeof warehouse || 'number' != typeof paymentType){
            		handleExceptions(exceptions.BadParameters);
                	return false;
            	}
            	return query(this.namespace + '.blockItems', {warehouse: warehouse, items: items}, callback);
            },	
            /**
             * Ilość przyjęć produktów dla podanego magazynu
             *
             * @param int warehouse ID magazynu
             * @param function callback
             * @return XMLHttpRequest
             */
            countInsertions: function(warehouse, search, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                if (search) {
                    return query(this.namespace + '.countInsertions', {warehouse: warehouse, search: search}, callback);
                } else {
                    return query(this.namespace + '.countInsertions', {warehouse: warehouse}, callback);
                }
            },
            /**
             * Sprawdza czy to ostatnie przyjęcie lub wydanie
             * @param int warehouse ID
             * @param int move
             * @param function callback
             * @return XMLHttpRequest
             */
            isLastMove: function(warehouse, move, callback){
            	return query(this.namespace + '.isLastMove', {warehouse: warehouse, move: move}, callback);
            },
            
            /**
             * Lista przyjęć produktów dla podanego magazynu
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            getInsertionList: function(warehouse, search, callback) {
                // oszukany overload
                if ('function' == typeof search) {
                    callback = search;
                    search = null;
                }
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || ('undefined' != typeof search && 'object' != typeof search)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                if (search) {
                    return query(this.namespace + '.getInsertionList', {warehouse: warehouse, search: search}, callback);
                } else {
                    return query(this.namespace + '.getInsertionList', {warehouse: warehouse}, callback);
                }
            },
            
            /**
             * Ilość wydań produktów dla podanego magazynu
             *
             * @param int warehouse ID magazynu
             * @param function callback
             * @return XMLHttpRequest
             */
            countDeletions: function(warehouse, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.countDeletions', {warehouse: warehouse}, callback);
            },

            /**
             * Lista wydań produktów dla podanego magazynu
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            getDeletionList: function(warehouse, search, callback) {
                // oszukany overload
                if ('function' == typeof search) {
                    callback = search;
                    search = null;
                }
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || ('undefined' != typeof search && 'object' != typeof search)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                if (search) {
                    return query(this.namespace + '.getDeletionList', {warehouse: warehouse, search: search}, callback);
                } else {
                    return query(this.namespace + '.getDeletionList', {warehouse: warehouse}, callback);
                }
            },
            /**
             * Usuwa zamówienia
             * 
             * @param int warehouse
             * @param int move
             * @param XMLHttpRequest
             */
            deleteMove: function (warehouse,move,callback){
            	return query(this.namespace + '.deleteMove',{warehouse: warehouse, move: move},callback);
            },	
            /**
             * Dodaje (przyjmuje) produkt(y) do magazynu
             *
             * @param int warehouse ID magazynu
             * @param string contractor
             * @param array items Produkty do dodania w formacie array(array('id' => int, 'name' => string, 'amount' => double, 'cost' => double))
             * @param int move
             * @param function callback
             * @return XMLHttpRequest
             */
            insertInventories: function(warehouse, contractor, items, move, callback) {
            	if ('function' == typeof move) {
                    callback = move;
                    move = false;
                }
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || 'object' != typeof items || 0 >= items.length) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	if(move){
            		return query(this.namespace + '.editMove', {warehouse: warehouse, contractor: contractor, items: items, move: move}, callback);
            	}else{	
            		return query(this.namespace + '.insertInventories', {warehouse: warehouse, contractor: contractor, items: items}, callback);
            	}
            },

            /**
             * Usuwa (wydaje) produkt(y) do magazynu
             *
             * @param int warehouse ID magazynu
             * @param array items Produkty do usunięcia w formacie array(array('name' => string, 'amount' => double, 'cost' => double))
             * @param function callback
             * @return XMLHttpRequest
             */
            deleteInventories: function(warehouse, contractor, items, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || 'object' != typeof items || 0 >= items.length) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.deleteInventories', {warehouse: warehouse, contractor: contractor, items: items}, callback);
            },
            /**
             * Edycja wydania 
             * 
             * @param warehouse
             * @param contractor nazwa kontrahenta
             * @param array items Produkty do usunięcia
             * @param move id wydania
             * @param function callback
             * @return XMLHttpRequest
             * 
             */
            deleteInventoriesByEdition: function(warehouse, contractor, items, move ,callback){
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || 'object' != typeof items || 0 >= items.length) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.deleteInventoriesByEdition', {warehouse: warehouse, contractor: contractor, items: items, move: move}, callback);
            },
            /**
             * Lista produktów dodanych (przyjętych) w konkretnym przyjęciu
             *
             * @param int warehouse ID magazynu
             * @param int insertion ID przyjęcia
             * @param function callback
             * @return XMLHttpRequest
             */
            getInventoryListByInsertion: function(warehouse, insertion, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || 'number' != typeof insertion || 0 >= insertion) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.getInventoryListByInsertion', {warehouse: warehouse, insertion: insertion}, callback);
            },

            /**
             * Lista produktów wydanych w konkretnym wydaniu
             *
             * @param int warehouse ID magazynu
             * @param int deletion ID wydania
             * @param function callback
             * @return XMLHttpRequest
             */
            getInventoryListByDeletion: function(warehouse, deletion, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || 'number' != typeof deletion || 0 >= deletion) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.getInventoryListByDeletion', {warehouse: warehouse, deletion: deletion}, callback);
            },
            /**
             * Zwraca ilość produktów, zapisanych w magazynie lub ilość kontrahentów
             *
             * @param int warehouse ID magazynu
             * @param string contractors
             * @param function callback
             * @return XMLHttpRequest
             */
            countAllItems: function(warehouse,contractors, callback){
            	if ('function' == typeof contractors) {
                    callback = contractors;
                    contractors = '';
                }
            	if(contractors){
            		return query(this.namespace + '.countAllItems', {warehouse: warehouse, contractors: true}, callback);
            	}else{	
            		return query(this.namespace + '.countAllItems', {warehouse: warehouse, contractors: false}, callback);
            	}	
            },	
            /**
             * Zwraca ilość produktów, znajdujących się w danym magazynie po przefiltrowaniu 
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            countInventories: function(warehouse, search, callback) {
                // oszukany overload
                if ('function' == typeof search) {
                    callback = search;
                    search = null;
                }
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || ('undefined' != typeof search && 'object' != typeof search)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                if (search) {
                    return query(this.namespace + '.countInventories', {warehouse: warehouse, search: search}, callback);
                } else {
                	
                    return query(this.namespace + '.countInventories', {warehouse: warehouse}, callback);
                }
            },
            /**
             * Zwraca listę znanych produktów, wraz z aktualną ich ilością.
             *
             * @param int warehouse ID magazynu
             * @param int limit - ile pobrać ?
             * @param int offset - od jakiego zacząć ?
             * @param string orderBy - po czym sortować 
             * @param boolean desc - jak sortowac
             * @param boolean withMissing - czy podbrac przedmioty ktorych stan w magazynie =0
             * @param function callback
             * @return XMLHttpRequest
             */
            getInventoryMyList: function(warehouse, limit, offset, orderBy, desc,withMissing, callback) {
            	
            	// oszukany overload
                if ('function' == typeof limit) {
                    callback = limit;
                    limit = 0;
                    offset = 0;
                    orderBy = '';
                    desc = false;
                }
                if ('function' == typeof offset) {
                    callback = offset;
                    offset = 0;
                    orderBy = '';
                    desc = false;
                }
                if ('function' == typeof orderBy) {
                    callback = orderBy;
                    orderBy = '';
                    desc = false;
                }
                if ('function' == typeof desc) {
                    callback = desc;
                    desc = false;
                }
                
            	return query(this.namespace + '.getInventoryMyList', {warehouse: warehouse, limit: limit, offset: offset, orderBy: orderBy, desc: desc,withMissing:withMissing}, callback);
            	
            },
            /**
             * Zwraca listę tagów, razem z ich czestotliwoscia przyporządkowanych do magazynu.
             *
             * @param int warehouse ID magazynu
             * @param string contractor
             * @param string like 
             * @param function callback
             * @return XMLHttpRequest
             */
            getWarehouseTags: function(warehouse, contractor, like, callback) {
            	// oszukany overload
               	if ('function' == typeof contractor) {
                    callback = contractor;
                    contractor = '';
                    like = '';
               	}
                if ('function' == typeof like){
                	callback = like;
                	like = '';
                }	
                
                if (like){
                	return query(this.namespace + '.getWarehouseTagsLike', {warehouse: warehouse, like:like}, callback);
                	
                }if(contractor){
                	return query(this.namespace + '.getWarehouseTags', {warehouse: warehouse, contractors: true}, callback);
                }else{
                	return query(this.namespace + '.getWarehouseTags', {warehouse: warehouse, contractors: false}, callback);
                }
            },
            /**
             * Zwraca listę .
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param string contractor
             * @param function callback
             * @return XMLHttpRequest
             */
            getItemByTag: function(warehouse, search, contractor, callback) {
                // oszukany overload
            	
            	if ('function' == typeof contractor) {
                    callback = contractor;
                    contractor = false;
                }
                
                if(contractor){
                	return query(this.namespace + '.getItemByTag', {warehouse: warehouse, search:search, contractor: true}, callback);
                }else{
                	
                	return query(this.namespace + '.getItemByTag', {warehouse: warehouse, search:search, contractor: false}, callback);
                }
            },
            /**
             * Zwraca przefiltrowaną listę produktów, znajdujących się w danym magazynie, wraz z aktualną ich ilością.
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            getInventoryList: function(warehouse, search, callback) {
                // oszukany overload
                if ('function' == typeof search) {
                    callback = search;
                    search = null;
                }
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || ('undefined' != typeof search && 'object' != typeof search)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                if (search) {
                    return query(this.namespace + '.getInventoryList', {warehouse: warehouse, search: search}, callback);
                } else {
                    return query(this.namespace + '.getInventoryList', {warehouse: warehouse}, callback);
                }
            },
            /**
             * Zapisuje informacje o produkcie.
             *
             * @param int warehouse ID magazynu
             * @param array item
             * @param function callback
             * @return XMLHttpRequest
             */
            saveItemInfo: function(warehouse, item, callback) {
                
            	return query(this.namespace + '.saveInventory', {warehouse: warehouse, item: item}, callback);
                
            },
            /**
             * Zwraca informacje o produkcie.
             *
             * @param int warehouse ID magazynu
             * @param int item id Nazwa produktu
             * @param function callback
             * @return XMLHttpRequest
             */
            getItemInfo: function(warehouse, item, callback) {
                
                return query(this.namespace + '.getItemInfo', {warehouse: warehouse, item: item}, callback); 
            },
            obsessiveAlert: function(warehouse, callback) {
            	return query(this.namespace + '.obsessiveAlert', {warehouse: warehouse}, callback); 
            },	
            /**
             * Zwraca statystyki produktu.
             *
             * @param int warehouse ID magazynu
             * @param int itemId id produktu
             * @param function callback
             * @return XMLHttpRequest
             */
            getItemStats: function(warehouse, itemId, callback) {
            	
                return query(this.namespace + '.getItemStats', {warehouse: warehouse, id: itemId}, callback);
                
            },
            /**
             * Zwraca ostatnią cenę.
             *
             * @param int warehouse ID magazynu
             * @param string item nazwa produktu
             * @param function callback
             * @return XMLHttpRequest
             */
            getLastPrice: function(warehouse, item, callback) {
            	
                return query(this.namespace + '.getLastPrice', {warehouse: warehouse, item: item}, callback);
                
            },
            /**
             * tempppp
             * 
             * @param int warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            temp: function(warehouse,callback) {
            	
                return query(this.namespace + '.temp', {warehouse: warehouse}, callback);
                
            },
            /**
             * Zwraca listę nazw produktów wraz z ich ID, znajdujących się w danym magazynie
             * (niezależnie czy ich stan magazynowy jest równy 0)
             *
             * @param int warehouse ID magazynu
             * @param function callback
             * @return XMLHttpRequest
             */
            getInventoryNameList: function(warehouse, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.getInventoryNameList', {warehouse: warehouse}, callback);
                return this;
            },
            /**
             * Zlicza listę produktów z podanym ciągiem znaków dla danego magazynu,
             * użyteczne do wyświetlania listy podpowiedzi produktów
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            countInventoryNames: function(warehouse, search, callback) {
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) ) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.countInventoryNames', {warehouse: warehouse, search: search}, callback);
            },	
            /**
             * Zwraca listę produktów z podanym ciągiem znaków dla danego magazynu,
             * użyteczne do wyświetlania listy podpowiedzi produktów
             *
             * @param int warehouse ID magazynu
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            findInventoryNames: function(warehouse, search, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) ) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
                
                return query(this.namespace + '.findInventoryNames', {warehouse: warehouse, search: search}, callback);
            },
            /**
             * Pobiera stan przedmiotu w magazynie.Jeśli nie ma to go tworzy(nie stan przedmiot tworzy...)
             * 
             * @param int warehouse
             * @param string name
             * @param function callback 
             * @return XMLHttpRequest
             */
            getOrAddItem: function (warehouse, name, callback) {
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) ) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	
            	return query(this.namespace + '.getOrAddItem', {warehouse: warehouse, name: name}, callback);
            },
            /**
             * Zwraca listę przedmiotów, których stan spadł poniżej stanu minimalnego
             * 
             * @param int warehouse id
             * @param int list id
             * @param function callback
             * @return XMLHttpRequest
             */
            reloadShoppingList: function(warehouse,list,callback){
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) ) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.reloadShoppingList', {warehouse: warehouse, list: list}, callback);
            },	
            /**
             * Zwraca listę przedmiotów, których stan spadł poniżej stanu minimalnego
             * 
             * @param int warehouse id
             * @param function callback
             * @return XMLHttpRequest
             */
            getItemsToBuy: function(warehouse,callback){
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) ) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.getItemsToBuy', {warehouse: warehouse}, callback);
            },
            /**
             * Zapisuje listę zakupów w bazie
             * 
             * @param int warehouse id
             * @param int id
             * @param string name
             * @param array shoppingList
             * @param function callback
             * @return XMLHttpRequest
             */
            saveShoppingList: function(warehouse,id,name,shoppingList,callback) {
            	return query(this.namespace + '.saveShoppingList', {warehouse: warehouse, id: id, name: name, itemList:shoppingList}, callback);
            },
            /**
             * Pobiera listę zakupów w pdfie
             * 
             * @param int warehouse
             * @param string name
             * @param array shoppingList
             * @param function callback
             * @return XMLHttpRequest
             */
            getShoppingListPdf: function(warehouse, name, shoppingList, callback){
            	return query(this.namespace + '.getShoppingListPdf', {warehouse: warehouse, name: name, shoppingList:shoppingList}, callback);
            },	
            /**
             * Zwraca listę kontrahentów z podanym ciągiem znaków dla danego magazynu,
             * użyteczne do wyświetlania listy podpowiedzi produktów
             *
             * @param int warehouse ID magazynu
             * @param int limit
             * @param int offset
             * @param array search
             * @param function callback
             * @return XMLHttpRequest
             */
            findContractorsNames: function(warehouse, limit, offset, search, callback) {
                if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback) || ('string' != typeof search)) {
                    handleExceptions(exceptions.BadParameters);
                    return false;
                }

                return query(this.namespace + '.findContractorsNames', {warehouse: warehouse, limit:limit, offset:offset, search: search}, callback);
            },
            /**
             * Pobiera listę list zakupów
             * 
             * @param int warehouse id magazynu
             * @param function callback
             * @return XMLHttpRequest
             */
            getUsersLists: function (warehouse, callback) {
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback)){
            		handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.getUsersLists', {warehouse: warehouse}, callback);
            },
            /**
             * pobiera typ aktualnego abonamentu
             * 
             * @param int warehouse
             * @param function callback
             * @return XMLHttpRequest
             */
            getPaymentMode: function (warehouse, callback) {
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback)){
            		handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.getPaymentMode', {warehouse: warehouse}, callback);
            },
            /**
             * Usuwa listę zakupów
             * 
             * @param int warehouse
             * @param int list
             * @param function callback
             * @return XMLHttpRequest
             */
            deleteShoppingList: function (warehouse,list, callback) {
            	if ('number' != typeof warehouse || 0 >= warehouse || (callback && 'function' != typeof callback)){
            		handleExceptions(exceptions.BadParameters);
                    return false;
                }
            	return query(this.namespace + '.deleteShoppingList', {warehouse: warehouse,list: list}, callback);
            },
            /**
             * Zwraca zdjęcia przedmiotu
             * 
             * @param int warehouse
             * @param int item
             * @param function callback
             * @return XMLHttpRequest
             */
            getAllItemsImage: function (warehouse,item,callback){
            	            	
            	return query(this.namespace + '.getAllItemsImage', {warehouse: warehouse, item: item}, callback);
            	
            },
            /**
             * Zwraca listę znanych kontrahentów.
             *
             * @param int warehouse ID magazynu
             * @param int limit - ile pobrać ?
             * @param int offset - od jakiego zacząć ?
             * @param string orderBy - po czym sortować 
             * @param boolean desc - jak sortowac
             * @param function callback
             * @return XMLHttpRequest
             */
            getContractorsMyList: function(warehouse, limit, offset, orderBy, desc,  callback) {
            	
            	// oszukany overload
                if ('function' == typeof limit) {
                    callback = limit;
                    limit = 0;
                    offset = 0;
                    orderBy = '';
                    desc = false;
                }
                if ('function' == typeof offset) {
                    callback = offset;
                    offset = 0;
                    orderBy = '';
                    desc = false;
                }
                if ('function' == typeof orderBy) {
                    callback = orderBy;
                    orderBy = '';
                    desc = false;
                }
                if ('function' == typeof desc) {
                    callback = desc;
                    desc = false;
                }
                
            	return query(this.namespace + '.getContractorsMyList', {warehouse: warehouse, limit: limit, offset: offset, orderBy: orderBy, desc: desc}, callback);
            	
            },
            /**
             * Zwraca informacje o kontrahencie.
             *
             * @param int warehouse ID magazynu
             * @param int id kontrahenta
             * @param function callback
             * @return XMLHttpRequest
             */
            getContractorInfo: function(warehouse, id, callback) {
                
                return query(this.namespace + '.getContractorInfo', {warehouse: warehouse, id: id}, callback);
                
            },
            /**
             * Statystyki kontrahenta.
             *
             * @param int warehouse ID magazynu
             * @param int id kontrahenta
             * @param function callback
             * @return XMLHttpRequest
             */
            getContractorsStats: function(warehouse, id, callback) {
                
                return query(this.namespace + '.getContractorsStats', {warehouse: warehouse, id: id}, callback);
                
            },
            /**
             * Generowanie raportu
             * 
             * @param int warehouse id
             * @param int contractor id
             * @param int peroid 
             * @param function callback
             * @return XMLHttpRequest
             */
            generateRaport: function(warehouse, contractor, peroid, callback){
            
            	return query(this.namespace + '.generateRaport', {warehouse: warehouse, contractor:contractor, peroid:peroid },callback);
            },
            /**
             * Generowanie pokwitowanie przyjecia
             * 
             * @param int warehouse id
             * @param int insertion id
             * @param function callback
             * @return XMLHttpRequest
             */
            getRecieveDoc: function(warehouse, insertion, callback){
            	return query(this.namespace + '.getRecieveDoc', {warehouse: warehouse, insertion: insertion},callback);
            },
            /**
             * Generowanie pokwitowania wydania
             * 
             * @param int warehouse id
             * @param int deletion id
             * @param function callback
             * @return XMLHttpRequest
             */
            getHandleOutDoc: function(warehouse, deletion, callback){
            	return query(this.namespace + '.getHandleOutDoc', {warehouse: warehouse, deletion: deletion}, callback);
            },	
            /**
             * Zapis szerokości i długości geograficznej siedziby kontrahenta.
             *
             * @param int warehouse ID magazynu
             * @param int id kontrahenta
             * @param float lat
             * @param float lng
             * @param function callback
             * @return XMLHttpRequest
             */
            latlngSave: function(warehouse, id, lat,lng, callback) {
                
                return query(this.namespace + '.latlngSave', {warehouse: warehouse, id: id, lat: lat, lng: lng}, callback);
                
            },
            /**
             * Zapisuje informacje o kontrahencie.
             *
             * @param int warehouse ID magazynu
             * @param array contractor
             * @param function callback
             * @return XMLHttpRequest
             */
            saveContractorInfo: function(warehouse, contractor, callback) {
                
            	return query(this.namespace + '.saveContractor', {warehouse: warehouse, contractor: contractor}, callback);
                
            },
            /**
             * Zapisuje informacje o kontrahencie.
             *
             * @param int warehouse ID magazynu
             * @param string name
             * @param string contractor
             * @param function callback
             * @return XMLHttpRequest
             */
            checkNameIsUnique: function(warehouse, name, contractor, callback) {
            	
            	if ('function' == typeof contractor) {
                    callback = contractor;
                    contractor = false;
                }else{
                	contractor = true;
                }	
            	return query(this.namespace + '.checkNameIsUnique', {warehouse: warehouse, name: name,  contractor: contractor}, callback);
                
            },
            /**
             * Pobiera nazwe i ilość przediotów, których stan w magazynie jest > 0 
             * 
             * @param int $warehouse ID magazynu
             * @param string $orderBy - kolumna po której sortujemy "amount" => amount "dowolny inny" => name
             * @param boolean $desc - jak cortować true -> desc
             * @return array Lista przedmiotów array(array('id'=>int,'name'=>string,'quantity=>float' );
             */
            getMyItems: function(warehouse,orderBy,desc){
            	return query(this.namespace + '.getInventoryMyList', {warehouse: warehouse, limit: null, offset: 0, orderBy: orderBy, desc: desc},callback);
            }
        }
    }

    // Give the init function the DrobeeApi prototype for later instantiation
    DrobeeApi.fn.init.prototype = DrobeeApi.fn;
})();
drobeeApi = new DrobeeApi();

drobeeApi.setDefaultExceptionsHandler(function(exception) {
    //MessageBox.Show('Error', 'Server returns error:<br/><b>' + exception.message + '</b> (' + exception.name + ')');
    MessageBox.Show('Error', 'Server returns error!');
});

var MessageBoxButtons = {
    'OK': ['OK'], // The message box contains an OK button.
    'OKCancel': ['OK', 'Cancel'], // The message box contains OK and Cancel buttons.
    'AbortRetryIgnore': ['Abort', 'Retry', 'Ignore'], // The message box contains Abort, Retry, and Ignore buttons.
    'YesNoCancel': ['Yes', 'No', 'Cancel'], // The message box contains Yes, No, and Cancel buttons.
    'YesNo': ['Yes', 'No'], // The message box contains Yes and No buttons.
    'RetryCancel': ['Retry', 'Cancel'] // The message box contains Retry and Cancel buttons.
                    
}

// Klasa wyświetlająca messagebox
var MessageBox = {
    // Show(String) Sama wiadomość
    // Show(String, String) Tytuł, wiadomość
    // Show(String, String, MessageBoxButtons) Tytuł, wiadomość, przyciski (domyślnie tylko OK)
    // ostatni parametr, to zawsze callback, który w parametrze wrzuci string - nazwę przycisku wciśniętego
    Show: function(title, message, messageBoxButtons, callback) {
        // oszukany overload
        if (!message.length && !messageBoxButtons.length) {
            message = title;
            title = null;
        }
        if (undefined == messageBoxButtons || !messageBoxButtons.length || !$.isArray(messageBoxButtons)) {
            messageBoxButtons = MessageBoxButtons.OK;
        }
        if ('function' == typeof message) {
            callback = message;
            message = null;
        } else if ('function' == typeof messageBoxButtons) {
            callback = messageBoxButtons;
            messageBoxButtons = null;
        }

        // Jakie przyciski?
        var buttons = {};
        $.each(messageBoxButtons, function(name, button) {
            buttons[button] = function() {
                if (null != callback && 'function' == typeof callback) {
                    callback.apply(this, [button]);
                    
                }
                if(button != 'Backup'){
                	$(this).dialog('close');
                }	
            }
        });

        // wyświetl
        $('<div id="dialog" title="' + title + '">' + message + '</div>').dialog({
            resizable: false,
            bgiframe: true,
            modal: true,
            buttons: buttons,
            zIndex: 5000
        });
    }
};

function strip_tags (str, allowed_tags) {
    // Strips HTML and PHP tags from a string
    //
    // version: 909.322
    // discuss at: http://phpjs.org/functions/strip_tags
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Luke Godfrey
    // +      input by: Pul
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Onno Marsman
    // +      input by: Alex
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Marc Palau
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Eric Nagel
    // +      input by: Bobby Drake
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Tomasz Wesolowski
    // *     example 1: strip_tags('<p>Kevin</p> <b>van</b> <i>Zonneveld</i>', '<i><b>');
    // *     returns 1: 'Kevin <b>van</b> <i>Zonneveld</i>'
    // *     example 2: strip_tags('<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>', '<p>');
    // *     returns 2: '<p>Kevin van Zonneveld</p>'
    // *     example 3: strip_tags("<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>", "<a>");
    // *     returns 3: '<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>'
    // *     example 4: strip_tags('1 < 5 5 > 1');
    // *     returns 4: '1 < 5 5 > 1'
    var key = '', allowed = false;
    var matches = [];
    var allowed_array = [];
    var allowed_tag = '';
    var i = 0;
    var k = '';
    var html = '';

    var replacer = function (search, replace, str) {
        return str.split(search).join(replace);
    };

    // Build allowes tags associative array
    if (allowed_tags) {
        allowed_array = allowed_tags.match(/([a-zA-Z0-9]+)/gi);
    }

    str += '';

    // Match tags
    matches = str.match(/(<\/?[\S][^>]*>)/gi);

    // Go through all HTML tags
    for (key in matches) {
        if (isNaN(key)) {
            // IE7 Hack
            continue;
        }

        // Save HTML tag
        html = matches[key].toString();

        // Is tag not in allowed list? Remove from str!
        allowed = false;

        // Go through all allowed tags
        for (k in allowed_array) {
            // Init
            allowed_tag = allowed_array[k];
            i = -1;

            if (i != 0) {i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
            if (i != 0) {i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
            if (i != 0) {i = html.toLowerCase().indexOf('</'+allowed_tag)   ;}

            // Determine
            if (i == 0) {
                allowed = true;
                break;
            }
        }

        if (!allowed) {
            str = replacer(html, "", str); // Custom replace. No regexing
        }
    }

    return str;
}

/**
 * Paginator
 */
(function() {
    /**
     * Klasa do stronicowania
     * 
     */
    Paginator = function(template, makeLinkCallback) {
        return new Paginator.fn.init(template, makeLinkCallback);
    };

    var
        /**
         * Obiekt "szablonu" stronicowania"
         *
         * @var object
         */
        paginatorTemplate = {
            container: $('<ul />').addClass('paginator'),
            normal: $('<li />').append($('<a />').addClass('element')),
            selected: $('<li />').addClass('selected').append($('<em />').addClass('element')),
            next: $('<li />').addClass('next').append($('<a />').addClass('element').html('Next')),
            previous: $('<li />').addClass('previous').append($('<a />').addClass('element').html('Previous')),
            more: $('<li />').html('...')
        },
        /**
         * Domyślna ilość elementów na stronę
         *
         * @var int
         */
        defaultItemCountPerPage = 10;

    Paginator.fn = Paginator.prototype = {
        /**
         * Element DOM paginatora
         *
         * @var object Element DOM jQuery
         */
        paginatorElement: null,
        /**
         * Ilość elementów do przestronicowania
         *
         * @var int
         */
        itemCount: 0,
        /**
         * Ilość stron
         *
         * @var int
         */
        pageCount: 0,
        /**
         * Aktualna strona
         *
         * @var int
         */
        currentPageNumber: 1,
        /**
         * Ilość elementów na stronę
         *
         * @var int
         */
        itemCountPerPage: 0,
        /**
         * Ilość linków do stron pokazywanych w paginatorze (reszta ukryta za pomocą wielokropka)
         *
         * @var int
         */
        pageRange: 5,
        /**
         * Funkcja pomocnicza do tworzenia linków (hrefów)
         *
         * @var function
         */
        makeLinkCallback: function(i) {
            var link = document.location;

            if ('' == link.hash) {
                link.hash = '#' + 'page=' + i;
            } else {
                link.hash += ',' + 'page=' + i;
            }

            return link.href;
        },

        /**
         * Konstruktor
         *
         * @param object template Szablon
         * @param function makeLinkCallback Funkcja tworząca link
         * @return Paginator
         */
        init: function(template, makeLinkFunction) {
            if ('undefined' != typeof makeLinkFunction && 'function' != typeof makeLinkFunction) {
                throw '"makeLinkCallback" is not a proper function callback';
            }
            if ('function' == typeof makeLinkFunction) {
                makeLinkCallback = makeLinkFunction;
            }

            if ('undefined' != typeof template && null != template) {
                template = $(template).clone();
                paginatorTemplate.container = template.clone().empty();
                paginatorTemplate.normal = template.find('> .normal');
                paginatorTemplate.selected = template.find('> .selected');
                paginatorTemplate.next = template.find('> .next');
                paginatorTemplate.previous = template.find('> .previous');
                paginatorTemplate.more = template.find('> .more');
            }

            this.itemCountPerPage = defaultItemCountPerPage;

            return this;
        },

        /**
         * Zmienia stronę
         *
         * @param int page Aktualna strona
         * @return Paginator
         */
        setCurrentPageNumber: function(page) {
            if (page != parseInt(page) || page <= 0) {
                throw 'Value is not a positive number';
            }

            if (page <= this.pageCount) {
                this.currentPageNumber = parseInt(page);
            } else {
                this.currentPageNumber = this.pageCount;
            }

            if (null != this.paginatorElement) {
                this.render();
            }

            return this;
        },

        /**
         * Zmienia ilość wyświetlanych elementów na stronę
         *
         * @param int count Ilość elementów na stronę
         * @return Paginator
         */
        setItemCountPerPage: function(count) {
            if (count != parseInt(count) || count <= 0) {
                throw 'Value is not a positive number';
            }
            
            this.itemCountPerPage = parseInt(count);
            this.currentPageNumber = 1;
            this.pageCount = Math.max(1, Math.ceil(this.itemCount / this.itemCountPerPage));
            
            if (null != this.paginatorElement) {
                this.render();
            }
            
            return this;
        },

        /**
         * Zmienia ilość elementów
         *
         * @param int items Ilość elementów
         * @return Paginator
         */
        setTotalItemCount: function(items) {
            if (items != parseInt(items) || items < 0) {
                throw 'Value is not a positive number';
            }

            this.itemCount = parseInt(items);
            this.currentPageNumber = 1;
            this.pageCount = Math.max(1, Math.ceil(this.itemCount / this.itemCountPerPage));
            
            if (null != this.paginatorElement) {
                this.render();
            }

            return this;
        },

        /**
         * Zmienia ilość linków do stron pokazywanych w paginatorze
         *
         * @param int range Zakres stron do pokazania
         * @return Paginator
         */
        setPageRange: function(range) {
            if (range != parseInt(range) || range <= 0) {
                throw 'Value is not a positive number';
            }

            this.pageRange = parseInt(range);

            return this;
        },

        /**
         * Zwraca ilość elementów
         *
         * @return int Ilość elementów
         */
        getTotalItemCount: function() {
            return this.itemCount;
        },

        /**
         * Zwraca bieżący numer strony
         *
         * @return int Aktualny numer strony
         */
        getCurrentPageNumber: function() {
            return this.currentPageNumber;
        },

        /**
         * Zwraca domyślną ilość wyświetlanych elementów na stronę
         *
         * @return int Ilość elementów na stronę
         */
        getDefaultItemCountPerPage: function() {
            return defaultItemCountPerPage;
        },

        /**
         * Zwraca ilość wyświetlanych elementów na stronę
         *
         * @return int Ilość elementów na stronę
         */
        getItemCountPerPage: function() {
            return this.itemCountPerPage;
        },
      
        /**
         * Zwraca ilość linków do stron pokazywanych w paginatorze
         *
         * @return int Zakres stron do pokazania
         */
        getPageRange: function() {
            return this.pageRange;
        },

        /**
         * Zwraca ilość stron
         *
         * @return int Ilość strno
         */
        count: function() {
            return this.pageCount;
        },

        /**
         * Tworzy obiekt paginatora
         *
         * @return object Element jQuery
         */
        render: function() {
            if (null == this.paginatorElement) {
                this.paginatorElement = paginatorTemplate.container.clone();
            } else {
                this.paginatorElement.empty();
            }

            function createPageElement(element, i, text) {
                var link;
                if ('string' != typeof text || null == text) {
                    text = i.toString();
                }

                link = element.find('a');
                link.attr('href', makeLinkCallback.apply(link, [i]));
                element.attr('title', 'Page ' + (i));
                element.find('.element').html(text);

                return element;
            }

            // jeśli nie ma w ogóle stron, zwróć pusty
            if (this.itemCount > this.itemCountPerPage) {

                // link "poprzedni element"
                if (this.getCurrentPageNumber() > 1) {
                    this.paginatorElement.append(createPageElement(paginatorTemplate.previous.clone(), this.getCurrentPageNumber() - 1, messages.previous));
                }

                // wyświetl pierwszy element, jeśli go nie ma wyświetlonego + kropki (more)
                if (this.getCurrentPageNumber() - this.getPageRange() > 1) {
                    this.paginatorElement.append(createPageElement(paginatorTemplate.normal.clone(), 1));
                    // kropki
                    this.paginatorElement.append(paginatorTemplate.more.clone());
                }

                // elementy przed aktualnym
                for (i = Math.max(1, this.getCurrentPageNumber() - this.getPageRange()); i <= (this.getCurrentPageNumber() - 1) && i > 0; ++ i) {
                    this.paginatorElement.append(createPageElement(paginatorTemplate.normal.clone(), i));
                }

                // aktualny element
                this.paginatorElement.append(createPageElement(paginatorTemplate.selected.clone(), this.getCurrentPageNumber()));

                // elementy za aktualnym
                for (i = (this.getCurrentPageNumber() + 1); i <= this.count() && i <= (this.getCurrentPageNumber() + this.getPageRange()); ++ i) {
                    this.paginatorElement.append(createPageElement(paginatorTemplate.normal.clone(), i));
                }

                // wyświetl ostatni element, jeśli go nie ma wyświetlonego + kropki (more)
                if (this.getCurrentPageNumber() + this.getPageRange() < this.count()) {
                    // kropki
                    this.paginatorElement.append(paginatorTemplate.more.clone());

                    this.paginatorElement.append(createPageElement(paginatorTemplate.normal.clone(), this.count()));
                }

                // linkt "następny element"
                if (this.count() > 1 && this.getCurrentPageNumber() < this.count()) {
                    this.paginatorElement.append(createPageElement(paginatorTemplate.next.clone(), this.getCurrentPageNumber() + 1, messages.next));
                }
            }
            
            return this.paginatorElement;
        },

        /**
         * Zwraca paginator w postaci stringa
         *
         * @return string Kod HTML paginatora
         */
        toString: function() {
            var paginator = this.render();
            if ('string' == typeof paginator) {
                return paginator;
            }

            return ($('<div />').append(paginator)).html();
        }
    };

    Paginator.fn.init.prototype = Paginator.fn;
})();

/**
 * "Plugin": nowy event onLocationChange
 */
(function($) {
    // clone object
    var previousLocation = $.extend({}, window.location);
    // Set an interval to check the location changes.
    // This will fire the method that we use to check
    // changes in the window location.
    setInterval(function() {
        // Check to see if the location has changed.
        if (previousLocation.href != window.location.href) {

            // The location has changed. Trigger a
            // change event on the location object,
            // passing in the current and previous
            // location values.
            $(window.location).trigger('change', {
                previousLocation: previousLocation
            });

            // Store the new and previous locations.
            previousLocation = $.extend({}, window.location);
        }
    }, 200); // 5 times per second will be enough
}) (jQuery);

$(function () {
    // Instrukcja do wpisania loginu i hasła
    if (($passwordInput = $('div.userPanel input#login_password1')).length && ($emailInput = $('div.userPanel input#login_email')).length) {
        var defaultPasswordInput = 'Hasło';
        var defaultEmailInput = 'Email';
        
        // funkcja kasująca i pojawiająca instrukcje
        inputChangeValue = function(element, value) {
            if (value == $(element).val()) {
                $(element).val('');
            } else if ('' == $(element).val()) {
                $(element).val(value);
            }
        };

        // na start ustaw domyślny tekst (instrukcę) do pól, jeśli puste
        inputChangeValue($emailInput, defaultEmailInput);
        inputChangeValue($passwordInput, defaultPasswordInput);

        // przypisz do eventu
        $emailInput
            .focus(function() {inputChangeValue($emailInput, defaultEmailInput);})
            .blur(function() {inputChangeValue($emailInput, defaultEmailInput);});

        $passwordInput
            .focus(function() {inputChangeValue($passwordInput, defaultPasswordInput);})
            .blur(function() {inputChangeValue($passwordInput, defaultPasswordInput);});
    }

    // Flashmessenger
    if (($flashMessenger = $('ul#flashMessenger')).length) {
        // stwórz przycisk zamknięcia
        var closeSpan = $('<span>[x]</span>');
        closeSpan.click(function() {
            $flashMessenger.clearQueue().fadeOut('slow');
        });
        
        
        $flashMessenger
            // dodaj przycisk zamknięcia
            .append(closeSpan)
            // pokaż
            .fadeIn()
            // zamigaj, żeby zwrócić uwagę
            .effect('pulsate', {'times': 3}, 300)
            // automatycznie ukryj po 30s
            .delay(30000).fadeOut('slow');

        
    }

    // formularz z głównej z pierwszym elementem do magazynu (trzeba go zapamiętać,
    // zarejestrować użyszkodnika i wyświetlić przy okazji
    if (($firstElement = $('form#startForm')).length) {
        //$.cookie('firstElementItem', null);
        //$.cookie('firstElementQuantity', null);

        $firstElement.submit(function() {
            $.cookie('firstElementItem', $(this).find('input[name="item"]').val(), {expires: 7, path: '/'});
            $.cookie('firstElementQuantity', $(this).find('input[name="quantity"]').val(), {expires: 7, path: '/'});
            window.location = $(this).attr('action');
            return false;
        });
    }
    
    // prosty indicator ajaxa (na każdej stronie)
/*    var ajaxIndicator = $('<div />')
        .css({
            backgroundColor: '#000',
            position: 'fixed',
            top: 0,
            left: 0,
            width: '3px',
            height: '3px'
        })
        .hide();
    $('body').append(ajaxIndicator);
    var ajaxIndicatorInterval;
    
    $('body').ajaxStart(function() {
        ajaxIndicator.show();
        ajaxIndicatorInterval = window.setInterval(function() {
            if ('rgb(0, 0, 0)' == ajaxIndicator.css('backgroundColor') || '#000000' == ajaxIndicator.css('backgroundColor')) {
                ajaxIndicator.css('backgroundColor', '#fff');
            } else {
                ajaxIndicator.css('backgroundColor', '#000');
            }
        }, 333);
    });

    $('body').ajaxStop(function() {
        ajaxIndicator.hide();
        window.clearInterval(ajaxIndicatorInterval);
        ajaxIndicatorInterval = null;
    });*/
});
