diff --git a/Base/Collection.js b/Base/Collection.js new file mode 100644 index 0000000..44943e3 --- /dev/null +++ b/Base/Collection.js @@ -0,0 +1,71 @@ +/** +* Возвращает объект 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); + 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; +}; \ 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..3411932 --- /dev/null +++ b/Event/Event.js @@ -0,0 +1,77 @@ +/** +* Возвращает объект Event +* +* @param {String} [name = "Событие"] Имя события +* @param {String} [address = ""] Адресс события +* @param {Object} time Дата события +* @param {Array} member Участники +* @param {Number} [raiting=3] Важность события (по шкале от 0 до 5) +* +* @example +* Event( +* "Совещание", "Екатеринбург, ул. Тургенева, д. 4, ауд. 150", eventTime, ["я"], 5) +* +* @see EventTime +*/ + +var Event = function (name, address, time, member, raiting) { + "use strict"; + + Model.call(this); + var myTime = time || new EventTime(new Date(), new Date()); + + this.set({ + name: name || "Событие", + address: address || "", + timeStart: myTime.start, + timeEnd: myTime.end, + member: member || [], + raiting: +raiting || 3 + }); +} + +inherits(Event, Model); +Event.prototype.constructor = Event; + + +/** +* Возвращает объект EventTime +* +* @private +* @param {Number|Date} start Время начала события +* @param {Number|Date} end Время конца события +* +* @example +* EventTime(new Date(2011, 10, 10, 14, 48, 00), new Date(2011, 10, 10, 15, 48, 00)) +* +* @return {Object} +*/ +function EventTime(start, end) { + "use strict"; + + return { + "start": start, + "end": end + }; +} + +/** + * Валидация собития + * + * @return {Array} Список ошибок + */ +Event.prototype.validate = function () { + "use strict"; + + var errors = []; + if (this.get("timeStart") > 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..da8c197 --- /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 event.get("timeStart") < new Date().getTime(); + }); +}; + +/** + * Предстоящие события + * + * @return {Events} + */ +Events.prototype.findFutureEvents = function () { + "use strict"; + + return this.filter(function (event) { + return event.get("timeStart") > new Date().getTime(); + }); +}; + +/** + * События с участием персоны с именем "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 event.get("timeStart") > time; + }); +}; + +/** + * События, которые произойдут до указанного времени + * + * @param time Временной отсчет + * + * @return {Events} + */ +Events.prototype.findEventsHappendBeforeTime = function (time) { + "use strict"; + + return this.filter(function (event) { + return 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/eventFormController.js b/eventFormController.js new file mode 100644 index 0000000..f9f8e8e --- /dev/null +++ b/eventFormController.js @@ -0,0 +1,203 @@ +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 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, address, new EventTime(timeStart, timeEnd), members, raiting); +} + +/** + * Перерисовка списка переданных событий + * + * @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") ? "Не указано" : event.timeStart.toUTCString(); + var timeEnd = (event.timeEnd == "Invalid Date") ? "Не указано" : event.timeEnd.toUTCString(); + + var eventHtml = + "