+      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=Part/show.json',
+          data: { 'part.id': item.id },
+          success: function(rsp) {
+            self.$real.trigger('set_item:PartPicker', rsp);
+          },
+        });
+      } else {
+        this.$real.trigger('set_item:PartPicker', 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=Part/ajax_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();
+        }
+      });
+    },
+    /*  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
+     */
+    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) {
+          event.preventDefault();
+          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;
+      }
+    },
+    open_dialog: function() {
+      if (this.o.multiple) {
+        this.dialog = new ns.PickerMultiPopup(this);
+      } else {
+        this.dialog = new ns.PickerPopup(this);
+      }
+    },
+    close_dialog: function() {
+      this.dialog.close_dialog();
+      this.dialog = undefined;
+    },
+    init: function() {
+      var self = this;
+      this.$dummy.autocomplete({
+        source: function(req, rsp) {
+          $.ajax($.extend(self.o, {
+            url:      'controller.pl?action=Part/ajax_autocomplete',
+            dataType: "json",
+            data:     self.ajax_data(req.term),
+            success:  function (data){ rsp(data) }
+          }));
+        },
+        select: function(event, ui) {
+          self.set_item(ui.item);
+          if (self.o.action.commit_one) {
+            self.run_action(self.o.action.commit_one);
+          }
+        },
+        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);
+      });
+
+      var popup_button = $('<span>').addClass('ppp_popup_button');
+      this.$dummy.after(popup_button);
+      popup_button.click(function() { self.open_dialog() });
+    },
+    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.PickerPopup = function(pp) {
+    this.timer = undefined;
+    this.pp    = pp;
+    this.open_dialog();
+  };