Extended autocompletion with customized templates

This commit is contained in:
Severin Münger
2013-09-12 17:15:05 +02:00
parent 27023283e7
commit 162735b328
12 changed files with 260 additions and 66 deletions

View File

@@ -34,6 +34,8 @@ feature {NONE} -- Initialization
-- router.map (create {WSF_URI_MAPPING}.make ("/hello", create {WSF_AGENT_URI_HANDLER}.make (agent execute_hello)))
map_agent_uri ("/", agent execute_hello, Void)
map_agent_uri ("/widget.js", agent load_js, Void)
map_agent_uri ("/widget.css", agent load_css, Void)
map_agent_uri ("/bootstrap.min.css", agent load_bootstrap, Void)
end
feature -- Helper: mapping
@@ -63,4 +65,20 @@ feature -- Execution
res.send (f)
end
load_css (req: WSF_REQUEST; res: WSF_RESPONSE)
local
f: WSF_FILE_RESPONSE
do
create f.make_html ("widget.css")
res.send (f)
end
load_bootstrap (req: WSF_REQUEST; res: WSF_RESPONSE)
local
f: WSF_FILE_RESPONSE
do
create f.make_html ("bootstrap.min.css")
res.send (f)
end
end

9
examples/widgetapp/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
note
description: "Summary description for {FLAG_AUTOCOMPLETION}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
FLAG_AUTOCOMPLETION
inherit
WSF_AUTOCOMPLETION
create
make
feature {NONE} -- Initialization
make (l: ITERABLE [TUPLE [STRING, STRING]])
do
template := "<img src=%"http://www.famfamfam.com/lab/icons/flags/icons/gif/{{=flag}}.gif%"> {{=value}}";
list := l
end
feature -- Implementation
autocompletion (input: STRING): JSON_ARRAY
local
o: JSON_OBJECT
do
create Result.make_array
across
list as c
loop
if attached {STRING} c.item.item (1) as first and attached {STRING} c.item.item (2) as second then
if second.as_lower.has_substring (input.as_lower) then
create o.make
o.put (create {JSON_STRING}.make_json (first), "flag")
o.put (create {JSON_STRING}.make_json (second), "value")
Result.add (o)
end
end
end
end
list: ITERABLE [TUPLE [STRING, STRING]]
end

View File

@@ -21,10 +21,14 @@ feature
form: WSF_FORM_CONTROL
n1_container: WSF_FORM_ELEMENT_CONTROL [STRING]
n2_container: WSF_FORM_ELEMENT_CONTROL [STRING]
n3_container: WSF_FORM_ELEMENT_CONTROL [STRING]
cats_container: WSF_FORM_ELEMENT_CONTROL [LIST [STRING]]
s: FLAG_AUTOCOMPLETION
do
create s.make(<<["dz", "Algeria"], ["be", "Belgium"] , ["ca", "Canada"],["de", "Deutschland"], ["england", "England"], ["fi", "Finland"], ["gr", "Greece"], ["hu", "Hungary"]>>)
create textbox1.make_input ("txtBox1", "1")
create textbox2.make_input ("txtBox2", "2")
create autocompletion1.make_autocomplete ("autocompletion1", s)
create button1.make_button ("sample_button1", "SUM")
create textbox_result.make_html ("txtBox3", "p", "")
button1.set_click_event (agent handle_click)
@@ -39,8 +43,10 @@ feature
n1_container.add_validator (create {OWN_VALIDATOR}.make_own)
create n2_container.make_form_element ("Number2", textbox2)
n2_container.add_validator (create {WSF_DECIMAL_VALIDATOR}.make_decimal_validator ("Invalid Number"))
create n3_container.make_form_element ("Autoc1", autocompletion1)
form.add_control (n1_container)
form.add_control (n2_container)
form.add_control (n3_container)
create cats_container.make_form_element ("Categories", cklist)
cats_container.add_validator (create {WSF_MIN_VALIDATOR [STRING]}.make_min_validator (1, "Choose at least one category"))
cats_container.add_validator (create {WSF_MAX_VALIDATOR [STRING]}.make_max_validator (1, "Choose at most one category"))
@@ -82,6 +88,8 @@ feature
textbox2: WSF_INPUT_CONTROL
autocompletion1: WSF_AUTOCOMPLETE_CONTROL
cklist: WSF_CHECKBOX_LIST_CONTROL
textbox_result: WSF_HTML_CONTROL

View File

@@ -1,9 +1,22 @@
cache = {}
template = tmpl = (str, data) ->
# Simple JavaScript Templating
# John Resig - http://ejohn.org/ - MIT Licensed
fn = (if not /\W/.test(str) then cache[str] = cache[str] or tmpl(str) else new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + "with(obj){p.push('" + str.replace(/[\r\t\n]/g, " ").split("{{").join("\t").replace(/((^|}})[^\t]*)'/g, "$1\r").replace(/\t=(.*?)}}/g, "',$1,'").split("\t").join("');").split("}}").join("p.push('").split("\r").join("\\'") + "');}return p.join('');"))
(if data then fn(data) else fn)
Mini =
compile:(t)->
{
render:template(t)
}
trigger_callback = (control_name,event)->
$.ajax
data:
control_name: control_name
event: event
states: JSON.stringify(states)
states: JSON.stringify(window.states)
cache: no
.done (new_states)->
#Update all classes
@@ -118,11 +131,21 @@ class WSF_TEXTAREA_CONTROL extends WSF_INPUT_CONTROL
class WSF_AUTOCOMPLETE_CONTROL extends WSF_INPUT_CONTROL
attach_events: () ->
super
self = @
@$el.typeahead({
name: @control_name
local: ["one",
"two",
"three"]
template: window.states[@control_name]['template']
engine: Mini
remote:
url:""
replace: (url, uriEncodedQuery) ->
window.states[self.control_name]['text'] = self.$el.val()
'?' + $.param
control_name: self.control_name
event: 'autocomplete'
states: JSON.stringify(window.states)
filter: (parsedResponse) ->
parsedResponse[self.control_name]['suggestions']
})
class WSF_CHECKBOX_CONTROL extends WSF_CONTROL

View File

@@ -0,0 +1,77 @@
/* ignore this line */
.container { margin:30px; }
.twitter-typeahead {
width: 100%;
position: relative;
}
.twitter-typeahead .tt-query,
.twitter-typeahead .tt-hint {
margin-bottom: 0;
width:100%;
height: 34px;
position: absolute;
top:0;
left:0;
}
.twitter-typeahead .tt-hint {
color:#a1a1a1;
z-index: 1;
padding: 6px 12px;
border:1px solid transparent;
}
.twitter-typeahead .tt-query {
z-index: 2;
border-radius: 4px!important;
/* add these 2 statements if you have an appended input group */
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
/* add these 2 statements if you have an prepended input group */
/* border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important; */
}
.tt-dropdown-menu {
min-width: 160px;
margin-top: 2px;
padding: 5px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.tt-suggestion {
display: block;
padding: 3px 20px;
}
.tt-suggestion.tt-is-under-cursor {
color: #fff;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)
}
.tt-suggestion.tt-is-under-cursor a {
color: #fff;
}
.tt-suggestion p {
margin: 0;

View File

@@ -1,15 +1,35 @@
// Generated by CoffeeScript 1.6.1
(function() {
var $el, WSF_BUTTON_CONTROL, WSF_CHECKBOX_CONTROL, WSF_CHECKBOX_LIST_CONTROL, WSF_CONTROL, WSF_FORM_ELEMENT_CONTROL, WSF_HTML_CONTROL, WSF_INPUT_CONTROL, WSF_MAX_VALIDATOR, WSF_MIN_VALIDATOR, WSF_REGEXP_VALIDATOR, WSF_TEXTAREA_CONTROL, WSF_VALIDATOR, controls, name, state, trigger_callback, type, typemap, validatormap, _ref, _ref1, _ref2,
var $el, Mini, WSF_AUTOCOMPLETE_CONTROL, WSF_BUTTON_CONTROL, WSF_CHECKBOX_CONTROL, WSF_CHECKBOX_LIST_CONTROL, WSF_CONTROL, WSF_FORM_ELEMENT_CONTROL, WSF_HTML_CONTROL, WSF_INPUT_CONTROL, WSF_MAX_VALIDATOR, WSF_MIN_VALIDATOR, WSF_REGEXP_VALIDATOR, WSF_TEXTAREA_CONTROL, WSF_VALIDATOR, cache, controls, name, state, template, tmpl, trigger_callback, type, typemap, validatormap, _ref, _ref1, _ref2,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
cache = {};
template = tmpl = function(str, data) {
var fn;
fn = (!/\W/.test(str) ? cache[str] = cache[str] || tmpl(str) : new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + "with(obj){p.push('" + str.replace(/[\r\t\n]/g, " ").split("{{").join("\t").replace(/((^|}})[^\t]*)'/g, "$1\r").replace(/\t=(.*?)}}/g, "',$1,'").split("\t").join("');").split("}}").join("p.push('").split("\r").join("\\'") + "');}return p.join('');"));
if (data) {
return fn(data);
} else {
return fn;
}
};
Mini = {
compile: function(t) {
return {
render: template(t)
};
}
};
trigger_callback = function(control_name, event) {
return $.ajax({
data: {
control_name: control_name,
event: event,
states: JSON.stringify(states)
states: JSON.stringify(window.states)
},
cache: false
}).done(function(new_states) {
@@ -228,75 +248,46 @@
return WSF_TEXTAREA_CONTROL.__super__.constructor.apply(this, arguments);
}
WSF_TEXTAREA_CONTROL.prototype.attach_events = function() {
return WSF_TEXTAREA_CONTROL;
})(WSF_INPUT_CONTROL);
WSF_AUTOCOMPLETE_CONTROL = (function(_super) {
__extends(WSF_AUTOCOMPLETE_CONTROL, _super);
function WSF_AUTOCOMPLETE_CONTROL() {
return WSF_AUTOCOMPLETE_CONTROL.__super__.constructor.apply(this, arguments);
}
WSF_AUTOCOMPLETE_CONTROL.prototype.attach_events = function() {
var self;
WSF_AUTOCOMPLETE_CONTROL.__super__.attach_events.apply(this, arguments);
self = this;
return this.$el.change(function() {
return self.change();
return this.$el.typeahead({
name: this.control_name,
template: window.states[this.control_name]['template'],
engine: Mini,
remote: {
url: "",
replace: function(url, uriEncodedQuery) {
window.states[self.control_name]['text'] = self.$el.val();
return '?' + $.param({
control_name: self.control_name,
event: 'autocomplete',
states: JSON.stringify(window.states)
});
},
filter: function(parsedResponse) {
return parsedResponse[self.control_name]['suggestions'];
}
}
});
};
WSF_TEXTAREA_CONTROL.prototype.change = function() {
window.states[this.control_name]['text'] = this.$el.val();
if (window.states[this.control_name]['callback_change']) {
trigger_callback(this.control_name, 'change');
}
return this.trigger('change');
};
return WSF_AUTOCOMPLETE_CONTROL;
WSF_TEXTAREA_CONTROL.prototype.value = function() {
return this.$el.val();
};
WSF_TEXTAREA_CONTROL.prototype.update = function(state) {
if (state.text != null) {
window.states[this.control_name]['text'] = state.text;
return this.$el.val(state.text);
}
};
return WSF_TEXTAREA_CONTROL;
})(WSF_CONTROL);
WSF_TEXTAREA_CONTROL = (function(_super) {
__extends(WSF_TEXTAREA_CONTROL, _super);
function WSF_TEXTAREA_CONTROL() {
return WSF_TEXTAREA_CONTROL.__super__.constructor.apply(this, arguments);
}
WSF_TEXTAREA_CONTROL.prototype.attach_events = function() {
var self;
self = this;
return this.$el.change(function() {
return self.change();
});
};
WSF_TEXTAREA_CONTROL.prototype.change = function() {
window.states[this.control_name]['text'] = this.$el.val();
if (window.states[this.control_name]['callback_change']) {
trigger_callback(this.control_name, 'change');
}
return this.trigger('change');
};
WSF_TEXTAREA_CONTROL.prototype.value = function() {
return this.$el.val();
};
WSF_TEXTAREA_CONTROL.prototype.update = function(state) {
if (state.text != null) {
window.states[this.control_name]['text'] = state.text;
return this.$el.val(state.text);
}
};
return WSF_TEXTAREA_CONTROL;
})(WSF_CONTROL);
})(WSF_INPUT_CONTROL);
WSF_CHECKBOX_CONTROL = (function(_super) {
@@ -476,6 +467,7 @@
"WSF_BUTTON_CONTROL": WSF_BUTTON_CONTROL,
"WSF_INPUT_CONTROL": WSF_INPUT_CONTROL,
"WSF_TEXTAREA_CONTROL": WSF_TEXTAREA_CONTROL,
"WSF_AUTOCOMPLETE_CONTROL": WSF_AUTOCOMPLETE_CONTROL,
"WSF_CHECKBOX_CONTROL": WSF_CHECKBOX_CONTROL,
"WSF_FORM_ELEMENT_CONTROL": WSF_FORM_ELEMENT_CONTROL,
"WSF_HTML_CONTROL": WSF_HTML_CONTROL,

View File

@@ -20,6 +20,7 @@
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf"/>
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf"/>
<cluster name="widgetapp" location=".\" recursive="true"/>
</target>
<target name="widgetapp_cgi" extends="common">

View File

@@ -13,4 +13,6 @@ feature
deferred
end
template: detachable STRING
end

View File

@@ -16,7 +16,7 @@ create
feature {NONE}
make (l: LIST [STRING])
make (l: ITERABLE [STRING])
do
list := l
end
@@ -41,6 +41,6 @@ feature -- Implementation
feature
list: LIST [STRING]
list: ITERABLE [STRING]
end

View File

@@ -11,7 +11,8 @@ inherit
WSF_INPUT_CONTROL
redefine
handle_callback
handle_callback,
state
end
create
@@ -22,12 +23,24 @@ feature {NONE} -- Creation
make_autocomplete (n: STRING; c: WSF_AUTOCOMPLETION)
do
make_autocomplete_with_agent (n, agent c.autocompletion)
if attached c.template as t then
template := t
end
end
make_autocomplete_with_agent (n: STRING; c: FUNCTION [ANY, TUPLE [STRING], JSON_ARRAY])
do
make_input (n, "")
create_json_list := c
template := "{{=value}}"
end
feature -- State
state: JSON_OBJECT
do
Result := Precursor {WSF_INPUT_CONTROL}
Result.put (create {JSON_STRING}.make_json (template), create {JSON_STRING}.make_json ("template"))
end
feature -- Callback
@@ -44,4 +57,6 @@ feature -- Autocomplete
create_json_list: FUNCTION [ANY, TUPLE [STRING], JSON_ARRAY]
template: STRING
end

View File

@@ -77,7 +77,8 @@ feature
create states.make
control.read_state (states)
data := "<html><head>"
data.append ("<link href=%"//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css%" rel=%"stylesheet%">")
data.append ("<link href=%"/bootstrap.min.css%" rel=%"stylesheet%">")
data.append ("<link href=%"/widget.css%" rel=%"stylesheet%">")
data.append ("</head><body>")
data.append (control.render)
data.append ("<script type=%"text/javascript%">window.states=")