diff --git a/Base/Collection.js b/Base/Collection.js new file mode 100644 index 0000000..24b6eda --- /dev/null +++ b/Base/Collection.js @@ -0,0 +1,92 @@ +/** +* Возвращает объект Collection +* +* @param {Array} items Элементы списка +* +* @example Collection([]); +*/ +var Collection = function (items) { + "use strict"; + + var i; + this.items = [] + for (i = 0; i < items.length; i++) { + this.items[i] = items[i].clone(); + } +}; + +/** + * Добавление элемента в коллекцию + * + * @return {Collection} + */ +Collection.prototype.add = function (model) { + "use strict"; + + var result = new this.constructor(this.items); + result.items.push(model); + result.sendCurrentState(); + return result; +}; + +/** + * @param {Function} selector + * + * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter + * + * @example + * new Collection([]).filter(function (item) { + * return item.get('attendee').indexOf("me") !== -1; + * }); + * @return {Collection} + */ +Collection.prototype.filter = function (selector) { + "use strict"; + + return new this.constructor(this.items.filter(selector)); +}; + +/** + * @param {String} fieldName + * + * @see http://javascript.ru/Array/sort + * + * @example + * new Collection([]).sortBy("raiting"); + * @return {Collection} + */ +Collection.prototype.sortBy = function (fieldName) { + "use strict"; + + var result = new this.constructor(this.items); + result.items.sort(function (first, second) { + if (first.get(fieldName) < second.get(fieldName)) { + return -1; + } + if (first.get(fieldName) > second.get(fieldName)) { + return 1; + } + return 0; + }); + return result; +}; + +/** + * Сериализация коллекции в формат JSON + */ +Collection.prototype.serialise = function () { + "use strict" + + return JSON.stringify(this.items); +}; + +/** + * Сохранение коллекции + */ +Collection.prototype.sendCurrentState = function () { + "use strict" + + var data = this.serialise(); + console.log(data); + postFile("current-event.json", data, callback); +}; \ No newline at end of file diff --git a/Base/Model.js b/Base/Model.js new file mode 100644 index 0000000..20c780a --- /dev/null +++ b/Base/Model.js @@ -0,0 +1,41 @@ +var Model = function (data) { + "use strict"; +}; + +/** + * @param {Object} attributes + * + * @example + * item.set({title: "March 20", content: "In his eyes she eclipses..."}); + */ +Model.prototype.set = function (attributes) { + "use strict"; + var keyName; + for (keyName in attributes) { + this[keyName] = attributes[keyName]; + } +}; + +/** + * @param {String} attribute + */ +Model.prototype.get = function (attribute) { + "use strict"; + return this[attribute]; +}; + +/** + * @param {Object} attributes + */ +Model.prototype.validate = function (attributes) { + "use strict"; + throw new Error('this is Abstract method'); +}; + +Model.prototype.clone = function () { + var attr, temp = new this.constructor(); + for (attr in this) { + temp[attr] = clone(this.get(attr)); + } + return temp; +} \ No newline at end of file diff --git a/Clone.js b/Clone.js new file mode 100644 index 0000000..02cb9f4 --- /dev/null +++ b/Clone.js @@ -0,0 +1,29 @@ +function clone(obj) { + if (null == obj || "object" != typeof obj) { + return obj; + } + + if (obj instanceof Date) { + var copy = new Date(); + copy.setTime(obj.getTime()); + return copy; + } + + if (obj instanceof Array) { + var i, copy = []; + for (i = 0; i < obj.length; ++i) { + copy[i] = clone(obj[i]); + } + return copy; + } + + if (obj instanceof Object) { + var copy = {}; + for (var attr in obj) { + if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]); + } + return copy; + } + + throw new Error("Unable to copy obj! Its type isn't supported."); +} \ No newline at end of file diff --git a/Event/Event.js b/Event/Event.js new file mode 100644 index 0000000..4c32901 --- /dev/null +++ b/Event/Event.js @@ -0,0 +1,45 @@ +/** +* Возвращает объект Event +* +* @param {Json} Событие +* +* @example +* Event({ +* name: "Совещание", +* address: "Екатеринбург, ул. Тургенева, д. 4, ауд. 150", +* timeStart: new Date(), +* timeEnd: new Date(), +* member: ["я"], +* raiting: 5); +*/ + +var Event = function (event) { + "use strict"; + + Model.call(this); + this.set(event); +} + +inherits(Event, Model); +Event.prototype.constructor = Event; + +/** + * Валидация собития + * + * @return {Array} Список ошибок + */ +Event.prototype.validate = function () { + "use strict"; + + var errors = []; + if (new Date(this.get("timeStart")) > new Date(this.get("timeEnd"))) { + errors.push("Время начала события больше чем время конца"); + } + if (this.get("raiting") < 0) { + errors.puch("Рэйтинг собития меньше допустимой величины"); + } + else if (this.get("raiting") > 5) { + errors.push("Рэйтинг события больше допустимой величины"); + } + return errors; +}; \ No newline at end of file diff --git a/Event/Events.js b/Event/Events.js new file mode 100644 index 0000000..973a5db --- /dev/null +++ b/Event/Events.js @@ -0,0 +1,127 @@ +/** + * @return {Object} Список событий + */ +var Events = function (items) { + "use strict"; + + Collection.call(this, items); +}; + +inherits(Events, Collection); +Events.prototype.constructor = Events; + +/** + * Прошедшие события + * + * @return {Events} + */ +Events.prototype.findPastEvents = function () { + "use strict"; + + return this.filter(function (event) { + return new Date(event.get("timeStart")) < new Date(); + }); +}; + +/** + * Предстоящие события + * + * @return {Events} + */ +Events.prototype.findFutureEvents = function () { + "use strict"; + + return this.filter(function (event) { + return new Date(event.get("timeStart")) > new Date(); + }); +}; + +/** + * События с участием персоны с именем "name" + * + * @param personName Имя персоны + * + * @return {Events} + */ +Events.prototype.findEventsWithPerson = function (personName) { + "use strict"; + + return this.filter(function (event) { + return event.get("member").some(function (member) { + return member === personName; + }); + }); +}; + +/** + * События без участия персоны с именем "name" + * + * @param personName Имя персоны + * + * @return {Events} + */ +Events.prototype.findEventsWithoutPerson = function (personName) { + "use strict"; + + return this.filter(function (event) { + return event.get("member").every(function (member) { + return member != personName; + }); + }); +}; + +/** + * События, которые произойдут после указанного времени + * + * @param time Временной отсчет + * + * @return {Events} + */ +Events.prototype.findEventsHappendLaterTime = function (time) { + "use strict"; + + return this.filter(function (event) { + return new Date(event.get("timeStart")) > time; + }); +}; + +/** + * События, которые произойдут до указанного времени + * + * @param time Временной отсчет + * + * @return {Events} + */ +Events.prototype.findEventsHappendBeforeTime = function (time) { + "use strict"; + + return this.filter(function (event) { + return new Date(event.get("timeStart")) < time; + }); +}; + +/** + * События, с рейтингом больше (или равного) переданному + * + * @param raiting Рейтинг + * + * @return {Events} + */ +Events.prototype.findEventsWithRaitingMore = function (raiting) { + "use strict"; + + return this.filter(function (event) { + return event.get("raiting") >= raiting; + }); +}; + +/** + * Сортировка по переданному полю + * + * @return {Events} + */ +Events.prototype.sortEventsBy = function (field) { + "use strict"; + + return this.sortBy(field); +}; \ No newline at end of file diff --git a/Inherits.js b/Inherits.js new file mode 100644 index 0000000..3cc478b --- /dev/null +++ b/Inherits.js @@ -0,0 +1,7 @@ +function inherits (Constructor, SuperConstructor) { + "use strict"; + + var Temp = function () {}; + Temp.prototype = SuperConstructor.prototype; + Constructor.prototype = new Temp(); +} \ No newline at end of file diff --git a/XMLHttpRequestWrapper.js b/XMLHttpRequestWrapper.js new file mode 100644 index 0000000..0ee30a8 --- /dev/null +++ b/XMLHttpRequestWrapper.js @@ -0,0 +1,28 @@ +function callback (text) { + console.log(text); +} + +function postFile (fileName, data, callback) { + "use strict" + + requestFile("POST", fileName, data, callback); +} + +function getFile(fileName, callback) { + "use strict" + + requestFile("GET", fileName, null, callback); +} + +function requestFile(method, fileName, data, callback) { + "use strict" + + var xhr = new XMLHttpRequest(); + xhr.open(method, fileName, true); + xhr.addEventListener('readystatechange', function() { + if (xhr.readyState === 4) { + callback(xhr.status === 200 ? xhr.responseText : "Error"); + } + }); + xhr.send(data); +} \ No newline at end of file diff --git a/current-event.json b/current-event.json index eecedbc..fb648e0 100644 --- a/current-event.json +++ b/current-event.json @@ -1 +1 @@ -[{"name":"pewpew","start":"2012-11-07T10:18:39.207Z","end":"2012-11-07T10:18:39.207Z"}] \ No newline at end of file +[{"name":"Мое первое событие","address":"Здесь и сейчас","timeStart":"2012-12-09T00:00:00.000Z","timeEnd":"2012-12-09T00:00:00.000Z","member":["Иванов Иван Иванович","Петров Петр Петрович"],"raiting":4},{"name":"Второе но более важное событие","address":"Тоже здесь","timeStart":"2012-12-13T00:00:00.000Z","timeEnd":"2012-12-14T00:00:00.000Z","member":["Сидоров Петр Ильич"],"raiting":5},{"name":"Совсем не важное событие","address":"Адрес","timeStart":"2012-12-08T00:00:00.000Z","timeEnd":"2012-12-27T00:00:00.000Z","member":["Господин Никто"],"raiting":1}] \ No newline at end of file diff --git a/eventFormController.js b/eventFormController.js new file mode 100644 index 0000000..ba5838f --- /dev/null +++ b/eventFormController.js @@ -0,0 +1,227 @@ +var myEvents = new Events([]); + +/** + * Обработка формы с событием + * 1. Извлечение данных о событии из формы + * 2. Очитка формы от введенных данных + * 3. Добавление события в список сохраненных + * 4. Перересовка списка событий + * + * @return false для того чтобы не перезагружалась страница + */ +function onSubmitEventForm() { + "use strict"; + + try { + var event = parseEventForm(); + var errors = event.validate(); + if (errors.length == 0) { + resetEventForm(); + myEvents = myEvents.add(event); + rePaintEvents(myEvents); + } + else { + setErrorMessage("Невозможно добавить событие, содержатся следующие ошибки:" + errors); + } + } catch (e) { + console.log(e.message); + console.log(e.stack); + } finally { + return false; + } +} + + +/** + * Загрузка сохраненных событий + */ +function loadEvents() { + "use strict"; + + var localEvents = getFile("current-event.json", function (json) { + myEvents.items = JSON + .parse(json) + .map(function (event) { + return new Event(event); + }); + rePaintEvents(myEvents); + }); +} + +/** + * Сортировка и фильтрация списка сохраненных событий и их перерисовка + */ +function showEventList() { + "use strict"; + + var sortSelector = document.getElementById("eventSortType"); + var events = myEvents.sortEventsBy(sortSelector.value); + if (document.getElementById("lastEvents").checked) { + events = events.findPastEvents(); + } + if (document.getElementById("futureEvents").checked) { + events = events.findFutureEvents(); + } + var withPerson = document.getElementById("withPerson").value; + if (withPerson != "") { + events = events.findEventsWithPerson(withPerson); + } + var withoutPerson = document.getElementById("withoutPerson").value; + if (withoutPerson != "") { + events = events.findEventsWithoutPerson(withoutPerson); + } + var raiting = document.getElementById("raitingMore").value; + events = events.findEventsWithRaitingMore(raiting); + + rePaintEvents(events); +} + +/** + * Очитка формы от введенных данных + */ +function resetEventForm () { + "use strict"; + + var form = document.getElementById("eventForm"); + form.reset(); + deleteMembers(); + setErrorMessage(""); +} + +/** + * Извлечение данных из формы + * + * @return {Object|Event} Событие + */ +function parseEventForm() { + "use strict"; + + var name = document.getElementById("eventName").value; + var address = document.getElementById("eventAddress").value; + var timeStart = + new Date( + Date.parse( + document.getElementById("eventDateStart").value + "T" + + document.getElementById("eventTimeStart").value)); + var timeEnd = + new Date( + Date.parse( + document.getElementById("eventDateEnd").value + "T" + + document.getElementById("eventTimeEnd").value)); + var memberHTML = document.querySelectorAll(".memberItem"); + var i, members = []; + for (i = 0; i < memberHTML.length; i++) { + members.push(memberHTML[i].innerHTML); + } + var raiting = document.getElementById("eventRaiting").value; + + return new Event({ + "name": name || "<Без имени>", + "address": address || "<Без адреса>", + "timeStart": timeStart, + "timeEnd": timeEnd, + "member": members, + "raiting": +raiting || 3 + }); +} + +/** + * Перерисовка списка переданных событий + * + * @param events список событий для отрисовки + */ +function rePaintEvents(events) { + "use strict"; + + var oldContainer = document.getElementById("myEvents"); + + var newContainer = document.createElement("div"); + newContainer.id = "myEvents"; + + events.items + .map(eventHtml) + .map(function (event) { + newContainer.innerHTML += event; + }); + + oldContainer.parentNode.replaceChild(newContainer, oldContainer); +} + +/** + * Создание представления для одного события + * + * @return {String} html представление события + */ +function eventHtml(event) { + "use strict"; + + var i, stars = ""; + for(i = 0; i < event.raiting ; i++) { + stars += "*" + } + var timeStart = (event.timeStart == "Invalid Date") ? "Не указано" : new Date(event.timeStart).toUTCString(); + var timeEnd = (event.timeEnd == "Invalid Date") ? "Не указано" : new Date(event.timeEnd).toUTCString(); + + var eventHtml = + "
Loading...
-
-
+
+
+
+