461 lines
17 KiB
JavaScript
461 lines
17 KiB
JavaScript
/*!
|
|
* Django Autocomplete Light
|
|
*/
|
|
|
|
var yl = yl || {};
|
|
yl.functions = {};
|
|
/**
|
|
* Register your own JS function for DAL.
|
|
*
|
|
* @param name The name of your function. This should be the same as the widget
|
|
* `autocomplete_function` property value.
|
|
* @param func The callback that will initialize your custom autocomplete.
|
|
*/
|
|
yl.registerFunction = function (name, func) {
|
|
if (this.functions.hasOwnProperty(name)) {
|
|
// This function already exists to show an error and skip.
|
|
console.error('The DAL function "' + name + '" has already been registered.');
|
|
return
|
|
}
|
|
if (typeof func != 'function') {
|
|
// It's not a function kill it.
|
|
throw new Error('The custom DAL function must be a function.');
|
|
}
|
|
this.functions[name] = func;
|
|
var event = new CustomEvent('dal-function-registered.' + name, {detail: {name: name, func: func}})
|
|
window.dispatchEvent(event);
|
|
};
|
|
|
|
|
|
window.addEventListener("load", function () {
|
|
|
|
// Check if `django.jQuery` exists otherwise set `django.jQuery` to non namespaced jQuery.
|
|
window.django = window.django || {};
|
|
if (!django.hasOwnProperty('jQuery') && jQuery !== 'undefined') {
|
|
django.jQuery = jQuery;
|
|
}
|
|
|
|
(function ($) {
|
|
$.fn.getFormPrefix = function () {
|
|
/* Get the form prefix for a field.
|
|
*
|
|
* For example:
|
|
*
|
|
* $(':input[name$=owner]').getFormsetPrefix()
|
|
*
|
|
* Would return an empty string for an input with name 'owner' but would return
|
|
* 'inline_model-0-' for an input named 'inline_model-0-owner'.
|
|
*/
|
|
var parts = $(this).attr('name').split('-');
|
|
var prefix = '';
|
|
|
|
for (var i in parts) {
|
|
var testPrefix = parts.slice(0, -i).join('-');
|
|
if (!testPrefix.length) continue;
|
|
testPrefix += '-';
|
|
|
|
var result = $(':input[name^=' + testPrefix + ']')
|
|
|
|
if (result.length) {
|
|
return testPrefix;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
$.fn.getFormPrefixes = function () {
|
|
/*
|
|
* Get the form prefixes for a field, from the most specific to the least.
|
|
*
|
|
* For example:
|
|
*
|
|
* $(':input[name$=owner]').getFormPrefixes()
|
|
*
|
|
* Would return:
|
|
* - [''] for an input named 'owner'.
|
|
* - ['inline_model-0-', ''] for an input named 'inline_model-0-owner' (i.e. nested with a nested inline).
|
|
* - ['sections-0-items-0-', 'sections-0-', ''] for an input named 'sections-0-items-0-product'
|
|
* (i.e. nested multiple time with django-nested-admin).
|
|
*/
|
|
var parts = $(this).attr('name').split('-').slice(0, -1);
|
|
var prefixes = [];
|
|
|
|
for (i = 0; i < parts.length; i += 2) {
|
|
var testPrefix = parts.slice(0, -i || parts.length).join('-');
|
|
if (!testPrefix.length)
|
|
continue;
|
|
|
|
testPrefix += '-';
|
|
|
|
var result = $(':input[name^=' + testPrefix + ']')
|
|
|
|
if (result.length)
|
|
prefixes.push(testPrefix);
|
|
}
|
|
|
|
prefixes.push('');
|
|
|
|
return prefixes;
|
|
}
|
|
|
|
/*
|
|
* This ensures the Language file is loaded and passes it our jQuery.
|
|
*/
|
|
if (typeof dalLoadLanguage !== 'undefined') {
|
|
dalLoadLanguage($);
|
|
} else {
|
|
document.addEventListener('dal-language-loaded', function (e) {
|
|
// `e.lang` is the language that was loaded.
|
|
dalLoadLanguage($);
|
|
})
|
|
}
|
|
|
|
// Fire init event for yl.registerFunction() execution.
|
|
var event = new CustomEvent('dal-init-function');
|
|
document.dispatchEvent(event);
|
|
|
|
var initialized = [];
|
|
|
|
$.fn.excludeTemplateForms = function() {
|
|
// exclude elements that contain '__prefix__' in their id
|
|
// these are used by django formsets for template forms
|
|
return this.not('[id*=__prefix__]').filter(function() {
|
|
// exclude elements that contain '-empty-' in their ids
|
|
// these are used by django-nested-admin for nested template formsets
|
|
// note that the filter also ensures that 'empty' is not actually the related_name for some relation
|
|
// by ensuring that it is not surrounded by numbers on both sides
|
|
return !this.id.match(/(?<!-\d+)-empty-(?!\d+-)/);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize a field element. This function calls the registered init function
|
|
* and ensures that the element is only initialized once.
|
|
*
|
|
* @param element The field to be initialized
|
|
*/
|
|
function initialize(element) {
|
|
if (typeof element === 'undefined' || typeof element === 'number') {
|
|
element = this;
|
|
}
|
|
|
|
// Ensure element is not already initialized.
|
|
if (initialized.indexOf(element) >= 0) {
|
|
return;
|
|
}
|
|
|
|
// The DAL function to execute.
|
|
var dalFunction = $(element).attr('data-autocomplete-light-function');
|
|
|
|
if (yl.functions.hasOwnProperty(dalFunction) && typeof yl.functions[dalFunction] == 'function') {
|
|
// If the function has been registered call it.
|
|
yl.functions[dalFunction]($, element);
|
|
} else if (yl.functions.hasOwnProperty(dalFunction)) {
|
|
// If the function exists but has not been registered wait for it to be registered.
|
|
window.addEventListener('dal-function-registered.' + dalFunction, function (e) {
|
|
yl.functions[dalFunction]($, element);
|
|
})
|
|
} else {
|
|
// Otherwise notify that the function should be registered.
|
|
console.warn('Your custom DAL function "' + dalFunction + '" uses a deprecated event listener that will be removed in future versions. https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#overriding-javascript-code')
|
|
}
|
|
|
|
// Fire init event for custom function execution.
|
|
// DEPRECATED
|
|
$(element).trigger('autocompleteLightInitialize');
|
|
|
|
// Add element to the array of already initialized fields
|
|
initialized.push(element);
|
|
}
|
|
|
|
if (!window.__dal__initialize) {
|
|
window.__dal__initialize = initialize;
|
|
|
|
$(document).ready(function () {
|
|
$('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize);
|
|
});
|
|
|
|
if ('MutationObserver' in window) {
|
|
new MutationObserver(function (mutations) {
|
|
var mutationRecord;
|
|
var addedNode;
|
|
|
|
for (var i = 0; i < mutations.length; i++) {
|
|
mutationRecord = mutations[i];
|
|
|
|
if (mutationRecord.addedNodes.length > 0) {
|
|
for (var j = 0; j < mutationRecord.addedNodes.length; j++) {
|
|
addedNode = mutationRecord.addedNodes[j];
|
|
|
|
$(addedNode).find('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize);
|
|
}
|
|
}
|
|
}
|
|
|
|
}).observe(document.documentElement, {childList: true, subtree: true});
|
|
} else {
|
|
$(document).on('DOMNodeInserted', function (e) {
|
|
$(e.target).find('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize);
|
|
});
|
|
}
|
|
}
|
|
|
|
// using jQuery
|
|
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 = $.trim(cookies[i]);
|
|
// Does this cookie string begin with the name we want?
|
|
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cookieValue;
|
|
}
|
|
|
|
document.csrftoken = getCookie('csrftoken');
|
|
if (document.csrftoken === null) {
|
|
// Try to get CSRF token from DOM when cookie is missing
|
|
var $csrf = $('form :input[name="csrfmiddlewaretoken"]');
|
|
if ($csrf.length > 0) {
|
|
document.csrftoken = $csrf[0].value;
|
|
}
|
|
}
|
|
})(django.jQuery);
|
|
|
|
// Does the same thing as django's admin/js/autocomplete.js, but uses yl.jQuery.
|
|
(function ($) {
|
|
'use strict';
|
|
var init = function ($element, options) {
|
|
var settings = $.extend({
|
|
ajax: {
|
|
data: function (params) {
|
|
return {
|
|
term: params.term,
|
|
page: params.page
|
|
};
|
|
}
|
|
}
|
|
}, options);
|
|
$element.select2(settings);
|
|
};
|
|
|
|
$.fn.djangoAdminSelect2 = function (options) {
|
|
var settings = $.extend({}, options);
|
|
$.each(this, function (i, element) {
|
|
var $element = $(element);
|
|
init($element, settings);
|
|
});
|
|
return this;
|
|
};
|
|
|
|
$(function () {
|
|
// Initialize all autocomplete widgets except the one in the template
|
|
// form used when a new formset is added.
|
|
$('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2();
|
|
});
|
|
|
|
$(document).on('formset:added', (function () {
|
|
return function (event, $newFormset) {
|
|
return $newFormset.find('.admin-autocomplete').djangoAdminSelect2();
|
|
};
|
|
})(this));
|
|
}(django.jQuery));
|
|
|
|
(function ($, yl) {
|
|
yl.forwardHandlerRegistry = yl.forwardHandlerRegistry || {};
|
|
|
|
yl.registerForwardHandler = function (name, handler) {
|
|
yl.forwardHandlerRegistry[name] = handler;
|
|
};
|
|
|
|
yl.getForwardHandler = function (name) {
|
|
return yl.forwardHandlerRegistry[name];
|
|
};
|
|
|
|
function getForwardStrategy(element) {
|
|
var checkForCheckboxes = function () {
|
|
var all = true;
|
|
$.each(element, function (ix, e) {
|
|
if ($(e).attr("type") !== "checkbox") {
|
|
all = false;
|
|
}
|
|
});
|
|
return all;
|
|
};
|
|
|
|
if (element.length === 1 &&
|
|
element.attr("type") === "checkbox" &&
|
|
element.attr("value") === undefined) {
|
|
// Single checkbox without 'value' attribute
|
|
// Boolean field
|
|
return "exists";
|
|
} else if (element.length === 1 &&
|
|
element.attr("multiple") !== undefined) {
|
|
// Multiple by HTML semantics. E. g. multiple select
|
|
// Multiple choice field
|
|
return "multiple";
|
|
} else if (checkForCheckboxes()) {
|
|
// Multiple checkboxes or one checkbox with 'value' attribute.
|
|
// Multiple choice field represented by checkboxes
|
|
return "multiple";
|
|
} else {
|
|
// Other cases
|
|
return "single";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get fields with name `name` relative to `element` with considering form
|
|
* prefixes.
|
|
* @param element the element
|
|
* @param name name of the field
|
|
* @returns jQuery object with found fields or empty jQuery object if no
|
|
* field was found
|
|
*/
|
|
yl.getFieldRelativeTo = function (element, name) {
|
|
var prefixes = $(element).getFormPrefixes();
|
|
|
|
for (var i = 0; i < prefixes.length; i++) {
|
|
var fieldSelector = "[name=" + prefixes[i] + name + "]";
|
|
var field = $(fieldSelector);
|
|
|
|
if (field.length) {
|
|
return field;
|
|
}
|
|
}
|
|
|
|
return $();
|
|
};
|
|
|
|
/**
|
|
* Get field value which is put to forwarded dictionary
|
|
* @param field the field
|
|
* @returns forwarded value
|
|
*/
|
|
yl.getValueFromField = function (field) {
|
|
var strategy = getForwardStrategy(field);
|
|
var serializedField = $(field).serializeArray();
|
|
|
|
if ((serializedField == false) && ($(field).prop('disabled'))) {
|
|
$(field).prop('disabled', false);
|
|
serializedField = $(field).serializeArray();
|
|
$(field).prop('disabled', true);
|
|
}
|
|
|
|
var getSerializedFieldElementAt = function (index) {
|
|
// Return serializedField[index]
|
|
// or null if something went wrong
|
|
if (serializedField.length > index) {
|
|
return serializedField[index];
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
var getValueOf = function (elem) {
|
|
// Return elem.value
|
|
// or null if something went wrong
|
|
if (elem.hasOwnProperty("value") &&
|
|
elem.value !== undefined
|
|
) {
|
|
return elem.value;
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
var getSerializedFieldValueAt = function (index) {
|
|
// Return serializedField[index].value
|
|
// or null if something went wrong
|
|
var elem = getSerializedFieldElementAt(index);
|
|
if (elem !== null) {
|
|
return getValueOf(elem);
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
if (strategy === "multiple") {
|
|
return serializedField.map(
|
|
function (item) {
|
|
return getValueOf(item);
|
|
}
|
|
);
|
|
} else if (strategy === "exists") {
|
|
return serializedField.length > 0;
|
|
} else {
|
|
return getSerializedFieldValueAt(0);
|
|
}
|
|
};
|
|
|
|
yl.getForwards = function (element) {
|
|
var forwardElem,
|
|
forwardList,
|
|
forwardedData,
|
|
divSelector,
|
|
form;
|
|
divSelector = "div.dal-forward-conf#dal-forward-conf-for-" +
|
|
element.attr("id") + ", " +
|
|
"div.dal-forward-conf#dal-forward-conf-for_" +
|
|
element.attr("id");
|
|
form = element.length > 0 ? $(element[0].form) : $();
|
|
|
|
forwardElem =
|
|
form.find(divSelector).find('script');
|
|
if (forwardElem.length === 0) {
|
|
return;
|
|
}
|
|
try {
|
|
forwardList = JSON.parse(forwardElem.text());
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
|
|
if (!Array.isArray(forwardList)) {
|
|
return;
|
|
}
|
|
|
|
forwardedData = {};
|
|
|
|
$.each(forwardList, function (ix, field) {
|
|
var srcName, dstName;
|
|
if (field.type === "const") {
|
|
forwardedData[field.dst] = field.val;
|
|
} else if (field.type === "self") {
|
|
if (field.hasOwnProperty("dst")) {
|
|
dstName = field.dst;
|
|
} else {
|
|
dstName = "self";
|
|
}
|
|
forwardedData[dstName] = yl.getValueFromField(element);
|
|
} else if (field.type === "field") {
|
|
srcName = field.src;
|
|
if (field.hasOwnProperty("dst")) {
|
|
dstName = field.dst;
|
|
} else {
|
|
dstName = srcName;
|
|
}
|
|
var forwardedField = yl.getFieldRelativeTo(element, srcName);
|
|
|
|
if (!forwardedField.length) {
|
|
return;
|
|
}
|
|
|
|
forwardedData[dstName] = yl.getValueFromField(forwardedField);
|
|
} else if (field.type === "javascript") {
|
|
var handler = yl.getForwardHandler(field.handler);
|
|
forwardedData[field.dst || field.handler] = handler(element);
|
|
}
|
|
|
|
});
|
|
return JSON.stringify(forwardedData);
|
|
};
|
|
|
|
})(django.jQuery, yl);
|
|
}); |