var EXPO = EXPO || {}; //isolated namespace // module requires JQuery library, dna.js template engine (http://dnajs.org/) // protection against duplication of a module code if (EXPO.searchBlock){ console.warn('WARNING: EXPO.searchBlock is already defined!'); }else{ EXPO.searchBlock = (function() { // variables var that = {}; /** * default module setting * @type {{place: {id: string, selectedItemTemplate: string, ajaxUrl: string}, subject: {id: string, ajaxUrl: string}}} */ that.settings = { place:{ id:'pw-place', selectedItemTemplate:'csb-selected', ajaxUrl: window.location.protocol + '//hit.expomap.ru/search-form/' }, subject:{ id:'pw-subj', ajaxUrl: window.location.protocol + '//hit.expomap.ru/search-form/' } }; that.lang ={}; //dependence's // module require dna.js tiny templating engine var common = EXPO.common; //private var Json2URI = function (jsonObj) { var str = Object.keys(jsonObj).map(function(key){ return encodeURIComponent(key) + '=' + encodeURIComponent(jsonObj[key]); }).join('&'); return ('?'+str); }, /** * make ajax GET request and launch handler if exist or return data * @param {Object|string} dataToSend * @param {string} url * @param {function} handler - function to execute when task is ready */ getRequest = function (dataToSend,url,handler) { if(!dataToSend){ dataToSend = ''; } $.ajax({ type: 'GET', url: url, data:dataToSend, success: function(data) { if(typeof handler == 'function'){ handler(data); } else{ return data; } } }); }, /** * rename name of property of an object/ Check for the old property name to avoid a ReferenceError in strict mode. * @param {Object} obj object to rename its properties * @param {string} oldName * @param {string} newName * @returns {renameProperty} */ renameProperty = function (obj,oldName, newName) { if (obj.hasOwnProperty(oldName)) { obj[newName] = obj[oldName]; } return this; }, /** * analogue of Array.length but for object * @param {Object} obj - Object to count its method * @returns {number} */ getObjectLength = function (obj) { var size = 0, key; for (key in obj) { if (obj.hasOwnProperty(key)) size++; } return size; }, /** * getSelectedToString returns string that contains selected values in both modal windows. values separated by ',' * @param {string} container * @param {number} cutLength - length to cut off string * @returns {string} string that is cutted off */ getSelectedToString = function(container, cutLength){ var $container = $('#'+container), index = 0, stringToReturn = '', closingSymbol = '...', closerText = EXPO.searchBlock.settings.closerText, closerTextSingle = EXPO.searchBlock.settings.closerTextSingle, dividerSymbol = ''; $element = $container.find('.csbs-text'); if($element.length > 2){ if($element.eq(0).text().length < cutLength){ dividerSymbol = ', '; stringToReturn += $element.eq(0).text() + dividerSymbol + $element.eq(1).text() + closerText.replace('_',$element.length-2); }else{ stringToReturn += $element.eq(0).text() + closerText.replace('_',$element.length-1); } } else if($element.length == 2){ if(($element.eq(0).text().length+$element.eq(1).text().length) < cutLength){ dividerSymbol = ', '; // если даже 2 первых слова вместе меньше чем порог, то не указывать количество позиций stringToReturn += $element.eq(0).text() + dividerSymbol + $element.eq(1).text()+' '; } else if($element.eq(0).text().length < cutLength){ dividerSymbol = ', '; stringToReturn += $element.eq(0).text() + dividerSymbol + $element.eq(1).text()+' '; }else{ stringToReturn += $element.eq(0).text() + closerTextSingle.replace('_',$element.length-1); } }else if($element.length){ stringToReturn += $element.eq(0).text()+' '; }else{ stringToReturn = false; } return stringToReturn; }, /** * ajax pending process indicator * @type {*|jQuery|HTMLElement} индикатор загрузки диалоговых окон */ $waiter = $('.wait-ajax.absolute'); /** * Constructor for modal window 'select subject' * @param {Object} options * @constructor */ var SubjectModal = function(options){ /** * options init * @type {Object} */ this.opt = options; var self = this, index = 0, $subjWrap = $('#'+self.opt.subjectTriggerWrapId), $topicBox, modalId = self.opt.id, $modal = $('#'+modalId), $checkBox = $('.csb-menu-input', $modal), $selectedItemsWrap = $('.csb-selected-items',$modal), setDefault = self.opt.defaultOn, applyBtnClass = self.opt.applyBtnClass, $modalTrigger = $('#'+self.opt.modalTrigger), /** * executed after render of modal Window * @function */ afterRenderHandler = function () { $waiter.hide(); }, /** * fill text beneth search field * @function triggerSetText */ triggerSetText = function () { var selectedString, cutLength = 16; if($(window).width() < 1086){ cutLength = 12; } selectedString = getSelectedToString(self.opt.selectedItemsContainer,cutLength); if(selectedString && selectedString != '~~text~~' && selectedString != '~~text~~...'){ $modalTrigger.text(selectedString); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).addClass('active'); }else{ $modalTrigger.text($modalTrigger.data('default')); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).removeClass('active'); } }; /** * this modal window DOM Instance * @type {*|jQuery|HTMLElement} */ this.$modalInst = $('#'+modalId); /** * clones of tags for selected(checked) subject (powered by dna.js objects: see http://dnajs.org/) * @type {Object} */ this.itemsSelected = {}; /** * clones of sublist of selected(checked) subject (powered by dna.js objects: see http://dnajs.org/) * @type {Object} */ this.sublist= {}; /** * Jquery object of filtering input (autocomplete input) * @type {*|jQuery|HTMLElement} */ this.$inputFilter = $('#'+self.opt.filterInputId); this.$selectedItemsWrap = $selectedItemsWrap; /** * flag for management of turn of asynchronous requests * @type {boolean} */ this.isReceived = true; $(function () { // topic checkbox selected event $('.topicChecks', $subjWrap).on('change', function () { if($(this).prop('checked')){ self._setVisible($(this).val()); }else{ self._setUnvisible($(this).val()); } }); // selected topic by default self.opt.defaultOn[] for(index = 0; index < setDefault.length; index++){ $topicBox = $('#'+setDefault[index]) $topicBox.prop('checked',true); $topicBox.trigger('change'); } //modal list and sublist behavior $modal.on('click', 'a.trigger', function () { var name = $(this).attr('data-name'), id = $(this).attr('data-id'), tmplId = $(this).attr('data-template-id'), $sublist = $(this).siblings('.dna-container'); // no more than once execution if($(this).attr('data-sub') == 'true'){ if($(this).parent().hasClass('level1')){ if(!$sublist.children().length){ $waiter.show(); self._renderSublist({name:name,id:id}, tmplId, afterRenderHandler); $(this).parent().addClass('active'); }else{ //slideUp & Down stuff self._slideToggle($sublist, $(this).parent()); } } } return false; }); /** * */ // modal theme checkbox change behavior $checkBox.on('change', function (event, param) { var checkboxId = $(this).attr('id'), $label = $(this).closest('.custom-radio-check'), $parent = $(this).closest('.level'), $parentCheckBox = $parent.parent().closest('.level').children('.custom-radio-check').find('.csb-menu-input'), $sublist = $parent.children('.sublist'); if(!param){ if(this.checked){ $label.addClass('active'); var text = $(this).closest('.level').find('.trigger').first().text(), tplObj = {'text':text,'id':checkboxId}; //tags field logic if(!$selectedItemsWrap.hasClass('visible')){ $selectedItemsWrap.addClass('visible'); } self._addTag(checkboxId, tplObj); // proper selection and selection group logic if($sublist.length){ $('.csb-menu-input',$sublist).each(function () { var thisId = this.getAttribute('id'), tagText = $(this).closest('.level').find('.trigger').first().text(), DOMlabel = common.closest(this,'custom-radio-check'); this.checked = true; self._destroyTag(thisId); common.addClass(DOMlabel,'active'); }); }else{ $parent = $parent.parent().closest('.level') // if checked items count equals all items count then parent item is checked and tags for children - deleted if ( $('.csb-menu-input',$parent.find('.sublist')).length == $('.csb-menu-input:checked',$parent.find('.sublist')).length){ var parentId = $parentCheckBox[0].getAttribute('id'), parentText = $parentCheckBox.closest('.level').find('.trigger').first().text(), DOMlabel =common.closest(this,'custom-radio-check'), DOMParentLabel = common.closest(DOMlabel.parentNode.parentNode,'level'), parentObj = {'text':parentText,'id':parentId}; $('.csb-menu-input:checked',$parent.find('.sublist')).each(function () { self._destroyTag(this.getAttribute('id')); }); $parentCheckBox.prop('checked', true); common.addClass($(DOMParentLabel).find('.custom-radio-check')[0],'active'); //$parentCheckBox.trigger('change',['true']); self._addTag(parentId,parentObj); } } //!uncheck event }else { $label.removeClass('active'); self._destroyTag(checkboxId); if(!$selectedItemsWrap.children('.dna-clone').length){ $selectedItemsWrap.removeClass('visible'); } //uncheck all sublist items while parent item is unchecked if($sublist.length){ //destroy all tags for sublist items $('.csb-menu-input',$sublist).each(function () { var thisId = $(this).attr('id'), DOMlabel = common.closest(this,'custom-radio-check'); this.checked = false; common.removeClass(DOMlabel, 'active'); self._destroyTag(thisId); }); }else{ // parent item is unchecked and tags for every children item are made if($parentCheckBox.length && $parentCheckBox[0].checked){ var DOMlabel = common.closest($parentCheckBox[0],'custom-radio-check'), DOMParentItem = common.closest(DOMlabel,'level'), DOMSublist = DOMParentItem.querySelector('.sublist'); $parentCheckBox.prop('checked', false); common.removeClass(DOMlabel, 'active'); self._destroyTag($parentCheckBox.attr('id')); // checks children items $('.csb-menu-input:checked',DOMSublist).each(function () { var id = this.getAttribute('id'), tagText = common.closest(this, 'level').querySelector('.trigger').textContent, tagObj = {'text':tagText,'id':id}; self._addTag(id,tagObj); }); } } } triggerSetText(); } }); //delete tag behavior $('.csbs-del', $selectedItemsWrap).on('click', function () { var checkboxId = $(this).attr('data-checkbox-id'), $uncheckBoxes = $('#'+checkboxId); $uncheckBoxes.prop('checked', false); $uncheckBoxes.trigger('change'); dna.destroy(self.itemsSelected[checkboxId]); if(!$selectedItemsWrap.children('.dna-clone').length){ $selectedItemsWrap.removeClass('visible'); } return false; }); $('.modal-clear', $modal).on('click', function () { self.resetList(); return false; }); self._autocompleteInit(); $('.'+applyBtnClass, $modal).on('click', function () { self.applyHandler(this); return false; }); // кнопка "очистить параметры" $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).on('click', function () { self.resetList(); EXPO.searchBlock.exhibitionField.clearValue(); }); }); }; /** * methods * @type {{_getAjax: Function, _setVisible: Function, _setUnvisible: Function, _checkCheckBox: Function, check: Function, _autocompleteInit: Function, _renderSublist: Function, _loadParentTree: Function, _destroyTag: Function, _addTag: Function, _slideToggle: Function, resetList: Function, applyHandler: Function}} */ SubjectModal.prototype = { /** * get ajax response when want sublists * @param {Object|sting} dataToSend * @param {function} handler - fires after request is complete * @private */ _getAjax: function (dataToSend, handler) { var self = this; if(!dataToSend){ dataToSend = ''; } $waiter.css({display:'block'}); $.ajax({ type: 'GET', url: self.opt.ajaxUrl, data:dataToSend, success: function(data) { if(typeof handler == 'function'){ $waiter.hide(); handler(data); } else{ return data; } } }); }, /** * filter list accordingly to checked checkbox in 'select type' part of modal * @param {string} type * @private */ _setVisible:function(type){ var self = this; $('.'+type, self.$modalInst).addClass('visible'); }, /** * filter list accordingly to checked checkbox in 'select type' part of modal * @param {string} type * @private */ _setUnvisible: function (type) { var self = this, $li = $('.'+type, self.$modalInst); $li.find('input[type="checkbox"]').each(function () { var $this = $(this); if($this.prop('checked')){ $this.prop('checked', false); $this.trigger('change'); } }); $li.find('.dna-container').each(function () { if($(this).children().length){ $(this).addClass('hidden'); } }); $li.removeClass('visible'); }, /** * check particular checkbox input * @param {string} id * @param {string} name * @param {boolean} strict - if true then select particular checkbox without 'popping' to parent category * @private */ _checkCheckBox: function (id,name) { var self = this, $chckBox; if(name == 'th'){ $chckBox = $('#tid_'+id, self.$modalInst); }else if(name == 'tg'){ $chckBox = $('#tgid_'+id, self.$modalInst); } if($chckBox.length && !$chckBox.prop('checked')){ $chckBox.prop('checked',true); $chckBox.trigger('change'); } }, /** * check particular checkbox input * @param {string} id * @param {string} name * @public */ check: function (id,name) { var self = this, $chckBox; if(name == 'th'){ $chckBox = $('#tid_'+id, self.$modalInst); }else if(name == 'tg'){ $chckBox = $('#tgid_'+id, self.$modalInst); } if($chckBox.length){ $chckBox.prop('checked',true); $chckBox.trigger('change'); $chckBox.parent().addClass('active'); } }, /** * initiliazing and setup autocomplete field for subject list * @private */ _autocompleteInit : function () { var self = this, dataObj, text , form = self.$inputFilter.attr('data-form'),index, $completeWrap = $('#'+self.opt.autoCompleteId), firstComplete = true, selectTag = function (event, ui) { //check of repeating execution $waiter.show(); var firstTime = true; for (var prop in self.sublist) { for (var prop2 in self.sublist[prop]) { if (prop2 == ui.item.value){ if($('#tid_'+ui.item.value+'[name="'+ui.item.name+'"]:checked').length){ firstTime = false; } } } } if($('#tid_'+ui.item.value+'[name="'+ui.item.name+'"]:checked').length){ firstTime = false; } // ban of repeating execution if (firstTime) { // konec var $checkbox = $('#tid_' + ui.item.value+'[name="'+ui.item.name+'"]'), requestObj, requestName, treeLoadHandler = function (data) { // make checkboxes selected after loading if(getObjectLength(data)){ self._loadParentTree(data, function () { self._checkCheckBox(ui.item.value,'tg'); $waiter.hide(); }); }else{ $waiter.hide(); console.warn() } }; // load tree related to selected item if (!$checkbox.length) { requestObj = { id: ui.item.value, name: ui.item.name }; getRequest(requestObj, self.opt.getParentUrl, treeLoadHandler); } else{ $waiter.hide(); $checkbox.prop('checked', true); $checkbox.trigger('change'); } } }, requestHandler = function(data){ dataObj = data; for(index = 0; index < dataObj.length; ++index) { renameProperty(dataObj[index],'text','label'); } for(index = 0; index < dataObj.length; ++index) { renameProperty(dataObj[index],'id','value'); } if(!self.$inputFilter.hasClass('ui-autocomplete-input')){ self.$inputFilter.placeComplete({ source:dataObj, minLength: 0, appendTo:$completeWrap, select:function(event, ui){ self.$inputFilter.val(''); self.$inputFilter.trigger('keyup'); selectTag(event, ui); event.preventDefault(); // return ui.label; } }); self.$inputFilter.placeComplete('search',""); firstComplete = false; }else{ self.$inputFilter.placeComplete('search',""); } }; /** * the ID value of the timer that is set */ self.timeout; self.$inputFilter.attr('autocomplete', 'on'); self.$inputFilter.on('keyup', function (event) { text = $(this).val(); event.stopImmediatePropagation(); clearTimeout(self.timeout); if (text.length > 2 && firstComplete){ self.timeout = setTimeout(function () { getRequest({'term':text, 'form':form}, self.opt.autoCompleteUrl, requestHandler); firstComplete = false; }, 1000); }else if(text.length == 0 && !firstComplete){ if(self.$inputFilter.hasClass('ui-autocomplete-input')){ self.$inputFilter.placeComplete( "destroy" ); firstComplete = true; } } return false; }).click(function () { return false; }); }, /** * render first level sublist * @param {Object} dataObj * @param {number|string} tmplId * @param {function} handler * @private */ _renderSublist: function (dataObj, tmplId, handler) { var self = this, index = 0, template = tmplId + '-sub', ajaxHandler = function (data) { if(data.length){ self.sublist[template] = {}; // for dna.clone definition see dnajs.org for (index ; index < data.length; index++) { self.sublist[template][data[index].id] = dna.clone(tmplId, data[index]); } handler(data.length); }else{ $waiter.hide(); } }; self._getAjax(dataObj, ajaxHandler); }, /** * if there is no children element in list, loads ITS Children sublist * @param {Object} data - current (children) object data * @param {number|string} handler * @param {function} counter * @private */ _loadParentTree: function (data, handler, counter) { var self = this, dataObj = data, optObj, sublistTemplateId, nestedObj,nestedSublistTemplateId, /** * makes request in order to recieve children list information fires after request for parent list * @param {number} length * @function */ handlerParent = function (){ $waiter.hide(); counter||counter===0?handler(counter):handler(); }; $waiter.show(); optObj = { name:dataObj.name, id:dataObj.id }; sublistTemplateId = $('#tid_'+dataObj.id).closest('.level').children('.trigger').attr('data-template-id'); self._renderSublist(optObj,sublistTemplateId,handlerParent); }, /** * destroy tag and clear tags block.for dna.clone definition see dnajs.org * @param {string} checkboxId * @private */ _destroyTag:function(checkboxId){ var self = this; if(self.itemsSelected[checkboxId]){ dna.destroy(self.itemsSelected[checkboxId]); } }, /** * for dna.clone definition see dnajs.org * @param {number} checkboxId * @param {Object} tplObj * @private */ _addTag: function (checkboxId, tplObj) { var self = this; self.itemsSelected[checkboxId] = dna.clone(self.opt.selectedItemTemplate,tplObj); }, /** * hide or show sublists according active selected link * @param {*|jQuery|HTMLElement} $sublist * @param {*|jQuery|HTMLElement} $this * @private */ _slideToggle:function ($sublist, $this){ if($sublist.hasClass('hidden')){ $sublist.removeClass('hidden'); $this.addClass('active'); }else{ $sublist.addClass('hidden').find('ul').addClass('hidden'); $this.removeClass('active'); } }, /** * reset all selected items, uncheck all selected checkboxes * @public */ resetList: function () { var self = this; for (var key in self.itemsSelected) { if (self.itemsSelected.hasOwnProperty(key)) { $('#'+key, self.$selfContainer).prop('checked', false).trigger('change'); dna.destroy(self.itemsSelected[key]); } } $('.level.active',self.$modalInst).removeClass('active'); self.$selectedItemsWrap.removeClass('visible'); $(this.$modalInst).find('input:checkbox:checked').prop('checked',false); }, // кнопка применить applyHandler: function (it) { $waiter.show(); }, /** * select particular item and render its tag and check checkbox * @param {Object} item * @public */ selectTag:function (item) { //check of repeating execution var firstTime = true, self = this, waitHandler = function () { self.isReceived = false; if(!item.children){ for (var prop in self.itemsSelected) { if (prop == 'tid_'+item.id){ firstTime = false; } } if($('#tid_'+item.id+':checked').length){ firstTime = false; } if(firstTime){ self.check(item.id, item.name); self.isReceived = true; }else{ $('#tid_' + item.id).prop('checked', true); $('#tid_' + item.id).trigger('change'); self.isReceived = true; } }else{ if($('#tgid_'+item.children.id).length){ firstTime = false; } //Если выбран родитель if($('#tid_'+item.id+':checked').length){ firstTime = false; } if(firstTime) { self._loadParentTree({name: item.name, id: item.id}, function () { self.check(item.children.id, item.children.name); self.isReceived = true; }); }else if(!$('#tgid_'+item.id+':checked').length){ $('#tgid_' + item.children.id).prop('checked', true); $('#tgid_' + item.children.id).trigger('change'); self.isReceived = true; } } }; this.wait(waitHandler); }, /** * waits so far the previous request will be executed and execute a method * @param {function} method * @param {Object|Array} args * @public */ wait:function(method, args) { var self = this, waitImages, waitHandler = function(self, method, args) { if (self.isReceived) { if (args) { method(args); } else{ method(); } clearInterval(waitImages); } }; waitImages = setInterval(function() {waitHandler(self, method, args)}, 100); } }; /** * Constructor for modal window 'select place' * @param {Object} options * @constructor */ var PlacesModal = function(options){ /** * object properties * @type {Object} */ this.opt = options; var self = this, $modal = $('#'+self.opt.id), $checkBox = $('input[type="checkbox"]', $modal), $selectedItemsWrap = $('.csb-selected-items',$modal), DOMTagsWrapper = $('.csb-selected-items',$modal)[0], $modalTrigger = $('#'+self.opt.modalTrigger), applyBtnClass = self.opt.applyBtnClass, idPrefix = 'id_', /** * set trigger link text under the search field * @function */ triggerSetText = function () { var selectedString, cutLength = 16; if($(window).width() < 1086){ cutLength = 6; } selectedString = getSelectedToString(self.opt.selectedItemsContainer,cutLength); if(selectedString && selectedString != '~~text~~' && selectedString != '~~text~~...'){ $modalTrigger.text(selectedString); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).addClass('active'); }else{ $modalTrigger.text($modalTrigger.data('default')); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).removeClass('active'); } }; /** * current template instances * @type {Object} */ this.curDNA = {}; /** * place modal list items that had selected * @type {Object} */ this.itemsSelected = {}; this.selectedWrap = $selectedItemsWrap; this.$selfContainer = $modal; this.$modal = $modal; this.idPrefix = idPrefix; /** * flag for management of turn of asynchronous requests * @type {boolean} */ this.isReceived = true; /** * Jquery object of filtering input (autocomplete input) * @type {*|jQuery|HTMLElement} */ this.$inputFilter = $('#'+self.opt.filterInputId); $(function () { self._autocompleteInit(); $modal.on('click', 'a.trigger', function () { var name = $(this).attr('data-name'), id = $(this).attr('data-id'), that = this, tmplId = $(this).attr('data-template-id'), $sublist = $(this).siblings('.dna-container'), afterRenderHandler = function (elem, data) { var DOMParent = common.closest(that,'level'), DOMParentCheckbox = DOMParent.querySelector('.csb-menu-input'); $('.csb-menu-input', $sublist).each(function () { var DOMCheckboxWrap = common.closest(this,'custom-radio-check'); if(!this.checked){ if(DOMParentCheckbox.checked){ this.checked = true; common.addClass(DOMCheckboxWrap,'active'); } }else{ if(!DOMParentCheckbox.checked){ this.checked = false; common.removeClass(DOMCheckboxWrap,'active'); } } }); $waiter.hide(); }; // no more than once execution if($(this).attr('data-sub') == 'true'){ if($(this).parent().hasClass('level1')){ if(!$sublist.children().length){ $waiter.show(); self._renderSublist({name:name,id:id}, tmplId, afterRenderHandler); }else{ //slideUp & Down stuff self._slideToggle($sublist, $(this).parent()); } }else if($(this).parent().hasClass('level2')){ if(!$sublist.children().length){ self._renderNested({name:name,id:id}, afterRenderHandler, tmplId, id); $(this).parent().addClass('active'); } else{ //slideUp & Down stuff self._slideToggle($sublist, $(this).parent()); } } } return false; }); $checkBox.on('change', function (event, param) { var id = this.getAttribute('id'), fakeCheckboxClass = 'custom-radio-check', fakeCheckbox = common.closest(this, fakeCheckboxClass), itemClass = 'level', activeClass = 'active', sublistClass = 'sublist', checkboxClass = 'csb-menu-input', highestItemClass = 'level1', tagClass = 'csb-selected', tagButtonClass = 'csbs-del', triggerClass = 'trigger', tagIdAttribute = 'data-checkbox-id', DOMParentRow = common.closest(this, itemClass), DOMParentItem = common.hasClass(DOMParentRow, highestItemClass) == false?common.closest(DOMParentRow.parentNode, itemClass):DOMParentRow, DOMParentCheckbox = DOMParentItem.querySelector('.'+checkboxClass), DOMSublist = DOMParentItem.querySelector('.'+sublistClass), DOMSublistInner = DOMParentRow.querySelector('.'+sublistClass), DOMHighestItem = common.closest(this, highestItemClass), DOMHighestCheckbox = DOMHighestItem.querySelector('.'+checkboxClass), DOMHighestSublist = DOMHighestItem.querySelector('.'+sublistClass), selectSublist = function (it) { var DOMParentItem = common.closest(it, itemClass) || this, DOMSublist = DOMParentItem.querySelector('.'+sublistClass); $('.'+checkboxClass, DOMSublist).each(function () { selectItem(this); }); }, unSelectSublist = function (it) { var DOMParentItem = common.closest(it, itemClass) || this, DOMSublist = DOMParentItem.querySelector('.'+sublistClass); $('.'+checkboxClass, DOMSublist).each(function () { unSelectItem(this); }); }, selectParent = function (it) { var DOMParentRow = common.closest(it, itemClass), DOMParentItem; if(common.hasClass(DOMParentRow,'level1')){ DOMParentItem = DOMParentRow; }else{ DOMParentItem = common.closest(DOMParentRow.parentNode, itemClass); } common.addClass(DOMParentItem.querySelector('.'+fakeCheckboxClass), activeClass); DOMParentItem.querySelector('.'+checkboxClass).checked = true; //it.selected = true; }, unSelectParent = function (it) { var DOMParentRow = common.closest(it, itemClass), DOMParentItem = common.closest(DOMParentRow.parentNode, itemClass) || DOMParentRow; common.removeClass(DOMParentItem.querySelector('.'+fakeCheckboxClass), activeClass); DOMParentItem.querySelector('.'+checkboxClass).checked = false; //it.checked = false; }, selectItem = function (it) { var itFakeCheckbox = common.closest(it, fakeCheckboxClass); common.addClass(itFakeCheckbox, activeClass); it.checked = true; common.addClass(DOMParentItem,activeClass); common.removeClass(DOMSublist,'hidden'); }, unSelectItem = function (it) { var itFakeCheckbox = common.closest(it, fakeCheckboxClass), DOMitem = common.closest(it, itemClass); common.removeClass(itFakeCheckbox, activeClass); it.checked = false; // if there is children items if(DOMitem.querySelector('.'+sublistClass) && !common.hasClass(DOMitem,highestItemClass)){ unSelectSublist(it); } }, allChildrenSelected = function () { //var DOMselected = DOMSublist.querySelectorAll('.'+checkboxClass+':checked'), var DOMSublistParent = common.closest(DOMSublist,sublistClass), $selected = $(DOMSublist).children('li').children('.'+fakeCheckboxClass).find('.'+checkboxClass+':checked'), selectedCount = $selected.length, //allCount = DOMSublist.querySelectorAll('.'+checkboxClass).length; allCount = $(DOMSublist).children('li').children('.'+fakeCheckboxClass).find('.'+checkboxClass).length; if(allCount == selectedCount && selectedCount != 0){ return true; } else{ return false; } }, allHighestSelected = function () { //var DOMselected = DOMSublist.querySelectorAll('.'+checkboxClass+':checked'), var $selected = $(DOMHighestSublist).children('li').children('.'+fakeCheckboxClass).find('.'+checkboxClass+':checked'), selectedCount = $selected.length, allCount = $(DOMHighestSublist).children('li').children('.'+fakeCheckboxClass).find('.'+checkboxClass).length; if(allCount == selectedCount && selectedCount != 0){ return true; } else{ return false; } }, parentSelected = function () { var parentCheckbox = DOMParentItem.querySelector('.'+checkboxClass); if(parentCheckbox.checked){ return true; }else{ return false; } }, // Функционал добавления тегов: если есть в панели выбранный элемент либо его дочерние то удалить эти эдементы; refreshTags = function (it) { var DOMSublist = common.closest(it,sublistClass); var DOMItem = common.closest(it,itemClass); var DOMSublistInner = DOMItem.querySelector('.'+sublistClass); var DOMAllTags = DOMTagsWrapper.querySelectorAll('.'+tagClass); var ARRsublist; var ARRsublistChildren = DOMSublist.querySelector('.'+sublistClass)?$('.'+checkboxClass+':checked',DOMSublist.querySelector('.'+sublistClass)):null; var ARRsublistChildrenLength = ARRsublistChildren?ARRsublistChildren.length:null; var ARRSublistIds = []; var ARRAllTagsIds = []; var allTagsLength = 0; var sublistIdsLength = 0; var itId = it.getAttribute('id'); var i = 0, j= 0, t= 0, tmp, k = 0; var tagId; var tagText = DOMParentRow.querySelector('.'+triggerClass).innerHTML; // если есть дочерние элементы if(DOMSublistInner){ ARRsublist = $(DOMSublistInner).children('li').children('.'+fakeCheckboxClass).find('.'+checkboxClass+':checked') //получаем массив id жлементовЮ для которых есть тег for(k; k < ARRsublist.length; k++){ tmp = ARRsublist[k].getAttribute('id'); ARRSublistIds.push(tmp); } //очистк for(t; t 2 && firstComplete){ self.timeout = setTimeout(function () { getRequest({'term':text, 'form':form}, self.opt.autoCompleteUrl, requestHandler); firstComplete = false; }, 1000); }else if(text.length == 0 && !firstComplete){ if(self.$inputFilter.hasClass('ui-autocomplete-input')){ self.$inputFilter.autocomplete( "destroy" ); firstComplete = true; } } return false; }).click(function () { return false; }); }, /** * loads and shows list tree related to selected id * @param {Object} data - is JSON object with information about parent elements.({text: "Германия", id: 47, parent: {text: "Европа", id: 4, name: "area"}, name: "co"}) * @param {function} handler - callback function * @param {number} counter * @private */ _loadParentTree: function (data, handler, counter) { var self = this, dataObj = data, optObj, sublistTemplateId, nestedObj,nestedSublistTemplateId, $midleLevelCheckbox = $('#id_'+dataObj.id), /** * makes request in order to recieve children list information fires after request for parent list * @param {number} length - the number of received elements * @function */ handlerNested = function (length) { var $checkbox = $('#id_'+dataObj.id), index = 0, afterAll = function (number) { $waiter.hide(); index++ if(index == number){ counter||counter===0?handler(counter):handler(); } }; $waiter.hide(); if($checkbox.length && getObjectLength(self.curDNA[sublistTemplateId+'-sub']) == length){ nestedObj = { name:dataObj.name, id:dataObj.id }; $waiter.show(); nestedSublistTemplateId = $('#id_'+dataObj.id).closest('.level').children('.trigger').attr('data-template-id'); self._renderNested(nestedObj, afterAll, nestedSublistTemplateId, dataObj.id); } }, /** * @function */ handlerParent = function (){ $waiter.hide(); counter||counter===0?handler(counter):handler(); }; $waiter.show(); //if element has parent element if(dataObj.hasOwnProperty('parent')){ //if checbox is existed if($midleLevelCheckbox.length){ nestedObj = { name:dataObj.name, id:dataObj.id }; nestedSublistTemplateId = $midleLevelCheckbox.closest('.level').children('.trigger').attr('data-template-id'); self._renderNested(nestedObj, function(){$waiter.hide();handler();}, nestedSublistTemplateId, dataObj.id); }else{ optObj = { name:dataObj.parent.name, id:dataObj.parent.id }; sublistTemplateId = $('#id_'+dataObj.parent.id).closest('.level').children('.trigger').attr('data-template-id'); self._renderSublist(optObj,sublistTemplateId,handlerNested); } }else{ optObj = { name:dataObj.name, id:dataObj.id }; sublistTemplateId = $('#id_'+dataObj.id).closest('.level').children('.trigger').attr('data-template-id'); self._renderSublist(optObj,sublistTemplateId,handlerParent); } }, /** * @param it * @public */ applyHandler: function (it) { console.log(this); }, _refreshText: function () { var selectedString, $modalTrigger = $('#'+this.opt.modalTrigger), cutLength = 16; selectedString = getSelectedToString(this.opt.selectedItemsContainer,cutLength); if(selectedString && selectedString != '~~text~~' && selectedString != '~~text~~...'){ $modalTrigger.text(selectedString); $modalTrigger.siblings('.'+this.opt.clearAllButtonClass).addClass('active'); }else{ $modalTrigger.text($modalTrigger.data('default')); $modalTrigger.siblings('.'+this.opt.clearAllButtonClass).removeClass('active'); } } }; /** * period selector modal window * @param {Object} options * @constructor */ var PeriodModal = function (options) { this.opt = options; var self = this, $dateFrom = $('#'+self.opt.dateFrom), $dateTo = $('#'+self.opt.dateTo), $modal = $('#'+self.opt.id), $modalTrigger = $('#'+self.opt.modalTrigger), applyBtnClass = self.opt.applyBtnClass, $activeField, /** * make string from input:text values * @returns {string} * @function */ inputsTostring = function(){ var self = this, stringToReturn = '', toChar = $modalTrigger.data('lng-to'), fromChar = $modalTrigger.data('lng-from'), toVal = $.trim($dateTo.val()), fromVal = $.trim($dateFrom.val()); if(toVal != ''&& fromVal != ''){ if($(window).width() < 1086){ stringToReturn = fromChar+': '+fromVal+'
'+toChar+': '+toVal+'.'; }else{ stringToReturn = fromChar+': '+fromVal+' '+toChar+': '+toVal+'.'; } } else if (toVal != ''&& fromVal == ''){ stringToReturn = toChar+': '+toVal+'.'; } else if (toVal == ''&& fromVal != ''){ stringToReturn = fromChar+': '+fromVal+'.'; } return stringToReturn; }, /** * set trigger link text * @param {string} selectedString * @function */ triggerSetText = function (selectedString) { var cutLength = 24; if($(window).width() < 1086){ cutLength = 6; } if(selectedString != ''){ $modalTrigger.html(selectedString).siblings('.trigger-label').addClass('hidden'); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).addClass('active'); }else{ $modalTrigger.html($modalTrigger.data('default')).siblings('.trigger-label').removeClass('hidden'); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).removeClass('active'); } }, /** * checks and corrects value of fields of a modal and corrects values so that in the field from always there was a date "to" (less) date in the field "on" * @param it * @function */ checkInterval = function (it) { var itId = it.getAttribute('id'), itVal = it.value, itValInt = Date.parse(itVal), thatId, thatVal, thatValInt, bfr; /** * check if it is a valid date */ if (self.validate()){ $(it).parent('.pwf-field').removeClass('err'); if(itId == self.opt.dateFrom){ thatId = $dateTo[0].getAttribute('id'); thatVal = $dateTo[0].value; }else { thatId = $dateFrom[0].getAttribute('id'); thatVal = $dateFrom[0].value; } thatValInt = Date.parse(thatVal); /** * swap values between date fields */ if(itValInt > thatValInt && itId == self.opt.dateFrom){ bfr = document.getElementById(itId).value; document.getElementById(itId).value = document.getElementById(thatId).value; document.getElementById(thatId).value = bfr; }else if(itValInt < thatValInt && itId == self.opt.dateTo){ bfr = document.getElementById(thatId).value; document.getElementById(thatId).value = document.getElementById(itId).value; document.getElementById(itId).value = bfr; } } }; this.$dateFrom = $dateFrom; this.$dateTo = $dateTo; this.$modalTrigger = $modalTrigger; $(function () { $.datepicker.setDefaults($.datepicker.regional["ru"]); $dateFrom.datepicker({ dateFormat: 'dd.mm.yy', showOn: 'button', showOtherMonths: true, constrainInput: true, onClose: function( selectedDate ) { $dateTo.datepicker( "option", "minDate", selectedDate ); } }).inputmask("99.99.9999",{ showMaskOnHover:false, insertMode:false, 'oncomplete': function () { $activeField = this; } }); $dateTo.datepicker({ dateFormat: 'dd.mm.yy', showOn: 'button', showOtherMonths: true, constrainInput: true, onClose: function( selectedDate ) { $dateFrom.datepicker( "option", "maxDate", selectedDate ); } }).inputmask("99.99.9999",{ showMaskOnHover:false, insertMode:false, 'oncomplete': function () { $activeField = this; } }); $('input[type="text"]',$modal).on('change', function () { var stringToset = inputsTostring(); if (self.validate()){ triggerSetText(stringToset); } }); $('.'+applyBtnClass, $modal).on('click', function () { if($activeField){ checkInterval($activeField); } self.applyHandler(this); return false; }); $modalTrigger.siblings('.'+self.opt.clearAllButtonClass).on('click', function () { $(this).removeClass('active'); self.resetList(); }); }); }; /** * methods * @type {{resetList: Function, applyHandler: Function}} */ PeriodModal.prototype = { /** * clear all input fields of modal window * @public */ resetList: function () { this.$dateFrom.val(''); this.$dateTo.val(''); this.$modalTrigger.text(this.$modalTrigger.attr('data-default')); }, /** * checks input fields to have valid date values, if not - adds err class to its container * @returns {boolean} */ validate: function () { var toVal = this.$dateTo.val(), fromVal = this.$dateFrom.val(), toValArr = toVal.split('.'), fromValArr = fromVal.split('.'), timestamps =[], validationResult = true; timestamps[0] = Date.parse(toValArr[1]+'.'+toValArr[0]+'.'+toValArr[2]); timestamps[1] = Date.parse(fromValArr[1]+'.'+fromValArr[0]+'.'+fromValArr[2]); if (!timestamps[0] && $.trim(toVal) != ''){ this.$dateTo.parent('.pwf-field').addClass('err'); validationResult = false; }else{ this.$dateTo.parent('.pwf-field').removeClass('err'); } if (!timestamps[1] && $.trim(fromVal) != ''){ this.$dateFrom.parent('.pwf-field').addClass('err'); validationResult = false; }else{ this.$dateFrom.parent('.pwf-field').removeClass('err'); } return validationResult; }, applyHandler: function (it) { } }; /** * constructor for main searchfield * @param {Object} options * @constructor */ var MainField = function (options) { this.opt = options; this.opt.anyChar = false; var self = this, firstComplete = true; this.$field = $('#'+self.opt.id); /** * much faster than JQuery * @type {HTMLElement} */ this.DOMcompleteWrap = document.getElementById(self.opt.autoCompleteWrapId); this.afterClear; /** * data from ajax response * @type {{items: Array, set: Function, get: Function, _rawDataChange: Function}} */ this.rawData = { items:[], /** * setter * @param data * @public */ set:function (data) { this.items = data; this._rawDataChange(); }, /** * get data * @returns {*} * @public */ get: function () { return this.items; }, /** * executes when this.rawData changes * @param {Object} e * @private */ _rawDataChange: function (e) { var self = this, index; for(index = 0; index < self.items.length; index++) { renameProperty(self.items[index],'text','label') renameProperty(self.items[index],'id','value') } } }; this.completeName = 'autocomplete'; this.firstComplete = true; this.$field.attr('autocomplete','off'); /** * the ID value of the timer that is set */ this.timeout; //autocomplete behaviour self.$field.on('keyup', function (event) { var reqObj; text = $(this).val(); event.stopImmediatePropagation(); clearTimeout(self.timeout); if(self.dataForm){ reqObj = { 'term':text, 'form':self.dataForm } }else{ reqObj = { 'term':text } } if(self.opt.anyChar){ if (text.length > 2){ self.timeout = setTimeout(function () { getRequest(reqObj, self.opt.autoCompleteUrl, function (data) { if(self.$field.hasClass('ui-autocomplete-input')){ self.$field[self.completeName]( "destroy" ); } self.rawData.set(data); self._initAutoComplete(); self.showList(''); }); }, 1000); } else if(text.length === 0){ if(self.$field.hasClass('ui-autocomplete-input')){ self.$field[self.completeName]( "destroy" ); common.removeClass(self.DOMcompleteWrap,'full-visible'); self._resetmodals(self.afterClear); } } }else{ if (text.length > 2 && firstComplete){ self.timeout = setTimeout(function () { getRequest(reqObj, self.opt.autoCompleteUrl, function (data) { self.rawData.set(data); self._initAutoComplete(); self.showList(''); }); firstComplete = false; }, 1000); }else if(text.length == 0 && !firstComplete){ if(self.$field.hasClass('ui-autocomplete-input')){ self.$field[self.completeName]( "destroy" ); common.removeClass(self.DOMcompleteWrap,'full-visible'); self._resetmodals(self.afterClear); firstComplete = true; } }else{ if(self.$field.hasClass('ui-autocomplete-input')){ self.showList(text); } } } //for autocomplete on each keypress //for normal flow autocomplete return false; }).click(function () { return false; }); }; /** * methods * @type {{_initAutoComplete: Function, _afterSelect: Function, _resetmodals: Function, selectHandler: Function, newRender: Function, selectCheckBoxes: Function, showList: Function, pullData: Function}} */ MainField.prototype = { /** * Jquery autocomplete initialization and autocomplete functionality on this.$field * @private */ _initAutoComplete: function () { var self = this; self.$field[self.completeName]({ source:self.rawData.get(), minLength: 0, appendTo:self.DOMcompleteWrap, select:function(event, ui){ self._afterSelect(event, ui); }, close: function () { common.removeClass(self.DOMcompleteWrap,'full-visible'); }, open: function () { common.addClass(self.DOMcompleteWrap,'full-visible'); } }); }, /** * fires after autocomplete is done and user selects item in it * @param {Object} event * @param {Object} ui * @private */ _afterSelect: function (event, ui) { var self = this; event.preventDefault(); // much faster than jquery if(ui.item.url){ window.location = ui.item.url; } else if(ui.item.label.length){ self.$field[0].value = ui.item.label; self.selectCheckBoxes(ui.item.id, ui.item.name); } }, /** * method fires callback when field is cleared * @param {function} callback * @returns {number} * @private */ _resetmodals: function (callback) { if(callback){ callback(); }else{ return 0; } }, /** * public property for redefine _afterSelect * @public */ selectHandler: function () { }, /** * this property must be redefined for each individual instances of MainField Object * select checkboxes if there is coincidence with search result and modal subject modal window * @param {number} id * @param {string} name * @public */ selectCheckBoxes: function (id, name) { }, /** * simple shows out autocomplete list * @param {string} text */ showList: function (text) { self = this; this.$field[self.completeName]('search',text); }, /** * brings data from server and transfer it to handler function * @param {function} handler */ pullData: function (handler) { }, clearValue: function () { this.$field.val(''); } }; /** * whole module initialization * @param {Object} options * @public */ that.init = function(options) { // settings extending $.extend(this.lang, options.lang); options.lang = null; $.extend(this.settings, options); // begin of initialization var self = this, submitHandler = function () { $(self.DOMform).find('input[name="~~name~~"]').remove(); }; if(this.settings.searchData != 'None' && this.settings.searchData){ this.previousSearch = JSON.parse(this.settings.searchData) } this.DOMform = document.getElementById(this.settings.formId); $(this.DOMform).on('submit', function () { submitHandler(); }); // custom autocomplete realization for exibition input $.widget( "custom.exibitionComplete", $.ui.autocomplete,{ _renderMenu: function( ul, items ) { var that = this, currentCategory = ""; $.each( items, function( index, item ) { if ( item.cat != currentCategory && item.cat) { ul.append( "
  • " + item.cat + "
  • " ); currentCategory = item.cat; } that._renderItemData( ul, item ); }); } }); $.widget( "custom.exibitionComplete", $.ui.autocomplete,{ _renderMenu: function( ul, items ) { var that = this, currentCategory = ""; $.each( items, function( index, item ) { if ( item.cat != currentCategory && item.cat) { ul.append( "
  • " + item.cat + "
  • " ); currentCategory = item.cat; } that._renderItemData( ul, item ); }); } }); $.widget( "custom.placeComplete", $.ui.autocomplete,{ _renderItem: function( ul, item ) { return $( "
  • " ) .append( $( "" ).text( item.label) ) .append(' ('+item.cat+')') .appendTo( ul ); } }); this.exhibitionField = new MainField(self.settings.firstField); this.exhibitionField.completeName = 'exibitionComplete'; this.exhibitionField.opt.anyChar = true; //modal windows this.placesModal = new PlacesModal(self.settings.place); this.subjModal = new SubjectModal(self.settings.subject); this.periodModal = new PeriodModal(self.settings.period); this.exhibitionField.selectCheckBoxes = function (id, name) { var self = this, DOMCheckboxes, nestedObj, iName = name; if(name == 'th'){ DOMCheckboxes = document.getElementById(self.opt.prefix+id); }else if(name == 'tg'){ DOMCheckboxes = document.getElementById(self.opt.prefixInner+id); iName = 'th'; } $waiter.show(); // load tree related to selected item if (!DOMCheckboxes) { nestedObj = { name:name, id:id }; EXPO.searchBlock.subjModal.resetList(); getRequest(nestedObj, EXPO.searchBlock.subjModal.opt.getParentUrl, function (data) { EXPO.searchBlock.subjModal._loadParentTree(data, function(){ EXPO.searchBlock.subjModal._checkCheckBox(id, name); }); }); } else{ $waiter.hide(); EXPO.searchBlock.subjModal.resetList(); $(DOMCheckboxes).prop('checked', true); $(DOMCheckboxes).trigger('change'); } }; this.placesField = new MainField(self.settings.placeField); this.placesField.dataForm = EXPO.searchBlock.placesModal.$inputFilter.attr('data-form'); this.placesField.selectCheckBoxes = function(id,name){ var self = this, DOMCheckboxes, nestedObj; DOMCheckboxes = document.getElementById(self.opt.prefix+id); if (!DOMCheckboxes) { nestedObj = { name:name, id:id }; EXPO.searchBlock.placesModal.resetList(); getRequest(nestedObj, EXPO.searchBlock.placesModal.opt.getParentUrl, function (data) { EXPO.searchBlock.placesModal._loadParentTree(data, function(){ EXPO.searchBlock.placesModal._checkCheckBox(id, true); }); }); } else{ $waiter.hide(); EXPO.searchBlock.placesModal.resetList(); $(DOMCheckboxes).prop('checked', true); $(DOMCheckboxes).trigger('change'); } }; // uncheck all checboxes in modal and clear label beneath searchfield this.exhibitionField.afterClear = function () { self.subjModal.resetList(); }; this.placesField.afterClear = function () { self.placesModal.resetList(); }; //модальное окно this.modalWindow = new common.Modal(self.settings.modal); this.periodModal.applyHandler = function () { if(this.validate()){ $waiter.show(); $(self.DOMform).submit(); } }; this.placesModal.applyHandler = function () { $waiter.show(); $(self.DOMform).submit(); }; this.subjModal.applyHandler = function () { $waiter.show(); $(self.DOMform).submit(); }; // // modal window calling $('.'+self.settings.modalTriggerClass).on('click', function (event) { event.preventDefault(); self.modalWindow.pullData(this.getAttribute('href')); self.modalWindow.open(); return false; }); // заполнение полей предыдущими значениями $(function () { if(self.previousSearch){ for (var i = 0; i < self.previousSearch.inputs.length; i++) { // окно выбора тематики if (self.previousSearch.inputs[i].name == 'th'){ self.subjModal.selectTag(self.previousSearch.inputs[i]); // окно выбора мест } else if (self.previousSearch.inputs[i].name == 'area'){ if(self.previousSearch.inputs[i].children && self.previousSearch.inputs[i].children.children){ self.placesModal.selectTag(self.previousSearch.inputs[i].children.children); }else if (self.previousSearch.inputs[i].children){ self.placesModal.selectTag(self.previousSearch.inputs[i].children); } else if(self.previousSearch.inputs[i]){ self.placesModal.selectTag(self.previousSearch.inputs[i]); } } } } }); }; return that; }()); }