@ -0,0 +1,4 @@ |
||||
<?xml version="1.0"?> |
||||
<users> |
||||
<user>4C79F1DB9A1FE199063E733A4FB34C31</user> |
||||
</users> |
||||
@ -0,0 +1,12 @@ |
||||
DATABASES = { |
||||
'default': { |
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2', |
||||
'NAME': 'proekton2', |
||||
'USER': 'postgres', |
||||
'PASSWORD': 'gum1756', |
||||
'HOST': 'localhost', |
||||
'PORT': '', |
||||
} |
||||
} |
||||
|
||||
TEMPLATE_DEBUG = True |
||||
@ -0,0 +1,16 @@ |
||||
from django.core.management.base import BaseCommand |
||||
from IPython.terminal.embed import InteractiveShellEmbed |
||||
from django.db.models import Model |
||||
|
||||
|
||||
class Command(BaseCommand): |
||||
def handle(self, *args, **options): |
||||
modules = ('specializations.models', ) |
||||
for module in modules: |
||||
m = __import__(module, fromlist='non-empty') |
||||
for a in m.__dict__: |
||||
v = getattr(m, a) |
||||
if hasattr(v, '__base__') and issubclass(v, Model): |
||||
globals()[a] = v |
||||
|
||||
InteractiveShellEmbed()() |
||||
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 382 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
@ -0,0 +1,117 @@ |
||||
label { |
||||
all: initial; |
||||
} |
||||
|
||||
.selected-container { |
||||
/*display: inline-block;*/ |
||||
/*width: 100%;*/ |
||||
min-height: 40px; |
||||
padding-bottom: 20px; |
||||
} |
||||
|
||||
.selected-container .selected-element { |
||||
display: inline-block; |
||||
padding: 5px 25px 5px 10px; |
||||
margin-top: 8px; |
||||
min-height: 45px; |
||||
background-color: #e3e3e3; |
||||
border: 1px solid #dbdbdb; |
||||
position: relative; |
||||
-moz-border-radius: 10px; /* Firefox */ |
||||
-webkit-border-radius: 10px; /* Safari 4 */ |
||||
border-radius: 10px; /* IE 9, Safari 5, Chrome */ |
||||
} |
||||
|
||||
.selected-element .header{ |
||||
font-size: 9pt; |
||||
color: #979494; |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
word-wrap: break-word; |
||||
} |
||||
|
||||
.selected-element .name { |
||||
font-size: 14pt; |
||||
white-space: nowrap; |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
vertical-align: middle; |
||||
width: 100%; |
||||
} |
||||
|
||||
|
||||
.selected-element .icon-remove{ |
||||
background-image: url('../img/cross01.png'); |
||||
background-size: 24px 24px; |
||||
/*display: inline-block;*/ |
||||
position: absolute; |
||||
right: 5px; |
||||
top: 15px; |
||||
width: 16px; |
||||
height: 16px; |
||||
cursor: pointer; |
||||
margin-left: -20px; |
||||
} |
||||
|
||||
/*DEMO*/ |
||||
.wrapper{ |
||||
background-color: #f2f2f2; |
||||
padding: 40px; |
||||
margin: 50px 30px; |
||||
} |
||||
|
||||
.form-container { |
||||
padding: 50px 45px; |
||||
} |
||||
|
||||
.separator { |
||||
margin: 20px 0; |
||||
} |
||||
|
||||
.max-width { |
||||
display: table !important; |
||||
width: 100% |
||||
} |
||||
|
||||
.vertical{ |
||||
width: 100%; |
||||
} |
||||
|
||||
.cell { |
||||
display: table-cell; |
||||
} |
||||
.custom-check { |
||||
background: url("../img/rect01.png") no-repeat center; |
||||
background-size: 200px 40px; |
||||
width: 200px; |
||||
height: 40px; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.custom-check.checked { |
||||
background: url("../img/rect02.png") no-repeat center; |
||||
background-size: 200px 40px; |
||||
width: 200px; |
||||
height: 40px; |
||||
} |
||||
|
||||
#page-preloader { |
||||
position: fixed; |
||||
left: 0; |
||||
top: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
background: #000; |
||||
z-index: 100500; |
||||
} |
||||
|
||||
#page-preloader .spinner { |
||||
width: 32px; |
||||
height: 32px; |
||||
position: absolute; |
||||
left: 50%; |
||||
top: 50%; |
||||
background: url('../img/ajax-loader.gif') no-repeat 50% 50%; |
||||
margin: -16px 0 0 -16px; |
||||
} |
||||
@ -0,0 +1,171 @@ |
||||
.select-box-container { |
||||
display: block; |
||||
/*margin-top: 40px;*/ |
||||
/*min-width: 200px;*/ |
||||
} |
||||
|
||||
.select-box-header{ |
||||
font-size: 14pt; |
||||
white-space: nowrap; |
||||
word-wrap: break-word; |
||||
/*position: relative;*/ |
||||
/*top:10px;*/ |
||||
} |
||||
|
||||
.select-box-header .header{ |
||||
display: inline-block; |
||||
color: black; |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
word-wrap: break-word; |
||||
max-width: 220px; |
||||
|
||||
} |
||||
|
||||
/*.select-box-header i {*/ |
||||
/*position: relative;*/ |
||||
/*top: -5px;*/ |
||||
/*}*/ |
||||
|
||||
.select-box-header .fa:hover{ |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.select-box-bottom { |
||||
clear: both; |
||||
} |
||||
|
||||
.select-box-results, .select-box-options { |
||||
display: block; |
||||
/*clear: both;*/ |
||||
position: absolute; |
||||
/*margin-top: -5px;*/ |
||||
z-index: 99999; |
||||
opacity: 0.9; |
||||
} |
||||
|
||||
.box-wrapper { |
||||
display: block; |
||||
max-height: 200px; |
||||
overflow-y: scroll; |
||||
border: 1px solid #868686; |
||||
background-color: #F2F2F2; |
||||
} |
||||
|
||||
.select-box-results input[type=checkbox] { |
||||
margin: 0 5px 0 5px; |
||||
} |
||||
|
||||
.select-box-results i { |
||||
font-weight: normal; |
||||
font-style: normal; |
||||
} |
||||
|
||||
.select-box-results, .select-box-options li, ul { |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
.select-box-results li, .select-box-options li { |
||||
padding: 2px 10px; |
||||
border-bottom: 1px solid #afb2b6; |
||||
list-style: none; |
||||
} |
||||
|
||||
.select-box-results label, .select-box-options label, |
||||
.select-box-results li, .select-box-options li { |
||||
cursor: pointer; |
||||
width: 100%; |
||||
} |
||||
|
||||
.select-box-results li:hover, .select-box-options li:hover { |
||||
background-color: #dcdcdc; |
||||
border-left: 2px solid red; |
||||
} |
||||
|
||||
.select-box-search { |
||||
display: block; |
||||
} |
||||
|
||||
input.select-box-search { |
||||
height: 40px; |
||||
width: 100%; |
||||
border: 1px solid #cccccc; |
||||
outline: none; |
||||
padding: 5px 40px 5px 20px; |
||||
background: url("../img/magnifying-glass-308581.svg") no-repeat right; |
||||
background-size: 40px 40px; |
||||
background-color: white; |
||||
/*padding-left: 40px;*/ |
||||
} |
||||
|
||||
.select-box-results .other-part { |
||||
color: darkgray; |
||||
/*border: 1px solid #00fa17;*/ |
||||
display: block; |
||||
} |
||||
|
||||
.select-box-results .other-header { |
||||
color: #000; |
||||
font-weight: bold; |
||||
margin-left: 10px; |
||||
} |
||||
|
||||
.select-box-results .main-part { |
||||
border-bottom: 1px solid #000000; |
||||
display: block; |
||||
} |
||||
|
||||
.select-box-options .box-wrapper, .select-box-results { |
||||
/*padding: 0 10px;*/ |
||||
min-width: 300px; |
||||
max-width: 400px; |
||||
} |
||||
|
||||
.button-add { |
||||
-moz-border-radius: 15px; /* Firefox */ |
||||
-webkit-border-radius: 15px; /* Safari 4 */ |
||||
border-radius: 15px; /* IE 9, Safari 5, Chrome */ |
||||
background-color: red; |
||||
padding: 0 15px; |
||||
/*border: none;*/ |
||||
text-decoration: none; |
||||
color: white; |
||||
|
||||
} |
||||
|
||||
.button-add .results { |
||||
position: absolute; |
||||
left: 280px; |
||||
z-index: 999; |
||||
} |
||||
|
||||
.button-add.options { |
||||
position: absolute; |
||||
/*FIXME: костыль*/ |
||||
left: 160px; |
||||
z-index: 999; |
||||
/*right: auto;*/ |
||||
/*@position: absolute;*/ |
||||
/*top: 40px;*/ |
||||
} |
||||
|
||||
.highlight { |
||||
color: red; |
||||
} |
||||
|
||||
.editable-container { |
||||
display: block; |
||||
float: left; |
||||
/*max-width: 400px;*/ |
||||
background-color: #e5e5e5; |
||||
min-height: 40px; |
||||
padding: 5px 10px; |
||||
/*min-width: 40px;*/ |
||||
position: relative; |
||||
left: -30px; |
||||
} |
||||
|
||||
.vertical-child{ |
||||
margin-top: 30px; |
||||
} |
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,299 @@ |
||||
// `
|
||||
class SelectBox { |
||||
constructor($container, data, has_editable_container, vertical_child) { |
||||
this.container_id = $container.attr("id"); |
||||
this.tree_data = new SpecTree(data); |
||||
// Вариант не выбран
|
||||
this.selected_id = undefined; |
||||
let template = SelectBox.getTemplate("Header", this.container_id, has_editable_container, vertical_child); |
||||
$container.replaceWith(template); |
||||
this._buildComponents(); |
||||
this._bindEvents(); |
||||
if (!has_editable_container) this.hide(); |
||||
} |
||||
|
||||
static getTemplate(header, id, editable_container, vertical_child) { |
||||
let main = ` |
||||
<div class="row"> |
||||
<div class="col-lg-3 test"> |
||||
<input class="select-box-search" type="text"> |
||||
<button class="button-add options">добавить</button> |
||||
</div> |
||||
<div class="col-lg-9"> |
||||
<span class="editable-container"></span> |
||||
</div> |
||||
</div> |
||||
`;
|
||||
let child = ` |
||||
<input class="select-box-search" type="text"> |
||||
<span style="clear:both; display:block"></span> |
||||
<button class="button-add options">добавить</button> |
||||
`;
|
||||
let insert_template = editable_container ? main : child; |
||||
vertical_child = vertical_child ? 'vertical-child': ''; |
||||
let htmlTemplate = |
||||
` |
||||
<span class="select-box-container ${vertical_child}" id="${id}"> |
||||
<div class="select-box-header"> |
||||
<span class="header">${header}</span> |
||||
<i class="fa fa-question-circle-o" aria-hidden="true" title="bla-bla-bla..."></i> |
||||
</div> |
||||
<span class="select-box-search"> |
||||
${insert_template} |
||||
</span> |
||||
<span class="select-box-options"> |
||||
<span class="box-wrapper"> |
||||
<ul> |
||||
|
||||
</ul> |
||||
</span> |
||||
</span> |
||||
<span class="select-box-results"> |
||||
<span class="box-wrapper"> |
||||
<span class="main-part"> |
||||
<ul> |
||||
</ul> |
||||
</span> |
||||
<span class="other-part"> |
||||
<span class="other-header">Из других категорий</span> |
||||
<ul> |
||||
</ul> |
||||
</span> |
||||
</span> |
||||
<button class="button-add results">добавить</button> |
||||
</span> |
||||
<span style="clear: both"></span> |
||||
</span> |
||||
`;
|
||||
return htmlTemplate; |
||||
} |
||||
|
||||
_buildComponents() { |
||||
this.$select_box = $(`#${this.container_id}`); |
||||
this.$header = this.$select_box.find('.select-box-header .header'); |
||||
this.$results_box = this.$select_box.find('.select-box-results'); |
||||
this.$options_box = this.$select_box.find('.select-box-options'); |
||||
this.$search_input = this.$select_box.find('input.select-box-search'); |
||||
this.$button_add = this.$results_box.find('.button-add'); |
||||
this.$button_add_options = this.$select_box.find('.button-add.options'); |
||||
this.$editable_container = this.$select_box.find('.editable-container'); |
||||
this._fillOptionsData(); |
||||
this.$results_box.hide(); |
||||
this.$options_box.hide(); |
||||
this.$button_add_options.hide(); |
||||
// TODO: сделать проверку на наличие всех нужных элементов и их корректый jq select
|
||||
} |
||||
|
||||
static highlight(string, sub_string) { |
||||
let index = string.toLowerCase().indexOf(sub_string.toLowerCase()); |
||||
if (index === -1) return string; |
||||
let light_template = (el) => `<span class="highlight">${el}</span>`; |
||||
let [to, select, after] = [string.slice(0, index), |
||||
string.slice(index, index + sub_string.length), |
||||
string.slice(index + sub_string.length)]; |
||||
return `${to}${light_template(select)}${after}` |
||||
} |
||||
|
||||
hide() { |
||||
this.$select_box.hide(); |
||||
} |
||||
|
||||
show() { |
||||
this.$select_box.show(); |
||||
} |
||||
|
||||
clear() { |
||||
this.$search_input.val(""); |
||||
this.$options_box.hide(); |
||||
this.$results_box.hide(); |
||||
// this.$button_add.hide();
|
||||
this.$button_add_options.hide(); |
||||
this.element_id = undefined; |
||||
this.parent_id = undefined; |
||||
this.$editable_container.html(""); |
||||
if (this.prev_select_box) this.hide(); |
||||
} |
||||
|
||||
clearAll(only_next) { |
||||
console.log("only_next = ", only_next); |
||||
if (this.next_select_box) { |
||||
this.next_select_box.clear() |
||||
} |
||||
if (!only_next) { |
||||
this.clear(); |
||||
if (this.prev_select_box) this.prev_select_box.clearAll(only_next) |
||||
} else { |
||||
if (this.next_select_box) this.next_select_box.clearAll(only_next) |
||||
} |
||||
|
||||
} |
||||
|
||||
setHeader(header) { |
||||
this.$header.html(header); |
||||
this.show(); |
||||
} |
||||
|
||||
setParent(parent_id) { |
||||
// console.log("setParent id -->", parent_id);
|
||||
this.parent_id = parent_id; |
||||
this._fillOptionsData(); |
||||
} |
||||
|
||||
setNearbySelectBox(next, prev) { |
||||
this.next_select_box = next; |
||||
this.prev_select_box = prev; |
||||
} |
||||
|
||||
getSelectedElements() { |
||||
let all_checked = this.$results_box.find(":checked"); |
||||
return all_checked.map(function () { |
||||
return $(this).data("id"); |
||||
}) |
||||
} |
||||
|
||||
connectSelectedContainer(selected_container) { |
||||
this.selected_container = selected_container; |
||||
this.selected_container = selected_container; |
||||
|
||||
} |
||||
|
||||
updateEditableContainer(el_id) { |
||||
// Если нет контейнера для отображения ...
|
||||
if (this.$editable_container.length) { |
||||
let chain_header = SelectedContainer.getHeader( |
||||
this.tree_data.getSpecChain(el_id, true), " / "); |
||||
let el_template = `<span class="">${chain_header}</span>`; |
||||
this.$editable_container.html(el_template); |
||||
return; |
||||
} |
||||
//..., передаем отображение предыдущему selectBox
|
||||
if (this.prev_select_box) this.prev_select_box.updateEditableContainer(el_id); |
||||
} |
||||
|
||||
_onclickOptionsElement(e) { |
||||
this.clearAll(true); |
||||
let el_id = $(e.target).data("id"); |
||||
this.selected_id = el_id; |
||||
this.updateEditableContainer(el_id); |
||||
this.$search_input.val($(e.target).html()); |
||||
this.element_id = el_id; |
||||
if (this.next_select_box) { |
||||
this.next_select_box.setParent(el_id); |
||||
this.next_select_box.setHeader($(e.target).html()); |
||||
} |
||||
if (this.prev_select_box) this.prev_select_box.$button_add_options.hide(); |
||||
|
||||
this.$button_add_options.show(); |
||||
this.$options_box.hide(); |
||||
} |
||||
|
||||
_onButtonAdd(e) { |
||||
let self = this; |
||||
|
||||
this.getSelectedElements().each(function () { |
||||
console.log("add el -->", this); |
||||
self.selected_container.add(this, 40); |
||||
}); |
||||
this.clearAll(); |
||||
e.preventDefault(); |
||||
return false; |
||||
} |
||||
|
||||
_onButtonAddOptions(e) { |
||||
this.selected_container.add(this.element_id, 40); |
||||
this.clearAll(); |
||||
e.preventDefault(); |
||||
return false; |
||||
} |
||||
|
||||
_fillOptionsData() { |
||||
let self = this; |
||||
let spec_list = this.tree_data.dataToList(this.parent_id); |
||||
let $container = this.$options_box.find('ul'); |
||||
$container.html(""); |
||||
spec_list.forEach(function (el) { |
||||
let el_html = `<li data-id="${el.id}">${el.name}</li>`; |
||||
$container.append($(el_html)) |
||||
}); |
||||
this.$select_box.find('li').on("click", this._onclickOptionsElement.bind(self)); |
||||
|
||||
} |
||||
|
||||
_fillResultsData(search_text) { |
||||
let self = this; |
||||
|
||||
function search(search_text, parent_id, exclude_id) { |
||||
// :FORMAT spec_list [{name, id}, ...]
|
||||
let spec_list = self.tree_data.dataToList(parent_id, true, exclude_id); |
||||
// console.log("search -->", spec_list.length);
|
||||
console.log("parent_id ", parent_id); |
||||
return spec_list.filter(function (el) { |
||||
return el.name.toLowerCase().indexOf(search_text.toLowerCase()) !== -1; |
||||
}); |
||||
} |
||||
|
||||
function fillContainer($container, template, search_text, parent_id, exclude_id) { |
||||
$container.html(""); |
||||
let search_res = search(search_text, parent_id, exclude_id); |
||||
if (!search_res.length || (!exclude_id && parent_id === null)) { |
||||
$container.append('<span>No Found</span>'); |
||||
return; |
||||
} |
||||
search_res.forEach(function (el) { |
||||
$container.append(template(SelectBox.highlight(el.name, search_text), el.id)); |
||||
}); |
||||
} |
||||
|
||||
// FILL RESULTS
|
||||
let result_template = (el, id) => `<li><label><input type="checkbox" data-id="${id}"><i>${el}</i></label></li>`; |
||||
// MAIN PART
|
||||
let $container = this.$results_box.find('.main-part ul'); |
||||
fillContainer($container, result_template, search_text, self.parent_id); |
||||
|
||||
// OTHER PART
|
||||
$container = this.$results_box.find('.other-part ul'); |
||||
fillContainer($container, result_template, search_text, null, self.parent_id); |
||||
} |
||||
|
||||
_looseFocus() { |
||||
this.$results_box.hide(); |
||||
this.$options_box.hide(); |
||||
if (!this.selected_id) this.$search_input.val(""); |
||||
} |
||||
|
||||
_bindEvents() { |
||||
let self = this; |
||||
$(document).click(function (event) { |
||||
if ($(event.target).closest(`#${self.container_id}`).length) { |
||||
return; |
||||
} |
||||
self._looseFocus(); |
||||
}); |
||||
// console.log("out ", this.$options_box);
|
||||
this.$search_input.on("input", function (e) { |
||||
self._fillResultsData(self.$search_input.val()); |
||||
self.$results_box.show(); |
||||
self.$options_box.hide(); |
||||
|
||||
}); |
||||
|
||||
this.$search_input.on("click", function (e) { |
||||
// console.log("in ", self.$options_box);
|
||||
self.$options_box.show(); |
||||
self.$results_box.hide(); |
||||
self.$button_add_options.hide(); |
||||
self.$search_input.val(""); |
||||
|
||||
}); |
||||
|
||||
|
||||
this.$button_add.on("click", function (e) { |
||||
self._onButtonAdd(e); |
||||
}); |
||||
|
||||
this.$button_add_options.on("click", this._onButtonAddOptions.bind(self)) |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
@ -0,0 +1,99 @@ |
||||
// `
|
||||
class SelectedContainer { |
||||
constructor($container, data) { |
||||
this.$self = $container; |
||||
this.elements_id = []; // [spec_id, spec_id, ...]
|
||||
this.tree_data = new SpecTree(data); |
||||
this.$input = this.$self.find('input[type="hidden"]'); |
||||
this.restoreElements(); |
||||
} |
||||
|
||||
restoreElements(){ |
||||
const self = this; |
||||
if (this.$input && this.$input.val()){ |
||||
let data = this.$input.val().split(',').filter((el) => el); |
||||
console.log("restore data = ", data); |
||||
this.elements_id = []; |
||||
data.forEach((el) => self.add(el)); |
||||
} |
||||
} |
||||
|
||||
static getTemplate(header, name, id) { |
||||
let htmlTemplate = |
||||
` |
||||
<div class="selected-element"> |
||||
<span class="header"> |
||||
${header} |
||||
</span> |
||||
<div class="name"> |
||||
${name} |
||||
</div> |
||||
<span data-id="${id}" class="icon-remove"></span> |
||||
</div> |
||||
`;
|
||||
return htmlTemplate |
||||
} |
||||
|
||||
static getHeader(spec_chain, separator, max_len) { |
||||
function toShortString(string, max_len) { |
||||
return string.slice(0, max_len) + (string.length > max_len ? "..." : ""); |
||||
} |
||||
|
||||
separator = separator || ' / '; |
||||
let str_chain = ""; |
||||
|
||||
spec_chain.forEach(function (el) { |
||||
str_chain = (max_len ? toShortString(el.name, max_len) : el.name) + (str_chain ? separator : "") + str_chain; |
||||
}); |
||||
|
||||
return str_chain; |
||||
} |
||||
|
||||
_removeById(spec_id) { |
||||
let index = this.elements_id.indexOf(spec_id); |
||||
if (index >= 0) { |
||||
this.elements_id.splice(index, 1); |
||||
} |
||||
$(`span[data-id='${spec_id}']`).parents('.selected-element').remove(); |
||||
} |
||||
|
||||
remove(e) { |
||||
let spec_id = $(e.target).data("id"); |
||||
this._removeById(spec_id); |
||||
if (this.$input) this.$input.val(this.elements_id.join(',')); |
||||
e.preventDefault(); |
||||
} |
||||
|
||||
add(_id, max_len) { |
||||
const id = Number(_id); |
||||
let self = this; |
||||
|
||||
let has_already = this.elements_id.filter(function (el) { |
||||
return self.tree_data.hasChildren(el, id) |
||||
}); |
||||
|
||||
// console.log(has_already);
|
||||
if (has_already.length || (this.elements_id).indexOf(Number(id)) != -1) { |
||||
//TODO: do popup messages
|
||||
console.log("Not actually"); |
||||
return; |
||||
} |
||||
|
||||
let not_valid = this.elements_id.filter(function (el) { |
||||
return self.tree_data.hasChildren(id, el) |
||||
}); |
||||
|
||||
not_valid.forEach(function (el) { |
||||
self._removeById(el); |
||||
}); |
||||
|
||||
const header = SelectedContainer.getHeader(this.tree_data.getSpecChain(id), "", max_len); |
||||
// console.log("header = ", header);
|
||||
const name = this.tree_data.getElementById(id).name; |
||||
this.elements_id.push(id); |
||||
if (this.$input) this.$input.val(this.elements_id.join(',')); |
||||
this.$self.append(SelectedContainer.getTemplate(header || "root", name, id)); |
||||
this.btn_remove = this.$self.find('.icon-remove'); |
||||
this.btn_remove.on("click", this.remove.bind(self)); |
||||
} |
||||
} |
||||
@ -0,0 +1,184 @@ |
||||
// `
|
||||
class SimpleSelect { |
||||
constructor($container, data) { |
||||
this.container_id = $container.attr("id"); |
||||
this.data = data || SimpleSelect._collectData($container); |
||||
let template = SimpleSelect.getTemplate("Header", this.container_id); |
||||
$container.replaceWith(template); |
||||
this._buildComponents(); |
||||
this._bindEvents(); |
||||
} |
||||
|
||||
static getTemplate(header, id) { |
||||
let insert_template = ` |
||||
<input class="select-box-search" type="text"> |
||||
<span style="clear:both; display:block"></span> |
||||
`;
|
||||
|
||||
let htmlTemplate = |
||||
` |
||||
<span class="select-box-container" id="${id}"> |
||||
<div class="select-box-header"> |
||||
<span class="header">${header}</span> |
||||
<i class="fa fa-question-circle-o" aria-hidden="true" title="bla-bla-bla..."></i> |
||||
</div> |
||||
<span class="select-box-search"> |
||||
${insert_template} |
||||
</span> |
||||
<span class="select-box-options"> |
||||
<span class="box-wrapper"> |
||||
<ul> |
||||
|
||||
</ul> |
||||
</span> |
||||
</span> |
||||
<span class="select-box-results"> |
||||
<span class="box-wrapper"> |
||||
<span class="main-part"> |
||||
<ul> |
||||
</ul> |
||||
</span> |
||||
</span> |
||||
</span> |
||||
<span style="clear: both"></span> |
||||
</span> |
||||
`;
|
||||
return htmlTemplate; |
||||
} |
||||
|
||||
clear() { |
||||
this.$search_input.val(""); |
||||
this.$options_box.hide(); |
||||
this.$results_box.hide(); |
||||
this.selected_id = undefined; |
||||
} |
||||
|
||||
connectSelectedContainer(selected_container) { |
||||
this.selected_container = selected_container; |
||||
|
||||
} |
||||
|
||||
|
||||
setHeader(header) { |
||||
this.$header.html(header); |
||||
} |
||||
|
||||
|
||||
static _collectData($container) { |
||||
let $options = $container.find('option'); |
||||
let data = []; |
||||
$options.each(function () { |
||||
data.push({id: $(this).val(), name: $(this).html()}); |
||||
// $(this).remove();
|
||||
}); |
||||
return data |
||||
} |
||||
|
||||
_fillOptionsData() { |
||||
let self = this; |
||||
let $container = this.$options_box.find('ul'); |
||||
$container.html(""); |
||||
this.data.forEach(function (el) { |
||||
let el_html = `<li data-id="${el.id}">${el.name}</li>`; |
||||
$container.append($(el_html)) |
||||
}); |
||||
this.$select_box.find('li').on("click", this._onclickOptionsElement.bind(self)); |
||||
} |
||||
|
||||
_fillResultsData(search_text) { |
||||
let self = this; |
||||
|
||||
function search(search_text) { |
||||
// :FORMAT spec_list [{name, id}, ...]
|
||||
return self.data.filter(function (el) { |
||||
return el.name.toLowerCase().indexOf(search_text.toLowerCase()) !== -1; |
||||
}); |
||||
} |
||||
|
||||
function fillContainer($container, template, search_text, parent_id, exclude_id) { |
||||
$container.html(""); |
||||
let search_res = search(search_text, parent_id, exclude_id); |
||||
if (!search_res.length || (!exclude_id && parent_id === null)) { |
||||
$container.append('<span>No Found</span>'); |
||||
return; |
||||
} |
||||
search_res.forEach(function (el) { |
||||
$container.append(template(SelectBox.highlight(el.name, search_text), el.id)); |
||||
}); |
||||
} |
||||
|
||||
// FILL RESULTS
|
||||
let result_template = (el, id) => `<li><label><input type="checkbox" data-id="${id}"><i>${el}</i></label></li>`; |
||||
// MAIN PART
|
||||
let $container = this.$results_box.find('.main-part ul'); |
||||
fillContainer($container, result_template, search_text, self.parent_id); |
||||
|
||||
// OTHER PART
|
||||
$container = this.$results_box.find('.other-part ul'); |
||||
fillContainer($container, result_template, search_text, null, self.parent_id); |
||||
//FIXME: duplicate code --^
|
||||
} |
||||
|
||||
_buildComponents() { |
||||
this.$select_box = $(`#${this.container_id}`); |
||||
this.$header = this.$select_box.find('.select-box-header .header'); |
||||
this.$results_box = this.$select_box.find('.select-box-results'); |
||||
this.$options_box = this.$select_box.find('.select-box-options'); |
||||
this.$search_input = this.$select_box.find('input.select-box-search'); |
||||
// this.$button_add = this.$results_box.find('.button-add');
|
||||
// this.$button_add_options = this.$select_box.find('.button-add.options');
|
||||
// this.$editable_container = this.$select_box.find('.editable-container');
|
||||
this._fillOptionsData(); |
||||
this.$results_box.hide(); |
||||
this.$options_box.hide(); |
||||
// this.$button_add_options.hide();
|
||||
// TODO: сделать проверку на наличие всех нужных элементов и их корректый jq select
|
||||
} |
||||
|
||||
_looseFocus() { |
||||
this.$results_box.hide(); |
||||
this.$options_box.hide(); |
||||
if (!this.selected_id) this.$search_input.val(""); |
||||
} |
||||
|
||||
_bindEvents() { |
||||
let self = this; |
||||
$(document).click(function (event) { |
||||
if ($(event.target).closest(`#${self.container_id}`).length) { |
||||
return; |
||||
} |
||||
self._looseFocus(); |
||||
}); |
||||
// console.log("out ", this.$options_box);
|
||||
this.$search_input.on("input", function (e) { |
||||
self._fillResultsData(self.$search_input.val()); |
||||
self.$results_box.show(); |
||||
self.$options_box.hide(); |
||||
|
||||
}); |
||||
|
||||
this.$search_input.on("click", function (e) { |
||||
self.$options_box.show(); |
||||
self.$results_box.hide(); |
||||
self.$search_input.val(""); |
||||
|
||||
}); |
||||
|
||||
|
||||
// this.$button_add.on("click", function (e) {
|
||||
// self._onButtonAdd(e);
|
||||
// });
|
||||
|
||||
} |
||||
|
||||
_onclickOptionsElement(e) { |
||||
console.log("selelc with id =", $(e.target).data("id")); |
||||
let el_id = $(e.target).data("id"); |
||||
this.selected_id = el_id; |
||||
// this.$search_input.val($(e.target).html());
|
||||
if (this.selected_container) this.selected_container.add(this.selected_id, 40); |
||||
this.$options_box.hide(); |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,74 @@ |
||||
// `
|
||||
class SimpleSelectedContainer { |
||||
constructor($container, data) { |
||||
this.$self = $container; |
||||
this.elements_id = []; // [spec_id, spec_id, ...]
|
||||
this.data = data; |
||||
this.$input = this.$self.find('input[type="hidden"]'); |
||||
this.restoreElements(); |
||||
} |
||||
|
||||
restoreElements() { |
||||
const self = this; |
||||
if (this.$input && this.$input.val()){ |
||||
let data = this.$input.val().split(',').filter((el) => el); |
||||
console.log("restore data = ", data); |
||||
this.elements_id = []; |
||||
data.forEach((el) => self.add(el)); |
||||
} |
||||
} |
||||
|
||||
static getTemplate(name, id) { |
||||
let htmlTemplate = |
||||
` |
||||
<div class="selected-element max-width"> |
||||
<div class="name"> |
||||
${name} |
||||
</div> |
||||
<span data-id="${id}" class="icon-remove"></span> |
||||
</div> |
||||
`;
|
||||
return htmlTemplate |
||||
} |
||||
|
||||
|
||||
_removeById(spec_id) { |
||||
let index = this.elements_id.indexOf(spec_id); |
||||
if (index >= 0) { |
||||
this.elements_id.splice(index, 1); |
||||
} |
||||
$(`span[data-id='${spec_id}']`).parents('.selected-element').remove(); |
||||
} |
||||
|
||||
remove(e) { |
||||
let spec_id = $(e.target).data("id"); |
||||
this._removeById(spec_id); |
||||
if (this.$input) this.$input.val(this.elements_id.join(',')); |
||||
e.preventDefault(); |
||||
} |
||||
|
||||
getElementById(id) { |
||||
for (let i = 0; i < this.data.length; i++) { |
||||
if (this.data[i].id == id) return this.data[i] |
||||
} |
||||
} |
||||
|
||||
add(_id, max_len) { |
||||
const id = Number(_id); |
||||
// TODO: добавить учет max_len
|
||||
let self = this; |
||||
if ((this.elements_id).indexOf(id) != -1) { |
||||
//TODO: do popup messages
|
||||
console.log("Not actually"); |
||||
return; |
||||
} |
||||
// console.log("!", this.getElementById(id));
|
||||
const name = this.getElementById(id).name; |
||||
this.elements_id.push(id); |
||||
if (this.$input) this.$input.val(this.elements_id.join(',')); |
||||
this.$self.append(SimpleSelectedContainer.getTemplate(name, id)); |
||||
this.btn_remove = this.$self.find('.icon-remove'); |
||||
this.btn_remove.on("click", this.remove.bind(self)); |
||||
console.log(); |
||||
} |
||||
} |
||||
@ -0,0 +1,115 @@ |
||||
class Node { |
||||
constructor(data, tree) { |
||||
this.name = data.name; |
||||
this.id = data.id; |
||||
if (data.parent === null) { |
||||
this.parent = "root"; |
||||
data.parent = {id:"root"}; |
||||
this.name = "" |
||||
} |
||||
if (data.parent.id && data.parent.id !== 'root') { |
||||
let el = tree._getElementById(data.parent.id); |
||||
this.parent = el.node || new Node(el, tree); |
||||
} |
||||
data.node = this; |
||||
this.children = data.children.map(function (el_obj) { |
||||
let el = tree._getElementById(el_obj.id); |
||||
if(!el) console.log("el not found with id", el_obj.id); |
||||
// console.log("el = ", el, "el.id = ", el_obj.id);
|
||||
if (el.node) return el.node; |
||||
el.node = new Node(el, tree); |
||||
return el.node |
||||
}); |
||||
|
||||
this.children = this.children || []; |
||||
} |
||||
} |
||||
|
||||
class SpecTree { |
||||
constructor(data) { |
||||
this.baseData = data; |
||||
this._root = new Node(data[0], this); |
||||
} |
||||
|
||||
/** |
||||
* получить element в базовой структуре |
||||
*/ |
||||
_getElementById(id) { |
||||
for (let i = 0; i < this.baseData.length; i++) { |
||||
// console.log(this.baseData[i].id, " / ", id);
|
||||
if (this.baseData[i].id == id) return this.baseData[i] |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* получить element в дереве |
||||
*/ |
||||
getElementById(id) { |
||||
function searchInChildren(children) { |
||||
for (let i = 0; i < children.length; i++) { |
||||
if (children[i].id == id) return children[i]; |
||||
let res = searchInChildren(children[i].children); |
||||
if (res) return res |
||||
} |
||||
} |
||||
|
||||
return searchInChildren(this._root.children) |
||||
} |
||||
|
||||
/** |
||||
* Является ли узел c el_id дочерним для parent_id |
||||
* @param el_id |
||||
* @param parent_id |
||||
*/ |
||||
hasChildren(el_id, parent_id){ |
||||
function checkParent(el, parent) { |
||||
if (el.parent == parent) return true; |
||||
if (el.parent && el.parent != 'root') return checkParent(el.parent, parent); |
||||
return false; |
||||
} |
||||
return checkParent(this.getElementById(el_id), this.getElementById(parent_id)) |
||||
} |
||||
|
||||
/** |
||||
* @param start_parent_id(number) - начиная с |
||||
* @param attached(bool) - включая вложенные/дочерние |
||||
* @param exclude_id - исключая узел c exclude_id и всеми его вложенными узлами |
||||
* @returns [{name, id}, ...] |
||||
*/ |
||||
dataToList(start_parent_id, attached, exclude_id) { |
||||
let data_list = []; |
||||
|
||||
function goInChildren(children) { |
||||
for (let i = 0; i < children.length; i++) { |
||||
if (children[i].id == exclude_id) continue; |
||||
data_list.push({name: children[i].name, id: children[i].id}); |
||||
if (attached) goInChildren(children[i].children); |
||||
} |
||||
} |
||||
let start = start_parent_id ? this.getElementById(start_parent_id) : this._root; |
||||
goInChildren(start.children); |
||||
return data_list |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param id |
||||
* @param incl(bool) - исключая сам элемент |
||||
* @returns {Array} всех узлов/элементов от элемента с id до корня |
||||
*/ |
||||
getSpecChain(id, incl){ |
||||
let chain = []; |
||||
let el = this.getElementById(id); |
||||
// console.log("el = ", el);
|
||||
function getParent(el) { |
||||
if (el.parent && el.parent != "root"){ |
||||
chain.push(el.parent); |
||||
getParent(el.parent) |
||||
} |
||||
} |
||||
getParent(el); |
||||
// console.log("chain = ", chain);
|
||||
if (incl) chain.unshift(el); |
||||
return chain |
||||
} |
||||
} |
||||
@ -0,0 +1,158 @@ |
||||
// `
|
||||
function findProjects() { |
||||
console.log("Find"); |
||||
const url = '/projects/test_page'; |
||||
let keywords = $('input[name=keywords]').val(); |
||||
let obj = {keywords}; |
||||
console.log("obj --> string", JSON.stringify(obj)); |
||||
window.location.href = `${url}?keywords=про`; |
||||
// $.ajax({
|
||||
// url: "/projects/test_page",
|
||||
// data: {'foo': 'bar'},
|
||||
// async: false
|
||||
// });
|
||||
} |
||||
$(function () { |
||||
function createFilterSpecs(_data) { |
||||
// SPECIALIZATIONS FILTER
|
||||
let data = _data.results; |
||||
let sb_main = new SelectBox($('#select-box-1'), data, true); |
||||
sb_main.setHeader("Специализации"); |
||||
let select_container = new SelectedContainer($('#selected-spec'), data); |
||||
sb_main.connectSelectedContainer(select_container); |
||||
let sb_1 = new SelectBox($('#select-box-2'), data, null, true); |
||||
let sb_2 = new SelectBox($('#select-box-3'), data, null, true); |
||||
let sb_3 = new SelectBox($('#select-box-4'), data, null, true); |
||||
let sb_4 = new SelectBox($('#select-box-5'), data, null, true); |
||||
|
||||
sb_1.connectSelectedContainer(select_container); |
||||
sb_2.connectSelectedContainer(select_container); |
||||
sb_3.connectSelectedContainer(select_container); |
||||
sb_4.connectSelectedContainer(select_container); |
||||
|
||||
sb_main.setNearbySelectBox(sb_1); |
||||
sb_1.setNearbySelectBox(sb_2, sb_main); |
||||
sb_2.setNearbySelectBox(sb_3, sb_1); |
||||
sb_3.setNearbySelectBox(sb_4, sb_2); |
||||
sb_4.setNearbySelectBox("", sb_3); |
||||
|
||||
} |
||||
|
||||
function createFilterBuildingClass(_data) { |
||||
// BUILDING-CLASSIFICATION FILTER
|
||||
let data = _data.results; |
||||
let sb_build_main = new SelectBox($('#sb-building-classification'), data, false); |
||||
sb_build_main.setHeader("Классификация здания"); |
||||
let sb_build_1 = new SelectBox($('#sb-building-sub-classification'), data, null, true); |
||||
let select_build_container = new SelectedContainer($('#selected-building-classification'), data, true); |
||||
sb_build_main.connectSelectedContainer(select_build_container); |
||||
sb_build_1.connectSelectedContainer(select_build_container); |
||||
|
||||
sb_build_main.setNearbySelectBox(sb_build_1); |
||||
sb_build_1.setNearbySelectBox("", sb_build_main); |
||||
} |
||||
|
||||
function createFilterConstructionType(_data) { |
||||
let data = _data.results; |
||||
let sb_constr_main = new SimpleSelect($('#sb-construction-type'), data); |
||||
sb_constr_main.setHeader("Вид строительства"); |
||||
let select_constr_type = new SimpleSelectedContainer($('#selected-construction-type'), data); |
||||
sb_constr_main.connectSelectedContainer(select_constr_type); |
||||
} |
||||
|
||||
function createFilerLocations(data) { |
||||
let sb_loc_main = new SelectBox($('#sb-location-1'), data); |
||||
sb_loc_main.setHeader("Местоположение"); |
||||
let select_loc = new SelectedContainer($('#selected-location'), data, true); |
||||
sb_loc_main.connectSelectedContainer(select_loc); |
||||
let sb_loc_1 = new SelectBox($('#sb-location-2'), data, null, true); |
||||
let sb_loc_2 = new SelectBox($('#sb-location-3'), data, null, true); |
||||
|
||||
sb_loc_1.connectSelectedContainer(select_loc); |
||||
sb_loc_2.connectSelectedContainer(select_loc); |
||||
|
||||
sb_loc_main.setNearbySelectBox(sb_loc_1); |
||||
sb_loc_1.setNearbySelectBox(sb_loc_2, sb_loc_main); |
||||
sb_loc_2.setNearbySelectBox("", sb_loc_1); |
||||
//TODO: Временно прелоадер на самом тяжелом объекте
|
||||
hidePreloader() |
||||
|
||||
} |
||||
|
||||
$.ajax({ |
||||
url: '/api/specializations_flat', |
||||
dataType: 'json', |
||||
data: {}, |
||||
success: createFilterSpecs |
||||
}); |
||||
|
||||
$.ajax({ |
||||
url: '/api/building_classifications', |
||||
dataType: 'json', |
||||
data: {}, |
||||
success: createFilterBuildingClass |
||||
}); |
||||
|
||||
$.ajax({ |
||||
url: '/api/construction_type', |
||||
dataType: 'json', |
||||
data: {}, |
||||
success: createFilterConstructionType |
||||
}); |
||||
|
||||
let data = []; |
||||
|
||||
function fullData(_data) { |
||||
data = data.concat(_data.results.length ? _data.results : []); |
||||
let url = _data.next; |
||||
if (url) { |
||||
$.ajax({ |
||||
url: url, |
||||
dataType: 'json', |
||||
data: {}, |
||||
success: fullData |
||||
}); |
||||
} else { |
||||
createFilerLocations(data); |
||||
} |
||||
return data; |
||||
} |
||||
|
||||
fullData({next: '/api/locations_flat', results: []}); |
||||
|
||||
function tuneCheckBoxes($boxes) { |
||||
$boxes.each(function () { |
||||
if ($(this).find("input").prop("checked")) $(this).toggleClass('checked') |
||||
}); |
||||
$boxes.on("click", function (e) { |
||||
$(e.target).toggleClass('checked'); |
||||
$(e.target).attr('data-state', $(e.target).attr('data-state') == 'enabled' ? 'disabled' : 'enabled') |
||||
let inside_checkBox = $(e.target).find("input"); |
||||
inside_checkBox.prop("checked", !inside_checkBox.prop("checked")); |
||||
|
||||
}); |
||||
} |
||||
|
||||
tuneCheckBoxes($('.custom-check')); |
||||
|
||||
// $(window).on('load',
|
||||
function hidePreloader() { |
||||
var $preloader = $('#page-preloader'), |
||||
$spinner = $preloader.find('.spinner'); |
||||
$spinner.fadeOut(); |
||||
$preloader.delay(350).fadeOut('slow'); |
||||
} |
||||
|
||||
|
||||
// $("#myBtn").click(function () {
|
||||
// $('<div class="alert alert-success alert-dismissable">' +
|
||||
// '<button type="button" class="close" ' +
|
||||
// 'data-dismiss="alert" aria-hidden="true">' +
|
||||
// '×' +
|
||||
// '</button>' +
|
||||
// 'modal info...' +
|
||||
// '</div>').appendTo("#alerts");
|
||||
// });
|
||||
|
||||
|
||||
}); |
||||
@ -0,0 +1 @@ |
||||
google-site-verification: google5a3cd420b0160075.html |
||||
@ -1,8 +1,8 @@ |
||||
User-agent: Yandex |
||||
Allow: /$ |
||||
Disallow: / |
||||
Host: https://proekton.com |
||||
|
||||
Host: proekton.com |
||||
User-agent: * |
||||
Allow: /$ |
||||
Disallow: / |
||||
@ -0,0 +1,6 @@ |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
||||
</head> |
||||
<body>Verification: e27d8c2662ff63f11978e0e73616f2fc</body> |
||||
</html> |
||||
@ -0,0 +1,6 @@ |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
||||
</head> |
||||
<body>Verification: 58d23691715ef942</body> |
||||
</html> |
||||