var AdoElement = function(config){

    //male oszukanswto
    var that = this;
    
    //Flaga informujaca, czy zewnetrzny skrypt jest w trakcie ladowania
    this.loadingLib = false;
    //Flaga informujaca o tym, czy skrypt slejwa zostal zaladowany/wykonany
    this.slaveLoaded = false;
    //Flaga informujaca, czy cala zabawa z przepisywaniem reklamy do bufora zaczela sie
    this.begin = false;
    //Bufor przechowujacy efekt dzialania przeciazonej metody document.write
    //do czasu jego przetworzenia
    this.buff = ado.placeholder;
    //Tymczasowy bufor
    this.tmpBuff = "";
    //Obiekt z konfiguracja placementu/AdoElementu
    this.config = config;
    //Metoda wywolywana po zaladowaniu placementu
    this.onLoad = config.onLoad;
    //Referencja do Obiektu w drzewie DOM, do ktorego ma zostao wpisana kreacja
    this.DOMElement = {};
    //Wyrazenia regularne potrzebne do procesingu bufora
    this.regs = {
        scriptBegin: /^\s*(<script\s*)(\s*(\w+)\s*=\s*(([^\s\"\'>]{1}[^\s>]*)|\"([^\"]*)\"|\'([^\']*)\'))*[^\w>]*>/i,
        scriptEnd: /(<\/script\b([^>\"\']|\"[^\"]*\"|\'[^\']*\')*>)/i,
        attr: /(\s*(\w+)\s*=\s*(([^\s\"\'>]{1}[^\s>]*)|\"([^\"]*)\"|\'([^\']*)\'))/
    };
    
    this.isOrgWrite = function(t){
    	if (ado.external === false) {
            ado.coreDocumentWrite(t);
            return true;
        }
        else {
            return false;
        }
    };
    
    //Metoda zasteprzeciazajaca aca document.wirte
    this.myWrite = function(t){
    	if (that.isOrgWrite(t) !== false) {
            return;
        }
        that.tmpBuff += t;
        if (that.config.id !== 'anonymous' && that.isPureHTML(that.tmpBuff)) {
            if (that.DOMElement) {
                that.addToDOMElement(that.tmpBuff);
                that.tmpBuff = "";
            }
            return;
        }
    };
    //Metoda przeciazajaca document.writeln
    this.myWriteln = function(t){
        that.myWrite(t+"\n");
    };
    /**
     * Metoda sprawdza, czy text wprowadzany do bufora, jest kompletnym
     * kodem HTML zdatnym do bezposredniego wpisania w DOM
     *
     * @param tekst string
     * @return boolean
     */
    this.isPureHTML = function(tekst){
//        if (!this.regs.scriptBegin.test(tekst) && !this.regs.scriptEnd.test(tekst)) {
//            var reg = /^\s*<[^\/].*?>\s*$/mg;
//            if (reg.test(tekst)) {
//                var begin = tekst.indexOf('<');
//                var end = tekst.lastIndexOf('>');
//                if (begin === -1 || end === -1) {
//                    return false;
//                }
//                var tagNameEnd = tekst.indexOf(" ", begin);
//                var tagName = tekst.substr(begin + 1, tagNameEnd - begin - 1);
//                if (tagName === tekst.substr(end - tagName.length, tagName.length)) {
//                    return true;
//                }
//            }
//        }
        return false;
    };
    /**
     * Czy placement jest masterem
     *
     * @return boolean
     */
    this.isMaster = function(){
        return this.config.master;
    };
    /**
     * Czy placement jest slavem
     *
     * @return boolean
     */
    this.isSlave = function(){
        return this.config.slave;
    };
    /**
     * Metoda uruchamiana przed wykonaniem kodu JS, inlineowego lub bibliotecznego.
     * Pzreciaza odpowiednie metody JS
     */
    this.preDispatch = function(){
        var id = this.config.id;
        //IE mysli, że ado.write === "object"
        if (typeof ado.write !== "function" && typeof ado.write !== "object") {
            ado.write = document.write;
            ado.writeln = document.writeln;
        }
        window.document.open = function(){};
        window.document.close = function(){};
        
        document.write = that.myWrite;
        document.writeln = that.myWriteln;
        that.open = window.document.open;
        that.close = window.document.close;
        
        ado.busy = true;
        if(that.config.id !== "anonymous") { ado.active = that.config.id; }
        
        if (that.tmpBuff !== "" && ado.debug) {
        	throw "tmpBuff NOT empty";
        }
    };
    /**
     * Metoda uruchamiana pod wykonaniu kodu JS.
     * "Oddaje" dostep do metod JS
     *
     * @param go boolean - czy po tym wywoac processing bufora
     */
    this.postDispatch = function(go){
        document.write = ado.write;
        document.writeln = ado.writeln;
        window.document.open = that.open;
        window.document.close = that.close;
        
        ado.busy = false;
        ado.active = "";
        if (typeof document.write === "undefined"){// || document.write === ado.write) {
            document.write = ado.write;
            document.writeln = ado.writeln;
        }
        
        if (that.buff.indexOf(ado.placeholder) !== -1) {
            that.buff = that.buff.replace(ado.placeholder, that.tmpBuff);
        }
        else {
            that.buff = that.tmpBuff + that.buff;
        }
		that.tmpBuff = "";
		
        if (go) {that.go();}
        if (ado.slowQueue.length > 0){
	        var fn = ado.slowQueue.pop();
	        if (typeof fn === "function") {
	            fn();
	        }	
        }
        
    };
    /**
     * Usuwanie komentarzy HTML z bufora
     */
    this.deleteComment = function(){
        var c = /\<!\s*--(.*?)(--\s*\>)/m;
        while (c.test(that.buff)) {
            that.buff = that.buff.replace(c, "");
        }
    };
    /**
     * Przetwarzanie bufora.
     */
    this.go = function(){
		this.deleteComment();
		var arr = [];
        
        // Jezeli w buforze uzbierala sie cala definicja skryptu
        if (this.regs.scriptBegin.test(this.buff) && this.regs.scriptBegin.test(this.buff)) {
            //info("Jezeli w buforze uzbierala sie cala definicja skryptu");
            arr = this.regs.scriptBegin.exec(that.buff);
            var attr = this.processAttributes(arr[0]);
            //dolacz kolejny skrypt
            if (attr.src) {
                this.processSrcScript(arr, attr.src, attr.charset);
            }
            // evaluj
            else if (attr.language !== "VBScript") {
                this.processInlineScript(arr);
            }
            // oszukaj VBScript
            else {
           		// debug("vbscript");
                this.processVBScript(arr);
            }
        }
        // Nie ma pelnej definicji skryptu
        // ale poszukujemy poczatku tej definicji, zeby 
        // od poczatku do miejsca tej definicji wyciagnac HTML-a
        else {
            //info("Nie ma pelnej definicji skryptu");
            if (!this.regs.scriptBegin.test(that.buff)) {
                var regexp = /<script/i;
                arr = regexp.exec(that.buff);
                if (arr) {
                    var index = that.buff.indexOf(arr[0]);
					that.DOMElement = ado.getById(that.config.id);
                    if (that.DOMElement) {
						that.addToDOMElement(this.buff.substr(0, index).replace(ado.placeholder, ""));
                    }
					this.buff = this.buff.substr(index);
                    this.go();
                }
                // a jak nie ma skryptu to przepisuje wszytsko
                else {
                    var rewrite = function(){
                        if (that.DOMElement) {
                            that.addToDOMElement(that.buff.replace(ado.placeholder, ""));
                            that.buff = "";//"ado.placeholder;
                        }
                        //funkcja wywolywana po zaladowaniu reklamy
						if(typeof that.onLoad === "function" && that.begin){
							that.onLoad();
							that.onLoad = false;
						} 
                    };
                    if (/^\s*</.test(that.buff)) {
                        if (/^\s*<.*>\s*$/mg.test(that.buff)) {
                            rewrite();
                        }
                    }
                    else {
                        rewrite();
                    }
                }
            }
        }
        return;
    };
    /**
     * Oszukujemy kod VBScript. Nie wykonujemy go, bo strona pewnie by wybuchla
     * Top jest kaawwalek kodu, ktory pozwoli zatuszowao to, ze z niektorych kreacji
     * nie usunieto jeszcze kodu VBScript
     *
     * @param arr Array
     */
    this.processVBScript = function(arr){
        var scriptEnd = this.regs.scriptEnd.exec(that.buff);
        this.buff = ado.placeholder + this.buff.substr(scriptEnd.index + scriptEnd[0].length);
        that.go();
    };
    /**
     * Wyciagniecie z bufora definicji skryptu zewnetrznego.
     *
     * @param arr Array
     * @param src String
     * @param charset String
     */
    this.processSrcScript = function(arr, src, charset){
        that.buff = that.buff.substr(arr[0].length);
        var scriptEnd = this.regs.scriptEnd.exec(that.buff);
        this.buff = ado.placeholder + this.buff.substr(scriptEnd.index + scriptEnd[0].length);
        this.appendScript(src, charset);
    };
    /**
     * Dolaczanie do DOM zewnetrznego skryptu JS
     *
     * @param url String
     * @params charset String
     */
    this.appendScript = function(url, charset, fast, callback){
    	// Funkcja wywolywana po loadzie zewnetrznego skryptu JS
        var onscriptload = function(object){
            if (that.isMaster()) {
                ado.registerMaster(that.config);
            }
            that.loadingLib = false;
            //przepisz anonimowy bufor do bufora rodzica
            //info("sie dololdlo: "+object.src);
            if (!fast) {ado.rewriteBuffor(object, that);}

            if(typeof callback === "function"){callback();}
            that.postDispatch(true);
        };
        
        this.loadingLib = true;
        
        var id = that.config.id;
        var fn = function(){
            ado.elems[id].preDispatch(true);
            ado.loadScript(url, onscriptload, charset);
        };
        //wrzucamy na kolejko funkcji oczekujacych na wykonanie
        ado.slowQueue.unshift(fn);
        return;
    };
    
    /**
     *	Sprawdza czy jest emisja z serwera i jeżeli tak, odpala
     */
    this.onServerEmission = function(){
    	var empty = function(){
    		if(typeof that.config.onServerEmissionEmpty === "function"){
    			that.config.onServerEmissionEmpty();	
    		}
    	};
    	
    	if(typeof adserver_emissions === "object"){
    		var length = 0;
			for (var i in adserver_emissions) {
				if(typeof adserver_emissions[i] !== "function"){length++;}
			}
    		if(length > ado.adserverEmissions){
	    		ado.adserverEmissions++;
	    		if(typeof that.config.onServerEmission === "function"){
	    			that.config.onServerEmission();	
	    		}
    		}
    		else {empty();}
    	}
    	else {empty();}
    };
    
    /**
     * Przetwarzanie Inlinowego skryptu JS
     *
     * @param arr Array
     */
    this.processInlineScript = function(arr){
        if (that.loadingLib) {
            return;
        }
        this.buff = this.buff.substr(arr[0].length);
        var scriptEnd = this.regs.scriptEnd.exec(that.buff);
        var iS = this.buff.substr(0, scriptEnd.index);
        
        this.buff = ado.placeholder + this.buff.substr(scriptEnd.index + scriptEnd[0].length);
        iS = iS.replace('/*<![CDATA[*/', '');
        iS = iS.replace('/* <![CDATA[ */', '');
        iS = iS.replace('/* ]]> */', '');
        iS = iS.replace('/*]]>*/', '');
        iS = iS.replace('<!--', '');
        iS = iS.replace('//-->', '');

        ado.evaluate = function(code){
        	//debug("ustawiam external = true");
            this.external = true;
            if (window.execScript) {
                window.execScript(code); // eval in global scope for IE
                return null; // execScript doesnt return anything
            }
            var result = globalScope.eval ? globalScope.eval(code) : eval(code);
            //debug("po ado evaluate ustawiam external = false");
            this.external = false;
            return result;
        };
		
        var fn = function(){
            that.preDispatch();
            try {
                ado.evaluate(iS);
            } 
            catch (ex) {
            }
            that.postDispatch(true);
        };
        
        if (ado.busy) {
            ado.slowQueue.unshift(fn);
        }
        else {
            fn();
        }
        return;
        
    };
    /**
     * Wyciagniecie atrybutow z definicji skryptu
     *
     * @param tag string - tag SCRIPT
     * @return Array
     */
    this.processAttributes = function(tag){
        var att = {};
        while (tag.length > 0) {
            var arr = that.regs.attr.exec(tag);
            if (!arr) {
                break;
            }
            tag = tag.substr(arr.index + arr[1].length);
            var value = null;
            if (arr[4]) {
                value = arr[4];
            }
            if (arr[5]) {
                value = arr[5];
            }
            if (arr[6]) {
                value = arr[6];
            }
            att[arr[2].toLowerCase()] = value;
        }
        return att;
    };
    /**
     * Pobranie referencji do Elementu DOM
     */
    this.getDOMElement = function(){
        this.DOMElement = ado.getById(this.config.id);
        if (!this.DOMElement) {
            return;
        }
    };
    
    /**
     * Czyszczenie innerHTML-a DIV-a i jego ukrywanie
     * Trzeba go ukryć bo IE defaultowo nadaje wysokość pustym elementom
     */
    this.emptyDOMElement = function(){
    	if(this.DOMElement){
    		this.DOMElement.innerHTML = "";
    		this.DOMElement.style.display = "none";	
    	}
    	
    };
    
    this.addToDOMElement = function(str){
    	if(this.DOMElement && str !== ""){
	    	this.DOMElement.style.display = "block";
	    	this.DOMElement.innerHTML += str;	
    	}
    };
    
    this.insertToDOMElement = function(str){
    	if(this.DOMElement && str !== ""){
	    	this.DOMElement.style.display = "block";
	    	this.DOMElement.innerHTML = str;
    	}
    };
    
    this.makeUrl = function(){
		if(this.config.preview){this.config.server=this.config.emiter;}
		var ct = this.config.contentType;
		switch(ct){
			case 'txt':
			case 'xml':
			case 'js':
				break;
			default:
				ct = 'js';
				break;
		}
		this.config.url = ado.protocol + "//" + this.config.server;
		this.config.url += "/_"+(new Date()).getTime()+"/ad."+ct+"?id=" + ado.trimAdoPrefix(this.config.orgId) + "/x=" + screen.width + "/y=" + screen.height;
		this.config.url += ado.makeKeywords(this.config.keys) + ado.makeVars(this.config.vars) + ado.makeFlash();
		return;
	};
    
	
	this.appendRedirUrl = function(){
		if(this.config.redir && this.config.redir !== "" && this.config.redir != "<%%REDIR%%>"){
			 this.config.url = this.config.url + "/redir="+this.config.redir;
		}
	};
    /**
     * Start
     *
     * old boolean - czy drukujemy po staremu
     */
    this.dispatch = function(old){
    	if (typeof this.config !== "object") {
            return;
        }
        this.getDOMElement();
        this.emptyDOMElement();
        
        if (typeof this.config !== "object" || !this.config.id || !this.config.server) {
            return;
        }
		ado.init();
        if (ado.protocol.substr(0, 4) !== 'http') {
            return;
        }
        if (!this.isSlave()) {
            if (this.config.preview) {
                this.config.url = this.config.server;
            }
            else {
                this.makeUrl();
            }
        }
        if (old === true) {
            if (this.config.preview && !ado.emiterRequest) {
            	if(this.config.url.indexOf('?') === -1){
            		this.config.url = this.config.url + "?arg=1";	
            	}
            	else {
            		this.config.url = this.config.url + "&arg=1";
            	}
            }
            else {
                this.config.url = this.config.url + "/arg=1";
            }
            this.appendRedirUrl();
            ado.coreDocumentWrite("<scr" + "ipt type='text/javascript' src='" + that.config.url + "'></scr" + "ipt>");
        }
        else {
        	this.appendRedirUrl();
            this.appendScript(this.config.url, null, true);
        }
    };
};

/**
 * Singleton
 */
var AdoContainer = function(){

    var that = this;
    var userAgent = navigator.userAgent.toLowerCase();
    /**
     * Dopisanie do id DOMElementu wartosci z this.iterator
     */
    var tuneId = function(config){
        config.orgId = config.id;
        config.id = config.id + "" + ado.iterator++;
        var de = ado.getById(config.orgId);
        if (de) {
            de.id = config.id;
        }
        return config;
    };
    /**
     * Kodowanie znakow
     *
     * @param s string
     * @return string
     */
    var keywordEncode = function(s){
        var d = "";
        var k = 0;
        var c = '';
        if (!s) {
            return;
        }
        for (k = 0; k < s.length; k++) {
            c = s.charCodeAt(k);
            if (c < 0x80) {
                d += s.charAt(k);
            }
            else 
                if (c >= 0x80 && c <= 0x7ff) {
                    d += String.fromCharCode(((c >> 6) & 0x1f) | 0xc0, (c & 0x3f) | 0x80);
                }
                else {
                    d += String.fromCharCode((c >> 12) | 0xe0, ((c >> 6) & 0x3f) | 0x80, (c & 0x3f) | 0x80);
                }
        }
        return escape(d).replace(/\//g, '%2F').replace(/\@/g, '%40').replace(/\*/g, '%2A').replace(/\+/g, '%2B').replace(/\%/g, '$');
    };
    this.init = function(){
    	ado.intervalID = setInterval(function(){
            ado.continous();
        }, 500);
        /**
         * Referencja do tagu <head> w DOM
         */
        if(typeof ado.head !== "undefined"){return;}
        ado.head = ado.getByTag("head", 0);
        
    };
    this.closed = false;
    /**
     * Tablica z obiektami AdoElement
     */
    this.elems = [];
    /**
     * Tablica ascocjacyjna this[urlExternalScript] = tuneId
     */
    this.urlsMapping = [];
    /**
     * Tablica z obiektami ladowanymi po wykonaniu ado.loadDefered()
     */
    this.deferedElems = [];
    /**
     * placementy czekajoce na ustawienia podglodu
     */
    this.deferPreviews = [];
    /**
     * Hash z zaleznosciami pomiedzy kodami master/slave
     * this.masterSlaves[<id_master>][] = <id_slave>
     */
    this.masterSlaves = [];
    /**
     * Tablica z idkami starymi masterami
     */
    this.masterOld = [];
    /**
     * Tablica ze slavami, ktore sie nie wykonaly, bo master sie jeszcze nie zaladowal.
     * Te slavy sie wyjkonaja jak ich Master sie zaladuje
     */
    this.waitingSlaves = [];
    /**
     * kolejka metod czekajacych na zwolnienie d.write.
     */
    this.slowQueue = [];
    /**
     * oryginal metody document.write z JS
     */
    this.write = document.write;
    /**
     * oryginal metody document.writeln z JS
     */
    this.writeln = document.writeln;
    /**
     * Flaga informujaca, czy przegladarka obsluzy nowe kody placementow.
     * Innymi slowy czy Wymagane jest uzycie oryginalnego document.werite
     */
    this.writeRequired = true;
    /**
     * Flaga, czy oryginalna metoda document.write, jest przeciazona przez inny AdoElement
     */
    this.busy = false;
    /**
     * ID metody setInterval
     */
    this.intervalID = 0;
    /**
     * Wartoso iteratora jest doklejana do ID placementu (DOM Eleemntu)
     * a nastepnie zwiekszana dla zachowania unikalnosci placementow o tym samym dziordziobuchu
     *
     */
    this.iterator = 0;//liczy placementy
    /**
     * Flaga czy wyrzucane beda wyjatki
     */
    this.debug = false;
    /**
     * Do zaznaczenia miejsca w buforze, w ktore to miejsce bede wrzucal zawartoso tmpBuff.
     */
    this.placeholder = "__MARKER__";
    /**
     * Czy wykonuje sie zewnetrzny skrypt.
     */
    this.external = false;
    /**
     * Czy WSZYSTKIE kody/kreacje emitowane w trybie old czy new.
     */
    this.mode = "old"; // lub new
    /**
     * Czy laduja sie ustawienia podgladu na zywo
     */
    this.loadingPreviewSettings = false;
    /**
     * Czy to leci request do placementu check
     */
    this.emiterRequest = false;
    /**
     * podrasowane id placementu przetwarzanego
     */
    this.active = "";
    /**
     * wlaczanie encodowania znakow diarektycznych w keywordach
     */
    this.characterEncoding = true;
    /**
     * czy skrypt dziala na stronie XML
     */
    this.xml = false;
    /**
     * URL pseudoemitera. Ustawiany z poziomu kreacji po odwolaniu sie do placementu checkedID
     */
    this.previewUrl = "";
    /**
     * Tablica z URL-ami emiterow
     */
    this.previewEnabled = [];
    /**
     * Tablica pomocnicza
     */
    this.tmp = [];
    /**
     * Ilosc emisji z adserwera, dla kont, ktore maja wlaczona te funkcjonalnosc
     */
    this.adserverEmissions = 0;
    /**
     * Flaga ustawiana po załadowaniu się dokumentu, potrzebna do live preview
     */
    this.windowLoad = false;
    /**
     * Protokol
     */
    this.protocol = "";
    /**
     * Wykrywanie przegladarki
     */
    this.browser = {
        version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
        safari: /webkit/.test(userAgent),
        opera: /opera/.test(userAgent),
        msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
        mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)
    };
    /**
     * Zmienne ustawiane w kreacj kampanii administracyjnej dla LivePreview
     * Umieszczone w celach dokumentacji i porzadku
     */
    /**
     * this.previewEnabled['<%%HOST%%>'] = true;
	 * this.previewUrl = "https://preview.adocean.pl/adman/previewcode_ex.php";
	 * this.previewDisableUrl = "<%%HOST%%>/adredir/id=<%%DISABLE_ID%%>/url=http://preview.adocean.pl/adman/preview_cancel.php"
	 * this.emiterCountryCode = "<%%EMITER_LANG%%>";
	 * this.translate = {'pl': 'Wylacz','en': 'Close','cz': 'Vypnout','hu': 'Letiltsa','ru': 'Pa ruski'}; 
     */
    /**
     * Metoda odpalana do czasu az wszystko w buforach elementow bedzie posprzatane
     */
    var times = 0;
    
    /**
     * Metoda do setupu ustawien ADO
     *
     * @cfg.mode - dzialanie po staremu/nowemu
     * @cfg.xml - flaga czy kod dziala na stronie XML
     * @cfg.characterEncoding - encodowanie kluczy
     *
     */
    this.config = function(cfg){
    	this.mode = cfg.mode;
    	this.xml = cfg.xml;
    	this.characterEncoding = cfg.characterEncoding;
    	if(cfg.protocol){
    		this.protocol = cfg.protocol;
    	}
    	else {
    		this.protocol = location.protocol;
    	}
    };
    
    /**
     * Mapowanie oryginalnego dziordziobucha na jego nowa (podrasowana) wartoso
     *
     * @param id string
     * @param soft boolean - nie sprawdzam czy begin = true, potrzebne do onServerEmission przy mode = old;
     * @return string|boolean
     */
    this.resolvId = function(id, soft){
        for (var i in ado.elems) {
        	if(ado.elems[i].config){
	        	if(soft){
		        	if (ado.elems[i].config.orgId === id || ado.elems[i].config.orgId === "ado-"+id) {return i;}	
	        	}
	        	else {
	        		if ((ado.elems[i].config.orgId === id || ado.elems[i].config.orgId === "ado-"+id) && ado.elems[i].begin === false) {return i;}	
	        	}	
        	}
        }
        return false;
    };
    
    
    this.continous = function(){
    	if (!ado.busy) {
            var empty = /^\s*$/;
            for (var i in this.elems) {
                if (typeof this.elems[i] === "object" && (!empty.test(this.elems[i].tmpBuff) || !empty.test(this.elems[i].buff))) {
                	if (typeof this.elems[i].postDispatch === "function"){
                		this.elems[i].postDispatch(true);
                		return;
                	}
                }
            }
            times++;
            times >= 3 ? clearInterval(ado.intervalID) : "";
        }
        return;
    };
    /**
     * Metoda odpala ladowanie placementow zdefiniowanych z opcja defer.
     */
    this.loadDefered = function(){
        if (!this.writeRequired) {return;}
        while (this.deferedElems.length > 0) {
            var config = this.deferedElems.shift();
            if(typeof config !== "object") {return;}
            if (config.old && config.defer) {
                if (ado.debug) {throw "You cannot use both 'defer' and 'old' options in placement confirguration";}
                else {return;}
            }
            delete (config.defer);
            if (config.master) {this.master(config);}
            else if (config.slave) {this.slave(config.id, config);}
            else {ado.placement(config);}
        }
    };
    
    this.loadDeferedPreview = function(){
        if (!this.writeRequired) {return;}
//        alert(this.deferPreviews.length);
        
        while (this.deferPreviews.length > 0) {
            var config = this.deferPreviews.shift();
            if(typeof config !== "object") {return;}
            if (config.old && config.defer) {
                if (ado.debug) {throw "You cannot use both 'defer' and 'old' options in placement confirguration";}
                else {return;}
            }
            delete (config.defer);
            if (config.master) {ado.master(config);}
            // to nizej nie jest potrzebne, bo ładowanie opoznionych slaveów załatwioa master
            else if (config.slave) {ado.slave(config.id, config);}
            else {ado.placement(config);}
        }
    };
    
    this.rewriteBuffor = function(object, context){
        ado.endExternal();
        if (typeof this.elems.anonymous === "object") {
            context.buff = this.elems.anonymous.tmpBuff + context.buff;
	    this.elems.anonymous.tmpBuff = "";
            context.go();
        }
    };
    
    this.beginExternal = function(){
    	//debug("beginExternal external = true");
    	this.external = true;
    	if(ado.active === "" || ado.mode === "old"){return;}
    	
        if (typeof this.elems.anonymous !== "object") {
            this.elems.anonymous = new AdoElement({
                id: "anonymous"
            });
        }
        this.elems.anonymous.preDispatch();
    };
    
    this.endExternal = function(){
    	//debug("external = false w endexternal");
    	this.external = false;
    	//nie wiem jeszcze czy to dobre i nie wie czy to wogóle potrzebne jest
        //if(ado.mode == "old"){return;}
    };
    
    this.addAdoPrefix = function(config){
    	if(config.id.length === 46){config.id = "ado-"+config.id;}
    	return config;
    };
    
    this.trimAdoPrefix = function(orgId){
    	if(orgId.length == 46){return orgId;}
		else if (orgId.indexOf('ado-') === 0){
			return orgId.substring(4,50);
		}
		else { return orgId; }
	};
    
    /**
     * Metoda umieszczana na poczatku kazdej kreacji w emiterze
     *
     * @param config.id String
     * @param config.arg - jezeli ustawione to wtedy drukujemy po staremu
     * @param config.fast - czy wlaczamy wersje fast
     */
    this.beginCreative = function(config){
    	if(ado.loadingPreviewSettings){return;}
    	ado.turnOffPreview();
    	
    	// Odpalenie zdarzenia onServerEmission
    	var tmp = this.elems[ado.resolvId(config.id, true)];
    	if(tmp){ tmp.onServerEmission(); }
        
        if (config.arg !== "" && config.arg !== "<%%ARG%%>") {
            return { config: {old:true} };
        } // znaczy, ze stary placement
		
        var resolvedId = ado.resolvId(config.id);
        
        if (!resolvedId || ado.mode == "old") {
            return { config: {old:true} };
        }
        //debug("beginCreative external = true");
        this.external = true;
        
        if (!ado.getById(resolvedId)) {
        	return { config: {old: true} };
            //throw new Error("Emiter Error! Wrong id parameter.");
        }
        this.elems[resolvedId].begin = true;
        if (!this.isBrowserSupport()) {ado.mode = "old";}// nie ma beginCreative
        else {this.elems[resolvedId].preDispatch();}
        return this.elems[resolvedId];
    };
    
    this.turnOffPreview = function(){
 		for(var i in ado.previewEnabled) {
 			if(typeof ado.previewEnabled[i] !== "function"){
 				if(ado.previewEnabled[i]) {ado.tmp.push(i);}
 				ado.previewEnabled[i] = false;	
 			}
 		}
    };

    /**
     * Metoda uruchamiana z poziomu pseudoemitera
     */
    this.turnOnPreview = function(){
    	for(var i in ado.tmp) {
    		if(typeof ado.previewEnabled[ado.tmp[i]] !== 'function'){
    			ado.previewEnabled[ado.tmp[i]] = true;	
    		}
 		}
 		ado.tmp = [];
    };
    
    this.refresh = function(id){
    	ado.init();
    	var elem = ado.elems[ado.resolvId(id, true)];
    	if(elem.isMaster()){
    		ado.master(elem.config);
    		for (var i in ado.elems) {
                if (typeof ado.elems[i] === "object" && ado.elems[i].config.slave && ado.elems[i].config.myMaster === elem.config.orgId) {
                    ado.elems[i].emptyDOMElement();
                    ado.slaveStart(ado.elems[i].config);
                }
            }
    	}
    	else {
    		ado.placement(elem.config);
    	}
    };
    
    /**
     * W przypadku wlaczonego podgladu i starej wersji kodow i braku pogdladu dla danego placementu
     * odpalamy z poziomu pseudo emitera te funkcje. (bo inaczej sie nie da :)
     */
//    this.writeAdFromEmiter = function(orgId){
//    	var elem = ado.elems[ado.resolvId(orgId)];
//    	if(elem.config.old){
//    		elem.makeUrl();
//    		ado.coreDocumentWrite("<scr" + "ipt type='text/javascript' src='" + elem.config.url + "'></scr" + "ipt>");
//    	}
//    	else {
//    		elem.makeUrl();
//			elem.appendScript(elem.config.url, null, true);
//    	}
//    };
    
	/**
     * Definicja zwyklego placementu
     *
     * @param config Object
     */
    this.placement = function(config){
    	if (!config.id || !config.server) {
            if (ado.debug) {throw "Missing fileds in config object in ado.placement().";}
            else {return;}
        }
        
        if (config.defer) {
        	ado.getById(config.id).style.display = "none";
            this.deferedElems.push(config);
            return;
        }
        //Obejscie buga w IE, ktory nie pozwala dodac Childa do HEAD-a, jak HEAD sie laduje.
        if(typeof ado.onHeadLoad === "function"){
        	ado.onHeadLoad();
        	ado.onHeadLoad = null;
        }
        
        if(ado.mode == "new" && ado.loadingPreviewSettings && !config.old && this.isBrowserSupport()){
            ado.deferPreviews.push(config);
            return;
        }
        if (!config.orgId) {
            config = tuneId(config);
        }
        if (config.timeout) {
            if (config.old) {
                if (ado.debug) {throw "You cannot use both 'timeout' and 'old' options in placement confirguration.";}
                else {return;}
            }
            
            //Wyszczysc stary placement
            if (ado.elems[config.id]) {
                ado.elems[config.id].emptyDOMElement();
            }
        }
        if (config.timeout) {
        	setTimeout(function(){
                ado.init();
                ado.placement(config);
            }, config.timeout);
        }
        
        //Preview codes
        if(ado.previewEnabled['http://'+config.server]){
            config.preview = true;
            config.server = ado.previewUrl + "?id=" + ado.trimAdoPrefix(config.orgId);
//			config.server = "https://preview.adocean.pl/adman_deploy_8_2008/previewcode_ex.php?id="+config.orgId;
        }
        if(ado.mode === "old"){
            config.old = true;
        }
        this.elems[config.id] = new AdoElement(config);
		//if(!ado.elems[config.id].DOMElement){return;}
		
        // IE BUG FIX 
        this.elems[config.id].begin = false;
        if (!this.isBrowserSupport() || config.old) {
        	if(!ado.emiterRequest){ this.elems[config.id].begin = true; }
            this.elems[config.id].dispatch(true);
        }
        else {
			this.elems[config.id].dispatch(false);
        }
    };
    /**
     * Definicja placementu master
     *
     * @param config Object
     */
    this.master = function(config){
        config.master = true;
        if (config.defer) {
            this.deferedElems.push(config);
            return;
        }
        if(ado.mode === "new" && ado.loadingPreviewSettings && !config.old && this.isBrowserSupport()){
            ado.deferPreviews.push(config);
            return;
        }
        
        if (!config.orgId) {
            config = tuneId(config);
        }
        
        if (config.timeout) {
            if (config.old) {
                if (ado.debug) {
                    throw "You cannot use both 'timeout' and 'old' options in master confirguration.";
                }
                else {
                    return;
                }
            }
            
//            if (ado.elems[config.id] && ado.elems[config.id].DOMElement) {
//                ado.elems[config.id].emptyDOMElement();
//            }
            
            var timeoutFunction = function(){
                ado.master(config);
                for (var i in ado.elems) {
                    if (typeof ado.elems[i] === "object" && ado.elems[i].config.slave && ado.elems[i].config.myMaster === config.orgId) {
                        ado.elems[i].emptyDOMElement();
                        ado.slaveStart(ado.elems[i].config);
                    }
                }
            };
            setTimeout(timeoutFunction, config.timeout);
        }
        
        if (typeof this.masterSlaves[config.orgId] === "undefined") {
            this.masterSlaves[config.orgId] = [];
        }
        this.elems[config.id] = new AdoElement(config);
        
        //Preview codes
        if(ado.previewEnabled['http://'+config.server]){
            config.preview = true;
            config.server = ado.previewUrl + "?id=" + ado.trimAdoPrefix(config.orgId);
//			config.server = "https://preview.adocean.pl/adman_deploy_8_2008/previewcode_ex.php?id="+config.orgId;
        }
        if (config.old || ado.mode === "old") {
       		this.masterOld[config.id] = true; 
            this.elems[config.id].dispatch(true);
        }
        else {
			ado.init();
            this.elems[config.id].dispatch(false);
        }
    };
    /**
     * Metoda odpalana po zaladowaniu placementu typu MASTER
     *
     * @param config Object
     */
    this.registerMaster = function(config){
        var isWaiting = function(fn){
            for (var i in ado.waitingSlaves) {
                if (i === fn) {
                    return true;
                }
            }
            return false;
        };
        if (!config.defer) {
            for (var j in that.masterSlaves[config.orgId]) {
                if (typeof that.masterSlaves[config.orgId][j] === "string") {
                    for (var k in ado.elems) {
                        if (typeof ado.elems[k] === "object" && ado.elems[k].config.orgId === that.masterSlaves[config.orgId][j]) {
                            // Sprawdzamy czy DOMElementy tych slaveow sa juz w drzewie dom 
                            // i czy sie wczesniej nie zaladowaly 
                            // i czy wczesniej czekaly na uruchomienie
                            if (!ado.elems[k].slaveLoaded && ado.elems[k].DOMElement && isWaiting(that.masterSlaves[config.orgId][j])) {
                                // jezeli tak to odpalamy dispatch tych slaveow (IE, Safari)
                                ado.slaveStart(ado.elems[k].config);
                            }
                            // jak nie to zostawiamy to w spokoju (FF, Opera)
                        }
                    }
                }
                
            }
        }
    };
    /**
     * Definicja placementu typu SLAVE
     *
     * @param fnName String
     * @param config Object
     */
    this.slave = function(fnName, config){
        if (!fnName || typeof fnName !== "string" || fnName === "") {
            if (ado.debug) {
                throw "Missing fnName param in ado.slave()";
            }
            else {
                return;
            }
        }
        if (!config || !config.myMaster) {
            if (ado.debug) {
                throw "Missing myMaster field in configuration object in ado.slave()";
            }
            else {
                return;
            }
        }
        
        //Obejscie buga w IE, ktory nie pozwala dodac Childa do HEAD-a, jak HEAD sie laduje.
        if(typeof ado.onHeadLoad === "function"){
        	ado.onHeadLoad();
        	ado.onHeadLoad = null;
        }
        
        var masterDefered = function(masterId){
            for (var i in ado.deferedElems) {
            	if(typeof ado.deferPreviews[i] === "object" && typeof ado.deferPreviews[i].id !== "undefined"){
            		if (ado.deferedElems[i].id === masterId) {
	                    return true;
	                }
            	}
            }
            return false;
        };
        
        var masterDeferedPreview = function(masterId){
            for (var i in ado.deferPreviews) {
            	if(typeof ado.deferPreviews[i] === "object" && typeof ado.deferPreviews[i].id !== "undefined"){
            		if (ado.deferPreviews[i].id === masterId) {
	                    return true;
	                }
            	}
            }
            return false;
        };
        
        if (typeof config !== "object") {config = {};}
        if (!config.id) {config.id = fnName;}
        config.slave = true;
        
        if (config.defer || masterDefered(config.myMaster)) {
        	ado.getById(config.id).style.display = "none";
            ado.deferedElems.push(config);
            return;
        }
        if(masterDeferedPreview(config)){
            ado.deferPreviews.push(config);
            return;
        }
        
        if (typeof this.masterSlaves[config.myMaster] !== "object") {
            this.masterSlaves[config.myMaster] = [];
        }
        this.masterSlaves[config.myMaster].push(fnName);
        if (!config.orgId) {
            config = tuneId(config);
        }
        this.elems[config.id] = new AdoElement(config);
        this.elems[config.id].getDOMElement();
        this.elems[config.id].emptyDOMElement();
        
        if (!this.isBrowserSupport() || config.old || ado.mode === "old") {
            ado.coreDocumentWrite("<scr" + "ipt type='text/javascript'>if(typeof " + config.orgId + "=='function') {" + config.orgId + "();}</scr" + "ipt>");
            //odpalenie eventu onload dla slejwa w mode = old;
            if(typeof config.onLoad === "function"){
            	config.onLoad();
            	config.onLoad = false;
            }
        }
        else {
            this.slaveStart(config);
        }
    };
    /**
     * Odpalanie ladowania placementu typu slave
     */
    this.slaveStart = function(config){
        try {
        	eval(config.orgId);
            this.elems[config.id].buff = "<scr" + "ipt type=\"text/javascript\">";
            this.elems[config.id].buff += config.orgId + "();";
            this.elems[config.id].buff += "ado.elems['" + config.id + "'].slaveLoaded = true;";
            this.elems[config.id].buff += "</scr" + "ipt>";
            this.elems[config.id].begin = false;
            this.elems[config.id].go();
        } 
        catch (ex) {
            ado.waitingSlaves[config.orgId] = function(){
                ado.slaveStart(config);
            };
        }
    };
    
    this.coreDocumentWrite = function(text){
    	// IE mysli, że typeof ado.write == object
        if (typeof ado.write === "function" || typeof ado.write === "object") {
            var temp = document.write;
            document.write = ado.write;
            document.write(text);
            document.write = temp;
        }
        else {
            document.write(text);
        }
        
    };
    /**
     * Pobierania elementu z DOM na podstawie nazwy tagu
     */
    this.getByTag = function(n, i){
        if (!i) {
            i = 0;
        }
        var objs = ado.getAllByTag(n);
        return objs[i];
    };
    
    this.getAllByTag = function(n) {
    	var objs = [];
        if (document.all) {
            objs = document.all.tags(n);
        }
        else if (document.getElementsByTagName) {
        	objs = document.getElementsByTagName(n);
        }
        else if (document.layers) {
			objs = document.layers[n];
		}
        return objs;
    };
    this.bind = function(elem, eventName, fn){
    	if (elem.addEventListener) {
		    elem.addEventListener(eventName, fn, false);
		}
		else if (elem.attachEvent) {
		    elem.attachEvent("on"+eventName, fn); 
		}
		else if (document.getElementById) {
//			eval(elem.+'on'+eventName = fn);
		}
    };
    
    /**
     * Sprawdzenie czy przegladarka obsluzy nowe kody placementow
     */
    this.isBrowserSupport = function(){
        this.writeRequired = (document.createElement && document.appendChild && document.getElementById) ? true : false;
        return this.writeRequired;
    };
    
    this.loadScript = function(url, callback, charset){
        var done = false;
        var script = document.createElement('script');
        //lata na niepozmieniane kody
        if(url.indexOf("http://javascript:") !== -1){return;}
        script.src = url;
        if (typeof charset !== "undefined" && charset !== null) {
            script.charset = charset;
        }
        script.onload = script.onreadystatechange = function(){
            if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
                done = true;
                callback(this);
            }
        };
        if(ado.browser.msie && ado.loadingPreviewSettings){
        	ado.onHeadLoad = function(){
        		ado.head.appendChild(script);
        	};
        }
		else {
			ado.head.appendChild(script);
		}
    };
    /**
     * Sklejenie kluczy do URL-a
     *
     * @param keys Array
     */
    this.makeKeywords = function(keys){
		/**
		 * Kawalek kodu specjalnie dla ad.netu
		 * Do obslugi nugg.ad-a
		 */
		var addNuggaddKey = function(keys){
			if(typeof na_prof === 'string') {
				if(keys === '') {
					keys = '/key=';
				}
				else {
					keys += ',';
				}
				keys += na_prof;
			}
			return keys;
		}

    	if (typeof keys === 'string') {
    		keys = keys.split(',');
        }
        var k = '';
        if (typeof keys === 'object' && keys.length > 0) {
        
            for (var key in keys) {
                if (typeof keys[key] === "string") {
                    if (ado.characterEncoding) {
                        k += "," + keywordEncode(keys[key].toLowerCase());
                    }
                    else {
                        k += "," + keys[key].toLowerCase();
                    }
                }
            }
            k = "/key=" + k.slice(1);
        }
		k = addNuggaddKey(k);
		return k;
    };
    /**
     * Sklejenie Varsow do URL-a
     *
     * @param vars Array
     */
    this.makeVars = function(vars){
        var v = '';
        if (typeof vars === 'object') {
            for (var key in vars) {
                if (typeof vars[key] === "string" || typeof vars[key] === 'number') {
                    v += "/" + key + "=" + vars[key];
                }
            }
        }
		else if (typeof vars === 'string'){
			if(vars.charAt(0) !== "&"){vars = "&"+vars;}
			if(vars.charAt(vars.length-1) === "&"){vars = vars.substr(0,vars.length-1);}
			vars = vars.replace("&", "/");
			while(vars.indexOf('&') !== -1){
				vars = vars.replace("&", "/");
			}
			v = vars;
		}
        return v;
    };
    
    /**
     * Detekcja flasha Kuby Kruszony
     * Uwzglednia bug zgloszony przez GG
     */
    this.makeFlash = function(){
    	//Detekcja flasha Kuby Kruszony
    	var fv='-';
        var fo = null;
		eval('try { f=(d==top.document)?1:2; if (typeof top.document.referrer=="string") { ref=top.document.referrer } } catch(e) {f=3;}');
		eval('try { fv=navigator.plugins["Shockwave Flash"].description; } catch (e) {}');
		eval('if (typeof ActiveXObject!="undefined") { try { fo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); } catch(e) { try { fo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); fv="X"; fo.AllowScriptAccess="always"; } catch(e) { if (fv=="X") { fv="WIN 6,0,20,0"; }} try { fo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); } catch(e) {} } if ((fv=="-" || fv=="X") && fo) { fv=fo.GetVariable("$version"); }}');
		return '/fv='+escape(fv);
    };

    /**
     * Alias do getElementById
     *
     * @param id String
     */
    this.getById = function(id){
        return document.getElementById(id);
    };

    /**
     *	Metoda wlaczajaca podglad kampanii na stronie
     *
     *  @cfg.emiter - domena emitera, np. s1.adnet.adocean.pl
     *  @cfg.id - dzirdziobuch placementu, ktory mowi, czy jest ciacho/wlaczony podglad.
     *  @cfg.enabled - [true|flase] - wlaczanie z poziomu witryny
     */
    this.preview = function(cfg){
        if(cfg.enabled === false) {return;}
        ado.emiterRequest = true;
        if(cfg.preview === true){
        	var uri = cfg.url;
        }
        else {
        	var uri = ado.protocol+"//" + cfg.emiter + '/_'+(new Date()).getTime()+'/ad.js?id='+cfg.id;	
        }
        if(ado.mode == "old"){
            document.write('<sc'+'ript src="'+uri+'"></scr'+'ipt>');
            ado.emiterRequest = false;
        }
        else {
            ado.loadingPreviewSettings = true;
            ado.loadScript(uri, function(){
                ado.loadingPreviewSettings = false;
                ado.emiterRequest = false;
//                if(ado.browser.msie && !ado.windowLoad){
//                	ado.onDOMReady(function(){ado.loadDeferedPreview();});
//                }
//                else {ado.loadDeferedPreview();}
                ado.loadDeferedPreview();
            });
        }
    };
    /**
     * zywcem wyjete z jQuery
     */
    this.onDOMReady = function(readyFn){
    	var countStyleSheets = function(){
    		var style = ado.getAllByTag('style');
    		var links = ado.getAllByTag('link');
    		var j = 0;
    		for(var i in links){
    			if(links[i].rel === "stylesheet"){j++;}
    		}
    		return style.length + j;
    	};
		
		function bindReady(){
			if ( document.addEventListener && !ado.browser.opera) {
				document.addEventListener( "DOMContentLoaded", readyFn, false );
				ado.windowLoad = true;return;
			}
			else if ( ado.browser.msie && window == top ) {
				(function(){
					if (ado.windowLoad) {return;}
					try {
						document.documentElement.doScroll("left");
					} catch( error ) {
						setTimeout( arguments.callee, 1 );
						return;
					}
					readyFn();
					ado.windowLoad = true;
					return;
				})();
			}
			else if ( ado.browser.opera ) {
				document.addEventListener( "DOMContentLoaded", function () {
					if (ado.windowLoad) {return;}
					for (var i = 0; i < document.styleSheets.length; i++) {
						if (document.styleSheets[i].disabled) {
							setTimeout( arguments.callee, 0 );
							return;
						}
					}
					readyFn();
					ado.windowLoad = true;
					return;
				}, false);
			}
			else if ( ado.browser.safari ) {
				var numStyles;
				(function(){
					if (ado.windowLoad) {return;}
					if ( document.readyState != "loaded" && document.readyState != "complete" ) {
						setTimeout( arguments.callee, 0 );
						return;
					}
					if ( numStyles === undefined ){	numStyles = countStyleSheets(); }
					if ( document.styleSheets.length != numStyles ) {
						setTimeout( arguments.callee, 0 );
						return;
					}
					readyFn();
					ado.windowLoad = true;
					return;
				})();
			}
			else {
				window.onload = readyFn;ado.windowLoad = true;return;
			}
		}
		bindReady();
    };

    
    /**
     * Odpalane po kliknieciu w overlay zamykajacy LivePreview
     */
    this.closeLivePreview = function(){
    	window.location = ado.previewDisableUrl + "?url="+encodeURIComponent(encodeURIComponent(window.location.href));
    };
};

if(typeof ado === "undefined"){
	ado = new AdoContainer();
	ado.init();
	var documentWriteTrash = function(){
		ado.coreDocumentWrite = function(t){
			if(ado.debug) { info(t, "Catched buffor"); }
		};
	};
	/**
	 * Zabezpieczenie eprzed pojawieniem sie document.write po zaladowaniu strony
	 */
//	ado.onDOMReady(documentWriteTrash);
	if (window.addEventListener) {window.addEventListener("load", documentWriteTrash, true);}
	else if (window.attachEvent) {window.attachEvent("onload", documentWriteTrash); }
	else if (document.getElementById) {window.onload=documentWriteTrash;}
}

/**
 * Referencja do globanej przestrzeni nazw
 */
var globalScope = this;