/ Gists

Gists

On gists

Viewport utitilies (edges overlapping control)

jQuery

utitlies.js #

		/*!
			* Check if an element is out of the viewport
			* (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
			* @param  {Node}  elem The element to check
			* @return {Object}     A set of booleans for each side of the element
			*/
			var isOutOfViewport = function (elem) {

				// Get element's bounding
				var bounding = elem.getBoundingClientRect();

				// Check if it's out of the viewport on each side
				var out = {};
				out.top = bounding.top < 0;
				out.left = bounding.left < 0;
				out.bottom = bounding.bottom > (window.innerHeight || document.documentElement.clientHeight);
				out.right = bounding.right > (window.innerWidth || document.documentElement.clientWidth);
				out.any = out.top || out.left || out.bottom || out.right;
				out.all = out.top && out.left && out.bottom && out.right;

				return out;

		};
		
		
		
		
		// from bottom to top
		// console.table({
		// 	'tooltipHeight': $tooltip.height(),
		// 	'offsetTop': offsetPos.y,
		// 	'positionTop': position.top,
		// 	'windowHeight': $(window).height(),
		// 	'documentHeight': $(document).height(),
		// 	'scrollTop': $(window).scrollTop(),
		// });
		
		if (offsetPos.y - $(window).scrollTop() + $tooltip.outerHeight(true) >  $(window).height())
		{
			var moveUp = offsetPos.y -  $tooltip.height() - 35;
			$tooltip.css({
				top: moveUp + "px"
			})
		}
		
		
		// right side
		var offset = $(this).offset();
		var position = $(this).position();
		var offsetPos = {
			x: offset.left,
			y: offset.top + 35,
		}

		// prevent overlapping on the right edge
		if (offsetPos.x + tooltipSize.w >= winSize.w)
		{
			offsetPos.x = winSize.w - tooltipSize.w - gutter;   
		}

On gists

11. Proxy

Navrhove vzory - Bohmer

proxy.php #

<?php

include "1.php";


/*

    Definice
    --------
    Návrhový vzor Proxy kontroluje přístup k objektu pomocí zástupce, který se používá
    místo vlastního objektu.

    Pro implementaci zástupce typu Protection Proxy je nutné provést následující
    kroky:
    1. Vytvořit třídu, která implementuje veškerá rozhraní implementované třídou,
    jejíž instance mají být chráněné.
    2. Ve všech metodách delegovat volání na objekt, který provede požadovanou
    úlohu.
    3. Vytvořit potomka třídy z bodu 2 a v relevantních metodách implementovat
    kontrolní mechanizmy.

*/


abstract class PublicationProxy implements Publication
{
    protected $publication;

    public function __construct(Publication $publication)
    {
        $this->publication = $publication;
    }

    public function open()
    {
        return $this->publication->open();
    }

    public function close()
    {
        return $this->publication->close();
    }

    public function setPageNumber($page)
    {
        return $this->publication->setPageNumber($page);
    }

    public function getPageNumber()
    {
        return $this->publication->getPageNumber();
    }

    public function getDailyRate($days = 1)
    {
        return $this->publication->getDailyRate($days);
    }

    public function getCategory()
    {
        return $this->publication->getCategory();
    }

    public function getPageCount()
    {
        return $this->publication->getPageCount();
    }
}


class PrepaidPublicationProxy extends PublicationProxy
{
    protected $startPage;
    protected $endPage;

    public function __construct(Publication $publication, $startPage, $endPage)
    {
        parent::__construct($publication);
        $this->startPage = $startPage;

        $this->endPage = $endPage;
    }

    public function setPageNumber($page)
    {
        if ($this->startPage > $page || $this->endPage < $page) {
            throw new OutOfRangeException(
                $page,
                $this->startPage,
                $this->endPage
            );
        }
        return $this->publication->setPageNumber($page);
    }
}


class OutOfRangeException extends \Exception
{
    protected $page;
    protected $startPage;
    protected $endPage;
    public function __construct($page, $startPage, $endPage)
    {
        $this->page = $page;
        $this->startPage = $startPage;
        $this->endPage = $endPage;
        $this->message = sprintf(
            "Požadovaná strana číslo %dse nenachází v předplaceném rozsahu stran %d - %d.",
            $this->page,
            $this->startPage,
            $this->endPage
        );
    }

    public function getPage()
    {
        return $this->page;
    }

    public function getStartPage()
    {
        return $this->startPage;
    }

    public function getEndPage()
    {
        return $this->endPage;
    }
}


$book = new Book('medicína', 650);
$proxy = new PrepaidPublicationProxy($book, 261, 414);
$proxy->open();

try 
{
    $proxy->setPageNumber(300);
    print "Kniha je otevřená na straně 300.\n";
    $proxy->setPageNumber(115);
    print "Kniha je otevřená na straně 115.\n";
} 
catch (OutOfRangeException $e) {
    print $e->getMessage() . "\n";
}
$proxy->close();

/*
    Kniha je otevřená na straně 300.
    Požadovaná strana číslo 115 se nenachází
    v předplaceném rozsahu stran 261-414.
*/

On gists

jQuery plugin from literal object

jQuery-plugins

plugin.js #

/*!
 * jQuery prototypal inheritance plugin boilerplate
 * Author: Alex Sexton, Scott Gonzalez
 * Further changes: @addyosmani
 * Licensed under the MIT license
 */

// myObject - an object representing a concept that you want
// to model (e.g. a car)
var myObject = {
  init: function( options, elem ) {
    // Mix in the passed-in options with the default options
    this.options = $.extend( {}, this.options, options );

    // Save the element reference, both as a jQuery
    // reference and a normal reference
    this.elem  = elem;
    this.$elem = $(elem);

    // Build the DOM's initial structure
    this._build();

    // return this so that we can chain and use the bridge with less code.
    return this;
  },
  options: {
    name: "No name"
  },
  _build: function(){
    //this.$elem.html('<h1>'+this.options.name+'</h1>');
  },
  myMethod: function( msg ){
    // You have direct access to the associated and cached
    // jQuery element
    console.log("myMethod triggered");
    // this.$elem.append('<p>'+msg+'</p>');
  }
};

// Object.create support test, and fallback for browsers without it
if ( typeof Object.create !== "function" ) {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

// Create a plugin based on a defined object
$.plugin = function( name, object ) {
  $.fn[name] = function( options ) {
    return this.each(function() {
      if ( ! $.data( this, name ) ) {
        $.data( this, name, Object.create(object).init(
        options, this ) );
      }
    });
  };
};

// Usage:
// With myObject, we could now essentially do this:
// $.plugin('myobj', myObject);

// and at this point we could do the following
// $('#elem').myobj({name: "John"});
// var inst = $('#elem').data('myobj');
// inst.myMethod('I am a method');

On gists

Plugin pattern - Boilerplate

jQuery-plugins

plugin.js #

// jQuery Plugin Boilerplate
// A boilerplate for jumpstarting jQuery plugins development
// version 1.1, May 14th, 2011
// by Stefan Gabos

(function($) {

    $.pluginName = function(element, options) {

        var defaults = {
            foo: 'bar',
            onFoo: function() {}
        }

        var plugin = this;

        plugin.settings = {}

        var $element = $(element),
             element = element;

        plugin.init = function() {
            plugin.settings = $.extend({}, defaults, options);
            // code goes here
        }

        plugin.foo_public_method = function() {
            // code goes here
        }

        var foo_private_method = function() {
            // code goes here
        }

        plugin.init();

    }

    $.fn.pluginName = function(options) {

        return this.each(function() {
            if (undefined == $(this).data('pluginName')) {
                var plugin = new $.pluginName(this, options);
                $(this).data('pluginName', plugin);
            }
        });

    }

})(jQuery);



// USAGE
$(document).ready(function() {

    // attach the plugin to an element
    $('#element').pluginName({'foo': 'bar'});

    // call a public method
    $('#element').data('pluginName').foo_public_method();

    // get the value of a property
    $('#element').data('pluginName').settings.foo;

});

On gists

10. Decorator

Navrhove vzory - Bohmer

decorator.php #

<?php

include "1.php";

/*
    Definice
    --------
    Návrhový vzor Decorator rozšiřuje daný objekt o novou funkčnost nebo změní stávající
    metody za běhu programu. Tento návrhový vzor nabízí flexibilní alternativu
    k vytváření potomků tříd.

    Pro dosažení požadovaného výsledku jsou nutné následující kroky:
    1. Vytvořit základní třídu pro dekorátory, která je stejného typu jako objekt,
    který má být dekorovaný. Vytvořit potomka této třídy nebo v nové třídě
    implementovat všechny metody rozhraní.

    2. V základní třídě definovat konstruktor, jemuž bude možné předat dekorovaný
    objekt. Tento objekt uložit ve třídě dekorátoru.

    3. Implementovat všechny metody základní třídy takovým způsobem, že se
    jejich volání bude delegovat na objekt, který se má dekorovat.

    4. Implementovat libovolné konkrétní dekorátory, jež jsou potomky třídy
    z bodu 3. Konkrétního chování dekorátoru se docílí přepsáním jednotlivých
    metod.
    5. Kombinací více dekorátorů lze dosáhnout jejich vzájemným dekorováním.
*/



abstract class PublicationDecorator implements Publication
{
    protected $publication;

    public function __construct(Publication $publication)
    {
        $this->publication = $publication;
    }

    public function open()
    {
        return $this->publication->open();
    }

    public function close()
    {
        return $this->publication->close();
    }

    public function setPageNumber($page)
    {
        return $this->publication->setPageNumber($page);
    }

    public function getPageNumber()
    {
        return $this->publication->getPageNumber();
    }

    public function getDailyRate($days = 1)
    {
        return $this->publication->getDailyRate($days);
    }

    public function getCategory()
    {
        return $this->publication->getCategory();
    }

    public function getPageCount()
    {
        return $this->publication->getPageCount();
    }

    public function __call($method, $args) 
    {
        return call_user_func_array(
        array($this->publication, $method), $args
        );
    }

    public function providesMethod($method) 
    {
        if (method_exists($this->publication, $method)) 
        {
            return TRUE;
        }

        if ($this->publication instanceof PublicationDecorator) 
        {
            return $this->publication->providesMethod($method);
        }

        return FALSE;
    }

    public function getPublication()
    {
        return $this->publication;
    }

}



class PublicationDecoratorAppendix extends PublicationDecorator
{
    public function getDailyRate($days = 1)
    {
        $rate = $this->publication->getDailyRate($days);
        return $rate + 2;
    }

    public function getPageCount()
    {
        $pageCount = $this->publication->getPageCount();
        return $pageCount + 30;
    }
}

// Použití
$book = new Book('medicína', 100);
$withAppendix = new PublicationDecoratorAppendix($book);

printf("Celkový počet stran s přílohou: %d\n",
$withAppendix->getPageCount()
);

printf("Denní sazba: %.2f Kč\n",
$withAppendix->getDailyRate()
);

/*
    Celkový počet stran s přílohou: 130
    Denní sazba: 12.00 Kč
*/


// Další decorator
class PublicationDecoratorNews extends PublicationDecorator 
{
    public function getDailyRate($days = 1) 
    {
        $rate = $this->publication->getDailyRate($days);
        return round($rate * 0.8, 2);
    }
}

$newsWithAppendix = new PublicationDecoratorNews($withAppendix);

printf("Celkový počet stran s přílohou: %d\n",
$newsWithAppendix->getPageCount()
);

printf("Denní sazba: %.2f Kč\n",
$newsWithAppendix->getDailyRate()
);

/*
    Celkový počet stran s přílohou: 130
    Denní sazba: 9.60 Kč
*/


/* 
    Další využití
    -------------
    Lze doplnit i nové metody

*/

class PublicationDecoratorCd extends PublicationDecorator
{
    protected $track = 1;

    public function getTrack()
    {
        return $this->track;
    }

    public function setTrack($track)
    {
        $this->track = $track;
    }

    public function getDailyRate($days = 1)
    {
        $rate = $this->publication->getDailyRate($days);
        return $rate * 1.3;
    }
}

$book = new Book('italská kuchyně', 280);
$withCd = new PublicationDecoratorCd($book);

printf("Denní sazba: %.2f Kč\n", $withCd->getDailyRate());

$withCd->setTrack(10);
printf("Aktuální stopa: %d \n", $withCd->getTrack());

/*
    Denní sazba: 13.00 Kč
    Aktuální stopa: 10
*/



/*
    Může vzniknout problém, že jiný dekorátor tyto metody nevidí,
    odchytne se to pres __call, ale je to takové magické...
*/
$book = new Book('italská kuchyně', 280);
$withCd = new PublicationDecoratorCd($book);
$newsWithCd = new PublicationDecoratorNews($withCd);

printf("Denní sazba: %.2f Kč\n", $newsWithCd->getDailyRate());
$newsWithCd->setTrack(10);
// printf("Aktuální stopa: %d \n", $newsWithCd->getTrack());

/*
    Denní sazba: 10.40 Kč
    Aktuální stopa: 10
*/


/* 
    Dá se to vyřešit lépe... 
    viz vlastní metoda na existenci providesMethods()
*/

$book = new Book('italská kuchyně', 280);
$withCd = new PublicationDecoratorCd($book);
$newsWithCd = new PublicationDecoratorNews($withCd);

if ($newsWithCd->providesMethod('setTrack')) 
{

   var_dump($newsWithCd->getPublication());
    $newsWithCd->setTrack(30);
}

printf("Aktuální stopa: %d \n", $newsWithCd->getTrack());

On gists

Css modal window 2 (ghost)

CSS CSS trick

modal.html #

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <style>
.blocker
{
  position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    z-index: 1;
    padding: 20px;
    box-sizing: border-box;
    background-color: #000;
    background-color: #000000bf;
    text-align: center;
}

.blocker::before
{
    content: "";
    display: inline-block;
    height: 100%;
    vertical-align: middle;
    margin-right: -0.05em;
}

.modal
{
  
    vertical-align: middle;
    position: relative;
    z-index: 2;
    max-width: 500px;
    box-sizing: border-box;
    width: 90%;
    background: #fff;
    padding: 15px 30px
}
  </style>
</head>
<body>

<!-- https://jquerymodal.com/ -->
  
  <div class="jquery-modal blocker current">
    <div class="modal" style="display: inline-block;">
        <h1>Hello there!</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><a href="#close-modal" rel="modal:close" class="close-modal ">Close</a></div>
    </div>
  </div>
</body>
</html>

On gists

Css modal window

CSS CSS trick

index.html #

<!-- https://jsbin.com/pecarecezu/edit?html,css,output -->

<div class="modal">
  <div class="header">
    <h1>Some heading</h1>
  </div>
  <div class="content">
    <p>
      lorem
    </p>
  </div>
</div>

On gists

9. Bridge

Navrhove vzory - Bohmer

bridge.php #

<?php

include "1.php";

/*
    Návrhový vzor Bridge odděluje rozhraní třídy od její vlastní implementace a umožňuje
    provádět jejich změnu nezávisle na sobě.
*/


/*
    Definice
    --------
    Návrhový vzor Bridge odděluje rozhraní třídy od její vlastní implementace a umožňuje
    provádět jejich změnu nezávisle na sobě.
    Pro dosažení tohoto cíle je nutné provést následující kroky:
    1. Definovat rozhraní nezbytné pro jednotlivé implementace.
    2. Vytvořit minimálně dvě třídy implementující rozhraní z bodu 1.
    3. Vytvořit způsob, jak abstrakci předat konkrétní implementaci.
    4. V abstrakci využívat služby implementace z bodu 2 přes rozhraní definované
    v bodě 1.


  Shrnutí
  -------
  Návrhový vzor Bridge odděluje abstrakci od její konkrétní implementace a zamezuje
  tak zbytečnému nárůstu počtu tříd při předávání implementací. Tento
  návrhový vzor řeší podobný problém jako návrhový vzor Adapter, představený
  v předchozí kapitole. Rozdíl mezi těmito dvěma vzory je především v tom, že
  vzor Bridge se používá již při návrhu aplikace, zatímco vzor Adapter řeší problém,
  jak zabezpečit komunikaci již hotových tříd.
*/

interface Storage 
{
    public function insertPublication($id, Publication $p);
    public function fetchPublications();
}


class StorageArray implements Storage
{
    protected $publications;

    public function insertPublication($id, Publication $p)
    {
        $this->publications[$id] = $p;
    }

    public function fetchPublications()
    {
        return $this->publications;
    }
}

// Upravena trida library oproti 1 dilu
abstract class AbstractLibrary
{
    protected $rentalActions = array();
    protected $debugger;
    protected $storage;

    public function __construct(Debugger $debugger, Storage $storage)
    {
        $this->debugger = $debugger;
        $this->storage = $storage;
    }

    protected function debug($message)
    {
        $this->debugger->debug($message);
    }

    public function rentPublication(Publication $p, Member $m)
    {
        $publicationId = array_search($p, $this->getPublications());

        if (false === $publicationId) {
            throw new UnknownPublicationException();
        }

        if (!$this->isPublicationAvailable($p)) {
            throw new PublicationNotAvailableException();
        }

        $rentalAction = new RentalAction($p, $m);
        $this->rentalActions[] = $rentalAction;
        $this->debug(
            $m->getName() . ' si vypůjčil publikaci: ' . $p->getCategory()
        );

        return $rentalAction;
    }

    public function returnPublication(Publication $p)
    {
        foreach ($this->rentalActions as $rentalAction) 
        {
            if ($rentalAction->getPublication() !== $p) {
                continue;
            }
            if ($rentalAction->isReturned()) {
                continue;
            }

            $rentalAction->markPublicationReturned();
            $this->debug(
                $rentalAction->getMember()->getName() .
                    ' vrátil publikaci: ' .
                    $p->getCategory()
            );

            return true;
        }

        return false;
    }

    public function isPublicationAvailable(Publication $p)
    {
        foreach ($this->rentalActions as $rentalAction) 
        {
            if ($rentalAction->getPublication() !== $p) {
                continue;
            }

            if ($rentalAction->isReturned()) {
                continue;
            }

            return false;
        }

        return true;
    }

    public function generateId()
    {
        $publications = $this->getPublications();
        return 'PUBLICATION-' . count($publications);
    }

    public function getPublications()
    {
        return $this->storage->fetchPublications();
    }

    abstract public function addToLibrary($id, Publication $p);
}


class Library extends AbstractLibrary
{
    public function addToLibrary($id, Publication $p)
    {
        $this->storage->insertPublication($id, $p);
        $this->debug('Nová publikace v knihovně: ' . $p->getCategory());
    }
}


// Storages
class StorageDatabase implements Storage
{
    protected $db;
    public function __construct($filename)
    {
        $this->db = new \SQLiteDatabase($filename, 0666);
        $this->initialiseDatabase();
    }
    public function insertPublication($id, Publication $p)
    {
        // INSERT TO DB
    }
    public function fetchPublications()
    {
       // SELECT FROM DB && FETCH to $publications (array or ...)
       return $publications
    }
    
    protected function initialiseDatabase() 
    {
     // connection to db
    }
}



class StorageArray implements Storage
{
    protected $publications;

    public function insertPublication($id, Publication $p)
    {
        $this->publications[$id] = $p;
    }

    public function fetchPublications()
    {
        return $this->publications;
    }
}


// USAGE

$publication1 = new Book('PC', 450);
$publication2 = new Book('MEDICINA', 50);

$storage = new StorageDatabase('library.sqlite');
$debugger = DebuggerEcho::getInstance();

$library = new Library($debugger, $storage);
$library->addToLibrary($library->generateId(), $publication1);
$library->addToLibrary($library->generateId(), $publication2);
$publications = $library->getPublications();

print_r($publications);

On gists

8. Adapter

Navrhove vzory - Bohmer

adapter.php #

<?php

include "1.php";

/*
    Návrhový vzor Adapter přizpůsobí rozhraní určité třídy rozhraní požadovanému
    klientem a umožní tak spolupráci tříd, které by kvůli nekompatibilním rozhraním
    nebyly schopné spolupracovat.


    Definice
    ---------
    Návrhový vzor Adapter přizpůsobí rozhraní určité třídy rozhraní požadovanému
    klientem a umožní tak spolupráci tříd, které by kvůli nekompatibilním rozhraním
    nebyly schopné spolupracovat.
    Pro vytvoření adaptéru, který umožní spolupráci dvou nekompatibilních rozhraní,
    je nutné provést následující kroky:
    1. Lokalizovat rozdíly mezi nabízeným a požadovaným rozhraním.
    2. Implementovat novou třídu, která bude poskytovat požadované rozhraní.
    3. Vytvořit způsob, jak předat adaptéru objekt, který má být přizpůsobený
    – například pomocí principu vkládání závislostí (Dependency Injection).
    4. Implementovat všechny metody vyžadované rozhraním a delegovat požadavky
    dále na příslušné metody původního objektu.
    5. Přihlížet k signalizování chyb.
    6. V aplikaci využívat objekt adaptéru a pomocí něho „obalit“ původní objekt.

*/




class Title
{
    const INFO_CATEGORY = 'category';
    const INFO_PAGECOUNT = 'pageCount';
    const TITLE_OPEN = 1;
    const TITLE_CLOSE = 0;

    protected $info = array();
    protected $currentPage = 0;
    protected $state = self::TITLE_CLOSE;

    public function __construct($pageCount, $category)
    {
        $this->info[self::INFO_PAGECOUNT] = $pageCount;
        $this->info[self::INFO_CATEGORY] = $category;
    }

    public function getInfo($info)
    {
        if (isset($this->info[$info])) {
            return $this->info[$info];
        }
    }

    public function setClosed($state)
    {
        $this->state = $state;
    }

    public function getClosed()
    {
        return $this->state;
    }

    public function setCurrentPage($page)
    {
        if (self::TITLE_CLOSE == $this->state) {
            $this->setClosed(self::TITLE_OPEN);
        }
        $this->currentPage = $page;
    }

    public function getCurrentPage()
    {
        if (self::TITLE_CLOSE == $this->state) {
            throw new ClosedException('Kniha není otevřena');
        }
        return $this->currentPage;
    }
}


// Lisi se nazvy metod, parametry atd ...
$title = new Title(100, 'PC');
$title->setCurrentPage(47);
printf(
"Kategorie: %s\n", $title->getInfo(Title::INFO_CATEGORY)
);
printf(
"Počet stran: %d\n", $title->getInfo(Title::INFO_PAGECOUNT)
);
printf(
"Kniha je otevřena na %d. straně\n", $title->getCurrentPage()
);


$book = new Book('PC', 100);
$book->open();
$book->setPageNumber(47);
printf(
"Kategorie: %s\n", $book->getCategory()
);
printf(
"Počet stran: %d\n", $book->getPageCount()
);
printf(
"Kniha je otevřena na %d. straně\n", $book->getPageNumber()
);

// OUTPUT

/*

Kategorie: PC
Počet stran: 100
Kniha je otevřena na 47. straně

Kategorie: PC
Počet stran: 100
Kniha je otevřena na 47. straně

*/

On gists

7. Composite

Navrhove vzory - Bohmer

composite.php #

<?php

<?php

include "1.php";

/*
    Návrhový vzor Composite spojuje více objektů do stromové struktury, v níž se navenek
    tváří jako jeden objekt a lze je tak i použít.

    ---

    Spojení objektů do stromové struktury znamená následující:
    1. Vytvořit možnost poskládat více objektů typu Debugger do stromové struktury.
    2. Objekt, který tuto stromovou strukturu obsahuje, se musí chovat jako jeden
    z objektů typu Debugger.
*/

/* 

    Návrhový vzor Composite spojuje více objektů do stromové struktury, v níž se navenek
    tváří jako jeden objekt a lze je tak i použít.
    Pro dosažení tohoto cíle je nutné provést následující kroky:
    
    1. V případě, že tak ještě nebylo učiněné, definovat rozhraní pro samotný
    objekt stromu.
    2. Vytvořit novou třídu, která implementuje toto rozhraní. Do této třídy přidat
    další metodu (například addChild()), které se budou předávat libovolné
    objekty implementující rozhraní z bodu 1. Tyto předané objekty následně
    uložit jako pole v atributu třídy.
    3. Implementovat všechny metody vyžadované rozhraním z bodu 1 takovým
    způsobem, že se prochází přes všechny objekty přidané do třídy v bodě 2
    a postupně je na ně delegováno volání příslušné metody.
    4. Vytvořit libovolné objekty a pomocí metody addChild() je přiřadit do stromové
    struktury.
    5. V aplikaci použít tento strom objektů místo jednotlivých objektů.

*/


class DebuggerComposite implements Debugger
{
    protected $debuggers = array();

    public function addDebugger(Debugger $debugger)
    {
        $this->debuggers[] = $debugger;
    }

    public function removeDebugger(Debugger $debugger)
    {
        $key = array_search($debugger, $this->debuggers);
        if (false === $key) {
            return false;
        }
        unset($this->debuggers[$key]);
        return true;
    }
    
    public function debug($message)
    {
        foreach ($this->debuggers as $debugger) {
            $debugger->debug($message);
        }
    }
}

// Jednodussi pouziti
$debuggerEcho = DebuggerEcho::getInstance();
$debuggerLog = DebuggerLog::getInstance('./library.log');

$composite = new DebuggerComposite();
$composite->addDebugger($debuggerEcho);
$composite->addDebugger($debuggerLog);

$debuggerEcho->debug('Výpis na monitor');
$debuggerLog->debug('Zápis do souboru');
$composite->debug('Monitor + Soubor');