temp.coffee 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. # обязательно подключение глобальных массивов
  2. globalThis.renderFns = require 'pug.json'
  3. globalThis.stylFns = require 'styl.json'
  4. # подключение мета информации (строго в данном фиде)
  5. document.head.insertAdjacentHTML 'beforeend','<meta charset="UTF-8">'
  6. document.head.insertAdjacentHTML 'beforeend','<meta name="viewport" content="width=device-width, initial-scale=1.0">'
  7. document.head.insertAdjacentHTML('beforeend','<title> Кохи Борбад - Концертный зал Душанбе</title>')
  8. # Настройка tailwind
  9. tailwind.config = require 'tailwind.config.js'
  10. # подключение основных стилей
  11. ## tailwind
  12. document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="main">'+stylFns['main.css']+'</style>')
  13. ## базовой стиль приложения
  14. document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="root">'+stylFns['app/temp.styl']+'</style>')
  15. # Создаем глобальную шину событий
  16. class AppEventBus
  17. constructor: ->
  18. @events = {}
  19. on: (event, callback) ->
  20. if !@events[event]
  21. @events[event] = []
  22. @events[event].push(callback)
  23. emit: (event, data) ->
  24. if @events[event]
  25. for callback in @events[event]
  26. try
  27. callback(data)
  28. catch error
  29. debug.log "Event bus error: " + error
  30. off: (event, callback) ->
  31. if @events[event]
  32. @events[event] = @events[event].filter (cb) -> cb != callback
  33. # Создаем глобально
  34. globalThis.EventBus = new AppEventBus()
  35. # Класс для работы с мультиязычными данными
  36. class MultilingualData
  37. constructor: ->
  38. @currentLanguage = 'ru'
  39. @availableLanguages = ['ru', 'en', 'tj']
  40. @fallbackLanguage = 'ru'
  41. # Установка текущего языка
  42. setLanguage: (language) ->
  43. if language in @availableLanguages
  44. @currentLanguage = language
  45. EventBus.emit('language_changed', language)
  46. debug.log "Язык изменен на: "+language
  47. # Получение текста для текущего языка
  48. getText: (textArray, fallback = '') ->
  49. if not textArray or not Array.isArray(textArray)
  50. return fallback
  51. languageIndex = @availableLanguages.indexOf(@currentLanguage)
  52. if languageIndex is -1 or languageIndex >= textArray.length
  53. languageIndex = @availableLanguages.indexOf(@fallbackLanguage)
  54. return textArray[languageIndex] or fallback
  55. # Получение всех текстов для отладки
  56. getAllTexts: (textArray) ->
  57. if not textArray or not Array.isArray(textArray)
  58. return {}
  59. result = {}
  60. for lang, index in @availableLanguages
  61. if textArray[index]
  62. result[lang] = textArray[index]
  63. return result
  64. # Класс для работы с базой данных
  65. class AppDatabase
  66. constructor: ->
  67. @db = null
  68. @baseUrl = 'https://oleg:631074@couchdb.favt.ru.net'
  69. @dbName = 'borbad_events'
  70. @initialized = false
  71. @multilingual = new MultilingualData()
  72. # Инициализация базы данных
  73. initialize: ->
  74. try
  75. PouchDB = require 'pouchdb'
  76. @db = new PouchDB(@baseUrl+"/"+@dbName)
  77. # Проверяем соединение
  78. info = await @db.info()
  79. debug.log "База данных подключена: "+info.db_name
  80. @initialized = true
  81. EventBus.emit('database_ready', @db)
  82. catch error
  83. debug.log "Ошибка подключения к базе данных: "+error
  84. EventBus.emit('database_error', error)
  85. # Получение документов по типу с мультиязычной обработкой
  86. getDocumentsByType: (type, options = {}) ->
  87. if not @initialized
  88. throw new Error("База данных не инициализирована")
  89. try
  90. viewName = options.view or 'published_by_domain_language'
  91. designDoc = options.designDoc or 'multilingual_content'
  92. result = await @db.query(designDoc+'/'+viewName, {
  93. startkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage]
  94. endkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, {}]
  95. include_docs: true
  96. descending: options.descending or true
  97. limit: options.limit or 10
  98. skip: options.skip or 0
  99. })
  100. # Обрабатываем мультиязычные данные
  101. processedDocs = result.rows.map (row) =>
  102. @processMultilingualDocument(row.doc)
  103. return processedDocs
  104. catch error
  105. debug.log "Ошибка получения документов типа "+type+": "+error
  106. return []
  107. # Обработка мультиязычного документа
  108. processMultilingualDocument: (doc) ->
  109. if not doc
  110. return null
  111. processedDoc = Object.assign({}, doc)
  112. # Обрабатываем мультиязычные поля
  113. multilingualFields = ['title', 'content', 'excerpt', 'author', 'name', 'description', 'location']
  114. for field in multilingualFields
  115. if doc[field] and Array.isArray(doc[field])
  116. processedDoc[field] = @multilingual.getText(doc[field])
  117. # Обрабатываем вложенные мультиязычные структуры
  118. if doc.seo
  119. processedDoc.seo = {}
  120. if doc.seo.description and Array.isArray(doc.seo.description)
  121. processedDoc.seo.description = @multilingual.getText(doc.seo.description)
  122. if doc.seo.title and Array.isArray(doc.seo.title)
  123. processedDoc.seo.title = @multilingual.getText(doc.seo.title)
  124. if doc.seo.keywords and Array.isArray(doc.seo.keywords)
  125. processedDoc.seo.keywords = @multilingual.getText(doc.seo.keywords, [])
  126. # Обрабатываем специфичные данные для разных типов
  127. if doc.type is 'event' and doc.event_data
  128. processedDoc.event_data = Object.assign({}, doc.event_data)
  129. if doc.event_data.location and Array.isArray(doc.event_data.location)
  130. processedDoc.event_data.location = @multilingual.getText(doc.event_data.location)
  131. if doc.event_data.price and Array.isArray(doc.event_data.price)
  132. processedDoc.event_data.price = @multilingual.getText(doc.event_data.price)
  133. if doc.type is 'product' and doc.product_data
  134. processedDoc.product_data = Object.assign({}, doc.product_data)
  135. if doc.product_data.price and Array.isArray(doc.product_data.price)
  136. processedDoc.product_data.price = @multilingual.getText(doc.product_data.price)
  137. if doc.type is 'slide' and doc.slide_data
  138. processedDoc.slide_data = Object.assign({}, doc.slide_data)
  139. if doc.slide_data.button_text and Array.isArray(doc.slide_data.button_text)
  140. processedDoc.slide_data.button_text = @multilingual.getText(doc.slide_data.button_text)
  141. if doc.slide_data.button_link and Array.isArray(doc.slide_data.button_link)
  142. processedDoc.slide_data.button_link = @multilingual.getText(doc.slide_data.button_link)
  143. return processedDoc
  144. # Получение блог постов
  145. getBlogPosts: (options = {}) ->
  146. options.type = 'blog_post'
  147. return await @getDocumentsByType('blog_post', options)
  148. # Получение мероприятий
  149. getEvents: (options = {}) ->
  150. options.type = 'event'
  151. options.view = 'by_date_multilingual'
  152. return await @getDocumentsByType('event', options)
  153. # Получение предстоящих мероприятий
  154. getUpcomingEvents: (options = {}) ->
  155. options.type = 'event'
  156. options.view = 'upcoming_events'
  157. return await @getDocumentsByType('event', options)
  158. # Получение товаров
  159. getProducts: (options = {}) ->
  160. options.type = 'product'
  161. options.view = 'by_status_multilingual'
  162. return await @getDocumentsByType('product', options)
  163. # Получение слайдов
  164. getSlides: (options = {}) ->
  165. options.type = 'slide'
  166. options.view = 'active_ordered_multilingual'
  167. options.descending = false
  168. return await @getDocumentsByType('slide', options)
  169. # Получение категорий
  170. getCategories: (options = {}) ->
  171. if not @initialized
  172. throw new Error("База данных не инициализирована")
  173. try
  174. result = await @db.query('categories_hierarchical_multilingual/by_level_multilingual', {
  175. startkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, 0]
  176. endkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, 0, {}]
  177. include_docs: true
  178. ascending: true
  179. })
  180. processedCategories = result.rows.map (row) =>
  181. @processMultilingualDocument(row.doc)
  182. return processedCategories
  183. catch error
  184. debug.log "Ошибка получения категорий: "+error
  185. return []
  186. # Поиск по контенту
  187. searchContent: (query, options = {}) ->
  188. if not @initialized
  189. throw new Error("База данных не инициализирована")
  190. try
  191. result = await @db.query('universal_multilingual_search/content_search', {
  192. startkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, query.toLowerCase()]
  193. endkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, query.toLowerCase() + "\ufff0"]
  194. include_docs: true
  195. limit: options.limit or 20
  196. })
  197. processedResults = result.rows.map (row) =>
  198. @processMultilingualDocument(row.doc)
  199. return processedResults
  200. catch error
  201. debug.log "Ошибка поиска: "+error
  202. return []
  203. # Получение документа по ID
  204. getDocumentById: (id) ->
  205. if not @initialized
  206. throw new Error("База данных не инициализирована")
  207. try
  208. doc = await @db.get(id)
  209. return @processMultilingualDocument(doc)
  210. catch error
  211. debug.log "Ошибка получения документа "+id+": "+error
  212. return null
  213. # Создаем глобальные экземпляры
  214. globalThis.AppDB = new AppDatabase()
  215. globalThis.Multilingual = new MultilingualData()
  216. # Маршруты
  217. routes = [
  218. { path: '/', component: require 'app/pages/Home' }
  219. { path: '/events', component: require 'app/pages/Events' }
  220. { path: '/events/:id', component: require 'app/pages/EventDetail' }
  221. # { path: '/blog', component: require 'app/pages/Blog' }
  222. # { path: '/blog/:id', component: require 'app/pages/BlogDetail' }
  223. # { path: '/products', component: require 'app/pages/Products' }
  224. # { path: '/products/:id', component: require 'app/pages/ProductDetail' }
  225. { path: '/about', component: require 'app/pages/About' }
  226. { path: '/contacts', component: require 'app/pages/Contacts' }
  227. ]
  228. # Глобальное определение vuejs приложения
  229. app = Vue.createApp
  230. name: 'app'
  231. data: ()->
  232. return
  233. dbReady: false
  234. loading: true
  235. error: null
  236. currentLanguage: 'ru'
  237. availableLanguages: ['ru', 'en', 'tj']
  238. # Глобальное состояние приложения
  239. appState:
  240. slides: []
  241. featuredEvents: []
  242. blogPosts: []
  243. products: []
  244. categories: []
  245. loading: true
  246. error: null
  247. # Состояние модальных окон
  248. modalState:
  249. isVisible: false
  250. component: null
  251. props: {}
  252. beforeMount: ()->
  253. debug.log "start beforeMount"
  254. # определение контекста vuejs приложения как глобальной переменной _
  255. globalThis._ = @
  256. # Инициализация базы данных
  257. AppDB.initialize().then =>
  258. @dbReady = true
  259. @loadInitialData()
  260. # Слушаем события смены языка
  261. EventBus.on 'language_changed', (language) =>
  262. @currentLanguage = language
  263. @loadInitialData()
  264. mounted: ->
  265. debug.log "App mounted"
  266. methods:
  267. # Загрузка начальных данных
  268. loadInitialData: ->
  269. @appState.loading = true
  270. @appState.error = null
  271. Promise.all([
  272. @loadSlides()
  273. @loadFeaturedEvents()
  274. @loadBlogPosts()
  275. @loadCategories()
  276. ]).then =>
  277. @appState.loading = false
  278. .catch (error) =>
  279. @appState.error = "Ошибка загрузки данных: "+error
  280. @appState.loading = false
  281. # Загрузка слайдов
  282. loadSlides: ->
  283. AppDB.getSlides(limit: 6).then (slides) =>
  284. @appState.slides = slides
  285. .catch (error) =>
  286. debug.log "Ошибка загрузки слайдов: "+error
  287. # Загрузка избранных мероприятий
  288. loadFeaturedEvents: ->
  289. AppDB.getUpcomingEvents(limit: 4).then (events) =>
  290. @appState.featuredEvents = events
  291. .catch (error) =>
  292. debug.log "Ошибка загрузки мероприятий: "+error
  293. # Загрузка блог постов
  294. loadBlogPosts: ->
  295. AppDB.getBlogPosts(limit: 6).then (posts) =>
  296. @appState.blogPosts = posts
  297. .catch (error) =>
  298. debug.log "Ошибка загрузки блог постов: "+error
  299. # Загрузка категорий
  300. loadCategories: ->
  301. AppDB.getCategories().then (categories) =>
  302. @appState.categories = categories
  303. .catch (error) =>
  304. debug.log "Ошибка загрузки категорий: "+error
  305. # Смена языка
  306. changeLanguage: (language) ->
  307. if language in @availableLanguages
  308. AppDB.multilingual.setLanguage(language)
  309. @currentLanguage = language
  310. # Поиск по сайту
  311. search: (query) ->
  312. if query and query.length > 2
  313. AppDB.searchContent(query).then (results) ->
  314. EventBus.emit('search_results', results)
  315. # Открытие модального окна
  316. openModal: (component, props = {}) ->
  317. @modalState.component = component
  318. @modalState.props = props
  319. @modalState.isVisible = true
  320. # Закрытие модального окна
  321. closeModal: ->
  322. @modalState.isVisible = false
  323. @modalState.component = null
  324. @modalState.props = {}
  325. render: (new Function '_ctx', '_cache', renderFns['app/temp.pug'])()
  326. components:
  327. 'themetoggle': require 'app/shared/ThemeToggle'
  328. 'multilevelmenu': require 'app/shared/MultiLevelMenu'
  329. 'imageslider': require 'app/shared/ImageSlider'
  330. 'app-link': require 'app/shared/AppLink'
  331. 'language-switcher': require 'app/shared/LanguageSwitcher'
  332. app.use(VueRouter.createRouter({
  333. routes: routes
  334. history: VueRouter.createWebHistory()
  335. scrollBehavior: (to, from, savedPosition) ->
  336. if savedPosition
  337. return savedPosition
  338. else
  339. return { x: 0, y: 0 }
  340. }))
  341. # подключаем в body ОБЯЗАТЕЛЬНО!!!
  342. app.mount('body')