foam-framework
Version:
MVC metaprogramming framework
222 lines (194 loc) • 6.06 kB
JavaScript
/**
* @license
* Copyright 2014 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
CLASS({
package: 'foam.ui',
name: 'AutocompleteView',
extends: 'foam.ui.PopupView',
help: 'Default autocomplete popup.',
requires: [
'foam.ui.ChoiceListView'
],
properties: [
'closeTimeout',
'autocompleter',
'completer',
'current',
{
model_: 'IntProperty',
name: 'closeTime',
units: 'ms',
help: 'Time to delay the actual close on a .close call.',
defaultValue: 200
},
{
name: 'view',
postSet: function(prev, v) {
if ( prev ) {
prev.data$.removeListener(this.complete);
prev.choices$.removeListener(this.choicesUpdate);
}
v.data$.addListener(this.complete);
v.choices$.addListener(this.choicesUpdate);
}
},
{
name: 'target',
postSet: function(prev, v) {
prev && prev.unsubscribe(['keydown'], this.onKeyDown);
v.subscribe(['keydown'], this.onKeyDown);
}
},
{
name: 'maxHeight',
defaultValue: 400
},
{
name: 'className',
defaultValue: 'autocompletePopup'
}
],
methods: {
autocomplete: function(partial) {
if ( ! this.completer ) {
var proto = this.X.lookup(this.autocompleter);
this.completer = proto.create(null, this.Y);
}
if ( ! this.view ) {
this.view = this.makeView();
}
this.current = partial;
this.open(this.target);
this.completer.autocomplete(partial);
},
makeView: function() {
return this.ChoiceListView.create({
dao: this.completer.autocompleteDao$Proxy,
extraClassName: 'autocomplete',
orientation: 'vertical',
mode: 'final',
objToChoice: this.completer.f,
useSelection: true
}, this.Y);
},
init: function(args) {
this.SUPER(args);
this.subscribe('blur', (function() {
this.close();
}).bind(this));
},
open: function(e, opt_delay) {
if ( this.closeTimeout ) {
this.X.clearTimeout(this.closeTimeout);
this.closeTimeout = 0;
}
if ( this.$ ) { this.position(this.$.firstElementChild, e.$ || e); return; }
var parentNode = e.$ || e;
var document = parentNode.ownerDocument;
console.assert( this.X.document === document, 'X.document is not global document');
var div = document.createElement('div');
var window = document.defaultView;
console.assert( this.X.window === window, 'X.window is not global window');
parentNode.insertAdjacentHTML('afterend', this.toHTML().trim());
this.position(this.$.firstElementChild, parentNode);
this.initHTML();
},
close: function(opt_now) {
if ( opt_now ) {
if ( this.closeTimeout ) {
this.X.clearTimeout(this.closeTimeout);
this.closeTimeout = 0;
}
this.SUPER();
return;
}
if ( this.closeTimeout ) return;
var realClose = this.SUPER;
var self = this;
this.closeTimeout = this.X.setTimeout(function() {
self.closeTimeout = 0;
realClose.call(self);
}, this.closeTime);
},
position: function(div, parentNode) {
var document = parentNode.ownerDocument;
var pos = findPageXY(parentNode);
var pageWH = [document.firstElementChild.offsetWidth, document.firstElementChild.offsetHeight];
if ( pageWH[1] - (pos[1] + parentNode.offsetHeight) < (this.height || this.maxHeight || 400) ) {
div.style.bottom = parentNode.offsetHeight;
document.defaultView.innerHeight - pos[1];
}
if ( pos[2].offsetWidth - pos[0] < 600 )
div.style.left = 600 - pos[2].offsetWidth;
else
div.style.left = -parentNode.offsetWidth;
if ( this.width ) div.style.width = this.width + 'px';
if ( this.height ) div.style.height = this.height + 'px';
if ( this.maxWidth ) {
div.style.maxWidth = this.maxWidth + 'px';
div.style.overflowX = 'auto';
}
if ( this.maxHeight ) {
div.style.maxHeight = this.maxHeight + 'px';
div.style.overflowY = 'auto';
}
}
},
listeners: [
{
name: 'onKeyDown',
code: function(_, __, e) {
if ( ! this.view ) return;
if ( e.keyCode === 38 /* arrow up */ ) {
this.view.index--;
this.view.scrollToSelection(this.$);
e.preventDefault();
} else if ( e.keyCode === 40 /* arrow down */ ) {
this.view.index++;
this.view.scrollToSelection(this.$);
e.preventDefault();
} else if ( e.keyCode === 13 /* enter */ ) {
this.view.commit();
e.preventDefault();
}
}
},
{
name: 'complete',
code: function() {
this.target.onAutocomplete(this.view.data);
this.view = this.makeView();
this.close(true);
}
},
{
name: 'choicesUpdate',
code: function() {
if ( this.view &&
( this.view.choices.length === 0 ||
( this.view.choices.length === 1 &&
this.view.choices[0][1] === this.current ) ) ) {
this.close(true);
}
}
}
],
templates: [
function toHTML() {/*
<span id="<%= this.id %>" style="position:relative"><div <%= this.cssClassAttr() %> style="position:absolute"><%= this.view %></div></span>
*/}
]
});