}
sub action_test_page {
- $::request->{layout}->add_javascripts('autocomplete_customer.js');
$_[0]->render('customer_vendor/test_page');
}
$self->{template_args} ||= {};
- $::request->{layout}->add_javascripts('autocomplete_customer.js');
$::request->{layout}->add_javascripts('kivi.CustomerVendor.js');
$::request->{layout}->add_javascripts('kivi.File.js');
}
my $id = delete($params{id}) || $self->name_to_id($name);
- my $fat_set_item = delete $params{fat_set_item};
my @classes = $params{class} ? ($params{class}) : ();
push @classes, 'customer_vendor_autocomplete';
- push @classes, 'customer-vendor-picker-fat-set-item' if $fat_set_item;
my $ret =
- $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
- join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(type)) .
+ $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
+ 'data-customer-vendor-picker-data' => JSON::to_json(\%params),
+ ) .
$self->input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
- $::request->layout->add_javascripts('autocomplete_customer.js');
+ $::request->layout->add_javascripts('kivi.CustomerVendor.js');
$::request->presenter->need_reinit_widgets($id);
$self->html_tag('span', $ret, class => 'customer_vendor_picker');
+++ /dev/null
-namespace('kivi', function(k){
- "use strict";
-
- k.CustomerVendorPicker = function($real, options) {
- // short circuit in case someone double inits us
- if ($real.data("customer_vendor_picker"))
- return $real.data("customer_vendor_picker");
-
- var KEY = {
- ESCAPE: 27,
- ENTER: 13,
- TAB: 9,
- LEFT: 37,
- RIGHT: 39,
- PAGE_UP: 33,
- PAGE_DOWN: 34,
- SHIFT: 16,
- CTRL: 17,
- ALT: 18,
- };
- var CLASSES = {
- PICKED: 'customer-vendor-picker-picked',
- UNDEFINED: 'customer-vendor-picker-undefined',
- FAT_SET_ITEM: 'customer-vendor-picker-fat-set-item',
- }
- var o = $.extend({
- limit: 20,
- delay: 50,
- fat_set_item: $real.hasClass(CLASSES.FAT_SET_ITEM),
- }, options);
- var STATES = {
- PICKED: CLASSES.PICKED,
- UNDEFINED: CLASSES.UNDEFINED
- }
- var real_id = $real.attr('id');
- var $dummy = $('#' + real_id + '_name');
- var $type = $('#' + real_id + '_type');
- var $unit = $('#' + real_id + '_unit');
- var state = STATES.PICKED;
- var last_real = $real.val();
- var last_dummy = $dummy.val();
- var timer;
-
- function ajax_data(term) {
- var data = {
- 'filter.all:substr:multi::ilike': term,
- 'filter.obsolete': 0,
- current: $real.val(),
- type: $type.val(),
- };
-
- return data;
- }
-
- function set_item (item) {
- if (item.id) {
- $real.val(item.id);
- // autocomplete ui has name, ajax items have description
- $dummy.val(item.name ? item.name : item.description);
- } else {
- $real.val('');
- $dummy.val('');
- }
- state = STATES.PICKED;
- last_real = $real.val();
- last_dummy = $dummy.val();
- $real.trigger('change');
-
- if (o.fat_set_item && item.id) {
- $.ajax({
- url: 'controller.pl?action=CustomerVendor/show.json',
- data: { id: item.id, db: item.type },
- success: function(rsp) {
- $real.trigger('set_item:CustomerVendorPicker', rsp);
- },
- });
- } else {
- $real.trigger('set_item:CustomerVendorPicker', item);
- }
- annotate_state();
- }
-
- function make_defined_state () {
- if (state == STATES.PICKED) {
- annotate_state();
- return true
- } else if (state == STATES.UNDEFINED && $dummy.val() === '')
- set_item({})
- else {
- set_item({ id: last_real, name: last_dummy })
- }
- annotate_state();
- }
-
- function annotate_state () {
- if (state == STATES.PICKED)
- $dummy.removeClass(STATES.UNDEFINED).addClass(STATES.PICKED);
- else if (state == STATES.UNDEFINED && $dummy.val() === '')
- $dummy.removeClass(STATES.UNDEFINED).addClass(STATES.PICKED);
- else {
- $dummy.addClass(STATES.UNDEFINED).removeClass(STATES.PICKED);
- }
- }
-
- function handle_changed_text(callbacks) {
- $.ajax({
- url: 'controller.pl?action=CustomerVendor/ajaj_autocomplete',
- dataType: "json",
- data: $.extend( ajax_data($dummy.val()), { prefer_exact: 1 } ),
- success: function (data) {
- if (data.length == 1) {
- set_item(data[0]);
- if (callbacks && callbacks.match_one) callbacks.match_one(data[0]);
- } else if (data.length > 1) {
- state = STATES.UNDEFINED;
- if (callbacks && callbacks.match_many) callbacks.match_many(data);
- } else {
- state = STATES.UNDEFINED;
- if (callbacks &&callbacks.match_none) callbacks.match_none();
- }
- annotate_state();
- }
- });
- }
-
- $dummy.autocomplete({
- source: function(req, rsp) {
- $.ajax($.extend(o, {
- url: 'controller.pl?action=CustomerVendor/ajaj_autocomplete',
- dataType: "json",
- data: ajax_data(req.term),
- success: function (data){ rsp(data) }
- }));
- },
- select: function(event, ui) {
- set_item(ui.item);
- },
- search: function(event, ui) {
- if ((event.which == KEY.SHIFT) || (event.which == KEY.CTRL) || (event.which == KEY.ALT))
- event.preventDefault();
- }
- });
- /* In case users are impatient and want to skip ahead:
- * Capture <enter> key events and check if it's a unique hit.
- * If it is, go ahead and assume it was selected. If it wasn't don't do
- * anything so that autocompletion kicks in. For <tab> don't prevent
- * propagation. It would be nice to catch it, but javascript is too stupid
- * to fire a tab event later on, so we'd have to reimplement the "find
- * next active element in tabindex order and focus it".
- */
- /* note:
- * event.which does not contain tab events in keypressed in firefox but will report 0
- * chrome does not fire keypressed at all on tab or escape
- */
- $dummy.keydown(function(event){
- if (event.which == KEY.ENTER || event.which == KEY.TAB) {
- // if string is empty assume they want to delete
- if ($dummy.val() === '') {
- set_item({});
- return true;
- } else if (state == STATES.PICKED) {
- return true;
- }
- if (event.which == KEY.TAB) {
- event.preventDefault();
- handle_changed_text();
- }
- if (event.which == KEY.ENTER) {
- handle_changed_text({
- match_one: function(){$('#update_button').click();},
- });
- return false;
- }
- } else if ((event.which != KEY.SHIFT) && (event.which != KEY.CTRL) && (event.which != KEY.ALT)) {
- state = STATES.UNDEFINED;
- }
- });
-
- $dummy.on('paste', function(){
- setTimeout(function() {
- handle_changed_text();
- }, 1);
- });
-
- $dummy.blur(function(){
- window.clearTimeout(timer);
- timer = window.setTimeout(annotate_state, 100);
- });
-
- // now add a picker div after the original input
- var pp = {
- real: function() { return $real },
- dummy: function() { return $dummy },
- type: function() { return $type },
- set_item: set_item,
- reset: make_defined_state,
- is_defined_state: function() { return state == STATES.PICKED },
- }
- $real.data('customer_vendor_picker', pp);
- return pp;
- }
-});
-
-$(function(){
- $('input.customer_vendor_autocomplete').each(function(i,real){
- kivi.CustomerVendorPicker($(real));
- })
-});
$ctrl.prop('checked', cvar.value == 1 ? 'checked' : '');
else if ((cvar.type == 'customer') || (cvar.type == 'vendor'))
- kivi.CustomerVendorPicker($ctrl).set_item({ id: cvar.id, name: cvar.value });
+ kivi.CustomerVendor.Picker($ctrl).set_item({ id: cvar.id, name: cvar.value });
else if (cvar.type == 'part')
kivi.Part.Picker($ctrl).set_item({ id: cvar.id, name: cvar.value });
event.preventDefault();
ns.inline_report(target, event.target + '', {});
};
-});
-function local_reinit_widgets() {
- $('#cv_phone,#shipto_shiptophone,#contact_cp_phone1,#contact_cp_phone2,#contact_cp_mobile1,#contact_cp_mobile2').each(function(idx, elt) {
- kivi.CustomerVendor.init_dial_action($(elt));
+ var KEY = {
+ TAB: 9,
+ ENTER: 13,
+ SHIFT: 16,
+ CTRL: 17,
+ ALT: 18,
+ ESCAPE: 27,
+ PAGE_UP: 33,
+ PAGE_DOWN: 34,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ };
+
+ ns.Picker = function($real, options) {
+ var self = this;
+ this.o = $.extend(true, {
+ limit: 20,
+ delay: 50,
+ action: {
+ commit_none: function(){ },
+ commit_one: function(){ $('#update_button').click(); },
+ commit_many: function(){ }
+ }
+ }, $real.data('customer-vendor-picker-data'), options);
+ this.$real = $real;
+ this.real_id = $real.attr('id');
+ this.last_real = $real.val();
+ this.$dummy = $($real.siblings()[0]);
+ this.autocomplete_open = false;
+ this.state = this.STATES.PICKED;
+ this.last_dummy = this.$dummy.val();
+ this.timer = undefined;
+
+ this.init();
+ };
+
+ ns.Picker.prototype = {
+ CLASSES: {
+ PICKED: 'customer-vendor-picker-picked',
+ UNDEFINED: 'customer-vendor-picker-undefined',
+ },
+ ajax_data: function(term) {
+ return {
+ 'filter.all:substr:multi::ilike': term,
+ 'filter.obsolete': 0,
+ current: this.$real.val(),
+ type: this.o.type,
+ };
+ },
+ set_item: function(item) {
+ var self = this;
+ if (item.id) {
+ this.$real.val(item.id);
+ // autocomplete ui has name, use the value for ajax items, which contains displayable_name
+ this.$dummy.val(item.name ? item.name : item.value);
+ } else {
+ this.$real.val('');
+ this.$dummy.val('');
+ }
+ this.state = this.STATES.PICKED;
+ this.last_real = this.$real.val();
+ this.last_dummy = this.$dummy.val();
+ this.$real.trigger('change');
+
+ if (this.o.fat_set_item && item.id) {
+ $.ajax({
+ url: 'controller.pl?action=CustomerVendor/show.json',
+ data: { 'id': item.id, 'db': item.type },
+ success: function(rsp) {
+ self.$real.trigger('set_item:CustomerVendorPicker', rsp);
+ },
+ });
+ } else {
+ this.$real.trigger('set_item:CustomerVendorPicker', item);
+ }
+ this.annotate_state();
+ },
+ set_multi_items: function(data) {
+ this.run_action(this.o.action.set_multi_items, [ data ]);
+ },
+ make_defined_state: function() {
+ if (this.state == this.STATES.PICKED) {
+ this.annotate_state();
+ return true
+ } else if (this.state == this.STATES.UNDEFINED && this.$dummy.val() === '')
+ this.set_item({})
+ else {
+ this.set_item({ id: this.last_real, name: this.last_dummy })
+ }
+ this.annotate_state();
+ },
+ annotate_state: function() {
+ if (this.state == this.STATES.PICKED)
+ this.$dummy.removeClass(this.STATES.UNDEFINED).addClass(this.STATES.PICKED);
+ else if (this.state == this.STATES.UNDEFINED && this.$dummy.val() === '')
+ this.$dummy.removeClass(this.STATES.UNDEFINED).addClass(this.STATES.PICKED);
+ else {
+ this.$dummy.addClass(this.STATES.UNDEFINED).removeClass(this.STATES.PICKED);
+ }
+ },
+ handle_changed_text: function(callbacks) {
+ var self = this;
+ $.ajax({
+ url: 'controller.pl?action=CustomerVendor/ajaj_autocomplete',
+ dataType: "json",
+ data: $.extend( self.ajax_data(self.$dummy.val()), { prefer_exact: 1 } ),
+ success: function (data) {
+ if (data.length == 1) {
+ self.set_item(data[0]);
+ if (callbacks && callbacks.match_one) self.run_action(callbacks.match_one, [ data[0] ]);
+ } else if (data.length > 1) {
+ self.state = self.STATES.UNDEFINED;
+ if (callbacks && callbacks.match_many) self.run_action(callbacks.match_many, [ data ]);
+ } else {
+ self.state = self.STATES.UNDEFINED;
+ if (callbacks && callbacks.match_none) self.run_action(callbacks.match_none, [ self, self.$dummy.val() ]);
+ }
+ self.annotate_state();
+ }
+ });
+ },
+ handle_keydown: function(event) {
+ var self = this;
+ if (event.which == KEY.ENTER || event.which == KEY.TAB) {
+ // if string is empty assume they want to delete
+ if (self.$dummy.val() === '') {
+ self.set_item({});
+ return true;
+ } else if (self.state == self.STATES.PICKED) {
+ if (self.o.action.commit_one) {
+ self.run_action(self.o.action.commit_one);
+ }
+ return true;
+ }
+ if (event.which == KEY.TAB) {
+ event.preventDefault();
+ self.handle_changed_text();
+ }
+ if (event.which == KEY.ENTER) {
+ self.handle_changed_text({
+ match_none: self.o.action.commit_none,
+ match_one: self.o.action.commit_one,
+ match_many: self.o.action.commit_many
+ });
+ return false;
+ }
+ } else if (event.which == KEY.DOWN && !self.autocomplete_open) {
+ var old_options = self.$dummy.autocomplete('option');
+ self.$dummy.autocomplete('option', 'minLength', 0);
+ self.$dummy.autocomplete('search', self.$dummy.val());
+ self.$dummy.autocomplete('option', 'minLength', old_options.minLength);
+ } else if ((event.which != KEY.SHIFT) && (event.which != KEY.CTRL) && (event.which != KEY.ALT)) {
+ self.state = self.STATES.UNDEFINED;
+ }
+ },
+ init: function() {
+ var self = this;
+ this.$dummy.autocomplete({
+ source: function(req, rsp) {
+ $.ajax($.extend({}, self.o, {
+ url: 'controller.pl?action=CustomerVendor/ajaj_autocomplete',
+ dataType: "json",
+ type: 'get',
+ data: self.ajax_data(req.term),
+ success: function (data){ rsp(data) }
+ }));
+ },
+ select: function(event, ui) {
+ self.set_item(ui.item);
+ },
+ search: function(event, ui) {
+ if ((event.which == KEY.SHIFT) || (event.which == KEY.CTRL) || (event.which == KEY.ALT))
+ event.preventDefault();
+ },
+ open: function() {
+ self.autocomplete_open = true;
+ },
+ close: function() {
+ self.autocomplete_open = false;
+ }
+ });
+ this.$dummy.keydown(function(event){ self.handle_keydown(event) });
+ this.$dummy.on('paste', function(){
+ setTimeout(function() {
+ self.handle_changed_text();
+ }, 1);
+ });
+ this.$dummy.blur(function(){
+ window.clearTimeout(self.timer);
+ self.timer = window.setTimeout(function() { self.annotate_state() }, 100);
+ });
+ },
+ run_action: function(code, args) {
+ if (typeof code === 'function')
+ code.apply(this, args)
+ else
+ kivi.run(code, args);
+ },
+ clear: function() {
+ this.set_item({});
+ }
+ };
+ ns.Picker.prototype.STATES = {
+ PICKED: ns.Picker.prototype.CLASSES.PICKED,
+ UNDEFINED: ns.Picker.prototype.CLASSES.UNDEFINED
+ };
+
+ ns.reinit_widgets = function() {
+ kivi.run_once_for('input.customer_vendor_autocomplete', 'customer_vendor_picker', function(elt) {
+ if (!$(elt).data('customer_vendor_picker'))
+ $(elt).data('customer_vendor_picker', new kivi.CustomerVendor.Picker($(elt)));
+ });
+
+ $('#cv_phone,#shipto_shiptophone,#contact_cp_phone1,#contact_cp_phone2,#contact_cp_mobile1,#contact_cp_mobile2').each(function(idx, elt) {
+ kivi.CustomerVendor.init_dial_action($(elt));
+ });
+ }
+
+ ns.init = function() {
+ ns.reinit_widgets();
+ }
+
+ $(function(){
+ ns.init();
});
-}
+});
});
if (ns.Part) ns.Part.reinit_widgets();
+ if (ns.CustomerVendor) ns.CustomerVendor.reinit_widgets();
if (ns.ProjectPicker)
ns.run_once_for('input.project_autocomplete', 'project_picker', function(elt) {
kivi.ProjectPicker($(elt));
});
- if (ns.CustomerVendorPicker)
- ns.run_once_for('input.customer_vendor_autocomplete', 'customer_vendor_picker', function(elt) {
- kivi.CustomerVendorPicker($(elt));
- });
-
if (ns.ChartPicker)
ns.run_once_for('input.chart_autocomplete', 'chart_picker', function(elt) {
kivi.ChartPicker($(elt));
empty_title = LxERP.t8('New contact'),
value_key = 'cp_id',
title_key = 'full_name',
- onchange = "kivi.CustomerVendor.selectContact({onFormSet: function(){ contactsMapWidget.testInputs(); local_reinit_widgets(); }});",
+ onchange = "kivi.CustomerVendor.selectContact({onFormSet: function(){ contactsMapWidget.testInputs(); kivi.reinit_widgets(); }});",
)
%]
</td>
title_key = 'displayable_id',
with_empty = 1,
empty_title = LxERP.t8('New shipto'),
- onchange = "kivi.CustomerVendor.selectShipto({onFormSet: function(){ shiptoMapWidget.testInputs(); local_reinit_widgets(); }});",
+ onchange = "kivi.CustomerVendor.selectShipto({onFormSet: function(){ shiptoMapWidget.testInputs(); kivi.reinit_widgets(); }});",
)
%]
</td>
<br><hr>
this one will be a reinit_widget after 4s:<br>
-<span id='vendor3' class="">
-<input id="vendor3_id" class="" type="hidden" name="vendor3_id" value="">
-<input id="vendor3_id_type" type="hidden" name="" value="vendor">
+<span id='vendor3' class="customer_vendor_picker">
+<input id="vendor3_id" class="" type="hidden" name="vendor3_id" value="" data-customer-vendor-picker-data="{"type":"vendor"}">
<input id="vendor3_id_name" type="text" name="" value="">
</span>