Browse Source

[autocomplete] Now prefetching using typeahead webnotes/erpnext#861

version-14
Rushabh Mehta 11 years ago
parent
commit
115e185131
15 changed files with 1296 additions and 78 deletions
  1. +2
    -2
      core/doctype/communication/communication.js
  2. +2
    -0
      public/build.json
  3. +12
    -30
      public/css/typeahead.css
  4. +1142
    -0
      public/js/lib/typeahead.js
  5. +0
    -7
      public/js/lib/typeahead.min.js
  6. +1
    -1
      public/js/wn/app.js
  7. +13
    -1
      public/js/wn/assets.js
  8. +92
    -16
      public/js/wn/form/control.js
  9. +3
    -1
      public/js/wn/form/link_selector.js
  10. +1
    -0
      public/js/wn/form/script_manager.js
  11. +14
    -1
      public/js/wn/misc/utils.js
  12. +1
    -6
      webnotes/app.py
  13. +1
    -1
      webnotes/auth.py
  14. +11
    -11
      webnotes/widgets/search.py
  15. +1
    -1
      website/templates/includes/login.js

+ 2
- 2
core/doctype/communication/communication.js View File

@@ -12,10 +12,10 @@ cur_frm.cscript.onload = function(doc) {
}; };
cur_frm.fields_dict.customer.get_query = function(doc,cdt,cdn) { cur_frm.fields_dict.customer.get_query = function(doc,cdt,cdn) {
return{ query:"controllers.queries.customer_query" } }
return { query:"controllers.queries.customer_query" } }


cur_frm.fields_dict.supplier.get_query = function(doc,cdt,cdn) { cur_frm.fields_dict.supplier.get_query = function(doc,cdt,cdn) {
return{ query:"controllers.queries.supplier_query" } }
return { query:"controllers.queries.supplier_query" } }
if(doc.content) if(doc.content)
doc.content = wn.utils.remove_script_and_style(doc.content); doc.content = wn.utils.remove_script_and_style(doc.content);


+ 2
- 0
public/build.json View File

@@ -31,6 +31,7 @@
"lib/public/css/common.css", "lib/public/css/common.css",
"lib/public/css/tree_grid.css", "lib/public/css/tree_grid.css",
"lib/public/css/nprogress.css", "lib/public/css/nprogress.css",
"lib/public/css/typeahead.css",
] ]
}, },
@@ -41,6 +42,7 @@
"lib/public/js/lib/jquery/jquery.hotkeys.js", "lib/public/js/lib/jquery/jquery.hotkeys.js",
"lib/public/js/lib/center_image.js", "lib/public/js/lib/center_image.js",
"lib/public/js/lib/bootstrap.min.js", "lib/public/js/lib/bootstrap.min.js",
"lib/public/js/lib/typeahead.js",
"lib/public/js/lib/nprogress.js", "lib/public/js/lib/nprogress.js",
"lib/public/js/wn/provide.js", "lib/public/js/wn/provide.js",


+ 12
- 30
public/css/typeahead.css View File

@@ -1,40 +1,16 @@
.typeahead, .typeahead,
.tt-query, .tt-query,
.tt-hint { .tt-hint {
width: 100%;
height: 46px;
padding: 8px 12px;
font-size: 24px;
line-height: 30px;
border: 2px solid #ccc;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
outline: none;
}

.typeahead {
background-color: #fff;
}

.typeahead:focus {
border: 2px solid #0097cf;
}

.tt-query {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
} }


.tt-hint { .tt-hint {
color: #999
color: #999;
} }


.tt-dropdown-menu { .tt-dropdown-menu {
width: 422px;
margin-top: 12px;
padding: 8px 0;
width: 100%;
margin-top: 3px;
padding: 3px 0;
background-color: #fff; background-color: #fff;
border: 1px solid #ccc; border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2); border: 1px solid rgba(0, 0, 0, 0.2);
@@ -48,14 +24,20 @@


.tt-suggestion { .tt-suggestion {
padding: 3px 20px; padding: 3px 20px;
font-size: 18px;
line-height: 24px;
} }


.tt-suggestion.tt-is-under-cursor { .tt-suggestion.tt-is-under-cursor {
color: #fff; color: #fff;
background-color: #0097cf; background-color: #0097cf;
}

.tt-suggestion.tt-is-under-cursor .text-muted {
color: #ddd;
}


.input-group input.tt-query {
border-top-left-radius: 4px !important;
border-bottom-left-radius: 4px !important;
} }


.tt-suggestion p { .tt-suggestion p {


+ 1142
- 0
public/js/lib/typeahead.js
File diff suppressed because it is too large
View File


+ 0
- 7
public/js/lib/typeahead.min.js
File diff suppressed because it is too large
View File


+ 1
- 1
public/js/wn/app.js View File

@@ -19,7 +19,7 @@ wn.Application = Class.extend({
init: function() { init: function() {
this.load_startup(); this.load_startup();
}, },
load_startup: function() { load_startup: function() {
var me = this; var me = this;
if(window.app) { if(window.app) {


+ 13
- 1
public/js/wn/assets.js View File

@@ -28,9 +28,21 @@ wn.assets = {
// if version is different then clear localstorage // if version is different then clear localstorage
if(window._version_number != localStorage.getItem("_version_number")) { if(window._version_number != localStorage.getItem("_version_number")) {
localStorage.clear(); localStorage.clear();
localStorage.setItem("_version_number", window._version_number);
console.log("Cleared App Cache."); console.log("Cleared App Cache.");
} }

if(localStorage._last_load) {
var not_updated_since = new Date() - new Date(localStorage._last_load);
if(not_updated_since < 10000 || not_updated_since > 86400000) {
localStorage.clear();
console.log("Cleared localstorage");
}
} else {
localStorage.clear();
console.log("Cleared localstorage");
}
localStorage._last_load = new Date();
localStorage._version_number = window._version_number;
}, },
// check if the asset exists in // check if the asset exists in


+ 92
- 16
public/js/wn/form/control.js View File

@@ -608,19 +608,9 @@ wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
this.has_input = true; this.has_input = true;
//this.bind_change_event(); //this.bind_change_event();
var me = this; var me = this;
this.$input.on("blur", function() {
if(me.selected) {
me.selected = false;
return;
}
if(me.doctype && me.docname) {
var value = me.get_value();
if(value!==me.last_value) {
me.parse_validate_and_set_in_model(value);
}
}});
this.setup_buttons(); this.setup_buttons();
this.setup_autocomplete();
this.setup_typeahead();
//this.setup_autocomplete();
}, },
setup_buttons: function() { setup_buttons: function() {
var me = this; var me = this;
@@ -630,7 +620,7 @@ wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
new wn.ui.form.LinkSelector({ new wn.ui.form.LinkSelector({
doctype: me.df.options, doctype: me.df.options,
target: me, target: me,
txt: me.$input.val()
txt: me.get_value()
}); });
}); });


@@ -653,17 +643,102 @@ wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
this.$input_area.find(".btn-new").remove(); this.$input_area.find(".btn-new").remove();
} }
}, },
setup_autocomplete: function() {
setup_typeahead: function() {
var me = this; var me = this;
var method = "webnotes.widgets.search.search_link";
var args = {};
this.set_custom_query(args);

// custom query
if(args.query) {
method = args.query
}

var _change = function() {
var val = me.get_value();
if(me.frm && me.frm.doc) {
me.selected = true;
me.parse_validate_and_set_in_model(val);
} else {
me.$input.trigger("change");
}
}

// filter based on arguments
var filter_fn = function(r) {
if(r.exc) console.log(r.exc);
var filter_args = {};
me.set_custom_query(filter_args)
if(filter_args.filters) {
return wn.utils.filter_dict(r.results, filter_args.filters);
} else {
return r.results;
}
}
// default query args
var query_args = {
cmd: method,
txt: "%",
page_len: "9999",
doctype: me.df.options,
}
// append filter keys (needed for client-side filtering)
if(args.filters) {
query_args.search_fields = ["name"].concat(keys(args.filters));
}
this.$input.typeahead("destroy").typeahead({
name: me.df.parent + ":" + me.df.fieldname,
prefetch: {
url: "server.py?" + wn.utils.get_url_from_dict(query_args),
filter: filter_fn,
},
remote: {
url: "server.py?" + wn.utils.get_url_from_dict($.extend(query_args, {"txt": null})) + "&txt=%QUERY",
filter: filter_fn,
},
template: function(d) {
if(keys(d).length > 1) {
d.info = $.map(d, function(val, key) { return key==="name" ? null : val }).join(", ");
return repl("<p>%(value)s<br><span class='text-muted'>%(info)s</span></p>", d);
} else {
return d.value;
}
}
}).on("typeahead:selected", function(d) {
_change();
}).on("typeahead:autocompleted", function(d) {
_change();
});
this.set_input = function(val) {
me.$input.typeahead("setQuery", val || "");
}
},
setup_autocomplete: function() {
this.$input.on("blur", function() {
if(me.selected) {
me.selected = false;
return;
}
if(me.doctype && me.docname) {
var value = me.get_value();
if(value!==me.last_value) {
me.parse_validate_and_set_in_model(value);
}
}});

this.$input.autocomplete({ this.$input.autocomplete({
source: function(request, response) { source: function(request, response) {
var args = { var args = {
'txt': request.term, 'txt': request.term,
'doctype': me.df.options, 'doctype': me.df.options,
}; };

me.set_custom_query(args); me.set_custom_query(args);

return wn.call({ return wn.call({
type: "GET", type: "GET",
method:'webnotes.widgets.search.search_link', method:'webnotes.widgets.search.search_link',
@@ -690,6 +765,7 @@ wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
} }
} }
}).data('uiAutocomplete')._renderItem = function(ul, item) { }).data('uiAutocomplete')._renderItem = function(ul, item) {
if(!item.label) item.label = item.value;
return $('<li></li>') return $('<li></li>')
.data('item.autocomplete', item) .data('item.autocomplete', item)
.append(repl('<a><span style="font-weight: bold;">%(label)s</span><br>\ .append(repl('<a><span style="font-weight: bold;">%(label)s</span><br>\


+ 3
- 1
public/js/wn/form/link_selector.js View File

@@ -22,7 +22,7 @@ wn.ui.form.LinkSelector = Class.extend({
"fields": [ "fields": [
{ {
fieldtype: "Data", fieldname: "txt", label: "Beginning with", fieldtype: "Data", fieldname: "txt", label: "Beginning with",
description: "You can use wildcard %"
description: "You can use wildcard %",
}, },
{ {
fieldtype: "Select", fieldname: "search_field", label: "Search With" fieldtype: "Select", fieldname: "search_field", label: "Search With"
@@ -55,6 +55,8 @@ wn.ui.form.LinkSelector = Class.extend({
} else { } else {
this.dialog.fields_dict.search_field.$wrapper.toggle(false); this.dialog.fields_dict.search_field.$wrapper.toggle(false);
} }
if(this.txt)
this.dialog.fields_dict.txt.set_input(this.txt);
this.dialog.fields_dict.search.$input.on("click", function() { this.dialog.fields_dict.search.$input.on("click", function() {
me.search(this); me.search(this);
}); });


+ 1
- 0
public/js/wn/form/script_manager.js View File

@@ -63,6 +63,7 @@ wn.ui.form.ScriptManager = Class.extend({
'options': df.options, 'options': df.options,
'fetch': fetch 'fetch': fetch
}, },
no_spinner: true,
callback: function(r) { callback: function(r) {
if(r.message=='Ok') { if(r.message=='Ok') {
if(r.fetch_values) if(r.fetch_values)


+ 14
- 1
public/js/wn/misc/utils.js View File

@@ -42,6 +42,14 @@ wn.utils = {
} else if(filters[key][0]=="not in") { } else if(filters[key][0]=="not in") {
if(filters[key][1].indexOf(d[key])!=-1) if(filters[key][1].indexOf(d[key])!=-1)
return; return;
} else if(filters[key][0]=="<") {
if (!(d[key] < filters[key])) return;
} else if(filters[key][0]=="<=") {
if (!(d[key] <= filters[key])) return;
} else if(filters[key][0]==">") {
if (!(d[key] > filters[key])) return;
} else if(filters[key][0]==">=") {
if (!(d[key] >= filters[key])) return;
} }
} else { } else {
if(d[key]!=filters[key]) return; if(d[key]!=filters[key]) return;
@@ -105,7 +113,12 @@ wn.utils = {
return args; return args;
}, },
get_url_from_dict: function(args) { get_url_from_dict: function(args) {
return encodeURIComponent($.map(args, function(val, key) { return key+"="+val; }).join("&") || "");
return $.map(args, function(val, key) {
if(val!==null)
return encodeURIComponent(key)+"="+encodeURIComponent(val);
else
return null;
}).join("&") || "";
}, },
disable_export_btn: function(btn) { disable_export_btn: function(btn) {
if(!wn.user.is_report_manager()) { if(!wn.user.is_report_manager()) {


+ 1
- 6
webnotes/app.py View File

@@ -18,11 +18,6 @@ import webnotes.webutils


local_manager = LocalManager([webnotes.local]) local_manager = LocalManager([webnotes.local])


class MyResponse(Response):
def set_cookie(self, *args, **kwargs):
print args, kwargs
super(Response, self).set_cookie(*args, **kwargs)

@Request.application @Request.application
def application(request): def application(request):
webnotes.local.request = request webnotes.local.request = request
@@ -32,7 +27,7 @@ def application(request):
webnotes.local.form_dict = webnotes._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \ webnotes.local.form_dict = webnotes._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \
for k, v in (request.form or request.args).iteritems() }) for k, v in (request.form or request.args).iteritems() })
webnotes.local._response = MyResponse()
webnotes.local._response = Response()


try: try:
webnotes.http_request = webnotes.auth.HTTPRequest() webnotes.http_request = webnotes.auth.HTTPRequest()


+ 1
- 1
webnotes/auth.py View File

@@ -57,7 +57,7 @@ class HTTPRequest:
import translate import translate
lang_list = translate.get_lang_dict() lang_list = translate.get_lang_dict()
lang_list = lang_list and lang_list.values() or [] lang_list = lang_list and lang_list.values() or []
if not lang: if not lang:
return return
if ";" in lang: # not considering weightage if ";" in lang: # not considering weightage


+ 11
- 11
webnotes/widgets/search.py View File

@@ -16,9 +16,10 @@ except ImportError:


# this is called by the Link Field # this is called by the Link Field
@webnotes.whitelist() @webnotes.whitelist()
def search_link(doctype, txt, query=None, filters=None):
search_widget(doctype, txt, query, page_len=20, filters=filters)
webnotes.response['results'] = build_for_autosuggest(webnotes.response["values"])
def search_link(doctype, txt, query=None, filters=None, page_len=20, searchfield="name"):
search_widget(doctype, txt, query, searchfield=searchfield, page_len=page_len, filters=filters)
webnotes.response['results'] = build_for_autosuggest(webnotes.response["values"], searchfield)
del webnotes.response["values"]


# this is called by the search box # this is called by the search box
@webnotes.whitelist() @webnotes.whitelist()
@@ -77,16 +78,15 @@ def get_std_fields_list(meta, key):


return ['`tab%s`.`%s`' % (meta[0].name, f.strip()) for f in sflist] return ['`tab%s`.`%s`' % (meta[0].name, f.strip()) for f in sflist]


def build_for_autosuggest(res):
def build_for_autosuggest(res, searchfield):
searchfield = [s.strip() for s in searchfield.split(",")]
results = [] results = []
for r in res: for r in res:
info = ''
if len(r) > 1:
info = ', '.join([cstr(t) for t in r[1:]])
if len(info) > 50:
info = "<span title=\"%s\">%s...</span>" % (info, info[:50])

results.append({'label':r[0], 'value':r[0], 'info':info})
out = {}
for i, s in enumerate(searchfield):
if s=="name": s="value"
out[s] = r[i]
results.append(out)
return results return results


def scrub_custom_query(query, key, txt): def scrub_custom_query(query, key, txt):


+ 1
- 1
website/templates/includes/login.js View File

@@ -123,7 +123,7 @@ login.show_forgot_password = function() {
} }


login.set_message = function(message, color) { login.set_message = function(message, color) {
wn.msgprint(html);
wn.msgprint(message);
return; return;
$('#login_message').html(message).toggle(true); $('#login_message').html(message).toggle(true);
} }

Loading…
Cancel
Save