diff --git a/.gitignore b/.gitignore
index 62c8935..9f11b75 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-.idea/
\ No newline at end of file
+.idea/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..634930d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "underscore"]
+	path = underscore
+	url = https://github.com/documentcloud/underscore.git
+[submodule "backbone"]
+	path = backbone
+	url = https://github.com/documentcloud/backbone.git
diff --git a/Collection.js b/Collection.js
new file mode 100644
index 0000000..411d633
--- /dev/null
+++ b/Collection.js
@@ -0,0 +1,91 @@
+/**
+ * Коллекции для модели Event.
+ * 
+ * @constructor
+ * @this {EventsCollection}
+ * @param {Array} collect_list Список Event классов.
+ */
+var EventsCollection = new Collection({
+    'model': Event
+});
+
+/**
+ * Отфильтровывает события, которые произошли до указанной даты.
+ *
+ * @return {EventsCollection} Новый объект EventsCollection.
+ */
+EventsCollection.prototype.start_before = function (date) {
+    return this.filter(function (event) {
+        return event.get('start_time') < date;
+    });
+};
+
+/**
+ * Возвращает количество событий (Event) в коллекции.
+ *
+ * @return {number} Кол-во событий.
+ */
+EventsCollection.prototype.count_events = function () {
+    return this.size();
+};
+
+/**
+ * Отфильтровывает события, которые произошли после указанной даты.
+ *
+ * @return {EventsCollection} Новый объект EventsCollection.
+ */
+EventsCollection.prototype.start_after = function (date) {
+    return this.filter(function (event) {
+        return event.get('start_time') > date;
+    });
+};
+
+/**
+ * Фильтрует личные события.
+ *
+ * @return {EventsCollection} Новый объект EventsCollection.
+ */
+EventsCollection.prototype.my_events = function () {
+    return this.filter(function (event) {
+        return event.get('go');
+    });
+};
+
+/**
+ *
+ * TESTS
+ *
+ */
+
+function ok(name, val1, val2) {
+    if (val1 !== val2) {
+        console.log(name + " [FAILED]");
+    } else {
+        console.log(name + " [OK]");
+    }
+}
+
+function Collection_tests() {
+    var a1 = createNewEvent(222, 333, 'lol'); // invalid date
+    var a2 = createNewEvent(223, 333, 'lol2');
+    var a3 = createNewEvent(100, 500, 'lol2');
+    var events_data = [a1, a2, a3];
+    var events_callect = new EventsCollection(events_data);
+
+    // t1
+    var events_before_date = events_callect.start_before(223);
+    ok('events_before_date filter', events_before_date.size(), 2);
+
+    // t2
+    ok('events_before_date count_events()', events_before_date.count_events(), 2);
+
+    // t3
+    var events_after_date = events_callect.start_after(222);
+    ok('events_after_date filter count_events()', events_after_date.count_events(), 1);
+
+    // t4 combine methods
+    var some_filtered_event = events_callect.start_before(223).start_after(100);
+    ok('sequence filters', some_filtered_event.count_events(), 1);
+
+    ok('MY EVENTS filter', some_filtered_event.my_events().size(), 0);
+}
diff --git a/Model.js b/Model.js
new file mode 100644
index 0000000..632e21f
--- /dev/null
+++ b/Model.js
@@ -0,0 +1,89 @@
+
+function isValidDate(d) {
+    if ( Object.prototype.toString.call(d) === "[object Date]" ) {
+        // it is a date
+        if ( isNaN( d.getTime() ) ) {  // d.valueOf() could also work
+            // date is not valid
+            return false;
+        }
+        else {
+            // date is valid
+            return true;
+        }
+    }
+    else {
+        // not a date
+        return false;
+    }
+}
+
+
+/**
+ * Модель для класса Event.
+ * 
+ * @constructor
+ * @param {Object} info Обект, описывающий общую информацию о мероприятии (место проведения, название, описание, временные рамки).
+ */
+var Event = new Model({
+    'default': {
+        title: "event",
+        go: false
+    },
+    'constructor': function (info) {
+        if (typeof info.start_time === 'string') {
+            info.start_time = new Date(Date.parse(info.start_time));
+        }
+        if (typeof info.end_time === 'string') {
+            info.end_time = new Date(Date.parse(info.end_time));
+        }
+
+        this.update(info);
+    },
+    'errors': function (name) {
+        if (!this.get('start_time') || !this.get('end_time')) {
+            return "miss required fields";
+        }
+        if (!isValidDate(this.get('start_time'))) {
+            return "invalid start_time value";
+        }
+        if (!isValidDate(this.get('end_time'))) {
+            return "invalid end_time value";
+        }
+        if (this.get('start_time') > this.get('end_time')) {
+            return "starat_time more then end_time";
+        }
+        return null;
+    }
+});
+
+/**
+ * Создает объект Event
+ *
+ * @param {Number|Date} start_at             Начало события
+ * @param {Number|Date} end_at               Конец события
+ * @param {String}      [name="Событие"]  Имя события
+ *
+ * @example
+ *    Event(new Date("2011-10-10T14:48:00"),
+ *          new Date("2011-10-10T15:48:00"),
+ *          "Совещание")
+ *
+ * @return {Object}
+ */
+function createNewEvent(start_at, end_at, name, go) {
+    var info = {
+        start_time: start_at,
+        end_time: end_at,
+        title: name
+    };
+    if (go) {
+        info.go = true;
+    }
+
+    return new Event(info);
+}
+
+
+function ModelTests() {
+    var a = createNewEvent(222, 333, 'lol'); // invalid date
+}
diff --git a/MyAbstractLayer.js b/MyAbstractLayer.js
new file mode 100644
index 0000000..c9a716f
--- /dev/null
+++ b/MyAbstractLayer.js
@@ -0,0 +1,513 @@
+/**
+ * Created with PyCharm.
+ * User: stribog
+ * Date: 07.11.12
+ * Time: 1:20
+ * To change this template use File | Settings | File Templates.
+ */
+
+/**
+ *
+ *
+ * Model
+ *
+ *
+*/
+
+/**
+ * Метакласс для создания классов Model.
+ *
+ * @constructor
+ * @param {Object}              ext                     Обект, расширяющийинформацию поведение класса или изменяющий его.
+ * @return {function constructor}                       Конструктор моделей
+ *
+ * @example
+ * var Event = Model({
+ *      'default' : {
+ *          'title': "myEvent"
+ *      },
+ *      'constructor' : function (title, start_time) {
+ *          if (title) {
+ *              this.title = title;
+ *          }
+ *          this.start = start_time;
+ *      },
+ *      errors: function (name) {
+ *          if (name == "title" && this.get("title") === "WTF") {
+ *             return "Can not create WTF event title";
+ *          }
+ *
+ *          var error = "";
+ *          if (!this.get("title")) {
+ *              error += "Required title. ";
+ *          }
+ *          if (this.get("title") && this.get("title") === "WTF") {
+ *              error += "Can not create WTF event title. ";
+ *          }
+ *          return error;
+ *      }
+ * });
+ *
+ * var my_event = new Event("cool skool party", 123214121);
+ *
+ * my_event.get("title");
+ * my_event.set("countPerson", 4);
+ */
+var Model = function (ext) {
+    var ModelConstructor = function () { // faced construct
+        if (this === window) {
+            throw new SyntaxError('WARNING: miss new');
+        }
+
+        this.__private_attrs = _.extend({}, this.default);
+        this.constructor.apply(this, arguments);
+        var errors = this.errors();
+        if (errors) {
+            throw new SyntaxError("Invalid value in constructor: " + errors);
+        }
+    };
+    ModelConstructor.prototype = _.extend({}, Model.prototype, ext);
+
+    return ModelConstructor;
+};
+
+/**
+ * Коллекция, с значениями по умолчанию, для полей модели.
+ */
+Model.prototype.default = {};
+
+/**
+ * Конструктор, который будет вызван в моделе, порожденной конструктором Model (метоклассом).
+ * Количество параметров и поведение конструктора определяется индивидуально.
+ *
+ * @constructor
+ */
+Model.prototype.constructor = function () {};
+
+/**
+ * Отдает текущее значение атрибута модели.
+ *
+ * @param name              Имя атрибута
+ * @return {*} Значение
+ *
+ * @example
+ * alert(my_event.get('title'));
+ */
+Model.prototype.get = function (name) {
+    if (typeof name !== "string") {
+        throw new SyntaxError("Invalid parameter");
+    }
+
+    return this.__private_attrs[name];
+};
+
+/**
+ * Устанавливает значение для модели.
+ * Если установлен флаг no_check_error становлен, проверка целостности не будет проводиться (функция error не будет вызвана).
+ *
+ * @param {String}      name                        Имя атрибута
+ * @param {*}           value                       Значение атрибута
+ * @param {Boolean}     no_check_error              Флаг, отвечающий за проверку ошибок
+ * @return {Boolean}                                Возвращает тру, если занчение установлено
+ *
+ * @example
+ * var my_event = new Event("cool skool party", 123214121);
+ * if (my_event.set("title", "WTF")) {
+ *      alert('ok');
+ * } else {
+ *      alert('no set =(');
+ * }
+ */
+Model.prototype.set = function (name, value, no_check_error) {
+    if (typeof name !== "string") {
+        throw new SyntaxError("Invalid parameter");
+    }
+
+    var back = this.__private_attrs[name];
+    this.__private_attrs[name] = value;
+
+    if (!no_check_error) {
+        var errors = this.errors(name);
+        if (errors) {
+            console.log('WARNING: rollback set method. validation error: ' + errors);
+            this.__private_attrs[name] = back; // rollback;
+            return false;
+        }
+    }
+    return true;
+};
+
+/**
+ * Устанавливает занчение для нескольких параметров.
+ * Если установка хотя бы одного из значений вызовет ошибку, тогда состояние модели не будет изменено.
+ *
+ * @param {Object}      dict                        Коллекция параметров
+ * @return {Boolean}                                Возвращает тру, если все занчения установлены
+ */
+Model.prototype.update = function (dict) {
+    var self = this;
+    var back = {};
+
+    _.forEach(dict, function(val, key) {
+        self.set(key, val, true);
+        back[key] = val;
+    });
+
+    var errors = this.errors();
+    if (errors) {
+        console.log('WARNING: rollback update method. validation error: ' + errors);
+        _.extend(this.__private_attrs, back); // rollback;
+        return false;
+    }
+
+    return true;
+};
+
+/**
+ * Проверяет установлено ли занчение атрибута.
+ *
+ * @param               name                        Имя атрибута
+ * @return {Boolean}                                Возваращает тру, если данный атрибут присутствует в модели
+ */
+Model.prototype.has = function (name) {
+    if (typeof name !== "string") {
+        throw new SyntaxError("Invalid parameter");
+    }
+
+    return this.__private_attrs[name] !== null;
+};
+
+/**
+ * Проверка на ошибки.
+ * Если параметр namе отсутствует, то будут проверены все атрибуты модели.
+ *
+ * @param               name                        Имя атрибута проверяемого на наличие ошибок (не обязательный параметр)
+ * @return {String}                                 Строкое представление текста ошибок, если они были замечены
+ */
+Model.prototype.errors = function (name) { return ""; };
+
+/*
+ *
+ *  Collection
+ *
+ */
+
+/**
+ * Метакласс для создания коллекций моделей.
+ *
+ * @constructor
+ * @param {Object}          ext                     Обект, расширяющийинформацию поведение коллекции или изменяющий его.
+ * @return {function constructor}                   Конструткор коллекций
+ *
+ * @example
+ * var EventsCollection = new Collection({
+ *      'model': new Model({
+ *          'default': {
+ *              'val0' : -1
+            },
+            'constructor': function (v) {
+                if (v) this.set('val0', v);
+            }
+        })
+ * });
+ *
+ *
+ * var my_event_collection = new EventsCollection();
+ *
+ * my_event_collection.add(new my_event_collection.model());
+ * my_event_collection.add(new my_event_collection.model(1));
+ *
+ * var my_event_collection_max_to_min_sorted = my_event_collection.sortBy(function (model) { return model.get('val0'); });
+ * var my_event_collection_less_zero_filtered = my_event_collection.filter(function (model) { return model.get('val0') < 0; });
+ *
+ * var M = new Model({
+ *     'default': {
+ *         'a': 0,
+ *         'b': 100
+ *     },
+ *     'constructor': function(a, b) {
+ *         if (a) this.set('a', a);
+ *         if (b) this.set('b', b);
+ *     }
+ * });
+ *
+ * var C = new Collection({
+ *     'model': M,
+ * });
+ *
+ * var c = new C([
+ *     new M(1), new M(2), new M(3), new M(4), new M(5), new M(6), new M(7)
+ * ]);
+ *
+ * c.each(function (m) {
+ *     if (!m.has('b')) m.set('b', 100);
+ * });
+ *
+ * c.map(function (m) {
+ *     return m.get('a');
+ * });
+ *
+ * c.find(function (m) {
+ *     return m.get('a') % 5 == 2;
+ * });
+ *
+ * c.all(function (m) {
+ *     return m.get('b') == 100;
+ * });
+ *
+ * c.any(function (m) {
+ *     return m.get('b') == 100;
+ * });
+ *
+ * c.include(new M(1)); // --> false
+ *
+ * c.contains(new M(1)); // --> false
+ *
+ * c.max(function (m) {
+ *     return m.get('a');
+ * });
+ *
+ * c.min(function (m) {
+ *     return m.get('a');
+ * });
+ *
+ * c.toArray();
+ *
+ * c.size();
+ *
+ * c.first();c.head();
+ * c.tail();c.last();
+ *
+ * c.indexOf(new M(1)); // --> -1
+ * c.lastIndexOf(new M(1)); // --> -1
+ *
+ * c.groupBy(function (m) {
+ *     return m.get('a') % 3;
+ * });
+ *
+ * c.isEmpty();
+ *
+ * c.foldl(function (memo, m) {
+ *     return memo + m.get('a');
+ * }, 0);
+ *
+ * c.foldr(function (memo, m) {
+ *     return memo + m.get('a');
+ * }, 0);
+ */
+var Collection = function (ext) {
+    var CollectionConstructor = function (models) { // faced construct
+        if (this === window) {
+            throw new SyntaxError('WARNING: miss new');
+        }
+
+        this.__private_models = [];
+        this.__private_constructor = arguments.callee; /* may be memory leek ? */
+
+        if (_.isArray(models)) {
+            for (var i = 0; i < models.length; i++) {
+                this.add(models[i]);
+            }
+        }
+        this.initialize.apply(this, arguments);
+    };
+    CollectionConstructor.prototype = _.extend({}, Collection.prototype, ext);
+
+    return CollectionConstructor;
+};
+
+/**
+ * Функция вызываемая после созания соллекции.
+ */
+Collection.prototype.initialize = function (models) {};
+
+/**
+ * Модель для членов коллекции
+ */
+Collection.prototype.model = new Model();
+
+/**
+ * Взятие элемента по индексу из коллекции.
+ *
+ * @param               index                   Индекс
+ * @return {*}                                  Экземпляр модели (instanceof this.model)
+ */
+Collection.prototype.at = function(index){ return this.__private_models[index]; };
+
+/**
+ * Срез коллекции
+ *
+ * @param               from                    Начальный индекс
+ * @param               to                      Конечный индекс
+ * @return {Array}                              Срез
+ */
+Collection.prototype.slice = function(from, to){ return this.__private_models.slice(from, to); };
+
+/**
+ * Добавление элемента в модель
+ *
+ * @param {Object instanceof this.model} model_obj      Элемент, который надо добавить в воллекцию
+ */
+Collection.prototype.add = function(model_obj){
+    if (! model_obj instanceof this.model) {
+        throw new SyntaxError("can add object with strange model");
+    }
+    this.__private_models.push(model_obj);
+};
+
+// Underscore methods that we want to implement on the Collection.
+var methods = ['each', 'map', 'find',
+    'all', 'any',
+    'include', 'contains',
+    'max', 'min',
+    'toArray',
+    'size',
+    'first', 'head', 'tail', 'last',
+    'indexOf', 'lastIndexOf', 'groupBy',
+    'isEmpty',
+    'foldr', 'foldl'];
+
+// Mix in each Underscore method as a proxy to `Collection#models`.
+_.each(methods, function(method) {
+    Collection.prototype[method] = function() {
+        var args = Array.prototype.slice.call(arguments);
+        args.unshift(this.__private_models);
+        return _[method].apply(_, args);
+    };
+});
+
+// Underscore methods
+var state_change_methods = ['filter', 'sortBy'];
+
+_.each(state_change_methods, function(method) {
+    Collection.prototype[method] = function() {
+        var args = Array.prototype.slice.call(arguments);
+        args.unshift(this.__private_models);
+        return new this.__private_constructor(_[method].apply(_, args));
+    };
+});
+
+/*
+ *  TESTS
+ */
+
+function ok(name, val1, val2) {
+    if (val1 !== val2) {
+        console.log(name + " [FAILED]");
+    } else {
+        console.log(name + " [OK]");
+    }
+}
+
+function TestModel() {
+    var M0 = new Model({
+        'default': {
+            'val0' : -1
+        },
+        'constructor': function (v0) {
+            if (v0) {
+                this.set('val0', v0);
+            }
+        },
+        'errors': function (name) {
+            if (this.get('val0') >= 0) {
+                return 'val0 can not be great than zero';
+            }
+            return null;
+        }
+    });
+
+    var m = new M0();
+    ok('Default', m.get('val0'), -1);
+
+    m = new M0(-2);
+    ok('Constructor', m.get('val0'), -2);
+
+    m = new M0(-3);
+    m.set('val9', -1);
+    ok('Set and Get', m.get('val9'), -1);
+
+    m = new M0(8888);
+    ok('Validator return false', m.set('val0', 9999), false);
+    ok('Validator 1', m.get('val0'), -1);
+    ok('Validator return true', m.set('val0', -5), true);
+    ok('Validator 2', m.get('val0'), -5);
+
+    var M1 = new Model({
+        'default': {
+            'val0' : -2
+        },
+        'constructor': function (v0, v1) {
+            if (v0) {
+                this.set('val0', v0);
+            }
+            if (v1) {
+                this.set('val1', v1);
+            }
+        }
+    });
+
+    m = new M1(4);
+    ok('Validate new Model no error', m.get('val0'), 4);
+
+     m = new M1();
+     ok('Validate new Model Default', m.get('val0'), -2);
+
+     m = new M1(-10, -20);
+     ok('Validate new Model Constructor 1', m.get('val0'), -10);
+     ok('Validate new Model Constructor 2', m.get('val1'), -20);
+
+     m = new M1();
+     m.set('val0', 9);
+     m.set('val1', 9);
+     ok('Validate new Model Get and Set', m.get('val0'), 9);
+     ok('Validate new Model Get and Set', m.get('val1'), 9);
+}
+function TestCollection() {
+    var M0 = new Model({
+        'default': {
+            'val0' : -1
+        },
+        'constructor': function (v0) {
+            if (v0) {
+                this.set('val0', v0);
+            }
+        },
+        'errors': function (name) {
+            if (this.get('val0') >= 0) {
+                return 'val0 can not be great than zero';
+            }
+            return null;
+        }
+    });
+
+    var C0 = new Collection({
+        'model': M0
+    });
+
+    var c = new C0();
+    ok('Collection Model test', c.model, M0);
+
+    var m = new M0(-10000);
+
+    c = new C0([
+        new M0(-10), new M0(-20), new M0(-30), new M0(-100), new M0(-200), new M0(-300), m
+    ]);
+    ok('Crate collection', c.size(), 7);
+
+    var cf = c.filter(function (val) {
+        return val.get('val0') < -50;
+    });
+    ok('Filter collection', cf.size(), 4);
+    ok('Initial collection', c.size(), 7);
+
+    ok('Include in collection', c.include(m), true);
+    ok('Max in collection', c.min(function (val) { return val.get('val0'); }), m);
+
+    // big ---> small
+    ok('SortBy collection', c.sortBy(function (val) { return (val.get('val0') % 3) * 10000 + val.get('val0') * -1; }).at(0).get('val0'), -20);
+
+}
+
+TestModel();
+TestCollection();
diff --git a/backbone b/backbone
new file mode 160000
index 0000000..35054da
--- /dev/null
+++ b/backbone
@@ -0,0 +1 @@
+Subproject commit 35054dada664bcbeb13b382780ed550c3e7ed205
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..7784dc8
--- /dev/null
+++ b/index.html
@@ -0,0 +1,7 @@
+<h1>hello</h1>
+<!-- script src="jquery-1.7.2.min.js"></script -->
+<script src="underscore/underscore.js"></script>
+<!-- script src="backbone.js"></script -->
+<script src="MyAbstractLayer.js"></script>
+<script src="Model.js"></script>
+<script src="Collection.js"></script>
diff --git a/underscore b/underscore
new file mode 160000
index 0000000..df050be
--- /dev/null
+++ b/underscore
@@ -0,0 +1 @@
+Subproject commit df050beae261ba5b1751b345d19f46e32a3dab69