├── .gitignore
├── patterns
├── behavioral
│ ├── visitor
│ │ └── readme.md
│ ├── template-method
│ │ └── readme.md
│ ├── interpreter
│ │ └── readme.md
│ ├── observer
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── observer.spec.js
│ │ └── observer.js
│ ├── iterator
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── iterator.spec.js
│ │ ├── iterator.ts
│ │ └── iterator.js
│ ├── memento
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── memento.spec.js
│ │ └── memento.js
│ ├── state
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── state.spec.js
│ │ └── state.js
│ ├── strategy
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── strategy.spec.js
│ │ └── strategy.js
│ ├── command
│ │ └── readme.md
│ ├── chain-of-responsibility
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── chain.spec.js
│ │ └── chain.js
│ └── mediator
│ │ ├── spec
│ │ ├── runner.html
│ │ └── mediator.spec.js
│ │ ├── readme.md
│ │ └── mediator.js
├── creational
│ ├── factory-method
│ │ ├── factory-method.js
│ │ ├── spec
│ │ │ └── factory-method.spec.js
│ │ └── readme.md
│ ├── abstract-factory
│ │ ├── abstract-factory.js
│ │ ├── spec
│ │ │ └── abstract-factory.spec.js
│ │ └── readme.md
│ ├── singleton
│ │ ├── readme.md
│ │ ├── singleton-static-constructor.js
│ │ ├── singleton-closure-static.js
│ │ ├── singleton-closure-constructor.js
│ │ └── spec
│ │ │ ├── runner.html
│ │ │ └── singleton.spec.js
│ ├── prototype
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── prototype.spec.js
│ │ └── prototype.js
│ ├── builder
│ │ └── readme.md
│ └── namespace
│ │ ├── spec
│ │ ├── runner.html
│ │ └── namespace.spec.js
│ │ ├── readme.md
│ │ └── namespace.js
├── enterprise
│ ├── registry
│ │ ├── readme.md
│ │ ├── spec
│ │ │ ├── runner.html
│ │ │ └── registry.spec.js
│ │ └── registry.js
│ └── reqres
│ │ ├── readme.md
│ │ ├── spec
│ │ ├── runner.html
│ │ └── reqres.spec.js
│ │ └── reqres.js
└── structural
│ ├── flyweight
│ └── readme.md
│ ├── bridge
│ └── readme.md
│ ├── module
│ ├── readme.md
│ ├── spec
│ │ ├── runner.html
│ │ └── module.spec.js
│ └── module.js
│ ├── proxy
│ ├── readme.md
│ ├── spec
│ │ ├── runner.html
│ │ └── proxy.spec.js
│ └── proxy.js
│ ├── facade
│ ├── readme.md
│ ├── spec
│ │ ├── runner.html
│ │ └── facade.spec.js
│ └── facade.js
│ ├── composite
│ ├── readme.md
│ ├── spec
│ │ ├── runner.html
│ │ └── composite.spec.js
│ └── composite.js
│ ├── decorator
│ ├── readme.md
│ ├── spec
│ │ ├── runner.html
│ │ └── decorator.spec.js
│ └── decorator.js
│ ├── adapter
│ ├── readme.md
│ ├── spec
│ │ ├── runner.html
│ │ └── adapter.spec.js
│ └── adapter.js
│ └── mixin
│ ├── readme.md
│ ├── mixin-function.js
│ ├── mixin-literal.js
│ └── spec
│ ├── runner.html
│ └── mixin.spec.js
├── tests
├── runner.js
└── prepare.js
├── README.md
└── assets
├── mocha.css
└── underscore.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /node_modules
--------------------------------------------------------------------------------
/patterns/behavioral/visitor/readme.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/patterns/behavioral/template-method/readme.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/patterns/creational/factory-method/factory-method.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/patterns/creational/abstract-factory/abstract-factory.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/runner.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | mocha.run();
3 | })();
--------------------------------------------------------------------------------
/patterns/enterprise/registry/readme.md:
--------------------------------------------------------------------------------
1 | # Реестр / Registry
2 |
3 |
--------------------------------------------------------------------------------
/patterns/behavioral/interpreter/readme.md:
--------------------------------------------------------------------------------
1 | # Интерпретатор / Interpreter
2 |
3 |
--------------------------------------------------------------------------------
/patterns/enterprise/reqres/readme.md:
--------------------------------------------------------------------------------
1 | # Запрос/Ответ / Request/Response
2 |
3 |
--------------------------------------------------------------------------------
/patterns/behavioral/observer/readme.md:
--------------------------------------------------------------------------------
1 | # Наблюдатель / Observer / Publisher-Subscriber
2 |
3 |
--------------------------------------------------------------------------------
/patterns/creational/factory-method/spec/factory-method.spec.js:
--------------------------------------------------------------------------------
1 | describe('Фабричный метод / Factory method', function () {
2 |
3 | });
--------------------------------------------------------------------------------
/patterns/creational/singleton/readme.md:
--------------------------------------------------------------------------------
1 | # Синглтон / Одиночка / Singleton
2 |
3 | ### Диаграмма
4 | ...
5 |
6 | ### Описание
7 | ...
--------------------------------------------------------------------------------
/patterns/creational/abstract-factory/spec/abstract-factory.spec.js:
--------------------------------------------------------------------------------
1 | describe('Абстрактная фабрика / Abstract factory', function () {
2 |
3 | });
--------------------------------------------------------------------------------
/patterns/structural/flyweight/readme.md:
--------------------------------------------------------------------------------
1 | # Приспособленец / Flyweight
2 |
3 | Использует разделение для эффективной поддержки большого числа мелких объектов.
--------------------------------------------------------------------------------
/patterns/structural/bridge/readme.md:
--------------------------------------------------------------------------------
1 | # Мост / Bridge
2 |
3 | Отделяет абстракцию от реализации, благодаря чему появляется возможность независимо изменять то и другое.
--------------------------------------------------------------------------------
/tests/prepare.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | assert = chai.assert;
3 | should = chai.should();
4 | expect = chai.expect;
5 |
6 | mocha.setup('bdd');
7 | })();
--------------------------------------------------------------------------------
/patterns/creational/prototype/readme.md:
--------------------------------------------------------------------------------
1 | # Прототип / Prototype
2 |
3 | Описывает виды создаваемых объектов с помощью прототипа и создает новые объекты путем его копирования.
--------------------------------------------------------------------------------
/patterns/structural/module/readme.md:
--------------------------------------------------------------------------------
1 | # Модуль / Module
2 |
3 | #### Ссылки
4 |
5 | * [Learning JavaScript Design Patterns] (http://addyosmani.com/resources/essentialjsdesignpatterns)
--------------------------------------------------------------------------------
/patterns/structural/proxy/readme.md:
--------------------------------------------------------------------------------
1 | # Заместитель / Proxy или Суррогат / Surrogate
2 |
3 | Подменяет другой объект, контролирует доступ к объекту, не изменяя при этом поведения самого объекта.
4 |
--------------------------------------------------------------------------------
/patterns/behavioral/iterator/readme.md:
--------------------------------------------------------------------------------
1 | # Итератор / Iterator или Курсор / Cursor
2 |
3 | Дает возможность последовательно обойти все элементы составного объекта, не раскрывая его внутреннего представления.
--------------------------------------------------------------------------------
/patterns/structural/facade/readme.md:
--------------------------------------------------------------------------------
1 | # Фасад / Facade
2 |
3 | Предоставляет унифицированный интерфейс к множеству интерфейсов в некоторой подсистеме. Определяет интерфейс более высокого уровня, облегчающий работу с подсистемой.
--------------------------------------------------------------------------------
/patterns/creational/builder/readme.md:
--------------------------------------------------------------------------------
1 | # Строитель / Builder
2 |
3 | Отделяет конструирование сложного объекта от его представления, позволяя использовать один и тот же процесс конструирования для создания различных представлений.
--------------------------------------------------------------------------------
/patterns/behavioral/memento/readme.md:
--------------------------------------------------------------------------------
1 | # Хранитель / Memento
2 |
3 | Позволяет, не нарушая инкапсуляции, получить и сохранить во внешней памяти внутреннее состояние объекта, чтобы позже объект можно было восстановить точно в таком же состоянии.
--------------------------------------------------------------------------------
/patterns/structural/composite/readme.md:
--------------------------------------------------------------------------------
1 | # Компоновщик / Composite
2 |
3 | Группирует объекты в древовидные структуры для представления иерархии типа "часть-целое". Позволяет клиентам работать с единичными объектами так же, как с группами объектов.
--------------------------------------------------------------------------------
/patterns/structural/decorator/readme.md:
--------------------------------------------------------------------------------
1 | # Декоратор / Decorator или Оболочка / Wrapper
2 |
3 | Динамически возлагает на объект новые функции. Применяется для расширения имеющейся функциональности и является гибкой альтернативой порождению подклассов.
--------------------------------------------------------------------------------
/patterns/behavioral/state/readme.md:
--------------------------------------------------------------------------------
1 | # Состояние / State
2 |
3 | Позволяет объекту варьировать свое поведение при изменении внутреннего состояния. При этом создается впечатление, что поменялся класс объекта.
4 |
5 | TODO: ? Разница между Состоянием и Стратегией
--------------------------------------------------------------------------------
/patterns/structural/adapter/readme.md:
--------------------------------------------------------------------------------
1 | # Адаптер / Adapter
2 |
3 | Преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами. Обеспечивает совместную работу классов, которая была бы невозможна без данного паттерна из-за несовместимости интерфейсов.
--------------------------------------------------------------------------------
/patterns/behavioral/strategy/readme.md:
--------------------------------------------------------------------------------
1 | # Стратегия / Strategy
2 |
3 | Паттерн определяет семейство алгоритмов, инкапсулируя их все и позволяя подставлять один вместо другого. Можно менять алгоритм независимо от клиента, который им пользуется.
4 |
5 | TODO: ? Разница между Состоянием и Стратегией
--------------------------------------------------------------------------------
/patterns/behavioral/command/readme.md:
--------------------------------------------------------------------------------
1 | # Команда / Command, Действие / Action или Транзакция / Transaction
2 |
3 | Инкапсулирует запрос в виде объекта, позволяя тем самым параметризовывать клиентов типом запроса, устанавливать очередность запросов, протоколировать их и поддерживать отмену выполнения операций.
--------------------------------------------------------------------------------
/patterns/creational/factory-method/readme.md:
--------------------------------------------------------------------------------
1 | # Фабричный метод / Factory method
2 |
3 | Определяет интерфейс для создания объектов, при этом выбранный класс инстанцируется подклассами.
4 |
5 | #### Ссылки
6 |
7 | * [Dofactory: Factory method] (http://www.dofactory.com/javascript-factory-method-pattern.aspx)
--------------------------------------------------------------------------------
/patterns/structural/mixin/readme.md:
--------------------------------------------------------------------------------
1 | # Примесь / Mixin
2 |
3 | Примесь это класс, реализующий какое-либо чётко выделенное поведение. Используется для уточнения поведения других классов, не предназначен для порождения самостоятельно используемых объектов.
4 |
5 | [Хорошая статья про примеси на Хабре] (http://habrahabr.ru/post/147901/)
--------------------------------------------------------------------------------
/patterns/creational/abstract-factory/readme.md:
--------------------------------------------------------------------------------
1 | # Абстрактная фабрика / Abstract factory
2 |
3 | Предоставляет интерфейс для создания семейств, связанных между собой, или независимых объектов, конкретные классы которых неизвестны.
4 |
5 | #### Ссылки
6 |
7 | * [Dofactory: Abstract factory] (http://www.dofactory.com/javascript-abstract-factory-pattern.aspx)
--------------------------------------------------------------------------------
/patterns/behavioral/chain-of-responsibility/readme.md:
--------------------------------------------------------------------------------
1 | # Цепочка обязаностей / Chain of Responsibility
2 |
3 | Можно избежать жесткой зависимости отправителя запроса от его получателя, при этом запросом начинает обрабатываться один из нескольких объектов. Объекты-получатели связываются в цепочку и запрос передается по цепочке, пока какой-то объект его не обработает.
--------------------------------------------------------------------------------
/patterns/creational/singleton/singleton-static-constructor.js:
--------------------------------------------------------------------------------
1 | function StaticConstructorSingleton() {
2 |
3 | if (typeof StaticConstructorSingleton.instance === 'object') {
4 | return StaticConstructorSingleton.instance;
5 | }
6 |
7 | StaticConstructorSingleton.instance = this;
8 |
9 | return this;
10 | }
11 |
12 | // Публичные методы
13 | StaticConstructorSingleton.prototype.toString = function () {
14 | return '[object StaticConstructorSingleton]';
15 | }
--------------------------------------------------------------------------------
/patterns/creational/singleton/singleton-closure-static.js:
--------------------------------------------------------------------------------
1 | var ClosureStaticSingleton = (function () {
2 | var instance;
3 |
4 | function createInstance() {
5 | // Приватные методы
6 | function _privateMethod () {
7 |
8 | }
9 |
10 | return {
11 | // Публичные методы
12 | };
13 | }
14 |
15 | return {
16 | getInstance: function () {
17 | if (!instance) {
18 | instance = createInstance();
19 | }
20 |
21 | return instance;
22 | }
23 | };
24 | })();
--------------------------------------------------------------------------------
/patterns/creational/singleton/singleton-closure-constructor.js:
--------------------------------------------------------------------------------
1 | var ClosureConstructorSingleton = new function () {
2 | var instance;
3 |
4 | // Конструктор
5 | function ClosureConstructorSingleton() {
6 | if (!instance) {
7 | instance = this;
8 | } else {
9 | return instance;
10 | }
11 | }
12 |
13 | // Приватные методы и свойства
14 | function _privateMethod() {
15 | //
16 | }
17 |
18 | // Публичные методы
19 | ClosureConstructorSingleton.prototype.toString = function () {
20 | return '[object ClosureConstructorSingleton]';
21 | };
22 |
23 | return ClosureConstructorSingleton;
24 | };
--------------------------------------------------------------------------------
/patterns/structural/mixin/mixin-function.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Базовый класс
3 | * */
4 | var BookFunctional = (function () {
5 |
6 | function BookFunctional(author, title) {
7 | this._title = title;
8 | this._author = author;
9 | }
10 |
11 | BookFunctional.prototype.getInfo = function () {
12 | return this._author + ' - ' + this._title;
13 | };
14 |
15 | return BookFunctional;
16 | })();
17 |
18 | /**
19 | * Сама примесь
20 | * */
21 | var BookFunctionalMixin = function () {
22 |
23 | this.getAuthor = function () {
24 | return this._author;
25 | };
26 |
27 | this.getTitle = function () {
28 | return this._title;
29 | };
30 |
31 | return this;
32 | };
33 |
34 | /**
35 | * Применение: BookFunctionalMixin.call(BookFunctional.prototype);
36 | * */
--------------------------------------------------------------------------------
/patterns/structural/mixin/mixin-literal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Базовый класс
3 | * */
4 | var BookLiteral = (function () {
5 |
6 | function BookLiteral(author, title) {
7 | this._title = title;
8 | this._author = author;
9 | }
10 |
11 | BookLiteral.prototype.getInfo = function () {
12 | return this._author + ' - ' + this._title;
13 | };
14 |
15 | return BookLiteral;
16 | })();
17 |
18 | /**
19 | * Сама примесь
20 | * */
21 | var BookLiteralMixin = {
22 |
23 | getAuthor: function () {
24 | return this._author;
25 | },
26 |
27 | getTitle: function () {
28 | return this._title;
29 | }
30 | };
31 |
32 | /**
33 | * Применение: _.extend(BookLiteral.prototype, BookLiteralMixin);
34 | * Метод _.extend() скопирует все методы примеси в объект Book
35 | * */
--------------------------------------------------------------------------------
/patterns/behavioral/state/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - State - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/proxy/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Proxy - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/facade/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Facade - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/module/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Module - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/adapter/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Adapter - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/behavioral/iterator/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Iterator - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/behavioral/memento/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Memento - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/patterns/behavioral/observer/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Observer - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/behavioral/strategy/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Strategy - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/enterprise/registry/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Registry - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/enterprise/reqres/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Request/Response - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/behavioral/mediator/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Mediator - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/patterns/creational/namespace/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Namespace - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/creational/prototype/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Prototype - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/composite/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Composite - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/decorator/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Decorator - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/behavioral/chain-of-responsibility/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Chain of Responsibility - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/patterns/structural/mixin/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Mixin - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/patterns/creational/singleton/spec/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Javascript Design Patterns - Singleton - Spec Runner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/patterns/creational/prototype/prototype.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Базовый класс
3 | * */
4 | var Person = (function () {
5 |
6 | function Person(firstname, lastname) {
7 | this.firstname = firstname;
8 | this.lastname = lastname;
9 | }
10 |
11 | Person.prototype.introduce = function () {
12 | return 'My name is ' + this.firstname + ' ' + this.lastname;
13 | };
14 |
15 | return Person;
16 | })();
17 |
18 | /**
19 | * Прототип
20 | * */
21 | var Proto = (function () {
22 |
23 | function Proto(person) {
24 | this.person = person;
25 | }
26 |
27 | Proto.prototype.clone = function () {
28 | var f = function () {
29 | };
30 | f.prototype = this.person;
31 | return new f();
32 | };
33 |
34 | return Proto;
35 | })();
36 |
37 | /**
38 | * Использование:
39 | * var john = new Person('John', 'Smith'),
40 | * johnPrototype = new Proto(john),
41 | * johnClone = johnPrototype.clone();
42 | *
43 | * console.log(johnClone.introduce());
44 | * johnClone будет клоном объекта john
45 | * */
--------------------------------------------------------------------------------
/patterns/structural/decorator/decorator.js:
--------------------------------------------------------------------------------
1 | // Базовый конструктор, который будет декорирован
2 | var Beverage = (function () {
3 | function Beverage(cost, desc) {
4 | this._cost = cost;
5 | this._desc = desc;
6 | }
7 |
8 | Beverage.prototype.getCost = function () {
9 | return this._cost;
10 | };
11 |
12 | Beverage.prototype.getDescription = function () {
13 | return this._desc;
14 | };
15 |
16 | return Beverage;
17 | })();
18 |
19 | // Конструктор декоратора
20 | var Vine = (function () {
21 | function Vine(name, color, cost, desc) {
22 | this._beverage = new Beverage(cost, desc);
23 | this._name = name;
24 | this._color = color;
25 | }
26 |
27 | Vine.prototype.getCost = function () {
28 | return this._beverage.getCost();
29 | };
30 |
31 | Vine.prototype.getDescription = function () {
32 | return this._beverage.getDescription();
33 | };
34 |
35 | Vine.prototype.getColor = function () {
36 | return this._color;
37 | };
38 |
39 | Vine.prototype.getName = function () {
40 | return this._name;
41 | };
42 |
43 | return Vine;
44 | })();
--------------------------------------------------------------------------------
/patterns/behavioral/iterator/iterator.ts:
--------------------------------------------------------------------------------
1 | class Iterator {
2 | private _cursor:number;
3 | private _items:Array;
4 |
5 | constructor(items) {
6 | this._cursor = 0;
7 | this._items = items;
8 | }
9 |
10 | current() {
11 | return this._items[this._cursor];
12 | }
13 |
14 | first() {
15 | this.reset();
16 | return this.current();
17 | }
18 |
19 | next() {
20 | if (this.hasNext()) {
21 | this._cursor++;
22 | return this.current();
23 | }
24 | }
25 |
26 | previous() {
27 | if (this.hasPrevious()) {
28 | this._cursor--;
29 | return this.current();
30 | }
31 | }
32 |
33 | last() {
34 | this._cursor = this._items.length - 1;
35 | return this.current();
36 | }
37 |
38 | hasNext() {
39 | return this._cursor < this._items.length;
40 | }
41 |
42 | hasPrevious() {
43 | return this._cursor > 0;
44 | }
45 |
46 | reset() {
47 | this._cursor = 0;
48 | }
49 |
50 | each(fn) {
51 | var item = this.first();
52 | for (; this.hasNext(); item = this.next()) {
53 | fn(item);
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/patterns/behavioral/strategy/spec/strategy.spec.js:
--------------------------------------------------------------------------------
1 | describe('Стратегия / Strategy', function () {
2 |
3 | var calc;
4 |
5 | beforeEach(function () {
6 | calc = new Calculator();
7 | });
8 |
9 | describe('Инстанс', function () {
10 |
11 | it('Calculator', function () {
12 | expect(calc).to.be.instanceOf(Calculator);
13 | });
14 |
15 | it('Методы', function () {
16 | expect(calc.set).to.be.a('function');
17 | expect(calc.execute).to.be.a('function');
18 | });
19 | });
20 |
21 | describe('Поведение', function () {
22 |
23 | it('Calculator.execute', function () {
24 | expect(calc.execute('add', 4, 5)).to.equal(9);
25 | expect(calc.execute('subtract', 4, 5)).to.equal(-1);
26 | expect(calc.execute('multiply', 4, 5)).to.equal(20);
27 | expect(calc.execute('divide', 40, 5)).to.equal(8);
28 | });
29 |
30 | it('Calculator.set', function () {
31 |
32 | calc
33 | .set('pow', function(x, y) {
34 | return Math.pow(x, y);
35 | });
36 |
37 | expect(calc.execute('pow', 15, 2)).to.equal(225);
38 |
39 | });
40 |
41 | });
42 | });
--------------------------------------------------------------------------------
/patterns/creational/namespace/readme.md:
--------------------------------------------------------------------------------
1 | # Пространство имен / Namespace
2 |
3 | Пространства имен помогают уменьшить количество глобальных переменных, необходимых нашим программам, и одновременно избежать конфликтов имен и чрезмерного употребления префиксов.
4 |
5 | ### Пример использования
6 |
7 | Создать неймспейс `App.modules.login`:
8 |
9 | ```js
10 | var App = App || {};
11 |
12 | Namespace.create(App, 'modules.login');
13 | ```
14 |
15 | Создать неймспейс с объектом, который хранит коды клавиш `App.keys`:
16 |
17 | ```js
18 | Namespace.create(App, 'keys', {
19 | BACKSPACE: 8,
20 | TAB: 9,
21 | ENTER: 13,
22 | SHIFT: 16,
23 | CTRL: 17,
24 | ALT: 18,
25 | ESC: 27,
26 | SPACE: 32,
27 | LEFT_ARROW: 37,
28 | UP_ARROW: 38,
29 | RIGHT_ARROW: 39,
30 | DOWN_ARROW: 40
31 | });
32 |
33 | console.log(App.keys.TAB); // => 9
34 | ```
35 |
36 | Создать неймспейс с функцией-фабрикой
37 |
38 | ```js
39 | Namespace.create(App, 'entities.user', function () {
40 | function User(name) {
41 | this._name = name;
42 | }
43 |
44 | User.prototype.introduce = function () {
45 | console.log('Привет, меня зовут ' + this._name);
46 | };
47 |
48 | return User;
49 | });
50 |
51 | var user = new App.entities.user('Козьма Прутков');
52 | user.introduce(); // => 'Привет, меня зовут Козьма Прутков'
53 | ```
54 |
55 | #### Ссылки
56 |
57 | + [Пространство имен в YUI3] (http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_namespace)
58 | + [Статья от Владимира Кузнецова] (http://noteskeeper.ru/264/)
59 |
--------------------------------------------------------------------------------
/patterns/creational/namespace/namespace.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Объект приложения, внутри которого будут определяться неймспейсы
3 | * */
4 | var Namespace = (function () {
5 |
6 | /**
7 | * Создает экземпляр
8 | * @constructor
9 | */
10 | function Namespace() {
11 | }
12 |
13 | /**
14 | * Возвращает блок пространства имен
15 | * @param {object|function} unit Блок
16 | * @returns {*}
17 | */
18 | function getUnit(unit) {
19 |
20 | var types = {
21 | 'function': function () {
22 | return new unit();
23 | },
24 | 'object': function () {
25 | return unit;
26 | }
27 | }, type = typeof unit;
28 |
29 | return types[type] ? types[type]() : undefined;
30 | }
31 |
32 | /**
33 | * Метод, который создает неймспейс
34 | * @param target {object} объект приложения, в котором создается неймспейс
35 | * @param hash {string} строка неймспейса
36 | * @param unit {(object|function)} модуль приложения
37 | * */
38 | Namespace.prototype.create = function (target, hash, unit) {
39 | var parts = hash.split('.'),
40 | length = parts.length,
41 | i = 0,
42 | current = target;
43 |
44 | for (; i < length; i++) {
45 | current[parts[i]] = current[parts[i]] || ( i === length - 1 && getUnit(unit)) || {};
46 | current = current[parts[i]];
47 | }
48 |
49 | return current;
50 | };
51 |
52 | return new Namespace();
53 | })();
--------------------------------------------------------------------------------
/patterns/structural/decorator/spec/decorator.spec.js:
--------------------------------------------------------------------------------
1 | describe('Декоратор / Decorator или Оболочка / Wrapper', function() {
2 |
3 | var beverage, vine;
4 |
5 | before(function () {
6 | beverage = new Beverage(100, 'Unknown beverage');
7 | vine = new Vine('Chateau Rieussec', 'white', 250, 'Sauternes 1-er Grand Cru Classe AOC');
8 | });
9 |
10 | describe('Инстансы', function () {
11 | it("Beverage", function () {
12 | expect(beverage).to.be.an.instanceOf(Beverage);
13 | expect(beverage.getCost).to.be.a('function');
14 | expect(beverage.getDescription).to.be.a('function');
15 | });
16 |
17 | it("Vine", function () {
18 | expect(vine).to.be.an.instanceOf(Vine);
19 | expect(vine.getCost).to.be.a('function');
20 | expect(vine.getDescription).to.be.a('function');
21 | expect(vine.getName).to.be.a('function');
22 | expect(vine.getColor).to.be.a('function');
23 | });
24 | });
25 |
26 | describe('Методы', function () {
27 | it("Метод getName()", function () {
28 | expect(vine.getName()).to.equal('Chateau Rieussec');
29 | });
30 |
31 | it("Метод getColor()", function () {
32 | expect(vine.getColor()).to.equal('white');
33 | });
34 |
35 | it("Метод getCost()", function () {
36 | expect(vine.getCost()).to.equal(250);
37 | });
38 |
39 | it("Метод getName()", function () {
40 | expect(vine.getDescription()).to.equal('Sauternes 1-er Grand Cru Classe AOC');
41 | });
42 | });
43 | });
--------------------------------------------------------------------------------
/patterns/behavioral/observer/observer.js:
--------------------------------------------------------------------------------
1 | var Observer = (function () {
2 | /**
3 | * Создает экземпляр наблюдателя
4 | * @constructor
5 | */
6 | function Observer() {
7 | this._handlers = [];
8 | }
9 |
10 | /**
11 | * Добавляет подписчика
12 | * @param {function} handler Функция-подписчик
13 | * @returns {Observer}
14 | */
15 | Observer.prototype.subscribe = function (handler) {
16 | this._handlers.push(handler);
17 | return this;
18 | };
19 |
20 | /**
21 | * Удаляет подписчика
22 | * @param {function} handler Функция-подписчик
23 | * @returns {Observer}
24 | */
25 | Observer.prototype.unsubscribe = function (handler) {
26 | var i = 0, length = this._handlers.length;
27 |
28 | for (; i < length; i++) {
29 | if (this._handlers[i] === handler) {
30 | this._handlers.splice(i, 1);
31 | break;
32 | }
33 | }
34 |
35 | return this;
36 | };
37 |
38 | /**
39 | * Инициирует запуск всех подписчиков (Публикует)
40 | * @param {*} data Данные, которые будут переданы в подписчики
41 | * @returns {Observer}
42 | */
43 | Observer.prototype.publish = function (data) {
44 | var i = 0, length = this._handlers.length;
45 |
46 | for (; i < length; i++) {
47 | this._handlers[i](data);
48 | }
49 |
50 | return this;
51 | };
52 |
53 | /**
54 | * Удаляет подписчиков
55 | * @returns {Observer}
56 | */
57 | Observer.prototype.reset = function () {
58 | this._handlers = [];
59 | return this;
60 | };
61 |
62 | return Observer;
63 | })();
--------------------------------------------------------------------------------
/patterns/behavioral/mediator/readme.md:
--------------------------------------------------------------------------------
1 | # Посредник / Mediator
2 |
3 | Определяет объект, в котором инкапсулировано знание о том, как взаимодействуют объекты из некоторого множества. Способствует уменьшению числа связей между объектами, позволяя им работать без явных ссылок друг на друга. Это, в свою очередь дает возможность независимо изменять схему взаимодействия.
4 |
5 | ### Пример использования
6 |
7 | Создадим экземпляр медиатора:
8 | ```js
9 | var mediator = new Mediator(); // : - разделитель неймспейсов
10 | ```
11 |
12 | Создадим обработчик для канала `lorem`:
13 | ```js
14 | mediator.on('lorem', function (data) {
15 | console.log('My namespace is ' + data.namespace);
16 | });
17 | ```
18 |
19 | Создадим обработчик для вложенного канала 'lorem:ipsum':
20 | ```js
21 | mediator.on('lorem:ipsum', function (data) {
22 | console.log('My namespace is ' + data.namespace);
23 | });
24 | ```
25 |
26 | Вызовем оба обработчика:
27 | ```js
28 | mediator.trigger('lorem', { namespace: 'lorem' });
29 | mediator.trigger('lorem:ipsum', { namespace: 'lorem:ipsum' });
30 | ```
31 |
32 | В консоли увидим:
33 | ```js
34 | My namespace is lorem
35 | My namespace is lorem:ipsum
36 | ```
37 |
38 | Вызовем все обработчики из неймспейса `lorem`:
39 | ```js
40 | mediator.broadcast('lorem', { namespace: 'lorem' });
41 | ```
42 |
43 | В консоли так же увидим:
44 | ```js
45 | My namespace is lorem
46 | My namespace is lorem:ipsum
47 | ```
48 |
49 | Удалим обработчики для неймспейса `lorem` с учетом вложенности:
50 | ```js
51 | mediator.off('lorem', true);
52 | // Такой вызов удалит обработчики как самого неймспейса lorem, так и вложенного в него lorem:ipsum
53 | ```
54 |
55 | #### Ссылки
56 |
57 | * [Пример простого медиатора в виде amd-модуля (gist)] (https://gist.github.com/instanceofpro/9184609)
--------------------------------------------------------------------------------
/patterns/structural/facade/facade.js:
--------------------------------------------------------------------------------
1 | // Подсистемы, которые будут сокрыты фасадом
2 | function Cash(money) {
3 | this._money = money;
4 | }
5 |
6 | Cash.prototype.pay = function (payment) {
7 | // True - если денег хватило на покупки, false - если не хватило
8 | return this._money > payment;
9 | };
10 |
11 | function Cart() {
12 | this._items = []; // Корзина пустая
13 | }
14 |
15 | Cart.prototype.add = function (item) {
16 | this._items.push(item);
17 | return this;
18 | };
19 |
20 | Cart.prototype.clean = function () {
21 | this._items = [];
22 | return this;
23 | };
24 |
25 | Cart.prototype.getCost = function () {
26 | var i = 0, sum = 0, length = this._items.length;
27 |
28 | for(; i < length; i++){
29 | sum += this._items[i];
30 | }
31 |
32 | return sum;
33 | };
34 |
35 | function Warehouse(goods) {
36 | this._goods = goods;
37 |
38 | /** Goods sample
39 | * {
40 | * milk: { count: 200, price: 6 },
41 | * bread: { count: 150, price: 5 },
42 | * meat: { count: 120, price: 20 }
43 | * }
44 | * */
45 | }
46 |
47 | Warehouse.prototype.getItem = function (item) {
48 | this._goods[item].count--;
49 | return this._goods[item].price;
50 | };
51 |
52 | Warehouse.prototype.getItemCount = function (item) {
53 | return this._goods[item].count;
54 | };
55 |
56 |
57 |
58 | // Фасад
59 | function Supermarket(shoppingList, money, goods) {
60 | this._list = shoppingList;
61 | this._money = money;
62 |
63 | this._cash = new Cash(this._money);
64 | this._cart = new Cart();
65 | this._store = new Warehouse(goods);
66 | }
67 |
68 | Supermarket.prototype.goShopping = function () {
69 | var i = 0, length = this._list.length;
70 |
71 | for(;i < length; i++) {
72 | this._cart.add(this._store.getItem(this._list[i]));
73 | }
74 |
75 | return this._cash.pay(this._cart.getCost());
76 | }
--------------------------------------------------------------------------------
/patterns/behavioral/chain-of-responsibility/chain.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Цепочка обязаностей / Chain of Responsibility
3 | * */
4 | var TodoList = (function () {
5 |
6 | /**
7 | * Создает экземпляр TodoList
8 | * @constructor
9 | * @this {TodoList}
10 | */
11 | function TodoList() {
12 | this._todos = {};
13 | }
14 |
15 | /**
16 | * Добавляет дело в список
17 | * @param {string} title Заголовок дела
18 | * @param {string} text Текст дела
19 | * @returns {TodoList} Ссылка на самого себя
20 | */
21 | TodoList.prototype.add = function (title, text) {
22 | if (this._todos[title] === undefined) {
23 | this._todos[title] = text;
24 | }
25 |
26 | return this;
27 | };
28 |
29 | /**
30 | * Отдает текст дела по его заголовку
31 | * @param {string} title Заголовок дела
32 | * @returns {string} Текст дела
33 | */
34 | TodoList.prototype.get = function (title) {
35 | return this._todos[title];
36 | };
37 |
38 | /**
39 | * Удаляет дело по его заголовку
40 | * @param {string} title Заголовк дела
41 | * @returns {TodoList}
42 | */
43 | TodoList.prototype.remove = function (title) {
44 | delete this._todos[title];
45 |
46 | return this;
47 | };
48 |
49 | /**
50 | * Редактирует заведенное дело
51 | * @param {string} title Заголовок дела
52 | * @param {string} text Текст дела
53 | * @returns {TodoList}
54 | */
55 | TodoList.prototype.edit = function (title, text) {
56 | if (this._todos[title] !== undefined) {
57 | this._todos[title] = text;
58 | }
59 |
60 | return this;
61 | };
62 |
63 | /**
64 | * Очищает список дел
65 | * @returns {TodoList}
66 | */
67 | TodoList.prototype.clean = function () {
68 | this._todos = {};
69 |
70 | return this;
71 | };
72 |
73 | return TodoList;
74 | })();
--------------------------------------------------------------------------------
/patterns/creational/prototype/spec/prototype.spec.js:
--------------------------------------------------------------------------------
1 | describe('Прототип / Prototype', function () {
2 |
3 | var john, johnProto;
4 |
5 | before(function () {
6 | john = new Person('John', 'Doe');
7 | johnProto = new Proto(john);
8 | });
9 |
10 | describe('Классы', function () {
11 |
12 | it('Person является классом', function () {
13 | expect(Person).to.be.a('function');
14 | expect(john).to.be.an.instanceOf(Person);
15 | });
16 |
17 | it('Экземпляр класса Person имеет метод introduce()', function() {
18 | expect(john.introduce).to.be.a('function');
19 | expect(john.introduce()).to.equal('My name is John Doe');
20 | });
21 |
22 | it('Proto является классом', function () {
23 | expect(Proto).to.be.a('function');
24 | expect(johnProto).to.be.an.instanceOf(Proto);
25 | });
26 |
27 | it('Экземпляр класса Proto имеет метод clone()', function() {
28 | expect(johnProto.clone).to.be.a('function');
29 | });
30 |
31 | });
32 |
33 | describe('Поведение', function () {
34 |
35 | var johnClone, johnAnotherClone;
36 |
37 | before(function () {
38 | johnClone = johnProto.clone();
39 | johnAnotherClone = johnProto.clone();
40 | });
41 |
42 | it('Метод .clone() возвращает объект', function () {
43 | expect(johnClone).to.be.an('object');
44 | });
45 |
46 | it('Возвращаемые методом .clone() объекты должны быть копиями экземпляра класса Person', function () {
47 | expect(john.introduce() === johnClone.introduce()).to.be.true;
48 | });
49 |
50 | it('Копии не должны быть одним и тем же объектом', function () {
51 | expect(john === johnClone).to.be.false;
52 | expect(john === johnAnotherClone).to.be.false;
53 | expect(johnAnotherClone === johnClone).to.be.false;
54 | });
55 | });
56 |
57 | });
--------------------------------------------------------------------------------
/patterns/structural/adapter/adapter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Англичанин
3 | * */
4 | var Englishman = (function () {
5 |
6 | function Englishman(firstname, lastname) {
7 | this._firstname = firstname;
8 | this._lastname = lastname;
9 | }
10 |
11 | Englishman.prototype.getFirstname = function () {
12 | return this._firstname;
13 | };
14 |
15 | Englishman.prototype.getLastname = function () {
16 | return this._lastname;
17 | };
18 |
19 | Englishman.prototype.introduce = function () {
20 | return 'My name is ' + this._firstname + ' ' + this._lastname;
21 | };
22 |
23 | Englishman.prototype.greet = function () {
24 | return 'Hello! How are you?';
25 | };
26 |
27 | return Englishman;
28 | })();
29 |
30 | /**
31 | * Итальянец
32 | * */
33 | var Italian = (function () {
34 |
35 | function Italian(firstname, lastname) {
36 | this._firstname = firstname;
37 | this._lastname = lastname;
38 | }
39 |
40 | Italian.prototype.getFirstname = function () {
41 | return this._firstname;
42 | };
43 |
44 | Italian.prototype.getLastname = function () {
45 | return this._lastname;
46 | };
47 |
48 | Italian.prototype.introdurre = function () {
49 | return 'Il mio nome è ' + this._firstname + ' ' + this._lastname;
50 | };
51 |
52 | Italian.prototype.ciao = function () {
53 | return 'Ciao! Come stai?';
54 | };
55 |
56 | return Italian;
57 | })();
58 |
59 | /**
60 | * Адаптер итальянца к англичанину
61 | * */
62 | var AdaptToEnglish = (function () {
63 |
64 | function Adapter(italian) {
65 | this._firstname = italian.getFirstname();
66 | this._lastname = italian.getLastname();
67 | }
68 |
69 | Adapter.prototype.introduce = function () {
70 | return 'My name is ' + this._firstname + ' ' + this._lastname;
71 | };
72 |
73 | Adapter.prototype.greet = function () {
74 | return 'Hello! How are you?';
75 | };
76 |
77 | return Adapter;
78 | })();
--------------------------------------------------------------------------------
/patterns/enterprise/registry/spec/registry.spec.js:
--------------------------------------------------------------------------------
1 | describe('Реестр / Registry', function() {
2 |
3 | describe('Простой реестр', function () {
4 |
5 | var registry;
6 |
7 | before(function () {
8 | registry = new SimpleRegistry();
9 | });
10 |
11 | it('Инстанс', function () {
12 | expect(SimpleRegistry).to.be.a('function');
13 | expect(registry).to.be.an.instanceOf(SimpleRegistry);
14 | expect(registry.set).to.be.a('function');
15 | expect(registry.get).to.be.a('function');
16 | expect(registry.has).to.be.a('function');
17 | });
18 |
19 | it('Поведение', function (){
20 | registry.set('foo', 'bar');
21 | expect(registry.has('foo')).to.be.true;
22 | expect(registry.get('foo')).to.equal('bar');
23 |
24 | expect(registry.has('hello')).to.be.false;
25 | expect(registry.get('hello')).to.be.undefined;
26 | });
27 |
28 | });
29 |
30 | describe('Реестр-одиночка', function () {
31 |
32 | var registry, another;
33 |
34 | before(function () {
35 | registry = new SingletonRegistry();
36 | another = SingletonRegistry();
37 | });
38 |
39 | it('Инстанс', function () {
40 | expect(SingletonRegistry).to.be.a('function');
41 | expect(registry).to.equal(another);
42 | expect(registry).to.be.an.instanceOf(SingletonRegistry);
43 | expect(registry.set).to.be.a('function');
44 | expect(registry.get).to.be.a('function');
45 | expect(registry.has).to.be.a('function');
46 | });
47 |
48 | it('Поведение', function (){
49 | registry.set('foo', 'bar');
50 | expect(registry.has('foo')).to.be.true;
51 | expect(registry.get('foo')).to.equal('bar');
52 | expect(another.has('foo')).to.be.true;
53 | expect(another.get('foo')).to.equal('bar');
54 |
55 | expect(registry.has('hello')).to.be.false;
56 | expect(registry.get('hello')).to.be.undefined;
57 | expect(another.has('hello')).to.be.false;
58 | expect(another.get('hello')).to.be.undefined;
59 | });
60 | });
61 | });
--------------------------------------------------------------------------------
/patterns/structural/composite/composite.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Класс-ветка, который может в себе содержать как такие же ветки, так и листья
3 | * */
4 | var Branch = (function () {
5 |
6 | function Branch(name) {
7 | this._name = name;
8 | this._children = [];
9 | }
10 |
11 | Branch.prototype.addChild = function (child) {
12 | this._children.push(child);
13 |
14 | return this;
15 | };
16 |
17 | Branch.prototype.removeChild = function (child) {
18 | var length = this._children.length,
19 | i = 0;
20 |
21 | for (; i < length; i++) {
22 | if (this._children[i] === child) {
23 | this._children.splice(i, 1);
24 | return;
25 | }
26 | }
27 | };
28 |
29 | Branch.prototype.getChild = function (item) {
30 | return this._children[item];
31 | };
32 |
33 | Branch.prototype.hasChild = function (child) {
34 | var length = this._children.length,
35 | i = 0;
36 |
37 | for (; i < length; i++) {
38 | if (this._children[i] === child) {
39 | return true;
40 | }
41 | }
42 |
43 | return false;
44 | };
45 |
46 | Branch.prototype.hasChildren = function() {
47 | return this.getChildrenCount() > 0;
48 | };
49 |
50 | Branch.prototype.getName = function() {
51 | return this._name;
52 | };
53 |
54 | Branch.prototype.getChildrenCount = function () {
55 | return this._children.length;
56 | };
57 |
58 | Branch.prototype.print = function() {
59 | var names = [],
60 | i = 0,
61 | length = this._children.length;
62 |
63 | if (length) {
64 | for (; i < length; i++) {
65 | names.push(this._children[i].getName());
66 | }
67 | }
68 |
69 | return this._name + ' -> [' + names.join(', ') + ']';
70 | };
71 |
72 | return Branch;
73 | })();
74 |
75 |
76 | /**
77 | * Класс-лист, который может принадлежать ветке
78 | * */
79 | var Leaf = (function () {
80 |
81 | function Leaf(name) {
82 | this._name = name;
83 | }
84 |
85 | Leaf.prototype.getName = function() {
86 | return this._name;
87 | };
88 |
89 | return Leaf;
90 | })();
--------------------------------------------------------------------------------
/patterns/creational/namespace/spec/namespace.spec.js:
--------------------------------------------------------------------------------
1 | describe('Пространство имен / Namespace', function () {
2 |
3 | describe('Инстанс', function () {
4 |
5 | it('Namespace', function () {
6 | expect(Namespace).to.be.a('object');
7 | });
8 |
9 | it('Методы', function () {
10 | expect(Namespace.create).to.exist;
11 | expect(Namespace.create).to.be.a('function');
12 | });
13 | });
14 |
15 | describe('Поведение', function () {
16 |
17 | it('Создание неймспейсов', function () {
18 | var App = App || {};
19 |
20 | expect(Namespace.create(App, 'modules.login')).to.equal(App.modules.login);
21 | expect(Namespace.create(App.modules, 'logout')).to.equal(App.modules.logout);
22 | expect(Namespace.create(App, 'modules')).to.equal(App.modules);
23 | expect(Namespace.create(App.modules.logout, 'success', {})).to.equal(App.modules.logout.success);
24 | expect(Namespace.create(App, 'modules.logout.fail', {})).to.equal(App.modules.logout.fail);
25 | });
26 |
27 | it('Создание неймспейсов с объектом', function () {
28 | var AnotherApp = AnotherApp || {};
29 |
30 | expect(Namespace.create(AnotherApp, 'modules.login', { foo: 'bar' })).to.equal(AnotherApp.modules.login);
31 | expect(AnotherApp.modules.login).to.have.property('foo');
32 | });
33 |
34 | it('Создание неймспейсов с функциями-фабриками', function () {
35 | var FnApp = FnApp || {};
36 |
37 | expect(Namespace.create(FnApp, 'entities.user', function () {
38 | function User(name) {
39 | this._name = name;
40 | }
41 |
42 | User.prototype.introduce = function () {
43 | return 'Привет, меня зовут ' + this._name;
44 | };
45 |
46 | return User;
47 | })).to.equal(FnApp.entities.user);
48 |
49 | var user = new FnApp.entities.user('Козьма Прутков');
50 |
51 | expect(user).to.be.instanceOf(FnApp.entities.user);
52 | expect(user.introduce).to.be.a('function');
53 | expect(user.introduce()).to.equal('Привет, меня зовут Козьма Прутков');
54 | });
55 |
56 | });
57 | });
--------------------------------------------------------------------------------
/patterns/behavioral/state/spec/state.spec.js:
--------------------------------------------------------------------------------
1 | describe('Состояние / State', function () {
2 |
3 | var calc, add, subtract, multiply, divide;
4 |
5 | before(function () {
6 | add = new Add();
7 | subtract = new Subtract();
8 | multiply = new Multiply();
9 | divide = new Divide();
10 | calc = new Calculator(add);
11 | });
12 |
13 | describe('Инстанс', function () {
14 |
15 | it('Calculator и методы', function () {
16 | expect(calc).to.be.instanceOf(Calculator);
17 | expect(calc.set).to.be.a('function');
18 | expect(calc.execute).to.be.a('function');
19 | });
20 |
21 | it('Add и методы', function () {
22 | expect(add).to.be.instanceOf(Add);
23 | expect(add.execute).to.be.a('function');
24 | });
25 |
26 | it('Subtract и методы', function () {
27 | expect(subtract).to.be.instanceOf(Subtract);
28 | expect(subtract.execute).to.be.a('function');
29 | });
30 |
31 | });
32 |
33 | describe('Поведение', function () {
34 |
35 | it('Сложение', function () {
36 | expect(calc.execute(4, 5)).to.equal(9);
37 | expect(calc.execute(0, 0)).to.equal(0);
38 | expect(calc.execute(-7, 3)).to.equal(-4);
39 | expect(calc.execute(-2, -8)).to.equal(-10);
40 | });
41 |
42 | it('Вычитание', function () {
43 | calc.set(subtract);
44 |
45 | expect(calc.execute(4, 5)).to.equal(-1);
46 | expect(calc.execute(0, 0)).to.equal(0);
47 | expect(calc.execute(-7, 3)).to.equal(-10);
48 | expect(calc.execute(-2, -8)).to.equal(6);
49 | });
50 |
51 | it('Умножение', function () {
52 | calc.set(multiply);
53 |
54 | expect(calc.execute(4, 5)).to.equal(20);
55 | expect(calc.execute(0, 0)).to.equal(0);
56 | expect(calc.execute(-7, 3)).to.equal(-21);
57 | expect(calc.execute(-2, -8)).to.equal(16);
58 | });
59 |
60 | it('Деление', function () {
61 | calc.set(divide);
62 |
63 | expect(calc.execute(40, 5)).to.equal(8);
64 | expect(calc.execute(2, 4)).to.equal(0.5);
65 | expect(calc.execute(-6, 3)).to.equal(-2);
66 | expect(calc.execute(-2, -8)).to.equal(0.25);
67 | });
68 | });
69 | });
--------------------------------------------------------------------------------
/patterns/behavioral/chain-of-responsibility/spec/chain.spec.js:
--------------------------------------------------------------------------------
1 | describe('Цепочка обязаностей / Chain of Responsibility', function () {
2 |
3 | var todos;
4 |
5 | describe('Инстанс', function () {
6 |
7 | beforeEach(function () {
8 | todos = new TodoList();
9 | });
10 |
11 | it('TodoList', function () {
12 | expect(todos).to.be.instanceOf(TodoList);
13 | });
14 |
15 | it('Методы', function () {
16 | expect(todos.add).to.be.a('function');
17 | expect(todos.edit).to.be.a('function');
18 | expect(todos.remove).to.be.a('function');
19 | expect(todos.clean).to.be.a('function');
20 | expect(todos.get).to.be.a('function');
21 | });
22 | });
23 |
24 | describe('Поведение', function () {
25 |
26 | beforeEach(function () {
27 | todos = new TodoList();
28 | });
29 |
30 | it('TodoList.add', function () {
31 | expect(todos.add('Первая запись', 'Текст первой записи')).to.equal(todos);
32 | });
33 |
34 | it('TodoList.get', function () {
35 | todos.add('Запись', 'Текст записи');
36 |
37 | expect(todos.get('Запись')).to.equal('Текст записи');
38 | expect(todos.get('Несуществующая запись')).to.be.undefined;
39 | });
40 |
41 | it('TodoList.edit', function () {
42 | todos.add('Запись', 'Текст записи');
43 |
44 | expect(todos.edit('Запись', 'Отредактированный текст записи')).to.equal(todos);
45 | expect(todos.get('Запись')).to.equal('Отредактированный текст записи');
46 | });
47 |
48 | it('TodoList.remove', function () {
49 | todos.add('Запись', 'Текст записи');
50 |
51 | expect(todos.remove('Запись')).to.equal(todos);
52 | expect(todos.get('Запись')).to.be.undefined;
53 | });
54 |
55 |
56 | it('TodoList.clean', function () {
57 | todos
58 | .add('Первая запись', 'Текст первой записи')
59 | .add('Вторая запись', 'Текст второй записи')
60 | .add('Третья запись', 'Текст третьей записи')
61 | .add('Четвертая запись', 'Текст четвертой записи');
62 |
63 | expect(todos.clean()).to.equal(todos);
64 | expect(todos.get('Первая запись')).to.be.undefined;
65 | expect(todos.get('Вторая запись')).to.be.undefined;
66 | expect(todos.get('Третья запись')).to.be.undefined;
67 | expect(todos.get('Четвертая запись')).to.be.undefined;
68 | });
69 | });
70 |
71 | });
--------------------------------------------------------------------------------
/patterns/structural/composite/spec/composite.spec.js:
--------------------------------------------------------------------------------
1 | describe('Компоновщик / Composite', function () {
2 |
3 | describe('Инстанс', function () {
4 |
5 | var branch, leaf;
6 |
7 | before(function () {
8 | branch = new Branch('Меню');
9 | leaf = new Leaf('Пункт меню');
10 | });
11 |
12 | it('Branch', function () {
13 | expect(Branch).to.be.a('function');
14 | expect(branch).to.be.an.instanceOf(Branch);
15 |
16 | expect(branch.getName).to.be.a('function');
17 | expect(branch.addChild).to.be.a('function');
18 | expect(branch.removeChild).to.be.a('function');
19 | expect(branch.getChild).to.be.a('function');
20 | expect(branch.hasChild).to.be.a('function');
21 | expect(branch.hasChildren).to.be.a('function');
22 | expect(branch.getChildrenCount).to.be.a('function');
23 | expect(branch.print).to.be.a('function');
24 |
25 | });
26 |
27 | it('Leaf', function() {
28 | expect(Leaf).to.be.a('function');
29 | expect(leaf).to.be.an.instanceOf(Leaf);
30 |
31 | expect(leaf.getName).to.be.a('function');
32 | });
33 |
34 | });
35 |
36 | describe('Поведение', function () {
37 |
38 | var menu, subMenu, leaf;
39 |
40 | beforeEach(function () {
41 | menu = new Branch('Меню ресторана');
42 | subMenu = new Branch('Винная карта');
43 | leaf = new Leaf('Вермут');
44 | });
45 |
46 | it('addChild', function() {
47 | expect(menu.getChildrenCount()).to.equal(0);
48 | menu.addChild(new Branch('Алкоголь'));
49 | expect(menu.getChildrenCount()).to.equal(1);
50 | });
51 |
52 | it('removeChild', function() {
53 | expect(menu.hasChildren()).to.be.false;
54 | menu.addChild(subMenu);
55 | expect(menu.hasChildren()).to.be.true;
56 | menu.removeChild(subMenu);
57 | expect(menu.hasChildren()).to.be.false;
58 | });
59 |
60 | it('getChild', function() {
61 | menu.addChild(subMenu);
62 | expect(menu.getChild(0)).to.equal(subMenu);
63 | expect(menu.getChild(0).getName()).to.equal('Винная карта');
64 | });
65 |
66 | it('hasChild', function() {
67 | menu.addChild(subMenu);
68 | expect(menu.hasChild(subMenu)).to.true;
69 | expect(menu.hasChild(leaf)).to.false;
70 |
71 | subMenu.addChild(leaf);
72 | expect(subMenu.hasChild(leaf)).to.true;
73 | });
74 | });
75 |
76 | });
--------------------------------------------------------------------------------
/patterns/behavioral/strategy/strategy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Калькулятор - стратегия
3 | * */
4 | var Calculator = (function () {
5 |
6 | /**
7 | * Создает экземпляр
8 | * @constructor
9 | */
10 | function Calculator() {
11 |
12 | /**
13 | * Встроенные алгоритмы
14 | * @private
15 | */
16 | this._methods = {
17 | /**
18 | * Складывает два операнда
19 | * @param {number} x Первый операнд
20 | * @param {number} y Второй операнд
21 | * @returns {number} Сумма
22 | */
23 | add: function (x, y) {
24 | return x + y;
25 | },
26 |
27 | /**
28 | * Вычитает из одного операнда другой
29 | * @param {number} x Уменьшаемое
30 | * @param {number} y Вычитаемое
31 | * @returns {number} Разница
32 | */
33 | subtract: function (x, y) {
34 | return x - y;
35 | },
36 |
37 | /**
38 | * Перемножает два операнда
39 | * @param {number} x Первый операнд
40 | * @param {number} y Второй операнд
41 | * @returns {number} Произведение
42 | */
43 | multiply: function (x, y) {
44 | return x * y;
45 | },
46 |
47 | /**
48 | * Делит один операнд на другой
49 | * @param {number} x Делимое
50 | * @param {number} y Делитель
51 | * @returns {number} Результат
52 | */
53 | divide: function (x, y) {
54 | return x / y;
55 | }
56 | };
57 | }
58 |
59 | /**
60 | * Добавляет новый метод в калькулятор
61 | * @param {string} name Имя метода
62 | * @param {function} method Функция-метод
63 | * @returns {Calculator}
64 | */
65 | Calculator.prototype.set = function (name, method) {
66 | if (this._methods[name] === undefined) {
67 | this._methods[name] = method;
68 | }
69 |
70 | return this;
71 | };
72 |
73 | /**
74 | * Вызывает метод и передает в него параметры
75 | * @param {string} method Имя метода
76 | * @param {number} x Первый операнд
77 | * @param {number} y Второй операнд
78 | * @returns {number} Результат
79 | */
80 | Calculator.prototype.execute = function (method, x, y) {
81 | if (this._methods[method] !== undefined) {
82 | return this._methods[method](x, y);
83 | } else {
84 | throw new Error('Method does not exists!');
85 | }
86 | };
87 |
88 | return Calculator;
89 | })();
--------------------------------------------------------------------------------
/patterns/behavioral/iterator/iterator.js:
--------------------------------------------------------------------------------
1 | var Iterator = (function () {
2 | /**
3 | * Создает экземпляр итератора
4 | * @param {array} items Итерируемый массив
5 | * @constructor
6 | */
7 | function Iterator(items) {
8 | this._cursor = 0;
9 | this._items = items;
10 | }
11 |
12 | /**
13 | * Отдает текущий элемент массива
14 | * @returns {*}
15 | */
16 | Iterator.prototype.current = function () {
17 | return this._items[this._cursor];
18 | };
19 |
20 | /**
21 | * Сдвигает курсор в начальное положение и отдает первый элемент массив
22 | * @returns {*}
23 | */
24 | Iterator.prototype.first = function () {
25 | this.reset();
26 | return this.current();
27 | };
28 |
29 | /**
30 | * Сдвигает курсор на один шаг вперед и отдает элемент массива
31 | * @returns {*}
32 | */
33 | Iterator.prototype.next = function () {
34 | if (this.hasNext()) {
35 | this._cursor++;
36 | return this.current();
37 | }
38 | };
39 |
40 | /**
41 | * Сдвигает курсор на одни шаг назад и отдает элемент массива
42 | * @returns {*}
43 | */
44 | Iterator.prototype.previous = function () {
45 | if (this.hasPrevious()) {
46 | this._cursor--;
47 | return this.current();
48 | }
49 | };
50 |
51 | /**
52 | * Сдвигает курсор в конец и отдает последний элемент массива
53 | * @returns {*}
54 | */
55 | Iterator.prototype.last = function () {
56 | this._cursor = this._items.length - 1;
57 | return this.current();
58 | };
59 |
60 | /**
61 | * Сброс курсора в начальное положение
62 | */
63 | Iterator.prototype.reset = function () {
64 | this._cursor = 0;
65 | };
66 |
67 | /**
68 | * Проверяет есть ли после текущего элемента еще один элемент
69 | * @returns {boolean} Истина/ложь
70 | */
71 | Iterator.prototype.hasNext = function () {
72 | return this._cursor < this._items.length;
73 | };
74 |
75 | /**
76 | * Проверяет есть ли перед текущим элементом еще один элемент
77 | * @returns {boolean} Истина/ложь
78 | */
79 | Iterator.prototype.hasPrevious = function () {
80 | return this._cursor > 0;
81 | };
82 |
83 | /**
84 | * Проходит по массиву и для каждого из его элементов выполняет функцию callback
85 | * @param {function} callback Функция, которая будет выполнена для каждого элемента
86 | */
87 | Iterator.prototype.each = function (callback) {
88 | var item = this.first();
89 | for(; this.hasNext(); item = this.next()) {
90 | callback(item);
91 | }
92 | };
93 |
94 | return Iterator;
95 | })();
96 |
--------------------------------------------------------------------------------
/patterns/behavioral/iterator/spec/iterator.spec.js:
--------------------------------------------------------------------------------
1 | describe('Итератор / Iterator или Курсор / Cursor', function () {
2 |
3 | var iterated, arr, sum;
4 |
5 | before(function () {
6 | arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
7 | iterated = new Iterator(arr);
8 | sum = 0;
9 | });
10 |
11 | describe('Проверка объекта', function () {
12 |
13 | it("Инстанс", function () {
14 | expect(iterated).to.be.an.instanceOf(Iterator);
15 | });
16 |
17 | it('Методы', function () {
18 | expect(iterated.current).to.be.a('function');
19 | expect(iterated.first).to.be.a('function');
20 | expect(iterated.last).to.be.a('function');
21 | expect(iterated.next).to.be.a('function');
22 | expect(iterated.previous).to.be.a('function');
23 | expect(iterated.reset).to.be.a('function');
24 | expect(iterated.hasNext).to.be.a('function');
25 | expect(iterated.hasPrevious).to.be.a('function');
26 | expect(iterated.each).to.be.a('function');
27 | });
28 | });
29 |
30 | describe('Работа методов', function () {
31 | it('Текущий элемент', function () {
32 | expect(iterated.current()).to.equal(0);
33 | });
34 |
35 | it("Следующий элемент", function () {
36 | expect(iterated.next()).to.equal(1);
37 | });
38 |
39 | it("Последний элемент", function () {
40 | expect(iterated.last()).to.equal(9);
41 | });
42 |
43 | it("Предыдущий элемент", function () {
44 | expect(iterated.previous()).to.equal(8);
45 | });
46 |
47 | it("Проход по всем элементам", function () {
48 | iterated.reset();
49 |
50 | iterated.each(function (item) {
51 | sum += item;
52 | });
53 |
54 | expect(sum).to.equal(45);
55 | });
56 |
57 | it("Есть ли следующий элемент", function () {
58 | iterated.first();
59 |
60 | expect(iterated.hasNext()).to.be.true;
61 | });
62 |
63 | it("Есть ли предыдущий элемент", function () {
64 | iterated.last();
65 |
66 | expect(iterated.hasPrevious()).to.be.true;
67 | });
68 |
69 | it("Сброс курсора", function () {
70 | iterated.reset();
71 |
72 | expect(iterated.current()).to.equal(0);
73 | });
74 |
75 | it("Выход из диапазона справа", function () {
76 | iterated.last();
77 |
78 | expect(iterated.next()).to.be.undefined;
79 | });
80 |
81 | it("Выход из диапазона слева", function () {
82 | iterated.first();
83 |
84 | expect(iterated.previous()).to.be.undefined;
85 | });
86 | });
87 | });
--------------------------------------------------------------------------------
/patterns/behavioral/observer/spec/observer.spec.js:
--------------------------------------------------------------------------------
1 | describe('Наблюдатель / Observer / Publisher-Subscriber', function () {
2 |
3 | var observer;
4 |
5 | describe('Проверка объекта', function () {
6 |
7 | before(function () {
8 | observer = new Observer();
9 | });
10 |
11 | it('Инстанс', function () {
12 | expect(observer).to.be.instanceOf(Observer);
13 | });
14 |
15 | it('Методы', function() {
16 | expect(observer.subscribe).to.be.a('function');
17 | expect(observer.publish).to.be.a('function');
18 | expect(observer.unsubscribe).to.be.a('function');
19 | });
20 | });
21 |
22 | describe('Поведение', function() {
23 |
24 | var firstFn, secondFn, thirdFn;
25 |
26 | beforeEach(function () {
27 | observer = new Observer();
28 | firstFn = sinon.spy();
29 | secondFn = sinon.spy();
30 | thirdFn = sinon.spy();
31 | });
32 |
33 | it('Добавление обработчиков', function () {
34 | observer
35 | .subscribe(firstFn)
36 | .subscribe(secondFn)
37 | .subscribe(thirdFn)
38 | .publish();
39 |
40 | expect(firstFn.called).to.be.true;
41 | expect(secondFn.called).to.be.true;
42 | expect(thirdFn.called).to.be.true;
43 | });
44 |
45 | it('Добавление обработчика с данными', function () {
46 |
47 | var obj = { text: 'Hello' };
48 |
49 | observer
50 | .subscribe(firstFn)
51 | .subscribe(secondFn)
52 | .subscribe(thirdFn)
53 | .publish(obj);
54 |
55 | expect(firstFn.calledWith(obj)).to.be.true;
56 | expect(secondFn.calledWith(obj)).to.be.true;
57 | expect(thirdFn.calledWith(obj)).to.be.true;
58 | });
59 |
60 | it('Удаление из списка обработчиков', function () {
61 |
62 | observer
63 | .subscribe(firstFn)
64 | .subscribe(secondFn)
65 | .reset()
66 | .subscribe(thirdFn)
67 | .publish();
68 |
69 | expect(firstFn.called).to.be.false;
70 | expect(secondFn.called).to.be.false;
71 | expect(thirdFn.called).to.be.true;
72 | });
73 |
74 | it('Очистка списка обработчиков', function () {
75 |
76 | observer
77 | .subscribe(firstFn)
78 | .subscribe(secondFn)
79 | .subscribe(thirdFn)
80 | .unsubscribe(firstFn)
81 | .publish();
82 |
83 | expect(firstFn.called).to.be.false;
84 | expect(secondFn.called).to.be.true;
85 | expect(thirdFn.called).to.be.true;
86 | });
87 | });
88 | });
--------------------------------------------------------------------------------
/patterns/structural/proxy/proxy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Класс, который будет защищать Proxy
3 | * */
4 | var Calc = (function () {
5 |
6 | function Calc() {
7 | /**
8 | * Инициализация
9 | * */
10 | }
11 |
12 | Calc.prototype.add = function (x, y) {
13 | return x + y;
14 | };
15 |
16 | Calc.prototype.subtract = function (x, y) {
17 | return x - y;
18 | };
19 |
20 | Calc.prototype.multiply = function (x, y) {
21 | return x * y;
22 | };
23 |
24 | Calc.prototype.divide = function (x, y) {
25 | return x / y;
26 | };
27 |
28 | Calc.prototype.power = function (x, y) {
29 | return Math.pow(x, y);
30 | };
31 |
32 | Calc.prototype.root = function (x, y) {
33 | return this.power(x, this.divide(1, y));
34 | };
35 |
36 | return Calc;
37 | })();
38 |
39 | /**
40 | * Класс, который выступает в роли суррогата класса Calc
41 | * */
42 | var CalcProxy = (function () {
43 |
44 | var calc;
45 |
46 | function Proxy() {
47 | calc = null;
48 | }
49 |
50 | /**
51 | * Экземпляр calc будет недоступен из вне.
52 | * Он хранится в замыкании.
53 | * Будет инициализирован только при первом обращении.
54 | * */
55 | function initialize() {
56 | if (calc === null) {
57 | calc = new Calc();
58 | }
59 | }
60 |
61 | function parse(value) {
62 | value = parseInt(value, 10);
63 | if (isNaN(value)) {
64 | throw new Error('Arguments are invalid!');
65 | } else {
66 | return value;
67 | }
68 | }
69 |
70 | Proxy.prototype.add = function (x, y) {
71 | initialize();
72 | return calc.add(parse(x), parse(y));
73 | };
74 |
75 | Proxy.prototype.subtract = function (x, y) {
76 | initialize();
77 | return calc.subtract(parse(x), parse(y));
78 | };
79 |
80 | Proxy.prototype.multiply = function (x, y) {
81 | initialize();
82 | return calc.multiply(parse(x), parse(y));
83 | };
84 |
85 | Proxy.prototype.divide = function (x, y) {
86 | initialize();
87 | y = parse(y);
88 |
89 | if (y === 0) {
90 | throw new Error('Division by zero!');
91 | }
92 |
93 | return calc.divide(parse(x), y);
94 | };
95 |
96 | Proxy.prototype.power = function (x, y) {
97 | initialize();
98 | return calc.power(parse(x), parse(y));
99 | };
100 |
101 | Proxy.prototype.root = function (x, y) {
102 | initialize();
103 | y = parse(y);
104 |
105 | if (y < 0) {
106 | throw new Error('Negative power!');
107 | }
108 |
109 | return calc.root(parse(x), y);
110 | };
111 |
112 | return Proxy;
113 | })();
--------------------------------------------------------------------------------
/patterns/structural/mixin/spec/mixin.spec.js:
--------------------------------------------------------------------------------
1 | describe('Примесь / Mixin', function () {
2 |
3 | var literalBook, functionalBook;
4 |
5 | before(function () {
6 | literalBook = new BookLiteral('Альбер Камю', 'Чума');
7 | functionalBook = new BookFunctional('Герберт Уэлсс', 'Война миров');
8 | });
9 |
10 | describe('Инстанс', function () {
11 |
12 | it('BookLiteral', function () {
13 | expect(literalBook).to.be.an('object');
14 | expect(literalBook).to.be.instanceOf(BookLiteral);
15 | expect(literalBook.getInfo).to.be.a('function');
16 | });
17 |
18 | it('BookFunctional', function () {
19 | expect(functionalBook).to.be.an('object');
20 | expect(functionalBook).to.be.instanceOf(BookFunctional);
21 | expect(functionalBook.getInfo).to.be.a('function');
22 | });
23 |
24 | it('BookLiteralMixin', function () {
25 | expect(BookLiteralMixin).to.be.an('object');
26 | expect(BookLiteralMixin.getAuthor).to.be.a('function');
27 | expect(BookLiteralMixin.getTitle).to.be.a('function');
28 | });
29 |
30 | it('BookFunctionalMixin', function () {
31 | expect(BookFunctionalMixin).to.be.an('function');
32 | });
33 |
34 | });
35 |
36 | describe('Поведение', function () {
37 |
38 | describe('Примесь - литерал', function () {
39 |
40 | it('Объект без примеси', function () {
41 | expect(literalBook.getAuthor).to.be.undefined;
42 | expect(literalBook.getTitle).to.be.undefined;
43 | });
44 |
45 | it('Объект с примесью', function () {
46 | _.extend(BookLiteral.prototype, BookLiteralMixin);
47 | literalBook = new BookLiteral('Альбер Камю', 'Чума');
48 |
49 | expect(literalBook.getAuthor).to.be.a('function');
50 | expect(literalBook.getAuthor()).to.equal('Альбер Камю');
51 | expect(literalBook.getTitle).to.be.a('function');
52 | expect(literalBook.getTitle()).to.equal('Чума');
53 | });
54 |
55 | });
56 |
57 | describe('Примесь - функция', function () {
58 |
59 | it('Объект без примеси', function () {
60 | expect(functionalBook.getAuthor).to.be.undefined;
61 | expect(functionalBook.getTitle).to.be.undefined;
62 | });
63 |
64 | it('Объект с примесью', function () {
65 | BookFunctionalMixin.call(BookFunctional.prototype);
66 | functionalBook = new BookFunctional('Герберт Уэлсс', 'Война миров');
67 |
68 | expect(functionalBook.getAuthor).to.be.a('function');
69 | expect(functionalBook.getAuthor()).to.equal('Герберт Уэлсс');
70 | expect(functionalBook.getTitle).to.be.a('function');
71 | expect(functionalBook.getTitle()).to.equal('Война миров');
72 | });
73 | });
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/patterns/enterprise/registry/registry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Простой реестр, множество копий
3 | * */
4 | var SimpleRegistry = (function () {
5 |
6 | /**
7 | * Создает экземпляр
8 | * @constructor
9 | */
10 | function Registry() {
11 | /** Место, где будут храниться записи */
12 | this._storage = {};
13 | }
14 |
15 | /**
16 | * Вносит запись в реестр
17 | * @param {string} key Ключ записи реестра
18 | * @param {*} value Значение записи реестра
19 | */
20 | Registry.prototype.set = function (key, value) {
21 | this._storage[key] = value;
22 | };
23 |
24 | /**
25 | * Возвращает запись из реестра по ключу
26 | * @param {string} key Ключ записи реестра
27 | * @returns {*} Значение записи реестра
28 | */
29 | Registry.prototype.get = function (key) {
30 | return this._storage[key];
31 | };
32 |
33 | /**
34 | * Проверяет существование записи в реестре
35 | * @param {string} key Ключ записи в реестре
36 | * @returns {boolean} Истина/ложь
37 | */
38 | Registry.prototype.has = function (key) {
39 | return this._storage[key] !== undefined;
40 | };
41 |
42 | return Registry;
43 | })();
44 |
45 | /**
46 | * Глобальный реестр, то есть реестр-синглтон
47 | * */
48 | var SingletonRegistry = (function () {
49 |
50 | var _instance, _storage;
51 |
52 | /**
53 | * Создает экземпляр реестра-синглтона
54 | * @constructor
55 | */
56 | function Registry() {
57 | if (!_instance) {
58 | _storage = {};
59 | _instance = this;
60 | } else {
61 | return _instance;
62 | }
63 | }
64 | /**
65 | * Дополнительно в конструктор можно передать
66 | * какое то значение по умолчанию, которое
67 | * отдавалось бы в случае обращения по
68 | * несуществующему ключу. В нашем же варианте
69 | * будет возвращаться undefined
70 | * */
71 |
72 | /**
73 | * Вносит запись в реестр
74 | * @param {string} key Ключ записи реестра
75 | * @param {*} value Значение записи реестра
76 | */
77 | Registry.prototype.set = function (key, value) {
78 | _storage[key] = value;
79 | };
80 |
81 | /**
82 | * Возвращает запись из реестра по ключу
83 | * @param {string} key Ключ записи реестра
84 | * @returns {*} Значение записи реестра
85 | */
86 | Registry.prototype.get = function (key) {
87 | return _storage[key];
88 | };
89 |
90 | /**
91 | * Проверяет существование записи в реестре
92 | * @param {string} key Ключ записи в реестре
93 | * @returns {boolean} Истина/ложь
94 | */
95 | Registry.prototype.has = function (key) {
96 | return _storage[key] !== undefined;
97 | };
98 |
99 | /**
100 | * В качестве дополнительного функционала можно добавить
101 | * метод reset() для очистки данных репозитория и
102 | * метод remove() для удаления какого конкретного значения
103 | * */
104 |
105 | return Registry;
106 | })();
--------------------------------------------------------------------------------
/patterns/structural/proxy/spec/proxy.spec.js:
--------------------------------------------------------------------------------
1 | describe('Заместитель / Proxy или Суррогат / Surrogate', function () {
2 |
3 | var proxy, calc;
4 |
5 | before(function () {
6 | calc = new Calc();
7 | proxy = new CalcProxy();
8 | });
9 |
10 | describe('Calc', function () {
11 |
12 | it('Инстанс', function () {
13 | expect(Calc).to.be.a('function');
14 | expect(calc).to.be.an.instanceOf(Calc);
15 | });
16 |
17 | describe('Методы', function () {
18 |
19 | it('add', function () {
20 | expect(calc.add).to.be.a('function');
21 | expect(calc.add(2, 2)).to.equal(4);
22 | });
23 |
24 | it('subtract', function () {
25 | expect(calc.subtract).to.be.a('function');
26 | expect(calc.subtract(8, 2)).to.equal(6);
27 | });
28 |
29 | it('multiply', function () {
30 | expect(calc.multiply).to.be.a('function');
31 | expect(calc.multiply(8, 2)).to.equal(16);
32 | });
33 |
34 |
35 | it('divide', function () {
36 | expect(calc.divide).to.be.a('function');
37 | expect(calc.divide(4, 2)).to.equal(2);
38 | });
39 |
40 |
41 | it('power', function () {
42 | expect(calc.power).to.be.a('function');
43 | expect(calc.power(8, 2)).to.equal(64);
44 | });
45 |
46 | it('root', function () {
47 | expect(calc.root).to.be.a('function');
48 | expect(calc.root(16, 2)).to.equal(4);
49 | });
50 | });
51 | });
52 |
53 | describe('CalcProxy', function () {
54 |
55 | it('Инстанс', function () {
56 | expect(CalcProxy).to.be.a('function');
57 | expect(proxy).to.be.an.instanceOf(CalcProxy);
58 | });
59 |
60 | describe('Методы', function () {
61 |
62 | it('add', function () {
63 | expect(proxy.add).to.be.a('function');
64 | expect(proxy.add('2x2', 2)).to.equal(4);
65 | });
66 |
67 | it('subtract', function () {
68 | expect(proxy.subtract).to.be.a('function');
69 | expect(proxy.subtract(8, 2)).to.equal(6);
70 | });
71 |
72 | it('multiply', function () {
73 | expect(proxy.multiply).to.be.a('function');
74 | expect(proxy.multiply('08', 2)).to.equal(16);
75 | });
76 |
77 |
78 | it('divide', function () {
79 | expect(proxy.divide).to.be.a('function');
80 | expect(proxy.divide(4, 2)).to.equal(2);
81 | });
82 |
83 |
84 | it('power', function () {
85 | expect(proxy.power).to.be.a('function');
86 | expect(proxy.power(8, '2 ')).to.equal(64);
87 | });
88 |
89 | it('root', function () {
90 | expect(proxy.root).to.be.a('function');
91 | expect(proxy.root('16', 2)).to.equal(4);
92 | });
93 | });
94 | });
95 | });
--------------------------------------------------------------------------------
/patterns/creational/singleton/spec/singleton.spec.js:
--------------------------------------------------------------------------------
1 | describe('Синглтон / Singleton', function () {
2 |
3 | var firstIns, secondIns;
4 |
5 | describe('Экземпляр хранится в замыкании, инстанс создается статическим методом', function () {
6 | before(function () {
7 | firstIns = ClosureStaticSingleton.getInstance();
8 | secondIns = ClosureStaticSingleton.getInstance();
9 |
10 | firstIns.toString = function() {
11 | return '[object ClosureStaticSingleton]';
12 | };
13 | });
14 |
15 | it('Метод, возвращающий инстанс синглтона должен существовать', function () {
16 | expect(ClosureStaticSingleton.getInstance).to.exist;
17 | });
18 |
19 | it('Обе переменные ссылаются на один и тот же объект', function () {
20 | expect(firstIns).to.equal(secondIns);
21 | });
22 |
23 | it('Метода toString должен существовать', function () {
24 | expect(secondIns.toString).to.exist;
25 | });
26 |
27 | it('Метод toString должен вернуть [object ClosureStaticSingleton]', function () {
28 | expect(secondIns.toString()).to.equal('[object ClosureStaticSingleton]');
29 | });
30 | });
31 |
32 | describe('Экземпляр хранится в замыкании, инстанс создается конструктором', function () {
33 | before(function () {
34 | firstIns = new ClosureConstructorSingleton();
35 | secondIns = new ClosureConstructorSingleton();
36 | });
37 |
38 | it('Метод, возвращающий инстанс синглтона должен существовать', function () {
39 | expect(ClosureConstructorSingleton).to.be.a('function');
40 | });
41 |
42 | it('Обе переменные ссылаются на один и тот же объект', function () {
43 | expect(firstIns === secondIns).to.equal(true);
44 | });
45 |
46 | it('Метода toString должен существовать', function () {
47 | expect(secondIns.toString).to.exist;
48 | });
49 |
50 | it('Метод toString должен вернуть [object ClosureConstructorSingleton]', function () {
51 | expect(secondIns.toString()).to.equal('[object ClosureConstructorSingleton]');
52 | });
53 | });
54 |
55 | describe('Экземпляр хранится в статическом свойстве, инстанс создается конструктором', function () {
56 | before(function () {
57 | firstIns = new StaticConstructorSingleton();
58 | secondIns = new StaticConstructorSingleton();
59 | });
60 |
61 | it('Метод, возвращающий инстанс синглтона должен существовать', function () {
62 | expect(StaticConstructorSingleton).to.be.a('function');
63 | });
64 |
65 | it('Обе переменные ссылаются на один и тот же объект', function () {
66 | expect(firstIns === secondIns).to.equal(true);
67 | });
68 |
69 | it('Метода toString должен существовать', function () {
70 | expect(secondIns.toString).to.exist;
71 | });
72 |
73 | it('Метод toString должен вернуть [object StaticConstructorSingleton]', function () {
74 | expect(secondIns.toString()).to.equal('[object StaticConstructorSingleton]');
75 | });
76 | });
77 | });
--------------------------------------------------------------------------------
/patterns/behavioral/memento/memento.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Originator
3 | * */
4 | var Person = (function () {
5 |
6 | /**
7 | * Создает экземпляр человека
8 | * @param {string} name Имя человека
9 | * @param {string} address Адрес местоположения человека
10 | * @constructor
11 | */
12 | function Person(name, address) {
13 | this.name = name;
14 | this.address = address;
15 | }
16 |
17 | /**
18 | * Меняет местоположение человека
19 | * @param {string} newAddress Новое местоположение человека
20 | */
21 | Person.prototype.move = function(newAddress) {
22 | this.address = newAddress;
23 | };
24 |
25 | /**
26 | * Возвращает текущее местопложение человека
27 | * @returns {string}
28 | */
29 | Person.prototype.whereami = function () {
30 | return this.address;
31 | };
32 |
33 | /**
34 | * Сохраняет текущее состояние человека в хранитель
35 | * @returns {Memento}
36 | */
37 | Person.prototype.save = function () {
38 | return new Memento(this);
39 | };
40 |
41 | /**
42 | * Откатывает состояние человека до состояния, которое хранится в memento
43 | * @param memento
44 | */
45 | Person.prototype.restore = function (memento) {
46 | if (memento === undefined) {
47 | throw new Error('Saved state is not found!');
48 | }
49 | var state = JSON.parse(memento.getState());
50 |
51 | this.name = state.name;
52 | this.address = state.address;
53 | };
54 |
55 | return Person;
56 | })();
57 |
58 | /**
59 | * Memento
60 | * */
61 | var Memento = (function () {
62 |
63 | /**
64 | * Создает экземпляр мементо
65 | * @param {*} state Состояние
66 | * @constructor
67 | */
68 | function Memento(state) {
69 | this.jsonState = JSON.stringify(state);
70 | }
71 |
72 | /**
73 | * Возвращает состояние
74 | * @returns {*}
75 | */
76 | Memento.prototype.getState = function () {
77 | return this.jsonState;
78 | };
79 |
80 | return Memento;
81 | })();
82 |
83 | /**
84 | * Caretaker /
85 | * */
86 | var Caretaker = (function (){
87 |
88 | /**
89 | * Создает экземпляр класса
90 | * @constructor
91 | */
92 | function Caretaker() {
93 | this.mementos = [];
94 | }
95 |
96 | /**
97 | * Сохраняет переданное в него состояние
98 | * @param {string} memento Состояние
99 | */
100 | Caretaker.prototype.set = function(memento) {
101 | this.mementos.push(memento);
102 | };
103 |
104 | /**
105 | * Возвращает сохраненное состояние по его индексу
106 | * @param {number} index Индекс сохраненного состояния
107 | * @returns {*}
108 | */
109 | Caretaker.prototype.get = function(index) {
110 | var ind = parseInt(index, 10);
111 | return this.mementos[isNaN(ind) ? this.mementos.length - 1 : ind];
112 | };
113 |
114 | /**
115 | * Очищает список сохраненных состояний
116 | */
117 | Caretaker.prototype.clean = function () {
118 | this.mementos = [];
119 | };
120 |
121 | return Caretaker;
122 | })();
--------------------------------------------------------------------------------
/patterns/behavioral/state/state.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Калькулятор - Состояние
3 | */
4 | var Calculator = (function () {
5 |
6 | /**
7 | * Создает экземпляр
8 | * @param {object} state Состояние
9 | * @constructor
10 | */
11 | function Calculator(state) {
12 | /**
13 | * Хранит в себе текущий объект-состояние, методы которого будут вызываться
14 | * @type {Object}
15 | * @private
16 | */
17 | this._state = state;
18 | }
19 |
20 | /**
21 | * Устанавливает состояние
22 | * @param {object} state Состояние
23 | */
24 | Calculator.prototype.set = function (state) {
25 | this._state = state;
26 | };
27 |
28 | /**
29 | * Вызывает метод execute() у текущего состояния
30 | * @returns {*|number}
31 | */
32 | Calculator.prototype.execute = function () {
33 | return this._state.execute.apply(this, arguments);
34 | };
35 |
36 | return Calculator;
37 | })();
38 |
39 | /**
40 | * Класс, который выполняет сложение
41 | * */
42 | var Add = (function () {
43 |
44 | /**
45 | * Создает экземпляр
46 | * @constructor
47 | */
48 | function Add() {
49 | }
50 |
51 | /**
52 | * Складывает два операнда
53 | * @param {number} x Первый операнд
54 | * @param {number} y Второй операнд
55 | * @returns {number} Сумма
56 | */
57 | Add.prototype.execute = function (x, y) {
58 | return x + y;
59 | };
60 |
61 | return Add;
62 | })();
63 |
64 | /**
65 | * Класс, который выполняет вычитание
66 | * */
67 | var Subtract = (function () {
68 |
69 | /**
70 | * Создает экземпляр
71 | * @constructor
72 | */
73 | function Subtract() {
74 | }
75 |
76 | /**
77 | * Вычитает из одного операнда другой
78 | * @param {number} x Уменьшаемое
79 | * @param {number} y Вычитаемое
80 | * @returns {number} Разница
81 | */
82 | Subtract.prototype.execute = function (x, y) {
83 | return x - y;
84 | };
85 |
86 | return Subtract;
87 | })();
88 |
89 | /**
90 | * Класс, который выполняет умножение
91 | * */
92 | var Multiply = (function () {
93 |
94 | /**
95 | * Создает экземпляр
96 | * @constructor
97 | */
98 | function Multiply() {
99 | }
100 |
101 | /**
102 | * Перемножает два операнда
103 | * @param {number} x Первый операнд
104 | * @param {number} y Второй операнд
105 | * @returns {number} Произведение
106 | */
107 | Multiply.prototype.execute = function (x, y) {
108 | return x * y;
109 | };
110 |
111 | return Multiply;
112 | })();
113 |
114 | /**
115 | * Класс, который выполняет деление
116 | * */
117 | var Divide = (function () {
118 |
119 | /**
120 | * Создает экземпляр
121 | * @constructor
122 | */
123 | function Divide() {
124 | }
125 |
126 | /**
127 | * Делит один операнд на другой
128 | * @param {number} x Делимое
129 | * @param {number} y Делитель
130 | * @returns {number} Результат
131 | */
132 | Divide.prototype.execute = function (x, y) {
133 | return x / y;
134 | };
135 |
136 | return Divide;
137 | })();
--------------------------------------------------------------------------------
/patterns/structural/adapter/spec/adapter.spec.js:
--------------------------------------------------------------------------------
1 | describe('Адаптер / Adapter', function () {
2 |
3 | var english, italian, adapter;
4 |
5 | before(function () {
6 | english = new Englishman('John', 'Smith');
7 | italian = new Italian('Silvio', 'Berlusconi');
8 | adapter = new AdaptToEnglish(italian);
9 | });
10 |
11 | describe('Englishman', function () {
12 |
13 | it('Инстанс', function () {
14 | expect(Englishman).to.be.a('function');
15 | expect(english).to.be.an.instanceOf(Englishman);
16 | });
17 |
18 | describe('Методы', function () {
19 |
20 | it('getFirstname()', function () {
21 | expect(english.getFirstname).to.be.a('function');
22 | expect(english.getFirstname()).to.equal('John');
23 | });
24 |
25 | it('getLastname()', function () {
26 | expect(english.getLastname).to.be.a('function');
27 | expect(english.getLastname()).to.equal('Smith');
28 | });
29 |
30 | it('introduce()', function () {
31 | expect(english.introduce).to.be.a('function');
32 | expect(english.introduce()).to.equal('My name is John Smith');
33 | });
34 |
35 | it('greet()', function () {
36 | expect(english.greet).to.be.a('function');
37 | expect(english.greet()).to.equal('Hello! How are you?');
38 | });
39 | });
40 | });
41 |
42 | describe('Italian', function () {
43 |
44 | it('Инстанс', function () {
45 | expect(Italian).to.be.a('function');
46 | expect(italian).to.be.an.instanceOf(Italian);
47 | });
48 |
49 | describe('Методы', function () {
50 |
51 | it('getFirstname()', function () {
52 | expect(italian.getFirstname).to.be.a('function');
53 | expect(italian.getFirstname()).to.equal('Silvio');
54 | });
55 |
56 | it('getLastname()', function () {
57 | expect(italian.getLastname).to.be.a('function');
58 | expect(italian.getLastname()).to.equal('Berlusconi');
59 | });
60 |
61 | it('introdurre()', function () {
62 | expect(italian.introdurre).to.be.a('function');
63 | expect(italian.introdurre()).to.equal('Il mio nome è Silvio Berlusconi');
64 | });
65 |
66 | it('greet()', function () {
67 | expect(italian.ciao).to.be.a('function');
68 | expect(italian.ciao()).to.equal('Ciao! Come stai?');
69 | });
70 | });
71 | });
72 |
73 | describe('AdaptToEnglish', function () {
74 |
75 | it('Инстанс', function () {
76 | expect(AdaptToEnglish).to.be.a('function');
77 | expect(adapter).to.be.an.instanceOf(AdaptToEnglish);
78 | });
79 |
80 | describe('Методы', function () {
81 |
82 | it('introduce()', function () {
83 | expect(adapter.introduce).to.be.a('function');
84 | expect(adapter.introduce()).to.equal('My name is Silvio Berlusconi');
85 | });
86 |
87 | it('greet()', function () {
88 | expect(adapter.greet).to.be.a('function');
89 | expect(adapter.greet()).to.equal('Hello! How are you?');
90 | });
91 | });
92 | });
93 | });
--------------------------------------------------------------------------------
/patterns/enterprise/reqres/spec/reqres.spec.js:
--------------------------------------------------------------------------------
1 | describe('Запрос/Ответ / Request/Response', function () {
2 |
3 | var reqres, server, client;
4 |
5 | before(function () {
6 | reqres = new RequestResponse();
7 | server = new Server(reqres);
8 | client = new Client(reqres);
9 | });
10 |
11 | describe('Request/Response', function () {
12 |
13 | it('RequestResponse является конструктором', function () {
14 | expect(RequestResponse).to.be.a('function');
15 | expect(reqres).to.be.an.instanceOf(RequestResponse);
16 | });
17 |
18 | it('RequestResponse обладает методом setResponse()', function() {
19 | expect(reqres.setResponse).to.be.a('function');
20 | });
21 |
22 | it('RequestResponse обладает методом removeResponse()', function() {
23 | expect(reqres.removeResponse).to.be.a('function');
24 | });
25 |
26 | it('RequestResponse обладает методом request()', function() {
27 | expect(reqres.request).to.be.a('function');
28 | });
29 |
30 | it('RequestResponse обладает методом clean()', function() {
31 | expect(reqres.clean).to.be.a('function');
32 | });
33 | });
34 |
35 | describe('Client и Server', function () {
36 |
37 | it('Client является конструктором', function () {
38 | expect(Client).to.be.a('function');
39 | expect(client).to.be.an.instanceOf(Client);
40 | });
41 |
42 | it('Client обладает методом add()', function() {
43 | expect(client.add).to.be.a('function');
44 | });
45 |
46 | it('Client обладает методом subtract()', function() {
47 | expect(client.subtract).to.be.a('function');
48 | });
49 |
50 | it('Client обладает методом multiply()', function() {
51 | expect(client.multiply).to.be.a('function');
52 | });
53 |
54 | it('Client обладает методом divide()', function() {
55 | expect(client.divide).to.be.a('function');
56 | });
57 |
58 | it('Server является конструктором', function () {
59 | expect(Server).to.be.a('function');
60 | expect(server).to.be.an.instanceOf(Server);
61 | });
62 | });
63 |
64 | describe('Поведение', function () {
65 |
66 | it("Через client: 4 + 5 = 9", function () {
67 | expect(client.add(4, 5)).to.equal(9);
68 | });
69 |
70 | it("Через reqres: 4 + 5 = 9", function () {
71 | expect(reqres.request('add', { x: 4, y: 5})).to.equal(9);
72 | });
73 |
74 | it("Через client: 21 - 3 = 18", function () {
75 | expect(client.subtract(21, 3)).to.equal(18);
76 | });
77 |
78 | it("Через reqres: 21 - 3 = 18", function () {
79 | expect(reqres.request('subtract', { x: 21, y: 3})).to.equal(18);
80 | });
81 |
82 | it("Через client: 16 * 15 = 240", function () {
83 | expect(client.multiply(16, 15)).to.equal(240);
84 | });
85 |
86 | it("Через reqres: 16 * 15 = 240", function () {
87 | expect(reqres.request('multiply', { x: 16, y: 15})).to.equal(240);
88 | });
89 |
90 | it("Через client: 22 / 2 = 11", function () {
91 | expect(client.divide(22, 2)).to.equal(11);
92 | });
93 |
94 | it("Через reqres: 22 / 2 = 11", function () {
95 | expect(reqres.request('divide', { x: 22, y: 2})).to.equal(11);
96 | });
97 | });
98 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Javascript Design Patterns (Черновик)
2 |
3 | ### Реализация на javascript шаблонов проектирования
4 |
5 | 1. Порождающие шаблоны / Creational patterns
6 | + Абстрактная фабрика / Abstract factory
7 | + [Пространство имен / Namespace] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/creational/namespace)
8 | + [Прототип / Prototype] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/creational/prototype)
9 | + [Синглтон / Одиночка / Singleton] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/creational/singleton)
10 | + Строитель / Builder
11 | + Фабричный метод / Factory method
12 | 2. Структурные шаблоны / Structural patterns
13 | + [Адаптер / Adapter] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/adapter)
14 | + [Декоратор / Decorator или Оболочка / Wrapper] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/decorator)
15 | + [Заместитель / Proxy или Суррогат / Surrogate] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/proxy)
16 | + [Компоновщик / Composite] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/composite)
17 | + [Модуль / Module] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/module)
18 | + Мост / Bridge
19 | + [Примесь / Mixin] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/mixin)
20 | + Приспособленец / Flyweight
21 | + [Фасад / Facade] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/facade)
22 | 3. Поведенческие шаблоны / Behavioral patterns
23 | + Интерпретатор / Interpreter
24 | + [Итератор / Iterator или Курсор / Cursor] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/iterator)
25 | + Команда / Command, Действие / Action или Транзакция / Transaction
26 | + [Наблюдатель / Observer / Publisher-Subscriber] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/observer)
27 | + Посетитель / Visitor
28 | + [Посредник / Mediator] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/mediator)
29 | + [Состояние / State] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/state)
30 | + [Стратегия / Strategy] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/strategy)
31 | + [Хранитель / Memento] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/memento)
32 | + [Цепочка обязаностей / Chain of Responsibility] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/chain-of-responsibility)
33 | + Шаблонный метод / Template Method
34 | 4. Шаблоны корпоративных приложений / Enterprise patterns
35 | + [Реестр / Registry] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/enterprise/registry)
36 | + Репозиторий / Repository
37 | + [Запрос/Ответ / Request/Response] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/enterprise/reqres)
38 | 5. Прочие - отсортировать
39 | + Единая точка входа / Front Controller
40 | + Sandbox / Песочница
41 | + Объектный пул / Object pool
42 | + Отложенная инициализация / Lazy initialization
--------------------------------------------------------------------------------
/patterns/structural/facade/spec/facade.spec.js:
--------------------------------------------------------------------------------
1 | describe('Фасад / Facade', function () {
2 |
3 | describe("Прверка инстансов", function () {
4 |
5 | var market, cash, cart, warehouse;
6 |
7 | before(function () {
8 | market = new Supermarket(['milk', 'bread', 'meat'], 100);
9 | cash = new Cash(100);
10 | cart = new Cart();
11 | warehouse = new Warehouse({
12 | milk: { count: 200, price: 6 },
13 | bread: { count: 150, price: 5 },
14 | meat: { count: 120, price: 20 }
15 | });
16 | });
17 |
18 | it("Supermarket", function () {
19 | expect(market).to.be.an.instanceOf(Supermarket);
20 | });
21 |
22 | it("Cash", function () {
23 | expect(cash).to.be.an.instanceOf(Cash);
24 | });
25 |
26 | it("Cart", function () {
27 | expect(cart).to.be.an.instanceOf(Cart);
28 | });
29 |
30 | it("Warehouse", function () {
31 | expect(warehouse).to.be.an.instanceOf(Warehouse);
32 | });
33 | });
34 |
35 | describe('Cash', function () {
36 |
37 | var cash;
38 |
39 | before(function () {
40 | cash = new Cash(100);
41 | });
42 |
43 | it('Cash.pay', function() {
44 | expect(cash.pay(200)).to.be.false;
45 | expect(cash.pay(90)).to.be.true;
46 | });
47 | });
48 |
49 | describe('Cart', function () {
50 |
51 | var cart;
52 |
53 | before(function () {
54 | cart = new Cart();
55 | });
56 |
57 | it('Cart.add', function () {
58 | cart
59 | .add(100)
60 | .add(100)
61 | .add(100)
62 | .add(100);
63 |
64 | expect(cart.getCost()).to.equal(400);
65 | });
66 |
67 | it('Cart.clean', function () {
68 | cart.clean();
69 |
70 | expect(cart.getCost()).to.equal(0);
71 | });
72 | });
73 |
74 | describe('Warehouse', function () {
75 |
76 | var count, warehouse;
77 |
78 | beforeEach(function () {
79 | warehouse = new Warehouse({
80 | milk: { count: 200, price: 6 },
81 | bread: { count: 150, price: 5 },
82 | meat: { count: 120, price: 20 }
83 | });
84 |
85 | count = warehouse.getItemCount('milk');
86 | });
87 |
88 | it('Warehouse.getItem', function () {
89 | expect(warehouse.getItem('milk')).to.equal(6);
90 | expect(count - warehouse.getItemCount('milk')).to.equal(1);
91 | });
92 |
93 | it('Warehouse.getItemCount', function () {
94 | expect(warehouse.getItemCount('milk')).to.equal(200);
95 | expect(warehouse.getItemCount('bread')).to.equal(150);
96 | expect(warehouse.getItemCount('meat')).to.equal(120);
97 | });
98 | });
99 |
100 | describe('Supermarket', function () {
101 |
102 | var supermarket;
103 |
104 | before(function () {
105 | supermarket = new Supermarket(['milk', 'bread', 'meat'], 100, {
106 | milk: { count: 200, price: 6 },
107 | bread: { count: 150, price: 5 },
108 | meat: { count: 120, price: 20 }
109 | });
110 | });
111 |
112 | it('Supermarket.goShopping', function () {
113 | expect(supermarket.goShopping()).to.be.true;
114 | });
115 |
116 | });
117 |
118 | });
--------------------------------------------------------------------------------
/patterns/structural/module/module.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Object Literal Module Pattern
3 | * Модуль в виде литерала объекта
4 | * */
5 | var calculatorLiteral = {
6 | /**
7 | * Локальной области видимости нет,
8 | * потому нет возможности создавать приватные свойтсва
9 | * */
10 | add: function (x, y) {
11 | return x + y;
12 | },
13 |
14 | subtract: function (x, y) {
15 | return x - y;
16 | },
17 |
18 | multiply: function (x, y) {
19 | return x * y;
20 | },
21 |
22 | divide: function (x, y) {
23 | return x / y;
24 | },
25 |
26 | PI: 3.1415926
27 | };
28 |
29 | /**
30 | * Модуль в виде объекта, который возвращает функция
31 | * */
32 | var calculatorFunc = (function () {
33 | /**
34 | * Приватные свойства
35 | * */
36 | function abs(value) {
37 | return value > 0 ? value : -value;
38 | }
39 |
40 | /**
41 | * Публичные свойства
42 | * */
43 | return {
44 | add: function (x, y) {
45 | return x + y;
46 | },
47 |
48 | subtract: function (x, y) {
49 | /**
50 | * Публичный метод использует приватный метод, при этом приватный метод недоступен
51 | * */
52 | return abs(x - y);
53 | },
54 |
55 | multiply: function (x, y) {
56 | return x * y;
57 | },
58 |
59 | divide: function (x, y) {
60 | return x / y;
61 | },
62 |
63 | PI: 3.1415926
64 | }
65 |
66 | })();
67 |
68 | /**
69 | * Revealing Module Pattern
70 | * Улучшенный вариант предыдущей реализации.
71 | * Все свойства приватны, в конце определения модуля
72 | * определяется каким свойствам открыть доступ
73 | * */
74 | var calculatorReleaving = (function () {
75 |
76 | var PI = 3.1415926;
77 |
78 | /**
79 | * Приватные методы
80 | * */
81 | function abs(value) {
82 | return value > 0 ? value : -value;
83 | }
84 |
85 | function add(x, y) {
86 | return x + y;
87 | }
88 |
89 | function subtract(x, y) {
90 | return abs(x - y);
91 | }
92 |
93 | function multiply(x, y) {
94 | return x * y;
95 | }
96 |
97 | function divide(x, y) {
98 | return x / y;
99 | }
100 |
101 | function ringLength(radius) {
102 | return 2 * PI * radius;
103 | }
104 |
105 | /**
106 | * Открытие доступа
107 | * */
108 | return {
109 | add: add,
110 | subtract: subtract,
111 | multiply: multiply,
112 | divide: divide,
113 | ringLength: ringLength
114 | };
115 | })();
116 |
117 | /**
118 | * Модуль, возвращающий конструктор
119 | * */
120 | var calculatorCtor = (function () {
121 |
122 | function Calculator() {
123 | /**
124 | * Инициализация
125 | * */
126 | }
127 |
128 | /**
129 | * Приватные свойства
130 | * */
131 | var PI = 3.1415926;
132 |
133 | function abs(value) {
134 | return value > 0 ? value : -value;
135 | }
136 |
137 | /**
138 | * Публичные свойства вносятся в прототип
139 | * */
140 | Calculator.prototype.add = function (x, y) {
141 | return x + y;
142 | };
143 |
144 | Calculator.prototype.subtract = function (x, y) {
145 | /**
146 | * Публичный метод использует приватный метод, при этом приватный метод недоступен
147 | * */
148 | return abs(x - y);
149 | };
150 |
151 | Calculator.prototype.multiply = function (x, y) {
152 | return x * y;
153 | };
154 |
155 | Calculator.prototype.divide = function (x, y) {
156 | return x / y;
157 | };
158 |
159 | /**
160 | * Возвращаем конструктор
161 | * */
162 | return Calculator;
163 | })();
--------------------------------------------------------------------------------
/patterns/structural/module/spec/module.spec.js:
--------------------------------------------------------------------------------
1 | describe('Модуль / Module', function () {
2 |
3 | describe('Object Literal', function () {
4 |
5 | it('Инстанс', function () {
6 | expect(calculatorLiteral).to.be.an('object');
7 | expect(calculatorLiteral).not.to.be.empty;
8 | });
9 |
10 | it('Методы', function () {
11 | expect(calculatorLiteral.add).to.be.a('function');
12 | expect(calculatorLiteral.add(2, 2)).to.equal(4);
13 | expect(calculatorLiteral.divide).to.be.a('function');
14 | expect(calculatorLiteral.divide(4, 2)).to.equal(2);
15 | expect(calculatorLiteral.multiply).to.be.a('function');
16 | expect(calculatorLiteral.multiply(8, 2)).to.equal(16);
17 | expect(calculatorLiteral.subtract).to.be.a('function');
18 | expect(calculatorLiteral.subtract(8, 2)).to.equal(6);
19 | });
20 | });
21 |
22 | describe('Function', function () {
23 |
24 | it('Инстанс', function () {
25 | expect(calculatorFunc).to.be.an('object');
26 | expect(calculatorFunc).not.to.be.empty;
27 | });
28 |
29 | it('Публичные методы', function () {
30 | expect(calculatorFunc.add).to.be.a('function');
31 | expect(calculatorFunc.add(2, 2)).to.equal(4);
32 | expect(calculatorFunc.divide).to.be.a('function');
33 | expect(calculatorFunc.divide(4, 2)).to.equal(2);
34 | expect(calculatorFunc.multiply).to.be.a('function');
35 | expect(calculatorFunc.multiply(8, 2)).to.equal(16);
36 | expect(calculatorFunc.subtract).to.be.a('function');
37 | expect(calculatorFunc.subtract(8, 2)).to.equal(6);
38 | });
39 |
40 | it('Приватный метод', function () {
41 | expect(calculatorFunc.abs).to.be.undefined;
42 | });
43 | });
44 |
45 | describe('Revealing Module', function () {
46 |
47 | it('Инстанс', function () {
48 | expect(calculatorReleaving).to.be.an('object');
49 | expect(calculatorReleaving).not.to.be.empty;
50 | });
51 |
52 | it('Публичные методы', function () {
53 | expect(calculatorReleaving.add).to.be.a('function');
54 | expect(calculatorReleaving.add(2, 2)).to.equal(4);
55 | expect(calculatorReleaving.divide).to.be.a('function');
56 | expect(calculatorReleaving.divide(4, 2)).to.equal(2);
57 | expect(calculatorReleaving.multiply).to.be.a('function');
58 | expect(calculatorReleaving.multiply(8, 2)).to.equal(16);
59 | expect(calculatorReleaving.subtract).to.be.a('function');
60 | expect(calculatorReleaving.subtract(8, 2)).to.equal(6);
61 | expect(calculatorReleaving.ringLength).to.be.a('function');
62 | expect(calculatorReleaving.ringLength(2)).to.equal(12.5663704);
63 | });
64 |
65 | it('Приватный метод', function () {
66 | expect(calculatorReleaving.abs).to.be.undefined;
67 | });
68 | });
69 |
70 | describe('Constructor', function () {
71 |
72 | var calc;
73 |
74 | before(function () {
75 | calc = new calculatorCtor();
76 | });
77 |
78 | it('Инстанс', function () {
79 | expect(calculatorCtor).to.be.a('function');
80 | expect(calc).to.be.an.instanceOf(calculatorCtor);
81 | });
82 |
83 | it('Публичные методы', function () {
84 | expect(calc.add).to.be.a('function');
85 | expect(calc.add(2, 2)).to.equal(4);
86 | expect(calc.divide).to.be.a('function');
87 | expect(calc.divide(4, 2)).to.equal(2);
88 | expect(calc.multiply).to.be.a('function');
89 | expect(calc.multiply(8, 2)).to.equal(16);
90 | expect(calc.subtract).to.be.a('function');
91 | expect(calc.subtract(8, 2)).to.equal(6);
92 | });
93 |
94 | it('Приватный метод', function () {
95 | expect(calc.abs).to.be.undefined;
96 | });
97 | });
98 | });
--------------------------------------------------------------------------------
/patterns/behavioral/memento/spec/memento.spec.js:
--------------------------------------------------------------------------------
1 | describe('Хранитель / Memento', function () {
2 |
3 | var person, caretaker, mem;
4 |
5 | describe('Классы', function (){
6 |
7 | before(function () {
8 | person = new Person('John', 'Moscow');
9 | caretaker = new Caretaker();
10 | mem = new Memento({hello: 'world'});
11 | });
12 |
13 | it('Person является классом', function () {
14 | expect(Person).to.be.a('function');
15 | expect(person).to.be.an.instanceOf(Person);
16 | });
17 |
18 | it('Экземпляр класса Person имеет метод move()', function() {
19 | expect(person.move).to.be.a('function');
20 | });
21 |
22 | it('Экземпляр класса Person имеет метод whereami()', function() {
23 | expect(person.whereami).to.be.a('function');
24 | expect(person.whereami()).to.equal('Moscow');
25 | });
26 |
27 | it('Экземпляр класса Person имеет метод save()', function() {
28 | expect(person.save).to.be.a('function');
29 | expect(person.save()).to.be.an.instanceOf(Memento);
30 | });
31 |
32 | it('Экземпляр класса Person имеет метод restore()', function() {
33 | var save = person.save();
34 | person.move('Tokyo');
35 | person.restore(save);
36 |
37 | expect(person.restore).to.be.a('function');
38 | expect(person.whereami()).to.equal('Moscow');
39 | });
40 |
41 | it('Memento является классом', function () {
42 | expect(Memento).to.be.a('function');
43 | expect(mem).to.be.an.instanceOf(Memento);
44 | });
45 |
46 | it('Экземпляр класса Memento имеет метод getState()', function() {
47 | expect(mem.getState).to.be.a('function');
48 | expect(mem.getState()).to.equal('{"hello":"world"}');
49 | });
50 |
51 | it('Caretaker является классом', function () {
52 | expect(Caretaker).to.be.a('function');
53 | expect(caretaker).to.be.an.instanceOf(Caretaker);
54 | });
55 |
56 | it('Экземпляр класса Memento имеет метод set()', function() {
57 | expect(caretaker.set).to.be.a('function');
58 | });
59 |
60 | it('Экземпляр класса Memento имеет метод get()', function() {
61 | expect(caretaker.get).to.be.a('function');
62 | });
63 |
64 | it('Экземпляр класса Memento имеет метод clean()', function() {
65 | expect(caretaker.clean).to.be.a('function');
66 | });
67 | });
68 |
69 | describe('Поведение', function () {
70 |
71 | beforeEach(function () {
72 | person = new Person('John', 'Moscow');
73 | caretaker = new Caretaker();
74 | });
75 |
76 | it('Сохраненное и восстановленное состояния должны быть идентичными', function () {
77 | var oldAddress = person.whereami();
78 |
79 | caretaker.set(person.save());
80 | person.restore(caretaker.get(0));
81 |
82 | var newAddress = person.whereami();
83 |
84 | expect(oldAddress === newAddress).to.be.true;
85 | });
86 |
87 | it('Множественное сохранение состояний', function () {
88 |
89 | caretaker.set(person.save());
90 |
91 | person.move('New York');
92 | caretaker.set(person.save());
93 |
94 | person.move('Tokyo');
95 | caretaker.set(person.save());
96 |
97 | person.move('London');
98 | caretaker.set(person.save());
99 |
100 | person.move('Paris');
101 | caretaker.set(person.save());
102 |
103 |
104 |
105 | person.restore(caretaker.get(0));
106 | expect(person.whereami()).to.equal('Moscow');
107 |
108 | person.restore(caretaker.get(1));
109 | expect(person.whereami()).to.equal('New York');
110 |
111 | person.restore(caretaker.get(2));
112 | expect(person.whereami()).to.equal('Tokyo');
113 |
114 | person.restore(caretaker.get(3));
115 | expect(person.whereami()).to.equal('London');
116 |
117 | person.restore(caretaker.get());
118 | expect(person.whereami()).to.equal('Paris');
119 |
120 | });
121 |
122 | });
123 |
124 | });
125 |
--------------------------------------------------------------------------------
/patterns/enterprise/reqres/reqres.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Класс, который отвечает за передачу запроса
3 | * */
4 | var RequestResponse = (function () {
5 |
6 | /**
7 | * Создает экземпляр
8 | * @constructor
9 | */
10 | function Reqres () {
11 | /**
12 | * Хранилище запросов
13 | * @private
14 | * */
15 | this._requests = {};
16 | }
17 |
18 | /**
19 | * Задает обработчик запроса
20 | * @param {string} name Имя запроса
21 | * @param {function} handler Обработчик запроса
22 | * @param {*} context Контекст запроса
23 | * @returns {RequestResponse}
24 | */
25 | Reqres.prototype.setResponse = function (name, handler, context) {
26 |
27 | if (!this._requests.hasOwnProperty(name)) {
28 | this._requests[name] = {
29 | fn: handler,
30 | context: context
31 | };
32 | } else {
33 | throw new Error('The response already exists!');
34 | }
35 |
36 | /**
37 | * На один запрос может быть только один ответ
38 | * */
39 | return this;
40 | };
41 |
42 | /**
43 | * Удаляет обработчик запроса
44 | * @param {string} name Имя запроса
45 | * @returns {RequestResponse}
46 | */
47 | Reqres.prototype.removeResponse = function (name) {
48 | if (this._requests.hasOwnProperty(name)) {
49 | this._requests[name] = undefined;
50 | }
51 |
52 | return this;
53 | };
54 |
55 | /**
56 | * Делает запрос.
57 | * @param {string} name Имя запроса
58 | * @param {*} data Данные, которые передаются в запрос
59 | * @returns {*} Результат выполнения обработчика запроса
60 | */
61 | Reqres.prototype.request = function (name, data) {
62 | if (this._requests.hasOwnProperty(name)) {
63 | return this._requests[name].fn.call(this._requests[name].context, data);
64 | } else {
65 | throw new Error('The request does not exist!');
66 | }
67 | };
68 |
69 | /**
70 | * Очищает хранилище запросов
71 | */
72 | Reqres.prototype.clean = function () {
73 | this._requests = {};
74 | };
75 |
76 | return Reqres;
77 | })();
78 |
79 | /**
80 | * Сервер
81 | * */
82 | var Server = (function () {
83 |
84 | /**
85 | * @param {RequestResponse} reqres Экзампляр хранителя запросов
86 | * @constructor
87 | *
88 | * В сервер инжектирован экзепмляр RequestResponse
89 | * Сервер не знает кто к нему обращается
90 | */
91 | function Server(reqres) {
92 | this._reqres = reqres;
93 |
94 | /**
95 | * Задаем обработчики запросов
96 | * */
97 | this._reqres
98 | .setResponse('add', function (data) {
99 | return data.x + data.y;
100 | })
101 | .setResponse('subtract', function (data) {
102 | return data.x - data.y;
103 | })
104 | .setResponse('multiply', function (data) {
105 | return data.x * data.y;
106 | })
107 | .setResponse('divide', function (data) {
108 | return data.x / data.y;
109 | });
110 | }
111 |
112 | return Server;
113 | })();
114 |
115 | /**
116 | * Клиент
117 | * */
118 | var Client = (function () {
119 |
120 | /**
121 | * @param {RequestResponse} reqres Экзампляр хранителя запросов
122 | * @constructor
123 | *
124 | * В клиент инжектирован экзепмляр RequestResponse
125 | * Клиент ничего не знает о сервере
126 | */
127 | function Client(reqres) {
128 | this._reqres = reqres;
129 | }
130 |
131 | /**
132 | * Возвращает результат запроса на сложение двух чисел
133 | * @param {number} x Первый операнд
134 | * @param {number} y Второй операнд
135 | * @returns {*} Результат запроса
136 | */
137 | Client.prototype.add = function(x, y) {
138 | return this._reqres.request('add', { x: x, y: y });
139 | };
140 |
141 | /**
142 | * Возвращает результат запроса на вычитание двух чисел
143 | * @param {number} x Уменьшаемое
144 | * @param {number} y Вычитаемое
145 | * @returns {*} Результат запроса
146 | */
147 | Client.prototype.subtract = function(x, y) {
148 | return this._reqres.request('subtract', { x: x, y: y });
149 | };
150 |
151 | /**
152 | * Возвращает результат запроса на умножение двух чисел
153 | * @param {number} x Первый операнд
154 | * @param {number} y Второй операнд
155 | * @returns {*} Результат запроса
156 | */
157 | Client.prototype.multiply = function(x, y) {
158 | return this._reqres.request('multiply', { x: x, y: y });
159 | };
160 |
161 | /**
162 | * Возвращает результат запроса на деление двух чисел
163 | * @param {number} x Делимое
164 | * @param {number} y Делитель
165 | * @returns {*} Результат запроса
166 | */
167 | Client.prototype.divide = function(x, y) {
168 | return this._reqres.request('divide', { x: x, y: y });
169 | };
170 |
171 | return Client;
172 | })();
--------------------------------------------------------------------------------
/assets/mocha.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | body {
4 | margin:0;
5 | }
6 |
7 | #mocha {
8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
9 | margin: 60px 50px;
10 | }
11 |
12 | #mocha ul,
13 | #mocha li {
14 | margin: 0;
15 | padding: 0;
16 | }
17 |
18 | #mocha ul {
19 | list-style: none;
20 | }
21 |
22 | #mocha h1,
23 | #mocha h2 {
24 | margin: 0;
25 | }
26 |
27 | #mocha h1 {
28 | margin-top: 15px;
29 | font-size: 1em;
30 | font-weight: 200;
31 | }
32 |
33 | #mocha h1 a {
34 | text-decoration: none;
35 | color: inherit;
36 | }
37 |
38 | #mocha h1 a:hover {
39 | text-decoration: underline;
40 | }
41 |
42 | #mocha .suite .suite h1 {
43 | margin-top: 0;
44 | font-size: .8em;
45 | }
46 |
47 | #mocha .hidden {
48 | display: none;
49 | }
50 |
51 | #mocha h2 {
52 | font-size: 12px;
53 | font-weight: normal;
54 | cursor: pointer;
55 | }
56 |
57 | #mocha .suite {
58 | margin-left: 15px;
59 | }
60 |
61 | #mocha .test {
62 | margin-left: 15px;
63 | overflow: hidden;
64 | }
65 |
66 | #mocha .test.pending:hover h2::after {
67 | content: '(pending)';
68 | font-family: arial, sans-serif;
69 | }
70 |
71 | #mocha .test.pass.medium .duration {
72 | background: #c09853;
73 | }
74 |
75 | #mocha .test.pass.slow .duration {
76 | background: #b94a48;
77 | }
78 |
79 | #mocha .test.pass::before {
80 | content: '✓';
81 | font-size: 12px;
82 | display: block;
83 | float: left;
84 | margin-right: 5px;
85 | color: #00d6b2;
86 | }
87 |
88 | #mocha .test.pass .duration {
89 | font-size: 9px;
90 | margin-left: 5px;
91 | padding: 2px 5px;
92 | color: #fff;
93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
96 | -webkit-border-radius: 5px;
97 | -moz-border-radius: 5px;
98 | -ms-border-radius: 5px;
99 | -o-border-radius: 5px;
100 | border-radius: 5px;
101 | }
102 |
103 | #mocha .test.pass.fast .duration {
104 | display: none;
105 | }
106 |
107 | #mocha .test.pending {
108 | color: #0b97c4;
109 | }
110 |
111 | #mocha .test.pending::before {
112 | content: '◦';
113 | color: #0b97c4;
114 | }
115 |
116 | #mocha .test.fail {
117 | color: #c00;
118 | }
119 |
120 | #mocha .test.fail pre {
121 | color: black;
122 | }
123 |
124 | #mocha .test.fail::before {
125 | content: '✖';
126 | font-size: 12px;
127 | display: block;
128 | float: left;
129 | margin-right: 5px;
130 | color: #c00;
131 | }
132 |
133 | #mocha .test pre.error {
134 | color: #c00;
135 | max-height: 300px;
136 | overflow: auto;
137 | }
138 |
139 | /**
140 | * (1): approximate for browsers not supporting calc
141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
142 | * ^^ seriously
143 | */
144 | #mocha .test pre {
145 | display: block;
146 | float: left;
147 | clear: left;
148 | font: 12px/1.5 monaco, monospace;
149 | margin: 5px;
150 | padding: 15px;
151 | border: 1px solid #eee;
152 | max-width: 85%; /*(1)*/
153 | max-width: calc(100% - 42px); /*(2)*/
154 | word-wrap: break-word;
155 | border-bottom-color: #ddd;
156 | -webkit-border-radius: 3px;
157 | -webkit-box-shadow: 0 1px 3px #eee;
158 | -moz-border-radius: 3px;
159 | -moz-box-shadow: 0 1px 3px #eee;
160 | border-radius: 3px;
161 | }
162 |
163 | #mocha .test h2 {
164 | position: relative;
165 | }
166 |
167 | #mocha .test a.replay {
168 | position: absolute;
169 | top: 3px;
170 | right: 0;
171 | text-decoration: none;
172 | vertical-align: middle;
173 | display: block;
174 | width: 15px;
175 | height: 15px;
176 | line-height: 15px;
177 | text-align: center;
178 | background: #eee;
179 | font-size: 15px;
180 | -moz-border-radius: 15px;
181 | border-radius: 15px;
182 | -webkit-transition: opacity 200ms;
183 | -moz-transition: opacity 200ms;
184 | transition: opacity 200ms;
185 | opacity: 0.3;
186 | color: #888;
187 | }
188 |
189 | #mocha .test:hover a.replay {
190 | opacity: 1;
191 | }
192 |
193 | #mocha-report.pass .test.fail {
194 | display: none;
195 | }
196 |
197 | #mocha-report.fail .test.pass {
198 | display: none;
199 | }
200 |
201 | #mocha-report.pending .test.pass,
202 | #mocha-report.pending .test.fail {
203 | display: none;
204 | }
205 | #mocha-report.pending .test.pass.pending {
206 | display: block;
207 | }
208 |
209 | #mocha-error {
210 | color: #c00;
211 | font-size: 1.5em;
212 | font-weight: 100;
213 | letter-spacing: 1px;
214 | }
215 |
216 | #mocha-stats {
217 | position: fixed;
218 | top: 15px;
219 | right: 10px;
220 | font-size: 12px;
221 | margin: 0;
222 | color: #888;
223 | z-index: 1;
224 | }
225 |
226 | #mocha-stats .progress {
227 | float: right;
228 | padding-top: 0;
229 | }
230 |
231 | #mocha-stats em {
232 | color: black;
233 | }
234 |
235 | #mocha-stats a {
236 | text-decoration: none;
237 | color: inherit;
238 | }
239 |
240 | #mocha-stats a:hover {
241 | border-bottom: 1px solid #eee;
242 | }
243 |
244 | #mocha-stats li {
245 | display: inline-block;
246 | margin: 0 5px;
247 | list-style: none;
248 | padding-top: 11px;
249 | }
250 |
251 | #mocha-stats canvas {
252 | width: 40px;
253 | height: 40px;
254 | }
255 |
256 | #mocha code .comment { color: #ddd; }
257 | #mocha code .init { color: #2f6fad; }
258 | #mocha code .string { color: #5890ad; }
259 | #mocha code .keyword { color: #8a6343; }
260 | #mocha code .number { color: #2f6fad; }
261 |
262 | @media screen and (max-device-width: 480px) {
263 | #mocha {
264 | margin: 60px 0px;
265 | }
266 |
267 | #mocha #stats {
268 | position: absolute;
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/patterns/behavioral/mediator/mediator.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Медиатор с каналами
3 | * */
4 | var Mediator = (function () {
5 | /**
6 | * Хранилище каналов.
7 | * Канал представляет собой объект с двумя свойствами:
8 | * handlers {array} - обработчики этого канала (fn {function} и context {object})
9 | * nested {object} - вложенные каналы
10 | * */
11 | var storage = {},
12 | sep;
13 |
14 | /**
15 | * @constructor
16 | * */
17 | function Mediator (separator) {
18 | /**
19 | * По умолчанию неймспейсы будут разделены двоеточием
20 | * */
21 | sep = separator || ':';
22 | }
23 |
24 | /**
25 | * Добавляет подписчик в медиатор
26 | * @this {Mediator}
27 | * @param {string} channel Канал, на который подписывается обработчик
28 | * @param {function} handler Обработчик
29 | * @param {object} context Контекст выполнения обработчика
30 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
31 | * */
32 | Mediator.prototype.on = function (channel, handler, context) {
33 | if (channel.length) {
34 | this.namespace(channel).handlers.push({
35 | fn: handler,
36 | context: context
37 | });
38 | }
39 |
40 | return this;
41 | };
42 |
43 | /**
44 | * Удаляет подписчики из медиатора
45 | * @this {Mediator}
46 | * @param {string} channel Канал, обработчики которого будут удалены
47 | * @param {boolean} withNested Логическое поле, которое позволяет отключить обработчики каналов-потомков
48 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
49 | */
50 | Mediator.prototype.off = function(channel, withNested) {
51 | if (this.has(channel)) {
52 | var ch = this.namespace(channel);
53 | ch.handlers = [];
54 |
55 | if (withNested) {
56 | ch.nested = {};
57 | }
58 | }
59 |
60 | return this;
61 | };
62 |
63 | /**
64 | * Вызывает обработчики для конкретного канала с передачей в них данных
65 | * @private
66 | * @this {Mediator}
67 | * @param {array} handlers Массив с обработчиками
68 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики
69 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
70 | */
71 | Mediator.prototype._runHandlers = function(handlers, data) {
72 | for (var length = handlers.length, i = 0; i < length; i++) {
73 | handlers[i].fn.call(handlers[i].context, data);
74 | }
75 |
76 | return this;
77 | };
78 |
79 | /**
80 | * Рекурсивно вызывает обработчики с передачей в них данных
81 | * @private
82 | * @this {Mediator}
83 | * @param {object} namespace Пространство имен
84 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики
85 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
86 | */
87 | Mediator.prototype._runHandlersRecursive = function(namespace, data) {
88 | this._runHandlers(namespace.handlers, data);
89 |
90 | for (var ns in namespace.nested) {
91 | if (namespace.nested.hasOwnProperty(ns)) {
92 | this._runHandlersRecursive(namespace.nested[ns], data);
93 | }
94 | }
95 |
96 | return this;
97 | }
98 |
99 | /**
100 | * Вызывает обработчики для конкретного канала с передачей в них данных
101 | * @this {Mediator}
102 | * @param {string} channel Имя канала
103 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики
104 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
105 | */
106 | Mediator.prototype.trigger = function(channel, data) {
107 | if (this.has(channel)) {
108 | this._runHandlers(this.namespace(channel).handlers, data);
109 | } else {
110 | throw new Error('The channel does not exist!');
111 | }
112 |
113 | return this;
114 | };
115 |
116 | /**
117 | * Вызывает обработчики для конкретного канала и вложенных каналов с передачей в них данных
118 | * @this {Mediator}
119 | * @param {string} channel Имя канала
120 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики
121 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
122 | */
123 | Mediator.prototype.broadcast = function (channel, data) {
124 | if (this.has(channel)) {
125 | this._runHandlersRecursive(this.namespace(channel), data);
126 | } else {
127 | throw new Error('The channel does not exist!');
128 | }
129 |
130 | return this;
131 | };
132 |
133 | /**
134 | * Удаление все обработчиков из медиатора
135 | * @this {Mediator}
136 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов
137 | */
138 | Mediator.prototype.clean = function (){
139 | storage = {};
140 | return this;
141 | };
142 |
143 | /**
144 | * Проверка существования определенного неймспейса
145 | * @this {Mediator}
146 | * @param {string} channel Канал, который будет проверяться на наличие
147 | * @returns {boolean} Истина/ложь
148 | * */
149 | Mediator.prototype.has = function (channel) {
150 | if (!channel.length) return false;
151 | var parts = channel.split(sep),
152 | length = parts.length,
153 | i = 0,
154 | current = storage;
155 |
156 | for (; i < length; i++) {
157 | if (current[parts[i]] !== undefined) {
158 | current = current[parts[i]].nested;
159 | } else {
160 | return false;
161 | }
162 | }
163 |
164 | return true;
165 | };
166 |
167 | /**
168 | * Функция для управления пространством имен обработчиков
169 | * @this {Mediator}
170 | * @param {string} channel Канал
171 | * @returns {object} current Хранилище обработчиков канала
172 | * */
173 | Mediator.prototype.namespace = function (channel) {
174 | var parts = channel.split(sep),
175 | length = parts.length,
176 | i = 0,
177 | current = storage;
178 |
179 | /**
180 | * Возвращает объект заданной структуры
181 | *
182 | * @returns {object}
183 | * */
184 | function getUnit() {
185 | return {
186 | nested: {},
187 | handlers: []
188 | }
189 | }
190 |
191 | for (; i < length; i++) {
192 | current[parts[i]] = current[parts[i]] || getUnit();
193 | current = (i !== length - 1 ? current[parts[i]].nested : current[parts[i]]);
194 | }
195 |
196 | return current;
197 | }
198 |
199 | /**
200 | * Возвращает содержимое объекта с обработчиками. Только для отладки.
201 | * @this {Mediator}
202 | * @returns {object} storage Хранилище обработчиков
203 | * */
204 | Mediator.prototype.getStorage = function() {
205 | return storage;
206 | };
207 |
208 | return Mediator;
209 | })();
210 |
--------------------------------------------------------------------------------
/patterns/behavioral/mediator/spec/mediator.spec.js:
--------------------------------------------------------------------------------
1 | describe('Посредник / Mediator', function () {
2 | var mediator;
3 |
4 | describe('Проверка объекта', function () {
5 |
6 | before(function () {
7 | mediator = new Mediator();
8 | });
9 |
10 | it('Инстанс', function () {
11 | expect(mediator).to.be.instanceOf(Mediator);
12 | });
13 |
14 | it('Методы', function() {
15 | expect(mediator.on).to.be.a('function');
16 | expect(mediator.off).to.be.a('function');
17 | expect(mediator.trigger).to.be.a('function');
18 | expect(mediator.broadcast).to.be.a('function');
19 | expect(mediator.has).to.be.a('function');
20 | expect(mediator.clean).to.be.a('function');
21 | expect(mediator.namespace).to.be.a('function');
22 | expect(mediator.getStorage).to.be.a('function');
23 | expect(mediator._runHandlers).to.be.a('function');
24 | expect(mediator._runHandlersRecursive).to.be.a('function');
25 | });
26 | });
27 |
28 | describe('Поведение', function () {
29 |
30 | var aFn, bFn, cFn, dFn, eFn;
31 |
32 | beforeEach(function () {
33 | mediator = new Mediator();
34 |
35 | aFn = sinon.spy();
36 | bFn = sinon.spy();
37 | cFn = sinon.spy();
38 | dFn = sinon.spy();
39 | eFn = sinon.spy();
40 | });
41 |
42 | it('Добавление обработчиков', function () {
43 | mediator
44 | .on('namespace', aFn)
45 | .on('namespace', bFn)
46 | .on('namespace', cFn)
47 | .on('namespace', dFn)
48 | .trigger('namespace');
49 |
50 |
51 | expect(aFn.called).to.be.true;
52 | expect(bFn.called).to.be.true;
53 | expect(cFn.called).to.be.true;
54 | expect(dFn.called).to.be.true;
55 | });
56 |
57 | it('Добавление обработчика с данными', function () {
58 |
59 | var o = { text: 'Hello' };
60 |
61 | mediator
62 | .on('namespace', aFn)
63 | .on('namespace', bFn)
64 | .on('namespace', cFn)
65 | .on('namespace', dFn)
66 | .trigger('namespace', o);
67 |
68 |
69 | expect(aFn.calledWith(o)).to.be.true;
70 | expect(bFn.calledWith(o)).to.be.true;
71 | expect(cFn.calledWith(o)).to.be.true;
72 | expect(dFn.calledWith(o)).to.be.true;
73 | });
74 |
75 | it('Добавление вложенных обработчиков', function () {
76 | mediator
77 | .on('a', aFn)
78 | .on('a:b', bFn)
79 | .on('a:b:c', cFn)
80 | .on('a:b:c:d', dFn)
81 | .trigger('a')
82 | .trigger('a:b:c');
83 |
84 |
85 | expect(aFn.called).to.be.true;
86 | expect(bFn.called).to.be.false;
87 | expect(cFn.called).to.be.true;
88 | expect(dFn.called).to.be.false;
89 | });
90 |
91 | it('Запуск всех вложенных обработчиков', function () {
92 | var o = { text: 'Hello' };
93 |
94 | mediator
95 | .on('a', aFn)
96 | .on('a:b', bFn)
97 | .on('a:b:c', cFn)
98 | .on('a:b:c:d', dFn)
99 | .broadcast('a', o);
100 |
101 | expect(aFn.calledWith(o)).to.be.true;
102 | expect(bFn.calledWith(o)).to.be.true;
103 | expect(cFn.calledWith(o)).to.be.true;
104 | expect(dFn.calledWith(o)).to.be.true;
105 | });
106 |
107 | it('Удаление из списка обработчиков', function () {
108 |
109 | mediator
110 | .on('namespace', aFn)
111 | .on('namespace', bFn)
112 | .off('namespace')
113 | .on('namespace', cFn)
114 | .trigger('namespace');
115 |
116 | expect(aFn.called).to.be.false;
117 | expect(bFn.called).to.be.false;
118 | expect(cFn.called).to.be.true;
119 | });
120 |
121 | it('Удаление всех вложенных обработчиков', function () {
122 | mediator
123 | .on('a', aFn)
124 | .on('a:b', bFn)
125 | .on('a:b:c', cFn)
126 | .on('a:b:c:d', dFn)
127 | .off('a', true);
128 |
129 | expect(aFn.called).to.be.false;
130 | expect(bFn.called).to.be.false;
131 | expect(cFn.called).to.be.false;
132 | expect(dFn.called).to.be.false;
133 | });
134 | });
135 | });
136 |
137 |
138 |
139 |
140 | ///**
141 | // * Пример использования
142 | // * */
143 | //var m = new Mediator();
144 | //
145 | ///**
146 | // * Добавляем обработчик
147 | // * */
148 | //m.on('hello', function (data) {
149 | // console.log('Эти данные пришли из медиатора: hello -> ' + data.name);
150 | //});
151 | //
152 | //m.on('hello:world', function(data) {
153 | // console.log('Эти данные пришли из медиатора: hello:world -> ' + data.name);
154 | //});
155 | //
156 | //m.on('hello:world:a', function(data) {
157 | // console.log('Эти данные пришли из медиатора: hello:world:a -> ' + data.name);
158 | //});
159 | //
160 | //m.on('hello:world:b', function(data) {
161 | // console.log('Эти данные пришли из медиатора: hello:world:b -> ' + data.name);
162 | //});
163 | //
164 | //m.on('hello:world:c', function(data) {
165 | // console.log('Эти данные пришли из медиатора: hello:world:c -> ' + data.name);
166 | //});
167 | //
168 | //m.on('hello:world:c:d', function(data) {
169 | // console.log('Эти данные пришли из медиатора: hello:world:c:d -> ' + data.name);
170 | //});
171 | //
172 | //m.on('hello:another', function(data) {
173 | // console.log('Эти данные пришли из медиатора: hello:another -> ' + data.name);
174 | //});
175 | //
176 | //m.on('hello:another:world', function(data) {
177 | // console.log('Эти данные пришли из медиатора: hello:another:world -> ' + data.name);
178 | //});
179 | //
180 | //m.on('hello:another:world:a', function(data) {
181 | // console.log('Эти данные пришли из медиатора: hello:another:world:a -> ' + data.name);
182 | //});
183 | //
184 | //m.on('hello:another:world:b', function(data) {
185 | // console.log('Эти данные пришли из медиатора: hello:another:world:b -> ' + data.name);
186 | //});
187 | //
188 | ////m.off('hello', true);
189 | //
190 | //m.on('goodbye', function(data) {
191 | // console.log('Эти данные пришли из медиатора: goodbye -> ' + data.name);
192 | //});
193 | //
194 | //m.on('goodbye:world', function(data) {
195 | // console.log('Эти данные пришли из медиатора: goodbye:world ->' + data.name);
196 | //});
197 | //
198 | //m.on('goodbye:another:world', function(data) {
199 | // console.log('Эти данные пришли из медиатора: goodbye:another:world -> ' + data.name);
200 | //});
201 | //
202 | //m.on('lorem:ipsum:dolor:sit:amet', function(data) {
203 | // console.log('Эти данные пришли из медиатора: lorem:ipsum:dolor:sit:amet -> ' + data.name);
204 | //});
205 | //
206 | //m.on('lorem:lorem', function(data) {
207 | // console.log('Эти данные пришли из медиатора: lorem:lorem -> ' + data.name);
208 | //});
209 | //
210 | //m.on('lorem:ipsum', function(data) {
211 | // console.log('Эти данные пришли из медиатора: lorem:ipsum -> ' + data.name);
212 | //});
213 | //
214 | ////m.off('lorem:ipsum:dolor');
215 | //
216 | ///**
217 | // * Вызываем обработчик
218 | // * */
219 | //m.broadcast('hello', { name: 'Аполлинарий Лаодикийский' });
220 | //m.trigger('hello:world', { name: 'Вильгельм фон Каульбах' });
221 | ////m.trigger('', { name: 'Дени Дидро' });
222 | ////m.trigger('', { name: 'Фридрих Ницше' });
223 | ////m.trigger('', { name: 'Иммануил Кант' });
224 | ////m.trigger('', { name: 'Анри Пуанкаре' });
225 | ////m.trigger('', { name: 'Эварист Галуа' });
226 | //m.trigger('hello:another:world', { name: 'Якоб Бернулли' });
227 | //m.trigger('goodbye', { name: 'Жан Даламбер' });
228 | //m.trigger('goodbye:world', { name: 'Алан Тьюринг' });
229 | //m.trigger('goodbye:another:world', { name: 'Норберт Винер' });
230 | //
231 | //
232 | //m.trigger('lorem:ipsum:dolor:sit:amet', { name: 'Александр Ляпунов' });
233 | //m.trigger('lorem:ipsum', { name: 'Артур Шопенгауэр' });
234 | //m.trigger('lorem:lorem', { name: 'Карл Вейерштрасс' });
235 | //m.broadcast('lorem', { name: 'Рекурсивно' });
236 | //
237 | ////console.log(m.getStorage());
--------------------------------------------------------------------------------
/assets/underscore.min.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.6.0
2 | // http://underscorejs.org
3 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
4 | // Underscore may be freely distributed under the MIT license.
5 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this);
6 | //# sourceMappingURL=underscore-min.map
--------------------------------------------------------------------------------