diff --git a/static/index.js b/static/index.js new file mode 100644 index 0000000..b531a65 --- /dev/null +++ b/static/index.js @@ -0,0 +1,1084 @@ +// Constants ----------------------------------------------- + + +var API_PAGE_SIZE = 100; +var LIVE_IMAGE_UPLOAD_URL = '/common/live-image-upload/create/'; +var STUB_IMAGE_URL = '/static/img/profile.jpg'; +var DEFAULT_WORK_TYPE = 403; + + +// Plugins init -------------------------------------------- + + +$('.datepicker').datepicker({ + changeMonth: true, + changeYear: true, +}) + +$('[data-tooltip]').tooltip({ + container: 'body', // Remove unwanted side-effects + trigger : 'hover', // Disable tooltip remaining visible after a click +}) + + + + + + + + + + + + + +// Specialization select ----------------------------------- + + +;(function() { + var specSelectOptions = { + language: 'ru', + //minimumInputLength: 1, // Commented out to immediately load remote data + placeholder: 'Выберите специализацию', // Required by `allowClear` + allowClear: true, + + ajax: { + url: null, + dataType: 'json', + quietMillis: 250, + cache: true, + + data: function(term, page) { + return { + name__icontains: term, + page: page, + } + }, + + results: function(data, page) { + return { + results: _.map(function(item) { + return { + id: item.id, + text: _.repeat(item.level-1, '  ') + item.name, + origItem: item, + } + }, data.results), + + more: (page * API_PAGE_SIZE) < data.count, + } + }, + }, + } + + + $('.-spec-work-type-combo-container').each(function(i, container) { + var $container = $(container) + var $specSelects = $container.find('.-spec-select') + var $chosenSpecId = $container.find('.-chosen-spec-id').first() + + var $specSelect1 = $container.find('.-spec-select-level-1').first() + + window.select = $specSelect1 + + var $specSelect2 = $container.find('.-spec-select-level-2').first() + var $specSelect3 = $container.find('.-spec-select-level-3').first() + var $specSelect4 = $container.find('.-spec-select-level-4').first() + var specSelects = [$specSelect1, $specSelect2, $specSelect3, $specSelect4] + + + + + + var $workTypeSelect = $container.find('.-project-work-type-select-field').first() + + if (!_.isEmpty($workTypeSelect)) { + var workTypeId = Number($workTypeSelect.find('option:selected').first().val()) || DEFAULT_WORK_TYPE; + + $workTypeSelect.on('change', function($evt) { + var $that = $(this) + var workTypeId = Number($that.val()) + + reinitSpecializationsByWorkType(specSelects, workTypeId).then(function(rootSpec) { + updateSpecializationWidgets(workTypeId, $container, $chosenSpecId, specSelects) + }) + }) + } else { + var $workTypeRadios = $container.find('.-project-work-type-radios-container').first().find('input[type=radio]') + + if (!_.isEmpty($workTypeRadios)) { + var workTypeId = Number($workTypeRadios.filter(':checked').val()) || DEFAULT_WORK_TYPE; + + $workTypeRadios.on('change', function($evt) { + var $that = $(this) + var workTypeId = Number($that.val()) + + reinitSpecializationsByWorkType(specSelects, workTypeId).then(function(rootSpec) { + updateSpecializationWidgets(workTypeId, $container, $chosenSpecId, specSelects) + }) + }) + } else { + var workTypeId = DEFAULT_WORK_TYPE; + } + } + + + + + + + reinitSpecializationsByWorkType(specSelects, workTypeId).then(function(rootSpec_) { + updateSpecializationWidgets(null, $container, $chosenSpecId, specSelects) + }) + + + var chosenSpecId = $chosenSpecId.val() + + if (chosenSpecId) + updateSpecializationWidgets(chosenSpecId, $container, $chosenSpecId, specSelects) + + $specSelects.on('change', function($evt) { + var specId = $evt.added ? $evt.added.id : null + updateSpecializationWidgets(specId, $container, $chosenSpecId, specSelects) + }) + }) + + + function reinitSpecializationsByWorkType(specSelects, workTypeId) { + return $.get('/api/specializations/?parent__id='+workTypeId) + .then(function(res) { + var rootSpecs = res.results; + console.log('workTypeId', workTypeId) + if (workTypeId == null) { + var url = '/api/specializations/'; + // var specId = null + } else { + var specializations = {}; + $.each(rootSpecs, function(key, spec) { + specializations[spec.id] = spec; + }); + // var rootSpec = specializations[workTypeId]; + var rootSpec = rootSpecs[0].parent; + console.log('rootSpec', rootSpec); + // var specId = rootSpec.id + var url = format('/api/specializations/?lft__gte=%s&rght__lte=%s', rootSpec.lft, rootSpec.rght) + } + + specSelects[0].select2(_.merge(specSelectOptions, {ajax: {url: url}})); + specSelects[1].select2(_.merge(specSelectOptions, {ajax: {url: null}})); + specSelects[2].select2(_.merge(specSelectOptions, {ajax: {url: null}})); + specSelects[3].select2(_.merge(specSelectOptions, {ajax: {url: null}})); + + + // TODO: Hardcoded: + + // specSelects[0].select2('container').closest('.-single-spec-select').css('display', workTypeId === 2 ? 'none' : 'block'); + specSelects[1].select2('container').closest('.-single-spec-select').find('.-dynamic-label').first().text(workTypeId === 2 ? 'Перечень услуг' : 'Стадия проекта'); + + + return rootSpec + }) + } + + + function updateSpecializationWidgets(specId, $container, $chosenSpecId, specSelects) { + return getSpecializationTree(specId).then(function(specs) { + var spec = specs.spec + + specSelects[1].select2(_.merge(specSelectOptions, {ajax: {url: specs.urlLevel2}})); + specSelects[2].select2(_.merge(specSelectOptions, {ajax: {url: specs.urlLevel3}})); + specSelects[3].select2(_.merge(specSelectOptions, {ajax: {url: specs.urlLevel4}})); + + var specLevel1 = specs.specLevel1; + var specLevel2 = specs.specLevel2; + var specLevel3 = specs.specLevel3; + var specLevel4 = specs.specLevel4; + + specSelects[0].select2('data', specLevel1 ? {id: specLevel1.id, text: specLevel1.name, origItem: specLevel1} : null) + specSelects[1].select2('data', specLevel2 ? {id: specLevel2.id, text: specLevel2.name, origItem: specLevel2} : null) + specSelects[2].select2('data', specLevel3 ? {id: specLevel3.id, text: specLevel3.name, origItem: specLevel3} : null) + specSelects[3].select2('data', specLevel4 ? {id: specLevel4.id, text: specLevel4.name, origItem: specLevel4} : null) + + $chosenSpecId.val(specId) + + specSelects[1].select2('enable', Boolean(specLevel1 && !_.isEmpty(specLevel1.children))) + specSelects[2].select2('enable', Boolean(specLevel2 && !_.isEmpty(specLevel2.children))) + specSelects[3].select2('enable', Boolean(specLevel3 && !_.isEmpty(specLevel3.children))) + + specializationTreeHasLevels(specId).then(function(haveLevels) { + specSelects[1].select2('container').closest('.-single-spec-select').css('display', haveLevels.level2 ? 'block' : 'none') + specSelects[2].select2('container').closest('.-single-spec-select').css('display', haveLevels.level3 ? 'block' : 'none') + specSelects[3].select2('container').closest('.-single-spec-select').css('display', haveLevels.level4 ? 'block' : 'none') + }) + }) + } +}()) + + + + + + + + + + + + + +// Specialization select (simple) ------------------------------- + + +;(function() { + var simpleSpecSelectsOptions = { + language: 'ru', + placeholder: 'Выберите специализацию', // Required by `allowClear` + allowClear: true, + + ajax: { + url: '/api/specializations/', + dataType: 'json', + quietMillis: 250, + cache: true, + + data: function(term, page) { + return { + name__icontains: term, + page: page, + } + }, + + results: function(data, page) { + return { + results: _.map(function(item) { + return { + id: item.id, + text: _.repeat(item.level-1, '  ') + item.name, + origItem: item, + } + }, data.results), + + more: (page * API_PAGE_SIZE) < data.count, + } + }, + }, + + //initSelection: function(element, callback) { + // var id = $(element).val() + // + // if (id !== '') { + // $.ajax({url: '/api/specializations/' + id + '/', method: 'GET', dataType: 'json'}) + // .then(function(data) {callback(data)}) + // } + //} + } + + var $simpleSpecContainer = $('#simpleSpecContainer') + var $emptySimpleSpecWidget = $simpleSpecContainer.find('.-simple-spec-widget').first() + + $simpleSpecContainer.find('.-chosen-simple-spec-id').each(function(i, el) { + var $el = $(el) + var specId = Number($el.val()) + + if (specId) + initSimpleSpecSelect2($el.closest('.-simple-spec-widget').find('.-simple-spec-select').first(), specId) + }) + + $('#addSpec').on('click', function($evt) { + var $newSimpleSpecWidget = $emptySimpleSpecWidget.clone() + $simpleSpecContainer.append($newSimpleSpecWidget) + var $newSimpleSpecSelect = $newSimpleSpecWidget.find('.-simple-spec-select').first() + + initSimpleSpecSelect2($newSimpleSpecSelect).then(function() { + $newSimpleSpecWidget.css('display', 'block') + }) + }) + + + $simpleSpecContainer.on('change', '.-simple-spec-select', function($evt) { + if ($evt.added) + $(this).parent().children('.-chosen-simple-spec-id').first().val($evt.added.id) + }) + + + function initSimpleSpecSelect2($select, specId) { + $select.select2(simpleSpecSelectsOptions) + + if (specId) { + return $.get(format('/api/specializations/%s/', specId)) + .then(function(spec) { + $select.select2('data', { + id: spec.id, + text: _.repeat(spec.level-1, '  ') + spec.name, + origItem: spec, + }) + }) + } else { + return $.when() + } + } +}()) + + + + + + + + + + +// Team invitation contractor select ------------------------------- + + +;(function() { + var contractorSelectOptions = { + language: 'ru', + placeholder: 'Выберите пользователя', // Required by `allowClear` + allowClear: true, + + ajax: { + url: '/api/users/', + dataType: 'json', + quietMillis: 250, + cache: true, + + data: function(term, page) { + return { + username__icontains: term, + page: page, + is_contractor: 'true', + } + }, + + results: function(data, page) { + return { + results: _.map(function(item) { + return { + id: item.id, + text: format('%s (%s)', item.username, item.get_full_name), + origItem: item, + } + }, data.results), + + more: (page * API_PAGE_SIZE) < data.count, + } + }, + }, + } + + function initContractorSelect($select, excludeIds) { + contractorSelectOptions.ajax.url = format('%s?id__in!=%s', contractorSelectOptions.ajax.url, excludeIds.join(',')) + return $select.select2(contractorSelectOptions) + } + + window.initContractorSelect = initContractorSelect +}()) + + + + + + + +// Order offer project select --------------------------------------------- + +;(function() { + var projectSelectOptions = { + language: 'ru', + placeholder: 'Выберите проект', // Required by `allowClear` + allowClear: true, + } + + function initProjectSelect($select, customerId, contractorId) { + var urlObj = new URI('/api/projects/') + + urlObj.setQuery({ + state: 'active', + customer: customerId, + order__contractor__isnull: true, + order__team__isnull: true, + x_no_contractor_answer: contractorId, + }) + + return $.get(urlObj.href()).then(function(res) { + var projects = res.results + + $select.select2(_.merge(projectSelectOptions, { + data: _.map(function(project) { + return { + id: project.id, + //text: project.name, + text: format('%s (%s)', project.name, project.id), // Tmp + origItem: project, + } + }, projects), + })) + }) + } + + window.initProjectSelect = initProjectSelect +}()) + + + + + + + +// Location select ---------------------------------------------- + + +;(function() { + var $locationSelects = $('.-location-select') + + var locationSelectOptions = { + language: 'ru', + placeholder: 'Выберите местоположение', // Required by `allowClear` + allowClear: true, + } + + + var $countrySelect = $('.-location-select-country') + var $regionSelect = $('.-location-select-region') + var $citySelect = $('.-location-select-city') + + + // Initialize: + + getLocationTree(null).then(function(locs) { + $countrySelect.select2(_.merge(locationSelectOptions, {data: locs.countries})) + $regionSelect.select2(_.merge(locationSelectOptions, {data: locs.regions})) + $citySelect.select2(_.merge(locationSelectOptions, {data: locs.cities})) + + var chosenLocId = $('#chosenLocationId').val() + + if (chosenLocId) + updateLocationWidgets(chosenLocId) + }) + + + $locationSelects.on('change', function($evt) { + updateLocationWidgets($evt.added ? $evt.added.id : null) + }) + + + function updateLocationWidgets(locId) { + return getLocationTree(locId).then(function(locs) { + $countrySelect.select2({data: locs.countries}) + $regionSelect.select2({data: locs.regions}) + $citySelect.select2({data: locs.cities}) + + var loc = locs.location + + if (loc && loc.level === 1) { + $countrySelect.select2('val', locs.country.id) + } else if (loc && loc.level === 2) { + $regionSelect.select2('val', locs.region.id) + $countrySelect.select2('val', locs.country.id) + } else if (loc && loc.level === 3) { + $citySelect.select2('val', locs.city.id) + $regionSelect.select2('val', locs.region.id) + $countrySelect.select2('val', locs.country.id) + } + + if (loc) + $('#chosenLocationId').val(loc.id) + }) + } + + + + $('#realtyId').on('change', function($evt) { + var realtyId = Number($(this).val()) + + if (realtyId) { + loadRealtyDetails(realtyId).then(function(res) { + $('#realtyName').val(res.name) + $('#realtyBuildingClassificationId').val(res.building_classification.id).change() + $('#realtyConstructionTypeId').val(res.construction_type.id).change() + updateLocationWidgets(res.location.id) + }) + } else { + $('#realtyName').val('') + $('#realtyBuildingClassificationId').val('').change() + $('#realtyConstructionTypeId').val('').change() + updateLocationWidgets(null) + } + }) + + function loadRealtyDetails(realtyId) { + return $.ajax({ + url: '/api/realties/' + realtyId + '/', + method: 'GET', + dataType: 'json', + }) + .then(function(res) {return res}) + } +}()) + + + + + + + + + + + +// File uploading --------------------------------------- + +// TODO: Add file number and overall size limit support + +;(function() { + var $fileUploadContainer = $('#fileUploadContainer') + + $('#fileUploadAddBtn').on('click', function($evt) { + $fileUploadContainer.find('.file-upload-widget').last().find('.file-upload-input').click() + }) + + $fileUploadContainer.on('change', '.file-upload-input', function($evt) { + var $fileInput = $(this) + var $fileUploadWidget = $fileInput.closest('.file-upload-widget') + var filePath = $fileInput.val().replace(/\\/g, '/') + var fileName = path.basename(filePath) + //var fileExt = path.extname(filePath) + var fileSize = $fileInput.get(0).files && humanFileSize($fileInput.get(0).files[0].size) + + if (fileName) { + $fileUploadWidget.find('.file-upload-label').text(fileName + ' ' + fileSize) + + var $newFileUploadWidget = $fileUploadWidget.clone() + $newFileUploadWidget.find('.file-upload-label').text('') + + $fileUploadContainer.find('ul').first().append($newFileUploadWidget) + + $fileUploadWidget.css('display', 'block') + } + }) + + $fileUploadContainer.on('click', '.file-upload-remove-btn', function($evt) { + var $btn = $(this) + $btn.closest('.file-upload-widget').remove() + }) + + $fileUploadContainer.on('click', '.existing-file-remove-btn', function($evt) { + var $btn = $(this) + $btn.closest('.existing-file-widget').remove() + }) +}()) + + + + + + + + + + + +// Project answer portfolio selection ---------------------- + + +$('input[type="checkbox"].answer-portfolio-select').on('change', function($evt) { + var $that = $(this) + + if ($('input[type="checkbox"].answer-portfolio-select:checked').length > 4) { + $that.prop('checked', false) + return + } + + var portfId = $that.val() + + var $container = $('#answer-portfolio-photo-widget-container') + + if ($that.prop('checked')) { + $.ajax({url: '/api/portfolios/' + portfId + '/', method: 'GET', dataType: 'json'}) + .then(function(portf) { + var photoUrl = portf.photos[0].img + var $newWidget = $container.find('.answer-portfolio-photo-widget').first().clone() + + $newWidget.data('portf-id', portfId) + + var $newWidgetPhotoCont = $newWidget.find('.answer-portfolio-photo-widget-photo-cont').first() + $newWidgetPhotoCont.css('background', 'url("' + photoUrl + '") no-repeat center') + + $container.append($newWidget) + + $newWidget.css('display', 'block') + }) + } else { + var $widgets = $container.find('.answer-portfolio-photo-widget') + var $widget = _.find(function(el) {return $(el).data('portf-id') === portfId}, $widgets) + $widget.remove() + } +}) + + +var initialPortfIds = $('input[type="checkbox"]:checked.answer-portfolio-select').map(function(i, el) {return $(el).val()}) + +loadAllPhotos(initialPortfIds) + + +function loadAllPhotos(portfIds) { + var $container = $('#answer-portfolio-photo-widget-container') + + _.each(function(portfId) { + $.ajax({url: '/api/portfolios/' + portfId + '/', method: 'GET', dataType: 'json'}) + .then(function(portf) { + var photoUrl = portf.photos[0].img + var $newWidget = $container.find('.answer-portfolio-photo-widget').first().clone() + + $newWidget.data('portf-id', portfId) + + var $newWidgetPhotoCont = $newWidget.find('.answer-portfolio-photo-widget-photo-cont').first() + $newWidgetPhotoCont.css('background', 'url("' + photoUrl + '") no-repeat center') + + $container.append($newWidget) + + $newWidget.css('display', 'block') + }) + }, portfIds) +} + + + + + + + + + +// Live image upload -------------------------------------------- + + +;(function() { + var $container = $('.-live-image-upload-container').first() + + var $liveImageUploadField = $container.find('.-live-image-upload-field').first() + var liveImageDeleteClass = '.-live-image-delete' + var $imageDeleteBtns = $('.-image-delete') + + var templ = _.template($container.find('.-templ').first().html()) + var $res = $container.find('.-res').first() + + var images = [] + + $liveImageUploadField.fileupload({ + url: LIVE_IMAGE_UPLOAD_URL, + dataType: 'json', + + done: function($evt, data) { + var image = data.result + images.push(image) + render() + } + }) + + $container.on('click', liveImageDeleteClass, function($evt) { + var $that = $(this) + var image = _.find({id: $that.data('imageId')}, images) + + $.post(image.deleteUrl).then(function(res) { + if (res.status == 'success') { + remove(images, image) + render() + } + }) + }) + + $imageDeleteBtns.on('click', function($evt) { + $(this).closest('.-image-widget').remove() + }) + + + // Helpers -------------------------------- + + function render() { + $res.html(templ({images: images})) + } +}()) + + + + + + + + + + +// Live image avatar (single) upload --------------------------------- + + +;(function() { + var $container = $('.-live-image-avatar-upload-container').first() + + var $avatarImage = $container.find('.-avatar-image').first() + var $liveImageUpload = $container.find('.-live-image-upload').first() + var $liveImageId = $container.find('.-live-image-id').first() + var $liveImageDelete = $container.find('.-live-image-delete').first() + + $avatarImage.attr('orig-src', $avatarImage.attr('src')) + + $liveImageUpload.fileupload({ + url: LIVE_IMAGE_UPLOAD_URL, + dataType: 'json', + + done: function($evt, data) { + var image = data.result + + $avatarImage.attr('src', image.thumbnailUrl) + $liveImageId.val(image.id) + + $liveImageDelete.data('url', image.deleteUrl) + $liveImageDelete.css('display', 'block') + } + }) + + $liveImageDelete.on('click', function($evt) { + var $that = $(this) + + $.post($that.data('url')).then(function(res) { + if (res.status == 'success') { + $avatarImage.attr('src', $avatarImage.attr('orig-src') || STUB_IMAGE_URL) + $liveImageId.val('') + $that.css('display', 'none') + } + }) + }) +}()) + + + + + + + + + + + +// Helpers --------------------------------------------- + + +function getSpecializationTree(specId) { + var specs = { + spec: null, + specLevel1: null, specLevel2: null, specLevel3: null, specLevel4: null, + urlLevel2: null, urlLevel3: null, urlLevel4: null, + } + + if (specId === null) { + return $.when(specs) + } else { + return $.ajax({url: '/api/specializations/' + specId + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + if (spec.level === 1) { + return _.merge(specs, { + spec: spec, + specLevel1: spec, + urlLevel2: format('/api/specializations/?lft__gt=%s&rght__lte=%s', spec.lft, spec.rght), + }) + } else if (spec.level === 2) { + var specLevel2 = spec + + return $.ajax({url: '/api/specializations/' + specLevel2.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + var specLevel1 = spec + + return _.merge(specs, { + spec: specLevel2, + specLevel1: specLevel1, + urlLevel2: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel1.lft, specLevel1.rght), + specLevel2: specLevel2, + urlLevel3: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel2.lft, specLevel2.rght), + }) + }) + } else if (spec.level === 3) { + var specLevel3 = spec + + return $.ajax({url: '/api/specializations/' + specLevel3.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + var specLevel2 = spec + + return $.ajax({url: '/api/specializations/' + specLevel2.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + var specLevel1 = spec + + return _.merge(specs, { + spec: specLevel3, + specLevel1: specLevel1, + urlLevel2: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel1.lft, specLevel1.rght), + specLevel2: specLevel2, + urlLevel3: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel2.lft, specLevel2.rght), + specLevel3: specLevel3, + urlLevel4: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel3.lft, specLevel3.rght), + }) + }) + }) + } else if (spec.level === 4) { + var specLevel4 = spec + + return $.ajax({url: '/api/specializations/' + specLevel4.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + var specLevel3 = spec + + return $.ajax({url: '/api/specializations/' + specLevel3.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + var specLevel2 = spec + + return $.ajax({url: '/api/specializations/' + specLevel2.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(spec) { + var specLevel1 = spec + + return _.merge(specs, { + spec: specLevel4, + specLevel1: specLevel1, + urlLevel2: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel1.lft, specLevel1.rght), + specLevel2: specLevel2, + urlLevel3: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel2.lft, specLevel2.rght), + specLevel3: specLevel3, + urlLevel4: format('/api/specializations/?lft__gt=%s&rght__lte=%s', specLevel3.lft, specLevel3.rght), + specLevel4: specLevel4, + }) + }) + }) + }) + } + }) + } +} + + + + + + + + + +function getLocationTree(locId) { + var locations = { + location: null, + country: null, region: null, city: null, + countries: [], regions: [], cities: [], + } + + if (locId === null) { + return $.ajax({url: '/api/locations/?level=1', method: 'GET', dataType: 'json'}) + .then(function(res) { + return _.merge(locations, { + countries: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, res.results), + }) + }) + } else { + return $.ajax({url: '/api/locations/' + locId + '/', method: 'GET', dataType: 'json'}).then(function(loc) { + locations = _.merge(locations, {location: loc}) + + if (loc.level === 1) { + var country = loc + + return $.when( + $.ajax({url: '/api/locations/?level=1', method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}), // Countries + + $.ajax({url: '/api/locations/?level=2&parent=' + country.id, method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}) // Regions + ) + .then(function(countries, regions) { + return _.merge(locations, { + country: country, + countries: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, countries), + regions: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, regions), + }) + }) + } else if (loc.level === 2) { + var region = loc + + return $.when( + $.ajax({url: '/api/locations/?level=1', method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}), // Countries + + $.ajax({url: '/api/locations/?level=2&parent=' + region.parent.id, method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}), // Regions + + $.ajax({url: '/api/locations/' + region.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(res) {return res}), // Country + + $.ajax({url: '/api/locations/?level=3&parent=' + region.id, method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}) // Cities + ) + .then(function(countries, regions, country, cities) { + return _.merge(locations, { + country: country, + region: region, + countries: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, countries), + regions: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, regions), + cities: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, cities), + }) + }) + } else if (loc.level === 3) { + var city = loc + + return $.when( + $.ajax({url: '/api/locations/?level=1', method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}), // Countries + + $.ajax({url: '/api/locations/' + city.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(res) {return res}), // Region + + $.ajax({url: '/api/locations/?level=3&parent=' + city.parent.id, method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}) // Cities + ) + .then(function(countries, region, cities) { + locations = _.merge(locations, { + region: region, + city: city, + countries: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, countries), + cities: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, cities), + }) + + return $.when( + $.ajax({url: '/api/locations/?level=2&parent=' + region.parent.id, method: 'GET', dataType: 'json'}) + .then(function(res) {return res.results}), // Regions + + $.ajax({url: '/api/locations/' + region.parent.id + '/', method: 'GET', dataType: 'json'}) + .then(function(res) {return res}) // Country + ) + .then(function(regions, country) { + return _.merge(locations, { + country: country, + regions: _.map(function(item) {return {text: item.name, id: item.id, origItem: item}}, regions), + }) + }) + }) + } + }) + } +} + + + + + + + + + + +function specializationTreeHasLevels(specId) { + if (specId == null) { + var haveLevels = { + level2: true, + level3: true, + level4: true, + } + + return $.when(haveLevels) + } else { + return $.get(format('/api/specializations/%s/', specId)).then(function(spec) { + return $.get(format('/api/specializations/?level=1&lft__lte=%s&rght__gte=%s', spec.lft, spec.rght)).then(function(res) { + var specTreeRoot = res.results[0] + + var url_ = format('/api/specializations/?lft__gt=%s&rght__lt=%s&level=%s', specTreeRoot.lft, specTreeRoot.rght) + + return $.when( + $.get(format(url_, 2)).then(function(res) {return res}), + $.get(format(url_, 3)).then(function(res) {return res}), + $.get(format(url_, 4)).then(function(res) {return res}) + ) + .then(function(resLvl1, resLvl2, resLvl3) { + var haveLevels = { + level2: !_.isEmpty(resLvl1.results), + level3: !_.isEmpty(resLvl2.results), + level4: !_.isEmpty(resLvl3.results), + } + + return haveLevels + }) + }) + }) + } +} + + + + + + + + + + + + +// Utils ----------------------------------------------- + + +var _call = Function.prototype.call +var _splice = _call.bind(Array.prototype.splice) + + +function humanFileSize(bytes, si) { + var thresh = si ? 1000 : 1024 + + if (Math.abs(bytes) < thresh) + return bytes + ' B' + + var units = si + ? ['kB','MB','GB','TB','PB','EB','ZB','YB'] + : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'] + + var u = -1 + + do { + bytes /= thresh + ++u + } while (Math.abs(bytes) >= thresh && u < units.length-1) + + return bytes.toFixed(1) + ' ' + units[u] +} + + +function format() { + var str = _.head(arguments) + var args = _.tail(arguments) + + var arg + + while (true) { + arg = args.shift() + + if (arg != null && str.search('%s') !== -1) + str = str.replace('%s', arg) + else + break + } + + return str +} + + +function remove(coll, item) { + _splice(coll, _.indexOf(item, coll), 1) + return coll +} + + +function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +}