diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7715e64 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "qunit"] + path = qunit + url = git://github.com/jquery/qunit.git diff --git a/Task/Scripts/BaseEvent.js b/Task/Scripts/BaseEvent.js new file mode 100644 index 0000000..4c1e784 --- /dev/null +++ b/Task/Scripts/BaseEvent.js @@ -0,0 +1,224 @@ +/*global Collection: true*/ +(function (toExport) { + "use strict"; +/** + * Создает оболочку над массивом событий, предоставляющую "sql" подобные операции + * + * @class Оболочка над массивом событий. + * @augments Collection + */ + var BaseEvent = function BaseEvent(events) { + "use strict"; + Collection.call(this, events); + } + + toExport.BaseEvent = BaseEvent; + + BaseEvent.prototype = Object.create(Collection.prototype, { + constructor: { + value: BaseEvent, + enumerable: false, + writable: true, + configurable: true + } + }); +/** + * + *@field {BaseEvent} - ссылка на "родной" конструктор +*/ + BaseEvent.prototype.constructor = BaseEvent; +/** + *@function Возвращает новую оболочку, но уже только с прошедшими событиями + * + *@return {BaseEvent} +*/ + BaseEvent.prototype.pastEventBase = function () { + var currentDate = new Date(); + return this.filter(function (event) { + return event.end.getTime() < currentDate.getTime(); + }); + }; +/** + * @function Возвращает новую оболочку, но уже только с ненаступившими событиями + * + * @return {BaseEvent} +*/ + BaseEvent.prototype.nextEventBase = function () { + var currentDate = new Date(); + return this.filter(function (event) { + return event.start.getTime() > currentDate.getTime(); + }); + }; +/** + * @function Возвращает новую оболочку, но уже с событиями, которые идут в данный момент + * + * @return +*/ + BaseEvent.prototype.nowEventBase = function () { + var currentDate = new Date(); + return this.filter(function (event) { + return (event.start.getTime() <= currentDate.getTime() && event.end.getTime() >= currentDate.getTime()); + }); + }; + +/** + * @function Возвращает новую оболочку, но уже с событиями, в которых участвует определенный человек + * + * @return + */ + BaseEvent.prototype.withFriend = function (myFriend) { + return this.filter(function (event) { + return event.parties.some(function (party) { + return party.name === myFriend.name; + }); + }); + }; +/** + * @function Увеличивает дату на день + * @private + * + * @field {Date} currentDate дата, которую будем увеличивать + * + * @return {Date} + */ + var addDay = function (currentDate) { + return new Date(new Date().getTime() + 24 * 60 * 60 * 1000); + } + +/** + * @function Возвращает новую оболочку, но уже с событиями, которые будут через день + * + * @return {BaseEvent} +*/ + BaseEvent.prototype.getEventAfterDay = function () { + var currentDate = addDay(new Date()); + return this.filter(function (event) { + return event.start.getTime() > currentDate.getTime(); + }); + }; + +/** + * @function Увеличивает дату на неделю + * @private + * + * @field {Date} currentDate дата, которую будем увеличивать + * + * @return {Date} + */ + var addWeek = function (currentDate) { + return new Date(currentDate.getTime() + 7 * 24 * 60 * 60 * 1000); + } + +/** + * @function Возвращает новую оболочку, но уже с событиями, которые будут через неделю + * + * @return {BaseEvent} +*/ + BaseEvent.prototype.getEventAfterWeek = function () { + var currentDate = addWeek(new Date()); + return this.filter(function (event) { + return event.start.getTime() > currentDate.getTime(); + }); + }; + +/** + * @function Увеличивает дату на месяц + * @private + * + * @field {Date} currentDate дата, которую будем увеличивать + * + * @return {Date} + */ + var addMonth = function (currentDate) { + if (currentDate.getMonth() === 11) { + currentDate = new Date(currentDate.getFullYear() + 1, 0, currentDate.getDay()); + } else { + currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, currentDate.getDay()); + } + return currentDate; + } + +/** + * @function Возвращает новую оболочку, но уже с событиями, которые будут через месяц + * + * @return {BaseEvent} +*/ + BaseEvent.prototype.getEventAfterMonth = function () { + var currentDate = addMonth(new Date()); + return this.filter(function (event) { + return event.start.getTime() > currentDate.getTime(); + }); + }; + +/** + * @function Возвращает новую оболочку, но уже с событиями, которые будут в определенный период + * + * @param {Date} fromDate - начала периода + * @param {Date} toDate - конец периода + * + * @return +*/ + BaseEvent.prototype.getEventFromPeriod = function (fromDate, toDate) { + return this.filter(function (event) { + return (event.start.getTime() > fromDate.getTime() && event.end.getTime() < toDate.getTime()); + }); + }; + +/** + * @function Компаратор рейтинга по убыванию + * @private + * + * @field {Date} a + * @field {Date} b + * + * @return {Numeric} + */ + var starsComparer = function compare(a, b) { + if (a.stars > b.stars) { + return -1; + } + if (a.stars < b.stars) { + return 1; + } + return 0; + }; + +/** + * @function Возвращает новую оболочку c теми же событиями, но отсортированными по уменьшению количества звезд + * + * @return {BaseEvent} +*/ + BaseEvent.prototype.sortByStars = function (ascending) { + + return this.sortBy(starsComparer, ascending); + }; + +/** + * @function Компаратор дат по возрастанию + * @private + * + * @field {Date} a + * @field {Date} b + * + * @return {Numeric} + */ + var dateComparer = function (a, b) { + if (a.start.getTime() < b.start.getTime()) { + return -1; + } + if (a.start.getTime() > b.start.getTime()) { + return 1; + } + return 0; + }; + +/** + * @function Возвращает новую оболочку c теми же событиями, но отсортированными по дате + * + * @return {BaseEvent} + */ + BaseEvent.prototype.sortByDate = function (ascending) { + + return this.sortBy(dateComparer, ascending); + }; +}(window)); \ No newline at end of file diff --git a/Task/Scripts/CalendaryErrorManager.js b/Task/Scripts/CalendaryErrorManager.js new file mode 100644 index 0000000..6c9ee6d --- /dev/null +++ b/Task/Scripts/CalendaryErrorManager.js @@ -0,0 +1,71 @@ +(function (toExport) { +/** + * @class Класс содержит обработчики ошибок при изменении элементов DOM связанных с календорем + * @augments DOMErrorManager + */ + + var CalendaryErrorManager = function (errorClassName) { + DOMErrorManager.call(this, errorClassName); + } + + toExport.CalendaryErrorManager = CalendaryErrorManager; + + CalendaryErrorManager.prototype = Object.create(DOMErrorManager.prototype, { + constructor: { + value: Event, + enumerable: false, + writable: true, + configurable: true + } + }); + +/** + * @function Обработчик ошибок объекта, содержащий начальное и конечное время + * + * @param {DOMdivElement} хранилище, содержащее таймер. + */ + CalendaryErrorManager.prototype.changeTime = function (timer) { + var textError = DOMValidator.isTimeInterval(timer); + this.changeTextError(timer, textError); + } + +/** + * @function Обработчик ошибок объекта, содержащий координаты + * + * @param {DOMdivElement} хранилище, содержащее координаты. + */ + CalendaryErrorManager.prototype.changeCoordinate = function (coordinates) { + var textError = DOMValidator.isCoordinate(coordinates); + this.changeTextError(coordinates, textError); + } + +/** + * @function Обработчик ошибок объекта, содержащий важныее данные + * + * @param {DOMdivElement} хранилище, содержащее важное поле. +*/ + CalendaryErrorManager.prototype.changeImportantStringField = function (importantStringField) { + var textError = DOMValidator.isImportantStringField(importantStringField, 5, 20); + this.changeTextError(importantStringField, textError); + } + +/** + * @function Обработчик ошибок объекта, содержащий поле с положительным числом + * + * @param {DOMdivElement} хранилище, содержащее поле с положительным числом. +*/ + CalendaryErrorManager.prototype.changePositiveNumber = function (positiveNumber) { + var textError = DOMValidator.isPositiveNumber(positiveNumber); + this.changeTextError(positiveNumber, textError); + } + +/** + * @function Обработчик ошибок объекта, содержащий поле с рейтингом + * + * @param {DOMdivElement} хранилище, содержащее поле с положительным числом. +*/ + CalendaryErrorManager.prototype.changeStars = function (stars) { + var textError = DOMValidator.isStars(stars); + this.changeTextError(stars, textError); + } +}(window)); \ No newline at end of file diff --git a/Task/Scripts/Collection.js b/Task/Scripts/Collection.js new file mode 100644 index 0000000..aed5a7c --- /dev/null +++ b/Task/Scripts/Collection.js @@ -0,0 +1,71 @@ +/*global Collection: true*/ +(function(toExport) { + "use strict"; +/** + * Создает оболочка для хранения массива объектов с операциями по извлечению более конкретных элементов + * @class Оболочка для храения массива объектов + * + * @param {Array} элементы коллекции +*/ + var Collection = function (otherItems) { + "use strict"; + var item; + this.items = []; + for (item in otherItems) { + if (otherItems.hasOwnProperty(item)) { + this.items.push(otherItems[item]); + } + } + }; + toExport.Collection = Collection; +/** + * @field {Collection} хранит ссылку на родной конструктор +*/ + Collection.prototype.constructor = Collection +/** + * @function создает новую коллекцию элементов с теме же элементами + с новым элементом obj + * + * @return {instanceof this.constructor} +*/ + Collection.prototype.add = function (obj) { + var newEvents = this.items.concat([obj]); + return new this.constructor(newEvents); + }; +/** + * @function создает новую коллекцию элементов с отфильтрованными элементами + * + * @param {Function} - делегат + * + * @return {instanceof this.constructor} +*/ + Collection.prototype.filter = function (selector) { + var newItems = this.items.filter(selector); + return new this.constructor(newItems); + }; +/** +* @function создает новую коллекцию элементов с теме же элементами + с новым элементом obj + * + * @param {Function} - компаратор + * @param {Function} - инвертировать, ли результат + * + * @return {instanceof this.constructor} +*/ + Collection.prototype.sortBy = function (comparator, isInvert) { + var newItems = [].concat(this.items); + if (newItems.length === 0) { + return []; + } + if (comparator) { + if (isInvert) { + newItems.sort(function (a, b) { + return -1 * comparator(a, b); + }); + } else { + newItems.sort(comparator); + } + } else { + newItems.sort(); + } + return new this.constructor(newItems); + }; +}(window)); \ No newline at end of file diff --git a/Task/Scripts/DOMErrorManager.js b/Task/Scripts/DOMErrorManager.js new file mode 100644 index 0000000..b06d7bd --- /dev/null +++ b/Task/Scripts/DOMErrorManager.js @@ -0,0 +1,72 @@ +(function (toExport) { + "use strict"; +/** + * @class * Класс содержит обработчики ошибок при изменении элементов DOM +*/ + var DOMErrorManager = function (errorClassName) { + this.errorClassName = errorClassName; + } + toExport.DOMErrorManager = DOMErrorManager; +/** + * @function Обработчик ошибок объекта, содержащий начальное и конечное время + * @param {DOMdivElement} хранилище, содержащее таймер. +*/ + DOMErrorManager.prototype.isFieldWithError = function (element) { + var lastElement = element.querySelector("."+this.errorClassName); + return !!lastElement; + } +/** + * @function Устанавливает в передаваемый элемент ошибку + * + * @param {DOMdivElement} element на что устанавливаем ошибку. + * @param {String} text текст ошибки. +*/ + DOMErrorManager.prototype.setTextError = function (element, text) { + var newError = document.createElement("span"), + newTextError = document.createTextNode(text); + newError.className = this.errorClassName; + newError.appendChild(newTextError); + element.appendChild(newError); + } +/** + * @function Стереть ошибку + * + * @param {DOMdivElement} element на что устанавливаем ошибку. +*/ + DOMErrorManager.prototype.removeTextError = function (element) { + var error = element.querySelector("."+this.errorClassName); + element.removeChild(error); + } +/** +* @function Изменить или стереть ошибку в зависимости от того есть она или нет +* +* @param {DOMdivElement} element хранилище элемента +* @param {DOMdivElement} element текст сообщения с ошибкой +*/ + DOMErrorManager.prototype.changeTextError = function (element, errorText) { + var currentErrorState = this.isFieldWithError(element); + if (errorText === "") { + if (currentErrorState) { + this.removeTextError(element); + } + } + else + { + if (currentErrorState) { + this.removeTextError(element); + } + this.setTextError(element, errorText); + } + } +/** +* @function Удалить всех детей элемента +* +* @param {DOMdivElement} хранилище детей +*/ + DOMErrorManager.prototype.removeAllChildren = function(element) { + var children = element.childNodes; + while(children.length) { + element.removeChild(children[0]) + } + } +}(window)); \ No newline at end of file diff --git a/Task/Scripts/DOMValidator.js b/Task/Scripts/DOMValidator.js new file mode 100644 index 0000000..7d42bbe --- /dev/null +++ b/Task/Scripts/DOMValidator.js @@ -0,0 +1,139 @@ +(function (toExport) { + "use strict"; +/** +* @function - проверяет является ли передаваемый объект Датой +* @private +* +* @param {Object} element - проверяемый объект +* +* @return {Boolean} +*/ + var isDate = function (element) { + return Event.prototype.dateValidator(element); + }; + +/** +* @function - проверяет является ли передаваемый объект числом +* @private +* +* @param {Object} element - проверяемый объект +* +* @return {Boolean} +*/ + var isNumeric = function(element) { + return !isNaN(parseFloat(element)); + }; + +/** +* @function - проверяет является ли передаваемый объект числом +* @private +* +* @param {Object} element - проверяемый объект +* +* @return {Boolean} +*/ + var isNumeric = function(element) { + return !isNaN(parseFloat(element)); + }; + +/** +* @namespace Пространство имен для DOM обработчиков ошибок +*/ + var DOMValidator = {}; + toExport.DOMValidator = DOMValidator; + +/** +* @field {Function} isTimeInterval - проверяет валидно ли поле содержащее промежуток времени +*/ + DOMValidator.isTimeInterval = function (divWithTimeInterval) { + var startDate = new Date(divWithTimeInterval.querySelector(".StartDate").value); + var finishDate = new Date(divWithTimeInterval.querySelector(".FinishDate").value); + + if (!isDate(startDate) && !isDate(finishDate)) + return "Обе даты некорректны"; + if (!isDate(startDate)) + return "Дата начала события некорректна"; + if (!isDate(finishDate)) + return "Дата конца события некорректна"; + if (startDate.getTime()> finishDate.getTime()) { + return "Даты перепутаны местами"; + } + return ""; + }; + +/** +@field {Function} isCoordinate - проверяет валидно ли поле содержащая координаты двух мерного пространства +*/ + DOMValidator.isCoordinate = function (divWithCoordinates) { + var xCoordinate = divWithCoordinates.querySelector(".XCoordinate").value; + var yCoordinate = divWithCoordinates.querySelector(".YCoordinate").value; + + if (!isNumeric(xCoordinate) && !isNumeric(yCoordinate)) { + return "Обе координаты некорректны"; + } + if (!isNumeric(xCoordinate)) { + return "Координата X - некорректна"; + } + if (!isNumeric(yCoordinate)) { + return "Координата Y - некорректна"; + } + return ""; + }; + +/** +* @field {Function} isTimeInterval - проверяет валидно ли поле содержащее промежуток времени +*/ + DOMValidator.isImportantStringField = function (divWithImportantStringField, minSize, maxSize) { + minSize = minSize || 0; + maxSize = maxSize || -1; + if (minSize < 0) { + minSize = 0; + } + if (minSize > maxSize) { + maxSize = -1; + } + var importantStringField = divWithImportantStringField.querySelector(".ImportantStringField").value; + if (maxSize != -1) { + if (minSize > importantStringField.length || maxSize < importantStringField.length) { + return "Поле должно содержать от " + minSize + " до " + maxSize + "символов"; + } + } + else { + if (minSize > importantStringField.length) { + return "Поле должно содержать как минимум " + minSize + " символов"; + } + } + return ""; + }; + +/** +* @field {Function} isStars - проверяет валидно ли поле содержащее рейтинг +*/ + DOMValidator.isStars = function (divWithStarField) { + var starsField = parseFloat(divWithStarField.querySelector(".StarsField").value); + if (isNaN(starsField)) { + return "В поле введено не число"; + } + if (starsField < 0 || starsField > 5) { + return "Количество звезд 0-5"; + } + if (parseInt(starsField) !== starsField) { + return "Количество звезд целое число"; + } + return ""; + }; + +/** +* @field {Function} isPositiveNumber - проверяет валидно ли поле содержащее целое положительное число +*/ + DOMValidator.isPositiveNumber = function (divWithPositiveNumberField) { + var positiveNumberField = divWithPositiveNumberField.querySelector(" .PositiveNumber").value; + if (!isNumeric(positiveNumberField)) { + return "В поле введено не число"; + } + if (parseFloat(positiveNumberField) < 0) { + return "В поле введено отрицательное число"; + } + return ""; + }; +}(window)); \ No newline at end of file diff --git a/Task/Scripts/Event.js b/Task/Scripts/Event.js new file mode 100644 index 0000000..d5d10b7 --- /dev/null +++ b/Task/Scripts/Event.js @@ -0,0 +1,142 @@ +/*global Model: true*/ +(function (toExport) { + "use strict"; +/** + * Создает новое событие в календаре + * @class Событие в календаре + * @augments Model + * + * @field {Number} - id Индификационный номер объекта по идее тут должен быть GUID + * @field {Object} - location объект содержащий локационные данные о событии + название события + * @field {Number} - реитинг + * @field {Number} - Цена посещения +*/ + var Event = function Event(data) { + "use strict"; + this.id = Math.random(); + this.location = { + "gps": {x: 0, y: 0}, + "nameLocation": "Earth" + }; + this.stars = 0; + this.cost = 0; + this.parties = []; + Model.call(this, data); + this.validate(this); + this.setLocation(this.location.gps, this.location.nameLocation); + this.stars = this.leaveMark(this.stars); + } + toExport.Event = Event; + Event.prototype = Object.create(Model.prototype, { + constructor: { + value: Event, + enumerable: false, + writable: true, + configurable: true + } + }); +/** + * @function Функция, проверяющая корректность даты + * + * @return {bool} +*/ + Event.prototype.dateValidator = function (date) { + "use strict"; + if (Object.prototype.toString.call(date) === "[object Date]") { + if (!isNaN(date.getTime())) { + return true; + } + } + return false; + }; +/** + * @function Проверяет на корректность координаты + * @private + * + * @field coordinate координаты в дву мерном пространстве + * @field name название события + * @filed {Boolean} +*/ + var isCorrectedCoordinate = function (coordinate, name) { + var isType = typeof coordinate !== "undefined" && typeof name === "string"; + if (!isType) { + return false; + } + var isX = typeof coordinate.x === "number"; + var isY = typeof coordinate.y === "number"; + return isX && isY; + } +/** + * @function set-ер установления локации события + * + * @field gps координаты в дву мерном пространстве + * @field название события +*/ + Event.prototype.setLocation = function (gps, name) { + + if (isCorrectedCoordinate(gps, name)) { + this.location.gps = gps; + this.location.nameLocation = name; + } else { + this.location = { + "gps" : {"x" : 0, "y" : 0}, + "nameLocation" : "Earth" + }; + } + }; +/** + * @function Коррекция значения рейтинга + * + * @return {Number} 0,1,2,3,4,5 +*/ + Event.prototype.leaveMark = function (stars) { + if (isNaN(parseFloat(stars)) || + !isFinite(stars) || + stars < 0) { + stars = 0; + } + if (stars > 5) { + stars = 5; + } + stars = Math.floor(stars); + return stars; + }; +/** + * @function Проверяет объект на корректность + * + * @field {Event} event - то что проверяем +*/ + Event.prototype.validate = function (event) { + if (event.cost < 0) { + throw new Error("Цена за вход не может быть отрицательной"); + } + if (!Array.isArray(event.parties)) { + throw new Error("Участники - это массив"); + } + var existsSomePartyWithoutNameField = event.parties.some(function (party) { + return !party.name; + }); + if (existsSomePartyWithoutNameField) { + throw new Error("У одного из участников нет поля <ИМЯ>"); + } + if (event.end < event.start) { + throw new Error("Даты начала и конца перепутаны"); + } + }; +/** + * @function Функция, печатающие значение локационных данных объекта + * + * @return {String} [location], (x, y) +*/ + Event.prototype.locationToString = function() { + return this.location.nameLocation + ", (" + this.location.gps.x + ";" + this.location.gps.y + ")"; + } +/** + * @function Функция, печатающие значение рейтинга в звездочках + * + * @return {String} ,*,**,***,****,***** +*/ + Event.prototype.starsToString= function() { + return new Array(this.stars + 1).join('*');; + } +}(window)); \ No newline at end of file diff --git a/Task/Scripts/Model.js b/Task/Scripts/Model.js new file mode 100644 index 0000000..4a8f174 --- /dev/null +++ b/Task/Scripts/Model.js @@ -0,0 +1,53 @@ +(function (toExport) { + "use strict"; +/** + * @class Абстрактный класс объектов ООП + * + * @param {data} - копируемый объект +*/ + var Model = function (data) { + var nameField; + for (nameField in data) { + this[nameField] = data[nameField]; + } + }; + toExport.Model = Model; + +/** + * @function setter + * + * @param {Object} - присваиваемый объект +*/ + Model.prototype.set = function (attributes) { + var nameAttr; + for (nameAttr in attributes) { + if (attributes.hasOwnProperty(nameAttr)) { + if (typeof this[nameAttr] !== "undefined") { + this[nameAttr] = attributes[nameAttr]; + } + } + } + }; + +/** + * @function getter + * + * @param {String} имя поля + * + * @return {Object} +*/ + Model.prototype.get = function (attribute) { + if (typeof attribute !== 'string' || typeof this[attribute] === "undefined") { + return; + } + return this[attribute]; + }; + +/** + * @function Проверяющая коррекцию объекта +*/ + Model.prototype.validate = function () { + "use strict"; + throw new Error('this is Abstract method'); + }; +}(window)); \ No newline at end of file diff --git a/Task/Scripts/calendary.js b/Task/Scripts/calendary.js new file mode 100644 index 0000000..1e7e1af --- /dev/null +++ b/Task/Scripts/calendary.js @@ -0,0 +1,254 @@ +/*global document: true*/ +/*global initTestBase: true*/ +/*global CalendaryErrorManager: true*/ +(function (toExport) { + "use strict"; +/** + * @namespace Пространство имен для календаря + * + * @field {EventFactory} объект, хранящий ссылки на inputы необходимые для создания нового события + * @field eventList ссылка на дом объект, хранящий список событий + * @field eventBase все события пользователя + * @field errorManager объект хранящий функции для валидации полей в дом и хранящий в себе некоторые тривиальные операции + * @field currentFilters фильтры наложенные на текущие события + */ + var Calendary = function () { + this.whois = "Alex.Mangin"; + this.EventFactory = { + "timer" : document.getElementById("NewEventTimeInterval"), + "nameLocation" : document.getElementById("NewEventNameLocation"), + "coordinate" : document.getElementById("NewEventCoordinate"), + "stars" : document.getElementById("NewEventStars"), + "cost" : document.getElementById("NewEventCost"), + "parties" : document.querySelector("#NewEventPartiesList ol") + }; + this.eventList = document.getElementById("eventList"); + this.eventBase = initTestBase(); + this.errorManager = new CalendaryErrorManager("Error"); + this.currentFilters = []; + } + toExport.Calendary = Calendary; +/** + * @function - функция, возвращающая текущую базу событий, но с наложенными фильтрами + * + * @param {[Function]} фильтры в виде функции + * + * @return {BaseEvent} +*/ + Calendary.prototype.ApplyFilter = function (filters) { + var base = this.eventBase, + i; + for (i = 0; i < filters.length; i = i + 1) { + base = filters[i].call(base); + } + return base; + } +/** + * @function - функция пытается создать событие из данных с формы +*/ + Calendary.prototype.CreateEvent = function () { + var parties = [], + partyList, + i, + eventDate, + inputs, + errors, + docfrag, + io, + input; + if (!this.isCorrecteNeedFields()) { + this.changeNeed(); + this.changeAddition(); + return + } + if (!this.isCorrecteAdditionFields()) { + if (!confirm('Некоторые незначительные поля некорректны, продолжить?')) { + this.changeAddition(); + return; + } + } + partyList = this.EventFactory.parties.querySelectorAll(" input"); + for ( i = 0; i < partyList.length; i = i + 1) { + if (partyList[i].value && partyList[i].value !== "") { + parties.push({"name" : partyList[i].value}); + } + } + eventDate = { + "id" : Math.random(), + "location" : { + "gps": { + "x": parseFloat(this.EventFactory.coordinate.querySelector(" .XCoordinate").value), + "y": parseFloat(this.EventFactory.coordinate.querySelector(" .YCoordinate").value) + }, + "nameLocation": this.EventFactory.nameLocation.querySelector("input").value, + }, + "stars" : parseFloat(this.EventFactory.stars.querySelector("input").value), + "cost" : parseFloat(this.EventFactory.cost.querySelector("input").value), + "start": new Date(this.EventFactory.timer.querySelector(".StartDate").value), + "end": new Date(this.EventFactory.timer.querySelector(".FinishDate").value), + "parties" : parties + } + if (DOMValidator.isCoordinate(this.EventFactory.coordinate)) { + eventDate.location.gps.x = 0; + eventDate.location.gps.y = 0; + } + if (DOMValidator.isStars(this.EventFactory.stars)) { + eventDate.stars = 0; + } + if (DOMValidator.isPositiveNumber(this.EventFactory.cost)) { + eventDate.cost = 0; + } + this.eventBase = this.eventBase.add(new Event(eventDate)); + inputs = document.querySelectorAll('#eventFactory input'); + for (i = 0; i < inputs.length; i = i + 1) { + if (inputs[i].type === "text" || inputs[i].type === "date") { + inputs[i].value = ""; + } + } + errors = document.querySelectorAll('#eventFactory .Error'); + for (i = 0; i < errors.length; i = i + 1) { + errors[i].parentElement.removeChild(errors[i]); + } + this.errorManager.removeAllChildren(this.EventFactory.parties); + docfrag = document.createDocumentFragment() + io = document.createElement("li"); + input = document.createElement("input"); + input.type = "text"; + io.appendChild(input); + for (i = 0; i < 3; i = i + 1) { + docfrag.appendChild(io.cloneNode(true)); + } + this.EventFactory.parties.appendChild(docfrag); + } +/** + * @function - функция обновляет отфильтрованный список со всеми наложенными фильтрами +*/ + Calendary.prototype.UpdateShowList = function () { + var createEventRow = function (number, event) { + var row = (function createRow() { + var rowTable = document.createElement("tr"), + cellTable = document.createElement("td"), + i; + for (i = 0; i < 7; i = i + 1) { + rowTable.appendChild(cellTable.cloneNode(false)); + } + return rowTable; + }()), + listParty, + n, + aDOMParty, + i; + row.children[0].appendChild(document.createTextNode(number)); + row.children[1].appendChild(document.createTextNode(event.locationToString())); + row.children[2].appendChild(document.createTextNode(event.starsToString())); + row.children[3].appendChild(document.createTextNode(event.start.toDateString())); + row.children[4].appendChild(document.createTextNode(event.end.toDateString())); + row.children[5].appendChild(document.createTextNode(event.cost + " $")); + listParty = document.createElement("select"); + for (i = 0; i < event.parties.length; i += 1) { + aDOMParty = document.createElement("option"); + aDOMParty.appendChild(document.createTextNode(event.parties[i].name)); + listParty.appendChild(aDOMParty); + } + if (event.parties.length) { + row.children[6].appendChild(listParty); + } + return row; + }, + newEventList, + currentBase, + i, + event; + this.errorManager.removeAllChildren(this.eventList); + newEventList = document.createDocumentFragment(); + currentBase = this.ApplyFilter(this.currentFilters); + for (i = 0; i < currentBase.items.length; i = i + 1) { + event = currentBase.items[i]; + newEventList.appendChild(createEventRow(i + 1, event)); + } + this.eventList.appendChild(newEventList); + } +/** + * @function функция вызывает обработчики ошибок необходимых полей +*/ + Calendary.prototype.changeNeed = function () { + this.errorManager.changeTime(this.EventFactory.timer); + this.errorManager.changeImportantStringField(this.EventFactory.nameLocation); + } +/** + * @function функция вызывает обработчики ошибок необязательных полей +*/ + Calendary.prototype.changeAddition = function () { + this.errorManager.changeCoordinate(this.EventFactory.coordinate); + this.errorManager.changePositiveNumber(this.EventFactory.cost); + this.errorManager.changeStars(this.EventFactory.stars); + } +/** + * @function функция проверяет корректность необходимых полей +*/ + Calendary.prototype.isCorrecteNeedFields = function () { + return DOMValidator.isTimeInterval(this.EventFactory.timer) === "" && + DOMValidator.isImportantStringField(this.EventFactory.nameLocation) === ""; + } +/** + * @function функция проверяет корректность дополнительных полей +*/ + Calendary.prototype.isCorrecteAdditionFields = function () { + return DOMValidator.isCoordinate(this.EventFactory.coordinate) === "" && + DOMValidator.isStars(this.EventFactory.stars) === "" && + DOMValidator.isPositiveNumber(this.EventFactory.cost) === ""; + } +/** + * @function функция добавляет дополнительное поле в коллекцию друзей + * @param {DIVdomElement} хранилище коллекции друзей +*/ + Calendary.prototype.addFriend = function (li) { + var newParty = document.createElement("li"), + input = document.createElement("input"); + input.type = "text"; + newParty.appendChild(input); + li.appendChild(newParty); + } +/** + * @function функция, обновляющая данные фильтра из DOM +*/ + Calendary.prototype.updateFilter = function () { + var filterRadios = document.querySelectorAll("#FilterEventList input[type = radio]"), + oldFilters = this.currentFilters, + newFilters = [], + i, + radioButton, + partys, + nonEmptyParty, + partyFilter; + for (i = 0; i < filterRadios.length; i = i + 1) { + radioButton = filterRadios[i]; + if (radioButton.checked && + radioButton.checked === true && + radioButton.value != "None") { + var nameFunc = radioButton.value.toString(); + newFilters.push(function() { + return this[nameFunc](); + }); + } + } + partys = document.querySelectorAll("#FilterFriens input"); + nonEmptyParty = []; + for (i = 0; i < partys.length; i = i + 1) { + if (partys[i].value != "") { + nonEmptyParty.push(partys[i].value); + } + } + partyFilter = function() { + var base = this, i + for (i = 0; i < nonEmptyParty.length; i = i + 1) { + base = base.withFriend({ + "name": nonEmptyParty[i] + }); + } + return base; + } + newFilters.push(partyFilter); + this.currentFilters = newFilters; + } +}(window)); \ No newline at end of file diff --git a/Task/Scripts/run.js b/Task/Scripts/run.js new file mode 100644 index 0000000..b5b85c7 --- /dev/null +++ b/Task/Scripts/run.js @@ -0,0 +1,42 @@ +/** + * + * + * @author Alex.Mangin + */ +(function (){ + var calendary = new Calendary(); + calendary.UpdateShowList(); + calendary.EventFactory.timer.addEventListener('blur', function() { + calendary.errorManager.changeTime(this); + }, true); + calendary.EventFactory.nameLocation.addEventListener('blur', function() { + calendary.errorManager.changeImportantStringField(this); + }, true); + calendary.EventFactory.coordinate.addEventListener('blur', function() { + calendary.errorManager.changeCoordinate(this); + }, true); + calendary.EventFactory.stars.addEventListener('blur', function() { + calendary.errorManager.changeStars(this); + }, true); + calendary.EventFactory.cost.addEventListener('blur', function() { + calendary.errorManager.changePositiveNumber(this); + }, true); + document.getElementById("SubmitNewEventButton").addEventListener('click', function() { + calendary.CreateEvent(); + calendary.UpdateShowList(); + }, false); + document.getElementById("AddFriend").addEventListener('click', function() { + calendary.addFriend(calendary.EventFactory.parties); + }, false); + var filterRadios =document.querySelectorAll("#FilterEventList input[type = radio]"); + for(var i = 0; i < filterRadios.length; i = i + 1) { + filterRadios[i].addEventListener('click', function() { + calendary.updateFilter(); + calendary.UpdateShowList(); + }) + } + document.getElementById("FIlterFreshPeopleList").addEventListener('blur', function() { + calendary.updateFilter(); + calendary.UpdateShowList(); + }, true); +}()); \ No newline at end of file diff --git a/Task/Scripts/test/BaseEventTest.html b/Task/Scripts/test/BaseEventTest.html new file mode 100644 index 0000000..be489e2 --- /dev/null +++ b/Task/Scripts/test/BaseEventTest.html @@ -0,0 +1,18 @@ + + +
+ + + + + + + + + +№ | +Что | +Звездочки ^^ | +Начало | +Конец | +Стоимость | +Участники | +
sdfkjsjdf | +Что | +Звездочки ^^ | +Начало | +Конец | +С | +Участники | +