diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74cee9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +/node_modules +/Server script/node_modules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b8ea4f6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "qunit"] + path = qunit + url = git://github.com/jquery/qunit.git +[submodule "jquery"] + path = jquery + url = git://github.com/jquery/jquery.git +[submodule "es5-shim"] + path = es5-shim + url = git://github.com/kriskowal/es5-shim.git diff --git a/Server script/GUID.js b/Server script/GUID.js new file mode 100644 index 0000000..bcf7a1d --- /dev/null +++ b/Server script/GUID.js @@ -0,0 +1,33 @@ +(function () { + "use strict"; + var randomHex4, + GUID; + /** + * @function генерирует случайный hex из четырех символов + * @private + * @return {String} + */ + randomHex4 = function () { + return Math.floor( + Math.random() * 0x10000 /* 65536 */ + ).toString(16); + }; + /** + * @namespaсe , содержащий инструменты для работы с GUID + */ + GUID = function () {}; + exports.GUID = GUID; + /** + * function генерирует guid в виде строки + * @return {String} + */ + GUID.createGUID = function () { + return ( + randomHex4() + randomHex4() + "-" + + randomHex4() + "-" + + randomHex4() + "-" + + randomHex4() + "-" + + randomHex4() + randomHex4() + randomHex4() + ); + }; +}()); diff --git a/Server script/ValidateEvent.js b/Server script/ValidateEvent.js new file mode 100644 index 0000000..a2b6002 --- /dev/null +++ b/Server script/ValidateEvent.js @@ -0,0 +1,16 @@ +(function () { + "use strict"; + var validate = function (event) { + //это можно сказать копи паст... + return !(typeof event.start === "undefined" || + typeof event.end === "undefined" || + typeof event.name === "undefined" || + typeof event.gps === "undefined" || + typeof event.gps.x === "undefined" || + typeof event.gps.y === "undefined" || + typeof event.stars === "undefined" || + typeof event.cost === "undefined" || + typeof event.parties === "undefined"); + }; + exports.validate = validate; +}()); diff --git a/Server script/main.js b/Server script/main.js new file mode 100644 index 0000000..f386ee7 --- /dev/null +++ b/Server script/main.js @@ -0,0 +1,74 @@ +(function () { + "use strict"; + var express = require('express'), + optimist = require('optimist').default('port', 8080).default('host', '127.0.0.1'), + color = require('colors'), + fs = require('fs'), + eventValidator = require('./validateEvent.js'), + guidModule = require('./guid.js'), + cfg, + app = express(), + printDate = function (date) { + return date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate(); + }; + app.use(express.static("../WebSite")); + app.use(express.bodyParser()); + + app.post("/saveNewEvent", function (req, res) { + console.log("Обработка запроса на добавления нового события в Бд....".green); + var newEventRequest = JSON.parse(req.param('request')), + newEvent; + if (!eventValidator.validate(newEventRequest)) { + console.log("Событие не валидное".red); + res.send({"request": false}); + console.log("Обработка закончена".green); + return; + } + console.log("Событие прошло валидацию"); + newEvent = { + "start": printDate(new Date(newEventRequest.start)), + "end": printDate(new Date(newEventRequest.end)), + "gps": { + "x": parseFloat(newEventRequest.gps.x), + "y": parseFloat(newEventRequest.gps.x) + }, + "name": newEventRequest.name, + "stars": newEventRequest.stars, + "cost": newEventRequest.cost, + "parties": newEventRequest.parties, + "id": guidModule.GUID.createGUID() + }; + console.log("Начинаем обновлять БД"); + console.log("Открываем бд"); + fs.readFile("../WebSite/base.json", "utf8", function (err, date) { + if (err) { + res.send({"request": false}); + console.log("Открыть бд не удалось".red); + console.log("Обработка закончена".red); + return; + } + console.log("Файл прочитался"); + + var eventList = JSON.parse(date); + console.log("Файл преобразовали в json"); + eventList.items.push(newEvent); + console.log("Сохраняем изменение в бд"); + fs.writeFile("../WebSite/base.json", JSON.stringify(eventList), "utf8", function (error) { + if (error) { + res.send({"request": false}); + console.log("Сохранить Бд не удалось".red); + console.log("Обработка закончена".red); + return; + } + console.log("Все удачно сохранилось"); + console.log("Обработка закончена".green); + res.send({"request": true, "id": newEvent.id}); + }); + }); + + }); + + cfg = optimist.argv; + app.listen(cfg.port, cfg.host); + console.log("Server up: ".green + cfg.host + ":" + cfg.port); +}()); \ No newline at end of file diff --git a/Server script/package.json b/Server script/package.json new file mode 100644 index 0000000..906b1a0 --- /dev/null +++ b/Server script/package.json @@ -0,0 +1,14 @@ +{ + "dependencies": { + "optimist": "*", + "express": "*", + "colors": "*" + }, + "name": "CalendarServer", + "version": "1.0.0", + "description": "Задание на dz8-nodejs", + "author": { + "name": "Mangin.Alex", + "email": "mywork.koder@gmail.com" + } +} \ No newline at end of file diff --git a/WebSite/Scripts/BaseEvent.js b/WebSite/Scripts/BaseEvent.js new file mode 100644 index 0000000..98b80b9 --- /dev/null +++ b/WebSite/Scripts/BaseEvent.js @@ -0,0 +1,162 @@ +/*global + Collection: false + */ +(function (toExport) { + "use strict"; + /** + * Создает оболочку над массивом событий, предоставляющую "sql" подобные операции + * + * @class Оболочка над массивом событий. + * @augments Collection + */ + var BaseEvent = function BaseEvent(events) { + Collection.call(this, events); + }, + starsComparer, + dateComparer; + + 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 Возвращает новую оболочку, но уже с событиями, которые будут в определенный период + * + * @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 Возвращает новую оболочку, но уже с событиями, которые стоят не меньше min и не больше max + * + * @param {Number} min - начала периода + * @param {Number} max - начала периода + * + * @return {BaseEvent} + */ + BaseEvent.prototype.getEventWithCost = function (min, max) { + return this.filter(function (event) { + return (event.cost >= min && event.cost <= max); + }); + }; + + /** + * @function Компаратор рейтинга по убыванию + * @private + * + * @field {Date} a + * @field {Date} b + * + * @return {Number} + */ + 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 {Number} + */ + 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/WebSite/Scripts/Calendar.js b/WebSite/Scripts/Calendar.js new file mode 100644 index 0000000..7009f7e --- /dev/null +++ b/WebSite/Scripts/Calendar.js @@ -0,0 +1,138 @@ +/*global + FactoryEvent: false, + FilterEventBase:false, + TableEventBase: false, + Event: false, + confirm:false, + alert:false, + Model:false, + $:false, + BaseEvent:false, + console:false + */ +(function (toExport) { + "use strict"; + /** + * + * @param {jQuery} $factory объект, содержащий фабрику событий + * @param {jQuery} $filter объект, содержащий различные фильтры + * @param {jQuery} $table + * @param {String} baseUrl + * @constructor контроллер каландаря + */ + var Calendar = function ($factory, $filter, $table, baseUrl) { + var cloneCalendar = this; + this.factory = new FactoryEvent($factory); + this.filter = new FilterEventBase($filter); + this.table = new TableEventBase($table); + + this.loadBase(baseUrl); + this.factory.WriteDefaultEvent(); + + this.factory.$container.find("input[type = text], input[type = date]").on("blur", function () { + cloneCalendar.factory.eventValidation(); + }); + + this.factory.$container.find("#SubmitNewEventButton").on("click", function () { + var eventObj = cloneCalendar.factory.readEvent(), + errors = Event.isValidate(eventObj), + isCritical = errors.some(function (element) { + return element.isCritical; + }); + cloneCalendar.factory.eventValidation(); + if (!isCritical) { + if (errors.length !== 0) { + if (confirm('Некоторые незначительные поля некорректны, продолжить?')) { + var defaultObj = new Event(), + field; + for(field in errors) { + if (errors.hasOwnProperty(field)) { + eventObj[field] = defaultObj[field]; + } + } + cloneCalendar.saveNewEvent("/saveNewEvent", eventObj); + } + } else { + cloneCalendar.saveNewEvent("/saveNewEvent", eventObj); + } + } + }); + //todo это гомосятина ! но почему то у input type = radio, нет события blur + this.filter.$container.find("input").on("click", function () { + var newBaseEvent = cloneCalendar.filter.apply(cloneCalendar.baseEvent); + cloneCalendar.table.updateTable(newBaseEvent); + }); + this.filter.$container.find("input").on("blur", function () { + var newBaseEvent = cloneCalendar.filter.apply(cloneCalendar.baseEvent); + cloneCalendar.table.updateTable(newBaseEvent); + }); + }; + toExport.Calendar = Calendar; + Calendar.prototype = Object.create(Model.prototype, { + constructor: { + value: Event, + enumerable: false, + writable: true, + configurable: true + } + }); + /** + * @function Загрузить по url базу событий + * @param baseUrl {String} url + * + */ + Calendar.prototype.loadBase = function (baseUrl) { + var calendar = this, + i, + events; + $.getJSON(baseUrl, function (date) { + events = []; + for (i = 0; i < date.items.length; i += 1) { + var item = date.items[i]; + events.push(new Event({ + "start": new Date(item.start), + "end": new Date(item.end), + "name": item.name, + "gps": { + "x": parseFloat(item.gps.x), + "y": parseFloat(item.gps.y) + }, + "cost": parseFloat(item.cost), + "stars": parseInt(item.stars, 10), + "parties": item.parties, + id: item.id + })); + } + calendar.baseEvent = new BaseEvent(events); + calendar.table.updateTable(calendar.baseEvent); + }); + }; + /** + * + * @param {String} saveUrl url + * @param {Object} newEvent новое событие похожий на Event. + * @function отправляет json с новым событием на сервер + */ + Calendar.prototype.saveNewEvent = function (saveUrl, newEvent) { + var cloneCalendar = this, + newBaseEvent; + $.post(saveUrl, {"request":JSON.stringify(newEvent)}) + .done(function (req, data) { + console.log(req); + if (req.request === true) { + newEvent.id = req.id; + cloneCalendar.baseEvent.items.push(new Event(newEvent)); + var newBaseEvent = cloneCalendar.filter.apply(cloneCalendar.baseEvent); + cloneCalendar.table.updateTable(newBaseEvent); + cloneCalendar.factory.WriteDefaultEvent(); + } else { + alert("Вы ввели не валидные данные... как вам это удалось. Позвоните в тех поддержку") + } + + }) + .fail(function () { + alert("Данные не сохранились. Позвоните в тех поддержку"); + console.log("Данные не сохранились"); + }); + }; +}(window)); \ No newline at end of file diff --git a/WebSite/Scripts/Collection.js b/WebSite/Scripts/Collection.js new file mode 100644 index 0000000..40e5e64 --- /dev/null +++ b/WebSite/Scripts/Collection.js @@ -0,0 +1,69 @@ +(function (toExport) { + "use strict"; +/** + * Создает оболочка для хранения массива объектов с операциями по извлечению более конкретных элементов + * @class Оболочка для храения массива объектов + * + * @param {Array} otherItems элементы коллекции +*/ + var Collection = function (otherItems) { + 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 {Object} instanceof this.constructor +*/ + Collection.prototype.add = function (obj) { + var newEvents = this.items.concat([obj]); + return new this.constructor(newEvents); + }; +/** + * @function создает новую коллекцию элементов с отфильтрованными элементами + * + * @param {Function} selector делегат + * + * @return {Object} instanceof this.constructor +*/ + Collection.prototype.filter = function (selector) { + var newItems = this.items.filter(selector); + return new this.constructor(newItems); + }; +/** +* @function создает новую коллекцию элементов с теме же элементами + с новым элементом obj + * + * @param {Function} comparator компаратор + * @param {Boolean} isInvert инвертировать, ли результат + * + * @return {Object} 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/WebSite/Scripts/Event.js b/WebSite/Scripts/Event.js new file mode 100644 index 0000000..1f4059c --- /dev/null +++ b/WebSite/Scripts/Event.js @@ -0,0 +1,192 @@ +/*global Model: false, +GUID:false, + FieldsError:false, + $:false, + window:false +*/ + +(function (toExport) { + "use strict"; + +/** + * Создает новое событие в календаре + * @class Событие в календаре + * @augments Model + * + * @field {String} - id в виде GUID + * @field {Object} - location объект содержащий локационные данные о событии + название события + * @field {Number} - реитинг + * @field {Number} - Цена посещения +*/ + var Event = function Event(data) { + if (typeof data === "undefined") { + toExport.Model.call(this, { + "start": new Date(), + "end" : new Date(), + "gps" : { + "x": 0, + "y": 0 + }, + "name" : "Что-то", + "stars" : 0, + "cost" : 0, + "parties" : [], + "id": "Empty" + }); + return this; + } + var eventDefaultVal = new Event(), + newEvent = this; + toExport.Model.call(this, eventDefaultVal); + toExport.Model.call(this, data); + Event.isValidate(data).forEach(function (error) { + newEvent[error.who] = eventDefaultVal[error.who]; + }); + return this; + }, + dateValidator, + isValidateTimeInterval, + isValidateGps, + isValidateName, + isValidateStars, + isValidateCost; + + toExport.Event = Event; + + Event.prototype = Object.create(toExport.Model.prototype, { + constructor: { + value: Event, + enumerable: false, + writable: true, + configurable: true + } + }); + +/** + * @function Функция, проверяющая корректность даты + * + * @return {Boolean} +*/ + dateValidator = function (date) { + if (Object.prototype.toString.call(date) === "[object Date]") { + if (!isNaN(date.getTime())) { + return true; + } + } + return false; + }; +/** + * + * @function проверяет корректный ли параметр интервала времени события + * @param {Object} event + * @return {Array} сообщения с ошибками + */ + isValidateTimeInterval = function (event) { + var errors = []; + if (!event.start || !dateValidator(event.start)) { + errors.push(new FieldsError("start", "Дата начала события задана не корректно", true)); + } + if (!event.end || !dateValidator(event.end)) { + errors.push(new FieldsError("end", "Дата конца событий задана не корректно", true)); + } + if (errors.length === 0) { + if (event.start.getTime() > event.end.getTime()) { + errors.push(new FieldsError("start", "Дата начала и конца события перепутаны местами", true)); + errors.push(new FieldsError("end", "Дата начала и конца события перепутаны местами", true)); + } + } + + return errors; + }; +/** + * + * @function проверяет корректный ли параметры содержащие координаты события + * @param {Object} event + * @return {Array} сообщения с ошибками + */ + isValidateGps = function (event) { + var errors = []; + + if (typeof event.gps === "undefined" || !$.isNumeric(event.gps.x)) { + errors.push(new FieldsError("gps.x", "Координата X - не число")); + } + if (typeof event.gps === "undefined" || !$.isNumeric(event.gps.y)) { + errors.push(new FieldsError("gps.y", "Координата Y - не число")); + } + return errors; + }; +/** + * + * @function проверяет корректный ли параметр названия события + * @param {Object} event + * @return {Array} сообщения с ошибками + */ + isValidateName = function (event) { + var errors = []; + if (event.name === "undefined" || event.name.length === 0 || event.name.length > 100) { + errors.push(new FieldsError("name", "Название события должно содержать имя от 1 до 100 символов", true)); + } + return errors; + }; +/** + * + * @function проверяет корректный ли параметр рейтинга события + * @param {Object} event + * @return {Array} сообщения с ошибками + */ + isValidateStars = function (event) { + var errors = []; + if (typeof event.stars === "undefined" || !$.isNumeric(event.stars)) { + errors.push(new FieldsError("stars", "В поле должно быть введено число")); + } else { + if (event.stars < 0 || event.stars > 5) { + errors.push(new FieldsError("stars", "Количество звезд от 0 до 5")); + } else { + if (Math.floor(event.stars) !== event.stars) { + errors.push(new FieldsError("stars", "Количество звезд целое число")); + } + } + } + return errors; + }; +/** + * + * @function проверяет корректный ли параметр стоимости посещения + * @param {Object} event + * @return {Array} сообщения с ошибками + */ + isValidateCost = function (event) { + var errors = []; + if (typeof event.cost === "undefined" || !$.isNumeric(event.cost)) { + errors.push(new FieldsError("cost", "В этом поле должно быть число")); + } else { + if (event.cost < 0) { + errors.push(new FieldsError("cost", "Цена за участие не может быть отрицательной")); + } + } + return errors; + }; +/** + * @function Проверяет объект на корректность + * + * @field {Object} event - то что проверяемый + * @return {Array} сообщение с ошибками. +*/ + Event.isValidate = function (event) { + return [].concat( + isValidateTimeInterval(event), + isValidateName(event), + isValidateGps(event), + isValidateCost(event), + isValidateStars(event) + ); + }; +/** + * @function Функция, печатающие значение рейтинга в звездочках + * + * @return {String} ,*,**,***,****,***** +*/ + Event.prototype.starsToString = function () { + return new Array(this.stars + 1).join('*'); + }; +}(window)); \ No newline at end of file diff --git a/WebSite/Scripts/FactoryEvent.js b/WebSite/Scripts/FactoryEvent.js new file mode 100644 index 0000000..df3317c --- /dev/null +++ b/WebSite/Scripts/FactoryEvent.js @@ -0,0 +1,112 @@ +/*global +$: false, +Event: false, + Model: false + */ +(function (toExport) { + "use strict"; + /** + * + * @param $container + * @constructor создать объект работающий с фабрикой событий расположенных на странице + */ + var FactoryEvent = function ($container) { + this.$container = $container; + this.$startEvent = this.$container.find(".EventsStartDate").eq(0); + this.$endEvent = this.$container.find(".EventsEndDate").eq(0); + this.$name = this.$container.find(".EventsName").eq(0); + this.$x = this.$container.find(".EventsX").eq(0); + this.$y = this.$container.find(".EventsY").eq(0); + this.$stars = this.$container.find(".EventsStars").eq(0); + this.$cost = this.$container.find(".EventsCost").eq(0); + }; + + toExport.FactoryEvent = FactoryEvent; + + /** + * @function - функция читает {Event} элемент из фибричных полей + * @return {Object} + */ + FactoryEvent.prototype.readEvent = function () { + var parties = []; + this.$container.find(".EventsParty").each(function () { + var party = $.trim($(this).val()); + if (party !== "") { + parties.push(party); + } + }); + return { + "start": new Date(this.$startEvent.val()), + "end": new Date(this.$endEvent.val()), + "name": this.$name.val(), + "gps": { + "x": parseFloat(this.$x.val()), + "y": parseFloat(this.$y.val()) + }, + "stars": parseFloat(this.$stars.val()), + "cost": parseInt(this.$cost.val(), 10), + "parties": parties + }; + }; + + /** + * @function - функция пишет в поля фабрики Default поля + */ + FactoryEvent.prototype.WriteDefaultEvent = function () { + var defaultEvent = new Event(); + this.$container.each(function () { + $(this).attr("value", ""); + }); + + this.$startEvent.val(Model.printDate(defaultEvent.start)); + this.$endEvent.val(Model.printDate(defaultEvent.end)); + this.$name.val(defaultEvent.name); + this.$x.val(defaultEvent.gps.x); + this.$y.val(defaultEvent.gps.y); + this.$stars.val(defaultEvent.stars); + this.$cost.val(defaultEvent.cost); + }; + /** + * @function проверяет объект созданный фабрикой, активизирует валидаторы + */ + FactoryEvent.prototype.eventValidation = function () { + var eventObj = this.readEvent(), + errors = Event.isValidate(eventObj), + fieldToDomElementHash = { + "start": this.$startEvent, + "end": this.$endEvent, + "name": this.$name, + "gps.x": this.$x, + "gps.y": this.$y, + "stars": this.$stars, + "cost": this.$cost + }, + fieldToDOM, + $validator; + for (fieldToDOM in fieldToDomElementHash) { + if (fieldToDomElementHash.hasOwnProperty(fieldToDOM)) { + $validator = fieldToDomElementHash[fieldToDOM].parents().eq(0).next(); + $validator.empty(); + $validator.removeClass("CriticalError"); + $validator.removeClass("Error"); + $validator.addClass("Valid"); + } + } + + $.each(errors, function (index, erorr) { + var $DOMObject = fieldToDomElementHash[erorr.nameField], + $validator = $DOMObject.parents().eq(0).next(); + if ($validator.children().length === 0) { + $validator.removeClass("Valid"); + if (erorr.isCritical === true) { + $validator.addClass("CriticalError"); + } else { + $validator.addClass("Error"); + } + $("", { + "text": erorr.message + }).appendTo($validator); + } + }); + }; +}(window)); \ No newline at end of file diff --git a/WebSite/Scripts/FieldError.js b/WebSite/Scripts/FieldError.js new file mode 100644 index 0000000..bc73f7e --- /dev/null +++ b/WebSite/Scripts/FieldError.js @@ -0,0 +1,23 @@ +/** + * Created with JetBrains WebStorm. + * User: painandfear + * Date: 11/27/12 + * Time: 9:53 PM + * To change this template use File | Settings | File Templates. + */ +(function (toExport) { + "use strict"; + /** + * + * @constructor {FieldError} - объект с сообщение о том что поле содержит не корректную информацию + * @field {nameField} - имя поля в котором возникла ошибка + * @field {isCritical} - критично ли поле или можно заполнить default значением + * @field {message} - дружелюбное сообщение для пользователя + */ + + toExport.FieldsError = function (nameField, message, isCritical) { + this.nameField = nameField || "errorField"; + this.message = message || ""; + this.isCritical = isCritical || false; + }; +}(window)); \ No newline at end of file diff --git a/WebSite/Scripts/FilterEvent.js b/WebSite/Scripts/FilterEvent.js new file mode 100644 index 0000000..50db1e9 --- /dev/null +++ b/WebSite/Scripts/FilterEvent.js @@ -0,0 +1,122 @@ +/*global +$:false, +console:false + */ +(function (toExport) { + "use strict"; + var FilterEventBase = function ($container) { + this.$container = $container; + }, + readFilter = function ($container) { + var timeStatusFilter = $container.find('[name = "TimeStatusFilter"]').filter("input:checked").val(), + costFilter = $container.find('[name = "CostFilter"]').filter("input:checked").val(), + sortFilter = $container.find('[name = "Sort"]').filter("input:checked").val(); + return [timeStatusFilter, + costFilter, + sortFilter + ]; + }; + /** + * + * @function применить группу фильтров к базе base, содержащей события календаря + * @param {BaseEvent} base база к которой применится группа фильтров + * @return {BaseEvent} база с примененными фильтрами + */ + FilterEventBase.prototype.apply = function (base) { + var filters = readFilter(this.$container), + newBase = base, + filterModule = this; + $.each(filters, function (i, filter) { + if (filterModule[filter]) { + newBase = filterModule[filter](newBase); + } else { + console.log("Фильтр не обнаружен: " + filter); + } + }); + return newBase; + }; + /** + * + * @function фильтр пустышка + * @param {BaseEvent} base база к которой применится фильтр + * @return {BaseEvent} + */ + FilterEventBase.prototype.none = function (base) { + return base; + }; + /** + * + * @function фильтр, возрвщающий прощедщие события + * @param {BaseEvent} base база к которой применится фильтр + * @return {BaseEvent} + */ + FilterEventBase.prototype.past = function (base) { + return base.pastEventBase(); + }; + /** + * + * @function фильтр, возрвщающий текущие события + * @param {BaseEvent} base база к которой применится фильтр + * @return {BaseEvent} + */ + FilterEventBase.prototype.now = function (base) { + return base.nowEventBase(); + }; + /** + * + * @function фильтр, возрвщающий грядущие события + * @param {BaseEvent} base база к которой применится фильтр + * @return {BaseEvent} + */ + FilterEventBase.prototype.next = function (base) { + return base.nextEventBase(); + }; + /** + * + * @function фильтр, возрвщающий события в определенный период + * @param {BaseEvent} base база к которой применится фильтр + * @return {BaseEvent} + */ + FilterEventBase.prototype.period = function (base) { + var startDate = new Date(this.$container.find(".StartDate").eq(0).val()), + finishDate = new Date(this.$container.find(".EndDate").eq(0).val()); + if (startDate.getTime() <= finishDate.getTime()) { + return base.getEventFromPeriod(startDate, finishDate); + } + return base; + }; + /** + * + * @function фильтр, возрвщающий события определенной стоимости + * @param base + * @return {BaseEvent} + */ + FilterEventBase.prototype.minMaxCost = function (base) { + var minCost = parseFloat(this.$container.find(".MinCost").eq(0).val()), + maxCost = parseFloat(this.$container.find(".MaxCost").eq(0).val()); + if (minCost <= maxCost) { + return base.getEventWithCost(minCost, maxCost); + } + return base; + }; + /** + * + * @function фильтр, сортирует по звездам + * @param base + * @return {BaseEvent} + */ + FilterEventBase.prototype.stars = function (base) { + return base.sortByStars(false); + }; + /** + * + * @function фильтр, сортирует по дате + * @param base + * @return {BaseEvent} + */ + FilterEventBase.prototype.date = function (base) { + return base.sortByDate(false); + }; + + toExport.FilterEventBase = FilterEventBase; +}(window)); \ No newline at end of file diff --git a/WebSite/Scripts/Model.js b/WebSite/Scripts/Model.js new file mode 100644 index 0000000..d290db5 --- /dev/null +++ b/WebSite/Scripts/Model.js @@ -0,0 +1,71 @@ +/*global +$: false, + window: false + */ +(function (toExport) { + "use strict"; +/** + * @class Абстрактный класс объектов ООП + * + * @param {Object} data копируемый объект +*/ + var Model = function (data) { + var nameField; + for (nameField in data) { + if (data.hasOwnProperty(nameField)) { + this[nameField] = data[nameField]; + } + } + }, + printDate; + toExport.Model = Model; + +/** + * @function setter + * + * @param {Object} attributes присваиваемый объект +*/ + 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} attribute имя поля + * + * @return {Object} +*/ + Model.prototype.get = function (attribute) { + if (typeof attribute !== 'string' || typeof this[attribute] === "undefined") { + return undefined; + } + return this[attribute]; + }; + + /** + * @function записать даты в нормальном виде + * @param {Date} date + */ + if (toExport.$.browser.msie) { + printDate = function (date) { + return date.getFullYear().toString() + "/" + date.getMonth().toString() + "/" + date.getDate().toString(); + }; + } else { + printDate = function (date) { + var month = date.getMonth(), + day = date.getDate(); + month = (month < 10) ? "0" + month.toString() : month.toString(); + day = (day < 10) ? "0" + day.toString() : day.toString(); + return date.getFullYear() + "-" + month + "-" + day; + }; + } + Model.printDate = printDate; +}(window)); \ No newline at end of file diff --git a/WebSite/Scripts/Polyfill.js b/WebSite/Scripts/Polyfill.js new file mode 100644 index 0000000..746a654 --- /dev/null +++ b/WebSite/Scripts/Polyfill.js @@ -0,0 +1,107 @@ +(function () { + "use strict"; + /** + * Полифил Object.create + * @source https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create + */ + if (typeof Object.create !== 'function') { + Object.create = function (o, props) { + var prop; + function F() {} + F.prototype = o; + if (typeof props === "object") { + for (prop in props) { + if (props.hasOwnProperty((prop))) { + F[prop] = props[prop]; + } + } + } + return new F(); + }; + } + /** + * Полифил Array.forEach + * @source https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach + */ + if (!Array.prototype.forEach) { + Array.prototype.forEach = function forEach( callback, thisArg) { + var T, k, O, len; + if (this === null) { + throw new TypeError("this is null or not defined"); + } + O = Object(this); + len = O.length >>> 0; + if ({}.toString.call(callback) !== "[object Function]") { + throw new TypeError(callback + " is not a function"); + } + if (thisArg) { + T = thisArg; + } + k = 0; + while (k < len) { + var kValue; + if (Object.prototype.hasOwnProperty.call(O, k)) { + kValue = O[k]; + callback.call(T, kValue, k, O); + } + k += 1; + } + }; + } + /** + * Полифил Array.some + * @source https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some + */ + if (!Array.prototype.some) { + Array.prototype.some = function (fun) { + if (this === null) { + throw new TypeError(); + } + var t = Object(this), + len = t.length >>> 0, + thisp, + i; + if (typeof fun !== "function") { + throw new TypeError(); + } + thisp = arguments[1]; + for (i = 0; i < len; i += 1) { + if (i in t && fun.call(thisp, t[i], i, t)) { + return true; + } + } + return false; + }; + } + /** + * Полифил Array.some + * @source https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter + */ + if (!Array.prototype.filter) { + Array.prototype.filter = function (fun) { + if (this === null) { + throw new TypeError(); + } + var t = Object(this), + len = t.length >>> 0, + res, + thisp, + i, + val; + if (typeof fun !== "function") { + throw new TypeError(); + } + res = []; + thisp = arguments[1]; + for (i = 0; i < len; i += 1) { + if (i in t) { + val = t[i]; // in case fun mutates this + if (fun.call(thisp, val, i, t)) { + res.push(val); + } + } + } + return res; + }; + } +}()); \ No newline at end of file diff --git a/WebSite/Scripts/Run.js b/WebSite/Scripts/Run.js new file mode 100644 index 0000000..fa1a286 --- /dev/null +++ b/WebSite/Scripts/Run.js @@ -0,0 +1,6 @@ +$(function (){ + var $eventFactory = $("#EventFactory"); + var $eventFilter = $("#EventFilter"); + var $eventTable = $("#eventList"); + var calendar = new Calendar($eventFactory, $eventFilter, $eventTable, "./base.json"); + }); \ No newline at end of file diff --git a/WebSite/Scripts/TableEventBase.js b/WebSite/Scripts/TableEventBase.js new file mode 100644 index 0000000..e1b1ad4 --- /dev/null +++ b/WebSite/Scripts/TableEventBase.js @@ -0,0 +1,61 @@ +/*global + tmpl: false, + Model: false, + $: false +*/ +(function (toExport) { + "use strict"; + var TableEventBase = function ($container) { + this.$container = $container; + }, + rowTable = '
№ | +Начало | +Конец | +Что | +X | +Y | +Звездочки | +Цена | +Участники | +
№ | +Начало | +Конец | +Что | +X | +Y | +Звездочки | +Цена | +Участники | +