Use the right autocompleter.js...

This commit is contained in:
2020-04-12 18:43:44 +01:00
parent c8d0c0d5d0
commit 2bf643cd7a
10 changed files with 469 additions and 27 deletions

View File

@@ -1 +0,0 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).autocomplete=t()}(this,(function(){"use strict";return function(e){var t,n,o=document,i=o.createElement("div"),r=i.style,f=navigator.userAgent,l=-1!==f.indexOf("Firefox")&&-1!==f.indexOf("Mobile"),a=e.debounceWaitMs||0,u=e.preventSubmit||!1,s=l?"input":"keyup",d=[],c="",p=2,v=e.showOnFocus,m=0;if(void 0!==e.minLength&&(p=e.minLength),!e.input)throw new Error("input undefined");var g=e.input;function h(){n&&window.clearTimeout(n)}function E(){return!!i.parentNode}function w(){var e;m++,d=[],c="",t=void 0,(e=i.parentNode)&&e.removeChild(i)}function L(){for(;i.firstChild;)i.removeChild(i.firstChild);var n=function(e,t){var n=o.createElement("div");return n.textContent=e.label||"",n};e.render&&(n=e.render);var f=function(e,t){var n=o.createElement("div");return n.textContent=e,n};e.renderGroup&&(f=e.renderGroup);var l=o.createDocumentFragment(),a="#9?$";if(d.forEach((function(o){if(o.group&&o.group!==a){a=o.group;var i=f(o.group,c);i&&(i.className+=" group",l.appendChild(i))}var r=n(o,c);r&&(r.addEventListener("click",(function(t){e.onSelect(o,g),w(),t.preventDefault(),t.stopPropagation()})),o===t&&(r.className+=" selected"),l.appendChild(r))})),i.appendChild(l),d.length<1){if(!e.emptyMsg)return void w();var u=o.createElement("div");u.className="empty",u.textContent=e.emptyMsg,i.appendChild(u)}i.parentNode||o.body.appendChild(i),function(){if(E()){r.height="auto",r.width=g.offsetWidth+"px";var t,n=0;f(),f(),e.customize&&t&&e.customize(g,t,i,n)}function f(){var e=o.documentElement,i=e.clientTop||o.body.clientTop||0,f=e.clientLeft||o.body.clientLeft||0,l=window.pageYOffset||e.scrollTop,a=window.pageXOffset||e.scrollLeft,u=(t=g.getBoundingClientRect()).top+g.offsetHeight+l-i,s=t.left+a-f;r.top=u+"px",r.left=s+"px",(n=window.innerHeight-(t.top+g.offsetHeight))<0&&(n=0),r.top=u+"px",r.bottom="",r.left=s+"px",r.maxHeight=n+"px"}}(),function(){var e=i.getElementsByClassName("selected");if(e.length>0){var t=e[0],n=t.previousElementSibling;if(n&&-1!==n.className.indexOf("group")&&!n.previousElementSibling&&(t=n),t.offsetTop<i.scrollTop)i.scrollTop=t.offsetTop;else{var o=t.offsetTop+t.offsetHeight,r=i.scrollTop+i.offsetHeight;o>r&&(i.scrollTop+=o-r)}}}()}function y(){E()&&L()}function b(){y()}function x(e){e.target!==i?y():e.preventDefault()}function C(e){for(var t=e.which||e.keyCode||0,n=0,o=[38,13,27,39,37,16,17,18,20,91,9];n<o.length;n++){if(t===o[n])return}t>=112&&t<=123||40===t&&E()||k(0)}function T(n){var o=n.which||n.keyCode||0;if(38===o||40===o||27===o){var i=E();if(27===o)w();else{if(!E||d.length<1)return;38===o?function(){if(d.length<1)t=void 0;else if(t===d[0])t=d[d.length-1];else for(var e=d.length-1;e>0;e--)if(t===d[e]||1===e){t=d[e-1];break}}():function(){if(d.length<1&&(t=void 0),t&&t!==d[d.length-1]){for(var e=0;e<d.length-1;e++)if(t===d[e]){t=d[e+1];break}}else t=d[0]}(),L()}return n.preventDefault(),void(i&&n.stopPropagation())}13===o&&(t&&(e.onSelect(t,g),w()),u&&n.preventDefault())}function N(){v&&k(1)}function k(o){var i=++m,r=g.value;r.length>=p||1===o?(h(),n=window.setTimeout((function(){e.fetch(r,(function(e){m===i&&e&&(c=r,t=(d=e).length>0?d[0]:void 0,L())}),0)}),0===o?a:0)):w()}function D(){setTimeout((function(){o.activeElement!==g&&w()}),200)}return i.className="autocomplete "+(e.className||""),r.position="absolute",i.addEventListener("mousedown",(function(e){e.stopPropagation(),e.preventDefault()})),g.addEventListener("keydown",T),g.addEventListener(s,C),g.addEventListener("blur",D),g.addEventListener("focus",N),window.addEventListener("resize",b),o.addEventListener("scroll",x,!0),{destroy:function(){g.removeEventListener("focus",N),g.removeEventListener("keydown",T),g.removeEventListener(s,C),g.removeEventListener("blur",D),window.removeEventListener("resize",b),o.removeEventListener("scroll",x,!0),h(),w(),m++}}}}));

View File

@@ -0,0 +1 @@
$(document).ready((function(){function e(e){targetObject=$("#"+e.attr("id")+"-update"),update_url=$("option:selected",e).data("update_url"),""==update_url?targetObject.attr("disabled",!0):(targetObject.attr("href",update_url),targetObject.attr("disabled",!1))}clearSelectionLabel="(no selection)",$(".selectpicker").each((function(){var t={ajax:{url:$(this).data("sourceurl"),type:"GET",dataType:"json",data:{term:"{{{q}}}"}},locale:{emptyTitle:""},clearOnEmpty:!1,preprocessData:function(e){var t,a=e.length,l=[];if(l.push({text:clearSelectionLabel,value:"",data:{update_url:"",subtext:""}}),a)for(t=0;t<a;t++)l.push($.extend(!0,e[t],{text:e[t].label,value:e[t].pk,data:{update_url:e[t].update,subtext:""}}));return l}};$(this).prepend($("<option></option>").attr("value","").text(clearSelectionLabel).data("update_url","")),$(this).selectpicker().ajaxSelectPicker(t),$(this).change((function(){e($(this))})),e($(this))})),$("#modal").on("hide.bs.modal",(function(e){null!=modaltarget&&""!=modalobject&&function(e,t,a,l){e.find("option").remove(),e.append($("<option></option>").attr("value",t).text(a).data("update_url",l)),e.selectpicker("render"),e.selectpicker("refresh"),e.selectpicker("val",t),e.change()}($(modaltarget),modalobject[0].pk,modalobject[0].fields.name,modalobject[0].update_url)}))}));

186
RIGS/static/js/jquery-ui.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
$(document).ready(function() {
clearSelectionLabel = '(no selection)';
function changeSelectedValue(obj,pk,text,update_url) { //Pass in JQuery object and new parameters
//console.log('Changing selected value');
obj.find('option').remove(); //Remove all the available options
obj.append( //Add the new option
$("<option></option>")
.attr("value",pk)
.text(text)
.data('update_url',update_url)
);
obj.selectpicker('render'); //Re-render the UI
obj.selectpicker('refresh'); //Re-render the UI
obj.selectpicker('val', pk); //Set the new value to be selected
obj.change(); //Trigger the change function manually
}
function refreshUpdateHref(obj) {
//console.log('Refreshing Update URL');
targetObject = $('#'+obj.attr('id')+'-update');
update_url = $('option:selected', obj).data('update_url');
if (update_url=="") { //Probably "clear selection" has been chosen
// console.log('Trying to disable');
targetObject.attr('disabled', true);
} else {
targetObject.attr('href', update_url);
targetObject.attr('disabled', false);
}
}
$(".selectpicker").each(function() {
var options = {
ajax: {
url: $(this).data('sourceurl'),
type: 'GET',
dataType: 'json',
// Use "{{{q}}}" as a placeholder and Ajax Bootstrap Select will
// automatically replace it with the value of the search query.
data: {
term: '{{{q}}}'
}
},
locale: {
emptyTitle: ''
},
clearOnEmpty:false,
//log: 3,
preprocessData: function (data) {
var i, l = data.length, array = [];
array.push({
text: clearSelectionLabel,
value: '',
data:{
update_url: '',
subtext:''
}
});
if (l) {
for(i = 0; i < l; i++){
array.push($.extend(true, data[i], {
text: data[i]['label'],
value: data[i]['pk'],
data:{
update_url: data[i]['update'],
subtext:''
}
}));
}
}
return array;
}
};
$(this).prepend($("<option></option>")
.attr("value",'')
.text(clearSelectionLabel)
.data('update_url','')); //Add "clear selection" option
$(this).selectpicker().ajaxSelectPicker(options); //Initiaise selectPicker
$(this).change(function(){ //on change, update the edit button href
// console.log('Selectbox Changed');
refreshUpdateHref($(this));
});
refreshUpdateHref($(this)); //Ensure href is correct at the beginning
});
//When update/edit modal box submitted
$('#modal').on('hide.bs.modal', function (e) {
if (modaltarget != undefined && modalobject != "") {
//Update the selector with new values
changeSelectedValue($(modaltarget),modalobject[0]['pk'],modalobject[0]['fields']['name'],modalobject[0]['update_url']);
}
});
});

View File

@@ -1 +1,140 @@
function setupItemTable(t){objectitems=JSON.parse(t),$.each(objectitems,(function(t,e){objectitems[t]=JSON.parse(e)})),newitem=-1}function nl2br(t,e){return(t+"").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g,"$1"+(e||void 0===e?"<br />":"<br>")+"$2")}function escapeHtml(t){return $("<div/>").text(t).html()}function updatePrices(){var t=0;for(var e in objectitems){var i=objectitems[e].fields,a=i.cost*i.quantity;$("#item-"+e+" .sub-total").html(parseFloat(a).toFixed(2)).data("subtotal",a),t+=Number(a)}$("#sumtotal").text(parseFloat(t).toFixed(2));var o=t*Number($("#vat-rate").data("rate"));$("#vat").text(parseFloat(o).toFixed(2)),$("#total").text(parseFloat(t+o).toFixed(2))}$("#item-table").on("click",".item-delete",(function(){delete objectitems[$(this).data("pk")],$("#item-"+$(this).data("pk")).remove(),updatePrices()})),$("#item-table").on("click",".item-add",(function(){$("#item-form").data("pk",newitem),$("#item_name").val(""),$("#item_description").val(""),$("#item_quantity").val(""),$("#item_cost").val(""),$($(this).data("target")).modal("show")})),$("#item-table").on("click",".item-edit",(function(){var t=$(this).data("pk");$("#item-form").data("pk",t);var e=objectitems[t].fields;$("#item_name").val(e.name),$("#item_description").val(e.description),$("#item_quantity").val(e.quantity),$("#item_cost").val(e.cost),$($(this).data("target")).modal("show")})),$("body").on("submit","#item-form",(function(t){t.preventDefault();var e,i=$(this).data("pk");if($("#itemModal").modal("hide"),i==newitem--){(e=new Object).name=$("#item_name").val(),e.description=$("#item_description").val(),e.cost=$("#item_cost").val(),e.quantity=$("#item_quantity").val();var a=0;for(item in objectitems)a++;e.order=a,objectitems[i]=new Object,objectitems[i].fields=e,$("#new-item-row").clone().attr("id","item-"+i).data("pk",i).appendTo("#item-table-body"),$("#item-"+i+" .item-delete, #item-"+i+" .item-edit").data("pk",i)}else(e=objectitems[i].fields).name=$("#item_name").val(),e.description=$("#item_description").val(),e.cost=$("#item_cost").val(),e.quantity=$("#item_quantity").val(),objectitems[i].fields=e;$row=$("#item-"+i),$row.find(".name").html(escapeHtml(e.name)),$row.find(".description").html(nl2br(escapeHtml(e.description))),$row.find(".cost").html(parseFloat(e.cost).toFixed(2)),$row.find(".quantity").html(e.quantity),updatePrices()})),$("body").on("submit",".itemised_form",(function(t){$("#id_items_json").val(JSON.stringify(objectitems))}));var fixHelper=function(t,e){return e.children().each((function(){$(this).width($(this).width())})),e};$("#item-table tbody").sortable({helper:fixHelper,update:function(t,e){info=$(this).sortable("toArray"),itemorder=new Array,$.each(info,(function(t,e){pk=$("#"+e).data("pk"),objectitems[pk].fields.order=t}))}});
function setupItemTable(items_json) {
objectitems = JSON.parse(items_json)
$.each(objectitems, function (key, val) {
objectitems[key] = JSON.parse(val);
})
newitem = -1;
}
function nl2br (str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
}
function escapeHtml (str) {
return $('<div/>').text(str).html();
}
function updatePrices() {
// individual rows
var sum = 0;
for (var pk in objectitems) {
var fields = objectitems[pk].fields;
var sub = fields.cost * fields.quantity;
$('#item-' + pk + ' .sub-total').html(parseFloat(sub).toFixed(2)).data('subtotal', sub);
sum += Number(sub);
}
$('#sumtotal').text(parseFloat(sum).toFixed(2));
var vat = sum * Number($('#vat-rate').data('rate'));
$('#vat').text(parseFloat(vat).toFixed(2));
$('#total').text(parseFloat(sum + vat).toFixed(2));
}
$('#item-table').on('click', '.item-delete', function () {
delete objectitems[$(this).data('pk')]
$('#item-' + $(this).data('pk')).remove();
updatePrices();
});
$('#item-table').on('click', '.item-add', function () {
$('#item-form').data('pk', newitem);
// Set the form values
$('#item_name').val('');
$('#item_description').val('');
$('#item_quantity').val('');
$('#item_cost').val('');
$($(this).data('target')).modal('show');
});
$('#item-table').on('click', '.item-edit', function () {
// set the pk as we will need this later
var pk = $(this).data('pk');
$('#item-form').data('pk', pk);
// Set the form values
var fields = objectitems[pk].fields;
$('#item_name').val(fields.name);
$('#item_description').val(fields.description);
$('#item_quantity').val(fields.quantity);
$('#item_cost').val(fields.cost);
$($(this).data('target')).modal('show');
});
$('body').on('submit', '#item-form', function (e) {
e.preventDefault();
var pk = $(this).data('pk');
$('#itemModal').modal('hide');
var fields;
if (pk == newitem--) {
// Create the new data structure and add it on.
fields = new Object();
fields['name'] = $('#item_name').val()
fields['description'] = $('#item_description').val();
fields['cost'] = $('#item_cost').val();
fields['quantity'] = $('#item_quantity').val();
var order = 0;
for (item in objectitems) {
order++;
}
fields['order'] = order;
objectitems[pk] = new Object();
objectitems[pk]['fields'] = fields;
// Add the new table
$('#new-item-row').clone().attr('id', 'item-' + pk).data('pk', pk).appendTo('#item-table-body');
$('#item-'+pk+' .item-delete, #item-'+pk+' .item-edit').data('pk', pk)
} else {
// Existing item
// update data structure
fields = objectitems[pk].fields;
fields.name = $('#item_name').val()
fields.description = $('#item_description').val();
fields.cost = $('#item_cost').val();
fields.quantity = $('#item_quantity').val();
objectitems[pk].fields = fields;
}
// update the table
$row = $('#item-' + pk);
$row.find('.name').html(escapeHtml(fields.name));
$row.find('.description').html(nl2br(escapeHtml(fields.description)));
$row.find('.cost').html(parseFloat(fields.cost).toFixed(2));
$row.find('.quantity').html(fields.quantity);
updatePrices();
});
$('body').on('submit', '.itemised_form', function (e) {
$('#id_items_json').val(JSON.stringify(objectitems));
});
// Return a helper with preserved width of cells
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};
$("#item-table tbody").sortable({
helper: fixHelper,
update: function (e, ui) {
info = $(this).sortable("toArray");
itemorder = new Array();
$.each(info, function (key, value) {
pk = $('#' + value).data('pk');
objectitems[pk].fields.order = key;
});
}
});

View File

@@ -11,21 +11,25 @@
{% endblock %}
{% block css %}
<link rel="stylesheet" href="{% static "css/bootstrap-select.css" %}"/>
<link rel="stylesheet" href="{% static "css/ajax-bootstrap-select.css" %}"/>
{{ block.super }}
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/>
{% endblock %}
{% block preload_js %}
<script src="{% static "js/bootstrap-select.js" %}"></script>
<script src="{% static "js/ajax-bootstrap-select.js" %}"></script>
{{ block.super }}
<script src="{% static 'js/bootstrap-select.js' %}"></script>
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
{% endblock %}
{% block js %}
<script src="{% static "js/interaction.js" %}"></script>
<script src="{% static "js/modal.js" %}"></script>
<script src="{% static "js/tooltip.js" %}"></script>
{{ block.super }}
<script src="{% static 'js/jquery-ui.js' %}"></script><!--TODO optimise--->
<script src="{% static 'js/interaction.js' %}"></script>
<script src="{% static 'js/modal.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
<script src="{% static "js/autocomplete.js" %}"></script>
<script src="{% static 'js/autocompleter.js' %}"></script>
<script>
function setTime23Hours() {
@@ -145,16 +149,21 @@
{# New rig buttons #}
{% if not object.pk %}
<div class="col-md-12 card">
<div class="card-body row no-gutters align-items-center" id="is_rig-selector">
<span class="col-6" data-toggle="tooltip"
title="Anything that involves TEC kit, crew, or otherwise us providing a service to anyone.">
<button type="button" class="btn btn-primary w-75" data-is_rig="1">Rig</button>
</span>
<span class="col-6" data-toggle="tooltip"
title="Things that aren't service-based, like training, meetings and site visits.">
<button type="button" class="btn btn-info w-75" data-is_rig="0">Non-Rig</button>
</span>
<div class="col-sm-12">
<div class="card row align-items-center">
<div class="card-body" id="is_rig-selector">
<span data-toggle="tooltip"
title="Anything that involves TEC kit, crew, or otherwise us providing a service to anyone.">
<button type="button" class="btn btn-primary" data-is_rig="1">Rig</button>
</span>
<span data-toggle="tooltip"
title="Things that aren't service-based, like training, meetings and site visits.">
<button type="button" class="btn btn-info" data-is_rig="0">Non-Rig</button>
</span>
<span data-toggle="tooltip" title="Coming soon...">
<button type="button" class="btn btn-warning" data-is_rig="-1">Subhire</button>
</span>
</div>
</div>
</div>
{% endif %}

View File

@@ -324,7 +324,7 @@ class SecureAPIRequest(generic.View):
return HttpResponse(data, content_type="application/" + format)
# Supply data for autocomplete ajax request in json form
term = request.GET.get('term', None)
term = request.GET.get('q', None)
if term:
if fields is None: # Default to just name
fields = ['name']
@@ -346,7 +346,7 @@ class SecureAPIRequest(generic.View):
data = {
'pk': o.pk,
'value': o.pk,
'label': o.name,
'text': o.name,
}
try: # See if there is a valid update URL
data['update'] = reverse("%s_update" % model, kwargs={'pk': o.pk})

View File

@@ -10,9 +10,9 @@
{% endblock %}
{% block js %}
<script src="{% static "js/bootstrap-select.js" %}"></script>
<script src="{% static "js/ajax-bootstrap-select.js" %}"></script>
<script src="{% static "js/autocomplete.js" %}"></script>
<script src="{% static 'js/bootstrap-select.js' %}"></script>
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
<script src="{% static 'js/autocompleter.js' %}"></script>
<script>
function checkIfCableHidden() {
if (document.getElementById("id_is_cable").checked) {

View File

@@ -21,7 +21,7 @@ function styles(done) {
'node_modules/fullcalendar/dist/fullcalendar.print.css',
'node_modules/bootstrap-select/dist/css/bootstrap-select.css',
'node_modules/ajax-bootstrap-select/dist/css/ajax-bootstrap-select.css',
'node_modules/autocompleter/autocomplete.css',
//'node_modules/autocompleter/autocomplete.css',
'node_modules/@activix/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css'])
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
@@ -34,6 +34,9 @@ function styles(done) {
function scripts() {
return gulp.src(['RIGS/static/js/src/**/*.js',
'node_modules/jquery/dist/jquery.js',
/* JQuery Plugins */
'node_modules/jquery-ui-dist/jquery-ui.js',
'node_modules/popper.js/dist/umd/popper.js',
'node_modules/raven-js/dist/raven.js', //TODO Upgrade to Sentry
/* Bootstrap Plugins */
@@ -51,7 +54,7 @@ function scripts() {
'node_modules/bootstrap-select/dist/js/bootstrap-select.js',
'node_modules/ajax-bootstrap-select/dist/js/ajax-bootstrap-select.js',
'node_modules/konami/konami.js',
'node_modules/autocompleter/autocomplete.js',
//'node_modules/autocompleter/autocomplete.js',
'node_modules/@activix/bootstrap-datetimepicker/js/bootstrap-datetimepicker.js'])
.pipe(flatten())
.pipe(terser())

View File

@@ -14,6 +14,7 @@
"bootstrap-select": "^1.13.12",
"fullcalendar": "^3.10.1",
"jquery": "^3.4.1",
"jquery-ui-dist": "^1.12.1",
"konami": "^1.6.2",
"moment": "^2.24.0",
"popper.js": "^1.16.1",