diff --git a/frappe/__init__.py b/frappe/__init__.py index 91f2a06eaa..b42e8cc114 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json from .exceptions import * from .utils.jinja import get_jenv, get_template, render_template -__version__ = '8.0.5' +__version__ = '8.0.6' __title__ = "Frappe Framework" local = Local() diff --git a/frappe/custom/doctype/custom_script/custom_script.json b/frappe/custom/doctype/custom_script/custom_script.json index 8485f0fe86..644fcb7986 100644 --- a/frappe/custom/doctype/custom_script/custom_script.json +++ b/frappe/custom/doctype/custom_script/custom_script.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, "autoname": "CustomScript.####", @@ -24,6 +25,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "DocType", @@ -55,6 +57,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Script Type", @@ -84,8 +87,9 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, "in_standard_filter": 0, "label": "Script", "length": 0, @@ -115,6 +119,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Sample", @@ -133,19 +138,19 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-glass", "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-12-29 14:40:24.037012", - "modified_by": "Administrator", + "modified": "2017-04-03 18:07:28.138437", + "modified_by": "faris@erpnext.com", "module": "Custom", "name": "Custom Script", "owner": "Administrator", @@ -160,7 +165,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -181,7 +185,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -196,7 +199,8 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_order": "ASC", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} diff --git a/frappe/geo/country_info.json b/frappe/geo/country_info.json index d92b65afa1..deac9f2532 100644 --- a/frappe/geo/country_info.json +++ b/frappe/geo/country_info.json @@ -846,7 +846,7 @@ "currency_fraction": "Cent", "currency_fraction_units": 100, "currency_symbol": "\u20ac", - "number_format": "#,###.##", + "number_format": "# ###,##", "timezones": [ "Europe/Paris" ] diff --git a/frappe/public/build.json b/frappe/public/build.json index 1eb86dc32a..fd32fceae6 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -139,6 +139,7 @@ "public/js/frappe/ui/toolbar/search.js", "public/js/frappe/ui/toolbar/search.html", "public/js/frappe/ui/toolbar/search_header.html", + "public/js/frappe/ui/toolbar/search_utils.js", "public/js/frappe/ui/toolbar/about.js", "public/js/frappe/ui/toolbar/navbar.html", "public/js/frappe/ui/toolbar/toolbar.js", @@ -213,7 +214,7 @@ ], "js/list.min.js": [ "public/js/frappe/ui/listing.html", - + "public/js/frappe/ui/base_list.js", "public/js/frappe/model/indicator.js", @@ -238,7 +239,7 @@ "public/js/frappe/list/list_item_row_head.html", "public/js/frappe/list/list_item_subject.html", "public/js/frappe/list/list_permission_footer.html", - + "public/js/frappe/list/list_renderer.js", "public/js/frappe/views/gantt/gantt_view.js", "public/js/frappe/views/calendar/calendar.js", @@ -256,7 +257,7 @@ "public/js/frappe/views/inbox/inbox_no_result.html", "public/js/frappe/views/inbox/inbox_view_item_row.html", "public/js/frappe/views/inbox/inbox_view_item_main_head.html", - + "public/js/frappe/views/kanban/kanban_board.html", "public/js/frappe/views/kanban/kanban_column.html", "public/js/frappe/views/kanban/kanban_card.html" diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css index 15009748f7..e2fe3a6d14 100644 --- a/frappe/public/css/desk.css +++ b/frappe/public/css/desk.css @@ -677,13 +677,100 @@ fieldset[disabled] .form-control { } .search-dialog .modal-dialog { width: 768px; - height: 500px; +} +.search-dialog .search-header { + display: flex; + align-items: center; + padding: 5px; } .search-dialog .modal-body { padding: 0px 15px; } -.search-dialog input.form-control, -.search-dialog .input-group-addon { +.search-dialog .empty-state { + color: #d4d9dd; + height: 500px; + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} +.search-dialog .empty-state .status-icon { + font-size: 40px; + position: relative; + margin-bottom: 10px; +} +.search-dialog .empty-state p { + font-size: 15px; + display: block; +} +.search-dialog .empty-state .cover { + color: white; + font-size: 6px; + position: absolute; +} +@keyframes twinkle { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@-o-keyframes twinkle { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@-moz-keyframes twinkle { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@-webkit-keyframes twinkle { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +.search-dialog .twinkle-one { + -webkit-animation: twinkle 1.5s ease infinite; + -moz-animation: twinkle 1.5s ease infinite; + -o-animation: twinkle 1.5s ease infinite; + animation: twinkle 1.5s ease infinite; +} +.search-dialog .twinkle-two { + -webkit-animation: twinkle 1.5s ease infinite 0.5s; + -moz-animation: twinkle 1.5s ease infinite 0.5s; + -o-animation: twinkle 1.5s ease infinite 0.5s; + animation: twinkle 1.5s ease infinite 0.5s; +} +.search-dialog .twinkle-three { + -webkit-animation: twinkle 1.5s ease infinite 1s; + -moz-animation: twinkle 1.5s ease infinite 1s; + -o-animation: twinkle 1.5s ease infinite 1s; + animation: twinkle 1.5s ease infinite 1s; +} +.search-dialog input.form-control { border: none; border-left-style: none; } @@ -691,9 +778,6 @@ fieldset[disabled] .form-control { outline: none; box-shadow: none; } -.search-dialog .input-group-addon { - background-color: #FFF; -} .search-dialog .layout-side-section, .search-dialog .layout-main-section { height: 500px; @@ -712,6 +796,7 @@ fieldset[disabled] .form-control { align-items: center; justify-content: space-between; padding-left: 20px; + background-color: #ffffff; } .search-dialog .layout-side-section .nav li a i { visibility: hidden; @@ -719,23 +804,9 @@ fieldset[disabled] .form-control { .search-dialog .layout-side-section .nav .active i { visibility: visible; } -.search-dialog .results-area .search-intro-placeholder { - color: #d4d9dd; - height: inherit; - display: flex; - justify-content: center; - align-items: center; -} -.search-dialog .results-area .search-intro-placeholder span { - text-align: center; -} -.search-dialog .results-area .search-intro-placeholder span i { - font-size: 64px; - display: block; -} -.search-dialog .results-area .search-intro-placeholder span p { - font-size: 15px; - display: block; +.search-dialog .layout-side-section .nav .select a, +.search-dialog .layout-side-section .nav a:hover { + background-color: #f7fafc; } .search-dialog .results-area .single-link a { color: #36414c; @@ -748,6 +819,9 @@ fieldset[disabled] .form-control { font-family: 'Octicons'; content: '\f0a4'; } +.search-dialog .module-section .result { + margin-bottom: 5px; +} .search-dialog .full-list .result { margin-top: 15px; } @@ -772,6 +846,27 @@ fieldset[disabled] .form-control { .search-dialog .more-results { display: none; } +.search-dialog .result p { + margin-top: 5px; + margin-bottom: 5px; +} +.search-dialog .result .result-image { + display: inline-block; + margin-right: 10px; + height: 60px; + width: 60px; + background-color: #fafbfc; +} +.search-dialog .result .result-image .flex-text { + display: flex; + height: 60px; + align-items: center; + justify-content: center; +} +.search-dialog .result .result-image span { + font-size: 30px; + color: #d1d8dd; +} @media (max-width: 767px) { .search-dialog .modal-dialog { width: auto; @@ -785,21 +880,12 @@ fieldset[disabled] .form-control { margin: 0px; border-top: none; } - .search-dialog .layout-side-section .sidebar-menu { - margin: 30px 0px; - } } @media (min-width: 600px) { .search-dialog .results-area .back-link { display: none; } } -.result p { - margin-top: 0.2em; -} -.search-result { - margin-bottom: 24px; -} .note-editor.note-frame .note-editing-area .note-editable { color: #36414C; } diff --git a/frappe/public/css/list.css b/frappe/public/css/list.css index 8e88423d78..8bc696f113 100644 --- a/frappe/public/css/list.css +++ b/frappe/public/css/list.css @@ -420,6 +420,7 @@ .list-item input[type=checkbox] { margin: 0; margin-right: 5px; + flex: 0 0 12px; } .list-item .liked-by, .list-item .liked-by-filter-button { diff --git a/frappe/public/js/frappe/list/list_item_subject.html b/frappe/public/js/frappe/list/list_item_subject.html index 2e9a5c04dc..41a18e9e04 100644 --- a/frappe/public/js/frappe/list/list_item_subject.html +++ b/frappe/public/js/frappe/list/list_item_subject.html @@ -11,7 +11,7 @@ {{ (_liked_by.length > 99 ? "99+" : _liked_by.length) || "" }} -{{ strip_html(_title) }} diff --git a/frappe/public/js/frappe/list/list_renderer.js b/frappe/public/js/frappe/list/list_renderer.js index 51dde1da1e..fa250fd1c6 100644 --- a/frappe/public/js/frappe/list/list_renderer.js +++ b/frappe/public/js/frappe/list/list_renderer.js @@ -447,9 +447,6 @@ frappe.views.ListRenderer = Class.extend({ data._title = strip_html(data[title_field] || data.name); data._full_title = data._title; - if (data._title.length > 35) { - data._title = data._title.slice(0, 35) + '...'; - } data._workflow = null; if (this.workflow_state_fieldname) { diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js index e0ca6516fb..339aeee48d 100644 --- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js +++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js @@ -9,11 +9,6 @@ frappe.search.AwesomeBar = Class.extend({ var $input = $(element); var input = $input.get(0); - this.search = new frappe.search.UnifiedSearch(); - this.global = new frappe.search.GlobalSearch(); - this.nav = new frappe.search.NavSearch(); - this.help = new frappe.search.HelpSearch(); - this.options = []; this.global_results = []; @@ -23,32 +18,22 @@ frappe.search.AwesomeBar = Class.extend({ autoFirst: true, list: [], filter: function (text, term) { + this.get_item(text.value).boo = "foo"; return true; }, data: function (item, input) { - var label = item.label + "%%%" + item.value + "%%%" + - (item.description || "") + "%%%" + (item.index || "") - + "%%%" + (item.type || "") + "%%%" + (item.prefix || ""); return { - label: label, + label: (item.index || ""), value: item.value }; }, item: function(item, term) { - var d = item; - var parts = item.split("%%%"), - d = { label: parts[0], value: parts[1], description: parts[2], - type: parts[4], prefix: parts[5]}; - - if(d.prefix) { - var html = "" + __((d.prefix + ' ' + d.label)) + ""; - } else if(d.type) { - var html = "" + __((d.label + ' ' + d.type)) + ""; - } else { - var html = "" + __(d.label || d.value) + ""; - } + var d = this.get_item(item.value); + var name = d.prefix ? __(d.prefix + ' ' + (d.label || d.value)) : + __(d.label || d.value); + var html = '' + name + ''; if(d.description && d.value!==d.description) { - html += '
' + __(d.description) + ''; + html += '
' + __(d.description) + ''; } return $('
  • ') .data('item.autocomplete', d) @@ -56,9 +41,7 @@ frappe.search.AwesomeBar = Class.extend({ .get(0); }, sort: function(a, b) { - var a_index = a.split("%%%")[3]; - var b_index = b.split("%%%")[3]; - return (a_index - b_index); + return (b.label - a.label); } }); @@ -67,53 +50,29 @@ frappe.search.AwesomeBar = Class.extend({ var txt = value.trim().replace(/\s\s+/g, ' '); var last_space = txt.lastIndexOf(' '); me.global_results = []; - - if(txt && txt.length > 2) { - me.global.get_awesome_bar_options(txt.toLowerCase(), me); - } + // if(txt && txt.length > 1) { + // me.global.get_awesome_bar_options(txt.toLowerCase(), me); + // } var $this = $(this); clearTimeout($this.data('timeout')); $this.data('timeout', setTimeout(function(){ me.options = []; - if(txt && txt.length > 2) { + if(txt && txt.length > 1) { if(last_space !== -1) { me.set_specifics(txt.slice(0,last_space), txt.slice(last_space+1)); } + me.add_defaults(txt); me.options = me.options.concat(me.build_options(txt)); - me.build_defaults(txt); me.options = me.options.concat(me.global_results); + } else { + me.options = me.options.concat( + me.deduplicate(frappe.search.utils.get_recent_pages(txt || ""))); } - - - me.make_calculator(txt); - me.add_recent(txt || ""); me.add_help(); - // de-duplicate - var out = [], routes = []; - me.options.forEach(function(option) { - if(option.route) { - if(option.route[0] === "List" && option.route[2]) { - option.route.splice(2); - } - var str_route = (typeof option.route==='string') ? - option.route : option.route.join('/'); - if(routes.indexOf(str_route)===-1) { - out.push(option); - routes.push(str_route); - } else { - var old = routes.indexOf(str_route); - if(out[old].index > option.index) { - out[old] = option; - } - } - } else { - out.push(option); - } - }); - awesomplete.list = out; + awesomplete.list = me.options; }, 100)); }); @@ -153,20 +112,20 @@ frappe.search.AwesomeBar = Class.extend({ frappe.route(); } } + $input.val(""); }); $input.on("awesomplete-selectcomplete", function(e) { $input.val(""); }); - this.setup_recent(); - this.search.setup(); + frappe.search.utils.setup_recent(); }, add_help: function() { this.options.push({ label: __("Help on Search"), value: "Help on Search", - index: 100, + index: -10, default: "Help", onclick: function() { var txt = '\ @@ -186,142 +145,6 @@ frappe.search.AwesomeBar = Class.extend({ }); }, - add_recent: function(txt) { - var me = this; - values = []; - $.each(me.recent, function(i, doctype) { - values.push([doctype[1], ['Form', doctype[0], doctype[1]]]); - }); - - values = values.reverse(); - - $.each(frappe.route_history, function(i, route) { - if(route[0]==='Form') { - values.push([route[2], route]); - } - else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], route[0])) { - if(route[1]) { - values.push([route[1], route]); - } - } - else if(route[0]) { - values.push([frappe.route_titles[route[0]] || route[0], route]); - } - }); - - this.find(values, txt, function(match) { - out = { - route: match[1] - } - if(match[1][0]==='Form') { - if(match[1][1] !== match[1][2]) { - out.label = __(match[1][1]) + " " + match[1][2].bold(); - out.value = __(match[1][1]) + " " + match[1][2]; - } else { - out.label = __(match[1][1]).bold(); - out.value = __(match[1][1]); - } - } else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], match[1][0])) { - var type = match[1][0], label = type; - if(type==='modules') label = 'Module'; - else if(type==='query-report') label = 'Report'; - out.label = __(match[1][1]).bold() + " " + __(label); - out.value = __(match[1][1]) + " " + __(label); - } else { - out.label = match[0].bold(); - out.value = match[0]; - } - out.index = 80; - out.default = "Recent"; - return out; - }, true); - }, - - find: function(list, txt, process, prepend) { - var me = this; - $.each(list, function(i, item) { - if($.isArray(item)) { - _item = item[0]; - } else { - _item = item; - } - _item = __(_item || '').toLowerCase().replace(/-/g, " "); - if(txt===_item || _item.indexOf(txt) !== -1) { - var option = process(item); - - if(option) { - if($.isPlainObject(option)) { - option = [option]; - } - - option.forEach(function(o) { o.match = item; }); - - if(prepend) { - me.options = option.concat(me.options); - } else { - me.options = me.options.concat(option); - } - } - } - }); - }, - - setup_recent: function() { - this.recent = JSON.parse(frappe.boot.user.recent || "[]") || []; - }, - - fuzzy_search: function(_txt, _item) { - parsed_item = __(_item || '').replace(/-|_/g, " "); - item = parsed_item.toLowerCase(); - txt = _txt.toLowerCase(); - - var ilen = item.length; - var tlen = txt.length; - var match_level = tlen/ilen; - var rendered_label = ""; - var i, j, skips = 0, mismatches = 0; - - if(tlen > ilen) { - return []; - } - if(parsed_item.indexOf(_txt) !== -1 && txt !== _txt) { - var regEx = new RegExp("("+ txt +")", "ig"); - rendered_label = parsed_item.replace(regEx, '$1'); - return [parsed_item, (ilen + 10), rendered_label]; - } - if(item.indexOf(txt) !== -1) { - var regEx = new RegExp("("+ txt +")", "ig"); - rendered_label = parsed_item.replace(regEx, '$1'); - return [parsed_item, 20 + (ilen + 10), rendered_label]; - } - outer: for (i = 0, j = 0; i < tlen; i++) { - var t_ch = txt.charCodeAt(i); - if(mismatches !== 0) skips++; - if(skips > 3) return []; - mismatches = 0; - while (j < ilen) { - var i_ch = item.charCodeAt(j); - if (i_ch === t_ch) { - var item_char = parsed_item.charAt(j); - if(item_char === item_char.toLowerCase()){ - rendered_label += '' + txt.charAt(i) + ''; - } else { - rendered_label += '' + txt.charAt(i).toUpperCase() + ''; - } - j++; - continue outer; - } - mismatches++; - if(mismatches > 2) return []; - rendered_label += parsed_item.charAt(j); - j++; - } - return []; - } - rendered_label += parsed_item.slice(j); - return [parsed_item, 40 + (ilen + 10), rendered_label]; - }, - set_specifics: function(txt, end_txt) { var me = this; var results = this.build_options(txt); @@ -332,19 +155,50 @@ frappe.search.AwesomeBar = Class.extend({ }); }, - build_defaults: function(txt) { + add_defaults: function(txt) { this.make_global_search(txt); this.make_search_in_current(txt); - this.options = this.options.concat(this.make_search_in_list(txt)); + this.make_calculator(txt); }, build_options: function(txt) { - return this.make_new_doc(txt).concat( - this.get_doctypes(txt), - this.get_reports(txt), - this.get_pages(txt), - this.get_modules(txt) + var options = frappe.search.utils.get_creatables(txt).concat( + frappe.search.utils.get_search_in_list(txt), + frappe.search.utils.get_doctypes(txt), + frappe.search.utils.get_reports(txt), + frappe.search.utils.get_pages(txt), + frappe.search.utils.get_modules(txt), + frappe.search.utils.get_recent_pages(txt || "") ); + var out = this.deduplicate(options); + return out.sort(function(a, b) { + return b.index - a.index; + }); + }, + + deduplicate: function(options) { + var out = [], routes = []; + options.forEach(function(option) { + if(option.route) { + if(option.route[0] === "List" && option.route[2]) { + option.route.splice(2); + } + var str_route = (typeof option.route==='string') ? + option.route : option.route.join('/'); + if(routes.indexOf(str_route)===-1) { + out.push(option); + routes.push(str_route); + } else { + var old = routes.indexOf(str_route); + if(out[old].index > option.index) { + out[old] = option; + } + } + } else { + out.push(option); + } + }); + return out; }, set_global_results: function(global_results, txt){ @@ -357,11 +211,10 @@ frappe.search.AwesomeBar = Class.extend({ label: __("Search for '" + txt.bold() + "'"), value: __("Search for '" + txt + "'"), match: txt, - index: 1, + index: 100, default: "Search", onclick: function() { - me.search.search_dialog.show(); - me.search.setup_search(txt, [me.nav, me.global, me.help]); + frappe.searchdialog.search.init_search(txt, "global_search"); } }); }, @@ -378,10 +231,10 @@ frappe.search.AwesomeBar = Class.extend({ label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]), value: __('Find {0} in {1}', [txt, route[1]]), route_options: options, - index: 2, onclick: function() { cur_list.refresh(); }, + index: 90, default: "Current", match: txt }); @@ -401,7 +254,7 @@ frappe.search.AwesomeBar = Class.extend({ label: formatted_value, value: __('{0} = {1}', [txt, val]), match: val, - index: 3, + index: 80, default: "Calculator", onclick: function() { msgprint(formatted_value, "Result"); @@ -412,208 +265,4 @@ frappe.search.AwesomeBar = Class.extend({ } } }, - - make_search_in_list: function(txt) { - var me = this; - var out = []; - if(in_list(txt.split(" "), "in") && (txt.slice(-2) !== "in")) { - parts = txt.split(" in "); - frappe.boot.user.can_read.forEach(function (item) { - var target = me.fuzzy_search(parts[1], item)[0]; - if(target) { - out.push({ - label: __('Find {0} in {1}', [__(parts[0]).bold(), __(target).bold()]), - value: __('Find {0} in {1}', [__(parts[0]), __(target)]), - route_options: {"name": ["like", "%" + parts[0] + "%"]}, - index: 4, - default: "In List", - route: ["List", item] - }); - } - }); - } - return out; - }, - - make_new_doc: function(txt) { - var me = this; - var out = []; - if(txt.split(" ")[0]==="new") { - frappe.boot.user.can_create.forEach(function (item) { - var result = me.fuzzy_search(txt.substr(4), item); - var target = result[0]; - var index = result[1]; - var rendered_label = result[2]; - if(target) { - out.push({ - label: rendered_label, - value: __("New {0}", [target]), - index: index, - type: "New", - prefix: "New", - match: item, - onclick: function() { frappe.new_doc(item, true); } - }); - } - }); - } - return out; - }, - - get_doctypes: function(txt) { - var me = this; - var out = []; - - var result, target, index, rendered_label; - var option = function(type, route, order) { - return { - label: rendered_label, - value: __(target + " " + type), - route: route, - index: index + order, - match: target, - type: type - } - }; - frappe.boot.user.can_read.forEach(function (item) { - result = me.fuzzy_search(txt, item); - target = result[0]; - index = result[1]; - rendered_label = result[2]; - if(target) { - // include 'making new' option - if(in_list(frappe.boot.user.can_create, item)) { - var match = item; - out.push({ - label: rendered_label, - value: __("New {0}", [target]), - index: index + 0.4, - type: "New", - prefix: "New", - match: item, - onclick: function() { frappe.new_doc(match, true); } - }); - } - if(in_list(frappe.boot.single_types, target)) { - out.push(option("", ["Form", target, target], 0)); - - } else if(in_list(frappe.boot.treeviews, target)) { - out.push(option("Tree", ["Tree", target], 0)); - - } else { - out.push(option("List", ["List", target], 0)); - if(frappe.model.can_get_report(target)) { - out.push(option("Report", ["Report", target], 0.1)); - } - if(frappe.boot.calendars.indexOf(target) !== -1) { - out.push(option("Calendar", ["List", target, "Calendar"], 0.2)); - out.push(option("Gantt", ["List", target, "Gantt"], 0.3)); - } - } - } - }); - return out; - }, - - get_reports: function(txt) { - var me = this; - var out = []; - Object.keys(frappe.boot.user.all_reports).forEach(function(item) { - var result = me.fuzzy_search(txt, item); - var target = result[0]; - var index = result[1]; - var rendered_label = result[2]; - if(target) { - var report = frappe.boot.user.all_reports[item]; - var route = []; - if(report.report_type == "Report Builder") - route = ["Report", report.ref_doctype, item]; - else - route = ["query-report", item]; - - out.push({ - label: rendered_label, - value: __("Report {0}" , [__(target)]), - match: txt, - index: index, - type: "Report", - prefix: "Report", - route: route - }); - } - }); - return out; - }, - - get_pages: function(txt) { - var me = this; - var out = []; - this.pages = {}; - $.each(frappe.boot.page_info, function(name, p) { - me.pages[p.title] = p; - p.name = name; - }); - Object.keys(this.pages).forEach(function(item) { - var result = me.fuzzy_search(txt, item); - var target = result[0]; - var index = result[1]; - var rendered_label = result[2]; - if(target) { - var page = me.pages[item]; - out.push({ - label: rendered_label, - value: __("Open {0}", [__(target)]), - match: txt, - index: index, - type: "Page", - prefix: "Open", - route: [page.route || page.name] - }); - } - }); - // calendar - var target = 'Calendar'; - if(__('calendar').indexOf(txt.toLowerCase()) === 0) { - out.push({ - label: __(target), - value: __("Open {0}", [__(target)]), - route: [target, 'Event'], - index: 5, - type: "Calendar", - prefix: "Open", - match: target - }); - } - return out; - }, - - get_modules: function(txt) { - var me = this; - var out = []; - Object.keys(frappe.modules).forEach(function(item) { - var result = me.fuzzy_search(txt, item); - var target = result[0]; - var index = result[1]; - var rendered_label = result[2]; - if(target) { - var module = frappe.modules[item]; - if(module._doctype) return; - ret = { - label: rendered_label, - value: __("Open {0}", [__(target)]), - match: txt, - index: index, - type: "Module", - prefix: "Open" - } - if(module.link) { - ret.route = [module.link]; - } else { - ret.route = ["Module", item]; - } - out.push(ret); - } - }); - return out; - }, }); diff --git a/frappe/public/js/frappe/ui/toolbar/search.html b/frappe/public/js/frappe/ui/toolbar/search.html index 67ea7cfe6d..41f6ae6aae 100644 --- a/frappe/public/js/frappe/ui/toolbar/search.html +++ b/frappe/public/js/frappe/ui/toolbar/search.html @@ -1,12 +1,6 @@ -
    - -
    -
    -
    - - - - - +
    + +
    +
    \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/toolbar/search.js b/frappe/public/js/frappe/ui/toolbar/search.js index 721700f66e..e2c0e67430 100644 --- a/frappe/public/js/frappe/ui/toolbar/search.js +++ b/frappe/public/js/frappe/ui/toolbar/search.js @@ -1,874 +1,395 @@ frappe.provide('frappe.search'); -frappe.search.UnifiedSearch = Class.extend({ +frappe.search.SearchDialog = Class.extend({ + init: function(opts) { + $.extend(this, opts); + this.make(); + }, + + make: function() { + var d = new frappe.ui.Dialog(); + $(d.header).html($(frappe.render_template("search_header"))); + this.search_dialog = d; + this.$search_modal = $(d.$wrapper).addClass('search-dialog'); + this.$modal_body = $(d.body); + this.$input = this.$search_modal.find(".search-input"); + this.setup(); + }, setup: function() { - var d; - if(!frappe.search.dialog) { - d = new frappe.ui.Dialog(); - frappe.search.dialog = d; + this.modal_state = 0; + this.current_keyword = ""; + this.more_count = 20; + this.full_lists = {}; + this.nav_lists = {}; + this.bind_input(); + this.bind_events(); + }, + + update: function($r) { + this.$search_modal.find('.loading-state').addClass('hide'); + this.$modal_body.append($r); + if(this.$modal_body.find('.search-results').length > 1) { + this.$modal_body.find('.search-results').first().addClass("hide"); + $r.removeClass("hide"); + this.$modal_body.find('.search-results').first().remove(); } else { - d = frappe.search.dialog; - $(d.body).empty(); + $r.removeClass("hide"); } + }, - $(frappe.render_template("search")).appendTo($(d.body)); - $(d.header).html($(frappe.render_template("search_header"))); - - this.search_dialog = d; - this.search_modal = $(d.$wrapper); - this.search_modal.addClass('search-dialog'); - - this.input = this.search_modal.find(".search-input"); - this.sidebar = this.search_modal.find(".search-sidebar"); - this.results_area = this.search_modal.find(".results-area"); + put_placeholder: function(status_text) { + var $placeholder = $('
    ' + + '
    ' + + '' + + ''+ + ''+ + ''+ + '

    ' + status_text + '

    ' + + '
    '); + this.update($placeholder); }, - setup_search: function(init_keywords, search_objects) { + bind_input: function() { var me = this; - var keywords = init_keywords; - this.search_objects = search_objects; - this.search_types = search_objects.map(function(s) { - return s.search_type; - }); - this.current_type = "All Results"; - this.reset(); - this.bind_keyboard_events(); - this.input.val(keywords); - this.input.on("input", function() { + this.$input.on("input", function() { var $this = $(this); clearTimeout($this.data('timeout')); - $this.data('timeout', setTimeout(function() { - if(me.input.val() === keywords) return; - keywords = me.input.val(); - me.reset(); - if(keywords.length > 2) { - me.build_results(keywords); + if(me.$input.val() === me.current_keyword) return; + keywords = me.$input.val(); + if(keywords.length > 1) { + me.get_results(keywords); } else { - me.current_type = ''; + me.current_keyword = ""; + me.put_placeholder(me.search.empty_state_text); } }, 300)); }); - this.build_results(keywords); - setTimeout(function() { me.input.select(); }, 500); - }, - - reset: function() { - this.sidebar.empty(); - this.results_area.empty(); - this.results_area.append($('
    ' + - '

    '+__("Search for anything")+'

    ')); }, - bind_keyboard_events: function() { - var me = this; - this.search_modal.on('keydown', function(e) { - if(me.sidebar.find('.list-link').length > 1) { - var list_types = me.get_all_list_types(); - var current_type_index = list_types.indexOf(me.current_type); - // DOWN and UP keys navigate sidebar - if(e.which === 40) { - if(current_type_index < list_types.length - 1) { - next_type = list_types[current_type_index + 1]; - me.sidebar.find('*[data-category="'+ next_type +'"]').trigger('click'); - } - } else if(e.which === 38) { - if(current_type_index > 0) { - last_type = list_types[current_type_index - 1]; - me.sidebar.find('*[data-category="'+ last_type +'"]').trigger('click'); - } - } else if (e.which === 9) { - // Tab key rolls back after the last result - if(me.results_area.find('a').last().is(":focus")) { - e.preventDefault(); - me.results_area.find('.module-section-link').first().focus(); - } - } else if(e.which === 8) { - // Backspace key focuses input - if(!me.input.is(":focus")) { - me.input.focus(); - } - } - } - }); - this.search_modal.on('keypress', function(e) { - if(!me.input.is(":focus")) { - me.input.focus(); - } - }); - }, - - build_results: function(keywords) { + bind_events: function() { var me = this; - this.summary = $('
    '); - this.sidelist = $(''); - this.full_lists = {}; - this.current = 0; - this.search_objects[me.current].build_results_object(me, keywords); - }, - render_results: function(results_obj, keywords) { - var me = this; - if(this.current === 0) { this.reset() } - results_obj.sidelist.forEach(function(list_item) { - me.sidelist.append(list_item); - }) - this.results_area.find('.results-status').remove(); - results_obj.sections.forEach(function(section) { - me.summary.append(section); + // Sidebar + this.$modal_body.on('click', '.list-link', function() { + var link = $(this); + me.$modal_body.find('.search-sidebar').find(".list-link").removeClass("active select"); + link.addClass("active select"); + var type = link.attr('data-category'); + me.$modal_body.find('.results-area').empty().html(me.full_lists[type]); + me.$modal_body.find('.module-section-link').first().focus(); + me.current_type = type; }); - this.full_lists = Object.assign(this.full_lists, results_obj.lists); - this.full_lists["All Results"] = this.summary; - this.render_next_search(keywords); - }, - bind_events: function() { - var me = this; - this.sidebar.find('.list-link').on('click', function() { - me.set_sidebar_link_action($(this)); - }); - this.results_area.find('.section-more').on('click', function() { + // Summary more links + this.$modal_body.on('click', '.section-more', function() { var type = $(this).attr('data-category'); - me.sidebar.find('*[data-category="'+ type +'"]').trigger('click'); - return false; + me.$modal_body.find('.search-sidebar').find('*[data-category="'+ type +'"]').trigger('click'); }); - this.results_area.find('.Help-result a').on('click', frappe.help.show_results); - }, - set_sidebar_link_action: function(link) { - var me = this; - this.sidebar.find(".list-link").removeClass("active"); - link.addClass("active"); - var type = link.attr('data-category'); - this.results_area.empty().html(this.full_lists[type]); - me.results_area.find('.module-section-link').first().focus(); + // Back-links (mobile-view) + this.$modal_body.on('click', '.all-results-link', function() { + me.$modal_body.find('.search-sidebar').find('*[data-category="All Results"]').trigger('click'); + }); - this.results_area.find('.section-more').on('click', function() { + // Full list more links + this.$modal_body.on('click', '.list-more', function() { var type = $(this).attr('data-category'); - me.sidebar.find('*[data-category="'+ type +'"]').trigger('click'); - return false; + var fetch_type = $(this).attr('data-search'); + var current_count = me.$modal_body.find('.result').length; + if(fetch_type === "Global") { + frappe.search.utils.get_global_results(me.current_keyword, + current_count, me.more_count, type) + .then(function(doctype_results) { + me.add_more_results(doctype_results); + }, function (err) { + console.error(err); + }); + } else { + results = me.nav_lists[type].slice(0, me.more_count); + me.nav_lists[type].splice(0, me.more_count); + me.add_more_results([{title: type, results: results}]); + } }); - this.current_type = type; - - this.set_back_link(); - this.set_list_more_link(type); - this.results_area.find('.Help-result a').on('click', frappe.help.show_results); - }, - - set_back_link: function() { - var me = this; - var back_link = this.results_area.find('.all-results-link'); - back_link.on('click', function() { - me.show_summary(); + // Switch to global search link + this.$modal_body.on('click', '.switch-to-global-search', function() { + me.search = me.searches['global_search']; + me.$input.attr("placeholder", me.search.input_placeholder); + me.put_placeholder(me.search.empty_state_text); + me.get_results(me.current_keyword); }); - }, - - show_summary: function() { - this.current_type = ''; - this.sidebar.find(".list-link").removeClass("active"); - this.sidebar.find(".list-link").first().addClass("active"); - this.results_area.empty().html(this.summary); - this.bind_events(); - }, - set_list_more_link: function(type) { - var me = this; - var more_link = this.results_area.find('.list-more'); - more_link.on('click', function() { - var more_search_type = $(this).attr('data-search'); - var s_obj = me.search_objects[me.search_types.indexOf(more_search_type)]; - s_obj.get_more_results(type); - }); + // Help results + this.$modal_body.on('click', 'a[data-path]', frappe.help.show_results); + this.bind_keyboard_events(); }, - add_more_results: function(more_data) { + bind_keyboard_events: function() { var me = this; - var more_results = more_data[0]; - var more = more_data[1]; - this.results_area.find('.module-section-link').last().addClass('.current-last'); - this.results_area.find('.list-more').before(more_results); - setTimeout(function() { me.results_area.find('.more-results').last().find('.module-section-link').first().focus(); }, 200); - if(!more) { - this.results_area.find('.list-more').hide(); - var no_of_results = this.results_area.find('.result').length; - var no_of_results_cue = $('

    '+ - no_of_results +' results found

    '); - this.results_area.find(".more-results:last").append(no_of_results_cue); - } - this.results_area.find('.more-results.last').slideDown(200, function() { - var height = me.results_area.find('.module-body').height() - 750; - if(me.results_area.find('.list-more').is(":visible")) { - me.results_area.animate({ - scrollTop: height - }, 300); - } - $(this).removeClass('last'); - }); - }, + this.$search_modal.on('keydown', function(e) { - render_next_search: function(keywords) { - this.current += 1; - if(this.current < this.search_objects.length){ - // More searches to go - this.search_objects[this.current].build_results_object(this, keywords); - } else { - this.sidebar.append(this.sidelist); - // If there's only one link in sidebar, there's no summary (show its full list) - if(this.sidebar.find('.list-link').length === 1) { - this.bind_events(); - this.sidebar.find('.list-link').trigger('click'); - this.results_area.find('.all-results-link').hide(); - - } else if (this.sidebar.find('.list-link').length === 0) { - this.results_area.html('

    '+ - 'No results found for: '+ "'"+ keywords +"'" +'

    '); - } else { - this.sidebar.find('.search-sidelist').prepend(''); - var list_types = this.get_all_list_types(); - if(list_types.indexOf(this.current_type) === -1) { - this.current_type = "All Results"; + if(me.$modal_body.find('.list-link').length > 1) { + if(me.modal_state === 0) { + // DOWN and UP keys navigate sidebar + if(e.which === DOWN_ARROW || e.which === TAB) { + e.preventDefault(); + var $link = me.$modal_body.find('.list-link.select').next(); + if($link.length > 0) { + // me.$modal_body.find('.list-link').removeClass('select'); + // $link.addClass('select'); + $link.trigger('click'); + } + } else if(e.which === UP_ARROW) { + e.preventDefault(); + var $link = me.$modal_body.find('.list-link.select').prev(); + if($link.length > 0) { + $link.trigger('click'); + } + } } - this.bind_events(); - this.sidebar.find('*[data-category="'+ this.current_type +'"]').trigger('click'); - this.bind_events(); } - } - }, - get_all_list_types: function() { - var types = []; - this.sidebar.find('.list-link').each(function() { - types.push($(this).attr('data-category')); - }); - return types; - }, - -}); - -frappe.search.GlobalSearch = Class.extend({ - init: function() { - this.search_type = 'Global Search'; - }, - - setup: function() { - this.types = []; - this.sections = []; - this.lists = {}; - this.more_length = 15; - this.start = {}; - this.section_length = 3; - - this.set_types(); - }, - - set_types: function() { - var me = this; - this.current_type = 0; - frappe.call({ - method: "frappe.utils.global_search.get_search_doctypes", - args: { text: me.keywords }, - callback: function(r) { - if(r.message) { - r.message.forEach(function(d) { - me.types.push(d.doctype); - me.start[d.doctype] = 0; - }); - me.sidelist = me.make_sidelist(); - me.get_result_set(me.types[me.current_type]); - } else { - me.render_object.render_next_search(me.keywords); - } + if(!me.$input.is(":focus")) { + me.$input.focus(); } }); - }, - make_sidelist: function() { - var me = this; - var sidelist = []; - this.types.forEach(function(type) { - sidelist.push(me.make_sidelist_item(type)); - }); - return sidelist; - }, - - make_sidelist_item: function(type) { - var sidelist_item = ''; - return $(__(sidelist_item, [this.search_type, type])); }, - get_result_set: function(doctype){ + init_search: function(keywords, search_type) { var me = this; - var more = true; - frappe.call({ - method: "frappe.utils.global_search.search_in_doctype", - args: { - doctype: doctype, - text: me.keywords, - start: me.start[doctype], - limit: me.more_length, - }, - callback: function(r) { - if(r.message) { - me.start[doctype] += me.more_length; - if(r.message.length < me.more_length) { - more = false; - } - me.make_type_results(doctype, r.message, more); - } - } - }); - }, - - make_type_results: function(type, results, more) { - var last_type = (type == this.types.slice(-1)); - this.sections.push(this.make_section(type, results)); - this.lists[type] = this.make_full_list(type, results, more); - if(!last_type) { - this.current_type += 1; - this.get_result_set(this.types[this.current_type]); + this.search = this.searches[search_type]; + this.$input.attr("placeholder", this.search.input_placeholder); + this.put_placeholder(this.search.empty_state_text); + this.get_results(keywords); + this.search_dialog.show(); + this.$input.val(keywords); + setTimeout(function() { me.$input.select(); }, 500); + }, + + get_results: function(keywords) { + this.current_keyword = keywords; + if(this.$modal_body.find('.empty-state').length > 0) { + this.put_placeholder(__("Searching ...")); + this.$modal_body.find('.cover').removeClass('hide') } else { - this.render_results(); - } - }, - - build_results_object: function(r, keywords) { - this.render_object = r; - this.keywords = keywords; - this.setup(); - }, - - render_results: function() { - var me = this; - if(this.sections.length > 0) { - this.render_object.render_results({ - sidelist: me.sidelist, - sections: me.sections, - lists: me.lists - }, me.keywords); + this.$search_modal.find('.loading-state').removeClass('hide'); } + this.search.get_results(keywords, this.parse_results.bind(this)); }, - make_result_item: function(type, result) { - var link_html = '
    ' + - '{1}' + - '

    {2}

    ' + - '
    '; - var formatted_result = this.format_result(result); - return $(__(link_html, formatted_result)); - }, - - format_result: function(result) { - var name, route = '#Form/' + result.doctype + '/' + result.name; - var description = this.get_finds(result.content, this.keywords); - if(result.doctype === "Communication") { - if(description.indexOf(',') === -1) { - name = description.slice(description.indexOf(':') + 8); - } else { - name = description.slice(description.indexOf(':') + 8, description.indexOf(',')); - } + parse_results: function(result_sets, keyword) { + result_sets = result_sets.filter(function(set) { + return set.results.length > 0; + }) + if(result_sets.length > 0) { + this.render_data(result_sets); } else { - name = result.name; + this.put_placeholder(this.search.no_results_status(keyword)); } - return [route, this.bold_keywords(name), description]; }, - get_finds: function(searchables, keywords) { + render_data: function(result_sets) { var me = this; - parts = searchables.split("|||"); - content_length = 300; - content = []; - current_length = 0; - var field_text = ""; - for(var i = 0; i < parts.length; i++) { - part = parts[i]; - if(part.toLowerCase().indexOf(keywords) !== -1) { - if(part.indexOf('&&&') !== -1) { - var colon_index = part.indexOf('&&&'); - var field_value = part.slice(colon_index + 3); - } else { - var colon_index = part.indexOf(':'); - var field_value = part.slice(colon_index + 1); - } - var field_name = part.slice(0, colon_index); - - var remaining_length = content_length - current_length; - current_length += field_name.length + field_value.length + 2; - if(current_length < content_length) { - field_text = '' + - me.bold_keywords(field_name, keywords) + ':' + '' + - me.bold_keywords(field_value, keywords); - if(content.indexOf(field_text) === -1) { - content.push(field_text); - } - } else { - if(field_name.length < remaining_length){ - remaining_length -= field_name.length; - field_text = '' + - me.bold_keywords(field_name, keywords) + ':' + ''; - field_value = field_value.slice(0, remaining_length); - field_value = field_value.slice(0, field_value.lastIndexOf(' ')) + ' ...'; - field_text += me.bold_keywords(field_value, keywords); - content.push(field_text); - } else { - content.push('...'); - } - break; - } - } - } - return content.join(', '); - }, + var $search_results = $(frappe.render_template("search")).addClass('hide'); + var $sidebar = $search_results.find(".search-sidebar").empty(); + var sidebar_item_html = ''; - bold_keywords: function(str, keywords) { - var regEx = new RegExp("("+ keywords +")", "ig"); - return str.replace(regEx, '$1'); - }, + this.modal_state = 0; + this.full_lists = { 'All Results': $('
    ') }; + this.nav_lists = {}; - make_section: function(type, results) { - var me = this; - var results_section = $('
    '+ - '
    ' + - '
    '+type+'
    ' + - '
    '+ - '
    '); - var results_col = results_section.find('.module-section-column'); - results.slice(0, this.section_length).forEach(function(result) { - results_col.append(me.make_result_item(type, result)); + result_sets.forEach(function(set) { + $sidebar.append($(__(sidebar_item_html, [set.title]))); + me.add_section_to_summary(set.title, set.results); + me.full_lists[set.title] = me.render_full_list(set.title, set.results, set.fetch_type); }); - if(results.length > this.section_length) { - results_col.append(''+__("More...")+''); + if(result_sets.length > 1) { + $sidebar.prepend($(__(sidebar_item_html, ["All Results"]))); } - return results_section; - }, - make_result_subtypes_property: function(results) { - var compressed_results = []; - var labels = []; - results.forEach(function(r) { - if(labels.indexOf(r.label) === -1) { - labels.push(r.label); - } - }); - labels.forEach(function(l) { - var item_group = { - title: l, - subtypes: [] - }; - results.forEach(function(r) { - if (r.label === l) { - item_group.subtypes.push(r); - } - }); - compressed_results.push(item_group); - }); - return compressed_results; + this.update($search_results.clone()); + this.$modal_body.find('.list-link').first().trigger('click'); }, - make_full_list: function(type, results, more) { - var me = this; - var results_list = $('
    '+'
    ' + '' + '
    '+type+'
    ' + '
    '); - if(type === "Lists") { - results = this.make_result_subtypes_property(results); + + var $results_col = $results_list.find('.module-section-column'); + for(var i = 0; i < max_length && results.length > 0; i++) { + $results_col.append(me.render_result(type, results.shift())); } - var results_col = results_list.find('.module-section-column'); - results.forEach(function(result) { - results_col.append(me.make_result_item(type, result)); - }); - if(more) { - results_col.append(''+__("More...")+''); + if(results.length > 0) { + if(fetch_type === "Nav") this.nav_lists[type] = results; + $results_col.append(''+__("More...")+''); } - return results_list; + return $results_list; }, - get_more_results: function(doctype) { + add_section_to_summary: function(type, results) { var me = this; - var more = true; - frappe.call({ - method: "frappe.utils.global_search.search_in_doctype", - args: { - doctype: doctype, - text: me.keywords, - start: me.start[doctype], - limit: me.more_length, - }, - callback: function(r) { - if(r.message) { - me.start[doctype] += me.more_length; - me.make_more_list(doctype, r.message, more); - } + var are_expansive = false; + var margin_more = "10px"; + for(var i = 0; i < results.length; i++) { + if(results[i]["description" || "image" || "subtypes"] || false) { + are_expansive = true; + break; } - }); - }, - - make_more_list: function(type, results, more) { - var me = this; - if(results.length < this.more_length) { more = false; } - - var more_results = $('
    '); - results.forEach(function(result) { - more_results.append(me.make_result_item(type, result)); - }); - this.render_object.add_more_results([more_results, more]); - }, - - get_awesome_bar_options: function(keywords, ref) { - var me = this; - var doctypes = []; - var current = 0; - var results = []; - - var get_doctypes = function(){ - var me = this; - frappe.call({ - method: "frappe.utils.global_search.get_search_doctypes", - args: { text: keywords }, - callback: function(r) { - if(r.message) { - r.message.forEach(function(d) { - doctypes.push(d.doctype); - }); - get_results(); - } - } - }); }; + if(results[0].image) margin_more = "20px"; + var [section_length, col_width] = are_expansive ? [3, "12"] : [4, "6"]; - var get_results = function() { - frappe.call({ - method: "frappe.utils.global_search.search_in_doctype", - args: { - doctype: doctypes[current], - text: keywords, - start: 0, - limit: 4, - }, - callback: function(r) { - if(r.message) { - r.message.forEach(function(d) { - results.push(make_option(d)); - }); - current += 1; - if(current < doctypes.length) { - get_results(); - } else { - ref.set_global_results(results, keywords); - } - } - } - }); - }; - - var make_option = function(data) { - return { - label: __("{0}: {1}", [__(data.doctype).bold(), data.name]), - value: __("{0}: {1}", [__(data.doctype), data.name]), - route: ["Form", data.doctype, data.name], - match: data.doctype, - index: 90, - default: "Global", - description: me.get_finds(data.content, keywords).slice(0,86) + '...' - } - }; - - get_doctypes(); - - } - -}); - -frappe.search.NavSearch = frappe.search.GlobalSearch.extend({ - init: function() { - this.search_type = 'Navigation'; - }, - set_types: function() { - var me = this; - this.section_length = 4; - - this.set_nav_results(me.keywords); - var types = ["Lists", "Reports", "Modules", "Administration", "Setup"]; - types.forEach(function(type) { - if(me.nav_results[type].length > 0) { - me.types.push(type); - me.start[type] = 0; - } - }); - if(this.types.length > 0) { - this.sidelist = this.make_sidelist(); - this.get_results(); - } else { - this.render_object.render_next_search(me.keywords); - } - }, - - set_nav_results: function(keywords) { - var me = this, lists = [], setup = []; - this.awesome_bar = new frappe.search.AwesomeBar(); - var compare = function(a, b) { - return a.index - b.index; + // check state of last summary section + if(this.full_lists['All Results'].find('.module-section').last().find('.col-sm-6').length !== 1 + || are_expansive) { + this.full_lists['All Results'].append($('
    ')); } - var all_doctypes = me.awesome_bar.get_doctypes(keywords); - all_doctypes.forEach(function(d) { - if(d.type === "") { - setup.push(d); - } else { - lists.push(d); - } + var $results_col = $(`
    +
    ${type}
    +
    +
    `); + results.slice(0, section_length).forEach(function(result) { + $results_col.append(me.render_result(type, result)); }); - this.nav_results = { - "Lists": lists.sort(compare), - "Reports": me.awesome_bar.get_reports(keywords).sort(compare), - "Modules": me.awesome_bar.get_modules(keywords).sort(compare), - "Administration": me.awesome_bar.get_pages(keywords).sort(compare), - "Setup": setup.sort(compare) + if(results.length > section_length) { + $results_col.append(`
    ${__("More...")}
    `); } - }, - get_results: function() { - var me = this; - this.types.forEach(function(type) { - me.get_result_set(type); - }); + this.full_lists['All Results'].find('.module-section').last().append($results_col); }, - get_result_set: function(type) { - var last_type = (type == this.types.slice(-1)); - var results = this.nav_results[type].slice(this.start[type], this.more_length); - this.start[type] += this.more_length; - var more = true; - if(results.slice(-1)[0] === this.nav_results[type].slice(-1)[0]) { - more = false; - } - this.make_type_results(type, results, more); - if(last_type) { - this.render_results(); - } - }, - - make_type_results: function(type, results, more) { - this.sections.push(this.make_section(type, results)); - this.lists[type] = this.make_full_list(type, results, more); - }, + render_result: function(type, result) { + var $result = $('
    '); - get_more_results: function(type) { - var results = this.nav_results[type].slice(this.start[type], - this.start[type]+this.more_length); - this.start[type] += this.more_length; - var more = true; - if(results.slice(-1)[0] === this.nav_results[type].slice(-1)[0]) { - more = false; + function get_link(result) { + var link; + if(result.route) { + link = 'href="#'+result.route.join('/')+'" '; + } else if (result.data_path) { + link = 'data-path="'+result.data_path+'"'; + } else { + link = ""; + } + return link; } - this.make_more_list(type, results, more) - }, - - make_path: function(route) { - path = '#'; - route.forEach(function(r) { - path += r + '/'; - }); - return path.slice(0, -1); - }, - make_result_item: function(type, result) { - var me = this; - if(!result.subtypes) { - var link_html = ''; - return this.make_result_link(type, result, link_html); + if(result.image) { + $result.append('
    ' + result.label + '
    '); + } else if (result.image === null) { + $result.append('
    '+ frappe.get_abbr(result.label) +'
    '); + } + var title_html = ''+ result.label +''; + var $result_text = $('
    '); + if(result.description) { + $result_text.append($('' + title_html + '')); + $result_text.append('

    '+ result.description +'

    '); } else { - var result_div = $(''); - result.subtypes.forEach(function(s) { - if(["Gantt", "Report", "Calendar"].indexOf(s.type) !== -1) { - var button_html = ''; - var button = $(__(button_html, [me.make_path(s.route), s.type])); - result_div.append(button); - } else if (s.type !== "New") { - title_link_html = '{1}'; - if(s.type === "List") { - var link = $(__(title_link_html, [me.make_path(s.route), result.title])); - } else { - var link = $(__(title_link_html, [me.make_path(s.route), result.title + ' ' + s.type])); + $result_text.append($(title_html)); + if(result.route_options) { + frappe.route_options = result.route_options; + } + $result_text.on('click', (e) => { + this.search_dialog.hide(); + if(result.onclick) { + result.onclick(result.match); + } else { + var previous_hash = window.location.hash; + frappe.set_route(result.route); + + // hashchange didn't fire! + if (window.location.hash == previous_hash) { + frappe.route(); } - result_div.append(link); } - }) - result_div.append($('

    ')); - return result_div; + }); } - }, + $result.append($result_text); - make_result_link: function(type, result, link_html) { - var me = this; - if(!result.onclick) { - var link = $(__(link_html, [me.make_path(result.route), result.label])); - link.on('click', function() { - if(result.route_options) { - frappe.route_options = result.route_options; - } - frappe.set_route(result.route); - return false; - }); - return link; - } else { - var link = $(__(link_html, ['#', result.label])); - link.on('click', function() { - frappe.new_doc(result.match, true); - // result.onclick(result.match, true); + if(result.subtypes) { + result.subtypes.forEach(function(subtype) { + $result.append(subtype); }); - return link; } - }, - make_dual_sections: function() { - this.dual_sections = []; - for(var i = 0; i < this.sections.length; i++) { - var types; - if(i+1 < this.types.length) { - types = this.types[i] + ',' + this.types[i+1]; - } else { - types = this.types[i]; - } - var results_section = $('
    '); - for(var j = 0; j < 2 && i < this.sections.length; j++, i++){ - results_section.append(this.sections[i]); - } - i--; - this.dual_sections.push(results_section); - } + return $result; }, - render_results: function() { + add_more_results: function(results_set) { var me = this; - this.make_dual_sections(); - if(this.dual_sections.length > 0) { - this.render_object.render_results({ - sidelist: me.sidelist, - sections: me.dual_sections, - lists: me.lists - }, me.keywords); - } - }, - - make_section: function(type, results) { - var me = this; - var results_column = $('
    ' + - '
    '+type+'
    ' + - '
    '+ - '
    '); - if(type === "Lists") { - results = this.make_result_subtypes_property(results); - } - results.slice(0, this.section_length).forEach(function(result) { - results_column.append(me.make_result_item(type, result)); + var more_results = $('
    '); + results_set[0].results.forEach(function(result) { + more_results.append(me.render_result(results_set[0].title, result)); }); - if(results.length > this.section_length) { - results_column.append(''+__("More...")+''); - } - return results_column; - } -}); + this.$modal_body.find('.list-more').before(more_results); -frappe.search.HelpSearch = frappe.search.GlobalSearch.extend({ - init: function() { - this.search_type = 'Help'; - }, - - set_types: function() { - this.section_length = 4; - this.types = [this.search_type]; - this.sidelist = this.make_sidelist(); - this.get_result_set(this.types[0]); - }, - - make_sidelist: function() { - var sidelist = []; - var sidelist_item = ''; - sidelist.push(sidelist_item); - return sidelist; - }, - - get_result_set: function(type) { - var me = this; - frappe.call({ - method: "frappe.utils.help.get_help", - args: { - text: me.keywords - }, - callback: function(r) { - if(r.message) { - // Help search doesn't have a more button for full list - me.make_type_results(type, r.message, false); - me.render_results(); - } else { - me.render_object.render_next_search(me.keywords); - } + if(results_set[0].results.length < this.more_count) { + // hide more button and add a result count + this.$modal_body.find('.list-more').hide(); + var no_of_results = this.$modal_body.find('.result').length; + var no_of_results_cue = $('

    '+ + no_of_results +' results found

    '); + this.$modal_body.find(".more-results:last").append(no_of_results_cue); + } + this.$modal_body.find('.more-results.last').slideDown(200, function() {}); + }, + + // Search objects + searches: { + global_search: { + input_placeholder: __("Global Search"), + empty_state_text: __("Search for anything"), + no_results_status: (keyword) => __("

    No results found for '" + keyword + "' in Global Search

    "), + + get_results: function(keywords, callback) { + var start = 0, limit = 100; + var results = frappe.search.utils.get_nav_results(keywords); + frappe.search.utils.get_global_results(keywords, start, limit) + .then(function(global_results) { + results = results.concat(global_results); + return frappe.search.utils.get_help_results(keywords); + }).then(function(help_results) { + results = results.concat(help_results); + callback(results, keywords); + }, function (err) { + console.error(err); + }); + } + }, + help: { + input_placeholder: __("Search Help"), + empty_state_text: __("Search the docs"), + no_results_status: (keyword) => __("

    No results found for '" + keyword + + "' in Help

    Would you like to search globally" + + " or the forums instead?

    "), + + get_results: function(keywords, callback) { + var results = []; + frappe.search.utils.get_help_results(keywords) + .then(function(help_results) { + results = results.concat(help_results); + callback(results, keywords); + }, function (err) { + console.error(err); + }); } - }); - }, - - make_type_results: function(type, results, more) { - this.sections.push(this.make_section(type, results)); - this.lists[type] = this.make_full_list(type, results, more); - }, - - make_section: function(type, results) { - var me = this; - var results_section = $('
    '+ - '
    ' + - '
    '+type+'
    ' + - '
    '+ - '
    '); - var results_col = results_section.find('.module-section-column'); - results.slice(0, this.section_length).forEach(function(result) { - results_col.append(me.make_condensed_result_item(type, result)); - }); - if(results.length > this.section_length) { - results_col.append(''+__("More...")+''); - } - return results_section; - }, - - make_condensed_result_item: function(type, result) { - var me = this; - var link_html = '
    ' + - '{1}' + - '

    ' + - '
    '; - var link = $(__(link_html, [result[2], result[0]])); - return link; - }, - - make_result_item: function(type, result) { - var me = this; - var regEx = new RegExp("{index}", "ig"); - var content = result[1].replace(regEx, ''); - var link_html = '
    ' + - '{1}' + - '

    {2}

    ' + - '
    '; - var link = $(__(link_html, [result[2], result[0], content])); - return link; }, -}); +}); \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/toolbar/search_header.html b/frappe/public/js/frappe/ui/toolbar/search_header.html index dcacb8c453..ecf5dbd4f5 100644 --- a/frappe/public/js/frappe/ui/toolbar/search_header.html +++ b/frappe/public/js/frappe/ui/toolbar/search_header.html @@ -1,7 +1,6 @@ -
    - - - - - +
    + + +

    {%= __("Searching")%} ...

    +
    \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/toolbar/search_utils.js b/frappe/public/js/frappe/ui/toolbar/search_utils.js new file mode 100644 index 0000000000..b4af96e3ad --- /dev/null +++ b/frappe/public/js/frappe/ui/toolbar/search_utils.js @@ -0,0 +1,553 @@ +frappe.provide('frappe.search'); + +frappe.search.utils = { + setup_recent: function() { + this.recent = JSON.parse(frappe.boot.user.recent || "[]") || []; + }, + + get_recent_pages: function(keywords) { + var me = this; + values = [], options = []; + function find(list, keywords, process) { + list.forEach(function(item, i) { + _item = ($.isArray(item)) ? item[0] : item; + _item = __(_item || '').toLowerCase().replace(/-/g, " "); + + if(keywords===_item || _item.indexOf(keywords) !== -1) { + var option = process(item); + + if(option) { + if($.isPlainObject(option)) { + option = [option]; + } + option.forEach(function(o) { o.match = item; }); + options = option.concat(options); + } + } + }); + } + + me.recent.forEach(function(doctype, i) { + values.push([doctype[1], ['Form', doctype[0], doctype[1]]]); + }); + + values = values.reverse(); + + frappe.route_history.forEach(function(route, i) { + if(route[0]==='Form') { + values.push([route[2], route]); + } + else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], route[0])) { + if(route[1]) { + values.push([route[1], route]); + } + } + else if(route[0]) { + values.push([frappe.route_titles[route[0]] || route[0], route]); + } + }); + + find(values, keywords, function(match) { + out = { + route: match[1] + } + if(match[1][0]==='Form') { + if(match[1][1] !== match[1][2]) { + out.label = __(match[1][1]) + " " + match[1][2].bold(); + out.value = __(match[1][1]) + " " + match[1][2]; + } else { + out.label = __(match[1][1]).bold(); + out.value = __(match[1][1]); + } + } else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], match[1][0])) { + var type = match[1][0], label = type; + if(type==='modules') label = 'Module'; + else if(type==='query-report') label = 'Report'; + out.label = __(match[1][1]).bold() + " " + __(label); + out.value = __(match[1][1]) + " " + __(label); + } else { + out.label = match[0].bold(); + out.value = match[0]; + } + out.index = 80; + return out; + }); + + return options; + }, + + get_search_in_list: function(keywords) { + var me = this; + var out = []; + if(in_list(keywords.split(" "), "in") && (keywords.slice(-2) !== "in")) { + parts = keywords.split(" in "); + frappe.boot.user.can_read.forEach(function (item) { + if(frappe.boot.user.can_search.includes(item)) { + var level = me.fuzzy_search(parts[1], item); + if(level) { + out.push({ + type: "In List", + prefix: "Find '" + __(parts[0]).bold() + "' in ", + label: __(me.bolden_match_part(item, parts[1])), + value: __('Find {0} in {1}', [__(parts[0]), __(item)]), + route_options: {"name": ["like", "%" + parts[0] + "%"]}, + index: 1 + level, + route: ["List", item] + }); + } + } + }); + } + return out; + }, + + get_creatables: function(keywords) { + var me = this; + var out = []; + if(keywords.split(" ")[0]==="new") { + frappe.boot.user.can_create.forEach(function (item) { + var level = me.fuzzy_search(keywords.substr(4), item); + if(level) { + out.push({ + type: "New", + prefix: "New ", + label: __(me.bolden_match_part(item, keywords.substr(4))), + value: __("New {0}", [item]), + index: 1 + level, + match: item, + onclick: function() { frappe.new_doc(item, true); } + }); + } + }); + } + return out; + }, + + get_doctypes: function(keywords) { + var me = this; + var out = []; + + var level, target; + var option = function(type, route, order) { + return { + type: type, + label: __("{0}" + " " + type, [__(me.bolden_match_part(target, keywords))]), + value: __(target + " " + type), + index: level + order, + match: target, + route: route, + } + }; + frappe.boot.user.can_read.forEach(function(item) { + if(frappe.boot.user.can_search.includes(item)) { + level = me.fuzzy_search(keywords, item); + if(level) { + target = item; + // include 'making new' option + if(in_list(frappe.boot.user.can_create, item)) { + var match = item; + out.push({ + type: "New", + label: __("New {0}", [__(me.bolden_match_part(item, keywords))]), + value: __("New {0}", [__(item)]), + index: level + 0.01, + match: item, + onclick: function() { frappe.new_doc(match, true); } + }); + } + if(in_list(frappe.boot.single_types, item)) { + out.push(option("", ["Form", item, item], 0.05)); + + } else if(in_list(frappe.boot.treeviews, item)) { + out.push(option("Tree", ["Tree", item], 0.05)); + + } else { + out.push(option("List", ["List", item], 0.05)); + if(frappe.model.can_get_report(item)) { + out.push(option("Report", ["Report", item], 0.04)); + } + if(frappe.boot.calendars.indexOf(item) !== -1) { + out.push(option("Calendar", ["List", item, "Calendar"], 0.03)); + out.push(option("Gantt", ["List", item, "Gantt"], 0.02)); + } + } + } + } + }); + return out; + }, + + get_reports: function(keywords) { + var me = this; + var out = []; + Object.keys(frappe.boot.user.all_reports).forEach(function(item) { + var level = me.fuzzy_search(keywords, item); + if(level > 0) { + var report = frappe.boot.user.all_reports[item]; + if(report.report_type == "Report Builder") + route = ["Report", report.ref_doctype, item]; + else + route = ["query-report", item]; + out.push({ + type: "Report", + prefix: "Report ", + label: __(me.bolden_match_part(item, keywords)), + value: __("Report {0}" , [item]), + index: level, + route: route + }); + } + }); + return out; + }, + + get_pages: function(keywords) { + var me = this; + var out = []; + this.pages = {}; + $.each(frappe.boot.page_info, function(name, p) { + me.pages[p.title] = p; + p.name = name; + }); + Object.keys(this.pages).forEach(function(item) { + var level = me.fuzzy_search(keywords, item); + if(level) { + var page = me.pages[item]; + out.push({ + type: "Page", + prefix: "Open ", + label: __(me.bolden_match_part(item, keywords)), + value: __("Open {0}", [__(item)]), + match: item, + index: level, + route: [page.route || page.name] + }); + } + }); + var target = 'Calendar'; + if(__('calendar').indexOf(keywords.toLowerCase()) === 0) { + out.push({ + type: "Calendar", + prefix: "Open ", + label: __('Calendar'), + value: __("Open {0}", [__(target)]), + index: me.fuzzy_search(keywords, 'Calendar'), + match: target, + route: [target, 'Event'], + }); + } + return out; + }, + + get_modules: function(keywords) { + var me = this; + var out = []; + Object.keys(frappe.modules).forEach(function(item) { + var level = me.fuzzy_search(keywords, item); + if(level > 0) { + var module = frappe.modules[item]; + if(module._doctype) return; + ret = { + type: "Module", + prefix: "Open ", + label: __(me.bolden_match_part(item, keywords)), + value: __("Open {0}", [__(item)]), + index: level, + } + if(module.link) { + ret.route = [module.link]; + } else { + ret.route = ["Module", item]; + } + out.push(ret); + } + }); + return out; + }, + + get_global_results: function (keywords, start, limit, doctype = "") { + var me = this; + function get_results_sets(data) { + var results_sets = [], result, set; + function get_existing_set (doctype) { + return results_sets.find(function(set) { + return set.title === doctype; + }); + } + + function make_description(content, doc_name) { + parts = content.split("|||"); + content_length = 300; + fields = []; + current_length = 0; + var field_text = ""; + for(var i = 0; i < parts.length; i++) { + part = parts[i]; + if(part.toLowerCase().indexOf(keywords) !== -1) { + if(part.indexOf('&&&') !== -1) { + var colon_index = part.indexOf('&&&'); + var field_value = part.slice(colon_index + 3); + } else { + var colon_index = part.indexOf(':'); + var field_value = part.slice(colon_index + 1); + } + var field_name = part.slice(0, colon_index); + + var remaining_length = content_length - current_length; + current_length += field_name.length + field_value.length + 2; + if(current_length < content_length) { + field_text = '' + + me.bolden_match_part(field_name, keywords) + ':' + '' + + me.bolden_match_part(field_value, keywords); + if(fields.indexOf(field_text) === -1 && doc_name !== field_value) { + fields.push(field_text); + } + } else { + if(field_name.length < remaining_length){ + remaining_length -= field_name.length; + field_text = '' + + me.bolden_match_part(field_name, keywords) + ':' + ''; + field_value = field_value.slice(0, remaining_length); + field_value = field_value.slice(0, field_value.lastIndexOf(' ')) + ' ...'; + field_text += me.bolden_match_part(field_value, keywords); + fields.push(field_text); + } else { + fields.push('...'); + } + break; + } + } + } + return fields.join(', '); + } + + data.forEach(function(d) { + // more properties + result = { + label: d.name, + value: d.name, + description: make_description(d.content, d.name), + route: ['Form', d.doctype, d.name], + } + if(d.image || d.image === null){ + result.image = d.image; + } + set = get_existing_set(d.doctype); + if(set) { + set.results.push(result); + } else { + set = { + title: d.doctype, + results: [result], + fetch_type: "Global" + } + results_sets.push(set); + } + + }); + return results_sets; + } + return new Promise(function(resolve, reject) { + frappe.call({ + method: "frappe.utils.global_search.search", + args: { + text: keywords, + start: start, + limit: limit, + doctype: doctype + }, + callback: function(r) { + if(r.message) { + resolve(get_results_sets(r.message)); + } else { + resolve([]); + } + } + }); + }); + }, + + get_help_results: function(keywords) { + function get_results_set(data) { + var result; + var set = { + title: "Help", + fetch_type: "Help", + results: [] + } + data.forEach(function(d) { + // more properties + result = { + label: d[0], + value: d[0], + description: d[1], + data_path: d[2], + onclick: function() { + + } + } + set.results.push(result); + }); + return [set]; + } + return new Promise(function(resolve, reject) { + frappe.call({ + method: "frappe.utils.help.get_help", + args: { + text: keywords + }, + callback: function(r) { + if(r.message) { + resolve(get_results_set(r.message)); + } else { + resolve([]); + } + } + }); + }); + }, + + get_nav_results: function(keywords) { + function sort_uniques(array) { + var routes = [], out = []; + array.forEach(function(d) { + if(d.route) { + if(d.route[0] === "List" && d.route[2]) { + d.route.splice(2); + } + var str_route = d.route.join('/'); + if(routes.indexOf(str_route) === -1) { + routes.push(str_route); + out.push(d); + } else { + var old = routes.indexOf(str_route); + if(out[old].index > d.index) { + out[old] = d; + } + } + } else { + out.push(d); + } + }); + return out.sort(function(a, b) { + return b.index - a.index; + }); + } + var lists = [], setup = []; + var all_doctypes = sort_uniques(this.get_doctypes(keywords)); + all_doctypes.forEach(function(d) { + if(d.type === "") { + setup.push(d); + } else { + lists.push(d); + } + }); + var in_keyword = keywords.split(" in ")[0]; + return [ + {title: "Recents", fetch_type: "Nav", results: sort_uniques(this.get_recent_pages(keywords))}, + {title: "Create a new ...", fetch_type: "Nav", results: sort_uniques(this.get_creatables(keywords))}, + {title: "Find '" + in_keyword + "' in ... ", fetch_type: "Nav", results: sort_uniques(this.get_search_in_list(keywords))}, + {title: "Lists", fetch_type: "Nav", results: lists}, + {title: "Reports", fetch_type: "Nav", results: sort_uniques(this.get_reports(keywords))}, + {title: "Administration", fetch_type: "Nav", results: sort_uniques(this.get_pages(keywords))}, + {title: "Modules", fetch_type: "Nav", results: sort_uniques(this.get_modules(keywords))}, + {title: "Setup", fetch_type: "Nav", results: setup}, + ] + }, + + fuzzy_search: function(keywords, _item) { + // Returns 10 for case-perfect contain, 0 for not found + // 9 for perfect contain, + // 0 - 6 for fuzzy contain + + // **Specific use-case step** + item = __(_item || '').replace(/-/g, " "); + + var ilen = item.length; + var klen = keywords.length; + var length_ratio = klen/ilen; + var max_skips = 3, max_mismatch_len = 2; + + if (klen > ilen) { return 0; } + + if(keywords === item || item.toLowerCase().indexOf(keywords) === 0) { + return 10 + length_ratio; + } + + if (item.indexOf(keywords) !== -1 && keywords !== keywords.toLowerCase()) { + return 9 + length_ratio; + } + + item = item.toLowerCase(); + keywords = keywords.toLowerCase(); + + if (item.indexOf(keywords) !== -1) { + return 8 + length_ratio; + } + + var skips = 0, mismatches = 0; + outer: for (var i = 0, j = 0; i < klen; i++) { + if(mismatches !== 0) skips++; + if(skips > max_skips) return 0; + var k_ch = keywords.charCodeAt(i); + mismatches = 0; + while (j < ilen) { + if (item.charCodeAt(j++) === k_ch) { + continue outer; + } + if(++mismatches > max_mismatch_len) return 0 ; + } + return 0; + } + + // Since indexOf didn't pass, there will be atleast 1 skip + // hence no divide by zero, but just to be safe + if((skips + mismatches) > 0) { + return (5 + length_ratio)/(skips + mismatches); + } else { + return 0; + } + }, + + bolden_match_part: function(str, subseq) { + var rendered = ""; + if(this.fuzzy_search(subseq, str) === 0) { + return str; + } else if(this.fuzzy_search(subseq, str) > 6) { + var regEx = new RegExp("("+ subseq +")", "ig"); + return str.replace(regEx, '$1'); + } else { + var str_orig = str; + var str = str.toLowerCase(); + var str_len = str.length; + var subseq = subseq.toLowerCase(); + + outer: for(var i = 0, j = 0; i < subseq.length; i++) { + var sub_ch = subseq.charCodeAt(i); + while(j < str_len) { + if(str.charCodeAt(j) === sub_ch) { + var str_char = str_orig.charAt(j); + if(str_char === str_char.toLowerCase()) { + rendered += '' + subseq.charAt(i) + ''; + } else { + rendered += '' + subseq.charAt(i).toUpperCase() + ''; + } + j++; + continue outer; + } + rendered += str_orig.charAt(j); + j++; + } + return str_orig; + } + rendered += str_orig.slice(j); + return rendered; + } + + }, + + unscrub_and_titlecase: function(str) { + return __(str || '').replace(/-|_/g, " ").replace(/\w*/g, + function(keywords){return keywords.charAt(0).toUpperCase() + keywords.substr(1).toLowerCase();}); + }, +} \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/toolbar/toolbar.js b/frappe/public/js/frappe/ui/toolbar/toolbar.js index 4fa42cbb61..70ef6b71af 100644 --- a/frappe/public/js/frappe/ui/toolbar/toolbar.js +++ b/frappe/public/js/frappe/ui/toolbar/toolbar.js @@ -12,12 +12,11 @@ frappe.ui.toolbar.Toolbar = Class.extend({ this.setup_sidebar(); - this.awesome_bar = new frappe.search.AwesomeBar(); - this.awesome_bar.setup("#navbar-search"); - this.awesome_bar.setup("#modal-search"); + var awesome_bar = new frappe.search.AwesomeBar(); + awesome_bar.setup("#navbar-search"); + awesome_bar.setup("#modal-search"); - this.search = this.awesome_bar.search; - this.help = this.awesome_bar.help; + this.setup_help(); $(document).on("notification-update", function() { frappe.ui.notifications.update_notifications(); @@ -25,8 +24,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({ $('.dropdown-toggle').dropdown(); - this.setup_help(); - $(document).trigger('toolbar_setup'); // clear all custom menus on page change @@ -81,6 +78,12 @@ frappe.ui.toolbar.Toolbar = Class.extend({ }, setup_help: function () { + frappe.provide('frappe.help'); + frappe.help.show_results = show_results; + + this.search = new frappe.search.SearchDialog(); + frappe.provide('frappe.searchdialog'); + frappe.searchdialog.search = this.search; $(".dropdown-help .dropdown-toggle").on("click", function () { $(".dropdown-help input").focus(); @@ -154,13 +157,9 @@ frappe.ui.toolbar.Toolbar = Class.extend({ var me = this; function show_help_results(keywords) { - me.search.search_dialog.show(); - me.search.setup_search(keywords, [me.help]); + me.search.init_search(keywords, "help"); } - frappe.provide('frappe.help'); - frappe.help.show_results = show_results; - function show_results(e) { //edit links href = e.target.href; diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index 3b25191c94..98614a635b 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -550,14 +550,85 @@ textarea.form-control { .search-dialog { .modal-dialog { width: 768px; - height: 500px; + } + + .search-header { + display: flex; + align-items: center; + padding: 5px; } .modal-body { padding: 0px 15px; + + } + + .empty-state { + color: #d4d9dd; + height: 500px; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + + .status-icon { + font-size: 40px; + position: relative; + margin-bottom: 10px; + } + + p { + font-size: 15px; + display: block; + } + + .cover { + color: white; + font-size: 6px; + position: absolute; + } + } + + @keyframes twinkle { + 0% { opacity:1; } + 50% { opacity:0; } + 100% { opacity:1; } + } + @-o-keyframes twinkle { + 0% { opacity:1; } + 50% { opacity:0; } + 100% { opacity:1; } + } + @-moz-keyframes twinkle { + 0% { opacity:1; } + 50% { opacity:0; } + 100% { opacity:1; } + } + @-webkit-keyframes twinkle { + 0% { opacity:1; } + 50% { opacity:0; } + 100% { opacity:1; } + } + .twinkle-one { + -webkit-animation: twinkle 1.5s ease infinite; + -moz-animation: twinkle 1.5s ease infinite; + -o-animation: twinkle 1.5s ease infinite; + animation: twinkle 1.5s ease infinite; + } + .twinkle-two { + -webkit-animation: twinkle 1.5s ease infinite 0.5s; + -moz-animation: twinkle 1.5s ease infinite 0.5s; + -o-animation: twinkle 1.5s ease infinite 0.5s; + animation: twinkle 1.5s ease infinite 0.5s; + } + .twinkle-three { + -webkit-animation: twinkle 1.5s ease infinite 1s; + -moz-animation: twinkle 1.5s ease infinite 1s; + -o-animation: twinkle 1.5s ease infinite 1s; + animation: twinkle 1.5s ease infinite 1s; } - input.form-control, .input-group-addon { + input.form-control { border: none; border-left-style:none; } @@ -567,10 +638,6 @@ textarea.form-control { box-shadow: none; } - .input-group-addon{ - background-color: #FFF; - } - .layout-side-section, .layout-main-section { height: 500px; @@ -593,6 +660,7 @@ textarea.form-control { align-items: center; justify-content: space-between; padding-left: 20px; + background-color: #ffffff; i { visibility: hidden; @@ -602,36 +670,14 @@ textarea.form-control { .active i { visibility: visible; } - } - } - - .results-area { - .search-intro-placeholder { - color: #d4d9dd; - height: inherit; - display: flex; - justify-content: center; - align-items: center; - - span { - text-align: center; - i { - font-size: 64px; - display: block; - } - - p { - font-size: 15px; - display: block; - } + .select a, a:hover { + background-color: #f7fafc; } } + } - // a { - // color: #5E64FF; - // } - + .results-area { .single-link a { color: #36414c; } @@ -647,6 +693,10 @@ textarea.form-control { font-family: 'Octicons'; content: '\f0a4'; } + + .result { + margin-bottom: 5px; + } } .full-list { @@ -684,6 +734,34 @@ textarea.form-control { display: none; } + .result { + p { + margin-top: 5px; + margin-bottom: 5px; + } + + .result-image { + display: inline-block; + margin-right: 10px; + height: 60px; + width: 60px; + background-color: #fafbfc; + + .flex-text { + display: flex; + height: 60px; + align-items: center; + justify-content: center; + } + + span { + font-size: 30px; + color: @text-extra-muted; + } + } + + } + @media (max-width: @screen-xs) { .modal-dialog { width: auto; @@ -699,10 +777,6 @@ textarea.form-control { margin: 0px; border-top: none; } - - .layout-side-section .sidebar-menu { - margin: 30px 0px; - } } @media (min-width: 600px) { @@ -712,16 +786,6 @@ textarea.form-control { } } -.result { - p { - margin-top: 0.2em; - } -} - -.search-result { - margin-bottom: 24px; -} - .note-editor.note-frame .note-editing-area .note-editable { color: @text-color; } diff --git a/frappe/public/less/list.less b/frappe/public/less/list.less index 0da6782f28..cfe5e120a8 100644 --- a/frappe/public/less/list.less +++ b/frappe/public/less/list.less @@ -514,6 +514,7 @@ input[type=checkbox] { margin: 0; margin-right: 5px; + flex: 0 0 12px; } .liked-by, .liked-by-filter-button { diff --git a/frappe/utils/global_search.py b/frappe/utils/global_search.py index b8fc9c31d1..8b9723db75 100644 --- a/frappe/utils/global_search.py +++ b/frappe/utils/global_search.py @@ -110,7 +110,7 @@ def delete_for_document(doc): name = %s''', (doc.doctype, doc.name), as_dict=True) @frappe.whitelist() -def search(text, start=0, limit=20): +def search(text, start=0, limit=20, doctype=""): '''Search for given text in __global_search :param text: phrase to be searched :param start: start results at, default 0 @@ -118,14 +118,31 @@ def search(text, start=0, limit=20): :return: Array of result objects''' text = "+" + text + "*" - results = frappe.db.sql(''' - select - doctype, name, content - from - __global_search - where - match(content) against (%s IN BOOLEAN MODE) - limit {start}, {limit}'''.format(start=start, limit=limit), text, as_dict=True) + if not doctype: + results = frappe.db.sql(''' + select + doctype, name, content + from + __global_search + where + match(content) against (%s IN BOOLEAN MODE) + limit {start}, {limit}'''.format(start=start, limit=limit), text+"*", as_dict=True) + else: + results = frappe.db.sql(''' + select + doctype, name, content + from + __global_search + where + doctype = %s AND + match(content) against (%s IN BOOLEAN MODE) + limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True) + + for r in results: + if frappe.get_meta(r.doctype).image_field: + doc = frappe.get_doc(r.doctype, r.name) + r.image = doc.get(doc.meta.image_field) + return results @frappe.whitelist(allow_guest=True) @@ -148,45 +165,3 @@ def web_search(text, start=0, limit=20): limit {start}, {limit}'''.format(start=start, limit=limit), text, as_dict=True) return results - -@frappe.whitelist() -def get_search_doctypes(text): - '''Search for all t - :param text: phrase to be searched - :return: Array of result objects''' - - text = "+" + text + "*" - results = frappe.db.sql(''' - select - doctype - from - __global_search - where - match(content) against (%s IN BOOLEAN MODE) - group by - doctype - order by - count(doctype) desc limit 0, 80''', text, as_dict=True) - return results - -@frappe.whitelist() -def search_in_doctype(doctype, text, start, limit): - '''Search for given text in given doctype in __global_search - :param doctype: doctype to be searched in - :param text: phrase to be searched - :param start: start results at, default 0 - :param limit: number of results to return, default 20 - :return: Array of result objects''' - - text = "+" + text + "*" - results = frappe.db.sql(''' - select - doctype, name, content - from - __global_search - where - doctype = %s AND - match(content) against (%s IN BOOLEAN MODE) - limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True) - - return results